@optima-chat/dev-skills 0.16.6 → 0.16.7
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/bin/helpers/entitlement/grant.ts +5 -0
- package/bin/helpers/entitlement/revoke.ts +5 -0
- package/bin/helpers/grant-credits.ts +8 -2
- package/bin/helpers/grant-subscription.ts +8 -4
- package/bin/helpers/operator.ts +9 -0
- package/dist/bin/helpers/entitlement/grant.js +7 -0
- package/dist/bin/helpers/entitlement/revoke.js +7 -0
- package/dist/bin/helpers/grant-credits.js +9 -2
- package/dist/bin/helpers/grant-subscription.js +9 -3
- package/dist/bin/helpers/operator.js +11 -0
- package/package.json +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { callBilling, validateEnvCnProd } from '../billing-http';
|
|
2
2
|
import { confirmIfProd } from '../confirm-prompt';
|
|
3
3
|
import { resolveTargetUser } from '../grant-subscription';
|
|
4
|
+
import { operatorActorId } from '../operator';
|
|
4
5
|
|
|
5
6
|
interface GrantArgs {
|
|
6
7
|
identifier: string;
|
|
7
8
|
productKey: string;
|
|
8
9
|
justification: string;
|
|
9
10
|
yes: boolean;
|
|
11
|
+
operator?: string;
|
|
10
12
|
env: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -21,6 +23,7 @@ Required:
|
|
|
21
23
|
|
|
22
24
|
Optional:
|
|
23
25
|
--yes Skip prod/cn-prod confirmation prompt (no-op on stage / cn-stage)
|
|
26
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
24
27
|
--env stage|prod|cn-prod|cn-stage (default: stage)
|
|
25
28
|
|
|
26
29
|
Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy=<clientId>.`);
|
|
@@ -35,6 +38,7 @@ Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy
|
|
|
35
38
|
case '--product-key': out.productKey = next; i++; break;
|
|
36
39
|
case '--justification': out.justification = next; i++; break;
|
|
37
40
|
case '--yes': out.yes = true; break;
|
|
41
|
+
case '--operator': out.operator = next; i++; break;
|
|
38
42
|
case '--env': out.env = next; i++; break;
|
|
39
43
|
default:
|
|
40
44
|
if (a.startsWith('--')) throw new Error(`Unknown arg: ${a}`);
|
|
@@ -67,6 +71,7 @@ export async function runGrant(argv: string[]): Promise<void> {
|
|
|
67
71
|
userId,
|
|
68
72
|
productKey: args.productKey,
|
|
69
73
|
justification: args.justification,
|
|
74
|
+
actorUserId: operatorActorId(args.operator ?? null),
|
|
70
75
|
});
|
|
71
76
|
console.log(`✓ Granted entitlement (HTTP ${res.status}):`);
|
|
72
77
|
console.log(JSON.stringify(res.body, null, 2));
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { callBilling, validateEnvCnProd } from '../billing-http';
|
|
2
2
|
import { confirmIfProd } from '../confirm-prompt';
|
|
3
3
|
import { resolveTargetUser } from '../grant-subscription';
|
|
4
|
+
import { operatorActorId } from '../operator';
|
|
4
5
|
|
|
5
6
|
interface RevokeArgs {
|
|
6
7
|
identifier: string;
|
|
7
8
|
productKey: string;
|
|
8
9
|
reason: string;
|
|
9
10
|
yes: boolean;
|
|
11
|
+
operator?: string;
|
|
10
12
|
env: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -28,6 +30,7 @@ Required:
|
|
|
28
30
|
|
|
29
31
|
Optional:
|
|
30
32
|
--yes Skip prod/cn-prod confirmation prompt (no-op on stage / cn-stage)
|
|
33
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
31
34
|
--env stage|prod|cn-prod|cn-stage (default: stage)
|
|
32
35
|
|
|
33
36
|
Refuses non-ADMIN_GRANT sources (PAYMENT, PARTNER) with source-specific
|
|
@@ -43,6 +46,7 @@ error pointing to the right reversal flow.`);
|
|
|
43
46
|
case '--product-key': out.productKey = next; i++; break;
|
|
44
47
|
case '--reason': out.reason = next; i++; break;
|
|
45
48
|
case '--yes': out.yes = true; break;
|
|
49
|
+
case '--operator': out.operator = next; i++; break;
|
|
46
50
|
case '--env': out.env = next; i++; break;
|
|
47
51
|
default:
|
|
48
52
|
if (a.startsWith('--')) throw new Error(`Unknown arg: ${a}`);
|
|
@@ -111,6 +115,7 @@ export async function runRevoke(argv: string[]): Promise<void> {
|
|
|
111
115
|
entitlementId: target.id,
|
|
112
116
|
refundReason: args.reason,
|
|
113
117
|
refundAmountCents: 0,
|
|
118
|
+
actorUserId: operatorActorId(args.operator ?? null),
|
|
114
119
|
});
|
|
115
120
|
console.log(`✓ Revoked entitlement (HTTP ${res.status}):`);
|
|
116
121
|
console.log(JSON.stringify(res.body, null, 2));
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { basename } from 'path';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
12
|
import { callBilling, validateEnvCnProd } from './billing-http';
|
|
13
|
+
import { operatorActorId } from './operator';
|
|
13
14
|
import { resolveTargetUser } from './grant-subscription';
|
|
14
15
|
|
|
15
16
|
const CREDITS_PER_USD = 700;
|
|
@@ -19,6 +20,7 @@ interface Parsed {
|
|
|
19
20
|
credits: number | null;
|
|
20
21
|
amountUsd: number | null;
|
|
21
22
|
description: string | null;
|
|
23
|
+
operator: string | null;
|
|
22
24
|
env: string;
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -36,6 +38,7 @@ Options:
|
|
|
36
38
|
--credits <n> Credits to grant (integer >= 1). Primary unit.
|
|
37
39
|
--amount <usd> Alt: grant by USD ($1 = ${CREDITS_PER_USD} credits). Provide exactly one of --credits / --amount.
|
|
38
40
|
--description <text> Description for audit trail (optional)
|
|
41
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
39
42
|
--env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
|
|
40
43
|
-h, --help Show this help
|
|
41
44
|
|
|
@@ -50,12 +53,14 @@ Examples:
|
|
|
50
53
|
let credits: number | null = null;
|
|
51
54
|
let amountUsd: number | null = null;
|
|
52
55
|
let description: string | null = null;
|
|
56
|
+
let operator: string | null = null;
|
|
53
57
|
let env = 'stage';
|
|
54
58
|
|
|
55
59
|
for (let i = 1; i < args.length; i++) {
|
|
56
60
|
if (args[i] === '--credits' && args[i + 1]) { credits = parseInt(args[++i], 10); }
|
|
57
61
|
else if (args[i] === '--amount' && args[i + 1]) { amountUsd = parseFloat(args[++i]); }
|
|
58
62
|
else if (args[i] === '--description' && args[i + 1]) { description = args[++i]; }
|
|
63
|
+
else if (args[i] === '--operator' && args[i + 1]) { operator = args[++i]; }
|
|
59
64
|
else if (args[i] === '--env' && args[i + 1]) { env = args[++i]; }
|
|
60
65
|
}
|
|
61
66
|
|
|
@@ -74,7 +79,7 @@ Examples:
|
|
|
74
79
|
}
|
|
75
80
|
validateEnvCnProd(env);
|
|
76
81
|
|
|
77
|
-
return { identifier, credits, amountUsd, description, env };
|
|
82
|
+
return { identifier, credits, amountUsd, description, operator, env };
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
async function main() {
|
|
@@ -83,7 +88,7 @@ async function main() {
|
|
|
83
88
|
console.warn('⚠️ optima-grant-balance 已更名为 optima-grant-credits(P15 钱包退役后回归积分)。请改用 `optima-grant-credits --credits <n>`;本别名仍可用,后续弃用。\n');
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
const { identifier, credits, amountUsd, description, env } = parseArgs(process.argv.slice(2));
|
|
91
|
+
const { identifier, credits, amountUsd, description, operator, env } = parseArgs(process.argv.slice(2));
|
|
87
92
|
|
|
88
93
|
const creditsDisplay = credits ?? Math.round((amountUsd as number) * CREDITS_PER_USD);
|
|
89
94
|
console.log(`\n🎁 Granting ${creditsDisplay} credits${amountUsd !== null ? ` ($${amountUsd.toFixed(2)})` : ''} to ${identifier} [${env.toUpperCase()}]\n`);
|
|
@@ -105,6 +110,7 @@ async function main() {
|
|
|
105
110
|
userId,
|
|
106
111
|
...amountField,
|
|
107
112
|
description: description ?? undefined,
|
|
113
|
+
actorUserId: operatorActorId(operator),
|
|
108
114
|
idempotencyKey: `dev-skills-grant:${randomUUID()}`,
|
|
109
115
|
},
|
|
110
116
|
);
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getUserById,
|
|
12
12
|
validateEnvCnProd,
|
|
13
13
|
} from './billing-http';
|
|
14
|
+
import { operatorActorId } from './operator';
|
|
14
15
|
|
|
15
16
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16
17
|
|
|
@@ -76,7 +77,7 @@ const PLANS_BY_ENV: Record<string, string[]> = {
|
|
|
76
77
|
'cn-stage': ['trial', 'starter', 'pro', 'enterprise', 'free'],
|
|
77
78
|
};
|
|
78
79
|
|
|
79
|
-
function parseArgs(args: string[]): { identifier: string; plan: string; months: number; env: string } {
|
|
80
|
+
function parseArgs(args: string[]): { identifier: string; plan: string; months: number; operator: string | null; env: string } {
|
|
80
81
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
81
82
|
console.log(`Usage: optima-grant-subscription <email|phone|userId> [options]
|
|
82
83
|
|
|
@@ -85,6 +86,7 @@ Options:
|
|
|
85
86
|
cn-prod/cn-stage additionally allow: free
|
|
86
87
|
(legacy *-cn ids are accepted and normalized to canonical)
|
|
87
88
|
--months <n> Duration in months (default: 1)
|
|
89
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
88
90
|
--env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
|
|
89
91
|
-h, --help Show this help`);
|
|
90
92
|
process.exit(0);
|
|
@@ -93,11 +95,13 @@ Options:
|
|
|
93
95
|
const identifier = args[0];
|
|
94
96
|
let plan: string | null = null;
|
|
95
97
|
let months = 1;
|
|
98
|
+
let operator: string | null = null;
|
|
96
99
|
let env = 'stage';
|
|
97
100
|
|
|
98
101
|
for (let i = 1; i < args.length; i++) {
|
|
99
102
|
if (args[i] === '--plan' && args[i + 1]) { plan = args[++i]; }
|
|
100
103
|
else if (args[i] === '--months' && args[i + 1]) { months = parseInt(args[++i], 10); }
|
|
104
|
+
else if (args[i] === '--operator' && args[i + 1]) { operator = args[++i]; }
|
|
101
105
|
else if (args[i] === '--env' && args[i + 1]) { env = args[++i]; }
|
|
102
106
|
}
|
|
103
107
|
|
|
@@ -113,7 +117,7 @@ Options:
|
|
|
113
117
|
}
|
|
114
118
|
if (months < 1) { console.error('Months must be >= 1'); process.exit(1); }
|
|
115
119
|
|
|
116
|
-
return { identifier, plan, months, env };
|
|
120
|
+
return { identifier, plan, months, operator, env };
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
/**
|
|
@@ -181,7 +185,7 @@ export async function resolveTargetUser(
|
|
|
181
185
|
}
|
|
182
186
|
|
|
183
187
|
async function main() {
|
|
184
|
-
const { identifier, plan, months, env } = parseArgs(process.argv.slice(2));
|
|
188
|
+
const { identifier, plan, months, operator, env } = parseArgs(process.argv.slice(2));
|
|
185
189
|
|
|
186
190
|
console.log(`\n🎁 Granting ${plan} subscription to ${identifier} (${classifyIdentifier(identifier)}) for ${months} month(s) [${env.toUpperCase()}]\n`);
|
|
187
191
|
|
|
@@ -198,7 +202,7 @@ async function main() {
|
|
|
198
202
|
weeklyTokenLimit: number;
|
|
199
203
|
expiresAt: string;
|
|
200
204
|
warning?: string;
|
|
201
|
-
}>(env, 'POST', '/api/billing/admin/grant-subscription', { userId, planId: plan, months });
|
|
205
|
+
}>(env, 'POST', '/api/billing/admin/grant-subscription', { userId, planId: plan, months, actorUserId: operatorActorId(operator) });
|
|
202
206
|
|
|
203
207
|
console.log(`✓ Subscription ${body.subscriptionId} (${body.planId})`);
|
|
204
208
|
console.log(`✓ Credits: ${body.credits.toLocaleString()} (expires ${body.expiresAt})`);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { userInfo } from 'os';
|
|
2
|
+
|
|
3
|
+
// #429(Owner 拍板 3):CLI 发放族自报操作者——informational,billing 信任 allowlist client
|
|
4
|
+
// 自述(actor 不参与授权);不带 --operator 时回退本机用户名。缺省链最终兜底在 billing
|
|
5
|
+
// 侧(channel=client_id)。格式:dev-skills:<name>。
|
|
6
|
+
export function operatorActorId(operatorFlag?: string | null): string {
|
|
7
|
+
const name = (operatorFlag ?? '').trim() || userInfo().username;
|
|
8
|
+
return `dev-skills:${name}`;
|
|
9
|
+
}
|
|
@@ -4,6 +4,7 @@ exports.runGrant = runGrant;
|
|
|
4
4
|
const billing_http_1 = require("../billing-http");
|
|
5
5
|
const confirm_prompt_1 = require("../confirm-prompt");
|
|
6
6
|
const grant_subscription_1 = require("../grant-subscription");
|
|
7
|
+
const operator_1 = require("../operator");
|
|
7
8
|
function parseArgs(argv) {
|
|
8
9
|
if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
|
|
9
10
|
console.log(`Usage: optima-entitlement grant <email|phone|userId> --product-key <slug> --justification "..." [options]
|
|
@@ -15,6 +16,7 @@ Required:
|
|
|
15
16
|
|
|
16
17
|
Optional:
|
|
17
18
|
--yes Skip prod/cn-prod confirmation prompt (no-op on stage / cn-stage)
|
|
19
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
18
20
|
--env stage|prod|cn-prod|cn-stage (default: stage)
|
|
19
21
|
|
|
20
22
|
Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy=<clientId>.`);
|
|
@@ -40,6 +42,10 @@ Hardcoded server-side: source=ADMIN_GRANT, priceCents=0, currency=USD, grantedBy
|
|
|
40
42
|
case '--yes':
|
|
41
43
|
out.yes = true;
|
|
42
44
|
break;
|
|
45
|
+
case '--operator':
|
|
46
|
+
out.operator = next;
|
|
47
|
+
i++;
|
|
48
|
+
break;
|
|
43
49
|
case '--env':
|
|
44
50
|
out.env = next;
|
|
45
51
|
i++;
|
|
@@ -72,6 +78,7 @@ async function runGrant(argv) {
|
|
|
72
78
|
userId,
|
|
73
79
|
productKey: args.productKey,
|
|
74
80
|
justification: args.justification,
|
|
81
|
+
actorUserId: (0, operator_1.operatorActorId)(args.operator ?? null),
|
|
75
82
|
});
|
|
76
83
|
console.log(`✓ Granted entitlement (HTTP ${res.status}):`);
|
|
77
84
|
console.log(JSON.stringify(res.body, null, 2));
|
|
@@ -4,6 +4,7 @@ exports.runRevoke = runRevoke;
|
|
|
4
4
|
const billing_http_1 = require("../billing-http");
|
|
5
5
|
const confirm_prompt_1 = require("../confirm-prompt");
|
|
6
6
|
const grant_subscription_1 = require("../grant-subscription");
|
|
7
|
+
const operator_1 = require("../operator");
|
|
7
8
|
function parseArgs(argv) {
|
|
8
9
|
if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
|
|
9
10
|
console.log(`Usage: optima-entitlement revoke <email|phone|userId> --product-key <slug> --reason "..." [options]
|
|
@@ -15,6 +16,7 @@ Required:
|
|
|
15
16
|
|
|
16
17
|
Optional:
|
|
17
18
|
--yes Skip prod/cn-prod confirmation prompt (no-op on stage / cn-stage)
|
|
19
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
18
20
|
--env stage|prod|cn-prod|cn-stage (default: stage)
|
|
19
21
|
|
|
20
22
|
Refuses non-ADMIN_GRANT sources (PAYMENT, PARTNER) with source-specific
|
|
@@ -41,6 +43,10 @@ error pointing to the right reversal flow.`);
|
|
|
41
43
|
case '--yes':
|
|
42
44
|
out.yes = true;
|
|
43
45
|
break;
|
|
46
|
+
case '--operator':
|
|
47
|
+
out.operator = next;
|
|
48
|
+
i++;
|
|
49
|
+
break;
|
|
44
50
|
case '--env':
|
|
45
51
|
out.env = next;
|
|
46
52
|
i++;
|
|
@@ -102,6 +108,7 @@ async function runRevoke(argv) {
|
|
|
102
108
|
entitlementId: target.id,
|
|
103
109
|
refundReason: args.reason,
|
|
104
110
|
refundAmountCents: 0,
|
|
111
|
+
actorUserId: (0, operator_1.operatorActorId)(args.operator ?? null),
|
|
105
112
|
});
|
|
106
113
|
console.log(`✓ Revoked entitlement (HTTP ${res.status}):`);
|
|
107
114
|
console.log(JSON.stringify(res.body, null, 2));
|
|
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
11
11
|
const path_1 = require("path");
|
|
12
12
|
const crypto_1 = require("crypto");
|
|
13
13
|
const billing_http_1 = require("./billing-http");
|
|
14
|
+
const operator_1 = require("./operator");
|
|
14
15
|
const grant_subscription_1 = require("./grant-subscription");
|
|
15
16
|
const CREDITS_PER_USD = 700;
|
|
16
17
|
function parseArgs(args) {
|
|
@@ -27,6 +28,7 @@ Options:
|
|
|
27
28
|
--credits <n> Credits to grant (integer >= 1). Primary unit.
|
|
28
29
|
--amount <usd> Alt: grant by USD ($1 = ${CREDITS_PER_USD} credits). Provide exactly one of --credits / --amount.
|
|
29
30
|
--description <text> Description for audit trail (optional)
|
|
31
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
30
32
|
--env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
|
|
31
33
|
-h, --help Show this help
|
|
32
34
|
|
|
@@ -40,6 +42,7 @@ Examples:
|
|
|
40
42
|
let credits = null;
|
|
41
43
|
let amountUsd = null;
|
|
42
44
|
let description = null;
|
|
45
|
+
let operator = null;
|
|
43
46
|
let env = 'stage';
|
|
44
47
|
for (let i = 1; i < args.length; i++) {
|
|
45
48
|
if (args[i] === '--credits' && args[i + 1]) {
|
|
@@ -51,6 +54,9 @@ Examples:
|
|
|
51
54
|
else if (args[i] === '--description' && args[i + 1]) {
|
|
52
55
|
description = args[++i];
|
|
53
56
|
}
|
|
57
|
+
else if (args[i] === '--operator' && args[i + 1]) {
|
|
58
|
+
operator = args[++i];
|
|
59
|
+
}
|
|
54
60
|
else if (args[i] === '--env' && args[i + 1]) {
|
|
55
61
|
env = args[++i];
|
|
56
62
|
}
|
|
@@ -69,14 +75,14 @@ Examples:
|
|
|
69
75
|
process.exit(1);
|
|
70
76
|
}
|
|
71
77
|
(0, billing_http_1.validateEnvCnProd)(env);
|
|
72
|
-
return { identifier, credits, amountUsd, description, env };
|
|
78
|
+
return { identifier, credits, amountUsd, description, operator, env };
|
|
73
79
|
}
|
|
74
80
|
async function main() {
|
|
75
81
|
// Deprecation notice when invoked via the legacy alias `optima-grant-balance`.
|
|
76
82
|
if ((0, path_1.basename)(process.argv[1] || '').includes('grant-balance')) {
|
|
77
83
|
console.warn('⚠️ optima-grant-balance 已更名为 optima-grant-credits(P15 钱包退役后回归积分)。请改用 `optima-grant-credits --credits <n>`;本别名仍可用,后续弃用。\n');
|
|
78
84
|
}
|
|
79
|
-
const { identifier, credits, amountUsd, description, env } = parseArgs(process.argv.slice(2));
|
|
85
|
+
const { identifier, credits, amountUsd, description, operator, env } = parseArgs(process.argv.slice(2));
|
|
80
86
|
const creditsDisplay = credits ?? Math.round(amountUsd * CREDITS_PER_USD);
|
|
81
87
|
console.log(`\n🎁 Granting ${creditsDisplay} credits${amountUsd !== null ? ` ($${amountUsd.toFixed(2)})` : ''} to ${identifier} [${env.toUpperCase()}]\n`);
|
|
82
88
|
if (description)
|
|
@@ -93,6 +99,7 @@ async function main() {
|
|
|
93
99
|
userId,
|
|
94
100
|
...amountField,
|
|
95
101
|
description: description ?? undefined,
|
|
102
|
+
actorUserId: (0, operator_1.operatorActorId)(operator),
|
|
96
103
|
idempotencyKey: `dev-skills-grant:${(0, crypto_1.randomUUID)()}`,
|
|
97
104
|
});
|
|
98
105
|
console.log(`✓ Granted ${body.credits} credits (lot ${body.lotId})`);
|
|
@@ -10,6 +10,7 @@ exports.resolveTargetUser = resolveTargetUser;
|
|
|
10
10
|
// 业务体:supersede 旧授予 + 实得 credits + token quota,重试双发不双倍)。
|
|
11
11
|
const db_utils_1 = require("./db-utils");
|
|
12
12
|
const billing_http_1 = require("./billing-http");
|
|
13
|
+
const operator_1 = require("./operator");
|
|
13
14
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14
15
|
/**
|
|
15
16
|
* Auto-detect what kind of identifier the operator passed. gateway#923: a
|
|
@@ -79,6 +80,7 @@ Options:
|
|
|
79
80
|
cn-prod/cn-stage additionally allow: free
|
|
80
81
|
(legacy *-cn ids are accepted and normalized to canonical)
|
|
81
82
|
--months <n> Duration in months (default: 1)
|
|
83
|
+
--operator <name> Operator self-report for billing audit (default: local username)
|
|
82
84
|
--env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
|
|
83
85
|
-h, --help Show this help`);
|
|
84
86
|
process.exit(0);
|
|
@@ -86,6 +88,7 @@ Options:
|
|
|
86
88
|
const identifier = args[0];
|
|
87
89
|
let plan = null;
|
|
88
90
|
let months = 1;
|
|
91
|
+
let operator = null;
|
|
89
92
|
let env = 'stage';
|
|
90
93
|
for (let i = 1; i < args.length; i++) {
|
|
91
94
|
if (args[i] === '--plan' && args[i + 1]) {
|
|
@@ -94,6 +97,9 @@ Options:
|
|
|
94
97
|
else if (args[i] === '--months' && args[i + 1]) {
|
|
95
98
|
months = parseInt(args[++i], 10);
|
|
96
99
|
}
|
|
100
|
+
else if (args[i] === '--operator' && args[i + 1]) {
|
|
101
|
+
operator = args[++i];
|
|
102
|
+
}
|
|
97
103
|
else if (args[i] === '--env' && args[i + 1]) {
|
|
98
104
|
env = args[++i];
|
|
99
105
|
}
|
|
@@ -112,7 +118,7 @@ Options:
|
|
|
112
118
|
console.error('Months must be >= 1');
|
|
113
119
|
process.exit(1);
|
|
114
120
|
}
|
|
115
|
-
return { identifier, plan, months, env };
|
|
121
|
+
return { identifier, plan, months, operator, env };
|
|
116
122
|
}
|
|
117
123
|
/**
|
|
118
124
|
* Resolve a CLI identifier (`<email|phone|userId>`) to a userId + verified
|
|
@@ -167,12 +173,12 @@ async function resolveTargetUser(env, identifier) {
|
|
|
167
173
|
return { userId, kind, identity: { phone: null, email: identifier } };
|
|
168
174
|
}
|
|
169
175
|
async function main() {
|
|
170
|
-
const { identifier, plan, months, env } = parseArgs(process.argv.slice(2));
|
|
176
|
+
const { identifier, plan, months, operator, env } = parseArgs(process.argv.slice(2));
|
|
171
177
|
console.log(`\n🎁 Granting ${plan} subscription to ${identifier} (${classifyIdentifier(identifier)}) for ${months} month(s) [${env.toUpperCase()}]\n`);
|
|
172
178
|
// Shared resolver: classify → resolve → reverse-verify echo → phone-assert
|
|
173
179
|
// (cn-prod/cn-stage), or email-only via SSH tunnel (AWS). See resolveTargetUser.
|
|
174
180
|
const { userId, identity } = await resolveTargetUser(env, identifier);
|
|
175
|
-
const { body } = await (0, billing_http_1.callBilling)(env, 'POST', '/api/billing/admin/grant-subscription', { userId, planId: plan, months });
|
|
181
|
+
const { body } = await (0, billing_http_1.callBilling)(env, 'POST', '/api/billing/admin/grant-subscription', { userId, planId: plan, months, actorUserId: (0, operator_1.operatorActorId)(operator) });
|
|
176
182
|
console.log(`✓ Subscription ${body.subscriptionId} (${body.planId})`);
|
|
177
183
|
console.log(`✓ Credits: ${body.credits.toLocaleString()} (expires ${body.expiresAt})`);
|
|
178
184
|
console.log(`✓ Token limits: session ${body.sessionTokenLimit.toLocaleString()} / weekly ${body.weeklyTokenLimit.toLocaleString()}`);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.operatorActorId = operatorActorId;
|
|
4
|
+
const os_1 = require("os");
|
|
5
|
+
// #429(Owner 拍板 3):CLI 发放族自报操作者——informational,billing 信任 allowlist client
|
|
6
|
+
// 自述(actor 不参与授权);不带 --operator 时回退本机用户名。缺省链最终兜底在 billing
|
|
7
|
+
// 侧(channel=client_id)。格式:dev-skills:<name>。
|
|
8
|
+
function operatorActorId(operatorFlag) {
|
|
9
|
+
const name = (operatorFlag ?? '').trim() || (0, os_1.userInfo)().username;
|
|
10
|
+
return `dev-skills:${name}`;
|
|
11
|
+
}
|