svcloud 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -47,6 +47,7 @@ svcloud mcp setup <harness> Write a coding agent harness's MCP config for svcl
47
47
  svcloud mcp check Diagnose sign-in state, harness configs, and live tool visibility
48
48
 
49
49
  Flags:
50
+ -v, --version Print the current version
50
51
  --json Machine-readable output, on every read command
51
52
  ```
52
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svcloud",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "The SV Cloud CLI. Alpha: login, logout, status, open, projects list, mcp, init, and runs are built; see PLANNING.md for what's still missing.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * `svcloud db` — PLANNING.md §4 Step 5. Mirrors F8's row editor
3
- * (`apps/cloud/src/routes/database.ts`) plus the two DDL routes added
4
- * alongside this command (`db_create_table`/`db_run_migration`, previously
5
- * MCP-tool-only — see that file's header). Cell/column payloads are taken as
6
- * JSON on the command line rather than a flag-per-field grammar: the primary
3
+ * (`apps/cloud/src/routes/database.ts`) plus the DDL routes added
4
+ * alongside this command (`db_create_table`/`db_run_migration`/`db_create_index`,
5
+ * previously MCP-tool-only — see that file's header). Cell/column payloads are taken
6
+ * as JSON on the command line rather than a flag-per-field grammar: the primary
7
7
  * caller for structured data like this is a coding agent, and it goes
8
8
  * through `svcloud mcp`'s tools directly (`lib/prompt.ts`'s header), not this
9
9
  * command — this path exists for a human or a script at a real terminal.
10
10
  */
11
11
  import { apiFetch } from "../lib/api";
12
12
  import { findProjectBySlug } from "../lib/find-project";
13
- import { die, printJson, printTable, takeOption } from "../lib/output";
13
+ import { die, hasFlag, printJson, printTable, takeOption } from "../lib/output";
14
14
 
15
15
  const USAGE = `Usage:
16
16
  svcloud db tables <app>
@@ -18,12 +18,14 @@ const USAGE = `Usage:
18
18
  svcloud db insert <app> <table> <cells-json>
19
19
  svcloud db update <app> <table> <row-id> <cells-json>
20
20
  svcloud db delete <app> <table> <row-id>
21
- svcloud db create-table <app> <table> <columns-json> --primary-key <name>
21
+ svcloud db create-table <app> <table> <columns-json> --primary-key <name> [--indexes <indexes-json>]
22
+ svcloud db create-index <app> <table> <columns-json> [--name <name>] [--unique]
22
23
  svcloud db migrate <app> <table> <column-json> [--default <value>]
23
24
 
24
25
  <cells-json> is a JSON object, e.g. '{"title":"Hello"}'
25
26
  <columns-json> is a JSON array, e.g. '[{"name":"id","type":"uuid","nullable":false},{"name":"title","type":"text"}]'
26
27
  <column-json> is a single column, e.g. '{"name":"count","type":"number","nullable":false}'
28
+ <indexes-json> is a JSON array of indexes, e.g. '[{"columns":["email"],"unique":true}]'
27
29
  Column types: text, number, boolean, timestamp, json, uuid`;
28
30
 
29
31
  interface DbColumn {
@@ -193,13 +195,25 @@ async function dbDelete(argv: string[], json: boolean): Promise<void> {
193
195
 
194
196
  async function dbCreateTable(argv: string[], json: boolean): Promise<void> {
195
197
  const pkOpt = takeOption(argv, "primary-key");
196
- const [slug, table, columnsJson] = pkOpt.rest;
198
+ const indexesOpt = takeOption(pkOpt.rest, "indexes");
199
+ const [slug, table, columnsJson] = indexesOpt.rest;
197
200
  if (!table || !pkOpt.value) die(USAGE);
198
201
  const projectId = await resolveProjectId(slug);
199
202
  const columns = parseJsonArg<unknown[]>(columnsJson, "<columns-json>");
203
+ const indexes = indexesOpt.value
204
+ ? parseJsonArg<unknown[]>(indexesOpt.value, "<indexes-json>")
205
+ : undefined;
200
206
  const result = await apiFetch<{ table: string; created: boolean }>(
201
207
  `/api/v1/projects/${projectId}/database/tables`,
202
- { method: "POST", body: { name: table, columns, primary_key: pkOpt.value } },
208
+ {
209
+ method: "POST",
210
+ body: {
211
+ name: table,
212
+ columns,
213
+ primary_key: pkOpt.value,
214
+ ...(indexes ? { indexes } : {}),
215
+ },
216
+ },
203
217
  );
204
218
  if (json) {
205
219
  printJson(result);
@@ -208,6 +222,36 @@ async function dbCreateTable(argv: string[], json: boolean): Promise<void> {
208
222
  console.log(`Created table "${result.table}".`);
209
223
  }
210
224
 
225
+ async function dbCreateIndex(argv: string[], json: boolean): Promise<void> {
226
+ const nameOpt = takeOption(argv, "name");
227
+ const uniqueFlag = hasFlag(nameOpt.rest, "unique");
228
+ const [slug, table, columnsJson] = uniqueFlag.rest;
229
+ if (!table || !columnsJson) die(USAGE);
230
+ const projectId = await resolveProjectId(slug);
231
+ const parsedColumns = parseJsonArg<unknown>(columnsJson, "<columns-json>");
232
+ const columns = Array.isArray(parsedColumns)
233
+ ? parsedColumns
234
+ : typeof parsedColumns === "string"
235
+ ? [parsedColumns]
236
+ : die("<columns-json> must be a JSON array of column names or a string.");
237
+ const result = await apiFetch<{ table: string; index: string; created: boolean }>(
238
+ `/api/v1/projects/${projectId}/database/tables/${encodeURIComponent(table)}/indexes`,
239
+ {
240
+ method: "POST",
241
+ body: {
242
+ columns,
243
+ name: nameOpt.value,
244
+ unique: uniqueFlag.present ? true : undefined,
245
+ },
246
+ },
247
+ );
248
+ if (json) {
249
+ printJson(result);
250
+ return;
251
+ }
252
+ console.log(`Created index "${result.index}" on "${result.table}".`);
253
+ }
254
+
211
255
  async function dbMigrate(argv: string[], json: boolean): Promise<void> {
212
256
  const defaultOpt = takeOption(argv, "default");
213
257
  const [slug, table, columnJson] = defaultOpt.rest;
@@ -241,9 +285,12 @@ export async function dbCommand(argv: string[], json: boolean): Promise<void> {
241
285
  return dbDelete(rest, json);
242
286
  case "create-table":
243
287
  return dbCreateTable(rest, json);
288
+ case "create-index":
289
+ return dbCreateIndex(rest, json);
244
290
  case "migrate":
245
291
  return dbMigrate(rest, json);
246
292
  default:
247
293
  die(USAGE);
248
294
  }
249
295
  }
296
+
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import { secretsCommand } from "./commands/secrets";
18
18
  import { statusCommand } from "./commands/status";
19
19
  import { whoamiCommand } from "./commands/whoami";
20
20
  import { AuthRequiredError, ApiError } from "./lib/api";
21
+ import { CLI_VERSION } from "./lib/config";
21
22
  import { hasFlag } from "./lib/output";
22
23
 
23
24
  const USAGE = `svcloud — the SV Cloud CLI
@@ -39,13 +40,26 @@ Usage:
39
40
  svcloud mcp check Diagnose sign-in state, harness configs, and live tool visibility
40
41
 
41
42
  Flags:
43
+ -v, --version Print the current version
42
44
  --json Machine-readable output, on every read command
43
45
  --repo, --name, --slug, --installation, --id, --watch, --limit, --primary-key, --default
44
46
  See each command's own usage
45
47
  `;
46
48
 
47
49
  async function main(): Promise<void> {
48
- const { present: json, rest: argv } = hasFlag(process.argv.slice(2), "json");
50
+ const rawArgv = process.argv.slice(2);
51
+ if (
52
+ hasFlag(rawArgv, "version").present ||
53
+ hasFlag(rawArgv, "v").present ||
54
+ rawArgv[0] === "-v" ||
55
+ rawArgv[0] === "--version" ||
56
+ rawArgv[0] === "version"
57
+ ) {
58
+ console.log(CLI_VERSION);
59
+ return;
60
+ }
61
+
62
+ const { present: json, rest: argv } = hasFlag(rawArgv, "json");
49
63
  const [command, ...rest] = argv;
50
64
 
51
65
  switch (command) {
@@ -85,6 +99,11 @@ async function main(): Promise<void> {
85
99
  case "deploy":
86
100
  await deployCommand(rest, json);
87
101
  return;
102
+ case "version":
103
+ case "-v":
104
+ case "--version":
105
+ console.log(CLI_VERSION);
106
+ return;
88
107
  case undefined:
89
108
  case "-h":
90
109
  case "--help":