@recur-tw/cli 0.1.0

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.
package/AGENT.md ADDED
@@ -0,0 +1,268 @@
1
+ ---
2
+ name: recur-cli
3
+ version: 0.1.0
4
+ description: Manage subscriptions, products, customers, webhooks, and checkout sessions on Recur — Taiwan's subscription payment platform.
5
+ requires:
6
+ bins: ["recur"]
7
+ env: ["RECUR_SECRET_KEY"]
8
+ capabilities:
9
+ - products
10
+ - customers
11
+ - subscriptions
12
+ - orders
13
+ - invoices
14
+ - webhooks
15
+ - checkout-sessions
16
+ - schema-introspection
17
+ - dry-run-validation
18
+ ---
19
+
20
+ # Recur CLI — Agent Instructions
21
+
22
+ > For AI agents using `@recur-tw/cli`. Humans: see README.md.
23
+
24
+ ## Quick Start
25
+
26
+ ```bash
27
+ # 1. Auth (once)
28
+ export RECUR_SECRET_KEY=sk_test_xxx
29
+
30
+ # 2. Discover API shape
31
+ recur schema --output json
32
+
33
+ # 3. Drill into a specific action
34
+ recur schema products.create --output json
35
+
36
+ # 4. Dry-run first (validates locally, no API call)
37
+ recur products create --json '{"name":"Test","price":299}' --dry-run --output json
38
+
39
+ # 5. Execute
40
+ recur products create --json '{"name":"Test","price":299}' --output json
41
+ ```
42
+
43
+ ## Rules (Non-Negotiable)
44
+
45
+ 1. **`--output json` always.** Table format is for humans. When piped, JSON is auto-detected.
46
+ 2. **`--dry-run` before mutations.** create, update, archive, cancel, delete — always dry-run first.
47
+ 3. **`--fields id,name,price` on list calls.** Minimizes tokens. Only request fields you need.
48
+ 4. **`--json` for write payloads.** Maps 1:1 to API body. Prefer over individual flags.
49
+ 5. **Confirm with user before executing writes** without `--dry-run`.
50
+ 6. **Prices are TWD integers.** `299` = NT$299. Never divide by 100.
51
+ 7. **`--immediately` is a boolean flag.** Use `--immediately` or omit. Never `--immediately false`.
52
+ 8. **IDs are CUID.** e.g. `k672i1kd6zgrw5b6w39xwpx3`. No prefix like `cus_` or `prod_`.
53
+
54
+ ## Authentication
55
+
56
+ Priority: `--key` flag > `RECUR_SECRET_KEY` env > `~/.recur/credentials.json`
57
+
58
+ ```bash
59
+ # Best for agents: env var (no filesystem writes)
60
+ export RECUR_SECRET_KEY=sk_test_xxx
61
+
62
+ # Or per-command
63
+ recur --key sk_test_xxx products list --output json
64
+ ```
65
+
66
+ ## Schema Introspection
67
+
68
+ The CLI is self-describing. Use `recur schema` to discover everything at runtime.
69
+
70
+ ```bash
71
+ # All resources (compact overview)
72
+ recur schema --output json
73
+ # Returns: [{resource, description, actions: [{name, method, path, description}]}]
74
+
75
+ # Specific action (full detail)
76
+ recur schema products.create --output json
77
+ ```
78
+
79
+ ### Schema Fields
80
+
81
+ | Field | What it tells you |
82
+ |-------|-------------------|
83
+ | `bodySchema[field].required` | Must be provided |
84
+ | `bodySchema[field].enum` | Valid values (array) |
85
+ | `bodySchema[field].cliFlag` | CLI flag when different from API field (e.g. `--product-id` for `productId`) |
86
+ | `responseFields[].name` | Field name for `--fields` |
87
+ | `responseFields[].type` | `string`, `number`, `boolean`, `datetime`, `object`, `array` |
88
+ | `responseFields[].description` | What this field contains |
89
+ | `responseFields[].nullable` | `true` if field can be null |
90
+ | `supportsDryRun` | Whether `--dry-run` is available |
91
+ | `pagination.cursor` | Cursor param name (e.g. `starting_after`) |
92
+ | `pagination.defaultLimit` | Default page size |
93
+ | `pagination.maxLimit` | Max page size |
94
+
95
+ ### Example Schema Output
96
+
97
+ ```json
98
+ {
99
+ "resource": "products",
100
+ "action": "create",
101
+ "method": "POST",
102
+ "path": "/v1/products",
103
+ "bodySchema": {
104
+ "name": { "type": "string", "description": "Product name", "required": true },
105
+ "price": { "type": "number", "description": "Price in TWD (integer)", "required": true },
106
+ "interval": { "type": "string", "description": "Billing interval", "enum": ["monthly", "yearly"] }
107
+ },
108
+ "responseFields": [
109
+ { "name": "id", "type": "string", "description": "Product ID (CUID)" },
110
+ { "name": "price", "type": "number", "description": "Price in TWD (integer)" },
111
+ { "name": "active", "type": "boolean" }
112
+ ],
113
+ "supportsDryRun": true
114
+ }
115
+ ```
116
+
117
+ ## Output Parsing
118
+
119
+ | Stream | Contains | Parse with |
120
+ |--------|----------|------------|
121
+ | **stdout** | Data only (JSON/CSV/NDJSON/table) | `jq`, JSON.parse |
122
+ | **stderr** | Warnings, errors, `[dry-run]` labels | Ignore or log |
123
+ | **Exit 0** | Success | Check code |
124
+ | **Exit 1** | Error | Read stderr |
125
+
126
+ ```bash
127
+ # Capture data, discard warnings
128
+ result=$(recur products list --fields id,name --output json 2>/dev/null)
129
+
130
+ # NDJSON for streaming (one JSON object per line)
131
+ recur customers list --output ndjson --fields id,email 2>/dev/null
132
+ ```
133
+
134
+ ### Error Shape
135
+
136
+ Errors print to stderr as `Error: [code] message`. The API error body follows:
137
+
138
+ ```json
139
+ {
140
+ "error": {
141
+ "code": "not_found",
142
+ "message": "Product not found",
143
+ "doc_url": "https://docs.recur.tw/api#errors"
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### Dry-Run Behavior
149
+
150
+ - Validates payload locally (required fields, enum values, numeric types)
151
+ - Prints validated payload to stdout as JSON
152
+ - Labels `[dry-run]` to stderr
153
+ - **No API call is made**
154
+ - Exit 1 if validation fails, exit 0 if valid
155
+
156
+ ```bash
157
+ # Dry-run output:
158
+ # stderr: [dry-run] Would create product:
159
+ # stdout: {"name":"Test","price":299,"type":"SUBSCRIPTION"}
160
+ recur products create --json '{"name":"Test","price":299,"type":"SUBSCRIPTION"}' --dry-run --output json
161
+ ```
162
+
163
+ ## Agent Workflow Patterns
164
+
165
+ ### Pattern 1: Lookup → Act → Verify
166
+
167
+ ```bash
168
+ # 1. Find the customer
169
+ email=$(recur customers list --fields id,email --output json 2>/dev/null | jq -r '.[] | select(.email=="user@example.com") | .id')
170
+
171
+ # 2. Dry-run the cancel
172
+ recur subscriptions cancel "$sub_id" --dry-run --output json
173
+
174
+ # 3. Confirm with user, then execute
175
+ recur subscriptions cancel "$sub_id" --output json
176
+ ```
177
+
178
+ ### Pattern 2: Minimal Reads
179
+
180
+ ```bash
181
+ # Bad: fetches all fields (wastes tokens)
182
+ recur products list --output json
183
+
184
+ # Good: only what you need
185
+ recur products list --fields id,name,price --output json
186
+ ```
187
+
188
+ ### Pattern 3: Schema-Driven Discovery
189
+
190
+ When you don't know the API shape, introspect first:
191
+
192
+ ```bash
193
+ # What resources exist?
194
+ recur schema --output json 2>/dev/null | jq '.[].resource'
195
+
196
+ # What can I do with subscriptions?
197
+ recur schema subscriptions.cancel --output json 2>/dev/null | jq '{bodySchema, supportsDryRun}'
198
+ ```
199
+
200
+ ## Error Recovery
201
+
202
+ All errors include machine-parseable hints:
203
+
204
+ | Error | CLI behavior |
205
+ |-------|-------------|
206
+ | Unknown resource | Lists available resources |
207
+ | Unknown action | Lists available actions for that resource |
208
+ | Missing required field | Lists missing field names |
209
+ | Invalid enum value | Lists valid values |
210
+ | Invalid ID | Describes what's wrong (path traversal, control chars) |
211
+ | HTTP 404 | Suggests checking ID or slug |
212
+
213
+ ## Available Resources
214
+
215
+ | Resource | Actions | Writes | Paginated |
216
+ |----------|---------|--------|-----------|
217
+ | products | list, get, create, update, archive | --dry-run, --json | No |
218
+ | customers | list, get, update | --dry-run, --json | Yes |
219
+ | subscriptions | list, get, cancel | --dry-run, --json | Yes |
220
+ | orders | list, get | Read-only | Yes |
221
+ | invoices | list, get | Read-only | Yes |
222
+ | webhooks | list, create, test, delete, listen | --dry-run, --json | No |
223
+ | checkouts | create, get | --dry-run, --json | No |
224
+
225
+ ## Context Budget Tips
226
+
227
+ 1. **`--fields`** — Only request fields you need. `--fields id,status` instead of full object.
228
+ 2. **`--limit`** — Set explicit limits. Default is 10-20 but say `--limit 5` if you only need a few.
229
+ 3. **Pipe to `jq`** — Extract what you need: `recur products list --output json 2>/dev/null | jq '.[0].id'`
230
+ 4. **Schema caching** — Call `recur schema` once per session, not per command.
231
+ 5. **NDJSON** — For large lists, process line-by-line instead of loading entire array.
232
+
233
+ ## Local Webhook Development
234
+
235
+ `recur webhooks listen` forwards live webhook events to a local URL via SSE relay. Use this during development to test webhook handlers without deploying to a public URL.
236
+
237
+ ```bash
238
+ # Forward all events to local server
239
+ recur webhooks listen http://localhost:3000/api/webhooks --output json
240
+
241
+ # Filter specific event types (client-side filtering)
242
+ recur webhooks listen http://localhost:3000/api/webhooks --events checkout.completed,order.paid
243
+ ```
244
+
245
+ ### How it works
246
+
247
+ 1. CLI connects to Recur's SSE relay with your Secret Key
248
+ 2. Relay assigns a per-session signing secret (`whsec_*`)
249
+ 3. When Recur dispatches a webhook event, the relay pushes it to the CLI via SSE
250
+ 4. CLI POSTs the event to your local URL with these headers:
251
+ - `X-Recur-Signature` — HMAC-SHA256 signature (verify with the signing secret)
252
+ - `X-Recur-Event-Type` — e.g. `checkout.completed`
253
+ - `X-Recur-Event-Id` — event ID
254
+ - `X-Recur-Timestamp` — ISO 8601 timestamp
255
+
256
+ ### Agent usage notes
257
+
258
+ - **Not a daemon.** `listen` is a long-running foreground process — run it in a separate terminal or background process.
259
+ - **Signing secret changes per session.** Don't cache it across sessions.
260
+ - **Auto-reconnects** on disconnect (up to 10 consecutive failures).
261
+ - **`--events` is client-side filtering.** All events are still sent from the relay; the CLI drops non-matching ones locally.
262
+ - **Exit codes:** 0 = graceful shutdown (Ctrl+C / SIGTERM), 1 = max failures exceeded.
263
+
264
+ ## Security
265
+
266
+ - All API responses are sanitized: control characters (ASCII < 0x20) are stripped from string values to prevent prompt injection.
267
+ - Resource IDs are validated against path traversal (`..`, `/`), query injection (`?`, `#`), and control characters.
268
+ - API keys are validated format before sending (`sk_test_*` or `sk_live_*`).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-present Recur (recur.tw)
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.
package/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # @recur-tw/cli
2
+
3
+ Recur 訂閱金流平台的命令列工具。管理產品、客戶、訂閱、訂單等資源,同時支援人類使用與 AI Agent 整合。
4
+
5
+ ## 安裝
6
+
7
+ ```bash
8
+ npm install -g @recur-tw/cli
9
+ ```
10
+
11
+ 或使用 npx 直接執行:
12
+
13
+ ```bash
14
+ npx @recur-tw/cli products list
15
+ ```
16
+
17
+ ## 快速開始
18
+
19
+ ### 1. 設定 API Key
20
+
21
+ 前往 [Recur Dashboard](https://app.recur.tw) 取得 Secret Key(`sk_test_*` 或 `sk_live_*`),然後執行:
22
+
23
+ ```bash
24
+ recur --key sk_test_xxx login
25
+ ```
26
+
27
+ API Key 會安全存儲在 `~/.recur/credentials.json`(檔案權限 `0600`)。
28
+
29
+ 也可以透過環境變數設定:
30
+
31
+ ```bash
32
+ export RECUR_SECRET_KEY=sk_test_xxx
33
+ ```
34
+
35
+ ### 2. 開始使用
36
+
37
+ ```bash
38
+ # 列出所有產品
39
+ recur products list
40
+
41
+ # 查看訂閱狀態
42
+ recur subscriptions list --status active
43
+
44
+ # 查看客戶資料
45
+ recur customers list --email user@example.com
46
+ ```
47
+
48
+ ## 指令一覽
49
+
50
+ ```
51
+ recur <資源> <動作> [選項]
52
+ ```
53
+
54
+ ### 全域選項
55
+
56
+ | 選項 | 說明 |
57
+ |------|------|
58
+ | `--key <sk_*>` | 指定 API Secret Key |
59
+ | `--profile <name>` | 使用指定的 profile |
60
+ | `--base-url <url>` | API 位址覆寫 |
61
+ | `--output <format>` | 輸出格式:`json`、`table`(預設)、`csv` |
62
+ | `--fields <fields>` | 逗號分隔的欄位篩選 |
63
+ | `--dry-run` | 僅本地驗證,不發送 API 請求 |
64
+ | `--json <payload>` | 直接傳入 JSON 作為 API request body |
65
+
66
+ ### 驗證管理
67
+
68
+ ```bash
69
+ recur login # 互動式設定 API Key
70
+ recur --key sk_test_xxx login # 直接設定
71
+ recur whoami # 顯示目前 profile
72
+ recur profiles # 列出所有 profile
73
+ recur use <profile> # 切換 profile
74
+ recur logout [profile] # 移除 profile
75
+ ```
76
+
77
+ #### 多環境管理
78
+
79
+ ```bash
80
+ # 儲存 sandbox profile
81
+ recur --key sk_test_xxx login --name sandbox
82
+
83
+ # 儲存 production profile
84
+ recur --key sk_live_xxx login --name production
85
+
86
+ # 切換環境
87
+ recur use production
88
+ recur products list # 使用 production key
89
+
90
+ # 或單次指定
91
+ recur --profile sandbox products list
92
+ ```
93
+
94
+ ### 產品 (Products)
95
+
96
+ ```bash
97
+ recur products list # 列出所有產品
98
+ recur products list --status active # 篩選狀態
99
+ recur products get <id> # 以 ID 查詢
100
+ recur products get <slug> # 以 slug 查詢
101
+ recur products create --name "方案" --price 299 --interval monthly --type SUBSCRIPTION
102
+ recur products create --json '{"name":"方案","price":299,"interval":"monthly"}'
103
+ recur products update <id> --name "新名稱"
104
+ recur products archive <id>
105
+ ```
106
+
107
+ ### 客戶 (Customers)
108
+
109
+ ```bash
110
+ recur customers list # 列出所有客戶
111
+ recur customers list --email user@example.com # 以 email 篩選
112
+ recur customers list --limit 50 --offset 100 # 分頁
113
+ recur customers get <id> # 查詢客戶詳情
114
+ recur customers update <id> --name "新名稱"
115
+ ```
116
+
117
+ ### 訂閱 (Subscriptions)
118
+
119
+ ```bash
120
+ recur subscriptions list # 列出所有訂閱
121
+ recur subscriptions list --status active # 篩選狀態
122
+ recur subscriptions list --customer-id cus_xxx # 以客戶篩選
123
+ recur subscriptions list --email user@example.com # 以 email 篩選
124
+ recur subscriptions get <id> # 查詢訂閱詳情
125
+ recur subscriptions cancel <id> # 期末取消
126
+ recur subscriptions cancel <id> --immediately # 立即取消
127
+ ```
128
+
129
+ ### 訂單 (Orders)
130
+
131
+ ```bash
132
+ recur orders list # 列出所有訂單
133
+ recur orders list --status paid # 篩選狀態
134
+ recur orders list --customer-id cus_xxx # 以客戶篩選
135
+ recur orders get <id> # 查詢訂單詳情
136
+ ```
137
+
138
+ ### 帳單 (Invoices)
139
+
140
+ ```bash
141
+ recur invoices list # 列出所有帳單
142
+ recur invoices list --subscription-id sub_xxx # 以訂閱篩選
143
+ recur invoices list --customer-id cus_xxx # 以客戶篩選
144
+ recur invoices list --status paid # 篩選狀態
145
+ recur invoices get <id> # 查詢帳單詳情
146
+ ```
147
+
148
+ ### Webhook
149
+
150
+ ```bash
151
+ recur webhooks list # 列出所有 webhook
152
+ recur webhooks create --url https://example.com/hook # 建立 webhook
153
+ recur webhooks create --json '{"url":"...","events":["checkout.completed"]}'
154
+ recur webhooks test <id> # 發送測試事件
155
+ recur webhooks test <id> --event subscription.canceled # 指定事件類型
156
+ recur webhooks delete <id> # 刪除 webhook
157
+ recur webhooks listen http://localhost:3000/api/webhooks # 即時轉發到本地
158
+ ```
159
+
160
+ ### 本地 Webhook 開發(`webhooks listen`)
161
+
162
+ 類似 `stripe listen`,`recur webhooks listen` 讓你在本地開發時即時接收 webhook 事件,不需要公開 URL。
163
+
164
+ ```bash
165
+ # 基本用法:轉發所有事件到本地伺服器
166
+ recur webhooks listen http://localhost:3000/api/webhooks
167
+
168
+ # 只轉發特定事件類型
169
+ recur webhooks listen http://localhost:3000/api/webhooks --events checkout.completed,order.paid
170
+ ```
171
+
172
+ #### 運作原理
173
+
174
+ 1. CLI 透過 SSE 連線到 Recur 的 relay 服務
175
+ 2. 收到一個專屬的 signing secret(`whsec_*`)
176
+ 3. 當 Recur 平台觸發 webhook 事件時,relay 即時推送到 CLI
177
+ 4. CLI 將事件 POST 到你指定的本地 URL,附帶以下 headers:
178
+ - `X-Recur-Signature` — HMAC-SHA256 簽名(用 signing secret 驗證)
179
+ - `X-Recur-Event-Type` — 事件類型
180
+ - `X-Recur-Event-Id` — 事件 ID
181
+ - `X-Recur-Timestamp` — 事件時間戳
182
+
183
+ #### 輸出範例
184
+
185
+ ```
186
+ Ready! Listening for webhook events...
187
+
188
+ Signing secret: whsec_abc123def456...
189
+ Forwarding to: http://localhost:3000/api/webhooks
190
+ Environment: sandbox
191
+
192
+ 12:03:45 -> checkout.completed [evt_abc123] 200 (23ms)
193
+ 12:03:46 -> subscription.activated [evt_def456] 200 (18ms)
194
+ 12:05:12 -> invoice.paid [evt_ghi789] 500 Internal Server Error (45ms)
195
+ ```
196
+
197
+ #### 選項
198
+
199
+ | 選項 | 說明 |
200
+ |------|------|
201
+ | `--events <types>` | 逗號分隔的事件類型篩選(CLI 端過濾) |
202
+ | `--relay-url <url>` | 覆寫 relay 服務 URL(開發用) |
203
+
204
+ #### 注意事項
205
+
206
+ - 需要 Secret Key(`sk_test_*` 或 `sk_live_*`)
207
+ - 斷線時自動重連(最多 10 次連續失敗後退出)
208
+ - `Ctrl+C` 優雅關閉
209
+ - Signing secret 每次連線都會重新產生
210
+
211
+ ### Checkout Sessions
212
+
213
+ ```bash
214
+ recur checkouts create --product-id prod_xxx --customer-email user@example.com
215
+ recur checkouts create --json '{"productId":"prod_xxx","successUrl":"https://..."}'
216
+ recur checkouts get <id> # 查詢 session 狀態
217
+ ```
218
+
219
+ ## 輸出格式
220
+
221
+ ### Table(預設,適合人類閱讀)
222
+
223
+ ```bash
224
+ $ recur products list --fields name,price,type
225
+
226
+ name price type
227
+ ─────────────── ───── ────────────
228
+ 大師方案 - 月繳 799 SUBSCRIPTION
229
+ 超級方案 - 月繳 499 SUBSCRIPTION
230
+
231
+ 2 results
232
+ ```
233
+
234
+ ### JSON(適合程式解析與 AI Agent)
235
+
236
+ ```bash
237
+ $ recur products list --output json
238
+
239
+ [
240
+ { "id": "prod_xxx", "name": "大師方案", "price": 799 },
241
+ ...
242
+ ]
243
+ ```
244
+
245
+ 搭配 `jq` 使用:
246
+
247
+ ```bash
248
+ recur products list --output json | jq '.[].name'
249
+ recur subscriptions list --output json | jq '[.[] | select(.status == "ACTIVE")]'
250
+ ```
251
+
252
+ ### CSV(適合匯出與試算表)
253
+
254
+ ```bash
255
+ $ recur customers list --output csv --fields email,name > customers.csv
256
+ ```
257
+
258
+ ### 欄位篩選
259
+
260
+ 用 `--fields` 減少輸出量,適合腳本和 AI Agent 控制 context window:
261
+
262
+ ```bash
263
+ recur products list --fields id,name,price
264
+ recur customers list --fields email,name --output json
265
+ ```
266
+
267
+ ## AI Agent 整合
268
+
269
+ 此 CLI 遵循 [Agent-Optimized CLI](https://justin.poehnelt.com/posts/rewrite-your-cli-for-ai-agents/) 設計原則:
270
+
271
+ ### Schema 自省
272
+
273
+ Agent 可以透過 `recur schema` 動態查詢 API 規格,不需要讀文件:
274
+
275
+ ```bash
276
+ # 列出所有可用資源
277
+ recur schema
278
+
279
+ # 查看特定資源的所有動作
280
+ recur schema products
281
+
282
+ # 查看特定動作的完整參數定義
283
+ recur schema products.create
284
+ ```
285
+
286
+ 輸出包含 `method`、`path`、`params`、`bodySchema`、`responseFields`、`supportsDryRun` 等結構化資訊。
287
+
288
+ ### Raw JSON Payload
289
+
290
+ `--json` 讓 Agent 直接傳入 API body,不需要逐一對應 flag:
291
+
292
+ ```bash
293
+ recur products create --json '{"name":"Pro Plan","price":599,"interval":"monthly","type":"SUBSCRIPTION"}'
294
+ ```
295
+
296
+ ### Dry Run
297
+
298
+ 所有寫入操作支援 `--dry-run`,讓 Agent 先驗證再執行:
299
+
300
+ ```bash
301
+ recur products create --dry-run --json '{"name":"Test","price":299}'
302
+ # [dry-run] Would create product:
303
+ # { "name": "Test", "price": 299 }
304
+ ```
305
+
306
+ ### Input 驗證(防止 AI 幻覺)
307
+
308
+ CLI 內建多層輸入驗證,防止 Agent 送出錯誤的 ID 或惡意輸入:
309
+
310
+ - Resource ID 前綴驗證(`prod_*`、`cus_*`、`sub_*` 等)
311
+ - 路徑穿越偵測(`../`、`/`、`\`)
312
+ - 特殊字元阻擋(`?`、`#`、`%`)
313
+ - 控制字元過濾
314
+
315
+ ## 驗證方式
316
+
317
+ API Key 的解析優先順序:
318
+
319
+ 1. `--key` 參數(最高優先)
320
+ 2. `RECUR_SECRET_KEY` 環境變數
321
+ 3. `~/.recur/credentials.json` 中的 active profile
322
+
323
+ ### API Key 類型
324
+
325
+ | 類型 | 格式 | 用途 |
326
+ |------|------|------|
327
+ | Secret Key (Sandbox) | `sk_test_*` | 測試環境,完整權限 |
328
+ | Secret Key (Production) | `sk_live_*` | 正式環境,完整權限 |
329
+
330
+ CLI 僅接受 Secret Key。Publishable Key(`pk_*`)僅供前端 SDK 使用。
331
+
332
+ ### 安全性
333
+
334
+ - API Key 以 `0600` 權限存儲在 `~/.recur/credentials.json`
335
+ - CLI 不直接連接資料庫,所有操作經由 API server 驗證
336
+ - 每個 API Key 綁定一個 Organization,資料完全隔離
337
+ - Key 以 SHA-256 hash 存儲在 server 端
338
+
339
+ ## 程式化使用
340
+
341
+ `@recur-tw/cli` 也提供 library API,可在 Node.js 中直接使用:
342
+
343
+ ```typescript
344
+ import { RecurClient, resolveSecretKey, resolveBaseUrl } from '@recur-tw/cli'
345
+
346
+ const client = new RecurClient({
347
+ baseUrl: resolveBaseUrl({}),
348
+ secretKey: resolveSecretKey({}),
349
+ })
350
+
351
+ const products = await client.get('/v1/products')
352
+ ```
353
+
354
+ ## 系統需求
355
+
356
+ - Node.js >= 18
357
+ - 作業系統:macOS、Linux、Windows
358
+
359
+ ## 授權
360
+
361
+ MIT
362
+
363
+ ## 相關連結
364
+
365
+ - [Recur 官網](https://recur.tw)
366
+ - [API 文件](https://recur.tw/docs/api)
367
+ - [SDK (recur-tw)](https://www.npmjs.com/package/recur-tw)
368
+ - [Dashboard](https://app.recur.tw)
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };