droid-patch 0.1.1 → 0.2.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.
@@ -0,0 +1,491 @@
1
+ # droid-patch
2
+
3
+ [English](./README.md) | 简体中文
4
+
5
+ 用于修补 droid 二进制文件的 CLI 工具。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install -g droid-patch
11
+ # 或直接使用 npx
12
+ npx droid-patch --help
13
+ ```
14
+
15
+ ## 使用方法
16
+
17
+ ### 修补并创建别名
18
+
19
+ ```bash
20
+ # 使用 --is-custom 修补并创建别名
21
+ npx droid-patch --is-custom droid-custom
22
+
23
+ # 使用 --skip-login 跳过登录验证
24
+ npx droid-patch --skip-login droid-nologin
25
+
26
+ # 使用 --websearch 启用本地搜索代理
27
+ npx droid-patch --websearch droid-search
28
+
29
+ # 组合多个修补选项
30
+ npx droid-patch --is-custom --skip-login --websearch droid-full
31
+
32
+ # 指定 droid 二进制文件路径
33
+ npx droid-patch --skip-login -p /path/to/droid my-droid
34
+
35
+ # 试运行 - 验证修补但不实际修改文件
36
+ npx droid-patch --skip-login --dry-run droid
37
+
38
+ # 详细输出
39
+ npx droid-patch --skip-login -v droid
40
+ ```
41
+
42
+ ### 输出到指定目录
43
+
44
+ ```bash
45
+ # 输出修补后的二进制文件到当前目录
46
+ npx droid-patch --skip-login -o . my-droid
47
+
48
+ # 输出到指定目录
49
+ npx droid-patch --skip-login -o /path/to/dir my-droid
50
+ ```
51
+
52
+ ### 可用选项
53
+
54
+ | 选项 | 说明 |
55
+ |------|------|
56
+ | `--is-custom` | 将 `isCustom:!0` 修改为 `isCustom:!1`(为自定义模型启用上下文压缩) |
57
+ | `--skip-login` | 通过注入假的 `FACTORY_API_KEY` 跳过登录验证 |
58
+ | `--api-base <url>` | 将 Factory API URL 替换为自定义服务器(最多 22 个字符) |
59
+ | `--websearch` | 注入本地 WebSearch 代理,支持多个搜索提供商 |
60
+ | `--dry-run` | 验证修补但不实际修改二进制文件 |
61
+ | `-p, --path <path>` | droid 二进制文件路径(默认:`~/.droid/bin/droid`) |
62
+ | `-o, --output <dir>` | 修补后二进制文件的输出目录(直接创建文件,不创建别名) |
63
+ | `--no-backup` | 跳过创建原始二进制文件的备份 |
64
+ | `-v, --verbose` | 启用详细输出 |
65
+
66
+ ### 管理别名和文件
67
+
68
+ ```bash
69
+ # 列出所有别名
70
+ npx droid-patch list
71
+
72
+ # 删除别名
73
+ npx droid-patch remove <alias-name>
74
+
75
+ # 通过路径删除修补后的二进制文件
76
+ npx droid-patch remove ./my-droid
77
+ npx droid-patch remove /path/to/patched-binary
78
+
79
+ # 检查代理状态
80
+ npx droid-patch proxy-status
81
+ ```
82
+
83
+ ### 检查版本
84
+
85
+ ```bash
86
+ npx droid-patch version
87
+ ```
88
+
89
+ ## PATH 配置
90
+
91
+ 创建别名时(不使用 `-o`),工具会尝试安装到已在 PATH 中的目录(如 `~/.local/bin`)。如果不可用,需要将别名目录添加到 PATH:
92
+
93
+ ```bash
94
+ # 添加到 shell 配置文件(~/.zshrc、~/.bashrc 等)
95
+ export PATH="$HOME/.droid-patch/aliases:$PATH"
96
+ ```
97
+
98
+ ## 工作原理
99
+
100
+ 1. **修补**:工具在 droid 二进制文件中搜索特定的字节模式,并用等长的替换内容进行替换
101
+ 2. **创建别名**(不使用 `-o`):
102
+ - 将修补后的二进制文件复制到 `~/.droid-patch/bins/`
103
+ - 在 PATH 目录或 `~/.droid-patch/aliases/` 中创建符号链接
104
+ - 在 macOS 上,自动使用 `codesign` 重新签名二进制文件
105
+ 3. **直接输出**(使用 `-o`):
106
+ - 将修补后的二进制文件直接保存到指定目录
107
+ - 在 macOS 上,自动使用 `codesign` 重新签名二进制文件
108
+
109
+ ## 可用的修补选项
110
+
111
+ ### `--is-custom`
112
+
113
+ 将 `isCustom:!0`(true)改为 `isCustom:!1`(false)。
114
+
115
+ **用途**:为自定义模型启用上下文压缩(自动摘要)功能,该功能通常仅对官方模型可用。
116
+
117
+ **注意**:副作用未知 - 在生产环境使用前请充分测试。
118
+
119
+ ### `--skip-login`
120
+
121
+ 将二进制文件中所有 `process.env.FACTORY_API_KEY` 引用替换为硬编码的假密钥 `"fk-droid-patch-skip-00000"`。
122
+
123
+ **用途**:无需设置 `FACTORY_API_KEY` 环境变量即可跳过登录/认证要求。
124
+
125
+ **工作原理**:
126
+ - 原始代码通过检查 `process.env.FACTORY_API_KEY` 进行认证
127
+ - 修补后,代码直接使用假密钥字符串,绕过环境变量检查
128
+ - 这是二进制级别的修补,因此在所有终端会话中都有效,无需任何环境设置
129
+
130
+ ### `--api-base <url>`
131
+
132
+ 将 Factory API 基础 URL(`https://api.factory.ai`)替换为自定义 URL。
133
+
134
+ **用途**:将 API 请求重定向到自定义服务器(如本地代理)。
135
+
136
+ **限制**:URL 必须不超过 22 个字符(与原始 URL 长度相同)。
137
+
138
+ **示例**:
139
+ ```bash
140
+ # 有效的 URL(<=22 个字符)
141
+ npx droid-patch --api-base "http://127.0.0.1:3000" droid-local
142
+ npx droid-patch --api-base "http://localhost:80" droid-local
143
+
144
+ # 无效(太长)
145
+ npx droid-patch --api-base "http://my-long-domain.com:3000" droid # 错误!
146
+ ```
147
+
148
+ ### `--websearch`
149
+
150
+ 通过本地代理服务器启用 WebSearch 功能,拦截 `/api/tools/exa/search` 请求。
151
+
152
+ **用途**:无需 Factory.ai 认证即可使用 WebSearch 功能。
153
+
154
+ **特性**:
155
+ - **多搜索提供商**:支持自动降级
156
+ - **自动启动**:运行别名时代理自动启动
157
+ - **自动关闭**:空闲 5 分钟后代理自动关闭(可配置)
158
+ - **进程检测**:只要 droid 在运行,代理就保持活跃
159
+
160
+ **使用方法**:
161
+ ```bash
162
+ # 创建带 websearch 的别名
163
+ npx droid-patch --websearch droid-search
164
+
165
+ # 直接运行 - 一切都是自动的!
166
+ droid-search
167
+ ```
168
+
169
+ ---
170
+
171
+ ## WebSearch 配置指南
172
+
173
+ `--websearch` 功能支持多个搜索提供商。通过 shell 配置文件(`~/.zshrc`、`~/.bashrc` 等)中的环境变量进行配置。
174
+
175
+ ### 搜索提供商优先级
176
+
177
+ 代理按以下顺序尝试提供商,使用第一个成功的:
178
+
179
+ | 优先级 | 提供商 | 质量 | 免费额度 | 设置难度 |
180
+ |--------|--------|------|----------|----------|
181
+ | 1 | Smithery Exa | 优秀 | 免费(通过 Smithery) | 简单 |
182
+ | 2 | Google PSE | 很好 | 10,000 次/天 | 中等 |
183
+ | 3 | Serper | 很好 | 2,500 免费额度 | 简单 |
184
+ | 4 | Brave Search | 好 | 2,000 次/月 | 简单 |
185
+ | 5 | SearXNG | 好 | 无限(自托管) | 较难 |
186
+ | 6 | DuckDuckGo | 基本 | 无限 | 无需配置 |
187
+
188
+ ---
189
+
190
+ ## 1. Smithery Exa(推荐)
191
+
192
+ [Smithery Exa](https://smithery.ai/server/exa) 通过 MCP 协议提供高质量的语义搜索结果。Smithery 作为 Exa 搜索 API 的免费代理。
193
+
194
+ ### 设置步骤
195
+
196
+ 1. **创建 Smithery 账号**
197
+ - 访问 [smithery.ai](https://smithery.ai)
198
+ - 注册免费账号
199
+
200
+ 2. **获取 API Key**
201
+ - 进入账号设置
202
+ - 复制 API key
203
+
204
+ 3. **获取 Profile ID**
205
+ - 访问 [smithery.ai/server/exa](https://smithery.ai/server/exa)
206
+ - Profile ID 显示在连接 URL 或设置中
207
+
208
+ 4. **配置环境变量**
209
+ ```bash
210
+ # 添加到 ~/.zshrc 或 ~/.bashrc
211
+ export SMITHERY_API_KEY="your_api_key_here"
212
+ export SMITHERY_PROFILE="your_profile_id"
213
+ ```
214
+
215
+ ### 价格
216
+
217
+ - 通过 Smithery **免费**(Smithery 免费代理 Exa API)
218
+ - 注意:官方 Exa API (exa.ai) 是收费的,但通过 Smithery 可以免费使用
219
+
220
+ ---
221
+
222
+ ## 2. Google 可编程搜索引擎 (PSE)
223
+
224
+ Google PSE 提供高质量的搜索结果,免费额度充足。
225
+
226
+ ### 设置步骤
227
+
228
+ #### 第一步:创建可编程搜索引擎
229
+
230
+ 1. 访问 [Google 可编程搜索引擎控制台](https://cse.google.com/all)
231
+ 2. 点击 **"添加"** 创建新的搜索引擎
232
+ 3. 配置:
233
+ - **要搜索的网站**:输入 `*` 以搜索整个网络
234
+ - **名称**:给它一个描述性的名称(如 "Web Search")
235
+ 4. 点击 **"创建"**
236
+ 5. 点击新搜索引擎的 **"控制面板"**
237
+ 6. 复制 **搜索引擎 ID (cx)** - 格式类似 `017576662512468239146:omuauf_lfve`
238
+
239
+ #### 第二步:获取 API Key
240
+
241
+ 1. 访问 [Google Cloud Console](https://console.cloud.google.com/)
242
+ 2. 创建新项目或选择现有项目
243
+ 3. 启用 **Custom Search API**:
244
+ - 进入 **"API 和服务"** > **"库"**
245
+ - 搜索 **"Custom Search API"**
246
+ - 点击 **"启用"**
247
+ 4. 创建凭据:
248
+ - 进入 **"API 和服务"** > **"凭据"**
249
+ - 点击 **"创建凭据"** > **"API 密钥"**
250
+ - 复制 API 密钥
251
+
252
+ #### 第三步:配置环境变量
253
+
254
+ ```bash
255
+ # 添加到 ~/.zshrc 或 ~/.bashrc
256
+ export GOOGLE_PSE_API_KEY="AIzaSy..." # 你的 API 密钥
257
+ export GOOGLE_PSE_CX="017576662512468239146:omuauf_lfve" # 你的搜索引擎 ID
258
+ ```
259
+
260
+ ### 免费额度限制
261
+
262
+ - 每天 **10,000 次查询** 免费
263
+ - 每次查询最多 10 个结果
264
+ - 超出后:每 1,000 次查询 $5
265
+
266
+ ---
267
+
268
+ ## 3. Serper
269
+
270
+ [Serper](https://serper.dev) 通过易用的 API 提供 Google 搜索结果。
271
+
272
+ ### 设置步骤
273
+
274
+ 1. **创建账号**
275
+ - 访问 [serper.dev](https://serper.dev)
276
+ - 注册免费账号
277
+
278
+ 2. **获取 API Key**
279
+ - 登录后,API key 显示在仪表板上
280
+ - 复制 API key
281
+
282
+ 3. **配置环境变量**
283
+ ```bash
284
+ # 添加到 ~/.zshrc 或 ~/.bashrc
285
+ export SERPER_API_KEY="your_api_key_here"
286
+ ```
287
+
288
+ ### 免费额度
289
+
290
+ - 注册时获得 **2,500 免费额度**
291
+ - 1 额度 = 1 次搜索查询
292
+ - 可购买付费计划获得更多用量
293
+
294
+ ---
295
+
296
+ ## 4. Brave Search
297
+
298
+ [Brave Search API](https://brave.com/search/api/) 提供注重隐私的搜索结果。
299
+
300
+ ### 设置步骤
301
+
302
+ 1. **创建账号**
303
+ - 访问 [brave.com/search/api](https://brave.com/search/api/)
304
+ - 点击 **"开始使用"**
305
+
306
+ 2. **订阅计划**
307
+ - 选择 **免费** 计划(每月 2,000 次查询)
308
+ - 或付费计划以获得更多查询次数
309
+
310
+ 3. **获取 API Key**
311
+ - 进入 API 仪表板
312
+ - 复制 API key
313
+
314
+ 4. **配置环境变量**
315
+ ```bash
316
+ # 添加到 ~/.zshrc 或 ~/.bashrc
317
+ export BRAVE_API_KEY="BSA..."
318
+ ```
319
+
320
+ ### 免费额度
321
+
322
+ - 每月 **2,000 次查询** 免费
323
+ - 速率限制:每秒 1 次查询
324
+ - 付费计划起价 $5/月,20,000 次查询
325
+
326
+ ---
327
+
328
+ ## 5. SearXNG(自托管)
329
+
330
+ [SearXNG](https://github.com/searxng/searxng) 是一个免费、注重隐私的元搜索引擎,可以自托管。
331
+
332
+ ### 设置步骤
333
+
334
+ #### 选项 A:使用公共实例
335
+
336
+ 可以使用公共 SearXNG 实例,但可用性和可靠性不稳定。
337
+
338
+ ```bash
339
+ # 公共实例示例(请检查是否可用)
340
+ export SEARXNG_URL="https://searx.be"
341
+ ```
342
+
343
+ 在 [searx.space](https://searx.space/) 查找公共实例
344
+
345
+ #### 选项 B:使用 Docker 自托管
346
+
347
+ 1. **使用 Docker 运行 SearXNG**
348
+ ```bash
349
+ docker run -d \
350
+ --name searxng \
351
+ -p 8080:8080 \
352
+ -e SEARXNG_BASE_URL=http://localhost:8080 \
353
+ searxng/searxng
354
+ ```
355
+
356
+ 2. **配置环境变量**
357
+ ```bash
358
+ # 添加到 ~/.zshrc 或 ~/.bashrc
359
+ export SEARXNG_URL="http://localhost:8080"
360
+ ```
361
+
362
+ ### 优点
363
+
364
+ - 无限搜索
365
+ - 不需要 API 密钥
366
+ - 注重隐私
367
+ - 聚合多个搜索引擎的结果
368
+
369
+ ### 缺点
370
+
371
+ - 需要自托管才能保证可靠性
372
+ - 公共实例可能较慢或不可用
373
+
374
+ ---
375
+
376
+ ## 6. DuckDuckGo(默认备用)
377
+
378
+ 当没有配置其他提供商或其他提供商不可用时,自动使用 DuckDuckGo 作为最终备用。
379
+
380
+ ### 配置
381
+
382
+ **无需配置!** DuckDuckGo 开箱即用。
383
+
384
+ ### 限制
385
+
386
+ - HTML 抓取(不如 API 可靠)
387
+ - 与其他提供商相比结果较基础
388
+ - 大量使用可能被限速
389
+
390
+ ---
391
+
392
+ ## 快速配置示例
393
+
394
+ ### 最简设置(免费,无需 API Key)
395
+
396
+ 直接使用 DuckDuckGo 备用:
397
+
398
+ ```bash
399
+ npx droid-patch --websearch droid-search
400
+ droid-search # 立即使用 DuckDuckGo 工作
401
+ ```
402
+
403
+ ### 推荐设置(最佳质量)
404
+
405
+ ```bash
406
+ # 添加到 ~/.zshrc 或 ~/.bashrc
407
+ export SMITHERY_API_KEY="your_smithery_key"
408
+ export SMITHERY_PROFILE="your_profile_id"
409
+
410
+ # 备用:Google PSE
411
+ export GOOGLE_PSE_API_KEY="your_google_key"
412
+ export GOOGLE_PSE_CX="your_search_engine_id"
413
+ ```
414
+
415
+ ### 经济实惠设置(全免费)
416
+
417
+ ```bash
418
+ # 添加到 ~/.zshrc 或 ~/.bashrc
419
+
420
+ # 选项 1:Google PSE(每天 10,000 次免费)
421
+ export GOOGLE_PSE_API_KEY="your_google_key"
422
+ export GOOGLE_PSE_CX="your_search_engine_id"
423
+
424
+ # 选项 2:Serper(2,500 免费额度)
425
+ export SERPER_API_KEY="your_serper_key"
426
+
427
+ # 选项 3:Brave(每月 2,000 次免费)
428
+ export BRAVE_API_KEY="your_brave_key"
429
+
430
+ # DuckDuckGo 始终作为最终备用可用
431
+ ```
432
+
433
+ ---
434
+
435
+ ## 代理管理
436
+
437
+ ### 自动关闭
438
+
439
+ 代理在空闲 5 分钟后自动关闭以节省资源。
440
+
441
+ ```bash
442
+ # 自定义超时时间(秒)
443
+ export DROID_PROXY_IDLE_TIMEOUT=600 # 10 分钟
444
+ export DROID_PROXY_IDLE_TIMEOUT=0 # 禁用自动关闭
445
+ ```
446
+
447
+ ### 检查代理状态
448
+
449
+ ```bash
450
+ npx droid-patch proxy-status
451
+ ```
452
+
453
+ 输出显示:
454
+ - 代理运行状态
455
+ - 进程 ID
456
+ - Droid 进程检测
457
+ - 空闲超时设置
458
+
459
+ ### 调试模式
460
+
461
+ 启用详细日志以排查搜索问题:
462
+
463
+ ```bash
464
+ export DROID_SEARCH_DEBUG=1
465
+ droid-search
466
+ ```
467
+
468
+ ---
469
+
470
+ ## 示例
471
+
472
+ ```bash
473
+ # 快速开始:创建带 websearch 的 droid
474
+ npx droid-patch --websearch droid-search
475
+ droid-search # 直接使用!
476
+
477
+ # 全功能 droid
478
+ npx droid-patch --is-custom --skip-login --websearch droid-full
479
+
480
+ # 在当前目录创建独立的修补后二进制文件
481
+ npx droid-patch --skip-login -o . my-droid
482
+ ./my-droid --version
483
+
484
+ # 清理
485
+ npx droid-patch remove droid-search # 删除别名和所有相关文件
486
+ npx droid-patch remove ./my-droid # 删除文件
487
+ ```
488
+
489
+ ## 许可证
490
+
491
+ MIT
@@ -379,7 +379,7 @@ async function removeAlias(aliasName) {
379
379
  const stats = lstatSync(pathSymlink);
380
380
  if (stats.isSymbolicLink()) {
381
381
  const target = await readlink(pathSymlink);
382
- if (target.includes(".droid-patch/bins")) {
382
+ if (target.includes(".droid-patch/bins") || target.includes(".droid-patch/websearch")) {
383
383
  await unlink(pathSymlink);
384
384
  console.log(styleText("green", ` Removed: ${pathSymlink}`));
385
385
  removed = true;
@@ -399,6 +399,25 @@ async function removeAlias(aliasName) {
399
399
  console.log(styleText("green", ` Removed binary: ${binaryPath}`));
400
400
  removed = true;
401
401
  }
402
+ const websearchDir = join(DROID_PATCH_DIR, "websearch");
403
+ const wrapperPath = join(websearchDir, aliasName);
404
+ const proxyPath = join(websearchDir, `${aliasName}-proxy.js`);
405
+ const preloadPath = join(websearchDir, `${aliasName}-preload.js`);
406
+ if (existsSync(wrapperPath)) {
407
+ await unlink(wrapperPath);
408
+ console.log(styleText("green", ` Removed wrapper: ${wrapperPath}`));
409
+ removed = true;
410
+ }
411
+ if (existsSync(proxyPath)) {
412
+ await unlink(proxyPath);
413
+ console.log(styleText("green", ` Removed proxy: ${proxyPath}`));
414
+ removed = true;
415
+ }
416
+ if (existsSync(preloadPath)) {
417
+ await unlink(preloadPath);
418
+ console.log(styleText("green", ` Removed preload: ${preloadPath}`));
419
+ removed = true;
420
+ }
402
421
  if (!removed) console.log(styleText("yellow", ` Alias "${aliasName}" not found`));
403
422
  else console.log(styleText("green", `[*] Alias "${aliasName}" removed successfully`));
404
423
  }
@@ -419,7 +438,7 @@ async function listAliases() {
419
438
  const stats = lstatSync(fullPath);
420
439
  if (stats.isSymbolicLink()) {
421
440
  const target = await readlink(fullPath);
422
- if (target.includes(".droid-patch/bins")) aliases.push({
441
+ if (target.includes(".droid-patch/bins") || target.includes(".droid-patch/websearch")) aliases.push({
423
442
  name: file,
424
443
  target,
425
444
  location: pathDir,
@@ -505,6 +524,89 @@ async function replaceOriginal(patchedBinaryPath, originalPath, verbose = false)
505
524
  backupPath: latestBackupPath
506
525
  };
507
526
  }
527
+ /**
528
+ * Create alias for wrapper script
529
+ * Unlike createAlias, this function creates symlink pointing to wrapper script
530
+ * Used for features like websearch that require preprocessing
531
+ */
532
+ async function createAliasForWrapper(wrapperPath, aliasName, verbose = false) {
533
+ ensureDirectories();
534
+ console.log(styleText("white", `[*] Creating alias: ${styleText("cyan", aliasName)}`));
535
+ const writablePathDir = findWritablePathDir();
536
+ if (writablePathDir) {
537
+ const targetPath = join(writablePathDir, aliasName);
538
+ if (verbose) console.log(styleText("gray", ` Wrapper: ${wrapperPath}`));
539
+ if (existsSync(targetPath)) {
540
+ await unlink(targetPath);
541
+ if (verbose) console.log(styleText("gray", ` Removed existing: ${targetPath}`));
542
+ }
543
+ await symlink(wrapperPath, targetPath);
544
+ console.log(styleText("green", `[*] Created: ${targetPath} -> ${wrapperPath}`));
545
+ console.log();
546
+ console.log(styleText("green", "─".repeat(60)));
547
+ console.log(styleText(["green", "bold"], " ALIAS READY - NO ACTION REQUIRED!"));
548
+ console.log(styleText("green", "─".repeat(60)));
549
+ console.log();
550
+ console.log(styleText("white", `The alias "${styleText(["cyan", "bold"], aliasName)}" is now available in ALL terminals.`));
551
+ console.log(styleText("gray", `(Installed to: ${writablePathDir})`));
552
+ return {
553
+ aliasPath: targetPath,
554
+ binaryPath: wrapperPath,
555
+ immediate: true
556
+ };
557
+ }
558
+ console.log(styleText("yellow", "[*] No writable PATH directory found, using fallback..."));
559
+ const symlinkPath = join(ALIASES_DIR, aliasName);
560
+ if (existsSync(symlinkPath)) {
561
+ await unlink(symlinkPath);
562
+ if (verbose) console.log(styleText("gray", ` Removed existing symlink`));
563
+ }
564
+ await symlink(wrapperPath, symlinkPath);
565
+ console.log(styleText("green", `[*] Created symlink: ${symlinkPath} -> ${wrapperPath}`));
566
+ const shellConfig = getShellConfigPath();
567
+ if (!checkPathInclusion()) if (!isPathConfigured(shellConfig)) {
568
+ console.log(styleText("white", `[*] Configuring PATH in ${shellConfig}...`));
569
+ if (addPathToShellConfig(shellConfig, verbose)) {
570
+ console.log(styleText("green", `[*] PATH configured successfully!`));
571
+ console.log();
572
+ console.log(styleText("yellow", "─".repeat(60)));
573
+ console.log(styleText(["yellow", "bold"], " ACTION REQUIRED"));
574
+ console.log(styleText("yellow", "─".repeat(60)));
575
+ console.log();
576
+ console.log(styleText("white", "To use the alias in this terminal, run:"));
577
+ console.log();
578
+ console.log(styleText("cyan", ` source ${shellConfig}`));
579
+ console.log();
580
+ console.log(styleText("gray", "Or simply open a new terminal window."));
581
+ console.log(styleText("yellow", "─".repeat(60)));
582
+ } else {
583
+ const exportLine = `export PATH="${ALIASES_DIR}:$PATH"`;
584
+ console.log();
585
+ console.log(styleText("yellow", "─".repeat(60)));
586
+ console.log(styleText(["yellow", "bold"], " Manual PATH Configuration Required"));
587
+ console.log(styleText("yellow", "─".repeat(60)));
588
+ console.log();
589
+ console.log(styleText("white", "Add this line to your shell config:"));
590
+ console.log(styleText("cyan", ` ${exportLine}`));
591
+ console.log();
592
+ console.log(styleText("gray", `Shell config file: ${shellConfig}`));
593
+ console.log(styleText("yellow", "─".repeat(60)));
594
+ }
595
+ } else {
596
+ console.log(styleText("green", `[*] PATH already configured in ${shellConfig}`));
597
+ console.log();
598
+ console.log(styleText("yellow", `Note: Run \`source ${shellConfig}\` or open a new terminal to use the alias.`));
599
+ }
600
+ else {
601
+ console.log(styleText("green", `[*] PATH already includes aliases directory`));
602
+ console.log();
603
+ console.log(styleText("green", `You can now use "${styleText(["cyan", "bold"], aliasName)}" command directly!`));
604
+ }
605
+ return {
606
+ aliasPath: symlinkPath,
607
+ binaryPath: wrapperPath
608
+ };
609
+ }
508
610
  async function restoreOriginal(originalPath) {
509
611
  ensureDirectories();
510
612
  const latestBackupPath = join(BINS_DIR, "droid-original-latest");
@@ -559,5 +661,5 @@ async function restoreOriginal(originalPath) {
559
661
  }
560
662
 
561
663
  //#endregion
562
- export { createAlias, listAliases, patchDroid, removeAlias, replaceOriginal, restoreOriginal };
563
- //# sourceMappingURL=alias-DcCF7R2B.js.map
664
+ export { createAlias, createAliasForWrapper, listAliases, patchDroid, removeAlias, replaceOriginal, restoreOriginal };
665
+ //# sourceMappingURL=alias-C9LRaTwF.js.map