@rebasepro/cli 0.9.1-canary.d906fb7 → 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.
- package/dist/commands/skills.d.ts +1 -1
- package/dist/index.es.js +115 -37
- package/dist/index.es.js.map +1 -1
- package/dist/utils/package-manager.d.ts +19 -5
- package/package.json +7 -7
- package/templates/overlays/baas/backend/package.json +1 -1
- package/templates/overlays/baas/backend/tsconfig.json +2 -1
- package/templates/template/backend/src/env.ts +30 -1
- package/templates/template/config/collections/users.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function skillsCommand(subcommand: string | undefined,
|
|
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
|
-
*
|
|
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.
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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,
|
|
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.
|
|
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
|
-
|
|
2562
|
-
|
|
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
|
|
2588
|
-
const
|
|
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
|
|
2694
|
-
const
|
|
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
|
|
2752
|
-
const
|
|
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.
|
|
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
|