Skip to content

Commit 21244ac

Browse files
author
admin
committed
refactor: 拆分 init/create 命令,init 只配置认证,create 独立创建隧道
- init 不再创建隧道,只保存 API Token + Account ID - 新增 create 命令专门创建隧道 - init 引导添加 CF 控制台直达链接 - 更新所有命令提示文字、README、skills 文档
1 parent 9e6772e commit 21244ac

10 files changed

Lines changed: 101 additions & 62 deletions

File tree

.claude/skills/cftunnel.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ curl -fsSL https://github.com/qingchencloud/cftunnel/releases/latest/download/cf
1212
## 快速上手
1313

1414
```bash
15-
cftunnel init # 交互式初始化
15+
cftunnel init # 配置认证
16+
cftunnel create my-tunnel # 创建隧道
1617
cftunnel add myapp 3000 --domain myapp.example.com # 添加路由
1718
cftunnel up # 启动隧道
1819
```
@@ -21,7 +22,8 @@ cftunnel up # 启动隧道
2122

2223
| 命令 | 说明 |
2324
|------|------|
24-
| `init` | 交互式初始化(支持 `--token`/`--account`/`--name` 非交互模式) |
25+
| `init` | 配置认证(支持 `--token`/`--account` 非交互模式) |
26+
| `create <名称>` | 创建隧道 |
2527
| `add <名称> <端口> --domain <域名>` | 添加路由(自动创建 CNAME) |
2628
| `remove <名称>` | 删除路由(清理 DNS) |
2729
| `list` | 列出所有路由 |

README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Cloudflare Tunnel 一键管理工具 — 3 条命令搞定内网穿透。
66

77
## 特性
88

9-
- **一键初始化**交互式向导,输入 Token 即可创建隧道
9+
- **分步初始化**`init` 配置认证,`create` 创建隧道,职责清晰
1010
- **自动 DNS** — 添加路由时自动创建 CNAME 记录
1111
- **进程托管** — 自动下载 cloudflared,支持注册系统服务开机自启
1212
- **自动更新** — 内置版本检查和自更新
@@ -66,24 +66,30 @@ make build
6666
6767
区域资源 → 包括 → 特定区域 → 选择你的域名
6868

69-
### 2. 初始化
69+
### 2. 初始化认证
7070

7171
```bash
7272
# 交互式(推荐)
7373
cftunnel init
7474

7575
# 非交互式
76-
cftunnel init --token <your-token> --account <account-id> --name my-tunnel
76+
cftunnel init --token <your-token> --account <account-id>
7777
```
7878

79-
### 3. 添加路由
79+
### 3. 创建隧道
80+
81+
```bash
82+
cftunnel create my-tunnel
83+
```
84+
85+
### 4. 添加路由
8086

8187
```bash
8288
# 将 app.example.com 指向本地 3000 端口
8389
cftunnel add myapp 3000 --domain app.example.com
8490
```
8591

86-
### 4. 启动隧道
92+
### 5. 启动隧道
8793

8894
```bash
8995
cftunnel up
@@ -97,7 +103,8 @@ cftunnel up
97103

98104
| 命令 | 说明 |
99105
|------|------|
100-
| `cftunnel init` | 交互式初始化(支持 `--token`/`--account`/`--name`|
106+
| `cftunnel init` | 配置认证信息(支持 `--token`/`--account`|
107+
| `cftunnel create <名称>` | 创建隧道 |
101108
| `cftunnel add <名称> <端口> --domain <域名>` | 添加路由(自动创建 CNAME) |
102109
| `cftunnel remove <名称>` | 删除路由(自动清理 DNS) |
103110
| `cftunnel list` | 列出所有路由 |
@@ -138,6 +145,7 @@ cftunnel up
138145

139146
```bash
140147
cftunnel init
148+
cftunnel create dev-tunnel
141149
cftunnel add dev 3000 --domain dev.example.com
142150
cftunnel up
143151
# 现在 dev.example.com 指向 localhost:3000
@@ -203,7 +211,8 @@ cftunnel 内置了 AI 助手 Skills,让 Claude Code、OpenClaw 等 AI 编码
203211
安装: curl -fsSL https://raw.githubusercontent.com/qingchencloud/cftunnel/main/install.sh | bash
204212

205213
常用命令:
206-
- cftunnel init --token <TOKEN> --account <ACCOUNT_ID> --name <名称> # 初始化
214+
- cftunnel init --token <TOKEN> --account <ACCOUNT_ID> # 配置认证
215+
- cftunnel create <名称> # 创建隧道
207216
- cftunnel add <名称> <端口> --domain <域名> # 添加路由
208217
- cftunnel remove <名称> # 删除路由
209218
- cftunnel up / down # 启停隧道
@@ -212,7 +221,7 @@ cftunnel 内置了 AI 助手 Skills,让 Claude Code、OpenClaw 等 AI 编码
212221
- cftunnel destroy --force # 删除隧道
213222
- cftunnel install # 注册系统服务
214223

215-
执行命令前请确认用户已完成 cftunnel init 初始化
224+
执行命令前请确认用户已完成 cftunnel init 和 cftunnel create
216225
添加路由时会自动创建 DNS CNAME 记录,删除时自动清理。
217226
```
218227

cmd/add.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var addCmd = &cobra.Command{
3131
return err
3232
}
3333
if cfg.Tunnel.ID == "" {
34-
return fmt.Errorf("请先运行 cftunnel init")
34+
return fmt.Errorf("请先运行 cftunnel init && cftunnel create <名称>")
3535
}
3636
if cfg.FindRoute(name) != nil {
3737
return fmt.Errorf("路由 %s 已存在", name)

cmd/create.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/qingchencloud/cftunnel/internal/cfapi"
8+
"github.com/qingchencloud/cftunnel/internal/config"
9+
"github.com/spf13/cobra"
10+
)
11+
12+
func init() {
13+
rootCmd.AddCommand(createCmd)
14+
}
15+
16+
var createCmd = &cobra.Command{
17+
Use: "create <隧道名称>",
18+
Short: "创建 Cloudflare Tunnel",
19+
Args: cobra.ExactArgs(1),
20+
RunE: func(cmd *cobra.Command, args []string) error {
21+
cfg, err := config.Load()
22+
if err != nil {
23+
return err
24+
}
25+
if cfg.Auth.APIToken == "" {
26+
return fmt.Errorf("请先运行 cftunnel init 配置认证信息")
27+
}
28+
if cfg.Tunnel.ID != "" {
29+
return fmt.Errorf("已存在隧道 %s (%s),如需重建请先 cftunnel destroy", cfg.Tunnel.Name, cfg.Tunnel.ID)
30+
}
31+
32+
client := cfapi.New(cfg.Auth.APIToken, cfg.Auth.AccountID)
33+
ctx := context.Background()
34+
35+
fmt.Println("正在创建隧道...")
36+
tunnel, err := client.CreateTunnel(ctx, args[0])
37+
if err != nil {
38+
return err
39+
}
40+
fmt.Printf("隧道已创建: %s (%s)\n", tunnel.Name, tunnel.ID)
41+
42+
token, err := client.GetTunnelToken(ctx, tunnel.ID)
43+
if err != nil {
44+
return err
45+
}
46+
47+
cfg.Tunnel = config.TunnelConfig{ID: tunnel.ID, Name: tunnel.Name, Token: token}
48+
if err := cfg.Save(); err != nil {
49+
return err
50+
}
51+
fmt.Println("\n下一步: cftunnel add <名称> <端口> --domain <域名>")
52+
return nil
53+
},
54+
}

cmd/init.go

Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,30 @@
11
package cmd
22

33
import (
4-
"context"
54
"fmt"
65
"strings"
76

87
"github.com/charmbracelet/huh"
9-
"github.com/qingchencloud/cftunnel/internal/cfapi"
108
"github.com/qingchencloud/cftunnel/internal/config"
119
"github.com/spf13/cobra"
1210
)
1311

14-
var initToken, initAccountID, initTunnelName string
12+
var initToken, initAccountID string
1513

1614
func init() {
1715
initCmd.Flags().StringVar(&initToken, "token", "", "API 令牌")
1816
initCmd.Flags().StringVar(&initAccountID, "account", "", "账户 ID")
19-
initCmd.Flags().StringVar(&initTunnelName, "name", "my-tunnel", "隧道名称")
2017
rootCmd.AddCommand(initCmd)
2118
}
2219

2320
var initCmd = &cobra.Command{
2421
Use: "init",
25-
Short: "交互式初始化:输入 Token → 选域名 → 创建隧道",
22+
Short: "配置 Cloudflare API 认证信息",
2623
RunE: func(cmd *cobra.Command, args []string) error {
27-
fmt.Println("=== Cloudflare Tunnel 初始化向导 ===")
24+
fmt.Println("=== Cloudflare Tunnel 初始化 ===")
2825
fmt.Println()
29-
fmt.Println("需要以下信息(首次使用请先创建 API 令牌):")
30-
fmt.Println()
31-
fmt.Println(" 1. API 令牌获取方式:")
32-
fmt.Println(" 登录 Cloudflare → 右上角头像 → 我的个人资料 → API 令牌 → 创建令牌")
26+
fmt.Println(" 1. 创建 API 令牌:")
27+
fmt.Println(" https://dash.cloudflare.com/profile/api-tokens → 创建令牌")
3328
fmt.Println(" 选择「创建自定义令牌」→「开始使用」")
3429
fmt.Println()
3530
fmt.Println(" 添加 3 条权限(点「+ 添加更多」逐条添加):")
@@ -39,66 +34,43 @@ var initCmd = &cobra.Command{
3934
fmt.Println(" │ 第 3 行: 区域 │ 区域设置 │ 读取 │")
4035
fmt.Println(" └──────────────────────────────────────────────────┘")
4136
fmt.Println(" 提示: 第 2、3 行需先将左侧「帐户」切换为「区域」")
42-
fmt.Println()
4337
fmt.Println(" 区域资源 → 包括 → 特定区域 → 选择你的域名")
4438
fmt.Println()
45-
fmt.Println(" 2. 账户 ID 获取方式(任选其一):")
46-
fmt.Println(" 方式 A: 首页 → 点击域名 → 页面右下角「API」区域 →「账户 ID」")
39+
fmt.Println(" 2. 获取账户 ID(任选其一):")
40+
fmt.Println(" 方式 A: https://dash.cloudflare.com → 点击域名 → 右下角「API」区域")
4741
fmt.Println(" 方式 B: 首页 → 账户名称旁「⋯」→ 复制账户 ID")
4842
fmt.Println()
4943

5044
apiToken := strings.TrimSpace(initToken)
5145
accountID := strings.TrimSpace(initAccountID)
52-
tunnelName := strings.TrimSpace(initTunnelName)
5346

54-
// 有 flag 则跳过 TUI
5547
if apiToken == "" || accountID == "" {
5648
err := huh.NewForm(
5749
huh.NewGroup(
58-
huh.NewInput().Title("API 令牌 (API Token)").Value(&apiToken).Placeholder("在 Cloudflare 控制台创建"),
59-
huh.NewInput().Title("账户 ID (Account ID)").Value(&accountID).Placeholder("32 位十六进制字符串"),
60-
huh.NewInput().Title("隧道名称").Value(&tunnelName).Placeholder("my-tunnel"),
50+
huh.NewInput().Title("API 令牌 (API Token)").Value(&apiToken).
51+
Placeholder("在上方链接创建"),
52+
huh.NewInput().Title("账户 ID (Account ID)").Value(&accountID).
53+
Placeholder("32 位十六进制字符串"),
6154
),
6255
).Run()
6356
if err != nil {
6457
return err
6558
}
6659
apiToken = strings.TrimSpace(apiToken)
6760
accountID = strings.TrimSpace(accountID)
68-
tunnelName = strings.TrimSpace(tunnelName)
69-
}
70-
if tunnelName == "" {
71-
tunnelName = "my-tunnel"
72-
}
73-
74-
client := cfapi.New(apiToken, accountID)
75-
ctx := context.Background()
76-
77-
// 创建隧道
78-
fmt.Println("正在创建隧道...")
79-
tunnel, err := client.CreateTunnel(ctx, tunnelName)
80-
if err != nil {
81-
return err
8261
}
83-
fmt.Printf("隧道已创建: %s (%s)\n", tunnel.Name, tunnel.ID)
8462

85-
// 获取 Token
86-
token, err := client.GetTunnelToken(ctx, tunnel.ID)
87-
if err != nil {
88-
return err
63+
if apiToken == "" || accountID == "" {
64+
return fmt.Errorf("API 令牌和账户 ID 不能为空")
8965
}
9066

91-
// 保存配置
92-
cfg := &config.Config{
93-
Version: 1,
94-
Auth: config.AuthConfig{APIToken: apiToken, AccountID: accountID},
95-
Tunnel: config.TunnelConfig{ID: tunnel.ID, Name: tunnel.Name, Token: token},
96-
}
67+
cfg, _ := config.Load()
68+
cfg.Auth = config.AuthConfig{APIToken: apiToken, AccountID: accountID}
9769
if err := cfg.Save(); err != nil {
9870
return err
9971
}
100-
fmt.Printf("配置已保存到 %s\n", config.Path())
101-
fmt.Println("\n下一步: cftunnel add <名称> <端口> --domain <域名>")
72+
fmt.Printf("认证信息已保存到 %s\n", config.Path())
73+
fmt.Println("\n下一步: cftunnel create <隧道名称>")
10274
return nil
10375
},
10476
}

cmd/install.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var installCmd = &cobra.Command{
2323
return err
2424
}
2525
if cfg.Tunnel.Token == "" {
26-
return fmt.Errorf("请先运行 cftunnel init")
26+
return fmt.Errorf("请先运行 cftunnel init && cftunnel create <名称>")
2727
}
2828
binPath, err := daemon.EnsureCloudflared()
2929
if err != nil {

cmd/status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var statusCmd = &cobra.Command{
2121
return err
2222
}
2323
if cfg.Tunnel.ID == "" {
24-
fmt.Println("未初始化,请运行 cftunnel init")
24+
fmt.Println("未初始化,请运行 cftunnel init && cftunnel create <名称>")
2525
return nil
2626
}
2727
fmt.Printf("隧道: %s (%s)\n", cfg.Tunnel.Name, cfg.Tunnel.ID)

cmd/up.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var upCmd = &cobra.Command{
2222
return err
2323
}
2424
if cfg.Tunnel.Token == "" {
25-
return fmt.Errorf("请先运行 cftunnel init")
25+
return fmt.Errorf("请先运行 cftunnel init && cftunnel create <名称>")
2626
}
2727
// 自动检查更新(非阻塞,仅提示)
2828
if cfg.SelfUpdate.AutoCheck {

install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ curl -fsSL "$URL" | tar xz -C "$TMP"
1919
sudo install -m 755 "$TMP/cftunnel" "$INSTALL_DIR/cftunnel"
2020
rm -rf "$TMP"
2121
echo "cftunnel 已安装到 $INSTALL_DIR/cftunnel"
22-
echo "运行 cftunnel init 开始使用"
22+
echo "运行 cftunnel init 开始配置"

skills/openclaw-cftunnel.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
## 快速上手
66

77
```bash
8-
cftunnel init # 初始化
8+
cftunnel init # 配置认证
9+
cftunnel create my-tunnel # 创建隧道
910
cftunnel add myapp 3000 --domain myapp.example.com # 添加路由
1011
cftunnel up # 启动
1112
```
1213

1314
## 全部命令
1415

15-
- `init [--token --account --name]` — 初始化隧道
16+
- `init [--token --account]` — 配置 API 认证
17+
- `create <名称>` — 创建隧道
1618
- `add <名称> <端口> --domain <域名>` — 添加路由
1719
- `remove <名称>` — 删除路由
1820
- `list` — 列出路由

0 commit comments

Comments
 (0)