@monoes/monomindcli 2.13.0 → 2.14.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/.claude/skills/monodesign/scripts/context.mjs +1 -1
- package/.claude/skills/monodesign/scripts/detector/detect-antipatterns-browser.js +2 -2
- package/.claude/skills/monodesign/scripts/detector/rules/checks.mjs +2 -2
- package/.claude/skills/monodesign/scripts/live-browser.js +1 -1
- package/bin/cli.js +21 -1
- package/bin/mcp-server.js +21 -1
- package/dist/src/capabilities/cap-documents.d.ts.map +1 -1
- package/dist/src/capabilities/cap-documents.js +23 -0
- package/dist/src/capabilities/cap-documents.js.map +1 -1
- package/dist/src/commands/doc-filters.d.ts +19 -0
- package/dist/src/commands/doc-filters.d.ts.map +1 -0
- package/dist/src/commands/doc-filters.js +58 -0
- package/dist/src/commands/doc-filters.js.map +1 -0
- package/dist/src/commands/doc-library.d.ts +21 -0
- package/dist/src/commands/doc-library.d.ts.map +1 -0
- package/dist/src/commands/doc-library.js +390 -0
- package/dist/src/commands/doc-library.js.map +1 -0
- package/dist/src/commands/doc-list.d.ts +9 -0
- package/dist/src/commands/doc-list.d.ts.map +1 -0
- package/dist/src/commands/doc-list.js +99 -0
- package/dist/src/commands/doc-list.js.map +1 -0
- package/dist/src/commands/doc.d.ts.map +1 -1
- package/dist/src/commands/doc.js +49 -37
- package/dist/src/commands/doc.js.map +1 -1
- package/dist/src/commands/doctor-mcp-probe.d.ts +48 -0
- package/dist/src/commands/doctor-mcp-probe.d.ts.map +1 -0
- package/dist/src/commands/doctor-mcp-probe.js +205 -0
- package/dist/src/commands/doctor-mcp-probe.js.map +1 -0
- package/dist/src/commands/doctor-project-checks.d.ts +12 -1
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +55 -4
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +10 -1
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +17 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/mcp.d.ts.map +1 -1
- package/dist/src/commands/mcp.js +4 -3
- package/dist/src/commands/mcp.js.map +1 -1
- package/dist/src/commands/org-observe.d.ts.map +1 -1
- package/dist/src/commands/org-observe.js +14 -0
- package/dist/src/commands/org-observe.js.map +1 -1
- package/dist/src/commands/org.d.ts.map +1 -1
- package/dist/src/commands/org.js +12 -2
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/init/claudemd-generator.d.ts.map +1 -1
- package/dist/src/init/claudemd-generator.js +2 -1
- package/dist/src/init/claudemd-generator.js.map +1 -1
- package/dist/src/init/mcp-generator.d.ts.map +1 -1
- package/dist/src/init/mcp-generator.js +3 -3
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/init/types.d.ts +6 -0
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/init/write-runtime-config.d.ts.map +1 -1
- package/dist/src/init/write-runtime-config.js +12 -2
- package/dist/src/init/write-runtime-config.js.map +1 -1
- package/dist/src/knowledge/capture-envelope.d.ts +80 -0
- package/dist/src/knowledge/capture-envelope.d.ts.map +1 -0
- package/dist/src/knowledge/capture-envelope.js +165 -0
- package/dist/src/knowledge/capture-envelope.js.map +1 -0
- package/dist/src/knowledge/capture-text.d.ts +23 -0
- package/dist/src/knowledge/capture-text.d.ts.map +1 -0
- package/dist/src/knowledge/capture-text.js +49 -0
- package/dist/src/knowledge/capture-text.js.map +1 -0
- package/dist/src/knowledge/citation.d.ts +121 -0
- package/dist/src/knowledge/citation.d.ts.map +1 -0
- package/dist/src/knowledge/citation.js +252 -0
- package/dist/src/knowledge/citation.js.map +1 -0
- package/dist/src/knowledge/document-pipeline.d.ts +60 -0
- package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
- package/dist/src/knowledge/document-pipeline.js +188 -4
- package/dist/src/knowledge/document-pipeline.js.map +1 -1
- package/dist/src/knowledge/html-extract.d.ts +43 -0
- package/dist/src/knowledge/html-extract.d.ts.map +1 -0
- package/dist/src/knowledge/html-extract.js +286 -0
- package/dist/src/knowledge/html-extract.js.map +1 -0
- package/dist/src/knowledge/html-tags.d.ts +30 -0
- package/dist/src/knowledge/html-tags.d.ts.map +1 -0
- package/dist/src/knowledge/html-tags.js +229 -0
- package/dist/src/knowledge/html-tags.js.map +1 -0
- package/dist/src/knowledge/library.d.ts +106 -0
- package/dist/src/knowledge/library.d.ts.map +1 -0
- package/dist/src/knowledge/library.js +195 -0
- package/dist/src/knowledge/library.js.map +1 -0
- package/dist/src/knowledge/mhtml.d.ts +60 -0
- package/dist/src/knowledge/mhtml.d.ts.map +1 -0
- package/dist/src/knowledge/mhtml.js +154 -0
- package/dist/src/knowledge/mhtml.js.map +1 -0
- package/dist/src/knowledge/related.d.ts +58 -0
- package/dist/src/knowledge/related.d.ts.map +1 -0
- package/dist/src/knowledge/related.js +222 -0
- package/dist/src/knowledge/related.js.map +1 -0
- package/dist/src/knowledge/section-diff.d.ts +48 -0
- package/dist/src/knowledge/section-diff.d.ts.map +1 -0
- package/dist/src/knowledge/section-diff.js +151 -0
- package/dist/src/knowledge/section-diff.js.map +1 -0
- package/dist/src/knowledge/watch.d.ts +92 -0
- package/dist/src/knowledge/watch.d.ts.map +1 -0
- package/dist/src/knowledge/watch.js +221 -0
- package/dist/src/knowledge/watch.js.map +1 -0
- package/dist/src/mcp-server.d.ts.map +1 -1
- package/dist/src/mcp-server.js +44 -87
- package/dist/src/mcp-server.js.map +1 -1
- package/dist/src/mcp-tools/browser-instrument-tools.d.ts +35 -0
- package/dist/src/mcp-tools/browser-instrument-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-instrument-tools.js +359 -0
- package/dist/src/mcp-tools/browser-instrument-tools.js.map +1 -0
- package/dist/src/mcp-tools/browser-metrics.d.ts +42 -0
- package/dist/src/mcp-tools/browser-metrics.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-metrics.js +91 -0
- package/dist/src/mcp-tools/browser-metrics.js.map +1 -0
- package/dist/src/mcp-tools/browser-profile-tools.d.ts +16 -0
- package/dist/src/mcp-tools/browser-profile-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-profile-tools.js +324 -0
- package/dist/src/mcp-tools/browser-profile-tools.js.map +1 -0
- package/dist/src/mcp-tools/browser-session.d.ts +68 -0
- package/dist/src/mcp-tools/browser-session.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-session.js +224 -0
- package/dist/src/mcp-tools/browser-session.js.map +1 -0
- package/dist/src/mcp-tools/browser-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/browser-tools.js +10 -176
- package/dist/src/mcp-tools/browser-tools.js.map +1 -1
- package/dist/src/mcp-tools/capture-resource-read.d.ts +115 -0
- package/dist/src/mcp-tools/capture-resource-read.d.ts.map +1 -0
- package/dist/src/mcp-tools/capture-resource-read.js +296 -0
- package/dist/src/mcp-tools/capture-resource-read.js.map +1 -0
- package/dist/src/mcp-tools/capture-resource-tools.d.ts +22 -0
- package/dist/src/mcp-tools/capture-resource-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/capture-resource-tools.js +182 -0
- package/dist/src/mcp-tools/capture-resource-tools.js.map +1 -0
- package/dist/src/mcp-tools/capture-resources.d.ts +142 -0
- package/dist/src/mcp-tools/capture-resources.d.ts.map +1 -0
- package/dist/src/mcp-tools/capture-resources.js +289 -0
- package/dist/src/mcp-tools/capture-resources.js.map +1 -0
- package/dist/src/mcp-tools/index.d.ts +3 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +6 -0
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/knowledge-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/knowledge-tools.js +9 -1
- package/dist/src/mcp-tools/knowledge-tools.js.map +1 -1
- package/dist/src/mcp-tools/resource-router.d.ts +86 -0
- package/dist/src/mcp-tools/resource-router.d.ts.map +1 -0
- package/dist/src/mcp-tools/resource-router.js +181 -0
- package/dist/src/mcp-tools/resource-router.js.map +1 -0
- package/dist/src/memory/memory-bridge.js +1 -1
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/orgrt/agent-runner.d.ts +38 -0
- package/dist/src/orgrt/agent-runner.d.ts.map +1 -1
- package/dist/src/orgrt/agent-runner.js +47 -0
- package/dist/src/orgrt/agent-runner.js.map +1 -1
- package/dist/src/orgrt/checkpoint.d.ts +12 -1
- package/dist/src/orgrt/checkpoint.d.ts.map +1 -1
- package/dist/src/orgrt/checkpoint.js +15 -6
- package/dist/src/orgrt/checkpoint.js.map +1 -1
- package/dist/src/orgrt/completion-gate.d.ts +74 -0
- package/dist/src/orgrt/completion-gate.d.ts.map +1 -1
- package/dist/src/orgrt/completion-gate.js +45 -0
- package/dist/src/orgrt/completion-gate.js.map +1 -1
- package/dist/src/orgrt/cost-tier.d.ts +144 -0
- package/dist/src/orgrt/cost-tier.d.ts.map +1 -0
- package/dist/src/orgrt/cost-tier.js +183 -0
- package/dist/src/orgrt/cost-tier.js.map +1 -0
- package/dist/src/orgrt/cross-org.d.ts.map +1 -1
- package/dist/src/orgrt/cross-org.js +14 -0
- package/dist/src/orgrt/cross-org.js.map +1 -1
- package/dist/src/orgrt/daemon.d.ts +12 -1
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +216 -49
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/decisions.d.ts +14 -2
- package/dist/src/orgrt/decisions.d.ts.map +1 -1
- package/dist/src/orgrt/decisions.js +216 -10
- package/dist/src/orgrt/decisions.js.map +1 -1
- package/dist/src/orgrt/file-roots.d.ts +7 -0
- package/dist/src/orgrt/file-roots.d.ts.map +1 -1
- package/dist/src/orgrt/file-roots.js +38 -1
- package/dist/src/orgrt/file-roots.js.map +1 -1
- package/dist/src/orgrt/forwarder.d.ts.map +1 -1
- package/dist/src/orgrt/forwarder.js +16 -2
- package/dist/src/orgrt/forwarder.js.map +1 -1
- package/dist/src/orgrt/idle-deadline.d.ts +74 -1
- package/dist/src/orgrt/idle-deadline.d.ts.map +1 -1
- package/dist/src/orgrt/idle-deadline.js +52 -2
- package/dist/src/orgrt/idle-deadline.js.map +1 -1
- package/dist/src/orgrt/loadouts.d.ts +38 -0
- package/dist/src/orgrt/loadouts.d.ts.map +1 -0
- package/dist/src/orgrt/loadouts.js +129 -0
- package/dist/src/orgrt/loadouts.js.map +1 -0
- package/dist/src/orgrt/mailbox.d.ts +17 -1
- package/dist/src/orgrt/mailbox.d.ts.map +1 -1
- package/dist/src/orgrt/mailbox.js +50 -3
- package/dist/src/orgrt/mailbox.js.map +1 -1
- package/dist/src/orgrt/policy.d.ts +42 -2
- package/dist/src/orgrt/policy.d.ts.map +1 -1
- package/dist/src/orgrt/policy.js +53 -8
- package/dist/src/orgrt/policy.js.map +1 -1
- package/dist/src/orgrt/prompt-vars.d.ts +11 -0
- package/dist/src/orgrt/prompt-vars.d.ts.map +1 -0
- package/dist/src/orgrt/prompt-vars.js +49 -0
- package/dist/src/orgrt/prompt-vars.js.map +1 -0
- package/dist/src/orgrt/questions.d.ts +22 -1
- package/dist/src/orgrt/questions.d.ts.map +1 -1
- package/dist/src/orgrt/questions.js +24 -3
- package/dist/src/orgrt/questions.js.map +1 -1
- package/dist/src/orgrt/review-packet.d.ts +29 -0
- package/dist/src/orgrt/review-packet.d.ts.map +1 -0
- package/dist/src/orgrt/review-packet.js +67 -0
- package/dist/src/orgrt/review-packet.js.map +1 -0
- package/dist/src/orgrt/role-sandbox.d.ts.map +1 -1
- package/dist/src/orgrt/role-sandbox.js +16 -3
- package/dist/src/orgrt/role-sandbox.js.map +1 -1
- package/dist/src/orgrt/session-ledger.d.ts +68 -0
- package/dist/src/orgrt/session-ledger.d.ts.map +1 -0
- package/dist/src/orgrt/session-ledger.js +128 -0
- package/dist/src/orgrt/session-ledger.js.map +1 -0
- package/dist/src/orgrt/session.d.ts +45 -5
- package/dist/src/orgrt/session.d.ts.map +1 -1
- package/dist/src/orgrt/session.js +397 -27
- package/dist/src/orgrt/session.js.map +1 -1
- package/dist/src/orgrt/task-dag.d.ts +34 -1
- package/dist/src/orgrt/task-dag.d.ts.map +1 -1
- package/dist/src/orgrt/task-dag.js +58 -7
- package/dist/src/orgrt/task-dag.js.map +1 -1
- package/dist/src/orgrt/tool-spill.d.ts +94 -0
- package/dist/src/orgrt/tool-spill.d.ts.map +1 -0
- package/dist/src/orgrt/tool-spill.js +180 -0
- package/dist/src/orgrt/tool-spill.js.map +1 -0
- package/dist/src/orgrt/types.d.ts +138 -0
- package/dist/src/orgrt/types.d.ts.map +1 -1
- package/dist/src/orgrt/types.js +148 -0
- package/dist/src/orgrt/types.js.map +1 -1
- package/dist/src/platform-adapters/renderers/mcp.d.ts +20 -2
- package/dist/src/platform-adapters/renderers/mcp.d.ts.map +1 -1
- package/dist/src/platform-adapters/renderers/mcp.js +25 -5
- package/dist/src/platform-adapters/renderers/mcp.js.map +1 -1
- package/dist/src/protocol-capabilities.d.ts +2 -1
- package/dist/src/protocol-capabilities.d.ts.map +1 -1
- package/dist/src/protocol-capabilities.js +2 -1
- package/dist/src/protocol-capabilities.js.map +1 -1
- package/dist/src/ui/dashboard.html +450 -164
- package/dist/src/ui/org-hil.mjs +276 -0
- package/dist/src/ui/org-runtime.mjs +350 -0
- package/dist/src/ui/orgs.html +15 -4
- package/dist/src/ui/routes-org.mjs +290 -541
- package/dist/src/ui/server.mjs +25 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -8
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MHTML / MHT container parsing (RCL-01).
|
|
3
|
+
*
|
|
4
|
+
* An MHTML archive is an RFC 2557 `multipart/related` MIME message: the page
|
|
5
|
+
* plus every subresource it referenced, each part carrying its own transfer
|
|
6
|
+
* encoding and charset. Chrome's `Page.captureSnapshot` writes them
|
|
7
|
+
* quoted-printable; Internet Explorer and most "save as web archive" tools
|
|
8
|
+
* write base64.
|
|
9
|
+
*
|
|
10
|
+
* Only the ROOT frame is prose. Picking the first `text/html` part instead is
|
|
11
|
+
* how an ad iframe ends up indexed as the article, so the root is resolved
|
|
12
|
+
* from the container's `Snapshot-Content-Location` / `Content-Location` when
|
|
13
|
+
* one is present.
|
|
14
|
+
*
|
|
15
|
+
* Read the file as `latin1` (or pass a Buffer): byte fidelity matters before
|
|
16
|
+
* transfer decoding, and the real charset is only known per part.
|
|
17
|
+
*
|
|
18
|
+
* @module v1/cli/knowledge/mhtml
|
|
19
|
+
*/
|
|
20
|
+
/** Decode a quoted-printable body to bytes: soft line breaks vanish, `=XX`
|
|
21
|
+
* becomes one byte, everything else is its own latin1 byte. */
|
|
22
|
+
export function decodeQuotedPrintable(input) {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (let i = 0; i < input.length; i++) {
|
|
25
|
+
const ch = input[i];
|
|
26
|
+
if (ch !== '=') {
|
|
27
|
+
out.push(input.charCodeAt(i) & 0xff);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (input[i + 1] === '\r' && input[i + 2] === '\n') {
|
|
31
|
+
i += 2; // soft line break
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (input[i + 1] === '\n') {
|
|
35
|
+
i += 1; // soft line break, LF-only file
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const hex = input.slice(i + 1, i + 3);
|
|
39
|
+
if (/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
40
|
+
out.push(Number.parseInt(hex, 16));
|
|
41
|
+
i += 2;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
out.push(0x3d); // a lone `=` — keep it
|
|
45
|
+
}
|
|
46
|
+
return Buffer.from(out);
|
|
47
|
+
}
|
|
48
|
+
function decodeBody(raw, encoding) {
|
|
49
|
+
switch (encoding) {
|
|
50
|
+
case 'quoted-printable':
|
|
51
|
+
return decodeQuotedPrintable(raw);
|
|
52
|
+
case 'base64':
|
|
53
|
+
return Buffer.from(raw.replace(/\s+/g, ''), 'base64');
|
|
54
|
+
default:
|
|
55
|
+
return Buffer.from(raw, 'latin1');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Decode bytes under a MIME charset, falling back to UTF-8 (then latin1)
|
|
59
|
+
* rather than failing an ingest over an exotic label. */
|
|
60
|
+
export function decodeCharset(bytes, charset) {
|
|
61
|
+
const label = (charset ?? 'utf-8').trim().toLowerCase();
|
|
62
|
+
for (const candidate of [label, 'utf-8']) {
|
|
63
|
+
try {
|
|
64
|
+
return new TextDecoder(candidate, { fatal: false }).decode(bytes);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
/* unknown label — try the next */
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return bytes.toString('latin1');
|
|
71
|
+
}
|
|
72
|
+
/** Split a MIME chunk into unfolded headers and the body after the first
|
|
73
|
+
* blank line. Continuation lines (leading space/tab) join their header. */
|
|
74
|
+
function splitHeaders(chunk) {
|
|
75
|
+
const sep = /\r?\n\r?\n/.exec(chunk);
|
|
76
|
+
const headerText = sep ? chunk.slice(0, sep.index) : chunk;
|
|
77
|
+
const body = sep ? chunk.slice(sep.index + sep[0].length) : '';
|
|
78
|
+
const headers = {};
|
|
79
|
+
let current = '';
|
|
80
|
+
for (const rawLine of headerText.split(/\r?\n/)) {
|
|
81
|
+
if (/^[ \t]/.test(rawLine) && current) {
|
|
82
|
+
headers[current] += ` ${rawLine.trim()}`;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const colon = rawLine.indexOf(':');
|
|
86
|
+
if (colon === -1)
|
|
87
|
+
continue;
|
|
88
|
+
current = rawLine.slice(0, colon).trim().toLowerCase();
|
|
89
|
+
headers[current] = rawLine.slice(colon + 1).trim();
|
|
90
|
+
}
|
|
91
|
+
return { headers, body };
|
|
92
|
+
}
|
|
93
|
+
function param(headerValue, name) {
|
|
94
|
+
if (!headerValue)
|
|
95
|
+
return null;
|
|
96
|
+
const m = new RegExp(`${name}\\s*=\\s*("[^"]*"|'[^']*'|[^;\\s]+)`, 'i').exec(headerValue);
|
|
97
|
+
return m ? m[1].replace(/^["']|["']$/g, '') : null;
|
|
98
|
+
}
|
|
99
|
+
function toPart(chunk) {
|
|
100
|
+
const { headers, body } = splitHeaders(chunk);
|
|
101
|
+
const contentType = (headers['content-type'] ?? 'text/html').split(';')[0].trim().toLowerCase();
|
|
102
|
+
const encoding = (headers['content-transfer-encoding'] ?? 'binary').trim().toLowerCase();
|
|
103
|
+
return {
|
|
104
|
+
contentType,
|
|
105
|
+
charset: param(headers['content-type'], 'charset')?.toLowerCase() ?? null,
|
|
106
|
+
location: headers['content-location'] ?? null,
|
|
107
|
+
encoding,
|
|
108
|
+
body: decodeBody(body, encoding),
|
|
109
|
+
headers,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Parse an MHTML container. Anything that is not a well-formed multipart
|
|
114
|
+
* message is treated as a single part, so a plain `.html` file renamed to
|
|
115
|
+
* `.mhtml` still ingests.
|
|
116
|
+
*/
|
|
117
|
+
export function parseMhtml(raw) {
|
|
118
|
+
const text = typeof raw === 'string' ? raw : raw.toString('latin1');
|
|
119
|
+
const { headers, body } = splitHeaders(text);
|
|
120
|
+
const snapshotLocation = headers['snapshot-content-location'] ?? headers['content-location'] ?? null;
|
|
121
|
+
const subject = headers.subject ?? null;
|
|
122
|
+
const boundary = param(headers['content-type'], 'boundary');
|
|
123
|
+
let parts;
|
|
124
|
+
if (!boundary) {
|
|
125
|
+
// No boundary: either a single-part message or a bare HTML file. Feeding
|
|
126
|
+
// the whole document through `toPart` covers both — an HTML file has no
|
|
127
|
+
// header block, so everything lands in the body.
|
|
128
|
+
parts = [toPart(/^[a-z-]+:/i.test(text) ? text : `Content-Type: text/html\r\n\r\n${text}`)];
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const delimiter = `--${boundary}`;
|
|
132
|
+
parts = body
|
|
133
|
+
.split(delimiter)
|
|
134
|
+
.slice(1) // text before the first delimiter is the MIME preamble
|
|
135
|
+
.filter((chunk) => !chunk.startsWith('--')) // the closing `--boundary--`
|
|
136
|
+
.map((chunk) => chunk.replace(/^\r?\n/, ''))
|
|
137
|
+
.map(toPart);
|
|
138
|
+
}
|
|
139
|
+
const html = parts.filter((p) => p.contentType === 'text/html' || p.contentType === 'application/xhtml+xml');
|
|
140
|
+
const root = (snapshotLocation ? html.find((p) => p.location === snapshotLocation) : undefined) ??
|
|
141
|
+
html[0] ??
|
|
142
|
+
null;
|
|
143
|
+
return { root, parts, snapshotLocation, subject };
|
|
144
|
+
}
|
|
145
|
+
/** Parse a container and return its root frame decoded to a string. */
|
|
146
|
+
export function mhtmlToHtml(raw) {
|
|
147
|
+
const parsed = parseMhtml(raw);
|
|
148
|
+
return {
|
|
149
|
+
html: parsed.root ? decodeCharset(parsed.root.body, parsed.root.charset) : '',
|
|
150
|
+
location: parsed.root?.location ?? parsed.snapshotLocation,
|
|
151
|
+
subject: parsed.subject,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=mhtml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mhtml.js","sourceRoot":"","sources":["../../../src/knowledge/mhtml.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Related-at-save (RCL-03) — "3 things in your brain relate to this".
|
|
3
|
+
*
|
|
4
|
+
* The extension calls this on EVERY capture, while the user watches. That
|
|
5
|
+
* single fact sets every design decision here:
|
|
6
|
+
*
|
|
7
|
+
* - it never throws. An empty store, a missing embedder, a URL that was
|
|
8
|
+
* never captured — all of them are an empty list, because a capture must
|
|
9
|
+
* not fail because the sidebar had nothing to say;
|
|
10
|
+
* - it is bounded. The vector pass races a timeout (default 2s) and the
|
|
11
|
+
* cheap signals — same URL, same site, read straight off the metadata log —
|
|
12
|
+
* are computed first, so a slow or absent embedder degrades to a fast
|
|
13
|
+
* answer instead of no answer;
|
|
14
|
+
* - the target itself is never in its own results, by path AND by canonical
|
|
15
|
+
* URL, so a re-capture does not "relate" to the version it replaced.
|
|
16
|
+
*
|
|
17
|
+
* Three signals, combined: exact URL (the same page, captured before),
|
|
18
|
+
* same site, and vector similarity over the chunks already embedded.
|
|
19
|
+
*
|
|
20
|
+
* @module v1/cli/knowledge/related
|
|
21
|
+
*/
|
|
22
|
+
export type RelatedReason = 'same-url' | 'same-site' | 'similar';
|
|
23
|
+
export interface RelatedDocument {
|
|
24
|
+
filePath: string;
|
|
25
|
+
scope: string;
|
|
26
|
+
title: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
site?: string;
|
|
29
|
+
/** 0–1. Exact-URL matches lead; similarity and site agreement compound. */
|
|
30
|
+
score: number;
|
|
31
|
+
reasons: RelatedReason[];
|
|
32
|
+
capturedAt?: string;
|
|
33
|
+
/** The most similar passage, when similarity is one of the reasons. */
|
|
34
|
+
excerpt?: string;
|
|
35
|
+
chunkIndex?: number;
|
|
36
|
+
anchor?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface RelatedOptions {
|
|
39
|
+
rootDir?: string;
|
|
40
|
+
scope?: string;
|
|
41
|
+
limit?: number;
|
|
42
|
+
/** Budget for the vector pass. The metadata signals ignore it: they are
|
|
43
|
+
* synchronous file reads. */
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
minScore?: number;
|
|
46
|
+
store?: 'project' | 'global' | 'all';
|
|
47
|
+
/** Text to compare against, when the caller already has it (a capture that
|
|
48
|
+
* has not been ingested yet). */
|
|
49
|
+
text?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The documents most related to `target` (a URL, a docId, or an indexed path).
|
|
53
|
+
*
|
|
54
|
+
* Safe to call on every capture: bounded, and empty rather than thrown on any
|
|
55
|
+
* failure.
|
|
56
|
+
*/
|
|
57
|
+
export declare function relatedDocuments(target: string, opts?: RelatedOptions): Promise<RelatedDocument[]>;
|
|
58
|
+
//# sourceMappingURL=related.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"related.d.ts","sourceRoot":"","sources":["../../../src/knowledge/related.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;kCAC8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC;sCACkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAwFD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,eAAe,EAAE,CAAC,CAmH5B"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Related-at-save (RCL-03) — "3 things in your brain relate to this".
|
|
3
|
+
*
|
|
4
|
+
* The extension calls this on EVERY capture, while the user watches. That
|
|
5
|
+
* single fact sets every design decision here:
|
|
6
|
+
*
|
|
7
|
+
* - it never throws. An empty store, a missing embedder, a URL that was
|
|
8
|
+
* never captured — all of them are an empty list, because a capture must
|
|
9
|
+
* not fail because the sidebar had nothing to say;
|
|
10
|
+
* - it is bounded. The vector pass races a timeout (default 2s) and the
|
|
11
|
+
* cheap signals — same URL, same site, read straight off the metadata log —
|
|
12
|
+
* are computed first, so a slow or absent embedder degrades to a fast
|
|
13
|
+
* answer instead of no answer;
|
|
14
|
+
* - the target itself is never in its own results, by path AND by canonical
|
|
15
|
+
* URL, so a re-capture does not "relate" to the version it replaced.
|
|
16
|
+
*
|
|
17
|
+
* Three signals, combined: exact URL (the same page, captured before),
|
|
18
|
+
* same site, and vector similarity over the chunks already embedded.
|
|
19
|
+
*
|
|
20
|
+
* @module v1/cli/knowledge/related
|
|
21
|
+
*/
|
|
22
|
+
import * as fs from 'node:fs';
|
|
23
|
+
import * as path from 'node:path';
|
|
24
|
+
import { captureIdentityUrl, readCaptureProvenance } from './capture-envelope.js';
|
|
25
|
+
import { passageQuote } from './citation.js';
|
|
26
|
+
import { documentCapturedAt, documentSite } from './library.js';
|
|
27
|
+
const DEFAULT_LIMIT = 3;
|
|
28
|
+
const DEFAULT_TIMEOUT_MS = 2000;
|
|
29
|
+
const DEFAULT_MIN_SCORE = 0.25;
|
|
30
|
+
const QUERY_CHARS = 1200;
|
|
31
|
+
const SAME_URL_SCORE = 1;
|
|
32
|
+
/** Ceiling for everything that is not literally the same page. */
|
|
33
|
+
const NEAR_SCORE_CAP = 0.95;
|
|
34
|
+
const SAME_SITE_SCORE = 0.45;
|
|
35
|
+
const BOTH_SIGNALS_BONUS = 0.1;
|
|
36
|
+
const EXCERPT_CHARS = 200;
|
|
37
|
+
function stripFragment(url) {
|
|
38
|
+
const hash = url.indexOf('#');
|
|
39
|
+
return (hash === -1 ? url : url.slice(0, hash)).trim();
|
|
40
|
+
}
|
|
41
|
+
function looksLikeUrl(target) {
|
|
42
|
+
return /^https?:\/\//i.test(target.trim());
|
|
43
|
+
}
|
|
44
|
+
function hostOf(url) {
|
|
45
|
+
if (!url)
|
|
46
|
+
return undefined;
|
|
47
|
+
try {
|
|
48
|
+
return new URL(url).hostname.toLowerCase().replace(/^www\./, '');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Words a URL alone gives us — the fallback query when there is no text. */
|
|
55
|
+
function urlWords(url) {
|
|
56
|
+
try {
|
|
57
|
+
const u = new URL(url);
|
|
58
|
+
return [u.hostname.replace(/^www\./, ''), ...u.pathname.split(/[/\-_.]+/)]
|
|
59
|
+
.filter((w) => w && !/^\d+$/.test(w) && w.length > 1)
|
|
60
|
+
.join(' ');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return url;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function queryText(record, target, provided) {
|
|
67
|
+
if (provided?.trim())
|
|
68
|
+
return provided.slice(0, QUERY_CHARS);
|
|
69
|
+
const parts = [];
|
|
70
|
+
if (record?.provenance?.title)
|
|
71
|
+
parts.push(record.provenance.title);
|
|
72
|
+
if (record && fs.existsSync(record.filePath)) {
|
|
73
|
+
try {
|
|
74
|
+
const { extractText } = await import('../capabilities/cap-documents.js');
|
|
75
|
+
const stat = fs.statSync(record.filePath);
|
|
76
|
+
const text = await extractText({
|
|
77
|
+
path: record.filePath,
|
|
78
|
+
absolutePath: record.filePath,
|
|
79
|
+
extension: path.extname(record.filePath).toLowerCase(),
|
|
80
|
+
size: stat.size,
|
|
81
|
+
modified: stat.mtime,
|
|
82
|
+
created: stat.birthtime,
|
|
83
|
+
});
|
|
84
|
+
if (text)
|
|
85
|
+
parts.push(text.slice(0, QUERY_CHARS));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
/* fall through to the URL words */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!parts.length)
|
|
92
|
+
parts.push(urlWords(looksLikeUrl(target) ? target : path.basename(target)));
|
|
93
|
+
return parts.join('\n').slice(0, QUERY_CHARS);
|
|
94
|
+
}
|
|
95
|
+
/** Resolve the vector pass or give up on time — whichever comes first. */
|
|
96
|
+
function withTimeout(work, ms, fallback) {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
const timer = setTimeout(() => resolve(fallback), ms);
|
|
99
|
+
work
|
|
100
|
+
.then((value) => {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
resolve(value);
|
|
103
|
+
})
|
|
104
|
+
.catch(() => {
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
resolve(fallback);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The documents most related to `target` (a URL, a docId, or an indexed path).
|
|
112
|
+
*
|
|
113
|
+
* Safe to call on every capture: bounded, and empty rather than thrown on any
|
|
114
|
+
* failure.
|
|
115
|
+
*/
|
|
116
|
+
export async function relatedDocuments(target, opts = {}) {
|
|
117
|
+
try {
|
|
118
|
+
const pipeline = await import('./document-pipeline.js');
|
|
119
|
+
const scope = opts.scope;
|
|
120
|
+
const rootDir = opts.rootDir ?? pipeline.getKnowledgeRoot(scope ?? 'shared');
|
|
121
|
+
const limit = opts.limit ?? DEFAULT_LIMIT;
|
|
122
|
+
const self = pipeline.findDocumentRecord(rootDir, target, scope);
|
|
123
|
+
// A capture that has been written but not yet ingested is the case this
|
|
124
|
+
// story exists for: the extension has the directory on disk and wants to
|
|
125
|
+
// know what it relates to BEFORE the ingest finishes. Its identity comes
|
|
126
|
+
// from the envelope's own meta.json.
|
|
127
|
+
const pending = !self && !looksLikeUrl(target) ? readCaptureProvenance(target) : null;
|
|
128
|
+
const selfUrl = self?.canonicalUrl ??
|
|
129
|
+
(looksLikeUrl(target) ? stripFragment(target) : captureIdentityUrl(pending));
|
|
130
|
+
const selfSite = hostOf(selfUrl);
|
|
131
|
+
const selfPaths = new Set([self?.filePath, looksLikeUrl(target) ? undefined : path.resolve(target)].filter((p) => !!p));
|
|
132
|
+
// Excluding by URL is right when the target IS that page — an indexed
|
|
133
|
+
// document, or a caller asking about the URL itself. It is wrong for a
|
|
134
|
+
// capture still sitting in the inbox: there the earlier capture of the
|
|
135
|
+
// same page is the single most related thing in the brain.
|
|
136
|
+
const excludeByUrl = !!self || looksLikeUrl(target);
|
|
137
|
+
const isSelf = (doc) => selfPaths.has(doc.filePath) || (excludeByUrl && !!selfUrl && doc.canonicalUrl === selfUrl);
|
|
138
|
+
const docs = pipeline.listDocuments(rootDir, scope).filter((d) => !isSelf(d));
|
|
139
|
+
const byPath = new Map(docs.map((d) => [d.filePath, d]));
|
|
140
|
+
const scored = new Map();
|
|
141
|
+
const put = (doc, reason, score, extra = {}) => {
|
|
142
|
+
const existing = scored.get(doc.filePath);
|
|
143
|
+
if (existing) {
|
|
144
|
+
if (!existing.reasons.includes(reason))
|
|
145
|
+
existing.reasons.push(reason);
|
|
146
|
+
// Agreeing signals compound, but only the same page itself reaches 1 —
|
|
147
|
+
// otherwise a same-site neighbour that also embeds well would tie with
|
|
148
|
+
// the earlier capture of this very page and win on sort order.
|
|
149
|
+
const cap = existing.reasons.includes('same-url') ? SAME_URL_SCORE : NEAR_SCORE_CAP;
|
|
150
|
+
existing.score = Math.min(cap, Math.max(existing.score, score) + BOTH_SIGNALS_BONUS);
|
|
151
|
+
Object.assign(existing, extra);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const url = doc.canonicalUrl ?? doc.provenance?.canonicalUrl ?? doc.provenance?.url;
|
|
155
|
+
scored.set(doc.filePath, {
|
|
156
|
+
filePath: doc.filePath,
|
|
157
|
+
scope: doc.scope,
|
|
158
|
+
title: doc.provenance?.title ?? path.basename(doc.filePath),
|
|
159
|
+
...(url ? { url } : {}),
|
|
160
|
+
...(documentSite(doc) ? { site: documentSite(doc) } : {}),
|
|
161
|
+
score,
|
|
162
|
+
reasons: [reason],
|
|
163
|
+
...(documentCapturedAt(doc) ? { capturedAt: documentCapturedAt(doc) } : {}),
|
|
164
|
+
...extra,
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
// 1 + 2 — the cheap signals, straight off the metadata log.
|
|
168
|
+
for (const doc of docs) {
|
|
169
|
+
const url = doc.canonicalUrl ?? doc.provenance?.canonicalUrl;
|
|
170
|
+
if (selfUrl && url && stripFragment(url) === selfUrl)
|
|
171
|
+
put(doc, 'same-url', SAME_URL_SCORE);
|
|
172
|
+
else if (selfSite && documentSite(doc) === selfSite)
|
|
173
|
+
put(doc, 'same-site', SAME_SITE_SCORE);
|
|
174
|
+
}
|
|
175
|
+
// 3 — similarity, bounded. A store with no embeddings comes back empty
|
|
176
|
+
// here and the metadata signals still stand.
|
|
177
|
+
// For a not-yet-ingested capture the text to compare against is the file
|
|
178
|
+
// the caller pointed at, read through the same extraction the ingest uses.
|
|
179
|
+
const queryRecord = self ?? (pending ? { filePath: path.resolve(target), provenance: pending } : undefined);
|
|
180
|
+
const query = await queryText(queryRecord, target, opts.text);
|
|
181
|
+
if (query.trim()) {
|
|
182
|
+
const hits = await withTimeout(pipeline.searchKnowledge(query, {
|
|
183
|
+
scope: scope ?? 'shared',
|
|
184
|
+
rootDir,
|
|
185
|
+
limit: Math.max(limit * 6, 12),
|
|
186
|
+
minScore: opts.minScore ?? DEFAULT_MIN_SCORE,
|
|
187
|
+
store: opts.store ?? 'all',
|
|
188
|
+
}), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS, []);
|
|
189
|
+
const best = new Map();
|
|
190
|
+
for (const hit of hits) {
|
|
191
|
+
if (!hit.filePath || selfPaths.has(hit.filePath))
|
|
192
|
+
continue;
|
|
193
|
+
const current = best.get(hit.filePath);
|
|
194
|
+
if (!current || hit.similarity > current.similarity)
|
|
195
|
+
best.set(hit.filePath, hit);
|
|
196
|
+
}
|
|
197
|
+
for (const [filePath, hit] of best) {
|
|
198
|
+
const doc = byPath.get(filePath);
|
|
199
|
+
if (!doc || isSelf(doc))
|
|
200
|
+
continue;
|
|
201
|
+
put(doc, 'similar', Math.min(1, hit.similarity), {
|
|
202
|
+
excerpt: passageQuote(hit.text ?? '', EXCERPT_CHARS),
|
|
203
|
+
chunkIndex: hit.chunkIndex,
|
|
204
|
+
...(hit.anchor ? { anchor: hit.anchor } : {}),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return [...scored.values()]
|
|
209
|
+
.sort((a, b) => b.score - a.score || (b.capturedAt ?? '').localeCompare(a.capturedAt ?? ''))
|
|
210
|
+
.slice(0, limit);
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
// Called on every capture: an unexpected failure is "nothing related",
|
|
214
|
+
// never a broken save. It is still a bug when it is one of ours, so it is
|
|
215
|
+
// visible under DEBUG rather than only in an empty sidebar.
|
|
216
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG) {
|
|
217
|
+
console.error(`[relatedDocuments] ${target}:`, err);
|
|
218
|
+
}
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=related.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"related.js","sourceRoot":"","sources":["../../../src/knowledge/related.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic diff between two versions of a captured page (RCL-08).
|
|
3
|
+
*
|
|
4
|
+
* A character diff of a re-captured page is noise: the byline date moved, an
|
|
5
|
+
* ad slot re-rendered, the markdown re-wrapped. What a watcher actually wants
|
|
6
|
+
* to be told is WHICH SECTIONS changed and roughly how much — "Pricing gained
|
|
7
|
+
* a paragraph, Changelog got three new entries, everything else is the same".
|
|
8
|
+
*
|
|
9
|
+
* So the unit here is the markdown section (heading + body), keyed by its
|
|
10
|
+
* full heading path, and the measure is words and sentences rather than
|
|
11
|
+
* characters. Sentences are also what gives a change its sample line: the
|
|
12
|
+
* first sentence that is in the new version and was not in the old one is the
|
|
13
|
+
* most useful single thing to print.
|
|
14
|
+
*
|
|
15
|
+
* No dependency, no LLM: heading paths and sentence sets are enough to say
|
|
16
|
+
* what moved.
|
|
17
|
+
*
|
|
18
|
+
* @module v1/cli/knowledge/section-diff
|
|
19
|
+
*/
|
|
20
|
+
export interface Section {
|
|
21
|
+
/** Full heading path, e.g. `Pricing > Enterprise`. `(intro)` for text
|
|
22
|
+
* before the first heading. */
|
|
23
|
+
key: string;
|
|
24
|
+
heading: string;
|
|
25
|
+
level: number;
|
|
26
|
+
body: string;
|
|
27
|
+
}
|
|
28
|
+
export interface SectionChange {
|
|
29
|
+
key: string;
|
|
30
|
+
addedWords: number;
|
|
31
|
+
removedWords: number;
|
|
32
|
+
/** First sentence present in the new version and absent from the old. */
|
|
33
|
+
sample?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface SectionDiff {
|
|
36
|
+
added: string[];
|
|
37
|
+
removed: string[];
|
|
38
|
+
changed: SectionChange[];
|
|
39
|
+
unchanged: number;
|
|
40
|
+
/** One line, ready to print or to put in a notification. */
|
|
41
|
+
summary: string;
|
|
42
|
+
}
|
|
43
|
+
/** Split markdown into sections keyed by heading path. Duplicate paths get a
|
|
44
|
+
* `#2` suffix so two "Notes" sections never collapse into one. */
|
|
45
|
+
export declare function splitSections(text: string): Section[];
|
|
46
|
+
/** What changed between two versions of the same page. */
|
|
47
|
+
export declare function diffSections(oldText: string, newText: string): SectionDiff;
|
|
48
|
+
//# sourceMappingURL=section-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-diff.d.ts","sourceRoot":"","sources":["../../../src/knowledge/section-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,OAAO;IACtB;oCACgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;CACjB;AAKD;mEACmE;AACnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAmCrD;AA2DD,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAgC1E"}
|