@batadata/cli 0.1.14 → 0.1.15
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/claim.d.ts +15 -0
- package/dist/commands/claim.js +81 -0
- package/dist/commands/new.d.ts +17 -0
- package/dist/commands/new.js +90 -0
- package/dist/index.js +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bata claim <token> — redeem a claimable database into your team.
|
|
3
|
+
*
|
|
4
|
+
* Authed. Resolves the team (like `bata create`), calls
|
|
5
|
+
* POST /v1/claim/redeem { claim_token, team_id }, and on success the project
|
|
6
|
+
* becomes a permanent member of your team (its TTL is cleared). Typed,
|
|
7
|
+
* agent-friendly errors for unknown / expired / already-claimed tokens.
|
|
8
|
+
*/
|
|
9
|
+
export interface ClaimArgs {
|
|
10
|
+
token?: string;
|
|
11
|
+
help: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Parse `bata claim <token>` args. Exported for tests. */
|
|
14
|
+
export declare function parseClaimArgs(args: string[]): ClaimArgs;
|
|
15
|
+
export declare function claim(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bata claim <token> — redeem a claimable database into your team.
|
|
3
|
+
*
|
|
4
|
+
* Authed. Resolves the team (like `bata create`), calls
|
|
5
|
+
* POST /v1/claim/redeem { claim_token, team_id }, and on success the project
|
|
6
|
+
* becomes a permanent member of your team (its TTL is cleared). Typed,
|
|
7
|
+
* agent-friendly errors for unknown / expired / already-claimed tokens.
|
|
8
|
+
*/
|
|
9
|
+
import { api, apiError, resolveTeamId } from "../api.js";
|
|
10
|
+
import { requireToken, saveConfig, isJsonMode } from "../config.js";
|
|
11
|
+
import { colors, log, json, success, kvList } from "../utils/logger.js";
|
|
12
|
+
import { emitError } from "../utils/errors.js";
|
|
13
|
+
/** Parse `bata claim <token>` args. Exported for tests. */
|
|
14
|
+
export function parseClaimArgs(args) {
|
|
15
|
+
let token;
|
|
16
|
+
for (const arg of args) {
|
|
17
|
+
if (arg === "--help" || arg === "-h")
|
|
18
|
+
return { help: true };
|
|
19
|
+
if (!arg.startsWith("-") && !token)
|
|
20
|
+
token = arg;
|
|
21
|
+
}
|
|
22
|
+
return { token, help: false };
|
|
23
|
+
}
|
|
24
|
+
function printHelp() {
|
|
25
|
+
log();
|
|
26
|
+
log(` ${colors.bold("bata claim")} ${colors.dim("<token>")}`);
|
|
27
|
+
log();
|
|
28
|
+
log(` Claim an instant database (from ${colors.cyan("bata new")}) into your team.`);
|
|
29
|
+
log(` Requires login — pass ${colors.cyan("--api-key")}, set ${colors.cyan("BATA_API_KEY")}, or run ${colors.cyan("bata login")}.`);
|
|
30
|
+
log();
|
|
31
|
+
log(` ${colors.bold("Examples")}`);
|
|
32
|
+
log(` ${colors.cyan("bata claim claim_EXAMPLE_TOKEN")}`);
|
|
33
|
+
log(` ${colors.cyan("bata claim claim_EXAMPLE_TOKEN --json")}`);
|
|
34
|
+
log();
|
|
35
|
+
}
|
|
36
|
+
export async function claim(args) {
|
|
37
|
+
const parsed = parseClaimArgs(args);
|
|
38
|
+
if (parsed.help) {
|
|
39
|
+
printHelp();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!parsed.token) {
|
|
43
|
+
emitError("MISSING_ARG", "A claim token is required. Usage: bata claim <token>", "Get one from `bata new`.");
|
|
44
|
+
}
|
|
45
|
+
const token = requireToken();
|
|
46
|
+
const teamId = await resolveTeamId(token);
|
|
47
|
+
if (!teamId) {
|
|
48
|
+
emitError("NO_TEAM", "No team found for this credential.", "Run `bata login` or check your API key.");
|
|
49
|
+
}
|
|
50
|
+
const res = await api.post("/v1/claim/redeem", { claim_token: parsed.token, team_id: teamId }, token);
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
const data = res.data;
|
|
53
|
+
const code = data?.code;
|
|
54
|
+
if (code === "CLAIM_TOKEN_UNKNOWN") {
|
|
55
|
+
emitError("NOT_FOUND", "Unknown or invalid claim token.", "Double-check the token from `bata new`.");
|
|
56
|
+
}
|
|
57
|
+
if (code === "CLAIM_EXPIRED") {
|
|
58
|
+
emitError("NOT_FOUND", "This database has expired and been deleted.", "Create a fresh one with `bata new`.");
|
|
59
|
+
}
|
|
60
|
+
if (code === "CLAIM_ALREADY_CLAIMED") {
|
|
61
|
+
emitError("NOT_FOUND", "This database has already been claimed.", "");
|
|
62
|
+
}
|
|
63
|
+
emitError("CLI_ERROR", apiError(res, "Failed to claim database"));
|
|
64
|
+
}
|
|
65
|
+
const claimed = res.data;
|
|
66
|
+
saveConfig({ defaultProject: claimed.project_id });
|
|
67
|
+
if (isJsonMode()) {
|
|
68
|
+
json(claimed);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
log();
|
|
72
|
+
success(`Claimed ${colors.cyan(claimed.name)} into your team.`);
|
|
73
|
+
log();
|
|
74
|
+
kvList([
|
|
75
|
+
["Project ID", colors.dim(claimed.project_id)],
|
|
76
|
+
["Team", colors.dim(claimed.team_id)],
|
|
77
|
+
]);
|
|
78
|
+
log();
|
|
79
|
+
log(` ${colors.dim("Connect:")} ${colors.cyan("bata db url")}`);
|
|
80
|
+
log();
|
|
81
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bata new — instant, no-signup Postgres (bata.new / claimable databases).
|
|
3
|
+
*
|
|
4
|
+
* Calls the PUBLIC POST /v1/claim/new (no credentials needed), prints the
|
|
5
|
+
* connection string, the expiry, and how to claim the database into a real
|
|
6
|
+
* team later. `--json` emits the full payload (including the one-time claim
|
|
7
|
+
* token) for agents.
|
|
8
|
+
*
|
|
9
|
+
* Honest wording: an UNCLAIMED database is permanently DELETED at its expiry.
|
|
10
|
+
*/
|
|
11
|
+
export interface NewArgs {
|
|
12
|
+
help: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Parse `bata new` args. No positional/flags today beyond --help (global
|
|
15
|
+
* flags like --json are stripped upstream in index.ts). Exported for tests. */
|
|
16
|
+
export declare function parseNewArgs(args: string[]): NewArgs;
|
|
17
|
+
export declare function newDb(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bata new — instant, no-signup Postgres (bata.new / claimable databases).
|
|
3
|
+
*
|
|
4
|
+
* Calls the PUBLIC POST /v1/claim/new (no credentials needed), prints the
|
|
5
|
+
* connection string, the expiry, and how to claim the database into a real
|
|
6
|
+
* team later. `--json` emits the full payload (including the one-time claim
|
|
7
|
+
* token) for agents.
|
|
8
|
+
*
|
|
9
|
+
* Honest wording: an UNCLAIMED database is permanently DELETED at its expiry.
|
|
10
|
+
*/
|
|
11
|
+
import { api, apiError } from "../api.js";
|
|
12
|
+
import { isJsonMode } from "../config.js";
|
|
13
|
+
import { colors, log, json, success, kvList } from "../utils/logger.js";
|
|
14
|
+
import { emitError } from "../utils/errors.js";
|
|
15
|
+
/** Parse `bata new` args. No positional/flags today beyond --help (global
|
|
16
|
+
* flags like --json are stripped upstream in index.ts). Exported for tests. */
|
|
17
|
+
export function parseNewArgs(args) {
|
|
18
|
+
for (const arg of args) {
|
|
19
|
+
if (arg === "--help" || arg === "-h")
|
|
20
|
+
return { help: true };
|
|
21
|
+
}
|
|
22
|
+
return { help: false };
|
|
23
|
+
}
|
|
24
|
+
function printHelp() {
|
|
25
|
+
log();
|
|
26
|
+
log(` ${colors.bold("bata new")}`);
|
|
27
|
+
log();
|
|
28
|
+
log(` Create an instant, no-signup Postgres database and print how to connect.`);
|
|
29
|
+
log(` You get a connection string immediately plus a one-time claim token.`);
|
|
30
|
+
log();
|
|
31
|
+
log(` ${colors.bold("Claim it later")}`);
|
|
32
|
+
log(` ${colors.cyan("bata claim <token>")} Move the database into your team (clears the expiry)`);
|
|
33
|
+
log();
|
|
34
|
+
log(` ${colors.yellow("Unclaimed databases are permanently DELETED at their expiry.")}`);
|
|
35
|
+
log();
|
|
36
|
+
log(` ${colors.bold("Options")}`);
|
|
37
|
+
log(` ${colors.dim("--json")} Machine-readable output (includes the claim token)`);
|
|
38
|
+
log(` ${colors.dim("--help, -h")} Show this help`);
|
|
39
|
+
log();
|
|
40
|
+
}
|
|
41
|
+
export async function newDb(args) {
|
|
42
|
+
const parsed = parseNewArgs(args);
|
|
43
|
+
if (parsed.help) {
|
|
44
|
+
printHelp();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// No credentials: this hits the PUBLIC endpoint.
|
|
48
|
+
const res = await api.post("/v1/claim/new", {});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const data = res.data;
|
|
51
|
+
const code = data?.code;
|
|
52
|
+
if (res.status === 404 && code === "CLAIMABLE_DISABLED") {
|
|
53
|
+
emitError("NOT_FOUND", "Instant databases (bata.new) are not enabled on this server.", "Contact the operator, or create a project with `bata create` after `bata login`.");
|
|
54
|
+
}
|
|
55
|
+
if (res.status === 503 || code === "CLAIM_POOL_EXHAUSTED") {
|
|
56
|
+
emitError("API_UNAVAILABLE", "The free database pool is full right now. Try again shortly.", "This is transient — retry in a minute.");
|
|
57
|
+
}
|
|
58
|
+
if (res.status === 429) {
|
|
59
|
+
emitError("API_UNAVAILABLE", "Rate limit reached for instant databases from this network.", "Wait a bit before creating another, or claim one you already made.");
|
|
60
|
+
}
|
|
61
|
+
emitError("CLI_ERROR", apiError(res, "Failed to create instant database"));
|
|
62
|
+
}
|
|
63
|
+
const db = res.data;
|
|
64
|
+
if (isJsonMode()) {
|
|
65
|
+
json(db);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
log();
|
|
69
|
+
success("Your instant database is ready to claim!");
|
|
70
|
+
log();
|
|
71
|
+
kvList([
|
|
72
|
+
["Project ID", colors.dim(db.project_id)],
|
|
73
|
+
["Expires", db.expires_at],
|
|
74
|
+
]);
|
|
75
|
+
log();
|
|
76
|
+
log(` ${colors.bold("Connection")}`);
|
|
77
|
+
log();
|
|
78
|
+
kvList([
|
|
79
|
+
["Direct", colors.dim(db.connection.direct)],
|
|
80
|
+
["Pooled", colors.dim(db.connection.pooled)],
|
|
81
|
+
]);
|
|
82
|
+
log();
|
|
83
|
+
log(` ${colors.bold("Claim token")} ${colors.dim("(shown once)")}`);
|
|
84
|
+
log(` ${colors.cyan(db.claim_token)}`);
|
|
85
|
+
log();
|
|
86
|
+
log(` ${colors.dim("Claim it into your team:")} ${colors.cyan(`bata claim ${db.claim_token}`)}`);
|
|
87
|
+
log(` ${colors.yellow("Unclaimed databases are permanently DELETED at expiry.")}`);
|
|
88
|
+
log(` ${colors.dim("First connection may take a few seconds while the compute wakes.")}`);
|
|
89
|
+
log();
|
|
90
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,8 @@ import { handleMigrate } from "./commands/migrate.js";
|
|
|
8
8
|
import { handleApiKeys } from "./commands/api-keys.js";
|
|
9
9
|
import { dev } from "./commands/dev.js";
|
|
10
10
|
import { create } from "./commands/create.js";
|
|
11
|
+
import { newDb } from "./commands/new.js";
|
|
12
|
+
import { claim } from "./commands/claim.js";
|
|
11
13
|
import { status } from "./commands/status.js";
|
|
12
14
|
import { connect } from "./commands/connect.js";
|
|
13
15
|
import { usage } from "./commands/usage.js";
|
|
@@ -25,6 +27,8 @@ function help() {
|
|
|
25
27
|
log(` ${colors.cyan("bata")} ${colors.dim("<command>")} ${colors.dim("[options]")}`);
|
|
26
28
|
log();
|
|
27
29
|
log(` ${colors.bold("Quick Start")}`);
|
|
30
|
+
log(` ${colors.cyan("new")} Instant no-signup database ${colors.dim("(no login) — claim it later")}`);
|
|
31
|
+
log(` ${colors.cyan("claim <token>")} Claim a ` + "`bata new`" + ` database into your team`);
|
|
28
32
|
log(` ${colors.cyan("create <name>")} Create a project and wait for it to be ready`);
|
|
29
33
|
log(` ${colors.cyan("connect <name>")} Open psql to a project (auto-wakes if suspended)`);
|
|
30
34
|
log(` ${colors.cyan("status")} Show all projects and their status`);
|
|
@@ -126,6 +130,12 @@ async function main() {
|
|
|
126
130
|
case "create":
|
|
127
131
|
await create(rest);
|
|
128
132
|
break;
|
|
133
|
+
case "new":
|
|
134
|
+
await newDb(rest);
|
|
135
|
+
break;
|
|
136
|
+
case "claim":
|
|
137
|
+
await claim(rest);
|
|
138
|
+
break;
|
|
129
139
|
case "status":
|
|
130
140
|
await status();
|
|
131
141
|
break;
|