bod-cli 0.7.2 → 0.8.1
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/cli.js +57 -16
- package/package.json +1 -1
- package/src/cli.ts +12 -2
- package/src/commands/db.ts +71 -20
package/dist/cli.js
CHANGED
|
@@ -20151,23 +20151,47 @@ function directTarget(port, token, label) {
|
|
|
20151
20151
|
}
|
|
20152
20152
|
};
|
|
20153
20153
|
}
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
const
|
|
20159
|
-
|
|
20160
|
-
|
|
20154
|
+
function isInAppFolder() {
|
|
20155
|
+
return existsSync6(join6(process.cwd(), "bodify.yaml"));
|
|
20156
|
+
}
|
|
20157
|
+
async function resolveDbTarget(appArg, wantLocal) {
|
|
20158
|
+
const inApp = isInAppFolder();
|
|
20159
|
+
const scopeLabel = inApp ? "APP" : "BODIFY-PLATFORM";
|
|
20160
|
+
if (wantLocal) {
|
|
20161
|
+
if (!inApp) {
|
|
20162
|
+
console.error(source_default.red("--local (-l) only works inside a Bodify app folder (needs bodify.yaml)."));
|
|
20163
|
+
process.exit(1);
|
|
20164
|
+
}
|
|
20165
|
+
const info = readServeInfo();
|
|
20166
|
+
if (info)
|
|
20167
|
+
return directTarget(info.dbPort, info.dbAdminPassword, `LOCAL ${scopeLabel} bod serve`);
|
|
20168
|
+
const envPw = readDotEnv("BODDB_ADMIN_PASSWORD");
|
|
20169
|
+
if (envPw)
|
|
20170
|
+
return directTarget(readDbPortFromYaml(), envPw, `LOCAL ${scopeLabel} via .env`);
|
|
20171
|
+
console.error(source_default.red("--local (-l) requires a running `bod serve` in this directory."));
|
|
20172
|
+
console.error(source_default.dim(" Expected `.bodify/serve.info.json` or BODDB_ADMIN_PASSWORD in .env — neither found."));
|
|
20173
|
+
process.exit(1);
|
|
20174
|
+
}
|
|
20161
20175
|
const { url, apiKey } = getResolvedInstance(loadConfig());
|
|
20162
20176
|
const client = new BodClient(url, apiKey);
|
|
20163
|
-
const
|
|
20177
|
+
const effectiveAppArg = inApp ? appArg : appArg ?? "bodify";
|
|
20178
|
+
const appId = await resolveAppId(client, resolveAppName(effectiveAppArg));
|
|
20179
|
+
const instanceLabel = process.env._BOD_INSTANCE_OVERRIDE || process.env.BOD_INSTANCE || "default";
|
|
20164
20180
|
return {
|
|
20165
|
-
label: `
|
|
20181
|
+
label: `REMOTE ${scopeLabel} ${instanceLabel} (${url}) → app ${appId}`,
|
|
20166
20182
|
request(method, sub, body) {
|
|
20167
20183
|
return client.request(method, `/apps/${appId}${sub}`, body);
|
|
20168
20184
|
}
|
|
20169
20185
|
};
|
|
20170
20186
|
}
|
|
20187
|
+
function printTargetBanner(target) {
|
|
20188
|
+
const isLocal = target.label.startsWith("LOCAL");
|
|
20189
|
+
const color = isLocal ? source_default.cyan : source_default.yellow;
|
|
20190
|
+
console.error(color(`→ DB target: ${target.label}${isInAppFolder() ? "" : " [no bodify.yaml in cwd]"}`));
|
|
20191
|
+
}
|
|
20192
|
+
function readLocalFlag() {
|
|
20193
|
+
return process.argv.slice(2).some((a2) => a2 === "--local" || a2 === "-l");
|
|
20194
|
+
}
|
|
20171
20195
|
async function readBody(positional, file) {
|
|
20172
20196
|
let raw;
|
|
20173
20197
|
if (positional)
|
|
@@ -20248,7 +20272,8 @@ var getCmd = defineCommand2({
|
|
|
20248
20272
|
},
|
|
20249
20273
|
async run({ args }) {
|
|
20250
20274
|
strictArgs(["path", "app", "a", "deep", "d", "limit", "n", "offset", "output", "o"]);
|
|
20251
|
-
const target = await resolveDbTarget(args.app);
|
|
20275
|
+
const target = await resolveDbTarget(args.app, readLocalFlag());
|
|
20276
|
+
printTargetBanner(target);
|
|
20252
20277
|
const path = normalizePath(args.path);
|
|
20253
20278
|
const qs = [];
|
|
20254
20279
|
if (!args.deep) {
|
|
@@ -20277,7 +20302,8 @@ var setCmd2 = defineCommand2({
|
|
|
20277
20302
|
async run({ args }) {
|
|
20278
20303
|
strictArgs(["path", "value", "app", "a", "file", "f"]);
|
|
20279
20304
|
const body = await readBody(args.value, args.file);
|
|
20280
|
-
const target = await resolveDbTarget(args.app);
|
|
20305
|
+
const target = await resolveDbTarget(args.app, readLocalFlag());
|
|
20306
|
+
printTargetBanner(target);
|
|
20281
20307
|
await target.request("PUT", `/db/${normalizePath(args.path)}`, body);
|
|
20282
20308
|
console.log(source_default.green(`✓ Set ${args.path}`));
|
|
20283
20309
|
}
|
|
@@ -20293,7 +20319,8 @@ var updateCmd = defineCommand2({
|
|
|
20293
20319
|
async run({ args }) {
|
|
20294
20320
|
strictArgs(["path", "value", "app", "a", "file", "f"]);
|
|
20295
20321
|
const body = await readBody(args.value, args.file);
|
|
20296
|
-
const target = await resolveDbTarget(args.app);
|
|
20322
|
+
const target = await resolveDbTarget(args.app, readLocalFlag());
|
|
20323
|
+
printTargetBanner(target);
|
|
20297
20324
|
await target.request("PATCH", `/db/${normalizePath(args.path)}`, body);
|
|
20298
20325
|
console.log(source_default.green(`✓ Updated ${args.path}`));
|
|
20299
20326
|
}
|
|
@@ -20309,7 +20336,8 @@ var pushCmd = defineCommand2({
|
|
|
20309
20336
|
async run({ args }) {
|
|
20310
20337
|
strictArgs(["path", "value", "app", "a", "file", "f"]);
|
|
20311
20338
|
const body = await readBody(args.value, args.file);
|
|
20312
|
-
const target = await resolveDbTarget(args.app);
|
|
20339
|
+
const target = await resolveDbTarget(args.app, readLocalFlag());
|
|
20340
|
+
printTargetBanner(target);
|
|
20313
20341
|
const res = await target.request("POST", `/db/${normalizePath(args.path)}`, body);
|
|
20314
20342
|
console.log(source_default.green(`✓ Pushed → ${args.path}/${res.key}`));
|
|
20315
20343
|
}
|
|
@@ -20327,7 +20355,8 @@ var deleteCmd = defineCommand2({
|
|
|
20327
20355
|
console.error(source_default.yellow(`Refusing to delete "${args.path}" without --confirm (-y).`));
|
|
20328
20356
|
process.exit(1);
|
|
20329
20357
|
}
|
|
20330
|
-
const target = await resolveDbTarget(args.app);
|
|
20358
|
+
const target = await resolveDbTarget(args.app, readLocalFlag());
|
|
20359
|
+
printTargetBanner(target);
|
|
20331
20360
|
await target.request("DELETE", `/db/${normalizePath(args.path)}`);
|
|
20332
20361
|
console.log(source_default.green(`✓ Deleted ${args.path}`));
|
|
20333
20362
|
}
|
|
@@ -20394,7 +20423,8 @@ var queryCmd = defineCommand2({
|
|
|
20394
20423
|
limit: args.limit ? Number(args.limit) : undefined,
|
|
20395
20424
|
offset: args.offset ? Number(args.offset) : undefined
|
|
20396
20425
|
};
|
|
20397
|
-
const target = await resolveDbTarget(args.app);
|
|
20426
|
+
const target = await resolveDbTarget(args.app, readLocalFlag());
|
|
20427
|
+
printTargetBanner(target);
|
|
20398
20428
|
const res = await target.request("POST", `/query/${normalizePath(args.path)}`, body);
|
|
20399
20429
|
let rows = res.data;
|
|
20400
20430
|
if (!args.deep && Array.isArray(rows)) {
|
|
@@ -20419,7 +20449,18 @@ var db_default = defineCommand2({
|
|
|
20419
20449
|
});
|
|
20420
20450
|
|
|
20421
20451
|
// src/cli.ts
|
|
20422
|
-
|
|
20452
|
+
function parseInstanceFlag() {
|
|
20453
|
+
const argv2 = process.argv;
|
|
20454
|
+
for (let i2 = 0;i2 < argv2.length; i2++) {
|
|
20455
|
+
const a2 = argv2[i2];
|
|
20456
|
+
if (a2.startsWith("--instance="))
|
|
20457
|
+
return a2.slice("--instance=".length);
|
|
20458
|
+
if (a2 === "--instance" && i2 + 1 < argv2.length)
|
|
20459
|
+
return argv2[i2 + 1];
|
|
20460
|
+
}
|
|
20461
|
+
return;
|
|
20462
|
+
}
|
|
20463
|
+
var instanceFlag = parseInstanceFlag();
|
|
20423
20464
|
var isLocal = process.argv.includes("--local") || process.argv.includes("-l");
|
|
20424
20465
|
if (instanceFlag)
|
|
20425
20466
|
setInstanceOverride(instanceFlag);
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -17,8 +17,18 @@ import sshCmd from './commands/ssh'
|
|
|
17
17
|
import publishCmd from './commands/publish'
|
|
18
18
|
import dbCmd from './commands/db'
|
|
19
19
|
|
|
20
|
-
// Parse --instance / --local / -l early so it's set before citty dispatches subcommands
|
|
21
|
-
|
|
20
|
+
// Parse --instance / --local / -l early so it's set before citty dispatches subcommands.
|
|
21
|
+
// Supports both `--instance=NAME` and `--instance NAME` forms.
|
|
22
|
+
function parseInstanceFlag(): string | undefined {
|
|
23
|
+
const argv = process.argv
|
|
24
|
+
for (let i = 0; i < argv.length; i++) {
|
|
25
|
+
const a = argv[i]
|
|
26
|
+
if (a.startsWith('--instance=')) return a.slice('--instance='.length)
|
|
27
|
+
if (a === '--instance' && i + 1 < argv.length) return argv[i + 1]
|
|
28
|
+
}
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
|
31
|
+
const instanceFlag = parseInstanceFlag()
|
|
22
32
|
const isLocal = process.argv.includes('--local') || process.argv.includes('-l')
|
|
23
33
|
if (instanceFlag) setInstanceOverride(instanceFlag)
|
|
24
34
|
else if (isLocal) setInstanceOverride('local')
|
package/src/commands/db.ts
CHANGED
|
@@ -79,31 +79,76 @@ function directTarget(port: number, token: string, label: string): DbTarget {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/** True if cwd has a `bodify.yaml` (i.e. we're inside a Bodify app project). */
|
|
83
|
+
function isInAppFolder(): boolean {
|
|
84
|
+
return existsSync(join(process.cwd(), 'bodify.yaml'))
|
|
85
|
+
}
|
|
86
|
+
|
|
82
87
|
/**
|
|
83
|
-
* Resolve a target for DB commands.
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
88
|
+
* Resolve a target for DB commands.
|
|
89
|
+
*
|
|
90
|
+
* Explicit, predictable rules — no silent auto-detection:
|
|
91
|
+
* • `-l` / `--local`: REQUIRE a running `bod serve` in cwd. Connects
|
|
92
|
+
* directly to its BodDB (port from `.bodify/serve.info.json`, or
|
|
93
|
+
* `.env BODDB_ADMIN_PASSWORD` + `bodify.yaml database.port`). Fails
|
|
94
|
+
* loudly if no local serve is discoverable — we never silently fall
|
|
95
|
+
* through to prod when `-l` was requested.
|
|
96
|
+
* • No `-l` (default): route through the Bodify agent proxy (remote prod
|
|
97
|
+
* by default, or the instance named by `--instance` / `BOD_INSTANCE`).
|
|
98
|
+
* Even when `bod serve` is running in the same directory, we stay on
|
|
99
|
+
* prod unless `-l` was passed — this is the opposite of the old
|
|
100
|
+
* behavior and prevents "which DB did I just write to?" confusion.
|
|
101
|
+
*
|
|
102
|
+
* Every resolved target carries a human label that the command layer prints
|
|
103
|
+
* as a banner before running, so the user can always see where writes land.
|
|
87
104
|
*/
|
|
88
|
-
async function resolveDbTarget(appArg: string | undefined): Promise<DbTarget> {
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
async function resolveDbTarget(appArg: string | undefined, wantLocal: boolean): Promise<DbTarget> {
|
|
106
|
+
const inApp = isInAppFolder()
|
|
107
|
+
const scopeLabel = inApp ? 'APP' : 'BODIFY-PLATFORM'
|
|
108
|
+
|
|
109
|
+
if (wantLocal) {
|
|
110
|
+
// `-l` only makes sense inside an app folder — the Bodify platform DB
|
|
111
|
+
// isn't something you run locally via `bod serve`.
|
|
112
|
+
if (!inApp) {
|
|
113
|
+
console.error(chalk.red('--local (-l) only works inside a Bodify app folder (needs bodify.yaml).'))
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
const info = readServeInfo()
|
|
117
|
+
if (info) return directTarget(info.dbPort, info.dbAdminPassword, `LOCAL ${scopeLabel} bod serve`)
|
|
118
|
+
const envPw = readDotEnv('BODDB_ADMIN_PASSWORD')
|
|
119
|
+
if (envPw) return directTarget(readDbPortFromYaml(), envPw, `LOCAL ${scopeLabel} via .env`)
|
|
120
|
+
console.error(chalk.red('--local (-l) requires a running `bod serve` in this directory.'))
|
|
121
|
+
console.error(chalk.dim(' Expected `.bodify/serve.info.json` or BODDB_ADMIN_PASSWORD in .env — neither found.'))
|
|
122
|
+
process.exit(1)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Remote: Bodify agent proxy. Inside an app folder → that app's DB;
|
|
126
|
+
// outside → the Bodify platform app's own DB.
|
|
96
127
|
const { url, apiKey } = getResolvedInstance(loadConfig())
|
|
97
128
|
const client = new BodClient(url, apiKey)
|
|
98
|
-
const
|
|
129
|
+
const effectiveAppArg = inApp ? appArg : (appArg ?? 'bodify')
|
|
130
|
+
const appId = await resolveAppId(client, resolveAppName(effectiveAppArg))
|
|
131
|
+
const instanceLabel = process.env._BOD_INSTANCE_OVERRIDE || process.env.BOD_INSTANCE || 'default'
|
|
99
132
|
return {
|
|
100
|
-
label: `
|
|
133
|
+
label: `REMOTE ${scopeLabel} ${instanceLabel} (${url}) → app ${appId}`,
|
|
101
134
|
request<T>(method: string, sub: string, body?: unknown): Promise<T> {
|
|
102
135
|
return client.request<T>(method, `/apps/${appId}${sub}`, body)
|
|
103
136
|
},
|
|
104
137
|
}
|
|
105
138
|
}
|
|
106
139
|
|
|
140
|
+
/** Print a clear banner showing the resolved DB target. Always on stderr. */
|
|
141
|
+
function printTargetBanner(target: DbTarget): void {
|
|
142
|
+
const isLocal = target.label.startsWith('LOCAL')
|
|
143
|
+
const color = isLocal ? chalk.cyan : chalk.yellow
|
|
144
|
+
console.error(color(`→ DB target: ${target.label}${isInAppFolder() ? '' : ' [no bodify.yaml in cwd]'}`))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Read --local / -l from argv before citty parses, so all subcommands see it. */
|
|
148
|
+
function readLocalFlag(): boolean {
|
|
149
|
+
return process.argv.slice(2).some(a => a === '--local' || a === '-l')
|
|
150
|
+
}
|
|
151
|
+
|
|
107
152
|
/** Read body from positional arg, --file, or stdin (in that order). */
|
|
108
153
|
async function readBody(positional: string | undefined, file: string | undefined): Promise<unknown> {
|
|
109
154
|
let raw: string | undefined
|
|
@@ -190,7 +235,8 @@ const getCmd = defineCommand({
|
|
|
190
235
|
},
|
|
191
236
|
async run({ args }) {
|
|
192
237
|
strictArgs(['path', 'app', 'a', 'deep', 'd', 'limit', 'n', 'offset', 'output', 'o'])
|
|
193
|
-
const target = await resolveDbTarget(args.app)
|
|
238
|
+
const target = await resolveDbTarget(args.app, readLocalFlag())
|
|
239
|
+
printTargetBanner(target)
|
|
194
240
|
const path = normalizePath(args.path)
|
|
195
241
|
const qs: string[] = []
|
|
196
242
|
if (!args.deep) {
|
|
@@ -218,7 +264,8 @@ const setCmd = defineCommand({
|
|
|
218
264
|
async run({ args }) {
|
|
219
265
|
strictArgs(['path', 'value', 'app', 'a', 'file', 'f'])
|
|
220
266
|
const body = await readBody(args.value, args.file)
|
|
221
|
-
const target = await resolveDbTarget(args.app)
|
|
267
|
+
const target = await resolveDbTarget(args.app, readLocalFlag())
|
|
268
|
+
printTargetBanner(target)
|
|
222
269
|
await target.request('PUT', `/db/${normalizePath(args.path)}`, body)
|
|
223
270
|
console.log(chalk.green(`✓ Set ${args.path}`))
|
|
224
271
|
},
|
|
@@ -235,7 +282,8 @@ const updateCmd = defineCommand({
|
|
|
235
282
|
async run({ args }) {
|
|
236
283
|
strictArgs(['path', 'value', 'app', 'a', 'file', 'f'])
|
|
237
284
|
const body = await readBody(args.value, args.file)
|
|
238
|
-
const target = await resolveDbTarget(args.app)
|
|
285
|
+
const target = await resolveDbTarget(args.app, readLocalFlag())
|
|
286
|
+
printTargetBanner(target)
|
|
239
287
|
await target.request('PATCH', `/db/${normalizePath(args.path)}`, body)
|
|
240
288
|
console.log(chalk.green(`✓ Updated ${args.path}`))
|
|
241
289
|
},
|
|
@@ -252,7 +300,8 @@ const pushCmd = defineCommand({
|
|
|
252
300
|
async run({ args }) {
|
|
253
301
|
strictArgs(['path', 'value', 'app', 'a', 'file', 'f'])
|
|
254
302
|
const body = await readBody(args.value, args.file)
|
|
255
|
-
const target = await resolveDbTarget(args.app)
|
|
303
|
+
const target = await resolveDbTarget(args.app, readLocalFlag())
|
|
304
|
+
printTargetBanner(target)
|
|
256
305
|
const res = await target.request<DbResponse>('POST', `/db/${normalizePath(args.path)}`, body)
|
|
257
306
|
console.log(chalk.green(`✓ Pushed → ${args.path}/${res.key}`))
|
|
258
307
|
},
|
|
@@ -271,7 +320,8 @@ const deleteCmd = defineCommand({
|
|
|
271
320
|
console.error(chalk.yellow(`Refusing to delete "${args.path}" without --confirm (-y).`))
|
|
272
321
|
process.exit(1)
|
|
273
322
|
}
|
|
274
|
-
const target = await resolveDbTarget(args.app)
|
|
323
|
+
const target = await resolveDbTarget(args.app, readLocalFlag())
|
|
324
|
+
printTargetBanner(target)
|
|
275
325
|
await target.request('DELETE', `/db/${normalizePath(args.path)}`)
|
|
276
326
|
console.log(chalk.green(`✓ Deleted ${args.path}`))
|
|
277
327
|
},
|
|
@@ -339,7 +389,8 @@ const queryCmd = defineCommand({
|
|
|
339
389
|
limit: args.limit ? Number(args.limit) : undefined,
|
|
340
390
|
offset: args.offset ? Number(args.offset) : undefined,
|
|
341
391
|
}
|
|
342
|
-
const target = await resolveDbTarget(args.app)
|
|
392
|
+
const target = await resolveDbTarget(args.app, readLocalFlag())
|
|
393
|
+
printTargetBanner(target)
|
|
343
394
|
const res = await target.request<DbResponse>('POST', `/query/${normalizePath(args.path)}`, body)
|
|
344
395
|
let rows = res.data as Array<Record<string, unknown>> | null
|
|
345
396
|
if (!args.deep && Array.isArray(rows)) {
|