@rebasepro/cli 0.9.1-canary.eab7ae2 → 0.9.1-canary.ed943fa
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/cloud/context.d.ts +0 -6
- package/dist/commands/cloud/deployments.d.ts +0 -1
- package/dist/commands/cloud/power.d.ts +0 -1
- package/dist/commands/skills.d.ts +1 -1
- package/dist/index.es.js +184 -17
- package/dist/index.es.js.map +1 -1
- package/package.json +7 -7
- package/templates/template/config/collections/users.ts +2 -0
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { createRebaseClient } from "@rebasepro/client";
|
|
2
|
-
/** Default hosted control plane (the Rebase Cloud console origin). */
|
|
3
|
-
export declare const DEFAULT_CLOUD_URL = "https://app.rebase.pro";
|
|
4
2
|
/** Project-local link file: <project>/.rebase/cloud.json */
|
|
5
3
|
export declare function projectLinkPath(cwd?: string): string;
|
|
6
|
-
/** Host that a bare `rebase cloud` command should target, if any. */
|
|
7
|
-
export declare function currentContextUrl(): string | undefined;
|
|
8
4
|
/** Persist the active organization id for a host. */
|
|
9
5
|
export declare function setContextOrg(url: string, org: string | undefined): void;
|
|
10
6
|
export declare function getContextOrg(url: string): string | undefined;
|
|
@@ -104,8 +100,6 @@ export declare function initOutputMode(rawArgs: string[]): boolean;
|
|
|
104
100
|
export declare function isJsonMode(): boolean;
|
|
105
101
|
/** Force the mode (tests only — production latches it via `initOutputMode`). */
|
|
106
102
|
export declare function setJsonModeForTest(value: boolean): void;
|
|
107
|
-
/** Write one JSON value to stdout, followed by a newline. */
|
|
108
|
-
export declare function printJson(value: unknown): void;
|
|
109
103
|
/**
|
|
110
104
|
* The one output primitive every new command uses: in JSON mode emit `json`
|
|
111
105
|
* (and nothing else); otherwise run `human`. Keeping the two behind a single
|
|
@@ -19,7 +19,6 @@ export interface DeploymentRow {
|
|
|
19
19
|
gitCommitHash?: string;
|
|
20
20
|
gitCommitMessage?: string;
|
|
21
21
|
}
|
|
22
|
-
export declare function deploymentImage(dep: DeploymentRow): string | null;
|
|
23
22
|
/**
|
|
24
23
|
* The backend's rule EXACTLY: a rollback is honoured only for a successful
|
|
25
24
|
* deploy that recorded an image. Any other row 409s `deploy_not_rollbackable`.
|
|
@@ -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
|
@@ -943,6 +943,13 @@ async function promptForOptions(rawArgs, pm) {
|
|
|
943
943
|
cloudUrl: resolveCloudUrl(rawArgs)
|
|
944
944
|
};
|
|
945
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
|
+
}
|
|
946
953
|
const questions = buildInitQuestions({
|
|
947
954
|
nameArg,
|
|
948
955
|
templateArg,
|
|
@@ -2470,22 +2477,43 @@ function installForAgent(agentKey, skills, projectDir) {
|
|
|
2470
2477
|
}
|
|
2471
2478
|
return count;
|
|
2472
2479
|
}
|
|
2473
|
-
async function skillsCommand(subcommand,
|
|
2480
|
+
async function skillsCommand(subcommand, rawArgs) {
|
|
2474
2481
|
switch (subcommand) {
|
|
2475
2482
|
case "install":
|
|
2476
|
-
await skillsInstall();
|
|
2483
|
+
await skillsInstall(rawArgs);
|
|
2477
2484
|
break;
|
|
2478
2485
|
case "--help":
|
|
2479
2486
|
case void 0:
|
|
2480
2487
|
printSkillsHelp();
|
|
2481
2488
|
break;
|
|
2482
2489
|
default:
|
|
2483
|
-
console.
|
|
2490
|
+
console.error(chalk.red(`Unknown skills subcommand: ${subcommand}`));
|
|
2484
2491
|
console.log("");
|
|
2485
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);
|
|
2486
2513
|
}
|
|
2514
|
+
return requested;
|
|
2487
2515
|
}
|
|
2488
|
-
async function skillsInstall() {
|
|
2516
|
+
async function skillsInstall(rawArgs = []) {
|
|
2489
2517
|
const projectDir = process.cwd();
|
|
2490
2518
|
let skillsDir;
|
|
2491
2519
|
try {
|
|
@@ -2499,8 +2527,14 @@ async function skillsInstall() {
|
|
|
2499
2527
|
console.error(`${chalk.red.bold("ERROR")} No skills found in ${skillsDir}`);
|
|
2500
2528
|
process.exit(1);
|
|
2501
2529
|
}
|
|
2502
|
-
let agents = detectAgents(projectDir);
|
|
2530
|
+
let agents = parseAgentFlags(rawArgs) ?? detectAgents(projectDir);
|
|
2503
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
|
+
}
|
|
2504
2538
|
const choices = Object.entries(AGENTS).map(([key, agent]) => ({
|
|
2505
2539
|
name: agent.label,
|
|
2506
2540
|
value: key,
|
|
@@ -2542,8 +2576,15 @@ ${chalk.green.bold("Subcommands")}
|
|
|
2542
2576
|
${chalk.blue.bold("install")} Install Rebase agent skills for your AI coding assistant
|
|
2543
2577
|
Supports: Cursor, Claude Code, Windsurf, Gemini CLI, Antigravity
|
|
2544
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
|
+
|
|
2545
2584
|
${chalk.green.bold("Examples")}
|
|
2546
2585
|
${chalk.cyan("rebase skills install")}
|
|
2586
|
+
${chalk.cyan("rebase skills install --agent claude")}
|
|
2587
|
+
${chalk.cyan("rebase skills install --agent claude,cursor")}
|
|
2547
2588
|
`);
|
|
2548
2589
|
}
|
|
2549
2590
|
//#endregion
|
|
@@ -2575,9 +2616,16 @@ function loadEnv(projectRoot) {
|
|
|
2575
2616
|
}
|
|
2576
2617
|
return env;
|
|
2577
2618
|
}
|
|
2578
|
-
function resolveBaseUrl(env) {
|
|
2579
|
-
|
|
2580
|
-
|
|
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"}`;
|
|
2581
2629
|
}
|
|
2582
2630
|
async function apiKeysCommand(subcommand, rawArgs) {
|
|
2583
2631
|
if (!subcommand || subcommand === "--help") {
|
|
@@ -2602,8 +2650,9 @@ async function apiKeysCommand(subcommand, rawArgs) {
|
|
|
2602
2650
|
}
|
|
2603
2651
|
}
|
|
2604
2652
|
async function listKeys(_rawArgs) {
|
|
2605
|
-
const
|
|
2606
|
-
const
|
|
2653
|
+
const projectRoot = requireProjectRoot();
|
|
2654
|
+
const env = loadEnv(projectRoot);
|
|
2655
|
+
const baseUrl = resolveBaseUrl(env, projectRoot);
|
|
2607
2656
|
const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;
|
|
2608
2657
|
if (!serviceKey) {
|
|
2609
2658
|
console.error(chalk.red("✗ SERVICE_KEY not found in .env — required for admin operations."));
|
|
@@ -2708,8 +2757,9 @@ async function createKey(rawArgs) {
|
|
|
2708
2757
|
expires_at = parsed.toISOString();
|
|
2709
2758
|
}
|
|
2710
2759
|
}
|
|
2711
|
-
const
|
|
2712
|
-
const
|
|
2760
|
+
const projectRoot = requireProjectRoot();
|
|
2761
|
+
const env = loadEnv(projectRoot);
|
|
2762
|
+
const baseUrl = resolveBaseUrl(env, projectRoot);
|
|
2713
2763
|
const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;
|
|
2714
2764
|
if (!serviceKey) {
|
|
2715
2765
|
console.error(chalk.red("✗ SERVICE_KEY not found in .env — required for admin operations."));
|
|
@@ -2766,8 +2816,9 @@ async function revokeKey(rawArgs) {
|
|
|
2766
2816
|
console.log(chalk.gray(" Usage: rebase api-keys revoke <key-id>"));
|
|
2767
2817
|
process.exit(1);
|
|
2768
2818
|
}
|
|
2769
|
-
const
|
|
2770
|
-
const
|
|
2819
|
+
const projectRoot = requireProjectRoot();
|
|
2820
|
+
const env = loadEnv(projectRoot);
|
|
2821
|
+
const baseUrl = resolveBaseUrl(env, projectRoot);
|
|
2771
2822
|
const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;
|
|
2772
2823
|
if (!serviceKey) {
|
|
2773
2824
|
console.error(chalk.red("✗ SERVICE_KEY not found in .env — required for admin operations."));
|
|
@@ -3236,13 +3287,18 @@ function fmtDate(value) {
|
|
|
3236
3287
|
*/
|
|
3237
3288
|
var POLL_INTERVAL_MS = 1500;
|
|
3238
3289
|
var POLL_TIMEOUT_MS = 900 * 1e3;
|
|
3290
|
+
var MAX_SOURCE_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
3239
3291
|
function sleep(ms) {
|
|
3240
3292
|
return new Promise((r) => setTimeout(r, ms));
|
|
3241
3293
|
}
|
|
3242
|
-
function run(cmd, cmdArgs, cwd) {
|
|
3294
|
+
function run(cmd, cmdArgs, cwd, env) {
|
|
3243
3295
|
return new Promise((resolve, reject) => {
|
|
3244
3296
|
const child = spawn(cmd, cmdArgs, {
|
|
3245
3297
|
cwd,
|
|
3298
|
+
env: env ? {
|
|
3299
|
+
...process.env,
|
|
3300
|
+
...env
|
|
3301
|
+
} : void 0,
|
|
3246
3302
|
stdio: [
|
|
3247
3303
|
"ignore",
|
|
3248
3304
|
"ignore",
|
|
@@ -3272,7 +3328,7 @@ async function createSourceTarball(sourceDir) {
|
|
|
3272
3328
|
for (const ignore of [".gitignore", ".rebaseignore"]) if (fs.existsSync(path.join(dir, ignore))) tarArgs.push(`--exclude-from=${ignore}`);
|
|
3273
3329
|
tarArgs.push(".");
|
|
3274
3330
|
try {
|
|
3275
|
-
await run("tar", tarArgs, dir);
|
|
3331
|
+
await run("tar", tarArgs, dir, { COPYFILE_DISABLE: "1" });
|
|
3276
3332
|
} catch (e) {
|
|
3277
3333
|
fail(`Failed to package source: ${e instanceof Error ? e.message : String(e)}`);
|
|
3278
3334
|
}
|
|
@@ -3282,6 +3338,7 @@ async function createSourceTarball(sourceDir) {
|
|
|
3282
3338
|
async function uploadSource(url, token, projectId, tarPath) {
|
|
3283
3339
|
const bytes = fs.readFileSync(tarPath);
|
|
3284
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.");
|
|
3285
3342
|
console.log(chalk.gray(` Uploading source (${sizeMb} MB)...`));
|
|
3286
3343
|
const res = await fetch(`${url}/api/functions/deploy/upload?projectId=${encodeURIComponent(projectId)}`, {
|
|
3287
3344
|
method: "POST",
|
|
@@ -4261,6 +4318,8 @@ ${chalk.green.bold("Options")}
|
|
|
4261
4318
|
${chalk.blue("--secret")} Mark a variable write-only ${chalk.gray("(set)")}
|
|
4262
4319
|
${chalk.blue("--json")} Machine-readable output
|
|
4263
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.")}
|
|
4264
4323
|
`);
|
|
4265
4324
|
}
|
|
4266
4325
|
function printEnvHelpJson() {
|
|
@@ -5169,6 +5228,10 @@ async function webhooksCommand(subcommand, rawArgs) {
|
|
|
5169
5228
|
}
|
|
5170
5229
|
}
|
|
5171
5230
|
async function storageCommand(rawArgs) {
|
|
5231
|
+
const action = rawArgs[2];
|
|
5232
|
+
if (action === "create") return storageCreateCommand(rawArgs);
|
|
5233
|
+
if (action === "attach") return storageAttachCommand(rawArgs);
|
|
5234
|
+
if (action === "--help" || action === "help") return printStorageHelp();
|
|
5172
5235
|
const { client } = await requireClient(rawArgs);
|
|
5173
5236
|
const projectId = await requireProject(rawArgs, client);
|
|
5174
5237
|
try {
|
|
@@ -5193,6 +5256,107 @@ async function storageCommand(rawArgs) {
|
|
|
5193
5256
|
reportError(e, "Failed to list storage");
|
|
5194
5257
|
}
|
|
5195
5258
|
}
|
|
5259
|
+
function printStorageHelp() {
|
|
5260
|
+
console.log("");
|
|
5261
|
+
console.log(chalk.bold(" rebase cloud storage"));
|
|
5262
|
+
console.log("");
|
|
5263
|
+
console.log(" " + chalk.blue.bold("storage") + " List this project's storage");
|
|
5264
|
+
console.log(" " + chalk.blue.bold("storage create") + " Provision platform-managed storage");
|
|
5265
|
+
console.log(" " + chalk.blue.bold("storage attach") + " Attach your own S3-compatible bucket");
|
|
5266
|
+
console.log("");
|
|
5267
|
+
console.log(chalk.gray(" attach options:"));
|
|
5268
|
+
console.log(chalk.gray(" --bucket <name> Bucket name (required)"));
|
|
5269
|
+
console.log(chalk.gray(" --access-key-id <id> Access key ID (required)"));
|
|
5270
|
+
console.log(chalk.gray(" --secret-access-key <s> Secret access key (required)"));
|
|
5271
|
+
console.log(chalk.gray(" --endpoint <url> S3 endpoint; omit for AWS"));
|
|
5272
|
+
console.log(chalk.gray(" --region <region> Region"));
|
|
5273
|
+
console.log(chalk.gray(" --force-path-style Required by MinIO and some gateways"));
|
|
5274
|
+
console.log("");
|
|
5275
|
+
console.log(chalk.gray(" Without either, a tenant falls back to the container filesystem and"));
|
|
5276
|
+
console.log(chalk.gray(" loses uploaded files on its next restart."));
|
|
5277
|
+
console.log("");
|
|
5278
|
+
}
|
|
5279
|
+
async function storageCreateCommand(rawArgs) {
|
|
5280
|
+
const { client } = await requireClient(rawArgs);
|
|
5281
|
+
const projectId = await requireProject(rawArgs, client);
|
|
5282
|
+
try {
|
|
5283
|
+
console.log("");
|
|
5284
|
+
console.log(chalk.gray(" Provisioning managed storage — this creates a bucket and its credentials..."));
|
|
5285
|
+
const res = await client.functions.invoke(`storage-provision/${encodeURIComponent(projectId)}`, void 0, { method: "POST" });
|
|
5286
|
+
const info = res.data ?? res.data;
|
|
5287
|
+
success(`Managed storage provisioned for ${displayProjectRef(rawArgs)}.`);
|
|
5288
|
+
keyValues([
|
|
5289
|
+
["Bucket", info.bucketName],
|
|
5290
|
+
["Region", info.region],
|
|
5291
|
+
["Endpoint", info.endpoint],
|
|
5292
|
+
["Access key", info.accessKeyId]
|
|
5293
|
+
]);
|
|
5294
|
+
console.log("");
|
|
5295
|
+
console.log(chalk.gray(" The secret key is stored encrypted and injected at deploy time; it is not displayed."));
|
|
5296
|
+
console.log(chalk.gray(" Redeploy for the tenant to pick it up: ") + chalk.bold("rebase cloud deploy"));
|
|
5297
|
+
console.log("");
|
|
5298
|
+
} catch (e) {
|
|
5299
|
+
reportError(e, "Failed to provision managed storage");
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
async function storageAttachCommand(rawArgs) {
|
|
5303
|
+
const parsed = arg({
|
|
5304
|
+
"--bucket": String,
|
|
5305
|
+
"--access-key-id": String,
|
|
5306
|
+
"--secret-access-key": String,
|
|
5307
|
+
"--endpoint": String,
|
|
5308
|
+
"--region": String,
|
|
5309
|
+
"--force-path-style": Boolean
|
|
5310
|
+
}, {
|
|
5311
|
+
argv: rawArgs.slice(3),
|
|
5312
|
+
permissive: true
|
|
5313
|
+
});
|
|
5314
|
+
const bucket = parsed["--bucket"];
|
|
5315
|
+
const accessKeyId = parsed["--access-key-id"];
|
|
5316
|
+
const secretAccessKey = parsed["--secret-access-key"];
|
|
5317
|
+
const missing = [
|
|
5318
|
+
!bucket && "--bucket",
|
|
5319
|
+
!accessKeyId && "--access-key-id",
|
|
5320
|
+
!secretAccessKey && "--secret-access-key"
|
|
5321
|
+
].filter(Boolean);
|
|
5322
|
+
if (missing.length > 0) fail(`Missing ${missing.join(", ")}.`, "A bucket without credentials cannot be used, and would be stored as though it could. Run `rebase cloud storage --help` for the full list.");
|
|
5323
|
+
const { client } = await requireClient(rawArgs);
|
|
5324
|
+
const projectId = await requireProject(rawArgs, client);
|
|
5325
|
+
try {
|
|
5326
|
+
const existing = (await client.data.collection("storages").find({
|
|
5327
|
+
where: { project: ["==", projectId] },
|
|
5328
|
+
limit: 1
|
|
5329
|
+
})).data[0];
|
|
5330
|
+
const row = {
|
|
5331
|
+
project: projectId,
|
|
5332
|
+
type: "byos",
|
|
5333
|
+
status: "active",
|
|
5334
|
+
s3Bucket: bucket,
|
|
5335
|
+
s3AccessKeyId: accessKeyId,
|
|
5336
|
+
s3SecretAccessKey: secretAccessKey,
|
|
5337
|
+
bucketName: bucket
|
|
5338
|
+
};
|
|
5339
|
+
if (parsed["--endpoint"]) row.s3Endpoint = parsed["--endpoint"];
|
|
5340
|
+
if (parsed["--region"]) {
|
|
5341
|
+
row.s3Region = parsed["--region"];
|
|
5342
|
+
row.region = parsed["--region"];
|
|
5343
|
+
}
|
|
5344
|
+
if (parsed["--force-path-style"]) row.s3ForcePathStyle = true;
|
|
5345
|
+
if (existing?.id) await client.data.collection("storages").update(String(existing.id), row);
|
|
5346
|
+
else await client.data.collection("storages").create(row);
|
|
5347
|
+
success(`Storage attached to ${displayProjectRef(rawArgs)}.`);
|
|
5348
|
+
keyValues([
|
|
5349
|
+
["Bucket", bucket],
|
|
5350
|
+
["Endpoint", parsed["--endpoint"] ?? "AWS S3"],
|
|
5351
|
+
["Region", parsed["--region"] ?? "(default)"]
|
|
5352
|
+
]);
|
|
5353
|
+
console.log("");
|
|
5354
|
+
console.log(chalk.gray(" Redeploy for the tenant to pick it up: ") + chalk.bold("rebase cloud deploy"));
|
|
5355
|
+
console.log("");
|
|
5356
|
+
} catch (e) {
|
|
5357
|
+
reportError(e, "Failed to attach storage");
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5196
5360
|
async function clustersCommand(rawArgs) {
|
|
5197
5361
|
const { client } = await requireClient(rawArgs);
|
|
5198
5362
|
try {
|
|
@@ -5513,6 +5677,8 @@ ${chalk.green.bold("Databases")}
|
|
|
5513
5677
|
${chalk.green.bold("Other resources")}
|
|
5514
5678
|
${chalk.blue.bold("webhooks list|create|delete")}
|
|
5515
5679
|
${chalk.blue.bold("storage")} List storage buckets
|
|
5680
|
+
${chalk.blue.bold("storage create")} Provision platform-managed storage
|
|
5681
|
+
${chalk.blue.bold("storage attach")} Attach your own S3-compatible bucket
|
|
5516
5682
|
${chalk.blue.bold("clusters")} List compute clusters
|
|
5517
5683
|
${chalk.blue.bold("billing setup")} Attach a card to the org ${chalk.gray("(one-time, opens browser)")}
|
|
5518
5684
|
${chalk.blue.bold("billing")} Show billing account + card on file
|
|
@@ -5621,9 +5787,10 @@ async function entry(args) {
|
|
|
5621
5787
|
await cloudCommand(effectiveSubcommand, args);
|
|
5622
5788
|
break;
|
|
5623
5789
|
default:
|
|
5624
|
-
console.
|
|
5790
|
+
console.error(chalk.red(`Unknown command: ${command}`));
|
|
5625
5791
|
console.log("");
|
|
5626
5792
|
printHelp();
|
|
5793
|
+
process.exit(1);
|
|
5627
5794
|
}
|
|
5628
5795
|
}
|
|
5629
5796
|
function printHelp() {
|