procproxy 0.1.2__tar.gz → 0.1.3__tar.gz

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.
Files changed (53) hide show
  1. {procproxy-0.1.2/src/procproxy.egg-info → procproxy-0.1.3}/PKG-INFO +54 -8
  2. {procproxy-0.1.2 → procproxy-0.1.3}/README.md +53 -7
  3. {procproxy-0.1.2 → procproxy-0.1.3}/pyproject.toml +1 -1
  4. {procproxy-0.1.2 → procproxy-0.1.3/src/procproxy.egg-info}/PKG-INFO +54 -8
  5. {procproxy-0.1.2 → procproxy-0.1.3}/src/procproxy.egg-info/SOURCES.txt +1 -0
  6. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/__init__.py +1 -1
  7. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/api.py +30 -0
  8. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/cli.py +26 -30
  9. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/config.py +2 -0
  10. procproxy-0.1.3/src/proxycore/config_init.py +85 -0
  11. {procproxy-0.1.2 → procproxy-0.1.3}/LICENSE +0 -0
  12. {procproxy-0.1.2 → procproxy-0.1.3}/setup.cfg +0 -0
  13. {procproxy-0.1.2 → procproxy-0.1.3}/src/procproxy.egg-info/dependency_links.txt +0 -0
  14. {procproxy-0.1.2 → procproxy-0.1.3}/src/procproxy.egg-info/entry_points.txt +0 -0
  15. {procproxy-0.1.2 → procproxy-0.1.3}/src/procproxy.egg-info/requires.txt +0 -0
  16. {procproxy-0.1.2 → procproxy-0.1.3}/src/procproxy.egg-info/top_level.txt +0 -0
  17. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/__main__.py +0 -0
  18. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/backend/__init__.py +0 -0
  19. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/backend/base.py +0 -0
  20. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/backend/null.py +0 -0
  21. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/backend/windivert/__init__.py +0 -0
  22. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/backend/windivert/filter.py +0 -0
  23. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/backend/windivert/pidmap.py +0 -0
  24. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/backend/windivert/pkt.py +0 -0
  25. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/canproxy.py +0 -0
  26. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/checkroute.py +0 -0
  27. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/config_source.py +0 -0
  28. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/data/default.yaml +0 -0
  29. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/dns/__init__.py +0 -0
  30. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/dns/cache.py +0 -0
  31. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/dns/modes.py +0 -0
  32. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/dns/udp.py +0 -0
  33. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/forward.py +0 -0
  34. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/ipc.py +0 -0
  35. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/log.py +0 -0
  36. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/pathsetup.py +0 -0
  37. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/probe.py +0 -0
  38. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/proc.py +0 -0
  39. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/rules.py +0 -0
  40. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/runtime.py +0 -0
  41. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/service/__init__.py +0 -0
  42. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/service/__main__.py +0 -0
  43. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/tunnel/__init__.py +0 -0
  44. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/tunnel/relay.py +0 -0
  45. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/tunnel/sessions.py +0 -0
  46. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/tunnel/sink.py +0 -0
  47. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/types.py +0 -0
  48. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/upstream/__init__.py +0 -0
  49. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/upstream/base.py +0 -0
  50. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/upstream/chain.py +0 -0
  51. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/upstream/http_connect.py +0 -0
  52. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/upstream/socks4.py +0 -0
  53. {procproxy-0.1.2 → procproxy-0.1.3}/src/proxycore/upstream/socks5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: procproxy
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: ProcProxy —— 进程级代理:按进程决定哪条连接走哪个上游代理
5
5
  Author: Longersking
6
6
  License-Expression: MIT
@@ -74,7 +74,7 @@ python procproxy.py doctor | rules | sessions | ... # 子命令(等价于 pyt
74
74
  pip install procproxy
75
75
  procproxy init # 生成 ~/.procproxy/config.yaml
76
76
  procproxy doctor # 环境自检
77
- procproxy forward WeChat.exe --port 8848 # 真实转发(需管理员)
77
+ procproxy forward --process WeChat.exe --port 8848 # 真实转发(需管理员)
78
78
  ```
79
79
 
80
80
  安装后直接运行 `procproxy` 会显示首次使用指引,不依赖任何样例文件。
@@ -250,7 +250,7 @@ python procproxy.py Weixin.exe WeChatAppEx.exe --port 8848
250
250
  ⚠️ 只有 ① 没有 ② → 端口填错了(**要填抓包工具的 MITM 监听口,不是它的 Web 管理口**)。
251
251
  ① ② 都正常但内容看不懂 → 那是 TLS,不是转发的问题。
252
252
 
253
- 完整指南(含排错与参数表)见 [`docs/14-把进程流量转发给抓包工具.md`](docs/14-把进程流量转发给抓包工具.md)。
253
+ 完整指南(含排错与参数表)见 [`docs/14-把进程流量转发给抓包工具.md`](https://github.com/Longersking/procproxy/blob/master/docs/14-%E6%8A%8A%E8%BF%9B%E7%A8%8B%E6%B5%81%E9%87%8F%E8%BD%AC%E5%8F%91%E7%BB%99%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7.md)。
254
254
 
255
255
  ---
256
256
 
@@ -340,14 +340,14 @@ rules: # 自上而下,**首个命中生
340
340
  7. 都没命中 → profile.default_action
341
341
  ```
342
342
 
343
- 完整语义见 [`docs/07-规则引擎与Profile格式.md`](docs/07-规则引擎与Profile格式.md)。
343
+ 完整语义见 [`docs/07-规则引擎与Profile格式.md`](https://github.com/Longersking/procproxy/blob/master/docs/07-%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E%E4%B8%8EProfile%E6%A0%BC%E5%BC%8F.md)。
344
344
 
345
345
  ---
346
346
 
347
347
  ## 编程接口
348
348
 
349
349
  `proxycore.api` 是与 CLI 共用同一套配置、规则引擎和会话表的 SDK。
350
- 完整教程见 [`docs/15-编程接口与SDK.md`](docs/15-编程接口与SDK.md)。
350
+ 完整教程见 [`docs/15-编程接口与SDK.md`](https://github.com/Longersking/procproxy/blob/master/docs/15-%E7%BC%96%E7%A8%8B%E6%8E%A5%E5%8F%A3%E4%B8%8ESDK.md)。
351
351
 
352
352
  ### 30 秒:零配置启动
353
353
 
@@ -373,6 +373,52 @@ with start_proxy() as ppx: # 不需要任何配置文件
373
373
  状态查询和联调;要真实抓包请用 `backend="windivert"`(需管理员)或
374
374
  `procproxy init` 生成自己的配置。
375
375
 
376
+ ### 初始化:生成一份可编辑配置
377
+
378
+ ```python
379
+ from proxycore.api import init_config
380
+
381
+ path = init_config(
382
+ "D:/cfg/procproxy.yaml", # 省略则用 PROCPROXY_CONFIG 或 ~/.procproxy/config.yaml
383
+ backend="windivert", # 真抓包需管理员;联调用 "null"
384
+ sink_port=19080,
385
+ )
386
+ print(path) # 绝对路径;等价于 procproxy init
387
+ ```
388
+
389
+ 生成的是最小模板,`upstreams` / `rules` 仍为空,需要按下面的案例补上。
390
+
391
+ ### 代理一个进程(最小完整配置)
392
+
393
+ ```yaml
394
+ version: 1
395
+ service:
396
+ sink: { host: 127.0.0.1, port: 19080 }
397
+ backend: windivert # 真抓包;联调改 null
398
+ profile:
399
+ name: chrome-via-socks
400
+ default_action: direct
401
+ dns_mode: remote
402
+ upstreams:
403
+ - { alias: socks, kind: socks5, host: 127.0.0.1, port: 1080, auth: none }
404
+ rules:
405
+ - { id: r-private, action: direct, targets: "*",
406
+ dst_ip: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8" }
407
+ - { id: r-chrome, action: proxy, upstream: socks,
408
+ targets: "chrome.exe", dst_port: "*" }
409
+ ```
410
+
411
+ ```python
412
+ from proxycore.api import start_proxy
413
+
414
+ with start_proxy("D:/cfg/chrome.yaml") as ppx: # 管理员终端
415
+ print(ppx.status())
416
+ print(ppx.sessions(limit=20))
417
+ ```
418
+
419
+ 规则自上而下、首个命中生效;启动后只影响**新建立**的连接。
420
+ 完整步骤、验证方法与常见坑见 [`docs/15-编程接口与SDK.md`](https://github.com/Longersking/procproxy/blob/master/docs/15-%E7%BC%96%E7%A8%8B%E6%8E%A5%E5%8F%A3%E4%B8%8ESDK.md)。
421
+
376
422
  ### 显式配置与测试转发
377
423
 
378
424
  ```python
@@ -541,7 +587,7 @@ src/proxycore/ 核心包
541
587
  └── service/ ipc.py Windows 服务 + 命名管道
542
588
  samples/ mock 服务 + 示例配置
543
589
  docs/ 文档(00 架构 / 07 规则 / 11 排错 / 14 抓包 / 15 SDK)
544
- tests/ 测试套件(619 passed / 4 skipped)
590
+ tests/ 测试套件(624 passed / 4 skipped)
545
591
  └── tools/ 验证脚本(run_demo / run_e2e / bench / …)
546
592
  ```
547
593
 
@@ -562,7 +608,7 @@ tests/ 测试套件(619 passed / 4 skipped)
562
608
 
563
609
  ## 排错
564
610
 
565
- 见 [`docs/11-调试排错手册.md`](docs/11-调试排错手册.md)(症状 → 定位 → 解决)。
611
+ 见 [`docs/11-调试排错手册.md`](https://github.com/Longersking/procproxy/blob/master/docs/11-%E8%B0%83%E8%AF%95%E6%8E%92%E9%94%99%E6%89%8B%E5%86%8C.md)(症状 → 定位 → 解决)。
566
612
 
567
613
  最常见的一条:**`[WinError 5] 拒绝访问` = 不是管理员**。
568
614
 
@@ -572,7 +618,7 @@ tests/ 测试套件(619 passed / 4 skipped)
572
618
 
573
619
  ```bash
574
620
  pip install -e ".[dev]"
575
- python -m pytest -q tests/ # 619 passed / 4 skipped(非管理员 Windows)
621
+ python -m pytest -q tests/ # 624 passed / 4 skipped(非管理员 Windows)
576
622
  python -m ruff check src tests
577
623
  python -m mypy src/proxycore
578
624
  ```
@@ -35,7 +35,7 @@ python procproxy.py doctor | rules | sessions | ... # 子命令(等价于 pyt
35
35
  pip install procproxy
36
36
  procproxy init # 生成 ~/.procproxy/config.yaml
37
37
  procproxy doctor # 环境自检
38
- procproxy forward WeChat.exe --port 8848 # 真实转发(需管理员)
38
+ procproxy forward --process WeChat.exe --port 8848 # 真实转发(需管理员)
39
39
  ```
40
40
 
41
41
  安装后直接运行 `procproxy` 会显示首次使用指引,不依赖任何样例文件。
@@ -211,7 +211,7 @@ python procproxy.py Weixin.exe WeChatAppEx.exe --port 8848
211
211
  ⚠️ 只有 ① 没有 ② → 端口填错了(**要填抓包工具的 MITM 监听口,不是它的 Web 管理口**)。
212
212
  ① ② 都正常但内容看不懂 → 那是 TLS,不是转发的问题。
213
213
 
214
- 完整指南(含排错与参数表)见 [`docs/14-把进程流量转发给抓包工具.md`](docs/14-把进程流量转发给抓包工具.md)。
214
+ 完整指南(含排错与参数表)见 [`docs/14-把进程流量转发给抓包工具.md`](https://github.com/Longersking/procproxy/blob/master/docs/14-%E6%8A%8A%E8%BF%9B%E7%A8%8B%E6%B5%81%E9%87%8F%E8%BD%AC%E5%8F%91%E7%BB%99%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7.md)。
215
215
 
216
216
  ---
217
217
 
@@ -301,14 +301,14 @@ rules: # 自上而下,**首个命中生
301
301
  7. 都没命中 → profile.default_action
302
302
  ```
303
303
 
304
- 完整语义见 [`docs/07-规则引擎与Profile格式.md`](docs/07-规则引擎与Profile格式.md)。
304
+ 完整语义见 [`docs/07-规则引擎与Profile格式.md`](https://github.com/Longersking/procproxy/blob/master/docs/07-%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E%E4%B8%8EProfile%E6%A0%BC%E5%BC%8F.md)。
305
305
 
306
306
  ---
307
307
 
308
308
  ## 编程接口
309
309
 
310
310
  `proxycore.api` 是与 CLI 共用同一套配置、规则引擎和会话表的 SDK。
311
- 完整教程见 [`docs/15-编程接口与SDK.md`](docs/15-编程接口与SDK.md)。
311
+ 完整教程见 [`docs/15-编程接口与SDK.md`](https://github.com/Longersking/procproxy/blob/master/docs/15-%E7%BC%96%E7%A8%8B%E6%8E%A5%E5%8F%A3%E4%B8%8ESDK.md)。
312
312
 
313
313
  ### 30 秒:零配置启动
314
314
 
@@ -334,6 +334,52 @@ with start_proxy() as ppx: # 不需要任何配置文件
334
334
  状态查询和联调;要真实抓包请用 `backend="windivert"`(需管理员)或
335
335
  `procproxy init` 生成自己的配置。
336
336
 
337
+ ### 初始化:生成一份可编辑配置
338
+
339
+ ```python
340
+ from proxycore.api import init_config
341
+
342
+ path = init_config(
343
+ "D:/cfg/procproxy.yaml", # 省略则用 PROCPROXY_CONFIG 或 ~/.procproxy/config.yaml
344
+ backend="windivert", # 真抓包需管理员;联调用 "null"
345
+ sink_port=19080,
346
+ )
347
+ print(path) # 绝对路径;等价于 procproxy init
348
+ ```
349
+
350
+ 生成的是最小模板,`upstreams` / `rules` 仍为空,需要按下面的案例补上。
351
+
352
+ ### 代理一个进程(最小完整配置)
353
+
354
+ ```yaml
355
+ version: 1
356
+ service:
357
+ sink: { host: 127.0.0.1, port: 19080 }
358
+ backend: windivert # 真抓包;联调改 null
359
+ profile:
360
+ name: chrome-via-socks
361
+ default_action: direct
362
+ dns_mode: remote
363
+ upstreams:
364
+ - { alias: socks, kind: socks5, host: 127.0.0.1, port: 1080, auth: none }
365
+ rules:
366
+ - { id: r-private, action: direct, targets: "*",
367
+ dst_ip: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8" }
368
+ - { id: r-chrome, action: proxy, upstream: socks,
369
+ targets: "chrome.exe", dst_port: "*" }
370
+ ```
371
+
372
+ ```python
373
+ from proxycore.api import start_proxy
374
+
375
+ with start_proxy("D:/cfg/chrome.yaml") as ppx: # 管理员终端
376
+ print(ppx.status())
377
+ print(ppx.sessions(limit=20))
378
+ ```
379
+
380
+ 规则自上而下、首个命中生效;启动后只影响**新建立**的连接。
381
+ 完整步骤、验证方法与常见坑见 [`docs/15-编程接口与SDK.md`](https://github.com/Longersking/procproxy/blob/master/docs/15-%E7%BC%96%E7%A8%8B%E6%8E%A5%E5%8F%A3%E4%B8%8ESDK.md)。
382
+
337
383
  ### 显式配置与测试转发
338
384
 
339
385
  ```python
@@ -502,7 +548,7 @@ src/proxycore/ 核心包
502
548
  └── service/ ipc.py Windows 服务 + 命名管道
503
549
  samples/ mock 服务 + 示例配置
504
550
  docs/ 文档(00 架构 / 07 规则 / 11 排错 / 14 抓包 / 15 SDK)
505
- tests/ 测试套件(619 passed / 4 skipped)
551
+ tests/ 测试套件(624 passed / 4 skipped)
506
552
  └── tools/ 验证脚本(run_demo / run_e2e / bench / …)
507
553
  ```
508
554
 
@@ -523,7 +569,7 @@ tests/ 测试套件(619 passed / 4 skipped)
523
569
 
524
570
  ## 排错
525
571
 
526
- 见 [`docs/11-调试排错手册.md`](docs/11-调试排错手册.md)(症状 → 定位 → 解决)。
572
+ 见 [`docs/11-调试排错手册.md`](https://github.com/Longersking/procproxy/blob/master/docs/11-%E8%B0%83%E8%AF%95%E6%8E%92%E9%94%99%E6%89%8B%E5%86%8C.md)(症状 → 定位 → 解决)。
527
573
 
528
574
  最常见的一条:**`[WinError 5] 拒绝访问` = 不是管理员**。
529
575
 
@@ -533,7 +579,7 @@ tests/ 测试套件(619 passed / 4 skipped)
533
579
 
534
580
  ```bash
535
581
  pip install -e ".[dev]"
536
- python -m pytest -q tests/ # 619 passed / 4 skipped(非管理员 Windows)
582
+ python -m pytest -q tests/ # 624 passed / 4 skipped(非管理员 Windows)
537
583
  python -m ruff check src tests
538
584
  python -m mypy src/proxycore
539
585
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "procproxy"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "ProcProxy —— 进程级代理:按进程决定哪条连接走哪个上游代理"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: procproxy
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: ProcProxy —— 进程级代理:按进程决定哪条连接走哪个上游代理
5
5
  Author: Longersking
6
6
  License-Expression: MIT
@@ -74,7 +74,7 @@ python procproxy.py doctor | rules | sessions | ... # 子命令(等价于 pyt
74
74
  pip install procproxy
75
75
  procproxy init # 生成 ~/.procproxy/config.yaml
76
76
  procproxy doctor # 环境自检
77
- procproxy forward WeChat.exe --port 8848 # 真实转发(需管理员)
77
+ procproxy forward --process WeChat.exe --port 8848 # 真实转发(需管理员)
78
78
  ```
79
79
 
80
80
  安装后直接运行 `procproxy` 会显示首次使用指引,不依赖任何样例文件。
@@ -250,7 +250,7 @@ python procproxy.py Weixin.exe WeChatAppEx.exe --port 8848
250
250
  ⚠️ 只有 ① 没有 ② → 端口填错了(**要填抓包工具的 MITM 监听口,不是它的 Web 管理口**)。
251
251
  ① ② 都正常但内容看不懂 → 那是 TLS,不是转发的问题。
252
252
 
253
- 完整指南(含排错与参数表)见 [`docs/14-把进程流量转发给抓包工具.md`](docs/14-把进程流量转发给抓包工具.md)。
253
+ 完整指南(含排错与参数表)见 [`docs/14-把进程流量转发给抓包工具.md`](https://github.com/Longersking/procproxy/blob/master/docs/14-%E6%8A%8A%E8%BF%9B%E7%A8%8B%E6%B5%81%E9%87%8F%E8%BD%AC%E5%8F%91%E7%BB%99%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7.md)。
254
254
 
255
255
  ---
256
256
 
@@ -340,14 +340,14 @@ rules: # 自上而下,**首个命中生
340
340
  7. 都没命中 → profile.default_action
341
341
  ```
342
342
 
343
- 完整语义见 [`docs/07-规则引擎与Profile格式.md`](docs/07-规则引擎与Profile格式.md)。
343
+ 完整语义见 [`docs/07-规则引擎与Profile格式.md`](https://github.com/Longersking/procproxy/blob/master/docs/07-%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E%E4%B8%8EProfile%E6%A0%BC%E5%BC%8F.md)。
344
344
 
345
345
  ---
346
346
 
347
347
  ## 编程接口
348
348
 
349
349
  `proxycore.api` 是与 CLI 共用同一套配置、规则引擎和会话表的 SDK。
350
- 完整教程见 [`docs/15-编程接口与SDK.md`](docs/15-编程接口与SDK.md)。
350
+ 完整教程见 [`docs/15-编程接口与SDK.md`](https://github.com/Longersking/procproxy/blob/master/docs/15-%E7%BC%96%E7%A8%8B%E6%8E%A5%E5%8F%A3%E4%B8%8ESDK.md)。
351
351
 
352
352
  ### 30 秒:零配置启动
353
353
 
@@ -373,6 +373,52 @@ with start_proxy() as ppx: # 不需要任何配置文件
373
373
  状态查询和联调;要真实抓包请用 `backend="windivert"`(需管理员)或
374
374
  `procproxy init` 生成自己的配置。
375
375
 
376
+ ### 初始化:生成一份可编辑配置
377
+
378
+ ```python
379
+ from proxycore.api import init_config
380
+
381
+ path = init_config(
382
+ "D:/cfg/procproxy.yaml", # 省略则用 PROCPROXY_CONFIG 或 ~/.procproxy/config.yaml
383
+ backend="windivert", # 真抓包需管理员;联调用 "null"
384
+ sink_port=19080,
385
+ )
386
+ print(path) # 绝对路径;等价于 procproxy init
387
+ ```
388
+
389
+ 生成的是最小模板,`upstreams` / `rules` 仍为空,需要按下面的案例补上。
390
+
391
+ ### 代理一个进程(最小完整配置)
392
+
393
+ ```yaml
394
+ version: 1
395
+ service:
396
+ sink: { host: 127.0.0.1, port: 19080 }
397
+ backend: windivert # 真抓包;联调改 null
398
+ profile:
399
+ name: chrome-via-socks
400
+ default_action: direct
401
+ dns_mode: remote
402
+ upstreams:
403
+ - { alias: socks, kind: socks5, host: 127.0.0.1, port: 1080, auth: none }
404
+ rules:
405
+ - { id: r-private, action: direct, targets: "*",
406
+ dst_ip: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8" }
407
+ - { id: r-chrome, action: proxy, upstream: socks,
408
+ targets: "chrome.exe", dst_port: "*" }
409
+ ```
410
+
411
+ ```python
412
+ from proxycore.api import start_proxy
413
+
414
+ with start_proxy("D:/cfg/chrome.yaml") as ppx: # 管理员终端
415
+ print(ppx.status())
416
+ print(ppx.sessions(limit=20))
417
+ ```
418
+
419
+ 规则自上而下、首个命中生效;启动后只影响**新建立**的连接。
420
+ 完整步骤、验证方法与常见坑见 [`docs/15-编程接口与SDK.md`](https://github.com/Longersking/procproxy/blob/master/docs/15-%E7%BC%96%E7%A8%8B%E6%8E%A5%E5%8F%A3%E4%B8%8ESDK.md)。
421
+
376
422
  ### 显式配置与测试转发
377
423
 
378
424
  ```python
@@ -541,7 +587,7 @@ src/proxycore/ 核心包
541
587
  └── service/ ipc.py Windows 服务 + 命名管道
542
588
  samples/ mock 服务 + 示例配置
543
589
  docs/ 文档(00 架构 / 07 规则 / 11 排错 / 14 抓包 / 15 SDK)
544
- tests/ 测试套件(619 passed / 4 skipped)
590
+ tests/ 测试套件(624 passed / 4 skipped)
545
591
  └── tools/ 验证脚本(run_demo / run_e2e / bench / …)
546
592
  ```
547
593
 
@@ -562,7 +608,7 @@ tests/ 测试套件(619 passed / 4 skipped)
562
608
 
563
609
  ## 排错
564
610
 
565
- 见 [`docs/11-调试排错手册.md`](docs/11-调试排错手册.md)(症状 → 定位 → 解决)。
611
+ 见 [`docs/11-调试排错手册.md`](https://github.com/Longersking/procproxy/blob/master/docs/11-%E8%B0%83%E8%AF%95%E6%8E%92%E9%94%99%E6%89%8B%E5%86%8C.md)(症状 → 定位 → 解决)。
566
612
 
567
613
  最常见的一条:**`[WinError 5] 拒绝访问` = 不是管理员**。
568
614
 
@@ -572,7 +618,7 @@ tests/ 测试套件(619 passed / 4 skipped)
572
618
 
573
619
  ```bash
574
620
  pip install -e ".[dev]"
575
- python -m pytest -q tests/ # 619 passed / 4 skipped(非管理员 Windows)
621
+ python -m pytest -q tests/ # 624 passed / 4 skipped(非管理员 Windows)
576
622
  python -m ruff check src tests
577
623
  python -m mypy src/proxycore
578
624
  ```
@@ -14,6 +14,7 @@ src/proxycore/canproxy.py
14
14
  src/proxycore/checkroute.py
15
15
  src/proxycore/cli.py
16
16
  src/proxycore/config.py
17
+ src/proxycore/config_init.py
17
18
  src/proxycore/config_source.py
18
19
  src/proxycore/forward.py
19
20
  src/proxycore/ipc.py
@@ -2,6 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.1.2"
5
+ __version__ = "0.1.3"
6
6
 
7
7
  __all__ = ["__version__"]
@@ -50,6 +50,7 @@ from pathlib import Path
50
50
  from typing import Any
51
51
 
52
52
  from .config import Config, ConfigError, ConfigManager
53
+ from .config_init import create_config_file
53
54
  from .config_source import ORIGIN_EXPLICIT, ORIGIN_OBJECT, normalize_config_path, resolve_config_source
54
55
  from .log import EventLogger, build_logger, get_logger
55
56
  from .runtime import Runtime
@@ -90,6 +91,34 @@ def load_config(
90
91
  return config
91
92
 
92
93
 
94
+ def init_config(
95
+ path: str | os.PathLike[str] | None = None,
96
+ *,
97
+ backend: str = "null",
98
+ sink_port: int = 19080,
99
+ force: bool = False,
100
+ ) -> Path:
101
+ """从内置模板生成一份可编辑配置,返回绝对路径。
102
+
103
+ 等价于 `procproxy init`,但可以在 Python 里调用:
104
+
105
+ ```python
106
+ from proxycore.api import init_config
107
+
108
+ path = init_config(backend="windivert", sink_port=19080)
109
+ # 之后编辑 path 里的 upstreams / rules,再用 start_proxy(path) 启动
110
+ ```
111
+
112
+ * `path=None` → `PROCPROXY_CONFIG` 或 `~/.procproxy/config.yaml`;
113
+ * 目标已存在时默认拒绝覆盖(`config_exists`),`force=True` 才覆盖;
114
+ * 失败抛 `ProxyStartError`,`code` 同底层 `ConfigError.code`。
115
+ """
116
+ try:
117
+ return create_config_file(path, backend=backend, sink_port=sink_port, force=force)
118
+ except ConfigError as exc:
119
+ raise _wrap_config_error(exc) from exc
120
+
121
+
93
122
  def _resolve_and_load(
94
123
  config: Config | str | os.PathLike[str] | None, *, check_sink: bool = False
95
124
  ) -> Config:
@@ -308,6 +337,7 @@ def run_forever(config: Config | str | os.PathLike[str] | None = None, **kwargs:
308
337
  __all__ = [
309
338
  "ProxyHandle",
310
339
  "ProxyStartError",
340
+ "init_config",
311
341
  "load_config",
312
342
  "make_runtime",
313
343
  "run_forever",
@@ -16,10 +16,17 @@ import time
16
16
  from pathlib import Path
17
17
  from typing import Any
18
18
 
19
- import yaml
20
-
21
19
  from proxycore import __version__
22
- from proxycore.config import E_CONFIG_NOT_FOUND, Config, ConfigError, ConfigManager, port_in_use
20
+ from proxycore.config import (
21
+ E_CONFIG_ARGUMENT_INVALID,
22
+ E_CONFIG_EXISTS,
23
+ E_CONFIG_NOT_FOUND,
24
+ E_SINK_PORT_UNAVAILABLE,
25
+ Config,
26
+ ConfigError,
27
+ ConfigManager,
28
+ port_in_use,
29
+ )
23
30
  from proxycore.config_source import (
24
31
  DEFAULT_CONFIG,
25
32
  ORIGIN_ENV,
@@ -243,36 +250,25 @@ def cmd_config(args: argparse.Namespace) -> int:
243
250
 
244
251
  def cmd_init(args: argparse.Namespace) -> int:
245
252
  """生成一份可编辑的启动配置(PyPI 安装后首次使用)。"""
246
- target = Path(args.path) if args.path else user_config_path()
247
- if target.exists() and not args.force:
248
- print(f"配置文件已存在: {target}(加 --force 覆盖)", file=sys.stderr)
249
- return 2
250
- if not 1024 <= args.sink_port <= 65535:
251
- print("--sink-port 必须在 1024-65535", file=sys.stderr)
252
- return 2
253
- packaged = packaged_default_config()
254
- if packaged is None:
255
- print("安装包缺少内置默认配置,请重新安装 procproxy", file=sys.stderr)
256
- return 2
253
+ from proxycore.config_init import create_config_file
257
254
 
258
- doc = yaml.safe_load(packaged.read_text(encoding="utf-8")) or {}
259
- service = doc.setdefault("service", {})
260
- service["backend"] = args.backend
261
- sink = service.setdefault("sink", {})
262
- sink.setdefault("host", "127.0.0.1")
263
- sink["port"] = args.sink_port
264
- target.parent.mkdir(parents=True, exist_ok=True)
265
- header = "# ProcProxy 配置(由 `procproxy init` 生成)\n"
266
- target.write_text(
267
- header + yaml.safe_dump(doc, allow_unicode=True, sort_keys=False),
268
- encoding="utf-8",
269
- )
270
255
  try:
271
- Config.load(target)
256
+ target = create_config_file(
257
+ args.path or None,
258
+ backend=args.backend,
259
+ sink_port=args.sink_port,
260
+ force=args.force,
261
+ )
272
262
  except ConfigError as exc:
273
- with contextlib.suppress(OSError):
274
- target.unlink()
275
- print(f"生成的配置未通过校验:{exc.code} — {exc.message}", file=sys.stderr)
263
+ if exc.code in {
264
+ E_CONFIG_EXISTS,
265
+ E_CONFIG_NOT_FOUND,
266
+ E_SINK_PORT_UNAVAILABLE,
267
+ E_CONFIG_ARGUMENT_INVALID,
268
+ }:
269
+ print(exc.message, file=sys.stderr)
270
+ else:
271
+ print(f"生成的配置未通过校验:{exc.code} — {exc.message}", file=sys.stderr)
276
272
  return 2
277
273
 
278
274
  print(f"已生成配置: {target}")
@@ -44,6 +44,8 @@ E_PROFILE_OPTION_UNSUPPORTED = "profile_option_unsupported"
44
44
  E_CONFIG_NOT_FOUND = "config_not_found"
45
45
  E_CONFIG_UNREADABLE = "config_unreadable"
46
46
  E_CONFIG_PARSE_INVALID = "config_parse_invalid"
47
+ E_CONFIG_EXISTS = "config_exists"
48
+ E_CONFIG_ARGUMENT_INVALID = "config_invalid_argument"
47
49
  # 运行时错误码
48
50
  E_BACKEND_UNAVAILABLE = "E_BACKEND_UNAVAILABLE"
49
51
 
@@ -0,0 +1,85 @@
1
+ """生成一份可编辑的启动配置(CLI `init` 与 Python `api.init_config` 共用)。
2
+
3
+ 模板来自安装包里的 `data/default.yaml`,只覆盖 backend 与 sink 端口;
4
+ upstreams / rules 留空,由用户或调用方按需填写。写入后立刻用
5
+ `Config.load` 校验,失败则删除半成品文件。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from pathlib import Path
12
+
13
+ import yaml
14
+
15
+ from .config import (
16
+ E_CONFIG_ARGUMENT_INVALID,
17
+ E_CONFIG_EXISTS,
18
+ E_CONFIG_NOT_FOUND,
19
+ E_CONFIG_UNREADABLE,
20
+ E_SINK_PORT_UNAVAILABLE,
21
+ Config,
22
+ ConfigError,
23
+ )
24
+ from .config_source import packaged_default_config, user_config_path
25
+
26
+ VALID_BACKENDS = ("null", "windivert")
27
+
28
+
29
+ def create_config_file(
30
+ path: str | os.PathLike[str] | None = None,
31
+ *,
32
+ backend: str = "null",
33
+ sink_port: int = 19080,
34
+ force: bool = False,
35
+ ) -> Path:
36
+ """从内置模板生成配置文件,返回绝对路径。
37
+
38
+ * `path=None`:写到 `PROCPROXY_CONFIG` 或 `~/.procproxy/config.yaml`;
39
+ * `force=False`:目标已存在时抛 `config_exists`,绝不覆盖;
40
+ * `backend`:`null`(联调)或 `windivert`(真抓包,需管理员);
41
+ * 生成后立即校验,校验失败会删除文件并抛出原始 `ConfigError`。
42
+ """
43
+ target = (Path(path) if path else user_config_path()).expanduser()
44
+ target = (Path.cwd() / target).resolve() if not target.is_absolute() else target.resolve()
45
+
46
+ if target.exists() and not force:
47
+ raise ConfigError(E_CONFIG_EXISTS, f"配置文件已存在: {target}(force=True 覆盖)")
48
+ if backend not in VALID_BACKENDS:
49
+ raise ConfigError(
50
+ E_CONFIG_ARGUMENT_INVALID,
51
+ f"未知 backend {backend!r}(可选:{', '.join(VALID_BACKENDS)})",
52
+ )
53
+ if not 1024 <= sink_port <= 65535:
54
+ raise ConfigError(
55
+ E_SINK_PORT_UNAVAILABLE,
56
+ f"sink 端口必须在 1024-65535,当前 {sink_port}",
57
+ )
58
+
59
+ packaged = packaged_default_config()
60
+ if packaged is None:
61
+ raise ConfigError(E_CONFIG_NOT_FOUND, "安装包缺少内置默认配置,请重新安装 procproxy")
62
+
63
+ doc = yaml.safe_load(packaged.read_text(encoding="utf-8")) or {}
64
+ service = doc.setdefault("service", {})
65
+ service["backend"] = backend
66
+ sink = service.setdefault("sink", {})
67
+ sink.setdefault("host", "127.0.0.1")
68
+ sink["port"] = sink_port
69
+
70
+ header = "# ProcProxy 配置(由 procproxy init / proxycore.api.init_config 生成)\n"
71
+ try:
72
+ target.parent.mkdir(parents=True, exist_ok=True)
73
+ target.write_text(
74
+ header + yaml.safe_dump(doc, allow_unicode=True, sort_keys=False),
75
+ encoding="utf-8",
76
+ )
77
+ except OSError as exc:
78
+ raise ConfigError(E_CONFIG_UNREADABLE, f"无法写入配置 {target}: {exc}") from exc
79
+
80
+ try:
81
+ Config.load(target)
82
+ except ConfigError:
83
+ target.unlink(missing_ok=True)
84
+ raise
85
+ return target
File without changes
File without changes