@sayknow-cli/coding-agent 0.3.11 → 0.3.12
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/CHANGELOG.md +0 -21
- package/dist/types/build-metadata.d.ts +7 -0
- package/dist/types/capability/mcp.d.ts +3 -1
- package/dist/types/commands/mcp.d.ts +1 -1
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/modes/acp/acp-event-mapper.d.ts +2 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +6 -0
- package/dist/types/modes/components/status-line.d.ts +1 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -2
- package/dist/types/modes/utils/ui-helpers.d.ts +12 -0
- package/dist/types/notifications/telegram-daemon.d.ts +2 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +4 -3
- package/dist/types/runtime-mcp/types.d.ts +4 -3
- package/dist/types/session/agent-session.d.ts +16 -0
- package/dist/types/session/session-manager.d.ts +1 -0
- package/dist/types/tools/skill.d.ts +9 -8
- package/dist/types/utils/linkedom.d.ts +4 -0
- package/dist/types/utils/pasted-image-path.d.ts +5 -3
- package/package.json +7 -7
- package/scripts/build-binary.ts +2 -43
- package/scripts/compile-args.ts +106 -0
- package/scripts/generate-docs-index.ts +1 -0
- package/src/build-metadata.ts +104 -0
- package/src/capability/mcp.ts +3 -1
- package/src/cli/mcp-cli.ts +108 -26
- package/src/cli.ts +21 -0
- package/src/commands/mcp.ts +8 -8
- package/src/commands/stats.ts +6 -2
- package/src/config/keybindings.ts +2 -2
- package/src/config/mcp-schema.json +21 -17
- package/src/config/model-profiles.ts +23 -23
- package/src/discovery/builtin.ts +14 -0
- package/src/discovery/mcp-json.ts +14 -0
- package/src/eval/js/executor.ts +7 -1
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/internal-urls/skc-protocol.ts +14 -1
- package/src/modes/acp/acp-agent.ts +72 -16
- package/src/modes/acp/acp-client-bridge.ts +27 -5
- package/src/modes/acp/acp-event-mapper.ts +39 -1
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/hook-selector.ts +4 -0
- package/src/modes/components/model-selector.ts +17 -6
- package/src/modes/components/session-selector.ts +2 -2
- package/src/modes/components/status-line/presets.ts +5 -19
- package/src/modes/components/status-line/segments.ts +14 -0
- package/src/modes/components/status-line/types.ts +6 -0
- package/src/modes/components/status-line.ts +13 -6
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +4 -1
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +9 -9
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +7 -3
- package/src/modes/interactive-mode.ts +12 -4
- package/src/modes/rpc/rpc-client.ts +30 -6
- package/src/modes/utils/ui-helpers.ts +154 -32
- package/src/notifications/telegram-daemon.ts +28 -2
- package/src/prompts/tools/skill.md +2 -2
- package/src/runtime-mcp/config-writer.ts +4 -3
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/types.ts +4 -3
- package/src/session/agent-session.ts +117 -3
- package/src/session/session-manager.ts +68 -40
- package/src/skc-runtime/state-runtime.ts +105 -12
- package/src/skc-runtime/workflow-manifest.generated.json +4 -28
- package/src/skc-runtime/workflow-manifest.ts +1 -7
- package/src/slash-commands/builtin-registry.ts +9 -4
- package/src/tools/bash-interactive.ts +9 -4
- package/src/tools/browser/readable.ts +2 -2
- package/src/tools/fetch.ts +5 -5
- package/src/tools/skill.ts +14 -11
- package/src/utils/linkedom.ts +14 -0
- package/src/utils/pasted-image-path.ts +23 -9
- package/src/web/scrapers/arxiv.ts +2 -2
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/iacr.ts +2 -2
- package/src/web/scrapers/readthedocs.ts +2 -2
- package/src/web/scrapers/twitter.ts +3 -2
- package/src/web/scrapers/wikipedia.ts +2 -2
|
@@ -811,12 +811,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
811
811
|
{
|
|
812
812
|
name: "session",
|
|
813
813
|
priority: 88,
|
|
814
|
-
description: "Show
|
|
814
|
+
description: "Show session info or delete the current session transcript/artifacts",
|
|
815
815
|
acpDescription: "Show session information",
|
|
816
816
|
acpInputHint: "info|delete",
|
|
817
817
|
subcommands: [
|
|
818
818
|
{ name: "info", description: "Show current session id, title, and workspace" },
|
|
819
|
-
{ name: "delete", description: "Delete current session and
|
|
819
|
+
{ name: "delete", description: "Delete current session transcript and artifacts" },
|
|
820
820
|
],
|
|
821
821
|
allowArgs: true,
|
|
822
822
|
handle: async (command, runtime) => {
|
|
@@ -845,7 +845,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
845
845
|
return usage(`Failed to delete session: ${errorMessage(err)}`, runtime);
|
|
846
846
|
}
|
|
847
847
|
await runtime.output(
|
|
848
|
-
|
|
848
|
+
[
|
|
849
|
+
`Deleted current session transcript and artifacts: ${sessionFile}`,
|
|
850
|
+
"Other sessions and topic/history metadata were not deleted.",
|
|
851
|
+
].join("\n"),
|
|
849
852
|
);
|
|
850
853
|
return commandConsumed();
|
|
851
854
|
}
|
|
@@ -1021,8 +1024,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1021
1024
|
return commandConsumed();
|
|
1022
1025
|
}
|
|
1023
1026
|
if (args === "login" || args.startsWith("login ")) {
|
|
1027
|
+
const providerId = args.slice("login".length).trim();
|
|
1028
|
+
const loginCommand = providerId ? `/login ${providerId}` : "/login [provider-id]";
|
|
1024
1029
|
await runtime.output(
|
|
1025
|
-
|
|
1030
|
+
`Open the terminal UI and run ${loginCommand} for OAuth/subscription account login. Paste callbacks with /login <redirect URL or code>.`,
|
|
1026
1031
|
);
|
|
1027
1032
|
return commandConsumed();
|
|
1028
1033
|
}
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from "@sayknow-cli/tui";
|
|
13
13
|
import { sanitizeText } from "@sayknow-cli/utils";
|
|
14
14
|
import type { Terminal as XtermTerminalType } from "@xterm/headless";
|
|
15
|
-
import xterm from "@xterm/headless";
|
|
16
15
|
import { Settings } from "../config/settings";
|
|
17
16
|
import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
|
|
18
17
|
import type { Theme } from "../modes/theme/theme";
|
|
@@ -32,8 +31,6 @@ function normalizeCaptureChunk(chunk: string): string {
|
|
|
32
31
|
return sanitizeWithOptionalSixelPassthrough(normalized, sanitizeText);
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
const XtermTerminal = xterm.Terminal;
|
|
36
|
-
|
|
37
34
|
function normalizeInputForPty(data: string, applicationCursorKeysMode: boolean): string {
|
|
38
35
|
const kitty = parseKittySequence(data);
|
|
39
36
|
if (kitty?.eventType === 3) {
|
|
@@ -113,6 +110,7 @@ class BashInteractiveOverlayComponent implements Component {
|
|
|
113
110
|
private readonly command: string,
|
|
114
111
|
private readonly uiTheme: Theme,
|
|
115
112
|
private readonly getTerminalRows: () => number,
|
|
113
|
+
XtermTerminal: typeof XtermTerminalType,
|
|
116
114
|
) {
|
|
117
115
|
this.#terminal = new XtermTerminal({
|
|
118
116
|
cols: 120,
|
|
@@ -305,10 +303,17 @@ export async function runInteractiveBashPty(
|
|
|
305
303
|
headBytes: resolveOutputSinkHeadBytes(settings),
|
|
306
304
|
maxColumns: resolveOutputMaxColumns(settings),
|
|
307
305
|
});
|
|
306
|
+
const { default: xterm } = await import("@xterm/headless");
|
|
307
|
+
const XtermTerminal = xterm.Terminal;
|
|
308
308
|
const result = await ui.custom<BashInteractiveResult>(
|
|
309
309
|
(tui, uiTheme, _keybindings, done) => {
|
|
310
310
|
const session = new PtySession();
|
|
311
|
-
const component = new BashInteractiveOverlayComponent(
|
|
311
|
+
const component = new BashInteractiveOverlayComponent(
|
|
312
|
+
options.command,
|
|
313
|
+
uiTheme,
|
|
314
|
+
() => tui.terminal.rows,
|
|
315
|
+
XtermTerminal,
|
|
316
|
+
);
|
|
312
317
|
component.setSession(session);
|
|
313
318
|
let finished = false;
|
|
314
319
|
const finalize = (run: PtyRunResult) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Readability } from "@mozilla/readability";
|
|
2
|
-
import {
|
|
2
|
+
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
3
3
|
import { htmlToBasicMarkdown } from "../../web/scrapers/types";
|
|
4
4
|
|
|
5
5
|
export type ReadableFormat = "text" | "markdown";
|
|
@@ -31,7 +31,7 @@ export async function extractReadableFromHtml(
|
|
|
31
31
|
url: string,
|
|
32
32
|
format: ReadableFormat,
|
|
33
33
|
): Promise<ReadableResult | null> {
|
|
34
|
-
const { document } =
|
|
34
|
+
const { document } = await parseHtmlLazy(html);
|
|
35
35
|
|
|
36
36
|
// --- Primary: Readability article extraction ---
|
|
37
37
|
const article = new Readability(document).parse();
|
package/src/tools/fetch.ts
CHANGED
|
@@ -5,7 +5,6 @@ import type { ImageContent, TextContent } from "@sayknow-cli/ai";
|
|
|
5
5
|
import { htmlToMarkdown } from "@sayknow-cli/natives";
|
|
6
6
|
import { type Component, Text } from "@sayknow-cli/tui";
|
|
7
7
|
import { $which, ptree, truncate } from "@sayknow-cli/utils";
|
|
8
|
-
import { parseHTML } from "linkedom";
|
|
9
8
|
import type { Settings } from "../config/settings";
|
|
10
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
11
10
|
import { type Theme, theme } from "../modes/theme/theme";
|
|
@@ -15,6 +14,7 @@ import { DEFAULT_MAX_BYTES, truncateHead } from "../session/streaming-output";
|
|
|
15
14
|
import { renderStatusLine } from "../tui";
|
|
16
15
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
17
16
|
import { formatDimensionNote, resizeImage } from "../utils/image-resize";
|
|
17
|
+
import { parseHtmlLazy } from "../utils/linkedom";
|
|
18
18
|
import { ensureTool } from "../utils/tools-manager";
|
|
19
19
|
import { INSANE_NOTES, tryInsaneFetch } from "../web/insane/bridge";
|
|
20
20
|
import { validatePublicHttpUrl, validatePublicHttpUrlForInsane } from "../web/insane/url-guard";
|
|
@@ -483,9 +483,9 @@ function cleanFeedText(text: string): string {
|
|
|
483
483
|
/**
|
|
484
484
|
* Parse RSS/Atom feed to markdown
|
|
485
485
|
*/
|
|
486
|
-
function parseFeedToMarkdown(content: string, maxItems = 10): string {
|
|
486
|
+
async function parseFeedToMarkdown(content: string, maxItems = 10): Promise<string> {
|
|
487
487
|
try {
|
|
488
|
-
const doc =
|
|
488
|
+
const doc = (await parseHtmlLazy(content)).document;
|
|
489
489
|
|
|
490
490
|
// Try RSS
|
|
491
491
|
const channel = doc.querySelector("channel");
|
|
@@ -1026,7 +1026,7 @@ async function renderUrl(
|
|
|
1026
1026
|
}
|
|
1027
1027
|
|
|
1028
1028
|
if (isFeed || (isXml && (rawContent.includes("<rss") || rawContent.includes("<feed")))) {
|
|
1029
|
-
const parsed = parseFeedToMarkdown(rawContent);
|
|
1029
|
+
const parsed = await parseFeedToMarkdown(rawContent);
|
|
1030
1030
|
const output = finalizeOutput(parsed);
|
|
1031
1031
|
return {
|
|
1032
1032
|
url,
|
|
@@ -1119,7 +1119,7 @@ async function renderUrl(
|
|
|
1119
1119
|
const altResult = await loadPage(resolved, { timeout, signal });
|
|
1120
1120
|
if (altResult.ok && altResult.content.trim().length > 200) {
|
|
1121
1121
|
notes.push(`Used feed alternate: ${resolved}`);
|
|
1122
|
-
const parsed = parseFeedToMarkdown(altResult.content);
|
|
1122
|
+
const parsed = await parseFeedToMarkdown(altResult.content);
|
|
1123
1123
|
const output = finalizeOutput(parsed);
|
|
1124
1124
|
return {
|
|
1125
1125
|
url,
|
package/src/tools/skill.ts
CHANGED
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
* Lets the agent hand off to another available skill in the current turn. The
|
|
5
5
|
* callee's SKILL.md is dispatched through the same custom-message path used by
|
|
6
6
|
* `/skill:<name>` typing, as a user-attribution message delivered same-turn
|
|
7
|
-
* (without `deliverAs: "nextTurn"`).
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* (without `deliverAs: "nextTurn"`). When the caller is one of the canonical
|
|
8
|
+
* workflow skills, the tool first runs the native state handoff so workflow
|
|
9
|
+
* mode-state and `skill-active-state.json` transition atomically. Runtime
|
|
10
|
+
* project/user skills have no native workflow mode-state, so they are dispatched
|
|
11
|
+
* directly and tracked by the prompt observer instead.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
* `{complete, completed, handoff, failed, cancelled, canceled, inactive}`.
|
|
14
|
-
* agent declares readiness either by writing `current_phase: "handoff"` to
|
|
15
|
-
* mode-state or by running the handoff verb directly.
|
|
13
|
+
* Canonical workflow chaining is refused unless the caller's `current_phase` is
|
|
14
|
+
* in `{complete, completed, handoff, failed, cancelled, canceled, inactive}`.
|
|
15
|
+
* The agent declares readiness either by writing `current_phase: "handoff"` to
|
|
16
|
+
* its mode-state or by running the handoff verb directly.
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import type { AgentTool, AgentToolResult } from "@sayknow-cli/agent-core";
|
|
@@ -24,6 +25,7 @@ import { buildSkillPromptMessage } from "../extensibility/skills";
|
|
|
24
25
|
import skillDescription from "../prompts/tools/skill.md" with { type: "text" };
|
|
25
26
|
import { SKILL_PROMPT_MESSAGE_TYPE } from "../session/messages";
|
|
26
27
|
import { runNativeStateCommand } from "../skc-runtime/state-runtime";
|
|
28
|
+
import { isCanonicalSkcWorkflowSkill } from "../skill-state/active-state";
|
|
27
29
|
import type { ToolSession } from ".";
|
|
28
30
|
import { ToolError } from "./tool-errors";
|
|
29
31
|
|
|
@@ -130,9 +132,10 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
|
|
|
130
132
|
throw new ToolError(`skill tool: unknown skill "${requestedName}".${hint}`);
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
// Phase guard + atomic handoff
|
|
134
|
-
//
|
|
135
|
-
|
|
135
|
+
// Phase guard + atomic native handoff only apply to canonical workflow
|
|
136
|
+
// skills. Runtime project/user skills do not have a native mode-state,
|
|
137
|
+
// so there is no `skc state <skill>` command to run for them.
|
|
138
|
+
if (activeSkill && isCanonicalSkcWorkflowSkill(activeSkill)) {
|
|
136
139
|
const phase = (this.#session.getActiveSkillPhase?.() ?? "running").trim().toLowerCase();
|
|
137
140
|
if (!TERMINAL_PHASES.has(phase)) {
|
|
138
141
|
throw new ToolError(
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type LinkedomModule = typeof import("linkedom");
|
|
2
|
+
|
|
3
|
+
type ParseHtml = LinkedomModule["parseHTML"];
|
|
4
|
+
|
|
5
|
+
let parseHtml: ParseHtml | undefined;
|
|
6
|
+
let linkedomImport: Promise<LinkedomModule> | undefined;
|
|
7
|
+
|
|
8
|
+
export async function parseHtmlLazy(markup: string): Promise<ReturnType<ParseHtml>> {
|
|
9
|
+
if (!parseHtml) {
|
|
10
|
+
linkedomImport ??= import("linkedom");
|
|
11
|
+
parseHtml = (await linkedomImport).parseHTML;
|
|
12
|
+
}
|
|
13
|
+
return parseHtml(markup);
|
|
14
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resolve pasted editor text to an image file path.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Some terminal/clipboard integrations paste a temporary filesystem path after a
|
|
5
|
+
* copied image is pasted into the editor (for example `/tmp/clipboard-...png`).
|
|
6
|
+
* Only those recognized clipboard temp files are auto-attached and replaced with
|
|
7
|
+
* an `[image N] source="/path"` reference so the model receives both the image
|
|
8
|
+
* payload and the raw temp file path. Ordinary image paths remain literal prompt
|
|
9
|
+
* text; users attach saved files explicitly with `@path/to/image.png`.
|
|
10
10
|
*/
|
|
11
11
|
import * as fs from "node:fs";
|
|
12
12
|
import * as os from "node:os";
|
|
@@ -14,6 +14,7 @@ import * as path from "node:path";
|
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
|
|
16
16
|
const IMAGE_FILE_EXTENSION_PATTERN = /\.(?:png|jpe?g|gif|webp)$/i;
|
|
17
|
+
const CLIPBOARD_TEMP_BASENAME_PATTERN = /^clipboard-\d{4}-\d{2}-\d{2}-\d{6}-[A-Za-z0-9-]+\.(?:png|jpe?g|gif|webp)$/i;
|
|
17
18
|
|
|
18
19
|
export interface DecodePastedPathOptions {
|
|
19
20
|
/**
|
|
@@ -150,16 +151,25 @@ function hasSupportedImageMagic(filePath: string): boolean {
|
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
function isRecognizedClipboardTempPath(filePath: string): boolean {
|
|
155
|
+
const resolved = path.resolve(filePath);
|
|
156
|
+
const tempRoot = path.resolve(os.tmpdir());
|
|
157
|
+
if (resolved !== tempRoot && !resolved.startsWith(`${tempRoot}${path.sep}`)) return false;
|
|
158
|
+
return CLIPBOARD_TEMP_BASENAME_PATTERN.test(path.basename(resolved));
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
/**
|
|
154
|
-
* Returns the resolved path when the whole pasted text is a
|
|
155
|
-
* existing image file
|
|
156
|
-
*
|
|
162
|
+
* Returns the resolved path when the whole pasted text is a recognized clipboard
|
|
163
|
+
* temp path to an existing image file, otherwise `undefined` (the paste is
|
|
164
|
+
* inserted as text). Arbitrary saved image paths are not auto-attached here;
|
|
165
|
+
* users attach them explicitly with `@path/to/image.png`.
|
|
157
166
|
*/
|
|
158
167
|
export function resolvePastedImagePath(text: string, options?: ResolvePastedImagePathOptions): string | undefined {
|
|
159
168
|
const candidate = decodePastedPathCandidate(text, options);
|
|
160
169
|
if (!candidate || !IMAGE_FILE_EXTENSION_PATTERN.test(candidate)) return undefined;
|
|
161
170
|
|
|
162
171
|
const resolved = path.resolve(options?.cwd ?? process.cwd(), candidate);
|
|
172
|
+
if (!isRecognizedClipboardTempPath(resolved)) return undefined;
|
|
163
173
|
try {
|
|
164
174
|
if (!fs.statSync(resolved).isFile()) return undefined;
|
|
165
175
|
} catch {
|
|
@@ -168,3 +178,7 @@ export function resolvePastedImagePath(text: string, options?: ResolvePastedImag
|
|
|
168
178
|
if (!hasSupportedImageMagic(resolved)) return undefined;
|
|
169
179
|
return resolved;
|
|
170
180
|
}
|
|
181
|
+
|
|
182
|
+
export function formatPastedImageReference(placeholder: string, imagePath: string): string {
|
|
183
|
+
return `${placeholder} source=${JSON.stringify(imagePath)}`;
|
|
184
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
2
2
|
import type { RenderResult, SpecialHandler } from "./types";
|
|
3
3
|
import { buildResult, loadPage } from "./types";
|
|
4
4
|
import { convertWithMarkit, fetchBinary } from "./utils";
|
|
@@ -31,7 +31,7 @@ export const handleArxiv: SpecialHandler = async (
|
|
|
31
31
|
if (!result.ok) return null;
|
|
32
32
|
|
|
33
33
|
// Parse the Atom feed response
|
|
34
|
-
const doc =
|
|
34
|
+
const doc = (await parseHtmlLazy(result.content)).document;
|
|
35
35
|
const entry = doc.querySelector("entry");
|
|
36
36
|
|
|
37
37
|
if (!entry) return null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { tryParseJson } from "@sayknow-cli/utils";
|
|
2
|
-
import {
|
|
2
|
+
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
3
3
|
import type { RenderResult, SpecialHandler } from "./types";
|
|
4
4
|
import { buildResult, htmlToBasicMarkdown, loadPage } from "./types";
|
|
5
5
|
|
|
@@ -97,7 +97,7 @@ export const handleGoPkg: SpecialHandler = async (
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
const doc =
|
|
100
|
+
const doc = (await parseHtmlLazy(pageResult.content)).document;
|
|
101
101
|
|
|
102
102
|
// Extract actual module path from breadcrumb or header
|
|
103
103
|
const breadcrumb = doc.querySelector(".go-Breadcrumb");
|
package/src/web/scrapers/iacr.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
2
2
|
import type { RenderResult, SpecialHandler } from "./types";
|
|
3
3
|
import { buildResult, loadPage } from "./types";
|
|
4
4
|
import { convertWithMarkit, fetchBinary } from "./utils";
|
|
@@ -30,7 +30,7 @@ export const handleIacr: SpecialHandler = async (
|
|
|
30
30
|
|
|
31
31
|
if (!result.ok) return null;
|
|
32
32
|
|
|
33
|
-
const doc =
|
|
33
|
+
const doc = (await parseHtmlLazy(result.content)).document;
|
|
34
34
|
|
|
35
35
|
// Extract metadata from the page
|
|
36
36
|
const title =
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Read the Docs handler for web-fetch
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
5
5
|
import { buildResult, htmlToBasicMarkdown, loadPage, type RenderResult, type SpecialHandler } from "./types";
|
|
6
6
|
|
|
7
7
|
export const handleReadTheDocs: SpecialHandler = async (
|
|
@@ -39,7 +39,7 @@ export const handleReadTheDocs: SpecialHandler = async (
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// Parse HTML
|
|
42
|
-
const root =
|
|
42
|
+
const root = (await parseHtmlLazy(result.content)).document;
|
|
43
43
|
|
|
44
44
|
// Extract main content from common Read the Docs selectors
|
|
45
45
|
let mainContent =
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { HTMLElement } from "linkedom";
|
|
2
2
|
import { ToolAbortError } from "../../tools/tool-errors";
|
|
3
|
+
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
3
4
|
import type { RenderResult, SpecialHandler } from "./types";
|
|
4
5
|
import { buildResult, loadPage } from "./types";
|
|
5
6
|
|
|
@@ -33,7 +34,7 @@ export const handleTwitter: SpecialHandler = async (
|
|
|
33
34
|
|
|
34
35
|
if (result.ok && result.content.length > 500) {
|
|
35
36
|
// Parse the Nitter HTML
|
|
36
|
-
const doc =
|
|
37
|
+
const doc = (await parseHtmlLazy(result.content)).document;
|
|
37
38
|
|
|
38
39
|
// Extract tweet content
|
|
39
40
|
const tweetContent = doc.querySelector(".tweet-content")?.textContent?.trim();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseHtmlLazy } from "../../utils/linkedom";
|
|
2
2
|
import type { RenderResult, SpecialHandler } from "./types";
|
|
3
3
|
import { buildResult, loadPage } from "./types";
|
|
4
4
|
|
|
@@ -45,7 +45,7 @@ export const handleWikipedia: SpecialHandler = async (
|
|
|
45
45
|
const contentResult = await loadPage(contentUrl, { timeout, signal });
|
|
46
46
|
|
|
47
47
|
if (contentResult.ok) {
|
|
48
|
-
const doc =
|
|
48
|
+
const doc = (await parseHtmlLazy(contentResult.content)).document;
|
|
49
49
|
|
|
50
50
|
// Extract main content sections
|
|
51
51
|
const sections = doc.querySelectorAll("section");
|