droid-patch 0.11.0 → 0.11.2

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.
@@ -21,33 +21,47 @@ async function patchDroid(options) {
21
21
  for (const patch of patches) {
22
22
  console.log(styleText("white", `[*] Checking patch: ${styleText("yellow", patch.name)}`));
23
23
  console.log(styleText("gray", ` ${patch.description}`));
24
- const positions = findAllPositions(workingBuffer, patch.pattern);
24
+ const variants = [{
25
+ pattern: patch.pattern,
26
+ replacement: patch.replacement
27
+ }, ...patch.variants || []];
28
+ let positions = [];
29
+ let matchedVariant;
30
+ for (const variant of variants) {
31
+ positions = findAllPositions(workingBuffer, variant.pattern);
32
+ if (positions.length > 0) {
33
+ matchedVariant = variant;
34
+ break;
35
+ }
36
+ }
25
37
  if (positions.length === 0) {
26
38
  console.log(styleText("yellow", ` ! Pattern not found - may already be patched`));
27
39
  results.push({
28
40
  name: patch.name,
29
41
  found: 0,
30
42
  success: false,
31
- alreadyPatched: workingBuffer.includes(patch.replacement)
43
+ alreadyPatched: variants.some((v) => workingBuffer.includes(v.replacement))
32
44
  });
33
- const replacementPositions = findAllPositions(workingBuffer, patch.replacement);
34
- if (replacementPositions.length > 0) {
35
- console.log(styleText("blue", ` ✓ Found ${replacementPositions.length} occurrences of patched pattern`));
45
+ let totalReplacementPositions = 0;
46
+ for (const variant of variants) totalReplacementPositions += findAllPositions(workingBuffer, variant.replacement).length;
47
+ if (totalReplacementPositions > 0) {
48
+ console.log(styleText("blue", ` ✓ Found ${totalReplacementPositions} occurrences of patched pattern`));
36
49
  console.log(styleText("blue", ` ✓ Binary appears to be already patched`));
37
50
  results[results.length - 1].alreadyPatched = true;
38
51
  results[results.length - 1].success = true;
39
52
  }
40
53
  continue;
41
54
  }
55
+ if (!matchedVariant) throw new Error(`Internal error: matchedVariant not set for patch ${patch.name}`);
42
56
  console.log(styleText("green", ` ✓ Found ${positions.length} occurrences`));
43
57
  if (verbose) {
44
58
  for (const pos of positions.slice(0, 5)) {
45
- const context = getContext(workingBuffer, pos, patch.pattern.length, 25);
59
+ const context = getContext(workingBuffer, pos, matchedVariant.pattern.length, 25);
46
60
  console.log(styleText("gray", ` @ 0x${pos.toString(16).padStart(8, "0")}: ...${context}...`));
47
61
  }
48
62
  if (positions.length > 5) console.log(styleText("gray", ` ... and ${positions.length - 5} more`));
49
63
  }
50
- if (!dryRun) for (const pos of positions) patch.replacement.copy(workingBuffer, pos);
64
+ if (!dryRun) for (const pos of positions) matchedVariant.replacement.copy(workingBuffer, pos);
51
65
  results.push({
52
66
  name: patch.name,
53
67
  found: positions.length,
@@ -105,8 +119,16 @@ async function patchDroid(options) {
105
119
  const verifyBuffer = await readFile(finalOutputPath);
106
120
  let allVerified = true;
107
121
  for (const patch of patches) {
108
- const oldCount = findAllPositions(verifyBuffer, patch.pattern).length;
109
- const newCount = findAllPositions(verifyBuffer, patch.replacement).length;
122
+ const variants = [{
123
+ pattern: patch.pattern,
124
+ replacement: patch.replacement
125
+ }, ...patch.variants || []];
126
+ let oldCount = 0;
127
+ let newCount = 0;
128
+ for (const variant of variants) {
129
+ oldCount += findAllPositions(verifyBuffer, variant.pattern).length;
130
+ newCount += findAllPositions(verifyBuffer, variant.replacement).length;
131
+ }
110
132
  if (oldCount === 0) console.log(styleText("green", ` ✓ ${patch.name}: Verified (${newCount} patched)`));
111
133
  else {
112
134
  console.log(styleText("red", ` ✗ ${patch.name}: ${oldCount} occurrences not patched`));
@@ -981,4 +1003,4 @@ async function clearAllAliases() {
981
1003
 
982
1004
  //#endregion
983
1005
  export { removeAlias as a, restoreOriginal as c, listAllMetadata as d, loadAliasMetadata as f, listAliases as i, createMetadata as l, patchDroid as m, createAlias as n, removeAliasesByFilter as o, saveAliasMetadata as p, createAliasForWrapper as r, replaceOriginal as s, clearAllAliases as t, formatPatches as u };
984
- //# sourceMappingURL=alias-CX4QSelz.mjs.map
1006
+ //# sourceMappingURL=alias-CEe6DN63.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alias-CEe6DN63.mjs","names":["results: PatchResult[]","positions: number[]","matchedVariant: (typeof variants)[number] | undefined","metaList: AliasMetadata[]","applied: string[]","exportLine: string","binaryDest","aliases: AliasInfo[]","matchingAliases: string[]"],"sources":["../src/patcher.ts","../src/metadata.ts","../src/alias.ts"],"sourcesContent":["import { readFile, writeFile, copyFile, chmod, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { styleText } from \"node:util\";\n\nexport interface Patch {\n name: string;\n description: string;\n pattern: Buffer;\n replacement: Buffer;\n variants?: Array<{\n pattern: Buffer;\n replacement: Buffer;\n }>;\n}\n\nexport interface PatchOptions {\n inputPath: string;\n outputPath?: string;\n patches: Patch[];\n dryRun?: boolean;\n backup?: boolean;\n verbose?: boolean;\n}\n\ninterface PatchResult {\n name: string;\n found: number;\n positions?: number[];\n success: boolean;\n alreadyPatched?: boolean;\n}\n\nexport interface PatchDroidResult {\n success: boolean;\n dryRun?: boolean;\n results: PatchResult[];\n outputPath?: string;\n noPatchNeeded?: boolean;\n patchedCount?: number;\n}\n\nexport async function patchDroid(options: PatchOptions): Promise<PatchDroidResult> {\n const {\n inputPath,\n outputPath,\n patches,\n dryRun = false,\n backup = true,\n verbose = false,\n } = options;\n\n const finalOutputPath = outputPath || `${inputPath}.patched`;\n\n if (!existsSync(inputPath)) {\n throw new Error(`Binary not found: ${inputPath}`);\n }\n\n const stats = await stat(inputPath);\n const fileSizeMB = (stats.size / (1024 * 1024)).toFixed(2);\n\n console.log(styleText(\"white\", `[*] Reading binary: ${styleText(\"cyan\", inputPath)}`));\n console.log(styleText(\"white\", `[*] File size: ${styleText(\"cyan\", fileSizeMB)} MB`));\n console.log();\n\n const data = await readFile(inputPath);\n const buffer = Buffer.from(data);\n\n // Use a working buffer that gets updated after each patch application\n // This ensures later patches search against the already-patched content\n const workingBuffer = Buffer.from(buffer);\n\n const results: PatchResult[] = [];\n\n for (const patch of patches) {\n console.log(styleText(\"white\", `[*] Checking patch: ${styleText(\"yellow\", patch.name)}`));\n console.log(styleText(\"gray\", ` ${patch.description}`));\n\n const variants = [\n { pattern: patch.pattern, replacement: patch.replacement },\n ...(patch.variants || []),\n ];\n\n // Search in the working buffer (which may have earlier patches applied)\n let positions: number[] = [];\n let matchedVariant: (typeof variants)[number] | undefined;\n for (const variant of variants) {\n positions = findAllPositions(workingBuffer, variant.pattern);\n if (positions.length > 0) {\n matchedVariant = variant;\n break;\n }\n }\n\n if (positions.length === 0) {\n console.log(styleText(\"yellow\", ` ! Pattern not found - may already be patched`));\n results.push({\n name: patch.name,\n found: 0,\n success: false,\n alreadyPatched: variants.some((v) => workingBuffer.includes(v.replacement)),\n });\n\n let totalReplacementPositions = 0;\n for (const variant of variants) {\n totalReplacementPositions += findAllPositions(workingBuffer, variant.replacement).length;\n }\n if (totalReplacementPositions > 0) {\n console.log(\n styleText(\n \"blue\",\n ` ✓ Found ${totalReplacementPositions} occurrences of patched pattern`,\n ),\n );\n console.log(styleText(\"blue\", ` ✓ Binary appears to be already patched`));\n results[results.length - 1].alreadyPatched = true;\n results[results.length - 1].success = true;\n }\n continue;\n }\n\n if (!matchedVariant) {\n throw new Error(`Internal error: matchedVariant not set for patch ${patch.name}`);\n }\n\n console.log(styleText(\"green\", ` ✓ Found ${positions.length} occurrences`));\n\n if (verbose) {\n for (const pos of positions.slice(0, 5)) {\n const context = getContext(workingBuffer, pos, matchedVariant.pattern.length, 25);\n console.log(\n styleText(\"gray\", ` @ 0x${pos.toString(16).padStart(8, \"0\")}: ...${context}...`),\n );\n }\n if (positions.length > 5) {\n console.log(styleText(\"gray\", ` ... and ${positions.length - 5} more`));\n }\n }\n\n // Apply patch immediately to working buffer so later patches see updated content\n if (!dryRun) {\n for (const pos of positions) {\n matchedVariant.replacement.copy(workingBuffer, pos);\n }\n }\n\n results.push({\n name: patch.name,\n found: positions.length,\n positions,\n success: true,\n });\n }\n\n console.log();\n\n if (dryRun) {\n console.log(styleText(\"blue\", \"─\".repeat(60)));\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN RESULTS\"));\n console.log(styleText(\"blue\", \"─\".repeat(60)));\n console.log();\n\n for (const result of results) {\n if (result.alreadyPatched) {\n console.log(styleText(\"blue\", ` [✓] ${result.name}: Already patched`));\n } else if (result.found > 0) {\n console.log(\n styleText(\"green\", ` [✓] ${result.name}: ${result.found} occurrences will be patched`),\n );\n } else {\n console.log(styleText(\"yellow\", ` [!] ${result.name}: Pattern not found`));\n }\n }\n\n return {\n success: results.every((r) => r.success || r.alreadyPatched),\n dryRun: true,\n results,\n };\n }\n\n const patchesNeeded = results.filter((r) => r.found > 0 && !r.alreadyPatched);\n\n if (patchesNeeded.length === 0) {\n const allPatched = results.every((r) => r.alreadyPatched);\n if (allPatched) {\n console.log(styleText(\"blue\", \"[*] All patches already applied. Binary is up to date.\"));\n return {\n success: true,\n outputPath: inputPath,\n results,\n noPatchNeeded: true,\n };\n }\n console.log(styleText(\"yellow\", \"[!] No patches could be applied.\"));\n return { success: false, results };\n }\n\n if (backup) {\n const backupPath = `${inputPath}.backup`;\n if (!existsSync(backupPath)) {\n await copyFile(inputPath, backupPath);\n console.log(styleText(\"white\", `[*] Created backup: ${styleText(\"cyan\", backupPath)}`));\n } else {\n console.log(styleText(\"gray\", `[*] Backup already exists: ${backupPath}`));\n }\n }\n\n console.log(styleText(\"white\", \"[*] Applying patches...\"));\n // Patches have already been applied to workingBuffer during the check phase\n // Count total patches applied\n const totalPatched = results.reduce((sum, r) => sum + (r.positions?.length || 0), 0);\n\n console.log(styleText(\"green\", `[*] Applied ${totalPatched} patches`));\n\n await writeFile(finalOutputPath, workingBuffer);\n console.log(\n styleText(\"white\", `[*] Patched binary saved: ${styleText(\"cyan\", finalOutputPath)}`),\n );\n\n await chmod(finalOutputPath, 0o755);\n console.log(styleText(\"gray\", \"[*] Set executable permission\"));\n\n console.log();\n console.log(styleText(\"white\", \"[*] Verifying patches...\"));\n const verifyBuffer = await readFile(finalOutputPath);\n\n let allVerified = true;\n for (const patch of patches) {\n const variants = [\n { pattern: patch.pattern, replacement: patch.replacement },\n ...(patch.variants || []),\n ];\n\n let oldCount = 0;\n let newCount = 0;\n for (const variant of variants) {\n oldCount += findAllPositions(verifyBuffer, variant.pattern).length;\n newCount += findAllPositions(verifyBuffer, variant.replacement).length;\n }\n\n if (oldCount === 0) {\n console.log(styleText(\"green\", ` ✓ ${patch.name}: Verified (${newCount} patched)`));\n } else {\n console.log(styleText(\"red\", ` ✗ ${patch.name}: ${oldCount} occurrences not patched`));\n allVerified = false;\n }\n }\n\n if (allVerified) {\n console.log();\n console.log(styleText(\"green\", \"[+] All patches verified successfully!\"));\n }\n\n if (process.platform === \"darwin\") {\n console.log();\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${finalOutputPath}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(styleText(\"yellow\", \"[!] Could not re-sign binary\"));\n console.log(\n styleText(\n \"gray\",\n ` You may need to run: codesign --force --deep --sign - ${finalOutputPath}`,\n ),\n );\n }\n\n try {\n execSync(`xattr -cr \"${finalOutputPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n return {\n success: allVerified,\n outputPath: finalOutputPath,\n results,\n patchedCount: totalPatched,\n };\n}\n\nfunction findAllPositions(buffer: Buffer, pattern: Buffer): number[] {\n const positions: number[] = [];\n let pos = 0;\n\n while (true) {\n pos = buffer.indexOf(pattern, pos);\n if (pos === -1) break;\n positions.push(pos);\n pos += pattern.length;\n }\n\n return positions;\n}\n\nfunction getContext(\n buffer: Buffer,\n position: number,\n patternLength: number,\n contextSize: number,\n): string {\n const start = Math.max(0, position - contextSize);\n const end = Math.min(buffer.length, position + patternLength + contextSize);\n const slice = buffer.slice(start, end);\n\n let str = \"\";\n for (let i = 0; i < slice.length; i++) {\n const c = slice[i];\n if (c >= 32 && c < 127) {\n str += String.fromCharCode(c);\n } else {\n str += \".\";\n }\n }\n return str;\n}\n","/**\n * Alias Metadata Management\n *\n * Stores and retrieves metadata about created aliases, including\n * which patches were applied. This enables the `update` command\n * to re-apply the same patches when the original droid binary is updated.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Metadata structure for an alias\n */\nexport interface AliasMetadata {\n /** Alias name */\n name: string;\n /** ISO timestamp when created */\n createdAt: string;\n /** ISO timestamp when last updated */\n updatedAt: string;\n /** Path to the original droid binary used for patching */\n originalBinaryPath: string;\n /** Path where the alias symlink was created */\n aliasPath?: string;\n /** droid-patch version used to create this alias */\n droidPatchVersion?: string;\n /** droid binary version */\n droidVersion?: string;\n /** Patches that were applied */\n patches: {\n isCustom: boolean;\n skipLogin: boolean;\n /** API base URL for binary patching or websearch forward target */\n apiBase: string | null;\n /** Whether websearch is enabled (external providers mode) */\n websearch: boolean;\n /** Whether websearch-proxy is enabled (native provider mode) */\n websearchProxy?: boolean;\n /** @deprecated Old proxy field, kept for backward compatibility */\n proxy?: string | null;\n reasoningEffort: boolean;\n /** Whether telemetry/Sentry is disabled */\n noTelemetry?: boolean;\n /** Standalone mode: mock non-LLM Factory APIs */\n standalone?: boolean;\n /** Hardcode autonomy mode to auto-high */\n autoHigh?: boolean;\n };\n}\n\n// Directory for storing metadata files\nconst META_DIR = join(homedir(), \".droid-patch\", \"meta\");\n\n/**\n * Ensure metadata directory exists\n */\nasync function ensureMetaDir(): Promise<void> {\n if (!existsSync(META_DIR)) {\n await mkdir(META_DIR, { recursive: true });\n }\n}\n\n/**\n * Get the path to a metadata file for an alias\n */\nfunction getMetaPath(aliasName: string): string {\n return join(META_DIR, `${aliasName}.json`);\n}\n\n/**\n * Save alias metadata to disk\n */\nexport async function saveAliasMetadata(meta: AliasMetadata): Promise<void> {\n await ensureMetaDir();\n const metaPath = getMetaPath(meta.name);\n await writeFile(metaPath, JSON.stringify(meta, null, 2));\n}\n\n/**\n * Load alias metadata from disk\n * Returns null if metadata doesn't exist\n */\nexport async function loadAliasMetadata(aliasName: string): Promise<AliasMetadata | null> {\n const metaPath = getMetaPath(aliasName);\n if (!existsSync(metaPath)) {\n return null;\n }\n try {\n const content = await readFile(metaPath, \"utf-8\");\n return JSON.parse(content) as AliasMetadata;\n } catch {\n return null;\n }\n}\n\n/**\n * List all alias metadata\n */\nexport async function listAllMetadata(): Promise<AliasMetadata[]> {\n await ensureMetaDir();\n\n const files = await readdir(META_DIR);\n const metaList: AliasMetadata[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".json\")) continue;\n\n const aliasName = file.replace(/\\.json$/, \"\");\n const meta = await loadAliasMetadata(aliasName);\n if (meta) {\n metaList.push(meta);\n }\n }\n\n return metaList;\n}\n\n/**\n * Remove alias metadata\n */\nexport async function removeAliasMetadata(aliasName: string): Promise<boolean> {\n const metaPath = getMetaPath(aliasName);\n if (!existsSync(metaPath)) {\n return false;\n }\n try {\n await unlink(metaPath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Create a new metadata object with current timestamp\n */\nexport function createMetadata(\n name: string,\n originalBinaryPath: string,\n patches: AliasMetadata[\"patches\"],\n options?: {\n droidPatchVersion?: string;\n droidVersion?: string;\n aliasPath?: string;\n },\n): AliasMetadata {\n const now = new Date().toISOString();\n return {\n name,\n createdAt: now,\n updatedAt: now,\n originalBinaryPath,\n aliasPath: options?.aliasPath,\n droidPatchVersion: options?.droidPatchVersion,\n droidVersion: options?.droidVersion,\n patches,\n };\n}\n\n/**\n * Format patches for display\n */\nexport function formatPatches(patches: AliasMetadata[\"patches\"]): string {\n const applied: string[] = [];\n if (patches.isCustom) applied.push(\"isCustom\");\n if (patches.skipLogin) applied.push(\"skipLogin\");\n // Show apiBase only when not using websearch (binary patch mode)\n if (patches.apiBase && !patches.websearch && !patches.websearchProxy)\n applied.push(`apiBase(${patches.apiBase})`);\n // Show websearch with optional custom target (external providers mode)\n if (patches.websearch) {\n const target = patches.apiBase || \"api.factory.ai\";\n applied.push(`websearch(${target})`);\n }\n // Show websearchProxy (native provider mode)\n if (patches.websearchProxy) {\n applied.push(\"websearchProxy(native)\");\n }\n // Support old proxy field for backward compatibility\n if (patches.proxy && !patches.websearch && !patches.websearchProxy)\n applied.push(`websearch(${patches.proxy})`);\n if (patches.reasoningEffort) applied.push(\"reasoningEffort\");\n if (patches.noTelemetry) applied.push(\"noTelemetry\");\n if (patches.standalone) applied.push(\"standalone\");\n if (patches.autoHigh) applied.push(\"autoHigh\");\n return applied.length > 0 ? applied.join(\", \") : \"(none)\";\n}\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n unlinkSync,\n lstatSync,\n readFileSync,\n appendFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { symlink, readlink, unlink, copyFile, chmod } from \"node:fs/promises\";\nimport { join, basename, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { styleText } from \"node:util\";\nimport { removeAliasMetadata, loadAliasMetadata, formatPatches } from \"./metadata.ts\";\n\nconst DROID_PATCH_DIR = join(homedir(), \".droid-patch\");\nconst ALIASES_DIR = join(DROID_PATCH_DIR, \"aliases\");\nconst BINS_DIR = join(DROID_PATCH_DIR, \"bins\");\n\nconst COMMON_PATH_DIRS = [\n join(homedir(), \".local/bin\"),\n join(homedir(), \"bin\"),\n join(homedir(), \".bin\"),\n \"/opt/homebrew/bin\",\n \"/usr/local/bin\",\n join(homedir(), \".npm-global/bin\"),\n join(homedir(), \".npm/bin\"),\n join(homedir(), \".pnpm-global/bin\"),\n join(homedir(), \".yarn/bin\"),\n join(homedir(), \".config/yarn/global/node_modules/.bin\"),\n join(homedir(), \".cargo/bin\"),\n join(homedir(), \"go/bin\"),\n join(homedir(), \".deno/bin\"),\n join(homedir(), \".bun/bin\"),\n join(homedir(), \".local/share/mise/shims\"),\n join(homedir(), \".asdf/shims\"),\n join(homedir(), \".nvm/current/bin\"),\n join(homedir(), \".volta/bin\"),\n join(homedir(), \".fnm/current/bin\"),\n];\n\nfunction ensureDirectories(): void {\n if (!existsSync(DROID_PATCH_DIR)) {\n mkdirSync(DROID_PATCH_DIR, { recursive: true });\n }\n if (!existsSync(ALIASES_DIR)) {\n mkdirSync(ALIASES_DIR, { recursive: true });\n }\n if (!existsSync(BINS_DIR)) {\n mkdirSync(BINS_DIR, { recursive: true });\n }\n}\n\nfunction checkPathInclusion(): boolean {\n const pathEnv = process.env.PATH || \"\";\n return pathEnv.split(\":\").includes(ALIASES_DIR);\n}\n\nexport function findWritablePathDir(): string | null {\n const pathEnv = process.env.PATH || \"\";\n const pathDirs = pathEnv.split(\":\");\n\n for (const dir of COMMON_PATH_DIRS) {\n if (pathDirs.includes(dir)) {\n try {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const testFile = join(dir, `.droid-patch-test-${Date.now()}`);\n writeFileSync(testFile, \"\");\n unlinkSync(testFile);\n return dir;\n } catch {\n continue;\n }\n }\n }\n\n return null;\n}\n\nfunction getShellConfigPath(): string {\n const shell = process.env.SHELL || \"/bin/bash\";\n const shellName = basename(shell);\n\n switch (shellName) {\n case \"zsh\":\n return join(homedir(), \".zshrc\");\n case \"bash\": {\n const bashProfile = join(homedir(), \".bash_profile\");\n if (existsSync(bashProfile)) return bashProfile;\n return join(homedir(), \".bashrc\");\n }\n case \"fish\":\n return join(homedir(), \".config/fish/config.fish\");\n default:\n return join(homedir(), \".profile\");\n }\n}\n\nfunction isPathConfigured(shellConfigPath: string): boolean {\n if (!existsSync(shellConfigPath)) {\n return false;\n }\n\n try {\n const content = readFileSync(shellConfigPath, \"utf-8\");\n return content.includes(\".droid-patch/aliases\") || content.includes(\"droid-patch/aliases\");\n } catch {\n return false;\n }\n}\n\nfunction addPathToShellConfig(shellConfigPath: string, verbose = false): boolean {\n const shell = process.env.SHELL || \"/bin/bash\";\n const shellName = basename(shell);\n\n let exportLine: string;\n if (shellName === \"fish\") {\n exportLine = `\\n# Added by droid-patch\\nfish_add_path \"${ALIASES_DIR}\"\\n`;\n } else {\n exportLine = `\\n# Added by droid-patch\\nexport PATH=\"${ALIASES_DIR}:$PATH\"\\n`;\n }\n\n try {\n appendFileSync(shellConfigPath, exportLine);\n if (verbose) {\n console.log(styleText(\"gray\", ` Added PATH export to: ${shellConfigPath}`));\n }\n return true;\n } catch (error) {\n console.log(\n styleText(\"yellow\", `[!] Could not write to ${shellConfigPath}: ${(error as Error).message}`),\n );\n return false;\n }\n}\n\nexport interface CreateAliasResult {\n aliasPath: string;\n binaryPath: string;\n immediate?: boolean;\n}\n\nexport async function createAlias(\n patchedBinaryPath: string,\n aliasName: string,\n verbose = false,\n): Promise<CreateAliasResult> {\n ensureDirectories();\n\n console.log(styleText(\"white\", `[*] Creating alias: ${styleText(\"cyan\", aliasName)}`));\n\n const writablePathDir = findWritablePathDir();\n\n if (writablePathDir) {\n const targetPath = join(writablePathDir, aliasName);\n const binaryDest = join(BINS_DIR, `${aliasName}-patched`);\n await copyFile(patchedBinaryPath, binaryDest);\n await chmod(binaryDest, 0o755);\n\n if (verbose) {\n console.log(styleText(\"gray\", ` Stored binary: ${binaryDest}`));\n }\n\n if (existsSync(targetPath)) {\n await unlink(targetPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing: ${targetPath}`));\n }\n }\n\n await symlink(binaryDest, targetPath);\n\n if (process.platform === \"darwin\") {\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${binaryDest}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(styleText(\"yellow\", \"[!] Could not re-sign binary\"));\n }\n\n try {\n execSync(`xattr -cr \"${binaryDest}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log(styleText(\"green\", `[*] Created: ${targetPath} -> ${binaryDest}`));\n console.log();\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" ALIAS READY - NO ACTION REQUIRED!\"));\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log();\n console.log(\n styleText(\n \"white\",\n `The alias \"${styleText([\"cyan\", \"bold\"], aliasName)}\" is now available in ALL terminals.`,\n ),\n );\n console.log(styleText(\"gray\", `(Installed to: ${writablePathDir})`));\n\n return {\n aliasPath: targetPath,\n binaryPath: binaryDest,\n immediate: true,\n };\n }\n\n console.log(styleText(\"yellow\", \"[*] No writable PATH directory found, using fallback...\"));\n\n const binaryDest = join(BINS_DIR, `${aliasName}-patched`);\n await copyFile(patchedBinaryPath, binaryDest);\n await chmod(binaryDest, 0o755);\n\n if (verbose) {\n console.log(styleText(\"gray\", ` Copied binary to: ${binaryDest}`));\n }\n\n if (process.platform === \"darwin\") {\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${binaryDest}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(\n styleText(\"yellow\", \"[!] Could not re-sign binary. You may need to do this manually:\"),\n );\n console.log(styleText(\"gray\", ` codesign --force --deep --sign - \"${binaryDest}\"`));\n }\n\n try {\n execSync(`xattr -cr \"${binaryDest}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n const symlinkPath = join(ALIASES_DIR, aliasName);\n\n if (existsSync(symlinkPath)) {\n await unlink(symlinkPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing symlink`));\n }\n }\n\n await symlink(binaryDest, symlinkPath);\n await chmod(symlinkPath, 0o755);\n\n console.log(styleText(\"green\", `[*] Created symlink: ${symlinkPath} -> ${binaryDest}`));\n\n const shellConfig = getShellConfigPath();\n\n if (!checkPathInclusion()) {\n if (!isPathConfigured(shellConfig)) {\n console.log(styleText(\"white\", `[*] Configuring PATH in ${shellConfig}...`));\n\n if (addPathToShellConfig(shellConfig, verbose)) {\n console.log(styleText(\"green\", `[*] PATH configured successfully!`));\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" ACTION REQUIRED\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"To use the alias in this terminal, run:\"));\n console.log();\n console.log(styleText(\"cyan\", ` source ${shellConfig}`));\n console.log();\n console.log(styleText(\"gray\", \"Or simply open a new terminal window.\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n } else {\n const exportLine = `export PATH=\"${ALIASES_DIR}:$PATH\"`;\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" Manual PATH Configuration Required\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"Add this line to your shell config:\"));\n console.log(styleText(\"cyan\", ` ${exportLine}`));\n console.log();\n console.log(styleText(\"gray\", `Shell config file: ${shellConfig}`));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already configured in ${shellConfig}`));\n console.log();\n console.log(\n styleText(\n \"yellow\",\n `Note: Run \\`source ${shellConfig}\\` or open a new terminal to use the alias.`,\n ),\n );\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already includes aliases directory`));\n console.log();\n console.log(\n styleText(\n \"green\",\n `You can now use \"${styleText([\"cyan\", \"bold\"], aliasName)}\" command directly!`,\n ),\n );\n }\n\n return {\n aliasPath: symlinkPath,\n binaryPath: binaryDest,\n };\n}\n\nexport async function removeAlias(aliasName: string): Promise<void> {\n console.log(styleText(\"white\", `[*] Removing alias: ${styleText(\"cyan\", aliasName)}`));\n\n let removed = false;\n\n // Check common PATH directories for symlinks\n for (const pathDir of COMMON_PATH_DIRS) {\n const pathSymlink = join(pathDir, aliasName);\n if (existsSync(pathSymlink)) {\n try {\n const stats = lstatSync(pathSymlink);\n if (stats.isSymbolicLink()) {\n const target = await readlink(pathSymlink);\n // Support regular aliases, old websearch wrappers, and new proxy wrappers\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n await unlink(pathSymlink);\n console.log(styleText(\"green\", ` Removed: ${pathSymlink}`));\n removed = true;\n }\n }\n } catch {\n // Ignore\n }\n }\n }\n\n // Check aliases directory\n const symlinkPath = join(ALIASES_DIR, aliasName);\n if (existsSync(symlinkPath)) {\n await unlink(symlinkPath);\n console.log(styleText(\"green\", ` Removed: ${symlinkPath}`));\n removed = true;\n }\n\n // Remove binary if exists\n const binaryPath = join(BINS_DIR, `${aliasName}-patched`);\n if (existsSync(binaryPath)) {\n await unlink(binaryPath);\n console.log(styleText(\"green\", ` Removed binary: ${binaryPath}`));\n removed = true;\n }\n\n // Remove new proxy wrapper and related files if exist\n const proxyDir = join(DROID_PATCH_DIR, \"proxy\");\n const proxyWrapperPath = join(proxyDir, aliasName);\n const proxyScriptPath = join(proxyDir, `${aliasName}-proxy.js`);\n\n if (existsSync(proxyWrapperPath)) {\n await unlink(proxyWrapperPath);\n console.log(styleText(\"green\", ` Removed wrapper: ${proxyWrapperPath}`));\n removed = true;\n }\n\n if (existsSync(proxyScriptPath)) {\n await unlink(proxyScriptPath);\n console.log(styleText(\"green\", ` Removed proxy script: ${proxyScriptPath}`));\n removed = true;\n }\n\n // Remove old websearch wrapper and related files if exist (backward compatibility)\n const websearchDir = join(DROID_PATCH_DIR, \"websearch\");\n const wrapperPath = join(websearchDir, aliasName);\n const oldProxyPath = join(websearchDir, `${aliasName}-proxy.js`);\n const preloadPath = join(websearchDir, `${aliasName}-preload.js`);\n\n if (existsSync(wrapperPath)) {\n await unlink(wrapperPath);\n console.log(styleText(\"green\", ` Removed legacy wrapper: ${wrapperPath}`));\n removed = true;\n }\n\n if (existsSync(oldProxyPath)) {\n await unlink(oldProxyPath);\n console.log(styleText(\"green\", ` Removed legacy proxy: ${oldProxyPath}`));\n removed = true;\n }\n\n if (existsSync(preloadPath)) {\n await unlink(preloadPath);\n console.log(styleText(\"green\", ` Removed legacy preload: ${preloadPath}`));\n removed = true;\n }\n\n // Remove statusline wrapper and monitor script if exist\n const statuslineDir = join(DROID_PATCH_DIR, \"statusline\");\n const statuslineWrapperPath = join(statuslineDir, aliasName);\n const statuslineMonitorPath = join(statuslineDir, `${aliasName}-statusline.js`);\n const statuslineSessionsPath = join(statuslineDir, `${aliasName}-sessions.js`);\n\n if (existsSync(statuslineWrapperPath)) {\n await unlink(statuslineWrapperPath);\n console.log(styleText(\"green\", ` Removed statusline wrapper: ${statuslineWrapperPath}`));\n removed = true;\n }\n\n if (existsSync(statuslineMonitorPath)) {\n await unlink(statuslineMonitorPath);\n console.log(styleText(\"green\", ` Removed statusline monitor: ${statuslineMonitorPath}`));\n removed = true;\n }\n\n if (existsSync(statuslineSessionsPath)) {\n await unlink(statuslineSessionsPath);\n console.log(styleText(\"green\", ` Removed sessions browser: ${statuslineSessionsPath}`));\n removed = true;\n }\n\n // Remove metadata\n const metaRemoved = await removeAliasMetadata(aliasName);\n if (metaRemoved) {\n console.log(styleText(\"green\", ` Removed metadata`));\n removed = true;\n }\n\n if (!removed) {\n console.log(styleText(\"yellow\", ` Alias \"${aliasName}\" not found`));\n } else {\n console.log(styleText(\"green\", `[*] Alias \"${aliasName}\" removed successfully`));\n }\n}\n\nexport async function listAliases(): Promise<void> {\n ensureDirectories();\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid-Patch Aliases\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n interface AliasInfo {\n name: string;\n target: string;\n location: string;\n immediate: boolean;\n }\n\n const aliases: AliasInfo[] = [];\n\n for (const pathDir of COMMON_PATH_DIRS) {\n if (!existsSync(pathDir)) continue;\n\n try {\n const files = readdirSync(pathDir);\n for (const file of files) {\n const fullPath = join(pathDir, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n // Support regular aliases, old websearch wrappers, and new proxy wrappers\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliases.push({\n name: file,\n target,\n location: pathDir,\n immediate: true,\n });\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n try {\n const files = readdirSync(ALIASES_DIR);\n\n for (const file of files) {\n const fullPath = join(ALIASES_DIR, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n if (!aliases.find((a) => a.name === file)) {\n aliases.push({\n name: file,\n target,\n location: ALIASES_DIR,\n immediate: false,\n });\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n\n if (aliases.length === 0) {\n console.log(styleText(\"gray\", \" No aliases configured.\"));\n console.log();\n console.log(styleText(\"gray\", \" Create one with: npx droid-patch --is-custom <alias-name>\"));\n } else {\n console.log(styleText(\"white\", ` Found ${aliases.length} alias(es):`));\n console.log();\n for (const alias of aliases) {\n const status = alias.immediate\n ? styleText(\"green\", \"✓ immediate\")\n : styleText(\"yellow\", \"requires source\");\n console.log(styleText(\"green\", ` • ${styleText([\"cyan\", \"bold\"], alias.name)} [${status}]`));\n console.log(styleText(\"gray\", ` → ${alias.target}`));\n\n // Load and display metadata\n const meta = await loadAliasMetadata(alias.name);\n if (meta) {\n // Version info\n const patchVer = meta.droidPatchVersion\n ? `droid-patch@${meta.droidPatchVersion}`\n : \"unknown\";\n const droidVer = meta.droidVersion ? `droid@${meta.droidVersion}` : \"unknown\";\n console.log(styleText(\"gray\", ` Versions: ${patchVer}, ${droidVer}`));\n\n // Flags/patches\n const flags = formatPatches(meta.patches);\n console.log(styleText(\"gray\", ` Flags: ${flags}`));\n\n // Created time\n if (meta.createdAt) {\n const date = new Date(meta.createdAt).toLocaleString();\n console.log(styleText(\"gray\", ` Created: ${date}`));\n }\n } else {\n console.log(styleText(\"yellow\", ` (no metadata - created by older version)`));\n }\n console.log();\n }\n }\n\n console.log();\n console.log(styleText(\"gray\", ` Aliases directory: ${ALIASES_DIR}`));\n console.log(\n styleText(\n \"gray\",\n ` PATH configured: ${checkPathInclusion() ? styleText(\"green\", \"Yes\") : styleText(\"yellow\", \"No\")}`,\n ),\n );\n console.log();\n}\n\nexport interface ReplaceOriginalResult {\n originalPath: string;\n backupPath: string;\n}\n\nexport async function replaceOriginal(\n patchedBinaryPath: string,\n originalPath: string,\n verbose = false,\n): Promise<ReplaceOriginalResult> {\n ensureDirectories();\n\n console.log(\n styleText(\"white\", `[*] Replacing original binary: ${styleText(\"cyan\", originalPath)}`),\n );\n\n const latestBackupPath = join(BINS_DIR, \"droid-original-latest\");\n\n if (!existsSync(latestBackupPath)) {\n await copyFile(originalPath, latestBackupPath);\n console.log(styleText(\"green\", `[*] Created backup: ${latestBackupPath}`));\n } else {\n if (verbose) {\n console.log(styleText(\"gray\", ` Backup already exists: ${latestBackupPath}`));\n }\n }\n\n await copyFile(patchedBinaryPath, originalPath);\n await chmod(originalPath, 0o755);\n console.log(styleText(\"green\", `[*] Replaced: ${originalPath}`));\n\n if (process.platform === \"darwin\") {\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${originalPath}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(styleText(\"yellow\", \"[!] Could not re-sign binary. You may need to run:\"));\n console.log(styleText(\"gray\", ` codesign --force --deep --sign - \"${originalPath}\"`));\n }\n\n try {\n execSync(`xattr -cr \"${originalPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" REPLACEMENT COMPLETE\"));\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"The patched binary is now active in all terminals.\"));\n console.log(styleText(\"white\", \"No need to restart or source anything!\"));\n console.log();\n console.log(styleText(\"gray\", `To restore the original, run:`));\n console.log(styleText(\"cyan\", ` npx droid-patch restore`));\n\n return {\n originalPath,\n backupPath: latestBackupPath,\n };\n}\n\n/**\n * Create alias for wrapper script\n * Unlike createAlias, this function creates symlink pointing to wrapper script\n * Used for features like websearch that require preprocessing\n */\nexport async function createAliasForWrapper(\n wrapperPath: string,\n aliasName: string,\n verbose = false,\n): Promise<CreateAliasResult> {\n ensureDirectories();\n\n console.log(styleText(\"white\", `[*] Creating alias: ${styleText(\"cyan\", aliasName)}`));\n\n const writablePathDir = findWritablePathDir();\n\n if (writablePathDir) {\n const targetPath = join(writablePathDir, aliasName);\n\n if (verbose) {\n console.log(styleText(\"gray\", ` Wrapper: ${wrapperPath}`));\n }\n\n if (existsSync(targetPath)) {\n await unlink(targetPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing: ${targetPath}`));\n }\n }\n\n await symlink(wrapperPath, targetPath);\n\n console.log(styleText(\"green\", `[*] Created: ${targetPath} -> ${wrapperPath}`));\n console.log();\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" ALIAS READY - NO ACTION REQUIRED!\"));\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log();\n console.log(\n styleText(\n \"white\",\n `The alias \"${styleText([\"cyan\", \"bold\"], aliasName)}\" is now available in ALL terminals.`,\n ),\n );\n console.log(styleText(\"gray\", `(Installed to: ${writablePathDir})`));\n\n return {\n aliasPath: targetPath,\n binaryPath: wrapperPath,\n immediate: true,\n };\n }\n\n // Fallback: use ~/.droid-patch/aliases\n console.log(styleText(\"yellow\", \"[*] No writable PATH directory found, using fallback...\"));\n\n const symlinkPath = join(ALIASES_DIR, aliasName);\n\n if (existsSync(symlinkPath)) {\n await unlink(symlinkPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing symlink`));\n }\n }\n\n await symlink(wrapperPath, symlinkPath);\n\n console.log(styleText(\"green\", `[*] Created symlink: ${symlinkPath} -> ${wrapperPath}`));\n\n const shellConfig = getShellConfigPath();\n\n if (!checkPathInclusion()) {\n if (!isPathConfigured(shellConfig)) {\n console.log(styleText(\"white\", `[*] Configuring PATH in ${shellConfig}...`));\n\n if (addPathToShellConfig(shellConfig, verbose)) {\n console.log(styleText(\"green\", `[*] PATH configured successfully!`));\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" ACTION REQUIRED\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"To use the alias in this terminal, run:\"));\n console.log();\n console.log(styleText(\"cyan\", ` source ${shellConfig}`));\n console.log();\n console.log(styleText(\"gray\", \"Or simply open a new terminal window.\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n } else {\n const exportLine = `export PATH=\"${ALIASES_DIR}:$PATH\"`;\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" Manual PATH Configuration Required\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"Add this line to your shell config:\"));\n console.log(styleText(\"cyan\", ` ${exportLine}`));\n console.log();\n console.log(styleText(\"gray\", `Shell config file: ${shellConfig}`));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already configured in ${shellConfig}`));\n console.log();\n console.log(\n styleText(\n \"yellow\",\n `Note: Run \\`source ${shellConfig}\\` or open a new terminal to use the alias.`,\n ),\n );\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already includes aliases directory`));\n console.log();\n console.log(\n styleText(\n \"green\",\n `You can now use \"${styleText([\"cyan\", \"bold\"], aliasName)}\" command directly!`,\n ),\n );\n }\n\n return {\n aliasPath: symlinkPath,\n binaryPath: wrapperPath,\n };\n}\n\nexport async function restoreOriginal(originalPath: string): Promise<void> {\n ensureDirectories();\n\n const latestBackupPath = join(BINS_DIR, \"droid-original-latest\");\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Restore Original Droid\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n if (!existsSync(latestBackupPath)) {\n const localBackup = `${originalPath}.backup`;\n if (existsSync(localBackup)) {\n console.log(styleText(\"white\", `[*] Found local backup: ${localBackup}`));\n console.log(styleText(\"white\", `[*] Restoring to: ${originalPath}`));\n\n await copyFile(localBackup, originalPath);\n await chmod(originalPath, 0o755);\n\n if (process.platform === \"darwin\") {\n try {\n execSync(`codesign --force --deep --sign - \"${originalPath}\"`, {\n stdio: \"pipe\",\n });\n execSync(`xattr -cr \"${originalPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" RESTORE COMPLETE\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"green\", \"Original droid binary has been restored from local backup.\"));\n return;\n }\n\n console.log(styleText(\"red\", \"[!] No backup found.\"));\n console.log(styleText(\"gray\", ` Checked: ${latestBackupPath}`));\n console.log(styleText(\"gray\", ` Checked: ${localBackup}`));\n console.log();\n console.log(styleText(\"gray\", \"If you have a manual backup, restore it with:\"));\n console.log(styleText(\"cyan\", ` cp /path/to/backup ${originalPath}`));\n return;\n }\n\n console.log(styleText(\"white\", `[*] Restoring from: ${latestBackupPath}`));\n console.log(styleText(\"white\", `[*] Restoring to: ${originalPath}`));\n\n const targetDir = dirname(originalPath);\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n }\n\n await copyFile(latestBackupPath, originalPath);\n await chmod(originalPath, 0o755);\n\n if (process.platform === \"darwin\") {\n try {\n execSync(`codesign --force --deep --sign - \"${originalPath}\"`, {\n stdio: \"pipe\",\n });\n execSync(`xattr -cr \"${originalPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" RESTORE COMPLETE\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"green\", \"Original droid binary has been restored.\"));\n console.log(styleText(\"green\", \"All terminals will now use the original version.\"));\n}\n\n/**\n * Filter options for removing aliases\n * Uses the same names as CLI options for consistency\n */\nexport type FilterFlag =\n | \"is-custom\"\n | \"skip-login\"\n | \"websearch\"\n | \"api-base\"\n | \"reasoning-effort\"\n | \"disable-telemetry\"\n | \"standalone\";\n\nexport interface RemoveFilterOptions {\n /** Remove aliases created by this droid-patch version */\n patchVersion?: string;\n /** Remove aliases for this droid version */\n droidVersion?: string;\n /** Remove aliases that have these flags enabled (all must match) */\n flags?: FilterFlag[];\n}\n\n/**\n * Remove aliases matching filter criteria\n */\nexport async function removeAliasesByFilter(filter: RemoveFilterOptions): Promise<void> {\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Remove Aliases by Filter\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Show filter criteria\n if (filter.patchVersion) {\n console.log(styleText(\"white\", ` Filter: droid-patch version = ${filter.patchVersion}`));\n }\n if (filter.droidVersion) {\n console.log(styleText(\"white\", ` Filter: droid version = ${filter.droidVersion}`));\n }\n if (filter.flags && filter.flags.length > 0) {\n console.log(styleText(\"white\", ` Filter: flags = ${filter.flags.join(\", \")}`));\n }\n console.log();\n\n // Collect all alias names\n const aliasNames = new Set<string>();\n\n // Check common PATH directories for symlinks\n for (const pathDir of COMMON_PATH_DIRS) {\n if (!existsSync(pathDir)) continue;\n\n try {\n const files = readdirSync(pathDir);\n for (const file of files) {\n const fullPath = join(pathDir, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasNames.add(file);\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n // Check aliases directory\n if (existsSync(ALIASES_DIR)) {\n try {\n const files = readdirSync(ALIASES_DIR);\n for (const file of files) {\n const fullPath = join(ALIASES_DIR, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n aliasNames.add(file);\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n // Filter aliases by metadata\n const matchingAliases: string[] = [];\n\n for (const aliasName of aliasNames) {\n const meta = await loadAliasMetadata(aliasName);\n\n // If no metadata, skip (can't filter without metadata)\n if (!meta) {\n continue;\n }\n\n let matches = true;\n\n // Check droid-patch version\n if (filter.patchVersion && meta.droidPatchVersion !== filter.patchVersion) {\n matches = false;\n }\n\n // Check droid version\n if (filter.droidVersion && meta.droidVersion !== filter.droidVersion) {\n matches = false;\n }\n\n // Check flags (all specified flags must match)\n if (filter.flags && filter.flags.length > 0) {\n const patches = meta.patches;\n for (const flag of filter.flags) {\n switch (flag) {\n case \"is-custom\":\n if (!patches.isCustom) matches = false;\n break;\n case \"skip-login\":\n if (!patches.skipLogin) matches = false;\n break;\n case \"websearch\":\n if (!patches.websearch) matches = false;\n break;\n case \"reasoning-effort\":\n if (!patches.reasoningEffort) matches = false;\n break;\n case \"api-base\":\n if (!patches.apiBase) matches = false;\n break;\n case \"disable-telemetry\":\n if (!patches.noTelemetry) matches = false;\n break;\n case \"standalone\":\n if (!patches.standalone) matches = false;\n break;\n }\n if (!matches) break;\n }\n }\n\n if (matches) {\n matchingAliases.push(aliasName);\n }\n }\n\n if (matchingAliases.length === 0) {\n console.log(styleText(\"yellow\", \" No aliases match the filter criteria.\"));\n console.log();\n return;\n }\n\n console.log(styleText(\"white\", ` Found ${matchingAliases.length} matching alias(es):`));\n for (const name of matchingAliases) {\n console.log(styleText(\"gray\", ` • ${name}`));\n }\n console.log();\n\n // Remove each matching alias\n for (const aliasName of matchingAliases) {\n await removeAlias(aliasName);\n console.log();\n }\n\n console.log(styleText(\"green\", `[*] Removed ${matchingAliases.length} alias(es)`));\n}\n\n/**\n * Clear all droid-patch aliases and related files\n */\nexport async function clearAllAliases(): Promise<void> {\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Clearing All Droid-Patch Data\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Collect all alias names\n const aliasNames = new Set<string>();\n\n // Check common PATH directories for symlinks\n for (const pathDir of COMMON_PATH_DIRS) {\n if (!existsSync(pathDir)) continue;\n\n try {\n const files = readdirSync(pathDir);\n for (const file of files) {\n const fullPath = join(pathDir, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasNames.add(file);\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n // Check aliases directory\n if (existsSync(ALIASES_DIR)) {\n try {\n const files = readdirSync(ALIASES_DIR);\n for (const file of files) {\n const fullPath = join(ALIASES_DIR, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n aliasNames.add(file);\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n if (aliasNames.size === 0) {\n console.log(styleText(\"yellow\", \" No aliases found.\"));\n } else {\n console.log(styleText(\"white\", ` Found ${aliasNames.size} alias(es) to remove:`));\n for (const name of aliasNames) {\n console.log(styleText(\"gray\", ` • ${name}`));\n }\n console.log();\n\n // Remove each alias\n for (const aliasName of aliasNames) {\n await removeAlias(aliasName);\n console.log();\n }\n }\n\n // Clean up directories (including legacy files)\n console.log(styleText(\"white\", \" Cleaning up directories...\"));\n const dirsToClean = [\n join(DROID_PATCH_DIR, \"bins\"),\n join(DROID_PATCH_DIR, \"aliases\"),\n join(DROID_PATCH_DIR, \"proxy\"),\n join(DROID_PATCH_DIR, \"websearch\"),\n join(DROID_PATCH_DIR, \"statusline\"),\n ];\n\n for (const dir of dirsToClean) {\n if (existsSync(dir)) {\n try {\n const files = readdirSync(dir);\n for (const file of files) {\n const fullPath = join(dir, file);\n try {\n await unlink(fullPath);\n console.log(styleText(\"green\", ` Removed: ${fullPath}`));\n } catch {\n // Ignore\n }\n }\n } catch {\n // Ignore\n }\n }\n }\n\n // Clean up legacy temp files from old versions\n const legacyTempFiles = [\"/tmp/droid-search-proxy.pid\", \"/tmp/droid-search-proxy.log\"];\n\n for (const tempFile of legacyTempFiles) {\n if (existsSync(tempFile)) {\n try {\n await unlink(tempFile);\n console.log(styleText(\"green\", ` Removed legacy: ${tempFile}`));\n } catch {\n // Ignore\n }\n }\n }\n\n // Clean up temp port files (pattern: /tmp/droid-websearch-*.port)\n try {\n const tmpFiles = readdirSync(\"/tmp\");\n for (const file of tmpFiles) {\n if (file.startsWith(\"droid-websearch-\") && file.endsWith(\".port\")) {\n const fullPath = join(\"/tmp\", file);\n try {\n await unlink(fullPath);\n console.log(styleText(\"green\", ` Removed temp: ${fullPath}`));\n } catch {\n // Ignore\n }\n }\n // Also clean old droid-search-proxy-*.port files\n if (file.startsWith(\"droid-search-proxy-\") && file.endsWith(\".port\")) {\n const fullPath = join(\"/tmp\", file);\n try {\n await unlink(fullPath);\n console.log(styleText(\"green\", ` Removed legacy temp: ${fullPath}`));\n } catch {\n // Ignore\n }\n }\n }\n } catch {\n // Ignore\n }\n\n // Clean up metadata file\n const metadataFile = join(DROID_PATCH_DIR, \"metadata.json\");\n if (existsSync(metadataFile)) {\n try {\n await unlink(metadataFile);\n console.log(styleText(\"green\", ` Removed: ${metadataFile}`));\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"[*] All droid-patch data cleared successfully\"));\n}\n"],"mappings":"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"}
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
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-CX4QSelz.mjs";
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-CEe6DN63.mjs";
3
3
  import bin from "tiny-bin";
4
4
  import { styleText } from "node:util";
5
5
  import { existsSync, readFileSync } from "node:fs";
@@ -237,9 +237,13 @@ function generateExternalSearchProxyServer(factoryApiUrl = "https://api.factory.
237
237
  * WebSearch Native Provider Mode (--websearch-proxy)
238
238
  *
239
239
  * Uses model's native websearch based on ~/.factory/settings.json configuration
240
- * Requires proxy plugin (anthropic4droid) to handle format conversion
240
+ * Requires proxy plugin to handle format conversion:
241
+ * - Anthropic provider: anthropic4droid plugin
242
+ * - OpenAI provider: openai4droid plugin (adds CODEX_INSTRUCTIONS)
241
243
  *
242
- * Supported providers: Anthropic, OpenAI (extensible)
244
+ * Supported providers:
245
+ * - Anthropic: web_search_20250305 server tool, results in web_search_tool_result
246
+ * - OpenAI: web_search tool, results in message.content[].annotations[] as url_citation
243
247
  */
244
248
  function generateNativeSearchProxyServer(factoryApiUrl = "https://api.factory.ai") {
245
249
  return `#!/usr/bin/env node
@@ -355,8 +359,10 @@ async function searchOpenAINative(query, numResults, modelConfig) {
355
359
  const { baseUrl, apiKey, model } = modelConfig;
356
360
 
357
361
  try {
362
+ // Note: instructions will be added by openai4droid proxy plugin
358
363
  const requestBody = {
359
364
  model: model,
365
+ stream: false,
360
366
  tools: [{ type: 'web_search' }],
361
367
  tool_choice: 'required',
362
368
  input: 'Search the web for: ' + query + '\\n\\nReturn up to ' + numResults + ' relevant results.'
@@ -375,15 +381,22 @@ async function searchOpenAINative(query, numResults, modelConfig) {
375
381
  try { response = JSON.parse(responseStr); } catch { return null; }
376
382
  if (response.error) { log('API error:', response.error.message); return null; }
377
383
 
384
+ // Extract results from url_citation annotations in message output
378
385
  const results = [];
379
386
  for (const item of (response.output || [])) {
380
- if (item.type === 'web_search_call' && item.status === 'completed') {
381
- for (const result of (item.results || [])) {
382
- results.push({
383
- title: result.title || '',
384
- url: result.url || '',
385
- content: result.snippet || result.content || ''
386
- });
387
+ if (item.type === 'message' && Array.isArray(item.content)) {
388
+ for (const content of item.content) {
389
+ if (content.type === 'output_text' && Array.isArray(content.annotations)) {
390
+ for (const annotation of content.annotations) {
391
+ if (annotation.type === 'url_citation' && annotation.url) {
392
+ results.push({
393
+ title: annotation.title || '',
394
+ url: annotation.url || '',
395
+ content: annotation.title || ''
396
+ });
397
+ }
398
+ }
399
+ }
387
400
  }
388
401
  }
389
402
  }
@@ -869,7 +882,11 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
869
882
  name: "reasoningEffortValidationBypass",
870
883
  description: "Bypass reasoning effort validation (allows xhigh in settings.json)",
871
884
  pattern: Buffer.from("T!==\"none\"&&T!==\"off\"&&!W.supportedReasoningEfforts.includes(T)"),
872
- replacement: Buffer.from("T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)")
885
+ replacement: Buffer.from("T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)"),
886
+ variants: [{
887
+ pattern: Buffer.from("T!==\"none\"&&T!==\"off\"&&!C.supportedReasoningEfforts.includes(T)"),
888
+ replacement: Buffer.from("T!=\"none\"&&T!=\"off\"&&0&&C.supportedReasoningEfforts.includes(T)")
889
+ }]
873
890
  });
874
891
  }
875
892
  if (noTelemetry) {
@@ -1127,7 +1144,11 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
1127
1144
  name: "reasoningEffortValidationBypass",
1128
1145
  description: "Bypass reasoning effort validation (allows xhigh in settings.json)",
1129
1146
  pattern: Buffer.from("T!==\"none\"&&T!==\"off\"&&!W.supportedReasoningEfforts.includes(T)"),
1130
- replacement: Buffer.from("T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)")
1147
+ replacement: Buffer.from("T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)"),
1148
+ variants: [{
1149
+ pattern: Buffer.from("T!==\"none\"&&T!==\"off\"&&!C.supportedReasoningEfforts.includes(T)"),
1150
+ replacement: Buffer.from("T!=\"none\"&&T!=\"off\"&&0&&C.supportedReasoningEfforts.includes(T)")
1151
+ }]
1131
1152
  });
1132
1153
  }
1133
1154
  if (meta.patches.noTelemetry) {
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["patches: Patch[]","flag: FilterFlag | undefined","allowedFlags: FilterFlag[]","unlink","metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[]","readlink","symlink"],"sources":["../src/websearch-external.ts","../src/websearch-native.ts","../src/websearch-patch.ts","../src/cli.ts"],"sourcesContent":["/**\n * WebSearch External Providers Mode (--websearch)\n *\n * Priority: Smithery Exa > Google PSE > Serper > Brave > SearXNG > DuckDuckGo\n */\n\nexport function generateSearchProxyServerCode(): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (External Providers Mode)\n// Priority: Smithery Exa > Google PSE > Serper > Brave > SearXNG > DuckDuckGo\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = 'https://api.factory.ai';\n\nfunction log() { if (DEBUG) console.error.apply(console, ['[websearch]'].concat(Array.from(arguments))); }\n\n// === External Search Providers ===\n\nasync function searchSmitheryExa(query, numResults) {\n const apiKey = process.env.SMITHERY_API_KEY;\n const profile = process.env.SMITHERY_PROFILE;\n if (!apiKey || !profile) return null;\n try {\n const serverUrl = 'https://server.smithery.ai/exa/mcp?api_key=' + encodeURIComponent(apiKey) + '&profile=' + encodeURIComponent(profile);\n const requestBody = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'web_search_exa', arguments: { query: query, numResults: numResults } } });\n const bodyStr = requestBody.replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + serverUrl + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const response = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 30000 }));\n if (response.result && response.result.content) {\n const textContent = response.result.content.find(function(c) { return c.type === 'text'; });\n if (textContent && textContent.text) {\n const results = JSON.parse(textContent.text);\n if (Array.isArray(results) && results.length > 0) {\n return results.slice(0, numResults).map(function(item) {\n return {\n title: item.title || '', url: item.url || '',\n content: item.text || item.snippet || (item.highlights ? item.highlights.join(' ') : '') || ''\n };\n });\n }\n }\n }\n } catch (e) { log('Smithery failed:', e.message); }\n return null;\n}\n\nasync function searchGooglePSE(query, numResults) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = 'https://www.googleapis.com/customsearch/v1?key=' + apiKey + '&cx=' + cx + '&q=' + encodeURIComponent(query) + '&num=' + Math.min(numResults, 10);\n const data = JSON.parse(execSync('curl -s \"' + url + '\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.error) return null;\n return (data.items || []).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n } catch (e) { log('Google PSE failed:', e.message); }\n return null;\n}\n\nasync function searchSerper(query, numResults) {\n const apiKey = process.env.SERPER_API_KEY;\n if (!apiKey) return null;\n try {\n const bodyStr = JSON.stringify({ q: query, num: numResults }).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s \"https://google.serper.dev/search\" -H \"X-API-KEY: ' + apiKey + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.organic && data.organic.length > 0) {\n return data.organic.slice(0, numResults).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n }\n } catch (e) { log('Serper failed:', e.message); }\n return null;\n}\n\nasync function searchBrave(query, numResults) {\n const apiKey = process.env.BRAVE_API_KEY;\n if (!apiKey) return null;\n try {\n const url = 'https://api.search.brave.com/res/v1/web/search?q=' + encodeURIComponent(query) + '&count=' + numResults;\n const curlCmd = 'curl -s \"' + url + '\" -H \"Accept: application/json\" -H \"X-Subscription-Token: ' + apiKey + '\"';\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.web && data.web.results && data.web.results.length > 0) {\n return data.web.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.description || '' }; });\n }\n } catch (e) { log('Brave failed:', e.message); }\n return null;\n}\n\nasync function searchSearXNG(query, numResults) {\n const searxngUrl = process.env.SEARXNG_URL;\n if (!searxngUrl) return null;\n try {\n const url = searxngUrl + '/search?q=' + encodeURIComponent(query) + '&format=json&engines=google,bing,duckduckgo';\n const data = JSON.parse(execSync('curl -s \"' + url + '\" -H \"Accept: application/json\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.results && data.results.length > 0) {\n return data.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.content || '' }; });\n }\n } catch (e) { log('SearXNG failed:', e.message); }\n return null;\n}\n\nasync function searchDuckDuckGo(query, numResults) {\n try {\n const apiUrl = 'https://api.duckduckgo.com/?q=' + encodeURIComponent(query) + '&format=json&no_html=1&skip_disambig=1';\n const data = JSON.parse(execSync('curl -s \"' + apiUrl + '\" -H \"User-Agent: Mozilla/5.0\"', { encoding: 'utf-8', timeout: 15000 }));\n const results = [];\n if (data.Abstract && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.Abstract });\n }\n var topics = data.RelatedTopics || [];\n for (var i = 0; i < topics.length && results.length < numResults; i++) {\n var topic = topics[i];\n if (topic.Text && topic.FirstURL) results.push({ title: topic.Text.substring(0, 100), url: topic.FirstURL, content: topic.Text });\n if (topic.Topics) {\n for (var j = 0; j < topic.Topics.length && results.length < numResults; j++) {\n var st = topic.Topics[j];\n if (st.Text && st.FirstURL) results.push({ title: st.Text.substring(0, 100), url: st.FirstURL, content: st.Text });\n }\n }\n }\n return results.length > 0 ? results : null;\n } catch (e) { log('DuckDuckGo failed:', e.message); }\n return null;\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n // Priority: Smithery > Google PSE > Serper > Brave > SearXNG > DuckDuckGo\n var results = await searchSmitheryExa(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'smithery-exa' };\n \n results = await searchGooglePSE(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'google-pse' };\n \n results = await searchSerper(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'serper' };\n \n results = await searchBrave(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'brave' };\n \n results = await searchSearXNG(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'searxng' };\n \n results = await searchDuckDuckGo(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'duckduckgo' };\n \n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'external-providers' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Proxy other requests\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('External providers proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n\nexport function generateExternalSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n const code = generateSearchProxyServerCode();\n return code.replace(\n \"const FACTORY_API = 'https://api.factory.ai';\",\n `const FACTORY_API = '${factoryApiUrl}';`,\n );\n}\n","/**\n * WebSearch Native Provider Mode (--websearch-proxy)\n *\n * Uses model's native websearch based on ~/.factory/settings.json configuration\n * Requires proxy plugin (anthropic4droid) to handle format conversion\n *\n * Supported providers: Anthropic, OpenAI (extensible)\n */\n\nexport function generateNativeSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (Native Provider Mode)\n// Reads ~/.factory/settings.json for model configuration\n// Requires proxy plugin (anthropic4droid) to handle format conversion\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\nconst os = require('os');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = '${factoryApiUrl}';\n\nfunction log(...args) { if (DEBUG) console.error('[websearch]', ...args); }\n\n// === Settings Configuration ===\n\nlet cachedSettings = null;\nlet settingsLastModified = 0;\n\nfunction getFactorySettings() {\n const settingsPath = path.join(os.homedir(), '.factory', 'settings.json');\n try {\n const stats = fs.statSync(settingsPath);\n if (cachedSettings && stats.mtimeMs === settingsLastModified) return cachedSettings;\n cachedSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n settingsLastModified = stats.mtimeMs;\n return cachedSettings;\n } catch (e) {\n log('Failed to load settings.json:', e.message);\n return null;\n }\n}\n\nfunction getCurrentModelConfig() {\n const settings = getFactorySettings();\n if (!settings) return null;\n \n const currentModelId = settings.sessionDefaultSettings?.model;\n if (!currentModelId) return null;\n \n const customModels = settings.customModels || [];\n const modelConfig = customModels.find(m => m.id === currentModelId);\n \n if (modelConfig) {\n log('Model:', modelConfig.displayName, '| Provider:', modelConfig.provider);\n return modelConfig;\n }\n \n if (!currentModelId.startsWith('custom:')) return null;\n log('Model not found:', currentModelId);\n return null;\n}\n\n// === Native Provider WebSearch ===\n\nasync function searchAnthropicNative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n const requestBody = {\n model: model,\n max_tokens: 4096,\n stream: false,\n system: 'You are a web search assistant. Use the web_search tool to find relevant information and return the results.',\n tools: [{ type: 'web_search_20250305', name: 'web_search', max_uses: 1 }],\n tool_choice: { type: 'tool', name: 'web_search' },\n messages: [{ role: 'user', content: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.' }]\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/v1/messages')) endpoint = endpoint.replace(/\\\\/$/, '') + '/v1/messages';\n \n log('Anthropic search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"anthropic-version: 2023-06-01\" -H \"x-api-key: ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n const results = [];\n for (const block of (response.content || [])) {\n if (block.type === 'web_search_tool_result') {\n for (const result of (block.content || [])) {\n if (result.type === 'web_search_result') {\n results.push({\n title: result.title || '',\n url: result.url || '',\n content: result.snippet || result.page_content || ''\n });\n }\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('Anthropic error:', e.message);\n return null;\n }\n}\n\nasync function searchOpenAINative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n const requestBody = {\n model: model,\n tools: [{ type: 'web_search' }],\n tool_choice: 'required',\n input: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.'\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/responses')) endpoint = endpoint.replace(/\\\\/$/, '') + '/responses';\n \n log('OpenAI search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"Authorization: Bearer ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n const results = [];\n for (const item of (response.output || [])) {\n if (item.type === 'web_search_call' && item.status === 'completed') {\n for (const result of (item.results || [])) {\n results.push({\n title: result.title || '',\n url: result.url || '',\n content: result.snippet || result.content || ''\n });\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('OpenAI error:', e.message);\n return null;\n }\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n const modelConfig = getCurrentModelConfig();\n if (!modelConfig) {\n log('No custom model configured');\n return { results: [], source: 'none' };\n }\n \n const provider = modelConfig.provider;\n let results = null;\n \n if (provider === 'anthropic') results = await searchAnthropicNative(query, numResults, modelConfig);\n else if (provider === 'openai') results = await searchOpenAINative(query, numResults, modelConfig);\n else log('Unsupported provider:', provider);\n \n if (results && results.length > 0) return { results: results, source: 'native-' + provider };\n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'native-provider' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Standalone mode: mock non-LLM APIs\n if (process.env.STANDALONE_MODE === '1') {\n const pathname = url.pathname;\n const isCoreLLMApi = pathname.startsWith('/api/llm/a/') || pathname.startsWith('/api/llm/o/');\n\n if (!isCoreLLMApi) {\n if (pathname === '/api/sessions/create') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ id: 'local-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10) }));\n return;\n }\n if (pathname === '/api/cli/whoami') {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({}));\n return;\n }\n }\n\n // Simple proxy - no SSE transformation (handled by proxy plugin)\n log('Proxy:', req.method, url.pathname);\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('Native provider proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n","/**\n * WebSearch Patch Generator\n *\n * Two modes:\n * - --websearch: External providers (Smithery, Google PSE, Serper, Brave, SearXNG, DuckDuckGo)\n * - --websearch-proxy: Native provider via proxy plugin (reads ~/.factory/settings.json)\n */\n\nimport type { Patch } from \"./patcher.ts\";\nimport { writeFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nimport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\n// Re-export for backward compatibility\nexport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nexport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\n/**\n * Generate wrapper script for standalone proxy mode\n */\nfunction generateWrapperScript(droidPath: string, proxyScriptPath: string): string {\n return `#!/bin/bash\n# Droid with WebSearch Proxy\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPORT_FILE=\"/tmp/droid-search-proxy-$$.port\"\n\nstart_proxy() {\n SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" &\n PROXY_PID=$!\n for i in {1..20}; do\n if [ -f \"$PORT_FILE\" ]; then\n PORT=$(cat \"$PORT_FILE\")\n if curl -s \"http://127.0.0.1:$PORT/health\" > /dev/null 2>&1; then\n echo \"[websearch] Proxy started on port $PORT\"\n return 0\n fi\n fi\n sleep 0.2\n done\n echo \"[websearch] Failed to start proxy\"\n kill $PROXY_PID 2>/dev/null\n return 1\n}\n\ncleanup() {\n [ -n \"$PROXY_PID\" ] && kill $PROXY_PID 2>/dev/null\n [ -f \"$PORT_FILE\" ] && rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT\n\nif ! start_proxy; then exit 1; fi\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$PORT\"\nexec \"$DROID_BIN\" \"$@\"\n`;\n}\n\n/**\n * Generate WebSearch Patch (legacy binary patch approach)\n */\nexport function generateWebSearchPatch(): Patch | null {\n const originalUrl = \"https://api.factory.ai\";\n const localUrl = \"http://127.0.0.1:23119\";\n\n if (localUrl.length > originalUrl.length) {\n console.error(`[websearch] Local URL too long: ${localUrl.length} > ${originalUrl.length}`);\n return null;\n }\n\n const paddedUrl = localUrl.padEnd(originalUrl.length, \" \");\n return {\n name: \"webSearch\",\n description: `Replace API URL with local proxy (${localUrl})`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n };\n}\n\n/**\n * Create WebSearch proxy files (legacy)\n */\nexport async function createWebSearchProxyFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ proxyScript: string; wrapperScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-search-proxy.js`);\n const wrapperScriptPath = join(outputDir, `${aliasName}-with-search`);\n\n await writeFile(proxyScriptPath, generateSearchProxyServerCode());\n console.log(`[*] Created search proxy: ${proxyScriptPath}`);\n\n await writeFile(wrapperScriptPath, generateWrapperScript(droidPath, proxyScriptPath));\n await chmod(wrapperScriptPath, 0o755);\n console.log(`[*] Created wrapper script: ${wrapperScriptPath}`);\n\n return { proxyScript: proxyScriptPath, wrapperScript: wrapperScriptPath };\n}\n\n/**\n * Get proxy server code (for export)\n */\nexport function getSearchProxyCode(): string {\n return generateSearchProxyServerCode();\n}\n\n/**\n * Generate unified wrapper script\n */\nfunction generateUnifiedWrapper(\n droidPath: string,\n proxyScriptPath: string,\n standalone: boolean = false,\n): string {\n const standaloneEnv = standalone ? \"STANDALONE_MODE=1 \" : \"\";\n return `#!/bin/bash\n# Droid with WebSearch\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPROXY_PID=\"\"\nPORT_FILE=\"/tmp/droid-websearch-$$.port\"\nSTANDALONE=\"${standalone ? \"1\" : \"0\"}\"\n\nshould_passthrough() {\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then break; fi\n case \"$arg\" in --help|-h|--version|-V) return 0 ;; esac\n done\n local end_opts=0\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then end_opts=1; continue; fi\n if [ \"$end_opts\" -eq 0 ] && [[ \"$arg\" == -* ]]; then continue; fi\n case \"$arg\" in help|version|completion|completions|exec) return 0 ;; esac\n break\n done\n return 1\n}\n\nif should_passthrough \"$@\"; then exec \"$DROID_BIN\" \"$@\"; fi\n\ncleanup() {\n if [ -n \"$PROXY_PID\" ] && kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Stopping proxy (PID: $PROXY_PID)\" >&2\n kill \"$PROXY_PID\" 2>/dev/null\n wait \"$PROXY_PID\" 2>/dev/null\n fi\n rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT INT TERM\n\n[ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Starting proxy...\" >&2\n[ \"$STANDALONE\" = \"1\" ] && [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Standalone mode enabled\" >&2\n\nif [ -n \"$DROID_SEARCH_DEBUG\" ]; then\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" 2>&1 &\nelse\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" >/dev/null 2>&1 &\nfi\nPROXY_PID=$!\n\nfor i in {1..50}; do\n if ! kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy process died\" >&2\n break\n fi\n if [ -f \"$PORT_FILE\" ]; then\n ACTUAL_PORT=$(cat \"$PORT_FILE\" 2>/dev/null)\n if [ -n \"$ACTUAL_PORT\" ] && curl -s \"http://127.0.0.1:$ACTUAL_PORT/health\" > /dev/null 2>&1; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy ready on port $ACTUAL_PORT (PID: $PROXY_PID)\" >&2\n break\n fi\n fi\n sleep 0.1\ndone\n\nif [ ! -f \"$PORT_FILE\" ] || [ -z \"$(cat \"$PORT_FILE\" 2>/dev/null)\" ]; then\n echo \"[websearch] Failed to start proxy, running without websearch\" >&2\n cleanup\n exec \"$DROID_BIN\" \"$@\"\nfi\n\nACTUAL_PORT=$(cat \"$PORT_FILE\")\nrm -f \"$PORT_FILE\"\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$ACTUAL_PORT\"\n\"$DROID_BIN\" \"$@\"\nDROID_EXIT_CODE=$?\nexit $DROID_EXIT_CODE\n`;\n}\n\n/**\n * Create unified WebSearch files\n *\n * @param outputDir - Directory to write files to\n * @param droidPath - Path to droid binary\n * @param aliasName - Alias name for the wrapper\n * @param apiBase - Custom API base URL for proxy to forward requests to\n * @param standalone - Standalone mode: mock non-LLM Factory APIs\n * @param useNativeProvider - Use native provider websearch (--websearch-proxy mode)\n */\nexport async function createWebSearchUnifiedFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n apiBase?: string,\n standalone: boolean = false,\n useNativeProvider: boolean = false,\n): Promise<{ wrapperScript: string; preloadScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-proxy.js`);\n const wrapperScriptPath = join(outputDir, aliasName);\n\n const factoryApiUrl = apiBase || \"https://api.factory.ai\";\n const proxyCode = useNativeProvider\n ? generateNativeSearchProxyServer(factoryApiUrl)\n : generateExternalSearchProxyServer(factoryApiUrl);\n\n await writeFile(proxyScriptPath, proxyCode);\n console.log(`[*] Created proxy script: ${proxyScriptPath}`);\n console.log(\n `[*] Mode: ${useNativeProvider ? \"native provider (requires proxy plugin)\" : \"external providers\"}`,\n );\n\n await writeFile(\n wrapperScriptPath,\n generateUnifiedWrapper(droidPath, proxyScriptPath, standalone),\n );\n await chmod(wrapperScriptPath, 0o755);\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n if (standalone) {\n console.log(`[*] Standalone mode enabled`);\n }\n\n return {\n wrapperScript: wrapperScriptPath,\n preloadScript: proxyScriptPath,\n };\n}\n\n// === Legacy Preload Mode (kept for compatibility) ===\n\nfunction generatePreloadScript(): string {\n return `// Droid WebSearch Preload Script\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\n\nconst PORT = process.env.DROID_SEARCH_PORT || 23119;\nconst FACTORY_API = 'https://api.factory.ai';\n\nasync function searchGooglePSE(query, num) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = \\`https://www.googleapis.com/customsearch/v1?key=\\${apiKey}&cx=\\${cx}&q=\\${encodeURIComponent(query)}&num=\\${Math.min(num, 10)}\\`;\n const res = await fetch(url);\n const data = await res.json();\n if (data.error) return null;\n return (data.items || []).map(item => ({ title: item.title, url: item.link, content: item.snippet || '' }));\n } catch { return null; }\n}\n\nfunction searchDuckDuckGo(query, num) {\n try {\n const url = \\`https://api.duckduckgo.com/?q=\\${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1\\`;\n const output = execSync(\\`curl -s \"\\${url}\"\\`, { encoding: 'utf8', timeout: 10000 });\n const data = JSON.parse(output);\n const results = [];\n if (data.AbstractText && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.AbstractText });\n }\n for (const t of (data.RelatedTopics || [])) {\n if (results.length >= num) break;\n if (t.Text && t.FirstURL) results.push({ title: t.Text.split(' - ')[0], url: t.FirstURL, content: t.Text });\n if (t.Topics) {\n for (const sub of t.Topics) {\n if (results.length >= num) break;\n if (sub.Text && sub.FirstURL) results.push({ title: sub.Text.split(' - ')[0], url: sub.FirstURL, content: sub.Text });\n }\n }\n }\n return results;\n } catch { return []; }\n}\n\nasync function search(query, num) {\n const googleResults = await searchGooglePSE(query, num);\n if (googleResults?.length > 0) return googleResults;\n return searchDuckDuckGo(query, num);\n}\n\nfunction isPortInUse(port) {\n try { execSync(\\`curl -s http://127.0.0.1:\\${port}/health\\`, { timeout: 1000 }); return true; } catch { return false; }\n}\n\nif (!isPortInUse(PORT)) {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url, \\`http://\\${req.headers.host}\\`);\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', c => body += c);\n req.on('end', async () => {\n try {\n const { query, numResults } = JSON.parse(body);\n const results = await search(query, numResults || 10);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results }));\n } catch (e) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyReq = https.request(proxyUrl, { method: req.method, headers: { ...req.headers, host: proxyUrl.host } }, proxyRes => {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n proxyReq.on('error', () => { res.writeHead(502); res.end(); });\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n });\n server.listen(PORT, '127.0.0.1');\n}\n`;\n}\n\nfunction generateBunfigToml(preloadScriptPath: string): string {\n return `preload = [\"${preloadScriptPath}\"]`;\n}\n\nfunction generatePreloadWrapperScript(droidPath: string, bunfigDir: string): string {\n return `#!/bin/bash\ncd \"${bunfigDir}\"\nexec \"${droidPath}\" --cwd \"$(pwd)\" \"$@\"\n`;\n}\n\nexport async function createWebSearchPreloadFiles(\n droidDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ preloadScript: string; bunfigPath: string; wrapperScript: string }> {\n if (!existsSync(droidDir)) {\n await mkdir(droidDir, { recursive: true });\n }\n\n const preloadScriptPath = join(droidDir, `${aliasName}-search-preload.js`);\n const bunfigPath = join(droidDir, \"bunfig.toml\");\n const wrapperScriptPath = join(droidDir, aliasName);\n\n await writeFile(preloadScriptPath, generatePreloadScript());\n await writeFile(bunfigPath, generateBunfigToml(preloadScriptPath));\n await writeFile(wrapperScriptPath, generatePreloadWrapperScript(droidPath, droidDir));\n await chmod(wrapperScriptPath, 0o755);\n\n console.log(`[*] Created preload: ${preloadScriptPath}`);\n console.log(`[*] Created bunfig: ${bunfigPath}`);\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n return { preloadScript: preloadScriptPath, bunfigPath, wrapperScript: wrapperScriptPath };\n}\n\nexport function getPreloadScriptCode(): string {\n return generatePreloadScript();\n}\n","import bin from \"tiny-bin\";\nimport { styleText } from \"node:util\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\nimport { patchDroid, type Patch } from \"./patcher.ts\";\nimport {\n createAlias,\n removeAlias,\n listAliases,\n createAliasForWrapper,\n clearAllAliases,\n removeAliasesByFilter,\n type FilterFlag,\n} from \"./alias.ts\";\nimport { createWebSearchUnifiedFiles } from \"./websearch-patch.ts\";\nimport {\n saveAliasMetadata,\n createMetadata,\n loadAliasMetadata,\n listAllMetadata,\n formatPatches,\n} from \"./metadata.ts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst version = getVersion();\n\nfunction getDroidVersion(droidPath: string): string | undefined {\n try {\n const result = execSync(`\"${droidPath}\" --version`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 5000,\n }).trim();\n // Parse version from output like \"droid 1.2.3\" or just \"1.2.3\"\n const match = result.match(/(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : result || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction findDefaultDroidPath(): string {\n const home = homedir();\n\n // Try `which droid` first to find droid in PATH\n try {\n const result = execSync(\"which droid\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n if (result && existsSync(result)) {\n return result;\n }\n } catch {\n // which command failed, continue with fallback paths\n }\n\n // Common installation paths\n const paths = [\n // Default sh install location\n join(home, \".droid\", \"bin\", \"droid\"),\n // Homebrew on Apple Silicon\n \"/opt/homebrew/bin/droid\",\n // Homebrew on Intel Mac / Linux\n \"/usr/local/bin/droid\",\n // Linux system-wide\n \"/usr/bin/droid\",\n // Current directory\n \"./droid\",\n ];\n\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n\n // Return default path even if not found (will error later with helpful message)\n return join(home, \".droid\", \"bin\", \"droid\");\n}\n\nbin(\"droid-patch\", \"CLI tool to patch droid binary with various modifications\")\n .package(\"droid-patch\", version)\n .option(\n \"--is-custom\",\n \"Patch isCustom:!0 to isCustom:!1 (enable context compression for custom models)\",\n )\n .option(\n \"--skip-login\",\n \"Inject a fake FACTORY_API_KEY to bypass login requirement (no real key needed)\",\n )\n .option(\n \"--api-base <url>\",\n \"Replace API URL (standalone: binary patch, max 22 chars; with --websearch: proxy forward target, no limit)\",\n )\n .option(\n \"--websearch\",\n \"Enable local WebSearch proxy with external providers (Smithery, Google PSE, etc.)\",\n )\n .option(\n \"--websearch-proxy\",\n \"Enable native provider websearch (requires proxy plugin, reads ~/.factory/settings.json)\",\n )\n .option(\"--standalone\", \"Standalone mode: mock non-LLM Factory APIs (use with --websearch)\")\n .option(\n \"--reasoning-effort\",\n \"Enable reasoning effort for custom models (set to high, enable UI selector)\",\n )\n .option(\n \"--disable-telemetry\",\n \"Disable telemetry and Sentry error reporting (block data uploads)\",\n )\n .option(\n \"--auto-high\",\n \"Set default autonomy mode to auto-high (bypass settings.json race condition)\",\n )\n .option(\"--dry-run\", \"Verify patches without actually modifying the binary\")\n .option(\"-p, --path <path>\", \"Path to the droid binary\")\n .option(\"-o, --output <dir>\", \"Output directory for patched binary\")\n .option(\"--no-backup\", \"Do not create backup of original binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .argument(\"[alias]\", \"Alias name for the patched binary\")\n .action(async (options, args) => {\n const alias = args?.[0] as string | undefined;\n const isCustom = options[\"is-custom\"] as boolean;\n const skipLogin = options[\"skip-login\"] as boolean;\n const apiBase = options[\"api-base\"] as string | undefined;\n const websearch = options[\"websearch\"] as boolean;\n const websearchProxy = options[\"websearch-proxy\"] as boolean;\n const standalone = options[\"standalone\"] as boolean;\n // When --websearch is used with --api-base, forward to custom URL\n // Otherwise forward to official Factory API\n const websearchTarget = websearch ? apiBase || \"https://api.factory.ai\" : undefined;\n // --websearch-proxy uses native provider websearch (requires proxy plugin)\n const websearchProxyEnabled = websearchProxy;\n const reasoningEffort = options[\"reasoning-effort\"] as boolean;\n const noTelemetry = options[\"disable-telemetry\"] as boolean;\n const autoHigh = options[\"auto-high\"] as boolean;\n const dryRun = options[\"dry-run\"] as boolean;\n const path = (options.path as string) || findDefaultDroidPath();\n const outputDir = options.output as string | undefined;\n const backup = options.backup !== false;\n const verbose = options.verbose as boolean;\n\n // If -o is specified with alias, output to that directory with alias name\n const outputPath = outputDir && alias ? join(outputDir, alias) : undefined;\n\n const needsBinaryPatch =\n !!isCustom ||\n !!skipLogin ||\n !!reasoningEffort ||\n !!noTelemetry ||\n !!autoHigh ||\n (!!apiBase && !websearch && !websearchProxy);\n\n // Check for conflicting flags\n if (websearch && websearchProxy) {\n console.log(styleText(\"red\", \"Error: Cannot use --websearch and --websearch-proxy together\"));\n console.log(styleText(\"gray\", \"Choose one:\"));\n console.log(\n styleText(\"gray\", \" --websearch External providers (Smithery, Google PSE, etc.)\"),\n );\n console.log(\n styleText(\"gray\", \" --websearch-proxy Native provider (requires proxy plugin)\"),\n );\n process.exit(1);\n }\n\n // Wrapper-only mode (no binary patching needed):\n // - --websearch or --websearch-proxy (optional --standalone)\n const isWebsearchMode = websearch || websearchProxy;\n if (!needsBinaryPatch && isWebsearchMode) {\n if (!alias) {\n const flag = websearchProxy ? \"--websearch-proxy\" : \"--websearch\";\n console.log(styleText(\"red\", `Error: Alias name required for ${flag}`));\n console.log(styleText(\"gray\", `Usage: npx droid-patch ${flag} <alias>`));\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Wrapper Setup\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"white\", `WebSearch: native provider mode`));\n console.log(styleText(\"gray\", ` Requires proxy plugin (anthropic4droid)`));\n console.log(styleText(\"gray\", ` Reads model config from ~/.factory/settings.json`));\n } else if (websearch) {\n console.log(styleText(\"white\", `WebSearch: external providers mode`));\n console.log(styleText(\"white\", `Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", `Standalone mode: enabled`));\n }\n console.log();\n\n let execTargetPath = path;\n // Create websearch proxy files (proxy script + wrapper)\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n // For --websearch-proxy, apiBase comes from settings.json at runtime\n // For --websearch, use apiBase or default Factory API\n const forwardTarget = websearchProxy ? undefined : websearchTarget;\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n forwardTarget,\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n // Create alias pointing to outer wrapper\n const aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: false,\n skipLogin: false,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: false,\n noTelemetry: false,\n standalone: standalone,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" Wrapper Ready!\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(\"Run directly:\");\n console.log(styleText(\"yellow\", ` ${alias}`));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"Native Provider WebSearch (--websearch-proxy):\"));\n console.log(styleText(\"gray\", \" Uses model's built-in websearch via proxy plugin\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n console.log();\n console.log(styleText(\"yellow\", \"IMPORTANT: Requires proxy plugin (anthropic4droid)\"));\n console.log();\n console.log(\"Supported providers:\");\n console.log(styleText(\"yellow\", \" - anthropic: Claude web_search_20250305 server tool\"));\n console.log(styleText(\"yellow\", \" - openai: OpenAI web_search tool\"));\n console.log(styleText(\"gray\", \" - generic-chat-completion-api: Not supported\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1 # Basic logs\"));\n console.log(styleText(\"gray\", \" export DROID_SEARCH_VERBOSE=1 # Full request/response\"));\n } else if (websearch) {\n console.log(styleText(\"cyan\", \"External Providers WebSearch (--websearch):\"));\n console.log(\n styleText(\"gray\", \" Uses external search providers (Smithery, Google PSE, etc.)\"),\n );\n console.log();\n console.log(\"Search providers (in priority order):\");\n console.log(styleText(\"yellow\", \" 1. Smithery Exa (best quality):\"));\n console.log(styleText(\"gray\", \" export SMITHERY_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export SMITHERY_PROFILE=your_profile\"));\n console.log(styleText(\"gray\", \" 2. Google PSE:\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_CX=your_search_engine_id\"));\n console.log(styleText(\"gray\", \" 3-6. Serper, Brave, SearXNG, DuckDuckGo (fallbacks)\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1\"));\n }\n return;\n }\n\n if (\n !isCustom &&\n !skipLogin &&\n !apiBase &&\n !websearch &&\n !reasoningEffort &&\n !noTelemetry &&\n !autoHigh\n ) {\n console.log(styleText(\"yellow\", \"No patch flags specified. Available patches:\"));\n console.log(styleText(\"gray\", \" --is-custom Patch isCustom for custom models\"));\n console.log(\n styleText(\"gray\", \" --skip-login Bypass login by injecting a fake API key\"),\n );\n console.log(\n styleText(\n \"gray\",\n \" --api-base Replace API URL (standalone: max 22 chars; with --websearch: no limit)\",\n ),\n );\n console.log(styleText(\"gray\", \" --websearch Enable local WebSearch proxy\"));\n console.log(\n styleText(\"gray\", \" --reasoning-effort Set reasoning effort level for custom models\"),\n );\n console.log(\n styleText(\"gray\", \" --disable-telemetry Disable telemetry and Sentry error reporting\"),\n );\n console.log(\n styleText(\"gray\", \" --auto-high Set default autonomy mode to auto-high\"),\n );\n console.log(\n styleText(\"gray\", \" --standalone Standalone mode: mock non-LLM Factory APIs\"),\n );\n console.log();\n console.log(\"Usage examples:\");\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom droid-custom\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --skip-login droid-nologin\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom --skip-login droid-patched\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch droid-search\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch --standalone droid-local\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --disable-telemetry droid-private\"));\n console.log(\n styleText(\n \"cyan\",\n \" npx droid-patch --websearch --api-base=http://127.0.0.1:20002 my-droid\",\n ),\n );\n process.exit(1);\n }\n\n if (!alias && !dryRun) {\n console.log(styleText(\"red\", \"Error: alias name is required\"));\n console.log(\n styleText(\n \"gray\",\n \"Usage: droid-patch [--is-custom] [--skip-login] [-o <dir>] <alias-name>\",\n ),\n );\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Binary Patcher\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n const patches: Patch[] = [];\n if (isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n // Add skip-login patch: replace process.env.FACTORY_API_KEY with a fixed fake key\n // \"process.env.FACTORY_API_KEY\" is 27 chars, we replace with \"fk-droid-patch-skip-00000\" (25 chars + quotes = 27)\n if (skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: 'Replace process.env.FACTORY_API_KEY with \"fk-droid-patch-skip-00000\"',\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Add api-base patch: replace the Factory API base URL\n // Original: \"https://api.factory.ai\" (22 chars)\n // We need to pad the replacement URL to be exactly 22 chars\n // Note: When --websearch is used, --api-base sets the forward target instead of binary patching\n if (apiBase && !websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const originalLength = originalUrl.length; // 22 chars\n\n // Validate and normalize the URL\n let normalizedUrl = apiBase.replace(/\\/+$/, \"\"); // Remove trailing slashes\n\n if (normalizedUrl.length > originalLength) {\n console.log(\n styleText(\"red\", `Error: API base URL must be ${originalLength} characters or less`),\n );\n console.log(\n styleText(\"gray\", ` Your URL: \"${normalizedUrl}\" (${normalizedUrl.length} chars)`),\n );\n console.log(styleText(\"gray\", ` Maximum: ${originalLength} characters`));\n console.log();\n console.log(styleText(\"yellow\", \"Tip: Use a shorter URL or set up a local redirect.\"));\n console.log(styleText(\"gray\", \" Examples:\"));\n console.log(styleText(\"gray\", \" http://127.0.0.1:3000 (19 chars)\"));\n console.log(styleText(\"gray\", \" http://localhost:80 (19 chars)\"));\n process.exit(1);\n }\n\n // Pad the URL with spaces at the end to match original length\n // Note: trailing spaces in URL are generally ignored\n const paddedUrl = normalizedUrl.padEnd(originalLength, \" \");\n\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${normalizedUrl}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n // Add reasoning-effort patch: set custom models to use \"high\" reasoning\n // Also modify UI conditions to show reasoning selector for custom models\n if (reasoningEffort) {\n // [\"none\"] is 8 chars, [\"high\"] is 8 chars - perfect match!\n patches.push({\n name: \"reasoningEffortSupported\",\n description: 'Change supportedReasoningEfforts:[\"none\"] to [\"high\"]',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n\n // \"none\" is 4 chars, \"high\" is 4 chars - perfect match!\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n\n // Change UI condition from length>1 to length>0\n // This allows custom models with single reasoning option to show the selector\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n\n // Change UI condition from length<=1 to length<=0\n // This enables the reasoning setting in /settings menu for custom models\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n\n // Bypass reasoning effort validation to allow settings.json override\n // This allows \"xhigh\" in settings.json to work even though default is \"high\"\n // v0.39.0+: T!==\"none\"&&T!==\"off\"&&!W.supportedReasoningEfforts.includes(T)\n // Changed: T!=\"none\"&&T!=\"off\"&&0&&W... - use != (2 chars less) + 0&& (2 chars more) = same length\n // Logic: && 0 && makes entire condition always false, bypassing validation\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from('T!==\"none\"&&T!==\"off\"&&!W.supportedReasoningEfforts.includes(T)'),\n replacement: Buffer.from('T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)'),\n });\n }\n\n // Add no-telemetry patches: disable telemetry uploads and Sentry error reporting\n // Strategy:\n // 1. Break environment variable names so Sentry is never initialized (Q1() returns false)\n // 2. Invert flushToWeb condition so it returns early without making any fetch request\n if (noTelemetry) {\n // Patch 1: Break Sentry environment variable checks\n // Q1() function checks: VITE_VERCEL_ENV, ENABLE_SENTRY, NEXT_PUBLIC_ENABLE_SENTRY, FACTORY_ENABLE_SENTRY\n // By changing first letter to X, the env vars will never match, so Q1() returns false\n // and Sentry is never initialized\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n\n // Patch 2: Make flushToWeb always return early to prevent ANY fetch request\n // Original: if(this.webEvents.length===0)return; // returns only when empty\n // Changed: if(!0||this.webEvents.length)return; // !0=true, ALWAYS returns\n // Result: Function always exits immediately, no telemetry is ever sent\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n // Add auto-high autonomy patch: hardcode getCurrentAutonomyMode to return \"auto-high\"\n // This bypasses the race condition where AutonomyManager.initialize() runs before\n // SettingsService has loaded settings.json, causing the default \"normal\" to be used.\n // Pattern (50 chars): getCurrentAutonomyMode(){return this.autonomyMode}\n // Replace (50 chars): getCurrentAutonomyMode(){return\"auto-high\" }\n if (autoHigh) {\n patches.push({\n name: \"autoHighAutonomy\",\n description: 'Hardcode getCurrentAutonomyMode() to return \"auto-high\"',\n pattern: Buffer.from(\"getCurrentAutonomyMode(){return this.autonomyMode}\"),\n replacement: Buffer.from('getCurrentAutonomyMode(){return\"auto-high\" }'),\n });\n }\n\n try {\n const result = await patchDroid({\n inputPath: path,\n outputPath: outputPath,\n patches,\n dryRun,\n backup,\n verbose,\n });\n\n if (dryRun) {\n console.log();\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"gray\", \"To apply the patches, run without --dry-run:\"));\n console.log(styleText(\"cyan\", ` npx droid-patch --is-custom ${alias || \"<alias-name>\"}`));\n process.exit(0);\n }\n\n // If -o is specified, just output the file without creating alias\n if (outputDir && result.success && result.outputPath) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", `Patched binary saved to: ${result.outputPath}`));\n process.exit(0);\n }\n\n if (result.success && result.outputPath && alias) {\n console.log();\n\n let execTargetPath = result.outputPath;\n\n if (websearch || websearchProxy) {\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n websearchProxy ? undefined : websearchTarget, // websearchProxy reads from settings.json at runtime\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"WebSearch enabled (native provider mode)\"));\n console.log(styleText(\"gray\", \" Requires proxy plugin (anthropic4droid)\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n } else {\n console.log(styleText(\"cyan\", \"WebSearch enabled (external providers mode)\"));\n console.log(styleText(\"white\", ` Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", ` Standalone mode: enabled`));\n }\n }\n\n let aliasResult;\n if (websearch || websearchProxy) {\n aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n } else {\n aliasResult = await createAlias(result.outputPath, alias, verbose);\n }\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: !!isCustom,\n skipLogin: !!skipLogin,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: !!reasoningEffort,\n noTelemetry: !!noTelemetry,\n standalone: !!standalone,\n autoHigh: !!autoHigh,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n }\n\n if (result.success) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n }\n\n process.exit(result.success ? 0 : 1);\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n if (verbose) console.error((error as Error).stack);\n process.exit(1);\n }\n })\n .command(\"list\", \"List all droid-patch aliases\")\n .action(async () => {\n await listAliases();\n })\n .command(\"remove\", \"Remove alias(es) by name or filter\")\n .argument(\"[alias-or-path]\", \"Alias name or file path to remove\")\n .option(\"--patch-version <version>\", \"Remove aliases created by this droid-patch version\")\n .option(\"--droid-version <version>\", \"Remove aliases for this droid version\")\n .option(\n \"--flag <flag>\",\n \"Remove aliases with this flag (is-custom, skip-login, websearch, api-base, reasoning-effort, disable-telemetry, standalone)\",\n )\n .action(async (options, args) => {\n const target = args?.[0] as string | undefined;\n const patchVersion = options[\"patch-version\"] as string | undefined;\n const droidVersion = options[\"droid-version\"] as string | undefined;\n const flagRaw = options.flag as string | undefined;\n let flag: FilterFlag | undefined;\n if (flagRaw) {\n const allowedFlags: FilterFlag[] = [\n \"is-custom\",\n \"skip-login\",\n \"websearch\",\n \"api-base\",\n \"reasoning-effort\",\n \"disable-telemetry\",\n \"standalone\",\n ];\n if (!allowedFlags.includes(flagRaw as FilterFlag)) {\n console.error(styleText(\"red\", `Error: Invalid --flag value: ${flagRaw}`));\n console.error(styleText(\"gray\", `Allowed: ${allowedFlags.join(\", \")}`));\n process.exit(1);\n }\n flag = flagRaw as FilterFlag;\n }\n\n // If filter options are provided, use filter mode\n if (patchVersion || droidVersion || flag) {\n await removeAliasesByFilter({\n patchVersion,\n droidVersion,\n flags: flag ? [flag] : undefined,\n });\n return;\n }\n\n // If no target and no filter, show error\n if (!target) {\n console.error(\n styleText(\n \"red\",\n \"Error: Provide an alias name or use filter options (--patch-version, --droid-version, --flag)\",\n ),\n );\n process.exit(1);\n }\n\n // Check if it's a file path (contains / or .)\n if (target.includes(\"/\") || existsSync(target)) {\n // It's a file path, delete directly\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(target);\n console.log(styleText(\"green\", `[*] Removed: ${target}`));\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n process.exit(1);\n }\n } else {\n // It's an alias name\n await removeAlias(target);\n }\n })\n .command(\"version\", \"Print droid-patch version\")\n .action(() => {\n console.log(`droid-patch v${version}`);\n })\n .command(\"clear\", \"Remove all droid-patch aliases and related files\")\n .action(async () => {\n await clearAllAliases();\n })\n .command(\"update\", \"Update aliases with latest droid binary\")\n .argument(\"[alias]\", \"Specific alias to update (optional, updates all if not specified)\")\n .option(\"--dry-run\", \"Preview without making changes\")\n .option(\"-p, --path <path>\", \"Path to new droid binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .action(async (options, args) => {\n const aliasName = args?.[0] as string | undefined;\n const dryRun = options[\"dry-run\"] as boolean;\n const newBinaryPath = (options.path as string) || findDefaultDroidPath();\n const verbose = options.verbose as boolean;\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid-Patch Update\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Verify the new binary exists\n if (!existsSync(newBinaryPath)) {\n console.log(styleText(\"red\", `Error: Droid binary not found at ${newBinaryPath}`));\n console.log(styleText(\"gray\", \"Use -p to specify a different path\"));\n process.exit(1);\n }\n\n // Get aliases to update\n let metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[];\n if (aliasName) {\n const meta = await loadAliasMetadata(aliasName);\n if (!meta) {\n console.log(styleText(\"red\", `Error: No metadata found for alias \"${aliasName}\"`));\n console.log(\n styleText(\"gray\", \"This alias may have been created before update tracking was added.\"),\n );\n console.log(styleText(\"gray\", \"Remove and recreate the alias to enable update support.\"));\n process.exit(1);\n }\n metaList = [meta];\n } else {\n metaList = await listAllMetadata();\n if (metaList.length === 0) {\n console.log(styleText(\"yellow\", \"No aliases with metadata found.\"));\n console.log(styleText(\"gray\", \"Create aliases with droid-patch to enable update support.\"));\n process.exit(0);\n }\n }\n\n console.log(styleText(\"white\", `Using droid binary: ${newBinaryPath}`));\n console.log(styleText(\"white\", `Found ${metaList.length} alias(es) to update`));\n if (dryRun) {\n console.log(styleText(\"blue\", \"(DRY RUN - no changes will be made)\"));\n }\n console.log();\n\n let successCount = 0;\n let failCount = 0;\n\n for (const meta of metaList) {\n if (!meta) continue;\n\n console.log(styleText(\"cyan\", `─`.repeat(40)));\n console.log(styleText(\"white\", `Updating: ${styleText([\"cyan\", \"bold\"], meta.name)}`));\n console.log(styleText(\"gray\", ` Patches: ${formatPatches(meta.patches)}`));\n\n if (dryRun) {\n console.log(styleText(\"blue\", ` [DRY RUN] Would re-apply patches`));\n successCount++;\n continue;\n }\n\n try {\n // Build patch list based on metadata\n const patches: Patch[] = [];\n\n if (meta.patches.isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n if (meta.patches.skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: \"Replace process.env.FACTORY_API_KEY with fake key\",\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Only apply apiBase binary patch when NOT using websearch\n // When websearch is enabled, apiBase is used as forward target, not binary patch\n if (meta.patches.apiBase && !meta.patches.websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const paddedUrl = meta.patches.apiBase.padEnd(originalUrl.length, \" \");\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${meta.patches.apiBase}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n if (meta.patches.reasoningEffort) {\n patches.push({\n name: \"reasoningEffortSupported\",\n description: 'Change supportedReasoningEfforts:[\"none\"] to [\"high\"]',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from('T!==\"none\"&&T!==\"off\"&&!W.supportedReasoningEfforts.includes(T)'),\n replacement: Buffer.from(\n 'T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)',\n ),\n });\n }\n\n if (meta.patches.noTelemetry) {\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n if (meta.patches.autoHigh) {\n patches.push({\n name: \"autoHighAutonomy\",\n description: 'Hardcode getCurrentAutonomyMode() to return \"auto-high\"',\n pattern: Buffer.from(\"getCurrentAutonomyMode(){return this.autonomyMode}\"),\n replacement: Buffer.from('getCurrentAutonomyMode(){return\"auto-high\" }'),\n });\n }\n\n // Determine output path based on whether this is a websearch alias\n const binsDir = join(homedir(), \".droid-patch\", \"bins\");\n const outputPath = join(binsDir, `${meta.name}-patched`);\n\n // Apply patches (only if there are binary patches to apply)\n if (patches.length > 0) {\n const result = await patchDroid({\n inputPath: newBinaryPath,\n outputPath,\n patches,\n dryRun: false,\n backup: false,\n verbose,\n });\n\n if (!result.success) {\n console.log(styleText(\"red\", ` ✗ Failed to apply patches`));\n failCount++;\n continue;\n }\n\n // Re-sign on macOS\n if (process.platform === \"darwin\") {\n try {\n const { execSync } = await import(\"node:child_process\");\n execSync(`codesign --force --deep --sign - \"${outputPath}\"`, {\n stdio: \"pipe\",\n });\n if (verbose) {\n console.log(styleText(\"gray\", ` Re-signed binary`));\n }\n } catch {\n console.log(styleText(\"yellow\", ` [!] Could not re-sign binary`));\n }\n }\n }\n\n let execTargetPath = patches.length > 0 ? outputPath : newBinaryPath;\n\n // If websearch is enabled, regenerate wrapper files\n // Support both new 'websearch' field and old 'proxy' field for backward compatibility\n const hasWebsearch = meta.patches.websearch || !!meta.patches.proxy;\n if (hasWebsearch) {\n // Determine forward target: apiBase > proxy (legacy) > default\n const forwardTarget =\n meta.patches.apiBase || meta.patches.proxy || \"https://api.factory.ai\";\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n meta.name,\n forwardTarget,\n meta.patches.standalone || false,\n );\n execTargetPath = wrapperScript;\n if (verbose) {\n console.log(styleText(\"gray\", ` Regenerated websearch wrapper`));\n if (meta.patches.standalone) {\n console.log(styleText(\"gray\", ` Standalone mode: enabled`));\n }\n }\n // Migrate old proxy field to new websearch field\n if (meta.patches.proxy && !meta.patches.websearch) {\n meta.patches.websearch = true;\n meta.patches.apiBase = meta.patches.proxy;\n delete meta.patches.proxy;\n }\n }\n\n // If this alias previously used removed features (statusline/sessions), drop legacy flags\n // so the updated alias points directly to the new target wrapper/binary.\n delete (meta.patches as Record<string, unknown>).statusline;\n delete (meta.patches as Record<string, unknown>).sessions;\n\n // Update symlink - find existing or use stored aliasPath\n const { symlink, unlink, readlink, lstat } = await import(\"node:fs/promises\");\n let aliasPath = meta.aliasPath;\n\n // If aliasPath not stored (old version), try to find existing symlink\n if (!aliasPath) {\n const commonPathDirs = [\n join(homedir(), \".local/bin\"),\n join(homedir(), \"bin\"),\n join(homedir(), \".bin\"),\n \"/opt/homebrew/bin\",\n \"/usr/local/bin\",\n join(homedir(), \".droid-patch\", \"aliases\"),\n ];\n\n for (const dir of commonPathDirs) {\n const possiblePath = join(dir, meta.name);\n if (existsSync(possiblePath)) {\n try {\n const stats = await lstat(possiblePath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(possiblePath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasPath = possiblePath;\n if (verbose) {\n console.log(styleText(\"gray\", ` Found existing symlink: ${aliasPath}`));\n }\n break;\n }\n }\n } catch {\n // Ignore errors, continue searching\n }\n }\n }\n }\n\n // Update symlink if we have a path\n if (aliasPath) {\n try {\n if (existsSync(aliasPath)) {\n const currentTarget = await readlink(aliasPath);\n if (currentTarget !== execTargetPath) {\n await unlink(aliasPath);\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Updated symlink: ${aliasPath}`));\n }\n }\n } else {\n // Symlink doesn't exist, recreate it\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Recreated symlink: ${aliasPath}`));\n }\n }\n // Store aliasPath in metadata for future updates\n meta.aliasPath = aliasPath;\n } catch (symlinkError) {\n console.log(\n styleText(\n \"yellow\",\n ` [!] Could not update symlink: ${(symlinkError as Error).message}`,\n ),\n );\n }\n }\n\n // Update metadata\n meta.updatedAt = new Date().toISOString();\n meta.originalBinaryPath = newBinaryPath;\n meta.droidVersion = getDroidVersion(newBinaryPath);\n meta.droidPatchVersion = version;\n await saveAliasMetadata(meta);\n\n console.log(styleText(\"green\", ` ✓ Updated successfully`));\n successCount++;\n } catch (error) {\n console.log(styleText(\"red\", ` ✗ Error: ${(error as Error).message}`));\n if (verbose) {\n console.error((error as Error).stack);\n }\n failCount++;\n }\n }\n\n console.log();\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n if (dryRun) {\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"gray\", ` Would update ${successCount} alias(es)`));\n } else if (failCount === 0) {\n console.log(styleText([\"green\", \"bold\"], \" UPDATE COMPLETE\"));\n console.log(styleText(\"gray\", ` Updated ${successCount} alias(es)`));\n } else {\n console.log(styleText([\"yellow\", \"bold\"], \" UPDATE FINISHED WITH ERRORS\"));\n console.log(styleText(\"gray\", ` Success: ${successCount}, Failed: ${failCount}`));\n }\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n })\n .run()\n .catch((err: Error) => {\n console.error(err);\n process.exit(1);\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["patches: Patch[]","flag: FilterFlag | undefined","allowedFlags: FilterFlag[]","unlink","metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[]","readlink","symlink"],"sources":["../src/websearch-external.ts","../src/websearch-native.ts","../src/websearch-patch.ts","../src/cli.ts"],"sourcesContent":["/**\n * WebSearch External Providers Mode (--websearch)\n *\n * Priority: Smithery Exa > Google PSE > Serper > Brave > SearXNG > DuckDuckGo\n */\n\nexport function generateSearchProxyServerCode(): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (External Providers Mode)\n// Priority: Smithery Exa > Google PSE > Serper > Brave > SearXNG > DuckDuckGo\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = 'https://api.factory.ai';\n\nfunction log() { if (DEBUG) console.error.apply(console, ['[websearch]'].concat(Array.from(arguments))); }\n\n// === External Search Providers ===\n\nasync function searchSmitheryExa(query, numResults) {\n const apiKey = process.env.SMITHERY_API_KEY;\n const profile = process.env.SMITHERY_PROFILE;\n if (!apiKey || !profile) return null;\n try {\n const serverUrl = 'https://server.smithery.ai/exa/mcp?api_key=' + encodeURIComponent(apiKey) + '&profile=' + encodeURIComponent(profile);\n const requestBody = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'web_search_exa', arguments: { query: query, numResults: numResults } } });\n const bodyStr = requestBody.replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + serverUrl + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const response = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 30000 }));\n if (response.result && response.result.content) {\n const textContent = response.result.content.find(function(c) { return c.type === 'text'; });\n if (textContent && textContent.text) {\n const results = JSON.parse(textContent.text);\n if (Array.isArray(results) && results.length > 0) {\n return results.slice(0, numResults).map(function(item) {\n return {\n title: item.title || '', url: item.url || '',\n content: item.text || item.snippet || (item.highlights ? item.highlights.join(' ') : '') || ''\n };\n });\n }\n }\n }\n } catch (e) { log('Smithery failed:', e.message); }\n return null;\n}\n\nasync function searchGooglePSE(query, numResults) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = 'https://www.googleapis.com/customsearch/v1?key=' + apiKey + '&cx=' + cx + '&q=' + encodeURIComponent(query) + '&num=' + Math.min(numResults, 10);\n const data = JSON.parse(execSync('curl -s \"' + url + '\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.error) return null;\n return (data.items || []).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n } catch (e) { log('Google PSE failed:', e.message); }\n return null;\n}\n\nasync function searchSerper(query, numResults) {\n const apiKey = process.env.SERPER_API_KEY;\n if (!apiKey) return null;\n try {\n const bodyStr = JSON.stringify({ q: query, num: numResults }).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s \"https://google.serper.dev/search\" -H \"X-API-KEY: ' + apiKey + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.organic && data.organic.length > 0) {\n return data.organic.slice(0, numResults).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n }\n } catch (e) { log('Serper failed:', e.message); }\n return null;\n}\n\nasync function searchBrave(query, numResults) {\n const apiKey = process.env.BRAVE_API_KEY;\n if (!apiKey) return null;\n try {\n const url = 'https://api.search.brave.com/res/v1/web/search?q=' + encodeURIComponent(query) + '&count=' + numResults;\n const curlCmd = 'curl -s \"' + url + '\" -H \"Accept: application/json\" -H \"X-Subscription-Token: ' + apiKey + '\"';\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.web && data.web.results && data.web.results.length > 0) {\n return data.web.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.description || '' }; });\n }\n } catch (e) { log('Brave failed:', e.message); }\n return null;\n}\n\nasync function searchSearXNG(query, numResults) {\n const searxngUrl = process.env.SEARXNG_URL;\n if (!searxngUrl) return null;\n try {\n const url = searxngUrl + '/search?q=' + encodeURIComponent(query) + '&format=json&engines=google,bing,duckduckgo';\n const data = JSON.parse(execSync('curl -s \"' + url + '\" -H \"Accept: application/json\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.results && data.results.length > 0) {\n return data.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.content || '' }; });\n }\n } catch (e) { log('SearXNG failed:', e.message); }\n return null;\n}\n\nasync function searchDuckDuckGo(query, numResults) {\n try {\n const apiUrl = 'https://api.duckduckgo.com/?q=' + encodeURIComponent(query) + '&format=json&no_html=1&skip_disambig=1';\n const data = JSON.parse(execSync('curl -s \"' + apiUrl + '\" -H \"User-Agent: Mozilla/5.0\"', { encoding: 'utf-8', timeout: 15000 }));\n const results = [];\n if (data.Abstract && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.Abstract });\n }\n var topics = data.RelatedTopics || [];\n for (var i = 0; i < topics.length && results.length < numResults; i++) {\n var topic = topics[i];\n if (topic.Text && topic.FirstURL) results.push({ title: topic.Text.substring(0, 100), url: topic.FirstURL, content: topic.Text });\n if (topic.Topics) {\n for (var j = 0; j < topic.Topics.length && results.length < numResults; j++) {\n var st = topic.Topics[j];\n if (st.Text && st.FirstURL) results.push({ title: st.Text.substring(0, 100), url: st.FirstURL, content: st.Text });\n }\n }\n }\n return results.length > 0 ? results : null;\n } catch (e) { log('DuckDuckGo failed:', e.message); }\n return null;\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n // Priority: Smithery > Google PSE > Serper > Brave > SearXNG > DuckDuckGo\n var results = await searchSmitheryExa(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'smithery-exa' };\n \n results = await searchGooglePSE(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'google-pse' };\n \n results = await searchSerper(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'serper' };\n \n results = await searchBrave(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'brave' };\n \n results = await searchSearXNG(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'searxng' };\n \n results = await searchDuckDuckGo(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'duckduckgo' };\n \n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'external-providers' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Proxy other requests\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('External providers proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n\nexport function generateExternalSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n const code = generateSearchProxyServerCode();\n return code.replace(\n \"const FACTORY_API = 'https://api.factory.ai';\",\n `const FACTORY_API = '${factoryApiUrl}';`,\n );\n}\n","/**\n * WebSearch Native Provider Mode (--websearch-proxy)\n *\n * Uses model's native websearch based on ~/.factory/settings.json configuration\n * Requires proxy plugin to handle format conversion:\n * - Anthropic provider: anthropic4droid plugin\n * - OpenAI provider: openai4droid plugin (adds CODEX_INSTRUCTIONS)\n *\n * Supported providers:\n * - Anthropic: web_search_20250305 server tool, results in web_search_tool_result\n * - OpenAI: web_search tool, results in message.content[].annotations[] as url_citation\n */\n\nexport function generateNativeSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (Native Provider Mode)\n// Reads ~/.factory/settings.json for model configuration\n// Requires proxy plugin (anthropic4droid) to handle format conversion\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\nconst os = require('os');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = '${factoryApiUrl}';\n\nfunction log(...args) { if (DEBUG) console.error('[websearch]', ...args); }\n\n// === Settings Configuration ===\n\nlet cachedSettings = null;\nlet settingsLastModified = 0;\n\nfunction getFactorySettings() {\n const settingsPath = path.join(os.homedir(), '.factory', 'settings.json');\n try {\n const stats = fs.statSync(settingsPath);\n if (cachedSettings && stats.mtimeMs === settingsLastModified) return cachedSettings;\n cachedSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n settingsLastModified = stats.mtimeMs;\n return cachedSettings;\n } catch (e) {\n log('Failed to load settings.json:', e.message);\n return null;\n }\n}\n\nfunction getCurrentModelConfig() {\n const settings = getFactorySettings();\n if (!settings) return null;\n \n const currentModelId = settings.sessionDefaultSettings?.model;\n if (!currentModelId) return null;\n \n const customModels = settings.customModels || [];\n const modelConfig = customModels.find(m => m.id === currentModelId);\n \n if (modelConfig) {\n log('Model:', modelConfig.displayName, '| Provider:', modelConfig.provider);\n return modelConfig;\n }\n \n if (!currentModelId.startsWith('custom:')) return null;\n log('Model not found:', currentModelId);\n return null;\n}\n\n// === Native Provider WebSearch ===\n\nasync function searchAnthropicNative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n const requestBody = {\n model: model,\n max_tokens: 4096,\n stream: false,\n system: 'You are a web search assistant. Use the web_search tool to find relevant information and return the results.',\n tools: [{ type: 'web_search_20250305', name: 'web_search', max_uses: 1 }],\n tool_choice: { type: 'tool', name: 'web_search' },\n messages: [{ role: 'user', content: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.' }]\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/v1/messages')) endpoint = endpoint.replace(/\\\\/$/, '') + '/v1/messages';\n \n log('Anthropic search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"anthropic-version: 2023-06-01\" -H \"x-api-key: ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n const results = [];\n for (const block of (response.content || [])) {\n if (block.type === 'web_search_tool_result') {\n for (const result of (block.content || [])) {\n if (result.type === 'web_search_result') {\n results.push({\n title: result.title || '',\n url: result.url || '',\n content: result.snippet || result.page_content || ''\n });\n }\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('Anthropic error:', e.message);\n return null;\n }\n}\n\nasync function searchOpenAINative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n // Note: instructions will be added by openai4droid proxy plugin\n const requestBody = {\n model: model,\n stream: false,\n tools: [{ type: 'web_search' }],\n tool_choice: 'required',\n input: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.'\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/responses')) endpoint = endpoint.replace(/\\\\/$/, '') + '/responses';\n \n log('OpenAI search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"Authorization: Bearer ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n // Extract results from url_citation annotations in message output\n const results = [];\n for (const item of (response.output || [])) {\n if (item.type === 'message' && Array.isArray(item.content)) {\n for (const content of item.content) {\n if (content.type === 'output_text' && Array.isArray(content.annotations)) {\n for (const annotation of content.annotations) {\n if (annotation.type === 'url_citation' && annotation.url) {\n results.push({\n title: annotation.title || '',\n url: annotation.url || '',\n content: annotation.title || ''\n });\n }\n }\n }\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('OpenAI error:', e.message);\n return null;\n }\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n const modelConfig = getCurrentModelConfig();\n if (!modelConfig) {\n log('No custom model configured');\n return { results: [], source: 'none' };\n }\n \n const provider = modelConfig.provider;\n let results = null;\n \n if (provider === 'anthropic') results = await searchAnthropicNative(query, numResults, modelConfig);\n else if (provider === 'openai') results = await searchOpenAINative(query, numResults, modelConfig);\n else log('Unsupported provider:', provider);\n \n if (results && results.length > 0) return { results: results, source: 'native-' + provider };\n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'native-provider' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Standalone mode: mock non-LLM APIs\n if (process.env.STANDALONE_MODE === '1') {\n const pathname = url.pathname;\n const isCoreLLMApi = pathname.startsWith('/api/llm/a/') || pathname.startsWith('/api/llm/o/');\n\n if (!isCoreLLMApi) {\n if (pathname === '/api/sessions/create') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ id: 'local-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10) }));\n return;\n }\n if (pathname === '/api/cli/whoami') {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({}));\n return;\n }\n }\n\n // Simple proxy - no SSE transformation (handled by proxy plugin)\n log('Proxy:', req.method, url.pathname);\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('Native provider proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n","/**\n * WebSearch Patch Generator\n *\n * Two modes:\n * - --websearch: External providers (Smithery, Google PSE, Serper, Brave, SearXNG, DuckDuckGo)\n * - --websearch-proxy: Native provider via proxy plugin (reads ~/.factory/settings.json)\n */\n\nimport type { Patch } from \"./patcher.ts\";\nimport { writeFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nimport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\n// Re-export for backward compatibility\nexport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nexport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\n/**\n * Generate wrapper script for standalone proxy mode\n */\nfunction generateWrapperScript(droidPath: string, proxyScriptPath: string): string {\n return `#!/bin/bash\n# Droid with WebSearch Proxy\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPORT_FILE=\"/tmp/droid-search-proxy-$$.port\"\n\nstart_proxy() {\n SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" &\n PROXY_PID=$!\n for i in {1..20}; do\n if [ -f \"$PORT_FILE\" ]; then\n PORT=$(cat \"$PORT_FILE\")\n if curl -s \"http://127.0.0.1:$PORT/health\" > /dev/null 2>&1; then\n echo \"[websearch] Proxy started on port $PORT\"\n return 0\n fi\n fi\n sleep 0.2\n done\n echo \"[websearch] Failed to start proxy\"\n kill $PROXY_PID 2>/dev/null\n return 1\n}\n\ncleanup() {\n [ -n \"$PROXY_PID\" ] && kill $PROXY_PID 2>/dev/null\n [ -f \"$PORT_FILE\" ] && rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT\n\nif ! start_proxy; then exit 1; fi\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$PORT\"\nexec \"$DROID_BIN\" \"$@\"\n`;\n}\n\n/**\n * Generate WebSearch Patch (legacy binary patch approach)\n */\nexport function generateWebSearchPatch(): Patch | null {\n const originalUrl = \"https://api.factory.ai\";\n const localUrl = \"http://127.0.0.1:23119\";\n\n if (localUrl.length > originalUrl.length) {\n console.error(`[websearch] Local URL too long: ${localUrl.length} > ${originalUrl.length}`);\n return null;\n }\n\n const paddedUrl = localUrl.padEnd(originalUrl.length, \" \");\n return {\n name: \"webSearch\",\n description: `Replace API URL with local proxy (${localUrl})`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n };\n}\n\n/**\n * Create WebSearch proxy files (legacy)\n */\nexport async function createWebSearchProxyFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ proxyScript: string; wrapperScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-search-proxy.js`);\n const wrapperScriptPath = join(outputDir, `${aliasName}-with-search`);\n\n await writeFile(proxyScriptPath, generateSearchProxyServerCode());\n console.log(`[*] Created search proxy: ${proxyScriptPath}`);\n\n await writeFile(wrapperScriptPath, generateWrapperScript(droidPath, proxyScriptPath));\n await chmod(wrapperScriptPath, 0o755);\n console.log(`[*] Created wrapper script: ${wrapperScriptPath}`);\n\n return { proxyScript: proxyScriptPath, wrapperScript: wrapperScriptPath };\n}\n\n/**\n * Get proxy server code (for export)\n */\nexport function getSearchProxyCode(): string {\n return generateSearchProxyServerCode();\n}\n\n/**\n * Generate unified wrapper script\n */\nfunction generateUnifiedWrapper(\n droidPath: string,\n proxyScriptPath: string,\n standalone: boolean = false,\n): string {\n const standaloneEnv = standalone ? \"STANDALONE_MODE=1 \" : \"\";\n return `#!/bin/bash\n# Droid with WebSearch\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPROXY_PID=\"\"\nPORT_FILE=\"/tmp/droid-websearch-$$.port\"\nSTANDALONE=\"${standalone ? \"1\" : \"0\"}\"\n\nshould_passthrough() {\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then break; fi\n case \"$arg\" in --help|-h|--version|-V) return 0 ;; esac\n done\n local end_opts=0\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then end_opts=1; continue; fi\n if [ \"$end_opts\" -eq 0 ] && [[ \"$arg\" == -* ]]; then continue; fi\n case \"$arg\" in help|version|completion|completions|exec) return 0 ;; esac\n break\n done\n return 1\n}\n\nif should_passthrough \"$@\"; then exec \"$DROID_BIN\" \"$@\"; fi\n\ncleanup() {\n if [ -n \"$PROXY_PID\" ] && kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Stopping proxy (PID: $PROXY_PID)\" >&2\n kill \"$PROXY_PID\" 2>/dev/null\n wait \"$PROXY_PID\" 2>/dev/null\n fi\n rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT INT TERM\n\n[ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Starting proxy...\" >&2\n[ \"$STANDALONE\" = \"1\" ] && [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Standalone mode enabled\" >&2\n\nif [ -n \"$DROID_SEARCH_DEBUG\" ]; then\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" 2>&1 &\nelse\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" >/dev/null 2>&1 &\nfi\nPROXY_PID=$!\n\nfor i in {1..50}; do\n if ! kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy process died\" >&2\n break\n fi\n if [ -f \"$PORT_FILE\" ]; then\n ACTUAL_PORT=$(cat \"$PORT_FILE\" 2>/dev/null)\n if [ -n \"$ACTUAL_PORT\" ] && curl -s \"http://127.0.0.1:$ACTUAL_PORT/health\" > /dev/null 2>&1; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy ready on port $ACTUAL_PORT (PID: $PROXY_PID)\" >&2\n break\n fi\n fi\n sleep 0.1\ndone\n\nif [ ! -f \"$PORT_FILE\" ] || [ -z \"$(cat \"$PORT_FILE\" 2>/dev/null)\" ]; then\n echo \"[websearch] Failed to start proxy, running without websearch\" >&2\n cleanup\n exec \"$DROID_BIN\" \"$@\"\nfi\n\nACTUAL_PORT=$(cat \"$PORT_FILE\")\nrm -f \"$PORT_FILE\"\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$ACTUAL_PORT\"\n\"$DROID_BIN\" \"$@\"\nDROID_EXIT_CODE=$?\nexit $DROID_EXIT_CODE\n`;\n}\n\n/**\n * Create unified WebSearch files\n *\n * @param outputDir - Directory to write files to\n * @param droidPath - Path to droid binary\n * @param aliasName - Alias name for the wrapper\n * @param apiBase - Custom API base URL for proxy to forward requests to\n * @param standalone - Standalone mode: mock non-LLM Factory APIs\n * @param useNativeProvider - Use native provider websearch (--websearch-proxy mode)\n */\nexport async function createWebSearchUnifiedFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n apiBase?: string,\n standalone: boolean = false,\n useNativeProvider: boolean = false,\n): Promise<{ wrapperScript: string; preloadScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-proxy.js`);\n const wrapperScriptPath = join(outputDir, aliasName);\n\n const factoryApiUrl = apiBase || \"https://api.factory.ai\";\n const proxyCode = useNativeProvider\n ? generateNativeSearchProxyServer(factoryApiUrl)\n : generateExternalSearchProxyServer(factoryApiUrl);\n\n await writeFile(proxyScriptPath, proxyCode);\n console.log(`[*] Created proxy script: ${proxyScriptPath}`);\n console.log(\n `[*] Mode: ${useNativeProvider ? \"native provider (requires proxy plugin)\" : \"external providers\"}`,\n );\n\n await writeFile(\n wrapperScriptPath,\n generateUnifiedWrapper(droidPath, proxyScriptPath, standalone),\n );\n await chmod(wrapperScriptPath, 0o755);\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n if (standalone) {\n console.log(`[*] Standalone mode enabled`);\n }\n\n return {\n wrapperScript: wrapperScriptPath,\n preloadScript: proxyScriptPath,\n };\n}\n\n// === Legacy Preload Mode (kept for compatibility) ===\n\nfunction generatePreloadScript(): string {\n return `// Droid WebSearch Preload Script\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\n\nconst PORT = process.env.DROID_SEARCH_PORT || 23119;\nconst FACTORY_API = 'https://api.factory.ai';\n\nasync function searchGooglePSE(query, num) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = \\`https://www.googleapis.com/customsearch/v1?key=\\${apiKey}&cx=\\${cx}&q=\\${encodeURIComponent(query)}&num=\\${Math.min(num, 10)}\\`;\n const res = await fetch(url);\n const data = await res.json();\n if (data.error) return null;\n return (data.items || []).map(item => ({ title: item.title, url: item.link, content: item.snippet || '' }));\n } catch { return null; }\n}\n\nfunction searchDuckDuckGo(query, num) {\n try {\n const url = \\`https://api.duckduckgo.com/?q=\\${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1\\`;\n const output = execSync(\\`curl -s \"\\${url}\"\\`, { encoding: 'utf8', timeout: 10000 });\n const data = JSON.parse(output);\n const results = [];\n if (data.AbstractText && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.AbstractText });\n }\n for (const t of (data.RelatedTopics || [])) {\n if (results.length >= num) break;\n if (t.Text && t.FirstURL) results.push({ title: t.Text.split(' - ')[0], url: t.FirstURL, content: t.Text });\n if (t.Topics) {\n for (const sub of t.Topics) {\n if (results.length >= num) break;\n if (sub.Text && sub.FirstURL) results.push({ title: sub.Text.split(' - ')[0], url: sub.FirstURL, content: sub.Text });\n }\n }\n }\n return results;\n } catch { return []; }\n}\n\nasync function search(query, num) {\n const googleResults = await searchGooglePSE(query, num);\n if (googleResults?.length > 0) return googleResults;\n return searchDuckDuckGo(query, num);\n}\n\nfunction isPortInUse(port) {\n try { execSync(\\`curl -s http://127.0.0.1:\\${port}/health\\`, { timeout: 1000 }); return true; } catch { return false; }\n}\n\nif (!isPortInUse(PORT)) {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url, \\`http://\\${req.headers.host}\\`);\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', c => body += c);\n req.on('end', async () => {\n try {\n const { query, numResults } = JSON.parse(body);\n const results = await search(query, numResults || 10);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results }));\n } catch (e) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyReq = https.request(proxyUrl, { method: req.method, headers: { ...req.headers, host: proxyUrl.host } }, proxyRes => {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n proxyReq.on('error', () => { res.writeHead(502); res.end(); });\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n });\n server.listen(PORT, '127.0.0.1');\n}\n`;\n}\n\nfunction generateBunfigToml(preloadScriptPath: string): string {\n return `preload = [\"${preloadScriptPath}\"]`;\n}\n\nfunction generatePreloadWrapperScript(droidPath: string, bunfigDir: string): string {\n return `#!/bin/bash\ncd \"${bunfigDir}\"\nexec \"${droidPath}\" --cwd \"$(pwd)\" \"$@\"\n`;\n}\n\nexport async function createWebSearchPreloadFiles(\n droidDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ preloadScript: string; bunfigPath: string; wrapperScript: string }> {\n if (!existsSync(droidDir)) {\n await mkdir(droidDir, { recursive: true });\n }\n\n const preloadScriptPath = join(droidDir, `${aliasName}-search-preload.js`);\n const bunfigPath = join(droidDir, \"bunfig.toml\");\n const wrapperScriptPath = join(droidDir, aliasName);\n\n await writeFile(preloadScriptPath, generatePreloadScript());\n await writeFile(bunfigPath, generateBunfigToml(preloadScriptPath));\n await writeFile(wrapperScriptPath, generatePreloadWrapperScript(droidPath, droidDir));\n await chmod(wrapperScriptPath, 0o755);\n\n console.log(`[*] Created preload: ${preloadScriptPath}`);\n console.log(`[*] Created bunfig: ${bunfigPath}`);\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n return { preloadScript: preloadScriptPath, bunfigPath, wrapperScript: wrapperScriptPath };\n}\n\nexport function getPreloadScriptCode(): string {\n return generatePreloadScript();\n}\n","import bin from \"tiny-bin\";\nimport { styleText } from \"node:util\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\nimport { patchDroid, type Patch } from \"./patcher.ts\";\nimport {\n createAlias,\n removeAlias,\n listAliases,\n createAliasForWrapper,\n clearAllAliases,\n removeAliasesByFilter,\n type FilterFlag,\n} from \"./alias.ts\";\nimport { createWebSearchUnifiedFiles } from \"./websearch-patch.ts\";\nimport {\n saveAliasMetadata,\n createMetadata,\n loadAliasMetadata,\n listAllMetadata,\n formatPatches,\n} from \"./metadata.ts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst version = getVersion();\n\nfunction getDroidVersion(droidPath: string): string | undefined {\n try {\n const result = execSync(`\"${droidPath}\" --version`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 5000,\n }).trim();\n // Parse version from output like \"droid 1.2.3\" or just \"1.2.3\"\n const match = result.match(/(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : result || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction findDefaultDroidPath(): string {\n const home = homedir();\n\n // Try `which droid` first to find droid in PATH\n try {\n const result = execSync(\"which droid\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n if (result && existsSync(result)) {\n return result;\n }\n } catch {\n // which command failed, continue with fallback paths\n }\n\n // Common installation paths\n const paths = [\n // Default sh install location\n join(home, \".droid\", \"bin\", \"droid\"),\n // Homebrew on Apple Silicon\n \"/opt/homebrew/bin/droid\",\n // Homebrew on Intel Mac / Linux\n \"/usr/local/bin/droid\",\n // Linux system-wide\n \"/usr/bin/droid\",\n // Current directory\n \"./droid\",\n ];\n\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n\n // Return default path even if not found (will error later with helpful message)\n return join(home, \".droid\", \"bin\", \"droid\");\n}\n\nbin(\"droid-patch\", \"CLI tool to patch droid binary with various modifications\")\n .package(\"droid-patch\", version)\n .option(\n \"--is-custom\",\n \"Patch isCustom:!0 to isCustom:!1 (enable context compression for custom models)\",\n )\n .option(\n \"--skip-login\",\n \"Inject a fake FACTORY_API_KEY to bypass login requirement (no real key needed)\",\n )\n .option(\n \"--api-base <url>\",\n \"Replace API URL (standalone: binary patch, max 22 chars; with --websearch: proxy forward target, no limit)\",\n )\n .option(\n \"--websearch\",\n \"Enable local WebSearch proxy with external providers (Smithery, Google PSE, etc.)\",\n )\n .option(\n \"--websearch-proxy\",\n \"Enable native provider websearch (requires proxy plugin, reads ~/.factory/settings.json)\",\n )\n .option(\"--standalone\", \"Standalone mode: mock non-LLM Factory APIs (use with --websearch)\")\n .option(\n \"--reasoning-effort\",\n \"Enable reasoning effort for custom models (set to high, enable UI selector)\",\n )\n .option(\n \"--disable-telemetry\",\n \"Disable telemetry and Sentry error reporting (block data uploads)\",\n )\n .option(\n \"--auto-high\",\n \"Set default autonomy mode to auto-high (bypass settings.json race condition)\",\n )\n .option(\"--dry-run\", \"Verify patches without actually modifying the binary\")\n .option(\"-p, --path <path>\", \"Path to the droid binary\")\n .option(\"-o, --output <dir>\", \"Output directory for patched binary\")\n .option(\"--no-backup\", \"Do not create backup of original binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .argument(\"[alias]\", \"Alias name for the patched binary\")\n .action(async (options, args) => {\n const alias = args?.[0] as string | undefined;\n const isCustom = options[\"is-custom\"] as boolean;\n const skipLogin = options[\"skip-login\"] as boolean;\n const apiBase = options[\"api-base\"] as string | undefined;\n const websearch = options[\"websearch\"] as boolean;\n const websearchProxy = options[\"websearch-proxy\"] as boolean;\n const standalone = options[\"standalone\"] as boolean;\n // When --websearch is used with --api-base, forward to custom URL\n // Otherwise forward to official Factory API\n const websearchTarget = websearch ? apiBase || \"https://api.factory.ai\" : undefined;\n const reasoningEffort = options[\"reasoning-effort\"] as boolean;\n const noTelemetry = options[\"disable-telemetry\"] as boolean;\n const autoHigh = options[\"auto-high\"] as boolean;\n const dryRun = options[\"dry-run\"] as boolean;\n const path = (options.path as string) || findDefaultDroidPath();\n const outputDir = options.output as string | undefined;\n const backup = options.backup !== false;\n const verbose = options.verbose as boolean;\n\n // If -o is specified with alias, output to that directory with alias name\n const outputPath = outputDir && alias ? join(outputDir, alias) : undefined;\n\n const needsBinaryPatch =\n !!isCustom ||\n !!skipLogin ||\n !!reasoningEffort ||\n !!noTelemetry ||\n !!autoHigh ||\n (!!apiBase && !websearch && !websearchProxy);\n\n // Check for conflicting flags\n if (websearch && websearchProxy) {\n console.log(styleText(\"red\", \"Error: Cannot use --websearch and --websearch-proxy together\"));\n console.log(styleText(\"gray\", \"Choose one:\"));\n console.log(\n styleText(\"gray\", \" --websearch External providers (Smithery, Google PSE, etc.)\"),\n );\n console.log(\n styleText(\"gray\", \" --websearch-proxy Native provider (requires proxy plugin)\"),\n );\n process.exit(1);\n }\n\n // Wrapper-only mode (no binary patching needed):\n // - --websearch or --websearch-proxy (optional --standalone)\n const isWebsearchMode = websearch || websearchProxy;\n if (!needsBinaryPatch && isWebsearchMode) {\n if (!alias) {\n const flag = websearchProxy ? \"--websearch-proxy\" : \"--websearch\";\n console.log(styleText(\"red\", `Error: Alias name required for ${flag}`));\n console.log(styleText(\"gray\", `Usage: npx droid-patch ${flag} <alias>`));\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Wrapper Setup\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"white\", `WebSearch: native provider mode`));\n console.log(styleText(\"gray\", ` Requires proxy plugin (anthropic4droid)`));\n console.log(styleText(\"gray\", ` Reads model config from ~/.factory/settings.json`));\n } else if (websearch) {\n console.log(styleText(\"white\", `WebSearch: external providers mode`));\n console.log(styleText(\"white\", `Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", `Standalone mode: enabled`));\n }\n console.log();\n\n let execTargetPath = path;\n // Create websearch proxy files (proxy script + wrapper)\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n // For --websearch-proxy, apiBase comes from settings.json at runtime\n // For --websearch, use apiBase or default Factory API\n const forwardTarget = websearchProxy ? undefined : websearchTarget;\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n forwardTarget,\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n // Create alias pointing to outer wrapper\n const aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: false,\n skipLogin: false,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: false,\n noTelemetry: false,\n standalone: standalone,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" Wrapper Ready!\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(\"Run directly:\");\n console.log(styleText(\"yellow\", ` ${alias}`));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"Native Provider WebSearch (--websearch-proxy):\"));\n console.log(styleText(\"gray\", \" Uses model's built-in websearch via proxy plugin\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n console.log();\n console.log(styleText(\"yellow\", \"IMPORTANT: Requires proxy plugin (anthropic4droid)\"));\n console.log();\n console.log(\"Supported providers:\");\n console.log(styleText(\"yellow\", \" - anthropic: Claude web_search_20250305 server tool\"));\n console.log(styleText(\"yellow\", \" - openai: OpenAI web_search tool\"));\n console.log(styleText(\"gray\", \" - generic-chat-completion-api: Not supported\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1 # Basic logs\"));\n console.log(styleText(\"gray\", \" export DROID_SEARCH_VERBOSE=1 # Full request/response\"));\n } else if (websearch) {\n console.log(styleText(\"cyan\", \"External Providers WebSearch (--websearch):\"));\n console.log(\n styleText(\"gray\", \" Uses external search providers (Smithery, Google PSE, etc.)\"),\n );\n console.log();\n console.log(\"Search providers (in priority order):\");\n console.log(styleText(\"yellow\", \" 1. Smithery Exa (best quality):\"));\n console.log(styleText(\"gray\", \" export SMITHERY_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export SMITHERY_PROFILE=your_profile\"));\n console.log(styleText(\"gray\", \" 2. Google PSE:\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_CX=your_search_engine_id\"));\n console.log(styleText(\"gray\", \" 3-6. Serper, Brave, SearXNG, DuckDuckGo (fallbacks)\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1\"));\n }\n return;\n }\n\n if (\n !isCustom &&\n !skipLogin &&\n !apiBase &&\n !websearch &&\n !reasoningEffort &&\n !noTelemetry &&\n !autoHigh\n ) {\n console.log(styleText(\"yellow\", \"No patch flags specified. Available patches:\"));\n console.log(styleText(\"gray\", \" --is-custom Patch isCustom for custom models\"));\n console.log(\n styleText(\"gray\", \" --skip-login Bypass login by injecting a fake API key\"),\n );\n console.log(\n styleText(\n \"gray\",\n \" --api-base Replace API URL (standalone: max 22 chars; with --websearch: no limit)\",\n ),\n );\n console.log(styleText(\"gray\", \" --websearch Enable local WebSearch proxy\"));\n console.log(\n styleText(\"gray\", \" --reasoning-effort Set reasoning effort level for custom models\"),\n );\n console.log(\n styleText(\"gray\", \" --disable-telemetry Disable telemetry and Sentry error reporting\"),\n );\n console.log(\n styleText(\"gray\", \" --auto-high Set default autonomy mode to auto-high\"),\n );\n console.log(\n styleText(\"gray\", \" --standalone Standalone mode: mock non-LLM Factory APIs\"),\n );\n console.log();\n console.log(\"Usage examples:\");\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom droid-custom\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --skip-login droid-nologin\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom --skip-login droid-patched\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch droid-search\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch --standalone droid-local\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --disable-telemetry droid-private\"));\n console.log(\n styleText(\n \"cyan\",\n \" npx droid-patch --websearch --api-base=http://127.0.0.1:20002 my-droid\",\n ),\n );\n process.exit(1);\n }\n\n if (!alias && !dryRun) {\n console.log(styleText(\"red\", \"Error: alias name is required\"));\n console.log(\n styleText(\n \"gray\",\n \"Usage: droid-patch [--is-custom] [--skip-login] [-o <dir>] <alias-name>\",\n ),\n );\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Binary Patcher\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n const patches: Patch[] = [];\n if (isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n // Add skip-login patch: replace process.env.FACTORY_API_KEY with a fixed fake key\n // \"process.env.FACTORY_API_KEY\" is 27 chars, we replace with \"fk-droid-patch-skip-00000\" (25 chars + quotes = 27)\n if (skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: 'Replace process.env.FACTORY_API_KEY with \"fk-droid-patch-skip-00000\"',\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Add api-base patch: replace the Factory API base URL\n // Original: \"https://api.factory.ai\" (22 chars)\n // We need to pad the replacement URL to be exactly 22 chars\n // Note: When --websearch is used, --api-base sets the forward target instead of binary patching\n if (apiBase && !websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const originalLength = originalUrl.length; // 22 chars\n\n // Validate and normalize the URL\n let normalizedUrl = apiBase.replace(/\\/+$/, \"\"); // Remove trailing slashes\n\n if (normalizedUrl.length > originalLength) {\n console.log(\n styleText(\"red\", `Error: API base URL must be ${originalLength} characters or less`),\n );\n console.log(\n styleText(\"gray\", ` Your URL: \"${normalizedUrl}\" (${normalizedUrl.length} chars)`),\n );\n console.log(styleText(\"gray\", ` Maximum: ${originalLength} characters`));\n console.log();\n console.log(styleText(\"yellow\", \"Tip: Use a shorter URL or set up a local redirect.\"));\n console.log(styleText(\"gray\", \" Examples:\"));\n console.log(styleText(\"gray\", \" http://127.0.0.1:3000 (19 chars)\"));\n console.log(styleText(\"gray\", \" http://localhost:80 (19 chars)\"));\n process.exit(1);\n }\n\n // Pad the URL with spaces at the end to match original length\n // Note: trailing spaces in URL are generally ignored\n const paddedUrl = normalizedUrl.padEnd(originalLength, \" \");\n\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${normalizedUrl}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n // Add reasoning-effort patch: set custom models to use \"high\" reasoning\n // Also modify UI conditions to show reasoning selector for custom models\n if (reasoningEffort) {\n // [\"none\"] is 8 chars, [\"high\"] is 8 chars - perfect match!\n patches.push({\n name: \"reasoningEffortSupported\",\n description: 'Change supportedReasoningEfforts:[\"none\"] to [\"high\"]',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n\n // \"none\" is 4 chars, \"high\" is 4 chars - perfect match!\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n\n // Change UI condition from length>1 to length>0\n // This allows custom models with single reasoning option to show the selector\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n\n // Change UI condition from length<=1 to length<=0\n // This enables the reasoning setting in /settings menu for custom models\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n\n // Bypass reasoning effort validation to allow settings.json override\n // This allows \"xhigh\" in settings.json to work even though default is \"high\"\n // v0.39.0+: T!==\"none\"&&T!==\"off\"&&!X.supportedReasoningEfforts.includes(T)\n // Changed: T!=\"none\"&&T!=\"off\"&&0&&X... - use != (2 chars less) + 0&& (2 chars more) = same length\n // Logic: && 0 && makes entire condition always false, bypassing validation\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from('T!==\"none\"&&T!==\"off\"&&!W.supportedReasoningEfforts.includes(T)'),\n replacement: Buffer.from('T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)'),\n variants: [\n {\n pattern: Buffer.from('T!==\"none\"&&T!==\"off\"&&!C.supportedReasoningEfforts.includes(T)'),\n replacement: Buffer.from(\n 'T!=\"none\"&&T!=\"off\"&&0&&C.supportedReasoningEfforts.includes(T)',\n ),\n },\n ],\n });\n }\n\n // Add no-telemetry patches: disable telemetry uploads and Sentry error reporting\n // Strategy:\n // 1. Break environment variable names so Sentry is never initialized (Q1() returns false)\n // 2. Invert flushToWeb condition so it returns early without making any fetch request\n if (noTelemetry) {\n // Patch 1: Break Sentry environment variable checks\n // Q1() function checks: VITE_VERCEL_ENV, ENABLE_SENTRY, NEXT_PUBLIC_ENABLE_SENTRY, FACTORY_ENABLE_SENTRY\n // By changing first letter to X, the env vars will never match, so Q1() returns false\n // and Sentry is never initialized\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n\n // Patch 2: Make flushToWeb always return early to prevent ANY fetch request\n // Original: if(this.webEvents.length===0)return; // returns only when empty\n // Changed: if(!0||this.webEvents.length)return; // !0=true, ALWAYS returns\n // Result: Function always exits immediately, no telemetry is ever sent\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n // Add auto-high autonomy patch: hardcode getCurrentAutonomyMode to return \"auto-high\"\n // This bypasses the race condition where AutonomyManager.initialize() runs before\n // SettingsService has loaded settings.json, causing the default \"normal\" to be used.\n // Pattern (50 chars): getCurrentAutonomyMode(){return this.autonomyMode}\n // Replace (50 chars): getCurrentAutonomyMode(){return\"auto-high\" }\n if (autoHigh) {\n patches.push({\n name: \"autoHighAutonomy\",\n description: 'Hardcode getCurrentAutonomyMode() to return \"auto-high\"',\n pattern: Buffer.from(\"getCurrentAutonomyMode(){return this.autonomyMode}\"),\n replacement: Buffer.from('getCurrentAutonomyMode(){return\"auto-high\" }'),\n });\n }\n\n try {\n const result = await patchDroid({\n inputPath: path,\n outputPath: outputPath,\n patches,\n dryRun,\n backup,\n verbose,\n });\n\n if (dryRun) {\n console.log();\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"gray\", \"To apply the patches, run without --dry-run:\"));\n console.log(styleText(\"cyan\", ` npx droid-patch --is-custom ${alias || \"<alias-name>\"}`));\n process.exit(0);\n }\n\n // If -o is specified, just output the file without creating alias\n if (outputDir && result.success && result.outputPath) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", `Patched binary saved to: ${result.outputPath}`));\n process.exit(0);\n }\n\n if (result.success && result.outputPath && alias) {\n console.log();\n\n let execTargetPath = result.outputPath;\n\n if (websearch || websearchProxy) {\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n websearchProxy ? undefined : websearchTarget, // websearchProxy reads from settings.json at runtime\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"WebSearch enabled (native provider mode)\"));\n console.log(styleText(\"gray\", \" Requires proxy plugin (anthropic4droid)\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n } else {\n console.log(styleText(\"cyan\", \"WebSearch enabled (external providers mode)\"));\n console.log(styleText(\"white\", ` Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", ` Standalone mode: enabled`));\n }\n }\n\n let aliasResult;\n if (websearch || websearchProxy) {\n aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n } else {\n aliasResult = await createAlias(result.outputPath, alias, verbose);\n }\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: !!isCustom,\n skipLogin: !!skipLogin,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: !!reasoningEffort,\n noTelemetry: !!noTelemetry,\n standalone: !!standalone,\n autoHigh: !!autoHigh,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n }\n\n if (result.success) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n }\n\n process.exit(result.success ? 0 : 1);\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n if (verbose) console.error((error as Error).stack);\n process.exit(1);\n }\n })\n .command(\"list\", \"List all droid-patch aliases\")\n .action(async () => {\n await listAliases();\n })\n .command(\"remove\", \"Remove alias(es) by name or filter\")\n .argument(\"[alias-or-path]\", \"Alias name or file path to remove\")\n .option(\"--patch-version <version>\", \"Remove aliases created by this droid-patch version\")\n .option(\"--droid-version <version>\", \"Remove aliases for this droid version\")\n .option(\n \"--flag <flag>\",\n \"Remove aliases with this flag (is-custom, skip-login, websearch, api-base, reasoning-effort, disable-telemetry, standalone)\",\n )\n .action(async (options, args) => {\n const target = args?.[0] as string | undefined;\n const patchVersion = options[\"patch-version\"] as string | undefined;\n const droidVersion = options[\"droid-version\"] as string | undefined;\n const flagRaw = options.flag as string | undefined;\n let flag: FilterFlag | undefined;\n if (flagRaw) {\n const allowedFlags: FilterFlag[] = [\n \"is-custom\",\n \"skip-login\",\n \"websearch\",\n \"api-base\",\n \"reasoning-effort\",\n \"disable-telemetry\",\n \"standalone\",\n ];\n if (!allowedFlags.includes(flagRaw as FilterFlag)) {\n console.error(styleText(\"red\", `Error: Invalid --flag value: ${flagRaw}`));\n console.error(styleText(\"gray\", `Allowed: ${allowedFlags.join(\", \")}`));\n process.exit(1);\n }\n flag = flagRaw as FilterFlag;\n }\n\n // If filter options are provided, use filter mode\n if (patchVersion || droidVersion || flag) {\n await removeAliasesByFilter({\n patchVersion,\n droidVersion,\n flags: flag ? [flag] : undefined,\n });\n return;\n }\n\n // If no target and no filter, show error\n if (!target) {\n console.error(\n styleText(\n \"red\",\n \"Error: Provide an alias name or use filter options (--patch-version, --droid-version, --flag)\",\n ),\n );\n process.exit(1);\n }\n\n // Check if it's a file path (contains / or .)\n if (target.includes(\"/\") || existsSync(target)) {\n // It's a file path, delete directly\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(target);\n console.log(styleText(\"green\", `[*] Removed: ${target}`));\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n process.exit(1);\n }\n } else {\n // It's an alias name\n await removeAlias(target);\n }\n })\n .command(\"version\", \"Print droid-patch version\")\n .action(() => {\n console.log(`droid-patch v${version}`);\n })\n .command(\"clear\", \"Remove all droid-patch aliases and related files\")\n .action(async () => {\n await clearAllAliases();\n })\n .command(\"update\", \"Update aliases with latest droid binary\")\n .argument(\"[alias]\", \"Specific alias to update (optional, updates all if not specified)\")\n .option(\"--dry-run\", \"Preview without making changes\")\n .option(\"-p, --path <path>\", \"Path to new droid binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .action(async (options, args) => {\n const aliasName = args?.[0] as string | undefined;\n const dryRun = options[\"dry-run\"] as boolean;\n const newBinaryPath = (options.path as string) || findDefaultDroidPath();\n const verbose = options.verbose as boolean;\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid-Patch Update\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Verify the new binary exists\n if (!existsSync(newBinaryPath)) {\n console.log(styleText(\"red\", `Error: Droid binary not found at ${newBinaryPath}`));\n console.log(styleText(\"gray\", \"Use -p to specify a different path\"));\n process.exit(1);\n }\n\n // Get aliases to update\n let metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[];\n if (aliasName) {\n const meta = await loadAliasMetadata(aliasName);\n if (!meta) {\n console.log(styleText(\"red\", `Error: No metadata found for alias \"${aliasName}\"`));\n console.log(\n styleText(\"gray\", \"This alias may have been created before update tracking was added.\"),\n );\n console.log(styleText(\"gray\", \"Remove and recreate the alias to enable update support.\"));\n process.exit(1);\n }\n metaList = [meta];\n } else {\n metaList = await listAllMetadata();\n if (metaList.length === 0) {\n console.log(styleText(\"yellow\", \"No aliases with metadata found.\"));\n console.log(styleText(\"gray\", \"Create aliases with droid-patch to enable update support.\"));\n process.exit(0);\n }\n }\n\n console.log(styleText(\"white\", `Using droid binary: ${newBinaryPath}`));\n console.log(styleText(\"white\", `Found ${metaList.length} alias(es) to update`));\n if (dryRun) {\n console.log(styleText(\"blue\", \"(DRY RUN - no changes will be made)\"));\n }\n console.log();\n\n let successCount = 0;\n let failCount = 0;\n\n for (const meta of metaList) {\n if (!meta) continue;\n\n console.log(styleText(\"cyan\", `─`.repeat(40)));\n console.log(styleText(\"white\", `Updating: ${styleText([\"cyan\", \"bold\"], meta.name)}`));\n console.log(styleText(\"gray\", ` Patches: ${formatPatches(meta.patches)}`));\n\n if (dryRun) {\n console.log(styleText(\"blue\", ` [DRY RUN] Would re-apply patches`));\n successCount++;\n continue;\n }\n\n try {\n // Build patch list based on metadata\n const patches: Patch[] = [];\n\n if (meta.patches.isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n if (meta.patches.skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: \"Replace process.env.FACTORY_API_KEY with fake key\",\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Only apply apiBase binary patch when NOT using websearch\n // When websearch is enabled, apiBase is used as forward target, not binary patch\n if (meta.patches.apiBase && !meta.patches.websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const paddedUrl = meta.patches.apiBase.padEnd(originalUrl.length, \" \");\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${meta.patches.apiBase}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n if (meta.patches.reasoningEffort) {\n patches.push({\n name: \"reasoningEffortSupported\",\n description: 'Change supportedReasoningEfforts:[\"none\"] to [\"high\"]',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from('T!==\"none\"&&T!==\"off\"&&!W.supportedReasoningEfforts.includes(T)'),\n replacement: Buffer.from(\n 'T!=\"none\"&&T!=\"off\"&&0&&W.supportedReasoningEfforts.includes(T)',\n ),\n variants: [\n {\n pattern: Buffer.from(\n 'T!==\"none\"&&T!==\"off\"&&!C.supportedReasoningEfforts.includes(T)',\n ),\n replacement: Buffer.from(\n 'T!=\"none\"&&T!=\"off\"&&0&&C.supportedReasoningEfforts.includes(T)',\n ),\n },\n ],\n });\n }\n\n if (meta.patches.noTelemetry) {\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n if (meta.patches.autoHigh) {\n patches.push({\n name: \"autoHighAutonomy\",\n description: 'Hardcode getCurrentAutonomyMode() to return \"auto-high\"',\n pattern: Buffer.from(\"getCurrentAutonomyMode(){return this.autonomyMode}\"),\n replacement: Buffer.from('getCurrentAutonomyMode(){return\"auto-high\" }'),\n });\n }\n\n // Determine output path based on whether this is a websearch alias\n const binsDir = join(homedir(), \".droid-patch\", \"bins\");\n const outputPath = join(binsDir, `${meta.name}-patched`);\n\n // Apply patches (only if there are binary patches to apply)\n if (patches.length > 0) {\n const result = await patchDroid({\n inputPath: newBinaryPath,\n outputPath,\n patches,\n dryRun: false,\n backup: false,\n verbose,\n });\n\n if (!result.success) {\n console.log(styleText(\"red\", ` ✗ Failed to apply patches`));\n failCount++;\n continue;\n }\n\n // Re-sign on macOS\n if (process.platform === \"darwin\") {\n try {\n const { execSync } = await import(\"node:child_process\");\n execSync(`codesign --force --deep --sign - \"${outputPath}\"`, {\n stdio: \"pipe\",\n });\n if (verbose) {\n console.log(styleText(\"gray\", ` Re-signed binary`));\n }\n } catch {\n console.log(styleText(\"yellow\", ` [!] Could not re-sign binary`));\n }\n }\n }\n\n let execTargetPath = patches.length > 0 ? outputPath : newBinaryPath;\n\n // If websearch is enabled, regenerate wrapper files\n // Support both new 'websearch' field and old 'proxy' field for backward compatibility\n const hasWebsearch = meta.patches.websearch || !!meta.patches.proxy;\n if (hasWebsearch) {\n // Determine forward target: apiBase > proxy (legacy) > default\n const forwardTarget =\n meta.patches.apiBase || meta.patches.proxy || \"https://api.factory.ai\";\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n meta.name,\n forwardTarget,\n meta.patches.standalone || false,\n );\n execTargetPath = wrapperScript;\n if (verbose) {\n console.log(styleText(\"gray\", ` Regenerated websearch wrapper`));\n if (meta.patches.standalone) {\n console.log(styleText(\"gray\", ` Standalone mode: enabled`));\n }\n }\n // Migrate old proxy field to new websearch field\n if (meta.patches.proxy && !meta.patches.websearch) {\n meta.patches.websearch = true;\n meta.patches.apiBase = meta.patches.proxy;\n delete meta.patches.proxy;\n }\n }\n\n // If this alias previously used removed features (statusline/sessions), drop legacy flags\n // so the updated alias points directly to the new target wrapper/binary.\n delete (meta.patches as Record<string, unknown>).statusline;\n delete (meta.patches as Record<string, unknown>).sessions;\n\n // Update symlink - find existing or use stored aliasPath\n const { symlink, unlink, readlink, lstat } = await import(\"node:fs/promises\");\n let aliasPath = meta.aliasPath;\n\n // If aliasPath not stored (old version), try to find existing symlink\n if (!aliasPath) {\n const commonPathDirs = [\n join(homedir(), \".local/bin\"),\n join(homedir(), \"bin\"),\n join(homedir(), \".bin\"),\n \"/opt/homebrew/bin\",\n \"/usr/local/bin\",\n join(homedir(), \".droid-patch\", \"aliases\"),\n ];\n\n for (const dir of commonPathDirs) {\n const possiblePath = join(dir, meta.name);\n if (existsSync(possiblePath)) {\n try {\n const stats = await lstat(possiblePath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(possiblePath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasPath = possiblePath;\n if (verbose) {\n console.log(styleText(\"gray\", ` Found existing symlink: ${aliasPath}`));\n }\n break;\n }\n }\n } catch {\n // Ignore errors, continue searching\n }\n }\n }\n }\n\n // Update symlink if we have a path\n if (aliasPath) {\n try {\n if (existsSync(aliasPath)) {\n const currentTarget = await readlink(aliasPath);\n if (currentTarget !== execTargetPath) {\n await unlink(aliasPath);\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Updated symlink: ${aliasPath}`));\n }\n }\n } else {\n // Symlink doesn't exist, recreate it\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Recreated symlink: ${aliasPath}`));\n }\n }\n // Store aliasPath in metadata for future updates\n meta.aliasPath = aliasPath;\n } catch (symlinkError) {\n console.log(\n styleText(\n \"yellow\",\n ` [!] Could not update symlink: ${(symlinkError as Error).message}`,\n ),\n );\n }\n }\n\n // Update metadata\n meta.updatedAt = new Date().toISOString();\n meta.originalBinaryPath = newBinaryPath;\n meta.droidVersion = getDroidVersion(newBinaryPath);\n meta.droidPatchVersion = version;\n await saveAliasMetadata(meta);\n\n console.log(styleText(\"green\", ` ✓ Updated successfully`));\n successCount++;\n } catch (error) {\n console.log(styleText(\"red\", ` ✗ Error: ${(error as Error).message}`));\n if (verbose) {\n console.error((error as Error).stack);\n }\n failCount++;\n }\n }\n\n console.log();\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n if (dryRun) {\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"gray\", ` Would update ${successCount} alias(es)`));\n } else if (failCount === 0) {\n console.log(styleText([\"green\", \"bold\"], \" UPDATE COMPLETE\"));\n console.log(styleText(\"gray\", ` Updated ${successCount} alias(es)`));\n } else {\n console.log(styleText([\"yellow\", \"bold\"], \" UPDATE FINISHED WITH ERRORS\"));\n console.log(styleText(\"gray\", ` Success: ${successCount}, Failed: ${failCount}`));\n }\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n })\n .run()\n .catch((err: Error) => {\n console.error(err);\n process.exit(1);\n });\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -4,6 +4,10 @@ interface Patch {
4
4
  description: string;
5
5
  pattern: Buffer;
6
6
  replacement: Buffer;
7
+ variants?: Array<{
8
+ pattern: Buffer;
9
+ replacement: Buffer;
10
+ }>;
7
11
  }
8
12
  interface PatchOptions {
9
13
  inputPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/patcher.ts","../src/alias.ts"],"sourcesContent":[],"mappings":";UAKiB,KAAA;EAAA,IAAA,EAAA,MAAK;EAOL,WAAA,EAAA,MAAY;EASnB,OAAA,EAbC,MAaU;EAQJ,WAAA,EApBF,MAoBkB;AASjC;AAA0C,UA1BzB,YAAA,CA0ByB;EAAuB,SAAA,EAAA,MAAA;EAAR,UAAA,CAAA,EAAA,MAAA;EAAO,OAAA,EAvBrD,KAuBqD,EAAA;;;;ACsGhE;AAMA,UD7HU,WAAA,CC6HuB;EA6KX,IAAA,EAAA,MAAA;EA8HA,KAAA,EAAA,MAAA;EAiIL,SAAA,CAAA,EAAA,MAAA,EAAA;EAKK,OAAA,EAAA,OAAA;EA8LA,cAAA,CAAA,EAAA,OAAe;;UDpuBpB,gBAAA;;;WAGN;;;;;iBAMW,UAAA,UAAoB,eAAe,QAAQ;;;AAjChD,UCuIA,iBAAA,CDnIF;EAGE,SAAA,EAAA,MAAY;EASnB,UAAA,EAAA,MAAW;EAQJ,SAAA,CAAA,EAAA,OAAgB;AASjC;AAA0C,iBC4GpB,WAAA,CD5GoB,iBAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,ECgHvC,ODhHuC,CCgH/B,iBDhH+B,CAAA;AAAuB,iBCyR3C,WAAA,CDzR2C,SAAA,EAAA,MAAA,CAAA,ECyRX,ODzRW,CAAA,IAAA,CAAA;AAAR,iBCuZnC,WAAA,CAAA,CDvZmC,ECuZpB,ODvZoB,CAAA,IAAA,CAAA;AAAO,UCwhB/C,qBAAA,CDxhB+C;;;;ACsG/C,iBAubK,eAAA,CAvbY,iBAAA,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EA2b/B,OA3b+B,CA2bvB,qBA3buB,CAAA;iBAqnBZ,eAAA,wBAAuC"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/patcher.ts","../src/alias.ts"],"sourcesContent":[],"mappings":";UAKiB,KAAA;EAAA,IAAA,EAAA,MAAK;EAGX,WAAA,EAAA,MAAA;EACI,OAAA,EADJ,MACI;EAEF,WAAA,EAFE,MAEF;EACI,QAAA,CAAA,EAFJ,KAEI,CAAA;IAFJ,OAAA,EACA,MADA;IAAK,WAAA,EAED,MAFC;EAMD,CAAA,CAAA;AAOhB;AAUgB,UAjBA,YAAA,CAiBgB;EASX,SAAA,EAAA,MAAU;EAAU,UAAA,CAAA,EAAA,MAAA;EAAuB,OAAA,EAvBtD,KAuBsD,EAAA;EAAR,MAAA,CAAA,EAAA,OAAA;EAAO,MAAA,CAAA,EAAA,OAAA;;;UAjBtD,WAAA;ECmHO,IAAA,EAAA,MAAA;EAMK,KAAA,EAAA,MAAA;EA6KA,SAAA,CAAA,EAAA,MAAW,EAAA;EA8HX,OAAA,EAAA,OAAW;EAiIhB,cAAA,CAAA,EAAA,OAAqB;AAKtC;AA8LsB,UDhuBL,gBAAA,CCguB4C;;;WD7tBlD;;;;;iBAMW,UAAA,UAAoB,eAAe,QAAQ;;;AArChD,UCuIA,iBAAA,CDvIK;EAGX,SAAA,EAAA,MAAA;EACI,UAAA,EAAA,MAAA;EAEF,SAAA,CAAA,EAAA,OAAA;;AADA,iBCwIS,WAAA,CDxIT,iBAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EC4IV,OD5IU,CC4IF,iBD5IE,CAAA;AAAK,iBCqTI,WAAA,CDrTJ,SAAA,EAAA,MAAA,CAAA,ECqToC,ODrTpC,CAAA,IAAA,CAAA;AAMD,iBC6aK,WAAA,CAAA,CD1aN,EC0aqB,OD1arB,CAAA,IAAA,CAAA;AAMN,UCqiBO,qBAAA,CDriBI;EAQJ,YAAA,EAAA,MAAgB;EASX,UAAA,EAAA,MAAU;;AAAiC,iBCyhB3C,eAAA,CDzhB2C,iBAAA,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EC6hB9D,OD7hB8D,CC6hBtD,qBD7hBsD,CAAA;ACwG3C,iBA+mBA,eAAA,CA3mBX,YAAR,EAAA,MAAO,CAAA,EA2mBmD,OA3mBnD,CAAA,IAAA,CAAA"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as removeAlias, c as restoreOriginal, i as listAliases, m as patchDroid, n as createAlias, s as replaceOriginal } from "./alias-CX4QSelz.mjs";
1
+ import { a as removeAlias, c as restoreOriginal, i as listAliases, m as patchDroid, n as createAlias, s as replaceOriginal } from "./alias-CEe6DN63.mjs";
2
2
 
3
3
  export { createAlias, listAliases, patchDroid, removeAlias, replaceOriginal, restoreOriginal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "droid-patch",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "CLI tool to patch droid binary with various modifications",
5
5
  "homepage": "https://github.com/kingsword09/droid-patch#readme",
6
6
  "bugs": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"alias-CX4QSelz.mjs","names":["results: PatchResult[]","positions: number[]","metaList: AliasMetadata[]","applied: string[]","exportLine: string","binaryDest","aliases: AliasInfo[]","matchingAliases: string[]"],"sources":["../src/patcher.ts","../src/metadata.ts","../src/alias.ts"],"sourcesContent":["import { readFile, writeFile, copyFile, chmod, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { styleText } from \"node:util\";\n\nexport interface Patch {\n name: string;\n description: string;\n pattern: Buffer;\n replacement: Buffer;\n}\n\nexport interface PatchOptions {\n inputPath: string;\n outputPath?: string;\n patches: Patch[];\n dryRun?: boolean;\n backup?: boolean;\n verbose?: boolean;\n}\n\ninterface PatchResult {\n name: string;\n found: number;\n positions?: number[];\n success: boolean;\n alreadyPatched?: boolean;\n}\n\nexport interface PatchDroidResult {\n success: boolean;\n dryRun?: boolean;\n results: PatchResult[];\n outputPath?: string;\n noPatchNeeded?: boolean;\n patchedCount?: number;\n}\n\nexport async function patchDroid(options: PatchOptions): Promise<PatchDroidResult> {\n const {\n inputPath,\n outputPath,\n patches,\n dryRun = false,\n backup = true,\n verbose = false,\n } = options;\n\n const finalOutputPath = outputPath || `${inputPath}.patched`;\n\n if (!existsSync(inputPath)) {\n throw new Error(`Binary not found: ${inputPath}`);\n }\n\n const stats = await stat(inputPath);\n const fileSizeMB = (stats.size / (1024 * 1024)).toFixed(2);\n\n console.log(styleText(\"white\", `[*] Reading binary: ${styleText(\"cyan\", inputPath)}`));\n console.log(styleText(\"white\", `[*] File size: ${styleText(\"cyan\", fileSizeMB)} MB`));\n console.log();\n\n const data = await readFile(inputPath);\n const buffer = Buffer.from(data);\n\n // Use a working buffer that gets updated after each patch application\n // This ensures later patches search against the already-patched content\n const workingBuffer = Buffer.from(buffer);\n\n const results: PatchResult[] = [];\n\n for (const patch of patches) {\n console.log(styleText(\"white\", `[*] Checking patch: ${styleText(\"yellow\", patch.name)}`));\n console.log(styleText(\"gray\", ` ${patch.description}`));\n\n // Search in the working buffer (which may have earlier patches applied)\n const positions = findAllPositions(workingBuffer, patch.pattern);\n\n if (positions.length === 0) {\n console.log(styleText(\"yellow\", ` ! Pattern not found - may already be patched`));\n results.push({\n name: patch.name,\n found: 0,\n success: false,\n alreadyPatched: workingBuffer.includes(patch.replacement),\n });\n\n const replacementPositions = findAllPositions(workingBuffer, patch.replacement);\n if (replacementPositions.length > 0) {\n console.log(\n styleText(\n \"blue\",\n ` ✓ Found ${replacementPositions.length} occurrences of patched pattern`,\n ),\n );\n console.log(styleText(\"blue\", ` ✓ Binary appears to be already patched`));\n results[results.length - 1].alreadyPatched = true;\n results[results.length - 1].success = true;\n }\n continue;\n }\n\n console.log(styleText(\"green\", ` ✓ Found ${positions.length} occurrences`));\n\n if (verbose) {\n for (const pos of positions.slice(0, 5)) {\n const context = getContext(workingBuffer, pos, patch.pattern.length, 25);\n console.log(\n styleText(\"gray\", ` @ 0x${pos.toString(16).padStart(8, \"0\")}: ...${context}...`),\n );\n }\n if (positions.length > 5) {\n console.log(styleText(\"gray\", ` ... and ${positions.length - 5} more`));\n }\n }\n\n // Apply patch immediately to working buffer so later patches see updated content\n if (!dryRun) {\n for (const pos of positions) {\n patch.replacement.copy(workingBuffer, pos);\n }\n }\n\n results.push({\n name: patch.name,\n found: positions.length,\n positions,\n success: true,\n });\n }\n\n console.log();\n\n if (dryRun) {\n console.log(styleText(\"blue\", \"─\".repeat(60)));\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN RESULTS\"));\n console.log(styleText(\"blue\", \"─\".repeat(60)));\n console.log();\n\n for (const result of results) {\n if (result.alreadyPatched) {\n console.log(styleText(\"blue\", ` [✓] ${result.name}: Already patched`));\n } else if (result.found > 0) {\n console.log(\n styleText(\"green\", ` [✓] ${result.name}: ${result.found} occurrences will be patched`),\n );\n } else {\n console.log(styleText(\"yellow\", ` [!] ${result.name}: Pattern not found`));\n }\n }\n\n return {\n success: results.every((r) => r.success || r.alreadyPatched),\n dryRun: true,\n results,\n };\n }\n\n const patchesNeeded = results.filter((r) => r.found > 0 && !r.alreadyPatched);\n\n if (patchesNeeded.length === 0) {\n const allPatched = results.every((r) => r.alreadyPatched);\n if (allPatched) {\n console.log(styleText(\"blue\", \"[*] All patches already applied. Binary is up to date.\"));\n return {\n success: true,\n outputPath: inputPath,\n results,\n noPatchNeeded: true,\n };\n }\n console.log(styleText(\"yellow\", \"[!] No patches could be applied.\"));\n return { success: false, results };\n }\n\n if (backup) {\n const backupPath = `${inputPath}.backup`;\n if (!existsSync(backupPath)) {\n await copyFile(inputPath, backupPath);\n console.log(styleText(\"white\", `[*] Created backup: ${styleText(\"cyan\", backupPath)}`));\n } else {\n console.log(styleText(\"gray\", `[*] Backup already exists: ${backupPath}`));\n }\n }\n\n console.log(styleText(\"white\", \"[*] Applying patches...\"));\n // Patches have already been applied to workingBuffer during the check phase\n // Count total patches applied\n const totalPatched = results.reduce((sum, r) => sum + (r.positions?.length || 0), 0);\n\n console.log(styleText(\"green\", `[*] Applied ${totalPatched} patches`));\n\n await writeFile(finalOutputPath, workingBuffer);\n console.log(\n styleText(\"white\", `[*] Patched binary saved: ${styleText(\"cyan\", finalOutputPath)}`),\n );\n\n await chmod(finalOutputPath, 0o755);\n console.log(styleText(\"gray\", \"[*] Set executable permission\"));\n\n console.log();\n console.log(styleText(\"white\", \"[*] Verifying patches...\"));\n const verifyBuffer = await readFile(finalOutputPath);\n\n let allVerified = true;\n for (const patch of patches) {\n const oldCount = findAllPositions(verifyBuffer, patch.pattern).length;\n const newCount = findAllPositions(verifyBuffer, patch.replacement).length;\n\n if (oldCount === 0) {\n console.log(styleText(\"green\", ` ✓ ${patch.name}: Verified (${newCount} patched)`));\n } else {\n console.log(styleText(\"red\", ` ✗ ${patch.name}: ${oldCount} occurrences not patched`));\n allVerified = false;\n }\n }\n\n if (allVerified) {\n console.log();\n console.log(styleText(\"green\", \"[+] All patches verified successfully!\"));\n }\n\n if (process.platform === \"darwin\") {\n console.log();\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${finalOutputPath}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(styleText(\"yellow\", \"[!] Could not re-sign binary\"));\n console.log(\n styleText(\n \"gray\",\n ` You may need to run: codesign --force --deep --sign - ${finalOutputPath}`,\n ),\n );\n }\n\n try {\n execSync(`xattr -cr \"${finalOutputPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n return {\n success: allVerified,\n outputPath: finalOutputPath,\n results,\n patchedCount: totalPatched,\n };\n}\n\nfunction findAllPositions(buffer: Buffer, pattern: Buffer): number[] {\n const positions: number[] = [];\n let pos = 0;\n\n while (true) {\n pos = buffer.indexOf(pattern, pos);\n if (pos === -1) break;\n positions.push(pos);\n pos += pattern.length;\n }\n\n return positions;\n}\n\nfunction getContext(\n buffer: Buffer,\n position: number,\n patternLength: number,\n contextSize: number,\n): string {\n const start = Math.max(0, position - contextSize);\n const end = Math.min(buffer.length, position + patternLength + contextSize);\n const slice = buffer.slice(start, end);\n\n let str = \"\";\n for (let i = 0; i < slice.length; i++) {\n const c = slice[i];\n if (c >= 32 && c < 127) {\n str += String.fromCharCode(c);\n } else {\n str += \".\";\n }\n }\n return str;\n}\n","/**\n * Alias Metadata Management\n *\n * Stores and retrieves metadata about created aliases, including\n * which patches were applied. This enables the `update` command\n * to re-apply the same patches when the original droid binary is updated.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Metadata structure for an alias\n */\nexport interface AliasMetadata {\n /** Alias name */\n name: string;\n /** ISO timestamp when created */\n createdAt: string;\n /** ISO timestamp when last updated */\n updatedAt: string;\n /** Path to the original droid binary used for patching */\n originalBinaryPath: string;\n /** Path where the alias symlink was created */\n aliasPath?: string;\n /** droid-patch version used to create this alias */\n droidPatchVersion?: string;\n /** droid binary version */\n droidVersion?: string;\n /** Patches that were applied */\n patches: {\n isCustom: boolean;\n skipLogin: boolean;\n /** API base URL for binary patching or websearch forward target */\n apiBase: string | null;\n /** Whether websearch is enabled (external providers mode) */\n websearch: boolean;\n /** Whether websearch-proxy is enabled (native provider mode) */\n websearchProxy?: boolean;\n /** @deprecated Old proxy field, kept for backward compatibility */\n proxy?: string | null;\n reasoningEffort: boolean;\n /** Whether telemetry/Sentry is disabled */\n noTelemetry?: boolean;\n /** Standalone mode: mock non-LLM Factory APIs */\n standalone?: boolean;\n /** Hardcode autonomy mode to auto-high */\n autoHigh?: boolean;\n };\n}\n\n// Directory for storing metadata files\nconst META_DIR = join(homedir(), \".droid-patch\", \"meta\");\n\n/**\n * Ensure metadata directory exists\n */\nasync function ensureMetaDir(): Promise<void> {\n if (!existsSync(META_DIR)) {\n await mkdir(META_DIR, { recursive: true });\n }\n}\n\n/**\n * Get the path to a metadata file for an alias\n */\nfunction getMetaPath(aliasName: string): string {\n return join(META_DIR, `${aliasName}.json`);\n}\n\n/**\n * Save alias metadata to disk\n */\nexport async function saveAliasMetadata(meta: AliasMetadata): Promise<void> {\n await ensureMetaDir();\n const metaPath = getMetaPath(meta.name);\n await writeFile(metaPath, JSON.stringify(meta, null, 2));\n}\n\n/**\n * Load alias metadata from disk\n * Returns null if metadata doesn't exist\n */\nexport async function loadAliasMetadata(aliasName: string): Promise<AliasMetadata | null> {\n const metaPath = getMetaPath(aliasName);\n if (!existsSync(metaPath)) {\n return null;\n }\n try {\n const content = await readFile(metaPath, \"utf-8\");\n return JSON.parse(content) as AliasMetadata;\n } catch {\n return null;\n }\n}\n\n/**\n * List all alias metadata\n */\nexport async function listAllMetadata(): Promise<AliasMetadata[]> {\n await ensureMetaDir();\n\n const files = await readdir(META_DIR);\n const metaList: AliasMetadata[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".json\")) continue;\n\n const aliasName = file.replace(/\\.json$/, \"\");\n const meta = await loadAliasMetadata(aliasName);\n if (meta) {\n metaList.push(meta);\n }\n }\n\n return metaList;\n}\n\n/**\n * Remove alias metadata\n */\nexport async function removeAliasMetadata(aliasName: string): Promise<boolean> {\n const metaPath = getMetaPath(aliasName);\n if (!existsSync(metaPath)) {\n return false;\n }\n try {\n await unlink(metaPath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Create a new metadata object with current timestamp\n */\nexport function createMetadata(\n name: string,\n originalBinaryPath: string,\n patches: AliasMetadata[\"patches\"],\n options?: {\n droidPatchVersion?: string;\n droidVersion?: string;\n aliasPath?: string;\n },\n): AliasMetadata {\n const now = new Date().toISOString();\n return {\n name,\n createdAt: now,\n updatedAt: now,\n originalBinaryPath,\n aliasPath: options?.aliasPath,\n droidPatchVersion: options?.droidPatchVersion,\n droidVersion: options?.droidVersion,\n patches,\n };\n}\n\n/**\n * Format patches for display\n */\nexport function formatPatches(patches: AliasMetadata[\"patches\"]): string {\n const applied: string[] = [];\n if (patches.isCustom) applied.push(\"isCustom\");\n if (patches.skipLogin) applied.push(\"skipLogin\");\n // Show apiBase only when not using websearch (binary patch mode)\n if (patches.apiBase && !patches.websearch && !patches.websearchProxy)\n applied.push(`apiBase(${patches.apiBase})`);\n // Show websearch with optional custom target (external providers mode)\n if (patches.websearch) {\n const target = patches.apiBase || \"api.factory.ai\";\n applied.push(`websearch(${target})`);\n }\n // Show websearchProxy (native provider mode)\n if (patches.websearchProxy) {\n applied.push(\"websearchProxy(native)\");\n }\n // Support old proxy field for backward compatibility\n if (patches.proxy && !patches.websearch && !patches.websearchProxy)\n applied.push(`websearch(${patches.proxy})`);\n if (patches.reasoningEffort) applied.push(\"reasoningEffort\");\n if (patches.noTelemetry) applied.push(\"noTelemetry\");\n if (patches.standalone) applied.push(\"standalone\");\n if (patches.autoHigh) applied.push(\"autoHigh\");\n return applied.length > 0 ? applied.join(\", \") : \"(none)\";\n}\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n unlinkSync,\n lstatSync,\n readFileSync,\n appendFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { symlink, readlink, unlink, copyFile, chmod } from \"node:fs/promises\";\nimport { join, basename, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { styleText } from \"node:util\";\nimport { removeAliasMetadata, loadAliasMetadata, formatPatches } from \"./metadata.ts\";\n\nconst DROID_PATCH_DIR = join(homedir(), \".droid-patch\");\nconst ALIASES_DIR = join(DROID_PATCH_DIR, \"aliases\");\nconst BINS_DIR = join(DROID_PATCH_DIR, \"bins\");\n\nconst COMMON_PATH_DIRS = [\n join(homedir(), \".local/bin\"),\n join(homedir(), \"bin\"),\n join(homedir(), \".bin\"),\n \"/opt/homebrew/bin\",\n \"/usr/local/bin\",\n join(homedir(), \".npm-global/bin\"),\n join(homedir(), \".npm/bin\"),\n join(homedir(), \".pnpm-global/bin\"),\n join(homedir(), \".yarn/bin\"),\n join(homedir(), \".config/yarn/global/node_modules/.bin\"),\n join(homedir(), \".cargo/bin\"),\n join(homedir(), \"go/bin\"),\n join(homedir(), \".deno/bin\"),\n join(homedir(), \".bun/bin\"),\n join(homedir(), \".local/share/mise/shims\"),\n join(homedir(), \".asdf/shims\"),\n join(homedir(), \".nvm/current/bin\"),\n join(homedir(), \".volta/bin\"),\n join(homedir(), \".fnm/current/bin\"),\n];\n\nfunction ensureDirectories(): void {\n if (!existsSync(DROID_PATCH_DIR)) {\n mkdirSync(DROID_PATCH_DIR, { recursive: true });\n }\n if (!existsSync(ALIASES_DIR)) {\n mkdirSync(ALIASES_DIR, { recursive: true });\n }\n if (!existsSync(BINS_DIR)) {\n mkdirSync(BINS_DIR, { recursive: true });\n }\n}\n\nfunction checkPathInclusion(): boolean {\n const pathEnv = process.env.PATH || \"\";\n return pathEnv.split(\":\").includes(ALIASES_DIR);\n}\n\nexport function findWritablePathDir(): string | null {\n const pathEnv = process.env.PATH || \"\";\n const pathDirs = pathEnv.split(\":\");\n\n for (const dir of COMMON_PATH_DIRS) {\n if (pathDirs.includes(dir)) {\n try {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const testFile = join(dir, `.droid-patch-test-${Date.now()}`);\n writeFileSync(testFile, \"\");\n unlinkSync(testFile);\n return dir;\n } catch {\n continue;\n }\n }\n }\n\n return null;\n}\n\nfunction getShellConfigPath(): string {\n const shell = process.env.SHELL || \"/bin/bash\";\n const shellName = basename(shell);\n\n switch (shellName) {\n case \"zsh\":\n return join(homedir(), \".zshrc\");\n case \"bash\": {\n const bashProfile = join(homedir(), \".bash_profile\");\n if (existsSync(bashProfile)) return bashProfile;\n return join(homedir(), \".bashrc\");\n }\n case \"fish\":\n return join(homedir(), \".config/fish/config.fish\");\n default:\n return join(homedir(), \".profile\");\n }\n}\n\nfunction isPathConfigured(shellConfigPath: string): boolean {\n if (!existsSync(shellConfigPath)) {\n return false;\n }\n\n try {\n const content = readFileSync(shellConfigPath, \"utf-8\");\n return content.includes(\".droid-patch/aliases\") || content.includes(\"droid-patch/aliases\");\n } catch {\n return false;\n }\n}\n\nfunction addPathToShellConfig(shellConfigPath: string, verbose = false): boolean {\n const shell = process.env.SHELL || \"/bin/bash\";\n const shellName = basename(shell);\n\n let exportLine: string;\n if (shellName === \"fish\") {\n exportLine = `\\n# Added by droid-patch\\nfish_add_path \"${ALIASES_DIR}\"\\n`;\n } else {\n exportLine = `\\n# Added by droid-patch\\nexport PATH=\"${ALIASES_DIR}:$PATH\"\\n`;\n }\n\n try {\n appendFileSync(shellConfigPath, exportLine);\n if (verbose) {\n console.log(styleText(\"gray\", ` Added PATH export to: ${shellConfigPath}`));\n }\n return true;\n } catch (error) {\n console.log(\n styleText(\"yellow\", `[!] Could not write to ${shellConfigPath}: ${(error as Error).message}`),\n );\n return false;\n }\n}\n\nexport interface CreateAliasResult {\n aliasPath: string;\n binaryPath: string;\n immediate?: boolean;\n}\n\nexport async function createAlias(\n patchedBinaryPath: string,\n aliasName: string,\n verbose = false,\n): Promise<CreateAliasResult> {\n ensureDirectories();\n\n console.log(styleText(\"white\", `[*] Creating alias: ${styleText(\"cyan\", aliasName)}`));\n\n const writablePathDir = findWritablePathDir();\n\n if (writablePathDir) {\n const targetPath = join(writablePathDir, aliasName);\n const binaryDest = join(BINS_DIR, `${aliasName}-patched`);\n await copyFile(patchedBinaryPath, binaryDest);\n await chmod(binaryDest, 0o755);\n\n if (verbose) {\n console.log(styleText(\"gray\", ` Stored binary: ${binaryDest}`));\n }\n\n if (existsSync(targetPath)) {\n await unlink(targetPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing: ${targetPath}`));\n }\n }\n\n await symlink(binaryDest, targetPath);\n\n if (process.platform === \"darwin\") {\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${binaryDest}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(styleText(\"yellow\", \"[!] Could not re-sign binary\"));\n }\n\n try {\n execSync(`xattr -cr \"${binaryDest}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log(styleText(\"green\", `[*] Created: ${targetPath} -> ${binaryDest}`));\n console.log();\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" ALIAS READY - NO ACTION REQUIRED!\"));\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log();\n console.log(\n styleText(\n \"white\",\n `The alias \"${styleText([\"cyan\", \"bold\"], aliasName)}\" is now available in ALL terminals.`,\n ),\n );\n console.log(styleText(\"gray\", `(Installed to: ${writablePathDir})`));\n\n return {\n aliasPath: targetPath,\n binaryPath: binaryDest,\n immediate: true,\n };\n }\n\n console.log(styleText(\"yellow\", \"[*] No writable PATH directory found, using fallback...\"));\n\n const binaryDest = join(BINS_DIR, `${aliasName}-patched`);\n await copyFile(patchedBinaryPath, binaryDest);\n await chmod(binaryDest, 0o755);\n\n if (verbose) {\n console.log(styleText(\"gray\", ` Copied binary to: ${binaryDest}`));\n }\n\n if (process.platform === \"darwin\") {\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${binaryDest}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(\n styleText(\"yellow\", \"[!] Could not re-sign binary. You may need to do this manually:\"),\n );\n console.log(styleText(\"gray\", ` codesign --force --deep --sign - \"${binaryDest}\"`));\n }\n\n try {\n execSync(`xattr -cr \"${binaryDest}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n const symlinkPath = join(ALIASES_DIR, aliasName);\n\n if (existsSync(symlinkPath)) {\n await unlink(symlinkPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing symlink`));\n }\n }\n\n await symlink(binaryDest, symlinkPath);\n await chmod(symlinkPath, 0o755);\n\n console.log(styleText(\"green\", `[*] Created symlink: ${symlinkPath} -> ${binaryDest}`));\n\n const shellConfig = getShellConfigPath();\n\n if (!checkPathInclusion()) {\n if (!isPathConfigured(shellConfig)) {\n console.log(styleText(\"white\", `[*] Configuring PATH in ${shellConfig}...`));\n\n if (addPathToShellConfig(shellConfig, verbose)) {\n console.log(styleText(\"green\", `[*] PATH configured successfully!`));\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" ACTION REQUIRED\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"To use the alias in this terminal, run:\"));\n console.log();\n console.log(styleText(\"cyan\", ` source ${shellConfig}`));\n console.log();\n console.log(styleText(\"gray\", \"Or simply open a new terminal window.\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n } else {\n const exportLine = `export PATH=\"${ALIASES_DIR}:$PATH\"`;\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" Manual PATH Configuration Required\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"Add this line to your shell config:\"));\n console.log(styleText(\"cyan\", ` ${exportLine}`));\n console.log();\n console.log(styleText(\"gray\", `Shell config file: ${shellConfig}`));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already configured in ${shellConfig}`));\n console.log();\n console.log(\n styleText(\n \"yellow\",\n `Note: Run \\`source ${shellConfig}\\` or open a new terminal to use the alias.`,\n ),\n );\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already includes aliases directory`));\n console.log();\n console.log(\n styleText(\n \"green\",\n `You can now use \"${styleText([\"cyan\", \"bold\"], aliasName)}\" command directly!`,\n ),\n );\n }\n\n return {\n aliasPath: symlinkPath,\n binaryPath: binaryDest,\n };\n}\n\nexport async function removeAlias(aliasName: string): Promise<void> {\n console.log(styleText(\"white\", `[*] Removing alias: ${styleText(\"cyan\", aliasName)}`));\n\n let removed = false;\n\n // Check common PATH directories for symlinks\n for (const pathDir of COMMON_PATH_DIRS) {\n const pathSymlink = join(pathDir, aliasName);\n if (existsSync(pathSymlink)) {\n try {\n const stats = lstatSync(pathSymlink);\n if (stats.isSymbolicLink()) {\n const target = await readlink(pathSymlink);\n // Support regular aliases, old websearch wrappers, and new proxy wrappers\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n await unlink(pathSymlink);\n console.log(styleText(\"green\", ` Removed: ${pathSymlink}`));\n removed = true;\n }\n }\n } catch {\n // Ignore\n }\n }\n }\n\n // Check aliases directory\n const symlinkPath = join(ALIASES_DIR, aliasName);\n if (existsSync(symlinkPath)) {\n await unlink(symlinkPath);\n console.log(styleText(\"green\", ` Removed: ${symlinkPath}`));\n removed = true;\n }\n\n // Remove binary if exists\n const binaryPath = join(BINS_DIR, `${aliasName}-patched`);\n if (existsSync(binaryPath)) {\n await unlink(binaryPath);\n console.log(styleText(\"green\", ` Removed binary: ${binaryPath}`));\n removed = true;\n }\n\n // Remove new proxy wrapper and related files if exist\n const proxyDir = join(DROID_PATCH_DIR, \"proxy\");\n const proxyWrapperPath = join(proxyDir, aliasName);\n const proxyScriptPath = join(proxyDir, `${aliasName}-proxy.js`);\n\n if (existsSync(proxyWrapperPath)) {\n await unlink(proxyWrapperPath);\n console.log(styleText(\"green\", ` Removed wrapper: ${proxyWrapperPath}`));\n removed = true;\n }\n\n if (existsSync(proxyScriptPath)) {\n await unlink(proxyScriptPath);\n console.log(styleText(\"green\", ` Removed proxy script: ${proxyScriptPath}`));\n removed = true;\n }\n\n // Remove old websearch wrapper and related files if exist (backward compatibility)\n const websearchDir = join(DROID_PATCH_DIR, \"websearch\");\n const wrapperPath = join(websearchDir, aliasName);\n const oldProxyPath = join(websearchDir, `${aliasName}-proxy.js`);\n const preloadPath = join(websearchDir, `${aliasName}-preload.js`);\n\n if (existsSync(wrapperPath)) {\n await unlink(wrapperPath);\n console.log(styleText(\"green\", ` Removed legacy wrapper: ${wrapperPath}`));\n removed = true;\n }\n\n if (existsSync(oldProxyPath)) {\n await unlink(oldProxyPath);\n console.log(styleText(\"green\", ` Removed legacy proxy: ${oldProxyPath}`));\n removed = true;\n }\n\n if (existsSync(preloadPath)) {\n await unlink(preloadPath);\n console.log(styleText(\"green\", ` Removed legacy preload: ${preloadPath}`));\n removed = true;\n }\n\n // Remove statusline wrapper and monitor script if exist\n const statuslineDir = join(DROID_PATCH_DIR, \"statusline\");\n const statuslineWrapperPath = join(statuslineDir, aliasName);\n const statuslineMonitorPath = join(statuslineDir, `${aliasName}-statusline.js`);\n const statuslineSessionsPath = join(statuslineDir, `${aliasName}-sessions.js`);\n\n if (existsSync(statuslineWrapperPath)) {\n await unlink(statuslineWrapperPath);\n console.log(styleText(\"green\", ` Removed statusline wrapper: ${statuslineWrapperPath}`));\n removed = true;\n }\n\n if (existsSync(statuslineMonitorPath)) {\n await unlink(statuslineMonitorPath);\n console.log(styleText(\"green\", ` Removed statusline monitor: ${statuslineMonitorPath}`));\n removed = true;\n }\n\n if (existsSync(statuslineSessionsPath)) {\n await unlink(statuslineSessionsPath);\n console.log(styleText(\"green\", ` Removed sessions browser: ${statuslineSessionsPath}`));\n removed = true;\n }\n\n // Remove metadata\n const metaRemoved = await removeAliasMetadata(aliasName);\n if (metaRemoved) {\n console.log(styleText(\"green\", ` Removed metadata`));\n removed = true;\n }\n\n if (!removed) {\n console.log(styleText(\"yellow\", ` Alias \"${aliasName}\" not found`));\n } else {\n console.log(styleText(\"green\", `[*] Alias \"${aliasName}\" removed successfully`));\n }\n}\n\nexport async function listAliases(): Promise<void> {\n ensureDirectories();\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid-Patch Aliases\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n interface AliasInfo {\n name: string;\n target: string;\n location: string;\n immediate: boolean;\n }\n\n const aliases: AliasInfo[] = [];\n\n for (const pathDir of COMMON_PATH_DIRS) {\n if (!existsSync(pathDir)) continue;\n\n try {\n const files = readdirSync(pathDir);\n for (const file of files) {\n const fullPath = join(pathDir, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n // Support regular aliases, old websearch wrappers, and new proxy wrappers\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliases.push({\n name: file,\n target,\n location: pathDir,\n immediate: true,\n });\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n try {\n const files = readdirSync(ALIASES_DIR);\n\n for (const file of files) {\n const fullPath = join(ALIASES_DIR, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n if (!aliases.find((a) => a.name === file)) {\n aliases.push({\n name: file,\n target,\n location: ALIASES_DIR,\n immediate: false,\n });\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n\n if (aliases.length === 0) {\n console.log(styleText(\"gray\", \" No aliases configured.\"));\n console.log();\n console.log(styleText(\"gray\", \" Create one with: npx droid-patch --is-custom <alias-name>\"));\n } else {\n console.log(styleText(\"white\", ` Found ${aliases.length} alias(es):`));\n console.log();\n for (const alias of aliases) {\n const status = alias.immediate\n ? styleText(\"green\", \"✓ immediate\")\n : styleText(\"yellow\", \"requires source\");\n console.log(styleText(\"green\", ` • ${styleText([\"cyan\", \"bold\"], alias.name)} [${status}]`));\n console.log(styleText(\"gray\", ` → ${alias.target}`));\n\n // Load and display metadata\n const meta = await loadAliasMetadata(alias.name);\n if (meta) {\n // Version info\n const patchVer = meta.droidPatchVersion\n ? `droid-patch@${meta.droidPatchVersion}`\n : \"unknown\";\n const droidVer = meta.droidVersion ? `droid@${meta.droidVersion}` : \"unknown\";\n console.log(styleText(\"gray\", ` Versions: ${patchVer}, ${droidVer}`));\n\n // Flags/patches\n const flags = formatPatches(meta.patches);\n console.log(styleText(\"gray\", ` Flags: ${flags}`));\n\n // Created time\n if (meta.createdAt) {\n const date = new Date(meta.createdAt).toLocaleString();\n console.log(styleText(\"gray\", ` Created: ${date}`));\n }\n } else {\n console.log(styleText(\"yellow\", ` (no metadata - created by older version)`));\n }\n console.log();\n }\n }\n\n console.log();\n console.log(styleText(\"gray\", ` Aliases directory: ${ALIASES_DIR}`));\n console.log(\n styleText(\n \"gray\",\n ` PATH configured: ${checkPathInclusion() ? styleText(\"green\", \"Yes\") : styleText(\"yellow\", \"No\")}`,\n ),\n );\n console.log();\n}\n\nexport interface ReplaceOriginalResult {\n originalPath: string;\n backupPath: string;\n}\n\nexport async function replaceOriginal(\n patchedBinaryPath: string,\n originalPath: string,\n verbose = false,\n): Promise<ReplaceOriginalResult> {\n ensureDirectories();\n\n console.log(\n styleText(\"white\", `[*] Replacing original binary: ${styleText(\"cyan\", originalPath)}`),\n );\n\n const latestBackupPath = join(BINS_DIR, \"droid-original-latest\");\n\n if (!existsSync(latestBackupPath)) {\n await copyFile(originalPath, latestBackupPath);\n console.log(styleText(\"green\", `[*] Created backup: ${latestBackupPath}`));\n } else {\n if (verbose) {\n console.log(styleText(\"gray\", ` Backup already exists: ${latestBackupPath}`));\n }\n }\n\n await copyFile(patchedBinaryPath, originalPath);\n await chmod(originalPath, 0o755);\n console.log(styleText(\"green\", `[*] Replaced: ${originalPath}`));\n\n if (process.platform === \"darwin\") {\n try {\n console.log(styleText(\"gray\", \"[*] Re-signing binary for macOS...\"));\n execSync(`codesign --force --deep --sign - \"${originalPath}\"`, {\n stdio: \"pipe\",\n });\n console.log(styleText(\"green\", \"[*] Binary re-signed successfully\"));\n } catch {\n console.log(styleText(\"yellow\", \"[!] Could not re-sign binary. You may need to run:\"));\n console.log(styleText(\"gray\", ` codesign --force --deep --sign - \"${originalPath}\"`));\n }\n\n try {\n execSync(`xattr -cr \"${originalPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" REPLACEMENT COMPLETE\"));\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"The patched binary is now active in all terminals.\"));\n console.log(styleText(\"white\", \"No need to restart or source anything!\"));\n console.log();\n console.log(styleText(\"gray\", `To restore the original, run:`));\n console.log(styleText(\"cyan\", ` npx droid-patch restore`));\n\n return {\n originalPath,\n backupPath: latestBackupPath,\n };\n}\n\n/**\n * Create alias for wrapper script\n * Unlike createAlias, this function creates symlink pointing to wrapper script\n * Used for features like websearch that require preprocessing\n */\nexport async function createAliasForWrapper(\n wrapperPath: string,\n aliasName: string,\n verbose = false,\n): Promise<CreateAliasResult> {\n ensureDirectories();\n\n console.log(styleText(\"white\", `[*] Creating alias: ${styleText(\"cyan\", aliasName)}`));\n\n const writablePathDir = findWritablePathDir();\n\n if (writablePathDir) {\n const targetPath = join(writablePathDir, aliasName);\n\n if (verbose) {\n console.log(styleText(\"gray\", ` Wrapper: ${wrapperPath}`));\n }\n\n if (existsSync(targetPath)) {\n await unlink(targetPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing: ${targetPath}`));\n }\n }\n\n await symlink(wrapperPath, targetPath);\n\n console.log(styleText(\"green\", `[*] Created: ${targetPath} -> ${wrapperPath}`));\n console.log();\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" ALIAS READY - NO ACTION REQUIRED!\"));\n console.log(styleText(\"green\", \"─\".repeat(60)));\n console.log();\n console.log(\n styleText(\n \"white\",\n `The alias \"${styleText([\"cyan\", \"bold\"], aliasName)}\" is now available in ALL terminals.`,\n ),\n );\n console.log(styleText(\"gray\", `(Installed to: ${writablePathDir})`));\n\n return {\n aliasPath: targetPath,\n binaryPath: wrapperPath,\n immediate: true,\n };\n }\n\n // Fallback: use ~/.droid-patch/aliases\n console.log(styleText(\"yellow\", \"[*] No writable PATH directory found, using fallback...\"));\n\n const symlinkPath = join(ALIASES_DIR, aliasName);\n\n if (existsSync(symlinkPath)) {\n await unlink(symlinkPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Removed existing symlink`));\n }\n }\n\n await symlink(wrapperPath, symlinkPath);\n\n console.log(styleText(\"green\", `[*] Created symlink: ${symlinkPath} -> ${wrapperPath}`));\n\n const shellConfig = getShellConfigPath();\n\n if (!checkPathInclusion()) {\n if (!isPathConfigured(shellConfig)) {\n console.log(styleText(\"white\", `[*] Configuring PATH in ${shellConfig}...`));\n\n if (addPathToShellConfig(shellConfig, verbose)) {\n console.log(styleText(\"green\", `[*] PATH configured successfully!`));\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" ACTION REQUIRED\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"To use the alias in this terminal, run:\"));\n console.log();\n console.log(styleText(\"cyan\", ` source ${shellConfig}`));\n console.log();\n console.log(styleText(\"gray\", \"Or simply open a new terminal window.\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n } else {\n const exportLine = `export PATH=\"${ALIASES_DIR}:$PATH\"`;\n console.log();\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log(styleText([\"yellow\", \"bold\"], \" Manual PATH Configuration Required\"));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", \"Add this line to your shell config:\"));\n console.log(styleText(\"cyan\", ` ${exportLine}`));\n console.log();\n console.log(styleText(\"gray\", `Shell config file: ${shellConfig}`));\n console.log(styleText(\"yellow\", \"─\".repeat(60)));\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already configured in ${shellConfig}`));\n console.log();\n console.log(\n styleText(\n \"yellow\",\n `Note: Run \\`source ${shellConfig}\\` or open a new terminal to use the alias.`,\n ),\n );\n }\n } else {\n console.log(styleText(\"green\", `[*] PATH already includes aliases directory`));\n console.log();\n console.log(\n styleText(\n \"green\",\n `You can now use \"${styleText([\"cyan\", \"bold\"], aliasName)}\" command directly!`,\n ),\n );\n }\n\n return {\n aliasPath: symlinkPath,\n binaryPath: wrapperPath,\n };\n}\n\nexport async function restoreOriginal(originalPath: string): Promise<void> {\n ensureDirectories();\n\n const latestBackupPath = join(BINS_DIR, \"droid-original-latest\");\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Restore Original Droid\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n if (!existsSync(latestBackupPath)) {\n const localBackup = `${originalPath}.backup`;\n if (existsSync(localBackup)) {\n console.log(styleText(\"white\", `[*] Found local backup: ${localBackup}`));\n console.log(styleText(\"white\", `[*] Restoring to: ${originalPath}`));\n\n await copyFile(localBackup, originalPath);\n await chmod(originalPath, 0o755);\n\n if (process.platform === \"darwin\") {\n try {\n execSync(`codesign --force --deep --sign - \"${originalPath}\"`, {\n stdio: \"pipe\",\n });\n execSync(`xattr -cr \"${originalPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" RESTORE COMPLETE\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"green\", \"Original droid binary has been restored from local backup.\"));\n return;\n }\n\n console.log(styleText(\"red\", \"[!] No backup found.\"));\n console.log(styleText(\"gray\", ` Checked: ${latestBackupPath}`));\n console.log(styleText(\"gray\", ` Checked: ${localBackup}`));\n console.log();\n console.log(styleText(\"gray\", \"If you have a manual backup, restore it with:\"));\n console.log(styleText(\"cyan\", ` cp /path/to/backup ${originalPath}`));\n return;\n }\n\n console.log(styleText(\"white\", `[*] Restoring from: ${latestBackupPath}`));\n console.log(styleText(\"white\", `[*] Restoring to: ${originalPath}`));\n\n const targetDir = dirname(originalPath);\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n }\n\n await copyFile(latestBackupPath, originalPath);\n await chmod(originalPath, 0o755);\n\n if (process.platform === \"darwin\") {\n try {\n execSync(`codesign --force --deep --sign - \"${originalPath}\"`, {\n stdio: \"pipe\",\n });\n execSync(`xattr -cr \"${originalPath}\"`, { stdio: \"pipe\" });\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" RESTORE COMPLETE\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"green\", \"Original droid binary has been restored.\"));\n console.log(styleText(\"green\", \"All terminals will now use the original version.\"));\n}\n\n/**\n * Filter options for removing aliases\n * Uses the same names as CLI options for consistency\n */\nexport type FilterFlag =\n | \"is-custom\"\n | \"skip-login\"\n | \"websearch\"\n | \"api-base\"\n | \"reasoning-effort\"\n | \"disable-telemetry\"\n | \"standalone\";\n\nexport interface RemoveFilterOptions {\n /** Remove aliases created by this droid-patch version */\n patchVersion?: string;\n /** Remove aliases for this droid version */\n droidVersion?: string;\n /** Remove aliases that have these flags enabled (all must match) */\n flags?: FilterFlag[];\n}\n\n/**\n * Remove aliases matching filter criteria\n */\nexport async function removeAliasesByFilter(filter: RemoveFilterOptions): Promise<void> {\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Remove Aliases by Filter\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Show filter criteria\n if (filter.patchVersion) {\n console.log(styleText(\"white\", ` Filter: droid-patch version = ${filter.patchVersion}`));\n }\n if (filter.droidVersion) {\n console.log(styleText(\"white\", ` Filter: droid version = ${filter.droidVersion}`));\n }\n if (filter.flags && filter.flags.length > 0) {\n console.log(styleText(\"white\", ` Filter: flags = ${filter.flags.join(\", \")}`));\n }\n console.log();\n\n // Collect all alias names\n const aliasNames = new Set<string>();\n\n // Check common PATH directories for symlinks\n for (const pathDir of COMMON_PATH_DIRS) {\n if (!existsSync(pathDir)) continue;\n\n try {\n const files = readdirSync(pathDir);\n for (const file of files) {\n const fullPath = join(pathDir, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasNames.add(file);\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n // Check aliases directory\n if (existsSync(ALIASES_DIR)) {\n try {\n const files = readdirSync(ALIASES_DIR);\n for (const file of files) {\n const fullPath = join(ALIASES_DIR, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n aliasNames.add(file);\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n // Filter aliases by metadata\n const matchingAliases: string[] = [];\n\n for (const aliasName of aliasNames) {\n const meta = await loadAliasMetadata(aliasName);\n\n // If no metadata, skip (can't filter without metadata)\n if (!meta) {\n continue;\n }\n\n let matches = true;\n\n // Check droid-patch version\n if (filter.patchVersion && meta.droidPatchVersion !== filter.patchVersion) {\n matches = false;\n }\n\n // Check droid version\n if (filter.droidVersion && meta.droidVersion !== filter.droidVersion) {\n matches = false;\n }\n\n // Check flags (all specified flags must match)\n if (filter.flags && filter.flags.length > 0) {\n const patches = meta.patches;\n for (const flag of filter.flags) {\n switch (flag) {\n case \"is-custom\":\n if (!patches.isCustom) matches = false;\n break;\n case \"skip-login\":\n if (!patches.skipLogin) matches = false;\n break;\n case \"websearch\":\n if (!patches.websearch) matches = false;\n break;\n case \"reasoning-effort\":\n if (!patches.reasoningEffort) matches = false;\n break;\n case \"api-base\":\n if (!patches.apiBase) matches = false;\n break;\n case \"disable-telemetry\":\n if (!patches.noTelemetry) matches = false;\n break;\n case \"standalone\":\n if (!patches.standalone) matches = false;\n break;\n }\n if (!matches) break;\n }\n }\n\n if (matches) {\n matchingAliases.push(aliasName);\n }\n }\n\n if (matchingAliases.length === 0) {\n console.log(styleText(\"yellow\", \" No aliases match the filter criteria.\"));\n console.log();\n return;\n }\n\n console.log(styleText(\"white\", ` Found ${matchingAliases.length} matching alias(es):`));\n for (const name of matchingAliases) {\n console.log(styleText(\"gray\", ` • ${name}`));\n }\n console.log();\n\n // Remove each matching alias\n for (const aliasName of matchingAliases) {\n await removeAlias(aliasName);\n console.log();\n }\n\n console.log(styleText(\"green\", `[*] Removed ${matchingAliases.length} alias(es)`));\n}\n\n/**\n * Clear all droid-patch aliases and related files\n */\nexport async function clearAllAliases(): Promise<void> {\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Clearing All Droid-Patch Data\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Collect all alias names\n const aliasNames = new Set<string>();\n\n // Check common PATH directories for symlinks\n for (const pathDir of COMMON_PATH_DIRS) {\n if (!existsSync(pathDir)) continue;\n\n try {\n const files = readdirSync(pathDir);\n for (const file of files) {\n const fullPath = join(pathDir, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(fullPath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/websearch\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasNames.add(file);\n }\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n // Check aliases directory\n if (existsSync(ALIASES_DIR)) {\n try {\n const files = readdirSync(ALIASES_DIR);\n for (const file of files) {\n const fullPath = join(ALIASES_DIR, file);\n try {\n const stats = lstatSync(fullPath);\n if (stats.isSymbolicLink()) {\n aliasNames.add(file);\n }\n } catch {\n // Ignore\n }\n }\n } catch {\n // Directory can't be read\n }\n }\n\n if (aliasNames.size === 0) {\n console.log(styleText(\"yellow\", \" No aliases found.\"));\n } else {\n console.log(styleText(\"white\", ` Found ${aliasNames.size} alias(es) to remove:`));\n for (const name of aliasNames) {\n console.log(styleText(\"gray\", ` • ${name}`));\n }\n console.log();\n\n // Remove each alias\n for (const aliasName of aliasNames) {\n await removeAlias(aliasName);\n console.log();\n }\n }\n\n // Clean up directories (including legacy files)\n console.log(styleText(\"white\", \" Cleaning up directories...\"));\n const dirsToClean = [\n join(DROID_PATCH_DIR, \"bins\"),\n join(DROID_PATCH_DIR, \"aliases\"),\n join(DROID_PATCH_DIR, \"proxy\"),\n join(DROID_PATCH_DIR, \"websearch\"),\n join(DROID_PATCH_DIR, \"statusline\"),\n ];\n\n for (const dir of dirsToClean) {\n if (existsSync(dir)) {\n try {\n const files = readdirSync(dir);\n for (const file of files) {\n const fullPath = join(dir, file);\n try {\n await unlink(fullPath);\n console.log(styleText(\"green\", ` Removed: ${fullPath}`));\n } catch {\n // Ignore\n }\n }\n } catch {\n // Ignore\n }\n }\n }\n\n // Clean up legacy temp files from old versions\n const legacyTempFiles = [\"/tmp/droid-search-proxy.pid\", \"/tmp/droid-search-proxy.log\"];\n\n for (const tempFile of legacyTempFiles) {\n if (existsSync(tempFile)) {\n try {\n await unlink(tempFile);\n console.log(styleText(\"green\", ` Removed legacy: ${tempFile}`));\n } catch {\n // Ignore\n }\n }\n }\n\n // Clean up temp port files (pattern: /tmp/droid-websearch-*.port)\n try {\n const tmpFiles = readdirSync(\"/tmp\");\n for (const file of tmpFiles) {\n if (file.startsWith(\"droid-websearch-\") && file.endsWith(\".port\")) {\n const fullPath = join(\"/tmp\", file);\n try {\n await unlink(fullPath);\n console.log(styleText(\"green\", ` Removed temp: ${fullPath}`));\n } catch {\n // Ignore\n }\n }\n // Also clean old droid-search-proxy-*.port files\n if (file.startsWith(\"droid-search-proxy-\") && file.endsWith(\".port\")) {\n const fullPath = join(\"/tmp\", file);\n try {\n await unlink(fullPath);\n console.log(styleText(\"green\", ` Removed legacy temp: ${fullPath}`));\n } catch {\n // Ignore\n }\n }\n }\n } catch {\n // Ignore\n }\n\n // Clean up metadata file\n const metadataFile = join(DROID_PATCH_DIR, \"metadata.json\");\n if (existsSync(metadataFile)) {\n try {\n await unlink(metadataFile);\n console.log(styleText(\"green\", ` Removed: ${metadataFile}`));\n } catch {\n // Ignore\n }\n }\n\n console.log();\n console.log(styleText(\"green\", \"[*] All droid-patch data cleared successfully\"));\n}\n"],"mappings":"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"}