@melaya/runner 1.1.13 → 1.1.16
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/browserBridge.js +158 -19
- package/package.json +44 -44
package/dist/browserBridge.js
CHANGED
|
@@ -59,17 +59,136 @@ const MELAYA_BRAND_SCRIPT = `(() => {
|
|
|
59
59
|
if (document.getElementById(ID + "_border")) return;
|
|
60
60
|
const style = document.createElement("style");
|
|
61
61
|
style.id = ID + "_style";
|
|
62
|
-
style.textContent = "@keyframes melBrandPulse{0%,100%{box-shadow:inset 0 0 9px 2px rgba(34,211,238,.5),inset 0 0 30px 7px rgba(124,111,240,.18)}50%{box-shadow:inset 0 0 16px 4px rgba(34,211,238,.8),inset 0 0 54px 13px rgba(124,111,240,.32)}}@keyframes melBrandGlow{0%,100%{opacity:.78}50%{opacity:1}}#" + ID + "_border{position:fixed;inset:0;pointer-events:none;z-index:2147483647;border-radius:7px;animation:melBrandPulse 2.6s ease-in-out infinite}#" + ID + "_badge{position:fixed;top:10px;left:50%;transform:translateX(-50%);pointer-events:none;z-index:2147483647;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-weight:600;font-size:12px;line-height:1;white-space:nowrap;max-width:calc(100vw - 24px);box-sizing:border-box;color:#E2E8F0;background:linear-gradient(180deg,rgba(15,23,42,.94),rgba(15,23,42,.82));padding:5px 13px 5px 7px;border-radius:999px;letter-spacing:.2px;border:1px solid rgba(34,211,238,.45);box-shadow:0 0 16px 1px rgba(34,211,238,.4),0 4px 14px rgba(0,0,0,.45);display:flex;align-items:center;gap:8px;backdrop-filter:blur(6px);animation:melBrandGlow 2.6s ease-in-out infinite}#" + ID + "_logo{height:16px;width:auto;display:block;filter:drop-shadow(0 0 4px rgba(34,211,238,.55))}@media (max-width:600px){#" + ID + "_badge{font-size:10px;padding:4px 10px 4px 6px;gap:6px;top:6px}#" + ID + "_logo{height:13px}}";
|
|
62
|
+
style.textContent = "@keyframes melBrandPulse{0%,100%{box-shadow:inset 0 0 9px 2px rgba(34,211,238,.5),inset 0 0 30px 7px rgba(124,111,240,.18)}50%{box-shadow:inset 0 0 16px 4px rgba(34,211,238,.8),inset 0 0 54px 13px rgba(124,111,240,.32)}}@keyframes melBrandGlow{0%,100%{opacity:.78}50%{opacity:1}}#" + ID + "_border{position:fixed;inset:0;pointer-events:none;z-index:2147483647;border-radius:7px;animation:melBrandPulse 2.6s ease-in-out infinite}#" + ID + "_badge{position:fixed;top:10px;left:50%;transform:translateX(-50%);pointer-events:none;z-index:2147483647;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-weight:600;font-size:12px;line-height:1;white-space:nowrap;max-width:calc(100vw - 24px);box-sizing:border-box;color:#E2E8F0;background:linear-gradient(180deg,rgba(15,23,42,.94),rgba(15,23,42,.82));padding:5px 13px 5px 7px;border-radius:999px;letter-spacing:.2px;border:1px solid rgba(34,211,238,.45);box-shadow:0 0 16px 1px rgba(34,211,238,.4),0 4px 14px rgba(0,0,0,.45);display:flex;align-items:center;gap:8px;backdrop-filter:blur(6px);animation:melBrandGlow 2.6s ease-in-out infinite}#" + ID + "_logo{height:16px;width:auto;display:block;filter:drop-shadow(0 0 4px rgba(34,211,238,.55))}#" + ID + "_label{display:inline-flex;align-items:center;height:16px;line-height:1}@media (max-width:600px){#" + ID + "_badge{font-size:10px;padding:4px 10px 4px 6px;gap:6px;top:6px}#" + ID + "_logo{height:13px}#" + ID + "_label{height:13px}}";
|
|
63
63
|
(document.head || root).appendChild(style);
|
|
64
64
|
const border = document.createElement("div"); border.id = ID + "_border";
|
|
65
65
|
const badge = document.createElement("div"); badge.id = ID + "_badge";
|
|
66
66
|
const logo = document.createElement("img"); logo.id = ID + "_logo"; logo.src = LOGO; logo.alt = "Melaya";
|
|
67
|
-
|
|
67
|
+
const label = document.createElement("span"); label.id = ID + "_label"; label.textContent = "Controlled by Melaya";
|
|
68
|
+
badge.appendChild(logo); badge.appendChild(label);
|
|
68
69
|
root.appendChild(border); root.appendChild(badge);
|
|
69
70
|
};
|
|
70
71
|
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", inject); else inject();
|
|
71
72
|
try { new MutationObserver(() => { if (!document.getElementById(ID + "_border")) inject(); }).observe(document.documentElement, { childList: true }); } catch (e) {}
|
|
72
73
|
})();`;
|
|
74
|
+
// ── Stealth init script ────────────────────────────────────────────────────
|
|
75
|
+
// Runs in EVERY frame BEFORE page scripts (context.addInitScript).
|
|
76
|
+
//
|
|
77
|
+
// CRITICAL: this is a HEADED real Brave/Chrome, so its fingerprint (plugins,
|
|
78
|
+
// GPU, cores, memory, UA) is ALREADY genuine. Overriding those would INTRODUCE
|
|
79
|
+
// inconsistencies a detector catches (e.g. a spoofed WebGL vendor that disagrees
|
|
80
|
+
// with the real driver, or a patched function whose toString is not native).
|
|
81
|
+
// So we touch ONLY the two things a CDP session genuinely leaks:
|
|
82
|
+
// 1. navigator.webdriver (can read truthy under CDP), and
|
|
83
|
+
// 2. the Notification.permission vs permissions.query mismatch CDP introduces.
|
|
84
|
+
// Any function we replace is given a native-looking toString so the patch is not
|
|
85
|
+
// itself a tell. We do NOT fake plugins/webgl/hardware/languages/chrome — a real
|
|
86
|
+
// headed browser already has correct, self-consistent values.
|
|
87
|
+
const STEALTH_SCRIPT = `(() => {
|
|
88
|
+
const def = (o, k, get) => { try { Object.defineProperty(o, k, { get, configurable: true }); } catch (e) {} };
|
|
89
|
+
const nativeToString = (fn, name) => {
|
|
90
|
+
try {
|
|
91
|
+
const s = "function " + name + "() { [native code] }";
|
|
92
|
+
Object.defineProperty(fn, "toString", { value: function () { return s; }, configurable: true, writable: true });
|
|
93
|
+
} catch (e) {}
|
|
94
|
+
};
|
|
95
|
+
// 1. navigator.webdriver → false. The launch flag disables it, but some CDP
|
|
96
|
+
// builds still expose it truthy; keep it false as a belt-and-suspenders.
|
|
97
|
+
def(Object.getPrototypeOf(navigator) || navigator, "webdriver", () => false);
|
|
98
|
+
try { delete Navigator.prototype.webdriver; } catch (e) {}
|
|
99
|
+
// 2. Notification.permission must agree with permissions.query (CDP mismatch).
|
|
100
|
+
try {
|
|
101
|
+
const q = navigator.permissions && navigator.permissions.query ? navigator.permissions.query.bind(navigator.permissions) : null;
|
|
102
|
+
if (q) {
|
|
103
|
+
const patched = function (p) {
|
|
104
|
+
return (p && p.name === "notifications")
|
|
105
|
+
? Promise.resolve({ state: (typeof Notification !== "undefined" ? Notification.permission : "default"), onchange: null })
|
|
106
|
+
: q(p);
|
|
107
|
+
};
|
|
108
|
+
nativeToString(patched, "query");
|
|
109
|
+
navigator.permissions.query = patched;
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {}
|
|
112
|
+
})();`;
|
|
113
|
+
// ── Humanized input helpers ────────────────────────────────────────────────
|
|
114
|
+
// Instant teleport-clicks and zero-delay typing are a behavioural bot tell.
|
|
115
|
+
// These move the pointer along an eased, jittered Bezier path, dwell before
|
|
116
|
+
// pressing, type with variable per-key cadence, and scroll in small steps.
|
|
117
|
+
// (PWPage is already aliased above to import("playwright").Page.)
|
|
118
|
+
const _rand = (min, max) => min + Math.random() * (max - min);
|
|
119
|
+
const _randInt = (min, max) => Math.floor(_rand(min, max + 1));
|
|
120
|
+
const _lastMouse = new WeakMap();
|
|
121
|
+
async function humanPause(page, min = 40, max = 140) {
|
|
122
|
+
await page.waitForTimeout(_randInt(min, max));
|
|
123
|
+
}
|
|
124
|
+
// Eased, jittered pointer travel from the last known position to (tx,ty).
|
|
125
|
+
async function humanMove(page, tx, ty) {
|
|
126
|
+
const vp = page.viewportSize() ?? { width: 1280, height: 800 };
|
|
127
|
+
const from = _lastMouse.get(page) ?? { x: _rand(0, vp.width), y: _rand(0, vp.height) };
|
|
128
|
+
const dist = Math.hypot(tx - from.x, ty - from.y);
|
|
129
|
+
const steps = Math.max(6, Math.min(42, Math.round(dist / _rand(18, 34))));
|
|
130
|
+
const curve = Math.min(90, dist * 0.28);
|
|
131
|
+
const midX = (from.x + tx) / 2 + _rand(-1, 1) * curve;
|
|
132
|
+
const midY = (from.y + ty) / 2 + _rand(-1, 1) * curve;
|
|
133
|
+
for (let i = 1; i <= steps; i++) {
|
|
134
|
+
const t = i / steps;
|
|
135
|
+
const e = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; // easeInOutQuad
|
|
136
|
+
const mt = 1 - e;
|
|
137
|
+
let x = mt * mt * from.x + 2 * mt * e * midX + e * e * tx;
|
|
138
|
+
let y = mt * mt * from.y + 2 * mt * e * midY + e * e * ty;
|
|
139
|
+
if (i < steps) {
|
|
140
|
+
x += _rand(-1.2, 1.2);
|
|
141
|
+
y += _rand(-1.2, 1.2);
|
|
142
|
+
} // hand tremor
|
|
143
|
+
await page.mouse.move(x, y);
|
|
144
|
+
if (i % _randInt(3, 5) === 0)
|
|
145
|
+
await page.waitForTimeout(_randInt(4, 16));
|
|
146
|
+
}
|
|
147
|
+
_lastMouse.set(page, { x: tx, y: ty });
|
|
148
|
+
}
|
|
149
|
+
// Move to a point a few px off the exact center, dwell, then press/release.
|
|
150
|
+
async function humanClick(page, x, y, clickCount = 1) {
|
|
151
|
+
const jx = x + _rand(-3, 3), jy = y + _rand(-3, 3);
|
|
152
|
+
await humanMove(page, jx, jy);
|
|
153
|
+
await humanPause(page, 30, 120);
|
|
154
|
+
for (let c = 0; c < clickCount; c++) {
|
|
155
|
+
await page.mouse.down();
|
|
156
|
+
await page.waitForTimeout(_randInt(40, 110)); // press dwell
|
|
157
|
+
await page.mouse.up();
|
|
158
|
+
if (c + 1 < clickCount)
|
|
159
|
+
await page.waitForTimeout(_randInt(60, 130));
|
|
160
|
+
}
|
|
161
|
+
_lastMouse.set(page, { x: jx, y: jy });
|
|
162
|
+
}
|
|
163
|
+
// Type with variable cadence: slower on spaces, longer at punctuation, and an
|
|
164
|
+
// occasional "thinking" pause.
|
|
165
|
+
async function humanType(page, text) {
|
|
166
|
+
for (const ch of text) {
|
|
167
|
+
await page.keyboard.type(ch);
|
|
168
|
+
let d = _rand(45, 150);
|
|
169
|
+
if (ch === " ")
|
|
170
|
+
d += _rand(10, 60);
|
|
171
|
+
if (/[.,!?;:]/.test(ch))
|
|
172
|
+
d += _rand(80, 220);
|
|
173
|
+
if (Math.random() < 0.04)
|
|
174
|
+
d += _rand(180, 450);
|
|
175
|
+
await page.waitForTimeout(Math.round(d));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Break a wheel scroll into a few smaller, jittered steps with pauses.
|
|
179
|
+
async function humanScroll(page, dx, dy) {
|
|
180
|
+
const steps = _randInt(3, 6);
|
|
181
|
+
let sx = 0, sy = 0;
|
|
182
|
+
for (let i = 0; i < steps; i++) {
|
|
183
|
+
const px = Math.round(dx / steps + _rand(-8, 8));
|
|
184
|
+
const py = Math.round(dy / steps + _rand(-14, 14));
|
|
185
|
+
await page.mouse.wheel(px, py);
|
|
186
|
+
sx += px;
|
|
187
|
+
sy += py;
|
|
188
|
+
await page.waitForTimeout(_randInt(40, 130));
|
|
189
|
+
}
|
|
190
|
+
return { dx: sx, dy: sy };
|
|
191
|
+
}
|
|
73
192
|
const MAX_BODY_BYTES = 512 * 1024; // /browser/run scripts included
|
|
74
193
|
const NODE_CAP = 240; // snapshot node cap (Section 8)
|
|
75
194
|
const SCREENSHOT_MAX_B64 = 1_400_000; // ~1 MB binary; ladder recompresses
|
|
@@ -260,17 +379,24 @@ export async function startBrowserBridge(opts) {
|
|
|
260
379
|
headless: reg.spec.headless === true,
|
|
261
380
|
viewport: { width: 1280, height: 800 },
|
|
262
381
|
acceptDownloads: false, // downloads default-denied (Section 10)
|
|
382
|
+
ignoreDefaultArgs: ["--enable-automation"],
|
|
263
383
|
args: [
|
|
264
384
|
"--no-first-run",
|
|
265
385
|
"--no-default-browser-check",
|
|
266
386
|
"--disable-background-networking",
|
|
267
387
|
"--disable-sync",
|
|
388
|
+
"--disable-blink-features=AutomationControlled",
|
|
268
389
|
],
|
|
269
390
|
});
|
|
270
391
|
sessions.attachHandles(rec, {
|
|
271
392
|
context,
|
|
272
393
|
ephemeralUserDataDir: ephemeral ? userDataDir : null,
|
|
273
394
|
});
|
|
395
|
+
// Stealth patches before any page script (see STEALTH_SCRIPT).
|
|
396
|
+
try {
|
|
397
|
+
await context.addInitScript(STEALTH_SCRIPT);
|
|
398
|
+
}
|
|
399
|
+
catch { /* non-fatal */ }
|
|
274
400
|
await enforceOnContext(context, reg.policy, hooks);
|
|
275
401
|
const page = context.pages()[0] ?? (await context.newPage());
|
|
276
402
|
sessions.leaseTarget(rec, reg.spec.grant.target.ref, page);
|
|
@@ -337,13 +463,17 @@ export async function startBrowserBridge(opts) {
|
|
|
337
463
|
// Normalise engine to a known BrowserEngineId; default to "chrome".
|
|
338
464
|
const knownEngines = new Set(["chrome", "edge", "brave", "chromium"]);
|
|
339
465
|
const engineId = (knownEngines.has(engine) ? engine : "chrome");
|
|
340
|
-
// Resolve profile directory
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
466
|
+
// Resolve profile directory. The dedicated Melaya profile is PERSISTENT (a
|
|
467
|
+
// stable per-engine dir under ~/.melaya-runner/browser-profiles/), NOT a
|
|
468
|
+
// fresh per-session temp dir. Two reasons, both about real-world usability
|
|
469
|
+
// and anti-bot survival:
|
|
470
|
+
// 1. Logins persist. A fresh profile forces the user to sign in to every
|
|
471
|
+
// site on every launch, under bot scrutiny — the "can't log in" pain.
|
|
472
|
+
// 2. A cookieless, history-less profile is itself a strong bot signal.
|
|
473
|
+
// A warmed profile that accrues real cookies/history looks human.
|
|
474
|
+
// It is still isolated from the user's PERSONAL browser profile (separate
|
|
475
|
+
// dir), so this is a dedicated-but-warm profile, not their everyday one.
|
|
476
|
+
const spaceSpec = { kind: "persistent", id: `browser-profiles-${engineId}-${profile}` };
|
|
347
477
|
// Reject duplicate launches for the same sessionId.
|
|
348
478
|
if (interactiveSessions.has(sessionId)) {
|
|
349
479
|
log(`[browser-launch] sessionId=${sessionId.slice(0, 16)} already active; ignoring duplicate launch`);
|
|
@@ -401,6 +531,14 @@ export async function startBrowserBridge(opts) {
|
|
|
401
531
|
// all navigations and new pages. The one-shot evaluate covers the
|
|
402
532
|
// already-open page (typically about:blank) immediately.
|
|
403
533
|
// Both are wrapped so a branding failure cannot fail the launch.
|
|
534
|
+
// Stealth FIRST (runs before page + brand scripts in every frame) so the
|
|
535
|
+
// navigator/webgl patches are in place before any site fingerprinting.
|
|
536
|
+
try {
|
|
537
|
+
await context.addInitScript(STEALTH_SCRIPT);
|
|
538
|
+
}
|
|
539
|
+
catch (stealthErr) {
|
|
540
|
+
log(`[browser-launch] stealth init-script install failed (non-fatal): ${stealthErr?.message || stealthErr}`);
|
|
541
|
+
}
|
|
404
542
|
try {
|
|
405
543
|
await context.addInitScript(MELAYA_BRAND_SCRIPT);
|
|
406
544
|
}
|
|
@@ -852,16 +990,16 @@ export async function startBrowserBridge(opts) {
|
|
|
852
990
|
}
|
|
853
991
|
const pt = { x: box.x + box.width / 2, y: box.y + box.height / 2 };
|
|
854
992
|
if (kind === "hover")
|
|
855
|
-
await page
|
|
993
|
+
await humanMove(page, pt.x, pt.y);
|
|
856
994
|
else
|
|
857
|
-
await page
|
|
995
|
+
await humanClick(page, pt.x, pt.y, kind === "dblclick" ? 2 : 1);
|
|
858
996
|
return { at: { x: Math.round(pt.x), y: Math.round(pt.y) }, matched_text: label };
|
|
859
997
|
}
|
|
860
998
|
const pt = await resolveActionPoint(reg, rec, lease, args);
|
|
861
999
|
if (kind === "hover")
|
|
862
|
-
await page
|
|
1000
|
+
await humanMove(page, pt.x, pt.y);
|
|
863
1001
|
else
|
|
864
|
-
await page
|
|
1002
|
+
await humanClick(page, pt.x, pt.y, kind === "dblclick" ? 2 : 1);
|
|
865
1003
|
return { at: { x: Math.round(pt.x), y: Math.round(pt.y) } };
|
|
866
1004
|
}
|
|
867
1005
|
case "tap": {
|
|
@@ -869,7 +1007,7 @@ export async function startBrowserBridge(opts) {
|
|
|
869
1007
|
// browser.py sends {x, y} as 0..1 fractions or CSS px.
|
|
870
1008
|
// resolveActionPoint already handles the fraction/px heuristic.
|
|
871
1009
|
const pt = await resolveActionPoint(reg, rec, lease, args);
|
|
872
|
-
await page
|
|
1010
|
+
await humanClick(page, pt.x, pt.y);
|
|
873
1011
|
return { at: { x: Math.round(pt.x), y: Math.round(pt.y) } };
|
|
874
1012
|
}
|
|
875
1013
|
case "input_text": {
|
|
@@ -877,10 +1015,11 @@ export async function startBrowserBridge(opts) {
|
|
|
877
1015
|
if (text.length > 20_000)
|
|
878
1016
|
throw new BridgeError("act_args_invalid", "text exceeds 20000 chars");
|
|
879
1017
|
const pt = await resolveActionPoint(reg, rec, lease, args);
|
|
880
|
-
await page
|
|
881
|
-
// Clear-then-type: select-all + type preserves site key handlers.
|
|
1018
|
+
await humanClick(page, pt.x, pt.y);
|
|
1019
|
+
// Clear-then-type: select-all + humanized type preserves site key handlers.
|
|
882
1020
|
await page.keyboard.press(process.platform === "darwin" ? "Meta+A" : "Control+A").catch(() => { });
|
|
883
|
-
await page
|
|
1021
|
+
await humanPause(page, 40, 120);
|
|
1022
|
+
await humanType(page, text);
|
|
884
1023
|
return { typed: text.length };
|
|
885
1024
|
}
|
|
886
1025
|
case "press_key": {
|
|
@@ -920,8 +1059,8 @@ export async function startBrowserBridge(opts) {
|
|
|
920
1059
|
else {
|
|
921
1060
|
dy = Math.max(-4000, Math.min(4000, Number(args.dy ?? 600)));
|
|
922
1061
|
}
|
|
923
|
-
await page
|
|
924
|
-
return { scrolled
|
|
1062
|
+
const scrolled = await humanScroll(page, dx, dy);
|
|
1063
|
+
return { scrolled };
|
|
925
1064
|
}
|
|
926
1065
|
case "get_text": {
|
|
927
1066
|
// Fix 1: read-only page-text extraction.
|
package/package.json
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@melaya/runner",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
|
|
5
|
-
"license": "UNLICENSED",
|
|
6
|
-
"private": false,
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"melaya-runner": "dist/cli.js"
|
|
10
|
-
},
|
|
11
|
-
"main": "dist/index.js",
|
|
12
|
-
"files": [
|
|
13
|
-
"dist/**/*.js",
|
|
14
|
-
"dist/**/*.d.ts",
|
|
15
|
-
"dist/**/*.py",
|
|
16
|
-
"localRagIngest.py",
|
|
17
|
-
"localRagRetrieve.py",
|
|
18
|
-
"nltk_data/**",
|
|
19
|
-
"README.md"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsc && node -e \"const fs=require('fs'); fs.copyFileSync('localRagIngest.py','dist/localRagIngest.py'); fs.copyFileSync('localRagRetrieve.py','dist/localRagRetrieve.py'); fs.copyFileSync('src/assistantHost.py','dist/assistantHost.py')\"",
|
|
23
|
-
"test": "node --test --import tsx \"src/**/*.test.ts\"",
|
|
24
|
-
"prepublishOnly": "npm run build"
|
|
25
|
-
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
"chalk": "^5.3.0",
|
|
28
|
-
"commander": "^12.0.0",
|
|
29
|
-
"ora": "^8.0.0",
|
|
30
|
-
"playwright": "^1.48.0",
|
|
31
|
-
"socket.io-client": "^4.8.0"
|
|
32
|
-
},
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@types/node": "^20.0.0",
|
|
35
|
-
"tsx": "^4.19.2",
|
|
36
|
-
"typescript": "^5.5.0"
|
|
37
|
-
},
|
|
38
|
-
"engines": {
|
|
39
|
-
"node": ">=18"
|
|
40
|
-
},
|
|
41
|
-
"publishConfig": {
|
|
42
|
-
"access": "public"
|
|
43
|
-
}
|
|
44
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@melaya/runner",
|
|
3
|
+
"version": "1.1.16",
|
|
4
|
+
"description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"melaya-runner": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/**/*.js",
|
|
14
|
+
"dist/**/*.d.ts",
|
|
15
|
+
"dist/**/*.py",
|
|
16
|
+
"localRagIngest.py",
|
|
17
|
+
"localRagRetrieve.py",
|
|
18
|
+
"nltk_data/**",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc && node -e \"const fs=require('fs'); fs.copyFileSync('localRagIngest.py','dist/localRagIngest.py'); fs.copyFileSync('localRagRetrieve.py','dist/localRagRetrieve.py'); fs.copyFileSync('src/assistantHost.py','dist/assistantHost.py')\"",
|
|
23
|
+
"test": "node --test --import tsx \"src/**/*.test.ts\"",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"chalk": "^5.3.0",
|
|
28
|
+
"commander": "^12.0.0",
|
|
29
|
+
"ora": "^8.0.0",
|
|
30
|
+
"playwright": "^1.48.0",
|
|
31
|
+
"socket.io-client": "^4.8.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"tsx": "^4.19.2",
|
|
36
|
+
"typescript": "^5.5.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
}
|
|
44
|
+
}
|