harnery 0.2.1 → 0.2.2
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/commander.d.ts.map +1 -1
- package/dist/commander.js +28 -3
- package/dist/lib/readability/client.d.ts +8 -6
- package/dist/lib/readability/client.d.ts.map +1 -1
- package/dist/lib/readability/client.js +13 -4
- package/package.json +2 -3
- package/src/commander.ts +24 -3
- package/src/lib/readability/client.ts +26 -14
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;
|
|
1
|
+
{"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BpC,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAEhC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IAEnB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,MAAM,GAAG,SAAS,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACjE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,KAAK,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,GAAG,OAAO,GAAG,IAAI,CAAC;IACrF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACpE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,eAAO,MAAM,WAAW,EAAE,WAkCzB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CA2C3E"}
|
package/dist/commander.js
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* Each subdirectory under src/commands/ exports its `register…` function
|
|
14
14
|
* (Commander pattern).
|
|
15
15
|
*/
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
16
19
|
import { Command } from "commander";
|
|
17
20
|
import { registerAgentsCommand } from "./commands/agents.js";
|
|
18
21
|
import { registerBackupCommand } from "./commands/backup.js";
|
|
@@ -120,7 +123,29 @@ export function createHarneryProgram(opts = {}) {
|
|
|
120
123
|
return program;
|
|
121
124
|
}
|
|
122
125
|
function readVersion() {
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
+
// Resolve package.json by walking up from this module. Works under Bun (this
|
|
127
|
+
// file runs from src/) and Node (from dist/): package.json sits at the
|
|
128
|
+
// package root above both. Falls back to "0.0.0" if it can't be found rather
|
|
129
|
+
// than crashing `--version`.
|
|
130
|
+
try {
|
|
131
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
132
|
+
for (let i = 0; i < 6; i++) {
|
|
133
|
+
try {
|
|
134
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
135
|
+
if (pkg.name === "harnery" && typeof pkg.version === "string")
|
|
136
|
+
return pkg.version;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// no package.json at this level, or not ours; keep walking up
|
|
140
|
+
}
|
|
141
|
+
const parent = dirname(dir);
|
|
142
|
+
if (parent === dir)
|
|
143
|
+
break;
|
|
144
|
+
dir = parent;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// import.meta.url unavailable or fs error; fall through
|
|
149
|
+
}
|
|
150
|
+
return "0.0.0";
|
|
126
151
|
}
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The library backing the `read` command.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* linkedom, @mozilla/readability, and turndown are heavy, so they are loaded
|
|
8
|
+
* lazily inside `htmlToMarkdown` rather than at module top level. A static
|
|
9
|
+
* import would pull them into CLI startup (every command is registered
|
|
10
|
+
* eagerly). linkedom replaced jsdom here: jsdom's transitive dependency tree
|
|
11
|
+
* (`html-encoding-sniffer`, `whatwg-url`) does a CommonJS `require()` of the
|
|
12
|
+
* ESM-only `@exodus/bytes`, which crashes a plain `node` process via
|
|
13
|
+
* ERR_REQUIRE_ESM. linkedom is a lighter, CJS-friendly DOM with no such chain.
|
|
14
|
+
* See ADR 0002.
|
|
13
15
|
*/
|
|
14
16
|
export interface ReadabilityOptions {
|
|
15
17
|
/** Base URL for resolving relative links. Default `http://local/`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/readability/client.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/readability/client.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAID;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAwE5B"}
|
|
@@ -10,14 +10,23 @@ export async function htmlToMarkdown(html, opts = {}) {
|
|
|
10
10
|
if (!html.trim()) {
|
|
11
11
|
throw new Error("Empty HTML input");
|
|
12
12
|
}
|
|
13
|
-
const [{ Readability }, {
|
|
13
|
+
const [{ Readability }, { parseHTML }, { default: TurndownService }, { tables }] = await Promise.all([
|
|
14
14
|
import("@mozilla/readability"),
|
|
15
|
-
import("
|
|
15
|
+
import("linkedom"),
|
|
16
16
|
import("turndown"),
|
|
17
17
|
import("turndown-plugin-gfm"),
|
|
18
18
|
]);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// jsdom took a `url` option to set the document base so Readability resolves
|
|
20
|
+
// relative links to absolute. linkedom's parseHTML has no such option, so we
|
|
21
|
+
// inject a <base> tag, which sets doc.baseURI to the same effect. Only when
|
|
22
|
+
// the HTML doesn't already carry its own <base>.
|
|
23
|
+
const baseUrl = opts.url ?? "http://local/";
|
|
24
|
+
const htmlWithBase = /<base\b/i.test(html)
|
|
25
|
+
? html
|
|
26
|
+
: html.replace(/<head\b[^>]*>/i, (m) => `${m}<base href="${baseUrl}">`);
|
|
27
|
+
// If there's no <head> to anchor the <base> into, fall back to the raw HTML
|
|
28
|
+
// (relative links stay relative, matching a document with no base).
|
|
29
|
+
const { document: doc } = parseHTML(htmlWithBase.includes("<base") ? htmlWithBase : html);
|
|
21
30
|
preprocessDom(doc);
|
|
22
31
|
let contentHtml;
|
|
23
32
|
let title = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harnery",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Multi-agent coordination + harness adapters + portable CLI utilities for Claude Code / Cursor / Codex.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryan Kelly",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
"commander": "^15.0.0",
|
|
142
142
|
"gpt-tokenizer": "^3.4.0",
|
|
143
143
|
"js-yaml": "^4.1.1",
|
|
144
|
-
"
|
|
144
|
+
"linkedom": "^0.18.0",
|
|
145
145
|
"mailparser": "^3.9.8",
|
|
146
146
|
"pixelmatch": "^7.2.0",
|
|
147
147
|
"playwright": "^1.59.1",
|
|
@@ -154,7 +154,6 @@
|
|
|
154
154
|
"@changesets/cli": "^2.27.0",
|
|
155
155
|
"@types/bun": "^1.2.0",
|
|
156
156
|
"@types/js-yaml": "^4.0.9",
|
|
157
|
-
"@types/jsdom": "^28.0.1",
|
|
158
157
|
"@types/mailparser": "^3.4.6",
|
|
159
158
|
"@types/node": "^26.0.0",
|
|
160
159
|
"@types/pixelmatch": "^5.2.6",
|
package/src/commander.ts
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* (Commander pattern).
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
17
20
|
import { Command } from "commander";
|
|
18
21
|
import { registerAgentsCommand } from "./commands/agents.ts";
|
|
19
22
|
import { registerBackupCommand } from "./commands/backup.ts";
|
|
@@ -236,7 +239,25 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
|
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
function readVersion(): string {
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
+
// Resolve package.json by walking up from this module. Works under Bun (this
|
|
243
|
+
// file runs from src/) and Node (from dist/): package.json sits at the
|
|
244
|
+
// package root above both. Falls back to "0.0.0" if it can't be found rather
|
|
245
|
+
// than crashing `--version`.
|
|
246
|
+
try {
|
|
247
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
248
|
+
for (let i = 0; i < 6; i++) {
|
|
249
|
+
try {
|
|
250
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
251
|
+
if (pkg.name === "harnery" && typeof pkg.version === "string") return pkg.version;
|
|
252
|
+
} catch {
|
|
253
|
+
// no package.json at this level, or not ours; keep walking up
|
|
254
|
+
}
|
|
255
|
+
const parent = dirname(dir);
|
|
256
|
+
if (parent === dir) break;
|
|
257
|
+
dir = parent;
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
// import.meta.url unavailable or fs error; fall through
|
|
261
|
+
}
|
|
262
|
+
return "0.0.0";
|
|
242
263
|
}
|
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
*
|
|
7
7
|
* The library backing the `read` command.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* linkedom, @mozilla/readability, and turndown are heavy, so they are loaded
|
|
10
|
+
* lazily inside `htmlToMarkdown` rather than at module top level. A static
|
|
11
|
+
* import would pull them into CLI startup (every command is registered
|
|
12
|
+
* eagerly). linkedom replaced jsdom here: jsdom's transitive dependency tree
|
|
13
|
+
* (`html-encoding-sniffer`, `whatwg-url`) does a CommonJS `require()` of the
|
|
14
|
+
* ESM-only `@exodus/bytes`, which crashes a plain `node` process via
|
|
15
|
+
* ERR_REQUIRE_ESM. linkedom is a lighter, CJS-friendly DOM with no such chain.
|
|
16
|
+
* See ADR 0002.
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
export interface ReadabilityOptions {
|
|
@@ -50,15 +52,25 @@ export async function htmlToMarkdown(
|
|
|
50
52
|
throw new Error("Empty HTML input");
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
const [{ Readability }, {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
const [{ Readability }, { parseHTML }, { default: TurndownService }, { tables }] =
|
|
56
|
+
await Promise.all([
|
|
57
|
+
import("@mozilla/readability"),
|
|
58
|
+
import("linkedom"),
|
|
59
|
+
import("turndown"),
|
|
60
|
+
import("turndown-plugin-gfm"),
|
|
61
|
+
]);
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
// jsdom took a `url` option to set the document base so Readability resolves
|
|
64
|
+
// relative links to absolute. linkedom's parseHTML has no such option, so we
|
|
65
|
+
// inject a <base> tag, which sets doc.baseURI to the same effect. Only when
|
|
66
|
+
// the HTML doesn't already carry its own <base>.
|
|
67
|
+
const baseUrl = opts.url ?? "http://local/";
|
|
68
|
+
const htmlWithBase = /<base\b/i.test(html)
|
|
69
|
+
? html
|
|
70
|
+
: html.replace(/<head\b[^>]*>/i, (m) => `${m}<base href="${baseUrl}">`);
|
|
71
|
+
// If there's no <head> to anchor the <base> into, fall back to the raw HTML
|
|
72
|
+
// (relative links stay relative, matching a document with no base).
|
|
73
|
+
const { document: doc } = parseHTML(htmlWithBase.includes("<base") ? htmlWithBase : html);
|
|
62
74
|
preprocessDom(doc);
|
|
63
75
|
|
|
64
76
|
let contentHtml: string;
|