@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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
82143
|
-
|
|
82144
|
-
|
|
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 === "
|
|
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
|
|
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
|
-
|
|
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`
|
|
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
|
|
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`.
|
|
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
|
|
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 +0,0 @@
|
|
|
1
|
-
@AGENTS.md
|