@duckmind/dm-windows-x64 0.60.4 → 0.60.8
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +70 -123
- package/extensions/dm-9router-ext/src/index.js +410 -5
- package/extensions/dm-caveman/extensions/caveman.js +283 -12
- package/extensions/dm-cliproxy/index.js +182 -2
- package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
- package/extensions/dm-cliproxy/src/apply.js +228 -1
- package/extensions/dm-cliproxy/src/cache.js +42 -1
- package/extensions/dm-cliproxy/src/commands.js +50 -2
- package/extensions/dm-cliproxy/src/compat.js +81 -1
- package/extensions/dm-cliproxy/src/config.js +192 -2
- package/extensions/dm-cliproxy/src/conflicts.js +46 -1
- package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
- package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
- package/extensions/dm-cliproxy/src/log.js +23 -1
- package/extensions/dm-cliproxy/src/status-quota.js +77 -1
- package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
- package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
- package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
- package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
- package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
- package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
- package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
- package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
- package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
- package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
- package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
- package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
- package/extensions/dm-context/src/context.js +144 -1
- package/extensions/dm-context/src/index.js +339 -7
- package/extensions/dm-context/src/utils.js +9 -1
- package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
- package/extensions/dm-cua/index.js +75 -6
- package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
- package/extensions/dm-cua/src/browser-install.mjs +331 -2
- package/extensions/dm-fff/src/index.js +688 -12
- package/extensions/dm-fff/src/query.js +60 -1
- package/extensions/dm-goal/src/goal.js +894 -23
- package/extensions/dm-image2/index.js +103 -9
- package/extensions/dm-image2/src/image-lib.mjs +1275 -8
- package/extensions/dm-subagents/install.mjs +62 -8
- package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
- package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
- package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
- package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
- package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
- package/extensions/dm-subagents/src/agents/agents.js +1139 -11
- package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
- package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
- package/extensions/dm-subagents/src/agents/identity.js +29 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
- package/extensions/dm-subagents/src/agents/skills.js +614 -6
- package/extensions/dm-subagents/src/extension/config.js +35 -2
- package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
- package/extensions/dm-subagents/src/extension/doctor.js +172 -15
- package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
- package/extensions/dm-subagents/src/extension/index.js +521 -359
- package/extensions/dm-subagents/src/extension/rpc.js +266 -7
- package/extensions/dm-subagents/src/extension/schemas.js +275 -1
- package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
- package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
- package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
- package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
- package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
- package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
- package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
- package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
- package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
- package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
- package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
- package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
- package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
- package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
- package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
- package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
- package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
- package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
- package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
- package/extensions/dm-subagents/src/shared/formatters.js +98 -7
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
- package/extensions/dm-subagents/src/shared/model-info.js +62 -1
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
- package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
- package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
- package/extensions/dm-subagents/src/shared/settings.js +198 -11
- package/extensions/dm-subagents/src/shared/status-format.js +53 -1
- package/extensions/dm-subagents/src/shared/types.js +184 -6
- package/extensions/dm-subagents/src/shared/utils.js +462 -2
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
- package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
- package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
- package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
- package/extensions/dm-subagents/src/tui/render.js +1542 -4
- package/extensions/dm-usage/index.js +1294 -9
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
- package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
- package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
- package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
- package/extensions/greedysearch-dm/bin/search.mjs +620 -540
- package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
- package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
- package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
- package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
- package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
- package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
- package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
- package/extensions/greedysearch-dm/index.js +123 -23
- package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
- package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
- package/extensions/greedysearch-dm/src/github.mjs +222 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
- package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
- package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
- package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
- package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
- package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
- package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
- package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
- package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
- package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
- package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
- package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
- package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
- package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
- package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
- package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
- package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
- package/package.json +1 -1
|
@@ -1,8 +1,127 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { randomInt } from "node:crypto";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const CDP = join(__dir, "..", "bin", "cdp.mjs");
|
|
8
|
+
const VALID_CDP_COMMANDS = new Set([
|
|
9
|
+
"list",
|
|
10
|
+
"snap",
|
|
11
|
+
"eval",
|
|
12
|
+
"shot",
|
|
13
|
+
"html",
|
|
14
|
+
"nav",
|
|
15
|
+
"net",
|
|
16
|
+
"click",
|
|
17
|
+
"clickxy",
|
|
18
|
+
"type",
|
|
19
|
+
"loadall",
|
|
20
|
+
"evalraw",
|
|
21
|
+
"browse",
|
|
22
|
+
"stop",
|
|
23
|
+
"--tab"
|
|
24
|
+
]);
|
|
25
|
+
function cdpSafeArgv(args) {
|
|
26
|
+
if (!Array.isArray(args) || args.length === 0) {
|
|
27
|
+
throw new Error("cdp: args must be a non-empty array");
|
|
28
|
+
}
|
|
29
|
+
if (args[0] === "test")
|
|
30
|
+
return args.map((v, i) => validateArg(v, i));
|
|
31
|
+
if (!VALID_CDP_COMMANDS.has(args[0])) {
|
|
32
|
+
throw new Error(`cdp: unknown subcommand '${args[0]}'`);
|
|
33
|
+
}
|
|
34
|
+
return args.map((v, i) => validateArg(v, i));
|
|
35
|
+
}
|
|
36
|
+
function validateArg(value, index) {
|
|
37
|
+
if (typeof value !== "string") {
|
|
38
|
+
throw new Error(`cdp: argv[${index}] must be a string (got ${typeof value})`);
|
|
39
|
+
}
|
|
40
|
+
if (value.includes("\x00")) {
|
|
41
|
+
throw new Error(`cdp: argv[${index}] contains a null byte`);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
export function cdp(args, timeoutMs = 30000) {
|
|
46
|
+
return cdpWithInput(args, null, timeoutMs);
|
|
47
|
+
}
|
|
48
|
+
export function cdpWithInput(args, input = null, timeoutMs = 30000) {
|
|
49
|
+
const safeArgs = cdpSafeArgv(args);
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const proc = spawn(nodeRuntimeCommand(), [CDP, ...safeArgs], {
|
|
52
|
+
stdio: [input == null ? "ignore" : "pipe", "pipe", "pipe"]
|
|
53
|
+
});
|
|
54
|
+
if (input != null) {
|
|
55
|
+
proc.stdin.write(input);
|
|
56
|
+
proc.stdin.end();
|
|
57
|
+
}
|
|
58
|
+
let out = "";
|
|
59
|
+
let err = "";
|
|
60
|
+
proc.stdout.on("data", (d) => out += d);
|
|
61
|
+
proc.stderr.on("data", (d) => err += d);
|
|
62
|
+
const timer = setTimeout(() => {
|
|
63
|
+
proc.kill();
|
|
64
|
+
reject(new Error(`cdp timeout: ${args[0]}`));
|
|
65
|
+
}, timeoutMs);
|
|
66
|
+
proc.on("close", (code) => {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
if (code === 0)
|
|
69
|
+
resolve(out.trim());
|
|
70
|
+
else
|
|
71
|
+
reject(new Error(err.trim() || `cdp exit ${code}`));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export function verifyInputText(actual, expected) {
|
|
76
|
+
const normalize = (value) => String(value ?? "").replace(/\s+/g, " ").trim();
|
|
77
|
+
const actualNormalized = normalize(actual);
|
|
78
|
+
const expectedNormalized = normalize(expected);
|
|
79
|
+
if (!expectedNormalized)
|
|
80
|
+
return false;
|
|
81
|
+
if (actualNormalized.length < Math.ceil(expectedNormalized.length * 0.8)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const sampleLength = Math.min(40, expectedNormalized.length);
|
|
85
|
+
return actualNormalized.includes(expectedNormalized.slice(0, sampleLength)) && actualNormalized.includes(expectedNormalized.slice(-sampleLength));
|
|
86
|
+
}
|
|
87
|
+
export async function getOrOpenTab(tabPrefix) {
|
|
88
|
+
if (tabPrefix)
|
|
89
|
+
return tabPrefix;
|
|
90
|
+
const list = await cdp(["list"]);
|
|
91
|
+
const anchor = list.split(`
|
|
92
|
+
`)[0]?.slice(0, 8);
|
|
93
|
+
if (!anchor)
|
|
94
|
+
throw new Error("No Chrome tabs found. Is Chrome running with --remote-debugging-port=9222?");
|
|
95
|
+
const raw = await cdp([
|
|
96
|
+
"evalraw",
|
|
97
|
+
anchor,
|
|
98
|
+
"Target.createTarget",
|
|
99
|
+
'{"url":"about:blank"}'
|
|
100
|
+
]);
|
|
101
|
+
let createdTarget;
|
|
102
|
+
try {
|
|
103
|
+
createdTarget = JSON.parse(raw);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw new Error(`Target.createTarget returned invalid JSON: ${error.message}`);
|
|
106
|
+
}
|
|
107
|
+
const { targetId } = createdTarget;
|
|
108
|
+
if (!targetId) {
|
|
109
|
+
throw new Error("Target.createTarget did not return a targetId");
|
|
110
|
+
}
|
|
111
|
+
await cdp(["list"]);
|
|
112
|
+
const tid = targetId.slice(0, 8);
|
|
113
|
+
try {
|
|
114
|
+
await injectHeadlessStealth(tid);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
process.stderr.write(`[getOrOpenTab] stealth injection failed: ${e.message}
|
|
117
|
+
`);
|
|
118
|
+
}
|
|
119
|
+
return tid;
|
|
120
|
+
}
|
|
121
|
+
export async function injectClipboardInterceptor(tab, globalVar) {
|
|
122
|
+
const code = `
|
|
4
123
|
(() => {
|
|
5
|
-
window.${
|
|
124
|
+
window.${globalVar} = null;
|
|
6
125
|
const _clipboard = navigator.clipboard;
|
|
7
126
|
if (!_clipboard) return;
|
|
8
127
|
const _origWriteText = typeof _clipboard.writeText === 'function'
|
|
@@ -13,7 +132,7 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
13
132
|
: null;
|
|
14
133
|
|
|
15
134
|
_clipboard.writeText = function(text) {
|
|
16
|
-
window.${
|
|
135
|
+
window.${globalVar} = String(text ?? '');
|
|
17
136
|
if (!_origWriteText) return Promise.resolve();
|
|
18
137
|
// The OS/browser clipboard write may be denied in automated Chrome or
|
|
19
138
|
// when the tab is not focused. We only need the captured text; returning
|
|
@@ -27,7 +146,7 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
27
146
|
for (const item of items || []) {
|
|
28
147
|
if (item.types && item.types.includes('text/plain')) {
|
|
29
148
|
const blob = await item.getType('text/plain');
|
|
30
|
-
window.${
|
|
149
|
+
window.${globalVar} = await blob.text();
|
|
31
150
|
break;
|
|
32
151
|
}
|
|
33
152
|
}
|
|
@@ -37,26 +156,36 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
37
156
|
catch (_) { return undefined; }
|
|
38
157
|
};
|
|
39
158
|
})();
|
|
40
|
-
`;
|
|
159
|
+
`;
|
|
160
|
+
await cdp(["eval", tab, code]);
|
|
161
|
+
}
|
|
162
|
+
export async function clickCopyAndAwaitClipboard(tab, clickExpr, globalVar, options = {}) {
|
|
163
|
+
const { timeoutMs = 2600 } = options;
|
|
164
|
+
const retryClick = options.retryClick != null ? options.retryClick : Math.floor(timeoutMs * 0.4);
|
|
165
|
+
const code = String.raw`
|
|
41
166
|
new Promise((resolve) => {
|
|
42
|
-
const _deadline = Date.now() + ${
|
|
43
|
-
const _retryAt = Date.now() + ${
|
|
167
|
+
const _deadline = Date.now() + ${timeoutMs};
|
|
168
|
+
const _retryAt = Date.now() + ${retryClick};
|
|
44
169
|
let _retried = false;
|
|
45
|
-
function _click() { try { ${
|
|
170
|
+
function _click() { try { ${clickExpr}; } catch(_) {} }
|
|
46
171
|
function _poll() {
|
|
47
|
-
const val = window.${
|
|
172
|
+
const val = window.${globalVar};
|
|
48
173
|
if (val) { resolve(val); return; }
|
|
49
174
|
if (!_retried && Date.now() >= _retryAt) {
|
|
50
175
|
_retried = true;
|
|
51
176
|
_click();
|
|
52
177
|
}
|
|
53
178
|
if (Date.now() < _deadline) { setTimeout(_poll, 100); }
|
|
54
|
-
else { resolve(window.${
|
|
179
|
+
else { resolve(window.${globalVar} || ''); }
|
|
55
180
|
}
|
|
56
181
|
_click();
|
|
57
182
|
_poll();
|
|
58
183
|
})
|
|
59
|
-
`;
|
|
184
|
+
`;
|
|
185
|
+
return await cdp(["eval", tab, code], timeoutMs + 5000).catch(() => "");
|
|
186
|
+
}
|
|
187
|
+
export async function injectHeadlessStealth(tab) {
|
|
188
|
+
const code = `
|
|
60
189
|
(function() {
|
|
61
190
|
// ── Runtime.enable / CDP detection masking ──────────────
|
|
62
191
|
try { delete window.__REBROWSER_RUNTIME_ENABLE; } catch(_) {}
|
|
@@ -341,22 +470,168 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
341
470
|
};
|
|
342
471
|
} catch(_) {}
|
|
343
472
|
})();
|
|
344
|
-
|
|
473
|
+
`;
|
|
474
|
+
await cdp([
|
|
475
|
+
"evalraw",
|
|
476
|
+
tab,
|
|
477
|
+
"Page.addScriptToEvaluateOnNewDocument",
|
|
478
|
+
JSON.stringify({ source: code })
|
|
479
|
+
]);
|
|
480
|
+
}
|
|
481
|
+
export function parseSourcesFromMarkdown(text) {
|
|
482
|
+
if (!text)
|
|
483
|
+
return [];
|
|
484
|
+
const results = [];
|
|
485
|
+
let idx = 0;
|
|
486
|
+
while (idx < text.length && results.length < 10) {
|
|
487
|
+
const openBracket = text.indexOf("[", idx);
|
|
488
|
+
if (openBracket === -1)
|
|
489
|
+
break;
|
|
490
|
+
const closeBracket = text.indexOf("](", openBracket);
|
|
491
|
+
if (closeBracket === -1)
|
|
492
|
+
break;
|
|
493
|
+
const openParen = closeBracket + 2;
|
|
494
|
+
let closeParen = -1;
|
|
495
|
+
for (let p = openParen;p < text.length; p++) {
|
|
496
|
+
const ch = text[p];
|
|
497
|
+
if (ch === ")") {
|
|
498
|
+
closeParen = p;
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
if (/\s/.test(ch))
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
if (closeParen !== -1) {
|
|
505
|
+
const title = text.slice(openBracket + 1, closeBracket);
|
|
506
|
+
const url = text.slice(openParen, closeParen);
|
|
507
|
+
if (/^https?:\/\//i.test(url) && title) {
|
|
508
|
+
if (!results.some((r) => r.url === url)) {
|
|
509
|
+
results.push({ title, url });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
idx = closeParen + 1;
|
|
513
|
+
} else {
|
|
514
|
+
idx = openBracket + 1;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return results;
|
|
518
|
+
}
|
|
519
|
+
function isAllDigits(s) {
|
|
520
|
+
if (!s)
|
|
521
|
+
return false;
|
|
522
|
+
for (let k = 0;k < s.length; k++) {
|
|
523
|
+
const c = s.charCodeAt(k);
|
|
524
|
+
if (c < 48 || c > 57)
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
export function parseSourcesFromMarkdownRefStyle(text) {
|
|
530
|
+
if (!text)
|
|
531
|
+
return [];
|
|
532
|
+
const results = [];
|
|
533
|
+
const refMap = new Map;
|
|
534
|
+
const refRegex = /^\[(\d+)\]:\s*(https?:\/\/[^\s"]+)(?:\s+"([^"]*)")?/gm;
|
|
535
|
+
let m;
|
|
536
|
+
while ((m = refRegex.exec(text)) !== null) {
|
|
537
|
+
const num = m[1];
|
|
538
|
+
const url = m[2];
|
|
539
|
+
const title = m[3] || "";
|
|
540
|
+
refMap.set(num, { url, title });
|
|
541
|
+
}
|
|
542
|
+
let cursor = 0;
|
|
543
|
+
while (cursor < text.length) {
|
|
544
|
+
const open = text.indexOf("[", cursor);
|
|
545
|
+
if (open === -1)
|
|
546
|
+
break;
|
|
547
|
+
const close = text.indexOf("]", open + 1);
|
|
548
|
+
if (close === -1)
|
|
549
|
+
break;
|
|
550
|
+
if (text[close + 1] !== "[") {
|
|
551
|
+
cursor = open + 1;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
const close2 = text.indexOf("]", close + 2);
|
|
555
|
+
if (close2 === -1)
|
|
556
|
+
break;
|
|
557
|
+
const inner = text.slice(open + 1, close);
|
|
558
|
+
const numStr = text.slice(close + 2, close2);
|
|
559
|
+
if (isAllDigits(numStr)) {
|
|
560
|
+
const ref = refMap.get(numStr);
|
|
561
|
+
if (ref && !results.some((r) => r.url === ref.url)) {
|
|
562
|
+
results.push({
|
|
563
|
+
title: inner.trim() || ref.title || "",
|
|
564
|
+
url: ref.url
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
cursor = close2 + 1;
|
|
569
|
+
}
|
|
570
|
+
return results;
|
|
571
|
+
}
|
|
572
|
+
export const TIMING = {
|
|
573
|
+
postNav: 800,
|
|
574
|
+
postNavSlow: 1200,
|
|
575
|
+
postClick: 300,
|
|
576
|
+
postType: 300,
|
|
577
|
+
inputPoll: 400,
|
|
578
|
+
copyPoll: 600,
|
|
579
|
+
afterVerify: 1500
|
|
580
|
+
};
|
|
581
|
+
export async function waitForCopyButton(tab, selector, options = {}) {
|
|
582
|
+
const { timeout = 60000, onPoll } = options;
|
|
583
|
+
if (!onPoll) {
|
|
584
|
+
const code = String.raw`
|
|
345
585
|
new Promise((resolve) => {
|
|
346
|
-
const _deadline = Date.now() + ${
|
|
586
|
+
const _deadline = Date.now() + ${timeout};
|
|
347
587
|
function _poll() {
|
|
348
|
-
if (document.querySelector('${
|
|
588
|
+
if (document.querySelector('${selector}')) { resolve(true); return; }
|
|
349
589
|
if (Date.now() < _deadline) { setTimeout(_poll, 150); }
|
|
350
590
|
else { resolve(false); }
|
|
351
591
|
}
|
|
352
592
|
_poll();
|
|
353
593
|
})
|
|
354
|
-
`;
|
|
594
|
+
`;
|
|
595
|
+
const found = await cdp(["eval", tab, code], timeout + 5000).catch(() => "false");
|
|
596
|
+
if (found === "true")
|
|
597
|
+
return;
|
|
598
|
+
throw new Error(`Copy button ('${selector}') did not appear within ${timeout}ms`);
|
|
599
|
+
}
|
|
600
|
+
const deadline = Date.now() + timeout;
|
|
601
|
+
let tick = 0;
|
|
602
|
+
while (Date.now() < deadline) {
|
|
603
|
+
const found = await cdp([
|
|
604
|
+
"eval",
|
|
605
|
+
tab,
|
|
606
|
+
`!!document.querySelector('${selector}')`
|
|
607
|
+
]).catch(() => "false");
|
|
608
|
+
if (found === "true")
|
|
609
|
+
return;
|
|
610
|
+
await onPoll(++tick).catch(() => null);
|
|
611
|
+
await new Promise((r) => setTimeout(r, jitter(TIMING.copyPoll)));
|
|
612
|
+
}
|
|
613
|
+
throw new Error(`Copy button ('${selector}') did not appear within ${timeout}ms`);
|
|
614
|
+
}
|
|
615
|
+
export function jitter(ms) {
|
|
616
|
+
const variance = ms * 0.2;
|
|
617
|
+
const offset = randomInt(-Math.floor(variance), Math.floor(variance) + 1);
|
|
618
|
+
return Math.max(50, Math.round(ms + offset));
|
|
619
|
+
}
|
|
620
|
+
export async function waitForStreamComplete(tab, options = {}) {
|
|
621
|
+
const {
|
|
622
|
+
timeout = 20000,
|
|
623
|
+
interval = 600,
|
|
624
|
+
stableRounds = 3,
|
|
625
|
+
selector = "document.body",
|
|
626
|
+
minLength = 0,
|
|
627
|
+
isStreamingExpr = "false"
|
|
628
|
+
} = options;
|
|
629
|
+
const code = String.raw`
|
|
355
630
|
new Promise((resolve, reject) => {
|
|
356
|
-
const _deadline = Date.now() + ${
|
|
357
|
-
const _baseInterval = ${
|
|
358
|
-
const _stableRounds = ${
|
|
359
|
-
const _minLength = ${
|
|
631
|
+
const _deadline = Date.now() + ${timeout};
|
|
632
|
+
const _baseInterval = ${interval};
|
|
633
|
+
const _stableRounds = ${stableRounds};
|
|
634
|
+
const _minLength = ${minLength};
|
|
360
635
|
let _lastLen = -1;
|
|
361
636
|
let _stableCount = 0;
|
|
362
637
|
|
|
@@ -367,9 +642,9 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
367
642
|
function _poll() {
|
|
368
643
|
try {
|
|
369
644
|
// Re-query DOM each tick — element may not exist at eval start
|
|
370
|
-
const el = ${
|
|
645
|
+
const el = ${selector};
|
|
371
646
|
const cur = el?.textContent?.length ?? 0;
|
|
372
|
-
const streaming = ${
|
|
647
|
+
const streaming = ${isStreamingExpr};
|
|
373
648
|
if (streaming) {
|
|
374
649
|
if (cur !== _lastLen) _lastLen = cur;
|
|
375
650
|
_stableCount = 0;
|
|
@@ -386,17 +661,25 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
386
661
|
setTimeout(_poll, _jitter(_baseInterval));
|
|
387
662
|
} else {
|
|
388
663
|
if (_lastLen >= _minLength && !streaming) { resolve(_lastLen); }
|
|
389
|
-
else { reject(new Error('Generation did not stabilise within ${
|
|
664
|
+
else { reject(new Error('Generation did not stabilise within ${timeout}ms')); }
|
|
390
665
|
}
|
|
391
666
|
} catch(e) { reject(e); }
|
|
392
667
|
}
|
|
393
668
|
|
|
394
669
|
_poll();
|
|
395
670
|
})
|
|
396
|
-
|
|
671
|
+
`;
|
|
672
|
+
const lenStr = await cdp(["eval", tab, code], timeout + 1e4);
|
|
673
|
+
const currentLen = parseInt(lenStr, 10) || 0;
|
|
674
|
+
if (currentLen >= minLength)
|
|
675
|
+
return currentLen;
|
|
676
|
+
throw new Error(`Generation did not stabilise within ${timeout}ms`);
|
|
677
|
+
}
|
|
678
|
+
export async function waitForSelector(tab, selector, timeoutMs = 15000, interval = 500) {
|
|
679
|
+
const code = String.raw`
|
|
397
680
|
new Promise((resolve) => {
|
|
398
|
-
const _deadline = Date.now() + ${
|
|
399
|
-
const _baseInterval = ${
|
|
681
|
+
const _deadline = Date.now() + ${timeoutMs};
|
|
682
|
+
const _baseInterval = ${interval};
|
|
400
683
|
|
|
401
684
|
function _jitter(ms) {
|
|
402
685
|
return Math.max(50, ms + (Math.random() * ms * 0.4 - ms * 0.2));
|
|
@@ -404,7 +687,7 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
404
687
|
|
|
405
688
|
function _poll() {
|
|
406
689
|
try {
|
|
407
|
-
if (document.querySelector('${
|
|
690
|
+
if (document.querySelector('${selector}')) { resolve(true); return; }
|
|
408
691
|
if (Date.now() < _deadline) { setTimeout(_poll, _jitter(_baseInterval)); }
|
|
409
692
|
else { resolve(false); }
|
|
410
693
|
} catch(_) { resolve(false); }
|
|
@@ -412,7 +695,100 @@ import{randomInt as E}from"node:crypto";import{spawn as A}from"node:child_proces
|
|
|
412
695
|
|
|
413
696
|
_poll();
|
|
414
697
|
})
|
|
415
|
-
`;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
698
|
+
`;
|
|
699
|
+
const result = await cdp(["eval", tab, code], timeoutMs + 5000);
|
|
700
|
+
return result === "true";
|
|
701
|
+
}
|
|
702
|
+
export async function prepareArgs(args) {
|
|
703
|
+
const stdinIdx = args.indexOf("--stdin");
|
|
704
|
+
if (stdinIdx === -1)
|
|
705
|
+
return args;
|
|
706
|
+
const query = await new Promise((resolve) => {
|
|
707
|
+
let data = "";
|
|
708
|
+
process.stdin.setEncoding("utf8");
|
|
709
|
+
process.stdin.on("data", (chunk) => data += chunk);
|
|
710
|
+
process.stdin.on("end", () => resolve(data.trim()));
|
|
711
|
+
});
|
|
712
|
+
const modified = [...args];
|
|
713
|
+
modified[stdinIdx] = query;
|
|
714
|
+
return modified;
|
|
715
|
+
}
|
|
716
|
+
export function parseArgs(args) {
|
|
717
|
+
const short = args.includes("--short");
|
|
718
|
+
let rest = args.filter((a) => a !== "--short");
|
|
719
|
+
const tabFlagIdx = rest.indexOf("--tab");
|
|
720
|
+
const tabPrefix = tabFlagIdx === -1 ? null : rest[tabFlagIdx + 1];
|
|
721
|
+
if (tabFlagIdx !== -1) {
|
|
722
|
+
rest = rest.filter((_, i) => i !== tabFlagIdx && i !== tabFlagIdx + 1);
|
|
723
|
+
}
|
|
724
|
+
const localeIdx = rest.indexOf("--locale");
|
|
725
|
+
const locale = localeIdx === -1 ? null : rest[localeIdx + 1];
|
|
726
|
+
if (localeIdx !== -1) {
|
|
727
|
+
rest = rest.filter((_, i) => i !== localeIdx && i !== localeIdx + 1);
|
|
728
|
+
}
|
|
729
|
+
const query = rest.join(" ");
|
|
730
|
+
return { query, tabPrefix, short, locale };
|
|
731
|
+
}
|
|
732
|
+
export function validateQuery(args, usage) {
|
|
733
|
+
if (!args.length || args[0] === "--help") {
|
|
734
|
+
process.stderr.write(usage);
|
|
735
|
+
process.exit(1);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
export function formatAnswer(answer, short, maxLen = 300) {
|
|
739
|
+
if (!short || answer.length <= maxLen)
|
|
740
|
+
return answer;
|
|
741
|
+
const truncated = answer.slice(0, maxLen);
|
|
742
|
+
const lastSpace = truncated.lastIndexOf(" ");
|
|
743
|
+
return lastSpace > 0 ? `${truncated.slice(0, lastSpace)}…` : `${truncated}…`;
|
|
744
|
+
}
|
|
745
|
+
export function outputJson(data) {
|
|
746
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
747
|
+
`);
|
|
748
|
+
}
|
|
749
|
+
export function logStage(env, stage, startTime = null) {
|
|
750
|
+
if (!env || typeof env !== "object")
|
|
751
|
+
return;
|
|
752
|
+
const elapsed = startTime ? ` (+${Date.now() - startTime}ms)` : "";
|
|
753
|
+
env.lastStage = stage;
|
|
754
|
+
if (!Array.isArray(env.stages))
|
|
755
|
+
env.stages = [];
|
|
756
|
+
env.stages.push({ stage, at: Date.now() });
|
|
757
|
+
const engine = env.engine || "extractor";
|
|
758
|
+
console.error(`[${engine}] stage: ${stage}${elapsed}`);
|
|
759
|
+
}
|
|
760
|
+
export function buildEnvelope({
|
|
761
|
+
engine,
|
|
762
|
+
mode = "headless",
|
|
763
|
+
clipboardEmpty = null,
|
|
764
|
+
fallbackUsed = null,
|
|
765
|
+
blockedBy = null,
|
|
766
|
+
verificationResult = null,
|
|
767
|
+
inputReady = null,
|
|
768
|
+
durationMs = null,
|
|
769
|
+
lastStage = null,
|
|
770
|
+
stages = null
|
|
771
|
+
} = {}) {
|
|
772
|
+
return {
|
|
773
|
+
engine,
|
|
774
|
+
mode,
|
|
775
|
+
clipboardEmpty,
|
|
776
|
+
fallbackUsed,
|
|
777
|
+
blockedBy,
|
|
778
|
+
verificationResult,
|
|
779
|
+
inputReady,
|
|
780
|
+
durationMs,
|
|
781
|
+
lastStage,
|
|
782
|
+
stages
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
export function handleError(error, envelope = null) {
|
|
786
|
+
if (envelope) {
|
|
787
|
+
const out = JSON.stringify({ _envelope: envelope, error: error.message });
|
|
788
|
+
process.stdout.write(`${out}
|
|
789
|
+
`);
|
|
790
|
+
}
|
|
791
|
+
process.stderr.write(`Error: ${error.message}
|
|
792
|
+
`);
|
|
793
|
+
process.exit(1);
|
|
794
|
+
}
|