droid-patch 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -55
- package/README.zh-CN.md +116 -55
- package/dist/{alias-BGsm9wXL.js → alias-DKVU8DM_.mjs} +248 -41
- package/dist/alias-DKVU8DM_.mjs.map +1 -0
- package/dist/{cli.js → cli.mjs} +273 -302
- package/dist/cli.mjs.map +1 -0
- package/dist/{index.d.ts → index.d.mts} +4 -12
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/package.json +8 -8
- package/dist/alias-BGsm9wXL.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- /package/dist/{cli.d.ts → cli.d.mts} +0 -0
package/dist/{cli.js → cli.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { a as removeAlias, d as listAllMetadata, f as loadAliasMetadata, i as listAliases, l as createMetadata, m as patchDroid, n as createAlias, o as removeAliasesByFilter, p as saveAliasMetadata, r as createAliasForWrapper, t as clearAllAliases, u as formatPatches } from "./alias-DKVU8DM_.mjs";
|
|
3
3
|
import bin from "tiny-bin";
|
|
4
4
|
import { styleText } from "node:util";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -15,15 +15,15 @@ import { chmod, mkdir, writeFile } from "node:fs/promises";
|
|
|
15
15
|
* Since BUN_CONFIG_PRELOAD doesn't work with compiled binaries,
|
|
16
16
|
* use a local proxy server to intercept search requests instead
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* - Set to 0 to disable timeout
|
|
18
|
+
* Each droid instance runs its own proxy server.
|
|
19
|
+
* The proxy is killed automatically when droid exits.
|
|
20
|
+
* @param factoryApiUrl - Custom Factory API URL (default: https://api.factory.ai)
|
|
22
21
|
*/
|
|
23
|
-
function generateSearchProxyServer() {
|
|
22
|
+
function generateSearchProxyServer(factoryApiUrl = "https://api.factory.ai") {
|
|
24
23
|
return `#!/usr/bin/env node
|
|
25
24
|
// Droid WebSearch Proxy Server
|
|
26
25
|
// Auto-generated by droid-patch --websearch
|
|
26
|
+
// This proxy runs as a child process of droid and is killed when droid exits
|
|
27
27
|
|
|
28
28
|
const http = require('http');
|
|
29
29
|
const https = require('https');
|
|
@@ -31,71 +31,13 @@ const { execSync } = require('child_process');
|
|
|
31
31
|
const fs = require('fs');
|
|
32
32
|
|
|
33
33
|
const DEBUG = process.env.DROID_SEARCH_DEBUG === '1';
|
|
34
|
-
const PORT = parseInt(process.env.SEARCH_PROXY_PORT || '
|
|
35
|
-
|
|
36
|
-
// Idle timeout in seconds, default 5 minutes, set to 0 to disable
|
|
37
|
-
const IDLE_TIMEOUT = parseInt(process.env.DROID_PROXY_IDLE_TIMEOUT || '300');
|
|
38
|
-
let lastActivityTime = Date.now();
|
|
39
|
-
let idleCheckTimer = null;
|
|
34
|
+
const PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0'); // 0 = auto-assign
|
|
35
|
+
const FACTORY_API = '${factoryApiUrl}';
|
|
40
36
|
|
|
41
37
|
function log(...args) {
|
|
42
38
|
if (DEBUG) console.error('[websearch]', ...args);
|
|
43
39
|
}
|
|
44
40
|
|
|
45
|
-
// Update activity time
|
|
46
|
-
function updateActivity() {
|
|
47
|
-
lastActivityTime = Date.now();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Check if any droid process is running (droid instances using the proxy)
|
|
51
|
-
function isDroidRunning() {
|
|
52
|
-
try {
|
|
53
|
-
const { execSync } = require('child_process');
|
|
54
|
-
// Use ps to check if droid.patched binary is running
|
|
55
|
-
// Exclude scripts and grep itself, only match actual droid binary processes
|
|
56
|
-
const result = execSync(
|
|
57
|
-
'ps aux | grep -E "[d]roid\\\\.patched" | grep -v grep | wc -l',
|
|
58
|
-
{ encoding: 'utf-8', timeout: 1000 }
|
|
59
|
-
).trim();
|
|
60
|
-
return parseInt(result) > 0;
|
|
61
|
-
} catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Check idle time and possibly exit
|
|
67
|
-
function checkIdleAndExit() {
|
|
68
|
-
if (IDLE_TIMEOUT <= 0) return; // Timeout disabled
|
|
69
|
-
|
|
70
|
-
// If droid process is running, refresh activity time (like heartbeat)
|
|
71
|
-
if (isDroidRunning()) {
|
|
72
|
-
log('Droid process detected, keeping proxy alive');
|
|
73
|
-
updateActivity();
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const idleMs = Date.now() - lastActivityTime;
|
|
78
|
-
const timeoutMs = IDLE_TIMEOUT * 1000;
|
|
79
|
-
|
|
80
|
-
if (idleMs >= timeoutMs) {
|
|
81
|
-
log(\`Idle for \${Math.round(idleMs / 1000)}s and no droid running, shutting down...\`);
|
|
82
|
-
cleanup();
|
|
83
|
-
process.exit(0);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Cleanup resources
|
|
88
|
-
function cleanup() {
|
|
89
|
-
if (idleCheckTimer) {
|
|
90
|
-
clearInterval(idleCheckTimer);
|
|
91
|
-
idleCheckTimer = null;
|
|
92
|
-
}
|
|
93
|
-
// Delete PID file
|
|
94
|
-
try {
|
|
95
|
-
fs.unlinkSync('/tmp/droid-search-proxy.pid');
|
|
96
|
-
} catch {}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
41
|
// === Search Implementation ===
|
|
100
42
|
|
|
101
43
|
// Smithery Exa MCP - highest priority, requires SMITHERY_API_KEY and SMITHERY_PROFILE
|
|
@@ -442,31 +384,16 @@ async function search(query, numResults = 10) {
|
|
|
442
384
|
|
|
443
385
|
// === HTTP Proxy Server ===
|
|
444
386
|
|
|
445
|
-
const FACTORY_API = 'https://api.factory.ai';
|
|
446
|
-
|
|
447
387
|
const server = http.createServer(async (req, res) => {
|
|
448
388
|
const url = new URL(req.url, \`http://\${req.headers.host}\`);
|
|
449
389
|
|
|
450
|
-
// Health check
|
|
390
|
+
// Health check
|
|
451
391
|
if (url.pathname === '/health') {
|
|
452
|
-
const idleSeconds = Math.round((Date.now() - lastActivityTime) / 1000);
|
|
453
|
-
const droidRunning = isDroidRunning();
|
|
454
392
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
455
|
-
res.end(JSON.stringify({
|
|
456
|
-
status: 'ok',
|
|
457
|
-
port: PORT,
|
|
458
|
-
idleTimeout: IDLE_TIMEOUT,
|
|
459
|
-
idleSeconds: idleSeconds,
|
|
460
|
-
droidRunning: droidRunning,
|
|
461
|
-
// If droid is running, won't shutdown; otherwise calculate based on idle time
|
|
462
|
-
willShutdownIn: IDLE_TIMEOUT > 0 && !droidRunning ? Math.max(0, IDLE_TIMEOUT - idleSeconds) : null
|
|
463
|
-
}));
|
|
393
|
+
res.end(JSON.stringify({ status: 'ok', port: server.address()?.port || PORT }));
|
|
464
394
|
return;
|
|
465
395
|
}
|
|
466
396
|
|
|
467
|
-
// Update activity time (only non-health-check requests refresh it)
|
|
468
|
-
updateActivity();
|
|
469
|
-
|
|
470
397
|
// Search endpoint - intercept
|
|
471
398
|
if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {
|
|
472
399
|
let body = '';
|
|
@@ -488,11 +415,54 @@ const server = http.createServer(async (req, res) => {
|
|
|
488
415
|
return;
|
|
489
416
|
}
|
|
490
417
|
|
|
491
|
-
//
|
|
418
|
+
// === Standalone mode (controlled by STANDALONE_MODE env) ===
|
|
419
|
+
// Whitelist approach: only allow core LLM APIs, mock everything else
|
|
420
|
+
if (process.env.STANDALONE_MODE === '1') {
|
|
421
|
+
const pathname = url.pathname;
|
|
422
|
+
|
|
423
|
+
// Whitelist: Core APIs that should be forwarded to upstream
|
|
424
|
+
const isCoreLLMApi = pathname.startsWith('/api/llm/a/') || pathname.startsWith('/api/llm/o/');
|
|
425
|
+
// /api/tools/exa/search is already handled above
|
|
426
|
+
|
|
427
|
+
if (!isCoreLLMApi) {
|
|
428
|
+
// Special handling for specific routes
|
|
429
|
+
if (pathname === '/api/sessions/create') {
|
|
430
|
+
log('Mock (dynamic):', pathname);
|
|
431
|
+
const sessionId = \`local-\${Date.now()}-\${Math.random().toString(36).slice(2, 10)}\`;
|
|
432
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
433
|
+
res.end(JSON.stringify({ id: sessionId }));
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (pathname === '/api/cli/whoami') {
|
|
438
|
+
log('Mock (401):', pathname);
|
|
439
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
440
|
+
res.end(JSON.stringify({ error: 'Unauthorized', message: 'Local mode - use token fallback' }));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (pathname === '/api/tools/get-url-contents') {
|
|
445
|
+
log('Mock (404):', pathname);
|
|
446
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
447
|
+
res.end(JSON.stringify({ error: 'Not available', message: 'Use local URL fetch fallback' }));
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// All other non-core APIs: return empty success
|
|
452
|
+
log('Mock (default):', pathname);
|
|
453
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
454
|
+
res.end(JSON.stringify({}));
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Proxy core LLM requests to upstream API
|
|
492
460
|
log('Proxy:', req.method, url.pathname);
|
|
493
461
|
|
|
494
462
|
const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);
|
|
495
|
-
|
|
463
|
+
// Choose http or https based on target protocol
|
|
464
|
+
const proxyModule = proxyUrl.protocol === 'https:' ? https : http;
|
|
465
|
+
const proxyReq = proxyModule.request(proxyUrl, {
|
|
496
466
|
method: req.method,
|
|
497
467
|
headers: { ...req.headers, host: proxyUrl.host }
|
|
498
468
|
}, proxyRes => {
|
|
@@ -524,6 +494,9 @@ server.listen(PORT, '127.0.0.1', () => {
|
|
|
524
494
|
fs.writeFileSync(portFile, String(actualPort));
|
|
525
495
|
}
|
|
526
496
|
|
|
497
|
+
// Output PORT= line for wrapper script to parse
|
|
498
|
+
console.log('PORT=' + actualPort);
|
|
499
|
+
|
|
527
500
|
const hasSmithery = process.env.SMITHERY_API_KEY && process.env.SMITHERY_PROFILE;
|
|
528
501
|
log('Search proxy started on http://127.0.0.1:' + actualPort);
|
|
529
502
|
log('Smithery Exa:', hasSmithery ? 'configured (priority 1)' : 'not set');
|
|
@@ -531,109 +504,92 @@ server.listen(PORT, '127.0.0.1', () => {
|
|
|
531
504
|
log('Serper:', process.env.SERPER_API_KEY ? 'configured' : 'not set');
|
|
532
505
|
log('Brave:', process.env.BRAVE_API_KEY ? 'configured' : 'not set');
|
|
533
506
|
log('SearXNG:', process.env.SEARXNG_URL || 'not set');
|
|
534
|
-
|
|
535
|
-
// Start idle check timer
|
|
536
|
-
// Check interval = min(timeout/2, 30s) to ensure timely timeout detection
|
|
537
|
-
if (IDLE_TIMEOUT > 0) {
|
|
538
|
-
const checkInterval = Math.min(IDLE_TIMEOUT * 500, 30000); // milliseconds
|
|
539
|
-
log(\`Idle timeout: \${IDLE_TIMEOUT}s (will auto-shutdown when idle)\`);
|
|
540
|
-
idleCheckTimer = setInterval(checkIdleAndExit, checkInterval);
|
|
541
|
-
} else {
|
|
542
|
-
log('Idle timeout: disabled (will run forever)');
|
|
543
|
-
}
|
|
544
507
|
});
|
|
545
508
|
|
|
546
|
-
process.on('SIGTERM', () => {
|
|
547
|
-
process.on('SIGINT', () => {
|
|
509
|
+
process.on('SIGTERM', () => { server.close(); process.exit(0); });
|
|
510
|
+
process.on('SIGINT', () => { server.close(); process.exit(0); });
|
|
548
511
|
`;
|
|
549
512
|
}
|
|
550
513
|
/**
|
|
551
514
|
* Generate unified Wrapper script
|
|
552
|
-
*
|
|
553
|
-
* -
|
|
554
|
-
* - Proxy
|
|
555
|
-
* - Proxy
|
|
515
|
+
* Each droid instance runs its own proxy:
|
|
516
|
+
* - Uses port 0 to let system auto-assign available port
|
|
517
|
+
* - Proxy runs as child process
|
|
518
|
+
* - Proxy is killed when droid exits
|
|
519
|
+
* - Supports multiple droid instances running simultaneously
|
|
556
520
|
*/
|
|
557
|
-
function generateUnifiedWrapper(droidPath, proxyScriptPath) {
|
|
521
|
+
function generateUnifiedWrapper(droidPath, proxyScriptPath, standalone = false) {
|
|
522
|
+
const standaloneEnv = standalone ? "STANDALONE_MODE=1 " : "";
|
|
558
523
|
return `#!/bin/bash
|
|
559
524
|
# Droid with WebSearch
|
|
560
525
|
# Auto-generated by droid-patch --websearch
|
|
526
|
+
# Each instance runs its own proxy on a system-assigned port
|
|
561
527
|
|
|
562
528
|
PROXY_SCRIPT="${proxyScriptPath}"
|
|
563
529
|
DROID_BIN="${droidPath}"
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
#
|
|
569
|
-
|
|
570
|
-
if [ -
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
# Process exists, check if port responds
|
|
575
|
-
if curl -s "http://127.0.0.1:$PORT/health" > /dev/null 2>&1; then
|
|
576
|
-
return 0
|
|
577
|
-
fi
|
|
578
|
-
fi
|
|
579
|
-
# PID file exists but process doesn't exist or port not responding, cleanup
|
|
580
|
-
rm -f "$PID_FILE"
|
|
530
|
+
PROXY_PID=""
|
|
531
|
+
PORT_FILE="/tmp/droid-websearch-\$\$.port"
|
|
532
|
+
STANDALONE="${standalone ? "1" : "0"}"
|
|
533
|
+
|
|
534
|
+
# Cleanup function - kill proxy when droid exits
|
|
535
|
+
cleanup() {
|
|
536
|
+
if [ -n "\$PROXY_PID" ] && kill -0 "\$PROXY_PID" 2>/dev/null; then
|
|
537
|
+
[ -n "\$DROID_SEARCH_DEBUG" ] && echo "[websearch] Stopping proxy (PID: \$PROXY_PID)" >&2
|
|
538
|
+
kill "\$PROXY_PID" 2>/dev/null
|
|
539
|
+
wait "\$PROXY_PID" 2>/dev/null
|
|
581
540
|
fi
|
|
582
|
-
|
|
541
|
+
rm -f "\$PORT_FILE"
|
|
583
542
|
}
|
|
584
543
|
|
|
585
|
-
#
|
|
586
|
-
|
|
587
|
-
# First check if port is occupied by another program
|
|
588
|
-
if lsof -i:"$PORT" > /dev/null 2>&1; then
|
|
589
|
-
# Port is occupied, check if it's our proxy
|
|
590
|
-
if curl -s "http://127.0.0.1:$PORT/health" > /dev/null 2>&1; then
|
|
591
|
-
[ -n "$DROID_SEARCH_DEBUG" ] && echo "[websearch] Proxy already running on port $PORT" >&2
|
|
592
|
-
return 0
|
|
593
|
-
else
|
|
594
|
-
echo "[websearch] Port $PORT is occupied by another process" >&2
|
|
595
|
-
return 1
|
|
596
|
-
fi
|
|
597
|
-
fi
|
|
598
|
-
|
|
599
|
-
[ -n "$DROID_SEARCH_DEBUG" ] && echo "[websearch] Starting shared proxy on port $PORT..." >&2
|
|
544
|
+
# Set up trap to cleanup on exit
|
|
545
|
+
trap cleanup EXIT INT TERM
|
|
600
546
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
echo $! > "$PID_FILE"
|
|
547
|
+
[ -n "\$DROID_SEARCH_DEBUG" ] && echo "[websearch] Starting proxy..." >&2
|
|
548
|
+
[ "\$STANDALONE" = "1" ] && [ -n "\$DROID_SEARCH_DEBUG" ] && echo "[websearch] Standalone mode enabled" >&2
|
|
604
549
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
550
|
+
# Start proxy with port 0 (system will assign available port)
|
|
551
|
+
# Proxy writes actual port to PORT_FILE
|
|
552
|
+
if [ -n "\$DROID_SEARCH_DEBUG" ]; then
|
|
553
|
+
${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE="\$PORT_FILE" node "\$PROXY_SCRIPT" 2>&1 &
|
|
554
|
+
else
|
|
555
|
+
${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE="\$PORT_FILE" node "\$PROXY_SCRIPT" >/dev/null 2>&1 &
|
|
556
|
+
fi
|
|
557
|
+
PROXY_PID=\$!
|
|
558
|
+
|
|
559
|
+
# Wait for proxy to start and get actual port (max 5 seconds)
|
|
560
|
+
for i in {1..50}; do
|
|
561
|
+
# Check if proxy process is still running
|
|
562
|
+
if ! kill -0 "\$PROXY_PID" 2>/dev/null; then
|
|
563
|
+
[ -n "\$DROID_SEARCH_DEBUG" ] && echo "[websearch] Proxy process died" >&2
|
|
564
|
+
break
|
|
565
|
+
fi
|
|
566
|
+
if [ -f "\$PORT_FILE" ]; then
|
|
567
|
+
ACTUAL_PORT=\$(cat "\$PORT_FILE" 2>/dev/null)
|
|
568
|
+
if [ -n "\$ACTUAL_PORT" ] && curl -s "http://127.0.0.1:\$ACTUAL_PORT/health" > /dev/null 2>&1; then
|
|
569
|
+
[ -n "\$DROID_SEARCH_DEBUG" ] && echo "[websearch] Proxy ready on port \$ACTUAL_PORT (PID: \$PROXY_PID)" >&2
|
|
570
|
+
break
|
|
610
571
|
fi
|
|
611
|
-
sleep 0.1
|
|
612
|
-
done
|
|
613
|
-
|
|
614
|
-
echo "[websearch] Failed to start proxy" >&2
|
|
615
|
-
rm -f "$PID_FILE"
|
|
616
|
-
return 1
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
# Ensure proxy is running
|
|
620
|
-
ensure_proxy() {
|
|
621
|
-
if is_proxy_running; then
|
|
622
|
-
[ -n "$DROID_SEARCH_DEBUG" ] && echo "[websearch] Using existing proxy on port $PORT" >&2
|
|
623
|
-
return 0
|
|
624
572
|
fi
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
#
|
|
629
|
-
if !
|
|
630
|
-
echo "[websearch]
|
|
631
|
-
|
|
573
|
+
sleep 0.1
|
|
574
|
+
done
|
|
575
|
+
|
|
576
|
+
# Check if proxy started successfully
|
|
577
|
+
if [ ! -f "\$PORT_FILE" ] || [ -z "\$(cat "\$PORT_FILE" 2>/dev/null)" ]; then
|
|
578
|
+
echo "[websearch] Failed to start proxy, running without websearch" >&2
|
|
579
|
+
cleanup
|
|
580
|
+
exec "\$DROID_BIN" "\$@"
|
|
632
581
|
fi
|
|
633
582
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
583
|
+
ACTUAL_PORT=\$(cat "\$PORT_FILE")
|
|
584
|
+
rm -f "\$PORT_FILE"
|
|
585
|
+
|
|
586
|
+
# Run droid with proxy
|
|
587
|
+
export FACTORY_API_BASE_URL_OVERRIDE="http://127.0.0.1:\$ACTUAL_PORT"
|
|
588
|
+
"\$DROID_BIN" "\$@"
|
|
589
|
+
DROID_EXIT_CODE=\$?
|
|
590
|
+
|
|
591
|
+
# Cleanup will be called by trap
|
|
592
|
+
exit \$DROID_EXIT_CODE
|
|
637
593
|
`;
|
|
638
594
|
}
|
|
639
595
|
/**
|
|
@@ -644,16 +600,23 @@ exec "$DROID_BIN" "$@"
|
|
|
644
600
|
* - proxy server intercepts search requests, passes through other requests
|
|
645
601
|
* - uses FACTORY_API_BASE_URL_OVERRIDE env var to point to proxy
|
|
646
602
|
* - alias works directly, no extra steps needed
|
|
603
|
+
*
|
|
604
|
+
* @param outputDir - Directory to write files to
|
|
605
|
+
* @param droidPath - Path to droid binary
|
|
606
|
+
* @param aliasName - Alias name for the wrapper
|
|
607
|
+
* @param apiBase - Custom API base URL for proxy to forward requests to
|
|
608
|
+
* @param standalone - Standalone mode: mock non-LLM Factory APIs
|
|
647
609
|
*/
|
|
648
|
-
async function createWebSearchUnifiedFiles(outputDir, droidPath, aliasName) {
|
|
610
|
+
async function createWebSearchUnifiedFiles(outputDir, droidPath, aliasName, apiBase, standalone = false) {
|
|
649
611
|
if (!existsSync(outputDir)) await mkdir(outputDir, { recursive: true });
|
|
650
612
|
const proxyScriptPath = join(outputDir, `${aliasName}-proxy.js`);
|
|
651
613
|
const wrapperScriptPath = join(outputDir, aliasName);
|
|
652
|
-
await writeFile(proxyScriptPath, generateSearchProxyServer());
|
|
614
|
+
await writeFile(proxyScriptPath, generateSearchProxyServer(apiBase || "https://api.factory.ai"));
|
|
653
615
|
console.log(`[*] Created proxy script: ${proxyScriptPath}`);
|
|
654
|
-
await writeFile(wrapperScriptPath, generateUnifiedWrapper(droidPath, proxyScriptPath));
|
|
616
|
+
await writeFile(wrapperScriptPath, generateUnifiedWrapper(droidPath, proxyScriptPath, standalone));
|
|
655
617
|
await chmod(wrapperScriptPath, 493);
|
|
656
618
|
console.log(`[*] Created wrapper: ${wrapperScriptPath}`);
|
|
619
|
+
if (standalone) console.log(`[*] Standalone mode enabled`);
|
|
657
620
|
return {
|
|
658
621
|
wrapperScript: wrapperScriptPath,
|
|
659
622
|
preloadScript: proxyScriptPath
|
|
@@ -666,13 +629,29 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
666
629
|
function getVersion() {
|
|
667
630
|
try {
|
|
668
631
|
const pkgPath = join(__dirname, "..", "package.json");
|
|
669
|
-
|
|
670
|
-
return pkg.version || "0.0.0";
|
|
632
|
+
return JSON.parse(readFileSync(pkgPath, "utf-8")).version || "0.0.0";
|
|
671
633
|
} catch {
|
|
672
634
|
return "0.0.0";
|
|
673
635
|
}
|
|
674
636
|
}
|
|
675
637
|
const version = getVersion();
|
|
638
|
+
function getDroidVersion(droidPath) {
|
|
639
|
+
try {
|
|
640
|
+
const result = execSync(`"${droidPath}" --version`, {
|
|
641
|
+
encoding: "utf-8",
|
|
642
|
+
stdio: [
|
|
643
|
+
"pipe",
|
|
644
|
+
"pipe",
|
|
645
|
+
"pipe"
|
|
646
|
+
],
|
|
647
|
+
timeout: 5e3
|
|
648
|
+
}).trim();
|
|
649
|
+
const match = result.match(/(\d+\.\d+\.\d+)/);
|
|
650
|
+
return match ? match[1] : result || void 0;
|
|
651
|
+
} catch {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
676
655
|
function findDefaultDroidPath() {
|
|
677
656
|
const home = homedir();
|
|
678
657
|
try {
|
|
@@ -696,20 +675,23 @@ function findDefaultDroidPath() {
|
|
|
696
675
|
for (const p of paths) if (existsSync(p)) return p;
|
|
697
676
|
return join(home, ".droid", "bin", "droid");
|
|
698
677
|
}
|
|
699
|
-
bin("droid-patch", "CLI tool to patch droid binary with various modifications").package("droid-patch", version).option("--is-custom", "Patch isCustom:!0 to isCustom:!1 (enable context compression for custom models)").option("--skip-login", "Inject a fake FACTORY_API_KEY to bypass login requirement (no real key needed)").option("--api-base <url>", "Replace Factory API
|
|
678
|
+
bin("droid-patch", "CLI tool to patch droid binary with various modifications").package("droid-patch", version).option("--is-custom", "Patch isCustom:!0 to isCustom:!1 (enable context compression for custom models)").option("--skip-login", "Inject a fake FACTORY_API_KEY to bypass login requirement (no real key needed)").option("--api-base <url>", "Replace Factory API URL with custom URL (binary patch, or forward target with --websearch)").option("--websearch", "Enable local WebSearch proxy (each instance runs own proxy, auto-cleanup on exit)").option("--standalone", "Standalone mode: mock non-LLM Factory APIs (use with --websearch)").option("--reasoning-effort", "Enable reasoning effort for custom models (set to high, enable UI selector)").option("--disable-telemetry", "Disable telemetry and Sentry error reporting (block data uploads)").option("--dry-run", "Verify patches without actually modifying the binary").option("-p, --path <path>", "Path to the droid binary").option("-o, --output <dir>", "Output directory for patched binary").option("--no-backup", "Do not create backup of original binary").option("-v, --verbose", "Enable verbose output").argument("[alias]", "Alias name for the patched binary").action(async (options, args) => {
|
|
700
679
|
const alias = args?.[0];
|
|
701
680
|
const isCustom = options["is-custom"];
|
|
702
681
|
const skipLogin = options["skip-login"];
|
|
703
682
|
const apiBase = options["api-base"];
|
|
704
|
-
const
|
|
683
|
+
const websearch = options["websearch"];
|
|
684
|
+
const standalone = options["standalone"];
|
|
685
|
+
const websearchTarget = websearch ? apiBase || "https://api.factory.ai" : void 0;
|
|
705
686
|
const reasoningEffort = options["reasoning-effort"];
|
|
687
|
+
const noTelemetry = options["disable-telemetry"];
|
|
706
688
|
const dryRun = options["dry-run"];
|
|
707
689
|
const path = options.path || findDefaultDroidPath();
|
|
708
690
|
const outputDir = options.output;
|
|
709
691
|
const backup = options.backup !== false;
|
|
710
692
|
const verbose = options.verbose;
|
|
711
693
|
const outputPath = outputDir && alias ? join(outputDir, alias) : void 0;
|
|
712
|
-
if (
|
|
694
|
+
if (websearch && !isCustom && !skipLogin && !reasoningEffort && !noTelemetry) {
|
|
713
695
|
if (!alias) {
|
|
714
696
|
console.log(styleText("red", "Error: Alias name required for --websearch"));
|
|
715
697
|
console.log(styleText("gray", "Usage: npx droid-patch --websearch <alias>"));
|
|
@@ -719,17 +701,24 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
719
701
|
console.log(styleText(["cyan", "bold"], " Droid WebSearch Setup"));
|
|
720
702
|
console.log(styleText("cyan", "═".repeat(60)));
|
|
721
703
|
console.log();
|
|
722
|
-
|
|
723
|
-
|
|
704
|
+
console.log(styleText("white", `Forward target: ${websearchTarget}`));
|
|
705
|
+
if (standalone) console.log(styleText("white", `Standalone mode: enabled`));
|
|
706
|
+
console.log();
|
|
707
|
+
const { wrapperScript } = await createWebSearchUnifiedFiles(join(homedir(), ".droid-patch", "proxy"), path, alias, websearchTarget, standalone);
|
|
724
708
|
await createAliasForWrapper(wrapperScript, alias, verbose);
|
|
725
|
-
const
|
|
709
|
+
const droidVersion = getDroidVersion(path);
|
|
710
|
+
await saveAliasMetadata(createMetadata(alias, path, {
|
|
726
711
|
isCustom: false,
|
|
727
712
|
skipLogin: false,
|
|
728
|
-
apiBase: null,
|
|
713
|
+
apiBase: apiBase || null,
|
|
729
714
|
websearch: true,
|
|
730
|
-
reasoningEffort: false
|
|
731
|
-
|
|
732
|
-
|
|
715
|
+
reasoningEffort: false,
|
|
716
|
+
noTelemetry: false,
|
|
717
|
+
standalone
|
|
718
|
+
}, {
|
|
719
|
+
droidPatchVersion: version,
|
|
720
|
+
droidVersion
|
|
721
|
+
}));
|
|
733
722
|
console.log();
|
|
734
723
|
console.log(styleText("green", "═".repeat(60)));
|
|
735
724
|
console.log(styleText(["green", "bold"], " WebSearch Ready!"));
|
|
@@ -755,22 +744,24 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
755
744
|
console.log(styleText("gray", " export DROID_SEARCH_DEBUG=1"));
|
|
756
745
|
return;
|
|
757
746
|
}
|
|
758
|
-
if (!isCustom && !skipLogin && !apiBase && !
|
|
747
|
+
if (!isCustom && !skipLogin && !apiBase && !websearch && !reasoningEffort && !noTelemetry) {
|
|
759
748
|
console.log(styleText("yellow", "No patch flags specified. Available patches:"));
|
|
760
749
|
console.log(styleText("gray", " --is-custom Patch isCustom for custom models"));
|
|
761
750
|
console.log(styleText("gray", " --skip-login Bypass login by injecting a fake API key"));
|
|
762
|
-
console.log(styleText("gray", " --api-base Replace Factory API URL
|
|
763
|
-
console.log(styleText("gray", " --websearch Enable local WebSearch
|
|
751
|
+
console.log(styleText("gray", " --api-base Replace Factory API URL (binary patch)"));
|
|
752
|
+
console.log(styleText("gray", " --websearch Enable local WebSearch proxy"));
|
|
764
753
|
console.log(styleText("gray", " --reasoning-effort Set reasoning effort level for custom models"));
|
|
754
|
+
console.log(styleText("gray", " --disable-telemetry Disable telemetry and Sentry error reporting"));
|
|
755
|
+
console.log(styleText("gray", " --standalone Standalone mode: mock non-LLM Factory APIs"));
|
|
765
756
|
console.log();
|
|
766
757
|
console.log("Usage examples:");
|
|
767
758
|
console.log(styleText("cyan", " npx droid-patch --is-custom droid-custom"));
|
|
768
759
|
console.log(styleText("cyan", " npx droid-patch --skip-login droid-nologin"));
|
|
769
760
|
console.log(styleText("cyan", " npx droid-patch --is-custom --skip-login droid-patched"));
|
|
770
|
-
console.log(styleText("cyan", " npx droid-patch --skip-login -o . my-droid"));
|
|
771
|
-
console.log(styleText("cyan", " npx droid-patch --api-base http://localhost:3000 droid-local"));
|
|
772
761
|
console.log(styleText("cyan", " npx droid-patch --websearch droid-search"));
|
|
773
|
-
console.log(styleText("cyan", " npx droid-patch --
|
|
762
|
+
console.log(styleText("cyan", " npx droid-patch --websearch --standalone droid-local"));
|
|
763
|
+
console.log(styleText("cyan", " npx droid-patch --disable-telemetry droid-private"));
|
|
764
|
+
console.log(styleText("cyan", " npx droid-patch --websearch --api-base=http://127.0.0.1:20002 my-droid"));
|
|
774
765
|
process.exit(1);
|
|
775
766
|
}
|
|
776
767
|
if (!alias && !dryRun) {
|
|
@@ -795,9 +786,9 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
795
786
|
pattern: Buffer.from("process.env.FACTORY_API_KEY"),
|
|
796
787
|
replacement: Buffer.from("\"fk-droid-patch-skip-00000\"")
|
|
797
788
|
});
|
|
798
|
-
if (apiBase) {
|
|
789
|
+
if (apiBase && !websearch) {
|
|
799
790
|
const originalUrl = "https://api.factory.ai";
|
|
800
|
-
const originalLength =
|
|
791
|
+
const originalLength = 22;
|
|
801
792
|
let normalizedUrl = apiBase.replace(/\/+$/, "");
|
|
802
793
|
if (normalizedUrl.length > originalLength) {
|
|
803
794
|
console.log(styleText("red", `Error: API base URL must be ${originalLength} characters or less`));
|
|
@@ -850,6 +841,26 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
850
841
|
replacement: Buffer.from("if(0&&!B.supportedReasoningEfforts.includes(R))")
|
|
851
842
|
});
|
|
852
843
|
}
|
|
844
|
+
if (noTelemetry) {
|
|
845
|
+
patches.push({
|
|
846
|
+
name: "noTelemetrySentryEnv1",
|
|
847
|
+
description: "Break ENABLE_SENTRY env var check (E->X)",
|
|
848
|
+
pattern: Buffer.from("ENABLE_SENTRY"),
|
|
849
|
+
replacement: Buffer.from("XNABLE_SENTRY")
|
|
850
|
+
});
|
|
851
|
+
patches.push({
|
|
852
|
+
name: "noTelemetrySentryEnv2",
|
|
853
|
+
description: "Break VITE_VERCEL_ENV env var check (V->X)",
|
|
854
|
+
pattern: Buffer.from("VITE_VERCEL_ENV"),
|
|
855
|
+
replacement: Buffer.from("XITE_VERCEL_ENV")
|
|
856
|
+
});
|
|
857
|
+
patches.push({
|
|
858
|
+
name: "noTelemetryFlushBlock",
|
|
859
|
+
description: "Make flushToWeb always return (!0|| = always true)",
|
|
860
|
+
pattern: Buffer.from("this.webEvents.length===0"),
|
|
861
|
+
replacement: Buffer.from("!0||this.webEvents.length")
|
|
862
|
+
});
|
|
863
|
+
}
|
|
853
864
|
try {
|
|
854
865
|
const result = await patchDroid({
|
|
855
866
|
inputPath: path,
|
|
@@ -880,30 +891,27 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
880
891
|
}
|
|
881
892
|
if (result.success && result.outputPath && alias) {
|
|
882
893
|
console.log();
|
|
883
|
-
if (
|
|
884
|
-
const
|
|
885
|
-
const { wrapperScript } = await createWebSearchUnifiedFiles(websearchDir, result.outputPath, alias);
|
|
894
|
+
if (websearch) {
|
|
895
|
+
const { wrapperScript } = await createWebSearchUnifiedFiles(join(homedir(), ".droid-patch", "proxy"), result.outputPath, alias, websearchTarget, standalone);
|
|
886
896
|
await createAliasForWrapper(wrapperScript, alias, verbose);
|
|
887
897
|
console.log();
|
|
888
|
-
console.log(styleText("cyan", "WebSearch
|
|
889
|
-
console.log(styleText("
|
|
890
|
-
console.log(styleText("
|
|
891
|
-
console.log();
|
|
892
|
-
console.log(styleText("yellow", " Smithery Exa"), styleText("gray", " - Best quality, free via smithery.ai"));
|
|
893
|
-
console.log(styleText("gray", " export SMITHERY_API_KEY=... SMITHERY_PROFILE=..."));
|
|
894
|
-
console.log(styleText("yellow", " Google PSE"), styleText("gray", " - 10,000/day free"));
|
|
895
|
-
console.log(styleText("gray", " export GOOGLE_PSE_API_KEY=... GOOGLE_PSE_CX=..."));
|
|
896
|
-
console.log();
|
|
897
|
-
console.log(styleText("gray", " See README for all providers and setup guides"));
|
|
898
|
+
console.log(styleText("cyan", "WebSearch enabled"));
|
|
899
|
+
console.log(styleText("white", ` Forward target: ${websearchTarget}`));
|
|
900
|
+
if (standalone) console.log(styleText("white", ` Standalone mode: enabled`));
|
|
898
901
|
} else await createAlias(result.outputPath, alias, verbose);
|
|
899
|
-
const
|
|
902
|
+
const droidVersion = getDroidVersion(path);
|
|
903
|
+
await saveAliasMetadata(createMetadata(alias, path, {
|
|
900
904
|
isCustom: !!isCustom,
|
|
901
905
|
skipLogin: !!skipLogin,
|
|
902
906
|
apiBase: apiBase || null,
|
|
903
|
-
websearch: !!
|
|
904
|
-
reasoningEffort: !!reasoningEffort
|
|
905
|
-
|
|
906
|
-
|
|
907
|
+
websearch: !!websearch,
|
|
908
|
+
reasoningEffort: !!reasoningEffort,
|
|
909
|
+
noTelemetry: !!noTelemetry,
|
|
910
|
+
standalone: !!standalone
|
|
911
|
+
}, {
|
|
912
|
+
droidPatchVersion: version,
|
|
913
|
+
droidVersion
|
|
914
|
+
}));
|
|
907
915
|
}
|
|
908
916
|
if (result.success) {
|
|
909
917
|
console.log();
|
|
@@ -919,8 +927,23 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
919
927
|
}
|
|
920
928
|
}).command("list", "List all droid-patch aliases").action(async () => {
|
|
921
929
|
await listAliases();
|
|
922
|
-
}).command("remove", "Remove
|
|
923
|
-
const target = args[0];
|
|
930
|
+
}).command("remove", "Remove alias(es) by name or filter").argument("[alias-or-path]", "Alias name or file path to remove").option("--patch-version <version>", "Remove aliases created by this droid-patch version").option("--droid-version <version>", "Remove aliases for this droid version").option("--flag <flag>", "Remove aliases with this flag (is-custom, skip-login, websearch, api-base, reasoning-effort, disable-telemetry, standalone)").action(async (options, args) => {
|
|
931
|
+
const target = args?.[0];
|
|
932
|
+
const patchVersion = options["patch-version"];
|
|
933
|
+
const droidVersion = options["droid-version"];
|
|
934
|
+
const flag = options.flag;
|
|
935
|
+
if (patchVersion || droidVersion || flag) {
|
|
936
|
+
await removeAliasesByFilter({
|
|
937
|
+
patchVersion,
|
|
938
|
+
droidVersion,
|
|
939
|
+
flags: flag ? [flag] : void 0
|
|
940
|
+
});
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
if (!target) {
|
|
944
|
+
console.error(styleText("red", "Error: Provide an alias name or use filter options (--patch-version, --droid-version, --flag)"));
|
|
945
|
+
process.exit(1);
|
|
946
|
+
}
|
|
924
947
|
if (target.includes("/") || existsSync(target)) {
|
|
925
948
|
const { unlink: unlink$1 } = await import("node:fs/promises");
|
|
926
949
|
try {
|
|
@@ -933,86 +956,8 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
933
956
|
} else await removeAlias(target);
|
|
934
957
|
}).command("version", "Print droid-patch version").action(() => {
|
|
935
958
|
console.log(`droid-patch v${version}`);
|
|
936
|
-
}).command("
|
|
937
|
-
|
|
938
|
-
const logFile = "/tmp/droid-search-proxy.log";
|
|
939
|
-
const port = 23119;
|
|
940
|
-
console.log(styleText("cyan", "═".repeat(60)));
|
|
941
|
-
console.log(styleText(["cyan", "bold"], " WebSearch Proxy Status"));
|
|
942
|
-
console.log(styleText("cyan", "═".repeat(60)));
|
|
943
|
-
console.log();
|
|
944
|
-
try {
|
|
945
|
-
const response = await fetch(`http://127.0.0.1:${port}/health`);
|
|
946
|
-
if (response.ok) {
|
|
947
|
-
const data = await response.json();
|
|
948
|
-
console.log(styleText("green", ` Status: Running ✓`));
|
|
949
|
-
console.log(styleText("white", ` Port: ${port}`));
|
|
950
|
-
if (existsSync(pidFile)) {
|
|
951
|
-
const { readFileSync: readFileSync$1 } = await import("node:fs");
|
|
952
|
-
const pid = readFileSync$1(pidFile, "utf-8").trim();
|
|
953
|
-
console.log(styleText("white", ` PID: ${pid}`));
|
|
954
|
-
}
|
|
955
|
-
if (data.droidRunning !== void 0) console.log(styleText("white", ` Droid running: ${data.droidRunning ? "yes (proxy will stay alive)" : "no"}`));
|
|
956
|
-
if (data.idleTimeout !== void 0) if (data.idleTimeout > 0) {
|
|
957
|
-
const idleMins = Math.floor((data.idleSeconds || 0) / 60);
|
|
958
|
-
const idleSecs = (data.idleSeconds || 0) % 60;
|
|
959
|
-
if (data.droidRunning) console.log(styleText("white", ` Idle: ${idleMins}m ${idleSecs}s (won't shutdown while droid runs)`));
|
|
960
|
-
else if (data.willShutdownIn !== null) {
|
|
961
|
-
const shutdownMins = Math.floor((data.willShutdownIn || 0) / 60);
|
|
962
|
-
const shutdownSecs = (data.willShutdownIn || 0) % 60;
|
|
963
|
-
console.log(styleText("white", ` Idle: ${idleMins}m ${idleSecs}s`));
|
|
964
|
-
console.log(styleText("white", ` Auto-shutdown in: ${shutdownMins}m ${shutdownSecs}s`));
|
|
965
|
-
}
|
|
966
|
-
} else console.log(styleText("white", ` Auto-shutdown: disabled`));
|
|
967
|
-
console.log(styleText("white", ` Log: ${logFile}`));
|
|
968
|
-
console.log();
|
|
969
|
-
console.log(styleText("gray", "To stop the proxy manually:"));
|
|
970
|
-
console.log(styleText("cyan", " npx droid-patch proxy-stop"));
|
|
971
|
-
console.log();
|
|
972
|
-
console.log(styleText("gray", "To disable auto-shutdown:"));
|
|
973
|
-
console.log(styleText("cyan", " export DROID_PROXY_IDLE_TIMEOUT=0"));
|
|
974
|
-
}
|
|
975
|
-
} catch {
|
|
976
|
-
console.log(styleText("yellow", ` Status: Not running`));
|
|
977
|
-
console.log();
|
|
978
|
-
console.log(styleText("gray", "The proxy will start automatically when you run droid-full."));
|
|
979
|
-
console.log(styleText("gray", "It will auto-shutdown after 5 minutes of idle (configurable)."));
|
|
980
|
-
}
|
|
981
|
-
console.log();
|
|
982
|
-
}).command("proxy-stop", "Stop the websearch proxy").action(async () => {
|
|
983
|
-
const pidFile = "/tmp/droid-search-proxy.pid";
|
|
984
|
-
if (!existsSync(pidFile)) {
|
|
985
|
-
console.log(styleText("yellow", "Proxy is not running (no PID file)"));
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
try {
|
|
989
|
-
const { readFileSync: readFileSync$1, unlinkSync: unlinkSync$1 } = await import("node:fs");
|
|
990
|
-
const pid = readFileSync$1(pidFile, "utf-8").trim();
|
|
991
|
-
process.kill(parseInt(pid), "SIGTERM");
|
|
992
|
-
unlinkSync$1(pidFile);
|
|
993
|
-
console.log(styleText("green", `[*] Proxy stopped (PID: ${pid})`));
|
|
994
|
-
} catch (error) {
|
|
995
|
-
console.log(styleText("yellow", `[!] Could not stop proxy: ${error.message}`));
|
|
996
|
-
try {
|
|
997
|
-
const { unlinkSync: unlinkSync$1 } = await import("node:fs");
|
|
998
|
-
unlinkSync$1(pidFile);
|
|
999
|
-
console.log(styleText("gray", "Cleaned up stale PID file"));
|
|
1000
|
-
} catch {}
|
|
1001
|
-
}
|
|
1002
|
-
}).command("proxy-log", "Show websearch proxy logs").action(async () => {
|
|
1003
|
-
const logFile = "/tmp/droid-search-proxy.log";
|
|
1004
|
-
if (!existsSync(logFile)) {
|
|
1005
|
-
console.log(styleText("yellow", "No log file found"));
|
|
1006
|
-
return;
|
|
1007
|
-
}
|
|
1008
|
-
const { readFileSync: readFileSync$1 } = await import("node:fs");
|
|
1009
|
-
const log = readFileSync$1(logFile, "utf-8");
|
|
1010
|
-
const lines = log.split("\n").slice(-50);
|
|
1011
|
-
console.log(styleText("cyan", "═".repeat(60)));
|
|
1012
|
-
console.log(styleText(["cyan", "bold"], " WebSearch Proxy Logs (last 50 lines)"));
|
|
1013
|
-
console.log(styleText("cyan", "═".repeat(60)));
|
|
1014
|
-
console.log();
|
|
1015
|
-
console.log(lines.join("\n"));
|
|
959
|
+
}).command("clear", "Remove all droid-patch aliases and related files").action(async () => {
|
|
960
|
+
await clearAllAliases();
|
|
1016
961
|
}).command("update", "Update aliases with latest droid binary").argument("[alias]", "Specific alias to update (optional, updates all if not specified)").option("--dry-run", "Preview without making changes").option("-p, --path <path>", "Path to new droid binary").option("-v, --verbose", "Enable verbose output").action(async (options, args) => {
|
|
1017
962
|
const aliasName = args?.[0];
|
|
1018
963
|
const dryRun = options["dry-run"];
|
|
@@ -1077,7 +1022,7 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
1077
1022
|
});
|
|
1078
1023
|
if (meta.patches.apiBase) {
|
|
1079
1024
|
const originalUrl = "https://api.factory.ai";
|
|
1080
|
-
const paddedUrl = meta.patches.apiBase.padEnd(
|
|
1025
|
+
const paddedUrl = meta.patches.apiBase.padEnd(22, " ");
|
|
1081
1026
|
patches.push({
|
|
1082
1027
|
name: "apiBase",
|
|
1083
1028
|
description: `Replace Factory API URL with "${meta.patches.apiBase}"`,
|
|
@@ -1117,18 +1062,36 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
1117
1062
|
replacement: Buffer.from("if(0&&!B.supportedReasoningEfforts.includes(R))")
|
|
1118
1063
|
});
|
|
1119
1064
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1065
|
+
if (meta.patches.noTelemetry) {
|
|
1066
|
+
patches.push({
|
|
1067
|
+
name: "noTelemetrySentryEnv1",
|
|
1068
|
+
description: "Break ENABLE_SENTRY env var check (E->X)",
|
|
1069
|
+
pattern: Buffer.from("ENABLE_SENTRY"),
|
|
1070
|
+
replacement: Buffer.from("XNABLE_SENTRY")
|
|
1071
|
+
});
|
|
1072
|
+
patches.push({
|
|
1073
|
+
name: "noTelemetrySentryEnv2",
|
|
1074
|
+
description: "Break VITE_VERCEL_ENV env var check (V->X)",
|
|
1075
|
+
pattern: Buffer.from("VITE_VERCEL_ENV"),
|
|
1076
|
+
replacement: Buffer.from("XITE_VERCEL_ENV")
|
|
1077
|
+
});
|
|
1078
|
+
patches.push({
|
|
1079
|
+
name: "noTelemetryFlushBlock",
|
|
1080
|
+
description: "Make flushToWeb always return (!0|| = always true)",
|
|
1081
|
+
pattern: Buffer.from("this.webEvents.length===0"),
|
|
1082
|
+
replacement: Buffer.from("!0||this.webEvents.length")
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
const outputPath = join(join(homedir(), ".droid-patch", "bins"), `${meta.name}-patched`);
|
|
1122
1086
|
if (patches.length > 0) {
|
|
1123
|
-
|
|
1087
|
+
if (!(await patchDroid({
|
|
1124
1088
|
inputPath: newBinaryPath,
|
|
1125
1089
|
outputPath,
|
|
1126
1090
|
patches,
|
|
1127
1091
|
dryRun: false,
|
|
1128
1092
|
backup: false,
|
|
1129
1093
|
verbose
|
|
1130
|
-
})
|
|
1131
|
-
if (!result.success) {
|
|
1094
|
+
})).success) {
|
|
1132
1095
|
console.log(styleText("red", ` ✗ Failed to apply patches`));
|
|
1133
1096
|
failCount++;
|
|
1134
1097
|
continue;
|
|
@@ -1141,13 +1104,20 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
1141
1104
|
console.log(styleText("yellow", ` [!] Could not re-sign binary`));
|
|
1142
1105
|
}
|
|
1143
1106
|
}
|
|
1144
|
-
if (meta.patches.websearch) {
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1107
|
+
if (meta.patches.websearch || !!meta.patches.proxy) {
|
|
1108
|
+
const forwardTarget = meta.patches.apiBase || meta.patches.proxy || "https://api.factory.ai";
|
|
1109
|
+
await createWebSearchUnifiedFiles(join(homedir(), ".droid-patch", "proxy"), patches.length > 0 ? outputPath : newBinaryPath, meta.name, forwardTarget, meta.patches.standalone || false);
|
|
1110
|
+
if (verbose) {
|
|
1111
|
+
console.log(styleText("gray", ` Regenerated websearch wrapper`));
|
|
1112
|
+
if (meta.patches.standalone) console.log(styleText("gray", ` Standalone mode: enabled`));
|
|
1113
|
+
}
|
|
1114
|
+
if (meta.patches.proxy && !meta.patches.websearch) {
|
|
1115
|
+
meta.patches.websearch = true;
|
|
1116
|
+
meta.patches.apiBase = meta.patches.proxy;
|
|
1117
|
+
delete meta.patches.proxy;
|
|
1118
|
+
}
|
|
1149
1119
|
}
|
|
1150
|
-
meta.updatedAt = new Date().toISOString();
|
|
1120
|
+
meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1151
1121
|
meta.originalBinaryPath = newBinaryPath;
|
|
1152
1122
|
await saveAliasMetadata(meta);
|
|
1153
1123
|
console.log(styleText("green", ` ✓ Updated successfully`));
|
|
@@ -1177,4 +1147,5 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
|
|
|
1177
1147
|
});
|
|
1178
1148
|
|
|
1179
1149
|
//#endregion
|
|
1180
|
-
|
|
1150
|
+
export { };
|
|
1151
|
+
//# sourceMappingURL=cli.mjs.map
|