@duckmind/dm-windows-x64 0.60.6 → 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,309 +1,510 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
import { spawn, execFileSync, execSync } from "node:child_process";
|
|
2
|
+
import { nodeRuntimeCommand } from "../utils/node-runtime.mjs";
|
|
3
|
+
import {
|
|
4
|
+
existsSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
renameSync,
|
|
7
|
+
unlinkSync,
|
|
8
|
+
writeFileSync
|
|
9
|
+
} from "node:fs";
|
|
10
|
+
import http from "node:http";
|
|
11
|
+
import { platform } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
cdp as _cdp,
|
|
15
|
+
injectHeadlessStealth
|
|
16
|
+
} from "../../extractors/common.mjs";
|
|
17
|
+
import { resolveSystemCmd } from "../utils/system-cmds.mjs";
|
|
18
|
+
import { findListeningProcessPid } from "./port-pid.mjs";
|
|
19
|
+
import {
|
|
20
|
+
moduleDirectory,
|
|
21
|
+
resolveGreedySearchLaunchScript
|
|
22
|
+
} from "./paths.mjs";
|
|
23
|
+
import {
|
|
24
|
+
ACTIVE_PORT_FILE,
|
|
25
|
+
CHROME_MODE_FILE,
|
|
26
|
+
GREEDY_ACTIVITY_FILE,
|
|
27
|
+
GREEDY_PID_FILE,
|
|
28
|
+
GREEDY_PORT,
|
|
29
|
+
GREEDY_PROFILE_DIR,
|
|
30
|
+
PAGES_CACHE
|
|
31
|
+
} from "./constants.mjs";
|
|
32
|
+
import {
|
|
33
|
+
readMetadata,
|
|
34
|
+
touchActivity as touchActivityBL,
|
|
35
|
+
acquireLaunchLock,
|
|
36
|
+
cleanupStaleSessions,
|
|
37
|
+
registerClient
|
|
38
|
+
} from "./browser-lifecycle.mjs";
|
|
39
|
+
const __dir = moduleDirectory(import.meta.url);
|
|
40
|
+
const PID_FILE = GREEDY_PID_FILE;
|
|
41
|
+
const ACTIVITY_FILE = GREEDY_ACTIVITY_FILE;
|
|
42
|
+
const HEADLESS_IDLE_TIMEOUT_MINUTES = Number.parseInt(process.env.GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES || "5", 10) || 5;
|
|
43
|
+
const VISIBLE_IDLE_TIMEOUT_MINUTES = Number.parseInt(process.env.GREEDY_SEARCH_VISIBLE_IDLE_TIMEOUT_MINUTES || "60", 10) || 60;
|
|
44
|
+
export function detectHeadlessFromChromeCommandLine(cmdLine, debugPort = GREEDY_PORT) {
|
|
45
|
+
const normalized = String(cmdLine || "").toLowerCase();
|
|
46
|
+
if (!normalized.includes(`--remote-debugging-port=${debugPort}`) || normalized.includes("--type=")) {
|
|
47
|
+
return null;
|
|
35
48
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
try { Object.setPrototypeOf(plugin2, Plugin.prototype); } catch(_) {}
|
|
45
|
-
var p = [plugin0, plugin1, plugin2];
|
|
46
|
-
p.item = function item(i) { return this[i] || null; };
|
|
47
|
-
p.namedItem = function namedItem(name) { return Array.prototype.find.call(this, function(x) { return x && x.name === name; }) || null; };
|
|
48
|
-
p.refresh = function refresh() {};
|
|
49
|
-
try { Object.setPrototypeOf(p, PluginArray.prototype); } catch(_) {}
|
|
49
|
+
return normalized.includes("--headless");
|
|
50
|
+
}
|
|
51
|
+
export function isChromeHeadless() {
|
|
52
|
+
try {
|
|
53
|
+
const portPid = getPortPid();
|
|
54
|
+
const cmdLine = portPid ? getProcessCommandLine(portPid) : null;
|
|
55
|
+
const headless = detectHeadlessFromChromeCommandLine(cmdLine);
|
|
56
|
+
if (headless !== null) {
|
|
50
57
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
configurable: true,
|
|
57
|
-
});
|
|
58
|
-
Object.defineProperty(navigator, 'mimeTypes', {
|
|
59
|
-
get: () => {
|
|
60
|
-
__greedyMimeTypes = __greedyMimeTypes || __makeMimeTypes();
|
|
61
|
-
return __greedyMimeTypes;
|
|
62
|
-
},
|
|
63
|
-
configurable: true,
|
|
64
|
-
});
|
|
65
|
-
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'], configurable: true });
|
|
58
|
+
writeFileSync(CHROME_MODE_FILE, headless ? "headless" : "visible", "utf8");
|
|
59
|
+
} catch {}
|
|
60
|
+
return headless;
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
66
63
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
enumerateDevices: () => Promise.resolve([
|
|
73
|
-
{ deviceId: 'default', kind: 'audioinput', label: '', groupId: 'default' },
|
|
74
|
-
{ deviceId: 'default', kind: 'audiooutput', label: '', groupId: 'default' },
|
|
75
|
-
{ deviceId: '', kind: 'videoinput', label: '', groupId: '' },
|
|
76
|
-
]),
|
|
77
|
-
getUserMedia: () => Promise.reject(new DOMException('NotAllowedError')),
|
|
78
|
-
getDisplayMedia: () => Promise.reject(new DOMException('NotAllowedError')),
|
|
79
|
-
}),
|
|
80
|
-
configurable: true,
|
|
81
|
-
});
|
|
64
|
+
if (!existsSync(CHROME_MODE_FILE))
|
|
65
|
+
return true;
|
|
66
|
+
return readFileSync(CHROME_MODE_FILE, "utf8").trim() === "headless";
|
|
67
|
+
} catch {
|
|
68
|
+
return true;
|
|
82
69
|
}
|
|
83
|
-
|
|
70
|
+
}
|
|
71
|
+
export function touchActivity() {
|
|
84
72
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
73
|
+
writeFileSync(ACTIVITY_FILE, String(Date.now()), "utf8");
|
|
74
|
+
} catch {}
|
|
75
|
+
try {
|
|
76
|
+
const md = readMetadata();
|
|
77
|
+
if (md)
|
|
78
|
+
touchActivityBL(md);
|
|
79
|
+
} catch {}
|
|
80
|
+
}
|
|
81
|
+
function getProcessCommandLine(pid) {
|
|
89
82
|
try {
|
|
90
|
-
if (
|
|
91
|
-
|
|
83
|
+
if (platform() === "win32") {
|
|
84
|
+
const output = execFileSync(resolveSystemCmd("powershell"), [
|
|
85
|
+
"-NoProfile",
|
|
86
|
+
"-NonInteractive",
|
|
87
|
+
"-Command",
|
|
88
|
+
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`
|
|
89
|
+
], { encoding: "utf8", windowsHide: true, timeout: 5000 });
|
|
90
|
+
return output.trim() || null;
|
|
92
91
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
app: { isInstalled: false, InstallState: {}, RunningState: {} },
|
|
98
|
-
runtime: {
|
|
99
|
-
OnInstalledReason: {}, OnRestartRequiredReason: {}, PlatformArch: {}, PlatformNaclArch: {}, PlatformOs: {}, RequestUpdateCheckStatus: {},
|
|
100
|
-
connect: () => ({}), sendMessage: () => {}, onMessage: { addListener: () => {} }
|
|
101
|
-
},
|
|
102
|
-
loadTimes: function() { return { requestTime: 0, startLoadTime: Date.now() - 5000, commitLoadTime: Date.now() - 3000, finishDocumentLoadTime: Date.now() - 2000, finishLoadTime: Date.now() - 1000, firstPaintTime: Date.now() - 800, navigationType: 'Other', wasFetchedViaSpdy: true, wasNpnNegotiated: true, npnNegotiatedProtocol: 'h2', wasAlternateProtocolAvailable: false, connectionInfo: 'http/2' }; },
|
|
103
|
-
csi: function() { var t = Date.now(); return { onloadT: t - 2000, startE: t - 5000, pageT: 'back', tran: 2 }; },
|
|
104
|
-
};
|
|
92
|
+
const output = execFileSync(resolveSystemCmd("ps"), ["-p", String(pid), "-o", "command="], { encoding: "utf8", timeout: 5000 });
|
|
93
|
+
return output.trim() || null;
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
105
96
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
97
|
+
}
|
|
98
|
+
function commandLineMatchesGreedyBrowser(commandLine) {
|
|
99
|
+
const normalized = String(commandLine || "").replaceAll("\\", "/");
|
|
100
|
+
const profile = GREEDY_PROFILE_DIR.replaceAll("\\", "/");
|
|
101
|
+
return normalized.includes(`--remote-debugging-port=${GREEDY_PORT}`) && normalized.includes(`--user-data-dir=${profile}`) && !normalized.includes("--type=");
|
|
102
|
+
}
|
|
103
|
+
function getPortPid() {
|
|
104
|
+
return findListeningProcessPid(GREEDY_PORT);
|
|
105
|
+
}
|
|
106
|
+
function getOwnedBrowserPid() {
|
|
107
|
+
const pid = getPortPid();
|
|
108
|
+
return pid && commandLineMatchesGreedyBrowser(getProcessCommandLine(pid)) ? pid : null;
|
|
109
|
+
}
|
|
110
|
+
async function gracefulCloseChrome(graceMs = 1500) {
|
|
111
|
+
const ownedPid = getOwnedBrowserPid();
|
|
112
|
+
if (!ownedPid) {
|
|
113
|
+
process.stderr.write(`[greedysearch] Refusing to close an unverified listener.
|
|
114
|
+
`);
|
|
115
|
+
return false;
|
|
115
116
|
}
|
|
116
117
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
const version = await new Promise((resolve, reject) => {
|
|
119
|
+
const req = http.get(`http://localhost:${GREEDY_PORT}/json/version`, (res) => {
|
|
120
|
+
let body = "";
|
|
121
|
+
res.on("data", (d) => body += d);
|
|
122
|
+
res.on("end", () => {
|
|
123
|
+
try {
|
|
124
|
+
resolve(JSON.parse(body));
|
|
125
|
+
} catch {
|
|
126
|
+
reject(new Error("bad JSON"));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
req.on("error", reject);
|
|
131
|
+
req.setTimeout(1000, () => {
|
|
132
|
+
req.destroy();
|
|
133
|
+
reject(new Error("timeout"));
|
|
134
|
+
});
|
|
122
135
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
const ws = new globalThis.WebSocket(version.webSocketDebuggerUrl);
|
|
137
|
+
await new Promise((resolve) => {
|
|
138
|
+
ws.onopen = () => {
|
|
139
|
+
if (getOwnedBrowserPid() !== ownedPid) {
|
|
140
|
+
ws.close();
|
|
141
|
+
resolve();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
ws.send(JSON.stringify({ id: 1, method: "Browser.close" }));
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
ws.close();
|
|
147
|
+
resolve();
|
|
148
|
+
}, 200);
|
|
149
|
+
};
|
|
150
|
+
ws.onerror = () => resolve();
|
|
151
|
+
setTimeout(resolve, 1000);
|
|
135
152
|
});
|
|
136
|
-
} catch
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
153
|
+
} catch {}
|
|
154
|
+
const deadline = Date.now() + graceMs;
|
|
155
|
+
while (Date.now() < deadline) {
|
|
156
|
+
const pid = getPortPid();
|
|
157
|
+
if (!pid)
|
|
158
|
+
return true;
|
|
159
|
+
if (pid !== ownedPid)
|
|
160
|
+
return false;
|
|
161
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
162
|
+
}
|
|
163
|
+
return killProcessOnPort(ownedPid);
|
|
164
|
+
}
|
|
165
|
+
function killProcessOnPort(expectedPid = null) {
|
|
143
166
|
try {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
const pid = getOwnedBrowserPid();
|
|
168
|
+
if (!pid || expectedPid && pid !== expectedPid)
|
|
169
|
+
return false;
|
|
170
|
+
if (platform() === "win32") {
|
|
171
|
+
execFileSync(resolveSystemCmd("taskkill"), ["/F", "/PID", String(pid)], {
|
|
172
|
+
stdio: "ignore",
|
|
173
|
+
windowsHide: true
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
process.kill(pid, "SIGKILL");
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export async function killChrome() {
|
|
184
|
+
if (!getOwnedBrowserPid()) {
|
|
185
|
+
if (getPortPid()) {
|
|
186
|
+
process.stderr.write(`[greedysearch] Refusing to kill an unverified listener.
|
|
187
|
+
`);
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
const ready = await probeGreedyChrome(500);
|
|
192
|
+
if (!ready) {
|
|
193
|
+
try {
|
|
194
|
+
unlinkSync(PID_FILE);
|
|
195
|
+
} catch {}
|
|
196
|
+
try {
|
|
197
|
+
unlinkSync(ACTIVITY_FILE);
|
|
198
|
+
} catch {}
|
|
199
|
+
try {
|
|
200
|
+
unlinkSync(CHROME_MODE_FILE);
|
|
201
|
+
} catch {}
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
const killed = await gracefulCloseChrome(1500);
|
|
151
205
|
try {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
this.globalAlpha = 0.9995;
|
|
155
|
-
return origStroke.apply(this, arguments);
|
|
156
|
-
});
|
|
157
|
-
} catch(_) {}
|
|
206
|
+
unlinkSync(PID_FILE);
|
|
207
|
+
} catch {}
|
|
158
208
|
try {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
var ctx = this.getContext('2d');
|
|
162
|
-
if (ctx) {
|
|
163
|
-
// Spread noise across canvas to break hash-based fingerprinting.
|
|
164
|
-
// Uses a deterministic pattern so it's consistent per page load
|
|
165
|
-
// but varies between sessions.
|
|
166
|
-
var w = this.width, h = this.height;
|
|
167
|
-
if (w > 0 && h > 0) {
|
|
168
|
-
var imgData = ctx.getImageData(0, 0, Math.min(w, 4), Math.min(h, 4));
|
|
169
|
-
if (imgData && imgData.data) {
|
|
170
|
-
for (var __i = 0; __i < imgData.data.length; __i += 4) {
|
|
171
|
-
imgData.data[__i] ^= (__canvasNoise + __i) & 0xFF;
|
|
172
|
-
}
|
|
173
|
-
ctx.putImageData(imgData, 0, 0);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return origToDataURL.apply(this, arguments);
|
|
178
|
-
});
|
|
179
|
-
} catch(_) {}
|
|
180
|
-
|
|
181
|
-
// ── AudioContext fingerprint noise ────────────────────
|
|
182
|
-
// Headless Chrome's AudioContext produces slightly different output.
|
|
183
|
-
// Subtle noise breaks audio-based fingerprinting.
|
|
209
|
+
unlinkSync(ACTIVITY_FILE);
|
|
210
|
+
} catch {}
|
|
184
211
|
try {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
unlinkSync(CHROME_MODE_FILE);
|
|
213
|
+
} catch {}
|
|
214
|
+
if (killed) {
|
|
215
|
+
process.stderr.write(`[greedysearch] Killed Chrome on port ${GREEDY_PORT}.
|
|
216
|
+
`);
|
|
217
|
+
}
|
|
218
|
+
return killed;
|
|
219
|
+
}
|
|
220
|
+
export const killHeadlessChrome = killChrome;
|
|
221
|
+
export async function checkAndKillIdle() {
|
|
222
|
+
const headless = isChromeHeadless();
|
|
223
|
+
const timeoutMinutes = headless ? HEADLESS_IDLE_TIMEOUT_MINUTES : VISIBLE_IDLE_TIMEOUT_MINUTES;
|
|
224
|
+
if (timeoutMinutes <= 0)
|
|
225
|
+
return false;
|
|
226
|
+
if (!existsSync(ACTIVITY_FILE)) {
|
|
227
|
+
touchActivity();
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
199
230
|
try {
|
|
200
|
-
|
|
201
|
-
if (!
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
231
|
+
const lastActivity = Number.parseInt(readFileSync(ACTIVITY_FILE, "utf8").trim(), 10);
|
|
232
|
+
if (!lastActivity)
|
|
233
|
+
return false;
|
|
234
|
+
const idleMs = Date.now() - lastActivity;
|
|
235
|
+
const idleMinutes = idleMs / 60000;
|
|
236
|
+
if (idleMinutes >= timeoutMinutes) {
|
|
237
|
+
return killChrome();
|
|
238
|
+
}
|
|
239
|
+
} catch {}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
export const cdp = _cdp;
|
|
243
|
+
export async function getAnyTab() {
|
|
244
|
+
const list = await cdp(["list"]);
|
|
245
|
+
const first = list.split(`
|
|
246
|
+
`)[0];
|
|
247
|
+
if (!first)
|
|
248
|
+
throw new Error("No Chrome tabs found");
|
|
249
|
+
return first.slice(0, 8);
|
|
250
|
+
}
|
|
251
|
+
export async function openNewTab(url = "about:blank") {
|
|
252
|
+
const anchor = await getAnyTab();
|
|
253
|
+
const hostname = new URL(url).hostname;
|
|
254
|
+
const needsStealth = hostname === "copilot.microsoft.com" || hostname === "www.perplexity.ai" || hostname === "perplexity.ai" || hostname.endsWith(".perplexity.ai");
|
|
255
|
+
if (needsStealth) {
|
|
256
|
+
const raw = await cdp([
|
|
257
|
+
"evalraw",
|
|
258
|
+
anchor,
|
|
259
|
+
"Target.createTarget",
|
|
260
|
+
JSON.stringify({ url: "about:blank" })
|
|
261
|
+
]);
|
|
262
|
+
const { targetId } = JSON.parse(raw);
|
|
263
|
+
const tid = targetId.slice(0, 8);
|
|
264
|
+
await cdp(["list"]).catch(() => null);
|
|
265
|
+
if (hostname === "copilot.microsoft.com") {
|
|
266
|
+
await injectHeadlessStealth(tid);
|
|
267
|
+
} else {
|
|
268
|
+
injectHeadlessStealth(tid).catch(() => {});
|
|
269
|
+
}
|
|
270
|
+
await cdp(["list"]).catch(() => null);
|
|
271
|
+
return targetId;
|
|
272
|
+
}
|
|
273
|
+
const raw = await cdp([
|
|
274
|
+
"evalraw",
|
|
275
|
+
anchor,
|
|
276
|
+
"Target.createTarget",
|
|
277
|
+
JSON.stringify({ url })
|
|
278
|
+
]);
|
|
279
|
+
const { targetId } = JSON.parse(raw);
|
|
280
|
+
await cdp(["list"]).catch(() => null);
|
|
281
|
+
return targetId;
|
|
282
|
+
}
|
|
283
|
+
export async function activateTab(targetId) {
|
|
207
284
|
try {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// two APIs are always consistent. Removes any "HeadlessChrome" brand entry.
|
|
285
|
+
const anchor = await getAnyTab();
|
|
286
|
+
await cdp([
|
|
287
|
+
"evalraw",
|
|
288
|
+
anchor,
|
|
289
|
+
"Target.activateTarget",
|
|
290
|
+
JSON.stringify({ targetId })
|
|
291
|
+
]);
|
|
292
|
+
} catch {}
|
|
293
|
+
}
|
|
294
|
+
export async function closeTab(targetId) {
|
|
219
295
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
{
|
|
226
|
-
];
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
{ brand: 'Not)A;Brand', version: '99.0.0.0' },
|
|
237
|
-
{ brand: 'Google Chrome', version: _uaFull },
|
|
238
|
-
{ brand: 'Chromium', version: _uaFull },
|
|
239
|
-
],
|
|
240
|
-
mobile: false, model: '', platform: 'Windows',
|
|
241
|
-
platformVersion: '15.0.0', uaFullVersion: _uaFull, wow64: false,
|
|
242
|
-
});
|
|
243
|
-
},
|
|
244
|
-
toJSON: function() { return { brands: _brands, mobile: false, platform: 'Windows' }; },
|
|
245
|
-
};
|
|
246
|
-
},
|
|
247
|
-
configurable: true,
|
|
248
|
-
});
|
|
249
|
-
} catch(_) {}
|
|
250
|
-
|
|
251
|
-
// ── CDP Runtime serialization guard ──────────────────
|
|
252
|
-
// Sites detect CDP by putting a getter on Error.prototype.stack
|
|
253
|
-
// and checking if console.log triggers it (only happens when
|
|
254
|
-
// Runtime domain is enabled). We monkey-patch console methods to
|
|
255
|
-
// strip custom getters from arguments before they reach CDP.
|
|
296
|
+
const anchor = await getAnyTab();
|
|
297
|
+
await cdp([
|
|
298
|
+
"evalraw",
|
|
299
|
+
anchor,
|
|
300
|
+
"Target.closeTarget",
|
|
301
|
+
JSON.stringify({ targetId })
|
|
302
|
+
]);
|
|
303
|
+
} catch {}
|
|
304
|
+
}
|
|
305
|
+
export async function closeTabs(targetIds = []) {
|
|
306
|
+
await Promise.all(targetIds.filter(Boolean).map((tid) => closeTab(tid).catch(() => {})));
|
|
307
|
+
if (targetIds.length > 0) {
|
|
308
|
+
await cdp(["list"]).catch(() => null);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
export function getFullTabFromCache(engine, engineDomains) {
|
|
256
312
|
try {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
313
|
+
if (!existsSync(PAGES_CACHE))
|
|
314
|
+
return null;
|
|
315
|
+
const pages = JSON.parse(readFileSync(PAGES_CACHE, "utf8"));
|
|
316
|
+
const found = pages.find((p) => p.url.includes(engineDomains[engine]));
|
|
317
|
+
return found ? found.targetId : null;
|
|
318
|
+
} catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
export function probeGreedyChrome(timeoutMs = 3000) {
|
|
323
|
+
return new Promise((resolve) => {
|
|
324
|
+
const req = http.get(`http://localhost:${GREEDY_PORT}/json/version`, (res) => {
|
|
325
|
+
res.resume();
|
|
326
|
+
resolve(res.statusCode === 200);
|
|
327
|
+
});
|
|
328
|
+
req.on("error", () => resolve(false));
|
|
329
|
+
req.setTimeout(timeoutMs, () => {
|
|
330
|
+
req.destroy();
|
|
331
|
+
resolve(false);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
async function waitForPortDown(timeoutMs = 3000, intervalMs = 200) {
|
|
336
|
+
const deadline = Date.now() + timeoutMs;
|
|
337
|
+
while (Date.now() < deadline) {
|
|
338
|
+
if (!await probeGreedyChrome(300))
|
|
339
|
+
return true;
|
|
340
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
341
|
+
}
|
|
342
|
+
return !await probeGreedyChrome(300);
|
|
343
|
+
}
|
|
344
|
+
export async function refreshPortFile() {
|
|
345
|
+
const LOCK_FILE = `${ACTIVE_PORT_FILE}.lock`;
|
|
346
|
+
const TEMP_FILE = `${ACTIVE_PORT_FILE}.tmp`;
|
|
347
|
+
const LOCK_STALE_MS = 5000;
|
|
348
|
+
const LOCK_WAIT_MS = 1000;
|
|
349
|
+
const lockAcquired = await new Promise((resolve) => {
|
|
350
|
+
const start = Date.now();
|
|
351
|
+
const tryLock = () => {
|
|
352
|
+
try {
|
|
353
|
+
const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
|
|
354
|
+
writeFileSync(LOCK_FILE, payload, { encoding: "utf8", flag: "wx" });
|
|
355
|
+
resolve(true);
|
|
356
|
+
} catch (e) {
|
|
357
|
+
if (e?.code !== "EEXIST") {
|
|
358
|
+
if (Date.now() - start < LOCK_WAIT_MS) {
|
|
359
|
+
setTimeout(tryLock, 50);
|
|
360
|
+
} else {
|
|
361
|
+
resolve(false);
|
|
362
|
+
}
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const lockRaw = readFileSync(LOCK_FILE, "utf8").trim();
|
|
367
|
+
const parsed = lockRaw.startsWith("{") ? JSON.parse(lockRaw) : { ts: Number(lockRaw) };
|
|
368
|
+
const lockTime = Number(parsed?.ts) || 0;
|
|
369
|
+
if (lockTime > 0 && Date.now() - lockTime > LOCK_STALE_MS) {
|
|
370
|
+
try {
|
|
371
|
+
unlinkSync(LOCK_FILE);
|
|
372
|
+
} catch {}
|
|
373
|
+
}
|
|
374
|
+
if (Date.now() - start < LOCK_WAIT_MS) {
|
|
375
|
+
setTimeout(tryLock, 50);
|
|
376
|
+
} else {
|
|
377
|
+
resolve(false);
|
|
378
|
+
}
|
|
379
|
+
} catch {
|
|
380
|
+
if (Date.now() - start < LOCK_WAIT_MS) {
|
|
381
|
+
setTimeout(tryLock, 50);
|
|
382
|
+
} else {
|
|
383
|
+
resolve(false);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
263
386
|
}
|
|
264
|
-
return a;
|
|
265
387
|
};
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
console.warn = __markNative(function warn() { return _origWarn.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
|
|
269
|
-
console.debug = __markNative(function debug() { return _origDebug.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
|
|
270
|
-
console.info = __markNative(function info() { return _origInfo.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
|
|
271
|
-
} catch(_) {}
|
|
272
|
-
|
|
273
|
-
// ── Native function masking ──────────────────────────
|
|
274
|
-
// Patched APIs should not stringify as user-defined stealth code.
|
|
388
|
+
tryLock();
|
|
389
|
+
});
|
|
275
390
|
try {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
391
|
+
const body = await new Promise((res, rej) => {
|
|
392
|
+
const req = http.get(`http://localhost:${GREEDY_PORT}/json/version`, (r) => {
|
|
393
|
+
let b = "";
|
|
394
|
+
r.on("data", (d) => b += d);
|
|
395
|
+
r.on("end", () => res(b));
|
|
396
|
+
});
|
|
397
|
+
req.on("error", rej);
|
|
398
|
+
req.setTimeout(3000, () => {
|
|
399
|
+
req.destroy();
|
|
400
|
+
rej(new Error("timeout"));
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
const { webSocketDebuggerUrl } = JSON.parse(body);
|
|
404
|
+
const wsPath = new URL(webSocketDebuggerUrl).pathname;
|
|
405
|
+
if (lockAcquired) {
|
|
406
|
+
writeFileSync(TEMP_FILE, `${GREEDY_PORT}
|
|
407
|
+
${wsPath}`, "utf8");
|
|
408
|
+
try {
|
|
409
|
+
unlinkSync(ACTIVE_PORT_FILE);
|
|
410
|
+
} catch {}
|
|
411
|
+
renameSync(TEMP_FILE, ACTIVE_PORT_FILE);
|
|
412
|
+
}
|
|
413
|
+
} catch {} finally {
|
|
414
|
+
if (lockAcquired) {
|
|
415
|
+
try {
|
|
416
|
+
unlinkSync(LOCK_FILE);
|
|
417
|
+
} catch {}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
export async function ensureChrome() {
|
|
422
|
+
if (process.env.GREEDY_SEARCH_RESEARCH_CHILD && await probeGreedyChrome()) {
|
|
423
|
+
await refreshPortFile();
|
|
424
|
+
try {
|
|
425
|
+
const md = readMetadata();
|
|
426
|
+
if (md) {
|
|
427
|
+
touchActivityBL(md);
|
|
428
|
+
registerClient(md);
|
|
281
429
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
`);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
430
|
+
} catch {}
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
cleanupStaleSessions();
|
|
434
|
+
const wasKilled = await checkAndKillIdle();
|
|
435
|
+
let ready = wasKilled ? false : await probeGreedyChrome();
|
|
436
|
+
if (!ready && !wasKilled) {
|
|
437
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
438
|
+
ready = await probeGreedyChrome();
|
|
439
|
+
}
|
|
440
|
+
let forceRelaunch = false;
|
|
441
|
+
if (ready) {
|
|
442
|
+
const headless = isChromeHeadless();
|
|
443
|
+
const wantsVisible = process.env.GREEDY_SEARCH_VISIBLE === "1";
|
|
444
|
+
if (!wantsVisible && !headless) {
|
|
445
|
+
process.stderr.write(`[greedysearch] Visible Chrome detected — switching to headless mode...
|
|
446
|
+
`);
|
|
447
|
+
await killHeadlessChrome();
|
|
448
|
+
await waitForPortDown();
|
|
449
|
+
forceRelaunch = true;
|
|
450
|
+
} else if (wantsVisible && headless) {
|
|
451
|
+
process.stderr.write(`[greedysearch] Headless Chrome detected — switching to visible mode...
|
|
452
|
+
`);
|
|
453
|
+
await killHeadlessChrome();
|
|
454
|
+
await waitForPortDown();
|
|
455
|
+
forceRelaunch = true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const readyAfterModeCheck = forceRelaunch ? false : await probeGreedyChrome();
|
|
459
|
+
if (readyAfterModeCheck) {
|
|
460
|
+
await refreshPortFile();
|
|
461
|
+
try {
|
|
462
|
+
const md = readMetadata();
|
|
463
|
+
if (md) {
|
|
464
|
+
touchActivityBL(md);
|
|
465
|
+
registerClient(md);
|
|
466
|
+
}
|
|
467
|
+
} catch {}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const lock = acquireLaunchLock();
|
|
471
|
+
if (!lock.acquired) {
|
|
472
|
+
const deadline = Date.now() + 12000;
|
|
473
|
+
let reReady = false;
|
|
474
|
+
while (Date.now() < deadline) {
|
|
475
|
+
reReady = await probeGreedyChrome(500);
|
|
476
|
+
if (reReady)
|
|
477
|
+
break;
|
|
478
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
479
|
+
}
|
|
480
|
+
if (reReady) {
|
|
481
|
+
await refreshPortFile();
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
try {
|
|
486
|
+
const reCheck = await probeGreedyChrome(1000);
|
|
487
|
+
if (reCheck) {
|
|
488
|
+
await refreshPortFile();
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
process.stderr.write(`GreedySearch Chrome not running on port ${GREEDY_PORT} — auto-launching...
|
|
492
|
+
`);
|
|
493
|
+
const launchScript = resolveGreedySearchLaunchScript({
|
|
494
|
+
moduleDir: __dir,
|
|
495
|
+
entrypoint: process.argv[1],
|
|
496
|
+
env: process.env
|
|
497
|
+
});
|
|
498
|
+
const launchArgs = [launchScript];
|
|
499
|
+
if (process.env.GREEDY_SEARCH_VISIBLE !== "1")
|
|
500
|
+
launchArgs.push("--headless");
|
|
501
|
+
await new Promise((resolve, reject) => {
|
|
502
|
+
const proc = spawn(nodeRuntimeCommand(), launchArgs, {
|
|
503
|
+
stdio: ["ignore", process.stderr, process.stderr]
|
|
504
|
+
});
|
|
505
|
+
proc.on("close", (code) => code === 0 ? resolve() : reject(new Error("launch.mjs failed")));
|
|
506
|
+
});
|
|
507
|
+
} finally {
|
|
508
|
+
lock.release();
|
|
509
|
+
}
|
|
510
|
+
}
|