@este.systems/dsc 0.1.0
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/LICENSE +21 -0
- package/README.md +408 -0
- package/bin/dsc.mjs +29 -0
- package/dist/agent.js +259 -0
- package/dist/agent.js.map +1 -0
- package/dist/api.js +333 -0
- package/dist/api.js.map +1 -0
- package/dist/approval.js +79 -0
- package/dist/approval.js.map +1 -0
- package/dist/audit.js +26 -0
- package/dist/audit.js.map +1 -0
- package/dist/compact.js +100 -0
- package/dist/compact.js.map +1 -0
- package/dist/history.js +212 -0
- package/dist/history.js.map +1 -0
- package/dist/index.js +830 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.js +543 -0
- package/dist/markdown.js.map +1 -0
- package/dist/prompt.js +44 -0
- package/dist/prompt.js.map +1 -0
- package/dist/repl_history.js +55 -0
- package/dist/repl_history.js.map +1 -0
- package/dist/search.js +215 -0
- package/dist/search.js.map +1 -0
- package/dist/tools.js +670 -0
- package/dist/tools.js.map +1 -0
- package/dist/ui.js +165 -0
- package/dist/ui.js.map +1 -0
- package/package.json +57 -0
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// last touched: 2026-06-08
|
|
2
|
+
// Static prefix — kept byte-identical across calls so DeepSeek's prompt-prefix
|
|
3
|
+
// cache hits it on every turn. Dynamic per-turn context is appended via
|
|
4
|
+
// buildSystemPrompt below; only those tail bytes are billed at miss rate.
|
|
5
|
+
export const SYSTEM_PROMPT = `You are dsc, a CLI coding assistant powered by DeepSeek. You operate inside the user's terminal in their working directory and can edit files and run shell commands via tools.
|
|
6
|
+
|
|
7
|
+
Available tools:
|
|
8
|
+
- read_file(path, offset?, limit?): read a file (returns lines with 1-based numbers).
|
|
9
|
+
- write_file(path, content): create a new file or fully overwrite an existing one. Prefer edit_file for changes to existing files.
|
|
10
|
+
- edit_file(path, old_string, new_string, replace_all?): replace an exact substring. old_string must be unique in the file unless replace_all is true. Include enough surrounding context to make old_string unique.
|
|
11
|
+
- bash(command, description?, timeout_ms?): run a shell command. Output is captured and truncated if very long.
|
|
12
|
+
- grep(pattern, path?, glob?, case_insensitive?): regex search across files (ripgrep when available, grep -rn fallback). Prefer this over running grep through bash for finding code.
|
|
13
|
+
- glob(pattern, path?): list paths matching a glob (e.g. 'src/**/*.ts'). Prefer this over running find through bash.
|
|
14
|
+
- web_fetch(url): GET a URL; HTML is stripped to text.
|
|
15
|
+
- web_search(query, count?, freshness?): search the web. Returns numbered title/url/snippet entries. Pair with web_fetch to read the full content of any specific URL.
|
|
16
|
+
|
|
17
|
+
Rules:
|
|
18
|
+
- Edits and shell commands may require user approval before they run; if a tool call returns "rejected by user", do not retry the same call. Ask the user what to change instead.
|
|
19
|
+
- Be concise. Skip preamble like "Sure, I'll do that" — just do it and summarize briefly at the end.
|
|
20
|
+
- When the user asks a question, answer; when they ask for changes, make them. Don't editorialize.
|
|
21
|
+
- Use relative paths in tool calls when natural; absolute paths are fine too.
|
|
22
|
+
- After making changes, give a one or two sentence summary of what you did. Don't repeat file contents the user can see.
|
|
23
|
+
- Do not invent files or APIs you haven't read. Use read_file or bash (grep/ls/find) to check.
|
|
24
|
+
- Do not prefix your replies with role labels like "A:", "Assistant:", "AI:", "Bot:". Just answer directly.`;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the static system prompt followed by a small dynamic block with the
|
|
27
|
+
* cwd, date, status line, and (when /compact has been run) a summary of older
|
|
28
|
+
* turns. Static text comes first so the prefix-cache prefix stays stable; only
|
|
29
|
+
* the trailing bytes vary per turn.
|
|
30
|
+
*/
|
|
31
|
+
export function buildSystemPrompt(ctx) {
|
|
32
|
+
const lines = [SYSTEM_PROMPT, "", "Current context:"];
|
|
33
|
+
lines.push(`- cwd: ${ctx.cwd}`);
|
|
34
|
+
lines.push(`- date: ${ctx.date.toISOString().slice(0, 10)}`);
|
|
35
|
+
if (ctx.statusLine)
|
|
36
|
+
lines.push(`- status: ${ctx.statusLine}`);
|
|
37
|
+
if (ctx.summary) {
|
|
38
|
+
lines.push("");
|
|
39
|
+
lines.push("Previously in this session (summary of compacted turns):");
|
|
40
|
+
lines.push(ctx.summary);
|
|
41
|
+
}
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,+EAA+E;AAC/E,wEAAwE;AACxE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;4GAmB+E,CAAC;AAW7G;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAkB;IAClD,MAAM,KAAK,GAAa,CAAC,aAAa,EAAE,EAAE,EAAE,kBAAkB,CAAC,CAAC;IAChE,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAChC,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7D,IAAI,GAAG,CAAC,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC9D,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;QACvE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
const MAX_LINES = 1000;
|
|
5
|
+
export function historyFilePath() {
|
|
6
|
+
const xdg = process.env.XDG_STATE_HOME;
|
|
7
|
+
const base = xdg && xdg.length ? xdg : path.join(homedir(), ".local", "state");
|
|
8
|
+
return path.join(base, "dsc", "history");
|
|
9
|
+
}
|
|
10
|
+
// Returns oldest-first, capped at MAX_LINES.
|
|
11
|
+
export async function load() {
|
|
12
|
+
let text;
|
|
13
|
+
try {
|
|
14
|
+
text = await fs.readFile(historyFilePath(), "utf8");
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const lines = text.split("\n").filter((l) => l.length > 0);
|
|
20
|
+
return lines.slice(-MAX_LINES);
|
|
21
|
+
}
|
|
22
|
+
let _lastAppended;
|
|
23
|
+
// Append a single line. Skips empty and consecutive duplicates.
|
|
24
|
+
export async function append(line) {
|
|
25
|
+
const trimmed = line.replace(/\n+$/g, "");
|
|
26
|
+
if (!trimmed)
|
|
27
|
+
return;
|
|
28
|
+
if (trimmed === _lastAppended)
|
|
29
|
+
return;
|
|
30
|
+
const file = historyFilePath();
|
|
31
|
+
try {
|
|
32
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
33
|
+
await fs.appendFile(file, trimmed + "\n", "utf8");
|
|
34
|
+
_lastAppended = trimmed;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// best-effort; ignore failures
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Truncate the file to the most recent MAX_LINES entries. Best-effort.
|
|
41
|
+
export async function compact() {
|
|
42
|
+
const lines = await load();
|
|
43
|
+
if (lines.length < MAX_LINES)
|
|
44
|
+
return;
|
|
45
|
+
const file = historyFilePath();
|
|
46
|
+
try {
|
|
47
|
+
const tmp = file + ".tmp";
|
|
48
|
+
await fs.writeFile(tmp, lines.join("\n") + "\n", "utf8");
|
|
49
|
+
await fs.rename(tmp, file);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// ignore
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=repl_history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repl_history.js","sourceRoot":"","sources":["../src/repl_history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACvC,MAAM,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC;AACjC,CAAC;AAED,IAAI,aAAiC,CAAC;AAEtC,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,IAAI,OAAO,KAAK,aAAa;QAAE,OAAO;IACtC,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;QAClD,aAAa,GAAG,OAAO,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS;QAAE,OAAO;IACrC,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC;QAC1B,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;QACzD,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC"}
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { getConfig, configPath } from "./api.js";
|
|
2
|
+
export class SearchError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
constructor(message, status) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.name = "SearchError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
// ─── Provider selection ──────────────────────────────────────────────────────
|
|
11
|
+
// Resolution order:
|
|
12
|
+
// 1. DSC_SEARCH_PROVIDER env var
|
|
13
|
+
// 2. config.search.provider in the deepseek.json
|
|
14
|
+
// 3. "ddg" (no key required)
|
|
15
|
+
export function getProvider() {
|
|
16
|
+
const env = process.env.DSC_SEARCH_PROVIDER?.trim().toLowerCase();
|
|
17
|
+
if (env === "brave" || env === "tavily" || env === "ddg")
|
|
18
|
+
return env;
|
|
19
|
+
const cfg = getSearchConfig();
|
|
20
|
+
const fromCfg = typeof cfg?.provider === "string" ? cfg.provider.toLowerCase() : null;
|
|
21
|
+
if (fromCfg === "brave" || fromCfg === "tavily" || fromCfg === "ddg")
|
|
22
|
+
return fromCfg;
|
|
23
|
+
return "ddg";
|
|
24
|
+
}
|
|
25
|
+
// Per-provider key resolution:
|
|
26
|
+
// 1. {PROVIDER}_API_KEY env var (BRAVE_API_KEY, TAVILY_API_KEY)
|
|
27
|
+
// 2. config.search.<provider>.api_key
|
|
28
|
+
// 3. null
|
|
29
|
+
export function getProviderKey(p) {
|
|
30
|
+
const envName = p === "brave" ? "BRAVE_API_KEY" : p === "tavily" ? "TAVILY_API_KEY" : null;
|
|
31
|
+
if (envName) {
|
|
32
|
+
const v = process.env[envName];
|
|
33
|
+
if (v)
|
|
34
|
+
return v;
|
|
35
|
+
}
|
|
36
|
+
const cfg = getSearchConfig();
|
|
37
|
+
const sub = cfg?.[p];
|
|
38
|
+
if (sub && typeof sub === "object") {
|
|
39
|
+
const key = sub.api_key;
|
|
40
|
+
if (typeof key === "string" && key.length)
|
|
41
|
+
return key;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function getSearchConfig() {
|
|
46
|
+
const obj = getConfig();
|
|
47
|
+
if (!obj)
|
|
48
|
+
return null;
|
|
49
|
+
const s = obj.search;
|
|
50
|
+
if (s && typeof s === "object")
|
|
51
|
+
return s;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
// ─── Top-level dispatch ──────────────────────────────────────────────────────
|
|
55
|
+
export async function search(opts) {
|
|
56
|
+
const provider = getProvider();
|
|
57
|
+
switch (provider) {
|
|
58
|
+
case "brave":
|
|
59
|
+
return searchBrave(opts);
|
|
60
|
+
case "tavily":
|
|
61
|
+
return searchTavily(opts);
|
|
62
|
+
case "ddg":
|
|
63
|
+
return searchDuckDuckGo(opts);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// ─── Brave Search (api.search.brave.com) ────────────────────────────────────
|
|
67
|
+
const BRAVE_FRESHNESS = {
|
|
68
|
+
day: "pd",
|
|
69
|
+
week: "pw",
|
|
70
|
+
month: "pm",
|
|
71
|
+
year: "py",
|
|
72
|
+
};
|
|
73
|
+
async function searchBrave(opts) {
|
|
74
|
+
const key = getProviderKey("brave");
|
|
75
|
+
if (!key)
|
|
76
|
+
throw new SearchError(missingKeyMsg("brave", "BRAVE_API_KEY"));
|
|
77
|
+
const params = new URLSearchParams({
|
|
78
|
+
q: opts.query,
|
|
79
|
+
count: String(Math.min(20, Math.max(1, opts.count ?? 5))),
|
|
80
|
+
safesearch: "moderate",
|
|
81
|
+
});
|
|
82
|
+
if (opts.freshness)
|
|
83
|
+
params.set("freshness", BRAVE_FRESHNESS[opts.freshness]);
|
|
84
|
+
const res = await fetch(`https://api.search.brave.com/res/v1/web/search?${params}`, {
|
|
85
|
+
headers: {
|
|
86
|
+
Accept: "application/json",
|
|
87
|
+
"Accept-Encoding": "gzip",
|
|
88
|
+
"X-Subscription-Token": key,
|
|
89
|
+
},
|
|
90
|
+
signal: opts.signal,
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
throw new SearchError(`Brave Search HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`, res.status);
|
|
94
|
+
}
|
|
95
|
+
const data = (await res.json());
|
|
96
|
+
return (data.web?.results ?? []).map((r) => ({
|
|
97
|
+
title: r.title,
|
|
98
|
+
url: r.url,
|
|
99
|
+
snippet: stripHtmlInline(r.description),
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
// ─── Tavily (api.tavily.com) ────────────────────────────────────────────────
|
|
103
|
+
async function searchTavily(opts) {
|
|
104
|
+
const key = getProviderKey("tavily");
|
|
105
|
+
if (!key)
|
|
106
|
+
throw new SearchError(missingKeyMsg("tavily", "TAVILY_API_KEY"));
|
|
107
|
+
const body = {
|
|
108
|
+
api_key: key,
|
|
109
|
+
query: opts.query,
|
|
110
|
+
max_results: Math.min(20, Math.max(1, opts.count ?? 5)),
|
|
111
|
+
search_depth: "basic",
|
|
112
|
+
};
|
|
113
|
+
if (opts.freshness)
|
|
114
|
+
body.days = freshnessToDays(opts.freshness);
|
|
115
|
+
const res = await fetch("https://api.tavily.com/search", {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers: { "Content-Type": "application/json" },
|
|
118
|
+
body: JSON.stringify(body),
|
|
119
|
+
signal: opts.signal,
|
|
120
|
+
});
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
throw new SearchError(`Tavily HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`, res.status);
|
|
123
|
+
}
|
|
124
|
+
const data = (await res.json());
|
|
125
|
+
return (data.results ?? []).map((r) => ({
|
|
126
|
+
title: r.title,
|
|
127
|
+
url: r.url,
|
|
128
|
+
snippet: r.content,
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
function freshnessToDays(f) {
|
|
132
|
+
return f === "day" ? 1 : f === "week" ? 7 : f === "month" ? 30 : 365;
|
|
133
|
+
}
|
|
134
|
+
// ─── DuckDuckGo (HTML scrape, no key) ───────────────────────────────────────
|
|
135
|
+
async function searchDuckDuckGo(opts) {
|
|
136
|
+
// The "lite" endpoint returns markup that's stable enough to regex.
|
|
137
|
+
// Brittle by nature — DDG can change the layout at any time and break this.
|
|
138
|
+
const params = new URLSearchParams({ q: opts.query });
|
|
139
|
+
const res = await fetch(`https://lite.duckduckgo.com/lite/?${params}`, {
|
|
140
|
+
headers: {
|
|
141
|
+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) dsc/0.1",
|
|
142
|
+
},
|
|
143
|
+
signal: opts.signal,
|
|
144
|
+
});
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
throw new SearchError(`DuckDuckGo HTTP ${res.status}`, res.status);
|
|
147
|
+
}
|
|
148
|
+
const html = await res.text();
|
|
149
|
+
const results = parseDdgLite(html);
|
|
150
|
+
const limit = Math.min(20, Math.max(1, opts.count ?? 5));
|
|
151
|
+
return results.slice(0, limit);
|
|
152
|
+
}
|
|
153
|
+
function parseDdgLite(html) {
|
|
154
|
+
const out = [];
|
|
155
|
+
// Result link: <a class="result-link" href="...">Title</a>
|
|
156
|
+
// Snippet: <td class="result-snippet">…</td>
|
|
157
|
+
const linkRe = /<a[^>]*class="[^"]*result-link[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
158
|
+
const snippetRe = /<td[^>]*class="[^"]*result-snippet[^"]*"[^>]*>([\s\S]*?)<\/td>/gi;
|
|
159
|
+
const links = [];
|
|
160
|
+
let m;
|
|
161
|
+
while ((m = linkRe.exec(html)) !== null) {
|
|
162
|
+
links.push({ url: cleanDdgUrl(decodeEntities(m[1])), title: stripHtmlInline(m[2]) });
|
|
163
|
+
}
|
|
164
|
+
const snippets = [];
|
|
165
|
+
while ((m = snippetRe.exec(html)) !== null) {
|
|
166
|
+
snippets.push(stripHtmlInline(m[1]));
|
|
167
|
+
}
|
|
168
|
+
for (let i = 0; i < links.length; i++) {
|
|
169
|
+
out.push({
|
|
170
|
+
title: links[i].title,
|
|
171
|
+
url: links[i].url,
|
|
172
|
+
snippet: snippets[i] ?? "",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
function cleanDdgUrl(url) {
|
|
178
|
+
// DDG wraps results as /l/?uddg=<encoded>. Unwrap when present.
|
|
179
|
+
const m = url.match(/[?&]uddg=([^&]+)/);
|
|
180
|
+
if (m) {
|
|
181
|
+
try {
|
|
182
|
+
return decodeURIComponent(m[1]);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return url;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return url;
|
|
189
|
+
}
|
|
190
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
191
|
+
function stripHtmlInline(html) {
|
|
192
|
+
return decodeEntities(html.replace(/<[^>]+>/g, "")).replace(/\s+/g, " ").trim();
|
|
193
|
+
}
|
|
194
|
+
function decodeEntities(s) {
|
|
195
|
+
return s
|
|
196
|
+
.replace(/&/g, "&")
|
|
197
|
+
.replace(/</g, "<")
|
|
198
|
+
.replace(/>/g, ">")
|
|
199
|
+
.replace(/"/g, '"')
|
|
200
|
+
.replace(/'/g, "'")
|
|
201
|
+
.replace(/'/g, "'")
|
|
202
|
+
.replace(/ /g, " ");
|
|
203
|
+
}
|
|
204
|
+
function missingKeyMsg(provider, envName) {
|
|
205
|
+
return (`${provider} search needs an API key. Either set ${envName}, or add ` +
|
|
206
|
+
`\`{"search": {"${provider}": {"api_key": "..."}}}\` to ${configPath()}.`);
|
|
207
|
+
}
|
|
208
|
+
export function formatResults(results) {
|
|
209
|
+
if (!results.length)
|
|
210
|
+
return "(no results)";
|
|
211
|
+
return results
|
|
212
|
+
.map((r, i) => `${i + 1}. ${r.title}\n ${r.url}\n ${r.snippet || "(no snippet)"}`)
|
|
213
|
+
.join("\n\n");
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
|