@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.
Files changed (197) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +70 -123
  3. package/extensions/dm-9router-ext/src/index.js +410 -5
  4. package/extensions/dm-caveman/extensions/caveman.js +283 -12
  5. package/extensions/dm-cliproxy/index.js +182 -2
  6. package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
  7. package/extensions/dm-cliproxy/src/apply.js +228 -1
  8. package/extensions/dm-cliproxy/src/cache.js +42 -1
  9. package/extensions/dm-cliproxy/src/commands.js +50 -2
  10. package/extensions/dm-cliproxy/src/compat.js +81 -1
  11. package/extensions/dm-cliproxy/src/config.js +192 -2
  12. package/extensions/dm-cliproxy/src/conflicts.js +46 -1
  13. package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
  14. package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
  15. package/extensions/dm-cliproxy/src/log.js +23 -1
  16. package/extensions/dm-cliproxy/src/status-quota.js +77 -1
  17. package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
  18. package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
  19. package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
  20. package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
  21. package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
  22. package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
  23. package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
  24. package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
  25. package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
  26. package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
  27. package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
  28. package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
  29. package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
  30. package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
  31. package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
  32. package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
  33. package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
  34. package/extensions/dm-context/src/context.js +144 -1
  35. package/extensions/dm-context/src/index.js +339 -7
  36. package/extensions/dm-context/src/utils.js +9 -1
  37. package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
  38. package/extensions/dm-cua/index.js +75 -6
  39. package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
  40. package/extensions/dm-cua/src/browser-install.mjs +331 -2
  41. package/extensions/dm-fff/src/index.js +688 -12
  42. package/extensions/dm-fff/src/query.js +60 -1
  43. package/extensions/dm-goal/src/goal.js +894 -23
  44. package/extensions/dm-image2/index.js +103 -9
  45. package/extensions/dm-image2/src/image-lib.mjs +1275 -8
  46. package/extensions/dm-subagents/install.mjs +62 -8
  47. package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
  48. package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
  49. package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
  50. package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
  51. package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
  52. package/extensions/dm-subagents/src/agents/agents.js +1139 -11
  53. package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
  54. package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
  55. package/extensions/dm-subagents/src/agents/identity.js +29 -1
  56. package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
  57. package/extensions/dm-subagents/src/agents/skills.js +614 -6
  58. package/extensions/dm-subagents/src/extension/config.js +35 -2
  59. package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
  60. package/extensions/dm-subagents/src/extension/doctor.js +172 -15
  61. package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
  62. package/extensions/dm-subagents/src/extension/index.js +521 -359
  63. package/extensions/dm-subagents/src/extension/rpc.js +266 -7
  64. package/extensions/dm-subagents/src/extension/schemas.js +275 -1
  65. package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
  66. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
  67. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
  68. package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
  69. package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
  70. package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
  71. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
  72. package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
  73. package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
  74. package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
  75. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
  76. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
  77. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
  78. package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
  79. package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
  80. package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
  81. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
  82. package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
  83. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
  84. package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
  85. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
  86. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
  87. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
  88. package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
  89. package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
  90. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
  91. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
  92. package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
  93. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
  94. package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
  95. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
  96. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
  97. package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
  98. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
  99. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
  100. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
  101. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
  102. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
  103. package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
  104. package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
  105. package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
  106. package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
  107. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
  108. package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
  109. package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
  110. package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
  111. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
  112. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
  113. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
  114. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
  115. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
  116. package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
  117. package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
  118. package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
  119. package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
  120. package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
  121. package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
  122. package/extensions/dm-subagents/src/shared/formatters.js +98 -7
  123. package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
  124. package/extensions/dm-subagents/src/shared/model-info.js +62 -1
  125. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
  126. package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
  127. package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
  128. package/extensions/dm-subagents/src/shared/settings.js +198 -11
  129. package/extensions/dm-subagents/src/shared/status-format.js +53 -1
  130. package/extensions/dm-subagents/src/shared/types.js +184 -6
  131. package/extensions/dm-subagents/src/shared/utils.js +462 -2
  132. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
  133. package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
  134. package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
  135. package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
  136. package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
  137. package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
  138. package/extensions/dm-subagents/src/tui/render.js +1542 -4
  139. package/extensions/dm-usage/index.js +1294 -9
  140. package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
  141. package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
  142. package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
  143. package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
  144. package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
  145. package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
  146. package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
  147. package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
  148. package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
  149. package/extensions/greedysearch-dm/bin/search.mjs +620 -540
  150. package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
  151. package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
  152. package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
  153. package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
  154. package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
  155. package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
  156. package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
  157. package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
  158. package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
  159. package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
  160. package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
  161. package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
  162. package/extensions/greedysearch-dm/index.js +123 -23
  163. package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
  164. package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
  165. package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
  166. package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
  167. package/extensions/greedysearch-dm/src/github.mjs +222 -7
  168. package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
  169. package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
  170. package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
  171. package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
  172. package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
  173. package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
  174. package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
  175. package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
  176. package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
  177. package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
  178. package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
  179. package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
  180. package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
  181. package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
  182. package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
  183. package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
  184. package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
  185. package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
  186. package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
  187. package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
  188. package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
  189. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
  190. package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
  191. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
  192. package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
  193. package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
  194. package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
  195. package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
  196. package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
  197. package/package.json +1 -1
@@ -1,10 +1,95 @@
1
- function P(q){return{bing:"Bing Copilot",google:"Google AI",gemini:"Gemini",copilot:"Copilot",perplexity:"Perplexity"}[q]??q.charAt(0).toUpperCase()+q.slice(1)}function W(q){if(!q)return"";if(q==="official-docs")return"official docs";return q.replace(/-/g," ")}function X(q){if(!q)return"Mixed";return q.charAt(0).toUpperCase()+q.slice(1)}function z(q){return String(q.displayUrl||q.canonicalUrl||q.url||"")}function M(q){return String(q.title||q.domain||z(q)||"Untitled source")}function R(q){if(typeof q.engineCount==="number")return q.engineCount;return(Array.isArray(q.engines)?q.engines:[]).length}function _(q){return new Map(q.map((G)=>[String(G.id||""),G]).filter(([G])=>G))}function D(q){let G=String(q.id||"?"),J=z(q),K=M(q),O=String(q.domain||""),Q=Array.isArray(q.engines)?q.engines:[],V=R(q),Z=W(String(q.sourceType||"")),$=q.fetch,B=$?.ok?`fetched ${$.status||200}`:$?.attempted?"fetch failed":"";return`- ${[`${G} - [${K}](${J})`,O,Z,Q.length?`cited by ${Q.map(P).join(", ")} (${V}/3)`:`${V}/3`,B].filter(Boolean).join(" - ")}`}function b(q,G=[],J=6){if(!q.length)return[];let K=_(q),O=G.map((Q)=>K.get(Q)).filter((Q)=>Boolean(Q));if(O.length>0)return O.slice(0,J);return q.slice(0,J)}function w(q,G,J,K=6){if(G.answer)q.push("## Answer"),q.push(String(G.answer)),q.push("");let O=G.agreement,Q=String(O?.summary||"").trim(),V=String(O?.level||"").trim();if(Q||V)q.push("## Consensus"),q.push(`- ${X(V)}${Q?` - ${Q}`:""}`),q.push("");let Z=Array.isArray(G.differences)?G.differences:[];if(Z.length>0){q.push("## Where Engines Differ");for(let Y of Z)q.push(`- ${Y}`);q.push("")}let $=Array.isArray(G.caveats)?G.caveats:[];if($.length>0){q.push("## Caveats");for(let Y of $)q.push(`- ${Y}`);q.push("")}let B=Array.isArray(G.claims)?G.claims:[];if(B.length>0){q.push("## Key Claims");for(let Y of B){let U=Array.isArray(Y.sourceIds)?Y.sourceIds:[],A=String(Y.support||"moderate");q.push(`- ${String(Y.claim||"")} [${A}${U.length?`; ${U.join(", ")}`:""}]`)}q.push("")}let k=Array.isArray(G.recommendedSources)?G.recommendedSources:[],F=b(J,k,K);if(F.length>0){q.push("## Top Sources");for(let Y of F)q.push(D(Y));q.push("")}}var C=800;function N(q){return q.split(`
2
- `).map((G)=>G.length>C?G.slice(0,C-1)+"…":G).join(`
3
- `)}function y(q,G){let J=[];if(q==="all")return N(E(G,J));return N(x(G,J))}function E(q,G){let{_synthesis:J,_sources:K,_needsHumanVerification:O,_research:Q}=q;if(O){let V=Array.isArray(O.engines)?O.engines.join(", "):"one or more engines";G.push("## Manual verification required"),G.push(String(O.message||"Visible Chrome is open. Solve the verification challenge, then rerun the same search.")),G.push(`Engines: ${V}`),G.push("")}if(J?.answer){if(Q?.mode==="iterative")I(G,Q);w(G,J,K||[],6);let V=String(J.synthesizedBy||"configured synthesizer");return G.push(Q?.mode==="iterative"?`*Research mode: iterative planning, source fetching, citation audit, and bundle output*
4
- `:`*Synthesized by ${V} from multi-engine results and fetched sources*
5
- `),G.join(`
6
- `).trim()}for(let[V,Z]of Object.entries(q)){if(V.startsWith("_"))continue;G.push(`
7
- ## ${P(V)}`),j(Z,G,3)}return G.join(`
8
- `).trim()}function I(q,G){let J=G.floor,K=J?.metrics,O=G.bundle,Q=G.manifest;if(q.push("## Research Run"),q.push(`- Status: ${J?.floorMet?"floor met":"partial / floor unmet"}`),Q?.terminationReason)q.push(`- Stop reason: ${String(Q.terminationReason)}`);if(K)q.push(`- Evidence: ${K.fetchedOk||0} fetched sources, ${K.primarySources||0} primary/official, ${K.claims||0} claims, ${K.cited||0} citations`),q.push(`- Questions: ${K.closedQuestions||0}/${K.totalQuestions||0} closed${K.openQuestions?`, ${K.openQuestions} open`:""}`);if(O?.dir)q.push(`- Bundle: ${String(O.dir)}`);q.push("")}function x(q,G){let J=q._needsHumanVerification;if(J){let K=Array.isArray(J.engines)?J.engines.join(", "):"this engine";G.push("## Manual verification required"),G.push(String(J.message||"Visible Chrome is open. Solve the verification challenge, then rerun the same search.")),G.push(`Engines: ${K}`),G.push("")}return j(q,G,5),G.join(`
9
- `).trim()}function j(q,G,J){if(q.error){G.push(`Error: ${q.error}`);return}if(q.answer)G.push(String(q.answer));let K=q.sources;if(Array.isArray(K)&&K.length>0){G.push(`
10
- Sources:`);for(let O of K.slice(0,J))G.push(`- [${O.title||O.url}](${O.url})`)}}export{y as formatResults};
1
+ import { formatEngineName } from "../utils/helpers.js";
2
+ import { renderSynthesis } from "./synthesis.js";
3
+ const MAX_LINE_WIDTH = 800;
4
+ function _truncateLongLines(text) {
5
+ return text.split(`
6
+ `).map((line) => line.length > MAX_LINE_WIDTH ? line.slice(0, MAX_LINE_WIDTH - 1) + "…" : line).join(`
7
+ `);
8
+ }
9
+ export function formatResults(engine, data) {
10
+ const lines = [];
11
+ if (engine === "all") {
12
+ return _truncateLongLines(formatAllEnginesResult(data, lines));
13
+ }
14
+ return _truncateLongLines(formatSingleEngineResult(data, lines));
15
+ }
16
+ function formatAllEnginesResult(data, lines) {
17
+ const synthesis = data._synthesis;
18
+ const dedupedSources = data._sources;
19
+ const needsHuman = data._needsHumanVerification;
20
+ const research = data._research;
21
+ if (needsHuman) {
22
+ const engines = Array.isArray(needsHuman.engines) ? needsHuman.engines.join(", ") : "one or more engines";
23
+ lines.push("## Manual verification required");
24
+ lines.push(String(needsHuman.message || "Visible Chrome is open. Solve the verification challenge, then rerun the same search."));
25
+ lines.push(`Engines: ${engines}`);
26
+ lines.push("");
27
+ }
28
+ if (synthesis?.answer) {
29
+ if (research?.mode === "iterative")
30
+ renderResearchHeader(lines, research);
31
+ renderSynthesis(lines, synthesis, dedupedSources || [], 6);
32
+ const synthesizedBy = String(synthesis.synthesizedBy || "configured synthesizer");
33
+ lines.push(research?.mode === "iterative" ? `*Research mode: iterative planning, source fetching, citation audit, and bundle output*
34
+ ` : `*Synthesized by ${synthesizedBy} from multi-engine results and fetched sources*
35
+ `);
36
+ return lines.join(`
37
+ `).trim();
38
+ }
39
+ for (const [eng, result] of Object.entries(data)) {
40
+ if (eng.startsWith("_"))
41
+ continue;
42
+ lines.push(`
43
+ ## ${formatEngineName(eng)}`);
44
+ formatEngineResult(result, lines, 3);
45
+ }
46
+ return lines.join(`
47
+ `).trim();
48
+ }
49
+ function renderResearchHeader(lines, research) {
50
+ const floor = research.floor;
51
+ const metrics = floor?.metrics;
52
+ const bundle = research.bundle;
53
+ const manifest = research.manifest;
54
+ lines.push("## Research Run");
55
+ lines.push(`- Status: ${floor?.floorMet ? "floor met" : "partial / floor unmet"}`);
56
+ if (manifest?.terminationReason)
57
+ lines.push(`- Stop reason: ${String(manifest.terminationReason)}`);
58
+ if (metrics) {
59
+ lines.push(`- Evidence: ${metrics.fetchedOk || 0} fetched sources, ${metrics.primarySources || 0} primary/official, ${metrics.claims || 0} claims, ${metrics.cited || 0} citations`);
60
+ lines.push(`- Questions: ${metrics.closedQuestions || 0}/${metrics.totalQuestions || 0} closed${metrics.openQuestions ? `, ${metrics.openQuestions} open` : ""}`);
61
+ }
62
+ if (bundle?.dir)
63
+ lines.push(`- Bundle: ${String(bundle.dir)}`);
64
+ lines.push("");
65
+ }
66
+ function formatSingleEngineResult(data, lines) {
67
+ const needsHuman = data._needsHumanVerification;
68
+ if (needsHuman) {
69
+ const engines = Array.isArray(needsHuman.engines) ? needsHuman.engines.join(", ") : "this engine";
70
+ lines.push("## Manual verification required");
71
+ lines.push(String(needsHuman.message || "Visible Chrome is open. Solve the verification challenge, then rerun the same search."));
72
+ lines.push(`Engines: ${engines}`);
73
+ lines.push("");
74
+ }
75
+ formatEngineResult(data, lines, 5);
76
+ return lines.join(`
77
+ `).trim();
78
+ }
79
+ function formatEngineResult(data, lines, maxSources) {
80
+ if (data.error) {
81
+ lines.push(`Error: ${data.error}`);
82
+ return;
83
+ }
84
+ if (data.answer) {
85
+ lines.push(String(data.answer));
86
+ }
87
+ const sources = data.sources;
88
+ if (Array.isArray(sources) && sources.length > 0) {
89
+ lines.push(`
90
+ Sources:`);
91
+ for (const s of sources.slice(0, maxSources)) {
92
+ lines.push(`- [${s.title || s.url}](${s.url})`);
93
+ }
94
+ }
95
+ }
@@ -1 +1,57 @@
1
- function O(q){return{bing:"Bing Copilot",google:"Google AI",gemini:"Gemini",copilot:"Copilot",perplexity:"Perplexity"}[q]??q.charAt(0).toUpperCase()+q.slice(1)}function Q(q){if(!q)return"";if(q==="official-docs")return"official docs";return q.replace(/-/g," ")}function R(q){return String(q.displayUrl||q.canonicalUrl||q.url||"")}function X(q){return String(q.title||q.domain||R(q)||"Untitled source")}function Y(q){if(typeof q.engineCount==="number")return q.engineCount;return(Array.isArray(q.engines)?q.engines:[]).length}function Z(q){return new Map(q.map((A)=>[String(A.id||""),A]).filter(([A])=>A))}function k(q){let A=String(q.id||"?"),B=R(q),G=X(q),D=String(q.domain||""),H=Array.isArray(q.engines)?q.engines:[],K=Y(q),V=Q(String(q.sourceType||"")),J=q.fetch,W=J?.ok?`fetched ${J.status||200}`:J?.attempted?"fetch failed":"";return`- ${[`${A} - [${G}](${B})`,D,V,H.length?`cited by ${H.map(O).join(", ")} (${K}/3)`:`${K}/3`,W].filter(Boolean).join(" - ")}`}function w(q,A){let B=A.fetch;if(!B?.attempted)return;let G=String(B.snippet||"").trim(),D=String(B.lastModified||"").trim();if(G)q.push(` Evidence: ${G}`);if(D)q.push(` Last-Modified: ${D}`);if(B.error)q.push(` Fetch error: ${String(B.error)}`)}function C(q,A=[],B=6){if(!q.length)return[];let G=Z(q),D=A.map((H)=>G.get(H)).filter((H)=>Boolean(H));if(D.length>0)return D.slice(0,B);return q.slice(0,B)}export{R as sourceUrl,X as sourceLabel,Y as sourceConsensus,w as renderSourceEvidence,C as pickSources,Z as getSourceMap,k as formatSourceLine};
1
+ import { formatEngineName, humanizeSourceType } from "../utils/helpers.js";
2
+ export function sourceUrl(source) {
3
+ return String(source.displayUrl || source.canonicalUrl || source.url || "");
4
+ }
5
+ export function sourceLabel(source) {
6
+ return String(source.title || source.domain || sourceUrl(source) || "Untitled source");
7
+ }
8
+ export function sourceConsensus(source) {
9
+ if (typeof source.engineCount === "number")
10
+ return source.engineCount;
11
+ const engines = Array.isArray(source.engines) ? source.engines : [];
12
+ return engines.length;
13
+ }
14
+ export function getSourceMap(sources) {
15
+ return new Map(sources.map((source) => [String(source.id || ""), source]).filter(([id]) => id));
16
+ }
17
+ export function formatSourceLine(source) {
18
+ const id = String(source.id || "?");
19
+ const url = sourceUrl(source);
20
+ const title = sourceLabel(source);
21
+ const domain = String(source.domain || "");
22
+ const engines = Array.isArray(source.engines) ? source.engines : [];
23
+ const consensus = sourceConsensus(source);
24
+ const typeLabel = humanizeSourceType(String(source.sourceType || ""));
25
+ const fetch = source.fetch;
26
+ const fetchStatus = fetch?.ok ? `fetched ${fetch.status || 200}` : fetch?.attempted ? "fetch failed" : "";
27
+ const pieces = [
28
+ `${id} - [${title}](${url})`,
29
+ domain,
30
+ typeLabel,
31
+ engines.length ? `cited by ${engines.map(formatEngineName).join(", ")} (${consensus}/3)` : `${consensus}/3`,
32
+ fetchStatus
33
+ ].filter(Boolean);
34
+ return `- ${pieces.join(" - ")}`;
35
+ }
36
+ export function renderSourceEvidence(lines, source) {
37
+ const fetch = source.fetch;
38
+ if (!fetch?.attempted)
39
+ return;
40
+ const snippet = String(fetch.snippet || "").trim();
41
+ const lastModified = String(fetch.lastModified || "").trim();
42
+ if (snippet)
43
+ lines.push(` Evidence: ${snippet}`);
44
+ if (lastModified)
45
+ lines.push(` Last-Modified: ${lastModified}`);
46
+ if (fetch.error)
47
+ lines.push(` Fetch error: ${String(fetch.error)}`);
48
+ }
49
+ export function pickSources(sources, recommendedIds = [], max = 6) {
50
+ if (!sources.length)
51
+ return [];
52
+ const sourceMap = getSourceMap(sources);
53
+ const recommended = recommendedIds.map((id) => sourceMap.get(id)).filter((source) => Boolean(source));
54
+ if (recommended.length > 0)
55
+ return recommended.slice(0, max);
56
+ return sources.slice(0, max);
57
+ }
@@ -1 +1,49 @@
1
- function B(r){if(!r)return"Mixed";return r.charAt(0).toUpperCase()+r.slice(1)}function E(r){return{bing:"Bing Copilot",google:"Google AI",gemini:"Gemini",copilot:"Copilot",perplexity:"Perplexity"}[r]??r.charAt(0).toUpperCase()+r.slice(1)}function x(r){if(!r)return"";if(r==="official-docs")return"official docs";return r.replace(/-/g," ")}function C(r){return String(r.displayUrl||r.canonicalUrl||r.url||"")}function H(r){return String(r.title||r.domain||C(r)||"Untitled source")}function J(r){if(typeof r.engineCount==="number")return r.engineCount;return(Array.isArray(r.engines)?r.engines:[]).length}function K(r){return new Map(r.map((t)=>[String(t.id||""),t]).filter(([t])=>t))}function M(r){let t=String(r.id||"?"),g=C(r),p=H(r),u=String(r.domain||""),f=Array.isArray(r.engines)?r.engines:[],d=J(r),G=x(String(r.sourceType||"")),$=r.fetch,U=$?.ok?`fetched ${$.status||200}`:$?.attempted?"fetch failed":"";return`- ${[`${t} - [${p}](${g})`,u,G,f.length?`cited by ${f.map(E).join(", ")} (${d}/3)`:`${d}/3`,U].filter(Boolean).join(" - ")}`}function S(r,t=[],g=6){if(!r.length)return[];let p=K(r),u=t.map((f)=>p.get(f)).filter((f)=>Boolean(f));if(u.length>0)return u.slice(0,g);return r.slice(0,g)}function W(r,t,g,p=6){if(t.answer)r.push("## Answer"),r.push(String(t.answer)),r.push("");let u=t.agreement,f=String(u?.summary||"").trim(),d=String(u?.level||"").trim();if(f||d)r.push("## Consensus"),r.push(`- ${B(d)}${f?` - ${f}`:""}`),r.push("");let G=Array.isArray(t.differences)?t.differences:[];if(G.length>0){r.push("## Where Engines Differ");for(let o of G)r.push(`- ${o}`);r.push("")}let $=Array.isArray(t.caveats)?t.caveats:[];if($.length>0){r.push("## Caveats");for(let o of $)r.push(`- ${o}`);r.push("")}let U=Array.isArray(t.claims)?t.claims:[];if(U.length>0){r.push("## Key Claims");for(let o of U){let A=Array.isArray(o.sourceIds)?o.sourceIds:[],D=String(o.support||"moderate");r.push(`- ${String(o.claim||"")} [${D}${A.length?`; ${A.join(", ")}`:""}]`)}r.push("")}let k=Array.isArray(t.recommendedSources)?t.recommendedSources:[],b=S(g,k,p);if(b.length>0){r.push("## Top Sources");for(let o of b)r.push(M(o));r.push("")}}export{W as renderSynthesis};
1
+ import { formatAgreementLevel } from "../utils/helpers.js";
2
+ import { formatSourceLine, pickSources } from "./sources.js";
3
+ export function renderSynthesis(lines, synthesis, sources, maxSources = 6) {
4
+ if (synthesis.answer) {
5
+ lines.push("## Answer");
6
+ lines.push(String(synthesis.answer));
7
+ lines.push("");
8
+ }
9
+ const agreement = synthesis.agreement;
10
+ const agreementSummary = String(agreement?.summary || "").trim();
11
+ const agreementLevel = String(agreement?.level || "").trim();
12
+ if (agreementSummary || agreementLevel) {
13
+ lines.push("## Consensus");
14
+ lines.push(`- ${formatAgreementLevel(agreementLevel)}${agreementSummary ? ` - ${agreementSummary}` : ""}`);
15
+ lines.push("");
16
+ }
17
+ const differences = Array.isArray(synthesis.differences) ? synthesis.differences : [];
18
+ if (differences.length > 0) {
19
+ lines.push("## Where Engines Differ");
20
+ for (const difference of differences)
21
+ lines.push(`- ${difference}`);
22
+ lines.push("");
23
+ }
24
+ const caveats = Array.isArray(synthesis.caveats) ? synthesis.caveats : [];
25
+ if (caveats.length > 0) {
26
+ lines.push("## Caveats");
27
+ for (const caveat of caveats)
28
+ lines.push(`- ${caveat}`);
29
+ lines.push("");
30
+ }
31
+ const claims = Array.isArray(synthesis.claims) ? synthesis.claims : [];
32
+ if (claims.length > 0) {
33
+ lines.push("## Key Claims");
34
+ for (const claim of claims) {
35
+ const sourceIds = Array.isArray(claim.sourceIds) ? claim.sourceIds : [];
36
+ const support = String(claim.support || "moderate");
37
+ lines.push(`- ${String(claim.claim || "")} [${support}${sourceIds.length ? `; ${sourceIds.join(", ")}` : ""}]`);
38
+ }
39
+ lines.push("");
40
+ }
41
+ const recommendedIds = Array.isArray(synthesis.recommendedSources) ? synthesis.recommendedSources : [];
42
+ const topSources = pickSources(sources, recommendedIds, maxSources);
43
+ if (topSources.length > 0) {
44
+ lines.push("## Top Sources");
45
+ for (const source of topSources)
46
+ lines.push(formatSourceLine(source));
47
+ lines.push("");
48
+ }
49
+ }
@@ -1,13 +1,228 @@
1
- var x={"user-agent":"GreedySearch/1.0",accept:"application/vnd.github+json","x-github-api-version":"2022-11-28"};function F(J){try{let z=new URL(J);if(!(z.hostname==="github.com"||z.hostname.endsWith(".github.com")))return null;let j=z.pathname.split("/").filter(Boolean);if(j.length<2)return null;let[q,K]=j;if(j.length===2)return{owner:q,repo:K,type:"root"};if(j.length>=4&&(j[2]==="blob"||j[2]==="tree")){let N=j[2],Q=j[3],C=j.slice(4).join("/");return{owner:q,repo:K,type:N,ref:Q,path:C}}return null}catch{return null}}async function Y(J,z=1e4){let j=new AbortController,q=setTimeout(()=>j.abort(),z);try{let K=await fetch(`https://api.github.com${J}`,{headers:x,signal:j.signal});if(clearTimeout(q),!K.ok)throw Error(`GitHub API ${K.status}: ${J}`);return await K.json()}catch(K){throw clearTimeout(q),K}}async function M(J,z){try{let j=await Y(`/repos/${J}/${z}/readme`);if(j.content&&j.encoding==="base64")return Buffer.from(j.content,"base64").toString("utf8");return""}catch{return""}}async function v(J,z,j="HEAD",q="",K){try{let N;if(j==="HEAD")if(K)N=await Y(`/repos/${J}/${z}/git/ref/heads/${K}`).catch(()=>null);else N=await Promise.any([Y(`/repos/${J}/${z}/git/ref/heads/main`),Y(`/repos/${J}/${z}/git/ref/heads/master`)]).catch(()=>null);else N=await Y(`/repos/${J}/${z}/git/ref/heads/${j}`).catch(()=>Y(`/repos/${J}/${z}/git/ref/heads/master`).catch(()=>null));if(!N?.object?.sha)return[];let C=(await Y(`/repos/${J}/${z}/git/commits/${N.object.sha}`)).tree.sha,X=(await Y(`/repos/${J}/${z}/git/trees/${C}`)).tree||[];if(q)X=X.filter((V)=>V.path.startsWith(q));return X.slice(0,50).map((V)=>({path:V.path,type:V.type==="tree"?"dir":"file",size:V.size}))}catch{return[]}}async function O(J,z,j,q,K=1e4,N){let Q=async(W)=>{let X=new AbortController,V=setTimeout(()=>X.abort(),K);try{let Z=await fetch(W,{headers:{"user-agent":x["user-agent"]},signal:X.signal});if(clearTimeout(V),Z.ok)return await Z.text();throw Error("not ok")}catch{throw clearTimeout(V),Error("failed")}};if(!j||j==="HEAD"){if(N)try{return await Q(`https://raw.githubusercontent.com/${J}/${z}/${N}/${q}`)}catch{return null}try{return await Promise.any([Q(`https://raw.githubusercontent.com/${J}/${z}/main/${q}`),Q(`https://raw.githubusercontent.com/${J}/${z}/master/${q}`)])}catch{return null}}let C=[`https://raw.githubusercontent.com/${J}/${z}/${j}/${q}`,`https://raw.githubusercontent.com/${J}/${z}/master/${q}`];for(let W of C)try{return await Q(W)}catch{}return null}async function U(J){let z=F(J);if(!z)return{ok:!1,error:"Not a valid GitHub URL"};let{owner:j,repo:q,type:K,ref:N,path:Q}=z;try{if(K==="root"||K==="tree"&&!Q){let C=await Y(`/repos/${j}/${q}`),[W,X]=await Promise.allSettled([M(j,q),v(j,q,N||"HEAD","",C.default_branch)]),V=W.status==="fulfilled"?W.value:"",Z=X.status==="fulfilled"?X.value:[],_=C?.description?`
1
+ const GITHUB_API = "https://api.github.com";
2
+ const DEFAULT_HEADERS = {
3
+ "user-agent": "GreedySearch/1.0",
4
+ accept: "application/vnd.github+json",
5
+ "x-github-api-version": "2022-11-28"
6
+ };
7
+ export function parseGitHubUrl(url) {
8
+ try {
9
+ const parsed = new URL(url);
10
+ if (!(parsed.hostname === "github.com" || parsed.hostname.endsWith(".github.com"))) {
11
+ return null;
12
+ }
13
+ const parts = parsed.pathname.split("/").filter(Boolean);
14
+ if (parts.length < 2) {
15
+ return null;
16
+ }
17
+ const [owner, repo] = parts;
18
+ if (parts.length === 2) {
19
+ return { owner, repo, type: "root" };
20
+ }
21
+ if (parts.length >= 4 && (parts[2] === "blob" || parts[2] === "tree")) {
22
+ const type = parts[2];
23
+ const ref = parts[3];
24
+ const path = parts.slice(4).join("/");
25
+ return { owner, repo, type, ref, path };
26
+ }
27
+ return null;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ async function apiGet(path, timeoutMs = 1e4) {
33
+ const controller = new AbortController;
34
+ const tid = setTimeout(() => controller.abort(), timeoutMs);
35
+ try {
36
+ const res = await fetch(`${GITHUB_API}${path}`, {
37
+ headers: DEFAULT_HEADERS,
38
+ signal: controller.signal
39
+ });
40
+ clearTimeout(tid);
41
+ if (!res.ok) {
42
+ throw new Error(`GitHub API ${res.status}: ${path}`);
43
+ }
44
+ return await res.json();
45
+ } catch (err) {
46
+ clearTimeout(tid);
47
+ throw err;
48
+ }
49
+ }
50
+ async function fetchReadme(owner, repo) {
51
+ try {
52
+ const data = await apiGet(`/repos/${owner}/${repo}/readme`);
53
+ if (data.content && data.encoding === "base64") {
54
+ return Buffer.from(data.content, "base64").toString("utf8");
55
+ }
56
+ return "";
57
+ } catch {
58
+ return "";
59
+ }
60
+ }
61
+ async function fetchTree(owner, repo, ref = "HEAD", subPath = "", defaultBranch) {
62
+ try {
63
+ let refData;
64
+ if (ref === "HEAD") {
65
+ if (defaultBranch) {
66
+ refData = await apiGet(`/repos/${owner}/${repo}/git/ref/heads/${defaultBranch}`).catch(() => null);
67
+ } else {
68
+ refData = await Promise.any([
69
+ apiGet(`/repos/${owner}/${repo}/git/ref/heads/main`),
70
+ apiGet(`/repos/${owner}/${repo}/git/ref/heads/master`)
71
+ ]).catch(() => null);
72
+ }
73
+ } else {
74
+ refData = await apiGet(`/repos/${owner}/${repo}/git/ref/heads/${ref}`).catch(() => apiGet(`/repos/${owner}/${repo}/git/ref/heads/master`).catch(() => null));
75
+ }
76
+ if (!refData?.object?.sha) {
77
+ return [];
78
+ }
79
+ const commit = await apiGet(`/repos/${owner}/${repo}/git/commits/${refData.object.sha}`);
80
+ const treeSha = commit.tree.sha;
81
+ const treeData = await apiGet(`/repos/${owner}/${repo}/git/trees/${treeSha}`);
82
+ let items = treeData.tree || [];
83
+ if (subPath) {
84
+ items = items.filter((item) => item.path.startsWith(subPath));
85
+ }
86
+ return items.slice(0, 50).map((item) => ({
87
+ path: item.path,
88
+ type: item.type === "tree" ? "dir" : "file",
89
+ size: item.size
90
+ }));
91
+ } catch {
92
+ return [];
93
+ }
94
+ }
95
+ async function fetchRawFile(owner, repo, ref, filePath, timeoutMs = 1e4, defaultBranch) {
96
+ const fetchOne = async (url) => {
97
+ const controller = new AbortController;
98
+ const tid = setTimeout(() => controller.abort(), timeoutMs);
99
+ try {
100
+ const res = await fetch(url, {
101
+ headers: { "user-agent": DEFAULT_HEADERS["user-agent"] },
102
+ signal: controller.signal
103
+ });
104
+ clearTimeout(tid);
105
+ if (res.ok) {
106
+ return await res.text();
107
+ }
108
+ throw new Error("not ok");
109
+ } catch {
110
+ clearTimeout(tid);
111
+ throw new Error("failed");
112
+ }
113
+ };
114
+ if (!ref || ref === "HEAD") {
115
+ if (defaultBranch) {
116
+ try {
117
+ return await fetchOne(`https://raw.githubusercontent.com/${owner}/${repo}/${defaultBranch}/${filePath}`);
118
+ } catch {
119
+ return null;
120
+ }
121
+ }
122
+ try {
123
+ return await Promise.any([
124
+ fetchOne(`https://raw.githubusercontent.com/${owner}/${repo}/main/${filePath}`),
125
+ fetchOne(`https://raw.githubusercontent.com/${owner}/${repo}/master/${filePath}`)
126
+ ]);
127
+ } catch {
128
+ return null;
129
+ }
130
+ }
131
+ const urls = [
132
+ `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filePath}`,
133
+ `https://raw.githubusercontent.com/${owner}/${repo}/master/${filePath}`
134
+ ];
135
+ for (const url of urls) {
136
+ try {
137
+ return await fetchOne(url);
138
+ } catch {}
139
+ }
140
+ return null;
141
+ }
142
+ export async function fetchGitHubContent(url) {
143
+ const parsed = parseGitHubUrl(url);
144
+ if (!parsed) {
145
+ return { ok: false, error: "Not a valid GitHub URL" };
146
+ }
147
+ const { owner, repo, type, ref, path } = parsed;
148
+ try {
149
+ if (type === "root" || type === "tree" && !path) {
150
+ const info = await apiGet(`/repos/${owner}/${repo}`);
151
+ const [readme, tree] = await Promise.allSettled([
152
+ fetchReadme(owner, repo),
153
+ fetchTree(owner, repo, ref || "HEAD", "", info.default_branch)
154
+ ]);
155
+ const readmeText = readme.status === "fulfilled" ? readme.value : "";
156
+ const treeItems = tree.status === "fulfilled" ? tree.value : [];
157
+ const description = info?.description ? `
2
158
 
3
- > ${C.description}`:"",L=C?.stargazers_count==null?"":` ⭐ ${C.stargazers_count}`,S=C?.language?` · ${C.language}`:"",$=`# ${j}/${q}${L}${S}${_}
159
+ > ${info.description}` : "";
160
+ const stars = info?.stargazers_count == null ? "" : ` ⭐ ${info.stargazers_count}`;
161
+ const language = info?.language ? ` · ${info.language}` : "";
162
+ let content = `# ${owner}/${repo}${stars}${language}${description}
4
163
 
5
- `;if(V)$+=V.slice(0,6000);else $+=`[No README found]
164
+ `;
165
+ if (readmeText) {
166
+ content += readmeText.slice(0, 6000);
167
+ } else {
168
+ content += `[No README found]
6
169
 
7
170
  Files:
8
- ${Z.map((k)=>` ${k.type==="dir"?"\uD83D\uDCC1":"\uD83D\uDCC4"} ${k.path}`).join(`
9
- `)}`;return{ok:!0,title:`${j}/${q}`,content:$,tree:Z.slice(0,30)}}if(K==="blob"&&Q){let C;if(!N||N==="HEAD")try{C=(await Y(`/repos/${j}/${q}`)).default_branch}catch{C=void 0}let W=await O(j,q,N,Q,1e4,C);if(W===null)return{ok:!1,error:`File not found: ${Q}`};return{ok:!0,title:`${j}/${q}: ${Q}`,content:W}}if(K==="tree"&&Q){let C;if(!N||N==="HEAD")try{C=(await Y(`/repos/${j}/${q}`)).default_branch}catch{C=void 0}let W=await v(j,q,N||"HEAD",Q,C),X=W.map((V)=>` ${V.type==="dir"?"\uD83D\uDCC1":"\uD83D\uDCC4"} ${V.path}`).join(`
10
- `);return{ok:!0,title:`${j}/${q}/${Q}`,content:`[Directory: ${Q}]
171
+ ${treeItems.map((t) => ` ${t.type === "dir" ? "\uD83D\uDCC1" : "\uD83D\uDCC4"} ${t.path}`).join(`
172
+ `)}`;
173
+ }
174
+ return {
175
+ ok: true,
176
+ title: `${owner}/${repo}`,
177
+ content,
178
+ tree: treeItems.slice(0, 30)
179
+ };
180
+ }
181
+ if (type === "blob" && path) {
182
+ let defaultBranch;
183
+ if (!ref || ref === "HEAD") {
184
+ try {
185
+ const repoInfo = await apiGet(`/repos/${owner}/${repo}`);
186
+ defaultBranch = repoInfo.default_branch;
187
+ } catch {
188
+ defaultBranch = undefined;
189
+ }
190
+ }
191
+ const content = await fetchRawFile(owner, repo, ref, path, 1e4, defaultBranch);
192
+ if (content === null) {
193
+ return { ok: false, error: `File not found: ${path}` };
194
+ }
195
+ return {
196
+ ok: true,
197
+ title: `${owner}/${repo}: ${path}`,
198
+ content
199
+ };
200
+ }
201
+ if (type === "tree" && path) {
202
+ let defaultBranch;
203
+ if (!ref || ref === "HEAD") {
204
+ try {
205
+ const repoInfo = await apiGet(`/repos/${owner}/${repo}`);
206
+ defaultBranch = repoInfo.default_branch;
207
+ } catch {
208
+ defaultBranch = undefined;
209
+ }
210
+ }
211
+ const treeItems = await fetchTree(owner, repo, ref || "HEAD", path, defaultBranch);
212
+ const listing = treeItems.map((t) => ` ${t.type === "dir" ? "\uD83D\uDCC1" : "\uD83D\uDCC4"} ${t.path}`).join(`
213
+ `);
214
+ return {
215
+ ok: true,
216
+ title: `${owner}/${repo}/${path}`,
217
+ content: `[Directory: ${path}]
11
218
 
12
219
  Files:
13
- ${X}`,tree:W}}return{ok:!1,error:"Unsupported GitHub URL type"}}catch(C){return{ok:!1,error:C.message}}}export{F as parseGitHubUrl,U as fetchGitHubContent};
220
+ ${listing}`,
221
+ tree: treeItems
222
+ };
223
+ }
224
+ return { ok: false, error: "Unsupported GitHub URL type" };
225
+ } catch (err) {
226
+ return { ok: false, error: err.message };
227
+ }
228
+ }
@@ -1,23 +1,154 @@
1
- var X={"user-agent":"GreedySearch/1.0 (Research Bot)",accept:"application/json"};function Z(b){try{let v=new URL(b),B=v.hostname.toLowerCase();if(!(B==="reddit.com"||B.endsWith(".reddit.com")))return null;let q=v.pathname;if(q.match(/^\/(u|user)\/[^/]+\/?$/i))return{type:"user",cleanUrl:O(b)};if(q.match(/^\/r\/[^/]+\/comments\/[^/]+/i))return{type:"post",cleanUrl:O(b)};return null}catch{return null}}function O(b){try{let v=new URL(b);return`${v.protocol}//${v.hostname}${v.pathname}`}catch{return b}}async function $(b,v=8000){let B=Date.now();try{let q=b.replace(/\/+$/,"")+".json",F=new AbortController,G=setTimeout(()=>F.abort(),15000),M=await fetch(q,{headers:X,signal:F.signal});if(clearTimeout(G),!M.ok)throw Error(`Reddit API ${M.status}`);let K=await M.json();if(!Array.isArray(K)||K.length<1)throw Error("Invalid Reddit API response structure");let V=K[0],W=K[1],J=V?.data?.children?.[0]?.data;if(!J)throw Error("No post data in Reddit response");let N=Y(J,W,v);return{ok:!0,url:b,finalUrl:b,status:200,contentType:"text/markdown",lastModified:"",title:J.title||"Reddit Post",byline:`u/${J.author}`,siteName:`r/${J.subreddit}`,lang:"en",publishedTime:new Date(J.created_utc*1000).toISOString(),excerpt:J.selftext?.slice(0,300).replace(/\n/g," ")||"",markdown:N,contentLength:N.length,needsBrowser:!1,duration:Date.now()-B}}catch(q){return{ok:!1,url:b,finalUrl:b,status:0,error:`Reddit fetch failed: ${q.message}`,needsBrowser:!1,duration:Date.now()-B}}}function Y(b,v,B){let q="";if(q+=`# ${b.title}
1
+ const REDDIT_HEADERS = {
2
+ "user-agent": "GreedySearch/1.0 (Research Bot)",
3
+ accept: "application/json"
4
+ };
5
+ export function parseRedditUrl(url) {
6
+ try {
7
+ const parsed = new URL(url);
8
+ const hostname = parsed.hostname.toLowerCase();
9
+ if (!(hostname === "reddit.com" || hostname.endsWith(".reddit.com"))) {
10
+ return null;
11
+ }
12
+ const pathname = parsed.pathname;
13
+ if (pathname.match(/^\/(u|user)\/[^/]+\/?$/i)) {
14
+ return { type: "user", cleanUrl: normalizeRedditUrl(url) };
15
+ }
16
+ if (pathname.match(/^\/r\/[^/]+\/comments\/[^/]+/i)) {
17
+ return { type: "post", cleanUrl: normalizeRedditUrl(url) };
18
+ }
19
+ return null;
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+ function normalizeRedditUrl(url) {
25
+ try {
26
+ const parsed = new URL(url);
27
+ return `${parsed.protocol}//${parsed.hostname}${parsed.pathname}`;
28
+ } catch {
29
+ return url;
30
+ }
31
+ }
32
+ export async function fetchRedditContent(url, maxChars = 8000) {
33
+ const start = Date.now();
34
+ try {
35
+ const jsonUrl = url.replace(/\/+$/, "") + ".json";
36
+ const controller = new AbortController;
37
+ const timeoutId = setTimeout(() => controller.abort(), 15000);
38
+ const response = await fetch(jsonUrl, {
39
+ headers: REDDIT_HEADERS,
40
+ signal: controller.signal
41
+ });
42
+ clearTimeout(timeoutId);
43
+ if (!response.ok) {
44
+ throw new Error(`Reddit API ${response.status}`);
45
+ }
46
+ const data = await response.json();
47
+ if (!Array.isArray(data) || data.length < 1) {
48
+ throw new Error("Invalid Reddit API response structure");
49
+ }
50
+ const postListing = data[0];
51
+ const commentsListing = data[1];
52
+ const post = postListing?.data?.children?.[0]?.data;
53
+ if (!post) {
54
+ throw new Error("No post data in Reddit response");
55
+ }
56
+ const markdown = formatRedditPost(post, commentsListing, maxChars);
57
+ return {
58
+ ok: true,
59
+ url,
60
+ finalUrl: url,
61
+ status: 200,
62
+ contentType: "text/markdown",
63
+ lastModified: "",
64
+ title: post.title || "Reddit Post",
65
+ byline: `u/${post.author}`,
66
+ siteName: `r/${post.subreddit}`,
67
+ lang: "en",
68
+ publishedTime: new Date(post.created_utc * 1000).toISOString(),
69
+ excerpt: post.selftext?.slice(0, 300).replace(/\n/g, " ") || "",
70
+ markdown,
71
+ contentLength: markdown.length,
72
+ needsBrowser: false,
73
+ duration: Date.now() - start
74
+ };
75
+ } catch (error) {
76
+ return {
77
+ ok: false,
78
+ url,
79
+ finalUrl: url,
80
+ status: 0,
81
+ error: `Reddit fetch failed: ${error.message}`,
82
+ needsBrowser: false,
83
+ duration: Date.now() - start
84
+ };
85
+ }
86
+ }
87
+ function formatRedditPost(post, commentsListing, maxChars) {
88
+ let md = "";
89
+ md += `# ${post.title}
2
90
 
3
- `,q+=`**Subreddit:** r/${b.subreddit} | **Author:** u/${b.author} | **Score:** ${b.score}
91
+ `;
92
+ md += `**Subreddit:** r/${post.subreddit} | **Author:** u/${post.author} | **Score:** ${post.score}
4
93
 
5
- `,b.selftext)q+=b.selftext,q+=`
94
+ `;
95
+ if (post.selftext) {
96
+ md += post.selftext;
97
+ md += `
6
98
 
7
- `;else if(b.url)try{let F=new URL(b.url).hostname.toLowerCase();if(F!=="reddit.com"&&!F.endsWith(".reddit.com"))q+=`**Link:** ${b.url}
99
+ `;
100
+ } else if (post.url) {
101
+ try {
102
+ const postUrlHost = new URL(post.url).hostname.toLowerCase();
103
+ if (postUrlHost !== "reddit.com" && !postUrlHost.endsWith(".reddit.com")) {
104
+ md += `**Link:** ${post.url}
8
105
 
9
- `}catch{q+=`**Link:** ${b.url}
106
+ `;
107
+ }
108
+ } catch {
109
+ md += `**Link:** ${post.url}
10
110
 
11
- `}if(v?.data?.children?.length>0){q+=`---
111
+ `;
112
+ }
113
+ }
114
+ if (commentsListing?.data?.children?.length > 0) {
115
+ md += `---
12
116
 
13
117
  ## Comments
14
118
 
15
- `;let F=v.data.children.filter((G)=>G.kind==="t1").slice(0,10);for(let G of F)q+=Q(G.data,0),q+=`
16
- `}if(q.length>B)q=q.slice(0,B).trim()+`
119
+ `;
120
+ const comments = commentsListing.data.children.filter((c) => c.kind === "t1").slice(0, 10);
121
+ for (const comment of comments) {
122
+ md += formatComment(comment.data, 0);
123
+ md += `
124
+ `;
125
+ }
126
+ }
127
+ if (md.length > maxChars) {
128
+ md = md.slice(0, maxChars).trim() + `
17
129
 
18
- ... (truncated)`;return q}function Q(b,v){if(!b||b.body==="[deleted]"||b.body==="[removed]")return"";let B="> ".repeat(v),q="";if(q+=`${B}**u/${b.author}** (${b.score} pts)
19
- `,q+=`${B}${b.body.replaceAll(`
20
- `,`
21
- `+B)}
22
- `,v<3&&b.replies?.data?.children){let F=b.replies.data.children.filter((G)=>G.kind==="t1");for(let G of F.slice(0,5))q+=`
23
- `+Q(G.data,v+1)}return q}export{Z as parseRedditUrl,$ as fetchRedditContent};
130
+ ... (truncated)`;
131
+ }
132
+ return md;
133
+ }
134
+ function formatComment(comment, depth) {
135
+ if (!comment || comment.body === "[deleted]" || comment.body === "[removed]") {
136
+ return "";
137
+ }
138
+ const indent = "> ".repeat(depth);
139
+ let md = "";
140
+ md += `${indent}**u/${comment.author}** (${comment.score} pts)
141
+ `;
142
+ md += `${indent}${comment.body.replaceAll(`
143
+ `, `
144
+ ` + indent)}
145
+ `;
146
+ if (depth < 3 && comment.replies?.data?.children) {
147
+ const replies = comment.replies.data.children.filter((r) => r.kind === "t1");
148
+ for (const reply of replies.slice(0, 5)) {
149
+ md += `
150
+ ` + formatComment(reply.data, depth + 1);
151
+ }
152
+ }
153
+ return md;
154
+ }