@osfactory/har 1.17.0 → 1.19.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
@@ -36568,6 +36568,29 @@ var init_control_config = __esm({
36568
36568
  }
36569
36569
  });
36570
36570
 
36571
+ // src/core/portal-hosts.ts
36572
+ function kernoApiUrlFor(portalUrl) {
36573
+ const override = process.env.HAR_API_URL?.trim().replace(/\/+$/, "");
36574
+ if (override) return override;
36575
+ try {
36576
+ return KERNO_API_BY_PORTAL_HOST[new URL(portalUrl).hostname];
36577
+ } catch {
36578
+ return void 0;
36579
+ }
36580
+ }
36581
+ var KERNO_API_BY_PORTAL_HOST;
36582
+ var init_portal_hosts = __esm({
36583
+ "src/core/portal-hosts.ts"() {
36584
+ "use strict";
36585
+ KERNO_API_BY_PORTAL_HOST = {
36586
+ "portal.kerno.io": "https://api.kerno.io",
36587
+ "app.harhq.com": "https://api.kerno.io",
36588
+ "portal.dev.kerno.io": "https://api.dev.kerno.io",
36589
+ "app.dev.harhq.com": "https://api.dev.kerno.io"
36590
+ };
36591
+ }
36592
+ });
36593
+
36571
36594
  // src/core/commit-bindings.ts
36572
36595
  function bindingsDir(harnessRoot) {
36573
36596
  return path11.join(harnessRoot, ".har", COMMIT_BINDINGS_DIR);
@@ -44307,7 +44330,8 @@ function rewindSince(since) {
44307
44330
  return new Date(new Date(since).getTime() - overlap).toISOString();
44308
44331
  }
44309
44332
  function watermarkKeyForTarget(target) {
44310
- return target.identityKey;
44333
+ const apiUrl = kernoApiUrlFor(target.url);
44334
+ return apiUrl ? `${apiUrl}|${target.identityKey}` : target.identityKey;
44311
44335
  }
44312
44336
  function legacyWatermarkKeysForTarget(target) {
44313
44337
  return target.url === target.identityKey ? [] : [target.url];
@@ -44397,9 +44421,14 @@ async function postJson(url, body, dryRun) {
44397
44421
  }
44398
44422
  return await response.json();
44399
44423
  }
44424
+ function portalEndpointUrl(target, endpoint) {
44425
+ const apiUrl = kernoApiUrlFor(target.url);
44426
+ if (!apiUrl) return `${target.url}${endpoint}`;
44427
+ return `${apiUrl}${KERNO_INGEST_PREFIX}${endpoint.slice(PORTAL_API_PREFIX.length)}`;
44428
+ }
44400
44429
  function postPortalOnce(target, endpoint, body) {
44401
44430
  return postForResponse(
44402
- `${target.url}${endpoint}`,
44431
+ portalEndpointUrl(target, endpoint),
44403
44432
  {
44404
44433
  "Content-Type": "application/json",
44405
44434
  Authorization: `Bearer ${target.token}`
@@ -44411,7 +44440,7 @@ async function refreshPortalToken(target) {
44411
44440
  if (!target.refreshToken) return false;
44412
44441
  let response;
44413
44442
  try {
44414
- response = await fetch(`${target.url}/api/cli/refresh`, {
44443
+ response = await fetch(portalEndpointUrl(target, "/api/cli/refresh"), {
44415
44444
  method: "POST",
44416
44445
  headers: { Authorization: `Bearer ${target.refreshToken}` }
44417
44446
  });
@@ -45167,7 +45196,7 @@ async function syncRunWithControlAsync(repoPath, run2) {
45167
45196
  if (!isRepoPortalSyncEnabled(canonical)) return;
45168
45197
  for (const portal of resolved.targets) {
45169
45198
  try {
45170
- if (!await isControlApiReachable(portal.url)) continue;
45199
+ if (!kernoApiUrlFor(portal.url) && !await isControlApiReachable(portal.url)) continue;
45171
45200
  await postPortal(portal, "/api/sync", { path: canonical, runs: [run2] });
45172
45201
  } catch (err) {
45173
45202
  const message = err instanceof Error ? err.message : String(err);
@@ -45176,7 +45205,7 @@ async function syncRunWithControlAsync(repoPath, run2) {
45176
45205
  }
45177
45206
  }
45178
45207
  }
45179
- var path48, DEFAULT_MAX_BATCH_BYTES, DEFAULT_SYNC_OVERLAP_MS, MAX_FETCH_ATTEMPTS, SYNC_TIMEOUT_MS, EXPLICIT_SYNC_TIMEOUT_MS;
45208
+ var path48, DEFAULT_MAX_BATCH_BYTES, DEFAULT_SYNC_OVERLAP_MS, MAX_FETCH_ATTEMPTS, PORTAL_API_PREFIX, KERNO_INGEST_PREFIX, SYNC_TIMEOUT_MS, EXPLICIT_SYNC_TIMEOUT_MS;
45180
45209
  var init_control_sync = __esm({
45181
45210
  "src/core/control-sync.ts"() {
45182
45211
  "use strict";
@@ -45185,6 +45214,7 @@ var init_control_sync = __esm({
45185
45214
  init_stages();
45186
45215
  init_schema2();
45187
45216
  init_control_config();
45217
+ init_portal_hosts();
45188
45218
  init_control_registry();
45189
45219
  init_portal_credentials();
45190
45220
  init_portal_targets();
@@ -45207,6 +45237,8 @@ var init_control_sync = __esm({
45207
45237
  DEFAULT_MAX_BATCH_BYTES = 4 * 1024 * 1024;
45208
45238
  DEFAULT_SYNC_OVERLAP_MS = 6e4;
45209
45239
  MAX_FETCH_ATTEMPTS = 3;
45240
+ PORTAL_API_PREFIX = "/api";
45241
+ KERNO_INGEST_PREFIX = "/api/har/ingest";
45210
45242
  SYNC_TIMEOUT_MS = 15e3;
45211
45243
  EXPLICIT_SYNC_TIMEOUT_MS = 12e4;
45212
45244
  }
@@ -81973,7 +82005,9 @@ function buildInstallPlan(detection, targets, options = {}) {
81973
82005
  agentsMd: writeAgentsMd,
81974
82006
  migrateLegacyAgentMd: writeAgentsMd && detection.agentMd,
81975
82007
  providers: targets,
81976
- claudeMd: targets.includes("claude"),
82008
+ // Deprecated: Claude Code reads AGENTS.md directly, so HAR no longer
82009
+ // creates a CLAUDE.md. An existing one is still kept pointing at AGENTS.md.
82010
+ claudeMd: targets.includes("claude") && detection.claudeMd,
81977
82011
  cursorRule: wantsCursor,
81978
82012
  skills: options.skills ?? []
81979
82013
  };
@@ -81987,7 +82021,7 @@ function formatInstallPlan(plan, detection) {
81987
82021
  lines.push(` ${mark(true)} Migrate ${LEGACY_AGENT_MD} \u2192 ${AGENTS_MD}, then remove legacy file`);
81988
82022
  }
81989
82023
  lines.push(
81990
- ` ${mark(plan.claudeMd)} Claude \u2014 ensure ${CLAUDE_MD} points at ${AGENTS_MD}`
82024
+ plan.claudeMd ? ` ${mark(true)} Claude \u2014 keep the existing ${CLAUDE_MD} pointing at ${AGENTS_MD}` : ` ${mark(false)} Claude \u2014 reads ${AGENTS_MD} directly (no ${CLAUDE_MD} created)`
81991
82025
  );
81992
82026
  lines.push(
81993
82027
  ` ${mark(plan.cursorRule)} Cursor \u2014 install/refresh .cursor/rules/har-workflow.mdc`
@@ -82121,13 +82155,6 @@ ${legacyTrimmed}
82121
82155
  return true;
82122
82156
  }
82123
82157
  var CLAUDE_MD_IMPORT = "@AGENTS.md";
82124
- function loadClaudeMdTemplate() {
82125
- const templatePath = resolveTemplateFile("CLAUDE.md.template");
82126
- if (!templatePath) {
82127
- throw new Error("CLAUDE.md.template not found. Run npm run build.");
82128
- }
82129
- return fs51.readFileSync(templatePath, "utf8");
82130
- }
82131
82158
  function isHarOwnedClaudeMd(content) {
82132
82159
  const trimmed = content.trim();
82133
82160
  if (trimmed === CLAUDE_MD_IMPORT) return true;
@@ -82139,11 +82166,9 @@ function hasAgentsImport(content) {
82139
82166
  }
82140
82167
  function ensureClaudeMdPointer(repoPath, options = {}) {
82141
82168
  const dest = path59.join(repoPath, CLAUDE_MD);
82142
- const thin = loadClaudeMdTemplate();
82143
- if (!fs51.existsSync(dest)) {
82144
- writeFileSafe(dest, thin);
82145
- return "created";
82146
- }
82169
+ if (!fs51.existsSync(dest)) return "skipped";
82170
+ const thin = `${CLAUDE_MD_IMPORT}
82171
+ `;
82147
82172
  const existing = fs51.readFileSync(dest, "utf8");
82148
82173
  if (isHarOwnedClaudeMd(existing) || options.force) {
82149
82174
  if (existing.trim() === CLAUDE_MD_IMPORT) return "skipped";
@@ -82251,9 +82276,7 @@ async function handleInstructionFiles(options) {
82251
82276
  }
82252
82277
  if (plan.claudeMd) {
82253
82278
  claudeMdAction = ensureClaudeMdPointer(repoPath);
82254
- if (claudeMdAction === "created") {
82255
- success(`Wrote ${CLAUDE_MD} (pointer \u2192 ${AGENTS_MD})`);
82256
- } else if (claudeMdAction === "updated" || claudeMdAction === "appended") {
82279
+ if (claudeMdAction === "updated" || claudeMdAction === "appended") {
82257
82280
  info(
82258
82281
  `${claudeMdAction === "updated" ? "Updated" : "Appended"} ${CLAUDE_MD} pointer \u2192 ${AGENTS_MD}`
82259
82282
  );
@@ -83681,13 +83704,7 @@ function buildInstructionFileNotes(repoPath) {
83681
83704
  notes.push({
83682
83705
  kind: "claude_md_pointer",
83683
83706
  path: "CLAUDE.md",
83684
- message: "Keep CLAUDE.md as the `@AGENTS.md` import \u2014 no workflow content of its own. Existing project-specific notes stay; the import goes on top."
83685
- });
83686
- } else {
83687
- notes.push({
83688
- kind: "claude_md_pointer",
83689
- path: "CLAUDE.md",
83690
- message: "Optional: add a CLAUDE.md containing `@AGENTS.md` when Claude Code is used on this repo."
83707
+ message: "Keep CLAUDE.md as the `@AGENTS.md` import \u2014 no workflow content of its own. Existing project-specific notes stay; the import goes on top. It is optional now that Claude Code reads AGENTS.md directly."
83691
83708
  });
83692
83709
  }
83693
83710
  return notes;
@@ -202,7 +202,7 @@ Launch creates an isolated session worktree at `~/worktrees/<base>-<sha4>-har-ag
202
202
 
203
203
  Coding agents discover the harness through two files:
204
204
 
205
- 1. **`AGENTS.md`** (repo root) — shared agent instructions (Codex auto-loads this; Claude via `CLAUDE.md` pointer)
205
+ 1. **`AGENTS.md`** (repo root) — shared agent instructions (Codex and Claude Code both auto-load this)
206
206
  2. **`.har/README.md`** — full index of what's in the harness
207
207
 
208
208
  Do **not** create `AGENT.md` (singular) — that legacy name is obsolete. If `AGENT.md` exists, merge any unique notes into `AGENTS.md` and delete `AGENT.md`.
@@ -219,14 +219,14 @@ If **no `AGENTS.md` exists**, create one at the repo root using this structure:
219
219
 
220
220
  If **`AGENTS.md` already exists** (project-owned), add or update a concise **HAR / agent environment** section — do not replace unrelated content.
221
221
 
222
- Keep **`CLAUDE.md`** as the line `@AGENTS.md` (Claude Code auto-loads `CLAUDE.md` and follows the import). Never paste the harness workflow into it — `AGENTS.md` is the single instruction file.
222
+ Do **not** create a `CLAUDE.md` — Claude Code reads `AGENTS.md` directly. If one already exists, keep it as the line `@AGENTS.md` and never paste the harness workflow into it — `AGENTS.md` is the single instruction file.
223
223
 
224
224
  ### Monorepos / multiple harnesses
225
225
 
226
226
  If this repository contains **more than one project or `.har` harness** (check for `.har/` directories above or below this one):
227
227
 
228
228
  - Maintain a **"Harnesses in this repo"** table in the ROOT `AGENTS.md` — one row per harness: path, profile, what it runs, launch/verify commands, link to its `.har/README.md`. Lead with "pick the harness that owns the files you are changing."
229
- - Keep a small `AGENTS.md` (and `CLAUDE.md` pointer) **inside each project directory** for local discovery, with a back-link to the root index.
229
+ - Keep a small `AGENTS.md` **inside each project directory** for local discovery, with a back-link to the root index.
230
230
  - Keep ONE Cursor rule at the repo root (`.cursor/rules/har-workflow.mdc`) listing all harnesses — not one rule per project.
231
231
 
232
232
  Include a **Run history** subsection:
@@ -90,7 +90,7 @@ come back here afterwards. Two standing rules:
90
90
 
91
91
  - Verification customization lives in `stages.json` / `.har/stages/` — the
92
92
  top-level `*.sh` files are generated shims, safe to regenerate.
93
- - Repo-root agent docs (`AGENTS.md`, `CLAUDE.md`) follow Step 3 below.
93
+ - Repo-root agent docs (`AGENTS.md`) follow Step 3 below.
94
94
 
95
95
  ## Step 3 — Refresh repo-root `AGENTS.md`
96
96
 
@@ -105,7 +105,7 @@ If harness commands, rules, or workflow changed, update the **HAR / agent enviro
105
105
 
106
106
  If `AGENTS.md` does not mention HAR yet, add a concise section. If it already has a HAR section, update it minimally — do not replace unrelated content.
107
107
 
108
- Do **not** create `AGENT.md` (singular). If legacy `AGENT.md` exists, merge unique notes into `AGENTS.md` and delete `AGENT.md`. Keep `CLAUDE.md` as the line `@AGENTS.md`.
108
+ Do **not** create `AGENT.md` (singular). If legacy `AGENT.md` exists, merge unique notes into `AGENTS.md` and delete `AGENT.md`. Do not create a `CLAUDE.md`; if one exists, keep it as the line `@AGENTS.md`.
109
109
 
110
110
  ## Rules
111
111
 
@@ -79,7 +79,7 @@ Fix the harness scripts until both pass. Then tear down or keep the slot as the
79
79
  After the user confirms, commit the harness:
80
80
 
81
81
  ```bash
82
- git add .har/ AGENTS.md CLAUDE.md .claude/ .cursor/ 2>/dev/null || git add .har/ AGENTS.md
82
+ git add .har/ AGENTS.md .claude/ .cursor/ 2>/dev/null || git add .har/ AGENTS.md
83
83
  git commit -m "chore: add har agent harness"
84
84
  ```
85
85
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osfactory/har",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "description": "HAR: open agent harness CLI and MCP server for coding agents, isolated worktrees, and software-factory workflows",
5
5
  "keywords": [
6
6
  "har",
@@ -1 +0,0 @@
1
- @AGENTS.md