@rebasepro/cli 0.9.1-canary.e2fc7b6 → 0.9.1-canary.e3f810f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- export declare function skillsCommand(subcommand: string | undefined, _args: string[]): Promise<void>;
1
+ export declare function skillsCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
package/dist/index.es.js CHANGED
@@ -9,8 +9,8 @@ import { execa, execaCommandSync } from "execa";
9
9
  import { cp } from "fs/promises";
10
10
  import { fileURLToPath } from "url";
11
11
  import crypto from "crypto";
12
+ import { execSync, spawn, spawnSync } from "child_process";
12
13
  import os from "os";
13
- import { execSync, spawn } from "child_process";
14
14
  import { createRebaseClient } from "@rebasepro/client";
15
15
  import { generateSDK } from "@rebasepro/codegen";
16
16
  import { createRequire } from "module";
@@ -23,28 +23,45 @@ import { createRequire } from "module";
23
23
  * the rest of the CLI never has to hardcode a specific PM.
24
24
  */
25
25
  /**
26
- * Detect the package manager from the environment or the target directory.
26
+ * Whether pnpm is runnable on this machine.
27
+ *
28
+ * Used to decide whether a fresh project can be scaffolded with pnpm. Kept
29
+ * cheap and non-interactive (short timeout, output discarded) so it never
30
+ * hangs detection if a corepack shim misbehaves.
31
+ */
32
+ function isPnpmAvailable() {
33
+ try {
34
+ return spawnSync("pnpm", ["--version"], {
35
+ stdio: "ignore",
36
+ timeout: 3e3
37
+ }).status === 0;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+ /**
43
+ * Detect the package manager for a Rebase project.
44
+ *
45
+ * Rebase recommends pnpm, so detection prefers it. Crucially, *how the CLI was
46
+ * invoked* (`npx` vs `pnpm dlx`, i.e. `npm_config_user_agent`) is deliberately
47
+ * ignored: running `npx @rebasepro/cli init` says nothing about how the user
48
+ * wants to manage the project they're creating, and letting it pin the scaffold
49
+ * to npm is what made every `npx`-invoked project an npm project.
27
50
  *
28
51
  * Detection order:
29
- * 1. Explicit override (if provided)
30
- * 2. `npm_config_user_agent` env var (set by npm/pnpm when running via `npx`/`pnpm dlx`)
31
- * 3. Lock-file presence in the target directory
32
- * 4. Default to pnpm (Rebase's recommended PM)
52
+ * 1. An existing lock file — an explicit choice we always respect
53
+ * (`pnpm-lock.yaml` wins over `package-lock.json` when both are present).
54
+ * 2. pnpm, whenever it is installed.
55
+ * 3. npm, only as a fallback when pnpm is genuinely unavailable.
33
56
  */
34
57
  function detectPackageManager(targetDir) {
35
- const userAgent = process.env.npm_config_user_agent ?? "";
36
- if (userAgent.startsWith("npm/")) return "npm";
37
- if (userAgent.startsWith("pnpm/")) return "pnpm";
38
- if (targetDir) {
39
- if (fs.existsSync(path.join(targetDir, "package-lock.json"))) return "npm";
40
- if (fs.existsSync(path.join(targetDir, "pnpm-lock.yaml"))) return "pnpm";
41
- }
42
- const cwd = process.cwd();
43
- if (cwd !== targetDir) {
44
- if (fs.existsSync(path.join(cwd, "package-lock.json"))) return "npm";
45
- if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
58
+ const dirs = [targetDir, process.cwd()].filter((d) => !!d);
59
+ for (const dir of dirs) {
60
+ if (fs.existsSync(path.join(dir, "pnpm-lock.yaml"))) return "pnpm";
61
+ if (fs.existsSync(path.join(dir, "package-lock.json"))) return "npm";
46
62
  }
47
- return "pnpm";
63
+ if (isPnpmAvailable()) return "pnpm";
64
+ return "npm";
48
65
  }
49
66
  /** Build the command helpers for a given package manager. */
50
67
  function getPMCommands(pm) {
@@ -118,7 +135,8 @@ function getPMCommands(pm) {
118
135
  runWorkspace: (workspace, script) => [
119
136
  "pnpm",
120
137
  "--filter",
121
- workspace,
138
+ `./${workspace}`,
139
+ "run",
122
140
  script
123
141
  ],
124
142
  dlx: (pkg, args) => [
@@ -925,6 +943,13 @@ async function promptForOptions(rawArgs, pm) {
925
943
  cloudUrl: resolveCloudUrl(rawArgs)
926
944
  };
927
945
  }
946
+ if (!process.stdin.isTTY) {
947
+ console.error(chalk.red("Cannot prompt: this is a non-interactive terminal (no TTY)."));
948
+ console.error(chalk.yellow(" Re-run with --yes to accept defaults, passing any choices as flags, e.g.:"));
949
+ console.error(chalk.yellow(` rebase init ${nameArg || "my-app"} --yes --template blog --flavor cms`));
950
+ console.error(chalk.gray(" Options: --template <blog|ecommerce|blank> --flavor <cms|baas> --database-url <url> --install --git"));
951
+ process.exit(1);
952
+ }
928
953
  const questions = buildInitQuestions({
929
954
  nameArg,
930
955
  templateArg,
@@ -2452,22 +2477,43 @@ function installForAgent(agentKey, skills, projectDir) {
2452
2477
  }
2453
2478
  return count;
2454
2479
  }
2455
- async function skillsCommand(subcommand, _args) {
2480
+ async function skillsCommand(subcommand, rawArgs) {
2456
2481
  switch (subcommand) {
2457
2482
  case "install":
2458
- await skillsInstall();
2483
+ await skillsInstall(rawArgs);
2459
2484
  break;
2460
2485
  case "--help":
2461
2486
  case void 0:
2462
2487
  printSkillsHelp();
2463
2488
  break;
2464
2489
  default:
2465
- console.log(chalk.red(`Unknown skills subcommand: ${subcommand}`));
2490
+ console.error(chalk.red(`Unknown skills subcommand: ${subcommand}`));
2466
2491
  console.log("");
2467
2492
  printSkillsHelp();
2493
+ process.exit(1);
2494
+ }
2495
+ }
2496
+ /**
2497
+ * Agents named explicitly on the command line, e.g. `--agent claude --agent cursor`
2498
+ * (also accepts a comma-separated list). Returns null when none were given.
2499
+ */
2500
+ function parseAgentFlags(rawArgs) {
2501
+ const requested = [];
2502
+ for (let i = 0; i < rawArgs.length; i++) {
2503
+ if (rawArgs[i] !== "--agent" && rawArgs[i] !== "-a") continue;
2504
+ const value = rawArgs[i + 1];
2505
+ if (value && !value.startsWith("-")) requested.push(...value.split(",").map((v) => v.trim()).filter(Boolean));
2506
+ }
2507
+ if (requested.length === 0) return null;
2508
+ const valid = Object.keys(AGENTS);
2509
+ const unknown = requested.filter((a) => !valid.includes(a));
2510
+ if (unknown.length > 0) {
2511
+ console.error(chalk.red(`Unknown agent(s): ${unknown.join(", ")}. Available: ${valid.join(", ")}`));
2512
+ process.exit(1);
2468
2513
  }
2514
+ return requested;
2469
2515
  }
2470
- async function skillsInstall() {
2516
+ async function skillsInstall(rawArgs = []) {
2471
2517
  const projectDir = process.cwd();
2472
2518
  let skillsDir;
2473
2519
  try {
@@ -2481,8 +2527,14 @@ async function skillsInstall() {
2481
2527
  console.error(`${chalk.red.bold("ERROR")} No skills found in ${skillsDir}`);
2482
2528
  process.exit(1);
2483
2529
  }
2484
- let agents = detectAgents(projectDir);
2530
+ let agents = parseAgentFlags(rawArgs) ?? detectAgents(projectDir);
2485
2531
  if (agents.length === 0) {
2532
+ if (!process.stdin.isTTY) {
2533
+ console.error(chalk.red("Cannot prompt: this is a non-interactive terminal (no TTY)."));
2534
+ console.error(chalk.yellow(` Name the agents explicitly, e.g. rebase skills install --agent ${Object.keys(AGENTS)[0]}`));
2535
+ console.error(chalk.gray(` Available: ${Object.keys(AGENTS).join(", ")}`));
2536
+ process.exit(1);
2537
+ }
2486
2538
  const choices = Object.entries(AGENTS).map(([key, agent]) => ({
2487
2539
  name: agent.label,
2488
2540
  value: key,
@@ -2524,8 +2576,15 @@ ${chalk.green.bold("Subcommands")}
2524
2576
  ${chalk.blue.bold("install")} Install Rebase agent skills for your AI coding assistant
2525
2577
  Supports: Cursor, Claude Code, Windsurf, Gemini CLI, Antigravity
2526
2578
 
2579
+ ${chalk.green.bold("Options")}
2580
+ ${chalk.blue("--agent, -a")} Agent(s) to install for, skipping detection and the prompt.
2581
+ Repeat the flag or pass a comma-separated list.
2582
+ Available: ${Object.keys(AGENTS).join(", ")}
2583
+
2527
2584
  ${chalk.green.bold("Examples")}
2528
2585
  ${chalk.cyan("rebase skills install")}
2586
+ ${chalk.cyan("rebase skills install --agent claude")}
2587
+ ${chalk.cyan("rebase skills install --agent claude,cursor")}
2529
2588
  `);
2530
2589
  }
2531
2590
  //#endregion
@@ -2557,9 +2616,16 @@ function loadEnv(projectRoot) {
2557
2616
  }
2558
2617
  return env;
2559
2618
  }
2560
- function resolveBaseUrl(env) {
2561
- const port = env.PORT || env.REBASE_PORT || "3001";
2562
- return env.REBASE_BASE_URL || `http://localhost:${port}`;
2619
+ function resolveBaseUrl(env, projectRoot) {
2620
+ if (env.REBASE_BASE_URL) return env.REBASE_BASE_URL;
2621
+ if (projectRoot) try {
2622
+ const urlFile = path.join(projectRoot, ".rebase-dev-url");
2623
+ if (fs.existsSync(urlFile)) {
2624
+ const devUrl = fs.readFileSync(urlFile, "utf-8").trim();
2625
+ if (devUrl) return devUrl;
2626
+ }
2627
+ } catch {}
2628
+ return `http://localhost:${env.PORT || env.REBASE_PORT || "3001"}`;
2563
2629
  }
2564
2630
  async function apiKeysCommand(subcommand, rawArgs) {
2565
2631
  if (!subcommand || subcommand === "--help") {
@@ -2584,8 +2650,9 @@ async function apiKeysCommand(subcommand, rawArgs) {
2584
2650
  }
2585
2651
  }
2586
2652
  async function listKeys(_rawArgs) {
2587
- const env = loadEnv(requireProjectRoot());
2588
- const baseUrl = resolveBaseUrl(env);
2653
+ const projectRoot = requireProjectRoot();
2654
+ const env = loadEnv(projectRoot);
2655
+ const baseUrl = resolveBaseUrl(env, projectRoot);
2589
2656
  const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;
2590
2657
  if (!serviceKey) {
2591
2658
  console.error(chalk.red("✗ SERVICE_KEY not found in .env — required for admin operations."));
@@ -2690,8 +2757,9 @@ async function createKey(rawArgs) {
2690
2757
  expires_at = parsed.toISOString();
2691
2758
  }
2692
2759
  }
2693
- const env = loadEnv(requireProjectRoot());
2694
- const baseUrl = resolveBaseUrl(env);
2760
+ const projectRoot = requireProjectRoot();
2761
+ const env = loadEnv(projectRoot);
2762
+ const baseUrl = resolveBaseUrl(env, projectRoot);
2695
2763
  const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;
2696
2764
  if (!serviceKey) {
2697
2765
  console.error(chalk.red("✗ SERVICE_KEY not found in .env — required for admin operations."));
@@ -2748,8 +2816,9 @@ async function revokeKey(rawArgs) {
2748
2816
  console.log(chalk.gray(" Usage: rebase api-keys revoke <key-id>"));
2749
2817
  process.exit(1);
2750
2818
  }
2751
- const env = loadEnv(requireProjectRoot());
2752
- const baseUrl = resolveBaseUrl(env);
2819
+ const projectRoot = requireProjectRoot();
2820
+ const env = loadEnv(projectRoot);
2821
+ const baseUrl = resolveBaseUrl(env, projectRoot);
2753
2822
  const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;
2754
2823
  if (!serviceKey) {
2755
2824
  console.error(chalk.red("✗ SERVICE_KEY not found in .env — required for admin operations."));
@@ -3218,13 +3287,18 @@ function fmtDate(value) {
3218
3287
  */
3219
3288
  var POLL_INTERVAL_MS = 1500;
3220
3289
  var POLL_TIMEOUT_MS = 900 * 1e3;
3290
+ var MAX_SOURCE_UPLOAD_BYTES = 100 * 1024 * 1024;
3221
3291
  function sleep(ms) {
3222
3292
  return new Promise((r) => setTimeout(r, ms));
3223
3293
  }
3224
- function run(cmd, cmdArgs, cwd) {
3294
+ function run(cmd, cmdArgs, cwd, env) {
3225
3295
  return new Promise((resolve, reject) => {
3226
3296
  const child = spawn(cmd, cmdArgs, {
3227
3297
  cwd,
3298
+ env: env ? {
3299
+ ...process.env,
3300
+ ...env
3301
+ } : void 0,
3228
3302
  stdio: [
3229
3303
  "ignore",
3230
3304
  "ignore",
@@ -3254,7 +3328,7 @@ async function createSourceTarball(sourceDir) {
3254
3328
  for (const ignore of [".gitignore", ".rebaseignore"]) if (fs.existsSync(path.join(dir, ignore))) tarArgs.push(`--exclude-from=${ignore}`);
3255
3329
  tarArgs.push(".");
3256
3330
  try {
3257
- await run("tar", tarArgs, dir);
3331
+ await run("tar", tarArgs, dir, { COPYFILE_DISABLE: "1" });
3258
3332
  } catch (e) {
3259
3333
  fail(`Failed to package source: ${e instanceof Error ? e.message : String(e)}`);
3260
3334
  }
@@ -3264,6 +3338,7 @@ async function createSourceTarball(sourceDir) {
3264
3338
  async function uploadSource(url, token, projectId, tarPath) {
3265
3339
  const bytes = fs.readFileSync(tarPath);
3266
3340
  const sizeMb = (bytes.length / 1024 / 1024).toFixed(1);
3341
+ if (bytes.length > MAX_SOURCE_UPLOAD_BYTES) fail(`Source context is ${sizeMb} MB — the upload cap is ${Math.round(MAX_SOURCE_UPLOAD_BYTES / 1024 / 1024)} MB.`, "Trim the build context: exclude sourcemaps (*.map), build output and large assets via .rebaseignore or .gitignore.");
3267
3342
  console.log(chalk.gray(` Uploading source (${sizeMb} MB)...`));
3268
3343
  const res = await fetch(`${url}/api/functions/deploy/upload?projectId=${encodeURIComponent(projectId)}`, {
3269
3344
  method: "POST",
@@ -4243,6 +4318,8 @@ ${chalk.green.bold("Options")}
4243
4318
  ${chalk.blue("--secret")} Mark a variable write-only ${chalk.gray("(set)")}
4244
4319
  ${chalk.blue("--json")} Machine-readable output
4245
4320
  ${chalk.blue("--project, -p")} Project slug ${chalk.gray("(defaults to the linked project)")}
4321
+
4322
+ ${chalk.gray("Values are encrypted at rest (AES-256-GCM) and only decrypted at deploy time.")}
4246
4323
  `);
4247
4324
  }
4248
4325
  function printEnvHelpJson() {
@@ -5603,9 +5680,10 @@ async function entry(args) {
5603
5680
  await cloudCommand(effectiveSubcommand, args);
5604
5681
  break;
5605
5682
  default:
5606
- console.log(chalk.red(`Unknown command: ${command}`));
5683
+ console.error(chalk.red(`Unknown command: ${command}`));
5607
5684
  console.log("");
5608
5685
  printHelp();
5686
+ process.exit(1);
5609
5687
  }
5610
5688
  }
5611
5689
  function printHelp() {
@@ -5665,6 +5743,6 @@ ${chalk.gray("Documentation: https://rebase.pro/docs")}
5665
5743
  `);
5666
5744
  }
5667
5745
  //#endregion
5668
- export { authCommand, buildCommand, buildInitQuestions, cloudCommand, configureEnvFile, createRebaseApp, dbCommand, detectPackageManager, devCommand, doctorCommand, entry, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, generateSdkCommand, getActiveBackendPlugin, getPMCommands, requireBackendDir, requireProjectRoot, resolveLocalBin, resolvePluginCliScript, resolveTsx, schemaCommand, startCommand, validateProjectName, validateTsxInstallation };
5746
+ export { authCommand, buildCommand, buildInitQuestions, cloudCommand, configureEnvFile, createRebaseApp, dbCommand, detectPackageManager, devCommand, doctorCommand, entry, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, generateSdkCommand, getActiveBackendPlugin, getPMCommands, isPnpmAvailable, requireBackendDir, requireProjectRoot, resolveLocalBin, resolvePluginCliScript, resolveTsx, schemaCommand, startCommand, validateProjectName, validateTsxInstallation };
5669
5747
 
5670
5748
  //# sourceMappingURL=index.es.js.map