postgresai 0.17.0-rc.4 → 0.17.0-rc.5
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/bin/postgres-ai.js +463 -105
- package/package.json +1 -1
package/dist/bin/postgres-ai.js
CHANGED
|
@@ -13466,8 +13466,11 @@ function resolveBaseUrls2(opts, cfg, defaults = {}) {
|
|
|
13466
13466
|
storageBaseUrl: normalizeBaseUrl2(storageCandidate)
|
|
13467
13467
|
};
|
|
13468
13468
|
}
|
|
13469
|
-
var DEFAULT_HTTP_REQUEST_TIMEOUT_MS2 = 25000;
|
|
13470
|
-
var init_util = () => {
|
|
13469
|
+
var DEFAULT_HTTP_REQUEST_TIMEOUT_MS2 = 25000, SENSITIVE_KEY_SRC2 = "(?:password|passwd|db[-_]?pass|connstr|secret|token|api[-_]?key|private[-_]?key|access[-_]?key|cred(?:ential)?s?|dsn)", SENSITIVE_PAIR_SEPARATOR_SRC2 = `["']?\\s*[:=]`, CREDENTIAL_CHAR_SRC2 = "[A-Za-z0-9_+/=~.-]", TEXT_SENSITIVE_PAIR2, CREDENTIAL_RUN_ONLY2;
|
|
13470
|
+
var init_util = __esm(() => {
|
|
13471
|
+
TEXT_SENSITIVE_PAIR2 = new RegExp(`(${SENSITIVE_KEY_SRC2}${SENSITIVE_PAIR_SEPARATOR_SRC2}\\s*)` + `(?:("[^"]*"|'[^']*')` + `|([^\\s,;&]+)` + `((?:\\s+(?![^\\s,;&]*${SENSITIVE_KEY_SRC2}${SENSITIVE_PAIR_SEPARATOR_SRC2})[^\\s,;&]+)*)` + `)`, "gi");
|
|
13472
|
+
CREDENTIAL_RUN_ONLY2 = new RegExp(`^${CREDENTIAL_CHAR_SRC2}+$`);
|
|
13473
|
+
});
|
|
13471
13474
|
|
|
13472
13475
|
// node_modules/commander/index.js
|
|
13473
13476
|
var exports_commander = {};
|
|
@@ -13530,7 +13533,7 @@ var {
|
|
|
13530
13533
|
// package.json
|
|
13531
13534
|
var package_default = {
|
|
13532
13535
|
name: "postgresai",
|
|
13533
|
-
version: "0.17.0-rc.
|
|
13536
|
+
version: "0.17.0-rc.5",
|
|
13534
13537
|
description: "postgres_ai CLI",
|
|
13535
13538
|
license: "Apache-2.0",
|
|
13536
13539
|
private: false,
|
|
@@ -16890,7 +16893,7 @@ var Result = import_lib.default.Result;
|
|
|
16890
16893
|
var TypeOverrides = import_lib.default.TypeOverrides;
|
|
16891
16894
|
var defaults = import_lib.default.defaults;
|
|
16892
16895
|
// package.json
|
|
16893
|
-
var version = "0.17.0-rc.
|
|
16896
|
+
var version = "0.17.0-rc.5";
|
|
16894
16897
|
var package_default2 = {
|
|
16895
16898
|
name: "postgresai",
|
|
16896
16899
|
version,
|
|
@@ -17166,6 +17169,22 @@ class HttpStatusError extends Error {
|
|
|
17166
17169
|
function isRetryableHttpStatus(status) {
|
|
17167
17170
|
return status >= 500 || status === 429;
|
|
17168
17171
|
}
|
|
17172
|
+
function isUnknownRpcSignature(status, text, rpc) {
|
|
17173
|
+
if (status !== 404)
|
|
17174
|
+
return false;
|
|
17175
|
+
try {
|
|
17176
|
+
const parsed = JSON.parse(text);
|
|
17177
|
+
return typeof parsed.message === "string" && /Could not find the .* function/.test(parsed.message) && parsed.message.includes(`${rpc}(`);
|
|
17178
|
+
} catch {
|
|
17179
|
+
return false;
|
|
17180
|
+
}
|
|
17181
|
+
}
|
|
17182
|
+
function positiveSeconds(v) {
|
|
17183
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
|
|
17184
|
+
}
|
|
17185
|
+
function asyncPollDelayMs(attempt) {
|
|
17186
|
+
return Math.min(1000 * 2 ** attempt, 15000);
|
|
17187
|
+
}
|
|
17169
17188
|
var DEFAULT_HTTP_REQUEST_TIMEOUT_MS = 25000;
|
|
17170
17189
|
|
|
17171
17190
|
class HttpRequestTimeoutError extends Error {
|
|
@@ -17219,9 +17238,104 @@ function redactSecretsForLog(text) {
|
|
|
17219
17238
|
return JSON.stringify(redactSecrets(parsed));
|
|
17220
17239
|
}
|
|
17221
17240
|
var TEXT_URL_USERINFO = /(\b[a-z][a-z0-9+.-]*:\/\/[^\s/:@]+):[^\s/@]+@/gi;
|
|
17222
|
-
var
|
|
17241
|
+
var SENSITIVE_KEY_SRC = "(?:password|passwd|db[-_]?pass|connstr|secret|token|api[-_]?key|private[-_]?key|access[-_]?key|cred(?:ential)?s?|dsn)";
|
|
17242
|
+
var SENSITIVE_PAIR_SEPARATOR_SRC = `["']?\\s*[:=]`;
|
|
17243
|
+
var CREDENTIAL_CHAR_SRC = "[A-Za-z0-9_+/=~.-]";
|
|
17244
|
+
var MIN_CREDENTIAL_RUN = 12;
|
|
17245
|
+
var TEXT_SENSITIVE_PAIR = new RegExp(`(${SENSITIVE_KEY_SRC}${SENSITIVE_PAIR_SEPARATOR_SRC}\\s*)` + `(?:("[^"]*"|'[^']*')` + `|([^\\s,;&]+)` + `((?:\\s+(?![^\\s,;&]*${SENSITIVE_KEY_SRC}${SENSITIVE_PAIR_SEPARATOR_SRC})[^\\s,;&]+)*)` + `)`, "gi");
|
|
17246
|
+
var CREDENTIAL_RUN_ONLY = new RegExp(`^${CREDENTIAL_CHAR_SRC}+$`);
|
|
17247
|
+
var RUN_LEADING_PUNCTUATION = /^[("'\[{<]+/;
|
|
17248
|
+
var RUN_TRAILING_PUNCTUATION = /[)"'\]}>!?.,;:]+$/;
|
|
17249
|
+
var LONG_CONSONANT_RUN = /[bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ]{5,}/;
|
|
17250
|
+
var UUID_SHAPE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
17251
|
+
function isWordShaped(s) {
|
|
17252
|
+
if (!/^[A-Za-z]+$/.test(s))
|
|
17253
|
+
return false;
|
|
17254
|
+
if (s.length <= 2)
|
|
17255
|
+
return true;
|
|
17256
|
+
if (hasInternalCapital(s))
|
|
17257
|
+
return false;
|
|
17258
|
+
if (!/[aeiouyAEIOUY]/.test(s))
|
|
17259
|
+
return false;
|
|
17260
|
+
return !LONG_CONSONANT_RUN.test(s);
|
|
17261
|
+
}
|
|
17262
|
+
function hasInternalCapital(s) {
|
|
17263
|
+
return /[a-z]/.test(s) && /.[A-Z]/.test(s);
|
|
17264
|
+
}
|
|
17265
|
+
function hasHardStructure(core) {
|
|
17266
|
+
if (/=/.test(core.replace(/={1,2}$/, "")))
|
|
17267
|
+
return true;
|
|
17268
|
+
return UUID_SHAPE.test(core);
|
|
17269
|
+
}
|
|
17270
|
+
function isSeparatedName(core) {
|
|
17271
|
+
const segments = core.split(/[/_.-]/).filter((part) => part.length > 0);
|
|
17272
|
+
if (segments.length < 2)
|
|
17273
|
+
return false;
|
|
17274
|
+
if (!segments.some(isNameSegment))
|
|
17275
|
+
return false;
|
|
17276
|
+
return segments.every((part) => isNameSegment(part) || /^[0-9]{1,4}$/.test(part));
|
|
17277
|
+
}
|
|
17278
|
+
function isNameSegment(part) {
|
|
17279
|
+
return isWordShaped(part) || /^[A-Z]+$/.test(part) && part.length <= 6;
|
|
17280
|
+
}
|
|
17281
|
+
function hasTokenSignal(core) {
|
|
17282
|
+
if (/[0-9_+]/.test(core))
|
|
17283
|
+
return true;
|
|
17284
|
+
if (/=$/.test(core))
|
|
17285
|
+
return true;
|
|
17286
|
+
if (hasInternalCapital(core))
|
|
17287
|
+
return true;
|
|
17288
|
+
return LONG_CONSONANT_RUN.test(core);
|
|
17289
|
+
}
|
|
17290
|
+
function runCore(run) {
|
|
17291
|
+
return run.replace(RUN_LEADING_PUNCTUATION, "").replace(RUN_TRAILING_PUNCTUATION, "");
|
|
17292
|
+
}
|
|
17293
|
+
function looksLikeCredentialRun(core) {
|
|
17294
|
+
return core.length >= MIN_CREDENTIAL_RUN && !hasHardStructure(core) && hasTokenSignal(core);
|
|
17295
|
+
}
|
|
17296
|
+
function isChainableFragment(core) {
|
|
17297
|
+
return core.length > 0 && CREDENTIAL_RUN_ONLY.test(core) && !hasHardStructure(core) && !isWordShaped(core);
|
|
17298
|
+
}
|
|
17299
|
+
function replaceSensitivePair(_match, keyPrefix, quotedValue, _bareValue, tail) {
|
|
17300
|
+
if (quotedValue !== undefined)
|
|
17301
|
+
return `${keyPrefix}[REDACTED]`;
|
|
17302
|
+
const runs = [...(tail ?? "").matchAll(/(\s+)(\S+)/g)].map((m) => {
|
|
17303
|
+
const run = m[2] ?? "";
|
|
17304
|
+
return { gap: m[1] ?? "", run, core: runCore(run) };
|
|
17305
|
+
});
|
|
17306
|
+
const absorbed = runs.map(() => false);
|
|
17307
|
+
for (let i = 0;i < runs.length; ) {
|
|
17308
|
+
if (!isChainableFragment(runs[i].core)) {
|
|
17309
|
+
i++;
|
|
17310
|
+
continue;
|
|
17311
|
+
}
|
|
17312
|
+
let end = i;
|
|
17313
|
+
while (end < runs.length && isChainableFragment(runs[end].core))
|
|
17314
|
+
end++;
|
|
17315
|
+
const members = runs.slice(i, end);
|
|
17316
|
+
const chain = members.map((r) => r.core).join("");
|
|
17317
|
+
const allNames = members.every((r) => isSeparatedName(r.core));
|
|
17318
|
+
if (!allNames && looksLikeCredentialRun(chain))
|
|
17319
|
+
for (let k = i;k < end; k++)
|
|
17320
|
+
absorbed[k] = true;
|
|
17321
|
+
i = end;
|
|
17322
|
+
}
|
|
17323
|
+
let kept = "";
|
|
17324
|
+
for (let k = 0;k < runs.length; k++) {
|
|
17325
|
+
const r = runs[k];
|
|
17326
|
+
if (!absorbed[k]) {
|
|
17327
|
+
kept += r.gap + r.run;
|
|
17328
|
+
continue;
|
|
17329
|
+
}
|
|
17330
|
+
const lead = r.run.match(RUN_LEADING_PUNCTUATION)?.[0] ?? "";
|
|
17331
|
+
const trail = r.run.match(RUN_TRAILING_PUNCTUATION)?.[0] ?? "";
|
|
17332
|
+
if (lead || trail)
|
|
17333
|
+
kept += r.gap + lead + trail;
|
|
17334
|
+
}
|
|
17335
|
+
return `${keyPrefix}[REDACTED]${kept}`;
|
|
17336
|
+
}
|
|
17223
17337
|
function redactTextSecrets(text) {
|
|
17224
|
-
return text.replace(TEXT_URL_USERINFO, "$1:[REDACTED]@").replace(TEXT_SENSITIVE_PAIR,
|
|
17338
|
+
return text.replace(TEXT_URL_USERINFO, "$1:[REDACTED]@").replace(TEXT_SENSITIVE_PAIR, replaceSensitivePair);
|
|
17225
17339
|
}
|
|
17226
17340
|
function isControlCodePoint(cp) {
|
|
17227
17341
|
return cp < 32 || cp === 127 || cp >= 128 && cp <= 159 || cp === 8232 || cp === 8233;
|
|
@@ -26473,68 +26587,103 @@ function renderMarkdownForTerminal(md) {
|
|
|
26473
26587
|
// lib/joe.ts
|
|
26474
26588
|
var DEFAULT_BUDGET_MS = 25000;
|
|
26475
26589
|
var DEFAULT_POLL_INTERVAL_MS = 800;
|
|
26590
|
+
var JOE_ASYNC_FALLBACK_WAIT_MS = 10 * 60 * 1000;
|
|
26591
|
+
var JOE_ASYNC_MAX_WAIT_MS = 60 * 60 * 1000;
|
|
26592
|
+
var JOE_ASYNC_MAX_POLL_FAILURES = 5;
|
|
26476
26593
|
async function callRpc(params) {
|
|
26594
|
+
return (await callRpcRaw(params)).data;
|
|
26595
|
+
}
|
|
26596
|
+
async function callRpcRaw(params) {
|
|
26477
26597
|
const { apiKey, apiBaseUrl, fn, body, operation, debug } = params;
|
|
26478
26598
|
if (!apiKey) {
|
|
26479
26599
|
throw new Error("API key is required");
|
|
26480
26600
|
}
|
|
26481
26601
|
const base = normalizeBaseUrl(apiBaseUrl);
|
|
26482
26602
|
const url = new URL(`${base}/rpc/${fn}`);
|
|
26483
|
-
const payload = JSON.stringify(body);
|
|
26484
26603
|
const headers = buildAuthHeaders(apiKey);
|
|
26485
|
-
|
|
26486
|
-
const
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
|
|
26498
|
-
|
|
26499
|
-
|
|
26500
|
-
|
|
26501
|
-
|
|
26502
|
-
|
|
26604
|
+
const send = async (bodyObj) => {
|
|
26605
|
+
const payload = JSON.stringify(bodyObj);
|
|
26606
|
+
if (debug) {
|
|
26607
|
+
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
26608
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
26609
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
26610
|
+
console.error(`Debug: Request body: ${redactSecretsForLog(payload)}`);
|
|
26611
|
+
}
|
|
26612
|
+
let response;
|
|
26613
|
+
const requestTimeout = requestTimeoutSignal(params.timeoutMs);
|
|
26614
|
+
try {
|
|
26615
|
+
response = await fetch(url.toString(), {
|
|
26616
|
+
method: "POST",
|
|
26617
|
+
headers,
|
|
26618
|
+
body: payload,
|
|
26619
|
+
signal: requestTimeout.signal
|
|
26620
|
+
});
|
|
26621
|
+
} catch (err) {
|
|
26622
|
+
if (isFetchTimeout(err)) {
|
|
26623
|
+
throw new HttpRequestTimeoutError(operation, requestTimeout.timeoutMs);
|
|
26624
|
+
}
|
|
26625
|
+
throw new Error(describeFetchError(operation, base, err));
|
|
26503
26626
|
}
|
|
26504
|
-
|
|
26505
|
-
|
|
26506
|
-
|
|
26507
|
-
|
|
26508
|
-
|
|
26509
|
-
|
|
26627
|
+
const text = await response.text();
|
|
26628
|
+
if (debug) {
|
|
26629
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
26630
|
+
console.error(`Debug: Response body: ${redactSecretsForLog(text)}`);
|
|
26631
|
+
}
|
|
26632
|
+
return { response, text };
|
|
26633
|
+
};
|
|
26634
|
+
let { response, text } = await send(body);
|
|
26635
|
+
if (params.fallbackBody && isUnknownRpcSignature(response.status, text, fn)) {
|
|
26636
|
+
({ response, text } = await send(params.fallbackBody));
|
|
26510
26637
|
}
|
|
26511
26638
|
if (!response.ok) {
|
|
26512
26639
|
throw new HttpStatusError(formatHttpError(operation, response.status, text, response.statusText), response.status);
|
|
26513
26640
|
}
|
|
26514
26641
|
try {
|
|
26515
|
-
return JSON.parse(text);
|
|
26642
|
+
return { data: JSON.parse(text), status: response.status };
|
|
26516
26643
|
} catch {
|
|
26517
26644
|
throw new Error(`${operation}: failed to parse response: ${redactSecretsForLog(text)}`);
|
|
26518
26645
|
}
|
|
26519
26646
|
}
|
|
26520
|
-
|
|
26647
|
+
function joeAsyncWaitMs(handle) {
|
|
26648
|
+
const expires = positiveSeconds(handle.expires_in_s);
|
|
26649
|
+
return Math.min(expires ? expires * 1000 : JOE_ASYNC_FALLBACK_WAIT_MS, JOE_ASYNC_MAX_WAIT_MS);
|
|
26650
|
+
}
|
|
26651
|
+
function asyncHandleCommandId(reply, operation) {
|
|
26652
|
+
const commandId = reply?.command_id;
|
|
26653
|
+
if (typeof commandId !== "string" || !/^[0-9]+$/.test(commandId)) {
|
|
26654
|
+
throw new Error(`${operation}: the platform accepted the command over the job channel but ` + `returned no command id: ${redactSecretsForLog(JSON.stringify(reply))}`);
|
|
26655
|
+
}
|
|
26656
|
+
return commandId;
|
|
26657
|
+
}
|
|
26658
|
+
async function startCommandDetailed(params) {
|
|
26521
26659
|
const { apiKey, apiBaseUrl, instanceId, command, debug, timeoutMs } = params;
|
|
26522
26660
|
if (!String(command ?? "").trim()) {
|
|
26523
26661
|
throw new Error("command text is required");
|
|
26524
26662
|
}
|
|
26525
|
-
const
|
|
26663
|
+
const operation = "Failed to run Joe command";
|
|
26664
|
+
const body = { instance_id: instanceId, command };
|
|
26665
|
+
const { data, status } = await callRpcRaw({
|
|
26526
26666
|
apiKey,
|
|
26527
26667
|
apiBaseUrl,
|
|
26528
26668
|
fn: "joe_command_run",
|
|
26529
|
-
body: {
|
|
26530
|
-
|
|
26669
|
+
body: { ...body, accept_async: true },
|
|
26670
|
+
fallbackBody: body,
|
|
26671
|
+
operation,
|
|
26531
26672
|
debug,
|
|
26532
26673
|
timeoutMs
|
|
26533
26674
|
});
|
|
26534
|
-
if (
|
|
26535
|
-
|
|
26675
|
+
if (status === 202) {
|
|
26676
|
+
const handle = data ?? {};
|
|
26677
|
+
return {
|
|
26678
|
+
commandId: asyncHandleCommandId(data, operation),
|
|
26679
|
+
asyncWaitMs: joeAsyncWaitMs(handle),
|
|
26680
|
+
firstAnswerEstimateS: positiveSeconds(handle.first_answer_estimate_s)
|
|
26681
|
+
};
|
|
26536
26682
|
}
|
|
26537
|
-
|
|
26683
|
+
if (typeof data !== "string" || !/^[0-9]+$/.test(data)) {
|
|
26684
|
+
throw new Error(`${operation}: expected a command id string, got: ${redactSecretsForLog(JSON.stringify(data))}`);
|
|
26685
|
+
}
|
|
26686
|
+
return { commandId: data, asyncWaitMs: null, firstAnswerEstimateS: null };
|
|
26538
26687
|
}
|
|
26539
26688
|
async function getCommandOutput(params) {
|
|
26540
26689
|
const { apiKey, apiBaseUrl, commandId, debug, timeoutMs } = params;
|
|
@@ -26565,7 +26714,10 @@ function normalizeProjectRow(row) {
|
|
|
26565
26714
|
joe_ready: Boolean(row.joe_ready ?? false),
|
|
26566
26715
|
tunnel: Boolean(row.tunnel ?? false),
|
|
26567
26716
|
instance_id: row.instance_id == null ? null : preserveIntegerId(row.instance_id),
|
|
26568
|
-
dblab_instance_id: row.dblab_instance_id == null ? null : preserveIntegerId(row.dblab_instance_id)
|
|
26717
|
+
dblab_instance_id: row.dblab_instance_id == null ? null : preserveIntegerId(row.dblab_instance_id),
|
|
26718
|
+
...row.monitoring_instance_ids !== undefined && {
|
|
26719
|
+
monitoring_instance_ids: row.monitoring_instance_ids ?? []
|
|
26720
|
+
}
|
|
26569
26721
|
};
|
|
26570
26722
|
}
|
|
26571
26723
|
async function listProjects(params) {
|
|
@@ -26661,18 +26813,29 @@ function buildJoeCommandText(command, input = {}) {
|
|
|
26661
26813
|
var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26662
26814
|
async function runCommand(params) {
|
|
26663
26815
|
const { apiKey, apiBaseUrl, instanceId, command, debug } = params;
|
|
26664
|
-
const
|
|
26665
|
-
const pollIntervalMs = params.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
26816
|
+
const explicitBudgetMs = typeof params.budgetMs === "number" && Number.isFinite(params.budgetMs) && params.budgetMs >= 0 ? params.budgetMs : null;
|
|
26666
26817
|
const now = params.now ?? Date.now;
|
|
26667
26818
|
const sleep = params.sleep ?? defaultSleep;
|
|
26668
|
-
const
|
|
26819
|
+
const started = await startCommandDetailed({ apiKey, apiBaseUrl, instanceId, command, debug });
|
|
26820
|
+
const commandId = started.commandId;
|
|
26821
|
+
const budgetMs = explicitBudgetMs ?? started.asyncWaitMs ?? DEFAULT_BUDGET_MS;
|
|
26822
|
+
const pollDelayMs = (attempt) => params.pollIntervalMs ?? (started.asyncWaitMs === null ? DEFAULT_POLL_INTERVAL_MS : asyncPollDelayMs(attempt));
|
|
26669
26823
|
const deadline = now() + budgetMs;
|
|
26670
26824
|
let status = "pending";
|
|
26671
26825
|
const remainingRequestMs = () => Math.max(1, Math.min(DEFAULT_HTTP_REQUEST_TIMEOUT_MS, deadline - now()));
|
|
26672
26826
|
if (now() >= deadline) {
|
|
26673
|
-
return { commandId, status, output: null, budgetExpired: true };
|
|
26827
|
+
return { commandId, status, output: null, budgetExpired: true, budgetMs };
|
|
26674
26828
|
}
|
|
26675
|
-
|
|
26829
|
+
let announced = false;
|
|
26830
|
+
const announce = () => {
|
|
26831
|
+
if (announced || started.asyncWaitMs === null)
|
|
26832
|
+
return;
|
|
26833
|
+
announced = true;
|
|
26834
|
+
const estimate = started.firstAnswerEstimateS;
|
|
26835
|
+
console.error(`Queued on the Joe job channel (command ${commandId}` + (estimate ? `, reaches Joe in about ${estimate}s` : "") + `); waiting up to ${Math.round(budgetMs / 1000)}s...`);
|
|
26836
|
+
};
|
|
26837
|
+
let transientFailures = 0;
|
|
26838
|
+
for (let attempt = 0;; attempt++) {
|
|
26676
26839
|
let output;
|
|
26677
26840
|
try {
|
|
26678
26841
|
output = await getCommandOutput({
|
|
@@ -26684,25 +26847,28 @@ async function runCommand(params) {
|
|
|
26684
26847
|
});
|
|
26685
26848
|
} catch (err) {
|
|
26686
26849
|
if (err instanceof HttpRequestTimeoutError) {
|
|
26687
|
-
return { commandId, status, output: null, budgetExpired: true };
|
|
26850
|
+
return { commandId, status, output: null, budgetExpired: true, budgetMs };
|
|
26688
26851
|
}
|
|
26689
26852
|
if (err instanceof HttpStatusError && isRetryableHttpStatus(err.status)) {
|
|
26690
|
-
|
|
26691
|
-
|
|
26853
|
+
transientFailures += 1;
|
|
26854
|
+
if (now() >= deadline || started.asyncWaitMs !== null && transientFailures >= JOE_ASYNC_MAX_POLL_FAILURES) {
|
|
26855
|
+
return { commandId, status, output: null, budgetExpired: true, budgetMs };
|
|
26692
26856
|
}
|
|
26693
|
-
await sleep(
|
|
26857
|
+
await sleep(pollDelayMs(attempt));
|
|
26694
26858
|
continue;
|
|
26695
26859
|
}
|
|
26696
26860
|
throw err;
|
|
26697
26861
|
}
|
|
26862
|
+
transientFailures = 0;
|
|
26698
26863
|
status = output.status;
|
|
26699
26864
|
if (status === "ok" || status === "error") {
|
|
26700
|
-
return { commandId, status, output, budgetExpired: false };
|
|
26865
|
+
return { commandId, status, output, budgetExpired: false, budgetMs };
|
|
26701
26866
|
}
|
|
26702
26867
|
if (now() >= deadline) {
|
|
26703
|
-
return { commandId, status, output: null, budgetExpired: true };
|
|
26868
|
+
return { commandId, status, output: null, budgetExpired: true, budgetMs };
|
|
26704
26869
|
}
|
|
26705
|
-
|
|
26870
|
+
announce();
|
|
26871
|
+
await sleep(pollDelayMs(attempt));
|
|
26706
26872
|
}
|
|
26707
26873
|
}
|
|
26708
26874
|
async function executeJoeCommand(params) {
|
|
@@ -26813,46 +26979,58 @@ function formatProjectsTable(projects) {
|
|
|
26813
26979
|
}
|
|
26814
26980
|
|
|
26815
26981
|
// lib/joe.ts
|
|
26982
|
+
var JOE_ASYNC_FALLBACK_WAIT_MS2 = 10 * 60 * 1000;
|
|
26983
|
+
var JOE_ASYNC_MAX_WAIT_MS2 = 60 * 60 * 1000;
|
|
26816
26984
|
async function callRpc2(params) {
|
|
26985
|
+
return (await callRpcRaw2(params)).data;
|
|
26986
|
+
}
|
|
26987
|
+
async function callRpcRaw2(params) {
|
|
26817
26988
|
const { apiKey, apiBaseUrl, fn, body, operation, debug } = params;
|
|
26818
26989
|
if (!apiKey) {
|
|
26819
26990
|
throw new Error("API key is required");
|
|
26820
26991
|
}
|
|
26821
26992
|
const base = normalizeBaseUrl(apiBaseUrl);
|
|
26822
26993
|
const url = new URL(`${base}/rpc/${fn}`);
|
|
26823
|
-
const payload = JSON.stringify(body);
|
|
26824
26994
|
const headers = buildAuthHeaders(apiKey);
|
|
26825
|
-
|
|
26826
|
-
const
|
|
26827
|
-
|
|
26828
|
-
|
|
26829
|
-
|
|
26830
|
-
|
|
26831
|
-
|
|
26832
|
-
|
|
26833
|
-
|
|
26834
|
-
|
|
26835
|
-
|
|
26836
|
-
|
|
26837
|
-
|
|
26838
|
-
|
|
26839
|
-
|
|
26840
|
-
|
|
26841
|
-
|
|
26842
|
-
|
|
26995
|
+
const send = async (bodyObj) => {
|
|
26996
|
+
const payload = JSON.stringify(bodyObj);
|
|
26997
|
+
if (debug) {
|
|
26998
|
+
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
26999
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
27000
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
27001
|
+
console.error(`Debug: Request body: ${redactSecretsForLog(payload)}`);
|
|
27002
|
+
}
|
|
27003
|
+
let response;
|
|
27004
|
+
const requestTimeout = requestTimeoutSignal(params.timeoutMs);
|
|
27005
|
+
try {
|
|
27006
|
+
response = await fetch(url.toString(), {
|
|
27007
|
+
method: "POST",
|
|
27008
|
+
headers,
|
|
27009
|
+
body: payload,
|
|
27010
|
+
signal: requestTimeout.signal
|
|
27011
|
+
});
|
|
27012
|
+
} catch (err) {
|
|
27013
|
+
if (isFetchTimeout(err)) {
|
|
27014
|
+
throw new HttpRequestTimeoutError(operation, requestTimeout.timeoutMs);
|
|
27015
|
+
}
|
|
27016
|
+
throw new Error(describeFetchError(operation, base, err));
|
|
26843
27017
|
}
|
|
26844
|
-
|
|
26845
|
-
|
|
26846
|
-
|
|
26847
|
-
|
|
26848
|
-
|
|
26849
|
-
|
|
27018
|
+
const text = await response.text();
|
|
27019
|
+
if (debug) {
|
|
27020
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
27021
|
+
console.error(`Debug: Response body: ${redactSecretsForLog(text)}`);
|
|
27022
|
+
}
|
|
27023
|
+
return { response, text };
|
|
27024
|
+
};
|
|
27025
|
+
let { response, text } = await send(body);
|
|
27026
|
+
if (params.fallbackBody && isUnknownRpcSignature(response.status, text, fn)) {
|
|
27027
|
+
({ response, text } = await send(params.fallbackBody));
|
|
26850
27028
|
}
|
|
26851
27029
|
if (!response.ok) {
|
|
26852
27030
|
throw new HttpStatusError(formatHttpError(operation, response.status, text, response.statusText), response.status);
|
|
26853
27031
|
}
|
|
26854
27032
|
try {
|
|
26855
|
-
return JSON.parse(text);
|
|
27033
|
+
return { data: JSON.parse(text), status: response.status };
|
|
26856
27034
|
} catch {
|
|
26857
27035
|
throw new Error(`${operation}: failed to parse response: ${redactSecretsForLog(text)}`);
|
|
26858
27036
|
}
|
|
@@ -26871,7 +27049,10 @@ function normalizeProjectRow2(row) {
|
|
|
26871
27049
|
joe_ready: Boolean(row.joe_ready ?? false),
|
|
26872
27050
|
tunnel: Boolean(row.tunnel ?? false),
|
|
26873
27051
|
instance_id: row.instance_id == null ? null : preserveIntegerId2(row.instance_id),
|
|
26874
|
-
dblab_instance_id: row.dblab_instance_id == null ? null : preserveIntegerId2(row.dblab_instance_id)
|
|
27052
|
+
dblab_instance_id: row.dblab_instance_id == null ? null : preserveIntegerId2(row.dblab_instance_id),
|
|
27053
|
+
...row.monitoring_instance_ids !== undefined && {
|
|
27054
|
+
monitoring_instance_ids: row.monitoring_instance_ids ?? []
|
|
27055
|
+
}
|
|
26875
27056
|
};
|
|
26876
27057
|
}
|
|
26877
27058
|
async function listProjects2(params) {
|
|
@@ -26930,24 +27111,10 @@ async function resolveDblabInstanceId(params) {
|
|
|
26930
27111
|
}
|
|
26931
27112
|
return String(match.dblab_instance_id);
|
|
26932
27113
|
}
|
|
26933
|
-
async function
|
|
26934
|
-
const { apiKey, apiBaseUrl,
|
|
26935
|
-
if (!apiKey) {
|
|
26936
|
-
throw new Error("API key is required");
|
|
26937
|
-
}
|
|
26938
|
-
if (!instanceId) {
|
|
26939
|
-
throw new Error("instanceId is required");
|
|
26940
|
-
}
|
|
27114
|
+
async function postDblabRpc(rpc, bodyObj, params) {
|
|
27115
|
+
const { apiKey, apiBaseUrl, operation, debug } = params;
|
|
26941
27116
|
const base = normalizeBaseUrl(apiBaseUrl);
|
|
26942
|
-
const url = new URL(`${base}/rpc
|
|
26943
|
-
const bodyObj = {
|
|
26944
|
-
instance_id: instanceId,
|
|
26945
|
-
action,
|
|
26946
|
-
method
|
|
26947
|
-
};
|
|
26948
|
-
if (data !== undefined) {
|
|
26949
|
-
bodyObj.data = data;
|
|
26950
|
-
}
|
|
27117
|
+
const url = new URL(`${base}/rpc/${rpc}`);
|
|
26951
27118
|
const body = JSON.stringify(bodyObj);
|
|
26952
27119
|
const headers = buildAuthHeaders(apiKey);
|
|
26953
27120
|
if (debug) {
|
|
@@ -26971,13 +27138,163 @@ async function callDblabApi(params) {
|
|
|
26971
27138
|
}
|
|
26972
27139
|
throw new Error(describeFetchError(operation, base, err));
|
|
26973
27140
|
}
|
|
26974
|
-
|
|
27141
|
+
let text;
|
|
27142
|
+
try {
|
|
27143
|
+
text = await response.text();
|
|
27144
|
+
} catch (err) {
|
|
27145
|
+
if (isFetchTimeout(err)) {
|
|
27146
|
+
throw new HttpRequestTimeoutError(operation, requestTimeout.timeoutMs);
|
|
27147
|
+
}
|
|
27148
|
+
throw new Error(describeFetchError(operation, base, err));
|
|
27149
|
+
}
|
|
26975
27150
|
if (debug) {
|
|
26976
27151
|
console.error(`Debug: Response status: ${response.status}`);
|
|
26977
27152
|
console.error(`Debug: Response body: ${redactSecretsForLog(text)}`);
|
|
26978
27153
|
}
|
|
27154
|
+
return { response, text };
|
|
27155
|
+
}
|
|
27156
|
+
var DBLAB_ASYNC_FALLBACK_WAIT_MS = 15 * 60 * 1000;
|
|
27157
|
+
var DBLAB_ASYNC_MAX_WAIT_MS = 60 * 60 * 1000;
|
|
27158
|
+
var DBLAB_POLL_MAX_CONSECUTIVE_FAILURES = 5;
|
|
27159
|
+
var DBLAB_JOB_PENDING = new Set(["queued", "running"]);
|
|
27160
|
+
function dblabMayHaveLandedAdvice() {
|
|
27161
|
+
return "The platform may have accepted this call even though the reply was lost — " + "check the instance's current state before running this command again.";
|
|
27162
|
+
}
|
|
27163
|
+
function dblabNotResentAdvice(jobId) {
|
|
27164
|
+
return `The call (job ${jobId}) was NOT re-sent and may still complete on the instance — ` + "check its current state (e.g. `pgai dblab clone list`) before running this command again.";
|
|
27165
|
+
}
|
|
27166
|
+
function stripControlsKeepingLines(text) {
|
|
27167
|
+
return text.split(`
|
|
27168
|
+
`).map(stripControlsToSpace).join(`
|
|
27169
|
+
`);
|
|
27170
|
+
}
|
|
27171
|
+
function scrubBoxText(text) {
|
|
27172
|
+
return stripControlsToSpace(redactTextSecrets(redactSecretsForLog(text))).replace(/\s+/g, " ").trim();
|
|
27173
|
+
}
|
|
27174
|
+
async function pollDblabCallOnce(jobIdRaw, jobId, params) {
|
|
27175
|
+
const { operation } = params;
|
|
27176
|
+
const advice = dblabNotResentAdvice(jobId);
|
|
27177
|
+
let response;
|
|
27178
|
+
let text;
|
|
27179
|
+
try {
|
|
27180
|
+
({ response, text } = await postDblabRpc("dblab_call_result", { p_job_id: jobIdRaw }, params));
|
|
27181
|
+
} catch (err) {
|
|
27182
|
+
return { kind: "transient", why: err instanceof Error ? err.message : String(err) };
|
|
27183
|
+
}
|
|
26979
27184
|
if (!response.ok) {
|
|
26980
|
-
|
|
27185
|
+
if (isRetryableHttpStatus(response.status)) {
|
|
27186
|
+
return { kind: "transient", why: `HTTP ${response.status}` };
|
|
27187
|
+
}
|
|
27188
|
+
throw new HttpStatusError(`${stripControlsKeepingLines(formatHttpError(operation, response.status, text, response.statusText))}
|
|
27189
|
+
${advice}`, response.status);
|
|
27190
|
+
}
|
|
27191
|
+
let res;
|
|
27192
|
+
try {
|
|
27193
|
+
res = JSON.parse(text);
|
|
27194
|
+
} catch {
|
|
27195
|
+
throw new Error(`${operation}: failed to parse response: ${stripControlsKeepingLines(redactSecretsForLog(text))}. ${advice}`);
|
|
27196
|
+
}
|
|
27197
|
+
if (!res || typeof res.status !== "string") {
|
|
27198
|
+
throw new Error(`${operation}: dblab_call_result returned an unexpected reply: ${stripControlsKeepingLines(redactSecretsForLog(text))}. ${advice}`);
|
|
27199
|
+
}
|
|
27200
|
+
return { kind: "result", res };
|
|
27201
|
+
}
|
|
27202
|
+
async function awaitDblabCall(handle, params) {
|
|
27203
|
+
const { operation } = params;
|
|
27204
|
+
const jobIdRaw = typeof handle.job_id === "string" ? handle.job_id : "";
|
|
27205
|
+
const jobId = jobIdRaw ? scrubBoxText(jobIdRaw) : "";
|
|
27206
|
+
if (!jobId) {
|
|
27207
|
+
throw new Error(`${operation}: the platform accepted the call but returned no job_id. ` + dblabMayHaveLandedAdvice());
|
|
27208
|
+
}
|
|
27209
|
+
const now = params.now ?? (() => Date.now());
|
|
27210
|
+
const sleep = params.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
27211
|
+
const waitMs = Math.min((positiveSeconds(handle.expires_in_s) ?? 0) * 1000 || DBLAB_ASYNC_FALLBACK_WAIT_MS, DBLAB_ASYNC_MAX_WAIT_MS);
|
|
27212
|
+
const deadline = now() + waitMs;
|
|
27213
|
+
const estimate = positiveSeconds(handle.first_answer_estimate_s);
|
|
27214
|
+
let announced = false;
|
|
27215
|
+
let failures = 0;
|
|
27216
|
+
let lastStatus = "unknown";
|
|
27217
|
+
for (let attempt = 0;; attempt++) {
|
|
27218
|
+
const outcome = await pollDblabCallOnce(jobIdRaw, jobId, params);
|
|
27219
|
+
if (outcome.kind === "transient") {
|
|
27220
|
+
failures += 1;
|
|
27221
|
+
if (failures >= DBLAB_POLL_MAX_CONSECUTIVE_FAILURES) {
|
|
27222
|
+
throw new Error(`${operation}: lost contact with the platform while waiting (${failures} polls in a row failed, ` + `last: ${outcome.why}). ${dblabNotResentAdvice(jobId)}`);
|
|
27223
|
+
}
|
|
27224
|
+
} else {
|
|
27225
|
+
failures = 0;
|
|
27226
|
+
const res = outcome.res;
|
|
27227
|
+
lastStatus = scrubBoxText(res.status);
|
|
27228
|
+
if (res.status === "done" && res.outcome === "ok") {
|
|
27229
|
+
return res.result ?? null;
|
|
27230
|
+
}
|
|
27231
|
+
if (!DBLAB_JOB_PENDING.has(res.status)) {
|
|
27232
|
+
const why = typeof res.error === "string" && res.error ? res.error : res.status === "expired" ? "no agent claimed it before it expired" : `outcome ${String(res.outcome ?? "unknown")}`;
|
|
27233
|
+
const klass = typeof res.failure_class === "string" && res.failure_class ? ` [${scrubBoxText(res.failure_class)}]` : "";
|
|
27234
|
+
const advice = res.status === "expired" ? "" : ` ${dblabNotResentAdvice(jobId)}`;
|
|
27235
|
+
throw new Error(`${operation}: the DBLab instance did not complete the call (status ${scrubBoxText(res.status)}) — ` + `${scrubBoxText(why)}${klass}.${advice}`);
|
|
27236
|
+
}
|
|
27237
|
+
}
|
|
27238
|
+
if (!announced && outcome.kind === "result") {
|
|
27239
|
+
announced = true;
|
|
27240
|
+
console.error(`Waiting for the DBLab instance to pick up the call (job ${jobId}` + (estimate ? `, typically ~${estimate}s` : "") + ")...");
|
|
27241
|
+
}
|
|
27242
|
+
const delay = asyncPollDelayMs(attempt);
|
|
27243
|
+
if (now() + delay >= deadline) {
|
|
27244
|
+
throw new Error(`${operation}: timed out waiting for the DBLab instance (job ${jobId}, last status ${lastStatus}). ` + dblabNotResentAdvice(jobId));
|
|
27245
|
+
}
|
|
27246
|
+
await sleep(delay);
|
|
27247
|
+
}
|
|
27248
|
+
}
|
|
27249
|
+
async function enqueueDblabCall(bodyObj, params, isWrite) {
|
|
27250
|
+
try {
|
|
27251
|
+
return await postDblabRpc("dblab_api_call", bodyObj, params);
|
|
27252
|
+
} catch (err) {
|
|
27253
|
+
if (!isWrite)
|
|
27254
|
+
throw err;
|
|
27255
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
27256
|
+
throw new Error(`${message}
|
|
27257
|
+
${dblabMayHaveLandedAdvice()}`, { cause: err });
|
|
27258
|
+
}
|
|
27259
|
+
}
|
|
27260
|
+
async function callDblabApi(params) {
|
|
27261
|
+
const { apiKey, apiBaseUrl, instanceId, action, method, data, operation, debug } = params;
|
|
27262
|
+
if (!apiKey) {
|
|
27263
|
+
throw new Error("API key is required");
|
|
27264
|
+
}
|
|
27265
|
+
if (!instanceId) {
|
|
27266
|
+
throw new Error("instanceId is required");
|
|
27267
|
+
}
|
|
27268
|
+
const bodyObj = {
|
|
27269
|
+
instance_id: instanceId,
|
|
27270
|
+
action,
|
|
27271
|
+
method
|
|
27272
|
+
};
|
|
27273
|
+
if (data !== undefined) {
|
|
27274
|
+
bodyObj.data = data;
|
|
27275
|
+
}
|
|
27276
|
+
const rpcParams = { apiKey, apiBaseUrl, operation, debug };
|
|
27277
|
+
const isWrite = method.toLowerCase() !== "get";
|
|
27278
|
+
let { response, text } = await enqueueDblabCall({ ...bodyObj, accept_async: true }, rpcParams, isWrite);
|
|
27279
|
+
if (isUnknownRpcSignature(response.status, text, "dblab_api_call")) {
|
|
27280
|
+
({ response, text } = await enqueueDblabCall(bodyObj, rpcParams, isWrite));
|
|
27281
|
+
}
|
|
27282
|
+
if (response.status === 202) {
|
|
27283
|
+
let handle;
|
|
27284
|
+
try {
|
|
27285
|
+
handle = JSON.parse(text);
|
|
27286
|
+
} catch {
|
|
27287
|
+
throw new Error(`${operation}: failed to parse response: ${stripControlsKeepingLines(redactSecretsForLog(text))}. ` + dblabMayHaveLandedAdvice());
|
|
27288
|
+
}
|
|
27289
|
+
if (!handle || typeof handle !== "object") {
|
|
27290
|
+
throw new Error(`${operation}: the platform accepted the call but returned no handle. ` + dblabMayHaveLandedAdvice());
|
|
27291
|
+
}
|
|
27292
|
+
return await awaitDblabCall(handle, rpcParams);
|
|
27293
|
+
}
|
|
27294
|
+
if (!response.ok) {
|
|
27295
|
+
const lost = isWrite && response.status >= 500;
|
|
27296
|
+
throw new HttpStatusError(stripControlsKeepingLines(formatHttpError(operation, response.status, text, response.statusText)) + (lost ? `
|
|
27297
|
+
${dblabMayHaveLandedAdvice()}` : ""), response.status);
|
|
26981
27298
|
}
|
|
26982
27299
|
if (text.trim() === "") {
|
|
26983
27300
|
return null;
|
|
@@ -26985,7 +27302,7 @@ async function callDblabApi(params) {
|
|
|
26985
27302
|
try {
|
|
26986
27303
|
return JSON.parse(text);
|
|
26987
27304
|
} catch {
|
|
26988
|
-
throw new Error(`${operation}: failed to parse response: ${redactSecretsForLog(text)}`);
|
|
27305
|
+
throw new Error(`${operation}: failed to parse response: ${stripControlsKeepingLines(redactSecretsForLog(text))}`);
|
|
26989
27306
|
}
|
|
26990
27307
|
}
|
|
26991
27308
|
async function createClone(params) {
|
|
@@ -27222,6 +27539,37 @@ async function callRpc3(name, apiKey, body, opts) {
|
|
|
27222
27539
|
throw new Error(`${name} returned a body that is not JSON`);
|
|
27223
27540
|
}
|
|
27224
27541
|
}
|
|
27542
|
+
async function resolveMonitoringInstanceId(params) {
|
|
27543
|
+
const ref = String(params.project ?? "").trim();
|
|
27544
|
+
if (!ref) {
|
|
27545
|
+
throw new Error("project is required (--project <id|alias>)");
|
|
27546
|
+
}
|
|
27547
|
+
const projects = await listProjects2({
|
|
27548
|
+
apiKey: params.apiKey,
|
|
27549
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
27550
|
+
orgId: params.orgId
|
|
27551
|
+
});
|
|
27552
|
+
const needle = ref.toLowerCase();
|
|
27553
|
+
const matches = isNumericProjectRef2(ref) ? projects.filter((p) => String(p.project_id) === BigInt(ref).toString()) : projects.filter((p) => p.alias !== null && p.alias.toLowerCase() === needle || p.name !== null && p.name.toLowerCase() === needle);
|
|
27554
|
+
if (matches.length === 0) {
|
|
27555
|
+
throw new Error(`Project not found for id/alias/name '${ref}'. Run 'pgai projects' to see available projects.`);
|
|
27556
|
+
}
|
|
27557
|
+
if (matches.length > 1) {
|
|
27558
|
+
throw new Error(`'${ref}' matches ${matches.length} projects (ids ${matches.map((p) => p.project_id).join(", ")}). ` + "Pass --project <id> instead.");
|
|
27559
|
+
}
|
|
27560
|
+
const match = matches[0];
|
|
27561
|
+
const ids = match.monitoring_instance_ids;
|
|
27562
|
+
if (ids === undefined) {
|
|
27563
|
+
throw new Error("This platform does not report monitoring instances per project yet. Pass --instance <uuid> instead.");
|
|
27564
|
+
}
|
|
27565
|
+
if (ids.length === 0) {
|
|
27566
|
+
throw new Error(`Project '${ref}' has no active monitoring instance.`);
|
|
27567
|
+
}
|
|
27568
|
+
if (ids.length > 1) {
|
|
27569
|
+
throw new Error(`Project '${ref}' has ${ids.length} active monitoring instances: ${ids.join(", ")}. Pass --instance <uuid>.`);
|
|
27570
|
+
}
|
|
27571
|
+
return ids[0];
|
|
27572
|
+
}
|
|
27225
27573
|
async function enqueueQuery(apiKey, args, opts) {
|
|
27226
27574
|
const body = {
|
|
27227
27575
|
p_instance_id: args.instanceId,
|
|
@@ -27240,9 +27588,9 @@ async function enqueueQuery(apiKey, args, opts) {
|
|
|
27240
27588
|
if (!jobId) {
|
|
27241
27589
|
throw new Error("instance_query_enqueue returned no job_id");
|
|
27242
27590
|
}
|
|
27243
|
-
return { jobId, firstAnswerEstimateS:
|
|
27591
|
+
return { jobId, firstAnswerEstimateS: positiveSeconds2(reply?.first_answer_estimate_s) };
|
|
27244
27592
|
}
|
|
27245
|
-
function
|
|
27593
|
+
function positiveSeconds2(v) {
|
|
27246
27594
|
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
|
|
27247
27595
|
}
|
|
27248
27596
|
async function readQueryResult(apiKey, jobId, opts) {
|
|
@@ -42810,7 +43158,7 @@ program2.command("orgs").description("list organizations this credential can rea
|
|
|
42810
43158
|
process.exitCode = 1;
|
|
42811
43159
|
}
|
|
42812
43160
|
});
|
|
42813
|
-
withOrgOptions(program2.command("promql")).description("run a PromQL query on a monitoring instance's own metric store").argument("<expr>", "PromQL expression").option("--instance <uuid>", "monitoring instance id (default: this box's .pgwatch-config)").option("--at <rfc3339>", "evaluate an instant query at this time (default: now; ignored with --range)").option("--range", "run a range query (requires --start, --end, --step)").option("--start <rfc3339>", "range start").option("--end <rfc3339>", "range end").option("--step <duration>", "range step, e.g. 60s or 5m").option("--timeout <duration>", "how long to wait for an answer (default: sized from the instance's poll pacing; 11m if the platform does not report it)").option("--json", "output the raw result payload").action(async (expr, opts) => {
|
|
43161
|
+
withOrgOptions(program2.command("promql")).description("run a PromQL query on a monitoring instance's own metric store").argument("<expr>", "PromQL expression").option("--instance <uuid>", "monitoring instance id (default: this box's .pgwatch-config); not with --project").option("--project <id|alias>", "the project's monitoring instance, by project id, alias or name; not with --instance").option("--at <rfc3339>", "evaluate an instant query at this time (default: now; ignored with --range)").option("--range", "run a range query (requires --start, --end, --step)").option("--start <rfc3339>", "range start").option("--end <rfc3339>", "range end").option("--step <duration>", "range step, e.g. 60s or 5m").option("--timeout <duration>", "how long to wait for an answer (default: sized from the instance's poll pacing; 11m if the platform does not report it)").option("--json", "output the raw result payload").action(async (expr, opts) => {
|
|
42814
43162
|
try {
|
|
42815
43163
|
const rootOpts = program2.opts();
|
|
42816
43164
|
const cfg = readConfig();
|
|
@@ -42821,9 +43169,19 @@ withOrgOptions(program2.command("promql")).description("run a PromQL query on a
|
|
|
42821
43169
|
return;
|
|
42822
43170
|
}
|
|
42823
43171
|
const { apiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
|
|
42824
|
-
|
|
43172
|
+
if (opts.instance && opts.project) {
|
|
43173
|
+
console.error("Pass --instance or --project, not both.");
|
|
43174
|
+
process.exitCode = 1;
|
|
43175
|
+
return;
|
|
43176
|
+
}
|
|
43177
|
+
const instanceId = opts.instance || (opts.project ? await resolveMonitoringInstanceId({
|
|
43178
|
+
apiKey,
|
|
43179
|
+
apiBaseUrl,
|
|
43180
|
+
project: opts.project,
|
|
43181
|
+
orgId: configOrgIdForBody(apiKey, cfg.orgId)
|
|
43182
|
+
}) : readInstanceIdFromProject());
|
|
42825
43183
|
if (!instanceId) {
|
|
42826
|
-
console.error("No monitoring instance. Pass --instance <uuid>, or run this on a box whose");
|
|
43184
|
+
console.error("No monitoring instance. Pass --project <id|alias> or --instance <uuid>, or run this on a box whose");
|
|
42827
43185
|
console.error(".pgwatch-config has an instance_id (written by 'pgai mon local-install').");
|
|
42828
43186
|
process.exitCode = 1;
|
|
42829
43187
|
return;
|
|
@@ -43675,7 +44033,7 @@ function tryParseJson(s) {
|
|
|
43675
44033
|
}
|
|
43676
44034
|
}
|
|
43677
44035
|
function printJoeOutcome(outcome, json, budgetMs) {
|
|
43678
|
-
const effectiveBudgetMs = typeof budgetMs === "number" && Number.isFinite(budgetMs) ? budgetMs : DEFAULT_BUDGET_MS;
|
|
44036
|
+
const effectiveBudgetMs = Number.isFinite(outcome.budgetMs) ? outcome.budgetMs : typeof budgetMs === "number" && Number.isFinite(budgetMs) ? budgetMs : DEFAULT_BUDGET_MS;
|
|
43679
44037
|
const budgetSeconds = Math.round(effectiveBudgetMs / 1000);
|
|
43680
44038
|
if (outcome.budgetExpired) {
|
|
43681
44039
|
if (json) {
|