devconfig-gen-singbox 2.1.1__tar.gz → 2.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.
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/PKG-INFO +14 -14
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/README.md +13 -13
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/pyproject.toml +2 -3
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/__init__.py +1 -1
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/cli.py +17 -1
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen_singbox.egg-info/PKG-INFO +14 -14
- devconfig_gen_singbox-2.1.2/src/devconfig_gen_singbox.egg-info/entry_points.txt +2 -0
- devconfig_gen_singbox-2.1.2/src/singbox_ops/cli.py +207 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_cli_e2e.py +1 -1
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_cli.py +13 -0
- devconfig_gen_singbox-2.1.1/src/devconfig_gen_singbox.egg-info/entry_points.txt +0 -4
- devconfig_gen_singbox-2.1.1/src/singbox_ops/cli.py +0 -109
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/LICENSE +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/setup.cfg +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/engine.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/formats.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/interactive.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/models.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/custom.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/env_provider.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/json_provider.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/data/rules.json +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/links.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/models.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/plugins/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/plugins/anytls.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/plugins/base.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/plugins/hysteria2.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/plugins/tuic.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/provider.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/route.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/singbox/schema.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/py.typed +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/registry.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/validation.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/web_ui.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen_singbox.egg-info/SOURCES.txt +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen_singbox.egg-info/dependency_links.txt +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen_singbox.egg-info/requires.txt +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen_singbox.egg-info/top_level.txt +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/acme/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/acme/base.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/acme/cloudflare_dns01.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/base.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/dns/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/dns/base.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/dns/cloudflare.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/export/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/export/local_files.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/runtime/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/runtime/nftables.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/runtime/packages.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/runtime/systemd.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/runtime/warp_watchdog.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/secrets/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/secrets/base.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/secrets/python_secrets.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/secrets/singbox_subprocess.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/secrets/static.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/state/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/state/local_json.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/__init__.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/command.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/context_builder.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/exceptions.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/network.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/orchestrator.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/plan.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/protocols.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/templates/singbox_auto_update.py.template +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/templates/warp_watchdog.sh.template +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_api_parity.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_custom_provider.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_devconfig_core.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_env_provider.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_formats.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_interactive.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_merge.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_provider_metadata.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_acme.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_context_builder.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_dns.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_orchestrator.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_plan.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_runtime.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_secrets.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_state_export.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_provider.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_validation.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_web_ui.py +0 -0
- {devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_web_ui_widgets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devconfig_gen_singbox
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Provider-based developer configuration generation engine and CLI
|
|
5
5
|
Author-email: henryliu443 <henryliu443@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -28,7 +28,7 @@ Dynamic: license-file
|
|
|
28
28
|
> **A sing-box domain implementation built on top of DevConfig-Gen.**
|
|
29
29
|
> **构建在 `DevConfig-Gen` 之上的 sing-box 领域实现(child / fork)。**
|
|
30
30
|
|
|
31
|
-
[](CHANGELOG.md)
|
|
32
32
|
[](LICENSE)
|
|
33
33
|

|
|
34
34
|

|
|
@@ -62,7 +62,7 @@ devconfig_gen_singbox
|
|
|
62
62
|
git clone https://github.com/henryliu443/devconfig_gen_singbox.git
|
|
63
63
|
cd devconfig_gen_singbox
|
|
64
64
|
pip install -e ".[yaml]"
|
|
65
|
-
|
|
65
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
未安装时亦可直接通过源码运行:
|
|
@@ -226,9 +226,9 @@ options:
|
|
|
226
226
|
## 输入示例 / Input Example
|
|
227
227
|
|
|
228
228
|
```bash
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist
|
|
230
|
+
devconfig_gen_singbox validate --provider singbox --input examples/singbox.yaml
|
|
231
|
+
devconfig_gen_singbox schema --provider singbox
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
完整 context 见 [`examples/singbox.yaml`](examples/singbox.yaml)。
|
|
@@ -281,14 +281,14 @@ singbox-ops destroy --plan examples/singbox-deploy.yaml
|
|
|
281
281
|
- 结构化校验与诊断(`Diagnostic` / `ValidationError`)
|
|
282
282
|
- JSON/YAML 序列化(标准库 + 可选 PyYAML,内置子集解析器兜底)
|
|
283
283
|
- 多源合并与 dotted-path 覆盖(`deep_merge`、`--set`)
|
|
284
|
-
- CLI、**终端向导**(`
|
|
285
|
-
(`
|
|
284
|
+
- CLI、**终端向导**(`devconfig_gen_singbox init`)、**本地 Web 工作台**
|
|
285
|
+
(`devconfig_gen_singbox ui`)与 Python API
|
|
286
286
|
|
|
287
287
|
同时也继承中立的 Provider `custom` / `json` / `env`。本仓库在其之上新增领域
|
|
288
288
|
Provider `singbox`:
|
|
289
289
|
|
|
290
290
|
```bash
|
|
291
|
-
|
|
291
|
+
devconfig_gen_singbox providers
|
|
292
292
|
# custom / env / json / singbox
|
|
293
293
|
```
|
|
294
294
|
|
|
@@ -299,15 +299,15 @@ devconfig-gen providers
|
|
|
299
299
|
|
|
300
300
|
```bash
|
|
301
301
|
# 生成 / 校验 / 查看 schema
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
302
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist --format yaml
|
|
303
|
+
devconfig_gen_singbox validate --provider singbox --input examples/singbox.yaml --json
|
|
304
|
+
devconfig_gen_singbox schema --provider singbox
|
|
305
305
|
|
|
306
306
|
# 交互式终端向导(所有 provider 通用,含 singbox)
|
|
307
|
-
|
|
307
|
+
devconfig_gen_singbox init --provider singbox --output-dir dist
|
|
308
308
|
|
|
309
309
|
# 本地 Web 工作台(零构建,标准库 http.server)
|
|
310
|
-
|
|
310
|
+
devconfig_gen_singbox ui --port 8848
|
|
311
311
|
```
|
|
312
312
|
|
|
313
313
|
```python
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **A sing-box domain implementation built on top of DevConfig-Gen.**
|
|
4
4
|
> **构建在 `DevConfig-Gen` 之上的 sing-box 领域实现(child / fork)。**
|
|
5
5
|
|
|
6
|
-
[](CHANGELOG.md)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|

|
|
9
9
|

|
|
@@ -37,7 +37,7 @@ devconfig_gen_singbox
|
|
|
37
37
|
git clone https://github.com/henryliu443/devconfig_gen_singbox.git
|
|
38
38
|
cd devconfig_gen_singbox
|
|
39
39
|
pip install -e ".[yaml]"
|
|
40
|
-
|
|
40
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
未安装时亦可直接通过源码运行:
|
|
@@ -201,9 +201,9 @@ options:
|
|
|
201
201
|
## 输入示例 / Input Example
|
|
202
202
|
|
|
203
203
|
```bash
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist
|
|
205
|
+
devconfig_gen_singbox validate --provider singbox --input examples/singbox.yaml
|
|
206
|
+
devconfig_gen_singbox schema --provider singbox
|
|
207
207
|
```
|
|
208
208
|
|
|
209
209
|
完整 context 见 [`examples/singbox.yaml`](examples/singbox.yaml)。
|
|
@@ -256,14 +256,14 @@ singbox-ops destroy --plan examples/singbox-deploy.yaml
|
|
|
256
256
|
- 结构化校验与诊断(`Diagnostic` / `ValidationError`)
|
|
257
257
|
- JSON/YAML 序列化(标准库 + 可选 PyYAML,内置子集解析器兜底)
|
|
258
258
|
- 多源合并与 dotted-path 覆盖(`deep_merge`、`--set`)
|
|
259
|
-
- CLI、**终端向导**(`
|
|
260
|
-
(`
|
|
259
|
+
- CLI、**终端向导**(`devconfig_gen_singbox init`)、**本地 Web 工作台**
|
|
260
|
+
(`devconfig_gen_singbox ui`)与 Python API
|
|
261
261
|
|
|
262
262
|
同时也继承中立的 Provider `custom` / `json` / `env`。本仓库在其之上新增领域
|
|
263
263
|
Provider `singbox`:
|
|
264
264
|
|
|
265
265
|
```bash
|
|
266
|
-
|
|
266
|
+
devconfig_gen_singbox providers
|
|
267
267
|
# custom / env / json / singbox
|
|
268
268
|
```
|
|
269
269
|
|
|
@@ -274,15 +274,15 @@ devconfig-gen providers
|
|
|
274
274
|
|
|
275
275
|
```bash
|
|
276
276
|
# 生成 / 校验 / 查看 schema
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist --format yaml
|
|
278
|
+
devconfig_gen_singbox validate --provider singbox --input examples/singbox.yaml --json
|
|
279
|
+
devconfig_gen_singbox schema --provider singbox
|
|
280
280
|
|
|
281
281
|
# 交互式终端向导(所有 provider 通用,含 singbox)
|
|
282
|
-
|
|
282
|
+
devconfig_gen_singbox init --provider singbox --output-dir dist
|
|
283
283
|
|
|
284
284
|
# 本地 Web 工作台(零构建,标准库 http.server)
|
|
285
|
-
|
|
285
|
+
devconfig_gen_singbox ui --port 8848
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devconfig_gen_singbox"
|
|
7
|
-
version = "2.1.
|
|
7
|
+
version = "2.1.2"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "henryliu443", email = "henryliu443@gmail.com" },
|
|
10
10
|
]
|
|
@@ -26,9 +26,8 @@ dev = ["build", "wheel", "PyYAML>=5.1", "cryptography>=3.4"]
|
|
|
26
26
|
docs = ["mkdocs-material>=9.5,<10"]
|
|
27
27
|
|
|
28
28
|
[project.scripts]
|
|
29
|
-
|
|
29
|
+
# One command, named exactly after the PyPI distribution.
|
|
30
30
|
devconfig_gen_singbox = "devconfig_gen.cli:main"
|
|
31
|
-
singbox-ops = "singbox_ops.cli:main"
|
|
32
31
|
|
|
33
32
|
[tool.setuptools.packages.find]
|
|
34
33
|
where = ["src"]
|
|
@@ -14,7 +14,7 @@ from .registry import default_registry
|
|
|
14
14
|
|
|
15
15
|
def build_parser() -> argparse.ArgumentParser:
|
|
16
16
|
parser = argparse.ArgumentParser(
|
|
17
|
-
prog="
|
|
17
|
+
prog="devconfig_gen_singbox",
|
|
18
18
|
description="Generate and validate structured JSON/YAML configuration.",
|
|
19
19
|
)
|
|
20
20
|
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
@@ -100,9 +100,25 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
100
100
|
)
|
|
101
101
|
ui_cmd.set_defaults(handler=_ui)
|
|
102
102
|
|
|
103
|
+
_register_ops_subparsers(sub)
|
|
104
|
+
|
|
103
105
|
return parser
|
|
104
106
|
|
|
105
107
|
|
|
108
|
+
def _register_ops_subparsers(sub) -> None:
|
|
109
|
+
"""Graft the optional ``singbox_ops`` subcommands onto this single CLI.
|
|
110
|
+
|
|
111
|
+
``singbox_ops`` is imported lazily, so importing ``devconfig_gen`` stays
|
|
112
|
+
side-effect free and the pure engine works even without the ops package.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
from singbox_ops.cli import add_subparsers
|
|
117
|
+
except Exception: # pragma: no cover - ops layer is optional
|
|
118
|
+
return
|
|
119
|
+
add_subparsers(sub)
|
|
120
|
+
|
|
121
|
+
|
|
106
122
|
def _list_providers(args) -> int:
|
|
107
123
|
for name in default_registry.names():
|
|
108
124
|
print(name)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devconfig_gen_singbox
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Provider-based developer configuration generation engine and CLI
|
|
5
5
|
Author-email: henryliu443 <henryliu443@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -28,7 +28,7 @@ Dynamic: license-file
|
|
|
28
28
|
> **A sing-box domain implementation built on top of DevConfig-Gen.**
|
|
29
29
|
> **构建在 `DevConfig-Gen` 之上的 sing-box 领域实现(child / fork)。**
|
|
30
30
|
|
|
31
|
-
[](CHANGELOG.md)
|
|
32
32
|
[](LICENSE)
|
|
33
33
|

|
|
34
34
|

|
|
@@ -62,7 +62,7 @@ devconfig_gen_singbox
|
|
|
62
62
|
git clone https://github.com/henryliu443/devconfig_gen_singbox.git
|
|
63
63
|
cd devconfig_gen_singbox
|
|
64
64
|
pip install -e ".[yaml]"
|
|
65
|
-
|
|
65
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
未安装时亦可直接通过源码运行:
|
|
@@ -226,9 +226,9 @@ options:
|
|
|
226
226
|
## 输入示例 / Input Example
|
|
227
227
|
|
|
228
228
|
```bash
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist
|
|
230
|
+
devconfig_gen_singbox validate --provider singbox --input examples/singbox.yaml
|
|
231
|
+
devconfig_gen_singbox schema --provider singbox
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
完整 context 见 [`examples/singbox.yaml`](examples/singbox.yaml)。
|
|
@@ -281,14 +281,14 @@ singbox-ops destroy --plan examples/singbox-deploy.yaml
|
|
|
281
281
|
- 结构化校验与诊断(`Diagnostic` / `ValidationError`)
|
|
282
282
|
- JSON/YAML 序列化(标准库 + 可选 PyYAML,内置子集解析器兜底)
|
|
283
283
|
- 多源合并与 dotted-path 覆盖(`deep_merge`、`--set`)
|
|
284
|
-
- CLI、**终端向导**(`
|
|
285
|
-
(`
|
|
284
|
+
- CLI、**终端向导**(`devconfig_gen_singbox init`)、**本地 Web 工作台**
|
|
285
|
+
(`devconfig_gen_singbox ui`)与 Python API
|
|
286
286
|
|
|
287
287
|
同时也继承中立的 Provider `custom` / `json` / `env`。本仓库在其之上新增领域
|
|
288
288
|
Provider `singbox`:
|
|
289
289
|
|
|
290
290
|
```bash
|
|
291
|
-
|
|
291
|
+
devconfig_gen_singbox providers
|
|
292
292
|
# custom / env / json / singbox
|
|
293
293
|
```
|
|
294
294
|
|
|
@@ -299,15 +299,15 @@ devconfig-gen providers
|
|
|
299
299
|
|
|
300
300
|
```bash
|
|
301
301
|
# 生成 / 校验 / 查看 schema
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
302
|
+
devconfig_gen_singbox generate --provider singbox --input examples/singbox.yaml --output-dir dist --format yaml
|
|
303
|
+
devconfig_gen_singbox validate --provider singbox --input examples/singbox.yaml --json
|
|
304
|
+
devconfig_gen_singbox schema --provider singbox
|
|
305
305
|
|
|
306
306
|
# 交互式终端向导(所有 provider 通用,含 singbox)
|
|
307
|
-
|
|
307
|
+
devconfig_gen_singbox init --provider singbox --output-dir dist
|
|
308
308
|
|
|
309
309
|
# 本地 Web 工作台(零构建,标准库 http.server)
|
|
310
|
-
|
|
310
|
+
devconfig_gen_singbox ui --port 8848
|
|
311
311
|
```
|
|
312
312
|
|
|
313
313
|
```python
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Operations subcommands, registered into the single ``devconfig_gen_singbox`` CLI.
|
|
2
|
+
|
|
3
|
+
There is exactly **one** command name in this project: the PyPI name
|
|
4
|
+
``devconfig_gen_singbox``. ``add_subparsers()`` is what ``devconfig_gen.cli``
|
|
5
|
+
calls to graft these subcommands onto that single command, so the ops layer
|
|
6
|
+
never introduces a second CLI name.
|
|
7
|
+
|
|
8
|
+
A-repo style: running ``deploy`` without ``--plan`` starts an interactive
|
|
9
|
+
wizard, so one command takes a user from prompts to a deployed server.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
from devconfig_gen import formats
|
|
20
|
+
|
|
21
|
+
from .core.context_builder import build_context
|
|
22
|
+
from .core.exceptions import OpsError
|
|
23
|
+
from .core.plan import DeployPlan, load_plan
|
|
24
|
+
from .core.orchestrator import DRY_RUN_IP, build_suite, deploy, destroy, make_runner
|
|
25
|
+
from .core.protocols import PROTOCOLS
|
|
26
|
+
|
|
27
|
+
PROG = "devconfig_gen_singbox"
|
|
28
|
+
__version__ = "0.1.0"
|
|
29
|
+
|
|
30
|
+
SUBCOMMANDS = (
|
|
31
|
+
("context", "Assemble and print the sing-box context (no side effects)"),
|
|
32
|
+
("plan", "Interactive wizard: write a plan.yaml, do not deploy"),
|
|
33
|
+
("deploy", "Deploy sing-box (interactive wizard when --plan is omitted)"),
|
|
34
|
+
("redeploy", "Regenerate credentials and redeploy"),
|
|
35
|
+
("destroy", "Tear down what a previous deploy created"),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def add_subparsers(sub) -> None:
|
|
40
|
+
"""Graft the operations subcommands onto the shared root parser."""
|
|
41
|
+
|
|
42
|
+
for name, help_text in SUBCOMMANDS:
|
|
43
|
+
command = sub.add_parser(name, help=help_text)
|
|
44
|
+
command.add_argument("--plan", help="Path to a JSON/YAML plan (omit for the wizard)")
|
|
45
|
+
command.add_argument("--domain", help="Domain root (skips that prompt)")
|
|
46
|
+
command.add_argument("--protocols", help="Comma-separated protocols (skips that prompt)")
|
|
47
|
+
if name == "plan":
|
|
48
|
+
command.add_argument("--output", default="plan.yaml", help="Where to write the plan")
|
|
49
|
+
if name == "context":
|
|
50
|
+
command.add_argument("--format", choices=("json", "yaml"), help="Output format")
|
|
51
|
+
if name in ("deploy", "redeploy", "destroy"):
|
|
52
|
+
command.add_argument(
|
|
53
|
+
"--dry-run",
|
|
54
|
+
action="store_true",
|
|
55
|
+
help="Print intended actions without touching the system",
|
|
56
|
+
)
|
|
57
|
+
command.set_defaults(handler=_run)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# --------------------------------------------------------------------------
|
|
61
|
+
# interactive wizard (mirrors the legacy A-repo prompt flow)
|
|
62
|
+
# --------------------------------------------------------------------------
|
|
63
|
+
def _ask(prompt, default=None, reader=None, writer=None):
|
|
64
|
+
reader = reader or sys.stdin
|
|
65
|
+
writer = writer or sys.stdout
|
|
66
|
+
suffix = f" [{default}]" if default else ""
|
|
67
|
+
writer.write(f"? {prompt}{suffix}: ")
|
|
68
|
+
writer.flush()
|
|
69
|
+
line = reader.readline()
|
|
70
|
+
if line == "":
|
|
71
|
+
return default
|
|
72
|
+
line = line.strip()
|
|
73
|
+
return line if line else default
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _ask_yes_no(prompt, default=True, reader=None, writer=None):
|
|
77
|
+
hint = "[Y/n]" if default else "[y/N]"
|
|
78
|
+
value = _ask(f"{prompt} {hint}", None, reader, writer)
|
|
79
|
+
if value is None:
|
|
80
|
+
return default
|
|
81
|
+
return str(value).strip().lower() in ("y", "yes", "true", "1")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def interactive_plan(domain=None, protocols=None, reader=None, writer=None) -> dict:
|
|
85
|
+
writer = writer or sys.stdout
|
|
86
|
+
writer.write("=" * 56 + "\n")
|
|
87
|
+
writer.write(f" {PROG} 交互式部署向导\n")
|
|
88
|
+
writer.write("=" * 56 + "\n")
|
|
89
|
+
|
|
90
|
+
while not domain:
|
|
91
|
+
domain = _ask("主域名 (例: example.com)", None, reader, writer)
|
|
92
|
+
if not domain:
|
|
93
|
+
writer.write(" [!] 域名必填\n")
|
|
94
|
+
|
|
95
|
+
protocols = protocols or _ask("启用协议 (逗号分隔)", ",".join(PROTOCOLS), reader, writer)
|
|
96
|
+
mode = _ask("出站模式 none/proxy/tun", "proxy", reader, writer)
|
|
97
|
+
server_ip = _ask("服务器公网 IP (留空=自动探测)", None, reader, writer)
|
|
98
|
+
use_dns = _ask_yes_no("用 Cloudflare 管理 DNS A 记录?", True, reader, writer)
|
|
99
|
+
use_acme = _ask_yes_no("签发 TLS 证书 (acme.sh DNS-01)?", True, reader, writer)
|
|
100
|
+
use_runtime = _ask_yes_no(
|
|
101
|
+
"启用运行时适配器 (packages/systemd/firewall/watchdog)?", True, reader, writer
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
server_out = _ask("服务端配置输出路径", "/etc/sing-box/config.json", reader, writer)
|
|
105
|
+
client_out = _ask("客户端配置输出路径", "/root/singbox-client.json", reader, writer)
|
|
106
|
+
links_out = _ask("分享链接输出路径", "/root/singbox-links.txt", reader, writer)
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
"domain_root": domain,
|
|
110
|
+
"protocols": [item.strip() for item in str(protocols).split(",") if item.strip()],
|
|
111
|
+
"tunnel_mode": mode,
|
|
112
|
+
"server_ip": server_ip or "auto",
|
|
113
|
+
"adapters": {
|
|
114
|
+
"secrets": "singbox-subprocess",
|
|
115
|
+
"dns": "cloudflare" if use_dns else None,
|
|
116
|
+
"acme": "cloudflare-dns01" if use_acme else None,
|
|
117
|
+
"state": "local-json" if use_runtime else None,
|
|
118
|
+
"runtime": {
|
|
119
|
+
"packages": "debian" if use_runtime else None,
|
|
120
|
+
"systemd": "systemd" if use_runtime else None,
|
|
121
|
+
"firewall": "nftables-basic" if use_runtime else None,
|
|
122
|
+
"watchdog": "warp" if use_runtime else None,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
"outputs": {
|
|
126
|
+
"server_config": server_out,
|
|
127
|
+
"client_config": client_out,
|
|
128
|
+
"links": links_out,
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _detect_or_placeholder(plan) -> str:
|
|
134
|
+
try:
|
|
135
|
+
from .core.network import detect_public_ipv4
|
|
136
|
+
|
|
137
|
+
return detect_public_ipv4()
|
|
138
|
+
except OpsError:
|
|
139
|
+
return DRY_RUN_IP
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _run(args) -> int:
|
|
143
|
+
try:
|
|
144
|
+
if getattr(args, "plan", None):
|
|
145
|
+
plan = load_plan(args.plan)
|
|
146
|
+
elif args.command == "destroy":
|
|
147
|
+
raise OpsError("destroy requires --plan")
|
|
148
|
+
else:
|
|
149
|
+
data = interactive_plan(
|
|
150
|
+
domain=getattr(args, "domain", None),
|
|
151
|
+
protocols=getattr(args, "protocols", None),
|
|
152
|
+
)
|
|
153
|
+
if args.command == "plan":
|
|
154
|
+
output = getattr(args, "output", "plan.yaml") or "plan.yaml"
|
|
155
|
+
with open(output, "w", encoding="utf-8") as handle:
|
|
156
|
+
handle.write(formats.dumps(data, "yaml"))
|
|
157
|
+
print(f"wrote {output}")
|
|
158
|
+
return 0
|
|
159
|
+
plan = DeployPlan.from_mapping(data)
|
|
160
|
+
|
|
161
|
+
dry_run = True if args.command == "context" else bool(getattr(args, "dry_run", False))
|
|
162
|
+
runner = make_runner(dry_run)
|
|
163
|
+
suite = build_suite(plan, runner)
|
|
164
|
+
|
|
165
|
+
if args.command == "context":
|
|
166
|
+
generated = suite.secrets.generate(plan, dry_run=True)
|
|
167
|
+
server_ip = plan.server_ip or _detect_or_placeholder(plan)
|
|
168
|
+
context = build_context(
|
|
169
|
+
plan,
|
|
170
|
+
generated.credentials,
|
|
171
|
+
server_ip,
|
|
172
|
+
subdomain_prefixes=generated.subdomain_prefixes,
|
|
173
|
+
)
|
|
174
|
+
print(formats.dumps(context, getattr(args, "format", None) or plan.output_format))
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
if args.command in ("deploy", "redeploy"):
|
|
178
|
+
report = deploy(plan, suite, dry_run=dry_run)
|
|
179
|
+
else:
|
|
180
|
+
report = destroy(plan, suite, dry_run=dry_run)
|
|
181
|
+
except OpsError as exc:
|
|
182
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
183
|
+
return 1
|
|
184
|
+
except (OSError, formats.FormatError, ValueError) as exc:
|
|
185
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
186
|
+
return 2
|
|
187
|
+
|
|
188
|
+
print(json.dumps(report.as_dict(), indent=2, ensure_ascii=False))
|
|
189
|
+
return 0
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
193
|
+
"""Standalone parser (used by ``python -m singbox_ops.cli``)."""
|
|
194
|
+
|
|
195
|
+
parser = argparse.ArgumentParser(prog=PROG, description="sing-box operations.")
|
|
196
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
197
|
+
add_subparsers(parser.add_subparsers(dest="command", required=True))
|
|
198
|
+
return parser
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def main(argv: Optional[list] = None) -> int:
|
|
202
|
+
args = build_parser().parse_args(argv)
|
|
203
|
+
return args.handler(args)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
raise SystemExit(main())
|
|
@@ -15,7 +15,7 @@ class TestCliEndToEnd(unittest.TestCase):
|
|
|
15
15
|
def test_version_matches_package_version(self):
|
|
16
16
|
result = run_cli("--version")
|
|
17
17
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
18
|
-
self.assertEqual(result.stdout.strip(), f"
|
|
18
|
+
self.assertEqual(result.stdout.strip(), f"devconfig_gen_singbox {__version__}")
|
|
19
19
|
|
|
20
20
|
def test_providers_lists_builtins(self):
|
|
21
21
|
result = run_cli("providers")
|
|
@@ -2,10 +2,23 @@ import json
|
|
|
2
2
|
import unittest
|
|
3
3
|
|
|
4
4
|
from tests._ops_support import EXAMPLES, run_ops_cli
|
|
5
|
+
from tests._support import run_cli
|
|
5
6
|
|
|
6
7
|
PLAN = str(EXAMPLES / "singbox-deploy.yaml")
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
class SingleCliNameTests(unittest.TestCase):
|
|
11
|
+
"""There is exactly one command, named after the PyPI distribution."""
|
|
12
|
+
|
|
13
|
+
def test_ops_subcommands_are_grafted_onto_the_single_cli(self):
|
|
14
|
+
result = run_cli("--help")
|
|
15
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
16
|
+
self.assertIn("devconfig_gen_singbox", result.stdout)
|
|
17
|
+
for name in ("generate", "init", "ui", "context", "plan", "deploy", "redeploy", "destroy"):
|
|
18
|
+
self.assertIn(name, result.stdout)
|
|
19
|
+
self.assertNotIn("singbox-ops", result.stdout)
|
|
20
|
+
|
|
21
|
+
|
|
9
22
|
class OpsCliTests(unittest.TestCase):
|
|
10
23
|
def test_context_command_prints_valid_provider_input(self):
|
|
11
24
|
result = run_ops_cli("context", "--plan", PLAN, "--format", "json")
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"""Command-line interface for the operations layer.
|
|
2
|
-
|
|
3
|
-
singbox-ops context --plan plan.yaml
|
|
4
|
-
singbox-ops deploy --plan plan.yaml [--dry-run]
|
|
5
|
-
singbox-ops redeploy --plan plan.yaml
|
|
6
|
-
singbox-ops destroy --plan plan.yaml [--dry-run]
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import argparse
|
|
12
|
-
import json
|
|
13
|
-
import sys
|
|
14
|
-
from typing import Optional
|
|
15
|
-
|
|
16
|
-
from devconfig_gen import formats
|
|
17
|
-
|
|
18
|
-
from .core.context_builder import build_context
|
|
19
|
-
from .core.exceptions import OpsError
|
|
20
|
-
from .core.plan import load_plan
|
|
21
|
-
from .core.orchestrator import (
|
|
22
|
-
DRY_RUN_IP,
|
|
23
|
-
build_suite,
|
|
24
|
-
deploy,
|
|
25
|
-
destroy,
|
|
26
|
-
make_runner,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
__version__ = "0.1.0"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def build_parser() -> argparse.ArgumentParser:
|
|
33
|
-
parser = argparse.ArgumentParser(
|
|
34
|
-
prog="singbox-ops",
|
|
35
|
-
description="Deploy and manage sing-box using the DevConfig-Gen engine.",
|
|
36
|
-
)
|
|
37
|
-
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
38
|
-
sub = parser.add_subparsers(dest="command", required=True)
|
|
39
|
-
|
|
40
|
-
for name, help_text in (
|
|
41
|
-
("context", "Assemble and print the sing-box context (no side effects)"),
|
|
42
|
-
("deploy", "Deploy sing-box using the plan"),
|
|
43
|
-
("redeploy", "Regenerate credentials and redeploy"),
|
|
44
|
-
("destroy", "Tear down what a previous deploy created"),
|
|
45
|
-
):
|
|
46
|
-
command = sub.add_parser(name, help=help_text)
|
|
47
|
-
command.add_argument("--plan", required=True, help="Path to a JSON/YAML plan")
|
|
48
|
-
if name != "context":
|
|
49
|
-
command.add_argument(
|
|
50
|
-
"--dry-run",
|
|
51
|
-
action="store_true",
|
|
52
|
-
help="Print intended actions without touching the system",
|
|
53
|
-
)
|
|
54
|
-
if name == "context":
|
|
55
|
-
command.add_argument("--format", choices=("json", "yaml"), help="Output format")
|
|
56
|
-
command.set_defaults(handler=_run)
|
|
57
|
-
return parser
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _detect_or_placeholder(plan) -> str:
|
|
61
|
-
try:
|
|
62
|
-
from .core.network import detect_public_ipv4
|
|
63
|
-
|
|
64
|
-
return detect_public_ipv4()
|
|
65
|
-
except OpsError:
|
|
66
|
-
return DRY_RUN_IP
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def _run(args) -> int:
|
|
70
|
-
try:
|
|
71
|
-
plan = load_plan(args.plan)
|
|
72
|
-
dry_run = True if args.command == "context" else bool(getattr(args, "dry_run", False))
|
|
73
|
-
runner = make_runner(dry_run)
|
|
74
|
-
suite = build_suite(plan, runner)
|
|
75
|
-
|
|
76
|
-
if args.command == "context":
|
|
77
|
-
generated = suite.secrets.generate(plan, dry_run=True)
|
|
78
|
-
server_ip = plan.server_ip or _detect_or_placeholder(plan)
|
|
79
|
-
context = build_context(
|
|
80
|
-
plan,
|
|
81
|
-
generated.credentials,
|
|
82
|
-
server_ip,
|
|
83
|
-
subdomain_prefixes=generated.subdomain_prefixes,
|
|
84
|
-
)
|
|
85
|
-
print(formats.dumps(context, getattr(args, "format", None) or plan.output_format))
|
|
86
|
-
return 0
|
|
87
|
-
|
|
88
|
-
if args.command in ("deploy", "redeploy"):
|
|
89
|
-
report = deploy(plan, suite, dry_run=dry_run)
|
|
90
|
-
else:
|
|
91
|
-
report = destroy(plan, suite, dry_run=dry_run)
|
|
92
|
-
except OpsError as exc:
|
|
93
|
-
print(f"error: {exc}", file=sys.stderr)
|
|
94
|
-
return 1
|
|
95
|
-
except (OSError, formats.FormatError, ValueError) as exc:
|
|
96
|
-
print(f"error: {exc}", file=sys.stderr)
|
|
97
|
-
return 2
|
|
98
|
-
|
|
99
|
-
print(json.dumps(report.as_dict(), indent=2, ensure_ascii=False))
|
|
100
|
-
return 0
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def main(argv: Optional[list] = None) -> int:
|
|
104
|
-
args = build_parser().parse_args(argv)
|
|
105
|
-
return args.handler(args)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if __name__ == "__main__":
|
|
109
|
-
raise SystemExit(main())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/interactive.py
RENAMED
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/__init__.py
RENAMED
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/devconfig_gen/providers/custom.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/acme/base.py
RENAMED
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/base.py
RENAMED
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/dns/__init__.py
RENAMED
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/dns/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/adapters/secrets/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/context_builder.py
RENAMED
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/orchestrator.py
RENAMED
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/src/singbox_ops/core/protocols.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_orchestrator.py
RENAMED
|
File without changes
|
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_runtime.py
RENAMED
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_secrets.py
RENAMED
|
File without changes
|
{devconfig_gen_singbox-2.1.1 → devconfig_gen_singbox-2.1.2}/tests/test_singbox_ops_state_export.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|