diff --git a/deploy/database.ts b/deploy/database.ts index f616191..eb823b2 100644 --- a/deploy/database.ts +++ b/deploy/database.ts @@ -1,6 +1,11 @@ import { Command, ValidationError } from "@cliffy/command"; import { createTrpcClient } from "../auth.ts"; -import { error, renderTemporalTimestamp, tablePrinter } from "../util.ts"; +import { + error, + renderTemporalTimestamp, + tablePrinter, + writeJsonResult, +} from "../util.ts"; import { green } from "@std/fmt/colors"; import type { GlobalContext } from "../main.ts"; import { parse as parseConnectionString } from "pg-connection-string"; @@ -244,6 +249,20 @@ const databasesQueryCommand = new Command() array: false, }); + if (options.json) { + if (res.kind === "ok") { + writeJsonResult({ rows: res.rows ?? [] }); + return; + } + if (res.kind === "postgres_error") { + error(options, res.error, { errorCode: "POSTGRES_ERROR" }); + } + if (res.error) { + error(options, res.message, { errorCode: "QUERY_ERROR" }); + } + return; + } + if (res.kind === "ok") { if ( Array.isArray(res.rows) && res.rows.length > 0 && @@ -318,6 +337,22 @@ const databasesListCommand = new Command() } & ConnectionInfo >; + if (options.json) { + writeJsonResult(list.map((database) => ({ + name: database.slug, + engine: database.engine, + createdAt: database.created_at, + assignments: database.assignments.map((a) => a.app_slug), + connection: database.safeConnectionConfig, + databases: database.databases.map((db) => ({ + name: db.name, + status: db.status, + createdAt: db.created_at, + })), + }))); + return; + } + tablePrinter( ["NAME", "ENGINE", "ASSIGNMENTS", "CONNECTION DETAILS"], list, diff --git a/deploy/env.ts b/deploy/env.ts index 1d95abf..08d1b81 100644 --- a/deploy/env.ts +++ b/deploy/env.ts @@ -1,6 +1,11 @@ import { Command } from "@cliffy/command"; import { parse as dotEnvParse } from "@std/dotenv"; -import { error, isNonInteractive, tablePrinter } from "../util.ts"; +import { + error, + isNonInteractive, + tablePrinter, + writeJsonResult, +} from "../util.ts"; import { green } from "@std/fmt/colors"; import { createTrpcClient } from "../auth.ts"; import type { GlobalContext } from "../main.ts"; @@ -42,6 +47,21 @@ const envListCommand = new Command() { org }, ) as Context[]; + if (options.json) { + writeJsonResult(envVars.map((envVar) => ({ + id: envVar.id, + key: envVar.key, + value: envVar.is_secret ? null : envVar.value, + isSecret: envVar.is_secret, + contexts: envVar.context_ids + ? envVar.context_ids.map((id) => + contexts.find((c) => c.id === id)?.name ?? id + ) + : null, + }))); + return; + } + if (envVars.length === 0) { console.log( "There are no environment variables set on this application.", diff --git a/deploy/mod.ts b/deploy/mod.ts index f4db154..49b23b8 100644 --- a/deploy/mod.ts +++ b/deploy/mod.ts @@ -147,6 +147,7 @@ const logsCommand = new Command() const seenIds = new Set(); let onceConnected = false; + const encoder = new TextEncoder(); const sub = trpcClient.subscription( "apps.logs", { @@ -161,7 +162,7 @@ const logsCommand = new Command() onData: (data: unknown) => { const typedData = data as "streaming" | null | LogEntry[]; if (typedData === "streaming") { - if (!onceConnected && !options.quiet) { + if (!onceConnected && !options.quiet && !options.json) { console.log("connected, streaming logs..."); } onceConnected = true; @@ -175,6 +176,25 @@ const logsCommand = new Command() seenIds.add(id); } + if (options.json) { + // NDJSON: one record per line on stdout, severity preserved as + // a numeric field so agents can filter without re-parsing. + Deno.stdout.writeSync(encoder.encode( + JSON.stringify({ + timestamp: log.Timestamp, + traceId: log.TraceId || null, + spanId: log.SpanId || null, + severity: log.SeverityText, + severityNumber: log.SeverityNumber, + body: log.Body, + scope: log.ScopeName, + revision: log.Revision, + attributes: log.LogAttributes, + }) + "\n", + )); + continue; + } + const prefix = `[${renderTemporalTimestamp(log.Timestamp)}${ log.TraceId ? ` (${log.TraceId})` : "" }]`;