@robzilla1738/agentswarm 0.3.0 → 0.6.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/README.md +51 -11
- package/dist/agent.js +18 -2
- package/dist/cli.js +39 -8
- package/dist/config.js +62 -6
- package/dist/crawltools.js +247 -0
- package/dist/deepseek.js +125 -10
- package/dist/executor.js +993 -144
- package/dist/hub.js +85 -6
- package/dist/journal.js +61 -11
- package/dist/memory.js +84 -0
- package/dist/pdftext.js +211 -0
- package/dist/prompts.js +124 -23
- package/dist/report.js +289 -0
- package/dist/run.js +15 -2
- package/dist/sandbox.js +11 -0
- package/dist/searchcore.js +244 -0
- package/dist/state.js +85 -3
- package/dist/tools.js +392 -25
- package/dist/util.js +85 -0
- package/dist/webtools.js +327 -66
- package/package.json +3 -2
- package/ui/out/404/index.html +1 -1
- package/ui/out/404.html +1 -1
- package/ui/out/_next/static/chunks/532-35122e93f37719b9.js +1 -0
- package/ui/out/_next/static/chunks/677-721ce1c8b7a6a317.js +1 -0
- package/ui/out/_next/static/chunks/app/page-dc9f6744d203e76c.js +1 -0
- package/ui/out/_next/static/chunks/app/run/page-3674e103981703a2.js +1 -0
- package/ui/out/_next/static/chunks/app/settings/page-41a5d8ba43ecfd4a.js +1 -0
- package/ui/out/_next/static/css/d95c2ba395730031.css +3 -0
- package/ui/out/fonts/PlanetKosmos.ttf +0 -0
- package/ui/out/index.html +1 -1
- package/ui/out/index.txt +3 -3
- package/ui/out/run/index.html +1 -1
- package/ui/out/run/index.txt +3 -3
- package/ui/out/settings/index.html +1 -1
- package/ui/out/settings/index.txt +3 -3
- package/ui/out/_next/static/chunks/383-289a866b246b41cc.js +0 -1
- package/ui/out/_next/static/chunks/619-ba102abea3e3d0e4.js +0 -1
- package/ui/out/_next/static/chunks/677-7ab85a6f38c3a235.js +0 -1
- package/ui/out/_next/static/chunks/app/page-0fda5b8e77d90b84.js +0 -1
- package/ui/out/_next/static/chunks/app/run/page-07aab6b1224c3c8c.js +0 -1
- package/ui/out/_next/static/chunks/app/settings/page-528482d468d84cfa.js +0 -1
- package/ui/out/_next/static/css/e2c82b53bf4519e8.css +0 -3
- /package/ui/out/_next/static/{Rm5Fhkds2-wIOnVlME55J → 7_pihFubDGD40BCy2ynlr}/_buildManifest.js +0 -0
- /package/ui/out/_next/static/{Rm5Fhkds2-wIOnVlME55J → 7_pihFubDGD40BCy2ynlr}/_ssgManifest.js +0 -0
package/dist/webtools.js
CHANGED
|
@@ -1,95 +1,218 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.webSearch = webSearch;
|
|
4
|
+
exports._resetEngineCooldowns = _resetEngineCooldowns;
|
|
5
|
+
exports.tinyfishSearch = tinyfishSearch;
|
|
6
|
+
exports.ddgSearch = ddgSearch;
|
|
7
|
+
exports.bingSearch = bingSearch;
|
|
8
|
+
exports.arxivSearch = arxivSearch;
|
|
9
|
+
exports.crossrefSearch = crossrefSearch;
|
|
10
|
+
exports.parseBingHtml = parseBingHtml;
|
|
4
11
|
exports.fetchUrl = fetchUrl;
|
|
5
|
-
const
|
|
12
|
+
const crawltools_1 = require("./crawltools");
|
|
13
|
+
const pdftext_1 = require("./pdftext");
|
|
14
|
+
const searchcore_1 = require("./searchcore");
|
|
6
15
|
const util_1 = require("./util");
|
|
7
16
|
const UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36 agentswarm/0.1";
|
|
17
|
+
/** How many of the merged pool get fetched for passage extraction in deep mode. */
|
|
18
|
+
const DEEP_FETCH = 12;
|
|
19
|
+
/** Quotable passages kept per fetched page. */
|
|
20
|
+
const DEEP_PASSAGES = 3;
|
|
8
21
|
/**
|
|
9
|
-
* Web search
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
22
|
+
* Web search: fan out across every available engine in parallel (DuckDuckGo +
|
|
23
|
+
* Bing scraping, plus TinyFish when keyed). In `deep` mode it also fans the
|
|
24
|
+
* query into a few complementary phrasings — so one call sweeps queries ×
|
|
25
|
+
* engines into a much larger pool — then quality-ranks and dedupes by
|
|
26
|
+
* canonical URL, fetches the top pages concurrently for quotable passages,
|
|
27
|
+
* and re-ranks by content quality. Ranking/passage algorithms live in
|
|
28
|
+
* searchcore.ts.
|
|
14
29
|
*/
|
|
15
|
-
async function webSearch(cfg, query, count, signal, deep = false, warn) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
async function webSearch(cfg, query, count, signal, deep = false, warn, _retried = false) {
|
|
31
|
+
// Deep searches widen recall by issuing complementary phrasings; the fast
|
|
32
|
+
// path stays a single query so an agent's tool loop isn't slowed.
|
|
33
|
+
const queries = deep ? (0, searchcore_1.expandQueries)(query) : [query];
|
|
34
|
+
const perEngine = Math.min(count, 15);
|
|
35
|
+
const engineCalls = [];
|
|
36
|
+
for (const q of queries) {
|
|
37
|
+
if (cfg.searchBackend === "tinyfish" && cfg.tinyfishApiKey) {
|
|
38
|
+
engineCalls.push(tinyfishSearch(cfg, q, perEngine, signal));
|
|
22
39
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
else {
|
|
41
|
+
engineCalls.push(ddgSearch(q, perEngine, signal), bingSearch(q, perEngine, signal));
|
|
42
|
+
if (cfg.searchBackend === "auto" && cfg.tinyfishApiKey) {
|
|
43
|
+
engineCalls.push(tinyfishSearch(cfg, q, perEngine, signal));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Scholarly questions also sweep the keyless academic APIs (deep mode only).
|
|
48
|
+
if (deep && (0, searchcore_1.looksAcademic)(query)) {
|
|
49
|
+
engineCalls.push(arxivSearch(query, perEngine, signal), crossrefSearch(query, perEngine, signal));
|
|
50
|
+
}
|
|
51
|
+
const settled = await Promise.allSettled(engineCalls);
|
|
52
|
+
const candidates = settled.flatMap((s) => (s.status === "fulfilled" ? s.value : []));
|
|
53
|
+
if (!candidates.length) {
|
|
54
|
+
const firstErr = settled.find((s) => s.status === "rejected");
|
|
55
|
+
if (firstErr && settled.every((s) => s.status === "rejected"))
|
|
56
|
+
throw firstErr.reason;
|
|
57
|
+
// Engines answered but nothing parsed/matched: one retry with a
|
|
58
|
+
// simplified phrasing before giving up.
|
|
59
|
+
if (!_retried) {
|
|
60
|
+
const alt = (0, searchcore_1.reformulate)(query);
|
|
61
|
+
if (alt) {
|
|
62
|
+
warn?.(`no results for "${query}" — retrying as "${alt}"`);
|
|
63
|
+
return webSearch(cfg, alt, count, signal, deep, warn, true);
|
|
32
64
|
}
|
|
33
|
-
/* fall through */
|
|
34
65
|
}
|
|
66
|
+
if (firstErr)
|
|
67
|
+
throw firstErr.reason;
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
const failures = settled.filter((s) => s.status === "rejected").length;
|
|
71
|
+
if (failures) {
|
|
72
|
+
warn?.(`${failures}/${settled.length} search engine calls failed; results come from the rest`);
|
|
73
|
+
}
|
|
74
|
+
const merged = (0, searchcore_1.mergeCandidates)(candidates, count);
|
|
75
|
+
if (!deep || !merged.length) {
|
|
76
|
+
return merged.map((c) => ({ title: c.title, url: c.url, snippet: c.snippet, date: c.date }));
|
|
35
77
|
}
|
|
36
|
-
|
|
78
|
+
return deepEnrich(merged, query, signal);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Deep mode: fetch the top pages concurrently, extract readable text and
|
|
82
|
+
* quotable passages, and re-rank by composite content quality. Pages that
|
|
83
|
+
* fail to fetch keep their snippet-level hit.
|
|
84
|
+
*/
|
|
85
|
+
async function deepEnrich(merged, query, signal) {
|
|
86
|
+
const terms = (0, searchcore_1.queryTerms)(query);
|
|
87
|
+
const toFetch = merged.slice(0, Math.min(merged.length, DEEP_FETCH));
|
|
88
|
+
const pages = await Promise.allSettled(toFetch.map((c) => fetchReadable(c.url, signal)));
|
|
89
|
+
const scoredHits = merged.map((c, i) => {
|
|
90
|
+
const base = { title: c.title, url: c.url, snippet: c.snippet, date: c.date };
|
|
91
|
+
const page = i < pages.length && pages[i].status === "fulfilled" ? pages[i].value : "";
|
|
92
|
+
if (!page)
|
|
93
|
+
return { hit: base, score: (0, searchcore_1.rankBonus)(i + 1, 20) };
|
|
94
|
+
const passages = (0, searchcore_1.selectPassages)(page, query);
|
|
95
|
+
const date = (0, searchcore_1.detectDate)(page.slice(0, 4000)) || c.date;
|
|
96
|
+
let domain = "";
|
|
37
97
|
try {
|
|
38
|
-
|
|
98
|
+
domain = new URL(c.url).hostname.replace(/^www\./, "");
|
|
39
99
|
}
|
|
40
100
|
catch {
|
|
41
|
-
/*
|
|
101
|
+
/* keep empty */
|
|
42
102
|
}
|
|
43
|
-
|
|
44
|
-
|
|
103
|
+
const score = (0, searchcore_1.scorePage)({ url: c.url, domain, title: c.title, text: page, date }, terms) +
|
|
104
|
+
(0, searchcore_1.passageBonus)(passages) +
|
|
105
|
+
(0, searchcore_1.rankBonus)(i + 1, 10);
|
|
106
|
+
return {
|
|
107
|
+
hit: { ...base, date, passages: passages.slice(0, DEEP_PASSAGES).map((p) => p.text) },
|
|
108
|
+
score,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
return scoredHits.sort((a, b) => b.score - a.score).map((s) => s.hit);
|
|
45
112
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
113
|
+
/** Fetch one page as cleaned readable text for passage extraction (~3000 words max). */
|
|
114
|
+
async function fetchReadable(url, signal) {
|
|
115
|
+
// GitHub repo pages bury the README in app markup — the raw file is cleaner.
|
|
116
|
+
const gh = /^https?:\/\/github\.com\/([^/]+)\/([^/#?]+)\/?$/.exec(url);
|
|
117
|
+
if (gh) {
|
|
118
|
+
for (const branch of ["main", "master"]) {
|
|
119
|
+
try {
|
|
120
|
+
const res = await fetch(`https://raw.githubusercontent.com/${gh[1]}/${gh[2]}/${branch}/README.md`, {
|
|
121
|
+
headers: { "user-agent": UA },
|
|
122
|
+
signal: mergeSignal(20_000, signal),
|
|
123
|
+
});
|
|
124
|
+
if (res.ok)
|
|
125
|
+
return clip(await res.text());
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
/* fall through */
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const res = await fetch(url, {
|
|
133
|
+
headers: { "user-agent": UA, accept: "text/html,application/pdf,text/*;q=0.9,*/*;q=0.5" },
|
|
134
|
+
signal: mergeSignal(20_000, signal),
|
|
135
|
+
redirect: "follow",
|
|
52
136
|
});
|
|
137
|
+
if (!res.ok)
|
|
138
|
+
throw new Error(`HTTP ${res.status}`);
|
|
139
|
+
const ctype = res.headers.get("content-type") || "";
|
|
140
|
+
if (/application\/pdf/i.test(ctype)) {
|
|
141
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
142
|
+
const pdf = buf.length <= 20_000_000 ? (0, pdftext_1.extractPdfText)(buf) : null;
|
|
143
|
+
if (!pdf)
|
|
144
|
+
throw new Error("pdf with no extractable text");
|
|
145
|
+
return clip(pdf.text);
|
|
146
|
+
}
|
|
147
|
+
if (!/text\/|html|xml|json/i.test(ctype))
|
|
148
|
+
throw new Error(`not textual: ${ctype}`);
|
|
149
|
+
const body = decodeBody(Buffer.from(await res.arrayBuffer()), ctype);
|
|
150
|
+
const text = /html/i.test(ctype) ? (0, util_1.htmlToText)(body) : body;
|
|
151
|
+
return clip(text);
|
|
53
152
|
}
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
153
|
+
function clip(text) {
|
|
154
|
+
const words = text.replace(/\s+/g, " ").trim().split(" ");
|
|
155
|
+
return words.slice(0, 3000).join(" ");
|
|
156
|
+
}
|
|
157
|
+
function mergeSignal(timeoutMs, signal) {
|
|
158
|
+
const t = AbortSignal.timeout(timeoutMs);
|
|
159
|
+
if (!signal)
|
|
160
|
+
return t;
|
|
161
|
+
return typeof AbortSignal.any === "function" ? AbortSignal.any([t, signal]) : signal;
|
|
162
|
+
}
|
|
163
|
+
// ---------------------------------------------------------------- engines
|
|
164
|
+
/**
|
|
165
|
+
* Per-engine rate-limit cooldowns: an engine that answers 429/403/503 sits
|
|
166
|
+
* out (60s, or the server's retry-after up to 120s) instead of getting
|
|
167
|
+
* hammered into a long block mid-research. A tiny retry-after (≤5s) is
|
|
168
|
+
* honored once in-call.
|
|
169
|
+
*/
|
|
170
|
+
const engineCooldown = new Map();
|
|
171
|
+
/** Test hook. */
|
|
172
|
+
function _resetEngineCooldowns() {
|
|
173
|
+
engineCooldown.clear();
|
|
174
|
+
}
|
|
175
|
+
async function engineFetch(engine, url, init, signal) {
|
|
176
|
+
const until = engineCooldown.get(engine) ?? 0;
|
|
177
|
+
if (until > Date.now()) {
|
|
178
|
+
throw new Error(`${engine} is cooling down after a rate limit (${Math.ceil((until - Date.now()) / 1000)}s left)`);
|
|
179
|
+
}
|
|
180
|
+
for (let attempt = 0;; attempt++) {
|
|
181
|
+
const res = await fetch(url, { ...init, signal: mergeSignal(20_000, signal) });
|
|
182
|
+
if (![429, 403, 503].includes(res.status))
|
|
183
|
+
return res;
|
|
184
|
+
const retryAfter = Number(res.headers.get("retry-after"));
|
|
185
|
+
if (attempt === 0 && Number.isFinite(retryAfter) && retryAfter > 0 && retryAfter <= 5) {
|
|
186
|
+
await new Promise((r) => setTimeout(r, retryAfter * 1000));
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const ms = Number.isFinite(retryAfter) && retryAfter > 0 ? Math.min(retryAfter, 120) * 1000 : 60_000;
|
|
190
|
+
engineCooldown.set(engine, Date.now() + ms);
|
|
191
|
+
throw new Error(`${engine} rate-limited (HTTP ${res.status}); cooling down ${Math.round(ms / 1000)}s`);
|
|
192
|
+
}
|
|
72
193
|
}
|
|
73
194
|
async function tinyfishSearch(cfg, query, count, signal) {
|
|
74
195
|
const url = `https://api.search.tinyfish.ai?query=${encodeURIComponent(query)}`;
|
|
75
196
|
const res = await fetch(url, {
|
|
76
197
|
headers: { "X-API-Key": cfg.tinyfishApiKey },
|
|
77
|
-
signal: signal
|
|
198
|
+
signal: mergeSignal(20_000, signal),
|
|
78
199
|
});
|
|
79
200
|
if (!res.ok)
|
|
80
201
|
throw new Error(`tinyfish search ${res.status}`);
|
|
81
202
|
const data = await res.json();
|
|
82
|
-
return (data.results || []).slice(0, count).map((r) => ({
|
|
203
|
+
return (data.results || []).slice(0, count).map((r, i) => ({
|
|
83
204
|
title: r.title || r.site_name || r.url,
|
|
84
205
|
url: r.url,
|
|
85
206
|
snippet: r.snippet || "",
|
|
207
|
+
rank: i + 1,
|
|
208
|
+
engine: "tinyfish",
|
|
86
209
|
}));
|
|
87
210
|
}
|
|
88
211
|
/**
|
|
89
212
|
* DuckDuckGo serves two scrape-friendly endpoints with different markup.
|
|
90
213
|
* A parse miss on one falls through to the other, so a DDG layout change has
|
|
91
|
-
* to break both before
|
|
92
|
-
* style and either attribute order (groups 1+2 or 3+4).
|
|
214
|
+
* to break both before the engine goes dark. Link regexes tolerate either
|
|
215
|
+
* quote style and either attribute order (groups 1+2 or 3+4).
|
|
93
216
|
*/
|
|
94
217
|
const DDG_ENDPOINTS = [
|
|
95
218
|
{
|
|
@@ -106,10 +229,9 @@ async function ddgSearch(query, count, signal) {
|
|
|
106
229
|
let reachedAny = false;
|
|
107
230
|
for (const ep of DDG_ENDPOINTS) {
|
|
108
231
|
try {
|
|
109
|
-
const res = await
|
|
232
|
+
const res = await engineFetch("duckduckgo", ep.url + encodeURIComponent(query), {
|
|
110
233
|
headers: { "user-agent": UA },
|
|
111
|
-
|
|
112
|
-
});
|
|
234
|
+
}, signal);
|
|
113
235
|
if (!res.ok)
|
|
114
236
|
throw new Error(`search failed: HTTP ${res.status}`);
|
|
115
237
|
reachedAny = true;
|
|
@@ -147,19 +269,125 @@ function parseDdgHtml(html, count, linkRe) {
|
|
|
147
269
|
continue;
|
|
148
270
|
if (url.includes("duckduckgo.com/y.js"))
|
|
149
271
|
continue; // ads
|
|
150
|
-
|
|
272
|
+
const snippet = snippets[hits.length] || "";
|
|
273
|
+
hits.push({ title, url, snippet, rank: hits.length + 1, engine: "ddg", date: (0, searchcore_1.detectDate)(snippet) });
|
|
151
274
|
}
|
|
152
275
|
return hits;
|
|
153
276
|
}
|
|
277
|
+
/** Bing's HTML results page: each hit is an <li class="b_algo"> with an <h2><a> link. */
|
|
278
|
+
async function bingSearch(query, count, signal) {
|
|
279
|
+
const res = await engineFetch("bing", `https://www.bing.com/search?q=${encodeURIComponent(query)}`, {
|
|
280
|
+
headers: { "user-agent": UA, "accept-language": "en-US,en;q=0.9" },
|
|
281
|
+
}, signal);
|
|
282
|
+
if (!res.ok)
|
|
283
|
+
throw new Error(`bing search ${res.status}`);
|
|
284
|
+
return parseBingHtml(await res.text(), count);
|
|
285
|
+
}
|
|
286
|
+
// ---------------------------------------------------------------- academic engines (keyless)
|
|
287
|
+
/** arXiv's Atom API — preprints with abstracts, no key needed. */
|
|
288
|
+
async function arxivSearch(query, count, signal) {
|
|
289
|
+
const url = `https://export.arxiv.org/api/query?search_query=all:${encodeURIComponent(query)}&max_results=${Math.min(count, 15)}`;
|
|
290
|
+
const res = await engineFetch("arxiv", url, { headers: { "user-agent": UA } }, signal);
|
|
291
|
+
if (!res.ok)
|
|
292
|
+
throw new Error(`arxiv search ${res.status}`);
|
|
293
|
+
const xml = await res.text();
|
|
294
|
+
const out = [];
|
|
295
|
+
for (const entry of xml.split(/<entry>/).slice(1)) {
|
|
296
|
+
if (out.length >= count)
|
|
297
|
+
break;
|
|
298
|
+
const title = strip((/<title>([\s\S]*?)<\/title>/.exec(entry) || [])[1] || "");
|
|
299
|
+
const id = ((/<id>([\s\S]*?)<\/id>/.exec(entry) || [])[1] || "").trim();
|
|
300
|
+
const summary = strip((/<summary>([\s\S]*?)<\/summary>/.exec(entry) || [])[1] || "");
|
|
301
|
+
const published = (/<published>(\d{4}-\d{2}-\d{2})/.exec(entry) || [])[1];
|
|
302
|
+
if (!id || !title || !/^https?:\/\//.test(id))
|
|
303
|
+
continue;
|
|
304
|
+
out.push({ title, url: id, snippet: summary.slice(0, 300), rank: out.length + 1, engine: "arxiv", date: published });
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
/** Crossref's works API — journal/conference metadata with DOIs, no key needed. */
|
|
309
|
+
async function crossrefSearch(query, count, signal) {
|
|
310
|
+
const url = `https://api.crossref.org/works?query=${encodeURIComponent(query)}&rows=${Math.min(count, 15)}&select=title,DOI,abstract,issued,container-title`;
|
|
311
|
+
const res = await engineFetch("crossref", url, { headers: { "user-agent": UA } }, signal);
|
|
312
|
+
if (!res.ok)
|
|
313
|
+
throw new Error(`crossref search ${res.status}`);
|
|
314
|
+
const data = await res.json();
|
|
315
|
+
const out = [];
|
|
316
|
+
for (const it of data?.message?.items ?? []) {
|
|
317
|
+
if (out.length >= count)
|
|
318
|
+
break;
|
|
319
|
+
const title = strip(String(Array.isArray(it.title) ? it.title[0] ?? "" : it.title ?? ""));
|
|
320
|
+
if (!title || !it.DOI)
|
|
321
|
+
continue;
|
|
322
|
+
const date = Array.isArray(it.issued?.["date-parts"]?.[0]) ? it.issued["date-parts"][0].join("-") : undefined;
|
|
323
|
+
const venue = Array.isArray(it["container-title"]) ? it["container-title"][0] : "";
|
|
324
|
+
const snippet = (strip(String(it.abstract ?? "")) || venue || "").slice(0, 300);
|
|
325
|
+
out.push({ title, url: `https://doi.org/${it.DOI}`, snippet, rank: out.length + 1, engine: "crossref", date });
|
|
326
|
+
}
|
|
327
|
+
return out;
|
|
328
|
+
}
|
|
329
|
+
function parseBingHtml(html, count) {
|
|
330
|
+
const hits = [];
|
|
331
|
+
const blocks = html.split(/<li class="b_algo[^"]*"/i).slice(1);
|
|
332
|
+
for (const block of blocks) {
|
|
333
|
+
if (hits.length >= count)
|
|
334
|
+
break;
|
|
335
|
+
const link = /<h2[^>]*>\s*<a[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i.exec(block);
|
|
336
|
+
if (!link)
|
|
337
|
+
continue;
|
|
338
|
+
const url = decodeBingUrl((0, util_1.decodeEntities)(link[1]));
|
|
339
|
+
if (!url || !/^https?:\/\//.test(url))
|
|
340
|
+
continue;
|
|
341
|
+
const title = strip(link[2]);
|
|
342
|
+
const sn = /<p[^>]*>([\s\S]*?)<\/p>/i.exec(block);
|
|
343
|
+
const snippet = sn ? strip(sn[1]) : "";
|
|
344
|
+
hits.push({ title, url, snippet, rank: hits.length + 1, engine: "bing", date: (0, searchcore_1.detectDate)(snippet) });
|
|
345
|
+
}
|
|
346
|
+
return hits;
|
|
347
|
+
}
|
|
348
|
+
/** Bing wraps result URLs in a /ck/ redirect with a base64url-encoded `u` param. */
|
|
349
|
+
function decodeBingUrl(href) {
|
|
350
|
+
let u;
|
|
351
|
+
try {
|
|
352
|
+
u = new URL(href, "https://www.bing.com");
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
if (!u.hostname.endsWith("bing.com") || !u.pathname.startsWith("/ck/"))
|
|
358
|
+
return href;
|
|
359
|
+
const encoded = u.searchParams.get("u");
|
|
360
|
+
if (!encoded)
|
|
361
|
+
return null;
|
|
362
|
+
const value = encoded.startsWith("a1") ? encoded.slice(2) : encoded;
|
|
363
|
+
const padded = value + "=".repeat((4 - (value.length % 4)) % 4);
|
|
364
|
+
try {
|
|
365
|
+
const decoded = Buffer.from(padded, "base64url").toString("utf8");
|
|
366
|
+
return decoded.startsWith("http://") || decoded.startsWith("https://") ? decoded : null;
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
154
372
|
function strip(html) {
|
|
155
373
|
return (0, util_1.decodeEntities)(html.replace(/<[^>]+>/g, "")).replace(/\s+/g, " ").trim();
|
|
156
374
|
}
|
|
157
375
|
/**
|
|
158
|
-
* Fetch a URL as readable text.
|
|
159
|
-
*
|
|
160
|
-
* HTML→text extraction.
|
|
376
|
+
* Fetch a URL as readable text. Prefers a configured crawl backend's scrape
|
|
377
|
+
* (Firecrawl/context.dev: real browser, clean markdown), then TinyFish Fetch,
|
|
378
|
+
* then a direct request with HTML→text extraction.
|
|
161
379
|
*/
|
|
162
380
|
async function fetchUrl(cfg, url, raw, maxChars, signal) {
|
|
381
|
+
if (!raw && (0, crawltools_1.hasScrapeBackend)(cfg)) {
|
|
382
|
+
try {
|
|
383
|
+
const text = await (0, crawltools_1.scrapeUrl)(cfg, url, signal);
|
|
384
|
+
if (text)
|
|
385
|
+
return (0, util_1.truncateMiddle)(text, maxChars, "chars");
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
/* fall through to TinyFish → direct */
|
|
389
|
+
}
|
|
390
|
+
}
|
|
163
391
|
if (cfg.tinyfishApiKey && !raw) {
|
|
164
392
|
try {
|
|
165
393
|
const text = await tinyfishFetch(cfg, url, signal);
|
|
@@ -171,18 +399,51 @@ async function fetchUrl(cfg, url, raw, maxChars, signal) {
|
|
|
171
399
|
}
|
|
172
400
|
}
|
|
173
401
|
const res = await fetch(url, {
|
|
174
|
-
headers: { "user-agent": UA, accept: "text/html,application/json,text/*;q=0.9,*/*;q=0.5" },
|
|
402
|
+
headers: { "user-agent": UA, accept: "text/html,application/json,application/pdf,text/*;q=0.9,*/*;q=0.5" },
|
|
175
403
|
signal: signal ?? AbortSignal.timeout(25000),
|
|
176
404
|
redirect: "follow",
|
|
177
405
|
});
|
|
178
406
|
const ctype = res.headers.get("content-type") || "";
|
|
179
|
-
const body = await res.text();
|
|
180
407
|
if (!res.ok) {
|
|
181
|
-
|
|
408
|
+
// An error page is not content: returning it as a successful result lets
|
|
409
|
+
// "HTTP 403 ... subscribe to continue" become a "fact" in someone's report.
|
|
410
|
+
const body = await res.text().catch(() => "");
|
|
411
|
+
throw new Error(`HTTP ${res.status} ${res.statusText} — page is not usable as a source (paywall/login/blocked?). ` +
|
|
412
|
+
`Try web_search for an alternative source.${body ? ` Server said: ${(0, util_1.oneLine)((0, util_1.htmlToText)(body), 200)}` : ""}`);
|
|
413
|
+
}
|
|
414
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
415
|
+
if (/application\/pdf/i.test(ctype) || buf.subarray(0, 5).toString("latin1") === "%PDF-") {
|
|
416
|
+
if (buf.length > 20_000_000)
|
|
417
|
+
throw new Error(`PDF is ${Math.round(buf.length / 1e6)}MB — too large to extract`);
|
|
418
|
+
const pdf = (0, pdftext_1.extractPdfText)(buf);
|
|
419
|
+
if (!pdf) {
|
|
420
|
+
throw new Error("PDF contains no extractable text (likely scanned or encrypted) — find an HTML version of this source.");
|
|
421
|
+
}
|
|
422
|
+
return (0, util_1.truncateMiddle)(`[PDF, ${pdf.pages} page${pdf.pages > 1 ? "s" : ""}]\n${pdf.text}`, maxChars, "chars");
|
|
182
423
|
}
|
|
424
|
+
const body = decodeBody(buf, ctype);
|
|
183
425
|
const text = !raw && /html/i.test(ctype) ? (0, util_1.htmlToText)(body) : body;
|
|
426
|
+
if (!raw && /html/i.test(ctype)) {
|
|
427
|
+
const trimmed = text.trim();
|
|
428
|
+
if (trimmed.length < 400 && /subscrib|sign.?in|log.?in|enable javascript|access denied|are you a (human|robot)|captcha/i.test(trimmed)) {
|
|
429
|
+
return `WARNING: this page returned only a paywall/anti-bot shell — the text below is probably not the real content. Try web_search for an alternative source.\n\n${trimmed}`;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
184
432
|
return (0, util_1.truncateMiddle)(text, maxChars, "chars");
|
|
185
433
|
}
|
|
434
|
+
/** Decode a response body honoring its content-type charset (UTF-8 fallback). */
|
|
435
|
+
function decodeBody(buf, ctype) {
|
|
436
|
+
const charset = /charset=([\w-]+)/i.exec(ctype)?.[1]?.toLowerCase();
|
|
437
|
+
if (charset && charset !== "utf-8" && charset !== "utf8") {
|
|
438
|
+
try {
|
|
439
|
+
return new TextDecoder(charset).decode(buf);
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
/* unknown label — fall through to utf-8 */
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return buf.toString("utf8");
|
|
446
|
+
}
|
|
186
447
|
async function tinyfishFetch(cfg, url, signal) {
|
|
187
448
|
const res = await fetch("https://api.fetch.tinyfish.ai", {
|
|
188
449
|
method: "POST",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robzilla1738/agentswarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"dev:ui": "npm --prefix ui run dev",
|
|
47
47
|
"serve": "node bin/swarm.js serve",
|
|
48
48
|
"demo": "node bin/swarm.js demo",
|
|
49
|
-
"test": "node test/e2e.js"
|
|
49
|
+
"test": "npm run test:unit && node test/e2e.js",
|
|
50
|
+
"test:unit": "node --test test/unit/*.test.js"
|
|
50
51
|
},
|
|
51
52
|
"engines": {
|
|
52
53
|
"node": ">=20.10"
|
package/ui/out/404/index.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><!--
|
|
1
|
+
<!DOCTYPE html><!--7_pihFubDGD40BCy2ynlr--><html lang="en" class="__variable_73ee6c __variable_3c557b"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/4c9affa5bc8f420e-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/bb3ef058b751a6ad-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/d95c2ba395730031.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-38639c05c96dbeca.js"/><script src="/_next/static/chunks/4bd1b696-c023c6e3521b1417.js" async=""></script><script src="/_next/static/chunks/255-2aa030c9ba2867e3.js" async=""></script><script src="/_next/static/chunks/main-app-889ed884f8bc78e3.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><meta name="theme-color" content="#050505"/><title>agentswarm</title><meta name="description" content="A local agent swarm for long-horizon, autonomous work."/><link rel="icon" href="/icon.png?bdc9175d46e1d0aa" type="image/png" sizes="256x256"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><script>try{var t=localStorage.getItem("theme");if(t==="light"||t==="dark")document.documentElement.dataset.theme=t}catch(e){}</script><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-38639c05c96dbeca.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[9766,[],\"\"]\n3:I[8924,[],\"\"]\n4:I[4431,[],\"OutletBoundary\"]\n6:I[5278,[],\"AsyncMetadataOutlet\"]\n8:I[4431,[],\"ViewportBoundary\"]\na:I[4431,[],\"MetadataBoundary\"]\nb:\"$Sreact.suspense\"\nd:I[7150,[],\"\"]\n:HL[\"/_next/static/media/4c9affa5bc8f420e-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/bb3ef058b751a6ad-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/d95c2ba395730031.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"7_pihFubDGD40BCy2ynlr\",\"p\":\"\",\"c\":[\"\",\"_not-found\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/d95c2ba395730031.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"__variable_73ee6c __variable_3c557b\",\"children\":[\"$\",\"body\",null,{\"children\":[[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"try{var t=localStorage.getItem(\\\"theme\\\");if(t===\\\"light\\\"||t===\\\"dark\\\")document.documentElement.dataset.theme=t}catch(e){}\"}}],[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L4\",null,{\"children\":[\"$L5\",[\"$\",\"$L6\",null,{\"promise\":\"$@7\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$La\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$b\",null,{\"fallback\":null,\"children\":\"$Lc\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"2\",{\"name\":\"theme-color\",\"content\":\"#050505\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,"e:I[622,[],\"IconMark\"]\n7:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"agentswarm\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"A local agent swarm for long-horizon, autonomous work.\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/icon.png?bdc9175d46e1d0aa\",\"type\":\"image/png\",\"sizes\":\"256x256\"}],[\"$\",\"$Le\",\"3\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"c:\"$7:metadata\"\n"])</script></body></html>
|
package/ui/out/404.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><!--
|
|
1
|
+
<!DOCTYPE html><!--7_pihFubDGD40BCy2ynlr--><html lang="en" class="__variable_73ee6c __variable_3c557b"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/4c9affa5bc8f420e-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/bb3ef058b751a6ad-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/d95c2ba395730031.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-38639c05c96dbeca.js"/><script src="/_next/static/chunks/4bd1b696-c023c6e3521b1417.js" async=""></script><script src="/_next/static/chunks/255-2aa030c9ba2867e3.js" async=""></script><script src="/_next/static/chunks/main-app-889ed884f8bc78e3.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><meta name="theme-color" content="#050505"/><title>agentswarm</title><meta name="description" content="A local agent swarm for long-horizon, autonomous work."/><link rel="icon" href="/icon.png?bdc9175d46e1d0aa" type="image/png" sizes="256x256"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><script>try{var t=localStorage.getItem("theme");if(t==="light"||t==="dark")document.documentElement.dataset.theme=t}catch(e){}</script><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-38639c05c96dbeca.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[9766,[],\"\"]\n3:I[8924,[],\"\"]\n4:I[4431,[],\"OutletBoundary\"]\n6:I[5278,[],\"AsyncMetadataOutlet\"]\n8:I[4431,[],\"ViewportBoundary\"]\na:I[4431,[],\"MetadataBoundary\"]\nb:\"$Sreact.suspense\"\nd:I[7150,[],\"\"]\n:HL[\"/_next/static/media/4c9affa5bc8f420e-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/bb3ef058b751a6ad-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/d95c2ba395730031.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"7_pihFubDGD40BCy2ynlr\",\"p\":\"\",\"c\":[\"\",\"_not-found\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/d95c2ba395730031.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"__variable_73ee6c __variable_3c557b\",\"children\":[\"$\",\"body\",null,{\"children\":[[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"try{var t=localStorage.getItem(\\\"theme\\\");if(t===\\\"light\\\"||t===\\\"dark\\\")document.documentElement.dataset.theme=t}catch(e){}\"}}],[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L4\",null,{\"children\":[\"$L5\",[\"$\",\"$L6\",null,{\"promise\":\"$@7\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$La\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$b\",null,{\"fallback\":null,\"children\":\"$Lc\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"2\",{\"name\":\"theme-color\",\"content\":\"#050505\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,"e:I[622,[],\"IconMark\"]\n7:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"agentswarm\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"A local agent swarm for long-horizon, autonomous work.\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/icon.png?bdc9175d46e1d0aa\",\"type\":\"image/png\",\"sizes\":\"256x256\"}],[\"$\",\"$Le\",\"3\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"c:\"$7:metadata\"\n"])</script></body></html>
|