@optima-chat/dev-skills 0.8.0 → 0.8.1

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.
@@ -1,6 +1,6 @@
1
1
  import { execSync } from 'child_process';
2
2
  import { fetchInfisicalSecret } from './infisical-secrets';
3
- import { getInfisicalConfig, getInfisicalToken, getCnInfisicalToken, getCnSecrets } from './db-utils';
3
+ import { getInfisicalConfig, getInfisicalToken, getCnInfisicalToken, getCnSecrets, resolveUserId } from './db-utils';
4
4
 
5
5
  const USER_AUTH_URLS: Record<string, string> = {
6
6
  stage: 'https://auth.stage.optima.onl',
@@ -292,6 +292,25 @@ export async function resolveUserIdByEmail(env: string, email: string): Promise<
292
292
  return parsed.user_id;
293
293
  }
294
294
 
295
+ /**
296
+ * Resolve email → userId across all envs, picking the path that exists for
297
+ * each cloud. AWS (stage/prod) reaches the database over the RDS SSH tunnel
298
+ * (db-utils.resolveUserId). cn-prod / cn-stage have no tunnel into the Aliyun
299
+ * VPC-internal RDS, so they resolve via user-auth's internal HTTP lookup
300
+ * (resolveUserIdByEmail) — the cn dev-skills token carries the
301
+ * internal:users:write scope that endpoint requires. Lets the entitlement
302
+ * subcommands support cn the same way grant-subscription already does, without
303
+ * each call site re-implementing the branch.
304
+ */
305
+ export async function resolveUserIdByEmailAnyEnv(env: string, email: string): Promise<string> {
306
+ if (env === 'cn-prod' || env === 'cn-stage') {
307
+ return resolveUserIdByEmail(env, email);
308
+ }
309
+ const cfg = getInfisicalConfig();
310
+ const token = getInfisicalToken(cfg);
311
+ return resolveUserId(email, env, cfg, token);
312
+ }
313
+
295
314
  /**
296
315
  * Resolve a user's id by phone via user-auth's internal lookup endpoint
297
316
  * (POST /api/v1/internal/users/lookup with {phone}). Mirrors
@@ -1,17 +1,21 @@
1
1
  import * as readline from 'readline';
2
2
 
3
3
  /**
4
- * On prod, print the resolved action and require typing "yes" to proceed.
5
- * No-op on stage or when --yes was passed. Exits 1 if user declines.
4
+ * On a production env, print the resolved action and require typing "yes" to
5
+ * proceed. Both AWS `prod` and Aliyun `cn-prod` are production cn-prod must
6
+ * gate identically or the cn rollout silently loses the prod safety prompt.
7
+ * No-op on stage / cn-stage or when --yes was passed. Exits 1 if user declines.
6
8
  */
9
+ const PROD_ENVS = new Set(['prod', 'cn-prod']);
10
+
7
11
  export async function confirmIfProd(
8
12
  env: string,
9
13
  actionDescription: string,
10
14
  skipFlag: boolean,
11
15
  ): Promise<void> {
12
- if (env !== 'prod' || skipFlag) return;
16
+ if (!PROD_ENVS.has(env) || skipFlag) return;
13
17
 
14
- console.log(`\n⚠️ About to perform on PROD:\n${actionDescription}\n`);
18
+ console.log(`\n⚠️ About to perform on ${env.toUpperCase()}:\n${actionDescription}\n`);
15
19
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
16
20
  const answer = await new Promise<string>((resolve) => {
17
21
  rl.question('Type "yes" to confirm: ', (a) => { rl.close(); resolve(a.trim()); });
@@ -1,4 +1,4 @@
1
- import { callBilling, validateEnv } from '../billing-http';
1
+ import { callBilling, validateEnvCnProd } from '../billing-http';
2
2
  import { confirmIfProd } from '../confirm-prompt';
3
3
 
4
4
  interface CreateArgs {
@@ -34,7 +34,7 @@ Optional:
34
34
  --ends <date> Valid-until
35
35
  --max <N> Max total redemptions (default: unlimited; 1 = single-use)
36
36
  --campaign <label> Grouping label
37
- --env stage|prod (default: stage)
37
+ --env stage|prod|cn-prod|cn-stage (default: stage)
38
38
  --yes Skip prod confirmation`);
39
39
  process.exit(0);
40
40
  }
@@ -62,7 +62,7 @@ Optional:
62
62
 
63
63
  export async function runCreate(argv: string[]): Promise<void> {
64
64
  const args = parseArgs(argv);
65
- validateEnv(args.env);
65
+ validateEnvCnProd(args.env);
66
66
  await confirmIfProd(args.env, `Create discount code ${args.code.toUpperCase()} (${args.percentOff}% off)`, args.yes);
67
67
 
68
68
  const body: Record<string, unknown> = { code: args.code, percentOff: args.percentOff };
@@ -1,4 +1,4 @@
1
- import { callBilling, validateEnv } from '../billing-http';
1
+ import { callBilling, validateEnvCnProd } from '../billing-http';
2
2
  import { confirmIfProd } from '../confirm-prompt';
3
3
 
4
4
  interface DisableArgs {
@@ -15,7 +15,7 @@ Required:
15
15
  --code <CODE>
16
16
 
17
17
  Optional:
18
- --env stage|prod (default: stage)
18
+ --env stage|prod|cn-prod|cn-stage (default: stage)
19
19
  --yes Skip prod confirmation`);
20
20
  process.exit(0);
21
21
  }
@@ -36,7 +36,7 @@ Optional:
36
36
 
37
37
  export async function runDisable(argv: string[]): Promise<void> {
38
38
  const args = parseArgs(argv);
39
- validateEnv(args.env);
39
+ validateEnvCnProd(args.env);
40
40
  await confirmIfProd(args.env, `Disable discount code ${args.code.toUpperCase()}`, args.yes);
41
41
  const res = await callBilling(args.env, 'PATCH', `/api/billing/admin/discount-codes/${encodeURIComponent(args.code)}`, { status: 'DISABLED' });
42
42
  console.log(`✓ Disabled (HTTP ${res.status}):`);
@@ -1,5 +1,5 @@
1
1
  import * as fs from 'fs';
2
- import { callBilling, validateEnv } from '../billing-http';
2
+ import { callBilling, validateEnvCnProd } from '../billing-http';
3
3
  import { confirmIfProd } from '../confirm-prompt';
4
4
 
5
5
  interface GenArgs {
@@ -35,7 +35,7 @@ Optional:
35
35
  --products <a,b,...> Limit to these productKeys
36
36
  --starts <date> Valid-from (YYYY-MM-DD or ISO)
37
37
  --ends <date> Valid-until
38
- --env stage|prod (default: stage)
38
+ --env stage|prod|cn-prod|cn-stage (default: stage)
39
39
  --yes Skip prod confirmation`);
40
40
  process.exit(0);
41
41
  }
@@ -63,7 +63,7 @@ Optional:
63
63
 
64
64
  export async function runGenerate(argv: string[]): Promise<void> {
65
65
  const args = parseArgs(argv);
66
- validateEnv(args.env);
66
+ validateEnvCnProd(args.env);
67
67
  await confirmIfProd(args.env, `Generate ${args.count} unique discount codes (${args.percentOff}% off, campaign ${args.campaign})`, args.yes);
68
68
 
69
69
  const body: Record<string, unknown> = { count: args.count, percentOff: args.percentOff, campaign: args.campaign };
@@ -1,4 +1,4 @@
1
- import { callBilling, validateEnv } from '../billing-http';
1
+ import { callBilling, validateEnvCnProd } from '../billing-http';
2
2
 
3
3
  interface ListArgs {
4
4
  campaign?: string;
@@ -15,7 +15,7 @@ Optional:
15
15
  --campaign <label> Filter by campaign
16
16
  --code <CODE> Filter by exact code
17
17
  --limit <N> Max rows (default 500, max 1000)
18
- --env stage|prod (default: stage)`);
18
+ --env stage|prod|cn-prod|cn-stage (default: stage)`);
19
19
  process.exit(0);
20
20
  }
21
21
  const out: Partial<ListArgs> = { env: 'stage' };
@@ -35,7 +35,7 @@ Optional:
35
35
 
36
36
  export async function runList(argv: string[]): Promise<void> {
37
37
  const args = parseArgs(argv);
38
- validateEnv(args.env);
38
+ validateEnvCnProd(args.env);
39
39
  const qs = new URLSearchParams();
40
40
  if (args.campaign) qs.set('campaign', args.campaign);
41
41
  if (args.code) qs.set('code', args.code);
@@ -1,6 +1,5 @@
1
- import { callBilling, validateEnv } from '../billing-http';
1
+ import { callBilling, validateEnvCnProd, resolveUserIdByEmailAnyEnv } from '../billing-http';
2
2
  import { confirmIfProd } from '../confirm-prompt';
3
- import { getInfisicalConfig, getInfisicalToken, resolveUserId } from '../db-utils';
4
3
 
5
4
  interface GrantArgs {
6
5
  email: string;
@@ -21,7 +20,7 @@ Required:
21
20
 
22
21
  Optional:
23
22
  --yes Skip prod confirmation prompt (no-op on stage)
24
- --env stage|prod (default: stage)
23
+ --env stage|prod|cn-prod|cn-stage (default: stage)
25
24
 
26
25
  Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy=<clientId>.`);
27
26
  process.exit(0);
@@ -47,11 +46,9 @@ Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy
47
46
 
48
47
  export async function runGrant(argv: string[]): Promise<void> {
49
48
  const args = parseArgs(argv);
50
- validateEnv(args.env);
49
+ validateEnvCnProd(args.env);
51
50
 
52
- const cfg = getInfisicalConfig();
53
- const token = getInfisicalToken(cfg);
54
- const userId = await resolveUserId(args.email, args.env, cfg, token);
51
+ const userId = await resolveUserIdByEmailAnyEnv(args.env, args.email);
55
52
 
56
53
  await confirmIfProd(
57
54
  args.env,
@@ -1,5 +1,4 @@
1
- import { callBilling, validateEnv } from '../billing-http';
2
- import { getInfisicalConfig, getInfisicalToken, resolveUserId } from '../db-utils';
1
+ import { callBilling, validateEnvCnProd, resolveUserIdByEmailAnyEnv } from '../billing-http';
3
2
 
4
3
  interface ListArgs {
5
4
  email: string;
@@ -14,7 +13,7 @@ Required:
14
13
  --email <user-email> Resolved to userId via user-auth DB
15
14
 
16
15
  Optional:
17
- --env stage|prod (default: stage)`);
16
+ --env stage|prod|cn-prod|cn-stage (default: stage)`);
18
17
  process.exit(0);
19
18
  }
20
19
  const out: Partial<ListArgs> = { env: 'stage' };
@@ -42,11 +41,9 @@ interface EntitlementRow {
42
41
 
43
42
  export async function runList(argv: string[]): Promise<void> {
44
43
  const args = parseArgs(argv);
45
- validateEnv(args.env);
44
+ validateEnvCnProd(args.env);
46
45
 
47
- const cfg = getInfisicalConfig();
48
- const token = getInfisicalToken(cfg);
49
- const userId = await resolveUserId(args.email, args.env, cfg, token);
46
+ const userId = await resolveUserIdByEmailAnyEnv(args.env, args.email);
50
47
 
51
48
  const res = await callBilling<{ entitlements: EntitlementRow[] }>(
52
49
  args.env,
@@ -1,6 +1,5 @@
1
- import { callBilling, validateEnv } from '../billing-http';
1
+ import { callBilling, validateEnvCnProd, resolveUserIdByEmailAnyEnv } from '../billing-http';
2
2
  import { confirmIfProd } from '../confirm-prompt';
3
- import { getInfisicalConfig, getInfisicalToken, resolveUserId } from '../db-utils';
4
3
 
5
4
  interface RevokeArgs {
6
5
  email: string;
@@ -28,7 +27,7 @@ Required:
28
27
 
29
28
  Optional:
30
29
  --yes Skip prod confirmation prompt (no-op on stage)
31
- --env stage|prod (default: stage)
30
+ --env stage|prod|cn-prod|cn-stage (default: stage)
32
31
 
33
32
  Refuses non-ADMIN_GRANT sources (PAYMENT, PARTNER) with source-specific
34
33
  error pointing to the right reversal flow.`);
@@ -59,11 +58,9 @@ const PARTNER_REFUSAL = `refusing to revoke a PARTNER-source entitlement via CLI
59
58
 
60
59
  export async function runRevoke(argv: string[]): Promise<void> {
61
60
  const args = parseArgs(argv);
62
- validateEnv(args.env);
61
+ validateEnvCnProd(args.env);
63
62
 
64
- const cfg = getInfisicalConfig();
65
- const token = getInfisicalToken(cfg);
66
- const userId = await resolveUserId(args.email, args.env, cfg, token);
63
+ const userId = await resolveUserIdByEmailAnyEnv(args.env, args.email);
67
64
 
68
65
  // Step 1: Fetch user's entitlements
69
66
  const listRes = await callBilling<{ entitlements: EntitlementRow[] }>(
@@ -6,6 +6,7 @@ exports.getServiceToken = getServiceToken;
6
6
  exports.callBilling = callBilling;
7
7
  exports.callSkills = callSkills;
8
8
  exports.resolveUserIdByEmail = resolveUserIdByEmail;
9
+ exports.resolveUserIdByEmailAnyEnv = resolveUserIdByEmailAnyEnv;
9
10
  exports.resolveUserIdByPhone = resolveUserIdByPhone;
10
11
  exports.getUserById = getUserById;
11
12
  const child_process_1 = require("child_process");
@@ -256,6 +257,24 @@ async function resolveUserIdByEmail(env, email) {
256
257
  console.log(`✓ Found user: ${parsed.user_id}`);
257
258
  return parsed.user_id;
258
259
  }
260
+ /**
261
+ * Resolve email → userId across all envs, picking the path that exists for
262
+ * each cloud. AWS (stage/prod) reaches the database over the RDS SSH tunnel
263
+ * (db-utils.resolveUserId). cn-prod / cn-stage have no tunnel into the Aliyun
264
+ * VPC-internal RDS, so they resolve via user-auth's internal HTTP lookup
265
+ * (resolveUserIdByEmail) — the cn dev-skills token carries the
266
+ * internal:users:write scope that endpoint requires. Lets the entitlement
267
+ * subcommands support cn the same way grant-subscription already does, without
268
+ * each call site re-implementing the branch.
269
+ */
270
+ async function resolveUserIdByEmailAnyEnv(env, email) {
271
+ if (env === 'cn-prod' || env === 'cn-stage') {
272
+ return resolveUserIdByEmail(env, email);
273
+ }
274
+ const cfg = (0, db_utils_1.getInfisicalConfig)();
275
+ const token = (0, db_utils_1.getInfisicalToken)(cfg);
276
+ return (0, db_utils_1.resolveUserId)(email, env, cfg, token);
277
+ }
259
278
  /**
260
279
  * Resolve a user's id by phone via user-auth's internal lookup endpoint
261
280
  * (POST /api/v1/internal/users/lookup with {phone}). Mirrors
@@ -36,13 +36,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.confirmIfProd = confirmIfProd;
37
37
  const readline = __importStar(require("readline"));
38
38
  /**
39
- * On prod, print the resolved action and require typing "yes" to proceed.
40
- * No-op on stage or when --yes was passed. Exits 1 if user declines.
39
+ * On a production env, print the resolved action and require typing "yes" to
40
+ * proceed. Both AWS `prod` and Aliyun `cn-prod` are production cn-prod must
41
+ * gate identically or the cn rollout silently loses the prod safety prompt.
42
+ * No-op on stage / cn-stage or when --yes was passed. Exits 1 if user declines.
41
43
  */
44
+ const PROD_ENVS = new Set(['prod', 'cn-prod']);
42
45
  async function confirmIfProd(env, actionDescription, skipFlag) {
43
- if (env !== 'prod' || skipFlag)
46
+ if (!PROD_ENVS.has(env) || skipFlag)
44
47
  return;
45
- console.log(`\n⚠️ About to perform on PROD:\n${actionDescription}\n`);
48
+ console.log(`\n⚠️ About to perform on ${env.toUpperCase()}:\n${actionDescription}\n`);
46
49
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
47
50
  const answer = await new Promise((resolve) => {
48
51
  rl.question('Type "yes" to confirm: ', (a) => { rl.close(); resolve(a.trim()); });
@@ -24,7 +24,7 @@ Optional:
24
24
  --ends <date> Valid-until
25
25
  --max <N> Max total redemptions (default: unlimited; 1 = single-use)
26
26
  --campaign <label> Grouping label
27
- --env stage|prod (default: stage)
27
+ --env stage|prod|cn-prod|cn-stage (default: stage)
28
28
  --yes Skip prod confirmation`);
29
29
  process.exit(0);
30
30
  }
@@ -81,7 +81,7 @@ Optional:
81
81
  }
82
82
  async function runCreate(argv) {
83
83
  const args = parseArgs(argv);
84
- (0, billing_http_1.validateEnv)(args.env);
84
+ (0, billing_http_1.validateEnvCnProd)(args.env);
85
85
  await (0, confirm_prompt_1.confirmIfProd)(args.env, `Create discount code ${args.code.toUpperCase()} (${args.percentOff}% off)`, args.yes);
86
86
  const body = { code: args.code, percentOff: args.percentOff };
87
87
  if (args.productKeys)
@@ -11,7 +11,7 @@ Required:
11
11
  --code <CODE>
12
12
 
13
13
  Optional:
14
- --env stage|prod (default: stage)
14
+ --env stage|prod|cn-prod|cn-stage (default: stage)
15
15
  --yes Skip prod confirmation`);
16
16
  process.exit(0);
17
17
  }
@@ -40,7 +40,7 @@ Optional:
40
40
  }
41
41
  async function runDisable(argv) {
42
42
  const args = parseArgs(argv);
43
- (0, billing_http_1.validateEnv)(args.env);
43
+ (0, billing_http_1.validateEnvCnProd)(args.env);
44
44
  await (0, confirm_prompt_1.confirmIfProd)(args.env, `Disable discount code ${args.code.toUpperCase()}`, args.yes);
45
45
  const res = await (0, billing_http_1.callBilling)(args.env, 'PATCH', `/api/billing/admin/discount-codes/${encodeURIComponent(args.code)}`, { status: 'DISABLED' });
46
46
  console.log(`✓ Disabled (HTTP ${res.status}):`);
@@ -59,7 +59,7 @@ Optional:
59
59
  --products <a,b,...> Limit to these productKeys
60
60
  --starts <date> Valid-from (YYYY-MM-DD or ISO)
61
61
  --ends <date> Valid-until
62
- --env stage|prod (default: stage)
62
+ --env stage|prod|cn-prod|cn-stage (default: stage)
63
63
  --yes Skip prod confirmation`);
64
64
  process.exit(0);
65
65
  }
@@ -114,7 +114,7 @@ Optional:
114
114
  }
115
115
  async function runGenerate(argv) {
116
116
  const args = parseArgs(argv);
117
- (0, billing_http_1.validateEnv)(args.env);
117
+ (0, billing_http_1.validateEnvCnProd)(args.env);
118
118
  await (0, confirm_prompt_1.confirmIfProd)(args.env, `Generate ${args.count} unique discount codes (${args.percentOff}% off, campaign ${args.campaign})`, args.yes);
119
119
  const body = { count: args.count, percentOff: args.percentOff, campaign: args.campaign };
120
120
  if (args.productKeys)
@@ -10,7 +10,7 @@ Optional:
10
10
  --campaign <label> Filter by campaign
11
11
  --code <CODE> Filter by exact code
12
12
  --limit <N> Max rows (default 500, max 1000)
13
- --env stage|prod (default: stage)`);
13
+ --env stage|prod|cn-prod|cn-stage (default: stage)`);
14
14
  process.exit(0);
15
15
  }
16
16
  const out = { env: 'stage' };
@@ -45,7 +45,7 @@ Optional:
45
45
  }
46
46
  async function runList(argv) {
47
47
  const args = parseArgs(argv);
48
- (0, billing_http_1.validateEnv)(args.env);
48
+ (0, billing_http_1.validateEnvCnProd)(args.env);
49
49
  const qs = new URLSearchParams();
50
50
  if (args.campaign)
51
51
  qs.set('campaign', args.campaign);
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runGrant = runGrant;
4
4
  const billing_http_1 = require("../billing-http");
5
5
  const confirm_prompt_1 = require("../confirm-prompt");
6
- const db_utils_1 = require("../db-utils");
7
6
  function parseArgs(argv) {
8
7
  if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
9
8
  console.log(`Usage: optima-entitlement grant --email <user> --product-key <slug> --justification "..." [options]
@@ -15,7 +14,7 @@ Required:
15
14
 
16
15
  Optional:
17
16
  --yes Skip prod confirmation prompt (no-op on stage)
18
- --env stage|prod (default: stage)
17
+ --env stage|prod|cn-prod|cn-stage (default: stage)
19
18
 
20
19
  Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy=<clientId>.`);
21
20
  process.exit(0);
@@ -57,10 +56,8 @@ Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy
57
56
  }
58
57
  async function runGrant(argv) {
59
58
  const args = parseArgs(argv);
60
- (0, billing_http_1.validateEnv)(args.env);
61
- const cfg = (0, db_utils_1.getInfisicalConfig)();
62
- const token = (0, db_utils_1.getInfisicalToken)(cfg);
63
- const userId = await (0, db_utils_1.resolveUserId)(args.email, args.env, cfg, token);
59
+ (0, billing_http_1.validateEnvCnProd)(args.env);
60
+ const userId = await (0, billing_http_1.resolveUserIdByEmailAnyEnv)(args.env, args.email);
64
61
  await (0, confirm_prompt_1.confirmIfProd)(args.env, `Action: GRANT product '${args.productKey}' to user ${args.email} (userId=${userId}) on ${args.env.toUpperCase()}\nJustification: ${args.justification}`, args.yes);
65
62
  console.log(`\n🎁 Granting ${args.productKey} to ${args.email}...`);
66
63
  const res = await (0, billing_http_1.callBilling)(args.env, 'POST', '/api/billing/admin/grant-entitlement', {
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runList = runList;
4
4
  const billing_http_1 = require("../billing-http");
5
- const db_utils_1 = require("../db-utils");
6
5
  function parseArgs(argv) {
7
6
  if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
8
7
  console.log(`Usage: optima-entitlement list --email <user-email> [options]
@@ -11,7 +10,7 @@ Required:
11
10
  --email <user-email> Resolved to userId via user-auth DB
12
11
 
13
12
  Optional:
14
- --env stage|prod (default: stage)`);
13
+ --env stage|prod|cn-prod|cn-stage (default: stage)`);
15
14
  process.exit(0);
16
15
  }
17
16
  const out = { env: 'stage' };
@@ -36,10 +35,8 @@ Optional:
36
35
  }
37
36
  async function runList(argv) {
38
37
  const args = parseArgs(argv);
39
- (0, billing_http_1.validateEnv)(args.env);
40
- const cfg = (0, db_utils_1.getInfisicalConfig)();
41
- const token = (0, db_utils_1.getInfisicalToken)(cfg);
42
- const userId = await (0, db_utils_1.resolveUserId)(args.email, args.env, cfg, token);
38
+ (0, billing_http_1.validateEnvCnProd)(args.env);
39
+ const userId = await (0, billing_http_1.resolveUserIdByEmailAnyEnv)(args.env, args.email);
43
40
  const res = await (0, billing_http_1.callBilling)(args.env, 'GET', `/api/billing/admin/entitlements?userId=${encodeURIComponent(userId)}`);
44
41
  const rows = res.body.entitlements ?? [];
45
42
  if (rows.length === 0) {
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runRevoke = runRevoke;
4
4
  const billing_http_1 = require("../billing-http");
5
5
  const confirm_prompt_1 = require("../confirm-prompt");
6
- const db_utils_1 = require("../db-utils");
7
6
  function parseArgs(argv) {
8
7
  if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
9
8
  console.log(`Usage: optima-entitlement revoke --email <user> --product-key <slug> --reason "..." [options]
@@ -15,7 +14,7 @@ Required:
15
14
 
16
15
  Optional:
17
16
  --yes Skip prod confirmation prompt (no-op on stage)
18
- --env stage|prod (default: stage)
17
+ --env stage|prod|cn-prod|cn-stage (default: stage)
19
18
 
20
19
  Refuses non-ADMIN_GRANT sources (PAYMENT, PARTNER) with source-specific
21
20
  error pointing to the right reversal flow.`);
@@ -60,10 +59,8 @@ const PAYMENT_REFUSAL = `refusing to revoke a PAYMENT-source entitlement via CLI
60
59
  const PARTNER_REFUSAL = `refusing to revoke a PARTNER-source entitlement via CLI; PARTNER grants are issued out-of-band and must be reversed via the partner contract / process that issued them. Manual psql is the escape hatch if absolutely necessary.`;
61
60
  async function runRevoke(argv) {
62
61
  const args = parseArgs(argv);
63
- (0, billing_http_1.validateEnv)(args.env);
64
- const cfg = (0, db_utils_1.getInfisicalConfig)();
65
- const token = (0, db_utils_1.getInfisicalToken)(cfg);
66
- const userId = await (0, db_utils_1.resolveUserId)(args.email, args.env, cfg, token);
62
+ (0, billing_http_1.validateEnvCnProd)(args.env);
63
+ const userId = await (0, billing_http_1.resolveUserIdByEmailAnyEnv)(args.env, args.email);
67
64
  // Step 1: Fetch user's entitlements
68
65
  const listRes = await (0, billing_http_1.callBilling)(args.env, 'GET', `/api/billing/admin/entitlements?userId=${encodeURIComponent(userId)}`);
69
66
  const all = listRes.body.entitlements ?? [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/dev-skills",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Claude Code Skills for Optima development team - cross-environment collaboration tools",
5
5
  "main": "index.js",
6
6
  "bin": {