@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.
Files changed (45) hide show
  1. package/README.md +51 -11
  2. package/dist/agent.js +18 -2
  3. package/dist/cli.js +39 -8
  4. package/dist/config.js +62 -6
  5. package/dist/crawltools.js +247 -0
  6. package/dist/deepseek.js +125 -10
  7. package/dist/executor.js +993 -144
  8. package/dist/hub.js +85 -6
  9. package/dist/journal.js +61 -11
  10. package/dist/memory.js +84 -0
  11. package/dist/pdftext.js +211 -0
  12. package/dist/prompts.js +124 -23
  13. package/dist/report.js +289 -0
  14. package/dist/run.js +15 -2
  15. package/dist/sandbox.js +11 -0
  16. package/dist/searchcore.js +244 -0
  17. package/dist/state.js +85 -3
  18. package/dist/tools.js +392 -25
  19. package/dist/util.js +85 -0
  20. package/dist/webtools.js +327 -66
  21. package/package.json +3 -2
  22. package/ui/out/404/index.html +1 -1
  23. package/ui/out/404.html +1 -1
  24. package/ui/out/_next/static/chunks/532-35122e93f37719b9.js +1 -0
  25. package/ui/out/_next/static/chunks/677-721ce1c8b7a6a317.js +1 -0
  26. package/ui/out/_next/static/chunks/app/page-dc9f6744d203e76c.js +1 -0
  27. package/ui/out/_next/static/chunks/app/run/page-3674e103981703a2.js +1 -0
  28. package/ui/out/_next/static/chunks/app/settings/page-41a5d8ba43ecfd4a.js +1 -0
  29. package/ui/out/_next/static/css/d95c2ba395730031.css +3 -0
  30. package/ui/out/fonts/PlanetKosmos.ttf +0 -0
  31. package/ui/out/index.html +1 -1
  32. package/ui/out/index.txt +3 -3
  33. package/ui/out/run/index.html +1 -1
  34. package/ui/out/run/index.txt +3 -3
  35. package/ui/out/settings/index.html +1 -1
  36. package/ui/out/settings/index.txt +3 -3
  37. package/ui/out/_next/static/chunks/383-289a866b246b41cc.js +0 -1
  38. package/ui/out/_next/static/chunks/619-ba102abea3e3d0e4.js +0 -1
  39. package/ui/out/_next/static/chunks/677-7ab85a6f38c3a235.js +0 -1
  40. package/ui/out/_next/static/chunks/app/page-0fda5b8e77d90b84.js +0 -1
  41. package/ui/out/_next/static/chunks/app/run/page-07aab6b1224c3c8c.js +0 -1
  42. package/ui/out/_next/static/chunks/app/settings/page-528482d468d84cfa.js +0 -1
  43. package/ui/out/_next/static/css/e2c82b53bf4519e8.css +0 -3
  44. /package/ui/out/_next/static/{Rm5Fhkds2-wIOnVlME55J → 7_pihFubDGD40BCy2ynlr}/_buildManifest.js +0 -0
  45. /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 child_process_1 = require("child_process");
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 backends, best first:
10
- * 1. SearchKit CLI (local, multi-engine, ranked + citable; `deep` fetches
11
- * pages and returns quotable passages)when installed.
12
- * 2. TinyFish Search (fast, structured)when a key is configured.
13
- * 3. DuckDuckGo HTML scraping always available, last resort.
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
- if (cfg.searchBackend === "auto" && searchkitOk !== false) {
17
- try {
18
- const hits = await searchkitSearch(cfg, query, count, deep, signal);
19
- searchkitOk = true;
20
- if (hits.length)
21
- return hits;
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
- catch (e) {
24
- // Not installed stop probing for the rest of this process.
25
- if (e?.code === "ENOENT")
26
- searchkitOk = false;
27
- else if (!searchkitWarned) {
28
- // Installed but failing — say so once instead of silently degrading.
29
- searchkitWarned = true;
30
- warn?.(`searchkit failed (${(0, util_1.errMsg)(e)}); falling back to ${cfg.tinyfishApiKey ? "TinyFish" : "DuckDuckGo"}. ` +
31
- `Set searchBackend=ddg to skip searchkit.`);
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
- if (cfg.searchBackend !== "ddg" && cfg.tinyfishApiKey) {
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
- return await tinyfishSearch(cfg, query, count, signal);
98
+ domain = new URL(c.url).hostname.replace(/^www\./, "");
39
99
  }
40
100
  catch {
41
- /* fall through to DDG */
101
+ /* keep empty */
42
102
  }
43
- }
44
- return ddgSearch(query, count, signal);
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
- // ---------------------------------------------------------------- searchkit
47
- let searchkitOk = null;
48
- let searchkitWarned = false;
49
- function runCli(cmd, args, timeoutMs, signal) {
50
- return new Promise((resolve, reject) => {
51
- (0, child_process_1.execFile)(cmd, args, { timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024, signal }, (err, stdout) => (err ? reject(err) : resolve(stdout)));
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
- async function searchkitSearch(cfg, query, count, deep, signal) {
55
- const args = ["search", query, "--json", "--max-results", String(count)];
56
- if (!deep)
57
- args.push("--no-fetch");
58
- const out = await runCli(cfg.searchkitCmd, args, deep ? 90_000 : 30_000, signal);
59
- const start = out.indexOf("{");
60
- if (start < 0)
61
- throw new Error("searchkit: no JSON in output");
62
- const data = JSON.parse(out.slice(start));
63
- return (data.results || []).slice(0, count).map((r) => ({
64
- title: r.title || r.url,
65
- url: r.url,
66
- snippet: r.snippet || "",
67
- date: r.published_date || undefined,
68
- passages: Array.isArray(r.passages)
69
- ? r.passages.slice(0, 2).map((p) => String(p.text || "")).filter(Boolean)
70
- : undefined,
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 ?? AbortSignal.timeout(20000),
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 search goes dark. Link regexes tolerate either quote
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 fetch(ep.url + encodeURIComponent(query), {
232
+ const res = await engineFetch("duckduckgo", ep.url + encodeURIComponent(query), {
110
233
  headers: { "user-agent": UA },
111
- signal: signal ?? AbortSignal.timeout(20000),
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
- hits.push({ title, url, snippet: snippets[hits.length] || "" });
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. Uses TinyFish Fetch (real browser, clean
159
- * markdown) when a key is configured; falls back to a direct request with
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
- return `HTTP ${res.status} ${res.statusText}\n${(0, util_1.truncateMiddle)(body, 2000, "chars")}`;
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.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"
@@ -1 +1 @@
1
- <!DOCTYPE html><!--Rm5Fhkds2_wIOnVlME55J--><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/e2c82b53bf4519e8.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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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/e2c82b53bf4519e8.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"Rm5Fhkds2-wIOnVlME55J\",\"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/e2c82b53bf4519e8.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>
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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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><!--Rm5Fhkds2_wIOnVlME55J--><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/e2c82b53bf4519e8.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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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/e2c82b53bf4519e8.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"Rm5Fhkds2-wIOnVlME55J\",\"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/e2c82b53bf4519e8.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>
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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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>