@specific.dev/cli 0.1.133 → 0.1.134

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.
Files changed (67) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +1 -1
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
  5. package/dist/admin/__next._full.txt +1 -1
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +1 -1
  8. package/dist/admin/__next._tree.txt +1 -1
  9. package/dist/admin/_not-found/__next._full.txt +1 -1
  10. package/dist/admin/_not-found/__next._head.txt +1 -1
  11. package/dist/admin/_not-found/__next._index.txt +1 -1
  12. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  13. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  14. package/dist/admin/_not-found/__next._tree.txt +1 -1
  15. package/dist/admin/_not-found/index.html +1 -1
  16. package/dist/admin/_not-found/index.txt +1 -1
  17. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
  18. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  19. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
  20. package/dist/admin/databases/__next._full.txt +1 -1
  21. package/dist/admin/databases/__next._head.txt +1 -1
  22. package/dist/admin/databases/__next._index.txt +1 -1
  23. package/dist/admin/databases/__next._tree.txt +1 -1
  24. package/dist/admin/databases/index.html +1 -1
  25. package/dist/admin/databases/index.txt +1 -1
  26. package/dist/admin/fullscreen/__next._full.txt +1 -1
  27. package/dist/admin/fullscreen/__next._head.txt +1 -1
  28. package/dist/admin/fullscreen/__next._index.txt +1 -1
  29. package/dist/admin/fullscreen/__next._tree.txt +1 -1
  30. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
  31. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  32. package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
  33. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  34. package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
  35. package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
  36. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
  37. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  38. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  39. package/dist/admin/fullscreen/databases/index.html +1 -1
  40. package/dist/admin/fullscreen/databases/index.txt +1 -1
  41. package/dist/admin/fullscreen/index.html +1 -1
  42. package/dist/admin/fullscreen/index.txt +1 -1
  43. package/dist/admin/index.html +1 -1
  44. package/dist/admin/index.txt +1 -1
  45. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +1 -1
  46. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
  47. package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +1 -1
  48. package/dist/admin/mail/__next._full.txt +1 -1
  49. package/dist/admin/mail/__next._head.txt +1 -1
  50. package/dist/admin/mail/__next._index.txt +1 -1
  51. package/dist/admin/mail/__next._tree.txt +1 -1
  52. package/dist/admin/mail/index.html +1 -1
  53. package/dist/admin/mail/index.txt +1 -1
  54. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +1 -1
  55. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +1 -1
  56. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
  57. package/dist/admin/workflows/__next._full.txt +1 -1
  58. package/dist/admin/workflows/__next._head.txt +1 -1
  59. package/dist/admin/workflows/__next._index.txt +1 -1
  60. package/dist/admin/workflows/__next._tree.txt +1 -1
  61. package/dist/admin/workflows/index.html +1 -1
  62. package/dist/admin/workflows/index.txt +1 -1
  63. package/dist/cli.js +233 -3
  64. package/package.json +1 -1
  65. /package/dist/admin/_next/static/{k4CkHzJBOS7_uNab7bdAE → INvT6nTMaJ_qj_9Q3KulI}/_buildManifest.js +0 -0
  66. /package/dist/admin/_next/static/{k4CkHzJBOS7_uNab7bdAE → INvT6nTMaJ_qj_9Q3KulI}/_clientMiddlewareManifest.json +0 -0
  67. /package/dist/admin/_next/static/{k4CkHzJBOS7_uNab7bdAE → INvT6nTMaJ_qj_9Q3KulI}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -367797,6 +367797,27 @@ var ApiClient = class {
367797
367797
  }
367798
367798
  return response.json();
367799
367799
  }
367800
+ async runObservabilityQuery(environmentId, sql) {
367801
+ const url = `${this.baseUrl}/environments/${environmentId}/observability/query`;
367802
+ writeLog("api", `POST ${url}`);
367803
+ const response = await fetch(url, {
367804
+ method: "POST",
367805
+ headers: {
367806
+ "Content-Type": "application/json",
367807
+ ...await this.authHeaders()
367808
+ },
367809
+ body: JSON.stringify({ sql })
367810
+ });
367811
+ writeLog("api", `Response: ${response.status} ${response.statusText}`);
367812
+ if (!response.ok) {
367813
+ throw await readApiError(
367814
+ response,
367815
+ "Query failed",
367816
+ `POST ${url}`
367817
+ );
367818
+ }
367819
+ return response.json();
367820
+ }
367800
367821
  };
367801
367822
  var SpecificClient = class {
367802
367823
  client;
@@ -367856,6 +367877,10 @@ var SpecificClient = class {
367856
367877
  async deletePreviewEnvironment(environmentId) {
367857
367878
  return this.client.deletePreviewEnvironment(environmentId);
367858
367879
  }
367880
+ // --- Observability ---
367881
+ async runObservabilityQuery(environmentId, sql) {
367882
+ return this.client.runObservabilityQuery(environmentId, sql);
367883
+ }
367859
367884
  };
367860
367885
  function toDeployment(response) {
367861
367886
  return {
@@ -373693,7 +373718,7 @@ function trackEvent(event, properties) {
373693
373718
  event,
373694
373719
  properties: {
373695
373720
  ...properties,
373696
- cli_version: "0.1.133",
373721
+ cli_version: "0.1.134",
373697
373722
  platform: process.platform,
373698
373723
  node_version: process.version,
373699
373724
  project_id: getProjectId()
@@ -377799,7 +377824,7 @@ function compareVersions(a, b) {
377799
377824
  return 0;
377800
377825
  }
377801
377826
  async function checkForUpdate() {
377802
- const currentVersion = "0.1.133";
377827
+ const currentVersion = "0.1.134";
377803
377828
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
377804
377829
  if (!response.ok) {
377805
377830
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -378113,11 +378138,208 @@ function statusCommand() {
378113
378138
  render10(/* @__PURE__ */ React12.createElement(StatusUI, null));
378114
378139
  }
378115
378140
 
378141
+ // src/lib/query/render-table.ts
378142
+ var MAX_COL_WIDTH = 60;
378143
+ var ANSI = {
378144
+ dim: "\x1B[2m",
378145
+ reset: "\x1B[0m"
378146
+ };
378147
+ function renderTable(input) {
378148
+ const { columns, rows, stats } = input;
378149
+ if (columns.length === 0) {
378150
+ return "(no columns)\n";
378151
+ }
378152
+ const cellMatrix = rows.map(
378153
+ (row) => columns.map((col, i) => formatCell(row[i], col.type))
378154
+ );
378155
+ const widths = columns.map((col, i) => {
378156
+ const headerWidth = visibleLength(col.name);
378157
+ const typeWidth = visibleLength(col.type);
378158
+ let max = Math.max(headerWidth, typeWidth);
378159
+ for (const row of cellMatrix) {
378160
+ const w = visibleLength(row[i]);
378161
+ if (w > max) max = w;
378162
+ }
378163
+ return Math.min(max, MAX_COL_WIDTH);
378164
+ });
378165
+ const aligns = columns.map(
378166
+ (col) => isNumericType(col.type) ? "right" : "left"
378167
+ );
378168
+ const out = [];
378169
+ out.push(borderLine(widths, "top"));
378170
+ out.push(headerRow(columns.map((c) => c.name), widths, aligns));
378171
+ out.push(headerRow(
378172
+ columns.map((c) => c.type),
378173
+ widths,
378174
+ aligns,
378175
+ /*dim*/
378176
+ true
378177
+ ));
378178
+ out.push(borderLine(widths, "mid"));
378179
+ for (const row of cellMatrix) {
378180
+ out.push(dataRow(row, widths, aligns));
378181
+ }
378182
+ out.push(borderLine(widths, "bottom"));
378183
+ out.push(statsLine(stats, rows.length));
378184
+ return out.join("\n") + "\n";
378185
+ }
378186
+ function formatCell(value, type) {
378187
+ if (value === null || value === void 0) {
378188
+ return `${ANSI.dim}NULL${ANSI.reset}`;
378189
+ }
378190
+ if (type.startsWith("Map(") && typeof value === "object") {
378191
+ return JSON.stringify(value);
378192
+ }
378193
+ if (type.startsWith("Array(") && Array.isArray(value)) {
378194
+ return JSON.stringify(value);
378195
+ }
378196
+ if (typeof value === "number") {
378197
+ return formatNumber(value);
378198
+ }
378199
+ if (typeof value === "string") {
378200
+ if (type.startsWith("DateTime") && /^\d{4}-\d{2}-\d{2} /.test(value)) {
378201
+ return value.replace(" ", "T") + "Z";
378202
+ }
378203
+ return value;
378204
+ }
378205
+ return String(value);
378206
+ }
378207
+ function formatNumber(n) {
378208
+ if (Number.isInteger(n)) {
378209
+ return n.toLocaleString("en-US");
378210
+ }
378211
+ return n.toString();
378212
+ }
378213
+ function isNumericType(type) {
378214
+ const inner = unwrapType(type);
378215
+ return /^(U?Int|Float|Decimal)/.test(inner);
378216
+ }
378217
+ function unwrapType(type) {
378218
+ const m = type.match(/^(?:Nullable|LowCardinality)\((.+)\)$/);
378219
+ return m ? unwrapType(m[1]) : type;
378220
+ }
378221
+ function visibleLength(s) {
378222
+ return s.replace(/\x1b\[[0-9;]*m/g, "").length;
378223
+ }
378224
+ function pad(s, width, align) {
378225
+ const visible = visibleLength(s);
378226
+ if (visible > width) {
378227
+ return truncate(s, width);
378228
+ }
378229
+ const padding = " ".repeat(width - visible);
378230
+ return align === "right" ? padding + s : s + padding;
378231
+ }
378232
+ function truncate(s, width) {
378233
+ if (width <= 1) return "\u2026";
378234
+ return s.slice(0, width - 1) + "\u2026";
378235
+ }
378236
+ function borderLine(widths, pos) {
378237
+ const left = pos === "top" ? "\u250C" : pos === "mid" ? "\u251C" : "\u2514";
378238
+ const sep2 = pos === "top" ? "\u252C" : pos === "mid" ? "\u253C" : "\u2534";
378239
+ const right = pos === "top" ? "\u2510" : pos === "mid" ? "\u2524" : "\u2518";
378240
+ return left + widths.map((w) => "\u2500".repeat(w + 2)).join(sep2) + right;
378241
+ }
378242
+ function headerRow(values, widths, aligns, dim = false) {
378243
+ const cells = values.map((v, i) => {
378244
+ const padded = pad(v, widths[i], aligns[i]);
378245
+ return dim ? `${ANSI.dim}${padded}${ANSI.reset}` : padded;
378246
+ });
378247
+ return "\u2502 " + cells.join(" \u2502 ") + " \u2502";
378248
+ }
378249
+ function dataRow(row, widths, aligns) {
378250
+ const cells = row.map((v, i) => pad(v, widths[i], aligns[i]));
378251
+ return "\u2502 " + cells.join(" \u2502 ") + " \u2502";
378252
+ }
378253
+ function statsLine(stats, returned) {
378254
+ const parts = [`${returned.toLocaleString("en-US")} rows`];
378255
+ if (stats.elapsed !== void 0) {
378256
+ parts.push(`${formatElapsed(stats.elapsed)}`);
378257
+ }
378258
+ if (stats.rows_read !== void 0) {
378259
+ parts.push(`${stats.rows_read.toLocaleString("en-US")} rows scanned`);
378260
+ }
378261
+ return `${ANSI.dim}${parts.join(" \xB7 ")}${ANSI.reset}`;
378262
+ }
378263
+ function formatElapsed(seconds) {
378264
+ const ms = seconds * 1e3;
378265
+ if (ms < 1e3) return `${ms.toFixed(0)}ms`;
378266
+ return `${seconds.toFixed(2)}s`;
378267
+ }
378268
+
378269
+ // src/commands/query.tsx
378270
+ async function queryCommand(sqlArg, options2) {
378271
+ try {
378272
+ const sql = await resolveSql(sqlArg);
378273
+ const projectId = readProjectId();
378274
+ const token = await getValidAccessToken();
378275
+ const client2 = new SpecificClient({ accessToken: token });
378276
+ const environmentId = await resolveEnvironment(client2, projectId, options2.env);
378277
+ const result = await client2.runObservabilityQuery(environmentId, sql);
378278
+ process.stdout.write(renderTable(result));
378279
+ } catch (err) {
378280
+ const message = err instanceof Error ? err.message : "Unknown error occurred";
378281
+ const friendly = friendlyErrorMessage(message);
378282
+ console.error(`Error: ${friendly}`);
378283
+ process.exit(1);
378284
+ }
378285
+ }
378286
+ async function resolveSql(arg) {
378287
+ if (arg && arg !== "-") return arg;
378288
+ if (process.stdin.isTTY) {
378289
+ throw new Error("Provide SQL as an argument or via stdin.");
378290
+ }
378291
+ return await readStdin();
378292
+ }
378293
+ async function readStdin() {
378294
+ return new Promise((resolve9, reject) => {
378295
+ let data = "";
378296
+ process.stdin.setEncoding("utf-8");
378297
+ process.stdin.on("data", (chunk) => data += chunk);
378298
+ process.stdin.on("end", () => resolve9(data.trim()));
378299
+ process.stdin.on("error", reject);
378300
+ });
378301
+ }
378302
+ async function resolveEnvironment(client2, projectId, envFlag) {
378303
+ const status = await client2.getProjectStatus(projectId);
378304
+ const environments = status.environments;
378305
+ if (envFlag) {
378306
+ const match = environments.find((e) => e.name === envFlag);
378307
+ if (!match) {
378308
+ const available = environments.map((e) => e.name).join(", ") || "(none)";
378309
+ throw new Error(
378310
+ `Environment "${envFlag}" not found. Available: ${available}`
378311
+ );
378312
+ }
378313
+ return match.id;
378314
+ }
378315
+ if (hasEnvironmentId()) {
378316
+ const savedId = readEnvironmentId();
378317
+ if (environments.some((e) => e.id === savedId)) {
378318
+ return savedId;
378319
+ }
378320
+ }
378321
+ if (environments.length === 1) {
378322
+ return environments[0].id;
378323
+ }
378324
+ if (environments.length === 0) {
378325
+ throw new Error("This project has no environments yet.");
378326
+ }
378327
+ const names = environments.map((e) => e.name).join(", ");
378328
+ throw new Error(
378329
+ `Project has multiple environments (${names}). Pass --env <name>.`
378330
+ );
378331
+ }
378332
+ function friendlyErrorMessage(raw) {
378333
+ const m = raw.match(/^Query failed: (.+?) \([A-Z_]+\)$/);
378334
+ if (m) return m[1];
378335
+ return raw;
378336
+ }
378337
+
378116
378338
  // src/cli-program.tsx
378117
378339
  var program = new Command();
378118
378340
  var env = "production";
378119
378341
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
378120
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.133").enablePositionalOptions();
378342
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.134").enablePositionalOptions();
378121
378343
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
378122
378344
  Examples:
378123
378345
  $ specific init
@@ -378194,6 +378416,14 @@ Examples:
378194
378416
  program.command("status").description("Show project, environments, and deployment status").addHelpText("after", `
378195
378417
  Examples:
378196
378418
  $ specific status`).action(statusCommand);
378419
+ program.command("query [sql]").description("Run a SQL query against your environment's observability data").option("--env <name>", "Target environment (defaults to the current one)").addHelpText("after", `
378420
+ Examples:
378421
+ $ specific query "SELECT count() FROM observability.logs"
378422
+ $ specific query --env staging "SELECT * FROM observability.logs LIMIT 5"
378423
+ $ cat queries/p99.sql | specific query
378424
+ $ specific query - < queries/p99.sql`).action((sql, options2) => {
378425
+ queryCommand(sql, options2);
378426
+ });
378197
378427
  program.command("beta").description("Manage beta feature flags").action(betaCommand);
378198
378428
  program.command("update").description("Update Specific CLI to the latest version").action(updateCommand);
378199
378429
  program.command("login").description("Log in to Specific").option("--device-code <code>", "Complete login using a device code from a previous non-interactive login").action((options2) => loginCommand(options2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/cli",
3
- "version": "0.1.133",
3
+ "version": "0.1.134",
4
4
  "description": "CLI for Specific infrastructure-as-code",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",