@sesamespace/hivemind 0.8.13 → 0.10.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 +2 -1
- package/dist/{chunk-MLY4VFOO.js → chunk-BHCDOHSK.js} +3 -3
- package/dist/{chunk-PFZO67E2.js → chunk-DPLCEMEC.js} +2 -2
- package/dist/{chunk-HTLHMXAL.js → chunk-FBQBBAPZ.js} +2 -2
- package/dist/{chunk-NSTTILSN.js → chunk-FK6WYXRM.js} +79 -2
- package/dist/chunk-FK6WYXRM.js.map +1 -0
- package/dist/{chunk-LJHJGDKY.js → chunk-ICSJNKI6.js} +62 -2
- package/dist/chunk-ICSJNKI6.js.map +1 -0
- package/dist/{chunk-4Y7A25UG.js → chunk-IXBIAX76.js} +2 -2
- package/dist/{chunk-ZM7RK5YV.js → chunk-M3A2WRXM.js} +560 -37
- package/dist/chunk-M3A2WRXM.js.map +1 -0
- package/dist/commands/fleet.js +3 -3
- package/dist/commands/init.js +3 -3
- package/dist/commands/start.js +3 -3
- package/dist/commands/upgrade.js +1 -1
- package/dist/commands/watchdog.js +3 -3
- package/dist/dashboard.html +873 -131
- package/dist/index.js +2 -2
- package/dist/main.js +375 -7
- package/dist/main.js.map +1 -1
- package/dist/start.js +1 -1
- package/install.sh +162 -0
- package/package.json +24 -23
- package/packages/memory/Cargo.lock +6480 -0
- package/packages/memory/Cargo.toml +21 -0
- package/packages/memory/src/src/context.rs +179 -0
- package/packages/memory/src/src/embeddings.rs +51 -0
- package/packages/memory/src/src/main.rs +887 -0
- package/packages/memory/src/src/promotion.rs +808 -0
- package/packages/memory/src/src/scoring.rs +142 -0
- package/packages/memory/src/src/store.rs +460 -0
- package/packages/memory/src/src/tasks.rs +321 -0
- package/.pnpmrc.json +0 -1
- package/AUTO-DEBUG-DESIGN.md +0 -267
- package/DASHBOARD-PLAN.md +0 -206
- package/MEMORY-ENHANCEMENT-PLAN.md +0 -211
- package/TOOL-USE-DESIGN.md +0 -173
- package/dist/chunk-LJHJGDKY.js.map +0 -1
- package/dist/chunk-NSTTILSN.js.map +0 -1
- package/dist/chunk-ZM7RK5YV.js.map +0 -1
- package/docs/TOOL-PARITY-PLAN.md +0 -191
- package/src/memory/dashboard-integration.ts +0 -295
- package/src/memory/index.ts +0 -187
- package/src/memory/performance-test.ts +0 -208
- package/src/memory/processors/agent-sync.ts +0 -312
- package/src/memory/processors/command-learner.ts +0 -298
- package/src/memory/processors/memory-api-client.ts +0 -105
- package/src/memory/processors/message-flow-integration.ts +0 -168
- package/src/memory/processors/research-digester.ts +0 -204
- package/test-caitlin-access.md +0 -11
- /package/dist/{chunk-MLY4VFOO.js.map → chunk-BHCDOHSK.js.map} +0 -0
- /package/dist/{chunk-PFZO67E2.js.map → chunk-DPLCEMEC.js.map} +0 -0
- /package/dist/{chunk-HTLHMXAL.js.map → chunk-FBQBBAPZ.js.map} +0 -0
- /package/dist/{chunk-4Y7A25UG.js.map → chunk-IXBIAX76.js.map} +0 -0
|
@@ -1343,14 +1343,15 @@ var Agent = class {
|
|
|
1343
1343
|
});
|
|
1344
1344
|
if (relevantEpisodes.length > 0) {
|
|
1345
1345
|
const episodeIds = relevantEpisodes.map((e) => e.id);
|
|
1346
|
-
this.memory.recordCoAccess(episodeIds).catch(() =>
|
|
1347
|
-
});
|
|
1346
|
+
this.memory.recordCoAccess(episodeIds).catch((err) => console.warn("[memory] recordCoAccess failed:", err.message));
|
|
1348
1347
|
for (const ep of relevantEpisodes) {
|
|
1349
|
-
this.memory.recordAccess(ep.id).catch(() =>
|
|
1350
|
-
});
|
|
1348
|
+
this.memory.recordAccess(ep.id).catch((err) => console.warn("[memory] recordAccess failed:", err.message));
|
|
1351
1349
|
}
|
|
1352
1350
|
}
|
|
1353
|
-
const l3Knowledge = await this.memory.getL3Knowledge(contextName).catch(() =>
|
|
1351
|
+
const l3Knowledge = await this.memory.getL3Knowledge(contextName).catch((err) => {
|
|
1352
|
+
console.warn("[memory] getL3Knowledge failed:", err.message);
|
|
1353
|
+
return [];
|
|
1354
|
+
});
|
|
1354
1355
|
const systemPromptResult = buildSystemPrompt({
|
|
1355
1356
|
config: this.config.agent,
|
|
1356
1357
|
episodes: relevantEpisodes,
|
|
@@ -1587,6 +1588,9 @@ var Agent = class {
|
|
|
1587
1588
|
getActiveContext() {
|
|
1588
1589
|
return this.contextManager.getActiveContext();
|
|
1589
1590
|
}
|
|
1591
|
+
getConversationHistories() {
|
|
1592
|
+
return this.conversationHistories;
|
|
1593
|
+
}
|
|
1590
1594
|
};
|
|
1591
1595
|
|
|
1592
1596
|
// packages/runtime/src/events.ts
|
|
@@ -4745,29 +4749,43 @@ function parseQuery(url) {
|
|
|
4745
4749
|
}
|
|
4746
4750
|
return params;
|
|
4747
4751
|
}
|
|
4748
|
-
|
|
4752
|
+
function readBody(req) {
|
|
4753
|
+
return new Promise((resolve21, reject) => {
|
|
4754
|
+
const chunks = [];
|
|
4755
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
4756
|
+
req.on("end", () => resolve21(Buffer.concat(chunks).toString()));
|
|
4757
|
+
req.on("error", reject);
|
|
4758
|
+
});
|
|
4759
|
+
}
|
|
4760
|
+
async function proxyMemory(memoryUrl, path, method, res, body) {
|
|
4749
4761
|
try {
|
|
4750
|
-
const
|
|
4751
|
-
|
|
4762
|
+
const opts = { method };
|
|
4763
|
+
if (body && (method === "POST" || method === "PATCH" || method === "PUT")) {
|
|
4764
|
+
opts.body = body;
|
|
4765
|
+
opts.headers = { "Content-Type": "application/json" };
|
|
4766
|
+
}
|
|
4767
|
+
const resp = await fetch(`${memoryUrl}${path}`, opts);
|
|
4768
|
+
const respBody = await resp.text();
|
|
4752
4769
|
res.writeHead(resp.status, {
|
|
4753
4770
|
"Content-Type": resp.headers.get("content-type") ?? "application/json",
|
|
4754
4771
|
"Access-Control-Allow-Origin": "*"
|
|
4755
4772
|
});
|
|
4756
|
-
res.end(
|
|
4773
|
+
res.end(respBody);
|
|
4757
4774
|
} catch (err) {
|
|
4758
4775
|
json(res, { error: err.message }, 502);
|
|
4759
4776
|
}
|
|
4760
4777
|
}
|
|
4761
|
-
function startDashboardServer(requestLogger, memoryConfig) {
|
|
4778
|
+
function startDashboardServer(requestLogger, memoryConfig, getL1) {
|
|
4762
4779
|
const memoryUrl = memoryConfig.daemon_url;
|
|
4763
4780
|
const server = createServer(async (req, res) => {
|
|
4764
4781
|
const method = req.method ?? "GET";
|
|
4765
4782
|
const rawUrl = req.url ?? "/";
|
|
4766
4783
|
const urlPath = rawUrl.split("?")[0];
|
|
4784
|
+
const queryStr = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
|
|
4767
4785
|
if (method === "OPTIONS") {
|
|
4768
4786
|
res.writeHead(204, {
|
|
4769
4787
|
"Access-Control-Allow-Origin": "*",
|
|
4770
|
-
"Access-Control-Allow-Methods": "GET, DELETE, OPTIONS",
|
|
4788
|
+
"Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
|
|
4771
4789
|
"Access-Control-Allow-Headers": "Content-Type"
|
|
4772
4790
|
});
|
|
4773
4791
|
res.end();
|
|
@@ -4800,10 +4818,32 @@ function startDashboardServer(requestLogger, memoryConfig) {
|
|
|
4800
4818
|
}
|
|
4801
4819
|
return;
|
|
4802
4820
|
}
|
|
4821
|
+
if (method === "GET" && urlPath === "/api/health") {
|
|
4822
|
+
await proxyMemory(memoryUrl, "/health", "GET", res);
|
|
4823
|
+
return;
|
|
4824
|
+
}
|
|
4825
|
+
if (method === "GET" && urlPath === "/api/stats") {
|
|
4826
|
+
await proxyMemory(memoryUrl, "/stats", "GET", res);
|
|
4827
|
+
return;
|
|
4828
|
+
}
|
|
4829
|
+
if (method === "GET" && urlPath === "/api/search") {
|
|
4830
|
+
await proxyMemory(memoryUrl, `/search${queryStr}`, "GET", res);
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
if (method === "GET" && urlPath === "/api/search/cross-context") {
|
|
4834
|
+
await proxyMemory(memoryUrl, `/search/cross-context${queryStr}`, "GET", res);
|
|
4835
|
+
return;
|
|
4836
|
+
}
|
|
4803
4837
|
if (method === "GET" && urlPath === "/api/contexts") {
|
|
4804
4838
|
await proxyMemory(memoryUrl, "/contexts", "GET", res);
|
|
4805
4839
|
return;
|
|
4806
4840
|
}
|
|
4841
|
+
const ctxDeleteMatch = urlPath.match(/^\/api\/contexts\/([^/]+)$/);
|
|
4842
|
+
if (method === "DELETE" && ctxDeleteMatch) {
|
|
4843
|
+
const name = decodeURIComponent(ctxDeleteMatch[1]);
|
|
4844
|
+
await proxyMemory(memoryUrl, `/contexts/${encodeURIComponent(name)}`, "DELETE", res);
|
|
4845
|
+
return;
|
|
4846
|
+
}
|
|
4807
4847
|
const episodesMatch = urlPath.match(/^\/api\/contexts\/([^/]+)\/episodes$/);
|
|
4808
4848
|
if (method === "GET" && episodesMatch) {
|
|
4809
4849
|
const name = decodeURIComponent(episodesMatch[1]);
|
|
@@ -4821,12 +4861,69 @@ function startDashboardServer(requestLogger, memoryConfig) {
|
|
|
4821
4861
|
);
|
|
4822
4862
|
return;
|
|
4823
4863
|
}
|
|
4824
|
-
const
|
|
4825
|
-
if (method === "
|
|
4826
|
-
const
|
|
4864
|
+
const scoringGetMatch = urlPath.match(/^\/api\/contexts\/([^/]+)\/scoring$/);
|
|
4865
|
+
if (method === "GET" && scoringGetMatch) {
|
|
4866
|
+
const name = decodeURIComponent(scoringGetMatch[1]);
|
|
4867
|
+
await proxyMemory(memoryUrl, `/scoring/${encodeURIComponent(name)}`, "GET", res);
|
|
4868
|
+
return;
|
|
4869
|
+
}
|
|
4870
|
+
if (method === "POST" && scoringGetMatch) {
|
|
4871
|
+
const name = decodeURIComponent(scoringGetMatch[1]);
|
|
4872
|
+
const body = await readBody(req);
|
|
4873
|
+
await proxyMemory(memoryUrl, `/contexts/${encodeURIComponent(name)}/scoring`, "POST", res, body);
|
|
4874
|
+
return;
|
|
4875
|
+
}
|
|
4876
|
+
const l3IdMatch = urlPath.match(/^\/api\/l3\/([^/]+)$/);
|
|
4877
|
+
if (method === "DELETE" && l3IdMatch) {
|
|
4878
|
+
const id = decodeURIComponent(l3IdMatch[1]);
|
|
4827
4879
|
await proxyMemory(memoryUrl, `/promotion/l3/${encodeURIComponent(id)}`, "DELETE", res);
|
|
4828
4880
|
return;
|
|
4829
4881
|
}
|
|
4882
|
+
if (method === "PATCH" && l3IdMatch) {
|
|
4883
|
+
const id = decodeURIComponent(l3IdMatch[1]);
|
|
4884
|
+
const body = await readBody(req);
|
|
4885
|
+
await proxyMemory(memoryUrl, `/promotion/l3/${encodeURIComponent(id)}`, "PATCH", res, body);
|
|
4886
|
+
return;
|
|
4887
|
+
}
|
|
4888
|
+
if (method === "POST" && urlPath === "/api/promotion/run") {
|
|
4889
|
+
await proxyMemory(memoryUrl, `/promotion/run${queryStr}`, "POST", res);
|
|
4890
|
+
return;
|
|
4891
|
+
}
|
|
4892
|
+
if (method === "GET" && urlPath === "/api/access/top") {
|
|
4893
|
+
await proxyMemory(memoryUrl, `/access/top${queryStr}`, "GET", res);
|
|
4894
|
+
return;
|
|
4895
|
+
}
|
|
4896
|
+
const accessMatch = urlPath.match(/^\/api\/episodes\/([^/]+)\/access$/);
|
|
4897
|
+
if (method === "GET" && accessMatch) {
|
|
4898
|
+
const id = decodeURIComponent(accessMatch[1]);
|
|
4899
|
+
await proxyMemory(memoryUrl, `/access/${encodeURIComponent(id)}`, "GET", res);
|
|
4900
|
+
return;
|
|
4901
|
+
}
|
|
4902
|
+
if (method === "GET" && urlPath === "/api/l1") {
|
|
4903
|
+
if (!getL1) {
|
|
4904
|
+
json(res, { contexts: {} });
|
|
4905
|
+
return;
|
|
4906
|
+
}
|
|
4907
|
+
const histories = getL1();
|
|
4908
|
+
const result = {};
|
|
4909
|
+
for (const [ctx, msgs] of histories) {
|
|
4910
|
+
result[ctx] = { count: msgs.length };
|
|
4911
|
+
}
|
|
4912
|
+
json(res, { contexts: result });
|
|
4913
|
+
return;
|
|
4914
|
+
}
|
|
4915
|
+
const l1CtxMatch = urlPath.match(/^\/api\/l1\/([^/]+)$/);
|
|
4916
|
+
if (method === "GET" && l1CtxMatch) {
|
|
4917
|
+
if (!getL1) {
|
|
4918
|
+
json(res, { messages: [] });
|
|
4919
|
+
return;
|
|
4920
|
+
}
|
|
4921
|
+
const ctx = decodeURIComponent(l1CtxMatch[1]);
|
|
4922
|
+
const histories = getL1();
|
|
4923
|
+
const messages = histories.get(ctx) || [];
|
|
4924
|
+
json(res, { messages: messages.map((m) => ({ role: m.role, content: m.content ?? "" })) });
|
|
4925
|
+
return;
|
|
4926
|
+
}
|
|
4830
4927
|
json(res, { error: "Not found" }, 404);
|
|
4831
4928
|
} catch (err) {
|
|
4832
4929
|
console.error("[dashboard] Request error:", err.message);
|
|
@@ -6039,6 +6136,7 @@ import { mkdirSync as mkdirSync9, existsSync as existsSync13 } from "fs";
|
|
|
6039
6136
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
6040
6137
|
var MAX_OUTPUT2 = 5e4;
|
|
6041
6138
|
var browserInstance = null;
|
|
6139
|
+
var contextInstances = /* @__PURE__ */ new Map();
|
|
6042
6140
|
var lastUsed = 0;
|
|
6043
6141
|
var idleTimer = null;
|
|
6044
6142
|
var IDLE_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -6047,7 +6145,16 @@ async function getBrowser() {
|
|
|
6047
6145
|
const modName = "playwright";
|
|
6048
6146
|
const pw = await Function("m", "return import(m)")(modName);
|
|
6049
6147
|
if (!browserInstance) {
|
|
6050
|
-
browserInstance = await pw.chromium.launch({
|
|
6148
|
+
browserInstance = await pw.chromium.launch({
|
|
6149
|
+
headless: true,
|
|
6150
|
+
args: [
|
|
6151
|
+
"--disable-dev-shm-usage",
|
|
6152
|
+
"--disable-setuid-sandbox",
|
|
6153
|
+
"--no-sandbox",
|
|
6154
|
+
"--disable-web-security",
|
|
6155
|
+
"--disable-features=VizDisplayCompositor"
|
|
6156
|
+
]
|
|
6157
|
+
});
|
|
6051
6158
|
if (!idleTimer) {
|
|
6052
6159
|
idleTimer = setInterval(async () => {
|
|
6053
6160
|
if (browserInstance && Date.now() - lastUsed > IDLE_TIMEOUT_MS) {
|
|
@@ -6064,7 +6171,43 @@ async function getBrowser() {
|
|
|
6064
6171
|
);
|
|
6065
6172
|
}
|
|
6066
6173
|
}
|
|
6174
|
+
async function getBrowserContext(sessionId = "default", options = {}) {
|
|
6175
|
+
const browser = await getBrowser();
|
|
6176
|
+
if (!contextInstances.has(sessionId)) {
|
|
6177
|
+
const contextOptions = {
|
|
6178
|
+
userAgent: options.userAgent || "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
|
6179
|
+
viewport: options.viewport || { width: 1280, height: 720 },
|
|
6180
|
+
locale: options.locale || "en-US",
|
|
6181
|
+
timezoneId: options.timezone || "America/New_York",
|
|
6182
|
+
deviceScaleFactor: options.deviceScaleFactor || 1,
|
|
6183
|
+
isMobile: options.isMobile || false,
|
|
6184
|
+
hasTouch: options.hasTouch || false,
|
|
6185
|
+
colorScheme: options.colorScheme || "light",
|
|
6186
|
+
reducedMotion: options.reducedMotion || "no-preference",
|
|
6187
|
+
permissions: options.permissions || [],
|
|
6188
|
+
geolocation: options.geolocation,
|
|
6189
|
+
offline: options.offline || false,
|
|
6190
|
+
acceptDownloads: options.downloadBehavior !== "deny"
|
|
6191
|
+
};
|
|
6192
|
+
const context = await browser.newContext(contextOptions);
|
|
6193
|
+
context.on("request", (request) => {
|
|
6194
|
+
console.log(`\u2192 ${request.method()} ${request.url()}`);
|
|
6195
|
+
});
|
|
6196
|
+
context.on("response", (response) => {
|
|
6197
|
+
console.log(`\u2190 ${response.status()} ${response.url()}`);
|
|
6198
|
+
});
|
|
6199
|
+
contextInstances.set(sessionId, context);
|
|
6200
|
+
}
|
|
6201
|
+
return contextInstances.get(sessionId);
|
|
6202
|
+
}
|
|
6067
6203
|
async function closeBrowser() {
|
|
6204
|
+
for (const [sessionId, context] of contextInstances) {
|
|
6205
|
+
try {
|
|
6206
|
+
await context.close();
|
|
6207
|
+
} catch {
|
|
6208
|
+
}
|
|
6209
|
+
}
|
|
6210
|
+
contextInstances.clear();
|
|
6068
6211
|
if (browserInstance) {
|
|
6069
6212
|
try {
|
|
6070
6213
|
await browserInstance.close();
|
|
@@ -6082,14 +6225,26 @@ function registerBrowserTools(registry, workspaceDir) {
|
|
|
6082
6225
|
registry.register(
|
|
6083
6226
|
"browse",
|
|
6084
6227
|
[
|
|
6085
|
-
"Navigate to a URL and interact with the page using
|
|
6228
|
+
"Navigate to a URL and interact with the page using an enhanced headless browser with session persistence.",
|
|
6086
6229
|
"Actions:",
|
|
6087
6230
|
" extract (default) \u2014 get the readable text content of the page",
|
|
6088
6231
|
" screenshot \u2014 take a PNG screenshot and return the file path",
|
|
6089
6232
|
" click \u2014 click an element matching a CSS selector",
|
|
6090
6233
|
" type \u2014 type text into an element matching a CSS selector",
|
|
6091
6234
|
" evaluate \u2014 run arbitrary JavaScript in the page and return the result",
|
|
6092
|
-
"
|
|
6235
|
+
" form_fill \u2014 fill out a form with multiple fields at once",
|
|
6236
|
+
" scroll \u2014 scroll the page (up, down, to element, or to coordinates)",
|
|
6237
|
+
" hover \u2014 hover over an element",
|
|
6238
|
+
" select \u2014 select an option from a dropdown",
|
|
6239
|
+
" upload \u2014 upload a file to a file input",
|
|
6240
|
+
" download \u2014 download a file and return the path",
|
|
6241
|
+
" pdf \u2014 generate a PDF of the page",
|
|
6242
|
+
" wait_for \u2014 wait for various conditions (selector, text, url, timeout)",
|
|
6243
|
+
" network_logs \u2014 get network request/response logs",
|
|
6244
|
+
" console_logs \u2014 get browser console logs",
|
|
6245
|
+
" cookies \u2014 get/set/clear cookies",
|
|
6246
|
+
" storage \u2014 get/set/clear localStorage/sessionStorage",
|
|
6247
|
+
"Supports session persistence, mobile simulation, and advanced browser features."
|
|
6093
6248
|
].join("\n"),
|
|
6094
6249
|
{
|
|
6095
6250
|
type: "object",
|
|
@@ -6100,21 +6255,101 @@ function registerBrowserTools(registry, workspaceDir) {
|
|
|
6100
6255
|
},
|
|
6101
6256
|
action: {
|
|
6102
6257
|
type: "string",
|
|
6103
|
-
enum: [
|
|
6258
|
+
enum: [
|
|
6259
|
+
"extract",
|
|
6260
|
+
"screenshot",
|
|
6261
|
+
"click",
|
|
6262
|
+
"type",
|
|
6263
|
+
"evaluate",
|
|
6264
|
+
"form_fill",
|
|
6265
|
+
"scroll",
|
|
6266
|
+
"hover",
|
|
6267
|
+
"select",
|
|
6268
|
+
"upload",
|
|
6269
|
+
"download",
|
|
6270
|
+
"pdf",
|
|
6271
|
+
"wait_for",
|
|
6272
|
+
"network_logs",
|
|
6273
|
+
"console_logs",
|
|
6274
|
+
"cookies",
|
|
6275
|
+
"storage"
|
|
6276
|
+
],
|
|
6104
6277
|
description: "Action to perform (default: extract)."
|
|
6105
6278
|
},
|
|
6106
6279
|
selector: {
|
|
6107
6280
|
type: "string",
|
|
6108
|
-
description: "CSS selector for
|
|
6281
|
+
description: "CSS selector for element-based actions."
|
|
6109
6282
|
},
|
|
6110
6283
|
text: {
|
|
6111
6284
|
type: "string",
|
|
6112
|
-
description: "Text to type
|
|
6285
|
+
description: "Text to type or search for."
|
|
6113
6286
|
},
|
|
6114
6287
|
javascript: {
|
|
6115
6288
|
type: "string",
|
|
6116
6289
|
description: "JavaScript to evaluate in the page (for 'evaluate' action)."
|
|
6117
6290
|
},
|
|
6291
|
+
formData: {
|
|
6292
|
+
type: "object",
|
|
6293
|
+
description: "Key-value pairs for form filling (selector: value)."
|
|
6294
|
+
},
|
|
6295
|
+
scrollDirection: {
|
|
6296
|
+
type: "string",
|
|
6297
|
+
enum: ["up", "down", "left", "right", "top", "bottom"],
|
|
6298
|
+
description: "Scroll direction or position."
|
|
6299
|
+
},
|
|
6300
|
+
scrollDistance: {
|
|
6301
|
+
type: "number",
|
|
6302
|
+
description: "Pixels to scroll (default: 500)."
|
|
6303
|
+
},
|
|
6304
|
+
coordinates: {
|
|
6305
|
+
type: "object",
|
|
6306
|
+
properties: {
|
|
6307
|
+
x: { type: "number" },
|
|
6308
|
+
y: { type: "number" }
|
|
6309
|
+
},
|
|
6310
|
+
description: "X,Y coordinates for scrolling or clicking."
|
|
6311
|
+
},
|
|
6312
|
+
filePath: {
|
|
6313
|
+
type: "string",
|
|
6314
|
+
description: "Path to file for upload action."
|
|
6315
|
+
},
|
|
6316
|
+
waitCondition: {
|
|
6317
|
+
type: "string",
|
|
6318
|
+
enum: ["selector", "text", "url", "timeout", "networkidle"],
|
|
6319
|
+
description: "What to wait for."
|
|
6320
|
+
},
|
|
6321
|
+
waitValue: {
|
|
6322
|
+
type: "string",
|
|
6323
|
+
description: "Value to wait for (selector, text, URL pattern)."
|
|
6324
|
+
},
|
|
6325
|
+
cookieData: {
|
|
6326
|
+
type: "object",
|
|
6327
|
+
description: "Cookie data for cookie operations."
|
|
6328
|
+
},
|
|
6329
|
+
storageData: {
|
|
6330
|
+
type: "object",
|
|
6331
|
+
description: "Storage data for localStorage/sessionStorage operations."
|
|
6332
|
+
},
|
|
6333
|
+
session: {
|
|
6334
|
+
type: "string",
|
|
6335
|
+
description: "Session ID for persistent browser context (default: 'default')."
|
|
6336
|
+
},
|
|
6337
|
+
viewport: {
|
|
6338
|
+
type: "object",
|
|
6339
|
+
properties: {
|
|
6340
|
+
width: { type: "number" },
|
|
6341
|
+
height: { type: "number" }
|
|
6342
|
+
},
|
|
6343
|
+
description: "Browser viewport size."
|
|
6344
|
+
},
|
|
6345
|
+
userAgent: {
|
|
6346
|
+
type: "string",
|
|
6347
|
+
description: "Custom user agent string."
|
|
6348
|
+
},
|
|
6349
|
+
mobile: {
|
|
6350
|
+
type: "boolean",
|
|
6351
|
+
description: "Simulate mobile device."
|
|
6352
|
+
},
|
|
6118
6353
|
waitForSelector: {
|
|
6119
6354
|
type: "string",
|
|
6120
6355
|
description: "CSS selector to wait for before performing the action."
|
|
@@ -6132,19 +6367,33 @@ function registerBrowserTools(registry, workspaceDir) {
|
|
|
6132
6367
|
const selector = params.selector;
|
|
6133
6368
|
const text = params.text;
|
|
6134
6369
|
const javascript = params.javascript;
|
|
6370
|
+
const formData = params.formData;
|
|
6371
|
+
const scrollDirection = params.scrollDirection;
|
|
6372
|
+
const scrollDistance = params.scrollDistance || 500;
|
|
6373
|
+
const coordinates = params.coordinates;
|
|
6374
|
+
const filePath = params.filePath;
|
|
6375
|
+
const waitCondition = params.waitCondition;
|
|
6376
|
+
const waitValue = params.waitValue;
|
|
6377
|
+
const cookieData = params.cookieData;
|
|
6378
|
+
const storageData = params.storageData;
|
|
6379
|
+
const sessionId = params.session || "default";
|
|
6380
|
+
const viewport = params.viewport;
|
|
6381
|
+
const userAgent = params.userAgent;
|
|
6382
|
+
const mobile = params.mobile;
|
|
6135
6383
|
const waitForSelector = params.waitForSelector;
|
|
6136
6384
|
const timeout = params.timeout || 3e4;
|
|
6137
|
-
let
|
|
6138
|
-
try {
|
|
6139
|
-
browser = await getBrowser();
|
|
6140
|
-
} catch (err) {
|
|
6141
|
-
return err.message;
|
|
6142
|
-
}
|
|
6385
|
+
let context;
|
|
6143
6386
|
let page;
|
|
6144
6387
|
try {
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6388
|
+
const browserOptions = {
|
|
6389
|
+
session: sessionId,
|
|
6390
|
+
viewport,
|
|
6391
|
+
userAgent,
|
|
6392
|
+
isMobile: mobile,
|
|
6393
|
+
hasTouch: mobile
|
|
6394
|
+
};
|
|
6395
|
+
context = await getBrowserContext(sessionId, browserOptions);
|
|
6396
|
+
page = await context.newPage();
|
|
6148
6397
|
page.setDefaultTimeout(timeout);
|
|
6149
6398
|
await page.goto(url, { waitUntil: "domcontentloaded", timeout });
|
|
6150
6399
|
if (waitForSelector) {
|
|
@@ -6170,12 +6419,31 @@ function registerBrowserTools(registry, workspaceDir) {
|
|
|
6170
6419
|
await page.screenshot({ path: filepath, fullPage: true });
|
|
6171
6420
|
return `Screenshot saved: ${filepath}`;
|
|
6172
6421
|
}
|
|
6422
|
+
case "pdf": {
|
|
6423
|
+
if (!existsSync13(screenshotDir)) {
|
|
6424
|
+
mkdirSync9(screenshotDir, { recursive: true });
|
|
6425
|
+
}
|
|
6426
|
+
const filename = `${randomUUID5()}.pdf`;
|
|
6427
|
+
const filepath = resolve12(screenshotDir, filename);
|
|
6428
|
+
await page.pdf({
|
|
6429
|
+
path: filepath,
|
|
6430
|
+
format: "A4",
|
|
6431
|
+
printBackground: true,
|
|
6432
|
+
margin: { top: "1cm", right: "1cm", bottom: "1cm", left: "1cm" }
|
|
6433
|
+
});
|
|
6434
|
+
return `PDF saved: ${filepath}`;
|
|
6435
|
+
}
|
|
6173
6436
|
case "click": {
|
|
6174
6437
|
if (!selector) {
|
|
6175
6438
|
return "Error: 'selector' parameter is required for click action.";
|
|
6176
6439
|
}
|
|
6177
|
-
|
|
6178
|
-
|
|
6440
|
+
if (coordinates) {
|
|
6441
|
+
await page.mouse.click(coordinates.x, coordinates.y);
|
|
6442
|
+
return `Clicked at coordinates: (${coordinates.x}, ${coordinates.y})`;
|
|
6443
|
+
} else {
|
|
6444
|
+
await page.click(selector);
|
|
6445
|
+
return `Clicked: ${selector}`;
|
|
6446
|
+
}
|
|
6179
6447
|
}
|
|
6180
6448
|
case "type": {
|
|
6181
6449
|
if (!selector) {
|
|
@@ -6187,6 +6455,169 @@ function registerBrowserTools(registry, workspaceDir) {
|
|
|
6187
6455
|
await page.fill(selector, text);
|
|
6188
6456
|
return `Typed into ${selector}: "${text}"`;
|
|
6189
6457
|
}
|
|
6458
|
+
case "form_fill": {
|
|
6459
|
+
if (!formData) {
|
|
6460
|
+
return "Error: 'formData' parameter is required for form_fill action.";
|
|
6461
|
+
}
|
|
6462
|
+
const results = [];
|
|
6463
|
+
for (const [sel, value] of Object.entries(formData)) {
|
|
6464
|
+
await page.fill(sel, value);
|
|
6465
|
+
results.push(`${sel}: "${value}"`);
|
|
6466
|
+
}
|
|
6467
|
+
return `Form filled:
|
|
6468
|
+
${results.join("\n")}`;
|
|
6469
|
+
}
|
|
6470
|
+
case "scroll": {
|
|
6471
|
+
if (coordinates) {
|
|
6472
|
+
await page.evaluate(({ x, y }) => window.scrollTo(x, y), coordinates);
|
|
6473
|
+
return `Scrolled to coordinates: (${coordinates.x}, ${coordinates.y})`;
|
|
6474
|
+
} else if (selector) {
|
|
6475
|
+
await page.locator(selector).scrollIntoViewIfNeeded();
|
|
6476
|
+
return `Scrolled to element: ${selector}`;
|
|
6477
|
+
} else if (scrollDirection) {
|
|
6478
|
+
switch (scrollDirection) {
|
|
6479
|
+
case "up":
|
|
6480
|
+
await page.keyboard.press("PageUp");
|
|
6481
|
+
break;
|
|
6482
|
+
case "down":
|
|
6483
|
+
await page.keyboard.press("PageDown");
|
|
6484
|
+
break;
|
|
6485
|
+
case "top":
|
|
6486
|
+
await page.keyboard.press("Home");
|
|
6487
|
+
break;
|
|
6488
|
+
case "bottom":
|
|
6489
|
+
await page.keyboard.press("End");
|
|
6490
|
+
break;
|
|
6491
|
+
default:
|
|
6492
|
+
await page.mouse.wheel(0, scrollDirection === "down" ? scrollDistance : -scrollDistance);
|
|
6493
|
+
}
|
|
6494
|
+
return `Scrolled ${scrollDirection}`;
|
|
6495
|
+
}
|
|
6496
|
+
return "Error: scroll requires coordinates, selector, or scrollDirection";
|
|
6497
|
+
}
|
|
6498
|
+
case "hover": {
|
|
6499
|
+
if (!selector) {
|
|
6500
|
+
return "Error: 'selector' parameter is required for hover action.";
|
|
6501
|
+
}
|
|
6502
|
+
await page.hover(selector);
|
|
6503
|
+
return `Hovered over: ${selector}`;
|
|
6504
|
+
}
|
|
6505
|
+
case "select": {
|
|
6506
|
+
if (!selector || !text) {
|
|
6507
|
+
return "Error: 'selector' and 'text' parameters are required for select action.";
|
|
6508
|
+
}
|
|
6509
|
+
await page.selectOption(selector, text);
|
|
6510
|
+
return `Selected "${text}" in ${selector}`;
|
|
6511
|
+
}
|
|
6512
|
+
case "upload": {
|
|
6513
|
+
if (!selector || !filePath) {
|
|
6514
|
+
return "Error: 'selector' and 'filePath' parameters are required for upload action.";
|
|
6515
|
+
}
|
|
6516
|
+
const absolutePath = resolve12(workspaceDir, filePath);
|
|
6517
|
+
if (!existsSync13(absolutePath)) {
|
|
6518
|
+
return `Error: File not found: ${absolutePath}`;
|
|
6519
|
+
}
|
|
6520
|
+
await page.setInputFiles(selector, absolutePath);
|
|
6521
|
+
return `Uploaded file ${filePath} to ${selector}`;
|
|
6522
|
+
}
|
|
6523
|
+
case "download": {
|
|
6524
|
+
const downloadPromise = page.waitForEvent("download");
|
|
6525
|
+
if (selector) {
|
|
6526
|
+
await page.click(selector);
|
|
6527
|
+
}
|
|
6528
|
+
const download = await downloadPromise;
|
|
6529
|
+
const filename = download.suggestedFilename() || `download_${randomUUID5()}`;
|
|
6530
|
+
const downloadPath = resolve12(workspaceDir, "downloads", filename);
|
|
6531
|
+
if (!existsSync13(resolve12(workspaceDir, "downloads"))) {
|
|
6532
|
+
mkdirSync9(resolve12(workspaceDir, "downloads"), { recursive: true });
|
|
6533
|
+
}
|
|
6534
|
+
await download.saveAs(downloadPath);
|
|
6535
|
+
return `Downloaded: ${downloadPath}`;
|
|
6536
|
+
}
|
|
6537
|
+
case "wait_for": {
|
|
6538
|
+
if (!waitCondition || !waitValue) {
|
|
6539
|
+
return "Error: 'waitCondition' and 'waitValue' parameters are required for wait_for action.";
|
|
6540
|
+
}
|
|
6541
|
+
switch (waitCondition) {
|
|
6542
|
+
case "selector":
|
|
6543
|
+
await page.waitForSelector(waitValue, { timeout });
|
|
6544
|
+
return `Waited for selector: ${waitValue}`;
|
|
6545
|
+
case "text":
|
|
6546
|
+
await page.waitForFunction(
|
|
6547
|
+
(text2) => document.body.innerText.includes(text2),
|
|
6548
|
+
waitValue,
|
|
6549
|
+
{ timeout }
|
|
6550
|
+
);
|
|
6551
|
+
return `Waited for text: ${waitValue}`;
|
|
6552
|
+
case "url":
|
|
6553
|
+
await page.waitForURL(waitValue, { timeout });
|
|
6554
|
+
return `Waited for URL: ${waitValue}`;
|
|
6555
|
+
case "networkidle":
|
|
6556
|
+
await page.waitForLoadState("networkidle", { timeout });
|
|
6557
|
+
return "Waited for network idle";
|
|
6558
|
+
case "timeout":
|
|
6559
|
+
await page.waitForTimeout(parseInt(waitValue));
|
|
6560
|
+
return `Waited for ${waitValue}ms`;
|
|
6561
|
+
default:
|
|
6562
|
+
return `Unknown wait condition: ${waitCondition}`;
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
case "network_logs": {
|
|
6566
|
+
const logs = await page.evaluate(() => {
|
|
6567
|
+
return "Network logging not yet implemented - use browser dev tools";
|
|
6568
|
+
});
|
|
6569
|
+
return logs;
|
|
6570
|
+
}
|
|
6571
|
+
case "console_logs": {
|
|
6572
|
+
const logs = [];
|
|
6573
|
+
page.on("console", (msg) => {
|
|
6574
|
+
logs.push(`[${msg.type()}] ${msg.text()}`);
|
|
6575
|
+
});
|
|
6576
|
+
await page.waitForTimeout(1e3);
|
|
6577
|
+
return logs.length > 0 ? logs.join("\n") : "No console logs";
|
|
6578
|
+
}
|
|
6579
|
+
case "cookies": {
|
|
6580
|
+
if (cookieData) {
|
|
6581
|
+
if (cookieData.action === "set") {
|
|
6582
|
+
await context.addCookies([cookieData.cookie]);
|
|
6583
|
+
return `Cookie set: ${cookieData.cookie.name}`;
|
|
6584
|
+
} else if (cookieData.action === "clear") {
|
|
6585
|
+
await context.clearCookies();
|
|
6586
|
+
return "Cookies cleared";
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
const cookies = await context.cookies();
|
|
6590
|
+
return JSON.stringify(cookies, null, 2);
|
|
6591
|
+
}
|
|
6592
|
+
case "storage": {
|
|
6593
|
+
if (storageData) {
|
|
6594
|
+
const storageType = storageData.type || "localStorage";
|
|
6595
|
+
if (storageData.action === "set") {
|
|
6596
|
+
await page.evaluate(({ type, key, value }) => {
|
|
6597
|
+
if (type === "localStorage") {
|
|
6598
|
+
localStorage.setItem(key, value);
|
|
6599
|
+
} else {
|
|
6600
|
+
sessionStorage.setItem(key, value);
|
|
6601
|
+
}
|
|
6602
|
+
}, { type: storageType, key: storageData.key, value: storageData.value });
|
|
6603
|
+
return `${storageType} set: ${storageData.key}`;
|
|
6604
|
+
} else if (storageData.action === "clear") {
|
|
6605
|
+
await page.evaluate((type) => {
|
|
6606
|
+
if (type === "localStorage") {
|
|
6607
|
+
localStorage.clear();
|
|
6608
|
+
} else {
|
|
6609
|
+
sessionStorage.clear();
|
|
6610
|
+
}
|
|
6611
|
+
}, storageType);
|
|
6612
|
+
return `${storageType} cleared`;
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
const storage = await page.evaluate(() => ({
|
|
6616
|
+
localStorage: Object.fromEntries(Object.entries(localStorage)),
|
|
6617
|
+
sessionStorage: Object.fromEntries(Object.entries(sessionStorage))
|
|
6618
|
+
}));
|
|
6619
|
+
return JSON.stringify(storage, null, 2);
|
|
6620
|
+
}
|
|
6190
6621
|
case "evaluate": {
|
|
6191
6622
|
if (!javascript) {
|
|
6192
6623
|
return "Error: 'javascript' parameter is required for evaluate action.";
|
|
@@ -6200,7 +6631,7 @@ function registerBrowserTools(registry, workspaceDir) {
|
|
|
6200
6631
|
return str ?? "(no result)";
|
|
6201
6632
|
}
|
|
6202
6633
|
default:
|
|
6203
|
-
return `Unknown action: ${action}.
|
|
6634
|
+
return `Unknown action: ${action}. Available actions: extract, screenshot, click, type, evaluate, form_fill, scroll, hover, select, upload, download, pdf, wait_for, network_logs, console_logs, cookies, storage.`;
|
|
6204
6635
|
}
|
|
6205
6636
|
} catch (err) {
|
|
6206
6637
|
return `browse error: ${err.message}`;
|
|
@@ -6214,6 +6645,98 @@ function registerBrowserTools(registry, workspaceDir) {
|
|
|
6214
6645
|
}
|
|
6215
6646
|
}
|
|
6216
6647
|
);
|
|
6648
|
+
registry.register(
|
|
6649
|
+
"browser_session",
|
|
6650
|
+
[
|
|
6651
|
+
"Manage browser sessions for persistent contexts across multiple browse operations.",
|
|
6652
|
+
"Actions:",
|
|
6653
|
+
" list \u2014 list all active browser sessions",
|
|
6654
|
+
" create \u2014 create a new browser session with custom options",
|
|
6655
|
+
" close \u2014 close a specific browser session",
|
|
6656
|
+
" close_all \u2014 close all browser sessions",
|
|
6657
|
+
" info \u2014 get information about a specific session"
|
|
6658
|
+
].join("\n"),
|
|
6659
|
+
{
|
|
6660
|
+
type: "object",
|
|
6661
|
+
properties: {
|
|
6662
|
+
action: {
|
|
6663
|
+
type: "string",
|
|
6664
|
+
enum: ["list", "create", "close", "close_all", "info"],
|
|
6665
|
+
description: "Session management action."
|
|
6666
|
+
},
|
|
6667
|
+
sessionId: {
|
|
6668
|
+
type: "string",
|
|
6669
|
+
description: "Session ID for create, close, or info actions."
|
|
6670
|
+
},
|
|
6671
|
+
options: {
|
|
6672
|
+
type: "object",
|
|
6673
|
+
description: "Browser options for create action (viewport, userAgent, mobile, etc.)."
|
|
6674
|
+
}
|
|
6675
|
+
},
|
|
6676
|
+
required: ["action"]
|
|
6677
|
+
},
|
|
6678
|
+
async (params) => {
|
|
6679
|
+
const action = params.action;
|
|
6680
|
+
const sessionId = params.sessionId;
|
|
6681
|
+
const options = params.options;
|
|
6682
|
+
try {
|
|
6683
|
+
switch (action) {
|
|
6684
|
+
case "list": {
|
|
6685
|
+
const sessions = Array.from(contextInstances.keys());
|
|
6686
|
+
return sessions.length > 0 ? `Active sessions: ${sessions.join(", ")}` : "No active browser sessions";
|
|
6687
|
+
}
|
|
6688
|
+
case "create": {
|
|
6689
|
+
if (!sessionId) {
|
|
6690
|
+
return "Error: 'sessionId' parameter is required for create action.";
|
|
6691
|
+
}
|
|
6692
|
+
if (contextInstances.has(sessionId)) {
|
|
6693
|
+
return `Error: Session '${sessionId}' already exists.`;
|
|
6694
|
+
}
|
|
6695
|
+
await getBrowserContext(sessionId, options || {});
|
|
6696
|
+
return `Created browser session: ${sessionId}`;
|
|
6697
|
+
}
|
|
6698
|
+
case "close": {
|
|
6699
|
+
if (!sessionId) {
|
|
6700
|
+
return "Error: 'sessionId' parameter is required for close action.";
|
|
6701
|
+
}
|
|
6702
|
+
const context = contextInstances.get(sessionId);
|
|
6703
|
+
if (!context) {
|
|
6704
|
+
return `Error: Session '${sessionId}' not found.`;
|
|
6705
|
+
}
|
|
6706
|
+
await context.close();
|
|
6707
|
+
contextInstances.delete(sessionId);
|
|
6708
|
+
return `Closed browser session: ${sessionId}`;
|
|
6709
|
+
}
|
|
6710
|
+
case "close_all": {
|
|
6711
|
+
const sessionCount = contextInstances.size;
|
|
6712
|
+
for (const [id, context] of contextInstances) {
|
|
6713
|
+
try {
|
|
6714
|
+
await context.close();
|
|
6715
|
+
} catch {
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
contextInstances.clear();
|
|
6719
|
+
return `Closed ${sessionCount} browser sessions`;
|
|
6720
|
+
}
|
|
6721
|
+
case "info": {
|
|
6722
|
+
if (!sessionId) {
|
|
6723
|
+
return "Error: 'sessionId' parameter is required for info action.";
|
|
6724
|
+
}
|
|
6725
|
+
const context = contextInstances.get(sessionId);
|
|
6726
|
+
if (!context) {
|
|
6727
|
+
return `Error: Session '${sessionId}' not found.`;
|
|
6728
|
+
}
|
|
6729
|
+
const pages = context.pages();
|
|
6730
|
+
return `Session '${sessionId}': ${pages.length} active pages`;
|
|
6731
|
+
}
|
|
6732
|
+
default:
|
|
6733
|
+
return `Unknown action: ${action}. Available actions: list, create, close, close_all, info.`;
|
|
6734
|
+
}
|
|
6735
|
+
} catch (err) {
|
|
6736
|
+
return `browser_session error: ${err.message}`;
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
);
|
|
6217
6740
|
}
|
|
6218
6741
|
|
|
6219
6742
|
// packages/runtime/src/tools/system.ts
|
|
@@ -7674,8 +8197,8 @@ async function startPipeline(configPath) {
|
|
|
7674
8197
|
}
|
|
7675
8198
|
}
|
|
7676
8199
|
const requestLogger = new RequestLogger(resolve20(dirname8(configPath), "data", "dashboard.db"));
|
|
7677
|
-
startDashboardServer(requestLogger, config.memory);
|
|
7678
8200
|
const agent = new Agent(config);
|
|
8201
|
+
startDashboardServer(requestLogger, config.memory, () => agent.getConversationHistories());
|
|
7679
8202
|
agent.setRequestLogger(requestLogger);
|
|
7680
8203
|
const hivemindHome = process.env.HIVEMIND_HOME || resolve20(process.env.HOME || "/root", "hivemind");
|
|
7681
8204
|
const toolRegistry = registerAllTools(hivemindHome, {
|
|
@@ -8029,7 +8552,7 @@ var WorkerServer = class {
|
|
|
8029
8552
|
return this.handleHealth(res);
|
|
8030
8553
|
}
|
|
8031
8554
|
if (method === "POST" && path === "/assign") {
|
|
8032
|
-
const body = await
|
|
8555
|
+
const body = await readBody2(req);
|
|
8033
8556
|
return this.handleAssign(body, res);
|
|
8034
8557
|
}
|
|
8035
8558
|
if (method === "DELETE" && path.startsWith("/assign/")) {
|
|
@@ -8040,7 +8563,7 @@ var WorkerServer = class {
|
|
|
8040
8563
|
return this.handleStatus(res);
|
|
8041
8564
|
}
|
|
8042
8565
|
if (method === "POST" && path === "/sync/push") {
|
|
8043
|
-
const body = await
|
|
8566
|
+
const body = await readBody2(req);
|
|
8044
8567
|
return this.handleSyncPush(body, res);
|
|
8045
8568
|
}
|
|
8046
8569
|
sendJson(res, 404, { error: "Not found" });
|
|
@@ -8118,7 +8641,7 @@ var WorkerServer = class {
|
|
|
8118
8641
|
sendJson(res, 200, result);
|
|
8119
8642
|
}
|
|
8120
8643
|
};
|
|
8121
|
-
function
|
|
8644
|
+
function readBody2(req) {
|
|
8122
8645
|
return new Promise((resolve21, reject) => {
|
|
8123
8646
|
const chunks = [];
|
|
8124
8647
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -8464,4 +8987,4 @@ smol-toml/dist/index.js:
|
|
|
8464
8987
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
8465
8988
|
*)
|
|
8466
8989
|
*/
|
|
8467
|
-
//# sourceMappingURL=chunk-
|
|
8990
|
+
//# sourceMappingURL=chunk-M3A2WRXM.js.map
|