postgresai 0.15.0-dev.6 → 0.15.0-dev.9
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/README.md +3 -1
- package/bin/postgres-ai.ts +119 -71
- package/bun.lock +4 -4
- package/dist/bin/postgres-ai.js +867 -232
- package/instances.demo.yml +14 -0
- package/lib/checkup-api.ts +25 -6
- package/lib/checkup.ts +225 -0
- package/lib/init.ts +195 -3
- package/lib/metrics-loader.ts +3 -1
- package/lib/supabase.ts +8 -1
- package/package.json +4 -4
- package/scripts/embed-checkup-dictionary.ts +9 -0
- package/scripts/embed-metrics.ts +2 -0
- package/test/PERMISSION_CHECK_TEST_SUMMARY.md +139 -0
- package/test/checkup.test.ts +1288 -2
- package/test/config-consistency.test.ts +321 -5
- package/test/init.integration.test.ts +27 -28
- package/test/init.test.ts +528 -8
- package/test/monitoring.test.ts +2 -2
- package/test/permission-check-sql.test.ts +116 -0
- package/test/schema-validation.test.ts +81 -0
- package/test/test-utils.ts +51 -2
- package/test/upgrade.test.ts +422 -0
package/dist/bin/postgres-ai.js
CHANGED
|
@@ -6,25 +6,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
6
6
|
var __defProp = Object.defineProperty;
|
|
7
7
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
function __accessProp(key) {
|
|
10
|
+
return this[key];
|
|
11
|
+
}
|
|
12
|
+
var __toESMCache_node;
|
|
13
|
+
var __toESMCache_esm;
|
|
9
14
|
var __toESM = (mod, isNodeMode, target) => {
|
|
15
|
+
var canCache = mod != null && typeof mod === "object";
|
|
16
|
+
if (canCache) {
|
|
17
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
18
|
+
var cached = cache.get(mod);
|
|
19
|
+
if (cached)
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
10
22
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
11
23
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
12
24
|
for (let key of __getOwnPropNames(mod))
|
|
13
25
|
if (!__hasOwnProp.call(to, key))
|
|
14
26
|
__defProp(to, key, {
|
|
15
|
-
get: (
|
|
27
|
+
get: __accessProp.bind(mod, key),
|
|
16
28
|
enumerable: true
|
|
17
29
|
});
|
|
30
|
+
if (canCache)
|
|
31
|
+
cache.set(mod, to);
|
|
18
32
|
return to;
|
|
19
33
|
};
|
|
20
34
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
35
|
+
var __returnValue = (v) => v;
|
|
36
|
+
function __exportSetter(name, newValue) {
|
|
37
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
38
|
+
}
|
|
21
39
|
var __export = (target, all) => {
|
|
22
40
|
for (var name in all)
|
|
23
41
|
__defProp(target, name, {
|
|
24
42
|
get: all[name],
|
|
25
43
|
enumerable: true,
|
|
26
44
|
configurable: true,
|
|
27
|
-
set: (
|
|
45
|
+
set: __exportSetter.bind(all, name)
|
|
28
46
|
});
|
|
29
47
|
};
|
|
30
48
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -80,7 +98,7 @@ var require_argument = __commonJS((exports) => {
|
|
|
80
98
|
this._name = name;
|
|
81
99
|
break;
|
|
82
100
|
}
|
|
83
|
-
if (this._name.
|
|
101
|
+
if (this._name.endsWith("...")) {
|
|
84
102
|
this.variadic = true;
|
|
85
103
|
this._name = this._name.slice(0, -3);
|
|
86
104
|
}
|
|
@@ -88,11 +106,12 @@ var require_argument = __commonJS((exports) => {
|
|
|
88
106
|
name() {
|
|
89
107
|
return this._name;
|
|
90
108
|
}
|
|
91
|
-
|
|
109
|
+
_collectValue(value, previous) {
|
|
92
110
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
93
111
|
return [value];
|
|
94
112
|
}
|
|
95
|
-
|
|
113
|
+
previous.push(value);
|
|
114
|
+
return previous;
|
|
96
115
|
}
|
|
97
116
|
default(value, description) {
|
|
98
117
|
this.defaultValue = value;
|
|
@@ -110,7 +129,7 @@ var require_argument = __commonJS((exports) => {
|
|
|
110
129
|
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
|
|
111
130
|
}
|
|
112
131
|
if (this.variadic) {
|
|
113
|
-
return this.
|
|
132
|
+
return this._collectValue(arg, previous);
|
|
114
133
|
}
|
|
115
134
|
return arg;
|
|
116
135
|
};
|
|
@@ -140,10 +159,14 @@ var require_help = __commonJS((exports) => {
|
|
|
140
159
|
class Help {
|
|
141
160
|
constructor() {
|
|
142
161
|
this.helpWidth = undefined;
|
|
162
|
+
this.minWidthToWrap = 40;
|
|
143
163
|
this.sortSubcommands = false;
|
|
144
164
|
this.sortOptions = false;
|
|
145
165
|
this.showGlobalOptions = false;
|
|
146
166
|
}
|
|
167
|
+
prepareContext(contextOptions) {
|
|
168
|
+
this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
|
|
169
|
+
}
|
|
147
170
|
visibleCommands(cmd) {
|
|
148
171
|
const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
|
|
149
172
|
const helpCommand = cmd._getHelpCommand();
|
|
@@ -218,22 +241,22 @@ var require_help = __commonJS((exports) => {
|
|
|
218
241
|
}
|
|
219
242
|
longestSubcommandTermLength(cmd, helper) {
|
|
220
243
|
return helper.visibleCommands(cmd).reduce((max, command) => {
|
|
221
|
-
return Math.max(max, helper.subcommandTerm(command)
|
|
244
|
+
return Math.max(max, this.displayWidth(helper.styleSubcommandTerm(helper.subcommandTerm(command))));
|
|
222
245
|
}, 0);
|
|
223
246
|
}
|
|
224
247
|
longestOptionTermLength(cmd, helper) {
|
|
225
248
|
return helper.visibleOptions(cmd).reduce((max, option) => {
|
|
226
|
-
return Math.max(max, helper.optionTerm(option)
|
|
249
|
+
return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
|
|
227
250
|
}, 0);
|
|
228
251
|
}
|
|
229
252
|
longestGlobalOptionTermLength(cmd, helper) {
|
|
230
253
|
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
|
|
231
|
-
return Math.max(max, helper.optionTerm(option)
|
|
254
|
+
return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
|
|
232
255
|
}, 0);
|
|
233
256
|
}
|
|
234
257
|
longestArgumentTermLength(cmd, helper) {
|
|
235
258
|
return helper.visibleArguments(cmd).reduce((max, argument) => {
|
|
236
|
-
return Math.max(max, helper.argumentTerm(argument)
|
|
259
|
+
return Math.max(max, this.displayWidth(helper.styleArgumentTerm(helper.argumentTerm(argument))));
|
|
237
260
|
}, 0);
|
|
238
261
|
}
|
|
239
262
|
commandUsage(cmd) {
|
|
@@ -271,7 +294,11 @@ var require_help = __commonJS((exports) => {
|
|
|
271
294
|
extraInfo.push(`env: ${option.envVar}`);
|
|
272
295
|
}
|
|
273
296
|
if (extraInfo.length > 0) {
|
|
274
|
-
|
|
297
|
+
const extraDescription = `(${extraInfo.join(", ")})`;
|
|
298
|
+
if (option.description) {
|
|
299
|
+
return `${option.description} ${extraDescription}`;
|
|
300
|
+
}
|
|
301
|
+
return extraDescription;
|
|
275
302
|
}
|
|
276
303
|
return option.description;
|
|
277
304
|
}
|
|
@@ -284,102 +311,202 @@ var require_help = __commonJS((exports) => {
|
|
|
284
311
|
extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
|
|
285
312
|
}
|
|
286
313
|
if (extraInfo.length > 0) {
|
|
287
|
-
const
|
|
314
|
+
const extraDescription = `(${extraInfo.join(", ")})`;
|
|
288
315
|
if (argument.description) {
|
|
289
|
-
return `${argument.description} ${
|
|
316
|
+
return `${argument.description} ${extraDescription}`;
|
|
290
317
|
}
|
|
291
|
-
return
|
|
318
|
+
return extraDescription;
|
|
292
319
|
}
|
|
293
320
|
return argument.description;
|
|
294
321
|
}
|
|
322
|
+
formatItemList(heading, items, helper) {
|
|
323
|
+
if (items.length === 0)
|
|
324
|
+
return [];
|
|
325
|
+
return [helper.styleTitle(heading), ...items, ""];
|
|
326
|
+
}
|
|
327
|
+
groupItems(unsortedItems, visibleItems, getGroup) {
|
|
328
|
+
const result = new Map;
|
|
329
|
+
unsortedItems.forEach((item) => {
|
|
330
|
+
const group = getGroup(item);
|
|
331
|
+
if (!result.has(group))
|
|
332
|
+
result.set(group, []);
|
|
333
|
+
});
|
|
334
|
+
visibleItems.forEach((item) => {
|
|
335
|
+
const group = getGroup(item);
|
|
336
|
+
if (!result.has(group)) {
|
|
337
|
+
result.set(group, []);
|
|
338
|
+
}
|
|
339
|
+
result.get(group).push(item);
|
|
340
|
+
});
|
|
341
|
+
return result;
|
|
342
|
+
}
|
|
295
343
|
formatHelp(cmd, helper) {
|
|
296
344
|
const termWidth = helper.padWidth(cmd, helper);
|
|
297
|
-
const helpWidth = helper.helpWidth
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
function formatItem(term, description) {
|
|
301
|
-
if (description) {
|
|
302
|
-
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
|
|
303
|
-
return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);
|
|
304
|
-
}
|
|
305
|
-
return term;
|
|
306
|
-
}
|
|
307
|
-
function formatList(textArray) {
|
|
308
|
-
return textArray.join(`
|
|
309
|
-
`).replace(/^/gm, " ".repeat(itemIndentWidth));
|
|
345
|
+
const helpWidth = helper.helpWidth ?? 80;
|
|
346
|
+
function callFormatItem(term, description) {
|
|
347
|
+
return helper.formatItem(term, termWidth, description, helper);
|
|
310
348
|
}
|
|
311
|
-
let output = [
|
|
349
|
+
let output = [
|
|
350
|
+
`${helper.styleTitle("Usage:")} ${helper.styleUsage(helper.commandUsage(cmd))}`,
|
|
351
|
+
""
|
|
352
|
+
];
|
|
312
353
|
const commandDescription = helper.commandDescription(cmd);
|
|
313
354
|
if (commandDescription.length > 0) {
|
|
314
355
|
output = output.concat([
|
|
315
|
-
helper.
|
|
356
|
+
helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth),
|
|
316
357
|
""
|
|
317
358
|
]);
|
|
318
359
|
}
|
|
319
360
|
const argumentList = helper.visibleArguments(cmd).map((argument) => {
|
|
320
|
-
return
|
|
361
|
+
return callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument)));
|
|
321
362
|
});
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
363
|
+
output = output.concat(this.formatItemList("Arguments:", argumentList, helper));
|
|
364
|
+
const optionGroups = this.groupItems(cmd.options, helper.visibleOptions(cmd), (option) => option.helpGroupHeading ?? "Options:");
|
|
365
|
+
optionGroups.forEach((options, group) => {
|
|
366
|
+
const optionList = options.map((option) => {
|
|
367
|
+
return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
|
|
368
|
+
});
|
|
369
|
+
output = output.concat(this.formatItemList(group, optionList, helper));
|
|
327
370
|
});
|
|
328
|
-
if (
|
|
329
|
-
output = output.concat(["Options:", formatList(optionList), ""]);
|
|
330
|
-
}
|
|
331
|
-
if (this.showGlobalOptions) {
|
|
371
|
+
if (helper.showGlobalOptions) {
|
|
332
372
|
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
|
|
333
|
-
return
|
|
373
|
+
return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
|
|
334
374
|
});
|
|
335
|
-
|
|
336
|
-
output = output.concat([
|
|
337
|
-
"Global Options:",
|
|
338
|
-
formatList(globalOptionList),
|
|
339
|
-
""
|
|
340
|
-
]);
|
|
341
|
-
}
|
|
375
|
+
output = output.concat(this.formatItemList("Global Options:", globalOptionList, helper));
|
|
342
376
|
}
|
|
343
|
-
const
|
|
344
|
-
|
|
377
|
+
const commandGroups = this.groupItems(cmd.commands, helper.visibleCommands(cmd), (sub) => sub.helpGroup() || "Commands:");
|
|
378
|
+
commandGroups.forEach((commands, group) => {
|
|
379
|
+
const commandList = commands.map((sub) => {
|
|
380
|
+
return callFormatItem(helper.styleSubcommandTerm(helper.subcommandTerm(sub)), helper.styleSubcommandDescription(helper.subcommandDescription(sub)));
|
|
381
|
+
});
|
|
382
|
+
output = output.concat(this.formatItemList(group, commandList, helper));
|
|
345
383
|
});
|
|
346
|
-
if (commandList.length > 0) {
|
|
347
|
-
output = output.concat(["Commands:", formatList(commandList), ""]);
|
|
348
|
-
}
|
|
349
384
|
return output.join(`
|
|
350
385
|
`);
|
|
351
386
|
}
|
|
387
|
+
displayWidth(str) {
|
|
388
|
+
return stripColor(str).length;
|
|
389
|
+
}
|
|
390
|
+
styleTitle(str) {
|
|
391
|
+
return str;
|
|
392
|
+
}
|
|
393
|
+
styleUsage(str) {
|
|
394
|
+
return str.split(" ").map((word) => {
|
|
395
|
+
if (word === "[options]")
|
|
396
|
+
return this.styleOptionText(word);
|
|
397
|
+
if (word === "[command]")
|
|
398
|
+
return this.styleSubcommandText(word);
|
|
399
|
+
if (word[0] === "[" || word[0] === "<")
|
|
400
|
+
return this.styleArgumentText(word);
|
|
401
|
+
return this.styleCommandText(word);
|
|
402
|
+
}).join(" ");
|
|
403
|
+
}
|
|
404
|
+
styleCommandDescription(str) {
|
|
405
|
+
return this.styleDescriptionText(str);
|
|
406
|
+
}
|
|
407
|
+
styleOptionDescription(str) {
|
|
408
|
+
return this.styleDescriptionText(str);
|
|
409
|
+
}
|
|
410
|
+
styleSubcommandDescription(str) {
|
|
411
|
+
return this.styleDescriptionText(str);
|
|
412
|
+
}
|
|
413
|
+
styleArgumentDescription(str) {
|
|
414
|
+
return this.styleDescriptionText(str);
|
|
415
|
+
}
|
|
416
|
+
styleDescriptionText(str) {
|
|
417
|
+
return str;
|
|
418
|
+
}
|
|
419
|
+
styleOptionTerm(str) {
|
|
420
|
+
return this.styleOptionText(str);
|
|
421
|
+
}
|
|
422
|
+
styleSubcommandTerm(str) {
|
|
423
|
+
return str.split(" ").map((word) => {
|
|
424
|
+
if (word === "[options]")
|
|
425
|
+
return this.styleOptionText(word);
|
|
426
|
+
if (word[0] === "[" || word[0] === "<")
|
|
427
|
+
return this.styleArgumentText(word);
|
|
428
|
+
return this.styleSubcommandText(word);
|
|
429
|
+
}).join(" ");
|
|
430
|
+
}
|
|
431
|
+
styleArgumentTerm(str) {
|
|
432
|
+
return this.styleArgumentText(str);
|
|
433
|
+
}
|
|
434
|
+
styleOptionText(str) {
|
|
435
|
+
return str;
|
|
436
|
+
}
|
|
437
|
+
styleArgumentText(str) {
|
|
438
|
+
return str;
|
|
439
|
+
}
|
|
440
|
+
styleSubcommandText(str) {
|
|
441
|
+
return str;
|
|
442
|
+
}
|
|
443
|
+
styleCommandText(str) {
|
|
444
|
+
return str;
|
|
445
|
+
}
|
|
352
446
|
padWidth(cmd, helper) {
|
|
353
447
|
return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
|
|
354
448
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
if (
|
|
449
|
+
preformatted(str) {
|
|
450
|
+
return /\n[^\S\r\n]/.test(str);
|
|
451
|
+
}
|
|
452
|
+
formatItem(term, termWidth, description, helper) {
|
|
453
|
+
const itemIndent = 2;
|
|
454
|
+
const itemIndentStr = " ".repeat(itemIndent);
|
|
455
|
+
if (!description)
|
|
456
|
+
return itemIndentStr + term;
|
|
457
|
+
const paddedTerm = term.padEnd(termWidth + term.length - helper.displayWidth(term));
|
|
458
|
+
const spacerWidth = 2;
|
|
459
|
+
const helpWidth = this.helpWidth ?? 80;
|
|
460
|
+
const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
|
|
461
|
+
let formattedDescription;
|
|
462
|
+
if (remainingWidth < this.minWidthToWrap || helper.preformatted(description)) {
|
|
463
|
+
formattedDescription = description;
|
|
464
|
+
} else {
|
|
465
|
+
const wrappedDescription = helper.boxWrap(description, remainingWidth);
|
|
466
|
+
formattedDescription = wrappedDescription.replace(/\n/g, `
|
|
467
|
+
` + " ".repeat(termWidth + spacerWidth));
|
|
468
|
+
}
|
|
469
|
+
return itemIndentStr + paddedTerm + " ".repeat(spacerWidth) + formattedDescription.replace(/\n/g, `
|
|
470
|
+
${itemIndentStr}`);
|
|
471
|
+
}
|
|
472
|
+
boxWrap(str, width) {
|
|
473
|
+
if (width < this.minWidthToWrap)
|
|
362
474
|
return str;
|
|
363
|
-
const
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
475
|
+
const rawLines = str.split(/\r\n|\n/);
|
|
476
|
+
const chunkPattern = /[\s]*[^\s]+/g;
|
|
477
|
+
const wrappedLines = [];
|
|
478
|
+
rawLines.forEach((line) => {
|
|
479
|
+
const chunks = line.match(chunkPattern);
|
|
480
|
+
if (chunks === null) {
|
|
481
|
+
wrappedLines.push("");
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
let sumChunks = [chunks.shift()];
|
|
485
|
+
let sumWidth = this.displayWidth(sumChunks[0]);
|
|
486
|
+
chunks.forEach((chunk) => {
|
|
487
|
+
const visibleWidth = this.displayWidth(chunk);
|
|
488
|
+
if (sumWidth + visibleWidth <= width) {
|
|
489
|
+
sumChunks.push(chunk);
|
|
490
|
+
sumWidth += visibleWidth;
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
wrappedLines.push(sumChunks.join(""));
|
|
494
|
+
const nextChunk = chunk.trimStart();
|
|
495
|
+
sumChunks = [nextChunk];
|
|
496
|
+
sumWidth = this.displayWidth(nextChunk);
|
|
497
|
+
});
|
|
498
|
+
wrappedLines.push(sumChunks.join(""));
|
|
499
|
+
});
|
|
500
|
+
return wrappedLines.join(`
|
|
379
501
|
`);
|
|
380
502
|
}
|
|
381
503
|
}
|
|
504
|
+
function stripColor(str) {
|
|
505
|
+
const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
|
|
506
|
+
return str.replace(sgrPattern, "");
|
|
507
|
+
}
|
|
382
508
|
exports.Help = Help;
|
|
509
|
+
exports.stripColor = stripColor;
|
|
383
510
|
});
|
|
384
511
|
|
|
385
512
|
// node_modules/commander/lib/option.js
|
|
@@ -410,6 +537,7 @@ var require_option = __commonJS((exports) => {
|
|
|
410
537
|
this.argChoices = undefined;
|
|
411
538
|
this.conflictsWith = [];
|
|
412
539
|
this.implied = undefined;
|
|
540
|
+
this.helpGroupHeading = undefined;
|
|
413
541
|
}
|
|
414
542
|
default(value, description) {
|
|
415
543
|
this.defaultValue = value;
|
|
@@ -448,11 +576,12 @@ var require_option = __commonJS((exports) => {
|
|
|
448
576
|
this.hidden = !!hide;
|
|
449
577
|
return this;
|
|
450
578
|
}
|
|
451
|
-
|
|
579
|
+
_collectValue(value, previous) {
|
|
452
580
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
453
581
|
return [value];
|
|
454
582
|
}
|
|
455
|
-
|
|
583
|
+
previous.push(value);
|
|
584
|
+
return previous;
|
|
456
585
|
}
|
|
457
586
|
choices(values) {
|
|
458
587
|
this.argChoices = values.slice();
|
|
@@ -461,7 +590,7 @@ var require_option = __commonJS((exports) => {
|
|
|
461
590
|
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
|
|
462
591
|
}
|
|
463
592
|
if (this.variadic) {
|
|
464
|
-
return this.
|
|
593
|
+
return this._collectValue(arg, previous);
|
|
465
594
|
}
|
|
466
595
|
return arg;
|
|
467
596
|
};
|
|
@@ -474,7 +603,14 @@ var require_option = __commonJS((exports) => {
|
|
|
474
603
|
return this.short.replace(/^-/, "");
|
|
475
604
|
}
|
|
476
605
|
attributeName() {
|
|
477
|
-
|
|
606
|
+
if (this.negate) {
|
|
607
|
+
return camelcase(this.name().replace(/^no-/, ""));
|
|
608
|
+
}
|
|
609
|
+
return camelcase(this.name());
|
|
610
|
+
}
|
|
611
|
+
helpGroup(heading) {
|
|
612
|
+
this.helpGroupHeading = heading;
|
|
613
|
+
return this;
|
|
478
614
|
}
|
|
479
615
|
is(arg) {
|
|
480
616
|
return this.short === arg || this.long === arg;
|
|
@@ -519,14 +655,38 @@ var require_option = __commonJS((exports) => {
|
|
|
519
655
|
function splitOptionFlags(flags) {
|
|
520
656
|
let shortFlag;
|
|
521
657
|
let longFlag;
|
|
522
|
-
const
|
|
523
|
-
|
|
658
|
+
const shortFlagExp = /^-[^-]$/;
|
|
659
|
+
const longFlagExp = /^--[^-]/;
|
|
660
|
+
const flagParts = flags.split(/[ |,]+/).concat("guard");
|
|
661
|
+
if (shortFlagExp.test(flagParts[0]))
|
|
524
662
|
shortFlag = flagParts.shift();
|
|
525
|
-
|
|
526
|
-
|
|
663
|
+
if (longFlagExp.test(flagParts[0]))
|
|
664
|
+
longFlag = flagParts.shift();
|
|
665
|
+
if (!shortFlag && shortFlagExp.test(flagParts[0]))
|
|
666
|
+
shortFlag = flagParts.shift();
|
|
667
|
+
if (!shortFlag && longFlagExp.test(flagParts[0])) {
|
|
527
668
|
shortFlag = longFlag;
|
|
528
|
-
longFlag =
|
|
529
|
-
}
|
|
669
|
+
longFlag = flagParts.shift();
|
|
670
|
+
}
|
|
671
|
+
if (flagParts[0].startsWith("-")) {
|
|
672
|
+
const unsupportedFlag = flagParts[0];
|
|
673
|
+
const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
|
|
674
|
+
if (/^-[^-][^-]/.test(unsupportedFlag))
|
|
675
|
+
throw new Error(`${baseError}
|
|
676
|
+
- a short flag is a single dash and a single character
|
|
677
|
+
- either use a single dash and a single character (for a short flag)
|
|
678
|
+
- or use a double dash for a long option (and can have two, like '--ws, --workspace')`);
|
|
679
|
+
if (shortFlagExp.test(unsupportedFlag))
|
|
680
|
+
throw new Error(`${baseError}
|
|
681
|
+
- too many short flags`);
|
|
682
|
+
if (longFlagExp.test(unsupportedFlag))
|
|
683
|
+
throw new Error(`${baseError}
|
|
684
|
+
- too many long flags`);
|
|
685
|
+
throw new Error(`${baseError}
|
|
686
|
+
- unrecognised flag format`);
|
|
687
|
+
}
|
|
688
|
+
if (shortFlag === undefined && longFlag === undefined)
|
|
689
|
+
throw new Error(`option creation failed due to no flags found in '${flags}'.`);
|
|
530
690
|
return { shortFlag, longFlag };
|
|
531
691
|
}
|
|
532
692
|
exports.Option = Option;
|
|
@@ -615,7 +775,7 @@ var require_command = __commonJS((exports) => {
|
|
|
615
775
|
var process2 = __require("node:process");
|
|
616
776
|
var { Argument, humanReadableArgName } = require_argument();
|
|
617
777
|
var { CommanderError } = require_error();
|
|
618
|
-
var { Help } = require_help();
|
|
778
|
+
var { Help, stripColor } = require_help();
|
|
619
779
|
var { Option, DualOptions } = require_option();
|
|
620
780
|
var { suggestSimilar } = require_suggestSimilar();
|
|
621
781
|
|
|
@@ -626,7 +786,7 @@ var require_command = __commonJS((exports) => {
|
|
|
626
786
|
this.options = [];
|
|
627
787
|
this.parent = null;
|
|
628
788
|
this._allowUnknownOption = false;
|
|
629
|
-
this._allowExcessArguments =
|
|
789
|
+
this._allowExcessArguments = false;
|
|
630
790
|
this.registeredArguments = [];
|
|
631
791
|
this._args = this.registeredArguments;
|
|
632
792
|
this.args = [];
|
|
@@ -653,18 +813,25 @@ var require_command = __commonJS((exports) => {
|
|
|
653
813
|
this._lifeCycleHooks = {};
|
|
654
814
|
this._showHelpAfterError = false;
|
|
655
815
|
this._showSuggestionAfterError = true;
|
|
816
|
+
this._savedState = null;
|
|
656
817
|
this._outputConfiguration = {
|
|
657
818
|
writeOut: (str) => process2.stdout.write(str),
|
|
658
819
|
writeErr: (str) => process2.stderr.write(str),
|
|
820
|
+
outputError: (str, write) => write(str),
|
|
659
821
|
getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : undefined,
|
|
660
822
|
getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : undefined,
|
|
661
|
-
|
|
823
|
+
getOutHasColors: () => useColor() ?? (process2.stdout.isTTY && process2.stdout.hasColors?.()),
|
|
824
|
+
getErrHasColors: () => useColor() ?? (process2.stderr.isTTY && process2.stderr.hasColors?.()),
|
|
825
|
+
stripColor: (str) => stripColor(str)
|
|
662
826
|
};
|
|
663
827
|
this._hidden = false;
|
|
664
828
|
this._helpOption = undefined;
|
|
665
829
|
this._addImplicitHelpCommand = undefined;
|
|
666
830
|
this._helpCommand = undefined;
|
|
667
831
|
this._helpConfiguration = {};
|
|
832
|
+
this._helpGroupHeading = undefined;
|
|
833
|
+
this._defaultCommandGroup = undefined;
|
|
834
|
+
this._defaultOptionGroup = undefined;
|
|
668
835
|
}
|
|
669
836
|
copyInheritedSettings(sourceCommand) {
|
|
670
837
|
this._outputConfiguration = sourceCommand._outputConfiguration;
|
|
@@ -729,7 +896,10 @@ var require_command = __commonJS((exports) => {
|
|
|
729
896
|
configureOutput(configuration) {
|
|
730
897
|
if (configuration === undefined)
|
|
731
898
|
return this._outputConfiguration;
|
|
732
|
-
|
|
899
|
+
this._outputConfiguration = {
|
|
900
|
+
...this._outputConfiguration,
|
|
901
|
+
...configuration
|
|
902
|
+
};
|
|
733
903
|
return this;
|
|
734
904
|
}
|
|
735
905
|
showHelpAfterError(displayHelp = true) {
|
|
@@ -760,12 +930,12 @@ var require_command = __commonJS((exports) => {
|
|
|
760
930
|
createArgument(name, description) {
|
|
761
931
|
return new Argument(name, description);
|
|
762
932
|
}
|
|
763
|
-
argument(name, description,
|
|
933
|
+
argument(name, description, parseArg, defaultValue) {
|
|
764
934
|
const argument = this.createArgument(name, description);
|
|
765
|
-
if (typeof
|
|
766
|
-
argument.default(defaultValue).argParser(
|
|
935
|
+
if (typeof parseArg === "function") {
|
|
936
|
+
argument.default(defaultValue).argParser(parseArg);
|
|
767
937
|
} else {
|
|
768
|
-
argument.default(
|
|
938
|
+
argument.default(parseArg);
|
|
769
939
|
}
|
|
770
940
|
this.addArgument(argument);
|
|
771
941
|
return this;
|
|
@@ -778,7 +948,7 @@ var require_command = __commonJS((exports) => {
|
|
|
778
948
|
}
|
|
779
949
|
addArgument(argument) {
|
|
780
950
|
const previousArgument = this.registeredArguments.slice(-1)[0];
|
|
781
|
-
if (previousArgument
|
|
951
|
+
if (previousArgument?.variadic) {
|
|
782
952
|
throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
|
|
783
953
|
}
|
|
784
954
|
if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
|
|
@@ -790,10 +960,13 @@ var require_command = __commonJS((exports) => {
|
|
|
790
960
|
helpCommand(enableOrNameAndArgs, description) {
|
|
791
961
|
if (typeof enableOrNameAndArgs === "boolean") {
|
|
792
962
|
this._addImplicitHelpCommand = enableOrNameAndArgs;
|
|
963
|
+
if (enableOrNameAndArgs && this._defaultCommandGroup) {
|
|
964
|
+
this._initCommandGroup(this._getHelpCommand());
|
|
965
|
+
}
|
|
793
966
|
return this;
|
|
794
967
|
}
|
|
795
|
-
|
|
796
|
-
const [, helpName, helpArgs] =
|
|
968
|
+
const nameAndArgs = enableOrNameAndArgs ?? "help [command]";
|
|
969
|
+
const [, helpName, helpArgs] = nameAndArgs.match(/([^ ]+) *(.*)/);
|
|
797
970
|
const helpDescription = description ?? "display help for command";
|
|
798
971
|
const helpCommand = this.createCommand(helpName);
|
|
799
972
|
helpCommand.helpOption(false);
|
|
@@ -803,6 +976,8 @@ var require_command = __commonJS((exports) => {
|
|
|
803
976
|
helpCommand.description(helpDescription);
|
|
804
977
|
this._addImplicitHelpCommand = true;
|
|
805
978
|
this._helpCommand = helpCommand;
|
|
979
|
+
if (enableOrNameAndArgs || description)
|
|
980
|
+
this._initCommandGroup(helpCommand);
|
|
806
981
|
return this;
|
|
807
982
|
}
|
|
808
983
|
addHelpCommand(helpCommand, deprecatedDescription) {
|
|
@@ -812,6 +987,7 @@ var require_command = __commonJS((exports) => {
|
|
|
812
987
|
}
|
|
813
988
|
this._addImplicitHelpCommand = true;
|
|
814
989
|
this._helpCommand = helpCommand;
|
|
990
|
+
this._initCommandGroup(helpCommand);
|
|
815
991
|
return this;
|
|
816
992
|
}
|
|
817
993
|
_getHelpCommand() {
|
|
@@ -891,6 +1067,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
891
1067
|
throw new Error(`Cannot add option '${option.flags}'${this._name && ` to command '${this._name}'`} due to conflicting flag '${matchingFlag}'
|
|
892
1068
|
- already used by option '${matchingOption.flags}'`);
|
|
893
1069
|
}
|
|
1070
|
+
this._initOptionGroup(option);
|
|
894
1071
|
this.options.push(option);
|
|
895
1072
|
}
|
|
896
1073
|
_registerCommand(command) {
|
|
@@ -903,6 +1080,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
903
1080
|
const newCmd = knownBy(command).join("|");
|
|
904
1081
|
throw new Error(`cannot add command '${newCmd}' as already have command '${existingCmd}'`);
|
|
905
1082
|
}
|
|
1083
|
+
this._initCommandGroup(command);
|
|
906
1084
|
this.commands.push(command);
|
|
907
1085
|
}
|
|
908
1086
|
addOption(option) {
|
|
@@ -925,7 +1103,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
925
1103
|
if (val !== null && option.parseArg) {
|
|
926
1104
|
val = this._callParseArg(option, val, oldValue, invalidValueMessage);
|
|
927
1105
|
} else if (val !== null && option.variadic) {
|
|
928
|
-
val = option.
|
|
1106
|
+
val = option._collectValue(val, oldValue);
|
|
929
1107
|
}
|
|
930
1108
|
if (val == null) {
|
|
931
1109
|
if (option.negate) {
|
|
@@ -1090,15 +1268,53 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1090
1268
|
return userArgs;
|
|
1091
1269
|
}
|
|
1092
1270
|
parse(argv, parseOptions) {
|
|
1271
|
+
this._prepareForParse();
|
|
1093
1272
|
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
1094
1273
|
this._parseCommand([], userArgs);
|
|
1095
1274
|
return this;
|
|
1096
1275
|
}
|
|
1097
1276
|
async parseAsync(argv, parseOptions) {
|
|
1277
|
+
this._prepareForParse();
|
|
1098
1278
|
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
1099
1279
|
await this._parseCommand([], userArgs);
|
|
1100
1280
|
return this;
|
|
1101
1281
|
}
|
|
1282
|
+
_prepareForParse() {
|
|
1283
|
+
if (this._savedState === null) {
|
|
1284
|
+
this.saveStateBeforeParse();
|
|
1285
|
+
} else {
|
|
1286
|
+
this.restoreStateBeforeParse();
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
saveStateBeforeParse() {
|
|
1290
|
+
this._savedState = {
|
|
1291
|
+
_name: this._name,
|
|
1292
|
+
_optionValues: { ...this._optionValues },
|
|
1293
|
+
_optionValueSources: { ...this._optionValueSources }
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
restoreStateBeforeParse() {
|
|
1297
|
+
if (this._storeOptionsAsProperties)
|
|
1298
|
+
throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
|
|
1299
|
+
- either make a new Command for each call to parse, or stop storing options as properties`);
|
|
1300
|
+
this._name = this._savedState._name;
|
|
1301
|
+
this._scriptPath = null;
|
|
1302
|
+
this.rawArgs = [];
|
|
1303
|
+
this._optionValues = { ...this._savedState._optionValues };
|
|
1304
|
+
this._optionValueSources = { ...this._savedState._optionValueSources };
|
|
1305
|
+
this.args = [];
|
|
1306
|
+
this.processedArgs = [];
|
|
1307
|
+
}
|
|
1308
|
+
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
1309
|
+
if (fs.existsSync(executableFile))
|
|
1310
|
+
return;
|
|
1311
|
+
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
1312
|
+
const executableMissing = `'${executableFile}' does not exist
|
|
1313
|
+
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
1314
|
+
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
1315
|
+
- ${executableDirMessage}`;
|
|
1316
|
+
throw new Error(executableMissing);
|
|
1317
|
+
}
|
|
1102
1318
|
_executeSubCommand(subcommand, args) {
|
|
1103
1319
|
args = args.slice();
|
|
1104
1320
|
let launchWithNode = false;
|
|
@@ -1122,7 +1338,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1122
1338
|
let resolvedScriptPath;
|
|
1123
1339
|
try {
|
|
1124
1340
|
resolvedScriptPath = fs.realpathSync(this._scriptPath);
|
|
1125
|
-
} catch
|
|
1341
|
+
} catch {
|
|
1126
1342
|
resolvedScriptPath = this._scriptPath;
|
|
1127
1343
|
}
|
|
1128
1344
|
executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
|
|
@@ -1148,6 +1364,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1148
1364
|
proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
|
|
1149
1365
|
}
|
|
1150
1366
|
} else {
|
|
1367
|
+
this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
|
|
1151
1368
|
args.unshift(executableFile);
|
|
1152
1369
|
args = incrementNodeInspectorPort(process2.execArgv).concat(args);
|
|
1153
1370
|
proc = childProcess.spawn(process2.execPath, args, { stdio: "inherit" });
|
|
@@ -1173,12 +1390,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1173
1390
|
});
|
|
1174
1391
|
proc.on("error", (err) => {
|
|
1175
1392
|
if (err.code === "ENOENT") {
|
|
1176
|
-
|
|
1177
|
-
const executableMissing = `'${executableFile}' does not exist
|
|
1178
|
-
- if '${subcommand._name}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
1179
|
-
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
1180
|
-
- ${executableDirMessage}`;
|
|
1181
|
-
throw new Error(executableMissing);
|
|
1393
|
+
this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
|
|
1182
1394
|
} else if (err.code === "EACCES") {
|
|
1183
1395
|
throw new Error(`'${executableFile}' not executable`);
|
|
1184
1396
|
}
|
|
@@ -1196,6 +1408,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1196
1408
|
const subCommand = this._findCommand(commandName);
|
|
1197
1409
|
if (!subCommand)
|
|
1198
1410
|
this.help({ error: true });
|
|
1411
|
+
subCommand._prepareForParse();
|
|
1199
1412
|
let promiseChain;
|
|
1200
1413
|
promiseChain = this._chainOrCallSubCommandHook(promiseChain, subCommand, "preSubcommand");
|
|
1201
1414
|
promiseChain = this._chainOrCall(promiseChain, () => {
|
|
@@ -1265,7 +1478,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1265
1478
|
this.processedArgs = processedArgs;
|
|
1266
1479
|
}
|
|
1267
1480
|
_chainOrCall(promise, fn) {
|
|
1268
|
-
if (promise
|
|
1481
|
+
if (promise?.then && typeof promise.then === "function") {
|
|
1269
1482
|
return promise.then(() => fn());
|
|
1270
1483
|
}
|
|
1271
1484
|
return fn();
|
|
@@ -1342,7 +1555,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1342
1555
|
promiseChain = this._chainOrCallHooks(promiseChain, "postAction");
|
|
1343
1556
|
return promiseChain;
|
|
1344
1557
|
}
|
|
1345
|
-
if (this.parent
|
|
1558
|
+
if (this.parent?.listenerCount(commandEvent)) {
|
|
1346
1559
|
checkForUnknownOptions();
|
|
1347
1560
|
this._processArguments();
|
|
1348
1561
|
this.parent.emit(commandEvent, operands, unknown);
|
|
@@ -1404,24 +1617,31 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1404
1617
|
cmd._checkForConflictingLocalOptions();
|
|
1405
1618
|
});
|
|
1406
1619
|
}
|
|
1407
|
-
parseOptions(
|
|
1620
|
+
parseOptions(args) {
|
|
1408
1621
|
const operands = [];
|
|
1409
1622
|
const unknown = [];
|
|
1410
1623
|
let dest = operands;
|
|
1411
|
-
const args = argv.slice();
|
|
1412
1624
|
function maybeOption(arg) {
|
|
1413
1625
|
return arg.length > 1 && arg[0] === "-";
|
|
1414
1626
|
}
|
|
1627
|
+
const negativeNumberArg = (arg) => {
|
|
1628
|
+
if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(arg))
|
|
1629
|
+
return false;
|
|
1630
|
+
return !this._getCommandAndAncestors().some((cmd) => cmd.options.map((opt) => opt.short).some((short) => /^-\d$/.test(short)));
|
|
1631
|
+
};
|
|
1415
1632
|
let activeVariadicOption = null;
|
|
1416
|
-
|
|
1417
|
-
|
|
1633
|
+
let activeGroup = null;
|
|
1634
|
+
let i = 0;
|
|
1635
|
+
while (i < args.length || activeGroup) {
|
|
1636
|
+
const arg = activeGroup ?? args[i++];
|
|
1637
|
+
activeGroup = null;
|
|
1418
1638
|
if (arg === "--") {
|
|
1419
1639
|
if (dest === unknown)
|
|
1420
1640
|
dest.push(arg);
|
|
1421
|
-
dest.push(...args);
|
|
1641
|
+
dest.push(...args.slice(i));
|
|
1422
1642
|
break;
|
|
1423
1643
|
}
|
|
1424
|
-
if (activeVariadicOption && !maybeOption(arg)) {
|
|
1644
|
+
if (activeVariadicOption && (!maybeOption(arg) || negativeNumberArg(arg))) {
|
|
1425
1645
|
this.emit(`option:${activeVariadicOption.name()}`, arg);
|
|
1426
1646
|
continue;
|
|
1427
1647
|
}
|
|
@@ -1430,14 +1650,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1430
1650
|
const option = this._findOption(arg);
|
|
1431
1651
|
if (option) {
|
|
1432
1652
|
if (option.required) {
|
|
1433
|
-
const value = args
|
|
1653
|
+
const value = args[i++];
|
|
1434
1654
|
if (value === undefined)
|
|
1435
1655
|
this.optionMissingArgument(option);
|
|
1436
1656
|
this.emit(`option:${option.name()}`, value);
|
|
1437
1657
|
} else if (option.optional) {
|
|
1438
1658
|
let value = null;
|
|
1439
|
-
if (args.length
|
|
1440
|
-
value = args
|
|
1659
|
+
if (i < args.length && (!maybeOption(args[i]) || negativeNumberArg(args[i]))) {
|
|
1660
|
+
value = args[i++];
|
|
1441
1661
|
}
|
|
1442
1662
|
this.emit(`option:${option.name()}`, value);
|
|
1443
1663
|
} else {
|
|
@@ -1454,7 +1674,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1454
1674
|
this.emit(`option:${option.name()}`, arg.slice(2));
|
|
1455
1675
|
} else {
|
|
1456
1676
|
this.emit(`option:${option.name()}`);
|
|
1457
|
-
|
|
1677
|
+
activeGroup = `-${arg.slice(2)}`;
|
|
1458
1678
|
}
|
|
1459
1679
|
continue;
|
|
1460
1680
|
}
|
|
@@ -1467,31 +1687,24 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1467
1687
|
continue;
|
|
1468
1688
|
}
|
|
1469
1689
|
}
|
|
1470
|
-
if (maybeOption(arg)) {
|
|
1690
|
+
if (dest === operands && maybeOption(arg) && !(this.commands.length === 0 && negativeNumberArg(arg))) {
|
|
1471
1691
|
dest = unknown;
|
|
1472
1692
|
}
|
|
1473
1693
|
if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
|
|
1474
1694
|
if (this._findCommand(arg)) {
|
|
1475
1695
|
operands.push(arg);
|
|
1476
|
-
|
|
1477
|
-
unknown.push(...args);
|
|
1696
|
+
unknown.push(...args.slice(i));
|
|
1478
1697
|
break;
|
|
1479
1698
|
} else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) {
|
|
1480
|
-
operands.push(arg);
|
|
1481
|
-
if (args.length > 0)
|
|
1482
|
-
operands.push(...args);
|
|
1699
|
+
operands.push(arg, ...args.slice(i));
|
|
1483
1700
|
break;
|
|
1484
1701
|
} else if (this._defaultCommandName) {
|
|
1485
|
-
unknown.push(arg);
|
|
1486
|
-
if (args.length > 0)
|
|
1487
|
-
unknown.push(...args);
|
|
1702
|
+
unknown.push(arg, ...args.slice(i));
|
|
1488
1703
|
break;
|
|
1489
1704
|
}
|
|
1490
1705
|
}
|
|
1491
1706
|
if (this._passThroughOptions) {
|
|
1492
|
-
dest.push(arg);
|
|
1493
|
-
if (args.length > 0)
|
|
1494
|
-
dest.push(...args);
|
|
1707
|
+
dest.push(arg, ...args.slice(i));
|
|
1495
1708
|
break;
|
|
1496
1709
|
}
|
|
1497
1710
|
dest.push(arg);
|
|
@@ -1702,6 +1915,32 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1702
1915
|
this._name = str;
|
|
1703
1916
|
return this;
|
|
1704
1917
|
}
|
|
1918
|
+
helpGroup(heading) {
|
|
1919
|
+
if (heading === undefined)
|
|
1920
|
+
return this._helpGroupHeading ?? "";
|
|
1921
|
+
this._helpGroupHeading = heading;
|
|
1922
|
+
return this;
|
|
1923
|
+
}
|
|
1924
|
+
commandsGroup(heading) {
|
|
1925
|
+
if (heading === undefined)
|
|
1926
|
+
return this._defaultCommandGroup ?? "";
|
|
1927
|
+
this._defaultCommandGroup = heading;
|
|
1928
|
+
return this;
|
|
1929
|
+
}
|
|
1930
|
+
optionsGroup(heading) {
|
|
1931
|
+
if (heading === undefined)
|
|
1932
|
+
return this._defaultOptionGroup ?? "";
|
|
1933
|
+
this._defaultOptionGroup = heading;
|
|
1934
|
+
return this;
|
|
1935
|
+
}
|
|
1936
|
+
_initOptionGroup(option) {
|
|
1937
|
+
if (this._defaultOptionGroup && !option.helpGroupHeading)
|
|
1938
|
+
option.helpGroup(this._defaultOptionGroup);
|
|
1939
|
+
}
|
|
1940
|
+
_initCommandGroup(cmd) {
|
|
1941
|
+
if (this._defaultCommandGroup && !cmd.helpGroup())
|
|
1942
|
+
cmd.helpGroup(this._defaultCommandGroup);
|
|
1943
|
+
}
|
|
1705
1944
|
nameFromFilename(filename) {
|
|
1706
1945
|
this._name = path.basename(filename, path.extname(filename));
|
|
1707
1946
|
return this;
|
|
@@ -1714,23 +1953,38 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1714
1953
|
}
|
|
1715
1954
|
helpInformation(contextOptions) {
|
|
1716
1955
|
const helper = this.createHelp();
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1956
|
+
const context = this._getOutputContext(contextOptions);
|
|
1957
|
+
helper.prepareContext({
|
|
1958
|
+
error: context.error,
|
|
1959
|
+
helpWidth: context.helpWidth,
|
|
1960
|
+
outputHasColors: context.hasColors
|
|
1961
|
+
});
|
|
1962
|
+
const text = helper.formatHelp(this, helper);
|
|
1963
|
+
if (context.hasColors)
|
|
1964
|
+
return text;
|
|
1965
|
+
return this._outputConfiguration.stripColor(text);
|
|
1721
1966
|
}
|
|
1722
|
-
|
|
1967
|
+
_getOutputContext(contextOptions) {
|
|
1723
1968
|
contextOptions = contextOptions || {};
|
|
1724
|
-
const
|
|
1725
|
-
let
|
|
1726
|
-
|
|
1727
|
-
|
|
1969
|
+
const error = !!contextOptions.error;
|
|
1970
|
+
let baseWrite;
|
|
1971
|
+
let hasColors;
|
|
1972
|
+
let helpWidth;
|
|
1973
|
+
if (error) {
|
|
1974
|
+
baseWrite = (str) => this._outputConfiguration.writeErr(str);
|
|
1975
|
+
hasColors = this._outputConfiguration.getErrHasColors();
|
|
1976
|
+
helpWidth = this._outputConfiguration.getErrHelpWidth();
|
|
1728
1977
|
} else {
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1978
|
+
baseWrite = (str) => this._outputConfiguration.writeOut(str);
|
|
1979
|
+
hasColors = this._outputConfiguration.getOutHasColors();
|
|
1980
|
+
helpWidth = this._outputConfiguration.getOutHelpWidth();
|
|
1981
|
+
}
|
|
1982
|
+
const write = (str) => {
|
|
1983
|
+
if (!hasColors)
|
|
1984
|
+
str = this._outputConfiguration.stripColor(str);
|
|
1985
|
+
return baseWrite(str);
|
|
1986
|
+
};
|
|
1987
|
+
return { error, write, hasColors, helpWidth };
|
|
1734
1988
|
}
|
|
1735
1989
|
outputHelp(contextOptions) {
|
|
1736
1990
|
let deprecatedCallback;
|
|
@@ -1738,35 +1992,44 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1738
1992
|
deprecatedCallback = contextOptions;
|
|
1739
1993
|
contextOptions = undefined;
|
|
1740
1994
|
}
|
|
1741
|
-
const
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1995
|
+
const outputContext = this._getOutputContext(contextOptions);
|
|
1996
|
+
const eventContext = {
|
|
1997
|
+
error: outputContext.error,
|
|
1998
|
+
write: outputContext.write,
|
|
1999
|
+
command: this
|
|
2000
|
+
};
|
|
2001
|
+
this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext));
|
|
2002
|
+
this.emit("beforeHelp", eventContext);
|
|
2003
|
+
let helpInformation = this.helpInformation({ error: outputContext.error });
|
|
1745
2004
|
if (deprecatedCallback) {
|
|
1746
2005
|
helpInformation = deprecatedCallback(helpInformation);
|
|
1747
2006
|
if (typeof helpInformation !== "string" && !Buffer.isBuffer(helpInformation)) {
|
|
1748
2007
|
throw new Error("outputHelp callback must return a string or a Buffer");
|
|
1749
2008
|
}
|
|
1750
2009
|
}
|
|
1751
|
-
|
|
2010
|
+
outputContext.write(helpInformation);
|
|
1752
2011
|
if (this._getHelpOption()?.long) {
|
|
1753
2012
|
this.emit(this._getHelpOption().long);
|
|
1754
2013
|
}
|
|
1755
|
-
this.emit("afterHelp",
|
|
1756
|
-
this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp",
|
|
2014
|
+
this.emit("afterHelp", eventContext);
|
|
2015
|
+
this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", eventContext));
|
|
1757
2016
|
}
|
|
1758
2017
|
helpOption(flags, description) {
|
|
1759
2018
|
if (typeof flags === "boolean") {
|
|
1760
2019
|
if (flags) {
|
|
1761
|
-
|
|
2020
|
+
if (this._helpOption === null)
|
|
2021
|
+
this._helpOption = undefined;
|
|
2022
|
+
if (this._defaultOptionGroup) {
|
|
2023
|
+
this._initOptionGroup(this._getHelpOption());
|
|
2024
|
+
}
|
|
1762
2025
|
} else {
|
|
1763
2026
|
this._helpOption = null;
|
|
1764
2027
|
}
|
|
1765
2028
|
return this;
|
|
1766
2029
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
2030
|
+
this._helpOption = this.createOption(flags ?? "-h, --help", description ?? "display help for command");
|
|
2031
|
+
if (flags || description)
|
|
2032
|
+
this._initOptionGroup(this._helpOption);
|
|
1770
2033
|
return this;
|
|
1771
2034
|
}
|
|
1772
2035
|
_getHelpOption() {
|
|
@@ -1777,11 +2040,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1777
2040
|
}
|
|
1778
2041
|
addHelpOption(option) {
|
|
1779
2042
|
this._helpOption = option;
|
|
2043
|
+
this._initOptionGroup(option);
|
|
1780
2044
|
return this;
|
|
1781
2045
|
}
|
|
1782
2046
|
help(contextOptions) {
|
|
1783
2047
|
this.outputHelp(contextOptions);
|
|
1784
|
-
let exitCode = process2.exitCode
|
|
2048
|
+
let exitCode = Number(process2.exitCode ?? 0);
|
|
1785
2049
|
if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
|
|
1786
2050
|
exitCode = 1;
|
|
1787
2051
|
}
|
|
@@ -1846,7 +2110,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1846
2110
|
return arg;
|
|
1847
2111
|
});
|
|
1848
2112
|
}
|
|
2113
|
+
function useColor() {
|
|
2114
|
+
if (process2.env.NO_COLOR || process2.env.FORCE_COLOR === "0" || process2.env.FORCE_COLOR === "false")
|
|
2115
|
+
return false;
|
|
2116
|
+
if (process2.env.FORCE_COLOR || process2.env.CLICOLOR_FORCE !== undefined)
|
|
2117
|
+
return true;
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
1849
2120
|
exports.Command = Command;
|
|
2121
|
+
exports.useColor = useColor;
|
|
1850
2122
|
});
|
|
1851
2123
|
|
|
1852
2124
|
// node_modules/commander/index.js
|
|
@@ -13151,7 +13423,7 @@ var {
|
|
|
13151
13423
|
// package.json
|
|
13152
13424
|
var package_default = {
|
|
13153
13425
|
name: "postgresai",
|
|
13154
|
-
version: "0.15.0-dev.
|
|
13426
|
+
version: "0.15.0-dev.9",
|
|
13155
13427
|
description: "postgres_ai CLI",
|
|
13156
13428
|
license: "Apache-2.0",
|
|
13157
13429
|
private: false,
|
|
@@ -13179,7 +13451,7 @@ var package_default = {
|
|
|
13179
13451
|
"embed-metrics": "bun run scripts/embed-metrics.ts",
|
|
13180
13452
|
"embed-checkup-dictionary": "bun run scripts/embed-checkup-dictionary.ts",
|
|
13181
13453
|
"embed-all": "bun run embed-metrics && bun run embed-checkup-dictionary",
|
|
13182
|
-
build: `bun run embed-all && bun build ./bin/postgres-ai.ts --outdir ./dist/bin --target node && node -e "const fs=require('fs');const f='./dist/bin/postgres-ai.js';fs.writeFileSync(f,fs.readFileSync(f,'utf8').replace('#!/usr/bin/env bun','#!/usr/bin/env node'))" && cp -r ./sql ./dist/sql`,
|
|
13454
|
+
build: `bun run embed-all && bun build ./bin/postgres-ai.ts --outdir ./dist/bin --target node && node -e "const fs=require('fs');const f='./dist/bin/postgres-ai.js';fs.writeFileSync(f,fs.readFileSync(f,'utf8').replace('#!/usr/bin/env bun','#!/usr/bin/env node'))" && cp -r ./sql ./dist/sql && cp ../instances.demo.yml ./instances.demo.yml`,
|
|
13183
13455
|
prepublishOnly: "npm run build",
|
|
13184
13456
|
start: "bun ./bin/postgres-ai.ts --help",
|
|
13185
13457
|
"start:node": "node ./dist/bin/postgres-ai.js --help",
|
|
@@ -13192,7 +13464,7 @@ var package_default = {
|
|
|
13192
13464
|
},
|
|
13193
13465
|
dependencies: {
|
|
13194
13466
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
13195
|
-
commander: "^
|
|
13467
|
+
commander: "^14.0.3",
|
|
13196
13468
|
"js-yaml": "^4.1.0",
|
|
13197
13469
|
pg: "^8.16.3"
|
|
13198
13470
|
},
|
|
@@ -13202,7 +13474,7 @@ var package_default = {
|
|
|
13202
13474
|
"@types/pg": "^8.15.6",
|
|
13203
13475
|
ajv: "^8.17.1",
|
|
13204
13476
|
"ajv-formats": "^3.0.1",
|
|
13205
|
-
typescript: "^
|
|
13477
|
+
typescript: "^6.0.2"
|
|
13206
13478
|
},
|
|
13207
13479
|
publishConfig: {
|
|
13208
13480
|
access: "public"
|
|
@@ -15982,7 +16254,7 @@ var Result = import_lib.default.Result;
|
|
|
15982
16254
|
var TypeOverrides = import_lib.default.TypeOverrides;
|
|
15983
16255
|
var defaults = import_lib.default.defaults;
|
|
15984
16256
|
// package.json
|
|
15985
|
-
var version = "0.15.0-dev.
|
|
16257
|
+
var version = "0.15.0-dev.9";
|
|
15986
16258
|
var package_default2 = {
|
|
15987
16259
|
name: "postgresai",
|
|
15988
16260
|
version,
|
|
@@ -16013,7 +16285,7 @@ var package_default2 = {
|
|
|
16013
16285
|
"embed-metrics": "bun run scripts/embed-metrics.ts",
|
|
16014
16286
|
"embed-checkup-dictionary": "bun run scripts/embed-checkup-dictionary.ts",
|
|
16015
16287
|
"embed-all": "bun run embed-metrics && bun run embed-checkup-dictionary",
|
|
16016
|
-
build: `bun run embed-all && bun build ./bin/postgres-ai.ts --outdir ./dist/bin --target node && node -e "const fs=require('fs');const f='./dist/bin/postgres-ai.js';fs.writeFileSync(f,fs.readFileSync(f,'utf8').replace('#!/usr/bin/env bun','#!/usr/bin/env node'))" && cp -r ./sql ./dist/sql`,
|
|
16288
|
+
build: `bun run embed-all && bun build ./bin/postgres-ai.ts --outdir ./dist/bin --target node && node -e "const fs=require('fs');const f='./dist/bin/postgres-ai.js';fs.writeFileSync(f,fs.readFileSync(f,'utf8').replace('#!/usr/bin/env bun','#!/usr/bin/env node'))" && cp -r ./sql ./dist/sql && cp ../instances.demo.yml ./instances.demo.yml`,
|
|
16017
16289
|
prepublishOnly: "npm run build",
|
|
16018
16290
|
start: "bun ./bin/postgres-ai.ts --help",
|
|
16019
16291
|
"start:node": "node ./dist/bin/postgres-ai.js --help",
|
|
@@ -16026,7 +16298,7 @@ var package_default2 = {
|
|
|
16026
16298
|
},
|
|
16027
16299
|
dependencies: {
|
|
16028
16300
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
16029
|
-
commander: "^
|
|
16301
|
+
commander: "^14.0.3",
|
|
16030
16302
|
"js-yaml": "^4.1.0",
|
|
16031
16303
|
pg: "^8.16.3"
|
|
16032
16304
|
},
|
|
@@ -16036,7 +16308,7 @@ var package_default2 = {
|
|
|
16036
16308
|
"@types/pg": "^8.15.6",
|
|
16037
16309
|
ajv: "^8.17.1",
|
|
16038
16310
|
"ajv-formats": "^3.0.1",
|
|
16039
|
-
typescript: "^
|
|
16311
|
+
typescript: "^6.0.2"
|
|
16040
16312
|
},
|
|
16041
16313
|
publishConfig: {
|
|
16042
16314
|
access: "public"
|
|
@@ -25439,8 +25711,9 @@ function isSslNegotiationError(err) {
|
|
|
25439
25711
|
}
|
|
25440
25712
|
async function connectWithSslFallback(ClientClass, adminConn, verbose) {
|
|
25441
25713
|
const tryConnect = async (config2) => {
|
|
25442
|
-
const client = new ClientClass(config2);
|
|
25714
|
+
const client = new ClientClass({ ...config2, connectionTimeoutMillis: 1e4 });
|
|
25443
25715
|
await client.connect();
|
|
25716
|
+
await client.query("SET statement_timeout = '30s'");
|
|
25444
25717
|
return client;
|
|
25445
25718
|
};
|
|
25446
25719
|
if (!adminConn.sslFallbackEnabled) {
|
|
@@ -25909,7 +26182,12 @@ async function verifyInitSetup(params) {
|
|
|
25909
26182
|
if (!schemaExistsRes.rows?.[0]?.ok) {
|
|
25910
26183
|
missingRequired.push("USAGE on schema postgres_ai");
|
|
25911
26184
|
}
|
|
25912
|
-
const viewExistsRes = await params.client.query(
|
|
26185
|
+
const viewExistsRes = await params.client.query(`
|
|
26186
|
+
select case
|
|
26187
|
+
when not has_schema_privilege(current_user, 'postgres_ai', 'USAGE') then null
|
|
26188
|
+
else to_regclass('postgres_ai.pg_statistic') is not null
|
|
26189
|
+
end as ok
|
|
26190
|
+
`);
|
|
25913
26191
|
if (!viewExistsRes.rows?.[0]?.ok) {
|
|
25914
26192
|
missingRequired.push("view postgres_ai.pg_statistic exists");
|
|
25915
26193
|
} else {
|
|
@@ -25993,6 +26271,119 @@ async function verifyInitSetup(params) {
|
|
|
25993
26271
|
} catch {}
|
|
25994
26272
|
}
|
|
25995
26273
|
}
|
|
26274
|
+
async function checkCurrentUserPermissions(client) {
|
|
26275
|
+
const sql = `
|
|
26276
|
+
with permission_checks as (
|
|
26277
|
+
select
|
|
26278
|
+
format('connect on database %I', current_database()) as permission_name,
|
|
26279
|
+
'required' as status,
|
|
26280
|
+
has_database_privilege(current_user, current_database(), 'connect') as granted
|
|
26281
|
+
|
|
26282
|
+
union all
|
|
26283
|
+
|
|
26284
|
+
select
|
|
26285
|
+
'pg_monitor role membership' as permission_name,
|
|
26286
|
+
'required' as status,
|
|
26287
|
+
-- CASE guarantees evaluation order: pg_has_role() is only called if the
|
|
26288
|
+
-- pg_monitor role exists, avoiding ERROR on PostgreSQL < 10 or when dropped.
|
|
26289
|
+
case
|
|
26290
|
+
when not exists (select from pg_roles where rolname = 'pg_monitor')
|
|
26291
|
+
then false
|
|
26292
|
+
else pg_has_role(current_user, 'pg_monitor', 'member')
|
|
26293
|
+
end as granted
|
|
26294
|
+
|
|
26295
|
+
union all
|
|
26296
|
+
|
|
26297
|
+
select
|
|
26298
|
+
'select on pg_catalog.pg_index' as permission_name,
|
|
26299
|
+
'required' as status,
|
|
26300
|
+
has_table_privilege(current_user, 'pg_catalog.pg_index', 'select') as granted
|
|
26301
|
+
|
|
26302
|
+
union all
|
|
26303
|
+
|
|
26304
|
+
select
|
|
26305
|
+
'postgres_ai.pg_statistic view exists' as permission_name,
|
|
26306
|
+
'optional' as status,
|
|
26307
|
+
case
|
|
26308
|
+
when not has_schema_privilege(current_user, 'postgres_ai', 'USAGE') then null
|
|
26309
|
+
else to_regclass('postgres_ai.pg_statistic') is not null
|
|
26310
|
+
end as granted
|
|
26311
|
+
|
|
26312
|
+
union all
|
|
26313
|
+
|
|
26314
|
+
select
|
|
26315
|
+
'select on postgres_ai.pg_statistic' as permission_name,
|
|
26316
|
+
'optional' as status,
|
|
26317
|
+
case
|
|
26318
|
+
when not has_schema_privilege(current_user, 'postgres_ai', 'USAGE') then null
|
|
26319
|
+
when to_regclass('postgres_ai.pg_statistic') is null then null
|
|
26320
|
+
else has_table_privilege(current_user, 'postgres_ai.pg_statistic', 'select')
|
|
26321
|
+
end as granted
|
|
26322
|
+
)
|
|
26323
|
+
select
|
|
26324
|
+
permission_name,
|
|
26325
|
+
status,
|
|
26326
|
+
granted,
|
|
26327
|
+
case
|
|
26328
|
+
when status = 'required' and not coalesce(granted, false) then
|
|
26329
|
+
case
|
|
26330
|
+
when permission_name like 'connect%' then
|
|
26331
|
+
format('grant connect on database %I to %I;', current_database(), current_user)
|
|
26332
|
+
when permission_name = 'pg_monitor role membership' then
|
|
26333
|
+
format('grant pg_monitor to %I;', current_user)
|
|
26334
|
+
when permission_name like 'select on pg_catalog.pg_index' then
|
|
26335
|
+
format('grant select on pg_catalog.pg_index to %I;', current_user)
|
|
26336
|
+
end
|
|
26337
|
+
when permission_name = 'postgres_ai.pg_statistic view exists' and granted = false then
|
|
26338
|
+
'-- create postgres_ai.pg_statistic view (see setup script)'
|
|
26339
|
+
when permission_name = 'select on postgres_ai.pg_statistic' and granted = false then
|
|
26340
|
+
format('grant select on postgres_ai.pg_statistic to %I;', current_user)
|
|
26341
|
+
else null
|
|
26342
|
+
end as fix_command
|
|
26343
|
+
from permission_checks
|
|
26344
|
+
order by
|
|
26345
|
+
case status when 'required' then 1 else 2 end,
|
|
26346
|
+
permission_name;
|
|
26347
|
+
`;
|
|
26348
|
+
const res = await client.query(sql);
|
|
26349
|
+
const rows = res.rows;
|
|
26350
|
+
const missingRequired = rows.filter((r) => r.status === "required" && r.granted !== true);
|
|
26351
|
+
const missingOptional = rows.filter((r) => r.status === "optional" && r.granted === false);
|
|
26352
|
+
return {
|
|
26353
|
+
ok: missingRequired.length === 0,
|
|
26354
|
+
rows,
|
|
26355
|
+
missingRequired,
|
|
26356
|
+
missingOptional
|
|
26357
|
+
};
|
|
26358
|
+
}
|
|
26359
|
+
function formatPermissionCheckMessages(result) {
|
|
26360
|
+
const warnings = [];
|
|
26361
|
+
const errors3 = [];
|
|
26362
|
+
for (const row of result.missingOptional) {
|
|
26363
|
+
const fix = row.fix_command ? ` Fix: ${row.fix_command}` : "";
|
|
26364
|
+
warnings.push(`Warning: optional permission missing — ${row.permission_name}.${fix}`);
|
|
26365
|
+
}
|
|
26366
|
+
if (!result.ok) {
|
|
26367
|
+
errors3.push(`Error: the database user is missing required permissions.
|
|
26368
|
+
`);
|
|
26369
|
+
errors3.push("Missing permissions:");
|
|
26370
|
+
for (const row of result.missingRequired) {
|
|
26371
|
+
errors3.push(` - ${row.permission_name}`);
|
|
26372
|
+
}
|
|
26373
|
+
const fixes = result.missingRequired.map((r) => r.fix_command).filter(Boolean);
|
|
26374
|
+
if (fixes.length > 0) {
|
|
26375
|
+
errors3.push(`
|
|
26376
|
+
To fix, run the following as a superuser:
|
|
26377
|
+
`);
|
|
26378
|
+
for (const fix of fixes) {
|
|
26379
|
+
errors3.push(` ${fix}`);
|
|
26380
|
+
}
|
|
26381
|
+
}
|
|
26382
|
+
errors3.push(`
|
|
26383
|
+
Alternatively, run 'postgresai prepare-db' to set up permissions automatically.`);
|
|
26384
|
+
}
|
|
26385
|
+
return { failed: !result.ok, warnings, errors: errors3 };
|
|
26386
|
+
}
|
|
25996
26387
|
|
|
25997
26388
|
// lib/supabase.ts
|
|
25998
26389
|
var SUPABASE_API_BASE = "https://api.supabase.com";
|
|
@@ -26155,6 +26546,9 @@ class SupabaseClient {
|
|
|
26155
26546
|
}
|
|
26156
26547
|
}
|
|
26157
26548
|
async function fetchPoolerDatabaseUrl(config2, username) {
|
|
26549
|
+
if (!isValidProjectRef(config2.projectRef)) {
|
|
26550
|
+
throw new Error(`Invalid Supabase project reference format: "${config2.projectRef}". Expected 10-30 alphanumeric characters.`);
|
|
26551
|
+
}
|
|
26158
26552
|
const url = `${SUPABASE_API_BASE}/v1/projects/${encodeURIComponent(config2.projectRef)}/config/database/pooler`;
|
|
26159
26553
|
const suffix = `.${config2.projectRef}`;
|
|
26160
26554
|
const effectiveUsername = username.endsWith(suffix) ? username : `${username}${suffix}`;
|
|
@@ -26327,7 +26721,10 @@ async function verifyInitSetupViaSupabase(params) {
|
|
|
26327
26721
|
missingRequired.push("USAGE on schema postgres_ai");
|
|
26328
26722
|
}
|
|
26329
26723
|
}
|
|
26330
|
-
const viewExistsRes = await params.client.query(
|
|
26724
|
+
const viewExistsRes = await params.client.query(`SELECT CASE
|
|
26725
|
+
WHEN NOT has_schema_privilege(current_user, 'postgres_ai', 'USAGE') THEN NULL
|
|
26726
|
+
ELSE to_regclass('postgres_ai.pg_statistic') IS NOT NULL
|
|
26727
|
+
END as ok`, true);
|
|
26331
26728
|
if (!viewExistsRes.rows?.[0]?.ok) {
|
|
26332
26729
|
missingRequired.push("view postgres_ai.pg_statistic exists");
|
|
26333
26730
|
} else {
|
|
@@ -27472,6 +27869,65 @@ limit 1000
|
|
|
27472
27869
|
},
|
|
27473
27870
|
gauges: ["real_size_mib", "table_size_mib", "extra_size", "extra_pct", "fillfactor", "bloat_size", "bloat_pct", "is_na", "reltuples"],
|
|
27474
27871
|
statement_timeout_seconds: 15
|
|
27872
|
+
},
|
|
27873
|
+
pg_stat_io: {
|
|
27874
|
+
description: "Collects I/O statistics from the PostgreSQL `pg_stat_io` view (available in PostgreSQL 16+). Provides insights into read and write operations by backend type, including the number of operations, MiB transferred (divided by 1048576), and time spent on I/O. This metric is essential for monitoring I/O performance and identifying potential bottlenecks in disk operations. The data is aggregated by backend type with a total row included via ROLLUP.",
|
|
27875
|
+
sqls: {
|
|
27876
|
+
11: "; -- pg_stat_io only available in PostgreSQL 16+",
|
|
27877
|
+
16: `select /* pgwatch_generated */
|
|
27878
|
+
(extract(epoch from now()) * 1e9)::int8 as epoch_ns,
|
|
27879
|
+
current_database() as tag_datname,
|
|
27880
|
+
coalesce(backend_type, 'total') as tag_backend_type,
|
|
27881
|
+
sum(coalesce(reads, 0))::int8 as reads,
|
|
27882
|
+
(sum(coalesce(reads, 0) * op_bytes) / 1048576.0)::int8 as read_bytes_mb,
|
|
27883
|
+
sum(coalesce(read_time, 0))::int8 as read_time_ms,
|
|
27884
|
+
sum(coalesce(writes, 0))::int8 as writes,
|
|
27885
|
+
(sum(coalesce(writes, 0) * op_bytes) / 1048576.0)::int8 as write_bytes_mb,
|
|
27886
|
+
sum(coalesce(write_time, 0))::int8 as write_time_ms,
|
|
27887
|
+
sum(coalesce(writebacks, 0))::int8 as writebacks,
|
|
27888
|
+
(sum(coalesce(writebacks, 0) * op_bytes) / 1048576.0)::int8 as writeback_bytes_mb,
|
|
27889
|
+
sum(coalesce(writeback_time, 0))::int8 as writeback_time_ms,
|
|
27890
|
+
sum(coalesce(fsyncs, 0))::int8 as fsyncs,
|
|
27891
|
+
sum(coalesce(fsync_time, 0))::int8 as fsync_time_ms,
|
|
27892
|
+
sum(coalesce(extends, 0))::int8 as extends,
|
|
27893
|
+
(sum(coalesce(extends, 0) * op_bytes) / 1048576.0)::int8 as extend_bytes_mb,
|
|
27894
|
+
sum(coalesce(hits, 0))::int8 as hits,
|
|
27895
|
+
sum(coalesce(evictions, 0))::int8 as evictions,
|
|
27896
|
+
sum(coalesce(reuses, 0))::int8 as reuses,
|
|
27897
|
+
max(extract(epoch from now() - stats_reset)::int) as stats_reset_s
|
|
27898
|
+
from
|
|
27899
|
+
pg_stat_io
|
|
27900
|
+
group by
|
|
27901
|
+
rollup (backend_type)`,
|
|
27902
|
+
18: `select /* pgwatch_generated */
|
|
27903
|
+
(extract(epoch from now()) * 1e9)::int8 as epoch_ns,
|
|
27904
|
+
current_database() as tag_datname,
|
|
27905
|
+
coalesce(backend_type, 'total') as tag_backend_type,
|
|
27906
|
+
sum(coalesce(reads, 0))::int8 as reads,
|
|
27907
|
+
(sum(coalesce(read_bytes, 0)) / 1048576.0)::int8 as read_bytes_mb,
|
|
27908
|
+
sum(coalesce(read_time, 0))::int8 as read_time_ms,
|
|
27909
|
+
sum(coalesce(writes, 0))::int8 as writes,
|
|
27910
|
+
(sum(coalesce(write_bytes, 0)) / 1048576.0)::int8 as write_bytes_mb,
|
|
27911
|
+
sum(coalesce(write_time, 0))::int8 as write_time_ms,
|
|
27912
|
+
sum(coalesce(writebacks, 0))::int8 as writebacks,
|
|
27913
|
+
-- PostgreSQL 18 has no writeback_bytes column; rows with NULL op_bytes contribute zero by design.
|
|
27914
|
+
(sum(coalesce(writebacks, 0) * coalesce(op_bytes, 0)) / 1048576.0)::int8 as writeback_bytes_mb,
|
|
27915
|
+
sum(coalesce(writeback_time, 0))::int8 as writeback_time_ms,
|
|
27916
|
+
sum(coalesce(fsyncs, 0))::int8 as fsyncs,
|
|
27917
|
+
sum(coalesce(fsync_time, 0))::int8 as fsync_time_ms,
|
|
27918
|
+
sum(coalesce(extends, 0))::int8 as extends,
|
|
27919
|
+
(sum(coalesce(extend_bytes, 0)) / 1048576.0)::int8 as extend_bytes_mb,
|
|
27920
|
+
sum(coalesce(hits, 0))::int8 as hits,
|
|
27921
|
+
sum(coalesce(evictions, 0))::int8 as evictions,
|
|
27922
|
+
sum(coalesce(reuses, 0))::int8 as reuses,
|
|
27923
|
+
max(extract(epoch from now() - stats_reset)::int) as stats_reset_s
|
|
27924
|
+
from
|
|
27925
|
+
pg_stat_io
|
|
27926
|
+
group by
|
|
27927
|
+
rollup (backend_type)`
|
|
27928
|
+
},
|
|
27929
|
+
gauges: ["*"],
|
|
27930
|
+
statement_timeout_seconds: 15
|
|
27475
27931
|
}
|
|
27476
27932
|
};
|
|
27477
27933
|
|
|
@@ -27497,7 +27953,8 @@ var METRIC_NAMES = {
|
|
|
27497
27953
|
settings: "settings",
|
|
27498
27954
|
dbStats: "db_stats",
|
|
27499
27955
|
dbSize: "db_size",
|
|
27500
|
-
statsReset: "stats_reset"
|
|
27956
|
+
statsReset: "stats_reset",
|
|
27957
|
+
I001: "pg_stat_io"
|
|
27501
27958
|
};
|
|
27502
27959
|
function transformMetricRow(row) {
|
|
27503
27960
|
const result = {};
|
|
@@ -29044,6 +29501,140 @@ async function generateG003(client, nodeName) {
|
|
|
29044
29501
|
};
|
|
29045
29502
|
return report;
|
|
29046
29503
|
}
|
|
29504
|
+
async function getIOStatistics(client, pgMajorVersion = 0, metricSqlOverride) {
|
|
29505
|
+
if (pgMajorVersion < 16) {
|
|
29506
|
+
return [];
|
|
29507
|
+
}
|
|
29508
|
+
try {
|
|
29509
|
+
const sql = metricSqlOverride ?? getMetricSql(METRIC_NAMES.I001, pgMajorVersion);
|
|
29510
|
+
if (!sql || sql.trim().startsWith(";")) {
|
|
29511
|
+
return [];
|
|
29512
|
+
}
|
|
29513
|
+
const result = await client.query(sql);
|
|
29514
|
+
return result.rows.map((row) => {
|
|
29515
|
+
const transformed = transformMetricRow(row);
|
|
29516
|
+
return {
|
|
29517
|
+
backend_type: String(transformed.backend_type || "unknown"),
|
|
29518
|
+
reads: parseInt(String(transformed.reads || 0), 10),
|
|
29519
|
+
read_bytes_mb: parseInt(String(transformed.read_bytes_mb || 0), 10),
|
|
29520
|
+
read_time_ms: parseInt(String(transformed.read_time_ms || 0), 10),
|
|
29521
|
+
writes: parseInt(String(transformed.writes || 0), 10),
|
|
29522
|
+
write_bytes_mb: parseInt(String(transformed.write_bytes_mb || 0), 10),
|
|
29523
|
+
write_time_ms: parseInt(String(transformed.write_time_ms || 0), 10),
|
|
29524
|
+
writebacks: parseInt(String(transformed.writebacks || 0), 10),
|
|
29525
|
+
writeback_bytes_mb: parseInt(String(transformed.writeback_bytes_mb || 0), 10),
|
|
29526
|
+
writeback_time_ms: parseInt(String(transformed.writeback_time_ms || 0), 10),
|
|
29527
|
+
fsyncs: parseInt(String(transformed.fsyncs || 0), 10),
|
|
29528
|
+
fsync_time_ms: parseInt(String(transformed.fsync_time_ms || 0), 10),
|
|
29529
|
+
extends: parseInt(String(transformed.extends || 0), 10),
|
|
29530
|
+
extend_bytes_mb: parseInt(String(transformed.extend_bytes_mb || 0), 10),
|
|
29531
|
+
hits: parseInt(String(transformed.hits || 0), 10),
|
|
29532
|
+
evictions: parseInt(String(transformed.evictions || 0), 10),
|
|
29533
|
+
reuses: parseInt(String(transformed.reuses || 0), 10)
|
|
29534
|
+
};
|
|
29535
|
+
});
|
|
29536
|
+
} catch (err) {
|
|
29537
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
29538
|
+
console.log(`[I001] Error fetching I/O statistics: ${errorMsg}`);
|
|
29539
|
+
return [];
|
|
29540
|
+
}
|
|
29541
|
+
}
|
|
29542
|
+
async function generateI001(client, nodeName) {
|
|
29543
|
+
const report = createBaseReport("I001", "I/O statistics (pg_stat_io)", nodeName);
|
|
29544
|
+
const postgresVersion = await getPostgresVersion(client);
|
|
29545
|
+
const parsedPgMajorVersion = parseInt(postgresVersion.server_major_ver, 10);
|
|
29546
|
+
const pgMajorVersion = Number.isFinite(parsedPgMajorVersion) ? parsedPgMajorVersion : 0;
|
|
29547
|
+
if (pgMajorVersion < 16) {
|
|
29548
|
+
report.results[nodeName] = {
|
|
29549
|
+
data: {
|
|
29550
|
+
available: false,
|
|
29551
|
+
min_version_required: "16",
|
|
29552
|
+
by_backend_type: [],
|
|
29553
|
+
analysis: {
|
|
29554
|
+
total_read_mb: 0,
|
|
29555
|
+
total_write_mb: 0,
|
|
29556
|
+
total_io_time_ms: 0,
|
|
29557
|
+
read_hit_ratio_pct: 0,
|
|
29558
|
+
avg_read_time_ms: null,
|
|
29559
|
+
avg_write_time_ms: null
|
|
29560
|
+
},
|
|
29561
|
+
stats_reset_s: null
|
|
29562
|
+
},
|
|
29563
|
+
postgres_version: postgresVersion
|
|
29564
|
+
};
|
|
29565
|
+
return report;
|
|
29566
|
+
}
|
|
29567
|
+
const ioStats = await getIOStatistics(client, pgMajorVersion);
|
|
29568
|
+
ioStats.sort((a, b) => {
|
|
29569
|
+
if (a.backend_type === "total")
|
|
29570
|
+
return -1;
|
|
29571
|
+
if (b.backend_type === "total")
|
|
29572
|
+
return 1;
|
|
29573
|
+
return a.backend_type.localeCompare(b.backend_type);
|
|
29574
|
+
});
|
|
29575
|
+
let totalStats = ioStats.find((s) => s.backend_type === "total");
|
|
29576
|
+
if (!totalStats && ioStats.length > 0) {
|
|
29577
|
+
totalStats = {
|
|
29578
|
+
backend_type: "total",
|
|
29579
|
+
reads: ioStats.reduce((sum, s) => sum + s.reads, 0),
|
|
29580
|
+
read_bytes_mb: ioStats.reduce((sum, s) => sum + s.read_bytes_mb, 0),
|
|
29581
|
+
read_time_ms: ioStats.reduce((sum, s) => sum + s.read_time_ms, 0),
|
|
29582
|
+
writes: ioStats.reduce((sum, s) => sum + s.writes, 0),
|
|
29583
|
+
write_bytes_mb: ioStats.reduce((sum, s) => sum + s.write_bytes_mb, 0),
|
|
29584
|
+
write_time_ms: ioStats.reduce((sum, s) => sum + s.write_time_ms, 0),
|
|
29585
|
+
writebacks: ioStats.reduce((sum, s) => sum + s.writebacks, 0),
|
|
29586
|
+
writeback_bytes_mb: ioStats.reduce((sum, s) => sum + s.writeback_bytes_mb, 0),
|
|
29587
|
+
writeback_time_ms: ioStats.reduce((sum, s) => sum + s.writeback_time_ms, 0),
|
|
29588
|
+
fsyncs: ioStats.reduce((sum, s) => sum + s.fsyncs, 0),
|
|
29589
|
+
fsync_time_ms: ioStats.reduce((sum, s) => sum + s.fsync_time_ms, 0),
|
|
29590
|
+
extends: ioStats.reduce((sum, s) => sum + (s.extends || 0), 0),
|
|
29591
|
+
extend_bytes_mb: ioStats.reduce((sum, s) => sum + (s.extend_bytes_mb || 0), 0),
|
|
29592
|
+
hits: ioStats.reduce((sum, s) => sum + s.hits, 0),
|
|
29593
|
+
evictions: ioStats.reduce((sum, s) => sum + s.evictions, 0),
|
|
29594
|
+
reuses: ioStats.reduce((sum, s) => sum + s.reuses, 0)
|
|
29595
|
+
};
|
|
29596
|
+
}
|
|
29597
|
+
const totalReadMb = totalStats?.read_bytes_mb || 0;
|
|
29598
|
+
const totalWriteMb = totalStats?.write_bytes_mb || 0;
|
|
29599
|
+
const totalReadTime = totalStats?.read_time_ms || 0;
|
|
29600
|
+
const totalWriteTime = totalStats?.write_time_ms || 0;
|
|
29601
|
+
const totalIoTimeMs = totalReadTime + totalWriteTime;
|
|
29602
|
+
const totalReads = totalStats?.reads || 0;
|
|
29603
|
+
const totalWrites = totalStats?.writes || 0;
|
|
29604
|
+
const totalHits = totalStats?.hits || 0;
|
|
29605
|
+
const totalRequests = totalHits + totalReads;
|
|
29606
|
+
const readHitRatioPct = totalRequests > 0 ? Math.round(totalHits / totalRequests * 1e4) / 100 : 0;
|
|
29607
|
+
const avgReadTimeMs = totalReads > 0 ? Math.round(totalReadTime / totalReads * 1000) / 1000 : null;
|
|
29608
|
+
const avgWriteTimeMs = totalWrites > 0 ? Math.round(totalWriteTime / totalWrites * 1000) / 1000 : null;
|
|
29609
|
+
let statsResetS = null;
|
|
29610
|
+
try {
|
|
29611
|
+
const resetResult = await client.query(`
|
|
29612
|
+
select max(extract(epoch from now() - stats_reset)::int) as stats_reset_s
|
|
29613
|
+
from pg_stat_io
|
|
29614
|
+
`);
|
|
29615
|
+
if (resetResult.rows.length > 0 && resetResult.rows[0].stats_reset_s !== null) {
|
|
29616
|
+
const parsedStatsResetS = parseInt(resetResult.rows[0].stats_reset_s, 10);
|
|
29617
|
+
statsResetS = Number.isFinite(parsedStatsResetS) ? parsedStatsResetS : null;
|
|
29618
|
+
}
|
|
29619
|
+
} catch (err) {}
|
|
29620
|
+
report.results[nodeName] = {
|
|
29621
|
+
data: {
|
|
29622
|
+
available: ioStats.length > 0,
|
|
29623
|
+
by_backend_type: ioStats,
|
|
29624
|
+
analysis: {
|
|
29625
|
+
total_read_mb: totalReadMb,
|
|
29626
|
+
total_write_mb: totalWriteMb,
|
|
29627
|
+
total_io_time_ms: totalIoTimeMs,
|
|
29628
|
+
read_hit_ratio_pct: readHitRatioPct,
|
|
29629
|
+
avg_read_time_ms: avgReadTimeMs,
|
|
29630
|
+
avg_write_time_ms: avgWriteTimeMs
|
|
29631
|
+
},
|
|
29632
|
+
stats_reset_s: statsResetS
|
|
29633
|
+
},
|
|
29634
|
+
postgres_version: postgresVersion
|
|
29635
|
+
};
|
|
29636
|
+
return report;
|
|
29637
|
+
}
|
|
29047
29638
|
var REPORT_GENERATORS = {
|
|
29048
29639
|
A002: generateA002,
|
|
29049
29640
|
A003: generateA003,
|
|
@@ -29059,7 +29650,8 @@ var REPORT_GENERATORS = {
|
|
|
29059
29650
|
G003: generateG003,
|
|
29060
29651
|
H001: generateH001,
|
|
29061
29652
|
H002: generateH002,
|
|
29062
|
-
H004: generateH004
|
|
29653
|
+
H004: generateH004,
|
|
29654
|
+
I001: generateI001
|
|
29063
29655
|
};
|
|
29064
29656
|
var CHECK_INFO = (() => {
|
|
29065
29657
|
const fullMap = buildCheckInfoMap();
|
|
@@ -29095,6 +29687,7 @@ function getCheckupEntry(code) {
|
|
|
29095
29687
|
}
|
|
29096
29688
|
|
|
29097
29689
|
// lib/checkup-api.ts
|
|
29690
|
+
import * as http2 from "http";
|
|
29098
29691
|
import * as https from "https";
|
|
29099
29692
|
import { URL as URL3 } from "url";
|
|
29100
29693
|
var DEFAULT_RETRY_CONFIG = {
|
|
@@ -29224,7 +29817,15 @@ async function postRpc(params) {
|
|
|
29224
29817
|
clearTimeout(timeoutId);
|
|
29225
29818
|
resolve6(value);
|
|
29226
29819
|
};
|
|
29227
|
-
|
|
29820
|
+
if (url.protocol === "http:") {
|
|
29821
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
29822
|
+
const isLoopback = ["localhost", "127.0.0.1", "::1"].includes(hostname);
|
|
29823
|
+
if (!isLoopback && process.env.CHECKUP_ALLOW_HTTP !== "1") {
|
|
29824
|
+
throw new Error(`Refusing to send API key over plaintext HTTP to '${url.host}'. ` + `Use https://, a loopback hostname, or set CHECKUP_ALLOW_HTTP=1.`);
|
|
29825
|
+
}
|
|
29826
|
+
}
|
|
29827
|
+
const transport = url.protocol === "http:" ? http2 : https;
|
|
29828
|
+
const req = transport.request(url, {
|
|
29228
29829
|
method: "POST",
|
|
29229
29830
|
headers,
|
|
29230
29831
|
signal: controller.signal
|
|
@@ -29594,18 +30195,13 @@ function closeReadline() {
|
|
|
29594
30195
|
rl = null;
|
|
29595
30196
|
}
|
|
29596
30197
|
}
|
|
29597
|
-
|
|
29598
|
-
|
|
29599
|
-
|
|
29600
|
-
|
|
29601
|
-
|
|
29602
|
-
|
|
29603
|
-
|
|
29604
|
-
} else {
|
|
29605
|
-
resolve7({ stdout, stderr });
|
|
29606
|
-
}
|
|
29607
|
-
});
|
|
29608
|
-
});
|
|
30198
|
+
function stripMatchingQuotes(value) {
|
|
30199
|
+
const trimmed = value.trim();
|
|
30200
|
+
const quote = trimmed[0];
|
|
30201
|
+
if (trimmed.length >= 2 && (quote === '"' || quote === "'") && trimmed.endsWith(quote)) {
|
|
30202
|
+
return trimmed.slice(1, -1);
|
|
30203
|
+
}
|
|
30204
|
+
return trimmed;
|
|
29609
30205
|
}
|
|
29610
30206
|
async function execFilePromise(file, args) {
|
|
29611
30207
|
return new Promise((resolve7, reject) => {
|
|
@@ -29891,7 +30487,7 @@ async function ensureDefaultMonitoringProject() {
|
|
|
29891
30487
|
throw new Error(`Failed to bootstrap docker-compose.yml: ${msg}`);
|
|
29892
30488
|
}
|
|
29893
30489
|
}
|
|
29894
|
-
if (fs6.existsSync(instancesFile) && fs6.
|
|
30490
|
+
if (fs6.existsSync(instancesFile) && fs6.lstatSync(instancesFile).isDirectory()) {
|
|
29895
30491
|
fs6.rmSync(instancesFile, { recursive: true, force: true });
|
|
29896
30492
|
}
|
|
29897
30493
|
if (!fs6.existsSync(instancesFile)) {
|
|
@@ -30963,6 +31559,20 @@ Usage: postgresai checkup ${checkId} postgresql://user@host:5432/dbname
|
|
|
30963
31559
|
spinner.update("Connecting to Postgres");
|
|
30964
31560
|
const connResult = await connectWithSslFallback(Client, adminConn);
|
|
30965
31561
|
client = connResult.client;
|
|
31562
|
+
spinner.update("Checking database permissions");
|
|
31563
|
+
const permCheck = await checkCurrentUserPermissions(client);
|
|
31564
|
+
const permMessages = formatPermissionCheckMessages(permCheck);
|
|
31565
|
+
for (const w of permMessages.warnings) {
|
|
31566
|
+
console.error(w);
|
|
31567
|
+
}
|
|
31568
|
+
if (permMessages.failed) {
|
|
31569
|
+
spinner.stop();
|
|
31570
|
+
for (const e of permMessages.errors) {
|
|
31571
|
+
console.error(e);
|
|
31572
|
+
}
|
|
31573
|
+
process.exitCode = 1;
|
|
31574
|
+
return;
|
|
31575
|
+
}
|
|
30966
31576
|
let reports;
|
|
30967
31577
|
if (checkId === "ALL") {
|
|
30968
31578
|
reports = await generateAllReports(client, opts.nodeName, (p) => {
|
|
@@ -31270,12 +31880,12 @@ async function runCompose(args, grafanaPassword) {
|
|
|
31270
31880
|
if (!env.VM_AUTH_USERNAME) {
|
|
31271
31881
|
const m = envContent.match(/^VM_AUTH_USERNAME=([^\r\n]+)/m);
|
|
31272
31882
|
if (m)
|
|
31273
|
-
env.VM_AUTH_USERNAME = m[1]
|
|
31883
|
+
env.VM_AUTH_USERNAME = stripMatchingQuotes(m[1]);
|
|
31274
31884
|
}
|
|
31275
31885
|
if (!env.VM_AUTH_PASSWORD) {
|
|
31276
31886
|
const m = envContent.match(/^VM_AUTH_PASSWORD=([^\r\n]+)/m);
|
|
31277
31887
|
if (m)
|
|
31278
|
-
env.VM_AUTH_PASSWORD = m[1]
|
|
31888
|
+
env.VM_AUTH_PASSWORD = stripMatchingQuotes(m[1]);
|
|
31279
31889
|
}
|
|
31280
31890
|
} catch (err) {
|
|
31281
31891
|
if (process.env.DEBUG) {
|
|
@@ -31322,6 +31932,9 @@ mon.command("local-install").description("install local monitoring stack (genera
|
|
|
31322
31932
|
const envFile = path6.resolve(projectDir, ".env");
|
|
31323
31933
|
let existingRegistry = null;
|
|
31324
31934
|
let existingPassword = null;
|
|
31935
|
+
let existingReplicatorPassword = null;
|
|
31936
|
+
let existingVmAuthUsername = null;
|
|
31937
|
+
let existingVmAuthPassword = null;
|
|
31325
31938
|
if (fs6.existsSync(envFile)) {
|
|
31326
31939
|
const existingEnv = fs6.readFileSync(envFile, "utf8");
|
|
31327
31940
|
const registryMatch = existingEnv.match(/^PGAI_REGISTRY=(.+)$/m);
|
|
@@ -31330,6 +31943,15 @@ mon.command("local-install").description("install local monitoring stack (genera
|
|
|
31330
31943
|
const pwdMatch = existingEnv.match(/^GF_SECURITY_ADMIN_PASSWORD=(.+)$/m);
|
|
31331
31944
|
if (pwdMatch)
|
|
31332
31945
|
existingPassword = pwdMatch[1].trim();
|
|
31946
|
+
const replicatorPwdMatch = existingEnv.match(/^REPLICATOR_PASSWORD=(.+)$/m);
|
|
31947
|
+
if (replicatorPwdMatch)
|
|
31948
|
+
existingReplicatorPassword = replicatorPwdMatch[1].trim();
|
|
31949
|
+
const vmAuthUserMatch = existingEnv.match(/^VM_AUTH_USERNAME=(.+)$/m);
|
|
31950
|
+
if (vmAuthUserMatch)
|
|
31951
|
+
existingVmAuthUsername = stripMatchingQuotes(vmAuthUserMatch[1]);
|
|
31952
|
+
const vmAuthPasswordMatch = existingEnv.match(/^VM_AUTH_PASSWORD=(.+)$/m);
|
|
31953
|
+
if (vmAuthPasswordMatch)
|
|
31954
|
+
existingVmAuthPassword = stripMatchingQuotes(vmAuthPasswordMatch[1]);
|
|
31333
31955
|
}
|
|
31334
31956
|
const imageTag = opts.tag || package_default.version;
|
|
31335
31957
|
const envLines = [`PGAI_TAG=${imageTag}`];
|
|
@@ -31339,6 +31961,9 @@ mon.command("local-install").description("install local monitoring stack (genera
|
|
|
31339
31961
|
if (existingPassword) {
|
|
31340
31962
|
envLines.push(`GF_SECURITY_ADMIN_PASSWORD=${existingPassword}`);
|
|
31341
31963
|
}
|
|
31964
|
+
envLines.push(`REPLICATOR_PASSWORD=${existingReplicatorPassword || crypto2.randomBytes(32).toString("hex")}`);
|
|
31965
|
+
envLines.push(`VM_AUTH_USERNAME=${existingVmAuthUsername || "vmauth"}`);
|
|
31966
|
+
envLines.push(`VM_AUTH_PASSWORD=${existingVmAuthPassword || crypto2.randomBytes(18).toString("base64")}`);
|
|
31342
31967
|
fs6.writeFileSync(envFile, envLines.join(`
|
|
31343
31968
|
`) + `
|
|
31344
31969
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -31462,18 +32087,23 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
|
|
|
31462
32087
|
console.log(`\u2713 Monitoring target '${instanceName}' added
|
|
31463
32088
|
`);
|
|
31464
32089
|
console.log("Testing connection to the added instance...");
|
|
31465
|
-
|
|
31466
|
-
|
|
31467
|
-
|
|
31468
|
-
|
|
31469
|
-
|
|
31470
|
-
|
|
32090
|
+
{
|
|
32091
|
+
let testClient = null;
|
|
32092
|
+
try {
|
|
32093
|
+
testClient = new Client({ connectionString: connStr, connectionTimeoutMillis: 1e4 });
|
|
32094
|
+
await testClient.connect();
|
|
32095
|
+
const result = await testClient.query("select version();");
|
|
32096
|
+
console.log("\u2713 Connection successful");
|
|
32097
|
+
console.log(`${result.rows[0].version}
|
|
31471
32098
|
`);
|
|
31472
|
-
|
|
31473
|
-
|
|
31474
|
-
|
|
31475
|
-
console.error(`\u2717 Connection failed: ${message}
|
|
32099
|
+
} catch (error2) {
|
|
32100
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
32101
|
+
console.error(`\u2717 Connection failed: ${message}
|
|
31476
32102
|
`);
|
|
32103
|
+
} finally {
|
|
32104
|
+
if (testClient)
|
|
32105
|
+
await testClient.end();
|
|
32106
|
+
}
|
|
31477
32107
|
}
|
|
31478
32108
|
} else if (opts.yes) {
|
|
31479
32109
|
console.log("Auto-yes mode: no database URL provided, skipping database setup");
|
|
@@ -31517,18 +32147,23 @@ You can provide either:`);
|
|
|
31517
32147
|
console.log(`\u2713 Monitoring target '${instanceName}' added
|
|
31518
32148
|
`);
|
|
31519
32149
|
console.log("Testing connection to the added instance...");
|
|
31520
|
-
|
|
31521
|
-
|
|
31522
|
-
|
|
31523
|
-
|
|
31524
|
-
|
|
31525
|
-
|
|
32150
|
+
{
|
|
32151
|
+
let testClient = null;
|
|
32152
|
+
try {
|
|
32153
|
+
testClient = new Client({ connectionString: connStr, connectionTimeoutMillis: 1e4 });
|
|
32154
|
+
await testClient.connect();
|
|
32155
|
+
const result = await testClient.query("select version();");
|
|
32156
|
+
console.log("\u2713 Connection successful");
|
|
32157
|
+
console.log(`${result.rows[0].version}
|
|
31526
32158
|
`);
|
|
31527
|
-
|
|
31528
|
-
|
|
31529
|
-
|
|
31530
|
-
console.error(`\u2717 Connection failed: ${message}
|
|
32159
|
+
} catch (error2) {
|
|
32160
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
32161
|
+
console.error(`\u2717 Connection failed: ${message}
|
|
31531
32162
|
`);
|
|
32163
|
+
} finally {
|
|
32164
|
+
if (testClient)
|
|
32165
|
+
await testClient.end();
|
|
32166
|
+
}
|
|
31532
32167
|
}
|
|
31533
32168
|
}
|
|
31534
32169
|
} else {
|
|
@@ -31549,15 +32184,17 @@ You can provide either:`);
|
|
|
31549
32184
|
];
|
|
31550
32185
|
const demoSrc = demoCandidates.find((p) => fs6.existsSync(p));
|
|
31551
32186
|
if (demoSrc) {
|
|
31552
|
-
if (fs6.existsSync(instancesPath) && fs6.
|
|
32187
|
+
if (fs6.existsSync(instancesPath) && fs6.lstatSync(instancesPath).isDirectory()) {
|
|
31553
32188
|
fs6.rmSync(instancesPath, { recursive: true, force: true });
|
|
31554
32189
|
}
|
|
31555
32190
|
fs6.copyFileSync(demoSrc, instancesPath);
|
|
31556
32191
|
console.log(`\u2713 Demo monitoring target configured
|
|
31557
32192
|
`);
|
|
31558
32193
|
} else {
|
|
31559
|
-
console.error(
|
|
32194
|
+
console.error(`Error: instances.demo.yml not found \u2014 cannot configure demo target.
|
|
32195
|
+
Searched: ${demoCandidates.join(", ")}
|
|
31560
32196
|
`);
|
|
32197
|
+
process.exit(1);
|
|
31561
32198
|
}
|
|
31562
32199
|
}
|
|
31563
32200
|
console.log(opts.demo ? "Step 3: Updating configuration..." : "Step 3: Updating configuration...");
|
|
@@ -31586,9 +32223,8 @@ You can provide either:`);
|
|
|
31586
32223
|
}
|
|
31587
32224
|
if (!grafanaPassword) {
|
|
31588
32225
|
console.log("Generating secure Grafana password...");
|
|
31589
|
-
const { stdout: password } = await
|
|
31590
|
-
|
|
31591
|
-
grafanaPassword = password.trim();
|
|
32226
|
+
const { stdout: password } = await execFilePromise("openssl", ["rand", "-base64", "12"]);
|
|
32227
|
+
grafanaPassword = password.trim().replace(/\n/g, "");
|
|
31592
32228
|
let configContent = "";
|
|
31593
32229
|
if (fs6.existsSync(cfgPath)) {
|
|
31594
32230
|
const stats = fs6.statSync(cfgPath);
|
|
@@ -31617,17 +32253,16 @@ You can provide either:`);
|
|
|
31617
32253
|
const userMatch = envContent.match(/^VM_AUTH_USERNAME=([^\r\n]+)/m);
|
|
31618
32254
|
const passMatch = envContent.match(/^VM_AUTH_PASSWORD=([^\r\n]+)/m);
|
|
31619
32255
|
if (userMatch)
|
|
31620
|
-
vmAuthUsername = userMatch[1]
|
|
32256
|
+
vmAuthUsername = stripMatchingQuotes(userMatch[1]);
|
|
31621
32257
|
if (passMatch)
|
|
31622
|
-
vmAuthPassword = passMatch[1]
|
|
32258
|
+
vmAuthPassword = stripMatchingQuotes(passMatch[1]);
|
|
31623
32259
|
}
|
|
31624
32260
|
if (!vmAuthUsername || !vmAuthPassword) {
|
|
31625
32261
|
console.log("Generating VictoriaMetrics auth credentials...");
|
|
31626
32262
|
vmAuthUsername = vmAuthUsername || "vmauth";
|
|
31627
32263
|
if (!vmAuthPassword) {
|
|
31628
|
-
const { stdout: vmPass } = await
|
|
31629
|
-
|
|
31630
|
-
vmAuthPassword = vmPass.trim();
|
|
32264
|
+
const { stdout: vmPass } = await execFilePromise("openssl", ["rand", "-base64", "12"]);
|
|
32265
|
+
vmAuthPassword = vmPass.trim().replace(/\n/g, "");
|
|
31631
32266
|
}
|
|
31632
32267
|
let envContent = "";
|
|
31633
32268
|
if (fs6.existsSync(envFile2)) {
|
|
@@ -31845,7 +32480,7 @@ mon.command("config").description("show monitoring services configuration").acti
|
|
|
31845
32480
|
console.log(`Project Directory: ${projectDir}`);
|
|
31846
32481
|
console.log(`Docker Compose File: ${composeFile}`);
|
|
31847
32482
|
console.log(`Instances File: ${instancesFile}`);
|
|
31848
|
-
if (fs6.existsSync(instancesFile) && !fs6.
|
|
32483
|
+
if (fs6.existsSync(instancesFile) && !fs6.lstatSync(instancesFile).isDirectory()) {
|
|
31849
32484
|
console.log(`
|
|
31850
32485
|
Instances configuration:
|
|
31851
32486
|
`);
|
|
@@ -31871,12 +32506,12 @@ mon.command("update").description("update monitoring stack").action(async () =>
|
|
|
31871
32506
|
return;
|
|
31872
32507
|
}
|
|
31873
32508
|
console.log("Fetching latest changes...");
|
|
31874
|
-
await
|
|
31875
|
-
const { stdout: branch } = await
|
|
32509
|
+
await execFilePromise("git", ["fetch", "origin"]);
|
|
32510
|
+
const { stdout: branch } = await execFilePromise("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
31876
32511
|
const currentBranch = branch.trim();
|
|
31877
32512
|
console.log(`Current branch: ${currentBranch}`);
|
|
31878
32513
|
console.log("Pulling latest changes...");
|
|
31879
|
-
const { stdout: pullOut } = await
|
|
32514
|
+
const { stdout: pullOut } = await execFilePromise("git", ["pull", "origin", currentBranch]);
|
|
31880
32515
|
console.log(pullOut);
|
|
31881
32516
|
console.log(`
|
|
31882
32517
|
Updating Docker images...`);
|
|
@@ -32020,7 +32655,7 @@ mon.command("check").description("monitoring services system readiness check").a
|
|
|
32020
32655
|
var targets = mon.command("targets").description("manage databases to monitor");
|
|
32021
32656
|
targets.command("list").description("list monitoring target databases").action(async () => {
|
|
32022
32657
|
const { instancesFile: instancesPath, projectDir } = await resolveOrInitPaths();
|
|
32023
|
-
if (!fs6.existsSync(instancesPath) || fs6.
|
|
32658
|
+
if (!fs6.existsSync(instancesPath) || fs6.lstatSync(instancesPath).isDirectory()) {
|
|
32024
32659
|
console.error(`instances.yml not found in ${projectDir}`);
|
|
32025
32660
|
process.exitCode = 1;
|
|
32026
32661
|
return;
|
|
@@ -32075,7 +32710,7 @@ targets.command("add [connStr] [name]").description("add monitoring target datab
|
|
|
32075
32710
|
const db = m[5];
|
|
32076
32711
|
const instanceName = name && name.trim() ? name.trim() : `${host}-${db}`.replace(/[^a-zA-Z0-9-]/g, "-");
|
|
32077
32712
|
try {
|
|
32078
|
-
if (fs6.existsSync(file) && !fs6.
|
|
32713
|
+
if (fs6.existsSync(file) && !fs6.lstatSync(file).isDirectory()) {
|
|
32079
32714
|
const content2 = fs6.readFileSync(file, "utf8");
|
|
32080
32715
|
const instances = load(content2) || [];
|
|
32081
32716
|
if (Array.isArray(instances)) {
|
|
@@ -32088,15 +32723,16 @@ targets.command("add [connStr] [name]").description("add monitoring target datab
|
|
|
32088
32723
|
}
|
|
32089
32724
|
}
|
|
32090
32725
|
} catch (err) {
|
|
32091
|
-
const isFile = fs6.existsSync(file) && !fs6.
|
|
32726
|
+
const isFile = fs6.existsSync(file) && !fs6.lstatSync(file).isDirectory();
|
|
32092
32727
|
const content2 = isFile ? fs6.readFileSync(file, "utf8") : "";
|
|
32093
|
-
|
|
32728
|
+
const escapedName = instanceName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
32729
|
+
if (new RegExp(`^- name: ${escapedName}$`, "m").test(content2)) {
|
|
32094
32730
|
console.error(`Monitoring target '${instanceName}' already exists`);
|
|
32095
32731
|
process.exitCode = 1;
|
|
32096
32732
|
return;
|
|
32097
32733
|
}
|
|
32098
32734
|
}
|
|
32099
|
-
if (fs6.existsSync(file) && fs6.
|
|
32735
|
+
if (fs6.existsSync(file) && fs6.lstatSync(file).isDirectory()) {
|
|
32100
32736
|
fs6.rmSync(file, { recursive: true, force: true });
|
|
32101
32737
|
}
|
|
32102
32738
|
const body = `- name: ${instanceName}
|
|
@@ -32118,7 +32754,7 @@ targets.command("add [connStr] [name]").description("add monitoring target datab
|
|
|
32118
32754
|
});
|
|
32119
32755
|
targets.command("remove <name>").description("remove monitoring target database").action(async (name) => {
|
|
32120
32756
|
const { instancesFile: file } = await resolveOrInitPaths();
|
|
32121
|
-
if (!fs6.existsSync(file) || fs6.
|
|
32757
|
+
if (!fs6.existsSync(file) || fs6.lstatSync(file).isDirectory()) {
|
|
32122
32758
|
console.error("instances.yml not found");
|
|
32123
32759
|
process.exitCode = 1;
|
|
32124
32760
|
return;
|
|
@@ -32147,7 +32783,7 @@ targets.command("remove <name>").description("remove monitoring target database"
|
|
|
32147
32783
|
});
|
|
32148
32784
|
targets.command("test <name>").description("test monitoring target database connectivity").action(async (name) => {
|
|
32149
32785
|
const { instancesFile: instancesPath } = await resolveOrInitPaths();
|
|
32150
|
-
if (!fs6.existsSync(instancesPath) || fs6.
|
|
32786
|
+
if (!fs6.existsSync(instancesPath) || fs6.lstatSync(instancesPath).isDirectory()) {
|
|
32151
32787
|
console.error("instances.yml not found");
|
|
32152
32788
|
process.exitCode = 1;
|
|
32153
32789
|
return;
|
|
@@ -32172,7 +32808,7 @@ targets.command("test <name>").description("test monitoring target database conn
|
|
|
32172
32808
|
return;
|
|
32173
32809
|
}
|
|
32174
32810
|
console.log(`Testing connection to monitoring target '${name}'...`);
|
|
32175
|
-
const client = new Client({ connectionString: instance.conn_str });
|
|
32811
|
+
const client = new Client({ connectionString: instance.conn_str, connectionTimeoutMillis: 1e4 });
|
|
32176
32812
|
try {
|
|
32177
32813
|
await client.connect();
|
|
32178
32814
|
const result = await client.query("select version();");
|
|
@@ -32441,9 +33077,8 @@ mon.command("generate-grafana-password").description("generate Grafana password
|
|
|
32441
33077
|
const { projectDir } = await resolveOrInitPaths();
|
|
32442
33078
|
const cfgPath = path6.resolve(projectDir, ".pgwatch-config");
|
|
32443
33079
|
try {
|
|
32444
|
-
const { stdout: password } = await
|
|
32445
|
-
|
|
32446
|
-
const newPassword = password.trim();
|
|
33080
|
+
const { stdout: password } = await execFilePromise("openssl", ["rand", "-base64", "12"]);
|
|
33081
|
+
const newPassword = password.trim().replace(/\n/g, "");
|
|
32447
33082
|
if (!newPassword) {
|
|
32448
33083
|
console.error("Failed to generate password");
|
|
32449
33084
|
process.exitCode = 1;
|
|
@@ -32522,8 +33157,8 @@ Grafana credentials:`);
|
|
|
32522
33157
|
if (vmUser && vmPass) {
|
|
32523
33158
|
console.log(`
|
|
32524
33159
|
VictoriaMetrics credentials:`);
|
|
32525
|
-
console.log(` Username: ${vmUser[1]
|
|
32526
|
-
console.log(` Password: ${vmPass[1]
|
|
33160
|
+
console.log(` Username: ${stripMatchingQuotes(vmUser[1])}`);
|
|
33161
|
+
console.log(` Password: ${stripMatchingQuotes(vmPass[1])}`);
|
|
32527
33162
|
}
|
|
32528
33163
|
}
|
|
32529
33164
|
console.log("");
|
|
@@ -33262,7 +33897,7 @@ mcp.command("install [client]").description("install MCP server configuration fo
|
|
|
33262
33897
|
try {
|
|
33263
33898
|
let pgaiPath;
|
|
33264
33899
|
try {
|
|
33265
|
-
const execPath = await
|
|
33900
|
+
const execPath = await execFilePromise("which", ["pgai"]);
|
|
33266
33901
|
pgaiPath = execPath.stdout.trim();
|
|
33267
33902
|
} catch {
|
|
33268
33903
|
pgaiPath = "pgai";
|
|
@@ -33270,7 +33905,7 @@ mcp.command("install [client]").description("install MCP server configuration fo
|
|
|
33270
33905
|
if (client === "claude-code") {
|
|
33271
33906
|
console.log("Installing PostgresAI MCP server for Claude Code...");
|
|
33272
33907
|
try {
|
|
33273
|
-
const { stdout, stderr } = await
|
|
33908
|
+
const { stdout, stderr } = await execFilePromise("claude", ["mcp", "add", "-s", "user", "postgresai", pgaiPath, "mcp", "start"]);
|
|
33274
33909
|
if (stdout)
|
|
33275
33910
|
console.log(stdout);
|
|
33276
33911
|
if (stderr)
|