@specific.dev/cli 0.1.132 → 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.
- package/dist/admin/404/index.html +1 -1
- package/dist/admin/404.html +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/__next._full.txt +1 -1
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +1 -1
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_not-found/__next._full.txt +1 -1
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.txt +1 -1
- package/dist/admin/_not-found/__next._tree.txt +1 -1
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/databases/__next._full.txt +1 -1
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +1 -1
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +1 -1
- package/dist/admin/fullscreen/__next._full.txt +1 -1
- package/dist/admin/fullscreen/__next._head.txt +1 -1
- package/dist/admin/fullscreen/__next._index.txt +1 -1
- package/dist/admin/fullscreen/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/index.html +1 -1
- package/dist/admin/fullscreen/databases/index.txt +1 -1
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/mail/__next._full.txt +1 -1
- package/dist/admin/mail/__next._head.txt +1 -1
- package/dist/admin/mail/__next._index.txt +1 -1
- package/dist/admin/mail/__next._tree.txt +1 -1
- package/dist/admin/mail/index.html +1 -1
- package/dist/admin/mail/index.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
- package/dist/admin/workflows/__next._full.txt +1 -1
- package/dist/admin/workflows/__next._head.txt +1 -1
- package/dist/admin/workflows/__next._index.txt +1 -1
- package/dist/admin/workflows/__next._tree.txt +1 -1
- package/dist/admin/workflows/index.html +1 -1
- package/dist/admin/workflows/index.txt +1 -1
- package/dist/cli.js +271 -37
- package/package.json +1 -1
- /package/dist/admin/_next/static/{QhvYQwbwiUXVu-lpkuczQ → INvT6nTMaJ_qj_9Q3KulI}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{QhvYQwbwiUXVu-lpkuczQ → INvT6nTMaJ_qj_9Q3KulI}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{QhvYQwbwiUXVu-lpkuczQ → 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.
|
|
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.
|
|
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}`);
|
|
@@ -378066,54 +378091,255 @@ async function projectListCommand() {
|
|
|
378066
378091
|
}
|
|
378067
378092
|
|
|
378068
378093
|
// src/commands/status.tsx
|
|
378069
|
-
|
|
378094
|
+
import React12, { useState as useState11, useEffect as useEffect11 } from "react";
|
|
378095
|
+
import { render as render10, Text as Text12, Box as Box11 } from "ink";
|
|
378096
|
+
import Spinner7 from "ink-spinner";
|
|
378097
|
+
function statusColor(s) {
|
|
378098
|
+
switch (s) {
|
|
378099
|
+
case "active":
|
|
378100
|
+
return "green";
|
|
378101
|
+
case "deploying":
|
|
378102
|
+
return "yellow";
|
|
378103
|
+
case "failed":
|
|
378104
|
+
return "red";
|
|
378105
|
+
case "none":
|
|
378106
|
+
return "gray";
|
|
378107
|
+
}
|
|
378108
|
+
}
|
|
378109
|
+
function StatusUI() {
|
|
378110
|
+
const [state, setState] = useState11({ phase: "loading" });
|
|
378111
|
+
useEffect11(() => {
|
|
378112
|
+
(async () => {
|
|
378113
|
+
try {
|
|
378114
|
+
const projectId = readProjectId();
|
|
378115
|
+
const token = await getValidAccessToken();
|
|
378116
|
+
const client2 = new SpecificClient({ accessToken: token });
|
|
378117
|
+
const status2 = await client2.getProjectStatus(projectId);
|
|
378118
|
+
setState({ phase: "success", status: status2 });
|
|
378119
|
+
} catch (err) {
|
|
378120
|
+
setState({
|
|
378121
|
+
phase: "error",
|
|
378122
|
+
error: err instanceof Error ? err.message : String(err)
|
|
378123
|
+
});
|
|
378124
|
+
}
|
|
378125
|
+
})();
|
|
378126
|
+
}, []);
|
|
378127
|
+
if (state.phase === "loading") {
|
|
378128
|
+
return /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text12, { color: "blue" }, /* @__PURE__ */ React12.createElement(Spinner7, { type: "dots" })), /* @__PURE__ */ React12.createElement(Text12, null, " Loading project status..."));
|
|
378129
|
+
}
|
|
378130
|
+
if (state.phase === "error") {
|
|
378131
|
+
process.exitCode = 1;
|
|
378132
|
+
return /* @__PURE__ */ React12.createElement(Text12, { color: "red" }, "Failed to get status: ", state.error);
|
|
378133
|
+
}
|
|
378134
|
+
const status = state.status;
|
|
378135
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, /* @__PURE__ */ React12.createElement(Text12, { bold: true }, status.name), /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, " (", status.id, ")")), status.environments.length === 0 ? /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, "No environments found.") : status.environments.map((env2) => /* @__PURE__ */ React12.createElement(Box11, { key: env2.id, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text12, null, /* @__PURE__ */ React12.createElement(Text12, { bold: true, color: "cyan" }, env2.name), /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, " (", env2.id, ")")), /* @__PURE__ */ React12.createElement(Text12, null, " ", "Status: ", /* @__PURE__ */ React12.createElement(Text12, { color: statusColor(env2.status), bold: true }, env2.status)), /* @__PURE__ */ React12.createElement(Text12, null, " ", "Active deployment:", " ", env2.activeDeployment ? /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, { color: "green" }, env2.activeDeployment.id), /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, " (", env2.activeDeployment.state, ")")) : /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, "none")), env2.inProgressDeployment && /* @__PURE__ */ React12.createElement(Text12, null, " ", "In progress:", " ", /* @__PURE__ */ React12.createElement(Text12, { color: "yellow" }, env2.inProgressDeployment.id), /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, " (", env2.inProgressDeployment.state, ")")), env2.publicUrls && Object.keys(env2.publicUrls).length > 0 && /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text12, null, " ", "Public URLs:"), Object.entries(env2.publicUrls).map(([serviceName, url]) => /* @__PURE__ */ React12.createElement(Text12, { key: serviceName }, " - ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, serviceName), ": ", /* @__PURE__ */ React12.createElement(Text12, { color: "blue" }, url)))))));
|
|
378136
|
+
}
|
|
378137
|
+
function statusCommand() {
|
|
378138
|
+
render10(/* @__PURE__ */ React12.createElement(StatusUI, null));
|
|
378139
|
+
}
|
|
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) {
|
|
378070
378271
|
try {
|
|
378272
|
+
const sql = await resolveSql(sqlArg);
|
|
378071
378273
|
const projectId = readProjectId();
|
|
378072
378274
|
const token = await getValidAccessToken();
|
|
378073
378275
|
const client2 = new SpecificClient({ accessToken: token });
|
|
378074
|
-
const
|
|
378075
|
-
|
|
378076
|
-
|
|
378077
|
-
|
|
378078
|
-
|
|
378079
|
-
|
|
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
|
+
);
|
|
378080
378312
|
}
|
|
378081
|
-
|
|
378082
|
-
|
|
378083
|
-
|
|
378084
|
-
|
|
378085
|
-
|
|
378086
|
-
|
|
378087
|
-
` Active deployment: ${env2.activeDeployment.id} (${env2.activeDeployment.state})`
|
|
378088
|
-
);
|
|
378089
|
-
} else {
|
|
378090
|
-
console.log(` Active deployment: none`);
|
|
378091
|
-
}
|
|
378092
|
-
if (env2.inProgressDeployments.length > 0) {
|
|
378093
|
-
console.log(` In progress:`);
|
|
378094
|
-
for (const d of env2.inProgressDeployments) {
|
|
378095
|
-
console.log(` - ${d.id} (${d.state})`);
|
|
378096
|
-
}
|
|
378097
|
-
}
|
|
378098
|
-
if (env2.publicUrls && Object.keys(env2.publicUrls).length > 0) {
|
|
378099
|
-
console.log(` Public URLs:`);
|
|
378100
|
-
for (const [serviceName, url] of Object.entries(env2.publicUrls)) {
|
|
378101
|
-
console.log(` - ${serviceName}: ${url}`);
|
|
378102
|
-
}
|
|
378103
|
-
}
|
|
378313
|
+
return match.id;
|
|
378314
|
+
}
|
|
378315
|
+
if (hasEnvironmentId()) {
|
|
378316
|
+
const savedId = readEnvironmentId();
|
|
378317
|
+
if (environments.some((e) => e.id === savedId)) {
|
|
378318
|
+
return savedId;
|
|
378104
378319
|
}
|
|
378105
|
-
} catch (error) {
|
|
378106
|
-
const message = error instanceof Error ? error.message : "Unknown error occurred";
|
|
378107
|
-
console.error(`Failed to get status: ${message}`);
|
|
378108
|
-
process.exit(1);
|
|
378109
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;
|
|
378110
378336
|
}
|
|
378111
378337
|
|
|
378112
378338
|
// src/cli-program.tsx
|
|
378113
378339
|
var program = new Command();
|
|
378114
378340
|
var env = "production";
|
|
378115
378341
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
378116
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
378342
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.134").enablePositionalOptions();
|
|
378117
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", `
|
|
378118
378344
|
Examples:
|
|
378119
378345
|
$ specific init
|
|
@@ -378190,6 +378416,14 @@ Examples:
|
|
|
378190
378416
|
program.command("status").description("Show project, environments, and deployment status").addHelpText("after", `
|
|
378191
378417
|
Examples:
|
|
378192
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
|
+
});
|
|
378193
378427
|
program.command("beta").description("Manage beta feature flags").action(betaCommand);
|
|
378194
378428
|
program.command("update").description("Update Specific CLI to the latest version").action(updateCommand);
|
|
378195
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
/package/dist/admin/_next/static/{QhvYQwbwiUXVu-lpkuczQ → INvT6nTMaJ_qj_9Q3KulI}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/dist/admin/_next/static/{QhvYQwbwiUXVu-lpkuczQ → INvT6nTMaJ_qj_9Q3KulI}/_ssgManifest.js
RENAMED
|
File without changes
|