Preserve abandoned smoke-script refactor WIP

This commit is contained in:
2026-07-25 08:49:44 -07:00
parent 8de9658b8c
commit dec7e49c1e
9 changed files with 2 additions and 565 deletions

View File

@@ -126,7 +126,6 @@ bin/context-kit status
bin/context-kit doctor bin/context-kit doctor
bin/context-kit install claude bin/context-kit install claude
bin/context-kit install opencode bin/context-kit install opencode
bin/context-kit redaction-check
``` ```
MCP entrypoints: MCP entrypoints:

View File

@@ -83,7 +83,6 @@ Usage:
context-kit build Build MCP images context-kit build Build MCP images
context-kit status Show services, images, sources, and the docs HTTP endpoint context-kit status Show services, images, sources, and the docs HTTP endpoint
context-kit doctor Check Docker, services, images, sources, and HTTP endpoints context-kit doctor Check Docker, services, images, sources, and HTTP endpoints
context-kit redaction-check Scan this repo for local paths and secret patterns
MCP server commands: MCP server commands:
context-kit web-search Per-call SearXNG-backed web-search MCP (stdio) context-kit web-search Per-call SearXNG-backed web-search MCP (stdio)
@@ -168,17 +167,6 @@ warn() {
printf 'warn: %s\n' "$*" >&2 printf 'warn: %s\n' "$*" >&2
} }
print_relative_paths() {
local path
while IFS= read -r path; do
[[ -n "${path}" ]] || continue
if [[ "${path}" == "${ROOT}/"* ]]; then
path="${path#"${ROOT}/"}"
fi
printf '%s\n' "${path}"
done
}
json_escape() { json_escape() {
local s="$1" local s="$1"
s="${s//\\/\\\\}" s="${s//\\/\\\\}"
@@ -550,50 +538,6 @@ cmd_install() {
esac esac
} }
cmd_redaction_check() {
local bad=0
local scan_paths=("${ROOT}")
if [[ "$#" -gt 0 ]]; then
scan_paths=("$@")
fi
local local_path_terms='/(home|Users)/[^/[:space:]]+|/data/(projects|opencode-mcp)[^[:space:]]*|[A-Za-z]:\\Users\\[^\\[:space:]]+'
local secret_terms='AKIA[0-9A-Z]{16}|BEGIN (RSA |OPENSSH |EC |DSA )?PRIVATE KEY|xox[baprs]-|sk-[A-Za-z0-9_-]{20,}|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|gitea_[A-Za-z0-9_-]{20,}'
# Scan only what would be published: skip .git plus everything .gitignore
# excludes by convention (local .env files, caches, logs).
local grep_opts=(
-RInE
--exclude-dir=.git
--exclude-dir=.cache
--exclude-dir=tmp
--exclude=.env
--exclude=.env.local
--exclude=*.log
)
local matches
matches="$(grep "${grep_opts[@]}" --files-with-matches "${local_path_terms}" "${scan_paths[@]}" 2>/dev/null || true)"
if [[ -n "${matches}" ]]; then
printf 'fail redaction-check found local path patterns in:\n' >&2
printf '%s\n' "${matches}" | print_relative_paths | sed 's/^/- /' >&2
bad=1
fi
matches="$(grep "${grep_opts[@]}" --files-with-matches "${secret_terms}" "${scan_paths[@]}" 2>/dev/null || true)"
if [[ -n "${matches}" ]]; then
printf 'fail redaction-check found secret-like patterns in:\n' >&2
printf '%s\n' "${matches}" | print_relative_paths | sed 's/^/- /' >&2
bad=1
fi
if [[ "${bad}" -eq 0 ]]; then
printf 'pass redaction-check found no local absolute paths or common secret patterns\n'
else
printf 'fail redaction-check found blocked content\n' >&2
fi
return "${bad}"
}
case "${1:-}" in case "${1:-}" in
start) shift; cmd_start "$@" ;; start) shift; cmd_start "$@" ;;
stop) shift; cmd_stop "$@" ;; stop) shift; cmd_stop "$@" ;;
@@ -605,7 +549,6 @@ case "${1:-}" in
docs) shift; cmd_docs "$@" ;; docs) shift; cmd_docs "$@" ;;
repomix) shift; cmd_repomix "$@" ;; repomix) shift; cmd_repomix "$@" ;;
install) shift; cmd_install "$@" ;; install) shift; cmd_install "$@" ;;
redaction-check) shift; cmd_redaction_check "$@" ;;
-h|--help|help|"") usage ;; -h|--help|help|"") usage ;;
*) usage >&2; exit 64 ;; *) usage >&2; exit 64 ;;
esac esac

View File

@@ -17,8 +17,7 @@ Tracked public files:
- `config/sources.*.txt`: optional public source profiles. - `config/sources.*.txt`: optional public source profiles.
- `snippets/`: portable assistant config snippets that use `context-kit` on - `snippets/`: portable assistant config snippets that use `context-kit` on
`PATH`. `PATH`.
- `compose.yml`, `docker/`, `bin/`, and `scripts/`: generic runtime and release - `compose.yml`, `docker/`, `bin/`, and `scripts/check`: generic runtime logic.
logic.
Ignored or external local files: Ignored or external local files:
@@ -50,8 +49,7 @@ host.
## User-Facing Variables ## User-Facing Variables
Only the variables below are part of the public configuration surface. Other The variables below are the supported public configuration surface.
`CONTEXT_KIT_*` variables used by scripts are release/test hooks and may change.
| Variable | Default | Purpose | | Variable | Default | Purpose |
|---|---|---| |---|---|---|

View File

@@ -9,19 +9,6 @@ bin/context-kit doctor
This checks Docker, Compose, images, the Docker network, SearXNG health, docs This checks Docker, Compose, images, the Docker network, SearXNG health, docs
HTTP readiness, and docs source configuration. HTTP readiness, and docs source configuration.
For release-grade MCP protocol checks, run:
```sh
scripts/release-check
```
Live provider checks are opt-in because search engines, remote docs, and model
downloads can fail independently of this repo:
```sh
CONTEXT_KIT_LIVE_CHECKS=1 scripts/release-check
```
## SearXNG Is Not Responding ## SearXNG Is Not Responding
Start it: Start it:

View File

@@ -1,188 +0,0 @@
import { spawn, spawnSync } from "node:child_process";
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
export function textFrom(result) {
return (result.content || [])
.filter(part => part.type === "text")
.map(part => part.text)
.join("\n");
}
export function requireToolSuccess(name, result) {
if (result?.isError) {
const payload = textFrom(result) || JSON.stringify(result);
throw new Error(`${name} returned an error: ${payload.slice(0, 500)}`);
}
return result;
}
export async function runSmoke({ usage, tmpPrefix, timeoutMs, clientInfo, scenario }) {
const command = process.argv[2];
const args = process.argv.slice(3);
if (!command) {
throw new Error(usage);
}
const client = new McpSmokeClient({ command, args, tmpPrefix });
const timeout = setTimeout(async () => {
await client.stop();
console.error(`MCP smoke timed out. stderr: ${client.stderrTail(2000)}`);
process.exit(1);
}, timeoutMs);
try {
await client.initialize(clientInfo);
const result = await scenario(client);
clearTimeout(timeout);
await client.stop();
console.log(JSON.stringify(result, null, 2));
} catch (error) {
clearTimeout(timeout);
await client.stop();
console.error(error.message);
const stderr = client.stderrTail(4000);
if (stderr) console.error(stderr);
process.exit(1);
}
}
class McpSmokeClient {
constructor({ command, args, tmpPrefix }) {
this.tmpDir = mkdtempSync(join(tmpdir(), tmpPrefix));
this.cidFile = join(this.tmpDir, "container.cid");
this.nextId = 1;
this.pending = new Map();
this.stdoutBuffer = "";
this.stderrBuffer = "";
this.exited = false;
this.child = spawn(command, args, {
cwd: new URL("..", import.meta.url).pathname,
env: { ...process.env, CONTEXT_KIT_DOCKER_CIDFILE: this.cidFile },
stdio: ["pipe", "pipe", "pipe"]
});
this.child.once("exit", (code, signal) => {
this.exited = true;
if (this.pending.size > 0) {
const error = new Error(`MCP child exited before responding (code=${code}, signal=${signal}). stderr: ${this.stderrTail(2000)}`);
for (const { reject } of this.pending.values()) reject(error);
this.pending.clear();
}
});
this.child.stderr.on("data", chunk => {
this.stderrBuffer += chunk.toString();
});
this.child.stdout.on("data", chunk => this.handleStdout(chunk));
}
stderrTail(length) {
return this.stderrBuffer.slice(-length);
}
handleStdout(chunk) {
this.stdoutBuffer += chunk.toString();
let newline;
while ((newline = this.stdoutBuffer.indexOf("\n")) >= 0) {
const line = this.stdoutBuffer.slice(0, newline).trim();
this.stdoutBuffer = this.stdoutBuffer.slice(newline + 1);
if (!line) continue;
let message;
try {
message = JSON.parse(line);
} catch {
continue;
}
if (message.id && this.pending.has(message.id)) {
const { resolve, reject } = this.pending.get(message.id);
this.pending.delete(message.id);
if (message.error) reject(new Error(JSON.stringify(message.error)));
else resolve(message.result);
}
}
}
request(method, params = {}) {
if (this.exited) {
return Promise.reject(new Error(`MCP child already exited. stderr: ${this.stderrTail(2000)}`));
}
const id = this.nextId++;
this.child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`);
return new Promise((resolve, reject) => this.pending.set(id, { resolve, reject }));
}
notify(method, params = {}) {
this.child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}\n`);
}
async initialize(clientInfo) {
await this.request("initialize", {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo
});
this.notify("notifications/initialized");
}
async tools() {
const listed = await this.request("tools/list");
return new Set((listed.tools || []).map(tool => tool.name));
}
async requireTools(names) {
const toolNames = await this.tools();
for (const name of names) {
if (!toolNames.has(name)) throw new Error(`missing tool: ${name}`);
}
return toolNames;
}
callTool(name, args = {}) {
return this.request("tools/call", { name, arguments: args });
}
stopContainer() {
if (!existsSync(this.cidFile)) return;
const containerId = readFileSync(this.cidFile, "utf8").trim();
if (!containerId) return;
spawnSync("docker", ["stop", containerId], { stdio: "ignore" });
}
stop() {
return new Promise(resolve => {
if (this.exited) {
this.stopContainer();
rmSync(this.tmpDir, { recursive: true, force: true });
resolve();
return;
}
let stopTimer;
let termTimer;
let killTimer;
this.child.once("exit", () => {
this.stopContainer();
clearTimeout(stopTimer);
clearTimeout(termTimer);
clearTimeout(killTimer);
rmSync(this.tmpDir, { recursive: true, force: true });
resolve();
});
stopTimer = setTimeout(() => this.stopContainer(), 1000);
termTimer = setTimeout(() => {
if (!this.exited) this.child.kill("SIGTERM");
}, 3000);
killTimer = setTimeout(() => {
if (!this.exited) this.child.kill("SIGKILL");
}, 6000);
this.child.stdin.end();
});
}
}

View File

@@ -1,156 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT}"
tmp_dir="$(mktemp -d)"
pick_port() {
python - <<'PY'
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(("127.0.0.1", 0))
print(sock.getsockname()[1])
PY
}
release_id="release-$$"
export CONTEXT_KIT_COMPOSE_PROJECT="context-kit-${release_id}"
export CONTEXT_KIT_DATA_DIR="${tmp_dir}/data"
export CONTEXT_KIT_PROJECT_DIR="${ROOT}"
export CONTEXT_KIT_SEARXNG_PORT="$(pick_port)"
export CONTEXT_KIT_DOCS_PORT="$(pick_port)"
export CONTEXT_KIT_DOCS_LOCAL_SOURCES_DIR="${tmp_dir}/local-sources"
export CONTEXT_KIT_DOCS_LOCAL_SOURCES_PORT="8769"
local_sources_profile="${tmp_dir}/sources.local.txt"
export CONTEXT_KIT_DOCS_SOURCES="config/sources.default.txt ${local_sources_profile}"
export CONTEXT_KIT_LOCAL_SOURCE_SMOKE_URL="http://127.0.0.1:${CONTEXT_KIT_DOCS_LOCAL_SOURCES_PORT}/release-fixture/llms.txt"
export CONTEXT_KIT_WEB_SEARCH_IMAGE="context-kit/web-search-mcp:${release_id}"
export CONTEXT_KIT_DOCS_IMAGE="context-kit/docs-mcp:${release_id}"
mkdir -p "${CONTEXT_KIT_DOCS_LOCAL_SOURCES_DIR}/release-fixture"
printf '%s\n' \
'# Release Check Local Source' \
'' \
'> Exercises machine-local llms.txt serving and refresh behavior.' \
'' \
'## MCP' \
'' \
'- [Model Context Protocol](https://modelcontextprotocol.io/)' \
> "${CONTEXT_KIT_DOCS_LOCAL_SOURCES_DIR}/release-fixture/llms.txt"
printf '%s\n' "${CONTEXT_KIT_LOCAL_SOURCE_SMOKE_URL}" > "${local_sources_profile}"
cleanup() {
docker compose -p "${CONTEXT_KIT_COMPOSE_PROJECT}" -f compose.yml down -v --remove-orphans >/dev/null 2>&1 || true
docker image rm "${CONTEXT_KIT_WEB_SEARCH_IMAGE}" "${CONTEXT_KIT_DOCS_IMAGE}" >/dev/null 2>&1 || true
rm -rf "${tmp_dir}"
}
trap cleanup EXIT
check_node() {
local file
for file in "$@"; do
node --check "${file}"
done
}
assert_redaction_check_does_not_disclose_matches() {
local fixture="${tmp_dir}/redaction-fixture.txt"
local output="${tmp_dir}/redaction-output.txt"
local blocked_path="/data/proj""ects/context-kit-private-fixture"
printf 'blocked=%s\n' "${blocked_path}" > "${fixture}"
if bin/context-kit redaction-check "${fixture}" >"${output}" 2>&1; then
printf 'redaction-check test unexpectedly passed\n' >&2
return 1
fi
if grep -F "${blocked_path}" "${output}" >/dev/null; then
printf 'redaction-check disclosed matched content\n' >&2
return 1
fi
}
assert_web_search_image() {
docker run --rm --entrypoint node \
-e EXPECTED_MAX_BYTES="${CONTEXT_KIT_WEB_SEARCH_MAX_BYTES:-52428800}" \
"${CONTEXT_KIT_WEB_SEARCH_IMAGE}" \
-e '
const fs = require("node:fs");
const expected = Number(process.env.EXPECTED_MAX_BYTES) || 0;
const actual = Number(process.env.MAX_BYTES) || 0;
if (process.getuid && process.getuid() === 0) process.exit(1);
if (actual !== expected) process.exit(1);
const serverPath = "/usr/local/lib/node_modules/@zhafron/mcp-web-search/dist/src/server.js";
const server = fs.readFileSync(serverPath, "utf8");
if (!server.includes("max_download_bytes: z.number().int().min(1).max(MAX_BYTES).optional()")) process.exit(1);
const bingPath = "/usr/local/lib/node_modules/@zhafron/mcp-web-search/dist/src/providers/bing.js";
const bing = fs.readFileSync(bingPath, "utf8");
if (!bing.includes("Context Kit override for @zhafron/mcp-web-search 1.3.0")) process.exit(1);
if (!bing.includes("waitForSelector")) process.exit(1);
if (!bing.includes("decodeBingRedirect")) process.exit(1);
' >/dev/null
docker run --rm --entrypoint /usr/bin/test \
"${CONTEXT_KIT_WEB_SEARCH_IMAGE}" \
-x "${CONTEXT_KIT_WEB_SEARCH_CHROME_PATH:-/usr/bin/chromium}"
}
git diff --check HEAD
git show --check --format= HEAD >/dev/null
git ls-files --cached --error-unmatch \
docker/web-search/patch-mcp-web-search.mjs \
docker/web-search/overrides/bing.js \
docker/docs/constraints.txt \
scripts/mcp-smoke-client.mjs \
scripts/smoke-web-search.mjs \
scripts/smoke-docs.mjs \
scripts/smoke-repomix.mjs \
scripts/release-check >/dev/null
bash -n bin/context-kit
bash -n scripts/release-check
sh -n docker/docs/entrypoint.sh
check_node docker/web-search/patch-mcp-web-search.mjs docker/web-search/overrides/bing.js scripts/mcp-smoke-client.mjs scripts/smoke-web-search.mjs scripts/smoke-docs.mjs scripts/smoke-repomix.mjs
node -e 'const fs=require("node:fs"); JSON.parse(fs.readFileSync("snippets/opencode.json", "utf8")); JSON.parse(fs.readFileSync("snippets/claude.mcp.json", "utf8"));'
CONTEXT_KIT_DOCS_HTTP_URL="http://127.0.0.1:8776/mcp" bin/context-kit install opencode > "${tmp_dir}/opencode-default.json"
cmp -s snippets/opencode.json "${tmp_dir}/opencode-default.json" || {
printf 'snippets/opencode.json differs from bin/context-kit install opencode output\n' >&2
diff -u snippets/opencode.json "${tmp_dir}/opencode-default.json" >&2 || true
exit 1
}
CONTEXT_KIT_DOCS_HTTP_URL="http://127.0.0.1:8776/mcp" bin/context-kit install claude > "${tmp_dir}/claude-default.json"
cmp -s snippets/claude.mcp.json "${tmp_dir}/claude-default.json" || {
printf 'snippets/claude.mcp.json differs from bin/context-kit install claude output\n' >&2
diff -u snippets/claude.mcp.json "${tmp_dir}/claude-default.json" >&2 || true
exit 1
}
bin/context-kit install opencode > "${tmp_dir}/opencode.json"
bin/context-kit install opencode --absolute > "${tmp_dir}/opencode-absolute.json"
bin/context-kit install claude > "${tmp_dir}/claude.json"
bin/context-kit install claude --absolute > "${tmp_dir}/claude-absolute.json"
node -e 'const fs=require("node:fs"); for (const file of process.argv.slice(1)) JSON.parse(fs.readFileSync(file, "utf8"));' \
"${tmp_dir}/opencode.json" \
"${tmp_dir}/opencode-absolute.json" \
"${tmp_dir}/claude.json" \
"${tmp_dir}/claude-absolute.json"
bin/context-kit redaction-check "${tmp_dir}/opencode.json" "${tmp_dir}/claude.json"
assert_redaction_check_does_not_disclose_matches
bin/context-kit redaction-check
docker compose -p "${CONTEXT_KIT_COMPOSE_PROJECT}" -f compose.yml config >/dev/null
if env -u HOME -u CONTEXT_KIT_DATA_DIR -u CONTEXT_KIT_DOCS_LOCAL_SOURCES_DIR docker compose --env-file /dev/null -p context-kit-release-home-check -f compose.yml config >"${tmp_dir}/compose-no-home.out" 2>"${tmp_dir}/compose-no-home.err"; then
printf 'compose config unexpectedly succeeded without HOME or CONTEXT_KIT_DATA_DIR\n' >&2
exit 1
fi
CONTEXT_KIT_DATA_DIR="${tmp_dir}/compose-data" env -u HOME docker compose --env-file /dev/null -p context-kit-release-home-check -f compose.yml config >/dev/null
bin/context-kit build
assert_web_search_image
bin/context-kit restart
bin/context-kit doctor
node scripts/smoke-web-search.mjs bin/context-kit web-search
node scripts/smoke-docs.mjs bin/context-kit docs
node scripts/smoke-repomix.mjs bin/context-kit repomix
printf 'pass release-check\n'

View File

@@ -1,53 +0,0 @@
import { requireToolSuccess, runSmoke } from "./mcp-smoke-client.mjs";
const live = process.env.CONTEXT_KIT_LIVE_CHECKS === "1";
const localSourceSmokeUrl = process.env.CONTEXT_KIT_LOCAL_SOURCE_SMOKE_URL;
runSmoke({
usage: "usage: node scripts/smoke-docs.mjs <command> [args...]",
tmpPrefix: "context-kit-docs-smoke-",
timeoutMs: 300000,
clientInfo: { name: "context-kit-docs-smoke", version: "0.0.0" },
scenario: async client => {
const toolNames = await client.requireTools(["docs_query", "docs_sources"]);
const sources = requireToolSuccess("docs_sources", await client.callTool("docs_sources"));
if (!Array.isArray(sources?.structuredContent?.result)) {
const sourcesText = JSON.stringify(sources);
throw new Error(`docs_sources returned unexpected payload: ${sourcesText.slice(0, 500)}`);
}
const result = {
tools: Array.from(toolNames).sort(),
docs_sources: "pass"
};
if (localSourceSmokeUrl) {
requireToolSuccess("docs_refresh/local_source_first", await client.callTool("docs_refresh", {
source: localSourceSmokeUrl
}));
requireToolSuccess("docs_refresh/local_source_second", await client.callTool("docs_refresh", {
source: localSourceSmokeUrl
}));
result.local_source_refresh = "pass";
}
if (live) {
const query = requireToolSuccess("docs_query", await client.callTool("docs_query", {
query: "Model Context Protocol documentation",
limit: 3,
auto_retrieve: true,
auto_retrieve_threshold: 0.1,
auto_retrieve_limit: 1,
max_bytes: 12000
}));
const queryText = JSON.stringify(query);
if (!queryText.includes("search_results") && !queryText.includes("Model Context Protocol")) {
throw new Error(`docs_query returned unexpected payload: ${queryText.slice(0, 500)}`);
}
result.docs_query = "pass";
}
return result;
}
});

View File

@@ -1,30 +0,0 @@
import { requireToolSuccess, runSmoke, textFrom } from "./mcp-smoke-client.mjs";
runSmoke({
usage: "usage: node scripts/smoke-repomix.mjs <command> [args...]",
tmpPrefix: "context-kit-repomix-smoke-",
timeoutMs: 120000,
clientInfo: { name: "context-kit-repomix-smoke", version: "0.0.0" },
scenario: async client => {
const toolNames = await client.requireTools(["pack_codebase"]);
const pack = requireToolSuccess("pack_codebase", await client.callTool("pack_codebase", {
directory: process.cwd(),
compress: true,
includePatterns: "README.md,snippets/opencode.json",
ignorePatterns: "",
topFilesLength: 2,
style: "xml"
}));
const packText = textFrom(pack) || JSON.stringify(pack);
if (!packText.includes("Successfully packed codebase") && !packText.includes("outputId")) {
throw new Error(`pack_codebase returned unexpected payload: ${packText.slice(0, 500)}`);
}
return {
tools: Array.from(toolNames).sort(),
pack_codebase: "pass"
};
}
});

View File

@@ -1,63 +0,0 @@
import { requireToolSuccess, runSmoke, textFrom } from "./mcp-smoke-client.mjs";
const live = process.env.CONTEXT_KIT_LIVE_CHECKS === "1";
runSmoke({
usage: "usage: node scripts/smoke-web-search.mjs <command> [args...]",
tmpPrefix: "context-kit-web-search-smoke-",
timeoutMs: 120000,
clientInfo: { name: "context-kit-web-search-smoke", version: "0.0.0" },
scenario: async client => {
const toolNames = await client.requireTools(["search_web", "fetch_url"]);
const localResult = await client.callTool("fetch_url", {
url: "http://127.0.0.1:1/",
max_download_bytes: 52428800
});
const localBlocked = Boolean(localResult.isError) && textFrom(localResult).includes("Blocked localhost/private URL");
if (!localBlocked) throw new Error("localhost/private URL was not blocked as expected");
const result = {
tools: Array.from(toolNames).sort(),
localhost_guard: "pass"
};
if (live) {
const searxng = textFrom(requireToolSuccess("search_web/searxng", await client.callTool("search_web", {
q: "Model Context Protocol",
limit: 2,
provider: "searxng"
})));
if (!searxng.includes("Model")) throw new Error(`SearXNG smoke returned unexpected text: ${searxng.slice(0, 500)}`);
const bing = textFrom(requireToolSuccess("search_web/bing", await client.callTool("search_web", {
q: "Model Context Protocol",
limit: 2,
provider: "bing"
})));
if (!bing.includes("Model")) throw new Error(`Bing smoke returned unexpected text: ${bing.slice(0, 500)}`);
const fetch = textFrom(requireToolSuccess("fetch_url/http", await client.callTool("fetch_url", {
url: "https://example.com/",
format: "markdown",
max_download_bytes: 52428800
})));
if (!fetch.includes("Example Domain")) throw new Error(`fetch smoke returned unexpected text: ${fetch.slice(0, 500)}`);
const browserFetch = textFrom(requireToolSuccess("fetch_url/browser", await client.callTool("fetch_url", {
url: "https://example.com/",
format: "markdown",
engine: "browser",
max_download_bytes: 52428800
})));
if (!browserFetch.includes("Example Domain")) throw new Error(`browser fetch smoke returned unexpected text: ${browserFetch.slice(0, 500)}`);
result.searxng = "pass";
result.bing = "pass";
result.fetch_url = "pass";
result.fetch_url_browser_engine_currently_http = "pass";
}
return result;
}
});