@batadata/cli 0.1.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/api.d.ts +28 -0
- package/dist/api.js +115 -0
- package/dist/api.js.map +1 -0
- package/dist/args.d.ts +20 -0
- package/dist/args.js +42 -0
- package/dist/args.js.map +1 -0
- package/dist/commands/api-keys.d.ts +11 -0
- package/dist/commands/api-keys.js +223 -0
- package/dist/commands/api-keys.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.js +133 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/connect.d.ts +7 -0
- package/dist/commands/connect.js +103 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/create.d.ts +7 -0
- package/dist/commands/create.js +197 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/db.d.ts +8 -0
- package/dist/commands/db.js +316 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +28 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/generate.d.ts +1 -0
- package/dist/commands/generate.js +83 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/migrate.d.ts +1 -0
- package/dist/commands/migrate.js +38 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/projects.d.ts +5 -0
- package/dist/commands/projects.js +259 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/schema.d.ts +1 -0
- package/dist/commands/schema.js +38 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.js +106 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config.d.ts +45 -0
- package/dist/config.js +96 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +169 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/logger.d.ts +26 -0
- package/dist/utils/logger.js +130 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/open.d.ts +1 -0
- package/dist/utils/open.js +20 -0
- package/dist/utils/open.js.map +1 -0
- package/dist/utils/prompts.d.ts +7 -0
- package/dist/utils/prompts.js +92 -0
- package/dist/utils/prompts.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bata connect <project-name> — Quick psql connection.
|
|
3
|
+
*
|
|
4
|
+
* Fetches the connection string from the API, auto-wakes suspended computes,
|
|
5
|
+
* and execs psql with the connection string.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync, spawn } from "node:child_process";
|
|
8
|
+
import { api } from "../api.js";
|
|
9
|
+
import { requireToken, loadConfig } from "../config.js";
|
|
10
|
+
import { colors, log, error, spinner, info as logInfo } from "../utils/logger.js";
|
|
11
|
+
export async function connect(args) {
|
|
12
|
+
const token = requireToken();
|
|
13
|
+
const config = loadConfig();
|
|
14
|
+
// Accept project name as argument, or use default
|
|
15
|
+
let projectId = config.defaultProject;
|
|
16
|
+
const projectName = args[0];
|
|
17
|
+
if (projectName && !projectName.startsWith("-")) {
|
|
18
|
+
// Resolve project name to ID
|
|
19
|
+
const s = spinner("Looking up project");
|
|
20
|
+
const query = {};
|
|
21
|
+
if (config.defaultTeam)
|
|
22
|
+
query.team_id = config.defaultTeam;
|
|
23
|
+
const projRes = await api.get("/v1/projects", token, query);
|
|
24
|
+
s.stop();
|
|
25
|
+
if (!projRes.ok) {
|
|
26
|
+
error("Failed to fetch projects.");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const projects = Array.isArray(projRes.data) ? projRes.data : [];
|
|
30
|
+
const found = projects.find((p) => p.name === projectName || p.id === projectName);
|
|
31
|
+
if (!found) {
|
|
32
|
+
error(`Project "${projectName}" not found.`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
projectId = found.id;
|
|
36
|
+
}
|
|
37
|
+
if (!projectId) {
|
|
38
|
+
error("No project specified. Usage: bata connect <project-name>");
|
|
39
|
+
log(` Or set a default with ${colors.cyan("bata create <name>")}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const s = spinner("Fetching connection info");
|
|
43
|
+
const connRes = await api.get(`/v1/connection-info/${projectId}`, token);
|
|
44
|
+
if (!connRes.ok || !connRes.data.connections?.length) {
|
|
45
|
+
s.stop();
|
|
46
|
+
error("Could not fetch connection string for this project.");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const primary = connRes.data.connections.find((c) => c.is_primary)
|
|
50
|
+
?? connRes.data.connections[0];
|
|
51
|
+
// Auto-wake suspended compute
|
|
52
|
+
if (primary.compute_status === "suspended") {
|
|
53
|
+
s.update("Waking suspended compute");
|
|
54
|
+
// Start the compute by calling the start endpoint
|
|
55
|
+
const query = {};
|
|
56
|
+
if (config.defaultTeam)
|
|
57
|
+
query.team_id = config.defaultTeam;
|
|
58
|
+
const startRes = await api.post(`/v1/computes/${primary.branch_id}/start`, {}, token);
|
|
59
|
+
if (!startRes.ok) {
|
|
60
|
+
// Non-fatal: connection might still work if proxy auto-wakes
|
|
61
|
+
log();
|
|
62
|
+
log(` ${colors.yellow("!")} Could not auto-wake compute. The connection proxy may wake it on demand.`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Wait a bit for the compute to start
|
|
66
|
+
await sleep(3000);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
s.stop();
|
|
70
|
+
const connectionUri = primary.direct;
|
|
71
|
+
if (!connectionUri) {
|
|
72
|
+
error("No connection string available for this project.");
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
log();
|
|
76
|
+
logInfo(`Connecting to ${colors.cyan(connRes.data.project_name)}`);
|
|
77
|
+
log(` ${colors.dim(connectionUri)}`);
|
|
78
|
+
log();
|
|
79
|
+
// Check if psql is available
|
|
80
|
+
try {
|
|
81
|
+
execSync("which psql", { stdio: "ignore" });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
error("psql not found. Install PostgreSQL client tools to use bata connect.");
|
|
85
|
+
log(` ${colors.dim("macOS:")} ${colors.cyan("brew install libpq")}`);
|
|
86
|
+
log(` ${colors.dim("Ubuntu:")} ${colors.cyan("sudo apt install postgresql-client")}`);
|
|
87
|
+
log();
|
|
88
|
+
log(` ${colors.dim("Or use your connection string directly:")}`);
|
|
89
|
+
log(` ${connectionUri}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
// Spawn psql interactively
|
|
93
|
+
const child = spawn("psql", [connectionUri], {
|
|
94
|
+
stdio: "inherit",
|
|
95
|
+
});
|
|
96
|
+
child.on("exit", (code) => {
|
|
97
|
+
process.exit(code || 0);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function sleep(ms) {
|
|
101
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAmBlF,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAc;IAC1C,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,kDAAkD;IAClD,IAAI,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;IACtC,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAE5B,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,6BAA6B;QAC7B,MAAM,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACxC,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,IAAI,MAAM,CAAC,WAAW;YAAE,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC;QAE3D,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,GAAG,CAAY,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC,CAAC,IAAI,EAAE,CAAC;QAET,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YAChB,KAAK,CAAC,2BAA2B,CAAC,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,EAAE,KAAK,WAAW,CACtD,CAAC;QAEF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,CAAC,YAAY,WAAW,cAAc,CAAC,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAClE,GAAG,CAAC,2BAA2B,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,GAAG,CAC3B,uBAAuB,SAAS,EAAE,EAClC,KAAK,CACN,CAAC;IAEF,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;QACrD,CAAC,CAAC,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,qDAAqD,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;WAC7D,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAEjC,8BAA8B;IAC9B,IAAI,OAAO,CAAC,cAAc,KAAK,WAAW,EAAE,CAAC;QAC3C,CAAC,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;QAErC,kDAAkD;QAClD,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,IAAI,MAAM,CAAC,WAAW;YAAE,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC;QAE3D,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAC7B,gBAAgB,OAAO,CAAC,SAAS,QAAQ,EACzC,EAAE,EACF,KAAK,CACN,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,6DAA6D;YAC7D,GAAG,EAAE,CAAC;YACN,GAAG,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;QAC1G,CAAC;aAAM,CAAC;YACN,sCAAsC;YACtC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,CAAC,CAAC,IAAI,EAAE,CAAC;IAET,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IACrC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,GAAG,EAAE,CAAC;IACN,OAAO,CAAC,iBAAiB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACnE,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACtC,GAAG,EAAE,CAAC;IAEN,6BAA6B;IAC7B,IAAI,CAAC;QACH,QAAQ,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,CAAC,sEAAsE,CAAC,CAAC;QAC9E,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;QACtE,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,EAAE,CAAC,CAAC;QACvF,GAAG,EAAE,CAAC;QACN,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,yCAAyC,CAAC,EAAE,CAAC,CAAC;QAClE,GAAG,CAAC,KAAK,aAAa,EAAE,CAAC,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,2BAA2B;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,EAAE;QAC3C,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bata create <project-name> — One-command project creation.
|
|
3
|
+
*
|
|
4
|
+
* Creates a project, waits for the compute to be ready, and prints the
|
|
5
|
+
* connection string. Designed for maximum speed-to-database.
|
|
6
|
+
*/
|
|
7
|
+
export declare function create(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bata create <project-name> — One-command project creation.
|
|
3
|
+
*
|
|
4
|
+
* Creates a project, waits for the compute to be ready, and prints the
|
|
5
|
+
* connection string. Designed for maximum speed-to-database.
|
|
6
|
+
*/
|
|
7
|
+
import { api, apiError, resolveTeamId, asList } from "../api.js";
|
|
8
|
+
import { requireToken, saveConfig, isJsonMode } from "../config.js";
|
|
9
|
+
import { colors, log, json, success, error, spinner, kvList, heading } from "../utils/logger.js";
|
|
10
|
+
function parseArgs(args) {
|
|
11
|
+
let name = "";
|
|
12
|
+
let region;
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg === "--region" && args[i + 1]) {
|
|
16
|
+
region = args[++i];
|
|
17
|
+
}
|
|
18
|
+
else if (arg === "--help" || arg === "-h") {
|
|
19
|
+
printHelp();
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
else if (!arg.startsWith("-") && !name) {
|
|
23
|
+
name = arg;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { name, region };
|
|
27
|
+
}
|
|
28
|
+
function printHelp() {
|
|
29
|
+
log();
|
|
30
|
+
log(` ${colors.bold("bata create")} ${colors.dim("<project-name>")}`);
|
|
31
|
+
log();
|
|
32
|
+
log(` Create a new BataDB project and wait for it to be ready.`);
|
|
33
|
+
log();
|
|
34
|
+
log(` ${colors.bold("Options")}`);
|
|
35
|
+
log(` ${colors.dim("--region <region>")} Region (default: us-east-1)`);
|
|
36
|
+
log(` ${colors.dim("--help, -h")} Show this help`);
|
|
37
|
+
log();
|
|
38
|
+
log(` ${colors.bold("Global")} ${colors.dim("(any command)")}`);
|
|
39
|
+
log(` ${colors.dim("--api-url <url>")} API URL (else BATA_API_URL or config)`);
|
|
40
|
+
log(` ${colors.dim("--api-key <key>")} API key (else BATA_API_KEY or saved login)`);
|
|
41
|
+
log(` ${colors.dim("--json")} Machine-readable output`);
|
|
42
|
+
log();
|
|
43
|
+
log(` ${colors.bold("Examples")}`);
|
|
44
|
+
log(` ${colors.cyan("bata create my-app")}`);
|
|
45
|
+
log(` ${colors.cyan("bata create my-app --region ams")}`);
|
|
46
|
+
log(` ${colors.cyan("BATA_API_KEY=bata_xxx bata create my-app")}`);
|
|
47
|
+
log();
|
|
48
|
+
}
|
|
49
|
+
export async function create(args) {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
const parsed = parseArgs(args);
|
|
52
|
+
if (!parsed.name) {
|
|
53
|
+
error("Project name is required. Usage: bata create <project-name>");
|
|
54
|
+
log(` Run ${colors.cyan("bata create --help")} for more options.`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Auth + API URL are resolved centrally (config.ts): --api-key/--api-url
|
|
58
|
+
// flags > BATA_API_KEY/BATA_API_URL env > saved config > default.
|
|
59
|
+
const token = requireToken();
|
|
60
|
+
heading(`Creating project: ${parsed.name}`);
|
|
61
|
+
const s = spinner("Creating project");
|
|
62
|
+
// Resolve the team even headlessly (the API requires it). Falls back to the
|
|
63
|
+
// user's first team when there's no saved defaultTeam.
|
|
64
|
+
const teamId = await resolveTeamId(token);
|
|
65
|
+
// Idempotent: if a project with this name already exists, reuse it instead
|
|
66
|
+
// of erroring on a duplicate — so an agent can safely re-run `bata create`.
|
|
67
|
+
const listed = await api.get("/v1/projects", token, teamId ? { team_id: teamId } : undefined);
|
|
68
|
+
const existing = listed.ok
|
|
69
|
+
? asList(listed.data).find((p) => p.name === parsed.name)
|
|
70
|
+
: undefined;
|
|
71
|
+
let project;
|
|
72
|
+
if (existing) {
|
|
73
|
+
project = existing;
|
|
74
|
+
saveConfig({ defaultProject: project.id });
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const body = {
|
|
78
|
+
name: parsed.name,
|
|
79
|
+
// us-east-1 is the platform's canonical region (the server default and the
|
|
80
|
+
// path verified working end-to-end). The Fly DC is iad but the project
|
|
81
|
+
// region label is us-east-1.
|
|
82
|
+
region: parsed.region ?? "us-east-1",
|
|
83
|
+
};
|
|
84
|
+
if (teamId) {
|
|
85
|
+
body.team_id = teamId;
|
|
86
|
+
}
|
|
87
|
+
const res = await api.post("/v1/projects", body, token);
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
s.stop();
|
|
90
|
+
if (isJsonMode())
|
|
91
|
+
json({ error: apiError(res, "Failed to create project") });
|
|
92
|
+
else
|
|
93
|
+
error(apiError(res, "Failed to create project"));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
project = res.data;
|
|
97
|
+
saveConfig({ defaultProject: project.id });
|
|
98
|
+
}
|
|
99
|
+
s.update("Waiting for compute to be ready");
|
|
100
|
+
// Poll operations until the create_project operation completes
|
|
101
|
+
const operationId = project.operations?.[0]?.id;
|
|
102
|
+
if (operationId) {
|
|
103
|
+
const ready = await pollOperation(operationId, token, s, teamId);
|
|
104
|
+
if (!ready) {
|
|
105
|
+
s.stop();
|
|
106
|
+
error("Project created but compute did not become ready within 120 seconds.");
|
|
107
|
+
log(` Project ID: ${colors.dim(project.id)}`);
|
|
108
|
+
log(` Check status with: ${colors.cyan("bata status")}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// No operation ID — wait a few seconds then check
|
|
114
|
+
await sleep(3000);
|
|
115
|
+
}
|
|
116
|
+
s.update("Fetching connection info");
|
|
117
|
+
// Fetch connection info (reveal=true so the returned string is usable —
|
|
118
|
+
// an agent needs the real password to connect, not a masked one).
|
|
119
|
+
const connRes = await api.get(`/v1/connection-info/${project.id}`, token, { reveal: "true" });
|
|
120
|
+
s.stop();
|
|
121
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
122
|
+
if (isJsonMode()) {
|
|
123
|
+
const conn = connRes.ok ? connRes.data.connections?.[0] : undefined;
|
|
124
|
+
json({
|
|
125
|
+
id: project.id,
|
|
126
|
+
name: parsed.name,
|
|
127
|
+
region: project.region || parsed.region || "us-east-1",
|
|
128
|
+
status: "active",
|
|
129
|
+
existing: Boolean(existing),
|
|
130
|
+
connection: conn
|
|
131
|
+
? { direct: conn.direct ?? null, pooled: conn.pooled ?? null, params: conn.params ?? null }
|
|
132
|
+
: null,
|
|
133
|
+
elapsed_seconds: Number(elapsed),
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
log();
|
|
138
|
+
success(`Project ${colors.cyan(parsed.name)} is ready!`);
|
|
139
|
+
log();
|
|
140
|
+
kvList([
|
|
141
|
+
["Project ID", colors.dim(project.id)],
|
|
142
|
+
["Region", project.region || parsed.region || "us-east-1"],
|
|
143
|
+
["Status", colors.green("active")],
|
|
144
|
+
]);
|
|
145
|
+
if (connRes.ok && connRes.data.connections?.length > 0) {
|
|
146
|
+
const conn = connRes.data.connections[0];
|
|
147
|
+
log();
|
|
148
|
+
log(` ${colors.bold("Connection")}`);
|
|
149
|
+
log();
|
|
150
|
+
if (conn.direct) {
|
|
151
|
+
kvList([
|
|
152
|
+
["Direct", colors.dim(conn.direct)],
|
|
153
|
+
["Pooled", colors.dim(conn.pooled ?? "-")],
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
if (conn.params) {
|
|
157
|
+
log();
|
|
158
|
+
kvList([
|
|
159
|
+
["Host", conn.params.host],
|
|
160
|
+
["Port", String(conn.params.port)],
|
|
161
|
+
["Database", conn.params.database],
|
|
162
|
+
["User", conn.params.user],
|
|
163
|
+
["SSL", conn.params.sslmode ?? "require"],
|
|
164
|
+
]);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
log();
|
|
168
|
+
log(` ${colors.dim("Connect:")} ${colors.cyan("bata db connect")}`);
|
|
169
|
+
log(` ${colors.dim("Total time:")} ${elapsed}s`);
|
|
170
|
+
log();
|
|
171
|
+
}
|
|
172
|
+
async function pollOperation(operationId, token, s, teamId) {
|
|
173
|
+
const maxWait = 120_000; // 2 minutes
|
|
174
|
+
const pollInterval = 2_000;
|
|
175
|
+
const start = Date.now();
|
|
176
|
+
while (Date.now() - start < maxWait) {
|
|
177
|
+
const query = {};
|
|
178
|
+
if (teamId)
|
|
179
|
+
query.team_id = teamId;
|
|
180
|
+
const res = await api.get(`/v1/operations/${operationId}`, token, query);
|
|
181
|
+
if (res.ok) {
|
|
182
|
+
const op = res.data;
|
|
183
|
+
if (op.status === "completed")
|
|
184
|
+
return true;
|
|
185
|
+
if (op.status === "failed") {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
s.update(`Waiting for compute (${op.status})`);
|
|
189
|
+
}
|
|
190
|
+
await sleep(pollInterval);
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
function sleep(ms) {
|
|
195
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function connect(): Promise<void>;
|
|
2
|
+
export declare function url(): Promise<void>;
|
|
3
|
+
export declare function branches(): Promise<void>;
|
|
4
|
+
export declare function branchCreate(name?: string): Promise<void>;
|
|
5
|
+
export declare function branchDelete(name?: string): Promise<void>;
|
|
6
|
+
export declare function studio(): Promise<void>;
|
|
7
|
+
export declare function query(sql?: string): Promise<void>;
|
|
8
|
+
export declare function handleDb(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
2
|
+
import { api, apiError } from "../api.js";
|
|
3
|
+
import { requireToken, loadConfig, isJsonMode } from "../config.js";
|
|
4
|
+
import { colors, log, json, error, spinner, table, heading, info as logInfo } from "../utils/logger.js";
|
|
5
|
+
import { prompt, confirm } from "../utils/prompts.js";
|
|
6
|
+
import { openBrowser } from "../utils/open.js";
|
|
7
|
+
async function getConnectionInfo(projectId, token) {
|
|
8
|
+
// reveal=true so the returned string is actually usable (the owner is asking).
|
|
9
|
+
const res = await api.get(`/v1/connection-info/${projectId}`, token, { reveal: "true" });
|
|
10
|
+
if (!res.ok)
|
|
11
|
+
return null;
|
|
12
|
+
const conns = res.data?.connections ?? [];
|
|
13
|
+
const c = conns.find((x) => x.is_primary) ?? conns[0];
|
|
14
|
+
if (!c)
|
|
15
|
+
return null;
|
|
16
|
+
return {
|
|
17
|
+
connection_uri: c.direct,
|
|
18
|
+
pooled_uri: c.pooled,
|
|
19
|
+
host: c.params?.host,
|
|
20
|
+
port: c.params?.port,
|
|
21
|
+
database: c.params?.database,
|
|
22
|
+
user: c.params?.user,
|
|
23
|
+
password: c.params?.password,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function formatDate(iso) {
|
|
27
|
+
if (!iso)
|
|
28
|
+
return "-";
|
|
29
|
+
const d = new Date(iso);
|
|
30
|
+
return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
31
|
+
}
|
|
32
|
+
export async function connect() {
|
|
33
|
+
const token = requireToken();
|
|
34
|
+
const config = loadConfig();
|
|
35
|
+
const projectId = config.defaultProject;
|
|
36
|
+
if (!projectId) {
|
|
37
|
+
error("No default project. Run bata projects create or set one with bata projects info <id>.");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const s = spinner("Fetching connection info");
|
|
41
|
+
const conn = await getConnectionInfo(projectId, token);
|
|
42
|
+
s.stop();
|
|
43
|
+
if (!conn || !conn.connection_uri) {
|
|
44
|
+
error("Could not fetch connection string for this project.");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
log();
|
|
48
|
+
logInfo(`Connecting to project ${colors.cyan(projectId)}`);
|
|
49
|
+
log(` ${colors.dim(conn.connection_uri)}`);
|
|
50
|
+
log();
|
|
51
|
+
// Check if psql is available
|
|
52
|
+
try {
|
|
53
|
+
execSync("which psql", { stdio: "ignore" });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
error("psql not found. Install PostgreSQL client to use bata db connect.");
|
|
57
|
+
log(` ${colors.dim("Connection string:")} ${conn.connection_uri}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
// Spawn psql interactively
|
|
61
|
+
const child = spawn("psql", [conn.connection_uri], {
|
|
62
|
+
stdio: "inherit",
|
|
63
|
+
});
|
|
64
|
+
child.on("exit", (code) => {
|
|
65
|
+
process.exit(code || 0);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export async function url() {
|
|
69
|
+
const token = requireToken();
|
|
70
|
+
const config = loadConfig();
|
|
71
|
+
const projectId = config.defaultProject;
|
|
72
|
+
if (!projectId) {
|
|
73
|
+
error("No default project set.");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const conn = await getConnectionInfo(projectId, token);
|
|
77
|
+
if (!conn || !conn.connection_uri) {
|
|
78
|
+
const msg = "Could not fetch connection string.";
|
|
79
|
+
if (isJsonMode())
|
|
80
|
+
json({ error: msg });
|
|
81
|
+
else
|
|
82
|
+
error(msg);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
if (isJsonMode()) {
|
|
86
|
+
json({ direct: conn.connection_uri, pooled: conn.pooled_uri ?? null });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Print raw URL for piping
|
|
90
|
+
process.stdout.write(conn.connection_uri + "\n");
|
|
91
|
+
}
|
|
92
|
+
export async function branches() {
|
|
93
|
+
const token = requireToken();
|
|
94
|
+
const config = loadConfig();
|
|
95
|
+
const jsonMode = isJsonMode();
|
|
96
|
+
const projectId = config.defaultProject;
|
|
97
|
+
if (!projectId) {
|
|
98
|
+
const msg = "No default project set.";
|
|
99
|
+
if (jsonMode)
|
|
100
|
+
json({ error: msg });
|
|
101
|
+
else
|
|
102
|
+
error(msg);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const s = jsonMode ? null : spinner("Fetching branches");
|
|
106
|
+
const query = {};
|
|
107
|
+
if (config.defaultTeam)
|
|
108
|
+
query.team_id = config.defaultTeam;
|
|
109
|
+
const res = await api.get(`/v1/projects/${projectId}`, token, query);
|
|
110
|
+
s?.stop();
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
if (jsonMode)
|
|
113
|
+
json({ error: apiError(res, "Failed to fetch branches") });
|
|
114
|
+
else
|
|
115
|
+
error(apiError(res, "Failed to fetch project branches"));
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
const branchList = res.data.branches || [];
|
|
119
|
+
if (jsonMode) {
|
|
120
|
+
json({
|
|
121
|
+
project_id: projectId,
|
|
122
|
+
project_name: res.data.name,
|
|
123
|
+
branches: branchList.map((b) => ({
|
|
124
|
+
id: b.id,
|
|
125
|
+
name: b.name,
|
|
126
|
+
is_primary: b.is_primary,
|
|
127
|
+
status: b.status,
|
|
128
|
+
created_at: b.created_at,
|
|
129
|
+
})),
|
|
130
|
+
count: branchList.length,
|
|
131
|
+
});
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
heading(`Branches — ${res.data.name}`);
|
|
135
|
+
if (branchList.length === 0) {
|
|
136
|
+
log(` ${colors.dim("No branches found.")}`);
|
|
137
|
+
log();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
table(["NAME", "PRIMARY", "STATUS", "CREATED"], branchList.map((b) => [
|
|
141
|
+
b.name,
|
|
142
|
+
b.is_primary ? colors.green("yes") : "-",
|
|
143
|
+
b.status || "-",
|
|
144
|
+
formatDate(b.created_at),
|
|
145
|
+
]));
|
|
146
|
+
log();
|
|
147
|
+
}
|
|
148
|
+
export async function branchCreate(name) {
|
|
149
|
+
const token = requireToken();
|
|
150
|
+
const config = loadConfig();
|
|
151
|
+
const projectId = config.defaultProject;
|
|
152
|
+
if (!projectId) {
|
|
153
|
+
error("No default project set.");
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
const branchName = name || (await prompt("Branch name"));
|
|
157
|
+
if (!branchName) {
|
|
158
|
+
error("Branch name is required.");
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
const s = spinner(`Creating branch ${colors.cyan(branchName)}`);
|
|
162
|
+
const body = {
|
|
163
|
+
name: branchName,
|
|
164
|
+
project_id: projectId,
|
|
165
|
+
};
|
|
166
|
+
if (config.defaultTeam)
|
|
167
|
+
body.team_id = config.defaultTeam;
|
|
168
|
+
const res = await api.post("/v1/branches", body, token);
|
|
169
|
+
s.stop();
|
|
170
|
+
if (!res.ok) {
|
|
171
|
+
error(apiError(res, "Failed to create branch"));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
log();
|
|
175
|
+
log(` ${colors.green(">")} Branch ${colors.cyan(branchName)} created`);
|
|
176
|
+
log();
|
|
177
|
+
}
|
|
178
|
+
export async function branchDelete(name) {
|
|
179
|
+
const token = requireToken();
|
|
180
|
+
const config = loadConfig();
|
|
181
|
+
const projectId = config.defaultProject;
|
|
182
|
+
if (!projectId) {
|
|
183
|
+
error("No default project set.");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
if (!name) {
|
|
187
|
+
error("Branch name is required. Usage: bata db branch delete <name>");
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
const ok = await confirm(`Delete branch ${colors.cyan(name)}?`, false);
|
|
191
|
+
if (!ok) {
|
|
192
|
+
log(" Aborted.");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const s = spinner(`Deleting branch ${name}`);
|
|
196
|
+
// We need to find the branch ID first
|
|
197
|
+
const query = {};
|
|
198
|
+
if (config.defaultTeam)
|
|
199
|
+
query.team_id = config.defaultTeam;
|
|
200
|
+
const projRes = await api.get(`/v1/projects/${projectId}`, token, query);
|
|
201
|
+
if (!projRes.ok || !projRes.data.branches) {
|
|
202
|
+
s.stop();
|
|
203
|
+
error("Failed to fetch branches.");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
const branch = projRes.data.branches.find((b) => b.name === name);
|
|
207
|
+
if (!branch) {
|
|
208
|
+
s.stop();
|
|
209
|
+
error(`Branch "${name}" not found.`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const res = await api.del(`/v1/branches/${branch.id}`, token, query);
|
|
213
|
+
s.stop();
|
|
214
|
+
if (!res.ok) {
|
|
215
|
+
error("Failed to delete branch.");
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
log();
|
|
219
|
+
log(` ${colors.green(">")} Branch ${colors.cyan(name)} deleted`);
|
|
220
|
+
log();
|
|
221
|
+
}
|
|
222
|
+
export async function studio() {
|
|
223
|
+
const config = loadConfig();
|
|
224
|
+
const projectId = config.defaultProject;
|
|
225
|
+
const studioUrl = projectId
|
|
226
|
+
? `https://bench-app-one.vercel.app/studio?project=${projectId}`
|
|
227
|
+
: "https://bench-app-one.vercel.app/studio";
|
|
228
|
+
log();
|
|
229
|
+
logInfo(`Opening Studio in browser`);
|
|
230
|
+
log(` ${colors.dim(studioUrl)}`);
|
|
231
|
+
log();
|
|
232
|
+
openBrowser(studioUrl);
|
|
233
|
+
}
|
|
234
|
+
export async function query(sql) {
|
|
235
|
+
const jsonMode = isJsonMode();
|
|
236
|
+
if (!sql) {
|
|
237
|
+
const msg = 'SQL query is required. Usage: bata db query "SELECT 1"';
|
|
238
|
+
if (jsonMode)
|
|
239
|
+
json({ error: msg });
|
|
240
|
+
else
|
|
241
|
+
error(msg);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
const token = requireToken();
|
|
245
|
+
const config = loadConfig();
|
|
246
|
+
const projectId = config.defaultProject;
|
|
247
|
+
if (!projectId) {
|
|
248
|
+
const msg = "No default project set.";
|
|
249
|
+
if (jsonMode)
|
|
250
|
+
json({ error: msg });
|
|
251
|
+
else
|
|
252
|
+
error(msg);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
const s = jsonMode ? null : spinner("Running query");
|
|
256
|
+
const res = await api.post(`/v1/query/${projectId}`, { sql }, token);
|
|
257
|
+
s?.stop();
|
|
258
|
+
if (!res.ok) {
|
|
259
|
+
if (jsonMode)
|
|
260
|
+
json({ error: apiError(res, "Query failed") });
|
|
261
|
+
else
|
|
262
|
+
error(apiError(res, "Query failed"));
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
const { columns, rows, rowCount } = res.data;
|
|
266
|
+
if (jsonMode) {
|
|
267
|
+
json({
|
|
268
|
+
columns: columns ?? [],
|
|
269
|
+
rows: rows ?? [],
|
|
270
|
+
row_count: rowCount ?? rows?.length ?? 0,
|
|
271
|
+
});
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (columns && rows) {
|
|
275
|
+
log();
|
|
276
|
+
table(columns.map((c) => c.toUpperCase()), rows.map((r) => r.map(String)));
|
|
277
|
+
log();
|
|
278
|
+
log(` ${colors.dim(`${rowCount ?? rows.length} row(s)`)}`);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
log();
|
|
282
|
+
log(` ${colors.dim("Query executed successfully.")}`);
|
|
283
|
+
}
|
|
284
|
+
log();
|
|
285
|
+
}
|
|
286
|
+
export async function handleDb(args) {
|
|
287
|
+
const sub = args[0];
|
|
288
|
+
switch (sub) {
|
|
289
|
+
case "connect":
|
|
290
|
+
return connect();
|
|
291
|
+
case "url":
|
|
292
|
+
return url();
|
|
293
|
+
case "branches":
|
|
294
|
+
return branches();
|
|
295
|
+
case "branch": {
|
|
296
|
+
const action = args[1];
|
|
297
|
+
if (action === "create")
|
|
298
|
+
return branchCreate(args[2]);
|
|
299
|
+
if (action === "delete")
|
|
300
|
+
return branchDelete(args[2]);
|
|
301
|
+
error(`Unknown: db branch ${action || ""}`);
|
|
302
|
+
log(` ${colors.dim("Available:")} create, delete`);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
case "studio":
|
|
307
|
+
return studio();
|
|
308
|
+
case "query":
|
|
309
|
+
return query(args.slice(1).join(" "));
|
|
310
|
+
default:
|
|
311
|
+
error(`Unknown subcommand: db ${sub || ""}`);
|
|
312
|
+
log(` ${colors.dim("Available:")} connect, url, branches, branch, studio, query`);
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=db.js.map
|