@whop/cli 0.0.6 → 0.2.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 +31 -91
- package/dist/{api-XJ22R3CT.js → api-U7WU3JXH.js} +5 -6
- package/dist/{chunk-Z7WDX64Z.js → chunk-2Y4EKBOD.js} +6171 -2207
- package/dist/chunk-2Y4EKBOD.js.map +1 -0
- package/dist/{chunk-CXNQ6UGC.js → chunk-KFCNNWPI.js} +7 -4
- package/dist/{chunk-CXNQ6UGC.js.map → chunk-KFCNNWPI.js.map} +1 -1
- package/dist/{dist-FTK7GWLH.js → dist-2MZJLO2W.js} +2 -3
- package/dist/{dist-FTK7GWLH.js.map → dist-2MZJLO2W.js.map} +1 -1
- package/dist/index.js +716 -200
- package/dist/index.js.map +1 -1
- package/package.json +60 -60
- package/dist/chunk-MELVTXNG.js +0 -24
- package/dist/chunk-MELVTXNG.js.map +0 -1
- package/dist/chunk-Z7WDX64Z.js.map +0 -1
- package/dist/dist-RLB3XRTU.js +0 -7296
- package/dist/dist-RLB3XRTU.js.map +0 -1
- /package/dist/{api-XJ22R3CT.js.map → api-U7WU3JXH.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -8,9 +8,12 @@ import {
|
|
|
8
8
|
Profile,
|
|
9
9
|
PromptCancelledError,
|
|
10
10
|
apiBaseUrl,
|
|
11
|
+
buildTarget,
|
|
11
12
|
chooseAuthMethod,
|
|
13
|
+
configDir,
|
|
12
14
|
createWhopFetch,
|
|
13
15
|
deriveProfileName,
|
|
16
|
+
detectChannel,
|
|
14
17
|
filterSpecByTag,
|
|
15
18
|
getActiveProfile,
|
|
16
19
|
getSecret,
|
|
@@ -22,17 +25,17 @@ import {
|
|
|
22
25
|
readConfig,
|
|
23
26
|
removeAllProfiles,
|
|
24
27
|
removeProfile,
|
|
25
|
-
|
|
28
|
+
resolveActiveAccountId,
|
|
26
29
|
selectProfile,
|
|
30
|
+
setActiveProfileAccount,
|
|
27
31
|
startSpinner,
|
|
28
32
|
switchProfile,
|
|
29
33
|
upsertProfile,
|
|
30
34
|
validateApiKey
|
|
31
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-2Y4EKBOD.js";
|
|
32
36
|
import {
|
|
33
37
|
external_exports
|
|
34
|
-
} from "./chunk-
|
|
35
|
-
import "./chunk-MELVTXNG.js";
|
|
38
|
+
} from "./chunk-KFCNNWPI.js";
|
|
36
39
|
|
|
37
40
|
// src/api/groups.json
|
|
38
41
|
var groups_default = [
|
|
@@ -92,6 +95,14 @@ var groups_default = [
|
|
|
92
95
|
"referrals",
|
|
93
96
|
"Referrals"
|
|
94
97
|
],
|
|
98
|
+
[
|
|
99
|
+
"people",
|
|
100
|
+
"People"
|
|
101
|
+
],
|
|
102
|
+
[
|
|
103
|
+
"events",
|
|
104
|
+
"Events"
|
|
105
|
+
],
|
|
95
106
|
[
|
|
96
107
|
"ads",
|
|
97
108
|
"Ads"
|
|
@@ -118,7 +129,7 @@ var groups_default = [
|
|
|
118
129
|
var loginOptions = external_exports.object({
|
|
119
130
|
apiKey: external_exports.string().optional().describe("API key for non-interactive login (skips the prompt)"),
|
|
120
131
|
profile: external_exports.string().optional().describe("Name for this profile (defaults to the account route)"),
|
|
121
|
-
|
|
132
|
+
accountId: external_exports.string().optional().describe(
|
|
122
133
|
"Business account to act on behalf of (biz_xxx) \u2014 for OAuth users who manage more than one"
|
|
123
134
|
)
|
|
124
135
|
});
|
|
@@ -183,7 +194,7 @@ async function performLogin(input) {
|
|
|
183
194
|
const { profileName, identity: identity2, existed: existed2 } = await performOAuthLogin({
|
|
184
195
|
profile: input.profile,
|
|
185
196
|
agent: input.agent,
|
|
186
|
-
|
|
197
|
+
accountId: input.accountId
|
|
187
198
|
});
|
|
188
199
|
return {
|
|
189
200
|
ok: true,
|
|
@@ -252,7 +263,7 @@ async function loginAdapter(c2) {
|
|
|
252
263
|
agent: c2.agent,
|
|
253
264
|
apiKey: c2.options.apiKey ?? c2.env.WHOP_API_KEY,
|
|
254
265
|
profile: c2.options.profile,
|
|
255
|
-
|
|
266
|
+
accountId: c2.options.accountId
|
|
256
267
|
});
|
|
257
268
|
} catch (error) {
|
|
258
269
|
if (error instanceof PromptCancelledError) {
|
|
@@ -277,14 +288,14 @@ async function loginAdapter(c2) {
|
|
|
277
288
|
}
|
|
278
289
|
]
|
|
279
290
|
};
|
|
280
|
-
} else if (result.code === "
|
|
291
|
+
} else if (result.code === "MULTIPLE_ACCOUNTS" || result.code === "ACCOUNT_NOT_FOUND") {
|
|
281
292
|
cta = {
|
|
282
293
|
description: "Choose which business account to use:",
|
|
283
294
|
commands: [
|
|
284
295
|
{
|
|
285
296
|
command: "auth login",
|
|
286
|
-
options: {
|
|
287
|
-
description: "Pass --
|
|
297
|
+
options: { accountId: true },
|
|
298
|
+
description: "Pass --account-id biz_xxx"
|
|
288
299
|
}
|
|
289
300
|
]
|
|
290
301
|
};
|
|
@@ -297,14 +308,14 @@ async function loginAdapter(c2) {
|
|
|
297
308
|
});
|
|
298
309
|
}
|
|
299
310
|
let firstRun = !result.existed;
|
|
300
|
-
const
|
|
301
|
-
if (
|
|
311
|
+
const accountId = getActiveProfile()?.accountId ?? "";
|
|
312
|
+
if (accountId) {
|
|
302
313
|
try {
|
|
303
|
-
const { createWhopFetch: createWhopFetch2 } = await import("./api-
|
|
304
|
-
const
|
|
305
|
-
const res = await
|
|
314
|
+
const { createWhopFetch: createWhopFetch2 } = await import("./api-U7WU3JXH.js");
|
|
315
|
+
const fetch3 = createWhopFetch2();
|
|
316
|
+
const res = await fetch3(
|
|
306
317
|
new Request(
|
|
307
|
-
`https://api.whop.com/products?company_id=${
|
|
318
|
+
`https://api.whop.com/products?company_id=${accountId}&first=1`
|
|
308
319
|
)
|
|
309
320
|
);
|
|
310
321
|
if (res.ok) {
|
|
@@ -314,14 +325,14 @@ async function loginAdapter(c2) {
|
|
|
314
325
|
} catch {
|
|
315
326
|
}
|
|
316
327
|
}
|
|
317
|
-
if (!
|
|
328
|
+
if (!accountId) {
|
|
318
329
|
return c2.ok(result.output, {
|
|
319
330
|
cta: {
|
|
320
|
-
description: "You're logged in, but
|
|
331
|
+
description: "You're logged in, but you don't have a business account yet. Create one and start selling in one command:",
|
|
321
332
|
commands: [
|
|
322
333
|
{
|
|
323
|
-
command: "
|
|
324
|
-
description: "
|
|
334
|
+
command: "quickstart",
|
|
335
|
+
description: "Create your business account, product, and checkout link"
|
|
325
336
|
}
|
|
326
337
|
]
|
|
327
338
|
}
|
|
@@ -585,10 +596,10 @@ import { text, isCancel } from "@clack/prompts";
|
|
|
585
596
|
// src/commerce/client.ts
|
|
586
597
|
var PLACEHOLDER = "https://api.whop.com";
|
|
587
598
|
var whopFetch = createWhopFetch();
|
|
588
|
-
async function makeWhopRequest(method,
|
|
599
|
+
async function makeWhopRequest(method, path3, body) {
|
|
589
600
|
const init = { method };
|
|
590
601
|
if (body !== void 0) init.body = JSON.stringify(body);
|
|
591
|
-
const res = await whopFetch(new Request(`${PLACEHOLDER}${
|
|
602
|
+
const res = await whopFetch(new Request(`${PLACEHOLDER}${path3}`, init));
|
|
592
603
|
if (!res.ok) {
|
|
593
604
|
const payload = await res.json().catch(() => null);
|
|
594
605
|
throw new Error(payload?.message ?? res.statusText);
|
|
@@ -630,13 +641,13 @@ function registerListCommand(group, config) {
|
|
|
630
641
|
});
|
|
631
642
|
}
|
|
632
643
|
function registerItemCommands(group, config) {
|
|
633
|
-
const { path, noun, idLabel, schema, updateOptions, transformBody } = config;
|
|
644
|
+
const { path: path3, noun, idLabel, schema, updateOptions, transformBody } = config;
|
|
634
645
|
const idArg = external_exports.object({ id: external_exports.string().describe(idLabel) });
|
|
635
646
|
group.command("get", {
|
|
636
647
|
description: `Get a ${noun} by ID`,
|
|
637
648
|
args: idArg,
|
|
638
649
|
output: schema,
|
|
639
|
-
run: (c2) => makeWhopRequest(`GET`, `${
|
|
650
|
+
run: (c2) => makeWhopRequest(`GET`, `${path3}/${c2.args.id}`)
|
|
640
651
|
});
|
|
641
652
|
if (updateOptions) {
|
|
642
653
|
group.command("update", {
|
|
@@ -646,14 +657,14 @@ function registerItemCommands(group, config) {
|
|
|
646
657
|
output: schema,
|
|
647
658
|
run: (c2) => {
|
|
648
659
|
const body = buildBody(c2.options);
|
|
649
|
-
return makeWhopRequest("PATCH", `${
|
|
660
|
+
return makeWhopRequest("PATCH", `${path3}/${c2.args.id}`, transformBody ? transformBody(body) : body);
|
|
650
661
|
}
|
|
651
662
|
});
|
|
652
663
|
}
|
|
653
664
|
group.command("delete", {
|
|
654
665
|
description: `Delete a ${noun}`,
|
|
655
666
|
args: idArg,
|
|
656
|
-
run: (c2) => makeWhopRequest("DELETE", `${
|
|
667
|
+
run: (c2) => makeWhopRequest("DELETE", `${path3}/${c2.args.id}`)
|
|
657
668
|
});
|
|
658
669
|
}
|
|
659
670
|
|
|
@@ -840,7 +851,7 @@ async function resolveProductId(providedId, isAgent) {
|
|
|
840
851
|
if (providedId) return { ok: true, productId: providedId, autoResolved: false };
|
|
841
852
|
const profile = getActiveProfile();
|
|
842
853
|
const params = new URLSearchParams({ first: "100" });
|
|
843
|
-
if (profile?.
|
|
854
|
+
if (profile?.accountId) params.set("company_id", profile.accountId);
|
|
844
855
|
let data;
|
|
845
856
|
try {
|
|
846
857
|
const res = await makeWhopRequest("GET", `/products?${params}`);
|
|
@@ -1111,7 +1122,10 @@ var quickstartOptions = external_exports.object({
|
|
|
1111
1122
|
"every_two_years",
|
|
1112
1123
|
"one_time"
|
|
1113
1124
|
]).optional().describe("Billing interval \u2014 use 'one_time' for a single payment"),
|
|
1114
|
-
|
|
1125
|
+
business_name: external_exports.string().optional().describe(
|
|
1126
|
+
"Name for a new business account \u2014 quickstart creates it and sets up your product under it (user logins only; ignored with API-key auth, which already belongs to a business)"
|
|
1127
|
+
),
|
|
1128
|
+
account_id: external_exports.string().optional().describe(
|
|
1115
1129
|
"Business account to act on behalf of (biz_xxx); defaults to your active account"
|
|
1116
1130
|
)
|
|
1117
1131
|
});
|
|
@@ -1121,7 +1135,11 @@ var quickstartOutput = external_exports.object({
|
|
|
1121
1135
|
checkoutUrl: external_exports.string().nullable().describe(
|
|
1122
1136
|
"The live checkout link to share \u2014 the journey's payoff, present once a plan exists"
|
|
1123
1137
|
),
|
|
1124
|
-
created: external_exports.object({
|
|
1138
|
+
created: external_exports.object({
|
|
1139
|
+
accountId: external_exports.string().optional(),
|
|
1140
|
+
productId: external_exports.string().optional(),
|
|
1141
|
+
planId: external_exports.string().optional()
|
|
1142
|
+
}).nullable().describe(
|
|
1125
1143
|
"What this run created, if anything \u2014 null for a read-only status check"
|
|
1126
1144
|
),
|
|
1127
1145
|
steps: external_exports.array(StepSchema).describe("The path to your first sale"),
|
|
@@ -1160,36 +1178,87 @@ ${chalk3.yellow("!")} Agent setup skipped: ${message}`);
|
|
|
1160
1178
|
);
|
|
1161
1179
|
}
|
|
1162
1180
|
}
|
|
1163
|
-
const
|
|
1181
|
+
const accountId = c2.options.account_id ?? await resolveActiveAccountId() ?? "";
|
|
1164
1182
|
const accountName = profile?.accountTitle ?? profile?.accountRoute ?? profile?.name ?? "your account";
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1183
|
+
const interactive = !c2.agent && !c2.formatExplicit;
|
|
1184
|
+
const canCreateBusiness = profile?.method === "oauth";
|
|
1185
|
+
let newBusinessName = canCreateBusiness ? c2.options.business_name : void 0;
|
|
1186
|
+
if (!canCreateBusiness && c2.options.business_name !== void 0 && interactive) {
|
|
1187
|
+
console.log(
|
|
1188
|
+
`
|
|
1189
|
+
${chalk3.dim("Ignoring --business_name \u2014 this API key already belongs to a business.")}`
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
if (!accountId && newBusinessName === void 0) {
|
|
1193
|
+
if (!canCreateBusiness) {
|
|
1194
|
+
return c2.error({
|
|
1195
|
+
code: "NO_ACCOUNT",
|
|
1196
|
+
message: "Couldn't resolve the business account for this API key. Check the key, or log in again.",
|
|
1197
|
+
retryable: true,
|
|
1198
|
+
cta: {
|
|
1199
|
+
commands: [{ command: "auth login", description: "Log in to Whop" }]
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
if (!interactive) {
|
|
1204
|
+
return c2.error({
|
|
1205
|
+
code: "NO_ACCOUNT",
|
|
1206
|
+
message: "No business account is linked to this login. Pass --business_name to create one and continue.",
|
|
1207
|
+
retryable: true,
|
|
1208
|
+
cta: {
|
|
1209
|
+
description: "Create your business account and product in one run:",
|
|
1210
|
+
commands: [
|
|
1211
|
+
{
|
|
1212
|
+
command: "quickstart",
|
|
1213
|
+
options: { business_name: "My Business" },
|
|
1214
|
+
description: "Create a business account, then your product"
|
|
1215
|
+
}
|
|
1216
|
+
]
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
console.log(
|
|
1221
|
+
`
|
|
1222
|
+
${chalk3.bold("You don't have a business account yet \u2014 let's create one.")}`
|
|
1223
|
+
);
|
|
1224
|
+
const name = await text2({
|
|
1225
|
+
message: "What's your business called?",
|
|
1226
|
+
placeholder: "My Business"
|
|
1179
1227
|
});
|
|
1228
|
+
if (isCancel3(name)) {
|
|
1229
|
+
return c2.error({
|
|
1230
|
+
code: "CANCELLED",
|
|
1231
|
+
message: "Setup cancelled.",
|
|
1232
|
+
exitCode: 130
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
newBusinessName = name;
|
|
1236
|
+
}
|
|
1237
|
+
if (newBusinessName !== void 0) {
|
|
1238
|
+
try {
|
|
1239
|
+
return await runCreate(c2, {
|
|
1240
|
+
accountId: "",
|
|
1241
|
+
accountName,
|
|
1242
|
+
products: [],
|
|
1243
|
+
newBusinessName
|
|
1244
|
+
});
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
if (err instanceof QuickstartError) return c2.error(err.envelope);
|
|
1247
|
+
throw err;
|
|
1248
|
+
}
|
|
1180
1249
|
}
|
|
1181
1250
|
const [productsRes, plansRes, checkoutRes] = await Promise.allSettled([
|
|
1182
1251
|
makeWhopRequest(
|
|
1183
1252
|
"GET",
|
|
1184
|
-
`/products?company_id=${
|
|
1253
|
+
`/products?company_id=${accountId}&first=100`
|
|
1185
1254
|
),
|
|
1186
1255
|
makeWhopRequest(
|
|
1187
1256
|
"GET",
|
|
1188
|
-
`/plans?account_id=${
|
|
1257
|
+
`/plans?account_id=${accountId}&first=100`
|
|
1189
1258
|
),
|
|
1190
1259
|
makeWhopRequest(
|
|
1191
1260
|
"GET",
|
|
1192
|
-
`/checkout_configurations?company_id=${
|
|
1261
|
+
`/checkout_configurations?company_id=${accountId}&first=100`
|
|
1193
1262
|
)
|
|
1194
1263
|
]);
|
|
1195
1264
|
const critical = [productsRes, plansRes].find((r) => r.status === "rejected");
|
|
@@ -1206,11 +1275,17 @@ ${chalk3.yellow("!")} Agent setup skipped: ${message}`);
|
|
|
1206
1275
|
const checkouts = checkoutRes.status === "fulfilled" ? checkoutRes.value.data ?? [] : [];
|
|
1207
1276
|
const ready = products.length > 0 && plans.length > 0;
|
|
1208
1277
|
const hasCreationFlags = c2.options.title !== void 0 || c2.options.price !== void 0 || c2.options.billing_period !== void 0;
|
|
1209
|
-
const
|
|
1210
|
-
const shouldCreate = hasCreationFlags || !ready && canMutateInteractively;
|
|
1278
|
+
const shouldCreate = hasCreationFlags || !ready && interactive;
|
|
1211
1279
|
if (shouldCreate) {
|
|
1212
1280
|
try {
|
|
1213
|
-
return await runCreate(c2, {
|
|
1281
|
+
return await runCreate(c2, {
|
|
1282
|
+
accountId,
|
|
1283
|
+
accountName,
|
|
1284
|
+
products,
|
|
1285
|
+
// Only offer the new-business choice in the fully-guided flow —
|
|
1286
|
+
// flags mean the user already said what they want.
|
|
1287
|
+
offerNewBusiness: interactive && !hasCreationFlags && c2.options.account_id === void 0 && canCreateBusiness
|
|
1288
|
+
});
|
|
1214
1289
|
} catch (err) {
|
|
1215
1290
|
if (err instanceof QuickstartError) return c2.error(err.envelope);
|
|
1216
1291
|
throw err;
|
|
@@ -1256,7 +1331,39 @@ async function runCreate(c2, state) {
|
|
|
1256
1331
|
${chalk3.bold("Let's get you set up to sell.")}
|
|
1257
1332
|
`);
|
|
1258
1333
|
}
|
|
1259
|
-
|
|
1334
|
+
let { accountId, accountName, products, newBusinessName } = state;
|
|
1335
|
+
if (state.offerNewBusiness) {
|
|
1336
|
+
const choice = await select2({
|
|
1337
|
+
message: "Which business account is this for?",
|
|
1338
|
+
options: [
|
|
1339
|
+
{ value: "current", label: accountName, hint: accountId },
|
|
1340
|
+
{ value: "new", label: "Create a new business account" }
|
|
1341
|
+
]
|
|
1342
|
+
});
|
|
1343
|
+
if (isCancel3(choice)) {
|
|
1344
|
+
throw new QuickstartError({
|
|
1345
|
+
code: "CANCELLED",
|
|
1346
|
+
message: "Setup cancelled.",
|
|
1347
|
+
exitCode: 130
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
if (choice === "new") {
|
|
1351
|
+
const name = await text2({
|
|
1352
|
+
message: "What's your new business called?",
|
|
1353
|
+
placeholder: "My Business"
|
|
1354
|
+
});
|
|
1355
|
+
if (isCancel3(name)) {
|
|
1356
|
+
throw new QuickstartError({
|
|
1357
|
+
code: "CANCELLED",
|
|
1358
|
+
message: "Setup cancelled.",
|
|
1359
|
+
exitCode: 130
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
newBusinessName = name;
|
|
1363
|
+
products = [];
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
const needsNewProduct = products.length === 0 || c2.options.title !== void 0;
|
|
1260
1367
|
const newTitle = needsNewProduct ? await resolveOrPrompt(
|
|
1261
1368
|
c2,
|
|
1262
1369
|
c2.options.title,
|
|
@@ -1270,7 +1377,7 @@ ${chalk3.bold("Let's get you set up to sell.")}
|
|
|
1270
1377
|
placeholder: "My Online Course"
|
|
1271
1378
|
})
|
|
1272
1379
|
) : void 0;
|
|
1273
|
-
const existing = needsNewProduct ? void 0 : await resolveExistingProduct(c2,
|
|
1380
|
+
const existing = needsNewProduct ? void 0 : await resolveExistingProduct(c2, products);
|
|
1274
1381
|
const price = await resolveOrPrompt(
|
|
1275
1382
|
c2,
|
|
1276
1383
|
c2.options.price,
|
|
@@ -1306,6 +1413,33 @@ ${chalk3.bold("Let's get you set up to sell.")}
|
|
|
1306
1413
|
]
|
|
1307
1414
|
})
|
|
1308
1415
|
);
|
|
1416
|
+
let accountCreated = false;
|
|
1417
|
+
if (newBusinessName !== void 0) {
|
|
1418
|
+
let account;
|
|
1419
|
+
try {
|
|
1420
|
+
account = await makeWhopRequest("POST", "/accounts", { title: newBusinessName });
|
|
1421
|
+
} catch (err) {
|
|
1422
|
+
return c2.error({
|
|
1423
|
+
code: "ACCOUNT_FAILED",
|
|
1424
|
+
message: `Failed to create the business account: ${err instanceof Error ? err.message : "unknown error"}`,
|
|
1425
|
+
retryable: true
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
accountId = account.id;
|
|
1429
|
+
accountName = account.title ?? newBusinessName;
|
|
1430
|
+
accountCreated = true;
|
|
1431
|
+
setActiveProfileAccount({
|
|
1432
|
+
id: account.id,
|
|
1433
|
+
title: account.title ?? void 0,
|
|
1434
|
+
route: account.route ?? void 0
|
|
1435
|
+
});
|
|
1436
|
+
if (!c2.agent) {
|
|
1437
|
+
console.log(
|
|
1438
|
+
`
|
|
1439
|
+
${chalk3.green("\u2713")} Created business account "${accountName}" ${chalk3.dim(`(${accountId})`)} \u2014 the CLI now acts on behalf of it.`
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1309
1443
|
let productId;
|
|
1310
1444
|
let productTitle;
|
|
1311
1445
|
let productCreated = false;
|
|
@@ -1320,7 +1454,7 @@ ${chalk3.bold("Let's get you set up to sell.")}
|
|
|
1320
1454
|
"/products",
|
|
1321
1455
|
{
|
|
1322
1456
|
title: newTitle,
|
|
1323
|
-
company_id:
|
|
1457
|
+
company_id: accountId
|
|
1324
1458
|
}
|
|
1325
1459
|
);
|
|
1326
1460
|
} catch (err) {
|
|
@@ -1337,7 +1471,7 @@ ${chalk3.bold("Let's get you set up to sell.")}
|
|
|
1337
1471
|
const isRenewal = billingPeriod !== "one_time";
|
|
1338
1472
|
const planBody = {
|
|
1339
1473
|
product_id: productId,
|
|
1340
|
-
account_id:
|
|
1474
|
+
account_id: accountId,
|
|
1341
1475
|
initial_price: price,
|
|
1342
1476
|
plan_type: isRenewal ? "renewal" : "one_time"
|
|
1343
1477
|
};
|
|
@@ -1373,6 +1507,15 @@ ${chalk3.bold("Let's get you set up to sell.")}
|
|
|
1373
1507
|
const checkoutUrl = plan.purchase_url ?? null;
|
|
1374
1508
|
const steps = [
|
|
1375
1509
|
{ id: "auth", label: "Log in", complete: true },
|
|
1510
|
+
...accountCreated ? [
|
|
1511
|
+
{
|
|
1512
|
+
id: "account",
|
|
1513
|
+
label: "Create a business account",
|
|
1514
|
+
complete: true,
|
|
1515
|
+
firstId: accountId,
|
|
1516
|
+
firstTitle: accountName
|
|
1517
|
+
}
|
|
1518
|
+
] : [],
|
|
1376
1519
|
{
|
|
1377
1520
|
id: "products",
|
|
1378
1521
|
label: "Create a product",
|
|
@@ -1395,20 +1538,17 @@ ${chalk3.bold("Let's get you set up to sell.")}
|
|
|
1395
1538
|
false
|
|
1396
1539
|
);
|
|
1397
1540
|
if (!c2.agent && !c2.formatExplicit) {
|
|
1398
|
-
printSummary(
|
|
1399
|
-
state.accountName,
|
|
1400
|
-
steps,
|
|
1401
|
-
true,
|
|
1402
|
-
checkoutUrl,
|
|
1403
|
-
nextStep,
|
|
1404
|
-
productTitle
|
|
1405
|
-
);
|
|
1541
|
+
printSummary(accountName, steps, true, checkoutUrl, nextStep, productTitle);
|
|
1406
1542
|
}
|
|
1407
1543
|
return c2.ok({
|
|
1408
|
-
account:
|
|
1544
|
+
account: accountName,
|
|
1409
1545
|
ready: true,
|
|
1410
1546
|
checkoutUrl,
|
|
1411
|
-
created: {
|
|
1547
|
+
created: {
|
|
1548
|
+
...accountCreated ? { accountId } : {},
|
|
1549
|
+
productId,
|
|
1550
|
+
planId: plan.id
|
|
1551
|
+
},
|
|
1412
1552
|
steps,
|
|
1413
1553
|
nextStep
|
|
1414
1554
|
});
|
|
@@ -1535,6 +1675,479 @@ ${c.success("\u2713 Your store is fully set up.")}`);
|
|
|
1535
1675
|
console.log("");
|
|
1536
1676
|
}
|
|
1537
1677
|
|
|
1678
|
+
// package.json
|
|
1679
|
+
var package_default = {
|
|
1680
|
+
name: "@whop/cli",
|
|
1681
|
+
version: "0.2.0",
|
|
1682
|
+
description: "The Whop CLI \u2014 build and manage Whop apps from your terminal. Human and agent friendly.",
|
|
1683
|
+
keywords: [
|
|
1684
|
+
"agent",
|
|
1685
|
+
"apps",
|
|
1686
|
+
"cli",
|
|
1687
|
+
"developer",
|
|
1688
|
+
"whop"
|
|
1689
|
+
],
|
|
1690
|
+
homepage: "https://whop.com/developers/",
|
|
1691
|
+
bugs: "https://github.com/whopio/whop-public-cli/issues",
|
|
1692
|
+
license: "MIT",
|
|
1693
|
+
repository: {
|
|
1694
|
+
type: "git",
|
|
1695
|
+
url: "git+https://github.com/whopio/whop-public-cli.git"
|
|
1696
|
+
},
|
|
1697
|
+
bin: {
|
|
1698
|
+
whop: "dist/index.js",
|
|
1699
|
+
"whop-cli": "dist/index.js"
|
|
1700
|
+
},
|
|
1701
|
+
files: [
|
|
1702
|
+
"dist"
|
|
1703
|
+
],
|
|
1704
|
+
type: "module",
|
|
1705
|
+
publishConfig: {
|
|
1706
|
+
access: "public"
|
|
1707
|
+
},
|
|
1708
|
+
scripts: {
|
|
1709
|
+
build: "tsx scripts/sync-spec.ts && pnpm run clean && tsup",
|
|
1710
|
+
"build:binary": "tsx scripts/build-binary.ts",
|
|
1711
|
+
clean: "rm -rf dist",
|
|
1712
|
+
dev: "tsx scripts/sync-spec.ts && tsx src/index.ts",
|
|
1713
|
+
start: "node dist/index.js",
|
|
1714
|
+
"check-types": "tsx scripts/sync-spec.ts && tsc --noEmit",
|
|
1715
|
+
lint: "oxlint .",
|
|
1716
|
+
"lint:fix": "oxlint --fix --fix-suggestions && oxfmt",
|
|
1717
|
+
"ci:lint": "oxlint -f github .",
|
|
1718
|
+
format: "oxfmt",
|
|
1719
|
+
"sync-spec": "tsx scripts/sync-spec.ts",
|
|
1720
|
+
test: "tsx --test scripts/*.test.ts"
|
|
1721
|
+
},
|
|
1722
|
+
dependencies: {
|
|
1723
|
+
"@clack/prompts": "^1.5.1",
|
|
1724
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
1725
|
+
"@whop/sdk": "^0.0.40",
|
|
1726
|
+
chalk: "5.4.1",
|
|
1727
|
+
yaml: "2.8.0"
|
|
1728
|
+
},
|
|
1729
|
+
devDependencies: {
|
|
1730
|
+
"@types/node": "25.3.5",
|
|
1731
|
+
incur: "github:whopio/incur#5ca60d5",
|
|
1732
|
+
tsup: "8.5.0",
|
|
1733
|
+
tsx: "4.19.4",
|
|
1734
|
+
typescript: "5.9.3"
|
|
1735
|
+
},
|
|
1736
|
+
engines: {
|
|
1737
|
+
node: ">=22"
|
|
1738
|
+
},
|
|
1739
|
+
packageManager: "pnpm@10.23.0"
|
|
1740
|
+
};
|
|
1741
|
+
|
|
1742
|
+
// src/lib/update/check.ts
|
|
1743
|
+
import fs from "fs";
|
|
1744
|
+
import path from "path";
|
|
1745
|
+
import { spawn } from "child_process";
|
|
1746
|
+
|
|
1747
|
+
// src/lib/colorize-output.ts
|
|
1748
|
+
var SECTION_HEADERS = /* @__PURE__ */ new Set([
|
|
1749
|
+
"Usage",
|
|
1750
|
+
"Arguments",
|
|
1751
|
+
"Options",
|
|
1752
|
+
"Examples",
|
|
1753
|
+
"Environment Variables",
|
|
1754
|
+
"Global Options",
|
|
1755
|
+
"Integrations",
|
|
1756
|
+
"Commands",
|
|
1757
|
+
"Aliases"
|
|
1758
|
+
]);
|
|
1759
|
+
var FORMAT_FLAGS = ["--json", "--format", "--full-output", "--schema", "--llms", "--llms-full", "--mcp", "--token-count", "--token-limit", "--token-offset"];
|
|
1760
|
+
function hasStructuredOutputFlag(argv2) {
|
|
1761
|
+
return argv2.some((a) => FORMAT_FLAGS.includes(a) || a.startsWith("--format="));
|
|
1762
|
+
}
|
|
1763
|
+
function shouldColorize(argv2) {
|
|
1764
|
+
if (!process.stdout.isTTY) return false;
|
|
1765
|
+
return !hasStructuredOutputFlag(argv2);
|
|
1766
|
+
}
|
|
1767
|
+
function paintFlag(flag) {
|
|
1768
|
+
return flag.replace(/(<[^>]+>)/g, (m) => c.muted(m)).replace(/(--[\w-]+|-[\w])/g, (m) => c.flag(m));
|
|
1769
|
+
}
|
|
1770
|
+
var COMMAND_ROW = /^(\s+)(whop\s+\S.*?)(\s{2,}#\s.*)?$/;
|
|
1771
|
+
function highlightUrls(text3) {
|
|
1772
|
+
return text3.replace(/https?:\/\/[^\s)]+/g, (m) => c.link(m));
|
|
1773
|
+
}
|
|
1774
|
+
function colorizeLine(line, section, nextLine) {
|
|
1775
|
+
const errMatch = line.match(/^(Error(?: \([^)]+\))?:)(.*)$/);
|
|
1776
|
+
if (errMatch) return { line: c.error(errMatch[1]) + highlightUrls(errMatch[2]), section };
|
|
1777
|
+
if (line.startsWith("Run `")) return { line: c.heading(line), section };
|
|
1778
|
+
const header = line.match(/^([A-Z][A-Za-z ]+):(.*)$/);
|
|
1779
|
+
if (header && SECTION_HEADERS.has(header[1])) {
|
|
1780
|
+
const rest = header[2].replace(/(whop[\w\s-]*)/g, (m) => c.command(m));
|
|
1781
|
+
return { line: c.heading(`${header[1]}:`) + rest, section: header[1] };
|
|
1782
|
+
}
|
|
1783
|
+
const cmd = line.match(COMMAND_ROW);
|
|
1784
|
+
if (cmd) {
|
|
1785
|
+
return { line: `${cmd[1]}${c.command(cmd[2])}${cmd[3] ? c.description(cmd[3]) : ""}`, section };
|
|
1786
|
+
}
|
|
1787
|
+
if (line.trim().endsWith(":") && nextLine !== void 0 && COMMAND_ROW.test(nextLine)) {
|
|
1788
|
+
return { line: c.heading(line), section };
|
|
1789
|
+
}
|
|
1790
|
+
if (line.trim() === "") return { line, section: null };
|
|
1791
|
+
if (section === "Options" || section === "Global Options" || section === "Environment Variables") {
|
|
1792
|
+
const m = line.match(/^(\s+)(\S.*?)(\s{2,})(.*)$/);
|
|
1793
|
+
if (m) return { line: `${m[1]}${paintFlag(m[2])}${m[3]}${c.description(m[4])}`, section };
|
|
1794
|
+
}
|
|
1795
|
+
if (section === "Commands" || section === "Integrations" || section === "Aliases") {
|
|
1796
|
+
const m = line.match(/^(\s+)(\S+)(\s{2,})(.*)$/);
|
|
1797
|
+
if (m) return { line: `${m[1]}${c.command(m[2])}${m[3]}${c.description(m[4])}`, section };
|
|
1798
|
+
}
|
|
1799
|
+
if (section === "Arguments") {
|
|
1800
|
+
const m = line.match(/^(\s+)(\S+)(\s{2,})(.*)$/);
|
|
1801
|
+
if (m) return { line: `${m[1]}${c.id(m[2])}${m[3]}${c.description(m[4])}`, section };
|
|
1802
|
+
}
|
|
1803
|
+
return { line: highlightUrls(line), section };
|
|
1804
|
+
}
|
|
1805
|
+
function colorizeOutput(text3) {
|
|
1806
|
+
let section = null;
|
|
1807
|
+
const lines = text3.split("\n");
|
|
1808
|
+
return lines.map((line, i) => {
|
|
1809
|
+
const result = colorizeLine(line, section, lines[i + 1]);
|
|
1810
|
+
section = result.section;
|
|
1811
|
+
return result.line;
|
|
1812
|
+
}).join("\n");
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// src/lib/update/check.ts
|
|
1816
|
+
var DEFAULT_MANIFEST_URL = "https://github.com/whopio/whop-public-cli/releases/latest/download/manifest.json";
|
|
1817
|
+
function manifestUrl(env = process.env) {
|
|
1818
|
+
return env.WHOP_CLI_MANIFEST_URL?.trim() || DEFAULT_MANIFEST_URL;
|
|
1819
|
+
}
|
|
1820
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1821
|
+
function cachePath(env) {
|
|
1822
|
+
return path.join(configDir(env), "update-check.json");
|
|
1823
|
+
}
|
|
1824
|
+
function readUpdateCache(env = process.env) {
|
|
1825
|
+
try {
|
|
1826
|
+
const parsed = JSON.parse(
|
|
1827
|
+
fs.readFileSync(cachePath(env), "utf8")
|
|
1828
|
+
);
|
|
1829
|
+
if (parsed != null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1830
|
+
return parsed;
|
|
1831
|
+
}
|
|
1832
|
+
} catch {
|
|
1833
|
+
}
|
|
1834
|
+
return {};
|
|
1835
|
+
}
|
|
1836
|
+
function writeUpdateCache(cache, env = process.env) {
|
|
1837
|
+
try {
|
|
1838
|
+
fs.mkdirSync(configDir(env), { recursive: true, mode: 448 });
|
|
1839
|
+
fs.writeFileSync(cachePath(env), `${JSON.stringify(cache, null, 2)}
|
|
1840
|
+
`, {
|
|
1841
|
+
mode: 384
|
|
1842
|
+
});
|
|
1843
|
+
} catch {
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
function isNewerVersion(candidate, current) {
|
|
1847
|
+
const parse = (v) => v.replace(/^v/, "").split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
1848
|
+
const a = parse(candidate);
|
|
1849
|
+
const b = parse(current);
|
|
1850
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
1851
|
+
if ((a[i] ?? 0) > (b[i] ?? 0)) return true;
|
|
1852
|
+
if ((a[i] ?? 0) < (b[i] ?? 0)) return false;
|
|
1853
|
+
}
|
|
1854
|
+
return false;
|
|
1855
|
+
}
|
|
1856
|
+
function upgradeCommandFor(channel) {
|
|
1857
|
+
switch (channel) {
|
|
1858
|
+
case "npm":
|
|
1859
|
+
return "npm install -g @whop/cli";
|
|
1860
|
+
case "brew":
|
|
1861
|
+
return "brew upgrade whop";
|
|
1862
|
+
default:
|
|
1863
|
+
return "whop upgrade";
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
function updateCheckSuppressed(argv2, env = process.env, stderrIsTty = process.stderr.isTTY ?? false) {
|
|
1867
|
+
if (env.WHOP_CLI_NO_UPDATE_CHECK) return true;
|
|
1868
|
+
if (env.CI) return true;
|
|
1869
|
+
if (!stderrIsTty) return true;
|
|
1870
|
+
if (hasStructuredOutputFlag(argv2)) return true;
|
|
1871
|
+
const first = argv2[0];
|
|
1872
|
+
if (first === "upgrade" || first === "mcp" || first === "completions" || first?.startsWith("--")) {
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
return false;
|
|
1876
|
+
}
|
|
1877
|
+
function spawnBackgroundCheck() {
|
|
1878
|
+
const args = detectChannel() === "standalone" || detectChannel() === "brew" ? ["upgrade", "--background"] : [process.argv[1], "upgrade", "--background"];
|
|
1879
|
+
try {
|
|
1880
|
+
spawn(process.execPath, args, {
|
|
1881
|
+
detached: true,
|
|
1882
|
+
stdio: "ignore"
|
|
1883
|
+
}).unref();
|
|
1884
|
+
} catch {
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
function scheduleUpdateNotice(argv2, currentVersion, env = process.env) {
|
|
1888
|
+
const channel = detectChannel(env);
|
|
1889
|
+
if (channel === "dev") return;
|
|
1890
|
+
if (updateCheckSuppressed(argv2, env)) return;
|
|
1891
|
+
const cache = readUpdateCache(env);
|
|
1892
|
+
if (cache.updatedTo && cache.updatedTo === currentVersion) {
|
|
1893
|
+
writeUpdateCache({ ...cache, updatedTo: void 0 }, env);
|
|
1894
|
+
process.on("exit", () => {
|
|
1895
|
+
process.stderr.write(
|
|
1896
|
+
`
|
|
1897
|
+
whop was updated to v${currentVersion} automatically.
|
|
1898
|
+
`
|
|
1899
|
+
);
|
|
1900
|
+
});
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
if (cache.latestVersion && isNewerVersion(cache.latestVersion, currentVersion)) {
|
|
1904
|
+
const latest = cache.latestVersion;
|
|
1905
|
+
process.on("exit", () => {
|
|
1906
|
+
process.stderr.write(
|
|
1907
|
+
`
|
|
1908
|
+
A new version of whop is available: v${currentVersion} \u2192 v${latest}
|
|
1909
|
+
Run \`${upgradeCommandFor(channel)}\` to update.
|
|
1910
|
+
`
|
|
1911
|
+
);
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
const lastChecked = cache.lastCheckedAt ? new Date(cache.lastCheckedAt).getTime() : 0;
|
|
1915
|
+
if (Date.now() - lastChecked > CHECK_INTERVAL_MS) {
|
|
1916
|
+
writeUpdateCache({ ...cache, lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString() }, env);
|
|
1917
|
+
spawnBackgroundCheck();
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// src/lib/update/self-update.ts
|
|
1922
|
+
import crypto from "crypto";
|
|
1923
|
+
import fs2 from "fs";
|
|
1924
|
+
import path2 from "path";
|
|
1925
|
+
var SelfUpdateError = class extends Error {
|
|
1926
|
+
constructor(code, message) {
|
|
1927
|
+
super(message);
|
|
1928
|
+
this.code = code;
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
async function fetchManifest(env = process.env) {
|
|
1932
|
+
const url = manifestUrl(env);
|
|
1933
|
+
let response;
|
|
1934
|
+
try {
|
|
1935
|
+
response = await fetch(url, {
|
|
1936
|
+
signal: AbortSignal.timeout(1e4),
|
|
1937
|
+
redirect: "follow"
|
|
1938
|
+
});
|
|
1939
|
+
} catch {
|
|
1940
|
+
throw new SelfUpdateError(
|
|
1941
|
+
"MANIFEST_UNREACHABLE",
|
|
1942
|
+
`Could not reach the update manifest at ${url}.`
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
if (!response.ok) {
|
|
1946
|
+
throw new SelfUpdateError(
|
|
1947
|
+
"MANIFEST_UNAVAILABLE",
|
|
1948
|
+
`Update manifest request failed with HTTP ${response.status}.`
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
let parsed;
|
|
1952
|
+
try {
|
|
1953
|
+
parsed = await response.json();
|
|
1954
|
+
} catch {
|
|
1955
|
+
throw new SelfUpdateError(
|
|
1956
|
+
"MANIFEST_INVALID",
|
|
1957
|
+
"Update manifest is not valid JSON."
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
const manifest = parsed;
|
|
1961
|
+
if (typeof manifest?.version !== "string" || manifest.assets == null || typeof manifest.assets !== "object") {
|
|
1962
|
+
throw new SelfUpdateError(
|
|
1963
|
+
"MANIFEST_INVALID",
|
|
1964
|
+
"Update manifest is missing version or assets."
|
|
1965
|
+
);
|
|
1966
|
+
}
|
|
1967
|
+
return manifest;
|
|
1968
|
+
}
|
|
1969
|
+
function assetForThisBinary(manifest) {
|
|
1970
|
+
if (!buildTarget) {
|
|
1971
|
+
throw new SelfUpdateError(
|
|
1972
|
+
"NOT_A_STANDALONE_BUILD",
|
|
1973
|
+
"This build has no compile target \u2014 self-update only applies to standalone binaries."
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1976
|
+
const asset = manifest.assets[buildTarget];
|
|
1977
|
+
if (!asset?.url || !asset.sha256) {
|
|
1978
|
+
throw new SelfUpdateError(
|
|
1979
|
+
"NO_ASSET_FOR_TARGET",
|
|
1980
|
+
`The latest release has no binary for ${buildTarget}.`
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
return { ...asset, target: buildTarget };
|
|
1984
|
+
}
|
|
1985
|
+
async function downloadAndSwap(asset, execPath = process.execPath) {
|
|
1986
|
+
let response;
|
|
1987
|
+
try {
|
|
1988
|
+
response = await fetch(asset.url, {
|
|
1989
|
+
signal: AbortSignal.timeout(12e4),
|
|
1990
|
+
redirect: "follow"
|
|
1991
|
+
});
|
|
1992
|
+
} catch {
|
|
1993
|
+
throw new SelfUpdateError(
|
|
1994
|
+
"DOWNLOAD_FAILED",
|
|
1995
|
+
`Could not download ${asset.url}.`
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
if (!response.ok) {
|
|
1999
|
+
throw new SelfUpdateError(
|
|
2000
|
+
"DOWNLOAD_FAILED",
|
|
2001
|
+
`Binary download failed with HTTP ${response.status}.`
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
2005
|
+
const digest = crypto.createHash("sha256").update(bytes).digest("hex");
|
|
2006
|
+
if (digest !== asset.sha256.toLowerCase()) {
|
|
2007
|
+
throw new SelfUpdateError(
|
|
2008
|
+
"CHECKSUM_MISMATCH",
|
|
2009
|
+
`Downloaded binary checksum ${digest} does not match the manifest \u2014 aborting.`
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
const dir = path2.dirname(execPath);
|
|
2013
|
+
const tmp = path2.join(dir, `.${path2.basename(execPath)}.${process.pid}.new`);
|
|
2014
|
+
try {
|
|
2015
|
+
fs2.writeFileSync(tmp, bytes, { mode: 493 });
|
|
2016
|
+
fs2.renameSync(tmp, execPath);
|
|
2017
|
+
} catch (err) {
|
|
2018
|
+
try {
|
|
2019
|
+
fs2.rmSync(tmp, { force: true });
|
|
2020
|
+
} catch {
|
|
2021
|
+
}
|
|
2022
|
+
const code = err.code;
|
|
2023
|
+
if (code === "EACCES" || code === "EPERM" || code === "EROFS") {
|
|
2024
|
+
throw new SelfUpdateError(
|
|
2025
|
+
"PERMISSION_DENIED",
|
|
2026
|
+
`No write access to ${dir}. Re-run with elevated permissions (e.g. \`sudo whop upgrade\`) or reinstall.`
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
2029
|
+
throw err;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
// src/lib/update/command.ts
|
|
2034
|
+
var upgradeOptions = external_exports.object({
|
|
2035
|
+
check: external_exports.boolean().optional().describe("Only check for a newer version, don't install it"),
|
|
2036
|
+
background: external_exports.boolean().optional().describe(
|
|
2037
|
+
"Internal: refresh the update cache silently (spawned by the automatic check)"
|
|
2038
|
+
)
|
|
2039
|
+
});
|
|
2040
|
+
var upgradeOutput = external_exports.object({
|
|
2041
|
+
status: external_exports.enum(["up-to-date", "update-available", "updated"]).describe("Result of the upgrade check"),
|
|
2042
|
+
current: external_exports.string().describe("The version currently running"),
|
|
2043
|
+
latest: external_exports.string().describe("The latest released version"),
|
|
2044
|
+
channel: external_exports.enum(["npm", "brew", "standalone", "dev"]).describe("How this CLI was installed"),
|
|
2045
|
+
upgradeCommand: external_exports.string().optional().describe("Run this shell command to install the available update")
|
|
2046
|
+
});
|
|
2047
|
+
async function runBackground(env) {
|
|
2048
|
+
const manifest = await fetchManifest(env);
|
|
2049
|
+
const cache = readUpdateCache(env);
|
|
2050
|
+
writeUpdateCache(
|
|
2051
|
+
{
|
|
2052
|
+
...cache,
|
|
2053
|
+
lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2054
|
+
latestVersion: manifest.version
|
|
2055
|
+
},
|
|
2056
|
+
env
|
|
2057
|
+
);
|
|
2058
|
+
if (detectChannel(env) === "standalone" && env.WHOP_CLI_AUTO_UPDATE !== "0" && isNewerVersion(manifest.version, package_default.version)) {
|
|
2059
|
+
await downloadAndSwap(assetForThisBinary(manifest));
|
|
2060
|
+
writeUpdateCache(
|
|
2061
|
+
{
|
|
2062
|
+
lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2063
|
+
latestVersion: manifest.version,
|
|
2064
|
+
updatedTo: manifest.version
|
|
2065
|
+
},
|
|
2066
|
+
env
|
|
2067
|
+
);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
async function runUpgrade(c2, env = process.env) {
|
|
2071
|
+
if (c2.options.background) {
|
|
2072
|
+
try {
|
|
2073
|
+
await runBackground(env);
|
|
2074
|
+
} catch {
|
|
2075
|
+
}
|
|
2076
|
+
return c2.ok({
|
|
2077
|
+
status: "up-to-date",
|
|
2078
|
+
current: package_default.version,
|
|
2079
|
+
latest: package_default.version,
|
|
2080
|
+
channel: detectChannel(env)
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
const channel = detectChannel(env);
|
|
2084
|
+
let manifest;
|
|
2085
|
+
try {
|
|
2086
|
+
manifest = await fetchManifest(env);
|
|
2087
|
+
} catch (err) {
|
|
2088
|
+
if (err instanceof SelfUpdateError) {
|
|
2089
|
+
return c2.error({ code: err.code, message: err.message, retryable: true });
|
|
2090
|
+
}
|
|
2091
|
+
throw err;
|
|
2092
|
+
}
|
|
2093
|
+
writeUpdateCache(
|
|
2094
|
+
{
|
|
2095
|
+
...readUpdateCache(env),
|
|
2096
|
+
lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2097
|
+
latestVersion: manifest.version
|
|
2098
|
+
},
|
|
2099
|
+
env
|
|
2100
|
+
);
|
|
2101
|
+
const base = { current: package_default.version, latest: manifest.version, channel };
|
|
2102
|
+
if (!isNewerVersion(manifest.version, package_default.version)) {
|
|
2103
|
+
return c2.ok({ status: "up-to-date", ...base });
|
|
2104
|
+
}
|
|
2105
|
+
if (channel === "npm" || channel === "brew") {
|
|
2106
|
+
return c2.ok({
|
|
2107
|
+
status: "update-available",
|
|
2108
|
+
...base,
|
|
2109
|
+
upgradeCommand: upgradeCommandFor(channel)
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
if (channel === "dev") {
|
|
2113
|
+
return c2.error({
|
|
2114
|
+
code: "DEV_BUILD",
|
|
2115
|
+
message: "Running from source \u2014 pull the latest instead of upgrading.",
|
|
2116
|
+
retryable: false
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
if (c2.options.check) {
|
|
2120
|
+
return c2.ok(
|
|
2121
|
+
{
|
|
2122
|
+
status: "update-available",
|
|
2123
|
+
...base,
|
|
2124
|
+
upgradeCommand: upgradeCommandFor(channel)
|
|
2125
|
+
},
|
|
2126
|
+
{
|
|
2127
|
+
cta: {
|
|
2128
|
+
description: `v${manifest.version} is available (you have v${package_default.version}). To update:`,
|
|
2129
|
+
commands: [
|
|
2130
|
+
{ command: "upgrade", description: "Install the latest version" }
|
|
2131
|
+
]
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
);
|
|
2135
|
+
}
|
|
2136
|
+
try {
|
|
2137
|
+
await downloadAndSwap(assetForThisBinary(manifest));
|
|
2138
|
+
} catch (err) {
|
|
2139
|
+
if (err instanceof SelfUpdateError) {
|
|
2140
|
+
return c2.error({
|
|
2141
|
+
code: err.code,
|
|
2142
|
+
message: err.message,
|
|
2143
|
+
retryable: err.code !== "PERMISSION_DENIED"
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
throw err;
|
|
2147
|
+
}
|
|
2148
|
+
return c2.ok({ status: "updated", ...base });
|
|
2149
|
+
}
|
|
2150
|
+
|
|
1538
2151
|
// src/commands.ts
|
|
1539
2152
|
var HANDWRITTEN_GROUPS = [
|
|
1540
2153
|
{
|
|
@@ -1543,7 +2156,7 @@ var HANDWRITTEN_GROUPS = [
|
|
|
1543
2156
|
section: "get-started",
|
|
1544
2157
|
register: (cli2) => cli2.command("quickstart", {
|
|
1545
2158
|
description: "Start selling \u2014 check your status and create what's missing",
|
|
1546
|
-
hint: "In a terminal it guides you to your first sale. Agents: a read-only status check unless you pass --title/--price/--billing_period.",
|
|
2159
|
+
hint: "In a terminal it guides you to your first sale \u2014 including creating a business account if you don't have one. Agents: a read-only status check unless you pass --business_name/--title/--price/--billing_period.",
|
|
1547
2160
|
options: quickstartOptions,
|
|
1548
2161
|
output: quickstartOutput,
|
|
1549
2162
|
examples: [
|
|
@@ -1555,6 +2168,15 @@ var HANDWRITTEN_GROUPS = [
|
|
|
1555
2168
|
billing_period: "monthly"
|
|
1556
2169
|
},
|
|
1557
2170
|
description: "Create a $9.99/mo product non-interactively"
|
|
2171
|
+
},
|
|
2172
|
+
{
|
|
2173
|
+
options: {
|
|
2174
|
+
business_name: "My Business",
|
|
2175
|
+
title: "My Course",
|
|
2176
|
+
price: 9.99,
|
|
2177
|
+
billing_period: "one_time"
|
|
2178
|
+
},
|
|
2179
|
+
description: "No business account yet? Create it and the product in one run"
|
|
1558
2180
|
}
|
|
1559
2181
|
],
|
|
1560
2182
|
outputPolicy: "agent-only",
|
|
@@ -1579,6 +2201,24 @@ var HANDWRITTEN_GROUPS = [
|
|
|
1579
2201
|
section: "commerce",
|
|
1580
2202
|
register: (cli2) => cli2.command(buildCheckoutGroup())
|
|
1581
2203
|
},
|
|
2204
|
+
{
|
|
2205
|
+
name: "upgrade",
|
|
2206
|
+
description: "Update the CLI to the latest version",
|
|
2207
|
+
section: "get-started",
|
|
2208
|
+
register: (cli2) => cli2.command("upgrade", {
|
|
2209
|
+
description: "Update the whop CLI to the latest version",
|
|
2210
|
+
options: upgradeOptions,
|
|
2211
|
+
output: upgradeOutput,
|
|
2212
|
+
examples: [
|
|
2213
|
+
{ description: "Update to the latest version" },
|
|
2214
|
+
{
|
|
2215
|
+
options: { check: true },
|
|
2216
|
+
description: "Check for a newer version without installing"
|
|
2217
|
+
}
|
|
2218
|
+
],
|
|
2219
|
+
run: (c2) => runUpgrade(c2)
|
|
2220
|
+
})
|
|
2221
|
+
},
|
|
1582
2222
|
{
|
|
1583
2223
|
name: "auth",
|
|
1584
2224
|
description: "Manage authentication",
|
|
@@ -1635,69 +2275,6 @@ async function setupAgents(cli2) {
|
|
|
1635
2275
|
}
|
|
1636
2276
|
}
|
|
1637
2277
|
|
|
1638
|
-
// package.json
|
|
1639
|
-
var package_default = {
|
|
1640
|
-
name: "@whop/cli",
|
|
1641
|
-
version: "0.0.6",
|
|
1642
|
-
description: "The Whop CLI \u2014 build and manage Whop apps from your terminal. Human and agent friendly.",
|
|
1643
|
-
keywords: [
|
|
1644
|
-
"agent",
|
|
1645
|
-
"apps",
|
|
1646
|
-
"cli",
|
|
1647
|
-
"developer",
|
|
1648
|
-
"whop"
|
|
1649
|
-
],
|
|
1650
|
-
homepage: "https://whop.com/developers/",
|
|
1651
|
-
bugs: "https://github.com/whopio/whop-sdk-ts/issues",
|
|
1652
|
-
license: "MIT",
|
|
1653
|
-
repository: {
|
|
1654
|
-
type: "git",
|
|
1655
|
-
url: "git+https://github.com/whopio/whop-sdk-ts.git",
|
|
1656
|
-
directory: "packages/cli"
|
|
1657
|
-
},
|
|
1658
|
-
bin: {
|
|
1659
|
-
whop: "dist/index.js",
|
|
1660
|
-
"whop-cli": "dist/index.js"
|
|
1661
|
-
},
|
|
1662
|
-
files: [
|
|
1663
|
-
"dist"
|
|
1664
|
-
],
|
|
1665
|
-
type: "module",
|
|
1666
|
-
publishConfig: {
|
|
1667
|
-
access: "public"
|
|
1668
|
-
},
|
|
1669
|
-
scripts: {
|
|
1670
|
-
build: "tsx scripts/sync-spec.ts && pnpm run clean && tsup",
|
|
1671
|
-
clean: "rm -rf dist",
|
|
1672
|
-
dev: "tsx scripts/sync-spec.ts && tsx src/index.ts",
|
|
1673
|
-
start: "node dist/index.js",
|
|
1674
|
-
"check-types": "tsx scripts/sync-spec.ts && tsc --noEmit",
|
|
1675
|
-
lint: "oxlint .",
|
|
1676
|
-
"lint:fix": "oxlint --fix --fix-suggestions && oxfmt",
|
|
1677
|
-
"ci:lint": "oxlint -f github .",
|
|
1678
|
-
format: "oxfmt",
|
|
1679
|
-
"sync-spec": "tsx scripts/sync-spec.ts",
|
|
1680
|
-
test: "tsx --test scripts/*.test.ts"
|
|
1681
|
-
},
|
|
1682
|
-
dependencies: {
|
|
1683
|
-
"@clack/prompts": "^1.5.1",
|
|
1684
|
-
"@napi-rs/keyring": "^1.3.0",
|
|
1685
|
-
"@whop/sdk": "^0.0.40",
|
|
1686
|
-
chalk: "5.4.1"
|
|
1687
|
-
},
|
|
1688
|
-
devDependencies: {
|
|
1689
|
-
"@types/node": "25.3.5",
|
|
1690
|
-
incur: "github:whopio/incur#5ca60d5",
|
|
1691
|
-
tsup: "8.5.0",
|
|
1692
|
-
tsx: "4.19.4",
|
|
1693
|
-
typescript: "5.9.3"
|
|
1694
|
-
},
|
|
1695
|
-
engines: {
|
|
1696
|
-
node: ">=22"
|
|
1697
|
-
},
|
|
1698
|
-
packageManager: "pnpm@10.23.0"
|
|
1699
|
-
};
|
|
1700
|
-
|
|
1701
2278
|
// src/lib/renderer.ts
|
|
1702
2279
|
var MAX_CELL_WIDTH = 40;
|
|
1703
2280
|
function isPlainObject(value) {
|
|
@@ -1782,8 +2359,8 @@ var cli = Cli_exports.create("whop", {
|
|
|
1782
2359
|
}
|
|
1783
2360
|
});
|
|
1784
2361
|
var spec = (tag) => filterSpecByTag(native_spec_default, tag);
|
|
1785
|
-
var
|
|
1786
|
-
var isAuthExempt = (cmd) => !cmd || cmd === "login" || cmd === "logout" || cmd === "completions" || cmd.startsWith("auth") || cmd.startsWith("mcp");
|
|
2362
|
+
var fetch2 = createWhopFetch();
|
|
2363
|
+
var isAuthExempt = (cmd) => !cmd || cmd === "login" || cmd === "logout" || cmd === "completions" || cmd === "upgrade" || cmd.startsWith("auth") || cmd.startsWith("mcp");
|
|
1787
2364
|
cli.use(async (c2, next) => {
|
|
1788
2365
|
if (isAuthExempt(c2.command)) return next();
|
|
1789
2366
|
if (getActiveProfile()) return next();
|
|
@@ -1823,7 +2400,7 @@ cli.use(async (c2, next) => {
|
|
|
1823
2400
|
});
|
|
1824
2401
|
registerHandwrittenGroups(cli);
|
|
1825
2402
|
for (const { name, tag } of API_GROUPS) {
|
|
1826
|
-
cli.command(name, { fetch, openapi: spec(tag) });
|
|
2403
|
+
cli.command(name, { fetch: fetch2, openapi: spec(tag) });
|
|
1827
2404
|
}
|
|
1828
2405
|
var cli_default = cli;
|
|
1829
2406
|
|
|
@@ -1860,6 +2437,13 @@ var api_structure_default = [
|
|
|
1860
2437
|
"Referrals"
|
|
1861
2438
|
]
|
|
1862
2439
|
},
|
|
2440
|
+
{
|
|
2441
|
+
group: "Tracking",
|
|
2442
|
+
tags: [
|
|
2443
|
+
"People",
|
|
2444
|
+
"Events"
|
|
2445
|
+
]
|
|
2446
|
+
},
|
|
1863
2447
|
{
|
|
1864
2448
|
group: "Ads",
|
|
1865
2449
|
tags: [
|
|
@@ -1997,73 +2581,9 @@ function buildGroupedHelp(version, description) {
|
|
|
1997
2581
|
return lines.join("\n");
|
|
1998
2582
|
}
|
|
1999
2583
|
|
|
2000
|
-
// src/lib/colorize-output.ts
|
|
2001
|
-
var SECTION_HEADERS = /* @__PURE__ */ new Set([
|
|
2002
|
-
"Usage",
|
|
2003
|
-
"Arguments",
|
|
2004
|
-
"Options",
|
|
2005
|
-
"Examples",
|
|
2006
|
-
"Environment Variables",
|
|
2007
|
-
"Global Options",
|
|
2008
|
-
"Integrations",
|
|
2009
|
-
"Commands",
|
|
2010
|
-
"Aliases"
|
|
2011
|
-
]);
|
|
2012
|
-
var FORMAT_FLAGS = ["--json", "--format", "--full-output", "--schema", "--llms", "--llms-full", "--mcp", "--token-count", "--token-limit", "--token-offset"];
|
|
2013
|
-
function shouldColorize(argv2) {
|
|
2014
|
-
if (!process.stdout.isTTY) return false;
|
|
2015
|
-
return !argv2.some((a) => FORMAT_FLAGS.includes(a) || a.startsWith("--format="));
|
|
2016
|
-
}
|
|
2017
|
-
function paintFlag(flag) {
|
|
2018
|
-
return flag.replace(/(<[^>]+>)/g, (m) => c.muted(m)).replace(/(--[\w-]+|-[\w])/g, (m) => c.flag(m));
|
|
2019
|
-
}
|
|
2020
|
-
var COMMAND_ROW = /^(\s+)(whop\s+\S.*?)(\s{2,}#\s.*)?$/;
|
|
2021
|
-
function highlightUrls(text3) {
|
|
2022
|
-
return text3.replace(/https?:\/\/[^\s)]+/g, (m) => c.link(m));
|
|
2023
|
-
}
|
|
2024
|
-
function colorizeLine(line, section, nextLine) {
|
|
2025
|
-
const errMatch = line.match(/^(Error(?: \([^)]+\))?:)(.*)$/);
|
|
2026
|
-
if (errMatch) return { line: c.error(errMatch[1]) + highlightUrls(errMatch[2]), section };
|
|
2027
|
-
if (line.startsWith("Run `")) return { line: c.heading(line), section };
|
|
2028
|
-
const header = line.match(/^([A-Z][A-Za-z ]+):(.*)$/);
|
|
2029
|
-
if (header && SECTION_HEADERS.has(header[1])) {
|
|
2030
|
-
const rest = header[2].replace(/(whop[\w\s-]*)/g, (m) => c.command(m));
|
|
2031
|
-
return { line: c.heading(`${header[1]}:`) + rest, section: header[1] };
|
|
2032
|
-
}
|
|
2033
|
-
const cmd = line.match(COMMAND_ROW);
|
|
2034
|
-
if (cmd) {
|
|
2035
|
-
return { line: `${cmd[1]}${c.command(cmd[2])}${cmd[3] ? c.description(cmd[3]) : ""}`, section };
|
|
2036
|
-
}
|
|
2037
|
-
if (line.trim().endsWith(":") && nextLine !== void 0 && COMMAND_ROW.test(nextLine)) {
|
|
2038
|
-
return { line: c.heading(line), section };
|
|
2039
|
-
}
|
|
2040
|
-
if (line.trim() === "") return { line, section: null };
|
|
2041
|
-
if (section === "Options" || section === "Global Options" || section === "Environment Variables") {
|
|
2042
|
-
const m = line.match(/^(\s+)(\S.*?)(\s{2,})(.*)$/);
|
|
2043
|
-
if (m) return { line: `${m[1]}${paintFlag(m[2])}${m[3]}${c.description(m[4])}`, section };
|
|
2044
|
-
}
|
|
2045
|
-
if (section === "Commands" || section === "Integrations" || section === "Aliases") {
|
|
2046
|
-
const m = line.match(/^(\s+)(\S+)(\s{2,})(.*)$/);
|
|
2047
|
-
if (m) return { line: `${m[1]}${c.command(m[2])}${m[3]}${c.description(m[4])}`, section };
|
|
2048
|
-
}
|
|
2049
|
-
if (section === "Arguments") {
|
|
2050
|
-
const m = line.match(/^(\s+)(\S+)(\s{2,})(.*)$/);
|
|
2051
|
-
if (m) return { line: `${m[1]}${c.id(m[2])}${m[3]}${c.description(m[4])}`, section };
|
|
2052
|
-
}
|
|
2053
|
-
return { line: highlightUrls(line), section };
|
|
2054
|
-
}
|
|
2055
|
-
function colorizeOutput(text3) {
|
|
2056
|
-
let section = null;
|
|
2057
|
-
const lines = text3.split("\n");
|
|
2058
|
-
return lines.map((line, i) => {
|
|
2059
|
-
const result = colorizeLine(line, section, lines[i + 1]);
|
|
2060
|
-
section = result.section;
|
|
2061
|
-
return result.line;
|
|
2062
|
-
}).join("\n");
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
2584
|
// src/index.ts
|
|
2066
2585
|
var argv = process.argv.slice(2);
|
|
2586
|
+
scheduleUpdateNotice(argv, package_default.version);
|
|
2067
2587
|
async function serve(argv2) {
|
|
2068
2588
|
if (shouldColorize(argv2)) {
|
|
2069
2589
|
await cli_default.serve(argv2, {
|
|
@@ -2086,8 +2606,4 @@ if (argv.length === 0 && !getActiveProfile() && !process.env.WHOP_API_KEY) {
|
|
|
2086
2606
|
} else {
|
|
2087
2607
|
await serve(argv);
|
|
2088
2608
|
}
|
|
2089
|
-
var index_default = cli_default;
|
|
2090
|
-
export {
|
|
2091
|
-
index_default as default
|
|
2092
|
-
};
|
|
2093
2609
|
//# sourceMappingURL=index.js.map
|