procproxy 0.1.0__tar.gz → 0.1.2__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.0/src/procproxy.egg-info → procproxy-0.1.2}/PKG-INFO +162 -16
  2. {procproxy-0.1.0 → procproxy-0.1.2}/README.md +161 -15
  3. {procproxy-0.1.0 → procproxy-0.1.2}/pyproject.toml +4 -1
  4. {procproxy-0.1.0 → procproxy-0.1.2/src/procproxy.egg-info}/PKG-INFO +162 -16
  5. {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/SOURCES.txt +2 -0
  6. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/__init__.py +1 -1
  7. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/__main__.py +7 -7
  8. procproxy-0.1.2/src/proxycore/api.py +315 -0
  9. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/canproxy.py +23 -18
  10. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/checkroute.py +14 -5
  11. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/cli.py +135 -37
  12. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/config.py +28 -6
  13. procproxy-0.1.2/src/proxycore/config_source.py +154 -0
  14. procproxy-0.1.2/src/proxycore/data/default.yaml +15 -0
  15. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/service/__init__.py +11 -2
  16. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/service/__main__.py +19 -5
  17. procproxy-0.1.0/src/proxycore/api.py +0 -226
  18. {procproxy-0.1.0 → procproxy-0.1.2}/LICENSE +0 -0
  19. {procproxy-0.1.0 → procproxy-0.1.2}/setup.cfg +0 -0
  20. {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/dependency_links.txt +0 -0
  21. {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/entry_points.txt +0 -0
  22. {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/requires.txt +0 -0
  23. {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/top_level.txt +0 -0
  24. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/__init__.py +0 -0
  25. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/base.py +0 -0
  26. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/null.py +0 -0
  27. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/__init__.py +0 -0
  28. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/filter.py +0 -0
  29. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/pidmap.py +0 -0
  30. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/pkt.py +0 -0
  31. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/__init__.py +0 -0
  32. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/cache.py +0 -0
  33. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/modes.py +0 -0
  34. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/udp.py +0 -0
  35. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/forward.py +0 -0
  36. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/ipc.py +0 -0
  37. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/log.py +0 -0
  38. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/pathsetup.py +0 -0
  39. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/probe.py +0 -0
  40. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/proc.py +0 -0
  41. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/rules.py +0 -0
  42. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/runtime.py +0 -0
  43. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/__init__.py +0 -0
  44. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/relay.py +0 -0
  45. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/sessions.py +0 -0
  46. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/sink.py +0 -0
  47. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/types.py +0 -0
  48. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/__init__.py +0 -0
  49. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/base.py +0 -0
  50. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/chain.py +0 -0
  51. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/http_connect.py +0 -0
  52. {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/socks4.py +0 -0
  53. {procproxy-0.1.0 → procproxy-0.1.2}/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.0
3
+ Version: 0.1.2
4
4
  Summary: ProcProxy —— 进程级代理:按进程决定哪条连接走哪个上游代理
5
5
  Author: Longersking
6
6
  License-Expression: MIT
@@ -68,8 +68,26 @@ python procproxy.py doctor | rules | sessions | ... # 子命令(等价于 pyt
68
68
 
69
69
  需要 **Python 3.10+**。
70
70
 
71
+ ### 从 PyPI 安装(推荐)
72
+
73
+ ```bash
74
+ pip install procproxy
75
+ procproxy init # 生成 ~/.procproxy/config.yaml
76
+ procproxy doctor # 环境自检
77
+ procproxy forward WeChat.exe --port 8848 # 真实转发(需管理员)
78
+ ```
79
+
80
+ 安装后直接运行 `procproxy` 会显示首次使用指引,不依赖任何样例文件。
81
+ Python SDK 也一样:`start_proxy()` 不传配置即可启动(内置 `backend=null`),
82
+ 不需要先跑 `init`;`init` 只用于生成可编辑配置。
83
+
84
+ 非管理员:WinDivert 抓包必须管理员;可以用 `--backend null` 做转发链路联调,
85
+ 或在服务已启动、且配置了 `service.ipc_allow_group` 时读取状态。
86
+
87
+ ### 源码开发安装
88
+
71
89
  ```bash
72
- pip install -e .
90
+ pip install -e ".[dev]"
73
91
  # 或者不装(零依赖方式,往 site-packages 写一个 .pth,让 python -m proxycore 全局可用)
74
92
  python procproxy.py install-path
75
93
  ```
@@ -77,7 +95,7 @@ python procproxy.py install-path
77
95
  数据面依赖(要用真抓包就必需):
78
96
 
79
97
  ```bash
80
- pip install pydivert psutil
98
+ pip install "procproxy[windivert]"
81
99
  ```
82
100
 
83
101
  > `pydivert` 自带**已签名**的 `WinDivert64.sys` —— 所以**不需要**开测试签名、不需要自己签驱动。
@@ -328,24 +346,151 @@ rules: # 自上而下,**首个命中生
328
346
 
329
347
  ## 编程接口
330
348
 
349
+ `proxycore.api` 是与 CLI 共用同一套配置、规则引擎和会话表的 SDK。
350
+ 完整教程见 [`docs/15-编程接口与SDK.md`](docs/15-编程接口与SDK.md)。
351
+
352
+ ### 30 秒:零配置启动
353
+
331
354
  ```python
332
- from proxycore import api
355
+ from proxycore.api import start_proxy
333
356
 
334
- # 上下文管理器:退出自动停
335
- with api.start_proxy("samples/profiles/dev.yaml") as handle:
336
- print(handle.status())
357
+ with start_proxy() as ppx: # 不需要任何配置文件
358
+ print(ppx.config_source) # "packaged"
359
+ print(ppx.config_path) # .../proxycore/data/default.yaml
360
+ print(ppx.status()) # 后端 / 规则 / 会话 / UDP / 配置来源
361
+ ```
362
+
363
+ 不传配置时的来源优先级:
337
364
 
338
- # 测试模式:sink 把所有连接转发到固定目标(不做抓包)
339
- with api.start_proxy("samples/profiles/dev.yaml", backend="null",
340
- sink_test_target="127.0.0.1:8080") as handle:
365
+ | 优先级 | 来源 | 说明 |
366
+ |---|---|---|
367
+ | 1 | `start_proxy(path)` | 显式路径(也接受内存中的 `Config` 对象) |
368
+ | 2 | `PROCPROXY_CONFIG` | 指向不存在文件会直接报错,不回退 |
369
+ | 3 | `~/.procproxy/config.yaml` | `procproxy init` 生成的用户配置 |
370
+ | 4 | 包内 `data/default.yaml` | `backend=null`,不抓包、不改道 |
371
+
372
+ 内置配置**不会**监听 sink,也**不会**创建任何文件。它适合装配、规则决策、
373
+ 状态查询和联调;要真实抓包请用 `backend="windivert"`(需管理员)或
374
+ `procproxy init` 生成自己的配置。
375
+
376
+ ### 显式配置与测试转发
377
+
378
+ ```python
379
+ from proxycore.api import start_proxy
380
+
381
+ # 显式路径 + 真实抓包(需要管理员)
382
+ with start_proxy(r"D:\cfg\procproxy.yaml", backend="windivert") as ppx:
383
+ print(ppx.sink) # sink 监听地址
384
+ print(ppx.sessions(limit=20))
385
+
386
+ # 联调模式:backend=null + 把 sink 收到的连接固定转发到目标
387
+ with start_proxy(backend="null", sink_test_target="127.0.0.1:8080",
388
+ upstream="local-mock") as ppx:
341
389
  ...
390
+ # curl -x http://127.0.0.1:19080 http://any/ → 127.0.0.1:8080
391
+ ```
392
+
393
+ ### 在代码里构建配置
394
+
395
+ 可以从字典构造 `Config`,也可以先落盘再按路径加载:
342
396
 
343
- # 只要规则决策,不要监听
344
- runtime = api.make_runtime("samples/profiles/dev.yaml", backend="null")
397
+ ```python
398
+ from proxycore.config import Config, atomic_write
399
+ from proxycore.api import start_proxy
400
+
401
+ doc = {
402
+ "version": 1,
403
+ "service": {"sink": {"host": "127.0.0.1", "port": 19080}, "backend": "null"},
404
+ "profile": {
405
+ "name": "myapp",
406
+ "default_action": "direct",
407
+ "dns_mode": "remote",
408
+ "rules": [
409
+ {"id": "r1", "action": "proxy", "upstream": "office",
410
+ "targets": "chrome.exe;msedge.exe", "dst_port": "443,80"},
411
+ ],
412
+ },
413
+ "upstreams": [
414
+ {"alias": "office", "kind": "socks5", "host": "127.0.0.1", "port": 1080},
415
+ ],
416
+ }
417
+
418
+ cfg = Config.from_dict(doc)
419
+ cfg.validate() # from_dict 只解析,不校验
420
+ atomic_write(cfg, "myconfig.yaml") # 临时文件 → 校验 → 原子替换
421
+
422
+ with start_proxy("myconfig.yaml") as ppx:
423
+ ...
424
+ ```
425
+
426
+ > `Config.from_dict()` 不会解析 `password_ref`;需要凭据时请用
427
+ > `Config.load()` / `start_proxy(path)`,或自己给 `upstream.password` 赋值。
428
+
429
+ ### 只做决策,不启动数据面
430
+
431
+ ```python
432
+ from proxycore.api import make_runtime
433
+
434
+ runtime = make_runtime() # 默认用内置配置,backend=null
345
435
  decision = runtime.decide(conn_request)
436
+ print(decision.action, decision.reason)
437
+ ```
438
+
439
+ ### 生命周期与句柄
440
+
441
+ ```python
442
+ ppx = start_proxy()
443
+ try:
444
+ print(ppx.status())
445
+ finally:
446
+ ppx.stop() # 幂等;with 退出时也会自动调用
447
+ ```
448
+
449
+ | 成员 | 说明 |
450
+ |---|---|
451
+ | `status()` | 后端、规则、会话、熔断、DNS、UDP、sink、`config_path`、`config_source` |
452
+ | `sessions(limit=50)` / `active_sessions()` | 会话明细 / 活跃数 |
453
+ | `reload(path="")` | 热重载;不传路径则重读当前文件。`sink.port`/`backend` 变更需重启 |
454
+ | `decide(conn)` | 只问规则引擎,不抓包、不改道 |
455
+ | `profile` | 当前生效的 `Profile` |
456
+ | `config_path` / `config_source` | 当前配置的绝对路径与来源 |
457
+ | `running` | 是否仍在运行 |
458
+
459
+ `start_proxy()` 会启动后台维护线程;`run_forever()` 会阻塞到 Ctrl+C。
460
+ 请在主线程使用,并在退出时 `stop()`(或使用 `with`)。
461
+
462
+ ### 错误处理
463
+
464
+ 配置 / 后端 / 启动失败统一抛 `ProxyStartError`,`code` 是稳定的机器可读标识:
465
+
466
+ ```python
467
+ from proxycore.api import ProxyStartError, start_proxy
468
+
469
+ try:
470
+ start_proxy("missing.yaml")
471
+ except ProxyStartError as exc:
472
+ print(exc.code) # config_not_found
346
473
  ```
347
474
 
348
- 跑 `python tests/tools/api_demo.py` 看三种用法的完整演示。
475
+ | code | 含义 |
476
+ |---|---|
477
+ | `config_not_found` | 配置文件不存在(显式路径 / 环境变量不会回退) |
478
+ | `config_unreadable` | 文件存在但读不了(权限、目录等) |
479
+ | `config_parse_invalid` | YAML 语法错误 / 根节点非映射 / 非 UTF-8 |
480
+ | `config_version_unsupported` | `version != 1` |
481
+ | `rule_upstream_missing` 等 | 规则或上游校验失败(完整列表见文档) |
482
+ | `E_BACKEND_UNAVAILABLE` | 后端不可用(未知后端、WinDivert 不可加载等) |
483
+
484
+ `api.load_config()` 同样抛 `ProxyStartError`,不是 `ConfigError`。
485
+
486
+ ### 环境变量
487
+
488
+ | 变量 | 作用 |
489
+ |---|---|
490
+ | `PROCPROXY_CONFIG` | 配置文件路径;显式路径之后、用户配置之前生效 |
491
+ | `PROCPROXY_RUNTIME_DIR` | 运行时状态/日志目录(CLI 与服务使用) |
492
+
493
+ 跑 `python tests/tools/api_demo.py` 看完整演示。
349
494
 
350
495
  ---
351
496
 
@@ -390,12 +535,13 @@ src/proxycore/ 核心包
390
535
  ├── tunnel/ sink + relay(本地落地与双向转发)
391
536
  ├── upstream/ SOCKS5 / SOCKS4 / HTTP CONNECT / 跳链
392
537
  ├── dns/ DNS 三模式与 UDP
538
+ ├── config_source.py 配置来源解析(显式 / env / 用户 / 样例 / 内置)
393
539
  ├── forward.py ← procproxy.py <进程名> 的实现
394
540
  ├── canproxy.py / checkroute.py / probe.py / pathsetup.py ← 其余子命令
395
541
  └── service/ ipc.py Windows 服务 + 命名管道
396
542
  samples/ mock 服务 + 示例配置
397
- docs/ 文档(00 架构与阅读路线 / 07 规则语义 / 11 排错 / 14 转发到抓包工具)
398
- tests/ 测试套件(555 passed)
543
+ docs/ 文档(00 架构 / 07 规则 / 11 排错 / 14 抓包 / 15 SDK)
544
+ tests/ 测试套件(619 passed / 4 skipped)
399
545
  └── tools/ 验证脚本(run_demo / run_e2e / bench / …)
400
546
  ```
401
547
 
@@ -426,7 +572,7 @@ tests/ 测试套件(555 passed)
426
572
 
427
573
  ```bash
428
574
  pip install -e ".[dev]"
429
- python -m pytest -q tests/ # 555 passed / 4 skipped(非管理员 Windows)
575
+ python -m pytest -q tests/ # 619 passed / 4 skipped(非管理员 Windows)
430
576
  python -m ruff check src tests
431
577
  python -m mypy src/proxycore
432
578
  ```
@@ -29,8 +29,26 @@ python procproxy.py doctor | rules | sessions | ... # 子命令(等价于 pyt
29
29
 
30
30
  需要 **Python 3.10+**。
31
31
 
32
+ ### 从 PyPI 安装(推荐)
33
+
34
+ ```bash
35
+ pip install procproxy
36
+ procproxy init # 生成 ~/.procproxy/config.yaml
37
+ procproxy doctor # 环境自检
38
+ procproxy forward WeChat.exe --port 8848 # 真实转发(需管理员)
39
+ ```
40
+
41
+ 安装后直接运行 `procproxy` 会显示首次使用指引,不依赖任何样例文件。
42
+ Python SDK 也一样:`start_proxy()` 不传配置即可启动(内置 `backend=null`),
43
+ 不需要先跑 `init`;`init` 只用于生成可编辑配置。
44
+
45
+ 非管理员:WinDivert 抓包必须管理员;可以用 `--backend null` 做转发链路联调,
46
+ 或在服务已启动、且配置了 `service.ipc_allow_group` 时读取状态。
47
+
48
+ ### 源码开发安装
49
+
32
50
  ```bash
33
- pip install -e .
51
+ pip install -e ".[dev]"
34
52
  # 或者不装(零依赖方式,往 site-packages 写一个 .pth,让 python -m proxycore 全局可用)
35
53
  python procproxy.py install-path
36
54
  ```
@@ -38,7 +56,7 @@ python procproxy.py install-path
38
56
  数据面依赖(要用真抓包就必需):
39
57
 
40
58
  ```bash
41
- pip install pydivert psutil
59
+ pip install "procproxy[windivert]"
42
60
  ```
43
61
 
44
62
  > `pydivert` 自带**已签名**的 `WinDivert64.sys` —— 所以**不需要**开测试签名、不需要自己签驱动。
@@ -289,24 +307,151 @@ rules: # 自上而下,**首个命中生
289
307
 
290
308
  ## 编程接口
291
309
 
310
+ `proxycore.api` 是与 CLI 共用同一套配置、规则引擎和会话表的 SDK。
311
+ 完整教程见 [`docs/15-编程接口与SDK.md`](docs/15-编程接口与SDK.md)。
312
+
313
+ ### 30 秒:零配置启动
314
+
292
315
  ```python
293
- from proxycore import api
316
+ from proxycore.api import start_proxy
294
317
 
295
- # 上下文管理器:退出自动停
296
- with api.start_proxy("samples/profiles/dev.yaml") as handle:
297
- print(handle.status())
318
+ with start_proxy() as ppx: # 不需要任何配置文件
319
+ print(ppx.config_source) # "packaged"
320
+ print(ppx.config_path) # .../proxycore/data/default.yaml
321
+ print(ppx.status()) # 后端 / 规则 / 会话 / UDP / 配置来源
322
+ ```
323
+
324
+ 不传配置时的来源优先级:
298
325
 
299
- # 测试模式:sink 把所有连接转发到固定目标(不做抓包)
300
- with api.start_proxy("samples/profiles/dev.yaml", backend="null",
301
- sink_test_target="127.0.0.1:8080") as handle:
326
+ | 优先级 | 来源 | 说明 |
327
+ |---|---|---|
328
+ | 1 | `start_proxy(path)` | 显式路径(也接受内存中的 `Config` 对象) |
329
+ | 2 | `PROCPROXY_CONFIG` | 指向不存在文件会直接报错,不回退 |
330
+ | 3 | `~/.procproxy/config.yaml` | `procproxy init` 生成的用户配置 |
331
+ | 4 | 包内 `data/default.yaml` | `backend=null`,不抓包、不改道 |
332
+
333
+ 内置配置**不会**监听 sink,也**不会**创建任何文件。它适合装配、规则决策、
334
+ 状态查询和联调;要真实抓包请用 `backend="windivert"`(需管理员)或
335
+ `procproxy init` 生成自己的配置。
336
+
337
+ ### 显式配置与测试转发
338
+
339
+ ```python
340
+ from proxycore.api import start_proxy
341
+
342
+ # 显式路径 + 真实抓包(需要管理员)
343
+ with start_proxy(r"D:\cfg\procproxy.yaml", backend="windivert") as ppx:
344
+ print(ppx.sink) # sink 监听地址
345
+ print(ppx.sessions(limit=20))
346
+
347
+ # 联调模式:backend=null + 把 sink 收到的连接固定转发到目标
348
+ with start_proxy(backend="null", sink_test_target="127.0.0.1:8080",
349
+ upstream="local-mock") as ppx:
302
350
  ...
351
+ # curl -x http://127.0.0.1:19080 http://any/ → 127.0.0.1:8080
352
+ ```
353
+
354
+ ### 在代码里构建配置
355
+
356
+ 可以从字典构造 `Config`,也可以先落盘再按路径加载:
303
357
 
304
- # 只要规则决策,不要监听
305
- runtime = api.make_runtime("samples/profiles/dev.yaml", backend="null")
358
+ ```python
359
+ from proxycore.config import Config, atomic_write
360
+ from proxycore.api import start_proxy
361
+
362
+ doc = {
363
+ "version": 1,
364
+ "service": {"sink": {"host": "127.0.0.1", "port": 19080}, "backend": "null"},
365
+ "profile": {
366
+ "name": "myapp",
367
+ "default_action": "direct",
368
+ "dns_mode": "remote",
369
+ "rules": [
370
+ {"id": "r1", "action": "proxy", "upstream": "office",
371
+ "targets": "chrome.exe;msedge.exe", "dst_port": "443,80"},
372
+ ],
373
+ },
374
+ "upstreams": [
375
+ {"alias": "office", "kind": "socks5", "host": "127.0.0.1", "port": 1080},
376
+ ],
377
+ }
378
+
379
+ cfg = Config.from_dict(doc)
380
+ cfg.validate() # from_dict 只解析,不校验
381
+ atomic_write(cfg, "myconfig.yaml") # 临时文件 → 校验 → 原子替换
382
+
383
+ with start_proxy("myconfig.yaml") as ppx:
384
+ ...
385
+ ```
386
+
387
+ > `Config.from_dict()` 不会解析 `password_ref`;需要凭据时请用
388
+ > `Config.load()` / `start_proxy(path)`,或自己给 `upstream.password` 赋值。
389
+
390
+ ### 只做决策,不启动数据面
391
+
392
+ ```python
393
+ from proxycore.api import make_runtime
394
+
395
+ runtime = make_runtime() # 默认用内置配置,backend=null
306
396
  decision = runtime.decide(conn_request)
397
+ print(decision.action, decision.reason)
398
+ ```
399
+
400
+ ### 生命周期与句柄
401
+
402
+ ```python
403
+ ppx = start_proxy()
404
+ try:
405
+ print(ppx.status())
406
+ finally:
407
+ ppx.stop() # 幂等;with 退出时也会自动调用
408
+ ```
409
+
410
+ | 成员 | 说明 |
411
+ |---|---|
412
+ | `status()` | 后端、规则、会话、熔断、DNS、UDP、sink、`config_path`、`config_source` |
413
+ | `sessions(limit=50)` / `active_sessions()` | 会话明细 / 活跃数 |
414
+ | `reload(path="")` | 热重载;不传路径则重读当前文件。`sink.port`/`backend` 变更需重启 |
415
+ | `decide(conn)` | 只问规则引擎,不抓包、不改道 |
416
+ | `profile` | 当前生效的 `Profile` |
417
+ | `config_path` / `config_source` | 当前配置的绝对路径与来源 |
418
+ | `running` | 是否仍在运行 |
419
+
420
+ `start_proxy()` 会启动后台维护线程;`run_forever()` 会阻塞到 Ctrl+C。
421
+ 请在主线程使用,并在退出时 `stop()`(或使用 `with`)。
422
+
423
+ ### 错误处理
424
+
425
+ 配置 / 后端 / 启动失败统一抛 `ProxyStartError`,`code` 是稳定的机器可读标识:
426
+
427
+ ```python
428
+ from proxycore.api import ProxyStartError, start_proxy
429
+
430
+ try:
431
+ start_proxy("missing.yaml")
432
+ except ProxyStartError as exc:
433
+ print(exc.code) # config_not_found
307
434
  ```
308
435
 
309
- 跑 `python tests/tools/api_demo.py` 看三种用法的完整演示。
436
+ | code | 含义 |
437
+ |---|---|
438
+ | `config_not_found` | 配置文件不存在(显式路径 / 环境变量不会回退) |
439
+ | `config_unreadable` | 文件存在但读不了(权限、目录等) |
440
+ | `config_parse_invalid` | YAML 语法错误 / 根节点非映射 / 非 UTF-8 |
441
+ | `config_version_unsupported` | `version != 1` |
442
+ | `rule_upstream_missing` 等 | 规则或上游校验失败(完整列表见文档) |
443
+ | `E_BACKEND_UNAVAILABLE` | 后端不可用(未知后端、WinDivert 不可加载等) |
444
+
445
+ `api.load_config()` 同样抛 `ProxyStartError`,不是 `ConfigError`。
446
+
447
+ ### 环境变量
448
+
449
+ | 变量 | 作用 |
450
+ |---|---|
451
+ | `PROCPROXY_CONFIG` | 配置文件路径;显式路径之后、用户配置之前生效 |
452
+ | `PROCPROXY_RUNTIME_DIR` | 运行时状态/日志目录(CLI 与服务使用) |
453
+
454
+ 跑 `python tests/tools/api_demo.py` 看完整演示。
310
455
 
311
456
  ---
312
457
 
@@ -351,12 +496,13 @@ src/proxycore/ 核心包
351
496
  ├── tunnel/ sink + relay(本地落地与双向转发)
352
497
  ├── upstream/ SOCKS5 / SOCKS4 / HTTP CONNECT / 跳链
353
498
  ├── dns/ DNS 三模式与 UDP
499
+ ├── config_source.py 配置来源解析(显式 / env / 用户 / 样例 / 内置)
354
500
  ├── forward.py ← procproxy.py <进程名> 的实现
355
501
  ├── canproxy.py / checkroute.py / probe.py / pathsetup.py ← 其余子命令
356
502
  └── service/ ipc.py Windows 服务 + 命名管道
357
503
  samples/ mock 服务 + 示例配置
358
- docs/ 文档(00 架构与阅读路线 / 07 规则语义 / 11 排错 / 14 转发到抓包工具)
359
- tests/ 测试套件(555 passed)
504
+ docs/ 文档(00 架构 / 07 规则 / 11 排错 / 14 抓包 / 15 SDK)
505
+ tests/ 测试套件(619 passed / 4 skipped)
360
506
  └── tools/ 验证脚本(run_demo / run_e2e / bench / …)
361
507
  ```
362
508
 
@@ -387,7 +533,7 @@ tests/ 测试套件(555 passed)
387
533
 
388
534
  ```bash
389
535
  pip install -e ".[dev]"
390
- python -m pytest -q tests/ # 555 passed / 4 skipped(非管理员 Windows)
536
+ python -m pytest -q tests/ # 619 passed / 4 skipped(非管理员 Windows)
391
537
  python -m ruff check src tests
392
538
  python -m mypy src/proxycore
393
539
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "procproxy"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "ProcProxy —— 进程级代理:按进程决定哪条连接走哪个上游代理"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -51,6 +51,9 @@ package-dir = { "" = "src" }
51
51
  [tool.setuptools.packages.find]
52
52
  where = ["src"]
53
53
 
54
+ [tool.setuptools.package-data]
55
+ "proxycore" = ["data/*.yaml"]
56
+
54
57
  # ---------------------------------------------------------------- pytest ----
55
58
  [tool.pytest.ini_options]
56
59
  minversion = "7.4"