@beastmode-develeap/beastmode 0.1.332 → 0.1.334
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.js +583 -558
- package/dist/index.js.map +1 -1
- package/dist/web/board.html +1 -1
- package/dist/web/build-commit.txt +1 -1
- package/dist/web/build-stamp.txt +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1637,11 +1637,36 @@ var init_secret_validator = __esm({
|
|
|
1637
1637
|
});
|
|
1638
1638
|
|
|
1639
1639
|
// src/engine/version.ts
|
|
1640
|
-
|
|
1640
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1641
|
+
import { fileURLToPath } from "url";
|
|
1642
|
+
import { dirname, join as join2 } from "path";
|
|
1643
|
+
function resolveEngineVersion() {
|
|
1644
|
+
try {
|
|
1645
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
1646
|
+
for (let i = 0; i < 6; i++) {
|
|
1647
|
+
const candidate = join2(dir, "package.json");
|
|
1648
|
+
try {
|
|
1649
|
+
const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
|
|
1650
|
+
if (pkg.name === PACKAGE_NAME && pkg.version) {
|
|
1651
|
+
return pkg.version;
|
|
1652
|
+
}
|
|
1653
|
+
} catch {
|
|
1654
|
+
}
|
|
1655
|
+
const parent = dirname(dir);
|
|
1656
|
+
if (parent === dir) break;
|
|
1657
|
+
dir = parent;
|
|
1658
|
+
}
|
|
1659
|
+
} catch {
|
|
1660
|
+
}
|
|
1661
|
+
return FALLBACK_VERSION;
|
|
1662
|
+
}
|
|
1663
|
+
var FALLBACK_VERSION, PACKAGE_NAME, ENGINE_VERSION, SCHEMA_VERSION;
|
|
1641
1664
|
var init_version = __esm({
|
|
1642
1665
|
"src/engine/version.ts"() {
|
|
1643
1666
|
"use strict";
|
|
1644
|
-
|
|
1667
|
+
FALLBACK_VERSION = "0.0.0-unknown";
|
|
1668
|
+
PACKAGE_NAME = "@beastmode-develeap/beastmode";
|
|
1669
|
+
ENGINE_VERSION = resolveEngineVersion();
|
|
1645
1670
|
SCHEMA_VERSION = 1;
|
|
1646
1671
|
}
|
|
1647
1672
|
});
|
|
@@ -1839,21 +1864,21 @@ var init_template_importer = __esm({
|
|
|
1839
1864
|
});
|
|
1840
1865
|
|
|
1841
1866
|
// src/engine/config-validator.ts
|
|
1842
|
-
import { existsSync as existsSync2, readFileSync as
|
|
1843
|
-
import { join as
|
|
1867
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
1868
|
+
import { join as join3 } from "path";
|
|
1844
1869
|
function validateFactory(factoryDir, env) {
|
|
1845
1870
|
const errors = [];
|
|
1846
1871
|
const warnings = [];
|
|
1847
|
-
const bmDir =
|
|
1872
|
+
const bmDir = join3(factoryDir, ".beastmode");
|
|
1848
1873
|
if (!existsSync2(bmDir)) {
|
|
1849
1874
|
return { valid: false, errors: [`No factory found at ${factoryDir} (.beastmode directory missing)`], warnings };
|
|
1850
1875
|
}
|
|
1851
|
-
const factoryJsonPath =
|
|
1876
|
+
const factoryJsonPath = join3(bmDir, "factory.json");
|
|
1852
1877
|
if (!existsSync2(factoryJsonPath)) {
|
|
1853
1878
|
errors.push("Missing .beastmode/factory.json");
|
|
1854
1879
|
} else {
|
|
1855
1880
|
try {
|
|
1856
|
-
const raw = JSON.parse(
|
|
1881
|
+
const raw = JSON.parse(readFileSync3(factoryJsonPath, "utf-8"));
|
|
1857
1882
|
const result = FactoryIdentitySchema.safeParse(raw);
|
|
1858
1883
|
if (!result.success) {
|
|
1859
1884
|
errors.push(`Invalid factory.json: ${result.error.issues.map((i) => i.message).join(", ")}`);
|
|
@@ -1862,12 +1887,12 @@ function validateFactory(factoryDir, env) {
|
|
|
1862
1887
|
errors.push("factory.json is not valid JSON");
|
|
1863
1888
|
}
|
|
1864
1889
|
}
|
|
1865
|
-
const configPath =
|
|
1890
|
+
const configPath = join3(bmDir, "config.json");
|
|
1866
1891
|
if (!existsSync2(configPath)) {
|
|
1867
1892
|
errors.push("Missing .beastmode/config.json");
|
|
1868
1893
|
} else {
|
|
1869
1894
|
try {
|
|
1870
|
-
const raw = JSON.parse(
|
|
1895
|
+
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1871
1896
|
const result = FactoryConfigSchema.safeParse(raw);
|
|
1872
1897
|
if (!result.success) {
|
|
1873
1898
|
for (const issue of result.error.issues) {
|
|
@@ -2267,8 +2292,8 @@ var init_plugin_resolver = __esm({
|
|
|
2267
2292
|
});
|
|
2268
2293
|
|
|
2269
2294
|
// src/engine/plugin-registry.ts
|
|
2270
|
-
import { readFileSync as
|
|
2271
|
-
import { join as
|
|
2295
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
|
|
2296
|
+
import { join as join4, resolve } from "path";
|
|
2272
2297
|
function getRegistryUrl() {
|
|
2273
2298
|
return process.env.BEASTMODE_REGISTRY_URL || DEFAULT_REGISTRY_URL;
|
|
2274
2299
|
}
|
|
@@ -2280,24 +2305,24 @@ function readLocalRegistry(filePath) {
|
|
|
2280
2305
|
if (!existsSync3(resolved)) {
|
|
2281
2306
|
throw new Error(`Local registry file not found: ${resolved}`);
|
|
2282
2307
|
}
|
|
2283
|
-
const raw = JSON.parse(
|
|
2308
|
+
const raw = JSON.parse(readFileSync4(resolved, "utf-8"));
|
|
2284
2309
|
return RegistryIndexSchema.parse(raw);
|
|
2285
2310
|
}
|
|
2286
2311
|
function getCachePath(factoryDir) {
|
|
2287
|
-
return
|
|
2312
|
+
return join4(factoryDir, ".beastmode", ".cache", "registry-index.json");
|
|
2288
2313
|
}
|
|
2289
2314
|
function readCache(factoryDir) {
|
|
2290
2315
|
const cachePath = getCachePath(factoryDir);
|
|
2291
2316
|
if (!existsSync3(cachePath)) return null;
|
|
2292
2317
|
try {
|
|
2293
|
-
return JSON.parse(
|
|
2318
|
+
return JSON.parse(readFileSync4(cachePath, "utf-8"));
|
|
2294
2319
|
} catch {
|
|
2295
2320
|
return null;
|
|
2296
2321
|
}
|
|
2297
2322
|
}
|
|
2298
2323
|
function writeCache(factoryDir, data) {
|
|
2299
2324
|
const cachePath = getCachePath(factoryDir);
|
|
2300
|
-
const cacheDir =
|
|
2325
|
+
const cacheDir = join4(factoryDir, ".beastmode", ".cache");
|
|
2301
2326
|
mkdirSync(cacheDir, { recursive: true });
|
|
2302
2327
|
writeFileSync(
|
|
2303
2328
|
cachePath,
|
|
@@ -2368,20 +2393,20 @@ var init_plugin_registry = __esm({
|
|
|
2368
2393
|
});
|
|
2369
2394
|
|
|
2370
2395
|
// src/engine/mcp-manager.ts
|
|
2371
|
-
import { readFileSync as
|
|
2372
|
-
import { dirname, join as
|
|
2396
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
2397
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
2373
2398
|
function getMcpServersPath(factoryDir) {
|
|
2374
|
-
return
|
|
2399
|
+
return join5(factoryDir, ".beastmode", "mcp-servers.json");
|
|
2375
2400
|
}
|
|
2376
2401
|
function readMcpServers(factoryDir) {
|
|
2377
2402
|
const filePath = getMcpServersPath(factoryDir);
|
|
2378
2403
|
if (!existsSync4(filePath)) return {};
|
|
2379
|
-
const raw = JSON.parse(
|
|
2404
|
+
const raw = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
2380
2405
|
return raw.servers || {};
|
|
2381
2406
|
}
|
|
2382
2407
|
function writeMcpServers(factoryDir, servers) {
|
|
2383
2408
|
const filePath = getMcpServersPath(factoryDir);
|
|
2384
|
-
const dir =
|
|
2409
|
+
const dir = dirname2(filePath);
|
|
2385
2410
|
if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
|
|
2386
2411
|
writeFileSync2(filePath, JSON.stringify({ servers }, null, 2) + "\n", "utf-8");
|
|
2387
2412
|
}
|
|
@@ -2413,10 +2438,10 @@ var init_mcp_manager = __esm({
|
|
|
2413
2438
|
});
|
|
2414
2439
|
|
|
2415
2440
|
// src/engine/hook-manager.ts
|
|
2416
|
-
import { readFileSync as
|
|
2417
|
-
import { dirname as
|
|
2441
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
2442
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
2418
2443
|
function getHooksPath(factoryDir) {
|
|
2419
|
-
return
|
|
2444
|
+
return join6(factoryDir, ".beastmode", "hooks.json");
|
|
2420
2445
|
}
|
|
2421
2446
|
function validateEvent(event) {
|
|
2422
2447
|
if (!HOOK_EVENTS.includes(event)) {
|
|
@@ -2433,12 +2458,12 @@ function emptyHooks() {
|
|
|
2433
2458
|
function readHooks(factoryDir) {
|
|
2434
2459
|
const filePath = getHooksPath(factoryDir);
|
|
2435
2460
|
if (!existsSync5(filePath)) return emptyHooks();
|
|
2436
|
-
const raw = JSON.parse(
|
|
2461
|
+
const raw = JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
2437
2462
|
return raw.hooks || emptyHooks();
|
|
2438
2463
|
}
|
|
2439
2464
|
function writeHooks(factoryDir, hooks) {
|
|
2440
2465
|
const filePath = getHooksPath(factoryDir);
|
|
2441
|
-
const dir =
|
|
2466
|
+
const dir = dirname3(filePath);
|
|
2442
2467
|
if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
|
|
2443
2468
|
writeFileSync3(
|
|
2444
2469
|
filePath,
|
|
@@ -2487,40 +2512,40 @@ var init_hook_manager = __esm({
|
|
|
2487
2512
|
// src/engine/plugin-installer.ts
|
|
2488
2513
|
import {
|
|
2489
2514
|
existsSync as existsSync6,
|
|
2490
|
-
readFileSync as
|
|
2515
|
+
readFileSync as readFileSync7,
|
|
2491
2516
|
writeFileSync as writeFileSync4,
|
|
2492
2517
|
cpSync,
|
|
2493
2518
|
rmSync,
|
|
2494
2519
|
mkdirSync as mkdirSync4
|
|
2495
2520
|
} from "fs";
|
|
2496
|
-
import { join as
|
|
2521
|
+
import { join as join7, resolve as resolve2 } from "path";
|
|
2497
2522
|
function readLock(factoryDir) {
|
|
2498
|
-
const lockPath =
|
|
2523
|
+
const lockPath = join7(factoryDir, ".beastmode", "extensions.lock");
|
|
2499
2524
|
if (!existsSync6(lockPath)) {
|
|
2500
2525
|
return { plugins: {}, locked_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2501
2526
|
}
|
|
2502
|
-
return JSON.parse(
|
|
2527
|
+
return JSON.parse(readFileSync7(lockPath, "utf-8"));
|
|
2503
2528
|
}
|
|
2504
2529
|
function writeLock(factoryDir, lock) {
|
|
2505
|
-
const lockPath =
|
|
2530
|
+
const lockPath = join7(factoryDir, ".beastmode", "extensions.lock");
|
|
2506
2531
|
lock.locked_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2507
2532
|
writeFileSync4(lockPath, JSON.stringify(lock, null, 2) + "\n", "utf-8");
|
|
2508
2533
|
}
|
|
2509
2534
|
function readConfig(factoryDir) {
|
|
2510
|
-
const configPath =
|
|
2535
|
+
const configPath = join7(factoryDir, ".beastmode", "config.json");
|
|
2511
2536
|
if (!existsSync6(configPath)) return {};
|
|
2512
|
-
return JSON.parse(
|
|
2537
|
+
return JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
2513
2538
|
}
|
|
2514
2539
|
function writeConfig(factoryDir, config) {
|
|
2515
|
-
const configPath =
|
|
2540
|
+
const configPath = join7(factoryDir, ".beastmode", "config.json");
|
|
2516
2541
|
writeFileSync4(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2517
2542
|
}
|
|
2518
2543
|
function registerPluginMcps(factoryDir, pluginName, pluginDir, manifest) {
|
|
2519
2544
|
const mcpNames = manifest.provides?.mcp_servers || [];
|
|
2520
2545
|
for (const mcpName of mcpNames) {
|
|
2521
|
-
const mcpDefPath =
|
|
2546
|
+
const mcpDefPath = join7(pluginDir, "mcp-servers", `${mcpName}.json`);
|
|
2522
2547
|
if (!existsSync6(mcpDefPath)) continue;
|
|
2523
|
-
const mcpDef = JSON.parse(
|
|
2548
|
+
const mcpDef = JSON.parse(readFileSync7(mcpDefPath, "utf-8"));
|
|
2524
2549
|
addMcp(factoryDir, mcpName, {
|
|
2525
2550
|
command: mcpDef.command,
|
|
2526
2551
|
args: mcpDef.args || [],
|
|
@@ -2533,9 +2558,9 @@ function registerPluginMcps(factoryDir, pluginName, pluginDir, manifest) {
|
|
|
2533
2558
|
function registerPluginHooks(factoryDir, pluginName, pluginDir, manifest) {
|
|
2534
2559
|
const hookNames = manifest.provides?.hooks || [];
|
|
2535
2560
|
for (const hookName of hookNames) {
|
|
2536
|
-
const hookDefPath =
|
|
2561
|
+
const hookDefPath = join7(pluginDir, "hooks", `${hookName}.json`);
|
|
2537
2562
|
if (!existsSync6(hookDefPath)) continue;
|
|
2538
|
-
const hookDef = JSON.parse(
|
|
2563
|
+
const hookDef = JSON.parse(readFileSync7(hookDefPath, "utf-8"));
|
|
2539
2564
|
const event = hookDef.event;
|
|
2540
2565
|
if (!HOOK_EVENTS.includes(event)) continue;
|
|
2541
2566
|
addHook(factoryDir, event, {
|
|
@@ -2569,13 +2594,13 @@ function unregisterPluginHooks(factoryDir, pluginName) {
|
|
|
2569
2594
|
}
|
|
2570
2595
|
async function installPlugin(factoryDir, sourcePath) {
|
|
2571
2596
|
const resolvedPath = resolve2(sourcePath);
|
|
2572
|
-
const manifestPath =
|
|
2597
|
+
const manifestPath = join7(resolvedPath, "plugin.json");
|
|
2573
2598
|
if (!existsSync6(manifestPath)) {
|
|
2574
2599
|
throw new Error(
|
|
2575
2600
|
`plugin.json not found at ${manifestPath}. Is this a BeastMode plugin directory?`
|
|
2576
2601
|
);
|
|
2577
2602
|
}
|
|
2578
|
-
const rawManifest = JSON.parse(
|
|
2603
|
+
const rawManifest = JSON.parse(readFileSync7(manifestPath, "utf-8"));
|
|
2579
2604
|
const manifest = parseManifest(rawManifest);
|
|
2580
2605
|
const compat = checkEngineCompat(manifest.engine_version, ENGINE_VERSION);
|
|
2581
2606
|
if (!compat.compatible) {
|
|
@@ -2589,15 +2614,15 @@ async function installPlugin(factoryDir, sourcePath) {
|
|
|
2589
2614
|
`Plugin '${manifest.name}' is already installed. Remove it first with 'beastmode remove plugin ${manifest.name}'.`
|
|
2590
2615
|
);
|
|
2591
2616
|
}
|
|
2592
|
-
const destDir =
|
|
2617
|
+
const destDir = join7(factoryDir, ".beastmode", "plugins", manifest.name);
|
|
2593
2618
|
mkdirSync4(destDir, { recursive: true });
|
|
2594
2619
|
cpSync(resolvedPath, destDir, { recursive: true });
|
|
2595
2620
|
registerPluginMcps(factoryDir, manifest.name, destDir, manifest);
|
|
2596
2621
|
registerPluginHooks(factoryDir, manifest.name, destDir, manifest);
|
|
2597
2622
|
if (manifest.config_defaults) {
|
|
2598
|
-
const defaultsPath =
|
|
2623
|
+
const defaultsPath = join7(destDir, manifest.config_defaults);
|
|
2599
2624
|
if (existsSync6(defaultsPath)) {
|
|
2600
|
-
const defaults = JSON.parse(
|
|
2625
|
+
const defaults = JSON.parse(readFileSync7(defaultsPath, "utf-8"));
|
|
2601
2626
|
const currentConfig = readConfig(factoryDir);
|
|
2602
2627
|
const mergedConfig = mergePluginDefaults(currentConfig, defaults);
|
|
2603
2628
|
writeConfig(factoryDir, mergedConfig);
|
|
@@ -2620,13 +2645,13 @@ function removePlugin(factoryDir, name, options) {
|
|
|
2620
2645
|
}
|
|
2621
2646
|
if (!force) {
|
|
2622
2647
|
const dependents = [];
|
|
2623
|
-
const pluginsDir =
|
|
2648
|
+
const pluginsDir = join7(factoryDir, ".beastmode", "plugins");
|
|
2624
2649
|
for (const [pluginName, _entry] of Object.entries(lock.plugins)) {
|
|
2625
2650
|
if (pluginName === name) continue;
|
|
2626
|
-
const pluginManifestPath =
|
|
2651
|
+
const pluginManifestPath = join7(pluginsDir, pluginName, "plugin.json");
|
|
2627
2652
|
if (!existsSync6(pluginManifestPath)) continue;
|
|
2628
2653
|
try {
|
|
2629
|
-
const pluginManifest = JSON.parse(
|
|
2654
|
+
const pluginManifest = JSON.parse(readFileSync7(pluginManifestPath, "utf-8"));
|
|
2630
2655
|
if (Array.isArray(pluginManifest.dependencies) && pluginManifest.dependencies.includes(name)) {
|
|
2631
2656
|
dependents.push(pluginName);
|
|
2632
2657
|
}
|
|
@@ -2641,7 +2666,7 @@ function removePlugin(factoryDir, name, options) {
|
|
|
2641
2666
|
}
|
|
2642
2667
|
unregisterPluginMcps(factoryDir, name);
|
|
2643
2668
|
unregisterPluginHooks(factoryDir, name);
|
|
2644
|
-
const pluginDir =
|
|
2669
|
+
const pluginDir = join7(factoryDir, ".beastmode", "plugins", name);
|
|
2645
2670
|
if (existsSync6(pluginDir)) {
|
|
2646
2671
|
rmSync(pluginDir, { recursive: true, force: true });
|
|
2647
2672
|
}
|
|
@@ -2665,12 +2690,12 @@ import {
|
|
|
2665
2690
|
readdirSync as readdirSync2,
|
|
2666
2691
|
cpSync as cpSync2,
|
|
2667
2692
|
rmSync as rmSync2,
|
|
2668
|
-
readFileSync as
|
|
2693
|
+
readFileSync as readFileSync8,
|
|
2669
2694
|
writeFileSync as writeFileSync5,
|
|
2670
2695
|
mkdirSync as mkdirSync5,
|
|
2671
2696
|
statSync as statSync2
|
|
2672
2697
|
} from "fs";
|
|
2673
|
-
import { join as
|
|
2698
|
+
import { join as join8, basename as basename2 } from "path";
|
|
2674
2699
|
function parseSkillFrontmatter(content) {
|
|
2675
2700
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2676
2701
|
if (!match) return {};
|
|
@@ -2693,14 +2718,14 @@ function addSkill(factoryDir, sourcePath) {
|
|
|
2693
2718
|
if (!stat.isDirectory()) {
|
|
2694
2719
|
throw new Error(`Skill source must be a directory: ${sourcePath}`);
|
|
2695
2720
|
}
|
|
2696
|
-
const skillMdPath =
|
|
2721
|
+
const skillMdPath = join8(sourcePath, "SKILL.md");
|
|
2697
2722
|
if (!existsSync7(skillMdPath)) {
|
|
2698
2723
|
throw new Error(
|
|
2699
2724
|
`SKILL.md not found in ${sourcePath}. Every skill must have a SKILL.md with frontmatter.`
|
|
2700
2725
|
);
|
|
2701
2726
|
}
|
|
2702
2727
|
const skillName = basename2(sourcePath);
|
|
2703
|
-
const destPath =
|
|
2728
|
+
const destPath = join8(factoryDir, ".beastmode", "skills", skillName);
|
|
2704
2729
|
if (existsSync7(destPath)) {
|
|
2705
2730
|
throw new Error(
|
|
2706
2731
|
`Skill '${skillName}' already exists at ${destPath}. Remove it first.`
|
|
@@ -2714,7 +2739,7 @@ function createSkill(factoryDir, name, description, body) {
|
|
|
2714
2739
|
if (!body.trim()) throw new Error("Skill body is required");
|
|
2715
2740
|
const slug = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2716
2741
|
if (!slug) throw new Error("Skill name must contain alphanumeric characters");
|
|
2717
|
-
const skillDir =
|
|
2742
|
+
const skillDir = join8(factoryDir, ".beastmode", "skills", slug);
|
|
2718
2743
|
if (existsSync7(skillDir)) {
|
|
2719
2744
|
throw new Error(
|
|
2720
2745
|
`Skill '${slug}' already exists. Remove it first or pick a different name.`
|
|
@@ -2731,11 +2756,11 @@ description: ${q(description.trim())}
|
|
|
2731
2756
|
|
|
2732
2757
|
${body.trim()}
|
|
2733
2758
|
`;
|
|
2734
|
-
writeFileSync5(
|
|
2759
|
+
writeFileSync5(join8(skillDir, "SKILL.md"), content, "utf-8");
|
|
2735
2760
|
return { slug, path: skillDir };
|
|
2736
2761
|
}
|
|
2737
2762
|
function removeSkill(factoryDir, name) {
|
|
2738
|
-
const skillPath =
|
|
2763
|
+
const skillPath = join8(factoryDir, ".beastmode", "skills", name);
|
|
2739
2764
|
if (!existsSync7(skillPath)) {
|
|
2740
2765
|
throw new Error(`Skill '${name}' not found at ${skillPath}.`);
|
|
2741
2766
|
}
|
|
@@ -2743,32 +2768,32 @@ function removeSkill(factoryDir, name) {
|
|
|
2743
2768
|
}
|
|
2744
2769
|
function listSkills(factoryDir) {
|
|
2745
2770
|
const skills = [];
|
|
2746
|
-
const bmDir =
|
|
2747
|
-
const customSkillsDir =
|
|
2771
|
+
const bmDir = join8(factoryDir, ".beastmode");
|
|
2772
|
+
const customSkillsDir = join8(bmDir, "skills");
|
|
2748
2773
|
if (existsSync7(customSkillsDir)) {
|
|
2749
2774
|
for (const entry of readdirSync2(customSkillsDir, { withFileTypes: true })) {
|
|
2750
2775
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
2751
|
-
const skillMdPath =
|
|
2776
|
+
const skillMdPath = join8(customSkillsDir, entry.name, "SKILL.md");
|
|
2752
2777
|
if (!existsSync7(skillMdPath)) continue;
|
|
2753
2778
|
const frontmatter = parseSkillFrontmatter(
|
|
2754
|
-
|
|
2779
|
+
readFileSync8(skillMdPath, "utf-8")
|
|
2755
2780
|
);
|
|
2756
2781
|
skills.push({
|
|
2757
2782
|
name: frontmatter.name || entry.name,
|
|
2758
2783
|
description: frontmatter.description || "",
|
|
2759
2784
|
source: "custom",
|
|
2760
|
-
path:
|
|
2785
|
+
path: join8(customSkillsDir, entry.name)
|
|
2761
2786
|
});
|
|
2762
2787
|
}
|
|
2763
2788
|
}
|
|
2764
|
-
const pluginsDir =
|
|
2789
|
+
const pluginsDir = join8(bmDir, "plugins");
|
|
2765
2790
|
if (existsSync7(pluginsDir)) {
|
|
2766
2791
|
for (const pluginEntry of readdirSync2(pluginsDir, {
|
|
2767
2792
|
withFileTypes: true
|
|
2768
2793
|
})) {
|
|
2769
2794
|
if (!pluginEntry.isDirectory() || pluginEntry.name.startsWith("."))
|
|
2770
2795
|
continue;
|
|
2771
|
-
const pluginSkillsDir =
|
|
2796
|
+
const pluginSkillsDir = join8(
|
|
2772
2797
|
pluginsDir,
|
|
2773
2798
|
pluginEntry.name,
|
|
2774
2799
|
"skills"
|
|
@@ -2779,20 +2804,20 @@ function listSkills(factoryDir) {
|
|
|
2779
2804
|
})) {
|
|
2780
2805
|
if (!skillEntry.isDirectory() || skillEntry.name.startsWith("."))
|
|
2781
2806
|
continue;
|
|
2782
|
-
const skillMdPath =
|
|
2807
|
+
const skillMdPath = join8(
|
|
2783
2808
|
pluginSkillsDir,
|
|
2784
2809
|
skillEntry.name,
|
|
2785
2810
|
"SKILL.md"
|
|
2786
2811
|
);
|
|
2787
2812
|
if (!existsSync7(skillMdPath)) continue;
|
|
2788
2813
|
const frontmatter = parseSkillFrontmatter(
|
|
2789
|
-
|
|
2814
|
+
readFileSync8(skillMdPath, "utf-8")
|
|
2790
2815
|
);
|
|
2791
2816
|
skills.push({
|
|
2792
2817
|
name: frontmatter.name || skillEntry.name,
|
|
2793
2818
|
description: frontmatter.description || "",
|
|
2794
2819
|
source: `plugin:${pluginEntry.name}`,
|
|
2795
|
-
path:
|
|
2820
|
+
path: join8(pluginSkillsDir, skillEntry.name)
|
|
2796
2821
|
});
|
|
2797
2822
|
}
|
|
2798
2823
|
}
|
|
@@ -3223,8 +3248,8 @@ var init_bridge = __esm({
|
|
|
3223
3248
|
});
|
|
3224
3249
|
|
|
3225
3250
|
// src/engine/project-record.ts
|
|
3226
|
-
import { existsSync as existsSync8, writeFileSync as writeFileSync6, renameSync, readFileSync as
|
|
3227
|
-
import { join as
|
|
3251
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync6, renameSync, readFileSync as readFileSync9, readdirSync as readdirSync3, mkdirSync as mkdirSync6, unlinkSync } from "fs";
|
|
3252
|
+
import { join as join9, basename as basename3 } from "path";
|
|
3228
3253
|
function computeVerifyPort(existingProjects) {
|
|
3229
3254
|
let port = 3001;
|
|
3230
3255
|
if (existingProjects) {
|
|
@@ -3386,13 +3411,13 @@ function createProjectRecord(input) {
|
|
|
3386
3411
|
};
|
|
3387
3412
|
}
|
|
3388
3413
|
function writeProjectRecord(projectsDir, name, record) {
|
|
3389
|
-
const dir =
|
|
3414
|
+
const dir = join9(projectsDir, name);
|
|
3390
3415
|
mkdirSync6(dir, { recursive: true });
|
|
3391
|
-
const filePath =
|
|
3416
|
+
const filePath = join9(dir, "project.json");
|
|
3392
3417
|
const tmpPath = `${filePath}.tmp`;
|
|
3393
3418
|
writeFileSync6(tmpPath, JSON.stringify(record, null, 2) + "\n");
|
|
3394
3419
|
renameSync(tmpPath, filePath);
|
|
3395
|
-
const extPath =
|
|
3420
|
+
const extPath = join9(dir, "extensions.json");
|
|
3396
3421
|
if (!existsSync8(extPath)) {
|
|
3397
3422
|
writeFileSync6(
|
|
3398
3423
|
extPath,
|
|
@@ -3409,17 +3434,17 @@ function writeProjectRecord(projectsDir, name, record) {
|
|
|
3409
3434
|
}
|
|
3410
3435
|
}
|
|
3411
3436
|
function readProjectRecord(projectsDir, name) {
|
|
3412
|
-
const subdirPath =
|
|
3413
|
-
const flatPath =
|
|
3437
|
+
const subdirPath = join9(projectsDir, name, "project.json");
|
|
3438
|
+
const flatPath = join9(projectsDir, `${name}.json`);
|
|
3414
3439
|
let filePath;
|
|
3415
3440
|
let isFlat = false;
|
|
3416
3441
|
if (existsSync8(subdirPath)) {
|
|
3417
3442
|
filePath = subdirPath;
|
|
3418
3443
|
if (existsSync8(flatPath)) {
|
|
3419
3444
|
try {
|
|
3420
|
-
const flatParsed = JSON.parse(
|
|
3445
|
+
const flatParsed = JSON.parse(readFileSync9(flatPath, "utf-8"));
|
|
3421
3446
|
const flatStack = flatParsed.stack;
|
|
3422
|
-
const subdirParsed = JSON.parse(
|
|
3447
|
+
const subdirParsed = JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
3423
3448
|
const subdirStack = subdirParsed.stack;
|
|
3424
3449
|
if (flatStack && flatStack.detected && flatStack.detected !== "unknown" && (!subdirStack || subdirStack.detected === "unknown")) {
|
|
3425
3450
|
subdirParsed.stack = flatStack;
|
|
@@ -3438,7 +3463,7 @@ function readProjectRecord(projectsDir, name) {
|
|
|
3438
3463
|
}
|
|
3439
3464
|
let parsed;
|
|
3440
3465
|
try {
|
|
3441
|
-
parsed = JSON.parse(
|
|
3466
|
+
parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
3442
3467
|
} catch {
|
|
3443
3468
|
return null;
|
|
3444
3469
|
}
|
|
@@ -3463,7 +3488,7 @@ function listProjectRecords(projectsDir) {
|
|
|
3463
3488
|
const records = [];
|
|
3464
3489
|
for (const entry of readdirSync3(projectsDir)) {
|
|
3465
3490
|
if (entry.startsWith(".")) continue;
|
|
3466
|
-
if (existsSync8(
|
|
3491
|
+
if (existsSync8(join9(projectsDir, entry, "project.json"))) {
|
|
3467
3492
|
if (!seen.has(entry)) {
|
|
3468
3493
|
seen.add(entry);
|
|
3469
3494
|
const record = readProjectRecord(projectsDir, entry);
|
|
@@ -3629,10 +3654,10 @@ var init_engine = __esm({
|
|
|
3629
3654
|
|
|
3630
3655
|
// src/cli/utils/file-writer.ts
|
|
3631
3656
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7, appendFileSync, existsSync as existsSync9 } from "fs";
|
|
3632
|
-
import { dirname as
|
|
3657
|
+
import { dirname as dirname4 } from "path";
|
|
3633
3658
|
function executeFileActions(actions) {
|
|
3634
3659
|
for (const action of actions) {
|
|
3635
|
-
const dir =
|
|
3660
|
+
const dir = dirname4(action.path);
|
|
3636
3661
|
mkdirSync7(dir, { recursive: true });
|
|
3637
3662
|
switch (action.action) {
|
|
3638
3663
|
case "create":
|
|
@@ -3817,21 +3842,21 @@ var init_api_routes = __esm({
|
|
|
3817
3842
|
});
|
|
3818
3843
|
|
|
3819
3844
|
// src/cli/ui/archival.ts
|
|
3820
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync4, renameSync as renameSync2, readFileSync as
|
|
3821
|
-
import { join as
|
|
3845
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync4, renameSync as renameSync2, readFileSync as readFileSync10, statSync as statSync3, writeFileSync as writeFileSync9, unlinkSync as unlinkSync2 } from "fs";
|
|
3846
|
+
import { join as join11 } from "path";
|
|
3822
3847
|
function archiveOldRuns(runsDir, archiveAfterDays) {
|
|
3823
3848
|
if (archiveAfterDays <= 0 || !existsSync12(runsDir)) return { archived: 0 };
|
|
3824
|
-
const archiveDir =
|
|
3849
|
+
const archiveDir = join11(runsDir, ".archive");
|
|
3825
3850
|
const cutoff = Date.now() - archiveAfterDays * 24 * 60 * 60 * 1e3;
|
|
3826
3851
|
let archived = 0;
|
|
3827
3852
|
for (const entry of readdirSync4(runsDir)) {
|
|
3828
3853
|
if (entry.startsWith(".") || !entry.startsWith("run-")) continue;
|
|
3829
|
-
const runDir =
|
|
3830
|
-
if (existsSync12(
|
|
3831
|
-
const cpPath =
|
|
3854
|
+
const runDir = join11(runsDir, entry);
|
|
3855
|
+
if (existsSync12(join11(runDir, "pinned"))) continue;
|
|
3856
|
+
const cpPath = join11(runDir, "checkpoint.json");
|
|
3832
3857
|
if (!existsSync12(cpPath)) continue;
|
|
3833
3858
|
try {
|
|
3834
|
-
const cp = JSON.parse(
|
|
3859
|
+
const cp = JSON.parse(readFileSync10(cpPath, "utf-8"));
|
|
3835
3860
|
const stage = (cp.current_stage || "").toLowerCase();
|
|
3836
3861
|
if (stage !== "done" && stage !== "ship") continue;
|
|
3837
3862
|
} catch {
|
|
@@ -3844,25 +3869,25 @@ function archiveOldRuns(runsDir, archiveAfterDays) {
|
|
|
3844
3869
|
continue;
|
|
3845
3870
|
}
|
|
3846
3871
|
mkdirSync8(archiveDir, { recursive: true });
|
|
3847
|
-
renameSync2(runDir,
|
|
3872
|
+
renameSync2(runDir, join11(archiveDir, entry));
|
|
3848
3873
|
archived++;
|
|
3849
3874
|
}
|
|
3850
3875
|
return { archived };
|
|
3851
3876
|
}
|
|
3852
3877
|
function pinRun(runsDir, runId) {
|
|
3853
|
-
const runDir =
|
|
3878
|
+
const runDir = join11(runsDir, runId);
|
|
3854
3879
|
if (!existsSync12(runDir)) return false;
|
|
3855
|
-
writeFileSync9(
|
|
3880
|
+
writeFileSync9(join11(runDir, "pinned"), (/* @__PURE__ */ new Date()).toISOString());
|
|
3856
3881
|
return true;
|
|
3857
3882
|
}
|
|
3858
3883
|
function unpinRun(runsDir, runId) {
|
|
3859
|
-
const pinFile =
|
|
3884
|
+
const pinFile = join11(runsDir, runId, "pinned");
|
|
3860
3885
|
if (!existsSync12(pinFile)) return false;
|
|
3861
3886
|
unlinkSync2(pinFile);
|
|
3862
3887
|
return true;
|
|
3863
3888
|
}
|
|
3864
3889
|
function isRunPinned(runsDir, runId) {
|
|
3865
|
-
return existsSync12(
|
|
3890
|
+
return existsSync12(join11(runsDir, runId, "pinned"));
|
|
3866
3891
|
}
|
|
3867
3892
|
var init_archival = __esm({
|
|
3868
3893
|
"src/cli/ui/archival.ts"() {
|
|
@@ -3887,17 +3912,17 @@ __export(inception_exports, {
|
|
|
3887
3912
|
saveArtifact: () => saveArtifact,
|
|
3888
3913
|
saveInceptionState: () => saveInceptionState
|
|
3889
3914
|
});
|
|
3890
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, readFileSync as
|
|
3891
|
-
import { join as
|
|
3892
|
-
import { fileURLToPath } from "url";
|
|
3915
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, readFileSync as readFileSync11, readdirSync as readdirSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
3916
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
3917
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3893
3918
|
function getMethodologiesDir() {
|
|
3894
3919
|
const candidates = [
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3920
|
+
join12(dirname5(fileURLToPath2(import.meta.url)), "methodologies"),
|
|
3921
|
+
join12(dirname5(fileURLToPath2(import.meta.url)), "..", "methodologies"),
|
|
3922
|
+
join12(process.cwd(), "src", "cli", "ui", "methodologies"),
|
|
3923
|
+
join12(process.cwd(), "dist", "methodologies"),
|
|
3924
|
+
join12(process.cwd(), "cli", "src", "cli", "ui", "methodologies"),
|
|
3925
|
+
join12(process.cwd(), "cli", "dist", "methodologies")
|
|
3901
3926
|
];
|
|
3902
3927
|
for (const dir of candidates) {
|
|
3903
3928
|
if (existsSync13(dir)) return dir;
|
|
@@ -3906,18 +3931,18 @@ function getMethodologiesDir() {
|
|
|
3906
3931
|
}
|
|
3907
3932
|
function getMethodology(id) {
|
|
3908
3933
|
const dir = getMethodologiesDir();
|
|
3909
|
-
const filePath =
|
|
3934
|
+
const filePath = join12(dir, `${id.replace(/_/g, "-")}.json`);
|
|
3910
3935
|
if (!existsSync13(filePath)) {
|
|
3911
3936
|
throw new Error(`Methodology not found: ${id}`);
|
|
3912
3937
|
}
|
|
3913
|
-
return JSON.parse(
|
|
3938
|
+
return JSON.parse(readFileSync11(filePath, "utf-8"));
|
|
3914
3939
|
}
|
|
3915
3940
|
function listMethodologies() {
|
|
3916
3941
|
const dir = getMethodologiesDir();
|
|
3917
3942
|
if (!existsSync13(dir)) return [];
|
|
3918
3943
|
return readdirSync5(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
3919
3944
|
try {
|
|
3920
|
-
const m = JSON.parse(
|
|
3945
|
+
const m = JSON.parse(readFileSync11(join12(dir, f), "utf-8"));
|
|
3921
3946
|
return { id: m.id, name: m.name, description: m.description };
|
|
3922
3947
|
} catch {
|
|
3923
3948
|
return null;
|
|
@@ -3925,7 +3950,7 @@ function listMethodologies() {
|
|
|
3925
3950
|
}).filter(Boolean);
|
|
3926
3951
|
}
|
|
3927
3952
|
function getProductDir(factoryDir, productName) {
|
|
3928
|
-
return
|
|
3953
|
+
return join12(factoryDir, ".beastmode", "products", productName);
|
|
3929
3954
|
}
|
|
3930
3955
|
function createInceptionState(factoryDir, opts) {
|
|
3931
3956
|
const methodology = getMethodology(opts.methodology);
|
|
@@ -3947,21 +3972,21 @@ function createInceptionState(factoryDir, opts) {
|
|
|
3947
3972
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3948
3973
|
projectName: null
|
|
3949
3974
|
};
|
|
3950
|
-
writeFileSync10(
|
|
3975
|
+
writeFileSync10(join12(productDir, "inception.json"), JSON.stringify(state, null, 2) + "\n");
|
|
3951
3976
|
return state;
|
|
3952
3977
|
}
|
|
3953
3978
|
function loadInception(factoryDir, productName) {
|
|
3954
|
-
const filePath =
|
|
3979
|
+
const filePath = join12(getProductDir(factoryDir, productName), "inception.json");
|
|
3955
3980
|
if (!existsSync13(filePath)) return null;
|
|
3956
3981
|
try {
|
|
3957
|
-
return JSON.parse(
|
|
3982
|
+
return JSON.parse(readFileSync11(filePath, "utf-8"));
|
|
3958
3983
|
} catch {
|
|
3959
3984
|
return null;
|
|
3960
3985
|
}
|
|
3961
3986
|
}
|
|
3962
3987
|
function saveInceptionState(factoryDir, state) {
|
|
3963
3988
|
const productDir = getProductDir(factoryDir, state.productName);
|
|
3964
|
-
writeFileSync10(
|
|
3989
|
+
writeFileSync10(join12(productDir, "inception.json"), JSON.stringify(state, null, 2) + "\n");
|
|
3965
3990
|
}
|
|
3966
3991
|
function advancePhase(factoryDir, productName) {
|
|
3967
3992
|
const state = loadInception(factoryDir, productName);
|
|
@@ -3982,27 +4007,27 @@ function advancePhase(factoryDir, productName) {
|
|
|
3982
4007
|
function saveArtifact(factoryDir, productName, filename, content) {
|
|
3983
4008
|
const productDir = getProductDir(factoryDir, productName);
|
|
3984
4009
|
mkdirSync9(productDir, { recursive: true });
|
|
3985
|
-
const filePath =
|
|
3986
|
-
mkdirSync9(
|
|
4010
|
+
const filePath = join12(productDir, filename);
|
|
4011
|
+
mkdirSync9(dirname5(filePath), { recursive: true });
|
|
3987
4012
|
writeFileSync10(filePath, content);
|
|
3988
4013
|
}
|
|
3989
4014
|
function loadArtifact(factoryDir, productName, filename) {
|
|
3990
|
-
const filePath =
|
|
4015
|
+
const filePath = join12(getProductDir(factoryDir, productName), filename);
|
|
3991
4016
|
if (!existsSync13(filePath)) return null;
|
|
3992
|
-
return
|
|
4017
|
+
return readFileSync11(filePath, "utf-8");
|
|
3993
4018
|
}
|
|
3994
4019
|
function migrateInceptionToSession(factoryDir, productName) {
|
|
3995
|
-
const productDir =
|
|
3996
|
-
const oldInception =
|
|
4020
|
+
const productDir = join12(factoryDir, ".beastmode", "products", productName);
|
|
4021
|
+
const oldInception = join12(productDir, "inception.json");
|
|
3997
4022
|
if (!existsSync13(oldInception)) return false;
|
|
3998
|
-
const sessionsDir =
|
|
4023
|
+
const sessionsDir = join12(productDir, "sessions");
|
|
3999
4024
|
if (existsSync13(sessionsDir) && readdirSync5(sessionsDir).some((d) => d.startsWith("session-"))) {
|
|
4000
4025
|
return false;
|
|
4001
4026
|
}
|
|
4002
|
-
const sessionDir =
|
|
4027
|
+
const sessionDir = join12(sessionsDir, "session-001");
|
|
4003
4028
|
mkdirSync9(sessionDir, { recursive: true });
|
|
4004
|
-
const content =
|
|
4005
|
-
writeFileSync10(
|
|
4029
|
+
const content = readFileSync11(oldInception, "utf-8");
|
|
4030
|
+
writeFileSync10(join12(sessionDir, "inception.json"), content);
|
|
4006
4031
|
const artifactFiles = [
|
|
4007
4032
|
"prd.md",
|
|
4008
4033
|
"architecture.md",
|
|
@@ -4015,9 +4040,9 @@ function migrateInceptionToSession(factoryDir, productName) {
|
|
|
4015
4040
|
"project-plan.md"
|
|
4016
4041
|
];
|
|
4017
4042
|
for (const file of artifactFiles) {
|
|
4018
|
-
const src =
|
|
4043
|
+
const src = join12(productDir, file);
|
|
4019
4044
|
if (existsSync13(src)) {
|
|
4020
|
-
writeFileSync10(
|
|
4045
|
+
writeFileSync10(join12(sessionDir, file), readFileSync11(src, "utf-8"));
|
|
4021
4046
|
unlinkSync3(src);
|
|
4022
4047
|
}
|
|
4023
4048
|
}
|
|
@@ -4025,11 +4050,11 @@ function migrateInceptionToSession(factoryDir, productName) {
|
|
|
4025
4050
|
return true;
|
|
4026
4051
|
}
|
|
4027
4052
|
function listProducts(factoryDir) {
|
|
4028
|
-
const productsDir =
|
|
4053
|
+
const productsDir = join12(factoryDir, ".beastmode", "products");
|
|
4029
4054
|
if (!existsSync13(productsDir)) return [];
|
|
4030
|
-
return readdirSync5(productsDir).filter((d) => existsSync13(
|
|
4055
|
+
return readdirSync5(productsDir).filter((d) => existsSync13(join12(productsDir, d, "inception.json"))).map((d) => {
|
|
4031
4056
|
try {
|
|
4032
|
-
const state = JSON.parse(
|
|
4057
|
+
const state = JSON.parse(readFileSync11(join12(productsDir, d, "inception.json"), "utf-8"));
|
|
4033
4058
|
return {
|
|
4034
4059
|
name: state.productName || d,
|
|
4035
4060
|
methodology: state.methodology || "unknown",
|
|
@@ -4152,13 +4177,13 @@ __export(strategy_exports, {
|
|
|
4152
4177
|
saveSessionArtifact: () => saveSessionArtifact,
|
|
4153
4178
|
saveStrategySession: () => saveStrategySession
|
|
4154
4179
|
});
|
|
4155
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, readFileSync as
|
|
4156
|
-
import { join as
|
|
4180
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, readFileSync as readFileSync12, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
4181
|
+
import { join as join13 } from "path";
|
|
4157
4182
|
function getProductDir2(factoryDir, projectName) {
|
|
4158
|
-
return
|
|
4183
|
+
return join13(factoryDir, ".beastmode", "products", projectName);
|
|
4159
4184
|
}
|
|
4160
4185
|
function getSessionsDir(factoryDir, projectName) {
|
|
4161
|
-
return
|
|
4186
|
+
return join13(getProductDir2(factoryDir, projectName), "sessions");
|
|
4162
4187
|
}
|
|
4163
4188
|
function nextSessionId(factoryDir, projectName) {
|
|
4164
4189
|
const sessionsDir = getSessionsDir(factoryDir, projectName);
|
|
@@ -4171,7 +4196,7 @@ function nextSessionId(factoryDir, projectName) {
|
|
|
4171
4196
|
function createStrategySession(factoryDir, projectName, opts) {
|
|
4172
4197
|
const methodology = getMethodology(opts.methodology);
|
|
4173
4198
|
const sessionId = nextSessionId(factoryDir, projectName);
|
|
4174
|
-
const sessionDir =
|
|
4199
|
+
const sessionDir = join13(getSessionsDir(factoryDir, projectName), sessionId);
|
|
4175
4200
|
mkdirSync10(sessionDir, { recursive: true });
|
|
4176
4201
|
const session = {
|
|
4177
4202
|
sessionId,
|
|
@@ -4193,52 +4218,52 @@ function createStrategySession(factoryDir, projectName, opts) {
|
|
|
4193
4218
|
sessionType: opts.sessionType || "free-form",
|
|
4194
4219
|
approach: opts.approach || "auto"
|
|
4195
4220
|
};
|
|
4196
|
-
writeFileSync11(
|
|
4221
|
+
writeFileSync11(join13(sessionDir, "inception.json"), JSON.stringify(session, null, 2) + "\n");
|
|
4197
4222
|
return session;
|
|
4198
4223
|
}
|
|
4199
4224
|
function listStrategySessions(factoryDir, projectName) {
|
|
4200
4225
|
const sessionsDir = getSessionsDir(factoryDir, projectName);
|
|
4201
4226
|
if (!existsSync14(sessionsDir)) return [];
|
|
4202
4227
|
return readdirSync6(sessionsDir).filter((d) => d.startsWith("session-")).sort().map((d) => {
|
|
4203
|
-
const file =
|
|
4228
|
+
const file = join13(sessionsDir, d, "inception.json");
|
|
4204
4229
|
if (!existsSync14(file)) return null;
|
|
4205
4230
|
try {
|
|
4206
|
-
return JSON.parse(
|
|
4231
|
+
return JSON.parse(readFileSync12(file, "utf-8"));
|
|
4207
4232
|
} catch {
|
|
4208
4233
|
return null;
|
|
4209
4234
|
}
|
|
4210
4235
|
}).filter(Boolean);
|
|
4211
4236
|
}
|
|
4212
4237
|
function loadStrategySession(factoryDir, projectName, sessionId) {
|
|
4213
|
-
const file =
|
|
4238
|
+
const file = join13(getSessionsDir(factoryDir, projectName), sessionId, "inception.json");
|
|
4214
4239
|
if (!existsSync14(file)) return null;
|
|
4215
4240
|
try {
|
|
4216
|
-
return JSON.parse(
|
|
4241
|
+
return JSON.parse(readFileSync12(file, "utf-8"));
|
|
4217
4242
|
} catch {
|
|
4218
4243
|
return null;
|
|
4219
4244
|
}
|
|
4220
4245
|
}
|
|
4221
4246
|
function saveStrategySession(factoryDir, projectName, sessionId, session) {
|
|
4222
|
-
const dir =
|
|
4247
|
+
const dir = join13(getSessionsDir(factoryDir, projectName), sessionId);
|
|
4223
4248
|
mkdirSync10(dir, { recursive: true });
|
|
4224
4249
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4225
|
-
writeFileSync11(
|
|
4250
|
+
writeFileSync11(join13(dir, "inception.json"), JSON.stringify(session, null, 2) + "\n");
|
|
4226
4251
|
}
|
|
4227
4252
|
function saveSessionArtifact(factoryDir, projectName, sessionId, filename, content) {
|
|
4228
|
-
const dir =
|
|
4253
|
+
const dir = join13(getSessionsDir(factoryDir, projectName), sessionId);
|
|
4229
4254
|
mkdirSync10(dir, { recursive: true });
|
|
4230
|
-
writeFileSync11(
|
|
4255
|
+
writeFileSync11(join13(dir, filename), content);
|
|
4231
4256
|
}
|
|
4232
4257
|
function loadSessionArtifact(factoryDir, projectName, sessionId, filename) {
|
|
4233
|
-
const file =
|
|
4258
|
+
const file = join13(getSessionsDir(factoryDir, projectName), sessionId, filename);
|
|
4234
4259
|
if (!existsSync14(file)) return null;
|
|
4235
|
-
return
|
|
4260
|
+
return readFileSync12(file, "utf-8");
|
|
4236
4261
|
}
|
|
4237
4262
|
function buildArtifactIndex(factoryDir, projectName) {
|
|
4238
4263
|
const artifacts = [];
|
|
4239
|
-
const brownfieldPath =
|
|
4264
|
+
const brownfieldPath = join13(factoryDir, ".beastmode", "projects", projectName, "brownfield.md");
|
|
4240
4265
|
if (existsSync14(brownfieldPath)) {
|
|
4241
|
-
const content =
|
|
4266
|
+
const content = readFileSync12(brownfieldPath, "utf-8");
|
|
4242
4267
|
const firstLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#")) || "";
|
|
4243
4268
|
artifacts.push({
|
|
4244
4269
|
type: "brownfield",
|
|
@@ -4251,19 +4276,19 @@ function buildArtifactIndex(factoryDir, projectName) {
|
|
|
4251
4276
|
for (const session of sessions) {
|
|
4252
4277
|
artifacts.push({
|
|
4253
4278
|
type: "strategy_session",
|
|
4254
|
-
path:
|
|
4279
|
+
path: join13(getSessionsDir(factoryDir, projectName), session.sessionId),
|
|
4255
4280
|
summary: `${session.name} (${session.methodology}) \u2014 ${session.status}`,
|
|
4256
4281
|
date: session.createdAt,
|
|
4257
4282
|
sessionId: session.sessionId
|
|
4258
4283
|
});
|
|
4259
4284
|
}
|
|
4260
|
-
const runsDir =
|
|
4285
|
+
const runsDir = join13(factoryDir, "runs", projectName);
|
|
4261
4286
|
if (existsSync14(runsDir)) {
|
|
4262
4287
|
const runDirs = readdirSync6(runsDir).filter((d) => d.startsWith("run-")).sort().reverse().slice(0, 10);
|
|
4263
4288
|
for (const runId of runDirs) {
|
|
4264
|
-
const nlspecPath =
|
|
4289
|
+
const nlspecPath = join13(runsDir, runId, "nlspec.md");
|
|
4265
4290
|
if (existsSync14(nlspecPath)) {
|
|
4266
|
-
const content =
|
|
4291
|
+
const content = readFileSync12(nlspecPath, "utf-8");
|
|
4267
4292
|
const title = content.split("\n").find((l) => l.startsWith("# ")) || runId;
|
|
4268
4293
|
artifacts.push({
|
|
4269
4294
|
type: "nlspec",
|
|
@@ -4275,12 +4300,12 @@ function buildArtifactIndex(factoryDir, projectName) {
|
|
|
4275
4300
|
}
|
|
4276
4301
|
}
|
|
4277
4302
|
}
|
|
4278
|
-
const learningsDir =
|
|
4303
|
+
const learningsDir = join13(getProductDir2(factoryDir, projectName), "learnings");
|
|
4279
4304
|
if (existsSync14(learningsDir)) {
|
|
4280
4305
|
const learningFiles = readdirSync6(learningsDir).filter((f) => f.endsWith(".md") && !f.startsWith("."));
|
|
4281
4306
|
for (const file of learningFiles) {
|
|
4282
|
-
const filePath =
|
|
4283
|
-
const content =
|
|
4307
|
+
const filePath = join13(learningsDir, file);
|
|
4308
|
+
const content = readFileSync12(filePath, "utf-8");
|
|
4284
4309
|
const firstHeading = content.split("\n").find((l) => l.startsWith("## ")) || file;
|
|
4285
4310
|
artifacts.push({
|
|
4286
4311
|
type: "learning",
|
|
@@ -4311,8 +4336,8 @@ var init_strategy = __esm({
|
|
|
4311
4336
|
// src/cli/ui/chat-handler.ts
|
|
4312
4337
|
import { randomUUID } from "crypto";
|
|
4313
4338
|
import { execSync as execSync2 } from "child_process";
|
|
4314
|
-
import { readFileSync as
|
|
4315
|
-
import { join as
|
|
4339
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
|
|
4340
|
+
import { join as join14 } from "path";
|
|
4316
4341
|
import http from "http";
|
|
4317
4342
|
import { WebSocketServer, WebSocket as WsWebSocket } from "ws";
|
|
4318
4343
|
function getRecentTokenUsage() {
|
|
@@ -4424,17 +4449,17 @@ function getToolDefinitions() {
|
|
|
4424
4449
|
function readJsonSafe(filePath) {
|
|
4425
4450
|
if (!existsSync15(filePath)) return null;
|
|
4426
4451
|
try {
|
|
4427
|
-
return JSON.parse(
|
|
4452
|
+
return JSON.parse(readFileSync13(filePath, "utf-8"));
|
|
4428
4453
|
} catch {
|
|
4429
4454
|
return null;
|
|
4430
4455
|
}
|
|
4431
4456
|
}
|
|
4432
4457
|
function getBoardUrl(factoryPath) {
|
|
4433
4458
|
if (process.env.BEASTMODE_BOARD_URL) return process.env.BEASTMODE_BOARD_URL;
|
|
4434
|
-
const configPath =
|
|
4459
|
+
const configPath = join14(factoryPath, ".beastmode", "config.json");
|
|
4435
4460
|
if (existsSync15(configPath)) {
|
|
4436
4461
|
try {
|
|
4437
|
-
const config = JSON.parse(
|
|
4462
|
+
const config = JSON.parse(readFileSync13(configPath, "utf-8"));
|
|
4438
4463
|
if (config.task_backend?.config?.url) return config.task_backend.config.url;
|
|
4439
4464
|
} catch {
|
|
4440
4465
|
}
|
|
@@ -4445,19 +4470,19 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4445
4470
|
try {
|
|
4446
4471
|
switch (toolName) {
|
|
4447
4472
|
case "factory_status": {
|
|
4448
|
-
const bmDir =
|
|
4449
|
-
const factory = readJsonSafe(
|
|
4450
|
-
const projectsDir =
|
|
4473
|
+
const bmDir = join14(factoryPath, ".beastmode");
|
|
4474
|
+
const factory = readJsonSafe(join14(bmDir, "factory.json"));
|
|
4475
|
+
const projectsDir = join14(bmDir, "projects");
|
|
4451
4476
|
let projects = [];
|
|
4452
4477
|
if (existsSync15(projectsDir)) {
|
|
4453
4478
|
projects = readdirSync7(projectsDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
4454
4479
|
}
|
|
4455
|
-
const runsDir =
|
|
4480
|
+
const runsDir = join14(factoryPath, "runs");
|
|
4456
4481
|
let runs = [];
|
|
4457
4482
|
if (existsSync15(runsDir)) {
|
|
4458
4483
|
runs = readdirSync7(runsDir).filter((d) => {
|
|
4459
4484
|
try {
|
|
4460
|
-
return statSync5(
|
|
4485
|
+
return statSync5(join14(runsDir, d)).isDirectory();
|
|
4461
4486
|
} catch {
|
|
4462
4487
|
return false;
|
|
4463
4488
|
}
|
|
@@ -4465,10 +4490,10 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4465
4490
|
}
|
|
4466
4491
|
let daemonStatus = "stopped";
|
|
4467
4492
|
let daemonPid = null;
|
|
4468
|
-
const pidFile =
|
|
4493
|
+
const pidFile = join14(bmDir, "daemon.pid");
|
|
4469
4494
|
if (existsSync15(pidFile)) {
|
|
4470
4495
|
try {
|
|
4471
|
-
daemonPid = parseInt(
|
|
4496
|
+
daemonPid = parseInt(readFileSync13(pidFile, "utf-8").trim(), 10);
|
|
4472
4497
|
process.kill(daemonPid, 0);
|
|
4473
4498
|
daemonStatus = "running";
|
|
4474
4499
|
} catch {
|
|
@@ -4516,7 +4541,7 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4516
4541
|
}, null, 2);
|
|
4517
4542
|
}
|
|
4518
4543
|
case "factory_config": {
|
|
4519
|
-
const configPath =
|
|
4544
|
+
const configPath = join14(factoryPath, ".beastmode", "config.json");
|
|
4520
4545
|
const config = readJsonSafe(configPath);
|
|
4521
4546
|
return config ? JSON.stringify(config, null, 2) : "No config.json found.";
|
|
4522
4547
|
}
|
|
@@ -4571,12 +4596,12 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4571
4596
|
}
|
|
4572
4597
|
}
|
|
4573
4598
|
case "list_runs": {
|
|
4574
|
-
const runsDir =
|
|
4599
|
+
const runsDir = join14(factoryPath, "runs");
|
|
4575
4600
|
if (!existsSync15(runsDir)) return "No runs directory.";
|
|
4576
4601
|
const runDirs = readdirSync7(runsDir).sort().reverse();
|
|
4577
4602
|
const results = [];
|
|
4578
4603
|
for (const id of runDirs.slice(0, 15)) {
|
|
4579
|
-
const cp = readJsonSafe(
|
|
4604
|
+
const cp = readJsonSafe(join14(runsDir, id, "checkpoint.json"));
|
|
4580
4605
|
if (cp) {
|
|
4581
4606
|
const hist = Array.isArray(cp.satisfaction_history) ? cp.satisfaction_history : [];
|
|
4582
4607
|
results.push({
|
|
@@ -4592,15 +4617,15 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4592
4617
|
case "run_detail": {
|
|
4593
4618
|
const runId = toolInput.run_id;
|
|
4594
4619
|
if (runId.includes("..")) return "Invalid run_id.";
|
|
4595
|
-
const runDir =
|
|
4620
|
+
const runDir = join14(factoryPath, "runs", runId);
|
|
4596
4621
|
if (!existsSync15(runDir)) return `Run not found: ${runId}`;
|
|
4597
|
-
const manifest = readJsonSafe(
|
|
4598
|
-
const checkpoint = readJsonSafe(
|
|
4599
|
-
const iterDir =
|
|
4622
|
+
const manifest = readJsonSafe(join14(runDir, "manifest.json"));
|
|
4623
|
+
const checkpoint = readJsonSafe(join14(runDir, "checkpoint.json"));
|
|
4624
|
+
const iterDir = join14(runDir, "iterations");
|
|
4600
4625
|
const iterations = [];
|
|
4601
4626
|
if (existsSync15(iterDir)) {
|
|
4602
4627
|
for (const d of readdirSync7(iterDir).sort()) {
|
|
4603
|
-
const sat = readJsonSafe(
|
|
4628
|
+
const sat = readJsonSafe(join14(iterDir, d, "satisfaction.json"));
|
|
4604
4629
|
iterations.push({ number: parseInt(d, 10), satisfaction: sat });
|
|
4605
4630
|
}
|
|
4606
4631
|
}
|
|
@@ -4611,17 +4636,17 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4611
4636
|
const runId = toolInput.run_id;
|
|
4612
4637
|
const relPath = toolInput.file_path;
|
|
4613
4638
|
if (runId.includes("..") || relPath.includes("..")) return "Invalid path.";
|
|
4614
|
-
const fullPath =
|
|
4639
|
+
const fullPath = join14(factoryPath, "runs", runId, relPath);
|
|
4615
4640
|
if (!existsSync15(fullPath)) return `File not found: runs/${runId}/${relPath}`;
|
|
4616
|
-
const content =
|
|
4641
|
+
const content = readFileSync13(fullPath, "utf-8");
|
|
4617
4642
|
return content.length > 1e4 ? content.slice(0, 1e4) + "\n\n... (truncated at 10KB)" : content;
|
|
4618
4643
|
}
|
|
4619
4644
|
case "list_projects": {
|
|
4620
|
-
const projDir =
|
|
4645
|
+
const projDir = join14(factoryPath, ".beastmode", "projects");
|
|
4621
4646
|
if (!existsSync15(projDir)) return "No projects registered.";
|
|
4622
4647
|
const projects = readdirSync7(projDir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
4623
4648
|
try {
|
|
4624
|
-
return JSON.parse(
|
|
4649
|
+
return JSON.parse(readFileSync13(join14(projDir, f), "utf-8"));
|
|
4625
4650
|
} catch {
|
|
4626
4651
|
return null;
|
|
4627
4652
|
}
|
|
@@ -4631,7 +4656,7 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4631
4656
|
case "read_file": {
|
|
4632
4657
|
const relPath = toolInput.path;
|
|
4633
4658
|
if (relPath.includes("..")) return "Invalid path.";
|
|
4634
|
-
const fullPath =
|
|
4659
|
+
const fullPath = join14(factoryPath, relPath);
|
|
4635
4660
|
if (!fullPath.startsWith(factoryPath)) return "Path traversal not allowed.";
|
|
4636
4661
|
if (!existsSync15(fullPath)) return `File not found: ${relPath}`;
|
|
4637
4662
|
try {
|
|
@@ -4639,13 +4664,13 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4639
4664
|
return `"${relPath}" is a directory with ${entries.length} entries: ${entries.slice(0, 30).join(", ")}`;
|
|
4640
4665
|
} catch {
|
|
4641
4666
|
}
|
|
4642
|
-
const content =
|
|
4667
|
+
const content = readFileSync13(fullPath, "utf-8");
|
|
4643
4668
|
return content.length > 1e4 ? content.slice(0, 1e4) + "\n\n... (truncated at 10KB)" : content;
|
|
4644
4669
|
}
|
|
4645
4670
|
case "list_directory": {
|
|
4646
4671
|
const relPath = toolInput.path || "";
|
|
4647
4672
|
if (relPath.includes("..")) return "Invalid path.";
|
|
4648
|
-
const fullPath =
|
|
4673
|
+
const fullPath = join14(factoryPath, relPath);
|
|
4649
4674
|
if (!fullPath.startsWith(factoryPath)) return "Path traversal not allowed.";
|
|
4650
4675
|
if (!existsSync15(fullPath)) return `Directory not found: ${relPath || "/"}`;
|
|
4651
4676
|
try {
|
|
@@ -4662,7 +4687,7 @@ async function executeTool(toolName, toolInput, factoryPath) {
|
|
|
4662
4687
|
}
|
|
4663
4688
|
}
|
|
4664
4689
|
function getChatHistoryDir(factoryPath) {
|
|
4665
|
-
const dir =
|
|
4690
|
+
const dir = join14(factoryPath, ".beastmode", "chat-history");
|
|
4666
4691
|
if (!existsSync15(dir)) mkdirSync11(dir, { recursive: true });
|
|
4667
4692
|
return dir;
|
|
4668
4693
|
}
|
|
@@ -4674,13 +4699,13 @@ function saveConversation(factoryPath, sessionId, messages, scope) {
|
|
|
4674
4699
|
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4675
4700
|
messages
|
|
4676
4701
|
};
|
|
4677
|
-
writeFileSync12(
|
|
4702
|
+
writeFileSync12(join14(dir, `${sessionId}.json`), JSON.stringify(data, null, 2));
|
|
4678
4703
|
}
|
|
4679
4704
|
function listConversations(factoryPath) {
|
|
4680
4705
|
const dir = getChatHistoryDir(factoryPath);
|
|
4681
4706
|
return readdirSync7(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
4682
4707
|
try {
|
|
4683
|
-
const data = JSON.parse(
|
|
4708
|
+
const data = JSON.parse(readFileSync13(join14(dir, f), "utf-8"));
|
|
4684
4709
|
const msgs = data.messages || [];
|
|
4685
4710
|
const firstUser = msgs.find((m) => m.role === "user");
|
|
4686
4711
|
const preview = typeof firstUser?.content === "string" ? firstUser.content.slice(0, 80) : "";
|
|
@@ -4698,10 +4723,10 @@ function listConversations(factoryPath) {
|
|
|
4698
4723
|
}
|
|
4699
4724
|
function loadConversation(factoryPath, sessionId) {
|
|
4700
4725
|
if (sessionId.includes("..")) return [];
|
|
4701
|
-
const file =
|
|
4726
|
+
const file = join14(getChatHistoryDir(factoryPath), `${sessionId}.json`);
|
|
4702
4727
|
if (!existsSync15(file)) return [];
|
|
4703
4728
|
try {
|
|
4704
|
-
const data = JSON.parse(
|
|
4729
|
+
const data = JSON.parse(readFileSync13(file, "utf-8"));
|
|
4705
4730
|
return data.messages || [];
|
|
4706
4731
|
} catch {
|
|
4707
4732
|
return [];
|
|
@@ -4709,7 +4734,7 @@ function loadConversation(factoryPath, sessionId) {
|
|
|
4709
4734
|
}
|
|
4710
4735
|
function buildSystemPrompt(factoryPath) {
|
|
4711
4736
|
let factoryName = "BeastMode Factory";
|
|
4712
|
-
const factoryJson = readJsonSafe(
|
|
4737
|
+
const factoryJson = readJsonSafe(join14(factoryPath, ".beastmode", "factory.json"));
|
|
4713
4738
|
if (factoryJson?.factory_name) factoryName = factoryJson.factory_name;
|
|
4714
4739
|
else if (factoryJson?.name) factoryName = factoryJson.name;
|
|
4715
4740
|
return `You are BeastMode Assistant \u2014 a concise, helpful assistant for the "${factoryName}" factory at ${factoryPath}.
|
|
@@ -5256,8 +5281,8 @@ var init_chat_handler = __esm({
|
|
|
5256
5281
|
});
|
|
5257
5282
|
|
|
5258
5283
|
// src/cli/ui/board-api-routes.ts
|
|
5259
|
-
import { readFileSync as
|
|
5260
|
-
import { join as
|
|
5284
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync13, existsSync as existsSync16, readdirSync as readdirSync8, unlinkSync as unlinkSync4, mkdirSync as mkdirSync12, statSync as statSync6 } from "fs";
|
|
5285
|
+
import { join as join15, basename as basename5, resolve as resolve4, dirname as dirname6, sep } from "path";
|
|
5261
5286
|
import { homedir } from "os";
|
|
5262
5287
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5263
5288
|
import { execSync as execSync3, spawnSync } from "child_process";
|
|
@@ -5301,10 +5326,10 @@ function assertSafeName(name) {
|
|
|
5301
5326
|
}
|
|
5302
5327
|
function getBoardUrl2(factoryDir) {
|
|
5303
5328
|
if (process.env.BEASTMODE_BOARD_URL) return process.env.BEASTMODE_BOARD_URL;
|
|
5304
|
-
const configPath =
|
|
5329
|
+
const configPath = join15(factoryDir, ".beastmode", "config.json");
|
|
5305
5330
|
if (existsSync16(configPath)) {
|
|
5306
5331
|
try {
|
|
5307
|
-
const config = JSON.parse(
|
|
5332
|
+
const config = JSON.parse(readFileSync14(configPath, "utf-8"));
|
|
5308
5333
|
if (config.task_backend?.config?.url) {
|
|
5309
5334
|
return config.task_backend.config.url;
|
|
5310
5335
|
}
|
|
@@ -5445,7 +5470,7 @@ function scopedQuery(query) {
|
|
|
5445
5470
|
function readJsonFile(filePath) {
|
|
5446
5471
|
if (!existsSync16(filePath)) return null;
|
|
5447
5472
|
try {
|
|
5448
|
-
return JSON.parse(
|
|
5473
|
+
return JSON.parse(readFileSync14(filePath, "utf-8"));
|
|
5449
5474
|
} catch {
|
|
5450
5475
|
return null;
|
|
5451
5476
|
}
|
|
@@ -5486,8 +5511,8 @@ function applyPresetOverrides(current, patch, presetName) {
|
|
|
5486
5511
|
}
|
|
5487
5512
|
function syncToDaemonConfigs(merged, factoryDir) {
|
|
5488
5513
|
const daemonConfigPaths = [
|
|
5489
|
-
|
|
5490
|
-
|
|
5514
|
+
join15(factoryDir, "config", "beastmode.daemon.json"),
|
|
5515
|
+
join15(factoryDir, "config", "beastmode.docker.json")
|
|
5491
5516
|
].filter(existsSync16);
|
|
5492
5517
|
const pipelineToDaemonMap = {
|
|
5493
5518
|
satisfaction_threshold: ["verification", "satisfaction_threshold"],
|
|
@@ -5512,7 +5537,7 @@ function syncToDaemonConfigs(merged, factoryDir) {
|
|
|
5512
5537
|
];
|
|
5513
5538
|
for (const daemonConfigPath of daemonConfigPaths) {
|
|
5514
5539
|
try {
|
|
5515
|
-
const daemonConfig = JSON.parse(
|
|
5540
|
+
const daemonConfig = JSON.parse(readFileSync14(daemonConfigPath, "utf-8"));
|
|
5516
5541
|
let changed = false;
|
|
5517
5542
|
for (const field of daemonFields) {
|
|
5518
5543
|
if (field in merged && merged[field] !== daemonConfig[field]) {
|
|
@@ -5575,25 +5600,25 @@ function syncToDaemonConfigs(merged, factoryDir) {
|
|
|
5575
5600
|
}
|
|
5576
5601
|
}
|
|
5577
5602
|
function getRunsDir(factoryDir) {
|
|
5578
|
-
const configPath =
|
|
5603
|
+
const configPath = join15(factoryDir, ".beastmode", "config.json");
|
|
5579
5604
|
if (existsSync16(configPath)) {
|
|
5580
5605
|
try {
|
|
5581
|
-
const config = JSON.parse(
|
|
5606
|
+
const config = JSON.parse(readFileSync14(configPath, "utf-8"));
|
|
5582
5607
|
if (config.runs_path) return config.runs_path;
|
|
5583
5608
|
} catch {
|
|
5584
5609
|
}
|
|
5585
5610
|
}
|
|
5586
|
-
return
|
|
5611
|
+
return join15(factoryDir, "runs");
|
|
5587
5612
|
}
|
|
5588
5613
|
function scanStrandedRuns(runsDir, newThreshold) {
|
|
5589
5614
|
if (!existsSync16(runsDir)) return [];
|
|
5590
5615
|
const stranded = [];
|
|
5591
5616
|
const walkRun = (runPath, projectId) => {
|
|
5592
|
-
const ckptPath =
|
|
5617
|
+
const ckptPath = join15(runPath, "checkpoint.json");
|
|
5593
5618
|
if (!existsSync16(ckptPath)) return;
|
|
5594
5619
|
let ckpt;
|
|
5595
5620
|
try {
|
|
5596
|
-
ckpt = JSON.parse(
|
|
5621
|
+
ckpt = JSON.parse(readFileSync14(ckptPath, "utf-8"));
|
|
5597
5622
|
} catch {
|
|
5598
5623
|
return;
|
|
5599
5624
|
}
|
|
@@ -5613,7 +5638,7 @@ function scanStrandedRuns(runsDir, newThreshold) {
|
|
|
5613
5638
|
try {
|
|
5614
5639
|
const entries = readdirSync8(runsDir);
|
|
5615
5640
|
for (const entry of entries) {
|
|
5616
|
-
const entryPath =
|
|
5641
|
+
const entryPath = join15(runsDir, entry);
|
|
5617
5642
|
let stat;
|
|
5618
5643
|
try {
|
|
5619
5644
|
stat = statSync6(entryPath);
|
|
@@ -5632,7 +5657,7 @@ function scanStrandedRuns(runsDir, newThreshold) {
|
|
|
5632
5657
|
}
|
|
5633
5658
|
for (const sub of subEntries) {
|
|
5634
5659
|
if (!sub.startsWith("run-")) continue;
|
|
5635
|
-
const subPath =
|
|
5660
|
+
const subPath = join15(entryPath, sub);
|
|
5636
5661
|
try {
|
|
5637
5662
|
if (statSync6(subPath).isDirectory()) {
|
|
5638
5663
|
walkRun(subPath, entry);
|
|
@@ -5649,10 +5674,10 @@ function scanStrandedRuns(runsDir, newThreshold) {
|
|
|
5649
5674
|
return stranded;
|
|
5650
5675
|
}
|
|
5651
5676
|
function _readAnalyzeMeta(projectsDir, name) {
|
|
5652
|
-
const metaPath =
|
|
5677
|
+
const metaPath = join15(projectsDir, name, "codebase-guide.meta.json");
|
|
5653
5678
|
if (!existsSync16(metaPath)) return null;
|
|
5654
5679
|
try {
|
|
5655
|
-
return JSON.parse(
|
|
5680
|
+
return JSON.parse(readFileSync14(metaPath, "utf-8"));
|
|
5656
5681
|
} catch {
|
|
5657
5682
|
return null;
|
|
5658
5683
|
}
|
|
@@ -5671,7 +5696,7 @@ function getBoardRoutes(factoryDir) {
|
|
|
5671
5696
|
method: "GET",
|
|
5672
5697
|
pattern: "/api/status",
|
|
5673
5698
|
handler: async () => {
|
|
5674
|
-
const bmDir =
|
|
5699
|
+
const bmDir = join15(factoryDir, ".beastmode");
|
|
5675
5700
|
const boardUrl = getBoardUrl2(factoryDir);
|
|
5676
5701
|
const factoryIdentity = await proxyToBoard(
|
|
5677
5702
|
boardUrl,
|
|
@@ -5680,11 +5705,11 @@ function getBoardRoutes(factoryDir) {
|
|
|
5680
5705
|
);
|
|
5681
5706
|
const projectsResp = await proxyToBoard(boardUrl, "GET", "/api/projects");
|
|
5682
5707
|
const projectCount = (projectsResp.projects || []).length;
|
|
5683
|
-
const lockPath =
|
|
5708
|
+
const lockPath = join15(bmDir, "extensions.lock");
|
|
5684
5709
|
let pluginNames = [];
|
|
5685
5710
|
if (existsSync16(lockPath)) {
|
|
5686
5711
|
try {
|
|
5687
|
-
const lock = JSON.parse(
|
|
5712
|
+
const lock = JSON.parse(readFileSync14(lockPath, "utf-8"));
|
|
5688
5713
|
pluginNames = Object.keys(lock.plugins || {});
|
|
5689
5714
|
} catch {
|
|
5690
5715
|
}
|
|
@@ -5704,24 +5729,24 @@ function getBoardRoutes(factoryDir) {
|
|
|
5704
5729
|
skillCount = listSkills(factoryDir).length;
|
|
5705
5730
|
} catch {
|
|
5706
5731
|
}
|
|
5707
|
-
const runsDir =
|
|
5732
|
+
const runsDir = join15(factoryDir, "runs");
|
|
5708
5733
|
let runDirs = [];
|
|
5709
5734
|
if (existsSync16(runsDir)) {
|
|
5710
5735
|
runDirs = readdirSync8(runsDir).filter((d) => {
|
|
5711
5736
|
if (d.startsWith(".")) return false;
|
|
5712
5737
|
try {
|
|
5713
|
-
return readdirSync8(
|
|
5738
|
+
return readdirSync8(join15(runsDir, d)).length > 0;
|
|
5714
5739
|
} catch {
|
|
5715
5740
|
return false;
|
|
5716
5741
|
}
|
|
5717
5742
|
}).sort();
|
|
5718
5743
|
}
|
|
5719
|
-
const pidFile =
|
|
5744
|
+
const pidFile = join15(factoryDir, ".beastmode", "daemon.pid");
|
|
5720
5745
|
let daemonPid = null;
|
|
5721
5746
|
let pidAlive = false;
|
|
5722
5747
|
if (existsSync16(pidFile)) {
|
|
5723
5748
|
try {
|
|
5724
|
-
daemonPid = parseInt(
|
|
5749
|
+
daemonPid = parseInt(readFileSync14(pidFile, "utf-8").trim(), 10);
|
|
5725
5750
|
process.kill(daemonPid, 0);
|
|
5726
5751
|
pidAlive = true;
|
|
5727
5752
|
} catch {
|
|
@@ -5778,10 +5803,10 @@ function getBoardRoutes(factoryDir) {
|
|
|
5778
5803
|
handler: async () => {
|
|
5779
5804
|
const boardUrl = getBoardUrl2(factoryDir);
|
|
5780
5805
|
let ageSecs = null;
|
|
5781
|
-
const heartbeatPath =
|
|
5806
|
+
const heartbeatPath = join15(factoryDir, "daemon", "logs", ".heartbeat");
|
|
5782
5807
|
if (existsSync16(heartbeatPath)) {
|
|
5783
5808
|
try {
|
|
5784
|
-
const ts = parseInt(
|
|
5809
|
+
const ts = parseInt(readFileSync14(heartbeatPath, "utf-8").trim(), 10);
|
|
5785
5810
|
if (!isNaN(ts)) {
|
|
5786
5811
|
ageSecs = Math.floor(Date.now() / 1e3) - ts;
|
|
5787
5812
|
}
|
|
@@ -5823,11 +5848,11 @@ function getBoardRoutes(factoryDir) {
|
|
|
5823
5848
|
let totalSlots = 3;
|
|
5824
5849
|
let shortPhaseReserve;
|
|
5825
5850
|
let slotsTimestamp;
|
|
5826
|
-
const slotsPath =
|
|
5851
|
+
const slotsPath = join15(factoryDir, "daemon", "logs", ".slots.json");
|
|
5827
5852
|
let usedDaemonSlotsFile = false;
|
|
5828
5853
|
if (existsSync16(slotsPath)) {
|
|
5829
5854
|
try {
|
|
5830
|
-
const slots = JSON.parse(
|
|
5855
|
+
const slots = JSON.parse(readFileSync14(slotsPath, "utf-8"));
|
|
5831
5856
|
if (typeof slots.busy === "number" && typeof slots.total === "number") {
|
|
5832
5857
|
busySlots = slots.busy;
|
|
5833
5858
|
totalSlots = slots.total;
|
|
@@ -5852,10 +5877,10 @@ function getBoardRoutes(factoryDir) {
|
|
|
5852
5877
|
} catch {
|
|
5853
5878
|
}
|
|
5854
5879
|
for (const name of ["beastmode.docker.json", "beastmode.daemon.json"]) {
|
|
5855
|
-
const configPath =
|
|
5880
|
+
const configPath = join15(factoryDir, "config", name);
|
|
5856
5881
|
if (!existsSync16(configPath)) continue;
|
|
5857
5882
|
try {
|
|
5858
|
-
const config = JSON.parse(
|
|
5883
|
+
const config = JSON.parse(readFileSync14(configPath, "utf-8"));
|
|
5859
5884
|
if (typeof config.max_slots === "number") {
|
|
5860
5885
|
totalSlots = config.max_slots;
|
|
5861
5886
|
}
|
|
@@ -5872,11 +5897,11 @@ function getBoardRoutes(factoryDir) {
|
|
|
5872
5897
|
if (shortPhaseReserve === void 0) {
|
|
5873
5898
|
shortPhaseReserve = Math.max(0, Math.min(1, totalSlots - 1));
|
|
5874
5899
|
}
|
|
5875
|
-
const alertsPath =
|
|
5900
|
+
const alertsPath = join15(factoryDir, "daemon", "logs", ".alerts.json");
|
|
5876
5901
|
let alerts = [];
|
|
5877
5902
|
if (existsSync16(alertsPath)) {
|
|
5878
5903
|
try {
|
|
5879
|
-
const parsed = JSON.parse(
|
|
5904
|
+
const parsed = JSON.parse(readFileSync14(alertsPath, "utf-8"));
|
|
5880
5905
|
if (Array.isArray(parsed)) alerts = parsed;
|
|
5881
5906
|
} catch {
|
|
5882
5907
|
}
|
|
@@ -6103,12 +6128,12 @@ function getBoardRoutes(factoryDir) {
|
|
|
6103
6128
|
method: "GET",
|
|
6104
6129
|
pattern: "/api/extensions/plugins",
|
|
6105
6130
|
handler: () => {
|
|
6106
|
-
const lockPath =
|
|
6131
|
+
const lockPath = join15(factoryDir, ".beastmode", "extensions.lock");
|
|
6107
6132
|
if (!existsSync16(lockPath)) {
|
|
6108
6133
|
return { plugins: {} };
|
|
6109
6134
|
}
|
|
6110
6135
|
try {
|
|
6111
|
-
const lock = JSON.parse(
|
|
6136
|
+
const lock = JSON.parse(readFileSync14(lockPath, "utf-8"));
|
|
6112
6137
|
return { plugins: lock.plugins || {} };
|
|
6113
6138
|
} catch {
|
|
6114
6139
|
return { plugins: {} };
|
|
@@ -6354,18 +6379,18 @@ function getBoardRoutes(factoryDir) {
|
|
|
6354
6379
|
let entries = [];
|
|
6355
6380
|
try {
|
|
6356
6381
|
entries = readdirSync8(target, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => {
|
|
6357
|
-
const full =
|
|
6382
|
+
const full = join15(target, d.name);
|
|
6358
6383
|
return {
|
|
6359
6384
|
name: d.name,
|
|
6360
6385
|
path: full,
|
|
6361
6386
|
is_dir: true,
|
|
6362
|
-
has_git: existsSync16(
|
|
6387
|
+
has_git: existsSync16(join15(full, ".git"))
|
|
6363
6388
|
};
|
|
6364
6389
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
6365
6390
|
} catch {
|
|
6366
6391
|
entries = [];
|
|
6367
6392
|
}
|
|
6368
|
-
const parent =
|
|
6393
|
+
const parent = dirname6(target);
|
|
6369
6394
|
return {
|
|
6370
6395
|
path: target,
|
|
6371
6396
|
parent: parent !== target ? parent : null,
|
|
@@ -6387,7 +6412,7 @@ function getBoardRoutes(factoryDir) {
|
|
|
6387
6412
|
}
|
|
6388
6413
|
const repoName = url.replace(/\.git$/, "").split(/[/:]/).filter(Boolean).pop() || "";
|
|
6389
6414
|
if (!repoName) throw new Error("Could not derive repo name from URL");
|
|
6390
|
-
const baseDir = clone_target ? resolve4(clone_target) : process.env.BEASTMODE_CLONE_DIR ? resolve4(process.env.BEASTMODE_CLONE_DIR) :
|
|
6415
|
+
const baseDir = clone_target ? resolve4(clone_target) : process.env.BEASTMODE_CLONE_DIR ? resolve4(process.env.BEASTMODE_CLONE_DIR) : join15(homedir(), "repos");
|
|
6391
6416
|
const allowedRoots = _getAllowedRoots();
|
|
6392
6417
|
if (!_isPathAllowed(baseDir, allowedRoots)) {
|
|
6393
6418
|
throw new Error(
|
|
@@ -6395,7 +6420,7 @@ function getBoardRoutes(factoryDir) {
|
|
|
6395
6420
|
);
|
|
6396
6421
|
}
|
|
6397
6422
|
if (!existsSync16(baseDir)) mkdirSync12(baseDir, { recursive: true });
|
|
6398
|
-
resolvedPath =
|
|
6423
|
+
resolvedPath = join15(baseDir, repoName);
|
|
6399
6424
|
if (!_isPathAllowed(resolvedPath, allowedRoots)) {
|
|
6400
6425
|
throw new Error(
|
|
6401
6426
|
`clone_target not allowed: ${resolvedPath} is outside the configured allowlist`
|
|
@@ -6502,16 +6527,16 @@ function getBoardRoutes(factoryDir) {
|
|
|
6502
6527
|
handler: async (body, params, query) => {
|
|
6503
6528
|
const { name } = params;
|
|
6504
6529
|
assertSafeName(name);
|
|
6505
|
-
const projectsDir =
|
|
6530
|
+
const projectsDir = join15(factoryDir, ".beastmode", "projects");
|
|
6506
6531
|
const projConfig = readProjectRecord(projectsDir, name);
|
|
6507
6532
|
if (!projConfig) throw new Error(`Project not found: ${name}`);
|
|
6508
6533
|
const projectPath = projConfig.path;
|
|
6509
|
-
const subDir =
|
|
6534
|
+
const subDir = join15(projectsDir, name);
|
|
6510
6535
|
if (!existsSync16(subDir)) mkdirSync12(subDir, { recursive: true });
|
|
6511
6536
|
const force = query?.force === "true" || query?.force === "1";
|
|
6512
6537
|
const tier = body?.tier;
|
|
6513
6538
|
if (tier === "quick") {
|
|
6514
|
-
const brownfieldPath =
|
|
6539
|
+
const brownfieldPath = join15(subDir, "brownfield.md");
|
|
6515
6540
|
if (!existsSync16(brownfieldPath)) {
|
|
6516
6541
|
const { detectStack: detectStackFn } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
6517
6542
|
let stackInfo = "Unknown stack";
|
|
@@ -6545,12 +6570,12 @@ Path: ${projectPath}
|
|
|
6545
6570
|
if (existingJob && existingJob.status === "running") {
|
|
6546
6571
|
return { analyzing: true, project: name, job_id: existingJob.job_id, status: "running" };
|
|
6547
6572
|
}
|
|
6548
|
-
const triggerPath =
|
|
6549
|
-
const inProgressPath =
|
|
6573
|
+
const triggerPath = join15(subDir, ".analyze-request.json");
|
|
6574
|
+
const inProgressPath = join15(subDir, ".analyze-in-progress.json");
|
|
6550
6575
|
if (existsSync16(triggerPath) || existsSync16(inProgressPath)) {
|
|
6551
6576
|
const existingTrigger = existsSync16(triggerPath) ? triggerPath : inProgressPath;
|
|
6552
6577
|
try {
|
|
6553
|
-
const t = JSON.parse(
|
|
6578
|
+
const t = JSON.parse(readFileSync14(existingTrigger, "utf-8"));
|
|
6554
6579
|
const job2 = {
|
|
6555
6580
|
job_id: t.job_id,
|
|
6556
6581
|
project: name,
|
|
@@ -6604,15 +6629,15 @@ Path: ${projectPath}
|
|
|
6604
6629
|
handler: (_body, params) => {
|
|
6605
6630
|
const { name } = params;
|
|
6606
6631
|
assertSafeName(name);
|
|
6607
|
-
const projectsDir =
|
|
6608
|
-
const subDir =
|
|
6609
|
-
const triggerPath =
|
|
6610
|
-
const inProgressPath =
|
|
6632
|
+
const projectsDir = join15(factoryDir, ".beastmode", "projects");
|
|
6633
|
+
const subDir = join15(projectsDir, name);
|
|
6634
|
+
const triggerPath = join15(subDir, ".analyze-request.json");
|
|
6635
|
+
const inProgressPath = join15(subDir, ".analyze-in-progress.json");
|
|
6611
6636
|
if (existsSync16(triggerPath) || existsSync16(inProgressPath)) {
|
|
6612
6637
|
const job2 = _analyzeJobs.get(name);
|
|
6613
6638
|
try {
|
|
6614
6639
|
const p = existsSync16(triggerPath) ? triggerPath : inProgressPath;
|
|
6615
|
-
const raw =
|
|
6640
|
+
const raw = readFileSync14(p, "utf-8");
|
|
6616
6641
|
const t = JSON.parse(raw);
|
|
6617
6642
|
const now = Date.now();
|
|
6618
6643
|
const startedMs = new Date(t.requested_at).getTime();
|
|
@@ -6698,7 +6723,7 @@ Path: ${projectPath}
|
|
|
6698
6723
|
handler: (_body, params, query) => {
|
|
6699
6724
|
const { name } = params;
|
|
6700
6725
|
assertSafeName(name);
|
|
6701
|
-
const projectsDir =
|
|
6726
|
+
const projectsDir = join15(factoryDir, ".beastmode", "projects");
|
|
6702
6727
|
const meta = _readAnalyzeMeta(projectsDir, name);
|
|
6703
6728
|
if (!meta || meta.status !== "complete") {
|
|
6704
6729
|
throw new HttpError(404, {
|
|
@@ -6709,11 +6734,11 @@ Path: ${projectPath}
|
|
|
6709
6734
|
const levelRaw = query?.level || "l1";
|
|
6710
6735
|
const level = levelRaw === "l0" || levelRaw === "l2" ? levelRaw : "l1";
|
|
6711
6736
|
const filename = level === "l0" ? "codebase-guide.l0.txt" : level === "l2" ? "codebase-guide.md" : "codebase-guide.l1.md";
|
|
6712
|
-
const filePath =
|
|
6737
|
+
const filePath = join15(projectsDir, name, filename);
|
|
6713
6738
|
if (!existsSync16(filePath)) {
|
|
6714
6739
|
throw new HttpError(404, { error: "Guide file not found" });
|
|
6715
6740
|
}
|
|
6716
|
-
const content =
|
|
6741
|
+
const content = readFileSync14(filePath, "utf-8");
|
|
6717
6742
|
return { level, content, meta };
|
|
6718
6743
|
}
|
|
6719
6744
|
},
|
|
@@ -6727,7 +6752,7 @@ Path: ${projectPath}
|
|
|
6727
6752
|
const runEntries = [];
|
|
6728
6753
|
for (const entry of readdirSync8(runsDir)) {
|
|
6729
6754
|
if (entry.startsWith(".")) continue;
|
|
6730
|
-
const entryPath =
|
|
6755
|
+
const entryPath = join15(runsDir, entry);
|
|
6731
6756
|
try {
|
|
6732
6757
|
const children = readdirSync8(entryPath);
|
|
6733
6758
|
if (entry.startsWith("run-")) {
|
|
@@ -6737,7 +6762,7 @@ Path: ${projectPath}
|
|
|
6737
6762
|
} else {
|
|
6738
6763
|
for (const child of children) {
|
|
6739
6764
|
if (!child.startsWith("run-") || child.startsWith(".")) continue;
|
|
6740
|
-
const childPath =
|
|
6765
|
+
const childPath = join15(entryPath, child);
|
|
6741
6766
|
try {
|
|
6742
6767
|
const grandchildren = readdirSync8(childPath);
|
|
6743
6768
|
if (grandchildren.length > 0) {
|
|
@@ -6759,9 +6784,9 @@ Path: ${projectPath}
|
|
|
6759
6784
|
}
|
|
6760
6785
|
}
|
|
6761
6786
|
const runs = Array.from(deduped.values()).sort((a, b) => b.id.localeCompare(a.id)).map(({ id, dir, projectId }) => {
|
|
6762
|
-
const manifest = readJsonFile(
|
|
6763
|
-
const checkpoint = readJsonFile(
|
|
6764
|
-
const prodVerif = readJsonFile(
|
|
6787
|
+
const manifest = readJsonFile(join15(dir, "manifest.json"));
|
|
6788
|
+
const checkpoint = readJsonFile(join15(dir, "checkpoint.json"));
|
|
6789
|
+
const prodVerif = readJsonFile(join15(dir, "prod-verification.json"));
|
|
6765
6790
|
const manifestData = manifest;
|
|
6766
6791
|
const cpData = checkpoint;
|
|
6767
6792
|
const prodAcceptFloor = 0.65;
|
|
@@ -6789,11 +6814,11 @@ Path: ${projectPath}
|
|
|
6789
6814
|
handler: (_body, params, query) => {
|
|
6790
6815
|
const { id } = params;
|
|
6791
6816
|
const runsDir = getRunsDir(factoryDir);
|
|
6792
|
-
let runDir =
|
|
6817
|
+
let runDir = join15(runsDir, id);
|
|
6793
6818
|
if (!existsSync16(runDir)) {
|
|
6794
6819
|
for (const proj of readdirSync8(runsDir)) {
|
|
6795
6820
|
if (proj.startsWith(".") || proj.startsWith("run-")) continue;
|
|
6796
|
-
const candidate =
|
|
6821
|
+
const candidate = join15(runsDir, proj, id);
|
|
6797
6822
|
if (existsSync16(candidate)) {
|
|
6798
6823
|
runDir = candidate;
|
|
6799
6824
|
break;
|
|
@@ -6801,15 +6826,15 @@ Path: ${projectPath}
|
|
|
6801
6826
|
}
|
|
6802
6827
|
}
|
|
6803
6828
|
if (!existsSync16(runDir)) throw new Error(`Run not found: ${id}`);
|
|
6804
|
-
const manifest = readJsonFile(
|
|
6805
|
-
const checkpoint = readJsonFile(
|
|
6806
|
-
const iterationsDir =
|
|
6829
|
+
const manifest = readJsonFile(join15(runDir, "manifest.json"));
|
|
6830
|
+
const checkpoint = readJsonFile(join15(runDir, "checkpoint.json"));
|
|
6831
|
+
const iterationsDir = join15(runDir, "iterations");
|
|
6807
6832
|
const iterations = [];
|
|
6808
6833
|
if (existsSync16(iterationsDir)) {
|
|
6809
6834
|
const iterDirs = readdirSync8(iterationsDir).sort();
|
|
6810
6835
|
for (const iterDir of iterDirs) {
|
|
6811
6836
|
const satisfaction = readJsonFile(
|
|
6812
|
-
|
|
6837
|
+
join15(iterationsDir, iterDir, "satisfaction.json")
|
|
6813
6838
|
);
|
|
6814
6839
|
iterations.push({
|
|
6815
6840
|
number: parseInt(iterDir, 10),
|
|
@@ -6837,11 +6862,11 @@ Path: ${projectPath}
|
|
|
6837
6862
|
pattern: "/api/runs/archive",
|
|
6838
6863
|
handler: () => {
|
|
6839
6864
|
const runsDir = getRunsDir(factoryDir);
|
|
6840
|
-
const configPath =
|
|
6865
|
+
const configPath = join15(factoryDir, ".beastmode", "config.json");
|
|
6841
6866
|
let days = 7;
|
|
6842
6867
|
if (existsSync16(configPath)) {
|
|
6843
6868
|
try {
|
|
6844
|
-
days = JSON.parse(
|
|
6869
|
+
days = JSON.parse(readFileSync14(configPath, "utf-8")).archive_after_days || 7;
|
|
6845
6870
|
} catch {
|
|
6846
6871
|
}
|
|
6847
6872
|
}
|
|
@@ -6867,8 +6892,8 @@ Path: ${projectPath}
|
|
|
6867
6892
|
method: "GET",
|
|
6868
6893
|
pattern: "/api/config",
|
|
6869
6894
|
handler: () => {
|
|
6870
|
-
const configPath =
|
|
6871
|
-
const raw = existsSync16(configPath) ? JSON.parse(
|
|
6895
|
+
const configPath = join15(factoryDir, ".beastmode", "config.json");
|
|
6896
|
+
const raw = existsSync16(configPath) ? JSON.parse(readFileSync14(configPath, "utf-8")) : generateDefaults();
|
|
6872
6897
|
syncToDaemonConfigs(raw, factoryDir);
|
|
6873
6898
|
delete raw.archive_s3_bucket;
|
|
6874
6899
|
return raw;
|
|
@@ -6888,8 +6913,8 @@ Path: ${projectPath}
|
|
|
6888
6913
|
const updatesClean = { ...updates };
|
|
6889
6914
|
delete updatesClean.force;
|
|
6890
6915
|
delete updatesClean._force;
|
|
6891
|
-
const configPath =
|
|
6892
|
-
const current = existsSync16(configPath) ? JSON.parse(
|
|
6916
|
+
const configPath = join15(factoryDir, ".beastmode", "config.json");
|
|
6917
|
+
const current = existsSync16(configPath) ? JSON.parse(readFileSync14(configPath, "utf-8")) : generateDefaults();
|
|
6893
6918
|
if (!force) {
|
|
6894
6919
|
const oldPipeline = current.pipeline || {};
|
|
6895
6920
|
const newPipeline = updatesClean.pipeline || {};
|
|
@@ -6936,8 +6961,8 @@ Path: ${projectPath}
|
|
|
6936
6961
|
pattern: "/api/config/reset",
|
|
6937
6962
|
handler: (body) => {
|
|
6938
6963
|
const { keyPath } = body || {};
|
|
6939
|
-
const configPath =
|
|
6940
|
-
const current = existsSync16(configPath) ? JSON.parse(
|
|
6964
|
+
const configPath = join15(factoryDir, ".beastmode", "config.json");
|
|
6965
|
+
const current = existsSync16(configPath) ? JSON.parse(readFileSync14(configPath, "utf-8")) : {};
|
|
6941
6966
|
const result = configReset(current, keyPath);
|
|
6942
6967
|
writeFileSync13(configPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
6943
6968
|
return result;
|
|
@@ -7029,12 +7054,12 @@ Path: ${projectPath}
|
|
|
7029
7054
|
handler: () => {
|
|
7030
7055
|
const runsDir = getRunsDir(factoryDir);
|
|
7031
7056
|
const retroDirs = [];
|
|
7032
|
-
const rootRetroDir =
|
|
7057
|
+
const rootRetroDir = join15(runsDir, ".retrospectives");
|
|
7033
7058
|
if (existsSync16(rootRetroDir)) retroDirs.push(rootRetroDir);
|
|
7034
7059
|
if (existsSync16(runsDir)) {
|
|
7035
7060
|
for (const entry of readdirSync8(runsDir)) {
|
|
7036
7061
|
if (entry === ".retrospectives" || entry.startsWith(".")) continue;
|
|
7037
|
-
const projectRetroDir =
|
|
7062
|
+
const projectRetroDir = join15(runsDir, entry, ".retrospectives");
|
|
7038
7063
|
if (existsSync16(projectRetroDir)) retroDirs.push(projectRetroDir);
|
|
7039
7064
|
}
|
|
7040
7065
|
}
|
|
@@ -7046,7 +7071,7 @@ Path: ${projectPath}
|
|
|
7046
7071
|
for (const retroDir of retroDirs) {
|
|
7047
7072
|
const retroFiles = readdirSync8(retroDir).filter((f) => f.startsWith("run-") && f.endsWith(".json")).sort().reverse();
|
|
7048
7073
|
for (const file of retroFiles) {
|
|
7049
|
-
const data = readJsonFile(
|
|
7074
|
+
const data = readJsonFile(join15(retroDir, file));
|
|
7050
7075
|
if (!data) continue;
|
|
7051
7076
|
const retro = data;
|
|
7052
7077
|
retrospectives.push(retro);
|
|
@@ -7068,10 +7093,10 @@ Path: ${projectPath}
|
|
|
7068
7093
|
let wisdom = "";
|
|
7069
7094
|
let wisdomMeta = {};
|
|
7070
7095
|
for (const retroDir of retroDirs) {
|
|
7071
|
-
const wisdomPath =
|
|
7096
|
+
const wisdomPath = join15(retroDir, "wisdom.md");
|
|
7072
7097
|
if (existsSync16(wisdomPath)) {
|
|
7073
|
-
wisdom =
|
|
7074
|
-
wisdomMeta = readJsonFile(
|
|
7098
|
+
wisdom = readFileSync14(wisdomPath, "utf-8");
|
|
7099
|
+
wisdomMeta = readJsonFile(join15(retroDir, "wisdom-meta.json")) || {};
|
|
7075
7100
|
break;
|
|
7076
7101
|
}
|
|
7077
7102
|
}
|
|
@@ -7083,11 +7108,11 @@ Path: ${projectPath}
|
|
|
7083
7108
|
pattern: "/api/learnings/wisdom/refresh",
|
|
7084
7109
|
handler: () => {
|
|
7085
7110
|
const runsDir = getRunsDir(factoryDir);
|
|
7086
|
-
const retroDir =
|
|
7111
|
+
const retroDir = join15(runsDir, ".retrospectives");
|
|
7087
7112
|
if (!existsSync16(retroDir)) {
|
|
7088
7113
|
mkdirSync12(retroDir, { recursive: true });
|
|
7089
7114
|
}
|
|
7090
|
-
writeFileSync13(
|
|
7115
|
+
writeFileSync13(join15(retroDir, ".refresh-requested"), (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
|
|
7091
7116
|
return { success: true };
|
|
7092
7117
|
}
|
|
7093
7118
|
},
|
|
@@ -7096,23 +7121,23 @@ Path: ${projectPath}
|
|
|
7096
7121
|
pattern: "/api/learnings/:runId/:index/dismiss",
|
|
7097
7122
|
handler: (_body, params) => {
|
|
7098
7123
|
const runsDir = getRunsDir(factoryDir);
|
|
7099
|
-
const candidateDirs = [
|
|
7124
|
+
const candidateDirs = [join15(runsDir, ".retrospectives")];
|
|
7100
7125
|
if (existsSync16(runsDir)) {
|
|
7101
7126
|
for (const entry of readdirSync8(runsDir)) {
|
|
7102
7127
|
if (entry === ".retrospectives" || entry.startsWith(".")) continue;
|
|
7103
|
-
candidateDirs.push(
|
|
7128
|
+
candidateDirs.push(join15(runsDir, entry, ".retrospectives"));
|
|
7104
7129
|
}
|
|
7105
7130
|
}
|
|
7106
7131
|
let filePath = null;
|
|
7107
7132
|
for (const dir of candidateDirs) {
|
|
7108
|
-
const candidate =
|
|
7133
|
+
const candidate = join15(dir, `${params.runId}.json`);
|
|
7109
7134
|
if (existsSync16(candidate)) {
|
|
7110
7135
|
filePath = candidate;
|
|
7111
7136
|
break;
|
|
7112
7137
|
}
|
|
7113
7138
|
}
|
|
7114
7139
|
if (!filePath) throw new Error(`Retrospective not found: ${params.runId}`);
|
|
7115
|
-
const data = JSON.parse(
|
|
7140
|
+
const data = JSON.parse(readFileSync14(filePath, "utf-8"));
|
|
7116
7141
|
const idx = parseInt(params.index, 10);
|
|
7117
7142
|
if (!Array.isArray(data.learnings) || idx < 0 || idx >= data.learnings.length) {
|
|
7118
7143
|
throw new Error(`Learning index out of range: ${idx}`);
|
|
@@ -7127,23 +7152,23 @@ Path: ${projectPath}
|
|
|
7127
7152
|
pattern: "/api/learnings/:runId/:index/restore",
|
|
7128
7153
|
handler: (_body, params) => {
|
|
7129
7154
|
const runsDir = getRunsDir(factoryDir);
|
|
7130
|
-
const candidateDirs = [
|
|
7155
|
+
const candidateDirs = [join15(runsDir, ".retrospectives")];
|
|
7131
7156
|
if (existsSync16(runsDir)) {
|
|
7132
7157
|
for (const entry of readdirSync8(runsDir)) {
|
|
7133
7158
|
if (entry === ".retrospectives" || entry.startsWith(".")) continue;
|
|
7134
|
-
candidateDirs.push(
|
|
7159
|
+
candidateDirs.push(join15(runsDir, entry, ".retrospectives"));
|
|
7135
7160
|
}
|
|
7136
7161
|
}
|
|
7137
7162
|
let filePath = null;
|
|
7138
7163
|
for (const dir of candidateDirs) {
|
|
7139
|
-
const candidate =
|
|
7164
|
+
const candidate = join15(dir, `${params.runId}.json`);
|
|
7140
7165
|
if (existsSync16(candidate)) {
|
|
7141
7166
|
filePath = candidate;
|
|
7142
7167
|
break;
|
|
7143
7168
|
}
|
|
7144
7169
|
}
|
|
7145
7170
|
if (!filePath) throw new Error(`Retrospective not found: ${params.runId}`);
|
|
7146
|
-
const data = JSON.parse(
|
|
7171
|
+
const data = JSON.parse(readFileSync14(filePath, "utf-8"));
|
|
7147
7172
|
const idx = parseInt(params.index, 10);
|
|
7148
7173
|
if (!Array.isArray(data.learnings) || idx < 0 || idx >= data.learnings.length) {
|
|
7149
7174
|
throw new Error(`Learning index out of range: ${idx}`);
|
|
@@ -7292,22 +7317,22 @@ Path: ${projectPath}
|
|
|
7292
7317
|
} catch {
|
|
7293
7318
|
}
|
|
7294
7319
|
try {
|
|
7295
|
-
const runsDir =
|
|
7320
|
+
const runsDir = join15(factoryDir, "runs", project);
|
|
7296
7321
|
if (existsSync16(runsDir)) {
|
|
7297
7322
|
const runDirs = readdirSync8(runsDir).filter((d) => d.startsWith("run-"));
|
|
7298
7323
|
for (const runId of runDirs) {
|
|
7299
|
-
const ckptPath =
|
|
7324
|
+
const ckptPath = join15(runsDir, runId, "checkpoint.json");
|
|
7300
7325
|
let subtitle = "";
|
|
7301
7326
|
let timestamp = "";
|
|
7302
7327
|
try {
|
|
7303
|
-
const st = statSync6(
|
|
7328
|
+
const st = statSync6(join15(runsDir, runId));
|
|
7304
7329
|
timestamp = st.mtime.toISOString();
|
|
7305
7330
|
} catch {
|
|
7306
7331
|
timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
7307
7332
|
}
|
|
7308
7333
|
if (existsSync16(ckptPath)) {
|
|
7309
7334
|
try {
|
|
7310
|
-
const ckpt = JSON.parse(
|
|
7335
|
+
const ckpt = JSON.parse(readFileSync14(ckptPath, "utf-8"));
|
|
7311
7336
|
const history = ckpt.satisfaction_history;
|
|
7312
7337
|
if (history && history.length > 0) {
|
|
7313
7338
|
const latest = history[history.length - 1];
|
|
@@ -7432,9 +7457,9 @@ __export(server_exports, {
|
|
|
7432
7457
|
});
|
|
7433
7458
|
import { createServer } from "http";
|
|
7434
7459
|
import { createHmac } from "crypto";
|
|
7435
|
-
import { readFileSync as
|
|
7436
|
-
import { join as
|
|
7437
|
-
import { fileURLToPath as
|
|
7460
|
+
import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
|
|
7461
|
+
import { join as join16, dirname as dirname7 } from "path";
|
|
7462
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7438
7463
|
import { WebSocketServer as WebSocketServer2, WebSocket as WsClient } from "ws";
|
|
7439
7464
|
function proxyBoardWebSocket(req, socket, head, boardWsUrl) {
|
|
7440
7465
|
_boardWsProxy.handleUpgrade(req, socket, head, (clientWs) => {
|
|
@@ -7475,16 +7500,16 @@ function proxyBoardWebSocket(req, socket, head, boardWsUrl) {
|
|
|
7475
7500
|
});
|
|
7476
7501
|
}
|
|
7477
7502
|
function resolveStaticDir() {
|
|
7478
|
-
const devPath =
|
|
7479
|
-
if (existsSync17(
|
|
7503
|
+
const devPath = join16(__dirname, "static");
|
|
7504
|
+
if (existsSync17(join16(devPath, "index.html"))) {
|
|
7480
7505
|
return devPath;
|
|
7481
7506
|
}
|
|
7482
|
-
const distWebPath =
|
|
7483
|
-
if (existsSync17(
|
|
7507
|
+
const distWebPath = join16(__dirname, "web");
|
|
7508
|
+
if (existsSync17(join16(distWebPath, "index.html"))) {
|
|
7484
7509
|
return distWebPath;
|
|
7485
7510
|
}
|
|
7486
|
-
const siblingPath =
|
|
7487
|
-
if (existsSync17(
|
|
7511
|
+
const siblingPath = join16(__dirname, "..", "web");
|
|
7512
|
+
if (existsSync17(join16(siblingPath, "index.html"))) {
|
|
7488
7513
|
return siblingPath;
|
|
7489
7514
|
}
|
|
7490
7515
|
throw new Error(
|
|
@@ -7593,9 +7618,9 @@ async function startServer(options = {}) {
|
|
|
7593
7618
|
const fallback = "<html><body><h1>BeastMode Init Wizard</h1><p>Static files not found.</p></body></html>";
|
|
7594
7619
|
if (!staticDir) return fallback;
|
|
7595
7620
|
try {
|
|
7596
|
-
const indexPath =
|
|
7621
|
+
const indexPath = join16(staticDir, "index.html");
|
|
7597
7622
|
if (!existsSync17(indexPath)) return fallback;
|
|
7598
|
-
return
|
|
7623
|
+
return readFileSync15(indexPath, "utf-8");
|
|
7599
7624
|
} catch {
|
|
7600
7625
|
return fallback;
|
|
7601
7626
|
}
|
|
@@ -7603,9 +7628,9 @@ async function startServer(options = {}) {
|
|
|
7603
7628
|
function loadBoardHtml() {
|
|
7604
7629
|
try {
|
|
7605
7630
|
const dir = staticDir || resolveStaticDir();
|
|
7606
|
-
const boardPath =
|
|
7631
|
+
const boardPath = join16(dir, "board.html");
|
|
7607
7632
|
if (!existsSync17(boardPath)) return null;
|
|
7608
|
-
return
|
|
7633
|
+
return readFileSync15(boardPath, "utf-8");
|
|
7609
7634
|
} catch {
|
|
7610
7635
|
return null;
|
|
7611
7636
|
}
|
|
@@ -7700,13 +7725,13 @@ async function startServer(options = {}) {
|
|
|
7700
7725
|
let commit_sha = null;
|
|
7701
7726
|
try {
|
|
7702
7727
|
const dir = staticDir || resolveStaticDir();
|
|
7703
|
-
const stampPath =
|
|
7728
|
+
const stampPath = join16(dir, "build-stamp.txt");
|
|
7704
7729
|
if (existsSync17(stampPath)) {
|
|
7705
|
-
stamp =
|
|
7730
|
+
stamp = readFileSync15(stampPath, "utf-8").trim();
|
|
7706
7731
|
}
|
|
7707
|
-
const commitPath =
|
|
7732
|
+
const commitPath = join16(dir, "build-commit.txt");
|
|
7708
7733
|
if (existsSync17(commitPath)) {
|
|
7709
|
-
commit_sha =
|
|
7734
|
+
commit_sha = readFileSync15(commitPath, "utf-8").trim() || null;
|
|
7710
7735
|
}
|
|
7711
7736
|
} catch {
|
|
7712
7737
|
}
|
|
@@ -7784,7 +7809,7 @@ async function startServer(options = {}) {
|
|
|
7784
7809
|
}
|
|
7785
7810
|
if (method === "GET" && url.startsWith("/static/")) {
|
|
7786
7811
|
const filename = url.slice("/static/".length);
|
|
7787
|
-
const filePath = staticDir ?
|
|
7812
|
+
const filePath = staticDir ? join16(staticDir, filename) : "";
|
|
7788
7813
|
if (filePath && existsSync17(filePath)) {
|
|
7789
7814
|
const ext = filename.split(".").pop()?.toLowerCase();
|
|
7790
7815
|
const mimeTypes = {
|
|
@@ -7796,7 +7821,7 @@ async function startServer(options = {}) {
|
|
|
7796
7821
|
ico: "image/x-icon"
|
|
7797
7822
|
};
|
|
7798
7823
|
const contentType = mimeTypes[ext || ""] || "application/octet-stream";
|
|
7799
|
-
const content =
|
|
7824
|
+
const content = readFileSync15(filePath);
|
|
7800
7825
|
res.writeHead(200, {
|
|
7801
7826
|
"Content-Type": contentType,
|
|
7802
7827
|
"Content-Length": content.length.toString(),
|
|
@@ -7934,8 +7959,8 @@ var init_server = __esm({
|
|
|
7934
7959
|
init_board_api_routes();
|
|
7935
7960
|
init_chat_handler();
|
|
7936
7961
|
_boardWsProxy = new WebSocketServer2({ noServer: true });
|
|
7937
|
-
__filename =
|
|
7938
|
-
__dirname =
|
|
7962
|
+
__filename = fileURLToPath3(import.meta.url);
|
|
7963
|
+
__dirname = dirname7(__filename);
|
|
7939
7964
|
INACTIVITY_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
7940
7965
|
UI_PASSWORD = process.env.BEASTMODE_UI_PASSWORD || "";
|
|
7941
7966
|
ALLOW_NO_AUTH = /^(1|true|yes)$/i.test(process.env.BEASTMODE_ALLOW_NO_AUTH || "");
|
|
@@ -8046,27 +8071,27 @@ var init_server = __esm({
|
|
|
8046
8071
|
|
|
8047
8072
|
// src/cli/commands/board.ts
|
|
8048
8073
|
import { Command } from "commander";
|
|
8049
|
-
import { resolve as resolve5, join as
|
|
8050
|
-
import { existsSync as existsSync18, readFileSync as
|
|
8074
|
+
import { resolve as resolve5, join as join17 } from "path";
|
|
8075
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14 } from "fs";
|
|
8051
8076
|
import { execSync as execSync4 } from "child_process";
|
|
8052
8077
|
function findFactoryDir(startDir) {
|
|
8053
8078
|
let dir = startDir || process.cwd();
|
|
8054
8079
|
const root = resolve5("/");
|
|
8055
8080
|
while (dir !== root) {
|
|
8056
|
-
if (existsSync18(
|
|
8081
|
+
if (existsSync18(join17(dir, ".beastmode", "factory.json"))) {
|
|
8057
8082
|
return dir;
|
|
8058
8083
|
}
|
|
8059
8084
|
const parent = resolve5(dir, "..");
|
|
8060
8085
|
if (parent === dir) break;
|
|
8061
8086
|
dir = parent;
|
|
8062
8087
|
}
|
|
8063
|
-
if (existsSync18(
|
|
8088
|
+
if (existsSync18(join17(dir, ".beastmode", "factory.json"))) {
|
|
8064
8089
|
return dir;
|
|
8065
8090
|
}
|
|
8066
8091
|
return null;
|
|
8067
8092
|
}
|
|
8068
8093
|
function inferProjectName(factoryDir) {
|
|
8069
|
-
const projectsDir =
|
|
8094
|
+
const projectsDir = join17(factoryDir, ".beastmode", "projects");
|
|
8070
8095
|
const records = listProjectRecords(projectsDir);
|
|
8071
8096
|
if (records.length === 1) return records[0].name;
|
|
8072
8097
|
return null;
|
|
@@ -8145,11 +8170,11 @@ async function runBoard(opts) {
|
|
|
8145
8170
|
let factoryDir = findFactoryDir();
|
|
8146
8171
|
if (!factoryDir) {
|
|
8147
8172
|
factoryDir = process.cwd();
|
|
8148
|
-
const bmDir =
|
|
8173
|
+
const bmDir = join17(factoryDir, ".beastmode");
|
|
8149
8174
|
if (!existsSync18(bmDir)) {
|
|
8150
8175
|
mkdirSync13(bmDir, { recursive: true });
|
|
8151
8176
|
}
|
|
8152
|
-
const factoryJsonPath2 =
|
|
8177
|
+
const factoryJsonPath2 = join17(bmDir, "factory.json");
|
|
8153
8178
|
if (!existsSync18(factoryJsonPath2)) {
|
|
8154
8179
|
writeFileSync14(
|
|
8155
8180
|
factoryJsonPath2,
|
|
@@ -8159,8 +8184,8 @@ async function runBoard(opts) {
|
|
|
8159
8184
|
info("No factory found \u2014 created minimal stub at .beastmode/factory.json");
|
|
8160
8185
|
}
|
|
8161
8186
|
}
|
|
8162
|
-
const factoryJsonPath =
|
|
8163
|
-
const factoryJson = JSON.parse(
|
|
8187
|
+
const factoryJsonPath = join17(factoryDir, ".beastmode", "factory.json");
|
|
8188
|
+
const factoryJson = JSON.parse(readFileSync16(factoryJsonPath, "utf-8"));
|
|
8164
8189
|
const factoryName = factoryJson.factory_name || "BeastMode Factory";
|
|
8165
8190
|
header(`BeastMode Board \u2014 ${factoryName}`);
|
|
8166
8191
|
const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
@@ -8233,26 +8258,26 @@ __export(sync_claude_creds_exports, {
|
|
|
8233
8258
|
});
|
|
8234
8259
|
import { Command as Command2 } from "commander";
|
|
8235
8260
|
import { execSync as execSync5, spawnSync as spawnSync2 } from "child_process";
|
|
8236
|
-
import { writeFileSync as writeFileSync15, readFileSync as
|
|
8237
|
-
import { join as
|
|
8261
|
+
import { writeFileSync as writeFileSync15, readFileSync as readFileSync17, chmodSync, mkdirSync as mkdirSync14, existsSync as existsSync19, unlinkSync as unlinkSync5 } from "fs";
|
|
8262
|
+
import { join as join18 } from "path";
|
|
8238
8263
|
import { homedir as homedir2, platform } from "os";
|
|
8239
8264
|
function systemdUserDir() {
|
|
8240
|
-
return
|
|
8265
|
+
return join18(homedir2(), ".config", "systemd", "user");
|
|
8241
8266
|
}
|
|
8242
8267
|
function systemdServicePath() {
|
|
8243
|
-
return
|
|
8268
|
+
return join18(systemdUserDir(), `${SYSTEMD_UNIT_NAME}.service`);
|
|
8244
8269
|
}
|
|
8245
8270
|
function systemdTimerPath() {
|
|
8246
|
-
return
|
|
8271
|
+
return join18(systemdUserDir(), `${SYSTEMD_UNIT_NAME}.timer`);
|
|
8247
8272
|
}
|
|
8248
8273
|
function linuxCredsPath() {
|
|
8249
|
-
return
|
|
8274
|
+
return join18(homedir2(), ".claude", ".credentials.json");
|
|
8250
8275
|
}
|
|
8251
8276
|
function plistPath() {
|
|
8252
|
-
return
|
|
8277
|
+
return join18(homedir2(), "Library", "LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`);
|
|
8253
8278
|
}
|
|
8254
8279
|
function agentLogPath() {
|
|
8255
|
-
return
|
|
8280
|
+
return join18(homedir2(), ".beastmode", "logs", "sync-claude-creds.log");
|
|
8256
8281
|
}
|
|
8257
8282
|
function readKeychainTokenSafe() {
|
|
8258
8283
|
try {
|
|
@@ -8284,9 +8309,9 @@ function writeCredentialsFile(rawJson) {
|
|
|
8284
8309
|
if (!oauth?.accessToken) {
|
|
8285
8310
|
throw new Error("Keychain entry missing claudeAiOauth.accessToken. Fix: run `claude login` again to reset the credential.");
|
|
8286
8311
|
}
|
|
8287
|
-
const claudeDir =
|
|
8312
|
+
const claudeDir = join18(homedir2(), ".claude");
|
|
8288
8313
|
if (!existsSync19(claudeDir)) mkdirSync14(claudeDir, { recursive: true });
|
|
8289
|
-
const credsPath =
|
|
8314
|
+
const credsPath = join18(claudeDir, ".credentials.json");
|
|
8290
8315
|
writeFileSync15(credsPath, rawJson + "\n", "utf-8");
|
|
8291
8316
|
chmodSync(credsPath, 384);
|
|
8292
8317
|
if (oauth.expiresAt) {
|
|
@@ -8300,7 +8325,7 @@ function writeCredentialsFile(rawJson) {
|
|
|
8300
8325
|
return credsPath;
|
|
8301
8326
|
}
|
|
8302
8327
|
function urgencyMarkerHostPath(factoryDir) {
|
|
8303
|
-
return
|
|
8328
|
+
return join18(factoryDir, "daemon", "logs", ".cred-refresh-needed");
|
|
8304
8329
|
}
|
|
8305
8330
|
function removeUrgencyMarker(factoryDir) {
|
|
8306
8331
|
if (!factoryDir) return;
|
|
@@ -8314,7 +8339,7 @@ function buildPlist(intervalSeconds, factoryDir) {
|
|
|
8314
8339
|
const nodePath = process.execPath;
|
|
8315
8340
|
const cliEntry = process.argv[1];
|
|
8316
8341
|
const logPath = agentLogPath();
|
|
8317
|
-
const keychainPath =
|
|
8342
|
+
const keychainPath = join18(homedir2(), "Library", "Keychains", "login.keychain-db");
|
|
8318
8343
|
const watchPaths = [keychainPath];
|
|
8319
8344
|
if (factoryDir) {
|
|
8320
8345
|
watchPaths.push(urgencyMarkerHostPath(factoryDir));
|
|
@@ -8354,8 +8379,8 @@ function installAgent(intervalSeconds, {
|
|
|
8354
8379
|
} = {}) {
|
|
8355
8380
|
const plist = plistPath();
|
|
8356
8381
|
const logPath = agentLogPath();
|
|
8357
|
-
mkdirSync14(
|
|
8358
|
-
mkdirSync14(
|
|
8382
|
+
mkdirSync14(join18(homedir2(), "Library", "LaunchAgents"), { recursive: true });
|
|
8383
|
+
mkdirSync14(join18(homedir2(), ".beastmode", "logs"), { recursive: true });
|
|
8359
8384
|
const uid = process.getuid?.();
|
|
8360
8385
|
if (existsSync19(plist)) {
|
|
8361
8386
|
spawnSync2("launchctl", ["bootout", `gui/${uid}`, plist], { stdio: "pipe" });
|
|
@@ -8451,7 +8476,7 @@ function syncClaudeCredsLinux() {
|
|
|
8451
8476
|
}
|
|
8452
8477
|
let parsed;
|
|
8453
8478
|
try {
|
|
8454
|
-
const raw =
|
|
8479
|
+
const raw = readFileSync17(credsPath, "utf-8");
|
|
8455
8480
|
parsed = JSON.parse(raw);
|
|
8456
8481
|
} catch {
|
|
8457
8482
|
return {
|
|
@@ -8480,7 +8505,7 @@ function syncClaudeCredsLinux() {
|
|
|
8480
8505
|
);
|
|
8481
8506
|
}
|
|
8482
8507
|
try {
|
|
8483
|
-
const raw2 =
|
|
8508
|
+
const raw2 = readFileSync17(credsPath, "utf-8");
|
|
8484
8509
|
const parsed2 = JSON.parse(raw2);
|
|
8485
8510
|
if (parsed2.claudeAiOauth?.expiresAt) {
|
|
8486
8511
|
const newMinutes = (parsed2.claudeAiOauth.expiresAt - Date.now()) / 6e4;
|
|
@@ -8534,10 +8559,10 @@ function installAgentLinux(intervalSeconds, { throwOnError = false } = {}) {
|
|
|
8534
8559
|
process.exit(1);
|
|
8535
8560
|
}
|
|
8536
8561
|
const unitDir = systemdUserDir();
|
|
8537
|
-
const logDir =
|
|
8562
|
+
const logDir = join18(homedir2(), ".beastmode", "logs");
|
|
8538
8563
|
mkdirSync14(unitDir, { recursive: true });
|
|
8539
8564
|
mkdirSync14(logDir, { recursive: true });
|
|
8540
|
-
const logPath =
|
|
8565
|
+
const logPath = join18(logDir, "sync-claude-creds.log");
|
|
8541
8566
|
const nodePath = process.execPath;
|
|
8542
8567
|
const cliEntry = process.argv[1];
|
|
8543
8568
|
writeFileSync15(
|
|
@@ -8782,26 +8807,26 @@ var init_sync_claude_creds = __esm({
|
|
|
8782
8807
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8783
8808
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8784
8809
|
import { z as z2 } from "zod";
|
|
8785
|
-
import { readFileSync as
|
|
8786
|
-
import { join as
|
|
8810
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync25, existsSync as existsSync32, readdirSync as readdirSync12 } from "fs";
|
|
8811
|
+
import { join as join31, resolve as resolve18, basename as basename7 } from "path";
|
|
8787
8812
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8788
8813
|
function readJsonFile2(filePath) {
|
|
8789
8814
|
if (!existsSync32(filePath)) return null;
|
|
8790
8815
|
try {
|
|
8791
|
-
return JSON.parse(
|
|
8816
|
+
return JSON.parse(readFileSync30(filePath, "utf-8"));
|
|
8792
8817
|
} catch {
|
|
8793
8818
|
return null;
|
|
8794
8819
|
}
|
|
8795
8820
|
}
|
|
8796
8821
|
function getFactoryPath() {
|
|
8797
8822
|
const envPath = process.env.BEASTMODE_FACTORY_PATH;
|
|
8798
|
-
if (envPath && existsSync32(
|
|
8823
|
+
if (envPath && existsSync32(join31(envPath, ".beastmode", "factory.json"))) {
|
|
8799
8824
|
return envPath;
|
|
8800
8825
|
}
|
|
8801
8826
|
let dir = process.cwd();
|
|
8802
8827
|
const root = resolve18("/");
|
|
8803
8828
|
while (dir !== root) {
|
|
8804
|
-
if (existsSync32(
|
|
8829
|
+
if (existsSync32(join31(dir, ".beastmode", "factory.json"))) {
|
|
8805
8830
|
return dir;
|
|
8806
8831
|
}
|
|
8807
8832
|
const parent = resolve18(dir, "..");
|
|
@@ -8813,17 +8838,17 @@ function getFactoryPath() {
|
|
|
8813
8838
|
);
|
|
8814
8839
|
}
|
|
8815
8840
|
function readFactoryStatus(factoryDir) {
|
|
8816
|
-
const bmDir =
|
|
8841
|
+
const bmDir = join31(factoryDir, ".beastmode");
|
|
8817
8842
|
const factoryIdentity = FactoryIdentitySchema.parse(
|
|
8818
|
-
JSON.parse(
|
|
8843
|
+
JSON.parse(readFileSync30(join31(bmDir, "factory.json"), "utf-8"))
|
|
8819
8844
|
);
|
|
8820
|
-
const projectsDir =
|
|
8845
|
+
const projectsDir = join31(bmDir, "projects");
|
|
8821
8846
|
const projectCount = listProjectRecords(projectsDir).length;
|
|
8822
|
-
const lockPath =
|
|
8847
|
+
const lockPath = join31(bmDir, "extensions.lock");
|
|
8823
8848
|
let pluginNames = [];
|
|
8824
8849
|
if (existsSync32(lockPath)) {
|
|
8825
8850
|
try {
|
|
8826
|
-
const lock = JSON.parse(
|
|
8851
|
+
const lock = JSON.parse(readFileSync30(lockPath, "utf-8"));
|
|
8827
8852
|
pluginNames = Object.keys(lock.plugins || {});
|
|
8828
8853
|
} catch {
|
|
8829
8854
|
}
|
|
@@ -8843,23 +8868,23 @@ function readFactoryStatus(factoryDir) {
|
|
|
8843
8868
|
skillCount = listSkills(factoryDir).length;
|
|
8844
8869
|
} catch {
|
|
8845
8870
|
}
|
|
8846
|
-
const runsDir =
|
|
8871
|
+
const runsDir = join31(factoryDir, "runs");
|
|
8847
8872
|
let runDirs = [];
|
|
8848
8873
|
if (existsSync32(runsDir)) {
|
|
8849
8874
|
runDirs = readdirSync12(runsDir).filter((d) => {
|
|
8850
8875
|
try {
|
|
8851
|
-
return readdirSync12(
|
|
8876
|
+
return readdirSync12(join31(runsDir, d)).length > 0;
|
|
8852
8877
|
} catch {
|
|
8853
8878
|
return false;
|
|
8854
8879
|
}
|
|
8855
8880
|
}).sort();
|
|
8856
8881
|
}
|
|
8857
|
-
const pidFile =
|
|
8882
|
+
const pidFile = join31(bmDir, "daemon.pid");
|
|
8858
8883
|
let daemonPid = null;
|
|
8859
8884
|
let pidAlive = false;
|
|
8860
8885
|
if (existsSync32(pidFile)) {
|
|
8861
8886
|
try {
|
|
8862
|
-
daemonPid = parseInt(
|
|
8887
|
+
daemonPid = parseInt(readFileSync30(pidFile, "utf-8").trim(), 10);
|
|
8863
8888
|
process.kill(daemonPid, 0);
|
|
8864
8889
|
pidAlive = true;
|
|
8865
8890
|
} catch {
|
|
@@ -8880,17 +8905,17 @@ function readFactoryStatus(factoryDir) {
|
|
|
8880
8905
|
return collectStatus(input);
|
|
8881
8906
|
}
|
|
8882
8907
|
function readBoardItems(factoryDir) {
|
|
8883
|
-
const filePath =
|
|
8908
|
+
const filePath = join31(factoryDir, ".beastmode", "board.json");
|
|
8884
8909
|
if (!existsSync32(filePath)) return [];
|
|
8885
8910
|
try {
|
|
8886
|
-
const raw = JSON.parse(
|
|
8911
|
+
const raw = JSON.parse(readFileSync30(filePath, "utf-8"));
|
|
8887
8912
|
return Array.isArray(raw.items) ? raw.items : [];
|
|
8888
8913
|
} catch {
|
|
8889
8914
|
return [];
|
|
8890
8915
|
}
|
|
8891
8916
|
}
|
|
8892
8917
|
function writeBoardItems(factoryDir, items) {
|
|
8893
|
-
const filePath =
|
|
8918
|
+
const filePath = join31(factoryDir, ".beastmode", "board.json");
|
|
8894
8919
|
writeFileSync25(filePath, JSON.stringify({ items }, null, 2) + "\n", "utf-8");
|
|
8895
8920
|
}
|
|
8896
8921
|
function createMcpServer() {
|
|
@@ -8914,8 +8939,8 @@ function createMcpServer() {
|
|
|
8914
8939
|
{ key_path: z2.string().describe("Dot-notation key path") },
|
|
8915
8940
|
async ({ key_path }) => {
|
|
8916
8941
|
const factoryDir = getFactoryPath();
|
|
8917
|
-
const configPath =
|
|
8918
|
-
const config = existsSync32(configPath) ? JSON.parse(
|
|
8942
|
+
const configPath = join31(factoryDir, ".beastmode", "config.json");
|
|
8943
|
+
const config = existsSync32(configPath) ? JSON.parse(readFileSync30(configPath, "utf-8")) : generateDefaults();
|
|
8919
8944
|
try {
|
|
8920
8945
|
const value = configGet(config, key_path);
|
|
8921
8946
|
return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
|
|
@@ -8933,8 +8958,8 @@ function createMcpServer() {
|
|
|
8933
8958
|
},
|
|
8934
8959
|
async ({ key_path, value }) => {
|
|
8935
8960
|
const factoryDir = getFactoryPath();
|
|
8936
|
-
const configPath =
|
|
8937
|
-
const config = existsSync32(configPath) ? JSON.parse(
|
|
8961
|
+
const configPath = join31(factoryDir, ".beastmode", "config.json");
|
|
8962
|
+
const config = existsSync32(configPath) ? JSON.parse(readFileSync30(configPath, "utf-8")) : generateDefaults();
|
|
8938
8963
|
const coerced = coerceValue(value);
|
|
8939
8964
|
const updated = configSet(config, key_path, coerced);
|
|
8940
8965
|
writeFileSync25(configPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
@@ -8971,14 +8996,14 @@ function createMcpServer() {
|
|
|
8971
8996
|
{},
|
|
8972
8997
|
async () => {
|
|
8973
8998
|
const factoryDir = getFactoryPath();
|
|
8974
|
-
const runsDir =
|
|
8999
|
+
const runsDir = join31(factoryDir, "runs");
|
|
8975
9000
|
if (!existsSync32(runsDir)) {
|
|
8976
9001
|
return { content: [{ type: "text", text: "No runs directory found." }] };
|
|
8977
9002
|
}
|
|
8978
9003
|
const runDirs = readdirSync12(runsDir).sort().reverse();
|
|
8979
9004
|
const activeRuns = [];
|
|
8980
9005
|
for (const id of runDirs.slice(0, 10)) {
|
|
8981
|
-
const cp = readJsonFile2(
|
|
9006
|
+
const cp = readJsonFile2(join31(runsDir, id, "checkpoint.json"));
|
|
8982
9007
|
if (cp) {
|
|
8983
9008
|
activeRuns.push({ id, checkpoint: cp });
|
|
8984
9009
|
}
|
|
@@ -8992,17 +9017,17 @@ function createMcpServer() {
|
|
|
8992
9017
|
{ run_id: z2.string().describe("Run ID (directory name)") },
|
|
8993
9018
|
async ({ run_id }) => {
|
|
8994
9019
|
const factoryDir = getFactoryPath();
|
|
8995
|
-
const runDir =
|
|
9020
|
+
const runDir = join31(factoryDir, "runs", run_id);
|
|
8996
9021
|
if (!existsSync32(runDir)) {
|
|
8997
9022
|
return { content: [{ type: "text", text: `Run not found: ${run_id}` }], isError: true };
|
|
8998
9023
|
}
|
|
8999
|
-
const manifest = readJsonFile2(
|
|
9000
|
-
const checkpoint = readJsonFile2(
|
|
9001
|
-
const iterationsDir =
|
|
9024
|
+
const manifest = readJsonFile2(join31(runDir, "manifest.json"));
|
|
9025
|
+
const checkpoint = readJsonFile2(join31(runDir, "checkpoint.json"));
|
|
9026
|
+
const iterationsDir = join31(runDir, "iterations");
|
|
9002
9027
|
const iterations = [];
|
|
9003
9028
|
if (existsSync32(iterationsDir)) {
|
|
9004
9029
|
for (const dir of readdirSync12(iterationsDir).sort()) {
|
|
9005
|
-
const satisfaction = readJsonFile2(
|
|
9030
|
+
const satisfaction = readJsonFile2(join31(iterationsDir, dir, "satisfaction.json"));
|
|
9006
9031
|
iterations.push({ number: parseInt(dir, 10), satisfaction });
|
|
9007
9032
|
}
|
|
9008
9033
|
}
|
|
@@ -9048,12 +9073,12 @@ function createMcpServer() {
|
|
|
9048
9073
|
{},
|
|
9049
9074
|
async () => {
|
|
9050
9075
|
const factoryDir = getFactoryPath();
|
|
9051
|
-
const bmDir =
|
|
9076
|
+
const bmDir = join31(factoryDir, ".beastmode");
|
|
9052
9077
|
let plugins = {};
|
|
9053
|
-
const lockPath =
|
|
9078
|
+
const lockPath = join31(bmDir, "extensions.lock");
|
|
9054
9079
|
if (existsSync32(lockPath)) {
|
|
9055
9080
|
try {
|
|
9056
|
-
const lock = JSON.parse(
|
|
9081
|
+
const lock = JSON.parse(readFileSync30(lockPath, "utf-8"));
|
|
9057
9082
|
plugins = lock.plugins || {};
|
|
9058
9083
|
} catch {
|
|
9059
9084
|
}
|
|
@@ -9087,7 +9112,7 @@ function createMcpServer() {
|
|
|
9087
9112
|
{},
|
|
9088
9113
|
async () => {
|
|
9089
9114
|
const factoryDir = getFactoryPath();
|
|
9090
|
-
const projectsDir =
|
|
9115
|
+
const projectsDir = join31(factoryDir, ".beastmode", "projects");
|
|
9091
9116
|
const projects = listProjectRecords(projectsDir);
|
|
9092
9117
|
return { content: [{ type: "text", text: JSON.stringify(projects, null, 2) }] };
|
|
9093
9118
|
}
|
|
@@ -9104,7 +9129,7 @@ function createMcpServer() {
|
|
|
9104
9129
|
}
|
|
9105
9130
|
const projectName = basename7(resolvedPath);
|
|
9106
9131
|
const stack = detectStack(resolvedPath);
|
|
9107
|
-
const projectsDir =
|
|
9132
|
+
const projectsDir = join31(factoryDir, ".beastmode", "projects");
|
|
9108
9133
|
let verifyPort = 3001;
|
|
9109
9134
|
for (const existing of listProjectRecords(projectsDir)) {
|
|
9110
9135
|
const port = existing.deploy?.verify_port;
|
|
@@ -9220,16 +9245,16 @@ init_engine();
|
|
|
9220
9245
|
init_file_writer();
|
|
9221
9246
|
import { Command as Command3 } from "commander";
|
|
9222
9247
|
import inquirer from "inquirer";
|
|
9223
|
-
import { resolve as resolve6, basename as basename6, join as
|
|
9224
|
-
import { existsSync as existsSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, readFileSync as
|
|
9248
|
+
import { resolve as resolve6, basename as basename6, join as join19 } from "path";
|
|
9249
|
+
import { existsSync as existsSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync18 } from "fs";
|
|
9225
9250
|
|
|
9226
9251
|
// src/cli/utils/docker.ts
|
|
9227
9252
|
import { existsSync as existsSync10 } from "fs";
|
|
9228
|
-
import { join as
|
|
9253
|
+
import { join as join10 } from "path";
|
|
9229
9254
|
import { execSync } from "child_process";
|
|
9230
9255
|
var GHCR_IMAGE_PREFIX = "ghcr.io/develeap/beastmode";
|
|
9231
9256
|
function findComposeFile(dir) {
|
|
9232
|
-
const path =
|
|
9257
|
+
const path = join10(dir, "docker-compose.yml");
|
|
9233
9258
|
return existsSync10(path) ? path : null;
|
|
9234
9259
|
}
|
|
9235
9260
|
function requireComposeFile(dir) {
|
|
@@ -9293,7 +9318,7 @@ function loginToGhcr(token) {
|
|
|
9293
9318
|
}
|
|
9294
9319
|
}
|
|
9295
9320
|
function seedConfigFromImage(targetDir, tag) {
|
|
9296
|
-
const configDir =
|
|
9321
|
+
const configDir = join10(targetDir, "config");
|
|
9297
9322
|
const containerName = "bm-config-seed";
|
|
9298
9323
|
try {
|
|
9299
9324
|
try {
|
|
@@ -9473,7 +9498,7 @@ function collect(val, acc) {
|
|
|
9473
9498
|
return acc;
|
|
9474
9499
|
}
|
|
9475
9500
|
function readSecretFile(filePath) {
|
|
9476
|
-
return
|
|
9501
|
+
return readFileSync18(resolve6(filePath), "utf-8").trim();
|
|
9477
9502
|
}
|
|
9478
9503
|
function isSourceRepo(dir) {
|
|
9479
9504
|
return existsSync20(resolve6(dir, "daemon")) && existsSync20(resolve6(dir, "board")) && existsSync20(resolve6(dir, "cli"));
|
|
@@ -9519,8 +9544,8 @@ async function runInit(name, opts) {
|
|
|
9519
9544
|
throw new Error(`Factory already exists at ./${factoryName}. Use 'beastmode config' to modify.`);
|
|
9520
9545
|
}
|
|
9521
9546
|
if (opts.from) {
|
|
9522
|
-
const { readFileSync:
|
|
9523
|
-
const templateContent =
|
|
9547
|
+
const { readFileSync: readFileSync37 } = await import("fs");
|
|
9548
|
+
const templateContent = readFileSync37(resolve6(opts.from), "utf-8");
|
|
9524
9549
|
const { parseTemplate: parseTemplate2 } = await Promise.resolve().then(() => (init_template_importer(), template_importer_exports));
|
|
9525
9550
|
const template = parseTemplate2(templateContent);
|
|
9526
9551
|
info(`Importing from template: ${opts.from}`);
|
|
@@ -9878,7 +9903,7 @@ async function runImageModeInit(name, opts) {
|
|
|
9878
9903
|
success(`Project path: ${projectPath}`);
|
|
9879
9904
|
} else if (opts.yes) {
|
|
9880
9905
|
const cwd = resolve6(".");
|
|
9881
|
-
if (!existsSync20(
|
|
9906
|
+
if (!existsSync20(join19(cwd, ".git"))) {
|
|
9882
9907
|
error(
|
|
9883
9908
|
"--project is required in non-interactive mode (--yes) unless you run from inside a git repository. Pass --project <path> or cd into your project clone first."
|
|
9884
9909
|
);
|
|
@@ -9896,7 +9921,7 @@ async function runImageModeInit(name, opts) {
|
|
|
9896
9921
|
validate: (input) => {
|
|
9897
9922
|
const p = resolve6(input);
|
|
9898
9923
|
if (!existsSync20(p)) return `Directory not found: ${p}`;
|
|
9899
|
-
if (!existsSync20(
|
|
9924
|
+
if (!existsSync20(join19(p, ".git"))) {
|
|
9900
9925
|
return `Not a git repo: ${p} (run 'git init' first, or pick a different path)`;
|
|
9901
9926
|
}
|
|
9902
9927
|
return true;
|
|
@@ -9937,7 +9962,7 @@ async function runImageModeInit(name, opts) {
|
|
|
9937
9962
|
step(3, totalSteps, "Generate");
|
|
9938
9963
|
mkdirSync15(targetDir, { recursive: true });
|
|
9939
9964
|
const composeContent = generateComposeYaml("latest");
|
|
9940
|
-
writeFileSync16(
|
|
9965
|
+
writeFileSync16(join19(targetDir, "docker-compose.yml"), composeContent, "utf-8");
|
|
9941
9966
|
success("docker-compose.yml");
|
|
9942
9967
|
const envLines = [
|
|
9943
9968
|
"# BeastMode environment \u2014 DO NOT COMMIT",
|
|
@@ -9977,10 +10002,10 @@ async function runImageModeInit(name, opts) {
|
|
|
9977
10002
|
"# PROJECT_REPO=owner/repo",
|
|
9978
10003
|
""
|
|
9979
10004
|
];
|
|
9980
|
-
writeFileSync16(
|
|
10005
|
+
writeFileSync16(join19(targetDir, ".env"), envLines.join("\n"), "utf-8");
|
|
9981
10006
|
success(`.env (PROJECT_DIR=${projectPath}, two-PAT model)`);
|
|
9982
10007
|
for (const dir of ["data", "runs", "daemon/logs", ".beastmode", "config"]) {
|
|
9983
|
-
mkdirSync15(
|
|
10008
|
+
mkdirSync15(join19(targetDir, dir), { recursive: true });
|
|
9984
10009
|
}
|
|
9985
10010
|
step(4, totalSteps, "Pull Images");
|
|
9986
10011
|
try {
|
|
@@ -10069,20 +10094,20 @@ async function runImageModeInit(name, opts) {
|
|
|
10069
10094
|
init_export_adapter();
|
|
10070
10095
|
init_display();
|
|
10071
10096
|
import { Command as Command4 } from "commander";
|
|
10072
|
-
import { readFileSync as
|
|
10073
|
-
import { resolve as resolve7, join as
|
|
10097
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync17, existsSync as existsSync21, readdirSync as readdirSync9 } from "fs";
|
|
10098
|
+
import { resolve as resolve7, join as join20 } from "path";
|
|
10074
10099
|
function exportFactory(factoryDir, outputPath) {
|
|
10075
|
-
const bmDir =
|
|
10076
|
-
const configPath =
|
|
10100
|
+
const bmDir = join20(factoryDir, ".beastmode");
|
|
10101
|
+
const configPath = join20(bmDir, "config.json");
|
|
10077
10102
|
if (!existsSync21(configPath)) {
|
|
10078
10103
|
throw new Error(`No factory found at ${factoryDir}`);
|
|
10079
10104
|
}
|
|
10080
|
-
const config = JSON.parse(
|
|
10081
|
-
const identity = JSON.parse(
|
|
10105
|
+
const config = JSON.parse(readFileSync19(configPath, "utf-8"));
|
|
10106
|
+
const identity = JSON.parse(readFileSync19(join20(bmDir, "factory.json"), "utf-8"));
|
|
10082
10107
|
let plugins = [];
|
|
10083
|
-
const lockPath =
|
|
10108
|
+
const lockPath = join20(bmDir, "extensions.lock");
|
|
10084
10109
|
if (existsSync21(lockPath)) {
|
|
10085
|
-
const lock = JSON.parse(
|
|
10110
|
+
const lock = JSON.parse(readFileSync19(lockPath, "utf-8"));
|
|
10086
10111
|
plugins = Object.keys(lock.plugins || {});
|
|
10087
10112
|
}
|
|
10088
10113
|
const template = {
|
|
@@ -10113,19 +10138,19 @@ function createArtifactExportCommand(artifact) {
|
|
|
10113
10138
|
const runDir = findRunDir(opts.run);
|
|
10114
10139
|
let sourceContent;
|
|
10115
10140
|
if (artifact === "scenarios") {
|
|
10116
|
-
const scenariosDir =
|
|
10141
|
+
const scenariosDir = join20(runDir, "scenarios");
|
|
10117
10142
|
if (!existsSync21(scenariosDir)) {
|
|
10118
10143
|
throw new Error(`No scenarios directory found in run ${opts.run}`);
|
|
10119
10144
|
}
|
|
10120
10145
|
const files = readdirSync9(scenariosDir).filter((f) => f.endsWith(".md")).sort();
|
|
10121
|
-
sourceContent = files.map((f) =>
|
|
10146
|
+
sourceContent = files.map((f) => readFileSync19(join20(scenariosDir, f), "utf-8")).join("\n\n---\n\n");
|
|
10122
10147
|
} else {
|
|
10123
10148
|
const artifactFile = artifact === "nlspec" ? "nlspec.md" : "plan.md";
|
|
10124
|
-
const artifactPath =
|
|
10149
|
+
const artifactPath = join20(runDir, artifactFile);
|
|
10125
10150
|
if (!existsSync21(artifactPath)) {
|
|
10126
10151
|
throw new Error(`${artifactFile} not found in run ${opts.run}`);
|
|
10127
10152
|
}
|
|
10128
|
-
sourceContent =
|
|
10153
|
+
sourceContent = readFileSync19(artifactPath, "utf-8");
|
|
10129
10154
|
}
|
|
10130
10155
|
const result = runExportAdapter(opts.adapter, sourceContent);
|
|
10131
10156
|
if (opts.output) {
|
|
@@ -10353,15 +10378,15 @@ init_presets();
|
|
|
10353
10378
|
init_display();
|
|
10354
10379
|
import { Command as Command8 } from "commander";
|
|
10355
10380
|
import { resolve as resolve11 } from "path";
|
|
10356
|
-
import { readFileSync as
|
|
10357
|
-
import { join as
|
|
10381
|
+
import { readFileSync as readFileSync20, existsSync as existsSync22 } from "fs";
|
|
10382
|
+
import { join as join21 } from "path";
|
|
10358
10383
|
function listPluginsAction(factoryDir) {
|
|
10359
|
-
const lockPath =
|
|
10384
|
+
const lockPath = join21(factoryDir, ".beastmode", "extensions.lock");
|
|
10360
10385
|
if (!existsSync22(lockPath)) {
|
|
10361
10386
|
console.log(" No plugins installed (extensions.lock not found).");
|
|
10362
10387
|
return;
|
|
10363
10388
|
}
|
|
10364
|
-
const lock = JSON.parse(
|
|
10389
|
+
const lock = JSON.parse(readFileSync20(lockPath, "utf-8"));
|
|
10365
10390
|
const plugins = lock.plugins || {};
|
|
10366
10391
|
const names = Object.keys(plugins);
|
|
10367
10392
|
if (names.length === 0) {
|
|
@@ -10460,8 +10485,8 @@ var listCommand = new Command8("list").description("List installed extensions an
|
|
|
10460
10485
|
init_import_adapter();
|
|
10461
10486
|
init_display();
|
|
10462
10487
|
import { Command as Command9 } from "commander";
|
|
10463
|
-
import { readFileSync as
|
|
10464
|
-
import { resolve as resolve12, join as
|
|
10488
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16, existsSync as existsSync23 } from "fs";
|
|
10489
|
+
import { resolve as resolve12, join as join22 } from "path";
|
|
10465
10490
|
var VALID_ARTIFACTS = ["nlspec", "plan", "scenarios"];
|
|
10466
10491
|
function createArtifactCommand(artifact) {
|
|
10467
10492
|
return new Command9(artifact).description(`Import external document as ${artifact}`).requiredOption("--from <path>", "Path to source file").requiredOption("--adapter <id>", "Adapter ID (e.g., generic:prd, bmad:brainstorm)").option("--output <path>", "Output path (default: stdout)").action((opts) => {
|
|
@@ -10470,7 +10495,7 @@ function createArtifactCommand(artifact) {
|
|
|
10470
10495
|
if (!existsSync23(sourcePath)) {
|
|
10471
10496
|
throw new Error(`Source file not found: ${sourcePath}`);
|
|
10472
10497
|
}
|
|
10473
|
-
const sourceContent =
|
|
10498
|
+
const sourceContent = readFileSync21(sourcePath, "utf-8");
|
|
10474
10499
|
const result = runImportAdapter(opts.adapter, sourceContent);
|
|
10475
10500
|
if (opts.output) {
|
|
10476
10501
|
const outputPath = resolve12(opts.output);
|
|
@@ -10478,7 +10503,7 @@ function createArtifactCommand(artifact) {
|
|
|
10478
10503
|
const scenarios = JSON.parse(result);
|
|
10479
10504
|
mkdirSync16(outputPath, { recursive: true });
|
|
10480
10505
|
for (const scenario of scenarios) {
|
|
10481
|
-
const filePath =
|
|
10506
|
+
const filePath = join22(outputPath, `${scenario.name}.md`);
|
|
10482
10507
|
writeFileSync18(filePath, scenario.content, "utf-8");
|
|
10483
10508
|
success(` ${scenario.name}.md`);
|
|
10484
10509
|
}
|
|
@@ -10508,52 +10533,52 @@ init_engine();
|
|
|
10508
10533
|
init_schemas();
|
|
10509
10534
|
init_display();
|
|
10510
10535
|
import { Command as Command10 } from "commander";
|
|
10511
|
-
import { existsSync as existsSync24, readFileSync as
|
|
10536
|
+
import { existsSync as existsSync24, readFileSync as readFileSync22, readdirSync as readdirSync10 } from "fs";
|
|
10512
10537
|
import { execSync as execSync6 } from "child_process";
|
|
10513
|
-
import { join as
|
|
10538
|
+
import { join as join23, resolve as resolve13 } from "path";
|
|
10514
10539
|
function statusAction(factoryDir, opts) {
|
|
10515
|
-
const bmDir =
|
|
10540
|
+
const bmDir = join23(factoryDir, ".beastmode");
|
|
10516
10541
|
if (!existsSync24(bmDir)) {
|
|
10517
10542
|
throw new Error(`No factory found at ${factoryDir}`);
|
|
10518
10543
|
}
|
|
10519
|
-
const factoryJsonPath =
|
|
10520
|
-
const rawIdentity = JSON.parse(
|
|
10544
|
+
const factoryJsonPath = join23(bmDir, "factory.json");
|
|
10545
|
+
const rawIdentity = JSON.parse(readFileSync22(factoryJsonPath, "utf-8"));
|
|
10521
10546
|
const factoryIdentity = FactoryIdentitySchema.parse(rawIdentity);
|
|
10522
|
-
const projectsDir =
|
|
10547
|
+
const projectsDir = join23(bmDir, "projects");
|
|
10523
10548
|
const projectCount = listProjectRecords(projectsDir).length;
|
|
10524
|
-
const pluginsDir =
|
|
10549
|
+
const pluginsDir = join23(bmDir, "plugins");
|
|
10525
10550
|
const pluginNames = existsSync24(pluginsDir) ? readdirSync10(pluginsDir) : [];
|
|
10526
|
-
const mcpPath =
|
|
10551
|
+
const mcpPath = join23(bmDir, "mcp-servers.json");
|
|
10527
10552
|
let mcpServers = {};
|
|
10528
10553
|
if (existsSync24(mcpPath)) {
|
|
10529
10554
|
try {
|
|
10530
|
-
const raw = JSON.parse(
|
|
10555
|
+
const raw = JSON.parse(readFileSync22(mcpPath, "utf-8"));
|
|
10531
10556
|
mcpServers = raw.servers || {};
|
|
10532
10557
|
} catch {
|
|
10533
10558
|
}
|
|
10534
10559
|
}
|
|
10535
|
-
const hooksPath =
|
|
10560
|
+
const hooksPath = join23(bmDir, "hooks.json");
|
|
10536
10561
|
let hooks = {};
|
|
10537
10562
|
if (existsSync24(hooksPath)) {
|
|
10538
10563
|
try {
|
|
10539
|
-
const raw = JSON.parse(
|
|
10564
|
+
const raw = JSON.parse(readFileSync22(hooksPath, "utf-8"));
|
|
10540
10565
|
hooks = raw.hooks || {};
|
|
10541
10566
|
} catch {
|
|
10542
10567
|
}
|
|
10543
10568
|
}
|
|
10544
|
-
const skillsDir =
|
|
10569
|
+
const skillsDir = join23(bmDir, "skills");
|
|
10545
10570
|
const skillCount = existsSync24(skillsDir) ? readdirSync10(skillsDir).length : 0;
|
|
10546
|
-
const runsDir =
|
|
10571
|
+
const runsDir = join23(factoryDir, "runs");
|
|
10547
10572
|
let runDirs = [];
|
|
10548
10573
|
if (existsSync24(runsDir)) {
|
|
10549
10574
|
runDirs = readdirSync10(runsDir).filter((d) => d.startsWith("run-")).sort();
|
|
10550
10575
|
}
|
|
10551
|
-
const pidPath =
|
|
10576
|
+
const pidPath = join23(bmDir, "daemon.pid");
|
|
10552
10577
|
let daemonPid = null;
|
|
10553
10578
|
let pidAlive = false;
|
|
10554
10579
|
if (existsSync24(pidPath)) {
|
|
10555
10580
|
try {
|
|
10556
|
-
daemonPid = parseInt(
|
|
10581
|
+
daemonPid = parseInt(readFileSync22(pidPath, "utf-8").trim(), 10);
|
|
10557
10582
|
process.kill(daemonPid, 0);
|
|
10558
10583
|
pidAlive = true;
|
|
10559
10584
|
} catch {
|
|
@@ -10627,18 +10652,18 @@ var statusCommand = new Command10("status").description("Show factory status ove
|
|
|
10627
10652
|
init_config_manager();
|
|
10628
10653
|
init_display();
|
|
10629
10654
|
import { Command as Command11 } from "commander";
|
|
10630
|
-
import { existsSync as existsSync25, readFileSync as
|
|
10631
|
-
import { join as
|
|
10655
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23, writeFileSync as writeFileSync19 } from "fs";
|
|
10656
|
+
import { join as join24, resolve as resolve14 } from "path";
|
|
10632
10657
|
import { execSync as execSync7 } from "child_process";
|
|
10633
10658
|
function readConfig2(factoryDir) {
|
|
10634
|
-
const configPath =
|
|
10659
|
+
const configPath = join24(factoryDir, ".beastmode", "config.json");
|
|
10635
10660
|
if (!existsSync25(configPath)) {
|
|
10636
10661
|
throw new Error("No config.json found. Run beastmode init first.");
|
|
10637
10662
|
}
|
|
10638
|
-
return JSON.parse(
|
|
10663
|
+
return JSON.parse(readFileSync23(configPath, "utf-8"));
|
|
10639
10664
|
}
|
|
10640
10665
|
function writeConfig2(factoryDir, config) {
|
|
10641
|
-
const configPath =
|
|
10666
|
+
const configPath = join24(factoryDir, ".beastmode", "config.json");
|
|
10642
10667
|
writeFileSync19(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
10643
10668
|
}
|
|
10644
10669
|
function configGetAction(factoryDir, key) {
|
|
@@ -10682,7 +10707,7 @@ var configSetCmd = new Command11("set").description("Set a config value by dot-n
|
|
|
10682
10707
|
});
|
|
10683
10708
|
var configEditCmd = new Command11("edit").description("Open config.json in $EDITOR").action(() => {
|
|
10684
10709
|
const factoryDir = resolve14(".");
|
|
10685
|
-
const configPath =
|
|
10710
|
+
const configPath = join24(factoryDir, ".beastmode", "config.json");
|
|
10686
10711
|
if (!existsSync25(configPath)) {
|
|
10687
10712
|
error("No config.json found. Run beastmode init first.");
|
|
10688
10713
|
process.exit(1);
|
|
@@ -10719,8 +10744,8 @@ init_version();
|
|
|
10719
10744
|
init_plugin_resolver();
|
|
10720
10745
|
init_display();
|
|
10721
10746
|
import { Command as Command12 } from "commander";
|
|
10722
|
-
import { existsSync as existsSync26, readFileSync as
|
|
10723
|
-
import { join as
|
|
10747
|
+
import { existsSync as existsSync26, readFileSync as readFileSync24, readdirSync as readdirSync11 } from "fs";
|
|
10748
|
+
import { join as join25, resolve as resolve15 } from "path";
|
|
10724
10749
|
import { execSync as execSync8, spawnSync as spawnSync3 } from "child_process";
|
|
10725
10750
|
import { homedir as homedir3, platform as platform2 } from "os";
|
|
10726
10751
|
import * as http3 from "http";
|
|
@@ -10761,7 +10786,7 @@ async function checkClaudeAuth(key) {
|
|
|
10761
10786
|
});
|
|
10762
10787
|
}
|
|
10763
10788
|
if (claudeInstalled && platform2() === "darwin" && !key) {
|
|
10764
|
-
const credsPath =
|
|
10789
|
+
const credsPath = join25(homedir3(), ".claude", ".credentials.json");
|
|
10765
10790
|
if (!existsSync26(credsPath)) {
|
|
10766
10791
|
results.push({
|
|
10767
10792
|
label: "Claude creds (Docker)",
|
|
@@ -10864,16 +10889,16 @@ async function checkProjectGithubToken(env, factoryDir) {
|
|
|
10864
10889
|
}
|
|
10865
10890
|
let ownerRepo = null;
|
|
10866
10891
|
if (factoryDir) {
|
|
10867
|
-
const envPath =
|
|
10892
|
+
const envPath = join25(factoryDir, ".env");
|
|
10868
10893
|
if (existsSync26(envPath)) {
|
|
10869
10894
|
try {
|
|
10870
|
-
const content =
|
|
10895
|
+
const content = readFileSync24(envPath, "utf-8");
|
|
10871
10896
|
const dirLine = content.split("\n").find(
|
|
10872
10897
|
(l) => l.trim().startsWith("PROJECT_DIR=") && !l.trim().startsWith("#")
|
|
10873
10898
|
);
|
|
10874
10899
|
if (dirLine) {
|
|
10875
10900
|
const projectDir = dirLine.split("=").slice(1).join("=").trim();
|
|
10876
|
-
if (projectDir && existsSync26(
|
|
10901
|
+
if (projectDir && existsSync26(join25(projectDir, ".git"))) {
|
|
10877
10902
|
const remote = tryExec(
|
|
10878
10903
|
`git -C "${projectDir}" remote get-url origin 2>/dev/null`
|
|
10879
10904
|
);
|
|
@@ -11089,7 +11114,7 @@ function checkProjectDirEnv(factoryDir) {
|
|
|
11089
11114
|
detail: "no factory in scope"
|
|
11090
11115
|
};
|
|
11091
11116
|
}
|
|
11092
|
-
const envPath =
|
|
11117
|
+
const envPath = join25(factoryDir, ".env");
|
|
11093
11118
|
if (!existsSync26(envPath)) {
|
|
11094
11119
|
return {
|
|
11095
11120
|
label: "PROJECT_DIR (.env)",
|
|
@@ -11100,7 +11125,7 @@ function checkProjectDirEnv(factoryDir) {
|
|
|
11100
11125
|
}
|
|
11101
11126
|
let content;
|
|
11102
11127
|
try {
|
|
11103
|
-
content =
|
|
11128
|
+
content = readFileSync24(envPath, "utf-8");
|
|
11104
11129
|
} catch {
|
|
11105
11130
|
return {
|
|
11106
11131
|
label: "PROJECT_DIR (.env)",
|
|
@@ -11137,7 +11162,7 @@ function checkProjectDirEnv(factoryDir) {
|
|
|
11137
11162
|
fix: `Clone your project to ${value} or re-run 'beastmode init --project <path>'`
|
|
11138
11163
|
};
|
|
11139
11164
|
}
|
|
11140
|
-
if (!existsSync26(
|
|
11165
|
+
if (!existsSync26(join25(value, ".git"))) {
|
|
11141
11166
|
return {
|
|
11142
11167
|
label: "PROJECT_DIR (.env)",
|
|
11143
11168
|
status: "fail",
|
|
@@ -11168,7 +11193,7 @@ async function checkFactoryContainers(factoryDir) {
|
|
|
11168
11193
|
detail: "no factory in scope"
|
|
11169
11194
|
};
|
|
11170
11195
|
}
|
|
11171
|
-
const composePath =
|
|
11196
|
+
const composePath = join25(factoryDir, "docker-compose.yml");
|
|
11172
11197
|
if (!existsSync26(composePath)) {
|
|
11173
11198
|
return {
|
|
11174
11199
|
label: "Factory containers",
|
|
@@ -11451,8 +11476,8 @@ function checkProjectDirectory(factoryDir) {
|
|
|
11451
11476
|
fix: "Run beastmode init to create a factory"
|
|
11452
11477
|
};
|
|
11453
11478
|
}
|
|
11454
|
-
const bmDir =
|
|
11455
|
-
const projectsDir =
|
|
11479
|
+
const bmDir = join25(factoryDir, ".beastmode");
|
|
11480
|
+
const projectsDir = join25(bmDir, "projects");
|
|
11456
11481
|
if (!existsSync26(projectsDir)) {
|
|
11457
11482
|
return {
|
|
11458
11483
|
label: "Project directory",
|
|
@@ -11480,7 +11505,7 @@ function checkProjectDirectory(factoryDir) {
|
|
|
11480
11505
|
anyFail = true;
|
|
11481
11506
|
continue;
|
|
11482
11507
|
}
|
|
11483
|
-
const isGit = existsSync26(
|
|
11508
|
+
const isGit = existsSync26(join25(projPath, ".git"));
|
|
11484
11509
|
const manifests = [
|
|
11485
11510
|
"package.json",
|
|
11486
11511
|
"Cargo.toml",
|
|
@@ -11491,7 +11516,7 @@ function checkProjectDirectory(factoryDir) {
|
|
|
11491
11516
|
"build.gradle",
|
|
11492
11517
|
"build.gradle.kts"
|
|
11493
11518
|
];
|
|
11494
|
-
const manifest = manifests.find((m) => existsSync26(
|
|
11519
|
+
const manifest = manifests.find((m) => existsSync26(join25(projPath, m)));
|
|
11495
11520
|
const framework = proj.stack?.detected || manifest?.replace(".json", "") || "unknown";
|
|
11496
11521
|
results.push(
|
|
11497
11522
|
`${projName}: ${projPath} (${framework})${isGit ? "" : " [no .git]"}`
|
|
@@ -11541,7 +11566,7 @@ function checkStack(factoryDir) {
|
|
|
11541
11566
|
detail: "no factory \u2014 run beastmode init"
|
|
11542
11567
|
};
|
|
11543
11568
|
}
|
|
11544
|
-
const projectsDir =
|
|
11569
|
+
const projectsDir = join25(factoryDir, ".beastmode", "projects");
|
|
11545
11570
|
if (!existsSync26(projectsDir)) {
|
|
11546
11571
|
return { label: "Stack", status: "warn", detail: "no projects configured" };
|
|
11547
11572
|
}
|
|
@@ -11593,12 +11618,12 @@ function checkBoardPassword(env, factoryDir) {
|
|
|
11593
11618
|
return { label: "Board password", status: "pass", detail: "set" };
|
|
11594
11619
|
}
|
|
11595
11620
|
if (factoryDir) {
|
|
11596
|
-
const dotEnv =
|
|
11597
|
-
const secretsEnv =
|
|
11621
|
+
const dotEnv = join25(factoryDir, ".env");
|
|
11622
|
+
const secretsEnv = join25(factoryDir, ".beastmode", "secrets.env.local");
|
|
11598
11623
|
for (const filePath of [dotEnv, secretsEnv]) {
|
|
11599
11624
|
if (existsSync26(filePath)) {
|
|
11600
11625
|
try {
|
|
11601
|
-
const content =
|
|
11626
|
+
const content = readFileSync24(filePath, "utf-8");
|
|
11602
11627
|
const lines = content.split("\n");
|
|
11603
11628
|
const line = lines.find((l) => l.startsWith("BEASTMODE_UI_PASSWORD="));
|
|
11604
11629
|
if (line) {
|
|
@@ -11620,12 +11645,12 @@ function checkBoardPassword(env, factoryDir) {
|
|
|
11620
11645
|
};
|
|
11621
11646
|
}
|
|
11622
11647
|
function doctorAction(factoryDir, env) {
|
|
11623
|
-
const bmDir =
|
|
11648
|
+
const bmDir = join25(factoryDir, ".beastmode");
|
|
11624
11649
|
const factoryDirExists = existsSync26(bmDir);
|
|
11625
11650
|
let factoryIdentity = null;
|
|
11626
11651
|
if (factoryDirExists) {
|
|
11627
11652
|
try {
|
|
11628
|
-
const raw = JSON.parse(
|
|
11653
|
+
const raw = JSON.parse(readFileSync24(join25(bmDir, "factory.json"), "utf-8"));
|
|
11629
11654
|
factoryIdentity = FactoryIdentitySchema.parse(raw);
|
|
11630
11655
|
} catch {
|
|
11631
11656
|
}
|
|
@@ -11633,10 +11658,10 @@ function doctorAction(factoryDir, env) {
|
|
|
11633
11658
|
let config = null;
|
|
11634
11659
|
let configParseError = null;
|
|
11635
11660
|
if (factoryDirExists) {
|
|
11636
|
-
const configPath =
|
|
11661
|
+
const configPath = join25(bmDir, "config.json");
|
|
11637
11662
|
if (existsSync26(configPath)) {
|
|
11638
11663
|
try {
|
|
11639
|
-
const raw = JSON.parse(
|
|
11664
|
+
const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
11640
11665
|
const result = FactoryConfigSchema.safeParse(raw);
|
|
11641
11666
|
if (result.success) {
|
|
11642
11667
|
config = raw;
|
|
@@ -11652,7 +11677,7 @@ function doctorAction(factoryDir, env) {
|
|
|
11652
11677
|
}
|
|
11653
11678
|
const projectPaths = [];
|
|
11654
11679
|
if (factoryDirExists) {
|
|
11655
|
-
const projectsDir =
|
|
11680
|
+
const projectsDir = join25(bmDir, "projects");
|
|
11656
11681
|
for (const proj of listProjectRecords(projectsDir)) {
|
|
11657
11682
|
if (proj.path) {
|
|
11658
11683
|
projectPaths.push({
|
|
@@ -11665,13 +11690,13 @@ function doctorAction(factoryDir, env) {
|
|
|
11665
11690
|
}
|
|
11666
11691
|
const installedPlugins = [];
|
|
11667
11692
|
if (factoryDirExists) {
|
|
11668
|
-
const pluginsDir =
|
|
11693
|
+
const pluginsDir = join25(bmDir, "plugins");
|
|
11669
11694
|
if (existsSync26(pluginsDir)) {
|
|
11670
11695
|
for (const pluginName of readdirSync11(pluginsDir)) {
|
|
11671
|
-
const manifestPath =
|
|
11696
|
+
const manifestPath = join25(pluginsDir, pluginName, "manifest.json");
|
|
11672
11697
|
if (existsSync26(manifestPath)) {
|
|
11673
11698
|
try {
|
|
11674
|
-
const manifest = JSON.parse(
|
|
11699
|
+
const manifest = JSON.parse(readFileSync24(manifestPath, "utf-8"));
|
|
11675
11700
|
installedPlugins.push({
|
|
11676
11701
|
name: pluginName,
|
|
11677
11702
|
engine_version: manifest.engine_version || "*"
|
|
@@ -11699,7 +11724,7 @@ var SYSTEMD_CREDS_TIMER = "beastmode-claude-creds.timer";
|
|
|
11699
11724
|
var LAUNCH_AGENT_LABEL2 = "com.develeap.beastmode.claude-creds";
|
|
11700
11725
|
function checkCredentialSyncAgent() {
|
|
11701
11726
|
const results = [];
|
|
11702
|
-
const credsPath =
|
|
11727
|
+
const credsPath = join25(homedir3(), ".claude", ".credentials.json");
|
|
11703
11728
|
if (!existsSync26(credsPath)) {
|
|
11704
11729
|
results.push({
|
|
11705
11730
|
label: "Claude credentials file",
|
|
@@ -11715,7 +11740,7 @@ function checkCredentialSyncAgent() {
|
|
|
11715
11740
|
detail: credsPath
|
|
11716
11741
|
});
|
|
11717
11742
|
try {
|
|
11718
|
-
const creds = JSON.parse(
|
|
11743
|
+
const creds = JSON.parse(readFileSync24(credsPath, "utf-8"));
|
|
11719
11744
|
const expiresAt = creds?.claudeAiOauth?.expiresAt;
|
|
11720
11745
|
if (typeof expiresAt === "number") {
|
|
11721
11746
|
const hoursLeft = Math.round((expiresAt - Date.now()) / 36e5);
|
|
@@ -12031,7 +12056,7 @@ var doctorCommand = new Command12("doctor").description("Health check \u2014 val
|
|
|
12031
12056
|
console.log();
|
|
12032
12057
|
const env = process.env;
|
|
12033
12058
|
const factoryDir = findFactoryDir() ?? resolve15(".");
|
|
12034
|
-
const hasFactory = existsSync26(
|
|
12059
|
+
const hasFactory = existsSync26(join25(factoryDir, ".beastmode"));
|
|
12035
12060
|
const checks = [];
|
|
12036
12061
|
checks.push(...await checkClaudeAuth(env.ANTHROPIC_API_KEY));
|
|
12037
12062
|
checks.push(...checkCredentialSyncAgent());
|
|
@@ -12107,12 +12132,12 @@ init_schemas();
|
|
|
12107
12132
|
init_version();
|
|
12108
12133
|
init_display();
|
|
12109
12134
|
import { Command as Command13 } from "commander";
|
|
12110
|
-
import { existsSync as existsSync28, readFileSync as
|
|
12111
|
-
import { join as
|
|
12135
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync21 } from "fs";
|
|
12136
|
+
import { join as join27, resolve as resolve16 } from "path";
|
|
12112
12137
|
|
|
12113
12138
|
// src/cli/utils/regenerate.ts
|
|
12114
|
-
import { existsSync as existsSync27, readFileSync as
|
|
12115
|
-
import { join as
|
|
12139
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25, writeFileSync as writeFileSync20, copyFileSync } from "fs";
|
|
12140
|
+
import { join as join26 } from "path";
|
|
12116
12141
|
var RECOGNIZED_KEYS = /* @__PURE__ */ new Set([
|
|
12117
12142
|
"PROJECT_DIR",
|
|
12118
12143
|
"PROJECT_GITHUB_TOKEN",
|
|
@@ -12226,8 +12251,8 @@ function buildNewEnv(values) {
|
|
|
12226
12251
|
return lines.join("\n");
|
|
12227
12252
|
}
|
|
12228
12253
|
function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
|
|
12229
|
-
const envPath =
|
|
12230
|
-
const composePath =
|
|
12254
|
+
const envPath = join26(factoryDir, ".env");
|
|
12255
|
+
const composePath = join26(factoryDir, "docker-compose.yml");
|
|
12231
12256
|
if (!existsSync27(envPath)) {
|
|
12232
12257
|
throw new Error(
|
|
12233
12258
|
`.env not found at ${envPath}. This does not look like a beastmode factory \u2014 run 'beastmode init' first.`
|
|
@@ -12238,7 +12263,7 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
|
|
|
12238
12263
|
`docker-compose.yml not found at ${composePath}. This does not look like a beastmode factory \u2014 run 'beastmode init' first.`
|
|
12239
12264
|
);
|
|
12240
12265
|
}
|
|
12241
|
-
const existingEnv =
|
|
12266
|
+
const existingEnv = readFileSync25(envPath, "utf-8");
|
|
12242
12267
|
const values = parseExistingEnv(existingEnv);
|
|
12243
12268
|
const missing = [];
|
|
12244
12269
|
if (!values.projectDir) missing.push("PROJECT_DIR");
|
|
@@ -12256,7 +12281,7 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
|
|
|
12256
12281
|
}
|
|
12257
12282
|
const newEnv = buildNewEnv(values);
|
|
12258
12283
|
const newCompose = generateComposeYaml("latest");
|
|
12259
|
-
const existingCompose =
|
|
12284
|
+
const existingCompose = readFileSync25(composePath, "utf-8");
|
|
12260
12285
|
const envChanged = newEnv !== existingEnv;
|
|
12261
12286
|
const composeChanged = newCompose !== existingCompose;
|
|
12262
12287
|
if (!envChanged && !composeChanged) {
|
|
@@ -12292,18 +12317,18 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
|
|
|
12292
12317
|
|
|
12293
12318
|
// src/cli/commands/upgrade.ts
|
|
12294
12319
|
function readIdentity(factoryDir) {
|
|
12295
|
-
const path =
|
|
12320
|
+
const path = join27(factoryDir, ".beastmode", "factory.json");
|
|
12296
12321
|
if (!existsSync28(path)) {
|
|
12297
12322
|
throw new Error("No factory.json found. Run beastmode init first.");
|
|
12298
12323
|
}
|
|
12299
|
-
return FactoryIdentitySchema.parse(JSON.parse(
|
|
12324
|
+
return FactoryIdentitySchema.parse(JSON.parse(readFileSync26(path, "utf-8")));
|
|
12300
12325
|
}
|
|
12301
12326
|
function readConfig3(factoryDir) {
|
|
12302
|
-
const path =
|
|
12327
|
+
const path = join27(factoryDir, ".beastmode", "config.json");
|
|
12303
12328
|
if (!existsSync28(path)) {
|
|
12304
12329
|
return {};
|
|
12305
12330
|
}
|
|
12306
|
-
return JSON.parse(
|
|
12331
|
+
return JSON.parse(readFileSync26(path, "utf-8"));
|
|
12307
12332
|
}
|
|
12308
12333
|
function upgradeCheckAction(factoryDir) {
|
|
12309
12334
|
const identity = readIdentity(factoryDir);
|
|
@@ -12316,11 +12341,11 @@ function upgradeAction(factoryDir, migrateOnly = false) {
|
|
|
12316
12341
|
const result = performUpgrade(identity, config, targetVersion, SCHEMA_VERSION);
|
|
12317
12342
|
if (result.changes.length > 0) {
|
|
12318
12343
|
writeFileSync21(
|
|
12319
|
-
|
|
12344
|
+
join27(factoryDir, ".beastmode", "factory.json"),
|
|
12320
12345
|
JSON.stringify(result.updatedIdentity, null, 2) + "\n"
|
|
12321
12346
|
);
|
|
12322
12347
|
writeFileSync21(
|
|
12323
|
-
|
|
12348
|
+
join27(factoryDir, ".beastmode", "config.json"),
|
|
12324
12349
|
JSON.stringify(result.updatedConfig, null, 2) + "\n"
|
|
12325
12350
|
);
|
|
12326
12351
|
}
|
|
@@ -12410,8 +12435,8 @@ init_board();
|
|
|
12410
12435
|
init_display();
|
|
12411
12436
|
init_migrator();
|
|
12412
12437
|
import { Command as Command14 } from "commander";
|
|
12413
|
-
import { resolve as resolve17, join as
|
|
12414
|
-
import { existsSync as existsSync29, readFileSync as
|
|
12438
|
+
import { resolve as resolve17, join as join28 } from "path";
|
|
12439
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, mkdirSync as mkdirSync17, writeFileSync as writeFileSync22 } from "fs";
|
|
12415
12440
|
var migrateCommand = new Command14("migrate").description("Migrate a daemon config into a .beastmode/ factory").option("--config <path>", "Path to beastmode.daemon.json").option("--dry-run", "Show what would be created without writing files").action(async (opts) => {
|
|
12416
12441
|
try {
|
|
12417
12442
|
await runMigrate(opts);
|
|
@@ -12431,25 +12456,25 @@ async function runMigrate(opts) {
|
|
|
12431
12456
|
}
|
|
12432
12457
|
header("BeastMode Migrate");
|
|
12433
12458
|
info(`Reading daemon config from: ${configPath}`);
|
|
12434
|
-
const configContent =
|
|
12459
|
+
const configContent = readFileSync27(configPath, "utf-8");
|
|
12435
12460
|
const daemonConfig = parseDaemonConfig(configContent);
|
|
12436
|
-
const runsDir =
|
|
12461
|
+
const runsDir = join28(cwd, "runs");
|
|
12437
12462
|
let runDirs = [];
|
|
12438
12463
|
const checkpoints = /* @__PURE__ */ new Map();
|
|
12439
12464
|
if (existsSync29(runsDir)) {
|
|
12440
12465
|
const { readdirSync: readdirSync13 } = await import("fs");
|
|
12441
12466
|
runDirs = readdirSync13(runsDir).filter((d) => {
|
|
12442
12467
|
try {
|
|
12443
|
-
return readdirSync13(
|
|
12468
|
+
return readdirSync13(join28(runsDir, d)).length > 0;
|
|
12444
12469
|
} catch {
|
|
12445
12470
|
return false;
|
|
12446
12471
|
}
|
|
12447
12472
|
});
|
|
12448
12473
|
for (const dir of runDirs) {
|
|
12449
|
-
const cpPath =
|
|
12474
|
+
const cpPath = join28(runsDir, dir, "checkpoint.json");
|
|
12450
12475
|
if (existsSync29(cpPath)) {
|
|
12451
12476
|
try {
|
|
12452
|
-
const cp = JSON.parse(
|
|
12477
|
+
const cp = JSON.parse(readFileSync27(cpPath, "utf-8"));
|
|
12453
12478
|
checkpoints.set(dir, cp);
|
|
12454
12479
|
} catch {
|
|
12455
12480
|
}
|
|
@@ -12506,28 +12531,28 @@ async function runMigrate(opts) {
|
|
|
12506
12531
|
warn("Dry run \u2014 no files written.");
|
|
12507
12532
|
return;
|
|
12508
12533
|
}
|
|
12509
|
-
const bmDir =
|
|
12534
|
+
const bmDir = join28(cwd, ".beastmode");
|
|
12510
12535
|
if (existsSync29(bmDir)) {
|
|
12511
12536
|
throw new Error(
|
|
12512
12537
|
"A .beastmode/ directory already exists. Remove it first to re-migrate."
|
|
12513
12538
|
);
|
|
12514
12539
|
}
|
|
12515
12540
|
for (const file of files) {
|
|
12516
|
-
const fullPath =
|
|
12541
|
+
const fullPath = join28(cwd, file.path);
|
|
12517
12542
|
const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
12518
12543
|
mkdirSync17(dir, { recursive: true });
|
|
12519
12544
|
writeFileSync22(fullPath, file.content, "utf-8");
|
|
12520
12545
|
}
|
|
12521
|
-
const runsSymlinkTarget =
|
|
12522
|
-
const bmRunsPath =
|
|
12546
|
+
const runsSymlinkTarget = join28(cwd, "runs");
|
|
12547
|
+
const bmRunsPath = join28(cwd, "runs");
|
|
12523
12548
|
if (existsSync29(runsSymlinkTarget)) {
|
|
12524
12549
|
info("Existing runs/ directory preserved in-place.");
|
|
12525
12550
|
}
|
|
12526
|
-
const boardPath =
|
|
12551
|
+
const boardPath = join28(bmDir, "board.json");
|
|
12527
12552
|
if (!existsSync29(boardPath)) {
|
|
12528
12553
|
writeFileSync22(boardPath, JSON.stringify({ items: [] }, null, 2), "utf-8");
|
|
12529
12554
|
}
|
|
12530
|
-
mkdirSync17(
|
|
12555
|
+
mkdirSync17(join28(bmDir, ".cache"), { recursive: true });
|
|
12531
12556
|
console.log();
|
|
12532
12557
|
success("Migration complete!");
|
|
12533
12558
|
info(`Factory created at: ${bmDir}`);
|
|
@@ -12550,8 +12575,8 @@ init_bridge();
|
|
|
12550
12575
|
init_schemas();
|
|
12551
12576
|
init_engine();
|
|
12552
12577
|
import { Command as Command15 } from "commander";
|
|
12553
|
-
import { join as
|
|
12554
|
-
import { existsSync as existsSync30, readFileSync as
|
|
12578
|
+
import { join as join29 } from "path";
|
|
12579
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28, writeFileSync as writeFileSync23, mkdirSync as mkdirSync18 } from "fs";
|
|
12555
12580
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
12556
12581
|
var runCommand = new Command15("run").description("Run a single pipeline task").argument("[project]", "Project name (defaults to first project)").option("--task <description>", "Task description").action(async (project, opts) => {
|
|
12557
12582
|
try {
|
|
@@ -12571,17 +12596,17 @@ async function runPipeline(projectName, opts) {
|
|
|
12571
12596
|
"No BeastMode factory found. Run 'beastmode init' first."
|
|
12572
12597
|
);
|
|
12573
12598
|
}
|
|
12574
|
-
const bmDir =
|
|
12599
|
+
const bmDir = join29(factoryDir, ".beastmode");
|
|
12575
12600
|
header("BeastMode Run");
|
|
12576
|
-
const configPath =
|
|
12601
|
+
const configPath = join29(bmDir, "config.json");
|
|
12577
12602
|
if (!existsSync30(configPath)) {
|
|
12578
12603
|
throw new Error("Factory config not found. Run 'beastmode init' first.");
|
|
12579
12604
|
}
|
|
12580
12605
|
const factoryConfig = FactoryConfigSchema.parse(
|
|
12581
|
-
JSON.parse(
|
|
12606
|
+
JSON.parse(readFileSync28(configPath, "utf-8"))
|
|
12582
12607
|
);
|
|
12583
12608
|
let projectConfig = null;
|
|
12584
|
-
const projectsDir =
|
|
12609
|
+
const projectsDir = join29(bmDir, "projects");
|
|
12585
12610
|
if (projectName) {
|
|
12586
12611
|
const record = readProjectRecord(projectsDir, projectName);
|
|
12587
12612
|
if (!record) {
|
|
@@ -12595,11 +12620,11 @@ async function runPipeline(projectName, opts) {
|
|
|
12595
12620
|
info(`Using project: ${projectConfig.name}`);
|
|
12596
12621
|
}
|
|
12597
12622
|
}
|
|
12598
|
-
const boardPath =
|
|
12623
|
+
const boardPath = join29(bmDir, "board.json");
|
|
12599
12624
|
let boardItems = [];
|
|
12600
12625
|
if (existsSync30(boardPath)) {
|
|
12601
12626
|
try {
|
|
12602
|
-
const raw = JSON.parse(
|
|
12627
|
+
const raw = JSON.parse(readFileSync28(boardPath, "utf-8"));
|
|
12603
12628
|
boardItems = Array.isArray(raw.items) ? raw.items : [];
|
|
12604
12629
|
} catch {
|
|
12605
12630
|
boardItems = [];
|
|
@@ -12618,9 +12643,9 @@ async function runPipeline(projectName, opts) {
|
|
|
12618
12643
|
boardItems.push(task);
|
|
12619
12644
|
writeFileSync23(boardPath, JSON.stringify({ items: boardItems }, null, 2), "utf-8");
|
|
12620
12645
|
info(`Created task: ${task.title} (${taskId})`);
|
|
12621
|
-
const cacheDir =
|
|
12646
|
+
const cacheDir = join29(bmDir, ".cache");
|
|
12622
12647
|
mkdirSync18(cacheDir, { recursive: true });
|
|
12623
|
-
const daemonConfigPath =
|
|
12648
|
+
const daemonConfigPath = join29(cacheDir, "daemon.json");
|
|
12624
12649
|
const daemonConfig = generateDaemonConfig(factoryConfig, projectConfig, factoryDir);
|
|
12625
12650
|
writeFileSync23(daemonConfigPath, JSON.stringify(daemonConfig, null, 2), "utf-8");
|
|
12626
12651
|
info(`Generated daemon config at: ${daemonConfigPath}`);
|
|
@@ -12673,7 +12698,7 @@ async function runPipeline(projectName, opts) {
|
|
|
12673
12698
|
const startTime = Date.now();
|
|
12674
12699
|
const pollInterval = setInterval(() => {
|
|
12675
12700
|
try {
|
|
12676
|
-
const board = JSON.parse(
|
|
12701
|
+
const board = JSON.parse(readFileSync28(boardPath, "utf-8"));
|
|
12677
12702
|
const items = Array.isArray(board.items) ? board.items : [];
|
|
12678
12703
|
const taskItem = items.find((i) => i.id === taskId);
|
|
12679
12704
|
if (taskItem) {
|
|
@@ -12720,8 +12745,8 @@ init_bridge();
|
|
|
12720
12745
|
init_schemas();
|
|
12721
12746
|
init_engine();
|
|
12722
12747
|
import { Command as Command16 } from "commander";
|
|
12723
|
-
import { join as
|
|
12724
|
-
import { existsSync as existsSync31, readFileSync as
|
|
12748
|
+
import { join as join30 } from "path";
|
|
12749
|
+
import { existsSync as existsSync31, readFileSync as readFileSync29, writeFileSync as writeFileSync24, mkdirSync as mkdirSync19 } from "fs";
|
|
12725
12750
|
var daemonCommand = new Command16("daemon").description("Start the BeastMode daemon via bridge").option("--dry-run", "Generate config but don't start daemon").option(
|
|
12726
12751
|
"--log-level <level>",
|
|
12727
12752
|
"Log level (DEBUG, INFO, WARNING, ERROR)",
|
|
@@ -12741,25 +12766,25 @@ async function runDaemon(opts) {
|
|
|
12741
12766
|
"No BeastMode factory found. Run 'beastmode init' first."
|
|
12742
12767
|
);
|
|
12743
12768
|
}
|
|
12744
|
-
const bmDir =
|
|
12769
|
+
const bmDir = join30(factoryDir, ".beastmode");
|
|
12745
12770
|
header("BeastMode Daemon");
|
|
12746
|
-
const configPath =
|
|
12771
|
+
const configPath = join30(bmDir, "config.json");
|
|
12747
12772
|
if (!existsSync31(configPath)) {
|
|
12748
12773
|
throw new Error("Factory config not found. Run 'beastmode init' first.");
|
|
12749
12774
|
}
|
|
12750
12775
|
const factoryConfig = FactoryConfigSchema.parse(
|
|
12751
|
-
JSON.parse(
|
|
12776
|
+
JSON.parse(readFileSync29(configPath, "utf-8"))
|
|
12752
12777
|
);
|
|
12753
12778
|
let projectConfig = null;
|
|
12754
|
-
const projectsDir =
|
|
12779
|
+
const projectsDir = join30(bmDir, "projects");
|
|
12755
12780
|
const projects = listProjectRecords(projectsDir);
|
|
12756
12781
|
if (projects.length > 0) {
|
|
12757
12782
|
projectConfig = projects[0];
|
|
12758
12783
|
info(`Using project: ${projectConfig.name}`);
|
|
12759
12784
|
}
|
|
12760
|
-
const cacheDir =
|
|
12785
|
+
const cacheDir = join30(bmDir, ".cache");
|
|
12761
12786
|
mkdirSync19(cacheDir, { recursive: true });
|
|
12762
|
-
const daemonConfigPath =
|
|
12787
|
+
const daemonConfigPath = join30(cacheDir, "daemon.json");
|
|
12763
12788
|
const daemonConfig = generateDaemonConfig(
|
|
12764
12789
|
factoryConfig,
|
|
12765
12790
|
projectConfig,
|
|
@@ -12804,7 +12829,7 @@ async function runDaemon(opts) {
|
|
|
12804
12829
|
});
|
|
12805
12830
|
info(`Starting daemon: ${pythonCmd} ${cmd.args.join(" ")}`);
|
|
12806
12831
|
console.log();
|
|
12807
|
-
const pidFile =
|
|
12832
|
+
const pidFile = join30(bmDir, "daemon.pid");
|
|
12808
12833
|
const { spawn: spawn4 } = await import("child_process");
|
|
12809
12834
|
const child = spawn4(pythonCmd, cmd.args, {
|
|
12810
12835
|
stdio: "inherit",
|
|
@@ -12854,12 +12879,12 @@ var mcpCommand = new Command17("mcp").description("Start the BeastMode MCP serve
|
|
|
12854
12879
|
// src/cli/commands/deploy.ts
|
|
12855
12880
|
init_display();
|
|
12856
12881
|
import { Command as Command18 } from "commander";
|
|
12857
|
-
import { resolve as resolve19, join as
|
|
12858
|
-
import { existsSync as existsSync33, writeFileSync as writeFileSync26, readFileSync as
|
|
12882
|
+
import { resolve as resolve19, join as join32 } from "path";
|
|
12883
|
+
import { existsSync as existsSync33, writeFileSync as writeFileSync26, readFileSync as readFileSync31 } from "fs";
|
|
12859
12884
|
import { execSync as execSync9 } from "child_process";
|
|
12860
12885
|
import { randomBytes } from "crypto";
|
|
12861
|
-
import { fileURLToPath as
|
|
12862
|
-
import { dirname as
|
|
12886
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12887
|
+
import { dirname as dirname8 } from "path";
|
|
12863
12888
|
var deployCommand = new Command18("deploy").description("Deploy BeastMode services as systemd services").option("--port <number>", "Port to serve the board UI on", "8420").option("--host <host>", "Host to bind the board UI to", "0.0.0.0").option("--dry-run", "Show what would be done without executing").option("--no-start", "Install services but don't start them").option("--stop", "Stop all 3 BeastMode services").option("--status", "Show status of all 3 BeastMode services").option("--cloud <provider>", "Deploy to cloud (aws)").option("--upgrade", "Update an existing cloud deployment").option("--destroy", "Destroy the cloud deployment (delete CloudFormation stack)").action(async (opts) => {
|
|
12864
12889
|
try {
|
|
12865
12890
|
await runDeploy(opts);
|
|
@@ -12910,7 +12935,7 @@ async function runDeploy(opts) {
|
|
|
12910
12935
|
process.exit(1);
|
|
12911
12936
|
}
|
|
12912
12937
|
const factoryDir = resolve19(".");
|
|
12913
|
-
const bmDir =
|
|
12938
|
+
const bmDir = join32(factoryDir, ".beastmode");
|
|
12914
12939
|
if (!existsSync33(bmDir)) {
|
|
12915
12940
|
error(
|
|
12916
12941
|
"No .beastmode directory found. Run 'beastmode init' or 'beastmode migrate' first."
|
|
@@ -12935,18 +12960,18 @@ async function runDeploy(opts) {
|
|
|
12935
12960
|
"../../index.js"
|
|
12936
12961
|
);
|
|
12937
12962
|
}
|
|
12938
|
-
const boardVenvPython =
|
|
12939
|
-
const daemonVenvPython =
|
|
12963
|
+
const boardVenvPython = join32(factoryDir, "board", ".venv", "bin", "python");
|
|
12964
|
+
const daemonVenvPython = join32(factoryDir, "daemon", ".venv", "bin", "python");
|
|
12940
12965
|
const boardPython = existsSync33(boardVenvPython) ? boardVenvPython : "python3";
|
|
12941
12966
|
const daemonPython = existsSync33(daemonVenvPython) ? daemonVenvPython : "python3";
|
|
12942
12967
|
const user = execSync9("whoami", { encoding: "utf-8" }).trim();
|
|
12943
12968
|
const home = process.env.HOME || `/home/${user}`;
|
|
12944
12969
|
const port = opts.port;
|
|
12945
12970
|
const host = opts.host;
|
|
12946
|
-
const dotEnv =
|
|
12947
|
-
const secretsEnv =
|
|
12948
|
-
const envContent = existsSync33(dotEnv) ?
|
|
12949
|
-
const secretsContent = existsSync33(secretsEnv) ?
|
|
12971
|
+
const dotEnv = join32(factoryDir, ".env");
|
|
12972
|
+
const secretsEnv = join32(bmDir, "secrets.env.local");
|
|
12973
|
+
const envContent = existsSync33(dotEnv) ? readFileSync31(dotEnv, "utf-8") : "";
|
|
12974
|
+
const secretsContent = existsSync33(secretsEnv) ? readFileSync31(secretsEnv, "utf-8") : "";
|
|
12950
12975
|
const hasPassword = envContent.includes("BEASTMODE_UI_PASSWORD=") && !envContent.includes("BEASTMODE_UI_PASSWORD=\n") || secretsContent.includes("BEASTMODE_UI_PASSWORD=") && !secretsContent.includes("BEASTMODE_UI_PASSWORD=\n") || !!process.env.BEASTMODE_UI_PASSWORD;
|
|
12951
12976
|
if (!hasPassword && opts.host === "0.0.0.0") {
|
|
12952
12977
|
const generated = randomBytes(18).toString("base64url");
|
|
@@ -12955,7 +12980,7 @@ async function runDeploy(opts) {
|
|
|
12955
12980
|
# Auto-generated board UI password (deploy)
|
|
12956
12981
|
BEASTMODE_UI_PASSWORD=${generated}
|
|
12957
12982
|
`;
|
|
12958
|
-
writeFileSync26(target, (existsSync33(target) ?
|
|
12983
|
+
writeFileSync26(target, (existsSync33(target) ? readFileSync31(target, "utf-8") : "") + append, "utf-8");
|
|
12959
12984
|
info(`Board UI password auto-generated and saved to ${target}`);
|
|
12960
12985
|
success(`Password: ${generated}`);
|
|
12961
12986
|
info("Save this password \u2014 you'll need it to access the board UI.");
|
|
@@ -13171,10 +13196,10 @@ async function deployToAWS(opts) {
|
|
|
13171
13196
|
error("AWS credentials not configured. Run: aws configure");
|
|
13172
13197
|
process.exit(1);
|
|
13173
13198
|
}
|
|
13174
|
-
const __filename2 =
|
|
13175
|
-
const __dirname2 =
|
|
13176
|
-
const templatePath =
|
|
13177
|
-
const cwdTemplate =
|
|
13199
|
+
const __filename2 = fileURLToPath4(import.meta.url);
|
|
13200
|
+
const __dirname2 = dirname8(__filename2);
|
|
13201
|
+
const templatePath = join32(__dirname2, "..", "..", "infra", "cloudformation", "beastmode.yaml");
|
|
13202
|
+
const cwdTemplate = join32(process.cwd(), "infra", "cloudformation", "beastmode.yaml");
|
|
13178
13203
|
const template = existsSync33(templatePath) ? templatePath : existsSync33(cwdTemplate) ? cwdTemplate : null;
|
|
13179
13204
|
if (!template) {
|
|
13180
13205
|
error("CloudFormation template not found. Expected at infra/cloudformation/beastmode.yaml");
|
|
@@ -13429,13 +13454,13 @@ var logsCommand = new Command21("logs").description("Stream BeastMode service lo
|
|
|
13429
13454
|
|
|
13430
13455
|
// src/cli/commands/update.ts
|
|
13431
13456
|
import { Command as Command22 } from "commander";
|
|
13432
|
-
import { readFileSync as
|
|
13457
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync27 } from "fs";
|
|
13433
13458
|
init_display();
|
|
13434
13459
|
async function runUpdate(opts) {
|
|
13435
13460
|
const cwd = opts.cwd ?? process.cwd();
|
|
13436
13461
|
const composePath = requireComposeFile(cwd);
|
|
13437
13462
|
if (opts.tag) {
|
|
13438
|
-
let content =
|
|
13463
|
+
let content = readFileSync32(composePath, "utf-8");
|
|
13439
13464
|
const tagPattern = new RegExp(
|
|
13440
13465
|
`(${GHCR_IMAGE_PREFIX.replace(/[/]/g, "\\/")}\\/(?:board|daemon|ui)):([\\w.\\-]+)`,
|
|
13441
13466
|
"g"
|
|
@@ -13468,7 +13493,7 @@ init_display();
|
|
|
13468
13493
|
import { Command as Command23 } from "commander";
|
|
13469
13494
|
import { spawn as spawn3 } from "child_process";
|
|
13470
13495
|
import { existsSync as existsSync38 } from "fs";
|
|
13471
|
-
import { basename as basename8, join as
|
|
13496
|
+
import { basename as basename8, join as join38, resolve as resolve20 } from "path";
|
|
13472
13497
|
|
|
13473
13498
|
// src/cli/runner-image-builder.ts
|
|
13474
13499
|
import { execSync as execSync10 } from "child_process";
|
|
@@ -13476,14 +13501,14 @@ import { createHash } from "crypto";
|
|
|
13476
13501
|
import {
|
|
13477
13502
|
existsSync as existsSync35,
|
|
13478
13503
|
mkdirSync as mkdirSync21,
|
|
13479
|
-
readFileSync as
|
|
13504
|
+
readFileSync as readFileSync34,
|
|
13480
13505
|
writeFileSync as writeFileSync28
|
|
13481
13506
|
} from "fs";
|
|
13482
|
-
import { join as
|
|
13507
|
+
import { join as join34 } from "path";
|
|
13483
13508
|
|
|
13484
13509
|
// src/cli/stack-detect.ts
|
|
13485
|
-
import { existsSync as existsSync34, readFileSync as
|
|
13486
|
-
import { join as
|
|
13510
|
+
import { existsSync as existsSync34, readFileSync as readFileSync33 } from "fs";
|
|
13511
|
+
import { join as join33 } from "path";
|
|
13487
13512
|
var NODE_LOCKFILES = [
|
|
13488
13513
|
"package-lock.json",
|
|
13489
13514
|
"pnpm-lock.yaml",
|
|
@@ -13521,7 +13546,7 @@ var STACK_LOCKFILES = {
|
|
|
13521
13546
|
};
|
|
13522
13547
|
function readFileSafe2(path) {
|
|
13523
13548
|
try {
|
|
13524
|
-
return
|
|
13549
|
+
return readFileSync33(path, "utf-8");
|
|
13525
13550
|
} catch {
|
|
13526
13551
|
return null;
|
|
13527
13552
|
}
|
|
@@ -13534,7 +13559,7 @@ function parseJsonSafe2(content) {
|
|
|
13534
13559
|
}
|
|
13535
13560
|
}
|
|
13536
13561
|
function detectRunnerStack(projectDir) {
|
|
13537
|
-
const pkgContent = readFileSafe2(
|
|
13562
|
+
const pkgContent = readFileSafe2(join33(projectDir, "package.json"));
|
|
13538
13563
|
if (pkgContent) {
|
|
13539
13564
|
const pkg = parseJsonSafe2(pkgContent);
|
|
13540
13565
|
if (pkg) {
|
|
@@ -13548,36 +13573,36 @@ function detectRunnerStack(projectDir) {
|
|
|
13548
13573
|
}
|
|
13549
13574
|
return { name: "node", language: "node" };
|
|
13550
13575
|
}
|
|
13551
|
-
if (existsSync34(
|
|
13576
|
+
if (existsSync34(join33(projectDir, "manage.py"))) {
|
|
13552
13577
|
return { name: "django", language: "python" };
|
|
13553
13578
|
}
|
|
13554
|
-
const pyproject = readFileSafe2(
|
|
13579
|
+
const pyproject = readFileSafe2(join33(projectDir, "pyproject.toml"));
|
|
13555
13580
|
if (pyproject) {
|
|
13556
13581
|
if (pyproject.toLowerCase().includes("fastapi")) {
|
|
13557
13582
|
return { name: "fastapi", language: "python" };
|
|
13558
13583
|
}
|
|
13559
13584
|
return { name: "python", language: "python" };
|
|
13560
13585
|
}
|
|
13561
|
-
if (existsSync34(
|
|
13586
|
+
if (existsSync34(join33(projectDir, "requirements.txt"))) {
|
|
13562
13587
|
return { name: "python", language: "python" };
|
|
13563
13588
|
}
|
|
13564
|
-
if (existsSync34(
|
|
13589
|
+
if (existsSync34(join33(projectDir, "go.mod"))) {
|
|
13565
13590
|
return { name: "go", language: "go" };
|
|
13566
13591
|
}
|
|
13567
|
-
if (existsSync34(
|
|
13592
|
+
if (existsSync34(join33(projectDir, "Cargo.toml"))) {
|
|
13568
13593
|
return { name: "rust", language: "rust" };
|
|
13569
13594
|
}
|
|
13570
|
-
if (existsSync34(
|
|
13595
|
+
if (existsSync34(join33(projectDir, "pom.xml"))) {
|
|
13571
13596
|
return { name: "java-maven", language: "java" };
|
|
13572
13597
|
}
|
|
13573
|
-
if (existsSync34(
|
|
13598
|
+
if (existsSync34(join33(projectDir, "build.gradle")) || existsSync34(join33(projectDir, "build.gradle.kts"))) {
|
|
13574
13599
|
return { name: "java-gradle", language: "java" };
|
|
13575
13600
|
}
|
|
13576
13601
|
return { name: "node", language: "node" };
|
|
13577
13602
|
}
|
|
13578
13603
|
function findLockfiles(projectDir, stackName) {
|
|
13579
13604
|
const candidates = STACK_LOCKFILES[stackName] ?? [];
|
|
13580
|
-
return candidates.filter((f) => existsSync34(
|
|
13605
|
+
return candidates.filter((f) => existsSync34(join33(projectDir, f)));
|
|
13581
13606
|
}
|
|
13582
13607
|
|
|
13583
13608
|
// src/cli/runner-image-builder.ts
|
|
@@ -13679,12 +13704,12 @@ function generateDockerfile(stack, lockfiles) {
|
|
|
13679
13704
|
function computeLockfileHash(projectDir, lockfiles) {
|
|
13680
13705
|
const hash = createHash("sha256");
|
|
13681
13706
|
for (const lf of lockfiles) {
|
|
13682
|
-
const path =
|
|
13707
|
+
const path = join34(projectDir, lf);
|
|
13683
13708
|
hash.update(lf);
|
|
13684
13709
|
hash.update("\0");
|
|
13685
13710
|
if (existsSync35(path)) {
|
|
13686
13711
|
try {
|
|
13687
|
-
hash.update(
|
|
13712
|
+
hash.update(readFileSync34(path));
|
|
13688
13713
|
} catch {
|
|
13689
13714
|
hash.update("<unreadable>");
|
|
13690
13715
|
}
|
|
@@ -13696,10 +13721,10 @@ function computeLockfileHash(projectDir, lockfiles) {
|
|
|
13696
13721
|
return hash.digest("hex").slice(0, 16);
|
|
13697
13722
|
}
|
|
13698
13723
|
function readLastBuild(projectDir) {
|
|
13699
|
-
const path =
|
|
13724
|
+
const path = join34(projectDir, RUNNER_STATE_DIR, LAST_BUILD_FILE);
|
|
13700
13725
|
if (!existsSync35(path)) return null;
|
|
13701
13726
|
try {
|
|
13702
|
-
const data = JSON.parse(
|
|
13727
|
+
const data = JSON.parse(readFileSync34(path, "utf-8"));
|
|
13703
13728
|
if (data && typeof data === "object" && typeof data.lockfileHash === "string" && typeof data.imageTag === "string" && typeof data.builtAt === "string") {
|
|
13704
13729
|
return data;
|
|
13705
13730
|
}
|
|
@@ -13718,10 +13743,10 @@ function imageTagFor(projectName, hash) {
|
|
|
13718
13743
|
return `beastmode-runner-${safeName}:${hash}`;
|
|
13719
13744
|
}
|
|
13720
13745
|
function writeStateFile(projectDir, fileName, payload) {
|
|
13721
|
-
const dir =
|
|
13746
|
+
const dir = join34(projectDir, RUNNER_STATE_DIR);
|
|
13722
13747
|
mkdirSync21(dir, { recursive: true });
|
|
13723
13748
|
writeFileSync28(
|
|
13724
|
-
|
|
13749
|
+
join34(dir, fileName),
|
|
13725
13750
|
JSON.stringify(payload, null, 2) + "\n",
|
|
13726
13751
|
"utf-8"
|
|
13727
13752
|
);
|
|
@@ -13847,11 +13872,11 @@ function resolveGitHubConfig() {
|
|
|
13847
13872
|
// src/cli/runner-helpers.ts
|
|
13848
13873
|
import { execSync as execSync11, spawn, spawnSync as spawnSync5 } from "child_process";
|
|
13849
13874
|
import { promises as fs } from "fs";
|
|
13850
|
-
import { join as
|
|
13875
|
+
import { join as join35 } from "path";
|
|
13851
13876
|
init_display();
|
|
13852
13877
|
async function writeRunnerMeta(dir, meta) {
|
|
13853
13878
|
await fs.mkdir(dir, { recursive: true });
|
|
13854
|
-
const path =
|
|
13879
|
+
const path = join35(dir, "runner-meta.json");
|
|
13855
13880
|
await fs.writeFile(path, JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
13856
13881
|
}
|
|
13857
13882
|
function resolveRepoSlug() {
|
|
@@ -14058,7 +14083,7 @@ import {
|
|
|
14058
14083
|
writeFileSync as writeFileSync29
|
|
14059
14084
|
} from "fs";
|
|
14060
14085
|
import { homedir as homedir4 } from "os";
|
|
14061
|
-
import { join as
|
|
14086
|
+
import { join as join36, dirname as dirname9 } from "path";
|
|
14062
14087
|
import { Readable } from "stream";
|
|
14063
14088
|
import { pipeline } from "stream/promises";
|
|
14064
14089
|
init_display();
|
|
@@ -14088,7 +14113,7 @@ function runnerDownloadUrl(os, arch) {
|
|
|
14088
14113
|
return `https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-${ghOs}-${arch}-${RUNNER_VERSION}.tar.gz`;
|
|
14089
14114
|
}
|
|
14090
14115
|
async function downloadAndExtractRunner(installDir, os, arch) {
|
|
14091
|
-
const runShPath =
|
|
14116
|
+
const runShPath = join36(installDir, "run.sh");
|
|
14092
14117
|
if (existsSync36(runShPath)) {
|
|
14093
14118
|
info(
|
|
14094
14119
|
`Runner binary already present at ${runShPath} \u2014 skipping download.`
|
|
@@ -14099,7 +14124,7 @@ async function downloadAndExtractRunner(installDir, os, arch) {
|
|
|
14099
14124
|
return;
|
|
14100
14125
|
}
|
|
14101
14126
|
const url = runnerDownloadUrl(os, arch);
|
|
14102
|
-
const tarball =
|
|
14127
|
+
const tarball = join36(installDir, "runner.tar.gz");
|
|
14103
14128
|
setupStep(
|
|
14104
14129
|
`Downloading GitHub Actions runner v${RUNNER_VERSION} (${os}/${arch})...`
|
|
14105
14130
|
);
|
|
@@ -14150,7 +14175,7 @@ async function configureRunner(installDir, repoUrl, token, name, labels) {
|
|
|
14150
14175
|
"--unattended",
|
|
14151
14176
|
"--replace"
|
|
14152
14177
|
];
|
|
14153
|
-
const result = spawnSync6(
|
|
14178
|
+
const result = spawnSync6(join36(installDir, "config.sh"), args, {
|
|
14154
14179
|
cwd: installDir,
|
|
14155
14180
|
stdio: ["ignore", "inherit", "pipe"],
|
|
14156
14181
|
encoding: "utf-8"
|
|
@@ -14178,7 +14203,7 @@ function startRunnerForeground(installDir) {
|
|
|
14178
14203
|
"Use --service to install as a launchd agent for persistent operation."
|
|
14179
14204
|
);
|
|
14180
14205
|
}
|
|
14181
|
-
spawn2(
|
|
14206
|
+
spawn2(join36(installDir, "run.sh"), [], {
|
|
14182
14207
|
cwd: installDir,
|
|
14183
14208
|
stdio: "inherit"
|
|
14184
14209
|
});
|
|
@@ -14203,10 +14228,10 @@ WantedBy=default.target
|
|
|
14203
14228
|
}
|
|
14204
14229
|
async function installSystemdService(installDir, name) {
|
|
14205
14230
|
const unitName = `beastmode-runner-${name}.service`;
|
|
14206
|
-
const unitDir =
|
|
14231
|
+
const unitDir = join36(homedir4(), ".config", "systemd", "user");
|
|
14207
14232
|
mkdirSync22(unitDir, { recursive: true });
|
|
14208
14233
|
writeFileSync29(
|
|
14209
|
-
|
|
14234
|
+
join36(unitDir, unitName),
|
|
14210
14235
|
systemdUnitContent(installDir, name),
|
|
14211
14236
|
"utf-8"
|
|
14212
14237
|
);
|
|
@@ -14260,14 +14285,14 @@ function launchdPlistContent(installDir, name) {
|
|
|
14260
14285
|
}
|
|
14261
14286
|
async function installLaunchdService(installDir, name) {
|
|
14262
14287
|
const label = `com.beastmode.runner.${name}`;
|
|
14263
|
-
const plistPath2 =
|
|
14288
|
+
const plistPath2 = join36(
|
|
14264
14289
|
homedir4(),
|
|
14265
14290
|
"Library",
|
|
14266
14291
|
"LaunchAgents",
|
|
14267
14292
|
`${label}.plist`
|
|
14268
14293
|
);
|
|
14269
|
-
mkdirSync22(
|
|
14270
|
-
mkdirSync22(
|
|
14294
|
+
mkdirSync22(dirname9(plistPath2), { recursive: true });
|
|
14295
|
+
mkdirSync22(join36(installDir, "logs"), { recursive: true });
|
|
14271
14296
|
writeFileSync29(plistPath2, launchdPlistContent(installDir, name), "utf-8");
|
|
14272
14297
|
try {
|
|
14273
14298
|
execSync12(`launchctl load ${plistPath2}`, { stdio: "inherit" });
|
|
@@ -14309,7 +14334,7 @@ async function nativeRunnerSetup(opts) {
|
|
|
14309
14334
|
}
|
|
14310
14335
|
const ghConfig = resolveGitHubConfig();
|
|
14311
14336
|
const repoSlug = opts.repo ?? resolveRepoSlug();
|
|
14312
|
-
const installDir =
|
|
14337
|
+
const installDir = join36(homedir4(), ".beastmode", "runners", opts.name);
|
|
14313
14338
|
if (opts.dryRun) {
|
|
14314
14339
|
info(
|
|
14315
14340
|
`[dry-run] Would install native runner '${opts.name}' for repo ${repoSlug}`
|
|
@@ -14359,11 +14384,11 @@ async function nativeRunnerSetup(opts) {
|
|
|
14359
14384
|
import {
|
|
14360
14385
|
existsSync as existsSync37,
|
|
14361
14386
|
mkdirSync as mkdirSync23,
|
|
14362
|
-
readFileSync as
|
|
14387
|
+
readFileSync as readFileSync35,
|
|
14363
14388
|
unlinkSync as unlinkSync7,
|
|
14364
14389
|
writeFileSync as writeFileSync30
|
|
14365
14390
|
} from "fs";
|
|
14366
|
-
import { dirname as
|
|
14391
|
+
import { dirname as dirname10, join as join37 } from "path";
|
|
14367
14392
|
var TARGET_LABEL = "[self-hosted, self-hosted-beastmode]";
|
|
14368
14393
|
var TARGET_WORKFLOWS = [
|
|
14369
14394
|
".github/workflows/test.yml",
|
|
@@ -14417,7 +14442,7 @@ async function assertOnlineRunnerExists() {
|
|
|
14417
14442
|
}
|
|
14418
14443
|
}
|
|
14419
14444
|
async function switchWorkflows(projectDir) {
|
|
14420
|
-
const statePath =
|
|
14445
|
+
const statePath = join37(projectDir, STATE_FILE);
|
|
14421
14446
|
if (existsSync37(statePath)) {
|
|
14422
14447
|
return { alreadySwitched: true, files: [], skipped: [] };
|
|
14423
14448
|
}
|
|
@@ -14430,12 +14455,12 @@ async function switchWorkflows(projectDir) {
|
|
|
14430
14455
|
const resultFiles = [];
|
|
14431
14456
|
const skipped = [];
|
|
14432
14457
|
for (const relPath of TARGET_WORKFLOWS) {
|
|
14433
|
-
const absPath =
|
|
14458
|
+
const absPath = join37(projectDir, relPath);
|
|
14434
14459
|
if (!existsSync37(absPath)) {
|
|
14435
14460
|
skipped.push({ relativePath: relPath, reason: "missing" });
|
|
14436
14461
|
continue;
|
|
14437
14462
|
}
|
|
14438
|
-
const content =
|
|
14463
|
+
const content = readFileSync35(absPath, "utf-8");
|
|
14439
14464
|
const { newContent, originals } = replaceRunsOn(content, TARGET_LABEL);
|
|
14440
14465
|
if (originals.length === 0) {
|
|
14441
14466
|
skipped.push({ relativePath: relPath, reason: "no_runs_on" });
|
|
@@ -14451,25 +14476,25 @@ async function switchWorkflows(projectDir) {
|
|
|
14451
14476
|
state.files.push({ relativePath: relPath, originals });
|
|
14452
14477
|
resultFiles.push({ relativePath: relPath, jobCount: originals.length });
|
|
14453
14478
|
}
|
|
14454
|
-
mkdirSync23(
|
|
14479
|
+
mkdirSync23(dirname10(statePath), { recursive: true });
|
|
14455
14480
|
writeFileSync30(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
14456
14481
|
return { alreadySwitched: false, files: resultFiles, skipped };
|
|
14457
14482
|
}
|
|
14458
14483
|
async function restoreWorkflows(projectDir) {
|
|
14459
|
-
const statePath =
|
|
14484
|
+
const statePath = join37(projectDir, STATE_FILE);
|
|
14460
14485
|
if (!existsSync37(statePath)) {
|
|
14461
14486
|
return { nothingToRestore: true, files: [] };
|
|
14462
14487
|
}
|
|
14463
14488
|
const state = JSON.parse(
|
|
14464
|
-
|
|
14489
|
+
readFileSync35(statePath, "utf-8")
|
|
14465
14490
|
);
|
|
14466
14491
|
const resultFiles = [];
|
|
14467
14492
|
for (const fileState of state.files) {
|
|
14468
|
-
const absPath =
|
|
14493
|
+
const absPath = join37(projectDir, fileState.relativePath);
|
|
14469
14494
|
if (!existsSync37(absPath)) {
|
|
14470
14495
|
throw new Error(`Workflow file not found: ${fileState.relativePath}`);
|
|
14471
14496
|
}
|
|
14472
|
-
const content =
|
|
14497
|
+
const content = readFileSync35(absPath, "utf-8");
|
|
14473
14498
|
const newContent = restoreRunsOn(content, fileState.originals);
|
|
14474
14499
|
writeFileSync30(absPath, newContent, "utf-8");
|
|
14475
14500
|
resultFiles.push({
|
|
@@ -14484,7 +14509,7 @@ async function restoreWorkflows(projectDir) {
|
|
|
14484
14509
|
// src/cli/commands/runner-cmd.ts
|
|
14485
14510
|
var runnerCommand = new Command23("runner").description("Manage self-hosted GitHub Actions runners");
|
|
14486
14511
|
function resolveProjectName(projectDir) {
|
|
14487
|
-
const projectsDir =
|
|
14512
|
+
const projectsDir = join38(projectDir, ".beastmode", "projects");
|
|
14488
14513
|
if (existsSync38(projectsDir)) {
|
|
14489
14514
|
try {
|
|
14490
14515
|
const records = listProjectRecords(projectsDir);
|
|
@@ -14874,16 +14899,16 @@ runnerCommand.command("build-image").description(
|
|
|
14874
14899
|
// src/cli/commands/project-cmd.ts
|
|
14875
14900
|
init_engine();
|
|
14876
14901
|
import { Command as Command24 } from "commander";
|
|
14877
|
-
import { existsSync as existsSync39, mkdirSync as mkdirSync24, readFileSync as
|
|
14878
|
-
import { join as
|
|
14902
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync24, readFileSync as readFileSync36, renameSync as renameSync3 } from "fs";
|
|
14903
|
+
import { join as join39, resolve as resolve21, basename as basename9 } from "path";
|
|
14879
14904
|
import { execSync as execSync13 } from "child_process";
|
|
14880
14905
|
var DEFAULT_MAX_PROJECTS = 5;
|
|
14881
14906
|
var MIN_DISK_WARNING_GB = 10;
|
|
14882
14907
|
function getMaxProjects(factoryDir) {
|
|
14883
|
-
const configPath =
|
|
14908
|
+
const configPath = join39(factoryDir, ".beastmode", "config.json");
|
|
14884
14909
|
if (existsSync39(configPath)) {
|
|
14885
14910
|
try {
|
|
14886
|
-
const config = JSON.parse(
|
|
14911
|
+
const config = JSON.parse(readFileSync36(configPath, "utf-8"));
|
|
14887
14912
|
if (typeof config.max_projects === "number") return config.max_projects;
|
|
14888
14913
|
} catch {
|
|
14889
14914
|
}
|
|
@@ -14904,8 +14929,8 @@ function projectAddAction(factoryDir, projectPath, opts) {
|
|
|
14904
14929
|
const resolvedPath = resolve21(projectPath);
|
|
14905
14930
|
if (!existsSync39(resolvedPath)) throw new Error(`Directory not found: ${resolvedPath}`);
|
|
14906
14931
|
const projectName = opts.name || basename9(resolvedPath);
|
|
14907
|
-
const projectsDir =
|
|
14908
|
-
if (existsSync39(
|
|
14932
|
+
const projectsDir = join39(factoryDir, ".beastmode", "projects");
|
|
14933
|
+
if (existsSync39(join39(projectsDir, projectName, "project.json"))) {
|
|
14909
14934
|
throw new Error(`Project already exists: ${projectName}`);
|
|
14910
14935
|
}
|
|
14911
14936
|
const currentCount = listProjectRecords(projectsDir).length;
|
|
@@ -14956,7 +14981,7 @@ function projectAddAction(factoryDir, projectPath, opts) {
|
|
|
14956
14981
|
} : void 0
|
|
14957
14982
|
});
|
|
14958
14983
|
writeProjectRecord(projectsDir, projectName, record);
|
|
14959
|
-
const runsDir =
|
|
14984
|
+
const runsDir = join39(factoryDir, "runs", projectName);
|
|
14960
14985
|
if (!existsSync39(runsDir)) mkdirSync24(runsDir, { recursive: true });
|
|
14961
14986
|
if (!boardId) {
|
|
14962
14987
|
void (async () => {
|
|
@@ -14986,15 +15011,15 @@ function projectAddAction(factoryDir, projectPath, opts) {
|
|
|
14986
15011
|
}
|
|
14987
15012
|
}
|
|
14988
15013
|
function projectListAction(factoryDir) {
|
|
14989
|
-
const projectsDir =
|
|
15014
|
+
const projectsDir = join39(factoryDir, ".beastmode", "projects");
|
|
14990
15015
|
return listProjectRecords(projectsDir);
|
|
14991
15016
|
}
|
|
14992
15017
|
function projectRemoveAction(factoryDir, name) {
|
|
14993
|
-
const projectDir =
|
|
15018
|
+
const projectDir = join39(factoryDir, ".beastmode", "projects", name);
|
|
14994
15019
|
if (!existsSync39(projectDir)) throw new Error(`Project not found: ${name}`);
|
|
14995
|
-
const archivedBase =
|
|
15020
|
+
const archivedBase = join39(factoryDir, ".beastmode", "projects", ".archived");
|
|
14996
15021
|
mkdirSync24(archivedBase, { recursive: true });
|
|
14997
|
-
renameSync3(projectDir,
|
|
15022
|
+
renameSync3(projectDir, join39(archivedBase, name));
|
|
14998
15023
|
}
|
|
14999
15024
|
var projectCommand = new Command24("project").description("Manage projects in this factory");
|
|
15000
15025
|
projectCommand.command("add <path>").description("Register a project").option("--name <name>", "Override project name").option("--board-id <id>", "Link to existing board ID").action((path, opts) => {
|
|
@@ -15021,7 +15046,7 @@ projectCommand.command("remove <name>").description("Archive a project").action(
|
|
|
15021
15046
|
init_display();
|
|
15022
15047
|
init_board();
|
|
15023
15048
|
import { Command as Command25 } from "commander";
|
|
15024
|
-
import { join as
|
|
15049
|
+
import { join as join40 } from "path";
|
|
15025
15050
|
import { existsSync as existsSync40, writeFileSync as writeFileSync31, mkdirSync as mkdirSync25 } from "fs";
|
|
15026
15051
|
var redeployCommand = new Command25("redeploy").description("Trigger a rebuild + restart of a BeastMode service").argument("<service>", "Service to redeploy (currently: daemon)").action(async (service) => {
|
|
15027
15052
|
try {
|
|
@@ -15043,8 +15068,8 @@ async function runRedeploy(service) {
|
|
|
15043
15068
|
"No BeastMode factory found. Run 'beastmode init' first."
|
|
15044
15069
|
);
|
|
15045
15070
|
}
|
|
15046
|
-
const markerDir =
|
|
15047
|
-
const markerPath =
|
|
15071
|
+
const markerDir = join40(factoryDir, "daemon", "logs");
|
|
15072
|
+
const markerPath = join40(markerDir, ".daemon-rebuild-requested");
|
|
15048
15073
|
if (!existsSync40(markerDir)) {
|
|
15049
15074
|
mkdirSync25(markerDir, { recursive: true });
|
|
15050
15075
|
}
|