@osfactory/har 0.12.1 → 0.13.1

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
@@ -23367,7 +23367,7 @@ async function handleAgentSkills(options) {
23367
23367
  const { repoPath, mode } = options;
23368
23368
  if (options.enabled === false) return false;
23369
23369
  let targets;
23370
- if (options.agents !== void 0) {
23370
+ if (typeof options.agents === "string") {
23371
23371
  targets = parseAgentTargets(options.agents);
23372
23372
  } else {
23373
23373
  targets = detectAgentTargets(repoPath);
@@ -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()) {
@@ -31075,19 +31109,14 @@ var envCommand = {
31075
31109
  describe: "Auto-apply AGENT.md proposal without prompting (--auto only)"
31076
31110
  }).option("cursor-rule", {
31077
31111
  type: "boolean",
31078
- default: false,
31079
- describe: "Create .cursor/rules/har-workflow.mdc without prompting"
31080
- }).option("no-cursor-rule", {
31081
- type: "boolean",
31082
- default: false,
31083
- describe: "Skip Cursor rule scaffolding"
31112
+ // No default: unset → prompt/auto-detect; --cursor-rule → true; --no-cursor-rule → false
31113
+ // (do not declare a separate --no-cursor-rule option — yargs negation owns that.)
31114
+ describe: "Create .cursor/rules/har-workflow.mdc without prompting (use --no-cursor-rule to skip)"
31084
31115
  }).option("agents", {
31085
31116
  type: "string",
31086
- describe: "Scaffold agent skills for these targets (comma-separated: claude,cursor,codex); auto-detected when omitted"
31087
- }).option("no-agents", {
31088
- type: "boolean",
31089
- default: false,
31090
- describe: "Skip agent skills scaffolding"
31117
+ // --no-agents is yargs negation of this string option (sets agents=false). Do not
31118
+ // declare a separate --no-agents boolean — it collides and crashes parseAgentTargets.
31119
+ describe: "Scaffold agent skills for these targets (comma-separated: claude,cursor,codex); auto-detected when omitted; --no-agents to skip"
31091
31120
  }),
31092
31121
  handleInit
31093
31122
  ).command(
@@ -31106,19 +31135,11 @@ var envCommand = {
31106
31135
  describe: "Adaptation summary to store in the manifest (--finalize only)"
31107
31136
  }).option("cursor-rule", {
31108
31137
  type: "boolean",
31109
- default: false,
31110
- describe: "Create .cursor/rules/har-workflow.mdc without prompting"
31111
- }).option("no-cursor-rule", {
31112
- type: "boolean",
31113
- default: false,
31114
- describe: "Skip Cursor rule scaffolding"
31138
+ // No default: unset → prompt/auto-detect; --cursor-rule → true; --no-cursor-rule → false
31139
+ describe: "Create .cursor/rules/har-workflow.mdc without prompting (use --no-cursor-rule to skip)"
31115
31140
  }).option("agents", {
31116
31141
  type: "string",
31117
- describe: "Scaffold agent skills for these targets (comma-separated: claude,cursor,codex); auto-detected when omitted"
31118
- }).option("no-agents", {
31119
- type: "boolean",
31120
- default: false,
31121
- describe: "Skip agent skills scaffolding"
31142
+ describe: "Scaffold agent skills for these targets (comma-separated: claude,cursor,codex); auto-detected when omitted; --no-agents to skip"
31122
31143
  }),
31123
31144
  handleMaintain
31124
31145
  ).command(
@@ -31294,14 +31315,13 @@ async function handleInit(argv) {
31294
31315
  recordRepoForControlSync(repoPath);
31295
31316
  await handleCursorRule({
31296
31317
  repoPath,
31297
- cursorRule: resolveCursorRuleFlag(argv.cursorRule, argv.noCursorRule),
31318
+ cursorRule: resolveCursorRuleFlag(argv.cursorRule),
31298
31319
  autoYes: argv.yes,
31299
31320
  mode: "init"
31300
31321
  });
31301
31322
  await handleAgentSkills({
31302
31323
  repoPath,
31303
- agents: argv.agents,
31304
- enabled: argv.noAgents ? false : void 0,
31324
+ ...resolveAgentsScaffoldOptions(argv.agents),
31305
31325
  autoYes: argv.yes,
31306
31326
  force: argv.force,
31307
31327
  mode: "init"
@@ -31367,14 +31387,13 @@ async function handleMaintain(argv) {
31367
31387
  }
31368
31388
  await handleCursorRule({
31369
31389
  repoPath,
31370
- cursorRule: resolveCursorRuleFlag(argv.cursorRule, argv.noCursorRule),
31390
+ cursorRule: resolveCursorRuleFlag(argv.cursorRule),
31371
31391
  autoYes: argv.yes,
31372
31392
  mode: "maintain"
31373
31393
  });
31374
31394
  await handleAgentSkills({
31375
31395
  repoPath,
31376
- agents: argv.agents,
31377
- enabled: argv.noAgents ? false : void 0,
31396
+ ...resolveAgentsScaffoldOptions(argv.agents),
31378
31397
  autoYes: argv.yes,
31379
31398
  mode: "maintain"
31380
31399
  });
@@ -31383,10 +31402,13 @@ async function handleMaintain(argv) {
31383
31402
  process.exit(1);
31384
31403
  }
31385
31404
  }
31386
- function resolveCursorRuleFlag(cursorRule, noCursorRule) {
31387
- if (noCursorRule) return false;
31388
- if (cursorRule) return true;
31389
- return void 0;
31405
+ function resolveCursorRuleFlag(cursorRule) {
31406
+ return cursorRule;
31407
+ }
31408
+ function resolveAgentsScaffoldOptions(agents) {
31409
+ if (agents === false) return { enabled: false };
31410
+ if (typeof agents === "string") return { agents };
31411
+ return {};
31390
31412
  }
31391
31413
  function emitManualAdaptationPrompt(repoPath, mode, profile = "default", bundleReport) {
31392
31414
  const prompt = mode === "init" ? buildInitAdaptationPrompt(repoPath, profile) : buildMaintainAdaptationPrompt(repoPath, bundleReport);
@@ -31750,7 +31772,7 @@ var controlCommand = {
31750
31772
  describe: "Mission Control dashboard (local)",
31751
31773
  builder: (yargs) => yargs.command(
31752
31774
  "up",
31753
- "Start Mission Control (Docker Compose)",
31775
+ "Start Mission Control (single Docker container, SQLite)",
31754
31776
  (y2) => y2.option("detach", { alias: "d", type: "boolean", default: true }).option("build", {
31755
31777
  type: "boolean",
31756
31778
  default: false,
@@ -31843,7 +31865,7 @@ async function handleUp(argv) {
31843
31865
  }
31844
31866
  async function handleDown() {
31845
31867
  header("har control down");
31846
- const code = runDockerCompose(["down"]);
31868
+ const code = stopMissionControl();
31847
31869
  process.exit(code);
31848
31870
  }
31849
31871
  async function handleRegister(argv) {
@@ -39867,10 +39889,10 @@ var HAR_MCP_TOOLS = [
39867
39889
  },
39868
39890
  {
39869
39891
  name: "har_control_up",
39870
- description: "Start local Mission Control (Docker Compose) and sync all harness repositories that were initialized with har env init.",
39892
+ 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
39893
  inputSchema: objectJsonSchema({
39872
39894
  repo: repoJsonProperty,
39873
- detach: { type: "boolean", description: "Run Docker Compose in detached mode (default true)" }
39895
+ detach: { type: "boolean", description: "Run the container in detached mode (default true)" }
39874
39896
  })
39875
39897
  }
39876
39898
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osfactory/har",
3
- "version": "0.12.1",
3
+ "version": "0.13.1",
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: