@optima-chat/dev-skills 0.9.3 → 0.11.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.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: "grant-credits"
3
+ description: "当用户请求赠送积分、发放积分、加积分、grant credits、充值积分、赠送余额(旧称)、奖励、补偿、推荐奖励、运营发放时,使用此技能。支持 Stage、Prod、cn-prod、cn-stage 四个环境;标识符支持邮箱/手机号/userId。"
4
+ allowed-tools: ["Bash"]
5
+ ---
6
+
7
+ # 发放积分(Grant Credits)
8
+
9
+ 为用户赠送积分(**bonus 积分桶,30 天有效期**)。经 billing 服务态端点 `POST /api/billing/admin/grant-credits` 入账,扣费时优先消费。
10
+
11
+ > **命名历史**:本命令原名 `grant-credits`(按积分)→ 余额时代曾改名 `grant-balance` + 改吃 USD → P15 钱包退役回归积分后改回 `grant-credits` + `--credits` 原生按积分。`optima-grant-balance` 仍作**废弃别名**保留(会打弃用提示)。
12
+
13
+ ## 执行方式:使用 CLI 工具
14
+
15
+ ```bash
16
+ optima-grant-credits <email|phone|userId> --credits <n> [options]
17
+ ```
18
+
19
+ **为什么用 CLI**:自动通过 email/手机号/userId 查 userId(cn 走 HTTP lookup、AWS 走 SSH 隧道)+ 执行前打印 `🎯 目标账号` 反查 + 留审计痕迹(credit_lot,幂等键前缀 `dev-skills-grant:`)+ 不影响订阅和已有积分(纯累加 bonus 桶)。
20
+
21
+ ### 参数
22
+
23
+ | 参数 | 说明 | 默认 |
24
+ |------|------|------|
25
+ | `<email\|phone\|userId>` | 用户标识(必填;cn 支持手机号/userId) | - |
26
+ | `--credits <n>` | **赠送积分数(整数 ≥1,主单位)** | - |
27
+ | `--amount <usd>` | 备选:按 USD 赠送($1 = 700 积分)。`--credits` / `--amount` **二选一** | - |
28
+ | `--description <text>` | 描述/原因(存入 credit lot(审计痕迹)) | - |
29
+ | `--env <env>` | stage, prod, cn-prod, cn-stage | stage |
30
+
31
+ ## 快速操作
32
+
33
+ ```bash
34
+ # 送 10000 积分(Prod)
35
+ optima-grant-credits user@example.com --credits 10000 --env prod
36
+
37
+ # cn-prod 手机号用户送 5000 积分
38
+ optima-grant-credits 18898654855 --credits 5000 --env cn-prod
39
+
40
+ # 按 USD 送(= 3500 积分),补偿场景
41
+ optima-grant-credits user@example.com --amount 5 --description "服务中断补偿" --env prod
42
+ ```
43
+
44
+ > 单位默认是**积分**。要按美元用 `--amount`($1=700 积分)。两者只能给一个。
45
+
46
+ ## 适用场景
47
+ 奖励/活动、客户补偿、推荐奖励、内部测试账户充值。
48
+
49
+ ## 与 grant-subscription 的区别
50
+
51
+ | | grant-credits | grant-subscription |
52
+ |---|---|---|
53
+ | 作用 | 追加 bonus 积分(30 天) | 开通/切换订阅计划 |
54
+ | 现有积分 | 不影响(纯累加) | 重置 granted |
55
+ | 现有订阅 | 不影响 | 取消旧的、建新的 |
56
+ | 适用 | 奖励、补偿、推广 | 开会员、升级计划 |
57
+
58
+ ## 安全提醒
59
+ 1. **Prod / cn-prod 谨慎**:执行前确认打印的 `🎯 目标账号`(手机/email/userId)与积分数。
60
+ 2. **纯追加**:不影响现有积分和订阅;重复执行会叠加。
61
+ 3. **30 天有效期**:bonus 桶到期作废。
62
+
63
+ ## 相关命令
64
+ - `optima-grant-subscription` - 开通订阅
65
+ - `optima-account status` - 查用户订阅+权益
66
+ - `optima-query-db` / billing `GET /api/billing/balance` - 核对积分余额
@@ -126,5 +126,5 @@ optima-grant-subscription user@example.com --plan starter --env prod
126
126
  ## 相关命令
127
127
 
128
128
  - `optima-grant-subscription` - 开通订阅(主要方式)
129
- - `optima-grant-balance` - 单独赠送 wallet 余额
129
+ - `optima-grant-credits` - 单独赠送积分
130
130
  - `optima-query-db` - 查询数据库验证结果
@@ -56,6 +56,6 @@ optima-account unban 18898654855 --env cn-prod
56
56
 
57
57
  ## 相关命令
58
58
 
59
- - `optima-grant-subscription` / `optima-grant-balance` - 开通会员 / 赠送 credits
59
+ - `optima-grant-subscription` / `optima-grant-credits` - 开通会员 / 赠送积分
60
60
  - `optima-entitlement` - 产品权益授予/撤销/查询
61
61
  - `optima-query-db` - 直查数据库核对(如 `is_active` / `banned_at`)
@@ -52,4 +52,4 @@ optima-entitlement revoke user@example.com --product-key scout-gift --reason "
52
52
  ## 相关命令
53
53
 
54
54
  - `optima-account status` - 查看用户订阅+权益汇总
55
- - `optima-grant-subscription` / `optima-grant-balance` - 开通会员 / 赠送 credits
55
+ - `optima-grant-subscription` / `optima-grant-credits` - 开通会员 / 赠送积分
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: "grant-credits"
3
+ description: "当用户请求赠送积分、发放积分、加积分、grant credits、充值积分、赠送余额(旧称)、奖励、补偿、推荐奖励、运营发放时,使用此技能。支持 Stage、Prod、cn-prod、cn-stage 四个环境;标识符支持邮箱/手机号/userId。"
4
+ allowed-tools: ["Bash"]
5
+ ---
6
+
7
+ # 发放积分(Grant Credits)
8
+
9
+ 为用户赠送积分(**bonus 积分桶,30 天有效期**)。经 billing 服务态端点 `POST /api/billing/admin/grant-credits` 入账,扣费时优先消费。
10
+
11
+ > **命名历史**:本命令原名 `grant-credits`(按积分)→ 余额时代曾改名 `grant-balance` + 改吃 USD → P15 钱包退役回归积分后改回 `grant-credits` + `--credits` 原生按积分。`optima-grant-balance` 仍作**废弃别名**保留(会打弃用提示)。
12
+
13
+ ## 执行方式:使用 CLI 工具
14
+
15
+ ```bash
16
+ optima-grant-credits <email|phone|userId> --credits <n> [options]
17
+ ```
18
+
19
+ **为什么用 CLI**:自动通过 email/手机号/userId 查 userId(cn 走 HTTP lookup、AWS 走 SSH 隧道)+ 执行前打印 `🎯 目标账号` 反查 + 留审计痕迹(credit_lot,幂等键前缀 `dev-skills-grant:`)+ 不影响订阅和已有积分(纯累加 bonus 桶)。
20
+
21
+ ### 参数
22
+
23
+ | 参数 | 说明 | 默认 |
24
+ |------|------|------|
25
+ | `<email\|phone\|userId>` | 用户标识(必填;cn 支持手机号/userId) | - |
26
+ | `--credits <n>` | **赠送积分数(整数 ≥1,主单位)** | - |
27
+ | `--amount <usd>` | 备选:按 USD 赠送($1 = 700 积分)。`--credits` / `--amount` **二选一** | - |
28
+ | `--description <text>` | 描述/原因(存入 credit lot(审计痕迹)) | - |
29
+ | `--env <env>` | stage, prod, cn-prod, cn-stage | stage |
30
+
31
+ ## 快速操作
32
+
33
+ ```bash
34
+ # 送 10000 积分(Prod)
35
+ optima-grant-credits user@example.com --credits 10000 --env prod
36
+
37
+ # cn-prod 手机号用户送 5000 积分
38
+ optima-grant-credits 18898654855 --credits 5000 --env cn-prod
39
+
40
+ # 按 USD 送(= 3500 积分),补偿场景
41
+ optima-grant-credits user@example.com --amount 5 --description "服务中断补偿" --env prod
42
+ ```
43
+
44
+ > 单位默认是**积分**。要按美元用 `--amount`($1=700 积分)。两者只能给一个。
45
+
46
+ ## 适用场景
47
+ 奖励/活动、客户补偿、推荐奖励、内部测试账户充值。
48
+
49
+ ## 与 grant-subscription 的区别
50
+
51
+ | | grant-credits | grant-subscription |
52
+ |---|---|---|
53
+ | 作用 | 追加 bonus 积分(30 天) | 开通/切换订阅计划 |
54
+ | 现有积分 | 不影响(纯累加) | 重置 granted |
55
+ | 现有订阅 | 不影响 | 取消旧的、建新的 |
56
+ | 适用 | 奖励、补偿、推广 | 开会员、升级计划 |
57
+
58
+ ## 安全提醒
59
+ 1. **Prod / cn-prod 谨慎**:执行前确认打印的 `🎯 目标账号`(手机/email/userId)与积分数。
60
+ 2. **纯追加**:不影响现有积分和订阅;重复执行会叠加。
61
+ 3. **30 天有效期**:bonus 桶到期作废。
62
+
63
+ ## 相关命令
64
+ - `optima-grant-subscription` - 开通订阅
65
+ - `optima-account status` - 查用户订阅+权益
66
+ - `optima-query-db` / billing `GET /api/billing/balance` - 核对积分余额
package/AGENTS.md CHANGED
@@ -10,7 +10,7 @@ Prefer the installed CLI tools over reimplementing long shell workflows:
10
10
  - `optima-show-env <service> <stage|prod> [options]`
11
11
  - `optima-generate-test-token [options]`
12
12
  - `optima-grant-subscription <email> [options]`
13
- - `optima-grant-balance <email> --amount <usd> [options]`
13
+ - `optima-grant-credits <email|phone|userId> --credits <n> [options]`
14
14
  - `optima-product <create|update|add-channel|toggle-channel|show> [options]` — manage paid-plugin marketplace Products + Stripe channels (Wave 1.5 admin endpoints; stage default)
15
15
  - `optima-entitlement <grant|revoke|list> [options]` — admin-grant / revoke / list paid-plugin entitlements (refuses revoke of PAYMENT / PARTNER source)
16
16
  - `optima-plugin <show|set-paid|set-default> [options]` — flip a plugin's skills-side paid/free state (isPaid) + defaultForUser (the user-facing gate; pairs with optima-product for the billing side)
@@ -27,7 +27,7 @@ After `npm install -g @optima-chat/dev-skills`, this package installs skills und
27
27
  - `generate-test-token`
28
28
  - `read-code`
29
29
  - `grant-subscription`
30
- - `grant-balance`
30
+ - `grant-credits`
31
31
  - `restart-ecs`
32
32
  - `use-commerce-cli`
33
33
 
package/README.md CHANGED
@@ -106,11 +106,11 @@ Claude:
106
106
  | `optima-generate-test-token` | 生成测试 token | `optima-generate-test-token --business-name "测试店铺"` |
107
107
  | `optima-discount` | 优惠码管理 | `optima-discount create --code LAUNCH20 --percent 20 --env stage` |
108
108
  | `optima-grant-subscription` | 开通/切换订阅 | `optima-grant-subscription 18898654855 --plan pro-cn --env cn-prod` |
109
- | `optima-grant-balance` | 赠送 credits | `optima-grant-balance user@example.com --amount 5 --env prod` |
109
+ | `optima-grant-credits` | 赠送积分 | `optima-grant-credits user@example.com --credits 10000 --env prod`(`optima-grant-balance` 为废弃别名)|
110
110
  | `optima-entitlement` | 产品权益 grant/revoke/list | `optima-entitlement grant 18898654855 --product-key scout-gift --justification "..." --env cn-prod` |
111
111
  | `optima-account` | 账号 status/ban/unban | `optima-account ban user@example.com --reason "abuse" --env prod` |
112
112
 
113
- > **4 环境 + 标识符**:`grant-subscription` / `grant-balance` / `entitlement` / `account` 均支持 `stage` / `prod` / `cn-prod` / `cn-stage`。标识符 `<email\|phone\|userId>`——**cn-prod / cn-stage 用户多为手机号注册**,三种均可;AWS stage/prod 仅 email。`ban`/`unban` 及 `account status` 的禁用态读取需 admin-用户凭证(Infisical `/shared-secrets/credentials`;cn 另需 `INFISICAL_CN_EMAIL/PASSWORD`)。
113
+ > **4 环境 + 标识符**:`grant-subscription` / `grant-credits` / `entitlement` / `account` 均支持 `stage` / `prod` / `cn-prod` / `cn-stage`。标识符 `<email\|phone\|userId>`——**cn-prod / cn-stage 用户多为手机号注册**,三种均可;AWS stage/prod 仅 email。`ban`/`unban` 及 `account status` 的禁用态读取需 admin-用户凭证(Infisical `/shared-secrets/credentials`;cn 另需 `INFISICAL_CN_EMAIL/PASSWORD`)。
114
114
 
115
115
  **特点**:
116
116
  - ✅ 支持 CI、Stage、Prod 三个环境(query-db)
package/bin/cli.js CHANGED
@@ -34,7 +34,7 @@ switch (command) {
34
34
  log(' optima-show-env <service> [env] Show service env vars', 'cyan');
35
35
  log(' optima-verify-health <service> [--env cn|prod|all] Probe L1-L5 上线健康', 'cyan');
36
36
  log(' optima-generate-test-token [--env production] Generate test token', 'cyan');
37
- log(' optima-grant-balance <email> --amount <usd> [--env] Grant USD wallet balance', 'cyan');
37
+ log(' optima-grant-credits <email|phone|userId> --credits <n> [--env] Grant credits (bonus, 30d)', 'cyan');
38
38
  log(' optima-grant-subscription <email|phone|userId> --plan <p> [--env] Grant subscription', 'cyan');
39
39
  log(' optima-logs <service> [--env] [--since] [--grep] [-n] View logs (cn=SLS 直连/aws=CloudWatch)', 'cyan');
40
40
  log(' /restart-ecs <service> [env] Restart ECS service (skill)', 'cyan');
@@ -49,7 +49,7 @@ switch (command) {
49
49
  log('\nExamples:', 'yellow');
50
50
  log(' /logs commerce-backend 100 prod', 'cyan');
51
51
  log(' optima-query-db user-auth "SELECT COUNT(*) FROM users" prod', 'cyan');
52
- log(' optima-grant-balance user@example.com --amount 5 --env prod', 'cyan');
52
+ log(' optima-grant-credits user@example.com --credits 10000 --env prod', 'cyan');
53
53
 
54
54
  log('\nMore Info:', 'yellow');
55
55
  log(' optima-dev-skills --version Show version', 'cyan');
@@ -41,7 +41,7 @@ export function validateEnv(env: string): 'stage' | 'prod' {
41
41
  /**
42
42
  * Variant for commands that support all four envs (stage/prod/cn-prod/cn-stage)
43
43
  * because they reach billing / skills / user-auth over HTTPS only — e.g.
44
- * grant-balance, grant-subscription, entitlement, account, and optima-plugin
44
+ * grant-credits, grant-subscription, entitlement, account, and optima-plugin
45
45
  * (set-paid/set-default/show). Commands that resolve users via the AWS RDS SSH
46
46
  * tunnel (which does not exist for cn — Aliyun VPC-internal RDS) stay on
47
47
  * validateEnv so a cn typo fails fast instead of dying inside tunnel setup.
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ // 发放积分(bonus 积分桶,30 天有效期)。调 billing 服务态端点
4
+ // POST /api/billing/admin/grant-credits(P15 钱包退役后的统一账本)。
5
+ //
6
+ // 历史:本命令原名 grant-credits(按积分),余额时代曾改名 grant-balance + 改吃 USD;
7
+ // P15 钱包退役回归积分后,名字/单位一直没改回 —— 现在改回 grant-credits + --credits
8
+ // 原生按积分发;--amount <usd> 作为按 USD 的兼容入口($1=700 积分);
9
+ // optima-grant-balance 作为废弃别名保留。
10
+ import { basename } from 'path';
11
+ import { randomUUID } from 'crypto';
12
+ import { callBilling, validateEnvCnProd } from './billing-http';
13
+ import { resolveTargetUser } from './grant-subscription';
14
+
15
+ const CREDITS_PER_USD = 700;
16
+
17
+ interface Parsed {
18
+ identifier: string;
19
+ credits: number | null;
20
+ amountUsd: number | null;
21
+ description: string | null;
22
+ env: string;
23
+ }
24
+
25
+ function parseArgs(args: string[]): Parsed {
26
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
27
+ console.log(`Usage: optima-grant-credits <email|phone|userId> --credits <n> [options]
28
+
29
+ Grant credits to a user (bonus bucket, expires in 30 days).
30
+ Used for promotional grants, compensation, referral rewards, etc.
31
+
32
+ Target user: <email|phone|userId> (positional). phone/userId only on
33
+ cn-prod / cn-stage; AWS stage/prod resolve email only.
34
+
35
+ Options:
36
+ --credits <n> Credits to grant (integer >= 1). Primary unit.
37
+ --amount <usd> Alt: grant by USD ($1 = ${CREDITS_PER_USD} credits). Provide exactly one of --credits / --amount.
38
+ --description <text> Description for audit trail (optional)
39
+ --env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
40
+ -h, --help Show this help
41
+
42
+ Examples:
43
+ optima-grant-credits user@example.com --credits 10000 --env prod
44
+ optima-grant-credits 18898654855 --credits 5000 --env cn-prod # 手机号(cn 用户多为手机号注册)
45
+ optima-grant-credits user@example.com --amount 5 --env prod # 按 USD(= 3500 积分)`);
46
+ process.exit(0);
47
+ }
48
+
49
+ const identifier = args[0];
50
+ let credits: number | null = null;
51
+ let amountUsd: number | null = null;
52
+ let description: string | null = null;
53
+ let env = 'stage';
54
+
55
+ for (let i = 1; i < args.length; i++) {
56
+ if (args[i] === '--credits' && args[i + 1]) { credits = parseInt(args[++i], 10); }
57
+ else if (args[i] === '--amount' && args[i + 1]) { amountUsd = parseFloat(args[++i]); }
58
+ else if (args[i] === '--description' && args[i + 1]) { description = args[++i]; }
59
+ else if (args[i] === '--env' && args[i + 1]) { env = args[++i]; }
60
+ }
61
+
62
+ // Exactly one of --credits / --amount (mirrors billing endpoint's oneOf).
63
+ if ((credits !== null) === (amountUsd !== null)) {
64
+ console.error('提供且只能提供 --credits <n> 或 --amount <usd> 之一');
65
+ process.exit(1);
66
+ }
67
+ if (credits !== null && (!Number.isInteger(credits) || credits < 1)) {
68
+ console.error('--credits 必须是 >= 1 的整数');
69
+ process.exit(1);
70
+ }
71
+ if (amountUsd !== null && (!Number.isFinite(amountUsd) || amountUsd <= 0)) {
72
+ console.error('--amount 必须 > 0 (USD)');
73
+ process.exit(1);
74
+ }
75
+ validateEnvCnProd(env);
76
+
77
+ return { identifier, credits, amountUsd, description, env };
78
+ }
79
+
80
+ async function main() {
81
+ // Deprecation notice when invoked via the legacy alias `optima-grant-balance`.
82
+ if (basename(process.argv[1] || '').includes('grant-balance')) {
83
+ console.warn('⚠️ optima-grant-balance 已更名为 optima-grant-credits(P15 钱包退役后回归积分)。请改用 `optima-grant-credits --credits <n>`;本别名仍可用,后续弃用。\n');
84
+ }
85
+
86
+ const { identifier, credits, amountUsd, description, env } = parseArgs(process.argv.slice(2));
87
+
88
+ const creditsDisplay = credits ?? Math.round((amountUsd as number) * CREDITS_PER_USD);
89
+ console.log(`\n🎁 Granting ${creditsDisplay} credits${amountUsd !== null ? ` ($${amountUsd.toFixed(2)})` : ''} to ${identifier} [${env.toUpperCase()}]\n`);
90
+ if (description) console.log(` Reason: ${description}`);
91
+
92
+ // Shared resolver: classify→resolve→reverse-verify echo→phone-assert on cn
93
+ // (so phone/userId works for cn's phone-registered users, gateway#923);
94
+ // email-only via the RDS SSH tunnel on AWS.
95
+ const { userId } = await resolveTargetUser(env, identifier);
96
+
97
+ // amountCredits(原生积分)与 amountUsd(USD 折算)二选一,对应 billing 端点的 oneOf。
98
+ const amountField = credits !== null ? { amountCredits: credits } : { amountUsd };
99
+
100
+ // 幂等键 per-invocation 生成、callBilling 的 5xx retry 复用同 body —— 「已
101
+ // commit 但响应 5xx」场景重试不双发(billing spec R2-M3)。
102
+ const { body } = await callBilling<{ success: boolean; lotId: string; credits: number }>(
103
+ env, 'POST', '/api/billing/admin/grant-credits',
104
+ {
105
+ userId,
106
+ ...amountField,
107
+ description: description ?? undefined,
108
+ idempotencyKey: `dev-skills-grant:${randomUUID()}`,
109
+ },
110
+ );
111
+
112
+ console.log(`✓ Granted ${body.credits} credits (lot ${body.lotId})`);
113
+ console.log(`\n✅ Done! ${identifier} received ${body.credits} bonus credits (expires in 30 days)\n`);
114
+ }
115
+
116
+ main().catch(error => {
117
+ console.error('\n❌ Error:', error.message);
118
+ process.exit(1);
119
+ });
@@ -119,7 +119,7 @@ Options:
119
119
  /**
120
120
  * Resolve a CLI identifier (`<email|phone|userId>`) to a userId + verified
121
121
  * account identity, handling the AWS-vs-cn split. Shared by grant-subscription,
122
- * grant-balance, the optima-entitlement subcommands, and optima-account so all
122
+ * grant-credits, the optima-entitlement subcommands, and optima-account so all
123
123
  * four accept the same identifier forms (gateway#923: phone/userId on cn).
124
124
  *
125
125
  * - AWS (stage/prod): email-only via the RDS SSH tunnel; no internal HTTP
@@ -49,7 +49,7 @@ function validateEnv(env) {
49
49
  /**
50
50
  * Variant for commands that support all four envs (stage/prod/cn-prod/cn-stage)
51
51
  * because they reach billing / skills / user-auth over HTTPS only — e.g.
52
- * grant-balance, grant-subscription, entitlement, account, and optima-plugin
52
+ * grant-credits, grant-subscription, entitlement, account, and optima-plugin
53
53
  * (set-paid/set-default/show). Commands that resolve users via the AWS RDS SSH
54
54
  * tunnel (which does not exist for cn — Aliyun VPC-internal RDS) stay on
55
55
  * validateEnv so a cn typo fails fast instead of dying inside tunnel setup.
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ // 发放积分(bonus 积分桶,30 天有效期)。调 billing 服务态端点
5
+ // POST /api/billing/admin/grant-credits(P15 钱包退役后的统一账本)。
6
+ //
7
+ // 历史:本命令原名 grant-credits(按积分),余额时代曾改名 grant-balance + 改吃 USD;
8
+ // P15 钱包退役回归积分后,名字/单位一直没改回 —— 现在改回 grant-credits + --credits
9
+ // 原生按积分发;--amount <usd> 作为按 USD 的兼容入口($1=700 积分);
10
+ // optima-grant-balance 作为废弃别名保留。
11
+ const path_1 = require("path");
12
+ const crypto_1 = require("crypto");
13
+ const billing_http_1 = require("./billing-http");
14
+ const grant_subscription_1 = require("./grant-subscription");
15
+ const CREDITS_PER_USD = 700;
16
+ function parseArgs(args) {
17
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
18
+ console.log(`Usage: optima-grant-credits <email|phone|userId> --credits <n> [options]
19
+
20
+ Grant credits to a user (bonus bucket, expires in 30 days).
21
+ Used for promotional grants, compensation, referral rewards, etc.
22
+
23
+ Target user: <email|phone|userId> (positional). phone/userId only on
24
+ cn-prod / cn-stage; AWS stage/prod resolve email only.
25
+
26
+ Options:
27
+ --credits <n> Credits to grant (integer >= 1). Primary unit.
28
+ --amount <usd> Alt: grant by USD ($1 = ${CREDITS_PER_USD} credits). Provide exactly one of --credits / --amount.
29
+ --description <text> Description for audit trail (optional)
30
+ --env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
31
+ -h, --help Show this help
32
+
33
+ Examples:
34
+ optima-grant-credits user@example.com --credits 10000 --env prod
35
+ optima-grant-credits 18898654855 --credits 5000 --env cn-prod # 手机号(cn 用户多为手机号注册)
36
+ optima-grant-credits user@example.com --amount 5 --env prod # 按 USD(= 3500 积分)`);
37
+ process.exit(0);
38
+ }
39
+ const identifier = args[0];
40
+ let credits = null;
41
+ let amountUsd = null;
42
+ let description = null;
43
+ let env = 'stage';
44
+ for (let i = 1; i < args.length; i++) {
45
+ if (args[i] === '--credits' && args[i + 1]) {
46
+ credits = parseInt(args[++i], 10);
47
+ }
48
+ else if (args[i] === '--amount' && args[i + 1]) {
49
+ amountUsd = parseFloat(args[++i]);
50
+ }
51
+ else if (args[i] === '--description' && args[i + 1]) {
52
+ description = args[++i];
53
+ }
54
+ else if (args[i] === '--env' && args[i + 1]) {
55
+ env = args[++i];
56
+ }
57
+ }
58
+ // Exactly one of --credits / --amount (mirrors billing endpoint's oneOf).
59
+ if ((credits !== null) === (amountUsd !== null)) {
60
+ console.error('提供且只能提供 --credits <n> 或 --amount <usd> 之一');
61
+ process.exit(1);
62
+ }
63
+ if (credits !== null && (!Number.isInteger(credits) || credits < 1)) {
64
+ console.error('--credits 必须是 >= 1 的整数');
65
+ process.exit(1);
66
+ }
67
+ if (amountUsd !== null && (!Number.isFinite(amountUsd) || amountUsd <= 0)) {
68
+ console.error('--amount 必须 > 0 (USD)');
69
+ process.exit(1);
70
+ }
71
+ (0, billing_http_1.validateEnvCnProd)(env);
72
+ return { identifier, credits, amountUsd, description, env };
73
+ }
74
+ async function main() {
75
+ // Deprecation notice when invoked via the legacy alias `optima-grant-balance`.
76
+ if ((0, path_1.basename)(process.argv[1] || '').includes('grant-balance')) {
77
+ console.warn('⚠️ optima-grant-balance 已更名为 optima-grant-credits(P15 钱包退役后回归积分)。请改用 `optima-grant-credits --credits <n>`;本别名仍可用,后续弃用。\n');
78
+ }
79
+ const { identifier, credits, amountUsd, description, env } = parseArgs(process.argv.slice(2));
80
+ const creditsDisplay = credits ?? Math.round(amountUsd * CREDITS_PER_USD);
81
+ console.log(`\n🎁 Granting ${creditsDisplay} credits${amountUsd !== null ? ` ($${amountUsd.toFixed(2)})` : ''} to ${identifier} [${env.toUpperCase()}]\n`);
82
+ if (description)
83
+ console.log(` Reason: ${description}`);
84
+ // Shared resolver: classify→resolve→reverse-verify echo→phone-assert on cn
85
+ // (so phone/userId works for cn's phone-registered users, gateway#923);
86
+ // email-only via the RDS SSH tunnel on AWS.
87
+ const { userId } = await (0, grant_subscription_1.resolveTargetUser)(env, identifier);
88
+ // amountCredits(原生积分)与 amountUsd(USD 折算)二选一,对应 billing 端点的 oneOf。
89
+ const amountField = credits !== null ? { amountCredits: credits } : { amountUsd };
90
+ // 幂等键 per-invocation 生成、callBilling 的 5xx retry 复用同 body —— 「已
91
+ // commit 但响应 5xx」场景重试不双发(billing spec R2-M3)。
92
+ const { body } = await (0, billing_http_1.callBilling)(env, 'POST', '/api/billing/admin/grant-credits', {
93
+ userId,
94
+ ...amountField,
95
+ description: description ?? undefined,
96
+ idempotencyKey: `dev-skills-grant:${(0, crypto_1.randomUUID)()}`,
97
+ });
98
+ console.log(`✓ Granted ${body.credits} credits (lot ${body.lotId})`);
99
+ console.log(`\n✅ Done! ${identifier} received ${body.credits} bonus credits (expires in 30 days)\n`);
100
+ }
101
+ main().catch(error => {
102
+ console.error('\n❌ Error:', error.message);
103
+ process.exit(1);
104
+ });
@@ -117,7 +117,7 @@ Options:
117
117
  /**
118
118
  * Resolve a CLI identifier (`<email|phone|userId>`) to a userId + verified
119
119
  * account identity, handling the AWS-vs-cn split. Shared by grant-subscription,
120
- * grant-balance, the optima-entitlement subcommands, and optima-account so all
120
+ * grant-credits, the optima-entitlement subcommands, and optima-account so all
121
121
  * four accept the same identifier forms (gateway#923: phone/userId on cn).
122
122
  *
123
123
  * - AWS (stage/prod): email-only via the RDS SSH tunnel; no internal HTTP
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/dev-skills",
3
- "version": "0.9.3",
3
+ "version": "0.11.0",
4
4
  "description": "Claude Code Skills for Optima development team - cross-environment collaboration tools",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  "optima-account": "dist/bin/helpers/account.js",
9
9
  "optima-entitlement": "dist/bin/helpers/entitlement.js",
10
10
  "optima-generate-test-token": "dist/bin/helpers/generate-test-token.js",
11
- "optima-grant-balance": "dist/bin/helpers/grant-balance.js",
11
+ "optima-grant-credits": "dist/bin/helpers/grant-credits.js",
12
+ "optima-grant-balance": "dist/bin/helpers/grant-credits.js",
12
13
  "optima-grant-subscription": "dist/bin/helpers/grant-subscription.js",
13
14
  "optima-logs": "dist/bin/helpers/logs.js",
14
15
  "optima-plugin": "dist/bin/helpers/plugin.js",
@@ -1,106 +0,0 @@
1
- ---
2
- name: "grant-balance"
3
- description: "当用户请求赠送余额、充值 USD 余额、grant balance、加余额、奖励、补偿、推荐奖励、运营发放时,使用此技能。支持 Stage、Prod、cn-prod、cn-stage 四个环境。"
4
- allowed-tools: ["Bash"]
5
- ---
6
-
7
- # 赠送 USD 余额(Grant Balance)
8
-
9
- 当你需要为用户赠送 wallet USD 余额时,使用这个场景。金额按 $1=700 积分换算,经 billing API 入 **bonus 积分桶(30 天有效期)**(P15 钱包退役后语义),billing 服务在扣费时会优先消费 granted balance。
10
-
11
- ## 执行方式:使用 CLI 工具
12
-
13
- ```bash
14
- optima-grant-balance <email|phone|userId> --amount <usd> [options]
15
- ```
16
-
17
- **为什么使用 CLI 工具**:
18
- - 自动通过 email/手机号/userId 查找 userId(cn 用户多为手机号;执行前打印 🎯 目标账号反查)
19
- - 自动处理 SSH 隧道和数据库连接
20
- - 不会影响现有订阅和已有余额(发放 bonus 积分(30 天有效期,重复执行会叠加发放))
21
- - 自动留审计痕迹(credit_lot 行,幂等键前缀 `dev-skills-grant:`)
22
-
23
- ## 适用情况
24
-
25
- - 奖励额外余额(推广、活动)
26
- - 客户补偿(服务中断等)
27
- - 推荐奖励
28
- - 内部测试账户充值
29
-
30
- ## 快速操作
31
-
32
- ```bash
33
- # 赠送 $5(Stage 环境,默认)
34
- optima-grant-balance user@example.com --amount 5
35
-
36
- # 赠送 $10 到 Prod
37
- optima-grant-balance user@example.com --amount 10 --env prod
38
-
39
- # 带描述(Reason 仅在 console 输出,不存 DB)
40
- optima-grant-balance user@example.com --amount 20 --description "服务中断补偿" --env prod
41
- ```
42
-
43
- > **单位是美元(USD)**。`--amount 5` 即赠送 $5.00 到 granted balance。
44
- > 数据库底层用 micro-USD 精度(1 USD = 700 积分(P15 统一账本口径)
45
-
46
- ### 参数说明
47
-
48
- | 参数 | 说明 | 默认值 |
49
- |------|------|--------|
50
- | `<email|phone|userId>` | 用户标识(必填;cn 支持手机号/userId) | - |
51
- | `--amount <usd>` | USD 金额(必填,> 0) | - |
52
- | `--description <text>` | 描述/原因(仅 console 输出) | - |
53
- | `--env <env>` | 环境:stage, prod, cn-prod, cn-stage | stage |
54
-
55
- > **cn-prod(国内环境)**:全程 HTTPS(auth.yzsgo.com / billing-api.yzsgo.com),email 查找走 user-auth internal lookup API(无 SSH 隧道)。金额输入仍是 USD($1 = 700 积分,与 CN ¥1 = 100 积分同一账本单位)。例:`optima-grant-balance user@example.com --amount 1 --env cn-prod`
56
- >
57
- > **cn-stage(阿里云预发)**:同 cn-prod 走 HTTPS(auth.stage.optima.chat / billing-api.stage.optima.chat)。M2M 凭证读 cn Infisical **staging** /shared-secrets/oauth-clients,运行时需 `INFISICAL_CN_EMAIL`/`INFISICAL_CN_PASSWORD`(admin user)。例:`optima-grant-balance user@example.com --amount 1 --env cn-stage`
58
-
59
- ## 与 grant-subscription 的区别
60
-
61
- | | grant-balance | grant-subscription |
62
- |---|---|---|
63
- | 作用 | 追加 USD granted balance | 开通/切换订阅计划 |
64
- | 现有余额 | 不影响(纯累加) | 重置 granted balance |
65
- | 现有订阅 | 不影响 | 取消旧的,创建新的 |
66
- | Token quota | 不影响 | 按计划更新 |
67
- | 适用场景 | 奖励、补偿、推广 | 开通会员、升级计划 |
68
-
69
- ## 常见使用场景
70
-
71
- ### 场景 1:客户补偿
72
-
73
- **用户请求**:"服务出了问题,给 xxx@gmail.com 补偿 $5"
74
-
75
- ```bash
76
- optima-grant-balance xxx@gmail.com --amount 5 --description "Service outage compensation" --env prod
77
- ```
78
-
79
- ### 场景 2:推荐奖励
80
-
81
- **用户请求**:"xxx 推荐了新用户,奖励 $3"
82
-
83
- ```bash
84
- optima-grant-balance xxx@gmail.com --amount 3 --description "Referral reward" --env prod
85
- ```
86
-
87
- ### 场景 3:运营发放
88
-
89
- **用户请求**:"给 xxx@gmail.com 充 $20 测试余额"
90
-
91
- ```bash
92
- optima-grant-balance xxx@gmail.com --amount 20 --env stage
93
- ```
94
-
95
- ## 安全提醒
96
-
97
- 1. **Stage 优先**:默认操作 Stage 环境
98
- 2. **Prod 谨慎**:操作 Prod 前确认邮箱和金额
99
- 3. **纯追加**:不会影响现有余额和订阅(累加到 granted balance)
100
- 4. **Audit trail**:每次赠送对应一个 credit_lot(幂等键前缀 `dev-skills-grant:`,type=bonus)
101
-
102
- ## 相关命令
103
-
104
- - `optima-grant-balance` - 赠送 USD 余额(主要方式)
105
- - `optima-grant-subscription` - 开通订阅计划
106
- - `optima-query-db` - 查询数据库验证结果(`GET /api/billing/balance(credits.byType.bonus)或 query-db credit_lot