frpsctl 0.2.0__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.
- frpsctl-0.2.0/.github/workflows/ci.yml +118 -0
- frpsctl-0.2.0/.github/workflows/release.yml +102 -0
- frpsctl-0.2.0/.gitignore +27 -0
- frpsctl-0.2.0/CHANGELOG.md +67 -0
- frpsctl-0.2.0/LICENSE +21 -0
- frpsctl-0.2.0/NOTICE +28 -0
- frpsctl-0.2.0/PKG-INFO +1131 -0
- frpsctl-0.2.0/README.md +1108 -0
- frpsctl-0.2.0/frpsctl-/350/256/276/350/256/241/346/226/271/346/241/210.md +2328 -0
- frpsctl-0.2.0/install.sh +244 -0
- frpsctl-0.2.0/pyproject.toml +81 -0
- frpsctl-0.2.0/src/frpsctl/__init__.py +14 -0
- frpsctl-0.2.0/src/frpsctl/__main__.py +8 -0
- frpsctl-0.2.0/src/frpsctl/cli/__init__.py +1509 -0
- frpsctl-0.2.0/src/frpsctl/cli/context.py +163 -0
- frpsctl-0.2.0/src/frpsctl/cli/ui.py +115 -0
- frpsctl-0.2.0/src/frpsctl/core/__init__.py +7 -0
- frpsctl-0.2.0/src/frpsctl/core/admin.py +288 -0
- frpsctl-0.2.0/src/frpsctl/core/config.py +674 -0
- frpsctl-0.2.0/src/frpsctl/core/doctor.py +489 -0
- frpsctl-0.2.0/src/frpsctl/core/health.py +103 -0
- frpsctl-0.2.0/src/frpsctl/core/healthcheck.py +211 -0
- frpsctl-0.2.0/src/frpsctl/core/instance.py +325 -0
- frpsctl-0.2.0/src/frpsctl/core/lifecycle.py +802 -0
- frpsctl-0.2.0/src/frpsctl/core/lock.py +143 -0
- frpsctl-0.2.0/src/frpsctl/core/platform.py +156 -0
- frpsctl-0.2.0/src/frpsctl/core/release.py +390 -0
- frpsctl-0.2.0/src/frpsctl/core/schema.py +249 -0
- frpsctl-0.2.0/src/frpsctl/core/systemd.py +420 -0
- frpsctl-0.2.0/src/frpsctl/core/transaction.py +323 -0
- frpsctl-0.2.0/src/frpsctl/core/version.py +112 -0
- frpsctl-0.2.0/src/frpsctl/errors.py +291 -0
- frpsctl-0.2.0/src/frpsctl/plugin/__init__.py +36 -0
- frpsctl-0.2.0/src/frpsctl/plugin/audit.py +250 -0
- frpsctl-0.2.0/src/frpsctl/plugin/engine.py +164 -0
- frpsctl-0.2.0/src/frpsctl/plugin/policy.py +449 -0
- frpsctl-0.2.0/src/frpsctl/plugin/quota.py +228 -0
- frpsctl-0.2.0/src/frpsctl/plugin/server.py +344 -0
- frpsctl-0.2.0/src/frpsctl/plugin/types.py +245 -0
- frpsctl-0.2.0/src/frpsctl/py.typed +0 -0
- frpsctl-0.2.0/tests/__init__.py +0 -0
- frpsctl-0.2.0/tests/conftest.py +236 -0
- frpsctl-0.2.0/tests/fake_frps.py +226 -0
- frpsctl-0.2.0/tests/faults.py +303 -0
- frpsctl-0.2.0/tests/test_cli.py +787 -0
- frpsctl-0.2.0/tests/test_facts.py +574 -0
- frpsctl-0.2.0/tests/test_faults.py +488 -0
- frpsctl-0.2.0/tests/test_integration.py +518 -0
- frpsctl-0.2.0/tests/test_plugin.py +943 -0
- frpsctl-0.2.0/tests/test_units.py +1642 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# 平台范围由设计文档 §3.4 决定:只支持 Linux,因此 CI 只有 Linux job。
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
env:
|
|
11
|
+
# 契约层需要真实 frps:CI 里按需下载并强校验(不缓存二进制,避免供应链漂移)
|
|
12
|
+
FRPS_VERSION: "0.71.0"
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test:
|
|
16
|
+
name: Python ${{ matrix.python-version }}
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
fail-fast: false
|
|
20
|
+
matrix:
|
|
21
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- uses: astral-sh/setup-uv@v5
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
|
|
30
|
+
- name: 安装依赖
|
|
31
|
+
run: |
|
|
32
|
+
uv venv --python ${{ matrix.python-version }}
|
|
33
|
+
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
34
|
+
|
|
35
|
+
- name: 静态分析
|
|
36
|
+
run: .venv/bin/ruff check src/ tests/
|
|
37
|
+
|
|
38
|
+
- name: 拉取真实 frps + frpc(契约层用)
|
|
39
|
+
run: |
|
|
40
|
+
# 用我们自己的 install 下载 + sha256 强校验 —— 顺带验证 §8.6 的信任模型。
|
|
41
|
+
# frpc 与 frps 在同一个 tar 包里,--with-frpc 不产生额外下载;
|
|
42
|
+
# 插件的唯一真实调用方是 frpc,没有它就只能靠手工拼报文验证协议。
|
|
43
|
+
.venv/bin/frpsctl install --version "$FRPS_VERSION" --with-frpc
|
|
44
|
+
BIN="$HOME/.local/share/frpsctl/bin"
|
|
45
|
+
echo "FRPSCTL_TEST_BINARY=$BIN/frps-${FRPS_VERSION}" >> "$GITHUB_ENV"
|
|
46
|
+
echo "FRPSCTL_TEST_FRPC=$BIN/frpc-${FRPS_VERSION}" >> "$GITHUB_ENV"
|
|
47
|
+
"$BIN/frps-${FRPS_VERSION}" -v
|
|
48
|
+
"$BIN/frpc-${FRPS_VERSION}" -v
|
|
49
|
+
|
|
50
|
+
- name: 单元 + 集成 + CLI 层(覆盖率门禁)
|
|
51
|
+
run: |
|
|
52
|
+
# 覆盖率门禁:非契约层的基线是 81%,低于 80% 视为回归(新增代码
|
|
53
|
+
# 不应把已有覆盖挤掉)。契约层需要真二进制,单独跑、不进覆盖率。
|
|
54
|
+
.venv/bin/pytest tests/ -q -m "not contract" \
|
|
55
|
+
--cov=frpsctl --cov-report=term --cov-fail-under=80
|
|
56
|
+
|
|
57
|
+
- name: 契约层(设计文档事实基线的自动化守卫)
|
|
58
|
+
run: .venv/bin/pytest tests/test_facts.py -q
|
|
59
|
+
|
|
60
|
+
- name: 故障注入层(异常路径的五项不变量)
|
|
61
|
+
run: .venv/bin/pytest tests/test_faults.py -q
|
|
62
|
+
|
|
63
|
+
- name: 插件契约层(真 frpc → 真 frps → 我们的插件)
|
|
64
|
+
run: .venv/bin/pytest tests/test_plugin.py -q -m contract
|
|
65
|
+
|
|
66
|
+
- name: 端到端冒烟(真 frps 全链路)
|
|
67
|
+
run: |
|
|
68
|
+
set -euo pipefail
|
|
69
|
+
export FRPSCTL_ROOT="$RUNNER_TEMP/smoke/instances"
|
|
70
|
+
export FRPSCTL_DATA_HOME="$RUNNER_TEMP/smoke/data"
|
|
71
|
+
mkdir -p "$FRPSCTL_DATA_HOME/bin"
|
|
72
|
+
cp "$FRPSCTL_TEST_BINARY" "$FRPSCTL_DATA_HOME/bin/"
|
|
73
|
+
ln -sf "frps-${FRPS_VERSION}" "$FRPSCTL_DATA_HOME/bin/frps"
|
|
74
|
+
|
|
75
|
+
free_port() { python3 -c "import socket;s=socket.socket();s.bind(('127.0.0.1',0));print(s.getsockname()[1])"; }
|
|
76
|
+
BP=$(free_port); DP=$(free_port)
|
|
77
|
+
|
|
78
|
+
.venv/bin/frpsctl init --no-input --bind-port "$BP" --dashboard-port "$DP" --allow-ports 6000-6100
|
|
79
|
+
.venv/bin/frpsctl verify
|
|
80
|
+
.venv/bin/frpsctl start --health-timeout 10
|
|
81
|
+
.venv/bin/frpsctl status
|
|
82
|
+
.venv/bin/frpsctl config set maxPortsPerClient 30
|
|
83
|
+
.venv/bin/frpsctl status --json
|
|
84
|
+
.venv/bin/frpsctl doctor
|
|
85
|
+
.venv/bin/frpsctl stop
|
|
86
|
+
.venv/bin/frpsctl status
|
|
87
|
+
|
|
88
|
+
no-binary:
|
|
89
|
+
name: 无二进制降级路径(Python 3.13)
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v4
|
|
93
|
+
- uses: astral-sh/setup-uv@v5
|
|
94
|
+
- name: 安装依赖
|
|
95
|
+
run: |
|
|
96
|
+
uv venv --python 3.13
|
|
97
|
+
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
98
|
+
- name: 契约层必须整体 skip 而不是失败
|
|
99
|
+
run: .venv/bin/pytest tests/test_facts.py -q -rs
|
|
100
|
+
|
|
101
|
+
contract-070:
|
|
102
|
+
name: 契约层(frp 0.70.0 下界)
|
|
103
|
+
runs-on: ubuntu-latest
|
|
104
|
+
steps:
|
|
105
|
+
- uses: actions/checkout@v4
|
|
106
|
+
- uses: astral-sh/setup-uv@v5
|
|
107
|
+
- name: 安装依赖
|
|
108
|
+
run: |
|
|
109
|
+
uv venv --python 3.13
|
|
110
|
+
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
111
|
+
- name: 版本门槛下界的契约验证
|
|
112
|
+
run: |
|
|
113
|
+
# 支持范围的下界必须与上界一样可信:§3.6 的门槛是 0.70.0,
|
|
114
|
+
# 契约层就在这里把它变成可执行断言(本机已实测 21/21 通过)。
|
|
115
|
+
export FRPSCTL_DATA_HOME="$RUNNER_TEMP/data070"
|
|
116
|
+
.venv/bin/frpsctl install --version 0.70.0
|
|
117
|
+
FRPSCTL_TEST_BINARY="$RUNNER_TEMP/data070/bin/frps-0.70.0" \
|
|
118
|
+
.venv/bin/pytest tests/test_facts.py -q
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# 发布流程(打 tag 触发):
|
|
4
|
+
# 1. 修改 src/frpsctl/__init__.py 的 __version__
|
|
5
|
+
# 2. 更新 CHANGELOG.md
|
|
6
|
+
# 3. git tag v<version> && git push origin v<version>
|
|
7
|
+
#
|
|
8
|
+
# 版本一致性由本流程强校验:tag 与 __version__ 不一致直接失败——防止发布物
|
|
9
|
+
# 与代码里的版本号漂移。
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags: ["v*"]
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build:
|
|
21
|
+
name: 构建 sdist 与 wheel
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- uses: astral-sh/setup-uv@v5
|
|
27
|
+
|
|
28
|
+
- name: 校验 tag 与 __version__ 一致
|
|
29
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
30
|
+
env:
|
|
31
|
+
TAG: ${{ github.ref_name }}
|
|
32
|
+
run: |
|
|
33
|
+
python3 - <<'EOF'
|
|
34
|
+
import os, pathlib, re, sys
|
|
35
|
+
|
|
36
|
+
text = pathlib.Path("src/frpsctl/__init__.py").read_text(encoding="utf-8")
|
|
37
|
+
version = re.search(r'__version__ = "([^"]+)"', text).group(1)
|
|
38
|
+
tag = os.environ["TAG"]
|
|
39
|
+
if f"v{version}" != tag:
|
|
40
|
+
sys.exit(f"tag {tag} 与 __version__ {version} 不一致——先改版本号再打 tag")
|
|
41
|
+
print(f"版本一致:{tag}")
|
|
42
|
+
EOF
|
|
43
|
+
|
|
44
|
+
- name: 构建
|
|
45
|
+
run: uv build
|
|
46
|
+
|
|
47
|
+
- name: 构建产物自检(wheel 必须包含 py.typed 与全部模块)
|
|
48
|
+
run: |
|
|
49
|
+
python3 - <<'EOF'
|
|
50
|
+
import pathlib, zipfile
|
|
51
|
+
|
|
52
|
+
wheel = next(pathlib.Path("dist").glob("*.whl"))
|
|
53
|
+
names = set(zipfile.ZipFile(wheel).namelist())
|
|
54
|
+
required = {"frpsctl/py.typed", "frpsctl/cli/__init__.py", "frpsctl/core/lifecycle.py"}
|
|
55
|
+
missing = required - names
|
|
56
|
+
if missing:
|
|
57
|
+
raise SystemExit(f"wheel 缺少文件:{sorted(missing)}")
|
|
58
|
+
print(f"{wheel.name} 自检通过({len(names)} 个条目)")
|
|
59
|
+
EOF
|
|
60
|
+
|
|
61
|
+
- uses: actions/upload-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: dist
|
|
64
|
+
path: dist/
|
|
65
|
+
|
|
66
|
+
github-release:
|
|
67
|
+
name: 创建 GitHub Release
|
|
68
|
+
needs: build
|
|
69
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/download-artifact@v4
|
|
73
|
+
with:
|
|
74
|
+
name: dist
|
|
75
|
+
path: dist
|
|
76
|
+
|
|
77
|
+
- uses: softprops/action-gh-release@v2
|
|
78
|
+
with:
|
|
79
|
+
files: dist/*
|
|
80
|
+
generate_release_notes: true
|
|
81
|
+
|
|
82
|
+
pypi:
|
|
83
|
+
name: 发布到 PyPI(Trusted Publishing)
|
|
84
|
+
needs: build
|
|
85
|
+
if: startsWith(github.ref, 'refs/tags/') && vars.PYPI_PUBLISH == 'true'
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
environment: pypi
|
|
88
|
+
permissions:
|
|
89
|
+
id-token: write
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/download-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
name: dist
|
|
94
|
+
path: dist
|
|
95
|
+
|
|
96
|
+
# 前置配置(一次性,在 PyPI 网页端):
|
|
97
|
+
# Publishing → Add a new pending publisher
|
|
98
|
+
# - Owner: <your-github-user> Repository: frpsctl
|
|
99
|
+
# - Workflow: release.yml Environment: pypi
|
|
100
|
+
# 然后在仓库 Settings → Variables 添加 PYPI_PUBLISH=true 启用本 job。
|
|
101
|
+
# 未配置时本 job 整体跳过,不会把 release 流程染红。
|
|
102
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
frpsctl-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# 虚拟环境
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
|
|
12
|
+
# 测试与类型检查缓存
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
|
|
19
|
+
# 编辑器
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
22
|
+
*.swp
|
|
23
|
+
|
|
24
|
+
# 插件运行期产物:审计日志与策略属于本地数据,不进版本库
|
|
25
|
+
plugin-audit.jsonl
|
|
26
|
+
plugin-audit.jsonl.*
|
|
27
|
+
plugin-policy.json
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
4
|
+
版本号遵循[语义化版本](https://semver.org/lang/zh-CN/)。
|
|
5
|
+
|
|
6
|
+
## [0.2.0] - 2026-09-16
|
|
7
|
+
|
|
8
|
+
第三轮全量迭代:安全缺口、部署守护、稳定性与发布链路。
|
|
9
|
+
|
|
10
|
+
### 新增
|
|
11
|
+
|
|
12
|
+
- `start` / `restart` 在健康 gate(L1 ∧ L2)未通过时以**退出码 12** 收场并向
|
|
13
|
+
stderr 告警——此前完全静默(退出码 0,脚本会当成成功)。未过 gate 的进程
|
|
14
|
+
仍被托管:`status` 可见、`stop` 可停。
|
|
15
|
+
- `install --mirror URL`(可重复)与 `FRPSCTL_MIRROR` 环境变量:下载镜像可配置,
|
|
16
|
+
兑现"下载地址可被镜像替换"的承诺。
|
|
17
|
+
- `service install --user/--group`:systemd 服务账户可指定(默认 `frps`)。
|
|
18
|
+
- `status` 输出 `listen` 行(人读)与 `listen` 字段(JSON),对齐设计文档 §7.4。
|
|
19
|
+
- `status --watch --json` 改为单行 NDJSON,可被逐行消费(`jq -c` 等)。
|
|
20
|
+
- shell 补全(`--install-completion` / `--show-completion`)。
|
|
21
|
+
- PyPI 发布链路:版本号单一来源(hatchling 读 `__init__.py`)、release workflow、
|
|
22
|
+
CHANGELOG、`py.typed`。
|
|
23
|
+
|
|
24
|
+
### 修复
|
|
25
|
+
|
|
26
|
+
- **安全**:`config diff` / `edit` / `set` / `rollback` 的内联表与**跨行结构**
|
|
27
|
+
(三引号多行字符串、多行内联表/数组)机密不再明文泄露;解析失败但形似含机密
|
|
28
|
+
时保守打码;裸键名 `token`/`password`/`clientSecret` 同样命中。
|
|
29
|
+
- `config rollback` 每次只产生 **1** 份快照:此前 `rollback_to` 与 `apply_change`
|
|
30
|
+
各存一份完全相同的内容,10 份历史实际只够 5 次操作,且快照创建在实例锁外。
|
|
31
|
+
- 全局选项前移支持 `--` 终止符:`config set k -- --json` 的字面值不再被搬走。
|
|
32
|
+
- `service install` 前置体检:服务账户不存在、二进制对服务用户不可达、
|
|
33
|
+
日志目录不可写、**路径位于家目录(`ProtectHome=true`)**——这些过去都要等
|
|
34
|
+
`systemctl start` 才炸,且错误现场与安装动作相隔很远。
|
|
35
|
+
- unit 的 `ReadWritePaths` 增加**实例目录**:`ProtectSystem=strict` 下其余路径
|
|
36
|
+
只读,而 frp 默认要往实例目录写 `./frps.log`。
|
|
37
|
+
- `config set` 重建配置时**保留原属主**:systemd 部署把配置移交给服务用户后,
|
|
38
|
+
再次改配置不会再"夺回"属主导致 frps 读不到。
|
|
39
|
+
- `plugin serve` 的 SIGTERM handler 安装纳入 `try`:安装瞬间收到信号不再绕过
|
|
40
|
+
审计刷盘(此前 `finally` 尚未生效,进程直接死亡)。
|
|
41
|
+
- 配额检查按用户锁重构:dashboard 查询不再持全局锁,一个用户的慢查询不会串行
|
|
42
|
+
挂住所有用户的登录链路(frp 侧对插件 HTTP 客户端没有超时)。
|
|
43
|
+
- `install --with-frpc --only-download` 不再切换 frpc 软链,与 frps 语义统一。
|
|
44
|
+
- `log` 改为纯 Python tail:不依赖外部 `tail` 命令,支持日志轮转后自动重开新文件。
|
|
45
|
+
- `plugin init` 生成策略文件改用原子写(此前 `write_text` 存在半截文件与权限窗口)。
|
|
46
|
+
- 健康等待期间进程死亡时按启动失败收尾并清理 `state.json`(不再留下假 RUNNING)。
|
|
47
|
+
- 负数数值选项(`--interval` / `--timeout` / `-n` 等)归入用法错误(2):
|
|
48
|
+
此前 `--timeout -1` 会跳过等待直接 SIGKILL——参数笔误造成不可逆动作。
|
|
49
|
+
- `status --watch` 重定向到文件时不再写入 ANSI 清屏码。
|
|
50
|
+
|
|
51
|
+
### 工程
|
|
52
|
+
|
|
53
|
+
- CI 增加 `ruff check` 与 80% 覆盖率门禁;`[dev]` 补齐 `pytest-cov` / `ruff`。
|
|
54
|
+
- 新增 70 条回归用例(单元 / CLI / 集成 / 插件各层,总计 296 条)。
|
|
55
|
+
|
|
56
|
+
## [0.1.0] - 2026-09-15
|
|
57
|
+
|
|
58
|
+
首个版本。
|
|
59
|
+
|
|
60
|
+
- 生命周期:`install` / `init` / `start` / `stop` / `restart` / `status` / `log`,
|
|
61
|
+
含实例锁、三重进程身份校验、启动早退检测、三层健康判定。
|
|
62
|
+
- 配置闭环:`config get|set|edit|diff|rollback`,tomlkit 无损补丁、官方
|
|
63
|
+
`frps verify` 权威校验、原子写、快照历史、失败自动回滚。
|
|
64
|
+
- 运维面:`doctor` 体检与安全 lint、`service install` systemd 集成、`kick`。
|
|
65
|
+
- 服务端插件:多用户鉴权、端口白名单、代理名/类型约束、`max_proxies` 配额、
|
|
66
|
+
异步 JSONL 审计;fail-closed 与绑回环硬约束。
|
|
67
|
+
- 五层测试:单元 / 集成 / CLI / 契约(真二进制)/ 故障注入。
|
frpsctl-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 frpsctl contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
frpsctl-0.2.0/NOTICE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# NOTICE
|
|
2
|
+
|
|
3
|
+
## 本仓库的许可
|
|
4
|
+
|
|
5
|
+
frpsctl 自身的代码以 **MIT** 许可发布,全文见 [`LICENSE`](LICENSE)。
|
|
6
|
+
|
|
7
|
+
## 关于 frp 的许可(重要)
|
|
8
|
+
|
|
9
|
+
本仓库**不包含** frp 的源代码,也不包含其二进制。
|
|
10
|
+
|
|
11
|
+
`frpsctl install` 会按需从 [fatedier/frp](https://github.com/fatedier/frp) 的
|
|
12
|
+
官方发布页下载 `frps` 二进制。该二进制是独立的第三方软件,遵循其自身的
|
|
13
|
+
**Apache License 2.0**,与本仓库的 MIT 许可互不相干:
|
|
14
|
+
|
|
15
|
+
- 它**不随本包分发**,由使用者自行获取;
|
|
16
|
+
- 使用它需要遵守 Apache-2.0 的条款(包括其许可与声明的保留要求);
|
|
17
|
+
- 本仓库的 MIT 许可**只覆盖 frpsctl 的 Python 代码**,不覆盖那个二进制。
|
|
18
|
+
|
|
19
|
+
本工具的设计前提正是"把 frps 当作外部可替换依赖"(见设计文档 ADR-4):
|
|
20
|
+
不 fork、不 patch、不内嵌,始终使用官方原版二进制,并对其做 sha256 强校验。
|
|
21
|
+
|
|
22
|
+
## 为什么不把这段写在 LICENSE 里
|
|
23
|
+
|
|
24
|
+
GitHub 的许可识别要求 `LICENSE` 文件**以标准模板开头且基本不被改动**。
|
|
25
|
+
把第三方软件的许可说明附在 MIT 正文之后,会导致该文件无法被识别为 MIT
|
|
26
|
+
(仓库页面会显示 "Other"),从而少一个 license 徽章。
|
|
27
|
+
|
|
28
|
+
因此拆成两个文件,各归其位:`LICENSE` 只放 MIT 正文,第三方许可说明放这里。
|