@osfactory/har 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30532,54 +30532,88 @@ function shouldBuildControlLocally() {
30532
30532
  }
30533
30533
 
30534
30534
  // src/core/control-lifecycle.ts
30535
+ var CONTROL_CONTAINER_NAME = "har-control";
30536
+ var CONTROL_DATA_VOLUME = "har_control_data";
30537
+ var CONTROL_CONTAINER_PORT = 3847;
30538
+ var CONTROL_DATA_DB_URL = "file:/data/har_control.db";
30535
30539
  function resolveControlDir() {
30536
30540
  return path38.resolve(__dirname, "..", "control");
30537
30541
  }
30538
- function resolveControlComposeFiles(options) {
30539
- const controlDir = resolveControlDir();
30540
- const files = [path38.join(controlDir, "docker-compose.yml")];
30541
- if (options?.build ?? shouldBuildControlLocally()) {
30542
- files.push(path38.join(controlDir, "docker-compose.build.yml"));
30543
- }
30544
- return files;
30542
+ function resolveControlBuildContext() {
30543
+ return path38.resolve(resolveControlDir(), "..");
30545
30544
  }
30546
- function buildDockerComposeEnv() {
30547
- return {
30548
- ...process.env,
30549
- HAR_CONTROL_IMAGE: getControlImageName(),
30550
- HAR_CONTROL_IMAGE_TAG: getControlImageTag()
30551
- };
30545
+ function buildDockerRunArgs(options) {
30546
+ const name = options.containerName ?? CONTROL_CONTAINER_NAME;
30547
+ const volume = options.volume ?? CONTROL_DATA_VOLUME;
30548
+ const args = ["run", "--name", name];
30549
+ if (options.detach) {
30550
+ args.push("-d", "--restart", "unless-stopped");
30551
+ } else {
30552
+ args.push("--rm");
30553
+ }
30554
+ args.push(
30555
+ "-p",
30556
+ `${options.hostPort}:${CONTROL_CONTAINER_PORT}`,
30557
+ "-v",
30558
+ `${volume}:/data`,
30559
+ "-e",
30560
+ `DATABASE_URL=${CONTROL_DATA_DB_URL}`,
30561
+ options.imageRef
30562
+ );
30563
+ return args;
30552
30564
  }
30553
- function runDockerCompose(args, options) {
30554
- const controlDir = resolveControlDir();
30555
- const composeFiles = resolveControlComposeFiles(options);
30556
- const composeArgs = composeFiles.flatMap((file) => ["-f", file]);
30557
- const result = (0, import_child_process8.spawnSync)("docker", ["compose", ...composeArgs, ...args], {
30558
- cwd: controlDir,
30565
+ function runDocker(args) {
30566
+ const result = (0, import_child_process8.spawnSync)("docker", args, {
30559
30567
  stdio: "inherit",
30560
- env: buildDockerComposeEnv()
30568
+ env: process.env
30561
30569
  });
30562
30570
  if (result.error) {
30563
30571
  throw result.error;
30564
30572
  }
30565
30573
  return result.status ?? 1;
30566
30574
  }
30575
+ function controlContainerExists(name = CONTROL_CONTAINER_NAME) {
30576
+ const result = (0, import_child_process8.spawnSync)("docker", ["ps", "-aq", "-f", `name=^${name}$`], {
30577
+ encoding: "utf8",
30578
+ stdio: ["pipe", "pipe", "ignore"]
30579
+ });
30580
+ return (result.stdout ?? "").trim().length > 0;
30581
+ }
30582
+ function stopMissionControl() {
30583
+ if (!controlContainerExists()) {
30584
+ return 0;
30585
+ }
30586
+ return runDocker(["rm", "-f", CONTROL_CONTAINER_NAME]);
30587
+ }
30567
30588
  async function startMissionControl(options) {
30568
30589
  const build = options.build ?? shouldBuildControlLocally();
30569
30590
  const imageRef = getControlImageRef();
30570
- if (!build) {
30571
- const pullCode = runDockerCompose(["pull"], { build: false });
30591
+ const apiUrl = getControlApiUrl();
30592
+ const hostPort = parseControlHostPort(apiUrl);
30593
+ if (build) {
30594
+ const buildCode = runDocker([
30595
+ "build",
30596
+ "-t",
30597
+ imageRef,
30598
+ "-f",
30599
+ path38.join(resolveControlDir(), "Dockerfile"),
30600
+ resolveControlBuildContext()
30601
+ ]);
30602
+ if (buildCode !== 0) {
30603
+ return { code: buildCode, apiUrl, imageRef };
30604
+ }
30605
+ } else {
30606
+ const pullCode = runDocker(["pull", imageRef]);
30572
30607
  if (pullCode !== 0) {
30573
- return { code: pullCode, apiUrl: getControlApiUrl(), imageRef };
30608
+ return { code: pullCode, apiUrl, imageRef };
30574
30609
  }
30575
30610
  }
30576
- const detach = options.detach !== false;
30577
- const upArgs = detach ? ["up", "-d"] : ["up"];
30578
- if (build) {
30579
- upArgs.push("--build");
30611
+ if (controlContainerExists()) {
30612
+ runDocker(["rm", "-f", CONTROL_CONTAINER_NAME]);
30580
30613
  }
30581
- const code = runDockerCompose(upArgs, { build });
30582
- return { code, apiUrl: getControlApiUrl(), imageRef };
30614
+ const detach = options.detach !== false;
30615
+ const code = runDocker(buildDockerRunArgs({ imageRef, hostPort, detach }));
30616
+ return { code, apiUrl, imageRef };
30583
30617
  }
30584
30618
  async function syncReposAfterControlStart(cwd) {
30585
30619
  if (!isControlEnabled()) {
@@ -31750,7 +31784,7 @@ var controlCommand = {
31750
31784
  describe: "Mission Control dashboard (local)",
31751
31785
  builder: (yargs) => yargs.command(
31752
31786
  "up",
31753
- "Start Mission Control (Docker Compose)",
31787
+ "Start Mission Control (single Docker container, SQLite)",
31754
31788
  (y2) => y2.option("detach", { alias: "d", type: "boolean", default: true }).option("build", {
31755
31789
  type: "boolean",
31756
31790
  default: false,
@@ -31843,7 +31877,7 @@ async function handleUp(argv) {
31843
31877
  }
31844
31878
  async function handleDown() {
31845
31879
  header("har control down");
31846
- const code = runDockerCompose(["down"]);
31880
+ const code = stopMissionControl();
31847
31881
  process.exit(code);
31848
31882
  }
31849
31883
  async function handleRegister(argv) {
@@ -39867,10 +39901,10 @@ var HAR_MCP_TOOLS = [
39867
39901
  },
39868
39902
  {
39869
39903
  name: "har_control_up",
39870
- description: "Start local Mission Control (Docker Compose) and sync all harness repositories that were initialized with har env init.",
39904
+ description: "Start local Mission Control (a single self-contained Docker container backed by SQLite) and sync all harness repositories that were initialized with har env init.",
39871
39905
  inputSchema: objectJsonSchema({
39872
39906
  repo: repoJsonProperty,
39873
- detach: { type: "boolean", description: "Run Docker Compose in detached mode (default true)" }
39907
+ detach: { type: "boolean", description: "Run the container in detached mode (default true)" }
39874
39908
  })
39875
39909
  }
39876
39910
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osfactory/har",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "LLM-powered CLI for reproducible agent development environments",
5
5
  "keywords": [
6
6
  "har",
@@ -1,7 +0,0 @@
1
- # Local development override — merged when `har control up --build` or HAR_CONTROL_BUILD=true.
2
- services:
3
- app:
4
- build:
5
- context: ..
6
- dockerfile: control/Dockerfile
7
- pull_policy: build
@@ -1,31 +0,0 @@
1
- services:
2
- db:
3
- image: postgres:16-alpine
4
- environment:
5
- POSTGRES_USER: har
6
- POSTGRES_PASSWORD: har
7
- POSTGRES_DB: har_control
8
- ports:
9
- - "5433:5432"
10
- volumes:
11
- - har_control_pg:/var/lib/postgresql/data
12
- healthcheck:
13
- test: ["CMD-SHELL", "pg_isready -U har -d har_control"]
14
- interval: 5s
15
- timeout: 5s
16
- retries: 5
17
-
18
- app:
19
- image: ${HAR_CONTROL_IMAGE:-theosfactory/har-control}:${HAR_CONTROL_IMAGE_TAG}
20
- pull_policy: always
21
- ports:
22
- - "3847:3847"
23
- environment:
24
- DATABASE_URL: postgresql://har:har@db:5432/har_control
25
- NODE_ENV: production
26
- depends_on:
27
- db:
28
- condition: service_healthy
29
-
30
- volumes:
31
- har_control_pg: