@optima-chat/dev-skills 0.7.29 → 0.7.32

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,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { getInfisicalConfig, getInfisicalToken, resolveUserId, connectBillingDB, escapeSQL } from './db-utils';
4
+
5
+ function parseArgs(args: string[]): { email: string; amountUsd: number; description: string | null; env: string } {
6
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
7
+ console.log(`Usage: optima-grant-balance <email> --amount <usd> [options]
8
+
9
+ Add USD balance to a user's wallet (granted_balance_micros).
10
+ Used for promotional grants, compensation, referral rewards, etc.
11
+
12
+ Options:
13
+ --amount <usd> USD amount to grant (required, e.g. 5 for $5.00)
14
+ --description <text> Description for audit trail (optional)
15
+ --env <env> Environment: stage, prod (default: stage)
16
+ -h, --help Show this help
17
+
18
+ Examples:
19
+ optima-grant-balance user@example.com --amount 5 --env prod
20
+ optima-grant-balance user@example.com --amount 10 --description "Service outage compensation"`);
21
+ process.exit(0);
22
+ }
23
+
24
+ const email = args[0];
25
+ let amountUsd = 0;
26
+ let description: string | null = null;
27
+ let env = 'stage';
28
+
29
+ for (let i = 1; i < args.length; i++) {
30
+ if (args[i] === '--amount' && args[i + 1]) { amountUsd = parseFloat(args[++i]); }
31
+ else if (args[i] === '--description' && args[i + 1]) { description = args[++i]; }
32
+ else if (args[i] === '--env' && args[i + 1]) { env = args[++i]; }
33
+ }
34
+
35
+ if (!Number.isFinite(amountUsd) || amountUsd <= 0) {
36
+ console.error('--amount is required and must be > 0 (USD)');
37
+ process.exit(1);
38
+ }
39
+ if (!['stage', 'prod'].includes(env)) {
40
+ console.error('Env must be stage or prod (billing DB not available in CI)');
41
+ process.exit(1);
42
+ }
43
+
44
+ return { email, amountUsd, description, env };
45
+ }
46
+
47
+ async function main() {
48
+ const { email, amountUsd, description, env } = parseArgs(process.argv.slice(2));
49
+ const infisicalConfig = getInfisicalConfig();
50
+ const token = getInfisicalToken(infisicalConfig);
51
+
52
+ // 1 USD = 1,000,000 micros. Round to integer micros.
53
+ const amountMicros = Math.round(amountUsd * 1_000_000);
54
+ console.log(`\n🎁 Granting $${amountUsd.toFixed(2)} to ${email} [${env.toUpperCase()}]\n`);
55
+ if (description) console.log(` Reason: ${description}`);
56
+
57
+ const userId = await resolveUserId(email, env, infisicalConfig, token);
58
+ const billing = await connectBillingDB(env, infisicalConfig, token);
59
+ const bq = billing.query;
60
+
61
+ const now = new Date().toISOString();
62
+ const safeUserId = escapeSQL(userId);
63
+
64
+ console.log(`Granting to wallet...`);
65
+ const txSQL = `
66
+ BEGIN;
67
+
68
+ -- Ensure wallet exists
69
+ INSERT INTO usd_wallets (id, user_id, balance_micros, reserved_micros, granted_balance_micros, created_at, updated_at)
70
+ VALUES (gen_random_uuid(), '${safeUserId}', 0, 0, 0, '${now}', '${now}')
71
+ ON CONFLICT (user_id) DO NOTHING;
72
+
73
+ -- Add to granted balance
74
+ UPDATE usd_wallets SET granted_balance_micros = granted_balance_micros + ${amountMicros}, updated_at = '${now}'
75
+ WHERE user_id = '${safeUserId}';
76
+
77
+ -- Record topup for audit trail (source='admin_grant' aligns with billing service convention)
78
+ INSERT INTO usd_wallet_topups (id, wallet_id, amount_micros, service_fee_micros, net_credit_micros, status, source, service_namespace, created_at, completed_at)
79
+ SELECT gen_random_uuid(), w.id, ${amountMicros}, 0, ${amountMicros}, 'completed', 'admin_grant', 'platform', '${now}', '${now}'
80
+ FROM usd_wallets w WHERE w.user_id = '${safeUserId}';
81
+
82
+ COMMIT;
83
+ `.trim();
84
+ bq(txSQL);
85
+ console.log(`✓ Granted $${amountUsd.toFixed(2)} to wallet`);
86
+
87
+ const balanceMicros = bq(`SELECT granted_balance_micros FROM usd_wallets WHERE user_id='${safeUserId}'`);
88
+ const balanceUsd = (parseInt(balanceMicros, 10) / 1000000).toFixed(2);
89
+ console.log(`\n✅ Done! ${email} now has $${balanceUsd} granted balance\n`);
90
+ }
91
+
92
+ main().catch(error => {
93
+ console.error('\n❌ Error:', error.message);
94
+ process.exit(1);
95
+ });
@@ -53,9 +53,10 @@ async function main() {
53
53
 
54
54
  const [planName, monthlyCreditsStr, sessionTokenLimitStr, weeklyTokenLimitStr] = planRow.split('|');
55
55
  const monthlyCredits = parseInt(monthlyCreditsStr, 10);
56
+ const grantMicros = monthlyCredits * 10000; // 1 credit = $0.01 = 10,000 micros
56
57
  const sessionTokenLimit = parseInt(sessionTokenLimitStr, 10);
57
58
  const weeklyTokenLimit = parseInt(weeklyTokenLimitStr, 10);
58
- console.log(`✓ Plan: ${planName} (credits: ${monthlyCredits}, session: ${sessionTokenLimit.toLocaleString()}, weekly: ${weeklyTokenLimit.toLocaleString()})`);
59
+ console.log(`✓ Plan: ${planName} (grant: $${(grantMicros / 1000000).toFixed(2)}, session: ${sessionTokenLimit.toLocaleString()}, weekly: ${weeklyTokenLimit.toLocaleString()})`);
59
60
 
60
61
  // Execute all mutations in a single transaction
61
62
  const now = new Date().toISOString();
@@ -77,18 +78,23 @@ BEGIN;
77
78
  UPDATE subscriptions SET status='canceled', canceled_at='${now}'
78
79
  WHERE user_id='${safeUserId}' AND status IN ('active','trialing');
79
80
 
80
- -- Zero out old subscription credits
81
- UPDATE credit_ledger SET remaining=0
82
- WHERE user_id='${safeUserId}' AND type IN ('monthly_grant','subscription') AND remaining > 0;
83
-
84
81
  -- Create new subscription
85
82
  INSERT INTO subscriptions (id, user_id, plan_id, status, billing_interval, current_period_start, current_period_end, created_at, updated_at)
86
83
  VALUES (concat('sub_gift_', substr(md5(random()::text), 1, 16)), '${safeUserId}', '${safePlan}', 'active', 'monthly', '${now}', '${periodEndISO}', '${now}', '${now}');
87
84
 
88
- -- Grant monthly credits
89
- INSERT INTO credit_ledger (id, user_id, type, description, initial_amount, remaining, expires_at, created_at)
90
- SELECT concat('crd_gift_', substr(md5(random()::text), 1, 16)), '${safeUserId}', 'subscription', '${safePlanName} plan gift (${months} month)', ${monthlyCredits}, ${monthlyCredits}, '${periodEndISO}', '${now}'
91
- WHERE ${monthlyCredits} > 0;
85
+ -- Ensure wallet exists (upsert)
86
+ INSERT INTO usd_wallets (id, user_id, balance_micros, reserved_micros, granted_balance_micros, created_at, updated_at)
87
+ VALUES (gen_random_uuid(), '${safeUserId}', 0, 0, 0, '${now}', '${now}')
88
+ ON CONFLICT (user_id) DO NOTHING;
89
+
90
+ -- Reset granted balance and set new grant
91
+ UPDATE usd_wallets SET granted_balance_micros = ${grantMicros}, updated_at = '${now}'
92
+ WHERE user_id = '${safeUserId}';
93
+
94
+ -- Record topup for audit trail
95
+ INSERT INTO usd_wallet_topups (id, wallet_id, amount_micros, service_fee_micros, net_credit_micros, status, source, service_namespace, created_at, completed_at)
96
+ SELECT gen_random_uuid(), w.id, ${grantMicros}, 0, ${grantMicros}, 'completed', 'subscription_grant', 'platform', '${now}', '${now}'
97
+ FROM usd_wallets w WHERE w.user_id = '${safeUserId}';
92
98
 
93
99
  -- Update existing active session quota, or insert new one if none exists
94
100
  UPDATE token_quotas SET plan_id='${safePlan}', monthly_limit=${sessionTokenLimit}, updated_at='${now}'
@@ -112,10 +118,9 @@ COMMIT;
112
118
  bq(txSQL);
113
119
 
114
120
  console.log('✓ Old subscriptions canceled');
115
- console.log('✓ Old credits cleared');
116
121
  console.log(`✓ ${planName} subscription created (expires: ${periodEnd.toLocaleDateString()})`);
117
- if (monthlyCredits > 0) {
118
- console.log(`✓ ${monthlyCredits} credits granted`);
122
+ if (grantMicros > 0) {
123
+ console.log(`✓ Wallet granted $${(grantMicros / 1000000).toFixed(2)} (${monthlyCredits} credits equivalent)`);
119
124
  }
120
125
  console.log(`✓ Token quotas updated (session: ${sessionTokenLimit.toLocaleString()}, weekly: ${weeklyTokenLimit.toLocaleString()})`);
121
126
 
@@ -43,6 +43,11 @@ const SERVICE_DB_MAP = {
43
43
  stage: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_shell' },
44
44
  prod: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_ai_shell' }
45
45
  },
46
+ 'gateway-core': {
47
+ ci: null, // CI 环境 gateway-core 不带 DB(本地 JSONL-only 模式)
48
+ stage: { databaseUrlPath: '/services/gateway-core', databaseUrlKey: 'DATABASE_URL' },
49
+ prod: { databaseUrlPath: '/services/gateway-core', databaseUrlKey: 'DATABASE_URL' }
50
+ },
46
51
  'optima-logistics': {
47
52
  ci: null,
48
53
  stage: { userKey: 'LOGISTICS_DB_USER', passwordKey: 'LOGISTICS_DB_PASSWORD', database: 'optima_stage_logistics' },
@@ -229,7 +234,7 @@ async function main() {
229
234
  if (args.length < 2) {
230
235
  console.error('Usage: query-db.ts <service> <sql> [environment]');
231
236
  console.error('');
232
- console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway, optima-logistics, billing, ads-backend, amazon-backend, browser-backend, shopify-backend, optima-generation, optima-sentinel');
237
+ console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway, gateway-core, optima-logistics, billing, ads-backend, amazon-backend, browser-backend, shopify-backend, optima-generation, optima-sentinel');
233
238
  console.error('Environments: ci (default), stage, prod');
234
239
  console.error('');
235
240
  console.error('Example: query-db.ts user-auth "SELECT COUNT(*) FROM users" prod');
File without changes
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const db_utils_1 = require("./db-utils");
5
+ function parseArgs(args) {
6
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
7
+ console.log(`Usage: optima-grant-balance <email> --amount <usd> [options]
8
+
9
+ Add USD balance to a user's wallet (granted_balance_micros).
10
+ Used for promotional grants, compensation, referral rewards, etc.
11
+
12
+ Options:
13
+ --amount <usd> USD amount to grant (required, e.g. 5 for $5.00)
14
+ --description <text> Description for audit trail (optional)
15
+ --env <env> Environment: stage, prod (default: stage)
16
+ -h, --help Show this help
17
+
18
+ Examples:
19
+ optima-grant-balance user@example.com --amount 5 --env prod
20
+ optima-grant-balance user@example.com --amount 10 --description "Service outage compensation"`);
21
+ process.exit(0);
22
+ }
23
+ const email = args[0];
24
+ let amountUsd = 0;
25
+ let description = null;
26
+ let env = 'stage';
27
+ for (let i = 1; i < args.length; i++) {
28
+ if (args[i] === '--amount' && args[i + 1]) {
29
+ amountUsd = parseFloat(args[++i]);
30
+ }
31
+ else if (args[i] === '--description' && args[i + 1]) {
32
+ description = args[++i];
33
+ }
34
+ else if (args[i] === '--env' && args[i + 1]) {
35
+ env = args[++i];
36
+ }
37
+ }
38
+ if (!Number.isFinite(amountUsd) || amountUsd <= 0) {
39
+ console.error('--amount is required and must be > 0 (USD)');
40
+ process.exit(1);
41
+ }
42
+ if (!['stage', 'prod'].includes(env)) {
43
+ console.error('Env must be stage or prod (billing DB not available in CI)');
44
+ process.exit(1);
45
+ }
46
+ return { email, amountUsd, description, env };
47
+ }
48
+ async function main() {
49
+ const { email, amountUsd, description, env } = parseArgs(process.argv.slice(2));
50
+ const infisicalConfig = (0, db_utils_1.getInfisicalConfig)();
51
+ const token = (0, db_utils_1.getInfisicalToken)(infisicalConfig);
52
+ // 1 USD = 1,000,000 micros. Round to integer micros.
53
+ const amountMicros = Math.round(amountUsd * 1000000);
54
+ console.log(`\n🎁 Granting $${amountUsd.toFixed(2)} to ${email} [${env.toUpperCase()}]\n`);
55
+ if (description)
56
+ console.log(` Reason: ${description}`);
57
+ const userId = await (0, db_utils_1.resolveUserId)(email, env, infisicalConfig, token);
58
+ const billing = await (0, db_utils_1.connectBillingDB)(env, infisicalConfig, token);
59
+ const bq = billing.query;
60
+ const now = new Date().toISOString();
61
+ const safeUserId = (0, db_utils_1.escapeSQL)(userId);
62
+ console.log(`Granting to wallet...`);
63
+ const txSQL = `
64
+ BEGIN;
65
+
66
+ -- Ensure wallet exists
67
+ INSERT INTO usd_wallets (id, user_id, balance_micros, reserved_micros, granted_balance_micros, created_at, updated_at)
68
+ VALUES (gen_random_uuid(), '${safeUserId}', 0, 0, 0, '${now}', '${now}')
69
+ ON CONFLICT (user_id) DO NOTHING;
70
+
71
+ -- Add to granted balance
72
+ UPDATE usd_wallets SET granted_balance_micros = granted_balance_micros + ${amountMicros}, updated_at = '${now}'
73
+ WHERE user_id = '${safeUserId}';
74
+
75
+ -- Record topup for audit trail (source='admin_grant' aligns with billing service convention)
76
+ INSERT INTO usd_wallet_topups (id, wallet_id, amount_micros, service_fee_micros, net_credit_micros, status, source, service_namespace, created_at, completed_at)
77
+ SELECT gen_random_uuid(), w.id, ${amountMicros}, 0, ${amountMicros}, 'completed', 'admin_grant', 'platform', '${now}', '${now}'
78
+ FROM usd_wallets w WHERE w.user_id = '${safeUserId}';
79
+
80
+ COMMIT;
81
+ `.trim();
82
+ bq(txSQL);
83
+ console.log(`✓ Granted $${amountUsd.toFixed(2)} to wallet`);
84
+ const balanceMicros = bq(`SELECT granted_balance_micros FROM usd_wallets WHERE user_id='${safeUserId}'`);
85
+ const balanceUsd = (parseInt(balanceMicros, 10) / 1000000).toFixed(2);
86
+ console.log(`\n✅ Done! ${email} now has $${balanceUsd} granted balance\n`);
87
+ }
88
+ main().catch(error => {
89
+ console.error('\n❌ Error:', error.message);
90
+ process.exit(1);
91
+ });
@@ -59,9 +59,10 @@ async function main() {
59
59
  }
60
60
  const [planName, monthlyCreditsStr, sessionTokenLimitStr, weeklyTokenLimitStr] = planRow.split('|');
61
61
  const monthlyCredits = parseInt(monthlyCreditsStr, 10);
62
+ const grantMicros = monthlyCredits * 10000; // 1 credit = $0.01 = 10,000 micros
62
63
  const sessionTokenLimit = parseInt(sessionTokenLimitStr, 10);
63
64
  const weeklyTokenLimit = parseInt(weeklyTokenLimitStr, 10);
64
- console.log(`✓ Plan: ${planName} (credits: ${monthlyCredits}, session: ${sessionTokenLimit.toLocaleString()}, weekly: ${weeklyTokenLimit.toLocaleString()})`);
65
+ console.log(`✓ Plan: ${planName} (grant: $${(grantMicros / 1000000).toFixed(2)}, session: ${sessionTokenLimit.toLocaleString()}, weekly: ${weeklyTokenLimit.toLocaleString()})`);
65
66
  // Execute all mutations in a single transaction
66
67
  const now = new Date().toISOString();
67
68
  const periodEnd = new Date();
@@ -80,18 +81,23 @@ BEGIN;
80
81
  UPDATE subscriptions SET status='canceled', canceled_at='${now}'
81
82
  WHERE user_id='${safeUserId}' AND status IN ('active','trialing');
82
83
 
83
- -- Zero out old subscription credits
84
- UPDATE credit_ledger SET remaining=0
85
- WHERE user_id='${safeUserId}' AND type IN ('monthly_grant','subscription') AND remaining > 0;
86
-
87
84
  -- Create new subscription
88
85
  INSERT INTO subscriptions (id, user_id, plan_id, status, billing_interval, current_period_start, current_period_end, created_at, updated_at)
89
86
  VALUES (concat('sub_gift_', substr(md5(random()::text), 1, 16)), '${safeUserId}', '${safePlan}', 'active', 'monthly', '${now}', '${periodEndISO}', '${now}', '${now}');
90
87
 
91
- -- Grant monthly credits
92
- INSERT INTO credit_ledger (id, user_id, type, description, initial_amount, remaining, expires_at, created_at)
93
- SELECT concat('crd_gift_', substr(md5(random()::text), 1, 16)), '${safeUserId}', 'subscription', '${safePlanName} plan gift (${months} month)', ${monthlyCredits}, ${monthlyCredits}, '${periodEndISO}', '${now}'
94
- WHERE ${monthlyCredits} > 0;
88
+ -- Ensure wallet exists (upsert)
89
+ INSERT INTO usd_wallets (id, user_id, balance_micros, reserved_micros, granted_balance_micros, created_at, updated_at)
90
+ VALUES (gen_random_uuid(), '${safeUserId}', 0, 0, 0, '${now}', '${now}')
91
+ ON CONFLICT (user_id) DO NOTHING;
92
+
93
+ -- Reset granted balance and set new grant
94
+ UPDATE usd_wallets SET granted_balance_micros = ${grantMicros}, updated_at = '${now}'
95
+ WHERE user_id = '${safeUserId}';
96
+
97
+ -- Record topup for audit trail
98
+ INSERT INTO usd_wallet_topups (id, wallet_id, amount_micros, service_fee_micros, net_credit_micros, status, source, service_namespace, created_at, completed_at)
99
+ SELECT gen_random_uuid(), w.id, ${grantMicros}, 0, ${grantMicros}, 'completed', 'subscription_grant', 'platform', '${now}', '${now}'
100
+ FROM usd_wallets w WHERE w.user_id = '${safeUserId}';
95
101
 
96
102
  -- Update existing active session quota, or insert new one if none exists
97
103
  UPDATE token_quotas SET plan_id='${safePlan}', monthly_limit=${sessionTokenLimit}, updated_at='${now}'
@@ -113,10 +119,9 @@ COMMIT;
113
119
  `.trim();
114
120
  bq(txSQL);
115
121
  console.log('✓ Old subscriptions canceled');
116
- console.log('✓ Old credits cleared');
117
122
  console.log(`✓ ${planName} subscription created (expires: ${periodEnd.toLocaleDateString()})`);
118
- if (monthlyCredits > 0) {
119
- console.log(`✓ ${monthlyCredits} credits granted`);
123
+ if (grantMicros > 0) {
124
+ console.log(`✓ Wallet granted $${(grantMicros / 1000000).toFixed(2)} (${monthlyCredits} credits equivalent)`);
120
125
  }
121
126
  console.log(`✓ Token quotas updated (session: ${sessionTokenLimit.toLocaleString()}, weekly: ${weeklyTokenLimit.toLocaleString()})`);
122
127
  console.log(`\n✅ Done! ${email} now has ${planName} plan until ${periodEnd.toLocaleDateString()}\n`);
@@ -62,6 +62,11 @@ const SERVICE_DB_MAP = {
62
62
  stage: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_shell' },
63
63
  prod: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_ai_shell' }
64
64
  },
65
+ 'gateway-core': {
66
+ ci: null, // CI 环境 gateway-core 不带 DB(本地 JSONL-only 模式)
67
+ stage: { databaseUrlPath: '/services/gateway-core', databaseUrlKey: 'DATABASE_URL' },
68
+ prod: { databaseUrlPath: '/services/gateway-core', databaseUrlKey: 'DATABASE_URL' }
69
+ },
65
70
  'optima-logistics': {
66
71
  ci: null,
67
72
  stage: { userKey: 'LOGISTICS_DB_USER', passwordKey: 'LOGISTICS_DB_PASSWORD', database: 'optima_stage_logistics' },
@@ -219,7 +224,7 @@ async function main() {
219
224
  if (args.length < 2) {
220
225
  console.error('Usage: query-db.ts <service> <sql> [environment]');
221
226
  console.error('');
222
- console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway, optima-logistics, billing, ads-backend, amazon-backend, browser-backend, shopify-backend, optima-generation, optima-sentinel');
227
+ console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway, gateway-core, optima-logistics, billing, ads-backend, amazon-backend, browser-backend, shopify-backend, optima-generation, optima-sentinel');
223
228
  console.error('Environments: ci (default), stage, prod');
224
229
  console.error('');
225
230
  console.error('Example: query-db.ts user-auth "SELECT COUNT(*) FROM users" prod');
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/dev-skills",
3
- "version": "0.7.29",
3
+ "version": "0.7.32",
4
4
  "description": "Claude Code Skills for Optima development team - cross-environment collaboration tools",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -9,7 +9,7 @@
9
9
  "optima-generate-test-token": "dist/bin/helpers/generate-test-token.js",
10
10
  "optima-show-env": "dist/bin/helpers/show-env.js",
11
11
  "optima-grant-subscription": "dist/bin/helpers/grant-subscription.js",
12
- "optima-grant-credits": "dist/bin/helpers/grant-credits.js"
12
+ "optima-grant-balance": "dist/bin/helpers/grant-balance.js"
13
13
  },
14
14
  "scripts": {
15
15
  "postinstall": "node scripts/install.js",
@@ -1,51 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "WebSearch",
5
- "WebFetch(domain:code.claude.com)",
6
- "WebFetch(domain:platform.claude.com)",
7
- "WebFetch(domain:github.com)",
8
- "Bash(gh repo view:*)",
9
- "Bash(gh repo clone:*)",
10
- "Bash(gh repo list:*)",
11
- "Read(//private/tmp/optima-docs/**)",
12
- "Read(//tmp/optima-docs/**)",
13
- "Bash(git init:*)",
14
- "Bash(gh repo create:*)",
15
- "Read(//private/tmp/optima-workspace/**)",
16
- "Read(//tmp/optima-workspace/**)",
17
- "Read(//tmp/optima-workspace/.claude/commands/**)",
18
- "Bash(git add:*)",
19
- "Bash(git push:*)",
20
- "Bash(find:*)",
21
- "Bash(git commit:*)",
22
- "Bash(aws logs get-log-events:*)",
23
- "Bash(npm install:*)",
24
- "Bash(optima-dev-skills:*)",
25
- "Bash(optima-generate-test-token:*)",
26
- "Bash(optima-query-db:*)",
27
- "Bash(gh variable set:*)",
28
- "Bash(npm publish:*)",
29
- "Bash(python3:*)",
30
- "Bash(gh api:*)",
31
- "Bash(curl -s http://auth.optima.chat/openapi.json)",
32
- "Bash(curl -s https://auth.optima.chat/openapi.json)",
33
- "Bash(cat:*)",
34
- "Bash(node /Users/verypro/optima-dev-skills/scripts/install.js:*)",
35
- "Bash(aws logs tail:*)",
36
- "Bash(grep:*)",
37
- "Bash(npm view:*)",
38
- "Bash(npm version:*)",
39
- "Bash(git checkout:*)",
40
- "Bash(git pull:*)",
41
- "Bash(node scripts/install.js:*)",
42
- "Bash(gh issue:*)",
43
- "Bash(npm run:*)",
44
- "Bash(gh pr:*)",
45
- "Bash(node:*)",
46
- "Bash(echo \"exit: $?\")"
47
- ],
48
- "deny": [],
49
- "ask": []
50
- }
51
- }
@@ -1,105 +0,0 @@
1
- ---
2
- name: "grant-credits"
3
- description: "当用户请求赠送 credits、充值积分、grant credits、加 credits、奖励积分、补偿 credits、推荐奖励时,使用此技能。支持 Stage、Prod 两个环境。"
4
- allowed-tools: ["Bash"]
5
- ---
6
-
7
- # 赠送 Credits
8
-
9
- 当你需要为用户赠送额外 credits 时,使用这个场景。
10
-
11
- ## 执行方式:使用 CLI 工具
12
-
13
- **重要**:使用 `optima-grant-credits` CLI 工具:
14
-
15
- ```bash
16
- optima-grant-credits <email> --amount <n> [options]
17
- ```
18
-
19
- **为什么使用 CLI 工具**:
20
- - 自动通过 email 查找 userId(跨 user-auth 数据库)
21
- - 自动处理 SSH 隧道和数据库连接
22
- - 不会影响现有订阅和 credits(纯追加)
23
- - 一条命令完成操作
24
-
25
- ## 适用情况
26
-
27
- - 赠送额外 credits(奖励、补偿、推广等)
28
- - 推荐奖励 credits
29
- - 运营活动发放 credits
30
- - 客户补偿
31
-
32
- ## 快速操作
33
-
34
- ```bash
35
- # 赠送 100 bonus credits,Stage 环境(默认)
36
- optima-grant-credits user@example.com --amount 100
37
-
38
- # 赠送 500 bonus credits,Prod 环境
39
- optima-grant-credits user@example.com --amount 500 --env prod
40
-
41
- # 推荐奖励 credits
42
- optima-grant-credits user@example.com --amount 200 --type referral --env prod
43
-
44
- # 自定义描述
45
- optima-grant-credits user@example.com --amount 300 --description "客户补偿 - 服务中断" --env prod
46
- ```
47
-
48
- ### 参数说明
49
-
50
- | 参数 | 说明 | 默认值 |
51
- |------|------|--------|
52
- | `<email>` | 用户邮箱(必填) | - |
53
- | `--amount <n>` | Credits 数量(必填,>=1) | - |
54
- | `--type <type>` | 类型:bonus, referral | bonus |
55
- | `--description <text>` | 描述(可选) | Auto-generated |
56
- | `--env <env>` | 环境:stage, prod | stage |
57
-
58
- ## 与 grant-subscription 的区别
59
-
60
- | | grant-credits | grant-subscription |
61
- |---|---|---|
62
- | 作用 | 追加 credits | 开通/切换订阅计划 |
63
- | 现有 credits | 不影响 | 清零后重新授予 |
64
- | 现有订阅 | 不影响 | 取消旧的,创建新的 |
65
- | Token quota | 不影响 | 按计划更新 |
66
- | 适用场景 | 奖励、补偿、推广 | 开通会员、升级计划 |
67
-
68
- ## 常见使用场景
69
-
70
- ### 场景 1:奖励 credits
71
-
72
- **用户请求**:"给 xxx@gmail.com 加 200 credits"
73
-
74
- ```bash
75
- optima-grant-credits xxx@gmail.com --amount 200 --env prod
76
- ```
77
-
78
- ### 场景 2:推荐奖励
79
-
80
- **用户请求**:"xxx 推荐了新用户,给他 referral 奖励 300"
81
-
82
- ```bash
83
- optima-grant-credits xxx@gmail.com --amount 300 --type referral --env prod
84
- ```
85
-
86
- ### 场景 3:客户补偿
87
-
88
- **用户请求**:"服务出了问题,补偿 xxx 500 credits"
89
-
90
- ```bash
91
- optima-grant-credits xxx@gmail.com --amount 500 --description "服务中断补偿" --env prod
92
- ```
93
-
94
- ## 安全提醒
95
-
96
- 1. **Stage 优先**:默认操作 Stage 环境
97
- 2. **Prod 谨慎**:操作 Prod 前确认邮箱和数量
98
- 3. **纯追加**:不会影响现有 credits 和订阅
99
- 4. **无过期**:bonus/referral credits 默认不设过期时间
100
-
101
- ## 相关命令
102
-
103
- - `optima-grant-credits` - 赠送 credits(主要方式)
104
- - `optima-grant-subscription` - 开通订阅计划
105
- - `optima-query-db` - 查询数据库验证结果
@@ -1,28 +0,0 @@
1
- ---
2
- name: "grant-credits"
3
- description: "Use when the user wants to add bonus or referral credits to an Optima user without changing their subscription."
4
- ---
5
-
6
- # Grant Credits
7
-
8
- Use this skill when the user asks to add credits directly.
9
-
10
- ## Preferred Command
11
-
12
- ```bash
13
- optima-grant-credits <email> --amount <n> [options]
14
- ```
15
-
16
- ## Examples
17
-
18
- ```bash
19
- optima-grant-credits user@example.com --amount 100
20
- optima-grant-credits user@example.com --amount 500 --env prod
21
- optima-grant-credits user@example.com --amount 300 --type referral --env prod
22
- ```
23
-
24
- ## Guidance
25
-
26
- - Default to `stage`.
27
- - Confirm the email and amount before using `prod`.
28
- - This operation appends credits and does not replace the user's subscription.
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { getInfisicalConfig, getInfisicalToken, resolveUserId, connectBillingDB, escapeSQL } from './db-utils';
4
-
5
- function parseArgs(args: string[]): { email: string; amount: number; type: string; description: string | null; env: string } {
6
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
7
- console.log(`Usage: optima-grant-credits <email> --amount <n> [options]
8
-
9
- Options:
10
- --amount <n> Credits to grant (required)
11
- --type <type> Credit type: bonus, referral (default: bonus)
12
- --description <text> Description (optional)
13
- --env <env> Environment: stage, prod (default: stage)
14
- -h, --help Show this help`);
15
- process.exit(0);
16
- }
17
-
18
- const email = args[0];
19
- let amount = 0;
20
- let type = 'bonus';
21
- let description: string | null = null;
22
- let env = 'stage';
23
-
24
- for (let i = 1; i < args.length; i++) {
25
- if (args[i] === '--amount' && args[i + 1]) { amount = parseInt(args[++i], 10); }
26
- else if (args[i] === '--type' && args[i + 1]) { type = args[++i]; }
27
- else if (args[i] === '--description' && args[i + 1]) { description = args[++i]; }
28
- else if (args[i] === '--env' && args[i + 1]) { env = args[++i]; }
29
- }
30
-
31
- if (amount < 1) { console.error('--amount is required and must be >= 1'); process.exit(1); }
32
- if (!['bonus', 'referral'].includes(type)) { console.error(`Unknown type: ${type}. Available: bonus, referral`); process.exit(1); }
33
- if (!['stage', 'prod'].includes(env)) { console.error('Env must be stage or prod (billing DB not available in CI)'); process.exit(1); }
34
-
35
- return { email, amount, type, description, env };
36
- }
37
-
38
- async function main() {
39
- const { email, amount, type, description, env } = parseArgs(process.argv.slice(2));
40
- const infisicalConfig = getInfisicalConfig();
41
- const token = getInfisicalToken(infisicalConfig);
42
-
43
- console.log(`\n🎁 Granting ${amount} ${type} credits to ${email} [${env.toUpperCase()}]\n`);
44
-
45
- const userId = await resolveUserId(email, env, infisicalConfig, token);
46
- const billing = await connectBillingDB(env, infisicalConfig, token);
47
- const bq = billing.query;
48
-
49
- const now = new Date().toISOString();
50
- const safeUserId = escapeSQL(userId);
51
- const safeType = escapeSQL(type);
52
- const safeDesc = escapeSQL(description || `Admin ${type} credit grant`);
53
-
54
- console.log(`Inserting ${amount} ${type} credits...`);
55
- const ledgerId = bq(`INSERT INTO credit_ledger (id, user_id, type, description, initial_amount, remaining, created_at) VALUES (concat('crd_${safeType}_', substr(md5(random()::text), 1, 16)), '${safeUserId}', '${safeType}', '${safeDesc}', ${amount}, ${amount}, '${now}') RETURNING id`);
56
- console.log(`✓ Credits granted (ledger ID: ${ledgerId})`);
57
-
58
- const balance = bq(`SELECT COALESCE(SUM(remaining), 0) FROM credit_ledger WHERE user_id='${safeUserId}' AND remaining > 0 AND (expires_at IS NULL OR expires_at > NOW())`);
59
- console.log(`\n✅ Done! ${email} now has ${balance} total credits\n`);
60
- }
61
-
62
- main().catch(error => {
63
- console.error('\n❌ Error:', error.message);
64
- process.exit(1);
65
- });