harnery 0.5.0 → 0.7.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/README.md +16 -6
- package/dist/commander.d.ts +29 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +94 -33
- package/dist/commands/browse-ai.js +1 -1
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +41 -9
- package/dist/commands/cookies.js +1 -1
- package/dist/commands/decision.d.ts +4 -0
- package/dist/commands/decision.d.ts.map +1 -0
- package/dist/commands/decision.js +354 -0
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +4 -0
- package/dist/commands/devtools.d.ts +4 -0
- package/dist/commands/devtools.d.ts.map +1 -0
- package/dist/commands/devtools.js +239 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +74 -2
- package/dist/commands/doctor.js +12 -4
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +3 -63
- package/dist/commands/fetch.js +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +54 -14
- package/dist/commands/scratch.js +1 -1
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +273 -62
- package/dist/commands/web-fetch.js +1 -1
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +32 -8
- package/dist/core/agents/events/consume.d.ts +25 -2
- package/dist/core/agents/events/consume.d.ts.map +1 -1
- package/dist/core/agents/events/consume.js +55 -7
- package/dist/core/agents/events/emit.d.ts +2 -1
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +6 -1
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +16 -5
- package/dist/core/agents/state/scratch.d.ts +1 -1
- package/dist/core/agents/state/scratch.js +2 -2
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +13 -0
- package/dist/core/hooks/cli.js +3 -3
- package/dist/core/hooks/effects/index.d.ts +11 -7
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +15 -17
- package/dist/core/hooks/events/emit.d.ts.map +1 -1
- package/dist/core/hooks/events/emit.js +4 -0
- package/dist/core/hooks/events/rotate.d.ts +43 -0
- package/dist/core/hooks/events/rotate.d.ts.map +1 -0
- package/dist/core/hooks/events/rotate.js +142 -0
- package/dist/core/hooks/harness/events.d.ts +11 -1
- package/dist/core/hooks/harness/events.d.ts.map +1 -1
- package/dist/core/hooks/harness/events.js +22 -3
- package/dist/core/hooks/harness/wiring.d.ts +8 -0
- package/dist/core/hooks/harness/wiring.d.ts.map +1 -1
- package/dist/core/hooks/harness/wiring.js +34 -5
- package/dist/core/scratch/index.d.ts.map +1 -0
- package/dist/{lib → core}/scratch/index.js +2 -2
- package/dist/lib/agent-browser/client.js +1 -1
- package/dist/lib/browser/client.d.ts +14 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +20 -0
- package/dist/lib/browser/index.d.ts +1 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/runts.d.ts +44 -0
- package/dist/lib/browser/runts.d.ts.map +1 -0
- package/dist/lib/browser/runts.js +193 -0
- package/dist/lib/completion/walk.js +1 -1
- package/dist/lib/cookies/client.d.ts +1 -1
- package/dist/lib/cookies/client.d.ts.map +1 -1
- package/dist/lib/cookies/client.js +1 -1
- package/dist/lib/decision/index.d.ts +212 -0
- package/dist/lib/decision/index.d.ts.map +1 -0
- package/dist/lib/decision/index.js +523 -0
- package/dist/lib/devtools.d.ts +178 -0
- package/dist/lib/devtools.d.ts.map +1 -0
- package/dist/lib/devtools.js +1328 -0
- package/dist/lib/docs-frontmatter-migrate.d.ts +33 -0
- package/dist/lib/docs-frontmatter-migrate.d.ts.map +1 -0
- package/dist/lib/docs-frontmatter-migrate.js +364 -0
- package/dist/lib/docs-frontmatter.d.ts +33 -0
- package/dist/lib/docs-frontmatter.d.ts.map +1 -0
- package/dist/lib/docs-frontmatter.js +130 -0
- package/dist/lib/docs-index.d.ts +1 -0
- package/dist/lib/docs-index.d.ts.map +1 -1
- package/dist/lib/docs-index.js +4 -5
- package/dist/lib/docs-lint.d.ts +3 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +67 -12
- package/dist/lib/docs-meta.d.ts +14 -0
- package/dist/lib/docs-meta.d.ts.map +1 -0
- package/dist/lib/docs-meta.js +34 -0
- package/dist/lib/docs-sweep.d.ts +12 -0
- package/dist/lib/docs-sweep.d.ts.map +1 -1
- package/dist/lib/docs-sweep.js +98 -103
- package/dist/lib/format.js +2 -2
- package/dist/lib/http/index.d.ts +1 -0
- package/dist/lib/http/index.d.ts.map +1 -1
- package/dist/lib/http/index.js +1 -0
- package/dist/lib/http/request.d.ts +77 -0
- package/dist/lib/http/request.d.ts.map +1 -0
- package/dist/lib/http/request.js +105 -0
- package/dist/lib/instructions/apply.d.ts +63 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -0
- package/dist/lib/instructions/apply.js +255 -0
- package/dist/lib/instructions/splice.d.ts +73 -0
- package/dist/lib/instructions/splice.d.ts.map +1 -0
- package/dist/lib/instructions/splice.js +118 -0
- package/dist/lib/instructions/templates.d.ts +45 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -0
- package/dist/lib/instructions/templates.js +258 -0
- package/dist/lib/tunnel/gate.d.ts +1 -0
- package/dist/lib/tunnel/gate.d.ts.map +1 -1
- package/dist/lib/tunnel/gate.js +15 -10
- package/dist/lib/tunnel/state.d.ts +11 -1
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +8 -3
- package/package.json +9 -6
- package/src/commander.ts +35 -0
- package/src/commands/agents.ts +97 -29
- package/src/commands/browse-ai.ts +1 -1
- package/src/commands/browse.ts +63 -8
- package/src/commands/cookies.ts +1 -1
- package/src/commands/decision.ts +438 -0
- package/src/commands/deinit.ts +5 -0
- package/src/commands/devtools.ts +284 -0
- package/src/commands/docs.ts +86 -2
- package/src/commands/doctor.ts +13 -4
- package/src/commands/env.ts +11 -77
- package/src/commands/fetch.ts +1 -1
- package/src/commands/init.ts +66 -15
- package/src/commands/scratch.ts +1 -1
- package/src/commands/tunnel.ts +316 -65
- package/src/commands/web-fetch.ts +1 -1
- package/src/core/agents/coord-client.ts +34 -7
- package/src/core/agents/events/consume.ts +65 -7
- package/src/core/agents/events/emit.ts +7 -1
- package/src/core/agents/rules/claim-conflict.ts +17 -6
- package/src/core/agents/state/scratch.ts +2 -2
- package/src/core/config.ts +15 -1
- package/src/core/hooks/cli.ts +3 -3
- package/src/core/hooks/effects/index.ts +23 -16
- package/src/core/hooks/events/emit.ts +5 -0
- package/src/core/hooks/events/rotate.ts +151 -0
- package/src/core/hooks/harness/events.ts +30 -3
- package/src/core/hooks/harness/wiring.ts +46 -5
- package/src/{lib → core}/scratch/index.ts +2 -2
- package/src/lib/agent-browser/client.ts +1 -1
- package/src/lib/browser/client.ts +28 -0
- package/src/lib/browser/index.ts +4 -0
- package/src/lib/browser/runts.ts +218 -0
- package/src/lib/completion/walk.ts +1 -1
- package/src/lib/cookies/client.ts +2 -2
- package/src/lib/decision/index.ts +685 -0
- package/src/lib/devtools.ts +1653 -0
- package/src/lib/docs-frontmatter-migrate.ts +427 -0
- package/src/lib/docs-frontmatter.ts +151 -0
- package/src/lib/docs-index.ts +4 -5
- package/src/lib/docs-lint.ts +61 -11
- package/src/lib/docs-meta.ts +44 -0
- package/src/lib/docs-sweep.ts +104 -102
- package/src/lib/format.ts +2 -2
- package/src/lib/http/index.ts +1 -0
- package/src/lib/http/request.ts +154 -0
- package/src/lib/instructions/apply.ts +318 -0
- package/src/lib/instructions/splice.ts +148 -0
- package/src/lib/instructions/templates.ts +295 -0
- package/src/lib/tunnel/gate.ts +15 -10
- package/src/lib/tunnel/state.ts +19 -4
- package/dist/lib/scratch/index.d.ts.map +0 -1
- /package/dist/{lib → core}/scratch/index.d.ts +0 -0
package/README.md
CHANGED
|
@@ -7,23 +7,24 @@
|
|
|
7
7
|
|
|
8
8
|
# Harnery
|
|
9
9
|
|
|
10
|
-
> Multi-agent coordination
|
|
10
|
+
> Multi-agent coordination for AI coding agents — Claude Code, Cursor, and Codex.
|
|
11
11
|
|
|
12
12
|
[](https://github.com/ryanjkelly/harnery/actions/workflows/ci.yml)
|
|
13
13
|
[](https://www.npmjs.com/package/harnery)
|
|
14
14
|
[](LICENSE)
|
|
15
15
|
|
|
16
|
-
> ⚠️ **Pre-1.0.** API surface is still settling. Pin a specific minor version (`harnery@^0.
|
|
16
|
+
> ⚠️ **Pre-1.0.** API surface is still settling. Pin a specific minor version (`harnery@^0.7.0`) and read the [CHANGELOG](CHANGELOG.md) before each upgrade.
|
|
17
17
|
|
|
18
18
|
## What it is
|
|
19
19
|
|
|
20
|
-
`harnery`
|
|
20
|
+
`harnery` keeps multiple AI coding agents from stepping on each other in a shared checkout. It was extracted from years of running several Claude Code / Cursor / Codex sessions at once against the same monorepo:
|
|
21
21
|
|
|
22
|
-
- **Multi-agent coordination:** per-agent heartbeats in `.harnery/active/`, claim-time and commit-time guards, the canonical event stream, harness adapters for Claude Code / Cursor / Codex.
|
|
23
|
-
- **
|
|
24
|
-
- **Standalone web UI:** `harn web up` boots a local Next.js dashboard for the coord layer, councils, and per-project state. Ships with the git clone, not the npm package (see [Install](#install)).
|
|
22
|
+
- **Multi-agent coordination:** per-agent heartbeats in `.harnery/active/`, claim-time and commit-time guards, the canonical event stream, councils, a decision docket, per-agent scratchpads, and harness adapters for Claude Code / Cursor / Codex.
|
|
23
|
+
- **Standalone web UI:** `harn web up` boots a local Next.js dashboard for the coord layer, councils, per-project state, and a local coding-agent status view (Claude Code / Codex / Cursor usage, backed by `harn devtools`). Ships with the git clone, not the npm package (see [Install](#install)).
|
|
25
24
|
- **Backup + sync:** `harn backup` snapshots `.harnery/` via [restic](https://restic.net/); `harn sync` keeps a curated subset live across machines via [rclone](https://rclone.org/) (Google Drive or any rclone remote).
|
|
26
25
|
|
|
26
|
+
The CLI also ships batteries: portable utility commands (`tokens`, `eml`, `env`, `grep`, `docs`, `browse`, `fetch`, `read`, `devtools`, and more) plus the library toolkit they're built from. Useful, cross-platform, dependency-light — and deliberately not the headline. Coordination is why harnery exists; the toolkit exists because the CLI needed it (see [Public surface tiers](#public-surface-tiers)).
|
|
27
|
+
|
|
27
28
|
## Install
|
|
28
29
|
|
|
29
30
|
```bash
|
|
@@ -80,6 +81,15 @@ await program.parseAsync(process.argv);
|
|
|
80
81
|
|
|
81
82
|
`mycli agents status` then resolves to **the same code** as `harn agents status`, loaded as a library. See [examples/extending-with-commander.ts](examples/extending-with-commander.ts) for the full pattern.
|
|
82
83
|
|
|
84
|
+
### Public surface tiers
|
|
85
|
+
|
|
86
|
+
The exports map draws the line between what harnery *is* and what it *ships with*:
|
|
87
|
+
|
|
88
|
+
- **Product tier** — `harnery`, `harnery/commander`, `harnery/core/*`: the coordination layer and CLI composition. This is the API to build against, and the reason to install harnery.
|
|
89
|
+
- **Toolkit tier** — every `harnery/lib/*` subpath (`http`, `cookies`, `format`, `readability`, `browser`, `machine`, …): the supporting utilities harnery's own CLI is built from, exposed for embedding hosts that want to lean on them. Supported, but secondary: it can evolve faster than the product tier, and it isn't a reason to adopt harnery on its own.
|
|
90
|
+
|
|
91
|
+
The boundary is enforced, not aspirational: CI verifies that no `harnery/lib/*` export imports the coordination core, directly or transitively (`scripts/check-layering.ts`), so pulling a toolkit module never drags in coordination state. Details: [Embedding + surface tiers](https://harnery.com/concepts/embedding/).
|
|
92
|
+
|
|
83
93
|
## Documentation
|
|
84
94
|
|
|
85
95
|
Full docs at **[harnery.com](https://harnery.com)**:
|
package/dist/commander.d.ts
CHANGED
|
@@ -42,6 +42,18 @@ export interface HarneryContextOpts {
|
|
|
42
42
|
*/
|
|
43
43
|
skipCommands?: readonly string[];
|
|
44
44
|
}
|
|
45
|
+
/** One row in a `harn env` section report. */
|
|
46
|
+
export interface EnvCheck {
|
|
47
|
+
label: string;
|
|
48
|
+
value: string;
|
|
49
|
+
status?: "ok" | "missing" | "warn" | "info";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A `harn env` section: an async probe returning its rows. Hosts register extra
|
|
53
|
+
* sections (e.g. cloud-provider connectivity) via `context.envSections`; harnery
|
|
54
|
+
* core ships only the generic sections (runtimes, docker, git).
|
|
55
|
+
*/
|
|
56
|
+
export type EnvSection = () => Promise<EnvCheck[]>;
|
|
45
57
|
export interface HarneryProgramContext {
|
|
46
58
|
/** Project name (e.g., "my-monorepo"). Used in user-facing log lines + telemetry tags. */
|
|
47
59
|
projectName?: string;
|
|
@@ -59,6 +71,13 @@ export interface HarneryProgramContext {
|
|
|
59
71
|
* submodule row.
|
|
60
72
|
*/
|
|
61
73
|
submodules?: readonly string[];
|
|
74
|
+
/**
|
|
75
|
+
* Extra `harn env` sections keyed by name (e.g. `{ gcp, bq }`). Merged into
|
|
76
|
+
* the built-in generic sections (runtimes, docker, git), so a host can expose
|
|
77
|
+
* `harn env <name>` for its own environment probes without harnery core
|
|
78
|
+
* carrying provider-specific checks. harn standalone ships none.
|
|
79
|
+
*/
|
|
80
|
+
envSections?: Record<string, EnvSection>;
|
|
62
81
|
/**
|
|
63
82
|
* Optional callback that returns extra HTTP headers to attach to outbound
|
|
64
83
|
* `fetch` calls based on the target URL. Useful for consumers that need
|
|
@@ -91,6 +110,16 @@ export interface HarneryProgramContext {
|
|
|
91
110
|
* exclusions (`.claude/`, `.harnery/`, `.codex/`, `.cursor/`).
|
|
92
111
|
*/
|
|
93
112
|
extraDocsExcludedPrefixes?: readonly string[];
|
|
113
|
+
/**
|
|
114
|
+
* Filenames permitted at the host project's `docs/` root (parent repo only).
|
|
115
|
+
* When set, `harn docs lint` flags any other `.md`/`.json` file sitting
|
|
116
|
+
* loose at `docs/` root (rule `docs-root-file`) — topic docs belong in
|
|
117
|
+
* `docs/<topic>/` subdirs. Names are matched exactly (basename). When
|
|
118
|
+
* omitted or empty, the rule is a no-op, so standalone `harn` and consumers
|
|
119
|
+
* that don't opt in are unaffected. Submodule `docs/` roots are never
|
|
120
|
+
* checked (their entry tiers differ from the parent's).
|
|
121
|
+
*/
|
|
122
|
+
docsRootAllowlist?: readonly string[];
|
|
94
123
|
/**
|
|
95
124
|
* Default Host header for `tunnel up` when `--vhost` is omitted: a literal
|
|
96
125
|
* host, or a resolver evaluated at start time (e.g. read a dev stack's
|
package/dist/commander.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"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"}
|
package/dist/commander.js
CHANGED
|
@@ -26,7 +26,9 @@ import { registerCompletionCommand } from "./commands/completion.js";
|
|
|
26
26
|
import { registerConfigGetCommand } from "./commands/config-get.js";
|
|
27
27
|
import { registerContextCommand } from "./commands/context.js";
|
|
28
28
|
import { registerCookiesCommand } from "./commands/cookies.js";
|
|
29
|
+
import { registerDecisionCommand } from "./commands/decision.js";
|
|
29
30
|
import { registerDeinitCommand } from "./commands/deinit.js";
|
|
31
|
+
import { registerDevtoolsCommand } from "./commands/devtools.js";
|
|
30
32
|
import { registerDocsCommand } from "./commands/docs.js";
|
|
31
33
|
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
32
34
|
import { registerEditBatchCommand } from "./commands/edit-batch.js";
|
|
@@ -110,6 +112,8 @@ export function createHarneryProgram(opts = {}) {
|
|
|
110
112
|
registerCompletionCommand(program, emit, opts.context);
|
|
111
113
|
registerContextCommand(program, emit, opts.context);
|
|
112
114
|
registerScratchCommand(program, emit);
|
|
115
|
+
registerDecisionCommand(program, emit);
|
|
116
|
+
registerDevtoolsCommand(program, emit);
|
|
113
117
|
registerTunnelCommand(program, emit, opts.context);
|
|
114
118
|
registerDocsCommand(program, emit, opts.context);
|
|
115
119
|
registerAgentsCommand(program, emit);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAwFnD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CA4OpF"}
|
package/dist/commands/agents.js
CHANGED
|
@@ -16,12 +16,17 @@ import { spawnSync } from "node:child_process";
|
|
|
16
16
|
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
17
17
|
import { homedir, tmpdir } from "node:os";
|
|
18
18
|
import { basename, join, resolve } from "node:path";
|
|
19
|
-
import {
|
|
19
|
+
import { readStreamTailBounded } from "../core/agents/events/consume.js";
|
|
20
|
+
import { emitCanonical, monorepoRoot, normalizeHarness, readHeartbeat, resolveOwner, resolveOwnerBySessionEnv, resolveOwnerWithSource, } from "../core/agents/index.js";
|
|
20
21
|
import { resolveBinName } from "../core/config.js";
|
|
22
|
+
/** Cap for CLI scans of the unbounded event ledger (`trace` / `health`). Well
|
|
23
|
+
* under V8's ~512MB max string length so a `readFileSync` of the whole file can
|
|
24
|
+
* never throw; covers ample recent history for a diagnostic scan. */
|
|
25
|
+
const STREAM_SCAN_CAP_BYTES = 128 * 1024 * 1024; // 128 MiB
|
|
21
26
|
import { parsePsChainLine } from "../core/hooks/resolve/anchor.js";
|
|
27
|
+
import { appendEntry, resolveOwnerByName } from "../core/scratch/index.js";
|
|
22
28
|
import { buildCouncilId, buildInviteMarkdown, COUNCIL_SCHEMA_VERSION, contributorsInRound, councilBodyDir, councilsArchiveDir, deleteArchivedCouncil, effectiveSteward, findManifestByPartialId, listKnownAgents, listManifests, moveFromArchive, moveToArchive, normalizeAgentName, pendingCouncilsForMember, readArchivedManifest, readManifest, readRoundPrompts, roundDir, setCouncilSteward, writeContribution, writeManifest, writePrompt, } from "../lib/council/index.js";
|
|
23
29
|
import { displayName as displayAgentName, ensureIdentity, listIdentities, lookupById as lookupIdentityById, lookupByName as lookupIdentityByName, } from "../lib/identities/index.js";
|
|
24
|
-
import { appendEntry, resolveOwnerByName } from "../lib/scratch/index.js";
|
|
25
30
|
const FRESHNESS_SECS = 600; // 10-minute heartbeat-freshness window.
|
|
26
31
|
const SUBAGENT_NOTE = "Bash identity is process-level in v1; if you're running inside a subagent, " +
|
|
27
32
|
"this resolves to the parent group's name, not the subagent's. A subagent-aware " +
|
|
@@ -75,8 +80,7 @@ export function registerAgentsCommand(program, emitParam) {
|
|
|
75
80
|
});
|
|
76
81
|
cmd
|
|
77
82
|
.command("show <name>")
|
|
78
|
-
.description("Deep-dive on one peer agent: registry state (files held, last tool, task) " +
|
|
79
|
-
"plus claude-sessions history (latest title, recent prompts, recent tools, tool-usage tallies). " +
|
|
83
|
+
.description("Deep-dive on one peer agent: registry state (files held, last tool, task, turn summary). " +
|
|
80
84
|
"Disambiguates name → instance_id via prefix match.")
|
|
81
85
|
.option("--json", "JSON envelope output")
|
|
82
86
|
.action(async (name, opts) => {
|
|
@@ -430,6 +434,7 @@ function runWhoami(opts) {
|
|
|
430
434
|
});
|
|
431
435
|
process.exit(1);
|
|
432
436
|
}
|
|
437
|
+
ensureCursorSession(root);
|
|
433
438
|
const resolved = resolveOwnerWithSource();
|
|
434
439
|
const myOwner = resolved.owner;
|
|
435
440
|
if (!myOwner) {
|
|
@@ -868,6 +873,49 @@ function formatLocalShort(iso) {
|
|
|
868
873
|
timeZone: "America/Chicago",
|
|
869
874
|
}).format(d);
|
|
870
875
|
}
|
|
876
|
+
function cursorEnvSessionId() {
|
|
877
|
+
const raw = process.env.CURSOR_SESSION_ID?.trim() || process.env.CURSOR_CONVERSATION_ID?.trim();
|
|
878
|
+
if (!raw)
|
|
879
|
+
return null;
|
|
880
|
+
return raw.startsWith("bc-") && raw.length > 3 ? raw.slice(3) : raw;
|
|
881
|
+
}
|
|
882
|
+
function shouldBootstrapCursorSession() {
|
|
883
|
+
return process.env.CURSOR_AGENT === "1" && cursorEnvSessionId() !== null;
|
|
884
|
+
}
|
|
885
|
+
function ensureCursorSession(root) {
|
|
886
|
+
if (!shouldBootstrapCursorSession())
|
|
887
|
+
return;
|
|
888
|
+
if (resolveOwnerBySessionEnv(root))
|
|
889
|
+
return;
|
|
890
|
+
const sessionId = cursorEnvSessionId();
|
|
891
|
+
if (!sessionId)
|
|
892
|
+
return;
|
|
893
|
+
const agentHook = resolve(root, "harnery", "bin", "agent-hook");
|
|
894
|
+
if (!existsSync(agentHook))
|
|
895
|
+
return;
|
|
896
|
+
const payload = JSON.stringify({
|
|
897
|
+
conversation_id: sessionId,
|
|
898
|
+
session_id: sessionId,
|
|
899
|
+
hook_event_name: "sessionStart",
|
|
900
|
+
workspace_roots: [root],
|
|
901
|
+
cwd: root,
|
|
902
|
+
composer_mode: "agent",
|
|
903
|
+
is_background_agent: false,
|
|
904
|
+
});
|
|
905
|
+
spawnSync("bash", [agentHook, "session-start", "--harness", "cursor"], {
|
|
906
|
+
input: payload,
|
|
907
|
+
cwd: root,
|
|
908
|
+
encoding: "utf8",
|
|
909
|
+
timeout: 3000,
|
|
910
|
+
env: {
|
|
911
|
+
...process.env,
|
|
912
|
+
HARNERY_AGENT_COORD_PLATFORM: "cursor",
|
|
913
|
+
HARNERY_COORD_ROOT_OVERRIDE: root,
|
|
914
|
+
CURSOR_SESSION_ID: sessionId,
|
|
915
|
+
CURSOR_CONVERSATION_ID: sessionId,
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
}
|
|
871
919
|
function runReleaseClaim(path) {
|
|
872
920
|
const root = monorepoRoot();
|
|
873
921
|
if (!root) {
|
|
@@ -915,6 +963,8 @@ function runSetTask(task, opts) {
|
|
|
915
963
|
// Identity: prefer explicit --session-id (the ppid-walk-free escape hatch,
|
|
916
964
|
// mirrors `status`), fall back to the ppid walk. Cursor shell tool calls
|
|
917
965
|
// don't descend from a pid-map-registered anchor, so the walk can miss there.
|
|
966
|
+
if (!opts?.sessionId)
|
|
967
|
+
ensureCursorSession(root);
|
|
918
968
|
const myOwner = opts?.sessionId ?? resolveOwner();
|
|
919
969
|
if (!myOwner) {
|
|
920
970
|
emit.error({
|
|
@@ -961,6 +1011,8 @@ function runStatus(opts) {
|
|
|
961
1011
|
}
|
|
962
1012
|
// Identity resolution: prefer explicit --session-id (hook-friendly), fall
|
|
963
1013
|
// back to ppid walk for interactive shell usage.
|
|
1014
|
+
if (!opts.sessionId)
|
|
1015
|
+
ensureCursorSession(root);
|
|
964
1016
|
const myOwner = opts.sessionId ?? resolveOwner();
|
|
965
1017
|
if (!myOwner) {
|
|
966
1018
|
emit.error({
|
|
@@ -1717,8 +1769,12 @@ function readStreamStats(root) {
|
|
|
1717
1769
|
let backlog = 0;
|
|
1718
1770
|
let seenCursor = cursor === null; // no cursor → everything is "backlog"
|
|
1719
1771
|
try {
|
|
1720
|
-
|
|
1721
|
-
|
|
1772
|
+
// Bounded tail read: the ledger grows without bound and a whole-file
|
|
1773
|
+
// readFileSync throws past V8's ~512MB string limit. lines/backlog are then
|
|
1774
|
+
// scoped to the scanned window; `bytes` (full size, via statSync above) still
|
|
1775
|
+
// reflects the true stream size for the health rollup.
|
|
1776
|
+
const { text } = readStreamTailBounded(streamPath, STREAM_SCAN_CAP_BYTES);
|
|
1777
|
+
for (const line of text.split("\n")) {
|
|
1722
1778
|
if (!line.trim())
|
|
1723
1779
|
continue;
|
|
1724
1780
|
lines++;
|
|
@@ -1835,27 +1891,32 @@ function runTrace(name, opts) {
|
|
|
1835
1891
|
const sinceMs = opts.since ? Date.now() - (parseWindowSecs(opts.since) ?? 0) * 1000 : 0;
|
|
1836
1892
|
const limit = Math.max(1, Number.parseInt(opts.limit, 10) || 200);
|
|
1837
1893
|
const candidateIds = targetId.includes("\x00") ? targetId.split("\x00") : [targetId];
|
|
1838
|
-
// Scan the
|
|
1894
|
+
// Scan the stream tail once, bucket events by instance_id for the candidates.
|
|
1895
|
+
// Bounded read: the ledger grows without bound and a whole-file readFileSync
|
|
1896
|
+
// throws past V8's ~512MB string limit. A trace of an agent whose events all
|
|
1897
|
+
// predate the window is reported as truncated rather than crashing.
|
|
1839
1898
|
const streamPath = resolve(root, ".harnery", "events.ndjson");
|
|
1840
1899
|
const byId = new Map();
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1900
|
+
const { text: streamText, truncated: streamTruncated } = readStreamTailBounded(streamPath, STREAM_SCAN_CAP_BYTES);
|
|
1901
|
+
for (const line of streamText.split("\n")) {
|
|
1902
|
+
if (!line)
|
|
1903
|
+
continue;
|
|
1904
|
+
try {
|
|
1905
|
+
const ev = JSON.parse(line);
|
|
1906
|
+
if (!ev.instance_id || !candidateIds.includes(ev.instance_id))
|
|
1844
1907
|
continue;
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
continue;
|
|
1851
|
-
const arr = byId.get(ev.instance_id) ?? [];
|
|
1852
|
-
arr.push(ev);
|
|
1853
|
-
byId.set(ev.instance_id, arr);
|
|
1854
|
-
}
|
|
1855
|
-
catch {
|
|
1856
|
-
/* skip */
|
|
1857
|
-
}
|
|
1908
|
+
if (sinceMs && Date.parse(ev.ts) < sinceMs)
|
|
1909
|
+
continue;
|
|
1910
|
+
const arr = byId.get(ev.instance_id) ?? [];
|
|
1911
|
+
arr.push(ev);
|
|
1912
|
+
byId.set(ev.instance_id, arr);
|
|
1858
1913
|
}
|
|
1914
|
+
catch {
|
|
1915
|
+
/* skip */
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
if (streamTruncated) {
|
|
1919
|
+
process.stderr.write(`note: event ledger exceeds ${Math.round(STREAM_SCAN_CAP_BYTES / 1024 / 1024)}MB; traced only the most recent window (older events omitted)\n`);
|
|
1859
1920
|
}
|
|
1860
1921
|
// If the name mapped to multiple instances, trace the one with the latest event.
|
|
1861
1922
|
let resolvedId = candidateIds[0];
|
|
@@ -2208,7 +2269,7 @@ function renderHealthBox(report) {
|
|
|
2208
2269
|
];
|
|
2209
2270
|
const localTime = formatLocalTime(new Date(report.generated_at));
|
|
2210
2271
|
const title = `Coord Health (${localTime})`;
|
|
2211
|
-
process.stdout.write(`${formatBox(title, rows)}\n`); // lint-ok-emission: chat-paste path; mirrors runStatus's direct write so the box surfaces in both TTY +
|
|
2272
|
+
process.stdout.write(`${formatBox(title, rows)}\n`); // lint-ok-emission: chat-paste path; mirrors runStatus's direct write so the box surfaces in both TTY + harn-session-teed contexts
|
|
2212
2273
|
if (report.anomalies.length > 0) {
|
|
2213
2274
|
process.stdout.write("\n"); // lint-ok-emission: same chat-paste path
|
|
2214
2275
|
for (const a of report.anomalies) {
|
|
@@ -2245,7 +2306,7 @@ function runHarnessProbe(id, opts) {
|
|
|
2245
2306
|
// and `resolveOwner` here, so the probe reports exactly what the live hot
|
|
2246
2307
|
// path resolves.
|
|
2247
2308
|
const anchorTokens = new Set(["claude", "claude-code", "cursor", "codex"]);
|
|
2248
|
-
const override = process.env.
|
|
2309
|
+
const override = process.env.HARNERY_AGENT_COORD_TEST_ANCHOR_PID;
|
|
2249
2310
|
let anchorPid = override && Number(override) > 0 ? override : "";
|
|
2250
2311
|
const chainParts = [];
|
|
2251
2312
|
let walkPid = process.pid;
|
|
@@ -2348,12 +2409,12 @@ function runHarnessProbe(id, opts) {
|
|
|
2348
2409
|
* dispatcher in an isolated sandbox.
|
|
2349
2410
|
*
|
|
2350
2411
|
* Sandbox isolation strategy:
|
|
2351
|
-
* - mkdtempSync(tmpdir(), "
|
|
2412
|
+
* - mkdtempSync(tmpdir(), "harn-harness-probe-") creates a non-git tmp dir.
|
|
2352
2413
|
* - The dispatcher's coord-root resolution falls back to
|
|
2353
|
-
* `
|
|
2414
|
+
* `HARNERY_COORD_ROOT_OVERRIDE` when git rev-parse fails. We set it to the sandbox.
|
|
2354
2415
|
* - We rewrite the payload's `cwd` field (Cursor cds to it) to the sandbox,
|
|
2355
2416
|
* so real `.harnery/` never gets touched.
|
|
2356
|
-
* - We set `
|
|
2417
|
+
* - We set `HARNERY_AGENT_COORD_OFF=0` explicitly so any user-side off-switch in
|
|
2357
2418
|
* the environment doesn't mask adapter crashes.
|
|
2358
2419
|
*
|
|
2359
2420
|
* Sample shape: probe-meta wrapped (`_probe_meta.event` + `.payload`) OR bare
|
|
@@ -2420,7 +2481,7 @@ function replayHarnessSamples(harness, root, relativeSampleDir, filter) {
|
|
|
2420
2481
|
StopFailure: "stop-failure",
|
|
2421
2482
|
};
|
|
2422
2483
|
const harnessFlag = harness === "claude_code" ? "claude-code" : harness;
|
|
2423
|
-
const sandbox = mkdtempSync(join(tmpdir(), "
|
|
2484
|
+
const sandbox = mkdtempSync(join(tmpdir(), "harn-harness-probe-"));
|
|
2424
2485
|
const results = [];
|
|
2425
2486
|
try {
|
|
2426
2487
|
for (const file of fixtures) {
|
|
@@ -2464,10 +2525,10 @@ function replayHarnessSamples(harness, root, relativeSampleDir, filter) {
|
|
|
2464
2525
|
timeout: 10_000,
|
|
2465
2526
|
env: {
|
|
2466
2527
|
...process.env,
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2528
|
+
HARNERY_COORD_ROOT_OVERRIDE: sandbox,
|
|
2529
|
+
HARNERY_AGENT_COORD_HARNESS: harness,
|
|
2530
|
+
HARNERY_AGENT_COORD_PLATFORM: harness,
|
|
2531
|
+
HARNERY_AGENT_COORD_OFF: "0",
|
|
2471
2532
|
},
|
|
2472
2533
|
});
|
|
2473
2534
|
const exit = dispatch.status ?? -1;
|
|
@@ -62,7 +62,7 @@ export function registerBrowseAiCommand(program, emitParam) {
|
|
|
62
62
|
function runBrowseAi(url, opts) {
|
|
63
63
|
const jar = opts.cookies === false
|
|
64
64
|
? null
|
|
65
|
-
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "
|
|
65
|
+
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "harn-browse-ai" });
|
|
66
66
|
const ab = new AgentBrowser({
|
|
67
67
|
jar,
|
|
68
68
|
timeoutMs: Number.parseInt(opts.timeout, 10),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browse.d.ts","sourceRoot":"","sources":["../../src/commands/browse.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"browse.d.ts","sourceRoot":"","sources":["../../src/commands/browse.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAiH1E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,WAAW,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA0KN"}
|
package/dist/commands/browse.js
CHANGED
|
@@ -92,6 +92,14 @@ export function registerBrowseCommand(program, emitParam, context) {
|
|
|
92
92
|
"Catches the class of mobile-layout bug where a nav/table overflows the viewport edge.")
|
|
93
93
|
.option("--check-overflow-fail", "Exit non-zero if --check-overflow detects horizontal overflow.")
|
|
94
94
|
.option("--no-check-overflow-annotate", "Skip drawing overflow annotations on the screenshot (JSON still emitted).")
|
|
95
|
+
.option("--check-runts [selector]", "Scan text blocks for runts (a single word alone on a block's last visual line) " +
|
|
96
|
+
"by counting words on the last line via per-word Range rects — width thresholds " +
|
|
97
|
+
"miss narrow-column runts. Optional selector scopes the sweep (default: whole body). " +
|
|
98
|
+
"Atomic tokens (URLs, emails, phone numbers) are excluded. Reports hits in the JSON " +
|
|
99
|
+
"envelope under `runts` and annotates them on the screenshot.")
|
|
100
|
+
.option("--check-runts-min-chars <n>", "Minimum block text length to scan (default 40; smaller labels can't meaningfully wrap).", "40")
|
|
101
|
+
.option("--check-runts-fail", "Exit non-zero if any runt is detected.")
|
|
102
|
+
.option("--no-check-runts-annotate", "Skip drawing runt boxes on the screenshot (JSON still emitted).")
|
|
95
103
|
.option("--baseline <name>", "Save the captured screenshot as a named baseline at " +
|
|
96
104
|
"~/.cache/harnery/visual-baselines/<name>.png. Use --diff <name> later to " +
|
|
97
105
|
"compare future captures against it (visual-regression check).")
|
|
@@ -117,7 +125,7 @@ async function runBrowse(url, opts, context) {
|
|
|
117
125
|
// Commander: `--no-cookies` turns `opts.cookies` into `false`. Default is `true`.
|
|
118
126
|
const jar = opts.cookies === false
|
|
119
127
|
? null
|
|
120
|
-
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "
|
|
128
|
+
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "harn-browse" });
|
|
121
129
|
const headed = opts.login || opts.headed;
|
|
122
130
|
const viewport = parseViewport(opts.viewport ?? "desktop");
|
|
123
131
|
const browser = new Browser({
|
|
@@ -165,7 +173,7 @@ async function runBrowse(url, opts, context) {
|
|
|
165
173
|
visibility = await browser.checkVisibility(opts.checkVisible, {
|
|
166
174
|
sampleGrid: Number.parseInt(opts.checkVisibleSampleGrid ?? "3", 10),
|
|
167
175
|
});
|
|
168
|
-
if (opts.
|
|
176
|
+
if (opts.checkVisibleAnnotate !== false) {
|
|
169
177
|
await browser.annotateVisibility(visibility);
|
|
170
178
|
}
|
|
171
179
|
}
|
|
@@ -177,9 +185,16 @@ async function runBrowse(url, opts, context) {
|
|
|
177
185
|
if (opts.checkOverflow) {
|
|
178
186
|
overflow = await browser.checkOverflow();
|
|
179
187
|
}
|
|
188
|
+
let runts;
|
|
189
|
+
if (opts.checkRunts) {
|
|
190
|
+
runts = await browser.checkRunts({
|
|
191
|
+
scope: typeof opts.checkRunts === "string" ? opts.checkRunts : null,
|
|
192
|
+
minChars: Number.parseInt(opts.checkRuntsMinChars ?? "40", 10),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
180
195
|
const widthThreshold = Number.parseFloat(opts.checkWidthThreshold ?? "0.9");
|
|
181
|
-
const annotateWidth = widths && opts.
|
|
182
|
-
const annotateOverflow = overflow && opts.
|
|
196
|
+
const annotateWidth = widths && opts.checkWidthAnnotate !== false;
|
|
197
|
+
const annotateOverflow = overflow && opts.checkOverflowAnnotate !== false;
|
|
183
198
|
if (annotateWidth || annotateOverflow) {
|
|
184
199
|
await browser.annotateLayout({
|
|
185
200
|
widths: annotateWidth ? widths : [],
|
|
@@ -187,6 +202,10 @@ async function runBrowse(url, opts, context) {
|
|
|
187
202
|
widthThreshold,
|
|
188
203
|
});
|
|
189
204
|
}
|
|
205
|
+
const annotateRunts = runts && runts.runts.length > 0 && opts.checkRuntsAnnotate !== false;
|
|
206
|
+
if (annotateRunts) {
|
|
207
|
+
await browser.annotateRunts(runts);
|
|
208
|
+
}
|
|
190
209
|
if (opts.login) {
|
|
191
210
|
await new Promise((res) => {
|
|
192
211
|
emit.log("[--login] Headed Chromium is open. Walk through your auth flow now. Press Enter here to close + persist cookies into the profile.", "info");
|
|
@@ -203,17 +222,20 @@ async function runBrowse(url, opts, context) {
|
|
|
203
222
|
if (opts.baseline || opts.diff) {
|
|
204
223
|
throw new Error("--baseline / --diff require trio mode (screenshot file). Remove --snapshot/--html/--json or capture the screenshot first.");
|
|
205
224
|
}
|
|
206
|
-
await runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, devOverlay, batchResult);
|
|
225
|
+
await runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, devOverlay, batchResult);
|
|
207
226
|
}
|
|
208
227
|
else {
|
|
209
|
-
await runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, devOverlay, batchResult);
|
|
228
|
+
await runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, devOverlay, batchResult);
|
|
210
229
|
}
|
|
211
|
-
if (visibility && opts.
|
|
230
|
+
if (visibility && opts.checkVisibleAnnotate !== false) {
|
|
212
231
|
await browser.clearVisibilityAnnotations();
|
|
213
232
|
}
|
|
214
233
|
if (annotateWidth || annotateOverflow) {
|
|
215
234
|
await browser.clearLayoutAnnotations();
|
|
216
235
|
}
|
|
236
|
+
if (annotateRunts) {
|
|
237
|
+
await browser.clearRuntsAnnotations();
|
|
238
|
+
}
|
|
217
239
|
if (opts.checkVisibleFail && visibility) {
|
|
218
240
|
const threshold = Number.parseFloat(opts.checkVisibleThreshold ?? "0.9");
|
|
219
241
|
const failed = visibility.filter((r) => !r.found || !r.cssVisible || r.visibleRatio < threshold);
|
|
@@ -249,6 +271,12 @@ async function runBrowse(url, opts, context) {
|
|
|
249
271
|
process.exitCode = 2;
|
|
250
272
|
}
|
|
251
273
|
}
|
|
274
|
+
if (opts.checkRuntsFail && runts && runts.runts.length > 0) {
|
|
275
|
+
for (const hit of runts.runts) {
|
|
276
|
+
emit.log(`check-runts FAIL ${hit.block}: last line is a lone "${hit.word}" ("…${hit.snippet.slice(-40)}")`, "warn");
|
|
277
|
+
}
|
|
278
|
+
process.exitCode = 2;
|
|
279
|
+
}
|
|
252
280
|
if (opts.checkOverflowFail && overflow?.hasHorizontalOverflow) {
|
|
253
281
|
const culprit = overflow.widerThanViewport[0] ?? overflow.rightOverflow[0] ?? null;
|
|
254
282
|
const detail = culprit
|
|
@@ -356,7 +384,7 @@ function splitBatchSteps(input) {
|
|
|
356
384
|
// ---------------------------------------------------------------------------
|
|
357
385
|
// Print mode (--snapshot / --html / --json): stdout, no file writes
|
|
358
386
|
// ---------------------------------------------------------------------------
|
|
359
|
-
async function runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, devOverlay, batchResult) {
|
|
387
|
+
async function runPrintMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, devOverlay, batchResult) {
|
|
360
388
|
let body = null;
|
|
361
389
|
if (opts.html) {
|
|
362
390
|
body = await browser.htmlContent(opts.selector);
|
|
@@ -385,6 +413,8 @@ async function runPrintMode(browser, navResult, opts, evalResult, visibility, wi
|
|
|
385
413
|
result.width = widths;
|
|
386
414
|
if (overflow)
|
|
387
415
|
result.overflow = overflow;
|
|
416
|
+
if (runts)
|
|
417
|
+
result.runts = runts;
|
|
388
418
|
if (devOverlay)
|
|
389
419
|
result.devOverlay = devOverlay;
|
|
390
420
|
if (batchResult && batchResult.clipboardReads.length > 0) {
|
|
@@ -408,7 +438,7 @@ async function runPrintMode(browser, navResult, opts, evalResult, visibility, wi
|
|
|
408
438
|
// ---------------------------------------------------------------------------
|
|
409
439
|
// Trio mode (default): write <prefix>.{png,html,json}
|
|
410
440
|
// ---------------------------------------------------------------------------
|
|
411
|
-
async function runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, devOverlay, batchResult) {
|
|
441
|
+
async function runTrioMode(browser, navResult, opts, evalResult, visibility, widths, overflow, runts, devOverlay, batchResult) {
|
|
412
442
|
const prefix = resolveOutPrefix(opts.out);
|
|
413
443
|
mkdirSync(dirname(prefix), { recursive: true });
|
|
414
444
|
const written = [];
|
|
@@ -449,6 +479,8 @@ async function runTrioMode(browser, navResult, opts, evalResult, visibility, wid
|
|
|
449
479
|
envelope.width = widths;
|
|
450
480
|
if (overflow)
|
|
451
481
|
envelope.overflow = overflow;
|
|
482
|
+
if (runts)
|
|
483
|
+
envelope.runts = runts;
|
|
452
484
|
if (devOverlay)
|
|
453
485
|
envelope.devOverlay = devOverlay;
|
|
454
486
|
if (batchResult && batchResult.clipboardReads.length > 0) {
|
package/dist/commands/cookies.js
CHANGED
|
@@ -12,7 +12,7 @@ const DEFAULT_STORE = resolve(homedir(), ".cache", "harnery", "cookies.json");
|
|
|
12
12
|
function jarFrom(opts) {
|
|
13
13
|
return new CookieJar({
|
|
14
14
|
path: opts.store ?? DEFAULT_STORE,
|
|
15
|
-
source: "
|
|
15
|
+
source: "harn-cookies",
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
// Module-scoped emit assigned by registerCookiesCommand; the module-level
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../../src/commands/decision.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA6CnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAgStF"}
|