@hasna/browser 0.4.7 → 0.4.9
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/dist/cli/index.js +349 -248
- package/dist/db/gallery.d.ts.map +1 -1
- package/dist/engines/tui.d.ts.map +1 -1
- package/dist/index.js +121 -96
- package/dist/lib/auth.d.ts +7 -1
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/gallery-diff.d.ts +1 -1
- package/dist/lib/gallery-diff.d.ts.map +1 -1
- package/dist/lib/recorder.d.ts +1 -0
- package/dist/lib/recorder.d.ts.map +1 -1
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/stealth.d.ts.map +1 -1
- package/dist/lib/tui-recording.d.ts +3 -0
- package/dist/lib/tui-recording.d.ts.map +1 -0
- package/dist/mcp/http.d.ts +17 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.test.d.ts +2 -0
- package/dist/mcp/http.test.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +2 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +477 -234
- package/dist/mcp/integration.d.ts.map +1 -1
- package/dist/mcp/tui.d.ts.map +1 -1
- package/dist/server/index.js +103 -101
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../src/mcp/integration.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsBzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../src/mcp/integration.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsBzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAgc3D"}
|
package/dist/mcp/tui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/mcp/tui.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/mcp/tui.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA+IzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,QAwczC"}
|
package/dist/server/index.js
CHANGED
|
@@ -9871,40 +9871,6 @@ var init_schema = __esm(() => {
|
|
|
9871
9871
|
init_dist();
|
|
9872
9872
|
});
|
|
9873
9873
|
|
|
9874
|
-
// src/db/console-log.ts
|
|
9875
|
-
var exports_console_log = {};
|
|
9876
|
-
__export(exports_console_log, {
|
|
9877
|
-
logConsoleMessage: () => logConsoleMessage,
|
|
9878
|
-
getConsoleMessage: () => getConsoleMessage,
|
|
9879
|
-
getConsoleLog: () => getConsoleLog,
|
|
9880
|
-
clearConsoleLog: () => clearConsoleLog
|
|
9881
|
-
});
|
|
9882
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
9883
|
-
function logConsoleMessage(data) {
|
|
9884
|
-
const db = getDatabase();
|
|
9885
|
-
const id = randomUUID3();
|
|
9886
|
-
db.prepare("INSERT INTO console_log (id, session_id, level, message, source, line_number) VALUES (?, ?, ?, ?, ?, ?)").run(id, data.session_id, data.level, data.message, data.source ?? null, data.line_number ?? null);
|
|
9887
|
-
return getConsoleMessage(id);
|
|
9888
|
-
}
|
|
9889
|
-
function getConsoleMessage(id) {
|
|
9890
|
-
const db = getDatabase();
|
|
9891
|
-
return db.query("SELECT * FROM console_log WHERE id = ?").get(id) ?? null;
|
|
9892
|
-
}
|
|
9893
|
-
function getConsoleLog(sessionId, level) {
|
|
9894
|
-
const db = getDatabase();
|
|
9895
|
-
if (level) {
|
|
9896
|
-
return db.query("SELECT * FROM console_log WHERE session_id = ? AND level = ? ORDER BY timestamp ASC").all(sessionId, level);
|
|
9897
|
-
}
|
|
9898
|
-
return db.query("SELECT * FROM console_log WHERE session_id = ? ORDER BY timestamp ASC").all(sessionId);
|
|
9899
|
-
}
|
|
9900
|
-
function clearConsoleLog(sessionId) {
|
|
9901
|
-
const db = getDatabase();
|
|
9902
|
-
db.prepare("DELETE FROM console_log WHERE session_id = ?").run(sessionId);
|
|
9903
|
-
}
|
|
9904
|
-
var init_console_log = __esm(() => {
|
|
9905
|
-
init_schema();
|
|
9906
|
-
});
|
|
9907
|
-
|
|
9908
9874
|
// src/lib/dialogs.ts
|
|
9909
9875
|
var exports_dialogs = {};
|
|
9910
9876
|
__export(exports_dialogs, {
|
|
@@ -17526,7 +17492,7 @@ var init_snapshots = __esm(() => {
|
|
|
17526
17492
|
});
|
|
17527
17493
|
|
|
17528
17494
|
// src/server/index.ts
|
|
17529
|
-
import { join as
|
|
17495
|
+
import { join as join13 } from "path";
|
|
17530
17496
|
import { existsSync as existsSync9 } from "fs";
|
|
17531
17497
|
|
|
17532
17498
|
// src/lib/session.ts
|
|
@@ -18576,6 +18542,16 @@ function selectEngine(useCase, explicit) {
|
|
|
18576
18542
|
return preferred;
|
|
18577
18543
|
}
|
|
18578
18544
|
|
|
18545
|
+
// src/lib/tui-recording.ts
|
|
18546
|
+
var recordingIntervals = new Map;
|
|
18547
|
+
function stopTuiRecording(sessionId) {
|
|
18548
|
+
const intervalId = recordingIntervals.get(sessionId);
|
|
18549
|
+
if (!intervalId)
|
|
18550
|
+
return;
|
|
18551
|
+
clearInterval(intervalId);
|
|
18552
|
+
recordingIntervals.delete(sessionId);
|
|
18553
|
+
}
|
|
18554
|
+
|
|
18579
18555
|
// src/db/network-log.ts
|
|
18580
18556
|
init_schema();
|
|
18581
18557
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -18704,8 +18680,28 @@ function startHAR(page) {
|
|
|
18704
18680
|
};
|
|
18705
18681
|
}
|
|
18706
18682
|
|
|
18683
|
+
// src/db/console-log.ts
|
|
18684
|
+
init_schema();
|
|
18685
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
18686
|
+
function logConsoleMessage(data) {
|
|
18687
|
+
const db = getDatabase();
|
|
18688
|
+
const id = randomUUID3();
|
|
18689
|
+
db.prepare("INSERT INTO console_log (id, session_id, level, message, source, line_number) VALUES (?, ?, ?, ?, ?, ?)").run(id, data.session_id, data.level, data.message, data.source ?? null, data.line_number ?? null);
|
|
18690
|
+
return getConsoleMessage(id);
|
|
18691
|
+
}
|
|
18692
|
+
function getConsoleMessage(id) {
|
|
18693
|
+
const db = getDatabase();
|
|
18694
|
+
return db.query("SELECT * FROM console_log WHERE id = ?").get(id) ?? null;
|
|
18695
|
+
}
|
|
18696
|
+
function getConsoleLog(sessionId, level) {
|
|
18697
|
+
const db = getDatabase();
|
|
18698
|
+
if (level) {
|
|
18699
|
+
return db.query("SELECT * FROM console_log WHERE session_id = ? AND level = ? ORDER BY timestamp ASC").all(sessionId, level);
|
|
18700
|
+
}
|
|
18701
|
+
return db.query("SELECT * FROM console_log WHERE session_id = ? ORDER BY timestamp ASC").all(sessionId);
|
|
18702
|
+
}
|
|
18703
|
+
|
|
18707
18704
|
// src/lib/console.ts
|
|
18708
|
-
init_console_log();
|
|
18709
18705
|
function enableConsoleCapture(page, sessionId) {
|
|
18710
18706
|
const onConsole = (msg) => {
|
|
18711
18707
|
const levelMap = {
|
|
@@ -18782,6 +18778,12 @@ if (!window.chrome.runtime) {
|
|
|
18782
18778
|
id: undefined,
|
|
18783
18779
|
};
|
|
18784
18780
|
}
|
|
18781
|
+
|
|
18782
|
+
// \u2500\u2500 5. Override navigator.userAgent to match HTTP header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
18783
|
+
Object.defineProperty(navigator, 'userAgent', {
|
|
18784
|
+
get: () => "${REALISTIC_USER_AGENT}",
|
|
18785
|
+
configurable: true,
|
|
18786
|
+
});
|
|
18785
18787
|
`;
|
|
18786
18788
|
async function applyStealthPatches(page) {
|
|
18787
18789
|
await page.context().addInitScript(STEALTH_SCRIPT);
|
|
@@ -18826,6 +18828,21 @@ if (dbPruneInterval.unref)
|
|
|
18826
18828
|
function createBunProxy(view) {
|
|
18827
18829
|
return view;
|
|
18828
18830
|
}
|
|
18831
|
+
function attachPlaywrightListeners(page, sessionId, cleanups, opts = {}) {
|
|
18832
|
+
if (opts.captureNetwork !== false) {
|
|
18833
|
+
try {
|
|
18834
|
+
cleanups.push(enableNetworkLogging(page, sessionId));
|
|
18835
|
+
} catch {}
|
|
18836
|
+
}
|
|
18837
|
+
if (opts.captureConsole !== false) {
|
|
18838
|
+
try {
|
|
18839
|
+
cleanups.push(enableConsoleCapture(page, sessionId));
|
|
18840
|
+
} catch {}
|
|
18841
|
+
}
|
|
18842
|
+
try {
|
|
18843
|
+
cleanups.push(setupDialogHandler(page, sessionId));
|
|
18844
|
+
} catch {}
|
|
18845
|
+
}
|
|
18829
18846
|
async function createSession2(opts = {}) {
|
|
18830
18847
|
if (opts.cdpUrl) {
|
|
18831
18848
|
const { connectToExistingBrowser: connectToExistingBrowser2 } = await Promise.resolve().then(() => (init_cdp(), exports_cdp));
|
|
@@ -18863,9 +18880,11 @@ async function createSession2(opts = {}) {
|
|
|
18863
18880
|
let browser = null;
|
|
18864
18881
|
let bunView = null;
|
|
18865
18882
|
let page;
|
|
18883
|
+
let actualEngine = resolvedEngine;
|
|
18866
18884
|
if (resolvedEngine === "bun") {
|
|
18867
18885
|
if (!isBunWebViewAvailable()) {
|
|
18868
18886
|
console.warn("[browser] Bun.WebView requested but not available \u2014 falling back to playwright. Run: bun upgrade --canary");
|
|
18887
|
+
actualEngine = "playwright";
|
|
18869
18888
|
browser = await launchPlaywright({ headless: opts.headless ?? true, viewport: opts.viewport, userAgent: opts.userAgent });
|
|
18870
18889
|
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
18871
18890
|
} else {
|
|
@@ -18885,9 +18904,11 @@ async function createSession2(opts = {}) {
|
|
|
18885
18904
|
}
|
|
18886
18905
|
if (!bunWorks) {
|
|
18887
18906
|
console.warn("[browser] Bun.WebView exists but Chrome not available \u2014 falling back to playwright");
|
|
18907
|
+
actualEngine = "playwright";
|
|
18888
18908
|
browser = await launchPlaywright({ headless: opts.headless ?? true, viewport: opts.viewport, userAgent: opts.userAgent });
|
|
18889
18909
|
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
18890
18910
|
} else {
|
|
18911
|
+
actualEngine = "bun";
|
|
18891
18912
|
bunView = testView;
|
|
18892
18913
|
if (opts.stealth) {}
|
|
18893
18914
|
page = createBunProxy(bunView);
|
|
@@ -18917,19 +18938,10 @@ async function createSession2(opts = {}) {
|
|
|
18917
18938
|
});
|
|
18918
18939
|
const cleanups2 = [];
|
|
18919
18940
|
cleanups2.push(() => closeTui(tuiSess));
|
|
18920
|
-
|
|
18921
|
-
|
|
18922
|
-
|
|
18923
|
-
|
|
18924
|
-
}
|
|
18925
|
-
if (opts.captureConsole !== false) {
|
|
18926
|
-
try {
|
|
18927
|
-
cleanups2.push(enableConsoleCapture(page, session2.id));
|
|
18928
|
-
} catch {}
|
|
18929
|
-
}
|
|
18930
|
-
try {
|
|
18931
|
-
cleanups2.push(setupDialogHandler(page, session2.id));
|
|
18932
|
-
} catch {}
|
|
18941
|
+
attachPlaywrightListeners(page, session2.id, cleanups2, {
|
|
18942
|
+
captureNetwork: opts.captureNetwork,
|
|
18943
|
+
captureConsole: opts.captureConsole
|
|
18944
|
+
});
|
|
18933
18945
|
handles.set(session2.id, { browser, bunView: null, tuiSession: tuiSess, page, engine: "tui", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false, startUrl: opts.startUrl ?? "bash" });
|
|
18934
18946
|
return { session: session2, page };
|
|
18935
18947
|
} else {
|
|
@@ -18959,7 +18971,7 @@ async function createSession2(opts = {}) {
|
|
|
18959
18971
|
}
|
|
18960
18972
|
})() : undefined);
|
|
18961
18973
|
const session = createSession({
|
|
18962
|
-
engine:
|
|
18974
|
+
engine: actualEngine,
|
|
18963
18975
|
projectId: opts.projectId,
|
|
18964
18976
|
agentId: opts.agentId,
|
|
18965
18977
|
startUrl: opts.startUrl,
|
|
@@ -18972,37 +18984,12 @@ async function createSession2(opts = {}) {
|
|
|
18972
18984
|
}
|
|
18973
18985
|
const cleanups = [];
|
|
18974
18986
|
if (!bunView) {
|
|
18975
|
-
|
|
18976
|
-
|
|
18977
|
-
|
|
18978
|
-
|
|
18979
|
-
}
|
|
18980
|
-
if (opts.captureConsole !== false) {
|
|
18981
|
-
try {
|
|
18982
|
-
cleanups.push(enableConsoleCapture(page, session.id));
|
|
18983
|
-
} catch {}
|
|
18984
|
-
}
|
|
18985
|
-
try {
|
|
18986
|
-
cleanups.push(setupDialogHandler(page, session.id));
|
|
18987
|
-
} catch {}
|
|
18988
|
-
} else {
|
|
18989
|
-
if (opts.captureConsole !== false) {
|
|
18990
|
-
try {
|
|
18991
|
-
const { logConsoleMessage: logConsoleMessage2 } = await Promise.resolve().then(() => (init_console_log(), exports_console_log));
|
|
18992
|
-
await bunView.addInitScript(`
|
|
18993
|
-
(() => {
|
|
18994
|
-
const orig = { log: console.log, warn: console.warn, error: console.error, debug: console.debug, info: console.info };
|
|
18995
|
-
['log','warn','error','debug','info'].forEach(level => {
|
|
18996
|
-
console[level] = (...args) => {
|
|
18997
|
-
orig[level](...args);
|
|
18998
|
-
};
|
|
18999
|
-
});
|
|
19000
|
-
})()
|
|
19001
|
-
`);
|
|
19002
|
-
} catch {}
|
|
19003
|
-
}
|
|
18987
|
+
attachPlaywrightListeners(page, session.id, cleanups, {
|
|
18988
|
+
captureNetwork: opts.captureNetwork,
|
|
18989
|
+
captureConsole: opts.captureConsole
|
|
18990
|
+
});
|
|
19004
18991
|
}
|
|
19005
|
-
handles.set(session.id, { browser, bunView, tuiSession: null, page, engine:
|
|
18992
|
+
handles.set(session.id, { browser, bunView, tuiSession: null, page, engine: actualEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false, startUrl: opts.startUrl ?? "" });
|
|
19006
18993
|
if (opts.startUrl) {
|
|
19007
18994
|
try {
|
|
19008
18995
|
if (bunView) {
|
|
@@ -19035,9 +19022,12 @@ async function closeSession2(sessionId) {
|
|
|
19035
19022
|
const handle = handles.get(sessionId);
|
|
19036
19023
|
try {
|
|
19037
19024
|
if (handle) {
|
|
19025
|
+
if (handle.engine === "tui") {
|
|
19026
|
+
stopTuiRecording(sessionId);
|
|
19027
|
+
}
|
|
19038
19028
|
for (const cleanup of handle.cleanups) {
|
|
19039
19029
|
try {
|
|
19040
|
-
cleanup();
|
|
19030
|
+
await cleanup();
|
|
19041
19031
|
} catch {}
|
|
19042
19032
|
}
|
|
19043
19033
|
if (handle.bunView) {
|
|
@@ -19162,16 +19152,23 @@ async function extract(page, opts = {}) {
|
|
|
19162
19152
|
// src/lib/screenshot.ts
|
|
19163
19153
|
init_types();
|
|
19164
19154
|
var import_sharp = __toESM(require_lib3(), 1);
|
|
19165
|
-
import { join as
|
|
19155
|
+
import { join as join10 } from "path";
|
|
19166
19156
|
import { mkdirSync as mkdirSync7 } from "fs";
|
|
19167
19157
|
|
|
19168
19158
|
// src/db/gallery.ts
|
|
19169
19159
|
init_schema();
|
|
19170
19160
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
19161
|
+
import { join as join9, resolve, relative as relative2, isAbsolute } from "path";
|
|
19171
19162
|
function validateDataPath(filePath) {
|
|
19172
19163
|
if (filePath.includes("..")) {
|
|
19173
19164
|
throw new Error(`File path must not contain '..': ${filePath}`);
|
|
19174
19165
|
}
|
|
19166
|
+
const dataDir = resolve(getDataDir2());
|
|
19167
|
+
const resolved = resolve(isAbsolute(filePath) ? filePath : join9(dataDir, filePath));
|
|
19168
|
+
const rel = relative2(dataDir, resolved);
|
|
19169
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
19170
|
+
throw new Error(`File path must be within data directory: ${filePath}`);
|
|
19171
|
+
}
|
|
19175
19172
|
return filePath;
|
|
19176
19173
|
}
|
|
19177
19174
|
function deserialize(row) {
|
|
@@ -19189,7 +19186,13 @@ function deserialize(row) {
|
|
|
19189
19186
|
original_size_bytes: row.original_size_bytes ?? undefined,
|
|
19190
19187
|
compressed_size_bytes: row.compressed_size_bytes ?? undefined,
|
|
19191
19188
|
compression_ratio: row.compression_ratio ?? undefined,
|
|
19192
|
-
tags:
|
|
19189
|
+
tags: (() => {
|
|
19190
|
+
try {
|
|
19191
|
+
return JSON.parse(row.tags);
|
|
19192
|
+
} catch {
|
|
19193
|
+
return [];
|
|
19194
|
+
}
|
|
19195
|
+
})(),
|
|
19193
19196
|
notes: row.notes ?? undefined,
|
|
19194
19197
|
is_favorite: row.is_favorite === 1,
|
|
19195
19198
|
created_at: row.created_at
|
|
@@ -19303,9 +19306,9 @@ function getGalleryStats(projectId) {
|
|
|
19303
19306
|
// src/lib/screenshot.ts
|
|
19304
19307
|
init_schema();
|
|
19305
19308
|
function getScreenshotDir(projectId) {
|
|
19306
|
-
const base =
|
|
19309
|
+
const base = join10(getDataDir2(), "screenshots");
|
|
19307
19310
|
const date = new Date().toISOString().split("T")[0];
|
|
19308
|
-
const dir = projectId ?
|
|
19311
|
+
const dir = projectId ? join10(base, projectId, date) : join10(base, date);
|
|
19309
19312
|
mkdirSync7(dir, { recursive: true });
|
|
19310
19313
|
return dir;
|
|
19311
19314
|
}
|
|
@@ -19321,7 +19324,7 @@ async function compressBuffer(raw, format, quality, maxWidth) {
|
|
|
19321
19324
|
}
|
|
19322
19325
|
}
|
|
19323
19326
|
async function generateThumbnail(raw, dir, stem) {
|
|
19324
|
-
const thumbPath =
|
|
19327
|
+
const thumbPath = join10(dir, `${stem}.thumb.webp`);
|
|
19325
19328
|
const thumbBuffer = await import_sharp.default(raw).resize({ width: 200, withoutEnlargement: true }).webp({ quality: 70, effort: 3 }).toBuffer();
|
|
19326
19329
|
await Bun.write(thumbPath, thumbBuffer);
|
|
19327
19330
|
return { path: thumbPath, base64: thumbBuffer.toString("base64") };
|
|
@@ -19389,7 +19392,7 @@ async function takeScreenshot(page, opts) {
|
|
|
19389
19392
|
const compressedSizeBytes = finalBuffer.length;
|
|
19390
19393
|
const compressionRatio = originalSizeBytes > 0 ? compressedSizeBytes / originalSizeBytes : 1;
|
|
19391
19394
|
const ext = format;
|
|
19392
|
-
const screenshotPath = opts?.path ??
|
|
19395
|
+
const screenshotPath = opts?.path ?? join10(dir, `${stem}.${ext}`);
|
|
19393
19396
|
await Bun.write(screenshotPath, finalBuffer);
|
|
19394
19397
|
let thumbnailPath;
|
|
19395
19398
|
let thumbnailBase64;
|
|
@@ -19679,16 +19682,15 @@ function listProjects() {
|
|
|
19679
19682
|
}
|
|
19680
19683
|
|
|
19681
19684
|
// src/server/index.ts
|
|
19682
|
-
init_console_log();
|
|
19683
19685
|
init_recordings();
|
|
19684
19686
|
|
|
19685
19687
|
// src/lib/downloads.ts
|
|
19686
19688
|
init_schema();
|
|
19687
|
-
import { join as
|
|
19689
|
+
import { join as join11, basename, extname } from "path";
|
|
19688
19690
|
import { mkdirSync as mkdirSync8, existsSync as existsSync7, readdirSync as readdirSync6, statSync as statSync2, unlinkSync as unlinkSync2, copyFileSync as copyFileSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
19689
19691
|
function getDownloadsDir(sessionId) {
|
|
19690
|
-
const base =
|
|
19691
|
-
const dir = sessionId ?
|
|
19692
|
+
const base = join11(getDataDir2(), "downloads");
|
|
19693
|
+
const dir = sessionId ? join11(base, sessionId) : base;
|
|
19692
19694
|
mkdirSync8(dir, { recursive: true });
|
|
19693
19695
|
return dir;
|
|
19694
19696
|
}
|
|
@@ -19705,7 +19707,7 @@ function listDownloads(sessionId) {
|
|
|
19705
19707
|
for (const entry of entries) {
|
|
19706
19708
|
if (entry.endsWith(".meta.json"))
|
|
19707
19709
|
continue;
|
|
19708
|
-
const full =
|
|
19710
|
+
const full = join11(d, entry);
|
|
19709
19711
|
const stat = statSync2(full);
|
|
19710
19712
|
if (stat.isDirectory()) {
|
|
19711
19713
|
scanDir(full);
|
|
@@ -19767,9 +19769,9 @@ function cleanStaleDownloads(olderThanDays = 7) {
|
|
|
19767
19769
|
// src/lib/gallery-diff.ts
|
|
19768
19770
|
init_schema();
|
|
19769
19771
|
var import_sharp2 = __toESM(require_lib3(), 1);
|
|
19770
|
-
import { join as
|
|
19772
|
+
import { join as join12 } from "path";
|
|
19771
19773
|
import { mkdirSync as mkdirSync9 } from "fs";
|
|
19772
|
-
async function diffImages(path1, path2) {
|
|
19774
|
+
async function diffImages(path1, path2, threshold = 10) {
|
|
19773
19775
|
const img1 = import_sharp2.default(path1);
|
|
19774
19776
|
const img2 = import_sharp2.default(path2);
|
|
19775
19777
|
const [meta1, meta2] = await Promise.all([img1.metadata(), img2.metadata()]);
|
|
@@ -19788,7 +19790,7 @@ async function diffImages(path1, path2) {
|
|
|
19788
19790
|
const dg = Math.abs(raw1[i + 1] - raw2[i + 1]);
|
|
19789
19791
|
const db = Math.abs(raw1[i + 2] - raw2[i + 2]);
|
|
19790
19792
|
const diff = (dr + dg + db) / 3;
|
|
19791
|
-
if (diff >
|
|
19793
|
+
if (diff > threshold) {
|
|
19792
19794
|
changedPixels++;
|
|
19793
19795
|
diffBuffer[i] = 255;
|
|
19794
19796
|
diffBuffer[i + 1] = 0;
|
|
@@ -19800,9 +19802,9 @@ async function diffImages(path1, path2) {
|
|
|
19800
19802
|
}
|
|
19801
19803
|
}
|
|
19802
19804
|
const dataDir = getDataDir2();
|
|
19803
|
-
const diffDir =
|
|
19805
|
+
const diffDir = join12(dataDir, "diffs");
|
|
19804
19806
|
mkdirSync9(diffDir, { recursive: true });
|
|
19805
|
-
const diffPath =
|
|
19807
|
+
const diffPath = join12(diffDir, `diff-${Date.now()}.webp`);
|
|
19806
19808
|
const diffImageBuffer = await import_sharp2.default(diffBuffer, { raw: { width: w, height: h, channels } }).webp({ quality: 85 }).toBuffer();
|
|
19807
19809
|
await Bun.write(diffPath, diffImageBuffer);
|
|
19808
19810
|
return {
|
|
@@ -20182,19 +20184,19 @@ var server = Bun.serve({
|
|
|
20182
20184
|
const id = path.split("/")[3];
|
|
20183
20185
|
return ok({ deleted: deleteDownload(id) });
|
|
20184
20186
|
}
|
|
20185
|
-
const dashboardDist =
|
|
20187
|
+
const dashboardDist = join13(import.meta.dir, "../../dashboard/dist");
|
|
20186
20188
|
if (existsSync9(dashboardDist)) {
|
|
20187
20189
|
const cleanPath = path.replace(/^\//, "");
|
|
20188
20190
|
if (cleanPath.includes("..") || cleanPath.startsWith("/"))
|
|
20189
20191
|
return notFound("Not found", headers);
|
|
20190
|
-
const filePath = path === "/" ?
|
|
20191
|
-
const resolved = await Bun.file(filePath).arrayBuffer().then(() =>
|
|
20192
|
+
const filePath = path === "/" ? join13(dashboardDist, "index.html") : join13(dashboardDist, cleanPath);
|
|
20193
|
+
const resolved = await Bun.file(filePath).arrayBuffer().then(() => join13(dashboardDist, cleanPath)) || "";
|
|
20192
20194
|
if (!resolved.startsWith(dashboardDist))
|
|
20193
20195
|
return notFound("Not found", headers);
|
|
20194
20196
|
if (existsSync9(filePath)) {
|
|
20195
20197
|
return new Response(Bun.file(filePath), { headers });
|
|
20196
20198
|
}
|
|
20197
|
-
return new Response(Bun.file(
|
|
20199
|
+
return new Response(Bun.file(join13(dashboardDist, "index.html")), { headers });
|
|
20198
20200
|
}
|
|
20199
20201
|
if (path === "/" || path === "") {
|
|
20200
20202
|
return new Response("@hasna/browser REST API running. Dashboard not built.", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/browser",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "General-purpose browser agent toolkit — Playwright, Chrome DevTools Protocol, Lightpanda with auto engine selection. CLI + MCP + REST + SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|