droid-patch 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -3
- package/README.zh-CN.md +34 -0
- package/dist/{alias-DwnTg_u5.mjs → alias-hvk8y5gC.mjs} +280 -38
- package/dist/alias-hvk8y5gC.mjs.map +1 -0
- package/dist/cli.mjs +494 -29
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/alias-DwnTg_u5.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -74,6 +74,40 @@ npx droid-patch --skip-login -o /path/to/dir my-droid
|
|
|
74
74
|
| `--no-backup` | Skip creating backup of original binary |
|
|
75
75
|
| `-v, --verbose` | Enable verbose output |
|
|
76
76
|
|
|
77
|
+
### Manage Custom Models
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# List all custom models
|
|
81
|
+
npx droid-patch list-models
|
|
82
|
+
|
|
83
|
+
# Add model interactively
|
|
84
|
+
npx droid-patch add-model
|
|
85
|
+
|
|
86
|
+
# Add model via command line
|
|
87
|
+
npx droid-patch add-model \
|
|
88
|
+
-m "claude-sonnet-4-20250514" \
|
|
89
|
+
-n "Sonnet [proxy]" \
|
|
90
|
+
-u "http://127.0.0.1:20002/droid" \
|
|
91
|
+
-k "your-api-key" \
|
|
92
|
+
-p "anthropic"
|
|
93
|
+
|
|
94
|
+
# Insert model at specific position
|
|
95
|
+
npx droid-patch add-model -i 0 # Interactive, insert at position 0
|
|
96
|
+
|
|
97
|
+
# Remove model (supports index, ID, or displayName)
|
|
98
|
+
npx droid-patch remove-model 0 # By index
|
|
99
|
+
npx droid-patch remove-model "custom:Sonnet-[proxy]-1" # By ID
|
|
100
|
+
npx droid-patch remove-model "Sonnet [proxy]" # By display name
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Model ID Format**: `custom:{DisplayName}-{index}`
|
|
104
|
+
|
|
105
|
+
- Spaces in `DisplayName` are replaced with `-`
|
|
106
|
+
- `index` is the position in the array (starting from 0)
|
|
107
|
+
- Example: `displayName: "Opus [proxy]"` → `id: "custom:Opus-[proxy]-0"`
|
|
108
|
+
|
|
109
|
+
**Important**: When deleting or inserting models, subsequent model IDs are automatically updated (because index changes).
|
|
110
|
+
|
|
77
111
|
### Manage Aliases and Files
|
|
78
112
|
|
|
79
113
|
```bash
|
|
@@ -285,19 +319,25 @@ Configure your custom model in `~/.factory/settings.json`:
|
|
|
285
319
|
"customModels": [
|
|
286
320
|
{
|
|
287
321
|
"model": "claude-sonnet-4-20250514",
|
|
288
|
-
"id": "custom:
|
|
322
|
+
"id": "custom:Opus-[proxy]-0",
|
|
289
323
|
"baseUrl": "http://127.0.0.1:20002/droid",
|
|
290
324
|
"apiKey": "your-api-key",
|
|
291
|
-
"displayName": "
|
|
325
|
+
"displayName": "Opus [proxy]",
|
|
292
326
|
"provider": "anthropic"
|
|
293
327
|
}
|
|
294
328
|
],
|
|
295
329
|
"sessionDefaultSettings": {
|
|
296
|
-
"model": "custom:
|
|
330
|
+
"model": "custom:Opus-[proxy]-0"
|
|
297
331
|
}
|
|
298
332
|
}
|
|
299
333
|
```
|
|
300
334
|
|
|
335
|
+
**Important**: The `id` field must match the `displayName` pattern:
|
|
336
|
+
|
|
337
|
+
- Format: `custom:{DisplayName}-{index}` where spaces are replaced with `-`
|
|
338
|
+
- Example: `displayName: "Opus [proxy]"` → `id: "custom:Opus-[proxy]-0"`
|
|
339
|
+
- The trailing number (`-0`) is the index (starting from 0)
|
|
340
|
+
|
|
301
341
|
### `--reasoning-effort`
|
|
302
342
|
|
|
303
343
|
Enables reasoning effort control for custom models by patching the binary to:
|
package/README.zh-CN.md
CHANGED
|
@@ -74,6 +74,40 @@ npx droid-patch --skip-login -o /path/to/dir my-droid
|
|
|
74
74
|
| `--no-backup` | 跳过创建原始二进制文件的备份 |
|
|
75
75
|
| `-v, --verbose` | 启用详细输出 |
|
|
76
76
|
|
|
77
|
+
### 管理自定义模型
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# 列出所有自定义模型
|
|
81
|
+
npx droid-patch list-models
|
|
82
|
+
|
|
83
|
+
# 交互式添加模型
|
|
84
|
+
npx droid-patch add-model
|
|
85
|
+
|
|
86
|
+
# 命令行添加模型
|
|
87
|
+
npx droid-patch add-model \
|
|
88
|
+
-m "claude-sonnet-4-20250514" \
|
|
89
|
+
-n "Sonnet [proxy]" \
|
|
90
|
+
-u "http://127.0.0.1:20002/droid" \
|
|
91
|
+
-k "your-api-key" \
|
|
92
|
+
-p "anthropic"
|
|
93
|
+
|
|
94
|
+
# 在指定位置插入模型
|
|
95
|
+
npx droid-patch add-model -i 0 # 交互式,插入到位置 0
|
|
96
|
+
|
|
97
|
+
# 删除模型(支持 index、ID 或 displayName)
|
|
98
|
+
npx droid-patch remove-model 0 # 按索引
|
|
99
|
+
npx droid-patch remove-model "custom:Sonnet-[proxy]-1" # 按 ID
|
|
100
|
+
npx droid-patch remove-model "Sonnet [proxy]" # 按显示名称
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**模型 ID 格式**:`custom:{DisplayName}-{index}`
|
|
104
|
+
|
|
105
|
+
- `DisplayName` 中的空格会被替换为 `-`
|
|
106
|
+
- `index` 是模型在数组中的位置(从 0 开始)
|
|
107
|
+
- 示例:`displayName: "Opus [proxy]"` → `id: "custom:Opus-[proxy]-0"`
|
|
108
|
+
|
|
109
|
+
**重要**:删除或插入模型时,后续模型的 ID 会自动更新(因为 index 会变化)。
|
|
110
|
+
|
|
77
111
|
### 管理别名和文件
|
|
78
112
|
|
|
79
113
|
```bash
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { styleText } from "node:util";
|
|
2
2
|
import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { basename, dirname, join } from "node:path";
|
|
4
|
-
import { homedir } from "node:os";
|
|
3
|
+
import { basename, delimiter, dirname, join } from "node:path";
|
|
4
|
+
import { homedir, platform } from "node:os";
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
6
6
|
import { chmod, copyFile, mkdir, readFile, readdir, readlink, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
7
7
|
|
|
8
8
|
//#region src/patcher.ts
|
|
9
|
+
const IS_WINDOWS$1 = platform() === "win32";
|
|
9
10
|
async function patchDroid(options) {
|
|
10
11
|
const { inputPath, outputPath, patches, dryRun = false, backup = true, verbose = false } = options;
|
|
11
12
|
const finalOutputPath = outputPath || `${inputPath}.patched`;
|
|
@@ -152,13 +153,24 @@ async function patchDroid(options) {
|
|
|
152
153
|
console.log(styleText("white", "[*] Applying patches..."));
|
|
153
154
|
const totalPatched = results.reduce((sum, r) => sum + (r.positions?.length || 0), 0);
|
|
154
155
|
console.log(styleText("green", `[*] Applied ${totalPatched} patches`));
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
let actualOutputPath = finalOutputPath;
|
|
157
|
+
try {
|
|
158
|
+
await writeFile(finalOutputPath, workingBuffer);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (error.code === "EBUSY" && IS_WINDOWS$1) {
|
|
161
|
+
const timestamp = Date.now();
|
|
162
|
+
const ext = finalOutputPath.endsWith(".exe") ? ".exe" : "";
|
|
163
|
+
actualOutputPath = `${finalOutputPath.replace(/\.exe$/, "").replace(/\.patched$/, "").replace(/-\d+$/, "")}-${timestamp}${ext ? ext : ".patched"}`;
|
|
164
|
+
console.log(styleText("yellow", `[!] Original file locked, saving to: ${actualOutputPath}`));
|
|
165
|
+
await writeFile(actualOutputPath, workingBuffer);
|
|
166
|
+
} else throw error;
|
|
167
|
+
}
|
|
168
|
+
console.log(styleText("white", `[*] Patched binary saved: ${styleText("cyan", actualOutputPath)}`));
|
|
169
|
+
await chmod(actualOutputPath, 493);
|
|
158
170
|
console.log(styleText("gray", "[*] Set executable permission"));
|
|
159
171
|
console.log();
|
|
160
172
|
console.log(styleText("white", "[*] Verifying patches..."));
|
|
161
|
-
const verifyBuffer = await readFile(
|
|
173
|
+
const verifyBuffer = await readFile(actualOutputPath);
|
|
162
174
|
let allVerified = true;
|
|
163
175
|
for (const patch of patches) {
|
|
164
176
|
if (patch.regexPattern && patch.regexReplacement) {
|
|
@@ -206,7 +218,7 @@ async function patchDroid(options) {
|
|
|
206
218
|
}
|
|
207
219
|
return {
|
|
208
220
|
success: allVerified,
|
|
209
|
-
outputPath:
|
|
221
|
+
outputPath: actualOutputPath,
|
|
210
222
|
results,
|
|
211
223
|
patchedCount: totalPatched
|
|
212
224
|
};
|
|
@@ -338,17 +350,17 @@ function formatPatches(patches) {
|
|
|
338
350
|
if (patches.reasoningEffort) applied.push("reasoningEffort");
|
|
339
351
|
if (patches.noTelemetry) applied.push("noTelemetry");
|
|
340
352
|
if (patches.standalone) applied.push("standalone");
|
|
341
|
-
if (patches.autoHigh) applied.push("autoHigh");
|
|
342
353
|
if (patches.specModelCustom) applied.push("specModelCustom");
|
|
343
354
|
return applied.length > 0 ? applied.join(", ") : "(none)";
|
|
344
355
|
}
|
|
345
356
|
|
|
346
357
|
//#endregion
|
|
347
358
|
//#region src/alias.ts
|
|
359
|
+
const IS_WINDOWS = platform() === "win32";
|
|
348
360
|
const DROID_PATCH_DIR = join(homedir(), ".droid-patch");
|
|
349
361
|
const ALIASES_DIR = join(DROID_PATCH_DIR, "aliases");
|
|
350
362
|
const BINS_DIR = join(DROID_PATCH_DIR, "bins");
|
|
351
|
-
const
|
|
363
|
+
const UNIX_PATH_DIRS = [
|
|
352
364
|
join(homedir(), ".local/bin"),
|
|
353
365
|
join(homedir(), "bin"),
|
|
354
366
|
join(homedir(), ".bin"),
|
|
@@ -369,17 +381,23 @@ const COMMON_PATH_DIRS = [
|
|
|
369
381
|
join(homedir(), ".volta/bin"),
|
|
370
382
|
join(homedir(), ".fnm/current/bin")
|
|
371
383
|
];
|
|
384
|
+
const WINDOWS_PATH_DIRS = [
|
|
385
|
+
join(homedir(), ".droid-patch", "bin"),
|
|
386
|
+
join(homedir(), "scoop", "shims"),
|
|
387
|
+
join(homedir(), "AppData", "Local", "Programs", "bin")
|
|
388
|
+
];
|
|
389
|
+
const COMMON_PATH_DIRS = IS_WINDOWS ? WINDOWS_PATH_DIRS : UNIX_PATH_DIRS;
|
|
372
390
|
function ensureDirectories() {
|
|
373
391
|
if (!existsSync(DROID_PATCH_DIR)) mkdirSync(DROID_PATCH_DIR, { recursive: true });
|
|
374
392
|
if (!existsSync(ALIASES_DIR)) mkdirSync(ALIASES_DIR, { recursive: true });
|
|
375
393
|
if (!existsSync(BINS_DIR)) mkdirSync(BINS_DIR, { recursive: true });
|
|
376
394
|
}
|
|
377
395
|
function checkPathInclusion() {
|
|
378
|
-
return (process.env.PATH || "").split(
|
|
396
|
+
return (process.env.PATH || "").split(delimiter).some((p) => p.toLowerCase() === ALIASES_DIR.toLowerCase());
|
|
379
397
|
}
|
|
380
398
|
function findWritablePathDir() {
|
|
381
|
-
const pathDirs = (process.env.PATH || "").split(
|
|
382
|
-
for (const dir of COMMON_PATH_DIRS) if (pathDirs.
|
|
399
|
+
const pathDirs = (process.env.PATH || "").split(delimiter);
|
|
400
|
+
for (const dir of COMMON_PATH_DIRS) if (pathDirs.some((p) => p.toLowerCase() === dir.toLowerCase())) try {
|
|
383
401
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
384
402
|
const testFile = join(dir, `.droid-patch-test-${Date.now()}`);
|
|
385
403
|
writeFileSync(testFile, "");
|
|
@@ -391,6 +409,7 @@ function findWritablePathDir() {
|
|
|
391
409
|
return null;
|
|
392
410
|
}
|
|
393
411
|
function getShellConfigPath() {
|
|
412
|
+
if (IS_WINDOWS) return "";
|
|
394
413
|
switch (basename(process.env.SHELL || "/bin/bash")) {
|
|
395
414
|
case "zsh": return join(homedir(), ".zshrc");
|
|
396
415
|
case "bash": {
|
|
@@ -403,6 +422,7 @@ function getShellConfigPath() {
|
|
|
403
422
|
}
|
|
404
423
|
}
|
|
405
424
|
function isPathConfigured(shellConfigPath) {
|
|
425
|
+
if (IS_WINDOWS || !shellConfigPath) return false;
|
|
406
426
|
if (!existsSync(shellConfigPath)) return false;
|
|
407
427
|
try {
|
|
408
428
|
const content = readFileSync(shellConfigPath, "utf-8");
|
|
@@ -411,6 +431,37 @@ function isPathConfigured(shellConfigPath) {
|
|
|
411
431
|
return false;
|
|
412
432
|
}
|
|
413
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Add directory to Windows user PATH using setx command
|
|
436
|
+
* This modifies the user's PATH permanently (requires terminal restart)
|
|
437
|
+
*/
|
|
438
|
+
function addToWindowsUserPath(dir) {
|
|
439
|
+
try {
|
|
440
|
+
let existingPath = "";
|
|
441
|
+
try {
|
|
442
|
+
const match = execSync("reg query \"HKCU\\Environment\" /v Path 2>nul", {
|
|
443
|
+
encoding: "utf-8",
|
|
444
|
+
stdio: [
|
|
445
|
+
"pipe",
|
|
446
|
+
"pipe",
|
|
447
|
+
"pipe"
|
|
448
|
+
]
|
|
449
|
+
}).match(/Path\s+REG_(?:EXPAND_)?SZ\s+(.+)/);
|
|
450
|
+
existingPath = match ? match[1].trim() : "";
|
|
451
|
+
} catch {}
|
|
452
|
+
if (existingPath.split(";").map((p) => p.toLowerCase().trim()).includes(dir.toLowerCase())) return true;
|
|
453
|
+
execSync(`setx PATH "${existingPath ? `${existingPath};${dir}` : dir}"`, { stdio: "pipe" });
|
|
454
|
+
return true;
|
|
455
|
+
} catch {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Generate Windows .cmd launcher script
|
|
461
|
+
*/
|
|
462
|
+
function generateWindowsLauncher(targetPath) {
|
|
463
|
+
return `@echo off\r\n"${targetPath}" %*\r\n`;
|
|
464
|
+
}
|
|
414
465
|
function addPathToShellConfig(shellConfigPath, verbose = false) {
|
|
415
466
|
const shellName = basename(process.env.SHELL || "/bin/bash");
|
|
416
467
|
let exportLine;
|
|
@@ -428,6 +479,7 @@ function addPathToShellConfig(shellConfigPath, verbose = false) {
|
|
|
428
479
|
async function createAlias(patchedBinaryPath, aliasName, verbose = false) {
|
|
429
480
|
ensureDirectories();
|
|
430
481
|
console.log(styleText("white", `[*] Creating alias: ${styleText("cyan", aliasName)}`));
|
|
482
|
+
if (IS_WINDOWS) return createWindowsAlias(patchedBinaryPath, aliasName, verbose);
|
|
431
483
|
const writablePathDir = findWritablePathDir();
|
|
432
484
|
if (writablePathDir) {
|
|
433
485
|
const targetPath = join(writablePathDir, aliasName);
|
|
@@ -536,10 +588,103 @@ async function createAlias(patchedBinaryPath, aliasName, verbose = false) {
|
|
|
536
588
|
binaryPath: binaryDest
|
|
537
589
|
};
|
|
538
590
|
}
|
|
591
|
+
/**
|
|
592
|
+
* Create alias on Windows using .cmd launcher and setx for PATH
|
|
593
|
+
*/
|
|
594
|
+
/**
|
|
595
|
+
* Try to copy file, handling Windows file locking
|
|
596
|
+
* If target is locked, use a new filename with timestamp
|
|
597
|
+
*/
|
|
598
|
+
async function copyFileWithLockHandling(src, dest, verbose = false) {
|
|
599
|
+
try {
|
|
600
|
+
await copyFile(src, dest);
|
|
601
|
+
return dest;
|
|
602
|
+
} catch (error) {
|
|
603
|
+
if (error.code === "EBUSY" && IS_WINDOWS) {
|
|
604
|
+
const timestamp = Date.now();
|
|
605
|
+
const ext = dest.endsWith(".exe") ? ".exe" : "";
|
|
606
|
+
const newDest = `${dest.replace(/\.exe$/, "").replace(/-\d+$/, "")}-${timestamp}${ext}`;
|
|
607
|
+
if (verbose) console.log(styleText("yellow", ` [!] File locked, using new path: ${newDest}`));
|
|
608
|
+
await copyFile(src, newDest);
|
|
609
|
+
return newDest;
|
|
610
|
+
}
|
|
611
|
+
throw error;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
async function createWindowsAlias(patchedBinaryPath, aliasName, verbose = false) {
|
|
615
|
+
const binDir = join(DROID_PATCH_DIR, "bin");
|
|
616
|
+
if (!existsSync(binDir)) mkdirSync(binDir, { recursive: true });
|
|
617
|
+
const binaryDest = await copyFileWithLockHandling(patchedBinaryPath, join(BINS_DIR, `${aliasName}-patched.exe`), verbose);
|
|
618
|
+
if (verbose) console.log(styleText("gray", ` Stored binary: ${binaryDest}`));
|
|
619
|
+
const cmdPath = join(binDir, `${aliasName}.cmd`);
|
|
620
|
+
writeFileSync(cmdPath, generateWindowsLauncher(binaryDest));
|
|
621
|
+
if (verbose) console.log(styleText("gray", ` Created launcher: ${cmdPath}`));
|
|
622
|
+
const pathAdded = addToWindowsUserPath(binDir);
|
|
623
|
+
console.log(styleText("green", `[*] Created: ${cmdPath}`));
|
|
624
|
+
console.log();
|
|
625
|
+
if (pathAdded) {
|
|
626
|
+
if (checkPathInclusion()) {
|
|
627
|
+
console.log(styleText("green", "─".repeat(60)));
|
|
628
|
+
console.log(styleText(["green", "bold"], " ALIAS READY!"));
|
|
629
|
+
console.log(styleText("green", "─".repeat(60)));
|
|
630
|
+
console.log();
|
|
631
|
+
console.log(styleText("white", `The alias "${styleText(["cyan", "bold"], aliasName)}" is now available.`));
|
|
632
|
+
console.log(styleText("gray", `(Installed to: ${binDir})`));
|
|
633
|
+
return {
|
|
634
|
+
aliasPath: cmdPath,
|
|
635
|
+
binaryPath: binaryDest,
|
|
636
|
+
immediate: true
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
640
|
+
console.log(styleText(["yellow", "bold"], " PATH Updated - Restart Terminal"));
|
|
641
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
642
|
+
console.log();
|
|
643
|
+
console.log(styleText("white", "PATH has been updated. Please restart your terminal."));
|
|
644
|
+
console.log(styleText("white", `Then you can use "${styleText(["cyan", "bold"], aliasName)}" command directly.`));
|
|
645
|
+
console.log();
|
|
646
|
+
console.log(styleText("gray", `Installed to: ${binDir}`));
|
|
647
|
+
} else {
|
|
648
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
649
|
+
console.log(styleText(["yellow", "bold"], " Manual PATH Configuration Required"));
|
|
650
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
651
|
+
console.log();
|
|
652
|
+
console.log(styleText("white", "Add this directory to your PATH:"));
|
|
653
|
+
console.log(styleText("cyan", ` ${binDir}`));
|
|
654
|
+
console.log();
|
|
655
|
+
console.log(styleText("gray", "Or run directly:"));
|
|
656
|
+
console.log(styleText("cyan", ` "${cmdPath}"`));
|
|
657
|
+
}
|
|
658
|
+
return {
|
|
659
|
+
aliasPath: cmdPath,
|
|
660
|
+
binaryPath: binaryDest,
|
|
661
|
+
immediate: false
|
|
662
|
+
};
|
|
663
|
+
}
|
|
539
664
|
async function removeAlias(aliasName) {
|
|
540
665
|
console.log(styleText("white", `[*] Removing alias: ${styleText("cyan", aliasName)}`));
|
|
541
666
|
let removed = false;
|
|
542
|
-
|
|
667
|
+
if (IS_WINDOWS) {
|
|
668
|
+
const cmdPath = join(join(DROID_PATCH_DIR, "bin"), `${aliasName}.cmd`);
|
|
669
|
+
if (existsSync(cmdPath)) {
|
|
670
|
+
await unlink(cmdPath);
|
|
671
|
+
console.log(styleText("green", ` Removed: ${cmdPath}`));
|
|
672
|
+
removed = true;
|
|
673
|
+
}
|
|
674
|
+
const exePath = join(BINS_DIR, `${aliasName}-patched.exe`);
|
|
675
|
+
if (existsSync(exePath)) {
|
|
676
|
+
await unlink(exePath);
|
|
677
|
+
console.log(styleText("green", ` Removed binary: ${exePath}`));
|
|
678
|
+
removed = true;
|
|
679
|
+
}
|
|
680
|
+
const proxyWrapperCmd = join(join(DROID_PATCH_DIR, "proxy"), `${aliasName}.cmd`);
|
|
681
|
+
if (existsSync(proxyWrapperCmd)) {
|
|
682
|
+
await unlink(proxyWrapperCmd);
|
|
683
|
+
console.log(styleText("green", ` Removed wrapper: ${proxyWrapperCmd}`));
|
|
684
|
+
removed = true;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (!IS_WINDOWS) for (const pathDir of COMMON_PATH_DIRS) {
|
|
543
688
|
const pathSymlink = join(pathDir, aliasName);
|
|
544
689
|
if (existsSync(pathSymlink)) try {
|
|
545
690
|
if (lstatSync(pathSymlink).isSymbolicLink()) {
|
|
@@ -629,43 +774,87 @@ async function listAliases() {
|
|
|
629
774
|
console.log(styleText("cyan", "═".repeat(60)));
|
|
630
775
|
console.log();
|
|
631
776
|
const aliases = [];
|
|
632
|
-
|
|
633
|
-
|
|
777
|
+
if (IS_WINDOWS) {
|
|
778
|
+
const binDir = join(DROID_PATCH_DIR, "bin");
|
|
779
|
+
if (existsSync(binDir)) try {
|
|
780
|
+
const files = readdirSync(binDir);
|
|
781
|
+
for (const file of files) if (file.endsWith(".cmd")) {
|
|
782
|
+
const aliasName = file.replace(/\.cmd$/, "");
|
|
783
|
+
const fullPath = join(binDir, file);
|
|
784
|
+
try {
|
|
785
|
+
const match = readFileSync(fullPath, "utf-8").match(/"([^"]+)"/);
|
|
786
|
+
const target = match ? match[1] : fullPath;
|
|
787
|
+
aliases.push({
|
|
788
|
+
name: aliasName,
|
|
789
|
+
target,
|
|
790
|
+
location: binDir,
|
|
791
|
+
immediate: checkPathInclusion()
|
|
792
|
+
});
|
|
793
|
+
} catch {
|
|
794
|
+
aliases.push({
|
|
795
|
+
name: aliasName,
|
|
796
|
+
target: fullPath,
|
|
797
|
+
location: binDir,
|
|
798
|
+
immediate: false
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
} catch {}
|
|
803
|
+
const proxyDir = join(DROID_PATCH_DIR, "proxy");
|
|
804
|
+
if (existsSync(proxyDir)) try {
|
|
805
|
+
const files = readdirSync(proxyDir);
|
|
806
|
+
for (const file of files) if (file.endsWith(".cmd")) {
|
|
807
|
+
const aliasName = file.replace(/\.cmd$/, "");
|
|
808
|
+
if (!aliases.find((a) => a.name === aliasName)) {
|
|
809
|
+
const fullPath = join(proxyDir, file);
|
|
810
|
+
aliases.push({
|
|
811
|
+
name: aliasName,
|
|
812
|
+
target: fullPath,
|
|
813
|
+
location: proxyDir,
|
|
814
|
+
immediate: false
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
} catch {}
|
|
819
|
+
} else {
|
|
820
|
+
for (const pathDir of COMMON_PATH_DIRS) {
|
|
821
|
+
if (!existsSync(pathDir)) continue;
|
|
822
|
+
try {
|
|
823
|
+
const files = readdirSync(pathDir);
|
|
824
|
+
for (const file of files) {
|
|
825
|
+
const fullPath = join(pathDir, file);
|
|
826
|
+
try {
|
|
827
|
+
if (lstatSync(fullPath).isSymbolicLink()) {
|
|
828
|
+
const target = await readlink(fullPath);
|
|
829
|
+
if (target.includes(".droid-patch/bins") || target.includes(".droid-patch/websearch") || target.includes(".droid-patch/proxy") || target.includes(".droid-patch/statusline")) aliases.push({
|
|
830
|
+
name: file,
|
|
831
|
+
target,
|
|
832
|
+
location: pathDir,
|
|
833
|
+
immediate: true
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
} catch {}
|
|
837
|
+
}
|
|
838
|
+
} catch {}
|
|
839
|
+
}
|
|
634
840
|
try {
|
|
635
|
-
const files = readdirSync(
|
|
841
|
+
const files = readdirSync(ALIASES_DIR);
|
|
636
842
|
for (const file of files) {
|
|
637
|
-
const fullPath = join(
|
|
843
|
+
const fullPath = join(ALIASES_DIR, file);
|
|
638
844
|
try {
|
|
639
845
|
if (lstatSync(fullPath).isSymbolicLink()) {
|
|
640
846
|
const target = await readlink(fullPath);
|
|
641
|
-
if (
|
|
847
|
+
if (!aliases.find((a) => a.name === file)) aliases.push({
|
|
642
848
|
name: file,
|
|
643
849
|
target,
|
|
644
|
-
location:
|
|
645
|
-
immediate:
|
|
850
|
+
location: ALIASES_DIR,
|
|
851
|
+
immediate: false
|
|
646
852
|
});
|
|
647
853
|
}
|
|
648
854
|
} catch {}
|
|
649
855
|
}
|
|
650
856
|
} catch {}
|
|
651
857
|
}
|
|
652
|
-
try {
|
|
653
|
-
const files = readdirSync(ALIASES_DIR);
|
|
654
|
-
for (const file of files) {
|
|
655
|
-
const fullPath = join(ALIASES_DIR, file);
|
|
656
|
-
try {
|
|
657
|
-
if (lstatSync(fullPath).isSymbolicLink()) {
|
|
658
|
-
const target = await readlink(fullPath);
|
|
659
|
-
if (!aliases.find((a) => a.name === file)) aliases.push({
|
|
660
|
-
name: file,
|
|
661
|
-
target,
|
|
662
|
-
location: ALIASES_DIR,
|
|
663
|
-
immediate: false
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
} catch {}
|
|
667
|
-
}
|
|
668
|
-
} catch {}
|
|
669
858
|
if (aliases.length === 0) {
|
|
670
859
|
console.log(styleText("gray", " No aliases configured."));
|
|
671
860
|
console.log();
|
|
@@ -744,6 +933,7 @@ async function replaceOriginal(patchedBinaryPath, originalPath, verbose = false)
|
|
|
744
933
|
async function createAliasForWrapper(wrapperPath, aliasName, verbose = false) {
|
|
745
934
|
ensureDirectories();
|
|
746
935
|
console.log(styleText("white", `[*] Creating alias: ${styleText("cyan", aliasName)}`));
|
|
936
|
+
if (IS_WINDOWS) return createWindowsWrapperAlias(wrapperPath, aliasName, verbose);
|
|
747
937
|
const writablePathDir = findWritablePathDir();
|
|
748
938
|
if (writablePathDir) {
|
|
749
939
|
const targetPath = join(writablePathDir, aliasName);
|
|
@@ -819,6 +1009,58 @@ async function createAliasForWrapper(wrapperPath, aliasName, verbose = false) {
|
|
|
819
1009
|
binaryPath: wrapperPath
|
|
820
1010
|
};
|
|
821
1011
|
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Create Windows alias for wrapper script (.cmd pointing to wrapper .cmd)
|
|
1014
|
+
*/
|
|
1015
|
+
async function createWindowsWrapperAlias(wrapperPath, aliasName, verbose = false) {
|
|
1016
|
+
const binDir = join(DROID_PATCH_DIR, "bin");
|
|
1017
|
+
if (!existsSync(binDir)) mkdirSync(binDir, { recursive: true });
|
|
1018
|
+
if (verbose) console.log(styleText("gray", ` Wrapper: ${wrapperPath}`));
|
|
1019
|
+
const cmdPath = join(binDir, `${aliasName}.cmd`);
|
|
1020
|
+
writeFileSync(cmdPath, generateWindowsLauncher(wrapperPath));
|
|
1021
|
+
if (verbose) console.log(styleText("gray", ` Created launcher: ${cmdPath}`));
|
|
1022
|
+
const pathAdded = addToWindowsUserPath(binDir);
|
|
1023
|
+
console.log(styleText("green", `[*] Created: ${cmdPath}`));
|
|
1024
|
+
console.log();
|
|
1025
|
+
if (pathAdded) {
|
|
1026
|
+
if (checkPathInclusion()) {
|
|
1027
|
+
console.log(styleText("green", "─".repeat(60)));
|
|
1028
|
+
console.log(styleText(["green", "bold"], " ALIAS READY!"));
|
|
1029
|
+
console.log(styleText("green", "─".repeat(60)));
|
|
1030
|
+
console.log();
|
|
1031
|
+
console.log(styleText("white", `The alias "${styleText(["cyan", "bold"], aliasName)}" is now available.`));
|
|
1032
|
+
console.log(styleText("gray", `(Installed to: ${binDir})`));
|
|
1033
|
+
return {
|
|
1034
|
+
aliasPath: cmdPath,
|
|
1035
|
+
binaryPath: wrapperPath,
|
|
1036
|
+
immediate: true
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
1040
|
+
console.log(styleText(["yellow", "bold"], " PATH Updated - Restart Terminal"));
|
|
1041
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
1042
|
+
console.log();
|
|
1043
|
+
console.log(styleText("white", "PATH has been updated. Please restart your terminal."));
|
|
1044
|
+
console.log(styleText("white", `Then you can use "${styleText(["cyan", "bold"], aliasName)}" command directly.`));
|
|
1045
|
+
console.log();
|
|
1046
|
+
console.log(styleText("gray", `Installed to: ${binDir}`));
|
|
1047
|
+
} else {
|
|
1048
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
1049
|
+
console.log(styleText(["yellow", "bold"], " Manual PATH Configuration Required"));
|
|
1050
|
+
console.log(styleText("yellow", "─".repeat(60)));
|
|
1051
|
+
console.log();
|
|
1052
|
+
console.log(styleText("white", "Add this directory to your PATH:"));
|
|
1053
|
+
console.log(styleText("cyan", ` ${binDir}`));
|
|
1054
|
+
console.log();
|
|
1055
|
+
console.log(styleText("gray", "Or run directly:"));
|
|
1056
|
+
console.log(styleText("cyan", ` "${cmdPath}"`));
|
|
1057
|
+
}
|
|
1058
|
+
return {
|
|
1059
|
+
aliasPath: cmdPath,
|
|
1060
|
+
binaryPath: wrapperPath,
|
|
1061
|
+
immediate: false
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
822
1064
|
async function restoreOriginal(originalPath) {
|
|
823
1065
|
ensureDirectories();
|
|
824
1066
|
const latestBackupPath = join(BINS_DIR, "droid-original-latest");
|
|
@@ -1058,4 +1300,4 @@ async function clearAllAliases() {
|
|
|
1058
1300
|
|
|
1059
1301
|
//#endregion
|
|
1060
1302
|
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 };
|
|
1061
|
-
//# sourceMappingURL=alias-
|
|
1303
|
+
//# sourceMappingURL=alias-hvk8y5gC.mjs.map
|