postgresai 0.15.0-dev.3 → 0.15.0-dev.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/bin/postgres-ai.ts +275 -50
- package/dist/bin/postgres-ai.js +833 -394
- package/lib/checkup.ts +16 -10
- package/lib/config.ts +3 -0
- package/lib/init.ts +1 -1
- package/lib/issues.ts +72 -72
- package/lib/reports.ts +12 -12
- package/lib/storage.ts +291 -0
- package/lib/util.ts +7 -1
- package/package.json +1 -1
- package/test/compose-cmd.test.ts +120 -0
- package/test/init.test.ts +1 -1
- package/test/issues.cli.test.ts +230 -1
- package/test/mcp-server.test.ts +69 -0
- package/test/reports.test.ts +3 -3
- package/test/storage.test.ts +761 -0
package/dist/bin/postgres-ai.js
CHANGED
|
@@ -27,6 +27,7 @@ var __export = (target, all) => {
|
|
|
27
27
|
set: (newValue) => all[name] = () => newValue
|
|
28
28
|
});
|
|
29
29
|
};
|
|
30
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
31
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
31
32
|
|
|
32
33
|
// node_modules/commander/lib/error.js
|
|
@@ -13046,6 +13047,92 @@ var require_dist2 = __commonJS((exports, module) => {
|
|
|
13046
13047
|
exports.default = formatsPlugin;
|
|
13047
13048
|
});
|
|
13048
13049
|
|
|
13050
|
+
// lib/util.ts
|
|
13051
|
+
var exports_util2 = {};
|
|
13052
|
+
__export(exports_util2, {
|
|
13053
|
+
resolveBaseUrls: () => resolveBaseUrls2,
|
|
13054
|
+
normalizeBaseUrl: () => normalizeBaseUrl2,
|
|
13055
|
+
maskSecret: () => maskSecret2,
|
|
13056
|
+
formatHttpError: () => formatHttpError2
|
|
13057
|
+
});
|
|
13058
|
+
function isHtmlContent2(text) {
|
|
13059
|
+
const trimmed = text.trim();
|
|
13060
|
+
return trimmed.startsWith("<!DOCTYPE") || trimmed.startsWith("<html") || trimmed.startsWith("<HTML");
|
|
13061
|
+
}
|
|
13062
|
+
function formatHttpError2(operation, status, responseBody) {
|
|
13063
|
+
const statusMessage = HTTP_STATUS_MESSAGES2[status] || "Request failed";
|
|
13064
|
+
let errMsg = `${operation}: HTTP ${status} - ${statusMessage}`;
|
|
13065
|
+
if (responseBody) {
|
|
13066
|
+
if (isHtmlContent2(responseBody)) {
|
|
13067
|
+
return errMsg;
|
|
13068
|
+
}
|
|
13069
|
+
try {
|
|
13070
|
+
const errObj = JSON.parse(responseBody);
|
|
13071
|
+
const message = errObj.message || errObj.error || errObj.detail;
|
|
13072
|
+
if (message && typeof message === "string") {
|
|
13073
|
+
errMsg += `
|
|
13074
|
+
${message}`;
|
|
13075
|
+
} else {
|
|
13076
|
+
errMsg += `
|
|
13077
|
+
${JSON.stringify(errObj, null, 2)}`;
|
|
13078
|
+
}
|
|
13079
|
+
} catch {
|
|
13080
|
+
const trimmed = responseBody.trim();
|
|
13081
|
+
if (trimmed.length > 0 && trimmed.length < 500) {
|
|
13082
|
+
errMsg += `
|
|
13083
|
+
${trimmed}`;
|
|
13084
|
+
}
|
|
13085
|
+
}
|
|
13086
|
+
}
|
|
13087
|
+
return errMsg;
|
|
13088
|
+
}
|
|
13089
|
+
function maskSecret2(secret) {
|
|
13090
|
+
if (!secret)
|
|
13091
|
+
return "";
|
|
13092
|
+
if (secret.length <= 8)
|
|
13093
|
+
return "****";
|
|
13094
|
+
if (secret.length <= 16)
|
|
13095
|
+
return `${secret.slice(0, 4)}${"*".repeat(secret.length - 8)}${secret.slice(-4)}`;
|
|
13096
|
+
return `${secret.slice(0, Math.min(12, secret.length - 8))}${"*".repeat(Math.max(4, secret.length - 16))}${secret.slice(-4)}`;
|
|
13097
|
+
}
|
|
13098
|
+
function normalizeBaseUrl2(value) {
|
|
13099
|
+
const trimmed = (value || "").replace(/\/$/, "");
|
|
13100
|
+
try {
|
|
13101
|
+
new URL(trimmed);
|
|
13102
|
+
} catch {
|
|
13103
|
+
throw new Error(`Invalid base URL: ${value}`);
|
|
13104
|
+
}
|
|
13105
|
+
return trimmed;
|
|
13106
|
+
}
|
|
13107
|
+
function resolveBaseUrls2(opts, cfg, defaults2 = {}) {
|
|
13108
|
+
const defApi = defaults2.apiBaseUrl || "https://postgres.ai/api/general/";
|
|
13109
|
+
const defUi = defaults2.uiBaseUrl || "https://console.postgres.ai";
|
|
13110
|
+
const defStorage = defaults2.storageBaseUrl || "https://postgres.ai/storage";
|
|
13111
|
+
const apiCandidate = opts?.apiBaseUrl || process.env.PGAI_API_BASE_URL || cfg?.baseUrl || defApi;
|
|
13112
|
+
const uiCandidate = opts?.uiBaseUrl || process.env.PGAI_UI_BASE_URL || defUi;
|
|
13113
|
+
const storageCandidate = opts?.storageBaseUrl || process.env.PGAI_STORAGE_BASE_URL || cfg?.storageBaseUrl || defStorage;
|
|
13114
|
+
return {
|
|
13115
|
+
apiBaseUrl: normalizeBaseUrl2(apiCandidate),
|
|
13116
|
+
uiBaseUrl: normalizeBaseUrl2(uiCandidate),
|
|
13117
|
+
storageBaseUrl: normalizeBaseUrl2(storageCandidate)
|
|
13118
|
+
};
|
|
13119
|
+
}
|
|
13120
|
+
var HTTP_STATUS_MESSAGES2;
|
|
13121
|
+
var init_util = __esm(() => {
|
|
13122
|
+
HTTP_STATUS_MESSAGES2 = {
|
|
13123
|
+
400: "Bad Request",
|
|
13124
|
+
401: "Unauthorized - check your API key",
|
|
13125
|
+
403: "Forbidden - access denied",
|
|
13126
|
+
404: "Not Found",
|
|
13127
|
+
408: "Request Timeout",
|
|
13128
|
+
429: "Too Many Requests - rate limited",
|
|
13129
|
+
500: "Internal Server Error",
|
|
13130
|
+
502: "Bad Gateway - server temporarily unavailable",
|
|
13131
|
+
503: "Service Unavailable - server temporarily unavailable",
|
|
13132
|
+
504: "Gateway Timeout - server temporarily unavailable"
|
|
13133
|
+
};
|
|
13134
|
+
});
|
|
13135
|
+
|
|
13049
13136
|
// node_modules/commander/esm.mjs
|
|
13050
13137
|
var import__ = __toESM(require_commander(), 1);
|
|
13051
13138
|
var {
|
|
@@ -13064,7 +13151,7 @@ var {
|
|
|
13064
13151
|
// package.json
|
|
13065
13152
|
var package_default = {
|
|
13066
13153
|
name: "postgresai",
|
|
13067
|
-
version: "0.15.0-dev.
|
|
13154
|
+
version: "0.15.0-dev.5",
|
|
13068
13155
|
description: "postgres_ai CLI",
|
|
13069
13156
|
license: "Apache-2.0",
|
|
13070
13157
|
private: false,
|
|
@@ -13140,6 +13227,7 @@ function readConfig() {
|
|
|
13140
13227
|
const config = {
|
|
13141
13228
|
apiKey: null,
|
|
13142
13229
|
baseUrl: null,
|
|
13230
|
+
storageBaseUrl: null,
|
|
13143
13231
|
orgId: null,
|
|
13144
13232
|
defaultProject: null,
|
|
13145
13233
|
projectName: null
|
|
@@ -13151,6 +13239,7 @@ function readConfig() {
|
|
|
13151
13239
|
const parsed = JSON.parse(content);
|
|
13152
13240
|
config.apiKey = parsed.apiKey ?? null;
|
|
13153
13241
|
config.baseUrl = parsed.baseUrl ?? null;
|
|
13242
|
+
config.storageBaseUrl = parsed.storageBaseUrl ?? null;
|
|
13154
13243
|
config.orgId = parsed.orgId ?? null;
|
|
13155
13244
|
config.defaultProject = parsed.defaultProject ?? null;
|
|
13156
13245
|
config.projectName = parsed.projectName ?? null;
|
|
@@ -15873,8 +15962,8 @@ var safeLoadAll = renamed("safeLoadAll", "loadAll");
|
|
|
15873
15962
|
var safeDump = renamed("safeDump", "dump");
|
|
15874
15963
|
|
|
15875
15964
|
// bin/postgres-ai.ts
|
|
15876
|
-
import * as
|
|
15877
|
-
import * as
|
|
15965
|
+
import * as fs6 from "fs";
|
|
15966
|
+
import * as path6 from "path";
|
|
15878
15967
|
import * as os3 from "os";
|
|
15879
15968
|
import * as crypto2 from "crypto";
|
|
15880
15969
|
|
|
@@ -15892,7 +15981,7 @@ var Result = import_lib.default.Result;
|
|
|
15892
15981
|
var TypeOverrides = import_lib.default.TypeOverrides;
|
|
15893
15982
|
var defaults = import_lib.default.defaults;
|
|
15894
15983
|
// package.json
|
|
15895
|
-
var version = "0.15.0-dev.
|
|
15984
|
+
var version = "0.15.0-dev.5";
|
|
15896
15985
|
var package_default2 = {
|
|
15897
15986
|
name: "postgresai",
|
|
15898
15987
|
version,
|
|
@@ -15971,6 +16060,7 @@ function readConfig2() {
|
|
|
15971
16060
|
const config = {
|
|
15972
16061
|
apiKey: null,
|
|
15973
16062
|
baseUrl: null,
|
|
16063
|
+
storageBaseUrl: null,
|
|
15974
16064
|
orgId: null,
|
|
15975
16065
|
defaultProject: null,
|
|
15976
16066
|
projectName: null
|
|
@@ -15982,6 +16072,7 @@ function readConfig2() {
|
|
|
15982
16072
|
const parsed = JSON.parse(content);
|
|
15983
16073
|
config.apiKey = parsed.apiKey ?? null;
|
|
15984
16074
|
config.baseUrl = parsed.baseUrl ?? null;
|
|
16075
|
+
config.storageBaseUrl = parsed.storageBaseUrl ?? null;
|
|
15985
16076
|
config.orgId = parsed.orgId ?? null;
|
|
15986
16077
|
config.defaultProject = parsed.defaultProject ?? null;
|
|
15987
16078
|
config.projectName = parsed.projectName ?? null;
|
|
@@ -16079,11 +16170,14 @@ function normalizeBaseUrl(value) {
|
|
|
16079
16170
|
function resolveBaseUrls(opts, cfg, defaults2 = {}) {
|
|
16080
16171
|
const defApi = defaults2.apiBaseUrl || "https://postgres.ai/api/general/";
|
|
16081
16172
|
const defUi = defaults2.uiBaseUrl || "https://console.postgres.ai";
|
|
16173
|
+
const defStorage = defaults2.storageBaseUrl || "https://postgres.ai/storage";
|
|
16082
16174
|
const apiCandidate = opts?.apiBaseUrl || process.env.PGAI_API_BASE_URL || cfg?.baseUrl || defApi;
|
|
16083
16175
|
const uiCandidate = opts?.uiBaseUrl || process.env.PGAI_UI_BASE_URL || defUi;
|
|
16176
|
+
const storageCandidate = opts?.storageBaseUrl || process.env.PGAI_STORAGE_BASE_URL || cfg?.storageBaseUrl || defStorage;
|
|
16084
16177
|
return {
|
|
16085
16178
|
apiBaseUrl: normalizeBaseUrl(apiCandidate),
|
|
16086
|
-
uiBaseUrl: normalizeBaseUrl(uiCandidate)
|
|
16179
|
+
uiBaseUrl: normalizeBaseUrl(uiCandidate),
|
|
16180
|
+
storageBaseUrl: normalizeBaseUrl(storageCandidate)
|
|
16087
16181
|
};
|
|
16088
16182
|
}
|
|
16089
16183
|
|
|
@@ -16115,18 +16209,18 @@ async function fetchIssues(params) {
|
|
|
16115
16209
|
};
|
|
16116
16210
|
if (debug) {
|
|
16117
16211
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16118
|
-
console.
|
|
16119
|
-
console.
|
|
16120
|
-
console.
|
|
16121
|
-
console.
|
|
16212
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16213
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16214
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16215
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16122
16216
|
}
|
|
16123
16217
|
const response = await fetch(url.toString(), {
|
|
16124
16218
|
method: "GET",
|
|
16125
16219
|
headers
|
|
16126
16220
|
});
|
|
16127
16221
|
if (debug) {
|
|
16128
|
-
console.
|
|
16129
|
-
console.
|
|
16222
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16223
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16130
16224
|
}
|
|
16131
16225
|
const data = await response.text();
|
|
16132
16226
|
if (response.ok) {
|
|
@@ -16157,18 +16251,18 @@ async function fetchIssueComments(params) {
|
|
|
16157
16251
|
};
|
|
16158
16252
|
if (debug) {
|
|
16159
16253
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16160
|
-
console.
|
|
16161
|
-
console.
|
|
16162
|
-
console.
|
|
16163
|
-
console.
|
|
16254
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16255
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16256
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16257
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16164
16258
|
}
|
|
16165
16259
|
const response = await fetch(url.toString(), {
|
|
16166
16260
|
method: "GET",
|
|
16167
16261
|
headers
|
|
16168
16262
|
});
|
|
16169
16263
|
if (debug) {
|
|
16170
|
-
console.
|
|
16171
|
-
console.
|
|
16264
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16265
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16172
16266
|
}
|
|
16173
16267
|
const data = await response.text();
|
|
16174
16268
|
if (response.ok) {
|
|
@@ -16202,18 +16296,18 @@ async function fetchIssue(params) {
|
|
|
16202
16296
|
};
|
|
16203
16297
|
if (debug) {
|
|
16204
16298
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16205
|
-
console.
|
|
16206
|
-
console.
|
|
16207
|
-
console.
|
|
16208
|
-
console.
|
|
16299
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16300
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16301
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16302
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16209
16303
|
}
|
|
16210
16304
|
const response = await fetch(url.toString(), {
|
|
16211
16305
|
method: "GET",
|
|
16212
16306
|
headers
|
|
16213
16307
|
});
|
|
16214
16308
|
if (debug) {
|
|
16215
|
-
console.
|
|
16216
|
-
console.
|
|
16309
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16310
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16217
16311
|
}
|
|
16218
16312
|
const data = await response.text();
|
|
16219
16313
|
if (response.ok) {
|
|
@@ -16275,11 +16369,11 @@ async function createIssue(params) {
|
|
|
16275
16369
|
};
|
|
16276
16370
|
if (debug) {
|
|
16277
16371
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16278
|
-
console.
|
|
16279
|
-
console.
|
|
16280
|
-
console.
|
|
16281
|
-
console.
|
|
16282
|
-
console.
|
|
16372
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16373
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
16374
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16375
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16376
|
+
console.error(`Debug: Request body: ${body}`);
|
|
16283
16377
|
}
|
|
16284
16378
|
const response = await fetch(url.toString(), {
|
|
16285
16379
|
method: "POST",
|
|
@@ -16287,8 +16381,8 @@ async function createIssue(params) {
|
|
|
16287
16381
|
body
|
|
16288
16382
|
});
|
|
16289
16383
|
if (debug) {
|
|
16290
|
-
console.
|
|
16291
|
-
console.
|
|
16384
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16385
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16292
16386
|
}
|
|
16293
16387
|
const data = await response.text();
|
|
16294
16388
|
if (response.ok) {
|
|
@@ -16330,11 +16424,11 @@ async function createIssueComment(params) {
|
|
|
16330
16424
|
};
|
|
16331
16425
|
if (debug) {
|
|
16332
16426
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16333
|
-
console.
|
|
16334
|
-
console.
|
|
16335
|
-
console.
|
|
16336
|
-
console.
|
|
16337
|
-
console.
|
|
16427
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16428
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
16429
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16430
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16431
|
+
console.error(`Debug: Request body: ${body}`);
|
|
16338
16432
|
}
|
|
16339
16433
|
const response = await fetch(url.toString(), {
|
|
16340
16434
|
method: "POST",
|
|
@@ -16342,8 +16436,8 @@ async function createIssueComment(params) {
|
|
|
16342
16436
|
body
|
|
16343
16437
|
});
|
|
16344
16438
|
if (debug) {
|
|
16345
|
-
console.
|
|
16346
|
-
console.
|
|
16439
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16440
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16347
16441
|
}
|
|
16348
16442
|
const data = await response.text();
|
|
16349
16443
|
if (response.ok) {
|
|
@@ -16393,11 +16487,11 @@ async function updateIssue(params) {
|
|
|
16393
16487
|
};
|
|
16394
16488
|
if (debug) {
|
|
16395
16489
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16396
|
-
console.
|
|
16397
|
-
console.
|
|
16398
|
-
console.
|
|
16399
|
-
console.
|
|
16400
|
-
console.
|
|
16490
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16491
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
16492
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16493
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16494
|
+
console.error(`Debug: Request body: ${body}`);
|
|
16401
16495
|
}
|
|
16402
16496
|
const response = await fetch(url.toString(), {
|
|
16403
16497
|
method: "POST",
|
|
@@ -16405,8 +16499,8 @@ async function updateIssue(params) {
|
|
|
16405
16499
|
body
|
|
16406
16500
|
});
|
|
16407
16501
|
if (debug) {
|
|
16408
|
-
console.
|
|
16409
|
-
console.
|
|
16502
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16503
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16410
16504
|
}
|
|
16411
16505
|
const data = await response.text();
|
|
16412
16506
|
if (response.ok) {
|
|
@@ -16445,11 +16539,11 @@ async function updateIssueComment(params) {
|
|
|
16445
16539
|
};
|
|
16446
16540
|
if (debug) {
|
|
16447
16541
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16448
|
-
console.
|
|
16449
|
-
console.
|
|
16450
|
-
console.
|
|
16451
|
-
console.
|
|
16452
|
-
console.
|
|
16542
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16543
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
16544
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16545
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16546
|
+
console.error(`Debug: Request body: ${body}`);
|
|
16453
16547
|
}
|
|
16454
16548
|
const response = await fetch(url.toString(), {
|
|
16455
16549
|
method: "POST",
|
|
@@ -16457,8 +16551,8 @@ async function updateIssueComment(params) {
|
|
|
16457
16551
|
body
|
|
16458
16552
|
});
|
|
16459
16553
|
if (debug) {
|
|
16460
|
-
console.
|
|
16461
|
-
console.
|
|
16554
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16555
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16462
16556
|
}
|
|
16463
16557
|
const data = await response.text();
|
|
16464
16558
|
if (response.ok) {
|
|
@@ -16497,18 +16591,18 @@ async function fetchActionItem(params) {
|
|
|
16497
16591
|
};
|
|
16498
16592
|
if (debug) {
|
|
16499
16593
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16500
|
-
console.
|
|
16501
|
-
console.
|
|
16502
|
-
console.
|
|
16503
|
-
console.
|
|
16594
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16595
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16596
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16597
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16504
16598
|
}
|
|
16505
16599
|
const response = await fetch(url.toString(), {
|
|
16506
16600
|
method: "GET",
|
|
16507
16601
|
headers
|
|
16508
16602
|
});
|
|
16509
16603
|
if (debug) {
|
|
16510
|
-
console.
|
|
16511
|
-
console.
|
|
16604
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16605
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16512
16606
|
}
|
|
16513
16607
|
const data = await response.text();
|
|
16514
16608
|
if (response.ok) {
|
|
@@ -16548,18 +16642,18 @@ async function fetchActionItems(params) {
|
|
|
16548
16642
|
};
|
|
16549
16643
|
if (debug) {
|
|
16550
16644
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16551
|
-
console.
|
|
16552
|
-
console.
|
|
16553
|
-
console.
|
|
16554
|
-
console.
|
|
16645
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16646
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16647
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16648
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16555
16649
|
}
|
|
16556
16650
|
const response = await fetch(url.toString(), {
|
|
16557
16651
|
method: "GET",
|
|
16558
16652
|
headers
|
|
16559
16653
|
});
|
|
16560
16654
|
if (debug) {
|
|
16561
|
-
console.
|
|
16562
|
-
console.
|
|
16655
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16656
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16563
16657
|
}
|
|
16564
16658
|
const data = await response.text();
|
|
16565
16659
|
if (response.ok) {
|
|
@@ -16611,11 +16705,11 @@ async function createActionItem(params) {
|
|
|
16611
16705
|
};
|
|
16612
16706
|
if (debug) {
|
|
16613
16707
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16614
|
-
console.
|
|
16615
|
-
console.
|
|
16616
|
-
console.
|
|
16617
|
-
console.
|
|
16618
|
-
console.
|
|
16708
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16709
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
16710
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16711
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16712
|
+
console.error(`Debug: Request body: ${body}`);
|
|
16619
16713
|
}
|
|
16620
16714
|
const response = await fetch(url.toString(), {
|
|
16621
16715
|
method: "POST",
|
|
@@ -16623,8 +16717,8 @@ async function createActionItem(params) {
|
|
|
16623
16717
|
body
|
|
16624
16718
|
});
|
|
16625
16719
|
if (debug) {
|
|
16626
|
-
console.
|
|
16627
|
-
console.
|
|
16720
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16721
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16628
16722
|
}
|
|
16629
16723
|
const data = await response.text();
|
|
16630
16724
|
if (response.ok) {
|
|
@@ -16688,11 +16782,11 @@ async function updateActionItem(params) {
|
|
|
16688
16782
|
};
|
|
16689
16783
|
if (debug) {
|
|
16690
16784
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16691
|
-
console.
|
|
16692
|
-
console.
|
|
16693
|
-
console.
|
|
16694
|
-
console.
|
|
16695
|
-
console.
|
|
16785
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16786
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
16787
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
16788
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16789
|
+
console.error(`Debug: Request body: ${body}`);
|
|
16696
16790
|
}
|
|
16697
16791
|
const response = await fetch(url.toString(), {
|
|
16698
16792
|
method: "POST",
|
|
@@ -16700,8 +16794,8 @@ async function updateActionItem(params) {
|
|
|
16700
16794
|
body
|
|
16701
16795
|
});
|
|
16702
16796
|
if (debug) {
|
|
16703
|
-
console.
|
|
16704
|
-
console.
|
|
16797
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16798
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
16705
16799
|
}
|
|
16706
16800
|
if (!response.ok) {
|
|
16707
16801
|
const data = await response.text();
|
|
@@ -16761,13 +16855,13 @@ async function fetchReports(params) {
|
|
|
16761
16855
|
};
|
|
16762
16856
|
if (debug) {
|
|
16763
16857
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16764
|
-
console.
|
|
16765
|
-
console.
|
|
16766
|
-
console.
|
|
16858
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16859
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16860
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16767
16861
|
}
|
|
16768
16862
|
const response = await fetch(url.toString(), { method: "GET", headers });
|
|
16769
16863
|
if (debug) {
|
|
16770
|
-
console.
|
|
16864
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16771
16865
|
}
|
|
16772
16866
|
const data = await response.text();
|
|
16773
16867
|
if (response.ok) {
|
|
@@ -16828,13 +16922,13 @@ async function fetchReportFiles(params) {
|
|
|
16828
16922
|
};
|
|
16829
16923
|
if (debug) {
|
|
16830
16924
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16831
|
-
console.
|
|
16832
|
-
console.
|
|
16833
|
-
console.
|
|
16925
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16926
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16927
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16834
16928
|
}
|
|
16835
16929
|
const response = await fetch(url.toString(), { method: "GET", headers });
|
|
16836
16930
|
if (debug) {
|
|
16837
|
-
console.
|
|
16931
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16838
16932
|
}
|
|
16839
16933
|
const data = await response.text();
|
|
16840
16934
|
if (response.ok) {
|
|
@@ -16875,13 +16969,13 @@ async function fetchReportFileData(params) {
|
|
|
16875
16969
|
};
|
|
16876
16970
|
if (debug) {
|
|
16877
16971
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
16878
|
-
console.
|
|
16879
|
-
console.
|
|
16880
|
-
console.
|
|
16972
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
16973
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
16974
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
16881
16975
|
}
|
|
16882
16976
|
const response = await fetch(url.toString(), { method: "GET", headers });
|
|
16883
16977
|
if (debug) {
|
|
16884
|
-
console.
|
|
16978
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
16885
16979
|
}
|
|
16886
16980
|
const data = await response.text();
|
|
16887
16981
|
if (response.ok) {
|
|
@@ -24264,18 +24358,18 @@ async function fetchIssues2(params) {
|
|
|
24264
24358
|
};
|
|
24265
24359
|
if (debug) {
|
|
24266
24360
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24267
|
-
console.
|
|
24268
|
-
console.
|
|
24269
|
-
console.
|
|
24270
|
-
console.
|
|
24361
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24362
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
24363
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24364
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24271
24365
|
}
|
|
24272
24366
|
const response = await fetch(url.toString(), {
|
|
24273
24367
|
method: "GET",
|
|
24274
24368
|
headers
|
|
24275
24369
|
});
|
|
24276
24370
|
if (debug) {
|
|
24277
|
-
console.
|
|
24278
|
-
console.
|
|
24371
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24372
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24279
24373
|
}
|
|
24280
24374
|
const data = await response.text();
|
|
24281
24375
|
if (response.ok) {
|
|
@@ -24306,18 +24400,18 @@ async function fetchIssueComments2(params) {
|
|
|
24306
24400
|
};
|
|
24307
24401
|
if (debug) {
|
|
24308
24402
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24309
|
-
console.
|
|
24310
|
-
console.
|
|
24311
|
-
console.
|
|
24312
|
-
console.
|
|
24403
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24404
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
24405
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24406
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24313
24407
|
}
|
|
24314
24408
|
const response = await fetch(url.toString(), {
|
|
24315
24409
|
method: "GET",
|
|
24316
24410
|
headers
|
|
24317
24411
|
});
|
|
24318
24412
|
if (debug) {
|
|
24319
|
-
console.
|
|
24320
|
-
console.
|
|
24413
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24414
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24321
24415
|
}
|
|
24322
24416
|
const data = await response.text();
|
|
24323
24417
|
if (response.ok) {
|
|
@@ -24351,18 +24445,18 @@ async function fetchIssue2(params) {
|
|
|
24351
24445
|
};
|
|
24352
24446
|
if (debug) {
|
|
24353
24447
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24354
|
-
console.
|
|
24355
|
-
console.
|
|
24356
|
-
console.
|
|
24357
|
-
console.
|
|
24448
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24449
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
24450
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24451
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24358
24452
|
}
|
|
24359
24453
|
const response = await fetch(url.toString(), {
|
|
24360
24454
|
method: "GET",
|
|
24361
24455
|
headers
|
|
24362
24456
|
});
|
|
24363
24457
|
if (debug) {
|
|
24364
|
-
console.
|
|
24365
|
-
console.
|
|
24458
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24459
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24366
24460
|
}
|
|
24367
24461
|
const data = await response.text();
|
|
24368
24462
|
if (response.ok) {
|
|
@@ -24424,11 +24518,11 @@ async function createIssue2(params) {
|
|
|
24424
24518
|
};
|
|
24425
24519
|
if (debug) {
|
|
24426
24520
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24427
|
-
console.
|
|
24428
|
-
console.
|
|
24429
|
-
console.
|
|
24430
|
-
console.
|
|
24431
|
-
console.
|
|
24521
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24522
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
24523
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24524
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24525
|
+
console.error(`Debug: Request body: ${body}`);
|
|
24432
24526
|
}
|
|
24433
24527
|
const response = await fetch(url.toString(), {
|
|
24434
24528
|
method: "POST",
|
|
@@ -24436,8 +24530,8 @@ async function createIssue2(params) {
|
|
|
24436
24530
|
body
|
|
24437
24531
|
});
|
|
24438
24532
|
if (debug) {
|
|
24439
|
-
console.
|
|
24440
|
-
console.
|
|
24533
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24534
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24441
24535
|
}
|
|
24442
24536
|
const data = await response.text();
|
|
24443
24537
|
if (response.ok) {
|
|
@@ -24479,11 +24573,11 @@ async function createIssueComment2(params) {
|
|
|
24479
24573
|
};
|
|
24480
24574
|
if (debug) {
|
|
24481
24575
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24482
|
-
console.
|
|
24483
|
-
console.
|
|
24484
|
-
console.
|
|
24485
|
-
console.
|
|
24486
|
-
console.
|
|
24576
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24577
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
24578
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24579
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24580
|
+
console.error(`Debug: Request body: ${body}`);
|
|
24487
24581
|
}
|
|
24488
24582
|
const response = await fetch(url.toString(), {
|
|
24489
24583
|
method: "POST",
|
|
@@ -24491,8 +24585,8 @@ async function createIssueComment2(params) {
|
|
|
24491
24585
|
body
|
|
24492
24586
|
});
|
|
24493
24587
|
if (debug) {
|
|
24494
|
-
console.
|
|
24495
|
-
console.
|
|
24588
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24589
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24496
24590
|
}
|
|
24497
24591
|
const data = await response.text();
|
|
24498
24592
|
if (response.ok) {
|
|
@@ -24542,11 +24636,11 @@ async function updateIssue2(params) {
|
|
|
24542
24636
|
};
|
|
24543
24637
|
if (debug) {
|
|
24544
24638
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24545
|
-
console.
|
|
24546
|
-
console.
|
|
24547
|
-
console.
|
|
24548
|
-
console.
|
|
24549
|
-
console.
|
|
24639
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24640
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
24641
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24642
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24643
|
+
console.error(`Debug: Request body: ${body}`);
|
|
24550
24644
|
}
|
|
24551
24645
|
const response = await fetch(url.toString(), {
|
|
24552
24646
|
method: "POST",
|
|
@@ -24554,8 +24648,8 @@ async function updateIssue2(params) {
|
|
|
24554
24648
|
body
|
|
24555
24649
|
});
|
|
24556
24650
|
if (debug) {
|
|
24557
|
-
console.
|
|
24558
|
-
console.
|
|
24651
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24652
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24559
24653
|
}
|
|
24560
24654
|
const data = await response.text();
|
|
24561
24655
|
if (response.ok) {
|
|
@@ -24594,11 +24688,11 @@ async function updateIssueComment2(params) {
|
|
|
24594
24688
|
};
|
|
24595
24689
|
if (debug) {
|
|
24596
24690
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24597
|
-
console.
|
|
24598
|
-
console.
|
|
24599
|
-
console.
|
|
24600
|
-
console.
|
|
24601
|
-
console.
|
|
24691
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24692
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
24693
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24694
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24695
|
+
console.error(`Debug: Request body: ${body}`);
|
|
24602
24696
|
}
|
|
24603
24697
|
const response = await fetch(url.toString(), {
|
|
24604
24698
|
method: "POST",
|
|
@@ -24606,8 +24700,8 @@ async function updateIssueComment2(params) {
|
|
|
24606
24700
|
body
|
|
24607
24701
|
});
|
|
24608
24702
|
if (debug) {
|
|
24609
|
-
console.
|
|
24610
|
-
console.
|
|
24703
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24704
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24611
24705
|
}
|
|
24612
24706
|
const data = await response.text();
|
|
24613
24707
|
if (response.ok) {
|
|
@@ -24646,18 +24740,18 @@ async function fetchActionItem2(params) {
|
|
|
24646
24740
|
};
|
|
24647
24741
|
if (debug) {
|
|
24648
24742
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24649
|
-
console.
|
|
24650
|
-
console.
|
|
24651
|
-
console.
|
|
24652
|
-
console.
|
|
24743
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24744
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
24745
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24746
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24653
24747
|
}
|
|
24654
24748
|
const response = await fetch(url.toString(), {
|
|
24655
24749
|
method: "GET",
|
|
24656
24750
|
headers
|
|
24657
24751
|
});
|
|
24658
24752
|
if (debug) {
|
|
24659
|
-
console.
|
|
24660
|
-
console.
|
|
24753
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24754
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24661
24755
|
}
|
|
24662
24756
|
const data = await response.text();
|
|
24663
24757
|
if (response.ok) {
|
|
@@ -24697,18 +24791,18 @@ async function fetchActionItems2(params) {
|
|
|
24697
24791
|
};
|
|
24698
24792
|
if (debug) {
|
|
24699
24793
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24700
|
-
console.
|
|
24701
|
-
console.
|
|
24702
|
-
console.
|
|
24703
|
-
console.
|
|
24794
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24795
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
24796
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24797
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24704
24798
|
}
|
|
24705
24799
|
const response = await fetch(url.toString(), {
|
|
24706
24800
|
method: "GET",
|
|
24707
24801
|
headers
|
|
24708
24802
|
});
|
|
24709
24803
|
if (debug) {
|
|
24710
|
-
console.
|
|
24711
|
-
console.
|
|
24804
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24805
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24712
24806
|
}
|
|
24713
24807
|
const data = await response.text();
|
|
24714
24808
|
if (response.ok) {
|
|
@@ -24760,11 +24854,11 @@ async function createActionItem2(params) {
|
|
|
24760
24854
|
};
|
|
24761
24855
|
if (debug) {
|
|
24762
24856
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24763
|
-
console.
|
|
24764
|
-
console.
|
|
24765
|
-
console.
|
|
24766
|
-
console.
|
|
24767
|
-
console.
|
|
24857
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24858
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
24859
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24860
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24861
|
+
console.error(`Debug: Request body: ${body}`);
|
|
24768
24862
|
}
|
|
24769
24863
|
const response = await fetch(url.toString(), {
|
|
24770
24864
|
method: "POST",
|
|
@@ -24772,8 +24866,8 @@ async function createActionItem2(params) {
|
|
|
24772
24866
|
body
|
|
24773
24867
|
});
|
|
24774
24868
|
if (debug) {
|
|
24775
|
-
console.
|
|
24776
|
-
console.
|
|
24869
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24870
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24777
24871
|
}
|
|
24778
24872
|
const data = await response.text();
|
|
24779
24873
|
if (response.ok) {
|
|
@@ -24837,11 +24931,11 @@ async function updateActionItem2(params) {
|
|
|
24837
24931
|
};
|
|
24838
24932
|
if (debug) {
|
|
24839
24933
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24840
|
-
console.
|
|
24841
|
-
console.
|
|
24842
|
-
console.
|
|
24843
|
-
console.
|
|
24844
|
-
console.
|
|
24934
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
24935
|
+
console.error(`Debug: POST URL: ${url.toString()}`);
|
|
24936
|
+
console.error(`Debug: Auth scheme: access-token`);
|
|
24937
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24938
|
+
console.error(`Debug: Request body: ${body}`);
|
|
24845
24939
|
}
|
|
24846
24940
|
const response = await fetch(url.toString(), {
|
|
24847
24941
|
method: "POST",
|
|
@@ -24849,8 +24943,8 @@ async function updateActionItem2(params) {
|
|
|
24849
24943
|
body
|
|
24850
24944
|
});
|
|
24851
24945
|
if (debug) {
|
|
24852
|
-
console.
|
|
24853
|
-
console.
|
|
24946
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24947
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
24854
24948
|
}
|
|
24855
24949
|
if (!response.ok) {
|
|
24856
24950
|
const data = await response.text();
|
|
@@ -24910,13 +25004,13 @@ async function fetchReports2(params) {
|
|
|
24910
25004
|
};
|
|
24911
25005
|
if (debug) {
|
|
24912
25006
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24913
|
-
console.
|
|
24914
|
-
console.
|
|
24915
|
-
console.
|
|
25007
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
25008
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
25009
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24916
25010
|
}
|
|
24917
25011
|
const response = await fetch(url.toString(), { method: "GET", headers });
|
|
24918
25012
|
if (debug) {
|
|
24919
|
-
console.
|
|
25013
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24920
25014
|
}
|
|
24921
25015
|
const data = await response.text();
|
|
24922
25016
|
if (response.ok) {
|
|
@@ -24977,13 +25071,13 @@ async function fetchReportFiles2(params) {
|
|
|
24977
25071
|
};
|
|
24978
25072
|
if (debug) {
|
|
24979
25073
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
24980
|
-
console.
|
|
24981
|
-
console.
|
|
24982
|
-
console.
|
|
25074
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
25075
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
25076
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
24983
25077
|
}
|
|
24984
25078
|
const response = await fetch(url.toString(), { method: "GET", headers });
|
|
24985
25079
|
if (debug) {
|
|
24986
|
-
console.
|
|
25080
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
24987
25081
|
}
|
|
24988
25082
|
const data = await response.text();
|
|
24989
25083
|
if (response.ok) {
|
|
@@ -25024,13 +25118,13 @@ async function fetchReportFileData2(params) {
|
|
|
25024
25118
|
};
|
|
25025
25119
|
if (debug) {
|
|
25026
25120
|
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
25027
|
-
console.
|
|
25028
|
-
console.
|
|
25029
|
-
console.
|
|
25121
|
+
console.error(`Debug: Resolved API base URL: ${base}`);
|
|
25122
|
+
console.error(`Debug: GET URL: ${url.toString()}`);
|
|
25123
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
25030
25124
|
}
|
|
25031
25125
|
const response = await fetch(url.toString(), { method: "GET", headers });
|
|
25032
25126
|
if (debug) {
|
|
25033
|
-
console.
|
|
25127
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
25034
25128
|
}
|
|
25035
25129
|
const data = await response.text();
|
|
25036
25130
|
if (response.ok) {
|
|
@@ -25097,41 +25191,198 @@ function renderMarkdownForTerminal(md) {
|
|
|
25097
25191
|
`);
|
|
25098
25192
|
}
|
|
25099
25193
|
|
|
25100
|
-
//
|
|
25101
|
-
|
|
25102
|
-
|
|
25103
|
-
|
|
25104
|
-
|
|
25105
|
-
|
|
25106
|
-
|
|
25107
|
-
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
|
|
25112
|
-
|
|
25113
|
-
|
|
25114
|
-
|
|
25115
|
-
|
|
25194
|
+
// bin/postgres-ai.ts
|
|
25195
|
+
init_util();
|
|
25196
|
+
|
|
25197
|
+
// lib/storage.ts
|
|
25198
|
+
import * as fs3 from "fs";
|
|
25199
|
+
import * as path3 from "path";
|
|
25200
|
+
var MAX_UPLOAD_SIZE = 500 * 1024 * 1024;
|
|
25201
|
+
var MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
25202
|
+
var MIME_TYPES = {
|
|
25203
|
+
".png": "image/png",
|
|
25204
|
+
".jpg": "image/jpeg",
|
|
25205
|
+
".jpeg": "image/jpeg",
|
|
25206
|
+
".gif": "image/gif",
|
|
25207
|
+
".webp": "image/webp",
|
|
25208
|
+
".svg": "image/svg+xml",
|
|
25209
|
+
".bmp": "image/bmp",
|
|
25210
|
+
".ico": "image/x-icon",
|
|
25211
|
+
".pdf": "application/pdf",
|
|
25212
|
+
".json": "application/json",
|
|
25213
|
+
".xml": "application/xml",
|
|
25214
|
+
".zip": "application/zip",
|
|
25215
|
+
".gz": "application/gzip",
|
|
25216
|
+
".tar": "application/x-tar",
|
|
25217
|
+
".csv": "text/csv",
|
|
25218
|
+
".txt": "text/plain",
|
|
25219
|
+
".log": "text/plain",
|
|
25220
|
+
".sql": "application/sql",
|
|
25221
|
+
".html": "text/html",
|
|
25222
|
+
".css": "text/css",
|
|
25223
|
+
".js": "application/javascript",
|
|
25224
|
+
".ts": "application/typescript",
|
|
25225
|
+
".md": "text/markdown",
|
|
25226
|
+
".yaml": "application/x-yaml",
|
|
25227
|
+
".yml": "application/x-yaml"
|
|
25228
|
+
};
|
|
25229
|
+
async function uploadFile(params) {
|
|
25230
|
+
const { apiKey, storageBaseUrl, filePath, debug } = params;
|
|
25231
|
+
if (!apiKey) {
|
|
25232
|
+
throw new Error("API key is required");
|
|
25233
|
+
}
|
|
25234
|
+
if (!storageBaseUrl) {
|
|
25235
|
+
throw new Error("storageBaseUrl is required");
|
|
25236
|
+
}
|
|
25237
|
+
if (!filePath) {
|
|
25238
|
+
throw new Error("filePath is required");
|
|
25239
|
+
}
|
|
25240
|
+
const resolvedPath = path3.resolve(filePath);
|
|
25241
|
+
if (!fs3.existsSync(resolvedPath)) {
|
|
25242
|
+
throw new Error(`File not found: ${resolvedPath}`);
|
|
25243
|
+
}
|
|
25244
|
+
const stat = fs3.statSync(resolvedPath);
|
|
25245
|
+
if (!stat.isFile()) {
|
|
25246
|
+
throw new Error(`Not a file: ${resolvedPath}`);
|
|
25247
|
+
}
|
|
25248
|
+
if (stat.size > MAX_UPLOAD_SIZE) {
|
|
25249
|
+
throw new Error(`File too large: ${stat.size} bytes (max ${MAX_UPLOAD_SIZE})`);
|
|
25250
|
+
}
|
|
25251
|
+
const base = normalizeBaseUrl(storageBaseUrl);
|
|
25252
|
+
if (new URL(base).protocol === "http:") {
|
|
25253
|
+
console.error("Warning: storage URL uses HTTP — API key will be sent unencrypted");
|
|
25254
|
+
}
|
|
25255
|
+
const url = `${base}/upload`;
|
|
25256
|
+
const fileBuffer = fs3.readFileSync(resolvedPath);
|
|
25257
|
+
const fileName = path3.basename(resolvedPath);
|
|
25258
|
+
const ext = path3.extname(fileName).toLowerCase();
|
|
25259
|
+
const mimeType = MIME_TYPES[ext] || "application/octet-stream";
|
|
25260
|
+
const formData = new FormData;
|
|
25261
|
+
formData.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
|
|
25262
|
+
const headers = {
|
|
25263
|
+
"access-token": apiKey
|
|
25264
|
+
};
|
|
25265
|
+
if (debug) {
|
|
25266
|
+
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
25267
|
+
console.error(`Debug: Storage base URL: ${base}`);
|
|
25268
|
+
console.error(`Debug: POST URL: ${url}`);
|
|
25269
|
+
console.error(`Debug: File: ${resolvedPath} (${stat.size} bytes, ${mimeType})`);
|
|
25270
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
25271
|
+
}
|
|
25272
|
+
const response = await fetch(url, {
|
|
25273
|
+
method: "POST",
|
|
25274
|
+
headers,
|
|
25275
|
+
body: formData
|
|
25276
|
+
});
|
|
25277
|
+
if (debug) {
|
|
25278
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
25279
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
25280
|
+
}
|
|
25281
|
+
const data = await response.text();
|
|
25282
|
+
if (response.ok) {
|
|
25283
|
+
try {
|
|
25284
|
+
return JSON.parse(data);
|
|
25285
|
+
} catch {
|
|
25286
|
+
throw new Error(`Failed to parse upload response: ${data}`);
|
|
25287
|
+
}
|
|
25288
|
+
} else {
|
|
25289
|
+
throw new Error(formatHttpError("Failed to upload file", response.status, data));
|
|
25116
25290
|
}
|
|
25117
|
-
return trimmed;
|
|
25118
25291
|
}
|
|
25119
|
-
function
|
|
25120
|
-
const
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25292
|
+
async function downloadFile(params) {
|
|
25293
|
+
const { apiKey, storageBaseUrl, fileUrl, outputPath, debug } = params;
|
|
25294
|
+
if (!apiKey) {
|
|
25295
|
+
throw new Error("API key is required");
|
|
25296
|
+
}
|
|
25297
|
+
if (!storageBaseUrl) {
|
|
25298
|
+
throw new Error("storageBaseUrl is required");
|
|
25299
|
+
}
|
|
25300
|
+
if (!fileUrl) {
|
|
25301
|
+
throw new Error("fileUrl is required");
|
|
25302
|
+
}
|
|
25303
|
+
const base = normalizeBaseUrl(storageBaseUrl);
|
|
25304
|
+
if (new URL(base).protocol === "http:") {
|
|
25305
|
+
console.error("Warning: storage URL uses HTTP — API key will be sent unencrypted");
|
|
25306
|
+
}
|
|
25307
|
+
let fullUrl;
|
|
25308
|
+
if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
|
|
25309
|
+
if (!fileUrl.startsWith(base + "/")) {
|
|
25310
|
+
throw new Error(`URL must be under storage base URL: ${base}`);
|
|
25311
|
+
}
|
|
25312
|
+
fullUrl = fileUrl;
|
|
25313
|
+
} else {
|
|
25314
|
+
const relativePath = fileUrl.startsWith("/") ? fileUrl : `/${fileUrl}`;
|
|
25315
|
+
fullUrl = `${base}${relativePath}`;
|
|
25316
|
+
}
|
|
25317
|
+
const urlFilename = path3.basename(new URL(fullUrl).pathname);
|
|
25318
|
+
if (!urlFilename) {
|
|
25319
|
+
throw new Error("Cannot derive filename from URL; please specify --output");
|
|
25320
|
+
}
|
|
25321
|
+
const saveTo = outputPath ? path3.resolve(outputPath) : path3.resolve(urlFilename);
|
|
25322
|
+
if (!outputPath) {
|
|
25323
|
+
const normalizedSave = path3.normalize(saveTo);
|
|
25324
|
+
if (!normalizedSave.startsWith(path3.normalize(process.cwd()))) {
|
|
25325
|
+
throw new Error("Derived output path escapes current directory; please specify --output");
|
|
25326
|
+
}
|
|
25327
|
+
}
|
|
25328
|
+
const headers = {
|
|
25329
|
+
"access-token": apiKey
|
|
25330
|
+
};
|
|
25331
|
+
if (debug) {
|
|
25332
|
+
const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
|
|
25333
|
+
console.error(`Debug: Storage base URL: ${base}`);
|
|
25334
|
+
console.error(`Debug: GET URL: ${fullUrl}`);
|
|
25335
|
+
console.error(`Debug: Output: ${saveTo}`);
|
|
25336
|
+
console.error(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
25337
|
+
}
|
|
25338
|
+
const response = await fetch(fullUrl, {
|
|
25339
|
+
method: "GET",
|
|
25340
|
+
headers
|
|
25341
|
+
});
|
|
25342
|
+
if (debug) {
|
|
25343
|
+
console.error(`Debug: Response status: ${response.status}`);
|
|
25344
|
+
console.error(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
25345
|
+
}
|
|
25346
|
+
if (!response.ok) {
|
|
25347
|
+
const data = await response.text();
|
|
25348
|
+
throw new Error(formatHttpError("Failed to download file", response.status, data));
|
|
25349
|
+
}
|
|
25350
|
+
const contentLength = response.headers.get("content-length");
|
|
25351
|
+
if (contentLength && parseInt(contentLength, 10) > MAX_DOWNLOAD_SIZE) {
|
|
25352
|
+
throw new Error(`File too large: ${contentLength} bytes (max ${MAX_DOWNLOAD_SIZE})`);
|
|
25353
|
+
}
|
|
25354
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
25355
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
25356
|
+
const parentDir = path3.dirname(saveTo);
|
|
25357
|
+
if (!fs3.existsSync(parentDir)) {
|
|
25358
|
+
fs3.mkdirSync(parentDir, { recursive: true });
|
|
25359
|
+
}
|
|
25360
|
+
fs3.writeFileSync(saveTo, buffer);
|
|
25124
25361
|
return {
|
|
25125
|
-
|
|
25126
|
-
|
|
25362
|
+
savedTo: saveTo,
|
|
25363
|
+
size: buffer.length,
|
|
25364
|
+
mimeType: response.headers.get("content-type")
|
|
25127
25365
|
};
|
|
25128
25366
|
}
|
|
25367
|
+
var IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp", ".ico"]);
|
|
25368
|
+
function buildMarkdownLink(fileUrl, storageBaseUrl, filename) {
|
|
25369
|
+
const base = normalizeBaseUrl(storageBaseUrl);
|
|
25370
|
+
const normalizedFileUrl = fileUrl.startsWith("/") ? fileUrl : `/${fileUrl}`;
|
|
25371
|
+
const fullUrl = fileUrl.startsWith("http://") || fileUrl.startsWith("https://") ? fileUrl : `${base}${normalizedFileUrl}`;
|
|
25372
|
+
const name = filename || path3.basename(new URL(fullUrl).pathname);
|
|
25373
|
+
const safeName = name.replace(/[\[\]()]/g, "\\$&");
|
|
25374
|
+
const ext = path3.extname(name).toLowerCase();
|
|
25375
|
+
if (IMAGE_EXTENSIONS.has(ext)) {
|
|
25376
|
+
return ``;
|
|
25377
|
+
}
|
|
25378
|
+
return `[${safeName}](${fullUrl})`;
|
|
25379
|
+
}
|
|
25129
25380
|
|
|
25130
25381
|
// lib/init.ts
|
|
25131
25382
|
import { randomBytes } from "crypto";
|
|
25132
25383
|
import { URL as URL2, fileURLToPath } from "url";
|
|
25133
|
-
import * as
|
|
25134
|
-
import * as
|
|
25384
|
+
import * as fs4 from "fs";
|
|
25385
|
+
import * as path4 from "path";
|
|
25135
25386
|
var DEFAULT_MONITORING_USER = "postgres_ai_mon";
|
|
25136
25387
|
var KNOWN_PROVIDERS = ["self-managed", "supabase"];
|
|
25137
25388
|
var SKIP_ROLE_CREATION_PROVIDERS = ["supabase"];
|
|
@@ -25203,7 +25454,7 @@ async function connectWithSslFallback(ClientClass, adminConn, verbose) {
|
|
|
25203
25454
|
throw sslErr;
|
|
25204
25455
|
}
|
|
25205
25456
|
if (verbose) {
|
|
25206
|
-
console.
|
|
25457
|
+
console.error("SSL connection failed, retrying without SSL...");
|
|
25207
25458
|
}
|
|
25208
25459
|
const noSslConfig = { ...adminConn.clientConfig, ssl: false };
|
|
25209
25460
|
try {
|
|
@@ -25222,21 +25473,21 @@ async function connectWithSslFallback(ClientClass, adminConn, verbose) {
|
|
|
25222
25473
|
}
|
|
25223
25474
|
function sqlDir() {
|
|
25224
25475
|
const currentFile = fileURLToPath(import.meta.url);
|
|
25225
|
-
const currentDir =
|
|
25476
|
+
const currentDir = path4.dirname(currentFile);
|
|
25226
25477
|
const candidates = [
|
|
25227
|
-
|
|
25228
|
-
|
|
25478
|
+
path4.resolve(currentDir, "..", "sql"),
|
|
25479
|
+
path4.resolve(currentDir, "..", "..", "sql")
|
|
25229
25480
|
];
|
|
25230
25481
|
for (const candidate of candidates) {
|
|
25231
|
-
if (
|
|
25482
|
+
if (fs4.existsSync(candidate)) {
|
|
25232
25483
|
return candidate;
|
|
25233
25484
|
}
|
|
25234
25485
|
}
|
|
25235
25486
|
throw new Error(`SQL directory not found. Searched: ${candidates.join(", ")}`);
|
|
25236
25487
|
}
|
|
25237
25488
|
function loadSqlTemplate(filename) {
|
|
25238
|
-
const p =
|
|
25239
|
-
return
|
|
25489
|
+
const p = path4.join(sqlDir(), filename);
|
|
25490
|
+
return fs4.readFileSync(p, "utf8");
|
|
25240
25491
|
}
|
|
25241
25492
|
function applyTemplate(sql, vars) {
|
|
25242
25493
|
return sql.replace(/\{\{([A-Z0-9_]+)\}\}/g, (_, key) => {
|
|
@@ -26215,12 +26466,12 @@ function createCallbackServer(port = 0, expectedState = null, timeoutMs = 300000
|
|
|
26215
26466
|
let resolveReady;
|
|
26216
26467
|
let rejectReady;
|
|
26217
26468
|
let serverInstance = null;
|
|
26218
|
-
const promise2 = new Promise((
|
|
26219
|
-
resolveCallback =
|
|
26469
|
+
const promise2 = new Promise((resolve5, reject) => {
|
|
26470
|
+
resolveCallback = resolve5;
|
|
26220
26471
|
rejectCallback = reject;
|
|
26221
26472
|
});
|
|
26222
|
-
const ready = new Promise((
|
|
26223
|
-
resolveReady =
|
|
26473
|
+
const ready = new Promise((resolve5, reject) => {
|
|
26474
|
+
resolveReady = resolve5;
|
|
26224
26475
|
rejectReady = reject;
|
|
26225
26476
|
});
|
|
26226
26477
|
let timeoutId = null;
|
|
@@ -26406,12 +26657,13 @@ function createCallbackServer(port = 0, expectedState = null, timeoutMs = 300000
|
|
|
26406
26657
|
}
|
|
26407
26658
|
|
|
26408
26659
|
// bin/postgres-ai.ts
|
|
26660
|
+
init_util();
|
|
26409
26661
|
import { createInterface } from "readline";
|
|
26410
26662
|
import * as childProcess from "child_process";
|
|
26411
26663
|
|
|
26412
26664
|
// lib/checkup.ts
|
|
26413
|
-
import * as
|
|
26414
|
-
import * as
|
|
26665
|
+
import * as fs5 from "fs";
|
|
26666
|
+
import * as path5 from "path";
|
|
26415
26667
|
|
|
26416
26668
|
// lib/metrics-embedded.ts
|
|
26417
26669
|
var METRICS = {
|
|
@@ -27844,7 +28096,7 @@ function parseVersionNum(versionNum) {
|
|
|
27844
28096
|
};
|
|
27845
28097
|
} catch (err) {
|
|
27846
28098
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
27847
|
-
console.
|
|
28099
|
+
console.error(`[parseVersionNum] Warning: Failed to parse "${versionNum}": ${errorMsg}`);
|
|
27848
28100
|
return { major: "", minor: "" };
|
|
27849
28101
|
}
|
|
27850
28102
|
}
|
|
@@ -28161,7 +28413,7 @@ async function getStatsReset(client, pgMajorVersion = 16) {
|
|
|
28161
28413
|
} catch (err) {
|
|
28162
28414
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28163
28415
|
postmasterStartupError = `Failed to query postmaster start time: ${errorMsg}`;
|
|
28164
|
-
console.
|
|
28416
|
+
console.error(`[getStatsReset] Warning: ${postmasterStartupError}`);
|
|
28165
28417
|
}
|
|
28166
28418
|
const statsResult = {
|
|
28167
28419
|
stats_reset_epoch: statsResetEpoch,
|
|
@@ -28214,7 +28466,7 @@ async function getRedundantIndexes(client, pgMajorVersion = 16) {
|
|
|
28214
28466
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28215
28467
|
const indexName = String(transformed.index_name || "unknown");
|
|
28216
28468
|
parseError = `Failed to parse redundant_to_json: ${errorMsg}`;
|
|
28217
|
-
console.
|
|
28469
|
+
console.error(`[H004] Warning: ${parseError} for index "${indexName}"`);
|
|
28218
28470
|
}
|
|
28219
28471
|
const result2 = {
|
|
28220
28472
|
schema_name: String(transformed.schema_name || ""),
|
|
@@ -28256,7 +28508,7 @@ function createBaseReport(checkId, checkTitle, nodeName) {
|
|
|
28256
28508
|
}
|
|
28257
28509
|
function readTextFileSafe(p) {
|
|
28258
28510
|
try {
|
|
28259
|
-
const value =
|
|
28511
|
+
const value = fs5.readFileSync(p, "utf8").trim();
|
|
28260
28512
|
return value || null;
|
|
28261
28513
|
} catch {
|
|
28262
28514
|
return null;
|
|
@@ -28269,8 +28521,8 @@ function resolveBuildTs() {
|
|
|
28269
28521
|
if (fromFile)
|
|
28270
28522
|
return fromFile;
|
|
28271
28523
|
try {
|
|
28272
|
-
const pkgRoot =
|
|
28273
|
-
const fromPkgFile = readTextFileSafe(
|
|
28524
|
+
const pkgRoot = path5.resolve(__dirname, "..");
|
|
28525
|
+
const fromPkgFile = readTextFileSafe(path5.join(pkgRoot, "BUILD_TS"));
|
|
28274
28526
|
if (fromPkgFile)
|
|
28275
28527
|
return fromPkgFile;
|
|
28276
28528
|
} catch (err) {
|
|
@@ -28278,13 +28530,13 @@ function resolveBuildTs() {
|
|
|
28278
28530
|
console.warn(`[resolveBuildTs] Warning: path resolution failed: ${errorMsg}`);
|
|
28279
28531
|
}
|
|
28280
28532
|
try {
|
|
28281
|
-
const pkgJsonPath =
|
|
28282
|
-
const st =
|
|
28533
|
+
const pkgJsonPath = path5.resolve(__dirname, "..", "package.json");
|
|
28534
|
+
const st = fs5.statSync(pkgJsonPath);
|
|
28283
28535
|
return st.mtime.toISOString();
|
|
28284
28536
|
} catch (err) {
|
|
28285
28537
|
if (process.env.DEBUG) {
|
|
28286
28538
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28287
|
-
console.
|
|
28539
|
+
console.error(`[resolveBuildTs] Could not stat package.json, using current time: ${errorMsg}`);
|
|
28288
28540
|
}
|
|
28289
28541
|
return new Date().toISOString();
|
|
28290
28542
|
}
|
|
@@ -28394,7 +28646,7 @@ async function generateD004(client, nodeName) {
|
|
|
28394
28646
|
}
|
|
28395
28647
|
} catch (err) {
|
|
28396
28648
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28397
|
-
console.
|
|
28649
|
+
console.error(`[D004] Error querying pg_stat_statements: ${errorMsg}`);
|
|
28398
28650
|
pgssError = errorMsg;
|
|
28399
28651
|
}
|
|
28400
28652
|
let kcacheAvailable = false;
|
|
@@ -28440,7 +28692,7 @@ async function generateD004(client, nodeName) {
|
|
|
28440
28692
|
}
|
|
28441
28693
|
} catch (err) {
|
|
28442
28694
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28443
|
-
console.
|
|
28695
|
+
console.error(`[D004] Error querying pg_stat_kcache: ${errorMsg}`);
|
|
28444
28696
|
kcacheError = errorMsg;
|
|
28445
28697
|
}
|
|
28446
28698
|
report.results[nodeName] = {
|
|
@@ -28551,7 +28803,10 @@ async function generateF004(client, nodeName) {
|
|
|
28551
28803
|
});
|
|
28552
28804
|
} catch (err) {
|
|
28553
28805
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28554
|
-
console.
|
|
28806
|
+
console.error(`[F004] Error estimating table bloat: ${errorMsg}`);
|
|
28807
|
+
if (errorMsg.includes("postgres_ai.")) {
|
|
28808
|
+
console.error(` Hint: Run "postgresai prepare-db <connection>" to create required objects.`);
|
|
28809
|
+
}
|
|
28555
28810
|
}
|
|
28556
28811
|
const { datname: dbName, size_bytes: dbSizeBytes } = await getCurrentDatabaseInfo(client, pgMajorVersion);
|
|
28557
28812
|
const totalCount = bloatedTables.length;
|
|
@@ -28625,7 +28880,10 @@ async function generateF005(client, nodeName) {
|
|
|
28625
28880
|
});
|
|
28626
28881
|
} catch (err) {
|
|
28627
28882
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28628
|
-
console.
|
|
28883
|
+
console.error(`[F005] Error estimating index bloat: ${errorMsg}`);
|
|
28884
|
+
if (errorMsg.includes("postgres_ai.")) {
|
|
28885
|
+
console.error(` Hint: Run "postgresai prepare-db <connection>" to create required objects.`);
|
|
28886
|
+
}
|
|
28629
28887
|
}
|
|
28630
28888
|
const { datname: dbName, size_bytes: dbSizeBytes } = await getCurrentDatabaseInfo(client, pgMajorVersion);
|
|
28631
28889
|
const totalCount = bloatedIndexes.length;
|
|
@@ -28712,7 +28970,7 @@ async function generateG001(client, nodeName) {
|
|
|
28712
28970
|
}
|
|
28713
28971
|
} catch (err) {
|
|
28714
28972
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28715
|
-
console.
|
|
28973
|
+
console.error(`[G001] Error calculating memory usage: ${errorMsg}`);
|
|
28716
28974
|
memoryError = errorMsg;
|
|
28717
28975
|
}
|
|
28718
28976
|
report.results[nodeName] = {
|
|
@@ -28772,7 +29030,7 @@ async function generateG003(client, nodeName) {
|
|
|
28772
29030
|
}
|
|
28773
29031
|
} catch (err) {
|
|
28774
29032
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28775
|
-
console.
|
|
29033
|
+
console.error(`[G003] Error querying deadlock stats: ${errorMsg}`);
|
|
28776
29034
|
deadlockError = errorMsg;
|
|
28777
29035
|
}
|
|
28778
29036
|
report.results[nodeName] = {
|
|
@@ -28878,7 +29136,7 @@ async function withRetry(fn, config2 = {}, onRetry) {
|
|
|
28878
29136
|
if (onRetry) {
|
|
28879
29137
|
onRetry(attempt, err, delayMs);
|
|
28880
29138
|
}
|
|
28881
|
-
await new Promise((
|
|
29139
|
+
await new Promise((resolve6) => setTimeout(resolve6, delayMs));
|
|
28882
29140
|
delayMs = Math.min(delayMs * backoffMultiplier, maxDelayMs);
|
|
28883
29141
|
}
|
|
28884
29142
|
}
|
|
@@ -28948,7 +29206,7 @@ async function postRpc(params) {
|
|
|
28948
29206
|
const controller = new AbortController;
|
|
28949
29207
|
let timeoutId = null;
|
|
28950
29208
|
let settled = false;
|
|
28951
|
-
return new Promise((
|
|
29209
|
+
return new Promise((resolve6, reject) => {
|
|
28952
29210
|
const settledReject = (err) => {
|
|
28953
29211
|
if (settled)
|
|
28954
29212
|
return;
|
|
@@ -28963,7 +29221,7 @@ async function postRpc(params) {
|
|
|
28963
29221
|
settled = true;
|
|
28964
29222
|
if (timeoutId)
|
|
28965
29223
|
clearTimeout(timeoutId);
|
|
28966
|
-
|
|
29224
|
+
resolve6(value);
|
|
28967
29225
|
};
|
|
28968
29226
|
const req = https.request(url, {
|
|
28969
29227
|
method: "POST",
|
|
@@ -29336,27 +29594,27 @@ function closeReadline() {
|
|
|
29336
29594
|
}
|
|
29337
29595
|
}
|
|
29338
29596
|
async function execPromise(command) {
|
|
29339
|
-
return new Promise((
|
|
29597
|
+
return new Promise((resolve7, reject) => {
|
|
29340
29598
|
childProcess.exec(command, (error2, stdout, stderr) => {
|
|
29341
29599
|
if (error2) {
|
|
29342
29600
|
const err = error2;
|
|
29343
29601
|
err.code = typeof error2.code === "number" ? error2.code : 1;
|
|
29344
29602
|
reject(err);
|
|
29345
29603
|
} else {
|
|
29346
|
-
|
|
29604
|
+
resolve7({ stdout, stderr });
|
|
29347
29605
|
}
|
|
29348
29606
|
});
|
|
29349
29607
|
});
|
|
29350
29608
|
}
|
|
29351
29609
|
async function execFilePromise(file, args) {
|
|
29352
|
-
return new Promise((
|
|
29610
|
+
return new Promise((resolve7, reject) => {
|
|
29353
29611
|
childProcess.execFile(file, args, (error2, stdout, stderr) => {
|
|
29354
29612
|
if (error2) {
|
|
29355
29613
|
const err = error2;
|
|
29356
29614
|
err.code = typeof error2.code === "number" ? error2.code : 1;
|
|
29357
29615
|
reject(err);
|
|
29358
29616
|
} else {
|
|
29359
|
-
|
|
29617
|
+
resolve7({ stdout, stderr });
|
|
29360
29618
|
}
|
|
29361
29619
|
});
|
|
29362
29620
|
});
|
|
@@ -29397,9 +29655,9 @@ function spawn2(cmd, args, options) {
|
|
|
29397
29655
|
};
|
|
29398
29656
|
}
|
|
29399
29657
|
async function question(prompt) {
|
|
29400
|
-
return new Promise((
|
|
29658
|
+
return new Promise((resolve7) => {
|
|
29401
29659
|
getReadline().question(prompt, (answer) => {
|
|
29402
|
-
|
|
29660
|
+
resolve7(answer);
|
|
29403
29661
|
});
|
|
29404
29662
|
});
|
|
29405
29663
|
}
|
|
@@ -29410,7 +29668,7 @@ function expandHomePath(p) {
|
|
|
29410
29668
|
if (s === "~")
|
|
29411
29669
|
return os3.homedir();
|
|
29412
29670
|
if (s.startsWith("~/") || s.startsWith("~\\")) {
|
|
29413
|
-
return
|
|
29671
|
+
return path6.join(os3.homedir(), s.slice(2));
|
|
29414
29672
|
}
|
|
29415
29673
|
return s;
|
|
29416
29674
|
}
|
|
@@ -29459,10 +29717,10 @@ function prepareOutputDirectory(outputOpt) {
|
|
|
29459
29717
|
if (!outputOpt)
|
|
29460
29718
|
return;
|
|
29461
29719
|
const outputDir = expandHomePath(outputOpt);
|
|
29462
|
-
const outputPath =
|
|
29463
|
-
if (!
|
|
29720
|
+
const outputPath = path6.isAbsolute(outputDir) ? outputDir : path6.resolve(process.cwd(), outputDir);
|
|
29721
|
+
if (!fs6.existsSync(outputPath)) {
|
|
29464
29722
|
try {
|
|
29465
|
-
|
|
29723
|
+
fs6.mkdirSync(outputPath, { recursive: true });
|
|
29466
29724
|
} catch (e) {
|
|
29467
29725
|
const errAny = e;
|
|
29468
29726
|
const code = typeof errAny?.code === "string" ? errAny.code : "";
|
|
@@ -29541,8 +29799,8 @@ async function uploadCheckupReports(uploadCfg, reports, spinner, logUpload) {
|
|
|
29541
29799
|
}
|
|
29542
29800
|
function writeReportFiles(reports, outputPath) {
|
|
29543
29801
|
for (const [checkId, report] of Object.entries(reports)) {
|
|
29544
|
-
const filePath =
|
|
29545
|
-
|
|
29802
|
+
const filePath = path6.join(outputPath, `${checkId}.json`);
|
|
29803
|
+
fs6.writeFileSync(filePath, JSON.stringify(report, null, 2), "utf8");
|
|
29546
29804
|
const title = report.checkTitle || checkId;
|
|
29547
29805
|
console.log(`\u2713 ${checkId} ${title}: ${filePath}`);
|
|
29548
29806
|
}
|
|
@@ -29587,7 +29845,7 @@ function getDefaultMonitoringProjectDir() {
|
|
|
29587
29845
|
const override = process.env.PGAI_PROJECT_DIR;
|
|
29588
29846
|
if (override && override.trim())
|
|
29589
29847
|
return override.trim();
|
|
29590
|
-
return
|
|
29848
|
+
return path6.join(getConfigDir(), "monitoring");
|
|
29591
29849
|
}
|
|
29592
29850
|
async function downloadText(url) {
|
|
29593
29851
|
const controller = new AbortController;
|
|
@@ -29604,53 +29862,64 @@ async function downloadText(url) {
|
|
|
29604
29862
|
}
|
|
29605
29863
|
async function ensureDefaultMonitoringProject() {
|
|
29606
29864
|
const projectDir = getDefaultMonitoringProjectDir();
|
|
29607
|
-
const composeFile =
|
|
29608
|
-
const instancesFile =
|
|
29609
|
-
if (!
|
|
29610
|
-
|
|
29611
|
-
}
|
|
29612
|
-
|
|
29613
|
-
|
|
29614
|
-
|
|
29615
|
-
|
|
29616
|
-
|
|
29617
|
-
|
|
29618
|
-
|
|
29865
|
+
const composeFile = path6.resolve(projectDir, "docker-compose.yml");
|
|
29866
|
+
const instancesFile = path6.resolve(projectDir, "instances.yml");
|
|
29867
|
+
if (!fs6.existsSync(projectDir)) {
|
|
29868
|
+
fs6.mkdirSync(projectDir, { recursive: true, mode: 448 });
|
|
29869
|
+
}
|
|
29870
|
+
const refs = [
|
|
29871
|
+
process.env.PGAI_PROJECT_REF,
|
|
29872
|
+
package_default.version,
|
|
29873
|
+
`v${package_default.version}`,
|
|
29874
|
+
"main"
|
|
29875
|
+
].filter((v) => Boolean(v && v.trim()));
|
|
29876
|
+
if (!fs6.existsSync(composeFile)) {
|
|
29619
29877
|
let lastErr;
|
|
29620
29878
|
for (const ref of refs) {
|
|
29621
29879
|
const url = `https://gitlab.com/postgres-ai/postgres_ai/-/raw/${encodeURIComponent(ref)}/docker-compose.yml`;
|
|
29622
29880
|
try {
|
|
29623
29881
|
const text = await downloadText(url);
|
|
29624
|
-
|
|
29882
|
+
fs6.writeFileSync(composeFile, text, { encoding: "utf8", mode: 384 });
|
|
29625
29883
|
break;
|
|
29626
29884
|
} catch (err) {
|
|
29627
29885
|
lastErr = err;
|
|
29628
29886
|
}
|
|
29629
29887
|
}
|
|
29630
|
-
if (!
|
|
29888
|
+
if (!fs6.existsSync(composeFile)) {
|
|
29631
29889
|
const msg = lastErr instanceof Error ? lastErr.message : String(lastErr);
|
|
29632
29890
|
throw new Error(`Failed to bootstrap docker-compose.yml: ${msg}`);
|
|
29633
29891
|
}
|
|
29634
29892
|
}
|
|
29635
|
-
|
|
29893
|
+
const demoFile = path6.resolve(projectDir, "instances.demo.yml");
|
|
29894
|
+
if (!fs6.existsSync(demoFile)) {
|
|
29895
|
+
for (const ref of refs) {
|
|
29896
|
+
const url = `https://gitlab.com/postgres-ai/postgres_ai/-/raw/${encodeURIComponent(ref)}/instances.demo.yml`;
|
|
29897
|
+
try {
|
|
29898
|
+
const text = await downloadText(url);
|
|
29899
|
+
fs6.writeFileSync(demoFile, text, { encoding: "utf8", mode: 384 });
|
|
29900
|
+
break;
|
|
29901
|
+
} catch {}
|
|
29902
|
+
}
|
|
29903
|
+
}
|
|
29904
|
+
if (!fs6.existsSync(instancesFile)) {
|
|
29636
29905
|
const header = `# PostgreSQL instances to monitor
|
|
29637
29906
|
` + `# Add your instances using: pgai mon targets add <connection-string> <name>
|
|
29638
29907
|
|
|
29639
29908
|
`;
|
|
29640
|
-
|
|
29909
|
+
fs6.writeFileSync(instancesFile, header, { encoding: "utf8", mode: 384 });
|
|
29641
29910
|
}
|
|
29642
|
-
const pgwatchConfig =
|
|
29643
|
-
if (!
|
|
29644
|
-
|
|
29911
|
+
const pgwatchConfig = path6.resolve(projectDir, ".pgwatch-config");
|
|
29912
|
+
if (!fs6.existsSync(pgwatchConfig)) {
|
|
29913
|
+
fs6.writeFileSync(pgwatchConfig, "", { encoding: "utf8", mode: 384 });
|
|
29645
29914
|
}
|
|
29646
|
-
const envFile =
|
|
29647
|
-
if (!
|
|
29915
|
+
const envFile = path6.resolve(projectDir, ".env");
|
|
29916
|
+
if (!fs6.existsSync(envFile)) {
|
|
29648
29917
|
const envText = `PGAI_TAG=${package_default.version}
|
|
29649
29918
|
# PGAI_REGISTRY=registry.gitlab.com/postgres-ai/postgres_ai
|
|
29650
29919
|
`;
|
|
29651
|
-
|
|
29920
|
+
fs6.writeFileSync(envFile, envText, { encoding: "utf8", mode: 384 });
|
|
29652
29921
|
}
|
|
29653
|
-
return { fs:
|
|
29922
|
+
return { fs: fs6, path: path6, projectDir, composeFile, instancesFile };
|
|
29654
29923
|
}
|
|
29655
29924
|
function getConfig(opts) {
|
|
29656
29925
|
let apiKey = opts.apiKey || process.env.PGAI_API_KEY || "";
|
|
@@ -29681,7 +29950,7 @@ function printResult(result, json2) {
|
|
|
29681
29950
|
}
|
|
29682
29951
|
}
|
|
29683
29952
|
var program2 = new Command;
|
|
29684
|
-
program2.name("postgres-ai").description("PostgresAI CLI").version(package_default.version).option("--api-key <key>", "API key (overrides PGAI_API_KEY)").option("--api-base-url <url>", "API base URL for backend RPC (overrides PGAI_API_BASE_URL)").option("--ui-base-url <url>", "UI base URL for browser routes (overrides PGAI_UI_BASE_URL)");
|
|
29953
|
+
program2.name("postgres-ai").description("PostgresAI CLI").version(package_default.version).option("--api-key <key>", "API key (overrides PGAI_API_KEY)").option("--api-base-url <url>", "API base URL for backend RPC (overrides PGAI_API_BASE_URL)").option("--ui-base-url <url>", "UI base URL for browser routes (overrides PGAI_UI_BASE_URL)").option("--storage-base-url <url>", "Storage base URL for file uploads (overrides PGAI_STORAGE_BASE_URL)");
|
|
29685
29954
|
program2.command("set-default-project <project>").description("store default project for checkup uploads").action(async (project) => {
|
|
29686
29955
|
const value = (project || "").trim();
|
|
29687
29956
|
if (!value) {
|
|
@@ -29692,6 +29961,23 @@ program2.command("set-default-project <project>").description("store default pro
|
|
|
29692
29961
|
writeConfig({ defaultProject: value });
|
|
29693
29962
|
console.log(`Default project saved: ${value}`);
|
|
29694
29963
|
});
|
|
29964
|
+
program2.command("set-storage-url <url>").description("store storage base URL for file uploads").action(async (url) => {
|
|
29965
|
+
const value = (url || "").trim();
|
|
29966
|
+
if (!value) {
|
|
29967
|
+
console.error("Error: url is required");
|
|
29968
|
+
process.exitCode = 1;
|
|
29969
|
+
return;
|
|
29970
|
+
}
|
|
29971
|
+
try {
|
|
29972
|
+
const { normalizeBaseUrl: normalizeBaseUrl3 } = await Promise.resolve().then(() => (init_util(), exports_util2));
|
|
29973
|
+
const normalized = normalizeBaseUrl3(value);
|
|
29974
|
+
writeConfig({ storageBaseUrl: normalized });
|
|
29975
|
+
console.log(`Storage URL saved: ${normalized}`);
|
|
29976
|
+
} catch {
|
|
29977
|
+
console.error(`Error: invalid URL: ${value}`);
|
|
29978
|
+
process.exitCode = 1;
|
|
29979
|
+
}
|
|
29980
|
+
});
|
|
29695
29981
|
program2.command("prepare-db [conn]").description("prepare database for monitoring: create monitoring user, required view(s), and grant permissions (idempotent)").option("--db-url <url>", "PostgreSQL connection URL (admin) to run the setup against (deprecated; pass it as positional arg)").option("-h, --host <host>", "PostgreSQL host (psql-like)").option("-p, --port <port>", "PostgreSQL port (psql-like)").option("-U, --username <username>", "PostgreSQL user (psql-like)").option("-d, --dbname <dbname>", "PostgreSQL database name (psql-like)").option("--admin-password <password>", "Admin connection password (otherwise uses PGPASSWORD if set)").option("--monitoring-user <name>", "Monitoring role name to create/update", DEFAULT_MONITORING_USER).option("--password <password>", "Monitoring role password (overrides PGAI_MON_PASSWORD)").option("--skip-optional-permissions", "Skip optional permissions (RDS/self-managed extras)", false).option("--provider <provider>", "Database provider (e.g., supabase). Affects which steps are executed.").option("--verify", "Verify that monitoring role/permissions are in place (no changes)", false).option("--reset-password", "Reset monitoring role password only (no other changes)", false).option("--print-sql", "Print SQL plan and exit (no changes applied)", false).option("--print-password", "Print generated monitoring password (DANGEROUS in CI logs)", false).option("--supabase", "Use Supabase Management API instead of direct PostgreSQL connection", false).option("--supabase-access-token <token>", "Supabase Management API access token (or SUPABASE_ACCESS_TOKEN env)").option("--supabase-project-ref <ref>", "Supabase project reference (or SUPABASE_PROJECT_REF env)").option("--json", "Output result as JSON (machine-readable)", false).addHelpText("after", [
|
|
29696
29982
|
"",
|
|
29697
29983
|
"Examples:",
|
|
@@ -29872,9 +30158,9 @@ program2.command("prepare-db [conn]").description("prepare database for monitori
|
|
|
29872
30158
|
} else {
|
|
29873
30159
|
console.log("\u2713 prepare-db verify: OK");
|
|
29874
30160
|
if (v.missingOptional.length > 0) {
|
|
29875
|
-
console.
|
|
30161
|
+
console.error("\u26A0 Optional items missing:");
|
|
29876
30162
|
for (const m of v.missingOptional)
|
|
29877
|
-
console.
|
|
30163
|
+
console.error(`- ${m}`);
|
|
29878
30164
|
}
|
|
29879
30165
|
}
|
|
29880
30166
|
return;
|
|
@@ -29995,9 +30281,9 @@ program2.command("prepare-db [conn]").description("prepare database for monitori
|
|
|
29995
30281
|
} else {
|
|
29996
30282
|
console.log(opts.resetPassword ? "\u2713 prepare-db password reset completed" : "\u2713 prepare-db completed");
|
|
29997
30283
|
if (skippedOptional.length > 0) {
|
|
29998
|
-
console.
|
|
30284
|
+
console.error("\u26A0 Some optional steps were skipped (not supported or insufficient privileges):");
|
|
29999
30285
|
for (const s of skippedOptional)
|
|
30000
|
-
console.
|
|
30286
|
+
console.error(`- ${s}`);
|
|
30001
30287
|
}
|
|
30002
30288
|
if (process.stdout.isTTY) {
|
|
30003
30289
|
console.log(`Applied ${applied.length} steps`);
|
|
@@ -30155,9 +30441,9 @@ program2.command("prepare-db [conn]").description("prepare database for monitori
|
|
|
30155
30441
|
} else {
|
|
30156
30442
|
console.log(`\u2713 prepare-db verify: OK${opts.provider ? ` (provider: ${opts.provider})` : ""}`);
|
|
30157
30443
|
if (v.missingOptional.length > 0) {
|
|
30158
|
-
console.
|
|
30444
|
+
console.error("\u26A0 Optional items missing:");
|
|
30159
30445
|
for (const m of v.missingOptional)
|
|
30160
|
-
console.
|
|
30446
|
+
console.error(`- ${m}`);
|
|
30161
30447
|
}
|
|
30162
30448
|
}
|
|
30163
30449
|
return;
|
|
@@ -30273,9 +30559,9 @@ program2.command("prepare-db [conn]").description("prepare database for monitori
|
|
|
30273
30559
|
} else {
|
|
30274
30560
|
console.log(opts.resetPassword ? "\u2713 prepare-db password reset completed" : "\u2713 prepare-db completed");
|
|
30275
30561
|
if (skippedOptional.length > 0) {
|
|
30276
|
-
console.
|
|
30562
|
+
console.error("\u26A0 Some optional steps were skipped (not supported or insufficient privileges):");
|
|
30277
30563
|
for (const s of skippedOptional)
|
|
30278
|
-
console.
|
|
30564
|
+
console.error(`- ${s}`);
|
|
30279
30565
|
}
|
|
30280
30566
|
if (process.stdout.isTTY) {
|
|
30281
30567
|
console.log(`Applied ${applied.length} steps`);
|
|
@@ -30475,9 +30761,9 @@ program2.command("unprepare-db [conn]").description("remove monitoring setup: dr
|
|
|
30475
30761
|
console.log(`Drop role: ${dropRole}`);
|
|
30476
30762
|
}
|
|
30477
30763
|
if (!opts.force && !jsonOutput && !shouldPrintSql) {
|
|
30478
|
-
const answer = await new Promise((
|
|
30764
|
+
const answer = await new Promise((resolve7) => {
|
|
30479
30765
|
const readline = getReadline();
|
|
30480
|
-
readline.question(`This will remove the monitoring setup for user "${opts.monitoringUser}"${dropRole ? " and drop the role" : ""}. Continue? [y/N] `, (ans) =>
|
|
30766
|
+
readline.question(`This will remove the monitoring setup for user "${opts.monitoringUser}"${dropRole ? " and drop the role" : ""}. Continue? [y/N] `, (ans) => resolve7(ans.trim().toLowerCase()));
|
|
30481
30767
|
});
|
|
30482
30768
|
if (answer !== "y" && answer !== "yes") {
|
|
30483
30769
|
console.log("Aborted.");
|
|
@@ -30531,11 +30817,11 @@ program2.command("unprepare-db [conn]").description("remove monitoring setup: dr
|
|
|
30531
30817
|
console.log("\u2713 unprepare-db completed");
|
|
30532
30818
|
console.log(`Applied ${applied.length} steps`);
|
|
30533
30819
|
} else {
|
|
30534
|
-
console.
|
|
30820
|
+
console.error("\u26A0 unprepare-db completed with errors");
|
|
30535
30821
|
console.log(`Applied ${applied.length} steps`);
|
|
30536
|
-
console.
|
|
30822
|
+
console.error("Errors:");
|
|
30537
30823
|
for (const err of errors3) {
|
|
30538
|
-
console.
|
|
30824
|
+
console.error(` - ${err}`);
|
|
30539
30825
|
}
|
|
30540
30826
|
process.exitCode = 1;
|
|
30541
30827
|
}
|
|
@@ -30834,12 +31120,12 @@ function resolvePaths() {
|
|
|
30834
31120
|
const startDir = process.cwd();
|
|
30835
31121
|
let currentDir = startDir;
|
|
30836
31122
|
while (true) {
|
|
30837
|
-
const composeFile =
|
|
30838
|
-
if (
|
|
30839
|
-
const instancesFile =
|
|
30840
|
-
return { fs:
|
|
31123
|
+
const composeFile = path6.resolve(currentDir, "docker-compose.yml");
|
|
31124
|
+
if (fs6.existsSync(composeFile)) {
|
|
31125
|
+
const instancesFile = path6.resolve(currentDir, "instances.yml");
|
|
31126
|
+
return { fs: fs6, path: path6, projectDir: currentDir, composeFile, instancesFile };
|
|
30841
31127
|
}
|
|
30842
|
-
const parentDir =
|
|
31128
|
+
const parentDir = path6.dirname(currentDir);
|
|
30843
31129
|
if (parentDir === currentDir)
|
|
30844
31130
|
break;
|
|
30845
31131
|
currentDir = parentDir;
|
|
@@ -30863,10 +31149,10 @@ function isDockerRunning() {
|
|
|
30863
31149
|
}
|
|
30864
31150
|
function getComposeCmd() {
|
|
30865
31151
|
const tryCmd = (cmd, args) => spawnSync2(cmd, args, { stdio: "ignore", timeout: 5000 }).status === 0;
|
|
30866
|
-
if (tryCmd("docker-compose", ["version"]))
|
|
30867
|
-
return ["docker-compose"];
|
|
30868
31152
|
if (tryCmd("docker", ["compose", "version"]))
|
|
30869
31153
|
return ["docker", "compose"];
|
|
31154
|
+
if (tryCmd("docker-compose", ["version"]))
|
|
31155
|
+
return ["docker-compose"];
|
|
30870
31156
|
return null;
|
|
30871
31157
|
}
|
|
30872
31158
|
function checkRunningContainers() {
|
|
@@ -30887,10 +31173,10 @@ function registerMonitoringInstance(apiKey, projectName, opts) {
|
|
|
30887
31173
|
const url = `${apiBaseUrl}/rpc/monitoring_instance_register`;
|
|
30888
31174
|
const debug = opts?.debug;
|
|
30889
31175
|
if (debug) {
|
|
30890
|
-
console.
|
|
31176
|
+
console.error(`
|
|
30891
31177
|
Debug: Registering monitoring instance...`);
|
|
30892
|
-
console.
|
|
30893
|
-
console.
|
|
31178
|
+
console.error(`Debug: POST ${url}`);
|
|
31179
|
+
console.error(`Debug: project_name=${projectName}`);
|
|
30894
31180
|
}
|
|
30895
31181
|
fetch(url, {
|
|
30896
31182
|
method: "POST",
|
|
@@ -30905,26 +31191,26 @@ Debug: Registering monitoring instance...`);
|
|
|
30905
31191
|
const body = await res.text().catch(() => "");
|
|
30906
31192
|
if (!res.ok) {
|
|
30907
31193
|
if (debug) {
|
|
30908
|
-
console.
|
|
30909
|
-
console.
|
|
31194
|
+
console.error(`Debug: Monitoring registration failed: HTTP ${res.status}`);
|
|
31195
|
+
console.error(`Debug: Response: ${body}`);
|
|
30910
31196
|
}
|
|
30911
31197
|
return;
|
|
30912
31198
|
}
|
|
30913
31199
|
if (debug) {
|
|
30914
|
-
console.
|
|
31200
|
+
console.error(`Debug: Monitoring registration response: ${body}`);
|
|
30915
31201
|
}
|
|
30916
31202
|
}).catch((err) => {
|
|
30917
31203
|
if (debug) {
|
|
30918
|
-
console.
|
|
31204
|
+
console.error(`Debug: Monitoring registration error: ${err.message}`);
|
|
30919
31205
|
}
|
|
30920
31206
|
});
|
|
30921
31207
|
}
|
|
30922
31208
|
function updatePgwatchConfig(configPath, updates) {
|
|
30923
31209
|
let lines = [];
|
|
30924
|
-
if (
|
|
30925
|
-
const stats =
|
|
31210
|
+
if (fs6.existsSync(configPath)) {
|
|
31211
|
+
const stats = fs6.statSync(configPath);
|
|
30926
31212
|
if (!stats.isDirectory()) {
|
|
30927
|
-
const content =
|
|
31213
|
+
const content = fs6.readFileSync(configPath, "utf8");
|
|
30928
31214
|
lines = content.split(/\r?\n/).filter((l) => l.trim() !== "");
|
|
30929
31215
|
}
|
|
30930
31216
|
}
|
|
@@ -30936,7 +31222,7 @@ function updatePgwatchConfig(configPath, updates) {
|
|
|
30936
31222
|
lines.push(`${key}=${value}`);
|
|
30937
31223
|
}
|
|
30938
31224
|
}
|
|
30939
|
-
|
|
31225
|
+
fs6.writeFileSync(configPath, lines.join(`
|
|
30940
31226
|
`) + `
|
|
30941
31227
|
`, { encoding: "utf8", mode: 384 });
|
|
30942
31228
|
}
|
|
@@ -30966,12 +31252,12 @@ async function runCompose(args, grafanaPassword) {
|
|
|
30966
31252
|
if (grafanaPassword) {
|
|
30967
31253
|
env.GF_SECURITY_ADMIN_PASSWORD = grafanaPassword;
|
|
30968
31254
|
} else {
|
|
30969
|
-
const cfgPath =
|
|
30970
|
-
if (
|
|
31255
|
+
const cfgPath = path6.resolve(projectDir, ".pgwatch-config");
|
|
31256
|
+
if (fs6.existsSync(cfgPath)) {
|
|
30971
31257
|
try {
|
|
30972
|
-
const stats =
|
|
31258
|
+
const stats = fs6.statSync(cfgPath);
|
|
30973
31259
|
if (!stats.isDirectory()) {
|
|
30974
|
-
const content =
|
|
31260
|
+
const content = fs6.readFileSync(cfgPath, "utf8");
|
|
30975
31261
|
const match = content.match(/^grafana_password=([^\r\n]+)/m);
|
|
30976
31262
|
if (match) {
|
|
30977
31263
|
env.GF_SECURITY_ADMIN_PASSWORD = match[1].trim();
|
|
@@ -30984,17 +31270,37 @@ async function runCompose(args, grafanaPassword) {
|
|
|
30984
31270
|
}
|
|
30985
31271
|
}
|
|
30986
31272
|
}
|
|
31273
|
+
const envFilePath = path6.resolve(projectDir, ".env");
|
|
31274
|
+
if (fs6.existsSync(envFilePath)) {
|
|
31275
|
+
try {
|
|
31276
|
+
const envContent = fs6.readFileSync(envFilePath, "utf8");
|
|
31277
|
+
if (!env.VM_AUTH_USERNAME) {
|
|
31278
|
+
const m = envContent.match(/^VM_AUTH_USERNAME=([^\r\n]+)/m);
|
|
31279
|
+
if (m)
|
|
31280
|
+
env.VM_AUTH_USERNAME = m[1].trim().replace(/^["']|["']$/g, "");
|
|
31281
|
+
}
|
|
31282
|
+
if (!env.VM_AUTH_PASSWORD) {
|
|
31283
|
+
const m = envContent.match(/^VM_AUTH_PASSWORD=([^\r\n]+)/m);
|
|
31284
|
+
if (m)
|
|
31285
|
+
env.VM_AUTH_PASSWORD = m[1].trim().replace(/^["']|["']$/g, "");
|
|
31286
|
+
}
|
|
31287
|
+
} catch (err) {
|
|
31288
|
+
if (process.env.DEBUG) {
|
|
31289
|
+
console.warn(`Warning: Could not read VM auth from .env: ${err instanceof Error ? err.message : String(err)}`);
|
|
31290
|
+
}
|
|
31291
|
+
}
|
|
31292
|
+
}
|
|
30987
31293
|
const finalArgs = [...args];
|
|
30988
31294
|
if (process.platform === "darwin" && args.includes("up")) {
|
|
30989
31295
|
finalArgs.push("--scale", "self-node-exporter=0");
|
|
30990
31296
|
}
|
|
30991
|
-
return new Promise((
|
|
31297
|
+
return new Promise((resolve7) => {
|
|
30992
31298
|
const child = spawn2(cmd[0], [...cmd.slice(1), "-f", composeFile, ...finalArgs], {
|
|
30993
31299
|
stdio: "inherit",
|
|
30994
31300
|
env,
|
|
30995
31301
|
cwd: projectDir
|
|
30996
31302
|
});
|
|
30997
|
-
child.on("close", (code) =>
|
|
31303
|
+
child.on("close", (code) => resolve7(code || 0));
|
|
30998
31304
|
});
|
|
30999
31305
|
}
|
|
31000
31306
|
program2.command("help", { isDefault: true }).description("show help").action(() => {
|
|
@@ -31015,16 +31321,16 @@ mon.command("local-install").description("install local monitoring stack (genera
|
|
|
31015
31321
|
console.log(`Project directory: ${projectDir}
|
|
31016
31322
|
`);
|
|
31017
31323
|
if (opts.project) {
|
|
31018
|
-
const cfgPath2 =
|
|
31324
|
+
const cfgPath2 = path6.resolve(projectDir, ".pgwatch-config");
|
|
31019
31325
|
updatePgwatchConfig(cfgPath2, { project_name: opts.project });
|
|
31020
31326
|
console.log(`Using project name: ${opts.project}
|
|
31021
31327
|
`);
|
|
31022
31328
|
}
|
|
31023
|
-
const envFile =
|
|
31329
|
+
const envFile = path6.resolve(projectDir, ".env");
|
|
31024
31330
|
let existingRegistry = null;
|
|
31025
31331
|
let existingPassword = null;
|
|
31026
|
-
if (
|
|
31027
|
-
const existingEnv =
|
|
31332
|
+
if (fs6.existsSync(envFile)) {
|
|
31333
|
+
const existingEnv = fs6.readFileSync(envFile, "utf8");
|
|
31028
31334
|
const registryMatch = existingEnv.match(/^PGAI_REGISTRY=(.+)$/m);
|
|
31029
31335
|
if (registryMatch)
|
|
31030
31336
|
existingRegistry = registryMatch[1].trim();
|
|
@@ -31040,7 +31346,7 @@ mon.command("local-install").description("install local monitoring stack (genera
|
|
|
31040
31346
|
if (existingPassword) {
|
|
31041
31347
|
envLines.push(`GF_SECURITY_ADMIN_PASSWORD=${existingPassword}`);
|
|
31042
31348
|
}
|
|
31043
|
-
|
|
31349
|
+
fs6.writeFileSync(envFile, envLines.join(`
|
|
31044
31350
|
`) + `
|
|
31045
31351
|
`, { encoding: "utf8", mode: 384 });
|
|
31046
31352
|
if (opts.tag) {
|
|
@@ -31048,8 +31354,8 @@ mon.command("local-install").description("install local monitoring stack (genera
|
|
|
31048
31354
|
`);
|
|
31049
31355
|
}
|
|
31050
31356
|
if (opts.demo && opts.dbUrl) {
|
|
31051
|
-
console.
|
|
31052
|
-
console.
|
|
31357
|
+
console.error("\u26A0 Both --demo and --db-url provided. Demo mode includes its own database.");
|
|
31358
|
+
console.error(`\u26A0 The --db-url will be ignored in demo mode.
|
|
31053
31359
|
`);
|
|
31054
31360
|
opts.dbUrl = undefined;
|
|
31055
31361
|
}
|
|
@@ -31064,7 +31370,7 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
|
|
|
31064
31370
|
}
|
|
31065
31371
|
const { running, containers } = checkRunningContainers();
|
|
31066
31372
|
if (running) {
|
|
31067
|
-
console.
|
|
31373
|
+
console.error(`\u26A0 Monitoring services are already running: ${containers.join(", ")}`);
|
|
31068
31374
|
console.log(`Use 'postgres-ai mon restart' to restart them
|
|
31069
31375
|
`);
|
|
31070
31376
|
return;
|
|
@@ -31076,12 +31382,12 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
|
|
|
31076
31382
|
if (apiKey) {
|
|
31077
31383
|
console.log("Using API key provided via --api-key parameter");
|
|
31078
31384
|
writeConfig({ apiKey });
|
|
31079
|
-
updatePgwatchConfig(
|
|
31385
|
+
updatePgwatchConfig(path6.resolve(projectDir, ".pgwatch-config"), { api_key: apiKey });
|
|
31080
31386
|
console.log(`\u2713 API key saved
|
|
31081
31387
|
`);
|
|
31082
31388
|
} else if (opts.yes) {
|
|
31083
31389
|
console.log("Auto-yes mode: no API key provided, skipping API key setup");
|
|
31084
|
-
console.
|
|
31390
|
+
console.error("\u26A0 Reports will be generated locally only");
|
|
31085
31391
|
console.log(`You can add an API key later with: postgres-ai add-key <api_key>
|
|
31086
31392
|
`);
|
|
31087
31393
|
} else {
|
|
@@ -31093,23 +31399,23 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
|
|
|
31093
31399
|
const trimmedKey = inputApiKey.trim();
|
|
31094
31400
|
if (trimmedKey) {
|
|
31095
31401
|
writeConfig({ apiKey: trimmedKey });
|
|
31096
|
-
updatePgwatchConfig(
|
|
31402
|
+
updatePgwatchConfig(path6.resolve(projectDir, ".pgwatch-config"), { api_key: trimmedKey });
|
|
31097
31403
|
apiKey = trimmedKey;
|
|
31098
31404
|
console.log(`\u2713 API key saved
|
|
31099
31405
|
`);
|
|
31100
31406
|
break;
|
|
31101
31407
|
}
|
|
31102
|
-
console.
|
|
31408
|
+
console.error("\u26A0 API key cannot be empty");
|
|
31103
31409
|
const retry = await question("Try again or skip API key setup, retry? (Y/n): ");
|
|
31104
31410
|
if (retry.toLowerCase() === "n") {
|
|
31105
|
-
console.
|
|
31411
|
+
console.error("\u26A0 Skipping API key setup - reports will be generated locally only");
|
|
31106
31412
|
console.log(`You can add an API key later with: postgres-ai add-key <api_key>
|
|
31107
31413
|
`);
|
|
31108
31414
|
break;
|
|
31109
31415
|
}
|
|
31110
31416
|
}
|
|
31111
31417
|
} else {
|
|
31112
|
-
console.
|
|
31418
|
+
console.error("\u26A0 Skipping API key setup - reports will be generated locally only");
|
|
31113
31419
|
console.log(`You can add an API key later with: postgres-ai add-key <api_key>
|
|
31114
31420
|
`);
|
|
31115
31421
|
}
|
|
@@ -31127,7 +31433,7 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
|
|
|
31127
31433
|
# Add your instances using: postgres-ai mon targets add
|
|
31128
31434
|
|
|
31129
31435
|
`;
|
|
31130
|
-
|
|
31436
|
+
fs6.writeFileSync(instancesPath, emptyInstancesContent, "utf8");
|
|
31131
31437
|
console.log(`Instances file: ${instancesPath}`);
|
|
31132
31438
|
console.log(`Project directory: ${projectDir2}
|
|
31133
31439
|
`);
|
|
@@ -31159,7 +31465,7 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
|
|
|
31159
31465
|
node_name: ${instanceName}
|
|
31160
31466
|
sink_type: ~sink_type~
|
|
31161
31467
|
`;
|
|
31162
|
-
|
|
31468
|
+
fs6.appendFileSync(instancesPath, body, "utf8");
|
|
31163
31469
|
console.log(`\u2713 Monitoring target '${instanceName}' added
|
|
31164
31470
|
`);
|
|
31165
31471
|
console.log("Testing connection to the added instance...");
|
|
@@ -31178,7 +31484,7 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
|
|
|
31178
31484
|
}
|
|
31179
31485
|
} else if (opts.yes) {
|
|
31180
31486
|
console.log("Auto-yes mode: no database URL provided, skipping database setup");
|
|
31181
|
-
console.
|
|
31487
|
+
console.error("\u26A0 No PostgreSQL instance added");
|
|
31182
31488
|
console.log(`You can add one later with: postgres-ai mon targets add
|
|
31183
31489
|
`);
|
|
31184
31490
|
} else {
|
|
@@ -31196,7 +31502,7 @@ You can provide either:`);
|
|
|
31196
31502
|
const m = connStr.match(/^postgresql:\/\/([^:]+):([^@]+)@([^:\/]+)(?::(\d+))?\/(.+)$/);
|
|
31197
31503
|
if (!m) {
|
|
31198
31504
|
console.error("\u2717 Invalid connection string format");
|
|
31199
|
-
console.
|
|
31505
|
+
console.error(`\u26A0 Continuing without adding instance
|
|
31200
31506
|
`);
|
|
31201
31507
|
} else {
|
|
31202
31508
|
const host = m[3];
|
|
@@ -31214,7 +31520,7 @@ You can provide either:`);
|
|
|
31214
31520
|
node_name: ${instanceName}
|
|
31215
31521
|
sink_type: ~sink_type~
|
|
31216
31522
|
`;
|
|
31217
|
-
|
|
31523
|
+
fs6.appendFileSync(instancesPath, body, "utf8");
|
|
31218
31524
|
console.log(`\u2713 Monitoring target '${instanceName}' added
|
|
31219
31525
|
`);
|
|
31220
31526
|
console.log("Testing connection to the added instance...");
|
|
@@ -31233,17 +31539,26 @@ You can provide either:`);
|
|
|
31233
31539
|
}
|
|
31234
31540
|
}
|
|
31235
31541
|
} else {
|
|
31236
|
-
console.
|
|
31542
|
+
console.error(`\u26A0 No PostgreSQL instance added - you can add one later with: postgres-ai mon targets add
|
|
31237
31543
|
`);
|
|
31238
31544
|
}
|
|
31239
31545
|
} else {
|
|
31240
|
-
console.
|
|
31546
|
+
console.error(`\u26A0 No PostgreSQL instance added - you can add one later with: postgres-ai mon targets add
|
|
31241
31547
|
`);
|
|
31242
31548
|
}
|
|
31243
31549
|
}
|
|
31244
31550
|
} else {
|
|
31245
|
-
console.log(
|
|
31551
|
+
console.log("Step 2: Demo mode enabled - using included demo PostgreSQL database");
|
|
31552
|
+
const { instancesFile: instancesPath, projectDir: projectDir2 } = await resolveOrInitPaths();
|
|
31553
|
+
const demoSrc = path6.resolve(projectDir2, "instances.demo.yml");
|
|
31554
|
+
if (fs6.existsSync(demoSrc)) {
|
|
31555
|
+
fs6.copyFileSync(demoSrc, instancesPath);
|
|
31556
|
+
console.log(`\u2713 Demo monitoring target configured
|
|
31246
31557
|
`);
|
|
31558
|
+
} else {
|
|
31559
|
+
console.error(`\u26A0 instances.demo.yml not found \u2014 demo target not configured
|
|
31560
|
+
`);
|
|
31561
|
+
}
|
|
31247
31562
|
}
|
|
31248
31563
|
console.log(opts.demo ? "Step 3: Updating configuration..." : "Step 3: Updating configuration...");
|
|
31249
31564
|
const code1 = await runCompose(["run", "--rm", "sources-generator"]);
|
|
@@ -31254,13 +31569,15 @@ You can provide either:`);
|
|
|
31254
31569
|
console.log(`\u2713 Configuration updated
|
|
31255
31570
|
`);
|
|
31256
31571
|
console.log(opts.demo ? "Step 4: Configuring Grafana security..." : "Step 4: Configuring Grafana security...");
|
|
31257
|
-
const cfgPath =
|
|
31572
|
+
const cfgPath = path6.resolve(projectDir, ".pgwatch-config");
|
|
31258
31573
|
let grafanaPassword = "";
|
|
31574
|
+
let vmAuthUsername = "";
|
|
31575
|
+
let vmAuthPassword = "";
|
|
31259
31576
|
try {
|
|
31260
|
-
if (
|
|
31261
|
-
const stats =
|
|
31577
|
+
if (fs6.existsSync(cfgPath)) {
|
|
31578
|
+
const stats = fs6.statSync(cfgPath);
|
|
31262
31579
|
if (!stats.isDirectory()) {
|
|
31263
|
-
const content =
|
|
31580
|
+
const content = fs6.readFileSync(cfgPath, "utf8");
|
|
31264
31581
|
const match = content.match(/^grafana_password=([^\r\n]+)/m);
|
|
31265
31582
|
if (match) {
|
|
31266
31583
|
grafanaPassword = match[1].trim();
|
|
@@ -31273,26 +31590,65 @@ You can provide either:`);
|
|
|
31273
31590
|
'`);
|
|
31274
31591
|
grafanaPassword = password.trim();
|
|
31275
31592
|
let configContent = "";
|
|
31276
|
-
if (
|
|
31277
|
-
const stats =
|
|
31593
|
+
if (fs6.existsSync(cfgPath)) {
|
|
31594
|
+
const stats = fs6.statSync(cfgPath);
|
|
31278
31595
|
if (!stats.isDirectory()) {
|
|
31279
|
-
configContent =
|
|
31596
|
+
configContent = fs6.readFileSync(cfgPath, "utf8");
|
|
31280
31597
|
}
|
|
31281
31598
|
}
|
|
31282
31599
|
const lines = configContent.split(/\r?\n/).filter((l) => !/^grafana_password=/.test(l));
|
|
31283
31600
|
lines.push(`grafana_password=${grafanaPassword}`);
|
|
31284
|
-
|
|
31601
|
+
fs6.writeFileSync(cfgPath, lines.filter(Boolean).join(`
|
|
31285
31602
|
`) + `
|
|
31286
31603
|
`, "utf8");
|
|
31287
31604
|
}
|
|
31288
31605
|
console.log(`\u2713 Grafana password configured
|
|
31289
31606
|
`);
|
|
31290
31607
|
} catch (error2) {
|
|
31291
|
-
console.
|
|
31608
|
+
console.error("\u26A0 Could not generate Grafana password automatically");
|
|
31292
31609
|
console.log(`Using default password: demo
|
|
31293
31610
|
`);
|
|
31294
31611
|
grafanaPassword = "demo";
|
|
31295
31612
|
}
|
|
31613
|
+
try {
|
|
31614
|
+
const envFile2 = path6.resolve(projectDir, ".env");
|
|
31615
|
+
if (fs6.existsSync(envFile2)) {
|
|
31616
|
+
const envContent = fs6.readFileSync(envFile2, "utf8");
|
|
31617
|
+
const userMatch = envContent.match(/^VM_AUTH_USERNAME=([^\r\n]+)/m);
|
|
31618
|
+
const passMatch = envContent.match(/^VM_AUTH_PASSWORD=([^\r\n]+)/m);
|
|
31619
|
+
if (userMatch)
|
|
31620
|
+
vmAuthUsername = userMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
31621
|
+
if (passMatch)
|
|
31622
|
+
vmAuthPassword = passMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
31623
|
+
}
|
|
31624
|
+
if (!vmAuthUsername || !vmAuthPassword) {
|
|
31625
|
+
console.log("Generating VictoriaMetrics auth credentials...");
|
|
31626
|
+
vmAuthUsername = vmAuthUsername || "vmauth";
|
|
31627
|
+
if (!vmAuthPassword) {
|
|
31628
|
+
const { stdout: vmPass } = await execPromise(`openssl rand -base64 12 | tr -d '
|
|
31629
|
+
'`);
|
|
31630
|
+
vmAuthPassword = vmPass.trim();
|
|
31631
|
+
}
|
|
31632
|
+
let envContent = "";
|
|
31633
|
+
if (fs6.existsSync(envFile2)) {
|
|
31634
|
+
envContent = fs6.readFileSync(envFile2, "utf8");
|
|
31635
|
+
}
|
|
31636
|
+
const envLines2 = envContent.split(/\r?\n/).filter((l) => !/^VM_AUTH_USERNAME=/.test(l) && !/^VM_AUTH_PASSWORD=/.test(l)).filter((l, i2, arr) => !(i2 === arr.length - 1 && l === ""));
|
|
31637
|
+
envLines2.push(`VM_AUTH_USERNAME=${vmAuthUsername}`);
|
|
31638
|
+
envLines2.push(`VM_AUTH_PASSWORD=${vmAuthPassword}`);
|
|
31639
|
+
fs6.writeFileSync(envFile2, envLines2.join(`
|
|
31640
|
+
`) + `
|
|
31641
|
+
`, { encoding: "utf8", mode: 384 });
|
|
31642
|
+
}
|
|
31643
|
+
console.log(`\u2713 VictoriaMetrics auth configured
|
|
31644
|
+
`);
|
|
31645
|
+
} catch (error2) {
|
|
31646
|
+
console.error("\u26A0 Could not generate VictoriaMetrics auth credentials automatically");
|
|
31647
|
+
if (process.env.DEBUG) {
|
|
31648
|
+
console.warn(` ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
31649
|
+
}
|
|
31650
|
+
}
|
|
31651
|
+
await runCompose(["rm", "-f", "-s", "config-init"]);
|
|
31296
31652
|
console.log("Step 5: Starting monitoring services...");
|
|
31297
31653
|
const code2 = await runCompose(["up", "-d", "--force-recreate"], grafanaPassword);
|
|
31298
31654
|
if (code2 !== 0) {
|
|
@@ -31340,6 +31696,9 @@ You can provide either:`);
|
|
|
31340
31696
|
console.log("\uD83D\uDE80 MAIN ACCESS POINT - Start here:");
|
|
31341
31697
|
console.log(" Grafana Dashboard: http://localhost:3000");
|
|
31342
31698
|
console.log(` Login: monitor / ${grafanaPassword}`);
|
|
31699
|
+
if (vmAuthUsername && vmAuthPassword) {
|
|
31700
|
+
console.log(` VictoriaMetrics Auth: ${vmAuthUsername} / ${vmAuthPassword}`);
|
|
31701
|
+
}
|
|
31343
31702
|
console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
31344
31703
|
`);
|
|
31345
31704
|
});
|
|
@@ -31383,7 +31742,7 @@ mon.command("stop").description("stop monitoring services").action(async () => {
|
|
|
31383
31742
|
let code = await runCompose(["down", "--remove-orphans"]);
|
|
31384
31743
|
if (code !== 0) {
|
|
31385
31744
|
await removeOrphanedContainers();
|
|
31386
|
-
await new Promise((
|
|
31745
|
+
await new Promise((resolve7) => setTimeout(resolve7, NETWORK_CLEANUP_DELAY_MS));
|
|
31387
31746
|
code = await runCompose(["down", "--remove-orphans"]);
|
|
31388
31747
|
}
|
|
31389
31748
|
if (code !== 0) {
|
|
@@ -31438,7 +31797,7 @@ mon.command("health").description("health check for monitoring services").option
|
|
|
31438
31797
|
if (attempt > 1) {
|
|
31439
31798
|
console.log(`Retrying (attempt ${attempt}/${maxAttempts})...
|
|
31440
31799
|
`);
|
|
31441
|
-
await new Promise((
|
|
31800
|
+
await new Promise((resolve7) => setTimeout(resolve7, 5000));
|
|
31442
31801
|
}
|
|
31443
31802
|
allHealthy = true;
|
|
31444
31803
|
for (const service of services) {
|
|
@@ -31486,11 +31845,11 @@ mon.command("config").description("show monitoring services configuration").acti
|
|
|
31486
31845
|
console.log(`Project Directory: ${projectDir}`);
|
|
31487
31846
|
console.log(`Docker Compose File: ${composeFile}`);
|
|
31488
31847
|
console.log(`Instances File: ${instancesFile}`);
|
|
31489
|
-
if (
|
|
31848
|
+
if (fs6.existsSync(instancesFile)) {
|
|
31490
31849
|
console.log(`
|
|
31491
31850
|
Instances configuration:
|
|
31492
31851
|
`);
|
|
31493
|
-
const text =
|
|
31852
|
+
const text = fs6.readFileSync(instancesFile, "utf8");
|
|
31494
31853
|
process.stdout.write(text);
|
|
31495
31854
|
if (!/\n$/.test(text))
|
|
31496
31855
|
console.log();
|
|
@@ -31505,8 +31864,8 @@ mon.command("update").description("update monitoring stack").action(async () =>
|
|
|
31505
31864
|
console.log(`Updating PostgresAI monitoring stack...
|
|
31506
31865
|
`);
|
|
31507
31866
|
try {
|
|
31508
|
-
const gitDir =
|
|
31509
|
-
if (!
|
|
31867
|
+
const gitDir = path6.resolve(process.cwd(), ".git");
|
|
31868
|
+
if (!fs6.existsSync(gitDir)) {
|
|
31510
31869
|
console.error("Not a git repository. Cannot update.");
|
|
31511
31870
|
process.exitCode = 1;
|
|
31512
31871
|
return;
|
|
@@ -31602,7 +31961,7 @@ mon.command("clean").description("cleanup monitoring services artifacts (stops s
|
|
|
31602
31961
|
if (downCode === 0) {
|
|
31603
31962
|
console.log("\u2713 Monitoring services stopped and removed");
|
|
31604
31963
|
} else {
|
|
31605
|
-
console.
|
|
31964
|
+
console.error("\u26A0 Could not stop services (may not be running)");
|
|
31606
31965
|
}
|
|
31607
31966
|
await removeOrphanedContainers();
|
|
31608
31967
|
console.log("\u2713 Removed orphaned containers");
|
|
@@ -31661,13 +32020,13 @@ mon.command("check").description("monitoring services system readiness check").a
|
|
|
31661
32020
|
var targets = mon.command("targets").description("manage databases to monitor");
|
|
31662
32021
|
targets.command("list").description("list monitoring target databases").action(async () => {
|
|
31663
32022
|
const { instancesFile: instancesPath, projectDir } = await resolveOrInitPaths();
|
|
31664
|
-
if (!
|
|
32023
|
+
if (!fs6.existsSync(instancesPath)) {
|
|
31665
32024
|
console.error(`instances.yml not found in ${projectDir}`);
|
|
31666
32025
|
process.exitCode = 1;
|
|
31667
32026
|
return;
|
|
31668
32027
|
}
|
|
31669
32028
|
try {
|
|
31670
|
-
const content =
|
|
32029
|
+
const content = fs6.readFileSync(instancesPath, "utf8");
|
|
31671
32030
|
const instances = load(content);
|
|
31672
32031
|
if (!instances || !Array.isArray(instances) || instances.length === 0) {
|
|
31673
32032
|
console.log("No monitoring targets configured");
|
|
@@ -31716,8 +32075,8 @@ targets.command("add [connStr] [name]").description("add monitoring target datab
|
|
|
31716
32075
|
const db = m[5];
|
|
31717
32076
|
const instanceName = name && name.trim() ? name.trim() : `${host}-${db}`.replace(/[^a-zA-Z0-9-]/g, "-");
|
|
31718
32077
|
try {
|
|
31719
|
-
if (
|
|
31720
|
-
const content2 =
|
|
32078
|
+
if (fs6.existsSync(file)) {
|
|
32079
|
+
const content2 = fs6.readFileSync(file, "utf8");
|
|
31721
32080
|
const instances = load(content2) || [];
|
|
31722
32081
|
if (Array.isArray(instances)) {
|
|
31723
32082
|
const exists = instances.some((inst) => inst.name === instanceName);
|
|
@@ -31729,7 +32088,7 @@ targets.command("add [connStr] [name]").description("add monitoring target datab
|
|
|
31729
32088
|
}
|
|
31730
32089
|
}
|
|
31731
32090
|
} catch (err) {
|
|
31732
|
-
const content2 =
|
|
32091
|
+
const content2 = fs6.existsSync(file) ? fs6.readFileSync(file, "utf8") : "";
|
|
31733
32092
|
if (new RegExp(`^- name: ${instanceName}$`, "m").test(content2)) {
|
|
31734
32093
|
console.error(`Monitoring target '${instanceName}' already exists`);
|
|
31735
32094
|
process.exitCode = 1;
|
|
@@ -31748,20 +32107,20 @@ targets.command("add [connStr] [name]").description("add monitoring target datab
|
|
|
31748
32107
|
node_name: ${instanceName}
|
|
31749
32108
|
sink_type: ~sink_type~
|
|
31750
32109
|
`;
|
|
31751
|
-
const content =
|
|
31752
|
-
|
|
32110
|
+
const content = fs6.existsSync(file) ? fs6.readFileSync(file, "utf8") : "";
|
|
32111
|
+
fs6.appendFileSync(file, (content && !/\n$/.test(content) ? `
|
|
31753
32112
|
` : "") + body, "utf8");
|
|
31754
32113
|
console.log(`Monitoring target '${instanceName}' added`);
|
|
31755
32114
|
});
|
|
31756
32115
|
targets.command("remove <name>").description("remove monitoring target database").action(async (name) => {
|
|
31757
32116
|
const { instancesFile: file } = await resolveOrInitPaths();
|
|
31758
|
-
if (!
|
|
32117
|
+
if (!fs6.existsSync(file)) {
|
|
31759
32118
|
console.error("instances.yml not found");
|
|
31760
32119
|
process.exitCode = 1;
|
|
31761
32120
|
return;
|
|
31762
32121
|
}
|
|
31763
32122
|
try {
|
|
31764
|
-
const content =
|
|
32123
|
+
const content = fs6.readFileSync(file, "utf8");
|
|
31765
32124
|
const instances = load(content);
|
|
31766
32125
|
if (!instances || !Array.isArray(instances)) {
|
|
31767
32126
|
console.error("Invalid instances.yml format");
|
|
@@ -31774,7 +32133,7 @@ targets.command("remove <name>").description("remove monitoring target database"
|
|
|
31774
32133
|
process.exitCode = 1;
|
|
31775
32134
|
return;
|
|
31776
32135
|
}
|
|
31777
|
-
|
|
32136
|
+
fs6.writeFileSync(file, dump(filtered), "utf8");
|
|
31778
32137
|
console.log(`Monitoring target '${name}' removed`);
|
|
31779
32138
|
} catch (err) {
|
|
31780
32139
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -31784,13 +32143,13 @@ targets.command("remove <name>").description("remove monitoring target database"
|
|
|
31784
32143
|
});
|
|
31785
32144
|
targets.command("test <name>").description("test monitoring target database connectivity").action(async (name) => {
|
|
31786
32145
|
const { instancesFile: instancesPath } = await resolveOrInitPaths();
|
|
31787
|
-
if (!
|
|
32146
|
+
if (!fs6.existsSync(instancesPath)) {
|
|
31788
32147
|
console.error("instances.yml not found");
|
|
31789
32148
|
process.exitCode = 1;
|
|
31790
32149
|
return;
|
|
31791
32150
|
}
|
|
31792
32151
|
try {
|
|
31793
|
-
const content =
|
|
32152
|
+
const content = fs6.readFileSync(instancesPath, "utf8");
|
|
31794
32153
|
const instances = load(content);
|
|
31795
32154
|
if (!instances || !Array.isArray(instances)) {
|
|
31796
32155
|
console.error("Invalid instances.yml format");
|
|
@@ -31851,8 +32210,8 @@ auth.command("login", { isDefault: true }).description("authenticate via browser
|
|
|
31851
32210
|
const cfg = readConfig();
|
|
31852
32211
|
const { apiBaseUrl, uiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
|
|
31853
32212
|
if (opts.debug) {
|
|
31854
|
-
console.
|
|
31855
|
-
console.
|
|
32213
|
+
console.error(`Debug: Resolved API base URL: ${apiBaseUrl}`);
|
|
32214
|
+
console.error(`Debug: Resolved UI base URL: ${uiBaseUrl}`);
|
|
31856
32215
|
}
|
|
31857
32216
|
try {
|
|
31858
32217
|
console.log("Starting local callback server...");
|
|
@@ -31871,8 +32230,8 @@ auth.command("login", { isDefault: true }).description("authenticate via browser
|
|
|
31871
32230
|
});
|
|
31872
32231
|
const initUrl = new URL(`${apiBaseUrl}/rpc/oauth_init`);
|
|
31873
32232
|
if (opts.debug) {
|
|
31874
|
-
console.
|
|
31875
|
-
console.
|
|
32233
|
+
console.error(`Debug: Trying to POST to: ${initUrl.toString()}`);
|
|
32234
|
+
console.error(`Debug: Request data: ${initData}`);
|
|
31876
32235
|
}
|
|
31877
32236
|
let initResponse;
|
|
31878
32237
|
try {
|
|
@@ -31910,7 +32269,7 @@ Please verify the --api-base-url parameter.`);
|
|
|
31910
32269
|
}
|
|
31911
32270
|
const authUrl = `${uiBaseUrl}/cli/auth?state=${encodeURIComponent(params.state)}&code_challenge=${encodeURIComponent(params.codeChallenge)}&code_challenge_method=S256&redirect_uri=${encodeURIComponent(redirectUri)}&api_url=${encodeURIComponent(apiBaseUrl)}`;
|
|
31912
32271
|
if (opts.debug) {
|
|
31913
|
-
console.
|
|
32272
|
+
console.error(`Debug: Auth URL: ${authUrl}`);
|
|
31914
32273
|
}
|
|
31915
32274
|
console.log(`
|
|
31916
32275
|
Opening browser for authentication...`);
|
|
@@ -32043,15 +32402,15 @@ To authenticate, run: pgai auth`);
|
|
|
32043
32402
|
});
|
|
32044
32403
|
auth.command("remove-key").description("remove API key").action(async () => {
|
|
32045
32404
|
const newConfigPath = getConfigPath();
|
|
32046
|
-
const hasNewConfig =
|
|
32405
|
+
const hasNewConfig = fs6.existsSync(newConfigPath);
|
|
32047
32406
|
let legacyPath;
|
|
32048
32407
|
try {
|
|
32049
32408
|
const { projectDir } = await resolveOrInitPaths();
|
|
32050
|
-
legacyPath =
|
|
32409
|
+
legacyPath = path6.resolve(projectDir, ".pgwatch-config");
|
|
32051
32410
|
} catch {
|
|
32052
|
-
legacyPath =
|
|
32411
|
+
legacyPath = path6.resolve(process.cwd(), ".pgwatch-config");
|
|
32053
32412
|
}
|
|
32054
|
-
const hasLegacyConfig =
|
|
32413
|
+
const hasLegacyConfig = fs6.existsSync(legacyPath) && fs6.statSync(legacyPath).isFile();
|
|
32055
32414
|
if (!hasNewConfig && !hasLegacyConfig) {
|
|
32056
32415
|
console.log("No API key configured");
|
|
32057
32416
|
return;
|
|
@@ -32061,11 +32420,11 @@ auth.command("remove-key").description("remove API key").action(async () => {
|
|
|
32061
32420
|
}
|
|
32062
32421
|
if (hasLegacyConfig) {
|
|
32063
32422
|
try {
|
|
32064
|
-
const content =
|
|
32423
|
+
const content = fs6.readFileSync(legacyPath, "utf8");
|
|
32065
32424
|
const filtered = content.split(/\r?\n/).filter((l) => !/^api_key=/.test(l)).join(`
|
|
32066
32425
|
`).replace(/\n+$/g, `
|
|
32067
32426
|
`);
|
|
32068
|
-
|
|
32427
|
+
fs6.writeFileSync(legacyPath, filtered, "utf8");
|
|
32069
32428
|
} catch (err) {
|
|
32070
32429
|
console.warn(`Warning: Could not update legacy config: ${err instanceof Error ? err.message : String(err)}`);
|
|
32071
32430
|
}
|
|
@@ -32076,7 +32435,7 @@ To authenticate again, run: pgai auth`);
|
|
|
32076
32435
|
});
|
|
32077
32436
|
mon.command("generate-grafana-password").description("generate Grafana password for monitoring services").action(async () => {
|
|
32078
32437
|
const { projectDir } = await resolveOrInitPaths();
|
|
32079
|
-
const cfgPath =
|
|
32438
|
+
const cfgPath = path6.resolve(projectDir, ".pgwatch-config");
|
|
32080
32439
|
try {
|
|
32081
32440
|
const { stdout: password } = await execPromise(`openssl rand -base64 12 | tr -d '
|
|
32082
32441
|
'`);
|
|
@@ -32087,17 +32446,17 @@ mon.command("generate-grafana-password").description("generate Grafana password
|
|
|
32087
32446
|
return;
|
|
32088
32447
|
}
|
|
32089
32448
|
let configContent = "";
|
|
32090
|
-
if (
|
|
32091
|
-
const stats =
|
|
32449
|
+
if (fs6.existsSync(cfgPath)) {
|
|
32450
|
+
const stats = fs6.statSync(cfgPath);
|
|
32092
32451
|
if (stats.isDirectory()) {
|
|
32093
32452
|
console.error(".pgwatch-config is a directory, expected a file. Skipping read.");
|
|
32094
32453
|
} else {
|
|
32095
|
-
configContent =
|
|
32454
|
+
configContent = fs6.readFileSync(cfgPath, "utf8");
|
|
32096
32455
|
}
|
|
32097
32456
|
}
|
|
32098
32457
|
const lines = configContent.split(/\r?\n/).filter((l) => !/^grafana_password=/.test(l));
|
|
32099
32458
|
lines.push(`grafana_password=${newPassword}`);
|
|
32100
|
-
|
|
32459
|
+
fs6.writeFileSync(cfgPath, lines.filter(Boolean).join(`
|
|
32101
32460
|
`) + `
|
|
32102
32461
|
`, "utf8");
|
|
32103
32462
|
console.log("\u2713 New Grafana password generated and saved");
|
|
@@ -32119,19 +32478,19 @@ Note: This command requires 'openssl' to be installed`);
|
|
|
32119
32478
|
});
|
|
32120
32479
|
mon.command("show-grafana-credentials").description("show Grafana credentials for monitoring services").action(async () => {
|
|
32121
32480
|
const { projectDir } = await resolveOrInitPaths();
|
|
32122
|
-
const cfgPath =
|
|
32123
|
-
if (!
|
|
32481
|
+
const cfgPath = path6.resolve(projectDir, ".pgwatch-config");
|
|
32482
|
+
if (!fs6.existsSync(cfgPath)) {
|
|
32124
32483
|
console.error("Configuration file not found. Run 'postgres-ai mon local-install' first.");
|
|
32125
32484
|
process.exitCode = 1;
|
|
32126
32485
|
return;
|
|
32127
32486
|
}
|
|
32128
|
-
const stats =
|
|
32487
|
+
const stats = fs6.statSync(cfgPath);
|
|
32129
32488
|
if (stats.isDirectory()) {
|
|
32130
32489
|
console.error(".pgwatch-config is a directory, expected a file. Cannot read credentials.");
|
|
32131
32490
|
process.exitCode = 1;
|
|
32132
32491
|
return;
|
|
32133
32492
|
}
|
|
32134
|
-
const content =
|
|
32493
|
+
const content = fs6.readFileSync(cfgPath, "utf8");
|
|
32135
32494
|
const lines = content.split(/\r?\n/);
|
|
32136
32495
|
let password = "";
|
|
32137
32496
|
for (const line of lines) {
|
|
@@ -32151,6 +32510,18 @@ Grafana credentials:`);
|
|
|
32151
32510
|
console.log(" URL: http://localhost:3000");
|
|
32152
32511
|
console.log(" Username: monitor");
|
|
32153
32512
|
console.log(` Password: ${password}`);
|
|
32513
|
+
const envFile = path6.resolve(projectDir, ".env");
|
|
32514
|
+
if (fs6.existsSync(envFile)) {
|
|
32515
|
+
const envContent = fs6.readFileSync(envFile, "utf8");
|
|
32516
|
+
const vmUser = envContent.match(/^VM_AUTH_USERNAME=([^\r\n]+)/m);
|
|
32517
|
+
const vmPass = envContent.match(/^VM_AUTH_PASSWORD=([^\r\n]+)/m);
|
|
32518
|
+
if (vmUser && vmPass) {
|
|
32519
|
+
console.log(`
|
|
32520
|
+
VictoriaMetrics credentials:`);
|
|
32521
|
+
console.log(` Username: ${vmUser[1].trim().replace(/^["']|["']$/g, "")}`);
|
|
32522
|
+
console.log(` Password: ${vmPass[1].trim().replace(/^["']|["']$/g, "")}`);
|
|
32523
|
+
}
|
|
32524
|
+
}
|
|
32154
32525
|
console.log("");
|
|
32155
32526
|
});
|
|
32156
32527
|
function interpretEscapes2(str2) {
|
|
@@ -32236,11 +32607,11 @@ issues.command("view <issueId>").description("view issue details and comments").
|
|
|
32236
32607
|
});
|
|
32237
32608
|
issues.command("post-comment <issueId> <content>").description("post a new comment to an issue").option("--parent <uuid>", "parent comment id").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (issueId, content, opts) => {
|
|
32238
32609
|
if (opts.debug) {
|
|
32239
|
-
console.
|
|
32610
|
+
console.error(`Debug: Original content: ${JSON.stringify(content)}`);
|
|
32240
32611
|
}
|
|
32241
32612
|
content = interpretEscapes2(content);
|
|
32242
32613
|
if (opts.debug) {
|
|
32243
|
-
console.
|
|
32614
|
+
console.error(`Debug: Interpreted content: ${JSON.stringify(content)}`);
|
|
32244
32615
|
}
|
|
32245
32616
|
const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Posting comment...");
|
|
32246
32617
|
try {
|
|
@@ -32386,11 +32757,11 @@ issues.command("update <issueId>").description("update an existing issue (title/
|
|
|
32386
32757
|
});
|
|
32387
32758
|
issues.command("update-comment <commentId> <content>").description("update an existing issue comment").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (commentId, content, opts) => {
|
|
32388
32759
|
if (opts.debug) {
|
|
32389
|
-
console.
|
|
32760
|
+
console.error(`Debug: Original content: ${JSON.stringify(content)}`);
|
|
32390
32761
|
}
|
|
32391
32762
|
content = interpretEscapes2(content);
|
|
32392
32763
|
if (opts.debug) {
|
|
32393
|
-
console.
|
|
32764
|
+
console.error(`Debug: Interpreted content: ${JSON.stringify(content)}`);
|
|
32394
32765
|
}
|
|
32395
32766
|
const rootOpts = program2.opts();
|
|
32396
32767
|
const cfg = readConfig();
|
|
@@ -32419,6 +32790,74 @@ issues.command("update-comment <commentId> <content>").description("update an ex
|
|
|
32419
32790
|
process.exitCode = 1;
|
|
32420
32791
|
}
|
|
32421
32792
|
});
|
|
32793
|
+
var issueFiles = issues.command("files").description("upload and download files for issues");
|
|
32794
|
+
issueFiles.command("upload <path>").description("upload a file to storage and get a markdown link").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (filePath, opts) => {
|
|
32795
|
+
const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Uploading file...");
|
|
32796
|
+
try {
|
|
32797
|
+
const rootOpts = program2.opts();
|
|
32798
|
+
const cfg = readConfig();
|
|
32799
|
+
const { apiKey } = getConfig(rootOpts);
|
|
32800
|
+
if (!apiKey) {
|
|
32801
|
+
spinner.stop();
|
|
32802
|
+
console.error("API key is required. Run 'pgai auth' first or set --api-key.");
|
|
32803
|
+
process.exitCode = 1;
|
|
32804
|
+
return;
|
|
32805
|
+
}
|
|
32806
|
+
const { storageBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
|
|
32807
|
+
const result = await uploadFile({
|
|
32808
|
+
apiKey,
|
|
32809
|
+
storageBaseUrl,
|
|
32810
|
+
filePath,
|
|
32811
|
+
debug: !!opts.debug
|
|
32812
|
+
});
|
|
32813
|
+
spinner.stop();
|
|
32814
|
+
if (opts.json) {
|
|
32815
|
+
printResult(result, true);
|
|
32816
|
+
} else {
|
|
32817
|
+
const md = buildMarkdownLink(result.url, storageBaseUrl, result.metadata.originalName);
|
|
32818
|
+
const displayUrl = result.url.startsWith("/") ? `${storageBaseUrl}${result.url}` : `${storageBaseUrl}/${result.url}`;
|
|
32819
|
+
console.log(`URL: ${displayUrl}`);
|
|
32820
|
+
console.log(`File: ${result.metadata.originalName}`);
|
|
32821
|
+
console.log(`Size: ${result.metadata.size} bytes`);
|
|
32822
|
+
console.log(`Type: ${result.metadata.mimeType}`);
|
|
32823
|
+
console.log(`Markdown: ${md}`);
|
|
32824
|
+
}
|
|
32825
|
+
} catch (err) {
|
|
32826
|
+
spinner.stop();
|
|
32827
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32828
|
+
console.error(message);
|
|
32829
|
+
process.exitCode = 1;
|
|
32830
|
+
}
|
|
32831
|
+
});
|
|
32832
|
+
issueFiles.command("download <url>").description("download a file from storage").option("-o, --output <path>", "output file path (default: derive from URL)").option("--debug", "enable debug output").action(async (fileUrl, opts) => {
|
|
32833
|
+
const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Downloading file...");
|
|
32834
|
+
try {
|
|
32835
|
+
const rootOpts = program2.opts();
|
|
32836
|
+
const cfg = readConfig();
|
|
32837
|
+
const { apiKey } = getConfig(rootOpts);
|
|
32838
|
+
if (!apiKey) {
|
|
32839
|
+
spinner.stop();
|
|
32840
|
+
console.error("API key is required. Run 'pgai auth' first or set --api-key.");
|
|
32841
|
+
process.exitCode = 1;
|
|
32842
|
+
return;
|
|
32843
|
+
}
|
|
32844
|
+
const { storageBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
|
|
32845
|
+
const result = await downloadFile({
|
|
32846
|
+
apiKey,
|
|
32847
|
+
storageBaseUrl,
|
|
32848
|
+
fileUrl,
|
|
32849
|
+
outputPath: opts.output,
|
|
32850
|
+
debug: !!opts.debug
|
|
32851
|
+
});
|
|
32852
|
+
spinner.stop();
|
|
32853
|
+
console.log(`Saved: ${result.savedTo}`);
|
|
32854
|
+
} catch (err) {
|
|
32855
|
+
spinner.stop();
|
|
32856
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32857
|
+
console.error(message);
|
|
32858
|
+
process.exitCode = 1;
|
|
32859
|
+
}
|
|
32860
|
+
});
|
|
32422
32861
|
issues.command("action-items <issueId>").description("list action items for an issue").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (issueId, opts) => {
|
|
32423
32862
|
const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Fetching action items...");
|
|
32424
32863
|
try {
|
|
@@ -32730,13 +33169,13 @@ reports.command("data [reportId]").description("get checkup report file data (in
|
|
|
32730
33169
|
});
|
|
32731
33170
|
spinner.stop();
|
|
32732
33171
|
if (opts.output) {
|
|
32733
|
-
const dir =
|
|
32734
|
-
|
|
33172
|
+
const dir = path6.resolve(opts.output);
|
|
33173
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
32735
33174
|
for (const f of result) {
|
|
32736
|
-
const safeName =
|
|
32737
|
-
const filePath =
|
|
33175
|
+
const safeName = path6.basename(f.filename);
|
|
33176
|
+
const filePath = path6.join(dir, safeName);
|
|
32738
33177
|
const content = f.type === "json" ? JSON.stringify(tryParseJson(f.data), null, 2) : f.data;
|
|
32739
|
-
|
|
33178
|
+
fs6.writeFileSync(filePath, content, "utf-8");
|
|
32740
33179
|
console.log(filePath);
|
|
32741
33180
|
}
|
|
32742
33181
|
} else if (opts.json) {
|
|
@@ -32854,29 +33293,29 @@ mcp.command("install [client]").description("install MCP server configuration fo
|
|
|
32854
33293
|
let configDir;
|
|
32855
33294
|
switch (client) {
|
|
32856
33295
|
case "cursor":
|
|
32857
|
-
configPath =
|
|
32858
|
-
configDir =
|
|
33296
|
+
configPath = path6.join(homeDir, ".cursor", "mcp.json");
|
|
33297
|
+
configDir = path6.dirname(configPath);
|
|
32859
33298
|
break;
|
|
32860
33299
|
case "windsurf":
|
|
32861
|
-
configPath =
|
|
32862
|
-
configDir =
|
|
33300
|
+
configPath = path6.join(homeDir, ".windsurf", "mcp.json");
|
|
33301
|
+
configDir = path6.dirname(configPath);
|
|
32863
33302
|
break;
|
|
32864
33303
|
case "codex":
|
|
32865
|
-
configPath =
|
|
32866
|
-
configDir =
|
|
33304
|
+
configPath = path6.join(homeDir, ".codex", "mcp.json");
|
|
33305
|
+
configDir = path6.dirname(configPath);
|
|
32867
33306
|
break;
|
|
32868
33307
|
default:
|
|
32869
33308
|
console.error(`Configuration not implemented for: ${client}`);
|
|
32870
33309
|
process.exitCode = 1;
|
|
32871
33310
|
return;
|
|
32872
33311
|
}
|
|
32873
|
-
if (!
|
|
32874
|
-
|
|
33312
|
+
if (!fs6.existsSync(configDir)) {
|
|
33313
|
+
fs6.mkdirSync(configDir, { recursive: true });
|
|
32875
33314
|
}
|
|
32876
33315
|
let config2 = { mcpServers: {} };
|
|
32877
|
-
if (
|
|
33316
|
+
if (fs6.existsSync(configPath)) {
|
|
32878
33317
|
try {
|
|
32879
|
-
const content =
|
|
33318
|
+
const content = fs6.readFileSync(configPath, "utf8");
|
|
32880
33319
|
config2 = JSON.parse(content);
|
|
32881
33320
|
if (!config2.mcpServers) {
|
|
32882
33321
|
config2.mcpServers = {};
|
|
@@ -32889,7 +33328,7 @@ mcp.command("install [client]").description("install MCP server configuration fo
|
|
|
32889
33328
|
command: pgaiPath,
|
|
32890
33329
|
args: ["mcp", "start"]
|
|
32891
33330
|
};
|
|
32892
|
-
|
|
33331
|
+
fs6.writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf8");
|
|
32893
33332
|
console.log(`\u2713 PostgresAI MCP server configured for ${client}`);
|
|
32894
33333
|
console.log(` Config file: ${configPath}`);
|
|
32895
33334
|
console.log("");
|