ckweb-cli 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/index.js +165 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,15 +72,19 @@ ckweb seo traffic "example.com"
|
|
|
72
72
|
| `referrals dashboard` | View referral stats |
|
|
73
73
|
| `referrals request-payout` | Request payout |
|
|
74
74
|
| `referrals payout-history` | View payout history |
|
|
75
|
+
| `referrals leaderboard\|tiers\|resolve\|track\|referees` | Referral discovery, tracking, and referee lookup |
|
|
75
76
|
| `payout profile` | View payout profiles |
|
|
76
77
|
| `payout create-profile` | Create payout profile |
|
|
77
78
|
| `payout delete-profile` | Delete payout profile |
|
|
78
79
|
|
|
80
|
+
Payout profile note: current `claudekit-web` payout profile endpoints are browser-session routes. Full API-key automation for payout profile and VNeID conversion flows requires a backend API-key contract first.
|
|
81
|
+
|
|
79
82
|
### Content
|
|
80
83
|
| Command | Description |
|
|
81
84
|
|---------|-------------|
|
|
82
85
|
| `blog list` | List blog articles |
|
|
83
86
|
| `blog get <id>` | Get article details |
|
|
87
|
+
| `blog regenerate-html --article <id>\|--all` | Regenerate article HTML (admin key) |
|
|
84
88
|
| `releases list [--tab]` | List product releases |
|
|
85
89
|
|
|
86
90
|
### Proxy Services (YouTube, Web, SEO)
|
|
@@ -97,12 +101,13 @@ ckweb seo traffic "example.com"
|
|
|
97
101
|
| `pricing exchange-rate` | Get USD/VND rate |
|
|
98
102
|
| `pricing early-access` | Early access status |
|
|
99
103
|
| `loyalty eligibility` | Check loyalty eligibility |
|
|
104
|
+
| `premium validate` | Validate premium access for current API key |
|
|
100
105
|
| `stats users` | Platform user count |
|
|
101
106
|
|
|
102
107
|
### Other
|
|
103
108
|
| Command | Description |
|
|
104
109
|
|---------|-------------|
|
|
105
|
-
| `github invite\|revoke` | GitHub repo access |
|
|
110
|
+
| `github validate\|invite\|revoke` | GitHub username validation and repo access |
|
|
106
111
|
| `newsletter subscribe\|unsubscribe` | Newsletter management |
|
|
107
112
|
| `waitlist join` | Join product waitlist |
|
|
108
113
|
| `health` | Check API health |
|
|
@@ -122,9 +127,13 @@ ckweb seo traffic "example.com"
|
|
|
122
127
|
| `admin invites` | list, create, get, delete, resend, accept, validate |
|
|
123
128
|
| `admin admins` | list, get, promote, demote, permissions |
|
|
124
129
|
| `admin discounts` | list, create, update, delete, sync-status, export |
|
|
125
|
-
| `admin discount-groups` | list, get, create, update, delete |
|
|
130
|
+
| `admin discount-groups` | list, get, create (--generate), generate, update, delete |
|
|
126
131
|
| `admin tiers` | list |
|
|
127
132
|
|
|
133
|
+
Admin payout note: backend payout PII/actions such as `/api/admin/payouts`, `/api/admin/payout-requests`, and `/api/admin/payout-vneid-requests` intentionally require an interactive browser admin session. The CLI documents those as unsupported by API-key auth instead of pretending they are safe automation commands.
|
|
134
|
+
|
|
135
|
+
Browser-session note: several dashboard endpoints in `claudekit-web` still use cookie session auth (`getCurrentUser()`), including user API-key management, teams, loyalty checkout, GDPR account actions, and referral payout requests. The coverage test tracks these as explicit backend-contract gaps, not working API-key CLI coverage.
|
|
136
|
+
|
|
128
137
|
## Authentication
|
|
129
138
|
|
|
130
139
|
Two types of API keys:
|
package/dist/index.js
CHANGED
|
@@ -211,7 +211,7 @@ function buildUrl(path2, params) {
|
|
|
211
211
|
}
|
|
212
212
|
function buildHeaders(apiKey, hasBody) {
|
|
213
213
|
const headers = {
|
|
214
|
-
"User-Agent": `ckweb-cli/${"0.
|
|
214
|
+
"User-Agent": `ckweb-cli/${"0.8.0"}`
|
|
215
215
|
};
|
|
216
216
|
if (hasBody) {
|
|
217
217
|
headers["Content-Type"] = "application/json";
|
|
@@ -823,6 +823,22 @@ var PAYOUT_COLUMNS = [
|
|
|
823
823
|
{ key: "status", header: "Status", width: 12 },
|
|
824
824
|
{ key: "createdAt", header: "Created", width: 22 }
|
|
825
825
|
];
|
|
826
|
+
var REFEREE_STATUSES = ["all", "pending", "approved", "paid", "cancelled"];
|
|
827
|
+
var REFEREE_SORTS = ["createdAt", "amount", "status"];
|
|
828
|
+
var REFEREE_ORDERS = ["asc", "desc"];
|
|
829
|
+
function parseBoundedInteger(value, label, min, max) {
|
|
830
|
+
const parsed = Number(value);
|
|
831
|
+
if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < min || parsed > max) {
|
|
832
|
+
throw new CliError(`${label} must be an integer between ${min} and ${max}`);
|
|
833
|
+
}
|
|
834
|
+
return parsed;
|
|
835
|
+
}
|
|
836
|
+
function assertOneOf(value, allowed, label) {
|
|
837
|
+
if (!allowed.includes(value)) {
|
|
838
|
+
throw new CliError(`${label} must be one of: ${allowed.join(", ")}`);
|
|
839
|
+
}
|
|
840
|
+
return value;
|
|
841
|
+
}
|
|
826
842
|
function registerReferralsCommand(program2) {
|
|
827
843
|
const referrals = program2.command("referrals").description("Referral program management");
|
|
828
844
|
referrals.command("dashboard").description("View your referral dashboard").action(async function() {
|
|
@@ -853,6 +869,69 @@ function registerReferralsCommand(program2) {
|
|
|
853
869
|
handleError(err);
|
|
854
870
|
}
|
|
855
871
|
});
|
|
872
|
+
referrals.command("leaderboard").description("View public referral leaderboard").action(async function() {
|
|
873
|
+
try {
|
|
874
|
+
const data = await fetchApi("GET", "/referrals/leaderboard", { noAuth: true });
|
|
875
|
+
formatOutput(data, getOutputOpts(this));
|
|
876
|
+
} catch (err) {
|
|
877
|
+
handleError(err);
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
referrals.command("tiers").description("List public referral tiers").action(async function() {
|
|
881
|
+
try {
|
|
882
|
+
const data = await fetchApi("GET", "/referrals/tiers", { noAuth: true });
|
|
883
|
+
formatOutput(data, getOutputOpts(this));
|
|
884
|
+
} catch (err) {
|
|
885
|
+
handleError(err);
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
referrals.command("resolve <code>").description("Resolve a referral code").action(async function(code) {
|
|
889
|
+
try {
|
|
890
|
+
const data = await fetchApi("GET", "/referrals/resolve", {
|
|
891
|
+
noAuth: true,
|
|
892
|
+
params: { code }
|
|
893
|
+
});
|
|
894
|
+
formatOutput(data, getOutputOpts(this));
|
|
895
|
+
} catch (err) {
|
|
896
|
+
handleError(err);
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
referrals.command("track <code>").description("Track a referral click").action(async function(code) {
|
|
900
|
+
try {
|
|
901
|
+
const data = await fetchApi("GET", "/referrals/track", {
|
|
902
|
+
noAuth: true,
|
|
903
|
+
params: { code }
|
|
904
|
+
});
|
|
905
|
+
formatOutput(data, getOutputOpts(this));
|
|
906
|
+
} catch (err) {
|
|
907
|
+
handleError(err);
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
referrals.command("referees").description("List referees attributed to your referral account").option("--status <status>", "Filter by commission status", "all").option("--sort <field>", "Sort by createdAt, amount, or status", "createdAt").option("--order <direction>", "Sort direction: asc or desc", "desc").option("--order-ref <id>", "Filter by 8-character order display ID").option("--limit <n>", "Page size", "50").option("--offset <n>", "Offset for pagination", "0").action(async function() {
|
|
911
|
+
try {
|
|
912
|
+
ensureAuth();
|
|
913
|
+
const opts = this.opts();
|
|
914
|
+
const limit = parseBoundedInteger(opts.limit, "--limit", 1, 100);
|
|
915
|
+
const offset = parseBoundedInteger(opts.offset, "--offset", 0, 5e3);
|
|
916
|
+
const params = {
|
|
917
|
+
status: assertOneOf(opts.status, REFEREE_STATUSES, "--status"),
|
|
918
|
+
sort: assertOneOf(opts.sort, REFEREE_SORTS, "--sort"),
|
|
919
|
+
order: assertOneOf(opts.order, REFEREE_ORDERS, "--order"),
|
|
920
|
+
limit: String(limit),
|
|
921
|
+
offset: String(offset)
|
|
922
|
+
};
|
|
923
|
+
if (opts.orderRef) {
|
|
924
|
+
if (!/^[0-9a-fA-F]{8}$/.test(opts.orderRef)) {
|
|
925
|
+
throw new CliError("--order-ref must be an 8-character order display ID");
|
|
926
|
+
}
|
|
927
|
+
params["orderRef"] = opts.orderRef.toLowerCase();
|
|
928
|
+
}
|
|
929
|
+
const data = await fetchApi("GET", "/referrals/referees", { params });
|
|
930
|
+
formatOutput(data, getOutputOpts(this));
|
|
931
|
+
} catch (err) {
|
|
932
|
+
handleError(err);
|
|
933
|
+
}
|
|
934
|
+
});
|
|
856
935
|
}
|
|
857
936
|
|
|
858
937
|
// src/commands/blog.ts
|
|
@@ -881,6 +960,26 @@ function registerBlogCommand(program2) {
|
|
|
881
960
|
handleError(err);
|
|
882
961
|
}
|
|
883
962
|
});
|
|
963
|
+
blog.command("regenerate-html").description("Regenerate stored HTML for missing article content").option("--article <id>", "Regenerate one article by ID").option("--all", "Regenerate all articles missing HTML").action(async function() {
|
|
964
|
+
try {
|
|
965
|
+
ensureAdminAuth();
|
|
966
|
+
const opts = this.opts();
|
|
967
|
+
if (!opts.article && !opts.all) {
|
|
968
|
+
throw new CliError("Provide --article <id> or explicit --all.");
|
|
969
|
+
}
|
|
970
|
+
if (opts.article && opts.all) {
|
|
971
|
+
throw new CliError("Use either --article <id> or --all, not both.");
|
|
972
|
+
}
|
|
973
|
+
const body = opts.article ? { articleId: opts.article } : {};
|
|
974
|
+
const data = await fetchApi("POST", "/blog/regenerate-html", {
|
|
975
|
+
adminAuth: true,
|
|
976
|
+
body
|
|
977
|
+
});
|
|
978
|
+
formatOutput(data, getOutputOpts(this));
|
|
979
|
+
} catch (err) {
|
|
980
|
+
handleError(err);
|
|
981
|
+
}
|
|
982
|
+
});
|
|
884
983
|
}
|
|
885
984
|
|
|
886
985
|
// src/commands/health.ts
|
|
@@ -987,6 +1086,17 @@ function registerCheckoutCommand(program2) {
|
|
|
987
1086
|
import * as readline3 from "readline/promises";
|
|
988
1087
|
function registerGithubCommand(program2) {
|
|
989
1088
|
const github = program2.command("github").description("GitHub repository access");
|
|
1089
|
+
github.command("validate <username>").description("Validate a GitHub username").action(async function(username) {
|
|
1090
|
+
try {
|
|
1091
|
+
const data = await fetchApi("GET", "/github/validate", {
|
|
1092
|
+
noAuth: true,
|
|
1093
|
+
params: { username }
|
|
1094
|
+
});
|
|
1095
|
+
formatOutput(data, getOutputOpts(this));
|
|
1096
|
+
} catch (err) {
|
|
1097
|
+
handleError(err);
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
990
1100
|
github.command("invite").description("Grant GitHub repository access via license").requiredOption("--license <id>", "License ID").action(async function() {
|
|
991
1101
|
try {
|
|
992
1102
|
ensureAuth();
|
|
@@ -1154,6 +1264,20 @@ function registerPricingCommand(program2) {
|
|
|
1154
1264
|
});
|
|
1155
1265
|
}
|
|
1156
1266
|
|
|
1267
|
+
// src/commands/premium.ts
|
|
1268
|
+
function registerPremiumCommand(program2) {
|
|
1269
|
+
const premium = program2.command("premium").description("Premium license utilities");
|
|
1270
|
+
premium.command("validate").description("Validate whether the current API key has premium access").action(async function() {
|
|
1271
|
+
try {
|
|
1272
|
+
ensureAuth();
|
|
1273
|
+
const data = await fetchApi("GET", "/premium/validate");
|
|
1274
|
+
formatOutput(data, getOutputOpts(this));
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
handleError(err);
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1157
1281
|
// src/commands/refunds.ts
|
|
1158
1282
|
import * as readline5 from "readline/promises";
|
|
1159
1283
|
function registerRefundsCommand(program2) {
|
|
@@ -2971,6 +3095,14 @@ var GROUP_COLUMNS = [
|
|
|
2971
3095
|
{ key: "codesUsed", header: "Used", width: 8 },
|
|
2972
3096
|
{ key: "createdAt", header: "Created", width: 22 }
|
|
2973
3097
|
];
|
|
3098
|
+
function parseGenerateCount(value, optionName = "--count") {
|
|
3099
|
+
if (value === void 0) return void 0;
|
|
3100
|
+
const count = Number(value);
|
|
3101
|
+
if (!Number.isInteger(count) || count < 1 || count > 100) {
|
|
3102
|
+
throw new CliError(`${optionName} must be an integer from 1 to 100`);
|
|
3103
|
+
}
|
|
3104
|
+
return count;
|
|
3105
|
+
}
|
|
2974
3106
|
function buildGroupBody(opts) {
|
|
2975
3107
|
const body = {};
|
|
2976
3108
|
if (opts.name !== void 0) body["name"] = opts.name;
|
|
@@ -3011,6 +3143,12 @@ function buildGroupBody(opts) {
|
|
|
3011
3143
|
if (opts.ends !== void 0) body["endsAt"] = opts.ends;
|
|
3012
3144
|
return body;
|
|
3013
3145
|
}
|
|
3146
|
+
async function generateDiscountGroupCodes(id, count) {
|
|
3147
|
+
return fetchApi("POST", `/admin/discount-groups/${id}/codes`, {
|
|
3148
|
+
adminAuth: true,
|
|
3149
|
+
body: { count }
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3014
3152
|
function registerAdminDiscountGroupsCommand(admin) {
|
|
3015
3153
|
const groups = admin.command("discount-groups").description("Discount group management");
|
|
3016
3154
|
groups.command("list").description("List discount groups").option("--query <text>", "Search by name or prefix").option("--page <n>", "Page number", "1").option("--limit <n>", "Page size", "50").action(async function() {
|
|
@@ -3038,15 +3176,37 @@ function registerAdminDiscountGroupsCommand(admin) {
|
|
|
3038
3176
|
handleError(err);
|
|
3039
3177
|
}
|
|
3040
3178
|
});
|
|
3041
|
-
groups.command("create").description("Create a discount group template").requiredOption("--name <name>", "Group name").requiredOption("--prefix <prefix>", "Code prefix (3-20 chars, uppercased)").requiredOption("--type <percentage|fixed>", "Discount type").option("--amount <n>", "Amount in cents (fixed type)").option("--basis-points <n>", "Basis points (percentage type, 0..10000)").option("--duration <once|repeating|forever>", "Discount duration").option("--duration-in-months <n>", "Months for repeating duration (1..24)").option("--max-redemptions-per-code <n>", "Per-code redemption cap").option("--starts <date>", "Start date (ISO 8601)").option("--ends <date>", "End date (ISO 8601)").action(async function() {
|
|
3179
|
+
groups.command("create").description("Create a discount group template").requiredOption("--name <name>", "Group name").requiredOption("--prefix <prefix>", "Code prefix (3-20 chars, uppercased)").requiredOption("--type <percentage|fixed>", "Discount type").option("--amount <n>", "Amount in cents (fixed type)").option("--basis-points <n>", "Basis points (percentage type, 0..10000)").option("--duration <once|repeating|forever>", "Discount duration").option("--duration-in-months <n>", "Months for repeating duration (1..24)").option("--max-redemptions-per-code <n>", "Per-code redemption cap").option("--starts <date>", "Start date (ISO 8601)").option("--ends <date>", "End date (ISO 8601)").option("--generate <n>", "Generate N discount codes after creating the group (1..100)").action(async function() {
|
|
3042
3180
|
try {
|
|
3043
3181
|
ensureAdminAuth();
|
|
3044
|
-
const
|
|
3182
|
+
const opts = this.opts();
|
|
3183
|
+
const generateCount = parseGenerateCount(opts.generate, "--generate");
|
|
3184
|
+
const body = buildGroupBody(opts);
|
|
3045
3185
|
const data = await fetchApi("POST", "/admin/discount-groups", {
|
|
3046
3186
|
adminAuth: true,
|
|
3047
3187
|
body
|
|
3048
3188
|
});
|
|
3049
3189
|
printSuccess(`Discount group created: ${data?.group?.id ?? ""}`);
|
|
3190
|
+
if (generateCount !== void 0) {
|
|
3191
|
+
const id = data?.group?.id;
|
|
3192
|
+
if (!id) throw new CliError("Backend did not return a discount group ID for code generation.");
|
|
3193
|
+
const generated = await generateDiscountGroupCodes(id, generateCount);
|
|
3194
|
+
printSuccess(`Generated ${generated.generated} discount code(s).`);
|
|
3195
|
+
formatOutput({ ...data, ...generated }, getOutputOpts(this));
|
|
3196
|
+
return;
|
|
3197
|
+
}
|
|
3198
|
+
formatOutput(data, getOutputOpts(this));
|
|
3199
|
+
} catch (err) {
|
|
3200
|
+
handleError(err);
|
|
3201
|
+
}
|
|
3202
|
+
});
|
|
3203
|
+
groups.command("generate <id>").description("Generate discount codes for a group").requiredOption("--count <n>", "Number of codes to generate (1..100)").action(async function(id) {
|
|
3204
|
+
try {
|
|
3205
|
+
ensureAdminAuth();
|
|
3206
|
+
validateId(id, "discount group ID");
|
|
3207
|
+
const count = parseGenerateCount(this.opts().count);
|
|
3208
|
+
const data = await generateDiscountGroupCodes(id, count);
|
|
3209
|
+
printSuccess(`Generated ${data.generated} discount code(s).`);
|
|
3050
3210
|
formatOutput(data, getOutputOpts(this));
|
|
3051
3211
|
} catch (err) {
|
|
3052
3212
|
handleError(err);
|
|
@@ -3133,7 +3293,7 @@ function registerAdminCommand(program2) {
|
|
|
3133
3293
|
// src/index.ts
|
|
3134
3294
|
loadDotenvFiles();
|
|
3135
3295
|
var program = new Command();
|
|
3136
|
-
program.name("ckweb").description("CLI for interacting with ClaudeKit.cc API").version("0.
|
|
3296
|
+
program.name("ckweb").description("CLI for interacting with ClaudeKit.cc API").version("0.8.0");
|
|
3137
3297
|
program.option("--json", "Output as JSON").option("--table", "Output as table").option("--quiet", "Minimal output");
|
|
3138
3298
|
registerAuthCommand(program);
|
|
3139
3299
|
registerHealthCommand(program);
|
|
@@ -3147,6 +3307,7 @@ registerLoyaltyCommand(program);
|
|
|
3147
3307
|
registerNewsletterCommand(program);
|
|
3148
3308
|
registerPayoutCommand(program);
|
|
3149
3309
|
registerPricingCommand(program);
|
|
3310
|
+
registerPremiumCommand(program);
|
|
3150
3311
|
registerRefundsCommand(program);
|
|
3151
3312
|
registerReleasesCommand(program);
|
|
3152
3313
|
registerStatsCommand(program);
|