@whop/cli 0.4.0 → 0.5.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/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  switchProfile,
36
36
  upsertProfile,
37
37
  validateApiKey
38
- } from "./chunk-2K4WBZA5.js";
38
+ } from "./chunk-X34EUMEM.js";
39
39
  import {
40
40
  external_exports
41
41
  } from "./chunk-KFCNNWPI.js";
@@ -122,6 +122,10 @@ var groups_default = [
122
122
  "audiences",
123
123
  "Audiences"
124
124
  ],
125
+ [
126
+ "media",
127
+ "Media"
128
+ ],
125
129
  [
126
130
  "social-accounts",
127
131
  "Social Accounts"
@@ -314,7 +318,7 @@ async function loginAdapter(c2) {
314
318
  const accountId = getActiveProfile()?.accountId ?? "";
315
319
  if (accountId) {
316
320
  try {
317
- const { createWhopFetch: createWhopFetch2 } = await import("./api-7L74UEG4.js");
321
+ const { createWhopFetch: createWhopFetch2 } = await import("./api-E6Y2WAEG.js");
318
322
  const fetch3 = createWhopFetch2();
319
323
  const res = await fetch3(
320
324
  new Request(
@@ -644,6 +648,9 @@ async function getApp(appId) {
644
648
  async function updateAppRoute(appId, route) {
645
649
  return makeWhopRequest("PATCH", `/apps/${appId}`, { route });
646
650
  }
651
+ async function updateAppSecrets(appId, secrets) {
652
+ return makeWhopRequest("PATCH", `/apps/${appId}`, { secrets });
653
+ }
647
654
  async function createAppBuild(input) {
648
655
  return makeWhopRequest("POST", "/app_builds", {
649
656
  app_id: input.app_id,
@@ -984,6 +991,17 @@ function replaceTopLevelName(content, route) {
984
991
 
985
992
  // src/apps/commands.ts
986
993
  var BUILD_ARCHIVE = "dist/whop-build.zip";
994
+ var UNSAFE_DEV_ENV_KEYS = /* @__PURE__ */ new Set([
995
+ "PATH",
996
+ "NODE_OPTIONS",
997
+ "NODE_EXTRA_CA_CERTS",
998
+ "NODE_REPL_EXTERNAL_MODULE"
999
+ ]);
1000
+ var UNSAFE_DEV_ENV_PREFIXES = ["NPM_", "PNPM_", "YARN_", "BUN_", "LD_", "DYLD_"];
1001
+ function isUnsafeDevEnvKey(key) {
1002
+ const upper = key.toUpperCase();
1003
+ return UNSAFE_DEV_ENV_KEYS.has(upper) || UNSAFE_DEV_ENV_PREFIXES.some((prefix) => upper.startsWith(prefix));
1004
+ }
987
1005
  var BuildSchema = external_exports.object({
988
1006
  id: external_exports.string(),
989
1007
  status: external_exports.string(),
@@ -1359,6 +1377,77 @@ async function withRoute(c2, app) {
1359
1377
  });
1360
1378
  }
1361
1379
  }
1380
+ function resolveSecretsAppId(appOption) {
1381
+ if (appOption) return appOption;
1382
+ const projectDir = findProjectDir();
1383
+ const config = projectDir ? readAppConfig(projectDir) : null;
1384
+ if (config) return config.app_id;
1385
+ throw new Error(
1386
+ `No app specified. Pass --app app_xxx or run inside a project with ${APP_CONFIG_FILENAME}.`
1387
+ );
1388
+ }
1389
+ function buildSecretsGroup() {
1390
+ const secrets = Cli_exports.create("secrets", {
1391
+ description: "Manage app secrets \u2014 encrypted at rest, injected as env bindings into the hosted runtime and `whop apps dev`"
1392
+ });
1393
+ secrets.command("list", {
1394
+ description: "Show the app's secrets",
1395
+ options: external_exports.object({
1396
+ app: external_exports.string().optional().describe("App id (defaults to the project's linked app)")
1397
+ }),
1398
+ run: async (c2) => {
1399
+ const remote = await getApp(resolveSecretsAppId(c2.options.app));
1400
+ return c2.ok({ app_id: remote.id, secrets: remote.secrets ?? {} });
1401
+ }
1402
+ });
1403
+ secrets.command("set", {
1404
+ description: "Add or overwrite secrets",
1405
+ options: external_exports.object({
1406
+ secret: external_exports.array(external_exports.string()).min(1).describe("KEY=VALUE pair (repeatable)"),
1407
+ app: external_exports.string().optional().describe("App id (defaults to the project's linked app)")
1408
+ }),
1409
+ examples: [
1410
+ { options: { secret: ["MAIL_API_KEY=mail-key-123"] } },
1411
+ { options: { secret: ["A=1", "B=2"], app: "app_xxxxxxxx" } }
1412
+ ],
1413
+ run: async (c2) => {
1414
+ const updates = {};
1415
+ for (const pair of c2.options.secret) {
1416
+ const eq = pair.indexOf("=");
1417
+ if (eq <= 0) {
1418
+ return c2.error({
1419
+ code: "INVALID_SECRET",
1420
+ message: `"${pair}" is not KEY=VALUE. Use --secret NAME=value (an empty value deletes the secret).`
1421
+ });
1422
+ }
1423
+ updates[pair.slice(0, eq)] = pair.slice(eq + 1);
1424
+ }
1425
+ const remote = await updateAppSecrets(
1426
+ resolveSecretsAppId(c2.options.app),
1427
+ updates
1428
+ );
1429
+ return c2.ok({ app_id: remote.id, secrets: remote.secrets ?? {} });
1430
+ }
1431
+ });
1432
+ secrets.command("unset", {
1433
+ description: "Delete secrets by name",
1434
+ options: external_exports.object({
1435
+ key: external_exports.array(external_exports.string()).min(1).describe("Secret name (repeatable)"),
1436
+ app: external_exports.string().optional().describe("App id (defaults to the project's linked app)")
1437
+ }),
1438
+ examples: [{ options: { key: ["MAIL_API_KEY"] } }],
1439
+ run: async (c2) => {
1440
+ const updates = {};
1441
+ for (const key of c2.options.key) updates[key] = null;
1442
+ const remote = await updateAppSecrets(
1443
+ resolveSecretsAppId(c2.options.app),
1444
+ updates
1445
+ );
1446
+ return c2.ok({ app_id: remote.id, secrets: remote.secrets ?? {} });
1447
+ }
1448
+ });
1449
+ return secrets;
1450
+ }
1362
1451
  async function buildAppGroup() {
1363
1452
  const app = Cli_exports.create("apps", {
1364
1453
  description: "Build and deploy fully-hosted web apps on Whop (*.whop.app)"
@@ -1369,6 +1458,7 @@ async function buildAppGroup() {
1369
1458
  });
1370
1459
  await registerSpecCommands(builds, "App builds");
1371
1460
  app.command(builds);
1461
+ app.command(buildSecretsGroup());
1372
1462
  app.command("dev", {
1373
1463
  description: "Run the local dev server for this app",
1374
1464
  hint: "Starts the project's dev script with WHOP_APP_ID set and a short-lived access token injected as WHOP_API_KEY (minted from your CLI credential), so server-side SDK calls work locally without env setup. An explicitly exported WHOP_API_KEY is used as-is.",
@@ -1423,6 +1513,40 @@ async function buildAppGroup() {
1423
1513
  );
1424
1514
  }
1425
1515
  }
1516
+ try {
1517
+ const remote = await getApp(config.app_id);
1518
+ const secretEntries = Object.entries(remote.secrets ?? {}).filter(
1519
+ ([key]) => !(key in process.env) && !(key in injected)
1520
+ );
1521
+ const unsafe = secretEntries.filter(([key]) => isUnsafeDevEnvKey(key));
1522
+ for (const [key, value] of secretEntries) {
1523
+ if (!isUnsafeDevEnvKey(key)) injected[key] = value;
1524
+ }
1525
+ if (unsafe.length > 0) {
1526
+ log(
1527
+ c2,
1528
+ chalk.yellow(
1529
+ `Skipped app secret${unsafe.length === 1 ? "" : "s"} ${unsafe.map(([key]) => key).join(", ")} \u2014 these names control the local runtime and are never injected by \`whop apps dev\`.`
1530
+ )
1531
+ );
1532
+ }
1533
+ const injectedCount = secretEntries.length - unsafe.length;
1534
+ if (injectedCount > 0) {
1535
+ log(
1536
+ c2,
1537
+ chalk.dim(
1538
+ `Injected ${injectedCount} app secret${injectedCount === 1 ? "" : "s"} into the environment (${secretEntries.filter(([key]) => !isUnsafeDevEnvKey(key)).map(([key]) => key).join(", ")}).`
1539
+ )
1540
+ );
1541
+ }
1542
+ } catch (err) {
1543
+ log(
1544
+ c2,
1545
+ chalk.yellow(
1546
+ `Couldn't fetch app secrets (${err instanceof Error ? err.message : "unknown error"}) \u2014 continuing without them.`
1547
+ )
1548
+ );
1549
+ }
1426
1550
  log(c2, chalk.dim(`Starting dev server for ${config.name}...`));
1427
1551
  try {
1428
1552
  runScript(projectDir, "dev", injected);
@@ -1443,9 +1567,7 @@ async function buildAppGroup() {
1443
1567
  app: external_exports.string().optional().describe(
1444
1568
  "Link this project to an app (app_xxx) before deploying \u2014 writes whop.app.json, replacing any existing link"
1445
1569
  ),
1446
- skip_promote: external_exports.boolean().optional().describe(
1447
- "Upload the build without promoting it to production"
1448
- ),
1570
+ skip_promote: external_exports.boolean().optional().describe("Upload the build without promoting it to production"),
1449
1571
  skip_typecheck: external_exports.boolean().optional().describe("Skip the typecheck step"),
1450
1572
  skip_build: external_exports.boolean().optional().describe("Skip the build step and upload the existing dist/ output")
1451
1573
  }),
@@ -2218,7 +2340,7 @@ ${c.success("\u2713 Your store is fully set up.")}`);
2218
2340
  // package.json
2219
2341
  var package_default = {
2220
2342
  name: "@whop/cli",
2221
- version: "0.4.0",
2343
+ version: "0.5.0",
2222
2344
  description: "The Whop CLI \u2014 build and manage Whop apps from your terminal. Human and agent friendly.",
2223
2345
  keywords: [
2224
2346
  "agent",
@@ -2990,6 +3112,12 @@ var api_structure_default = [
2990
3112
  "Audiences"
2991
3113
  ]
2992
3114
  },
3115
+ {
3116
+ group: "Media",
3117
+ tags: [
3118
+ "Media"
3119
+ ]
3120
+ },
2993
3121
  {
2994
3122
  group: "Identity",
2995
3123
  tags: [