postgresai 0.14.0-dev.75 → 0.14.0-dev.77
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 +312 -6
- package/dist/bin/postgres-ai.js +916 -40
- package/dist/sql/02.extensions.sql +8 -0
- package/dist/sql/{02.permissions.sql → 03.permissions.sql} +1 -0
- package/dist/sql/sql/02.extensions.sql +8 -0
- package/dist/sql/sql/{02.permissions.sql → 03.permissions.sql} +1 -0
- package/dist/sql/sql/uninit/01.helpers.sql +5 -0
- package/dist/sql/sql/uninit/02.permissions.sql +30 -0
- package/dist/sql/sql/uninit/03.role.sql +27 -0
- package/dist/sql/uninit/01.helpers.sql +5 -0
- package/dist/sql/uninit/02.permissions.sql +30 -0
- package/dist/sql/uninit/03.role.sql +27 -0
- package/lib/checkup-dictionary.ts +113 -0
- package/lib/checkup.ts +21 -14
- package/lib/init.ts +109 -8
- package/package.json +9 -7
- package/scripts/embed-checkup-dictionary.ts +106 -0
- package/sql/02.extensions.sql +8 -0
- package/sql/{02.permissions.sql → 03.permissions.sql} +1 -0
- package/sql/uninit/01.helpers.sql +5 -0
- package/sql/uninit/02.permissions.sql +30 -0
- package/sql/uninit/03.role.sql +27 -0
- package/test/checkup.test.ts +17 -18
- package/test/init.test.ts +245 -11
- package/lib/metrics-embedded.ts +0 -79
- /package/dist/sql/{03.optional_rds.sql → 04.optional_rds.sql} +0 -0
- /package/dist/sql/{04.optional_self_managed.sql → 05.optional_self_managed.sql} +0 -0
- /package/dist/sql/{05.helpers.sql → 06.helpers.sql} +0 -0
- /package/dist/sql/sql/{03.optional_rds.sql → 04.optional_rds.sql} +0 -0
- /package/dist/sql/sql/{04.optional_self_managed.sql → 05.optional_self_managed.sql} +0 -0
- /package/dist/sql/sql/{05.helpers.sql → 06.helpers.sql} +0 -0
- /package/sql/{03.optional_rds.sql → 04.optional_rds.sql} +0 -0
- /package/sql/{04.optional_self_managed.sql → 05.optional_self_managed.sql} +0 -0
- /package/sql/{05.helpers.sql → 06.helpers.sql} +0 -0
package/dist/bin/postgres-ai.js
CHANGED
|
@@ -13064,7 +13064,7 @@ var {
|
|
|
13064
13064
|
// package.json
|
|
13065
13065
|
var package_default = {
|
|
13066
13066
|
name: "postgresai",
|
|
13067
|
-
version: "0.14.0-dev.
|
|
13067
|
+
version: "0.14.0-dev.77",
|
|
13068
13068
|
description: "postgres_ai CLI",
|
|
13069
13069
|
license: "Apache-2.0",
|
|
13070
13070
|
private: false,
|
|
@@ -13090,15 +13090,17 @@ var package_default = {
|
|
|
13090
13090
|
},
|
|
13091
13091
|
scripts: {
|
|
13092
13092
|
"embed-metrics": "bun run scripts/embed-metrics.ts",
|
|
13093
|
-
|
|
13093
|
+
"embed-checkup-dictionary": "bun run scripts/embed-checkup-dictionary.ts",
|
|
13094
|
+
"embed-all": "bun run embed-metrics && bun run embed-checkup-dictionary",
|
|
13095
|
+
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`,
|
|
13094
13096
|
prepublishOnly: "npm run build",
|
|
13095
13097
|
start: "bun ./bin/postgres-ai.ts --help",
|
|
13096
13098
|
"start:node": "node ./dist/bin/postgres-ai.js --help",
|
|
13097
|
-
dev: "bun run embed-
|
|
13098
|
-
test: "bun run embed-
|
|
13099
|
-
"test:fast": "bun run embed-
|
|
13100
|
-
"test:coverage": "bun run embed-
|
|
13101
|
-
typecheck: "bun run embed-
|
|
13099
|
+
dev: "bun run embed-all && bun --watch ./bin/postgres-ai.ts",
|
|
13100
|
+
test: "bun run embed-all && bun test",
|
|
13101
|
+
"test:fast": "bun run embed-all && bun test",
|
|
13102
|
+
"test:coverage": "bun run embed-all && bun test --coverage && echo 'Coverage report: cli/coverage/lcov-report/index.html'",
|
|
13103
|
+
typecheck: "bun run embed-all && bunx tsc --noEmit"
|
|
13102
13104
|
},
|
|
13103
13105
|
dependencies: {
|
|
13104
13106
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
@@ -15887,7 +15889,7 @@ var Result = import_lib.default.Result;
|
|
|
15887
15889
|
var TypeOverrides = import_lib.default.TypeOverrides;
|
|
15888
15890
|
var defaults = import_lib.default.defaults;
|
|
15889
15891
|
// package.json
|
|
15890
|
-
var version = "0.14.0-dev.
|
|
15892
|
+
var version = "0.14.0-dev.77";
|
|
15891
15893
|
var package_default2 = {
|
|
15892
15894
|
name: "postgresai",
|
|
15893
15895
|
version,
|
|
@@ -15916,15 +15918,17 @@ var package_default2 = {
|
|
|
15916
15918
|
},
|
|
15917
15919
|
scripts: {
|
|
15918
15920
|
"embed-metrics": "bun run scripts/embed-metrics.ts",
|
|
15919
|
-
|
|
15921
|
+
"embed-checkup-dictionary": "bun run scripts/embed-checkup-dictionary.ts",
|
|
15922
|
+
"embed-all": "bun run embed-metrics && bun run embed-checkup-dictionary",
|
|
15923
|
+
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`,
|
|
15920
15924
|
prepublishOnly: "npm run build",
|
|
15921
15925
|
start: "bun ./bin/postgres-ai.ts --help",
|
|
15922
15926
|
"start:node": "node ./dist/bin/postgres-ai.js --help",
|
|
15923
|
-
dev: "bun run embed-
|
|
15924
|
-
test: "bun run embed-
|
|
15925
|
-
"test:fast": "bun run embed-
|
|
15926
|
-
"test:coverage": "bun run embed-
|
|
15927
|
-
typecheck: "bun run embed-
|
|
15927
|
+
dev: "bun run embed-all && bun --watch ./bin/postgres-ai.ts",
|
|
15928
|
+
test: "bun run embed-all && bun test",
|
|
15929
|
+
"test:fast": "bun run embed-all && bun test",
|
|
15930
|
+
"test:coverage": "bun run embed-all && bun test --coverage && echo 'Coverage report: cli/coverage/lcov-report/index.html'",
|
|
15931
|
+
typecheck: "bun run embed-all && bunx tsc --noEmit"
|
|
15928
15932
|
},
|
|
15929
15933
|
dependencies: {
|
|
15930
15934
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
@@ -24961,7 +24965,11 @@ end $$;`;
|
|
|
24961
24965
|
const roleSql = applyTemplate(loadSqlTemplate("01.role.sql"), { ...vars, ROLE_STMT: roleStmt });
|
|
24962
24966
|
steps.push({ name: "01.role", sql: roleSql });
|
|
24963
24967
|
}
|
|
24964
|
-
|
|
24968
|
+
steps.push({
|
|
24969
|
+
name: "02.extensions",
|
|
24970
|
+
sql: loadSqlTemplate("02.extensions.sql")
|
|
24971
|
+
});
|
|
24972
|
+
let permissionsSql = applyTemplate(loadSqlTemplate("03.permissions.sql"), vars);
|
|
24965
24973
|
if (SKIP_ALTER_USER_PROVIDERS.includes(provider)) {
|
|
24966
24974
|
permissionsSql = permissionsSql.split(`
|
|
24967
24975
|
`).filter((line) => {
|
|
@@ -24973,21 +24981,21 @@ end $$;`;
|
|
|
24973
24981
|
`);
|
|
24974
24982
|
}
|
|
24975
24983
|
steps.push({
|
|
24976
|
-
name: "
|
|
24984
|
+
name: "03.permissions",
|
|
24977
24985
|
sql: permissionsSql
|
|
24978
24986
|
});
|
|
24979
24987
|
steps.push({
|
|
24980
|
-
name: "
|
|
24981
|
-
sql: applyTemplate(loadSqlTemplate("
|
|
24988
|
+
name: "06.helpers",
|
|
24989
|
+
sql: applyTemplate(loadSqlTemplate("06.helpers.sql"), vars)
|
|
24982
24990
|
});
|
|
24983
24991
|
if (params.includeOptionalPermissions) {
|
|
24984
24992
|
steps.push({
|
|
24985
|
-
name: "
|
|
24986
|
-
sql: applyTemplate(loadSqlTemplate("
|
|
24993
|
+
name: "04.optional_rds",
|
|
24994
|
+
sql: applyTemplate(loadSqlTemplate("04.optional_rds.sql"), vars),
|
|
24987
24995
|
optional: true
|
|
24988
24996
|
}, {
|
|
24989
|
-
name: "
|
|
24990
|
-
sql: applyTemplate(loadSqlTemplate("
|
|
24997
|
+
name: "05.optional_self_managed",
|
|
24998
|
+
sql: applyTemplate(loadSqlTemplate("05.optional_self_managed.sql"), vars),
|
|
24991
24999
|
optional: true
|
|
24992
25000
|
});
|
|
24993
25001
|
}
|
|
@@ -25055,6 +25063,62 @@ async function applyInitPlan(params) {
|
|
|
25055
25063
|
}
|
|
25056
25064
|
return { applied, skippedOptional };
|
|
25057
25065
|
}
|
|
25066
|
+
async function buildUninitPlan(params) {
|
|
25067
|
+
const monitoringUser = params.monitoringUser || DEFAULT_MONITORING_USER;
|
|
25068
|
+
const database = params.database;
|
|
25069
|
+
const provider = params.provider ?? "self-managed";
|
|
25070
|
+
const dropRole = params.dropRole ?? true;
|
|
25071
|
+
const qRole = quoteIdent(monitoringUser);
|
|
25072
|
+
const qDb = quoteIdent(database);
|
|
25073
|
+
const qRoleLiteral = quoteLiteral(monitoringUser);
|
|
25074
|
+
const steps = [];
|
|
25075
|
+
const vars = {
|
|
25076
|
+
ROLE_IDENT: qRole,
|
|
25077
|
+
DB_IDENT: qDb,
|
|
25078
|
+
ROLE_LITERAL: qRoleLiteral
|
|
25079
|
+
};
|
|
25080
|
+
steps.push({
|
|
25081
|
+
name: "01.drop_helpers",
|
|
25082
|
+
sql: applyTemplate(loadSqlTemplate("uninit/01.helpers.sql"), vars)
|
|
25083
|
+
});
|
|
25084
|
+
steps.push({
|
|
25085
|
+
name: "02.revoke_permissions",
|
|
25086
|
+
sql: applyTemplate(loadSqlTemplate("uninit/02.permissions.sql"), vars)
|
|
25087
|
+
});
|
|
25088
|
+
if (dropRole && !SKIP_ROLE_CREATION_PROVIDERS.includes(provider)) {
|
|
25089
|
+
steps.push({
|
|
25090
|
+
name: "03.drop_role",
|
|
25091
|
+
sql: applyTemplate(loadSqlTemplate("uninit/03.role.sql"), vars)
|
|
25092
|
+
});
|
|
25093
|
+
}
|
|
25094
|
+
return { monitoringUser, database, steps, dropRole };
|
|
25095
|
+
}
|
|
25096
|
+
async function applyUninitPlan(params) {
|
|
25097
|
+
const applied = [];
|
|
25098
|
+
const errors3 = [];
|
|
25099
|
+
const executeStep = async (step) => {
|
|
25100
|
+
await params.client.query("begin;");
|
|
25101
|
+
try {
|
|
25102
|
+
await params.client.query(step.sql, step.params);
|
|
25103
|
+
await params.client.query("commit;");
|
|
25104
|
+
} catch (e) {
|
|
25105
|
+
try {
|
|
25106
|
+
await params.client.query("rollback;");
|
|
25107
|
+
} catch {}
|
|
25108
|
+
throw e;
|
|
25109
|
+
}
|
|
25110
|
+
};
|
|
25111
|
+
for (const step of params.plan.steps) {
|
|
25112
|
+
try {
|
|
25113
|
+
await executeStep(step);
|
|
25114
|
+
applied.push(step.name);
|
|
25115
|
+
} catch (e) {
|
|
25116
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
25117
|
+
errors3.push(`${step.name}: ${msg}`);
|
|
25118
|
+
}
|
|
25119
|
+
}
|
|
25120
|
+
return { applied, errors: errors3 };
|
|
25121
|
+
}
|
|
25058
25122
|
async function verifyInitSetup(params) {
|
|
25059
25123
|
await params.client.query("begin isolation level repeatable read;");
|
|
25060
25124
|
try {
|
|
@@ -26482,6 +26546,572 @@ function transformMetricRow(row) {
|
|
|
26482
26546
|
return result;
|
|
26483
26547
|
}
|
|
26484
26548
|
|
|
26549
|
+
// lib/checkup-dictionary-embedded.ts
|
|
26550
|
+
var CHECKUP_DICTIONARY_DATA = [
|
|
26551
|
+
{
|
|
26552
|
+
code: "A001",
|
|
26553
|
+
title: "System information",
|
|
26554
|
+
description: "OS, kernel, hardware details",
|
|
26555
|
+
category: "system",
|
|
26556
|
+
sort_order: null,
|
|
26557
|
+
is_system_report: false
|
|
26558
|
+
},
|
|
26559
|
+
{
|
|
26560
|
+
code: "A002",
|
|
26561
|
+
title: "Postgres major version",
|
|
26562
|
+
description: "Major version and end-of-life status",
|
|
26563
|
+
category: "system",
|
|
26564
|
+
sort_order: null,
|
|
26565
|
+
is_system_report: false
|
|
26566
|
+
},
|
|
26567
|
+
{
|
|
26568
|
+
code: "A003",
|
|
26569
|
+
title: "Postgres settings",
|
|
26570
|
+
description: "Full list of current settings",
|
|
26571
|
+
category: "system",
|
|
26572
|
+
sort_order: null,
|
|
26573
|
+
is_system_report: false
|
|
26574
|
+
},
|
|
26575
|
+
{
|
|
26576
|
+
code: "A004",
|
|
26577
|
+
title: "Cluster information",
|
|
26578
|
+
description: "Data directory, cluster name, system identifier",
|
|
26579
|
+
category: "system",
|
|
26580
|
+
sort_order: null,
|
|
26581
|
+
is_system_report: false
|
|
26582
|
+
},
|
|
26583
|
+
{
|
|
26584
|
+
code: "A005",
|
|
26585
|
+
title: "Extensions",
|
|
26586
|
+
description: "Installed and available extensions",
|
|
26587
|
+
category: "system",
|
|
26588
|
+
sort_order: null,
|
|
26589
|
+
is_system_report: false
|
|
26590
|
+
},
|
|
26591
|
+
{
|
|
26592
|
+
code: "A006",
|
|
26593
|
+
title: "Postgres setting deviations",
|
|
26594
|
+
description: "Settings that differ from defaults",
|
|
26595
|
+
category: "system",
|
|
26596
|
+
sort_order: null,
|
|
26597
|
+
is_system_report: false
|
|
26598
|
+
},
|
|
26599
|
+
{
|
|
26600
|
+
code: "A007",
|
|
26601
|
+
title: "Altered settings",
|
|
26602
|
+
description: "Settings changed via ALTER SYSTEM or per-user/database",
|
|
26603
|
+
category: "system",
|
|
26604
|
+
sort_order: null,
|
|
26605
|
+
is_system_report: false
|
|
26606
|
+
},
|
|
26607
|
+
{
|
|
26608
|
+
code: "A008",
|
|
26609
|
+
title: "Disk usage and file system type",
|
|
26610
|
+
description: "Disk space, mount points, filesystem types",
|
|
26611
|
+
category: "system",
|
|
26612
|
+
sort_order: null,
|
|
26613
|
+
is_system_report: false
|
|
26614
|
+
},
|
|
26615
|
+
{
|
|
26616
|
+
code: "A009",
|
|
26617
|
+
title: "Tablespaces",
|
|
26618
|
+
description: "Tablespace locations and usage",
|
|
26619
|
+
category: "system",
|
|
26620
|
+
sort_order: null,
|
|
26621
|
+
is_system_report: false
|
|
26622
|
+
},
|
|
26623
|
+
{
|
|
26624
|
+
code: "A010",
|
|
26625
|
+
title: "Corruption control",
|
|
26626
|
+
description: "Checksums, wal_log_hints, backup safety",
|
|
26627
|
+
category: "system",
|
|
26628
|
+
sort_order: null,
|
|
26629
|
+
is_system_report: false
|
|
26630
|
+
},
|
|
26631
|
+
{
|
|
26632
|
+
code: "A011",
|
|
26633
|
+
title: "Connection management and pooling",
|
|
26634
|
+
description: "Connection limits and pooler detection",
|
|
26635
|
+
category: "system",
|
|
26636
|
+
sort_order: null,
|
|
26637
|
+
is_system_report: false
|
|
26638
|
+
},
|
|
26639
|
+
{
|
|
26640
|
+
code: "A012",
|
|
26641
|
+
title: "Anti-crash checks",
|
|
26642
|
+
description: "fsync, full_page_writes, synchronous_commit",
|
|
26643
|
+
category: "system",
|
|
26644
|
+
sort_order: null,
|
|
26645
|
+
is_system_report: false
|
|
26646
|
+
},
|
|
26647
|
+
{
|
|
26648
|
+
code: "A013",
|
|
26649
|
+
title: "Postgres minor version",
|
|
26650
|
+
description: "Minor version and available updates",
|
|
26651
|
+
category: "system",
|
|
26652
|
+
sort_order: null,
|
|
26653
|
+
is_system_report: false
|
|
26654
|
+
},
|
|
26655
|
+
{
|
|
26656
|
+
code: "B001",
|
|
26657
|
+
title: "SLO/SLA, RPO, RTO",
|
|
26658
|
+
description: "Recovery objectives and current capabilities",
|
|
26659
|
+
category: "backup",
|
|
26660
|
+
sort_order: null,
|
|
26661
|
+
is_system_report: false
|
|
26662
|
+
},
|
|
26663
|
+
{
|
|
26664
|
+
code: "B002",
|
|
26665
|
+
title: "File system and mount flags",
|
|
26666
|
+
description: "Mount options affecting durability",
|
|
26667
|
+
category: "backup",
|
|
26668
|
+
sort_order: null,
|
|
26669
|
+
is_system_report: false
|
|
26670
|
+
},
|
|
26671
|
+
{
|
|
26672
|
+
code: "B003",
|
|
26673
|
+
title: "Full and incremental backups",
|
|
26674
|
+
description: "Backup tooling and schedule",
|
|
26675
|
+
category: "backup",
|
|
26676
|
+
sort_order: null,
|
|
26677
|
+
is_system_report: false
|
|
26678
|
+
},
|
|
26679
|
+
{
|
|
26680
|
+
code: "B004",
|
|
26681
|
+
title: "WAL archiving",
|
|
26682
|
+
description: "Archive configuration and status",
|
|
26683
|
+
category: "backup",
|
|
26684
|
+
sort_order: null,
|
|
26685
|
+
is_system_report: false
|
|
26686
|
+
},
|
|
26687
|
+
{
|
|
26688
|
+
code: "B005",
|
|
26689
|
+
title: "Backup restore testing and monitoring",
|
|
26690
|
+
description: "Restore testing practices and alerting",
|
|
26691
|
+
category: "backup",
|
|
26692
|
+
sort_order: null,
|
|
26693
|
+
is_system_report: false
|
|
26694
|
+
},
|
|
26695
|
+
{
|
|
26696
|
+
code: "C001",
|
|
26697
|
+
title: "SLO/SLA",
|
|
26698
|
+
description: "Availability objectives",
|
|
26699
|
+
category: "replication",
|
|
26700
|
+
sort_order: null,
|
|
26701
|
+
is_system_report: false
|
|
26702
|
+
},
|
|
26703
|
+
{
|
|
26704
|
+
code: "C002",
|
|
26705
|
+
title: "Replication mode and method",
|
|
26706
|
+
description: "Sync/async, streaming/WAL shipping",
|
|
26707
|
+
category: "replication",
|
|
26708
|
+
sort_order: null,
|
|
26709
|
+
is_system_report: false
|
|
26710
|
+
},
|
|
26711
|
+
{
|
|
26712
|
+
code: "C003",
|
|
26713
|
+
title: "Single points of failure",
|
|
26714
|
+
description: "SPOF analysis",
|
|
26715
|
+
category: "replication",
|
|
26716
|
+
sort_order: null,
|
|
26717
|
+
is_system_report: false
|
|
26718
|
+
},
|
|
26719
|
+
{
|
|
26720
|
+
code: "C004",
|
|
26721
|
+
title: "Failover",
|
|
26722
|
+
description: "Failover mechanism and automation",
|
|
26723
|
+
category: "replication",
|
|
26724
|
+
sort_order: null,
|
|
26725
|
+
is_system_report: false
|
|
26726
|
+
},
|
|
26727
|
+
{
|
|
26728
|
+
code: "C005",
|
|
26729
|
+
title: "Switchover",
|
|
26730
|
+
description: "Planned switchover procedures",
|
|
26731
|
+
category: "replication",
|
|
26732
|
+
sort_order: null,
|
|
26733
|
+
is_system_report: false
|
|
26734
|
+
},
|
|
26735
|
+
{
|
|
26736
|
+
code: "C006",
|
|
26737
|
+
title: "Delayed replica",
|
|
26738
|
+
description: "Delayed standby configuration",
|
|
26739
|
+
category: "replication",
|
|
26740
|
+
sort_order: null,
|
|
26741
|
+
is_system_report: false
|
|
26742
|
+
},
|
|
26743
|
+
{
|
|
26744
|
+
code: "C007",
|
|
26745
|
+
title: "Replication slots and lag",
|
|
26746
|
+
description: "Slot status, lag, standby feedback",
|
|
26747
|
+
category: "replication",
|
|
26748
|
+
sort_order: null,
|
|
26749
|
+
is_system_report: false
|
|
26750
|
+
},
|
|
26751
|
+
{
|
|
26752
|
+
code: "D001",
|
|
26753
|
+
title: "Logging settings",
|
|
26754
|
+
description: "Log destination, verbosity, rotation",
|
|
26755
|
+
category: "monitoring",
|
|
26756
|
+
sort_order: null,
|
|
26757
|
+
is_system_report: false
|
|
26758
|
+
},
|
|
26759
|
+
{
|
|
26760
|
+
code: "D002",
|
|
26761
|
+
title: "Useful Linux tools",
|
|
26762
|
+
description: "Recommended OS-level tooling",
|
|
26763
|
+
category: "monitoring",
|
|
26764
|
+
sort_order: null,
|
|
26765
|
+
is_system_report: false
|
|
26766
|
+
},
|
|
26767
|
+
{
|
|
26768
|
+
code: "D003",
|
|
26769
|
+
title: "Monitoring metrics",
|
|
26770
|
+
description: "Key metrics to track",
|
|
26771
|
+
category: "monitoring",
|
|
26772
|
+
sort_order: null,
|
|
26773
|
+
is_system_report: false
|
|
26774
|
+
},
|
|
26775
|
+
{
|
|
26776
|
+
code: "D004",
|
|
26777
|
+
title: "pg_stat_statements and pg_stat_kcache",
|
|
26778
|
+
description: "Query stats extension settings",
|
|
26779
|
+
category: "monitoring",
|
|
26780
|
+
sort_order: null,
|
|
26781
|
+
is_system_report: false
|
|
26782
|
+
},
|
|
26783
|
+
{
|
|
26784
|
+
code: "D005",
|
|
26785
|
+
title: "track_io_timing and auto_explain",
|
|
26786
|
+
description: "I/O timing and plan logging",
|
|
26787
|
+
category: "monitoring",
|
|
26788
|
+
sort_order: null,
|
|
26789
|
+
is_system_report: false
|
|
26790
|
+
},
|
|
26791
|
+
{
|
|
26792
|
+
code: "D006",
|
|
26793
|
+
title: "Recommended DBA toolsets",
|
|
26794
|
+
description: "Useful third-party tools",
|
|
26795
|
+
category: "monitoring",
|
|
26796
|
+
sort_order: null,
|
|
26797
|
+
is_system_report: false
|
|
26798
|
+
},
|
|
26799
|
+
{
|
|
26800
|
+
code: "D007",
|
|
26801
|
+
title: "Postgres troubleshooting tools",
|
|
26802
|
+
description: "Built-in and ecosystem diagnostic tools",
|
|
26803
|
+
category: "monitoring",
|
|
26804
|
+
sort_order: null,
|
|
26805
|
+
is_system_report: false
|
|
26806
|
+
},
|
|
26807
|
+
{
|
|
26808
|
+
code: "E001",
|
|
26809
|
+
title: "WAL and checkpoint settings",
|
|
26810
|
+
description: "WAL size, checkpoint timing, I/O limits",
|
|
26811
|
+
category: "wal",
|
|
26812
|
+
sort_order: null,
|
|
26813
|
+
is_system_report: false
|
|
26814
|
+
},
|
|
26815
|
+
{
|
|
26816
|
+
code: "E002",
|
|
26817
|
+
title: "Checkpoint and bgwriter activity",
|
|
26818
|
+
description: "Checkpoint frequency, bgwriter stats",
|
|
26819
|
+
category: "wal",
|
|
26820
|
+
sort_order: null,
|
|
26821
|
+
is_system_report: false
|
|
26822
|
+
},
|
|
26823
|
+
{
|
|
26824
|
+
code: "F001",
|
|
26825
|
+
title: "Autovacuum: current settings",
|
|
26826
|
+
description: "Autovacuum configuration",
|
|
26827
|
+
category: "vacuum",
|
|
26828
|
+
sort_order: null,
|
|
26829
|
+
is_system_report: false
|
|
26830
|
+
},
|
|
26831
|
+
{
|
|
26832
|
+
code: "F002",
|
|
26833
|
+
title: "Autovacuum: transaction ID wraparound",
|
|
26834
|
+
description: "XID age and wraparound risk",
|
|
26835
|
+
category: "vacuum",
|
|
26836
|
+
sort_order: null,
|
|
26837
|
+
is_system_report: false
|
|
26838
|
+
},
|
|
26839
|
+
{
|
|
26840
|
+
code: "F003",
|
|
26841
|
+
title: "Autovacuum: dead tuples",
|
|
26842
|
+
description: "Dead tuple counts and cleanup status",
|
|
26843
|
+
category: "vacuum",
|
|
26844
|
+
sort_order: null,
|
|
26845
|
+
is_system_report: false
|
|
26846
|
+
},
|
|
26847
|
+
{
|
|
26848
|
+
code: "F004",
|
|
26849
|
+
title: "Autovacuum: heap bloat estimate",
|
|
26850
|
+
description: "Estimated table bloat",
|
|
26851
|
+
category: "vacuum",
|
|
26852
|
+
sort_order: null,
|
|
26853
|
+
is_system_report: false
|
|
26854
|
+
},
|
|
26855
|
+
{
|
|
26856
|
+
code: "F005",
|
|
26857
|
+
title: "Autovacuum: index bloat estimate",
|
|
26858
|
+
description: "Estimated index bloat",
|
|
26859
|
+
category: "vacuum",
|
|
26860
|
+
sort_order: null,
|
|
26861
|
+
is_system_report: false
|
|
26862
|
+
},
|
|
26863
|
+
{
|
|
26864
|
+
code: "F006",
|
|
26865
|
+
title: "Precise heap bloat analysis",
|
|
26866
|
+
description: "Detailed table bloat measurement",
|
|
26867
|
+
category: "vacuum",
|
|
26868
|
+
sort_order: null,
|
|
26869
|
+
is_system_report: false
|
|
26870
|
+
},
|
|
26871
|
+
{
|
|
26872
|
+
code: "F007",
|
|
26873
|
+
title: "Precise index bloat analysis",
|
|
26874
|
+
description: "Detailed index bloat measurement",
|
|
26875
|
+
category: "vacuum",
|
|
26876
|
+
sort_order: null,
|
|
26877
|
+
is_system_report: false
|
|
26878
|
+
},
|
|
26879
|
+
{
|
|
26880
|
+
code: "F008",
|
|
26881
|
+
title: "Autovacuum: resource usage",
|
|
26882
|
+
description: "Autovacuum I/O and CPU impact",
|
|
26883
|
+
category: "vacuum",
|
|
26884
|
+
sort_order: null,
|
|
26885
|
+
is_system_report: false
|
|
26886
|
+
},
|
|
26887
|
+
{
|
|
26888
|
+
code: "G001",
|
|
26889
|
+
title: "Memory-related settings",
|
|
26890
|
+
description: "shared_buffers, work_mem, maintenance_work_mem",
|
|
26891
|
+
category: "tuning",
|
|
26892
|
+
sort_order: null,
|
|
26893
|
+
is_system_report: false
|
|
26894
|
+
},
|
|
26895
|
+
{
|
|
26896
|
+
code: "G002",
|
|
26897
|
+
title: "Connections and current activity",
|
|
26898
|
+
description: "Connection usage and active sessions",
|
|
26899
|
+
category: "tuning",
|
|
26900
|
+
sort_order: null,
|
|
26901
|
+
is_system_report: false
|
|
26902
|
+
},
|
|
26903
|
+
{
|
|
26904
|
+
code: "G003",
|
|
26905
|
+
title: "Timeouts, locks, deadlocks",
|
|
26906
|
+
description: "Lock settings and deadlock stats",
|
|
26907
|
+
category: "tuning",
|
|
26908
|
+
sort_order: null,
|
|
26909
|
+
is_system_report: false
|
|
26910
|
+
},
|
|
26911
|
+
{
|
|
26912
|
+
code: "G004",
|
|
26913
|
+
title: "Query planner settings",
|
|
26914
|
+
description: "Planner cost and behavior settings",
|
|
26915
|
+
category: "tuning",
|
|
26916
|
+
sort_order: null,
|
|
26917
|
+
is_system_report: false
|
|
26918
|
+
},
|
|
26919
|
+
{
|
|
26920
|
+
code: "G005",
|
|
26921
|
+
title: "I/O settings",
|
|
26922
|
+
description: "effective_io_concurrency, random_page_cost",
|
|
26923
|
+
category: "tuning",
|
|
26924
|
+
sort_order: null,
|
|
26925
|
+
is_system_report: false
|
|
26926
|
+
},
|
|
26927
|
+
{
|
|
26928
|
+
code: "G006",
|
|
26929
|
+
title: "Statistics target settings",
|
|
26930
|
+
description: "default_statistics_target and per-column targets",
|
|
26931
|
+
category: "tuning",
|
|
26932
|
+
sort_order: null,
|
|
26933
|
+
is_system_report: false
|
|
26934
|
+
},
|
|
26935
|
+
{
|
|
26936
|
+
code: "H001",
|
|
26937
|
+
title: "Invalid indexes",
|
|
26938
|
+
description: "Indexes that failed to build",
|
|
26939
|
+
category: "indexes",
|
|
26940
|
+
sort_order: null,
|
|
26941
|
+
is_system_report: false
|
|
26942
|
+
},
|
|
26943
|
+
{
|
|
26944
|
+
code: "H002",
|
|
26945
|
+
title: "Unused indexes",
|
|
26946
|
+
description: "Indexes with no scans",
|
|
26947
|
+
category: "indexes",
|
|
26948
|
+
sort_order: null,
|
|
26949
|
+
is_system_report: false
|
|
26950
|
+
},
|
|
26951
|
+
{
|
|
26952
|
+
code: "H003",
|
|
26953
|
+
title: "Non-indexed foreign keys",
|
|
26954
|
+
description: "FKs missing supporting indexes",
|
|
26955
|
+
category: "indexes",
|
|
26956
|
+
sort_order: null,
|
|
26957
|
+
is_system_report: false
|
|
26958
|
+
},
|
|
26959
|
+
{
|
|
26960
|
+
code: "H004",
|
|
26961
|
+
title: "Redundant indexes",
|
|
26962
|
+
description: "Indexes covered by other indexes",
|
|
26963
|
+
category: "indexes",
|
|
26964
|
+
sort_order: null,
|
|
26965
|
+
is_system_report: false
|
|
26966
|
+
},
|
|
26967
|
+
{
|
|
26968
|
+
code: "J001",
|
|
26969
|
+
title: "Capacity planning",
|
|
26970
|
+
description: "Growth trends and resource projections",
|
|
26971
|
+
category: "capacity",
|
|
26972
|
+
sort_order: null,
|
|
26973
|
+
is_system_report: false
|
|
26974
|
+
},
|
|
26975
|
+
{
|
|
26976
|
+
code: "K001",
|
|
26977
|
+
title: "Globally aggregated query metrics",
|
|
26978
|
+
description: "Overall query stats summary",
|
|
26979
|
+
category: "queries",
|
|
26980
|
+
sort_order: null,
|
|
26981
|
+
is_system_report: false
|
|
26982
|
+
},
|
|
26983
|
+
{
|
|
26984
|
+
code: "K002",
|
|
26985
|
+
title: "Workload type",
|
|
26986
|
+
description: "Read/write/mixed workload classification",
|
|
26987
|
+
category: "queries",
|
|
26988
|
+
sort_order: null,
|
|
26989
|
+
is_system_report: false
|
|
26990
|
+
},
|
|
26991
|
+
{
|
|
26992
|
+
code: "K003",
|
|
26993
|
+
title: "Top queries by total time",
|
|
26994
|
+
description: "Highest total execution + planning time",
|
|
26995
|
+
category: "queries",
|
|
26996
|
+
sort_order: null,
|
|
26997
|
+
is_system_report: false
|
|
26998
|
+
},
|
|
26999
|
+
{
|
|
27000
|
+
code: "K004",
|
|
27001
|
+
title: "Top queries by temp bytes written",
|
|
27002
|
+
description: "Queries spilling to disk",
|
|
27003
|
+
category: "queries",
|
|
27004
|
+
sort_order: null,
|
|
27005
|
+
is_system_report: false
|
|
27006
|
+
},
|
|
27007
|
+
{
|
|
27008
|
+
code: "K005",
|
|
27009
|
+
title: "Top queries by WAL generation",
|
|
27010
|
+
description: "Queries generating most WAL",
|
|
27011
|
+
category: "queries",
|
|
27012
|
+
sort_order: null,
|
|
27013
|
+
is_system_report: false
|
|
27014
|
+
},
|
|
27015
|
+
{
|
|
27016
|
+
code: "K006",
|
|
27017
|
+
title: "Top queries by shared blocks read",
|
|
27018
|
+
description: "Queries with most disk reads",
|
|
27019
|
+
category: "queries",
|
|
27020
|
+
sort_order: null,
|
|
27021
|
+
is_system_report: false
|
|
27022
|
+
},
|
|
27023
|
+
{
|
|
27024
|
+
code: "K007",
|
|
27025
|
+
title: "Top queries by shared blocks hit",
|
|
27026
|
+
description: "Queries with most buffer hits",
|
|
27027
|
+
category: "queries",
|
|
27028
|
+
sort_order: null,
|
|
27029
|
+
is_system_report: false
|
|
27030
|
+
},
|
|
27031
|
+
{
|
|
27032
|
+
code: "K008",
|
|
27033
|
+
title: "Top queries by shared blocks accessed",
|
|
27034
|
+
description: "Queries touching most data",
|
|
27035
|
+
category: "queries",
|
|
27036
|
+
sort_order: null,
|
|
27037
|
+
is_system_report: false
|
|
27038
|
+
},
|
|
27039
|
+
{
|
|
27040
|
+
code: "L001",
|
|
27041
|
+
title: "Table sizes",
|
|
27042
|
+
description: "Table and toast sizes",
|
|
27043
|
+
category: "schema",
|
|
27044
|
+
sort_order: null,
|
|
27045
|
+
is_system_report: false
|
|
27046
|
+
},
|
|
27047
|
+
{
|
|
27048
|
+
code: "L002",
|
|
27049
|
+
title: "Data types being used",
|
|
27050
|
+
description: "Column type distribution",
|
|
27051
|
+
category: "schema",
|
|
27052
|
+
sort_order: null,
|
|
27053
|
+
is_system_report: false
|
|
27054
|
+
},
|
|
27055
|
+
{
|
|
27056
|
+
code: "L003",
|
|
27057
|
+
title: "Integer overflow risks in primary keys",
|
|
27058
|
+
description: "PKs approaching integer limits",
|
|
27059
|
+
category: "schema",
|
|
27060
|
+
sort_order: null,
|
|
27061
|
+
is_system_report: false
|
|
27062
|
+
},
|
|
27063
|
+
{
|
|
27064
|
+
code: "L004",
|
|
27065
|
+
title: "Tables without primary key",
|
|
27066
|
+
description: "Tables missing PK or unique constraint",
|
|
27067
|
+
category: "schema",
|
|
27068
|
+
sort_order: null,
|
|
27069
|
+
is_system_report: false
|
|
27070
|
+
},
|
|
27071
|
+
{
|
|
27072
|
+
code: "M001",
|
|
27073
|
+
title: "Top queries by mean execution time",
|
|
27074
|
+
description: "Slowest queries on average",
|
|
27075
|
+
category: "queries",
|
|
27076
|
+
sort_order: null,
|
|
27077
|
+
is_system_report: false
|
|
27078
|
+
},
|
|
27079
|
+
{
|
|
27080
|
+
code: "M002",
|
|
27081
|
+
title: "Top queries by rows processed",
|
|
27082
|
+
description: "Queries touching most rows",
|
|
27083
|
+
category: "queries",
|
|
27084
|
+
sort_order: null,
|
|
27085
|
+
is_system_report: false
|
|
27086
|
+
},
|
|
27087
|
+
{
|
|
27088
|
+
code: "M003",
|
|
27089
|
+
title: "Top queries by I/O time",
|
|
27090
|
+
description: "Queries with highest I/O wait",
|
|
27091
|
+
category: "queries",
|
|
27092
|
+
sort_order: null,
|
|
27093
|
+
is_system_report: false
|
|
27094
|
+
},
|
|
27095
|
+
{
|
|
27096
|
+
code: "N001",
|
|
27097
|
+
title: "Wait events by type and query",
|
|
27098
|
+
description: "Wait event analysis",
|
|
27099
|
+
category: "waits",
|
|
27100
|
+
sort_order: null,
|
|
27101
|
+
is_system_report: false
|
|
27102
|
+
}
|
|
27103
|
+
];
|
|
27104
|
+
|
|
27105
|
+
// lib/checkup-dictionary.ts
|
|
27106
|
+
var dictionaryByCode = new Map(CHECKUP_DICTIONARY_DATA.map((entry) => [entry.code, entry]));
|
|
27107
|
+
function buildCheckInfoMap() {
|
|
27108
|
+
const result = {};
|
|
27109
|
+
for (const entry of CHECKUP_DICTIONARY_DATA) {
|
|
27110
|
+
result[entry.code] = entry.title;
|
|
27111
|
+
}
|
|
27112
|
+
return result;
|
|
27113
|
+
}
|
|
27114
|
+
|
|
26485
27115
|
// lib/checkup.ts
|
|
26486
27116
|
var __dirname = "/builds/postgres-ai/postgres_ai/cli/lib";
|
|
26487
27117
|
var SECONDS_PER_DAY = 86400;
|
|
@@ -27238,19 +27868,15 @@ var REPORT_GENERATORS = {
|
|
|
27238
27868
|
H002: generateH002,
|
|
27239
27869
|
H004: generateH004
|
|
27240
27870
|
};
|
|
27241
|
-
var CHECK_INFO = {
|
|
27242
|
-
|
|
27243
|
-
|
|
27244
|
-
|
|
27245
|
-
|
|
27246
|
-
|
|
27247
|
-
|
|
27248
|
-
|
|
27249
|
-
|
|
27250
|
-
H001: "Invalid indexes",
|
|
27251
|
-
H002: "Unused indexes",
|
|
27252
|
-
H004: "Redundant indexes"
|
|
27253
|
-
};
|
|
27871
|
+
var CHECK_INFO = (() => {
|
|
27872
|
+
const fullMap = buildCheckInfoMap();
|
|
27873
|
+
const expressCheckIds = Object.keys(REPORT_GENERATORS);
|
|
27874
|
+
const filtered = {};
|
|
27875
|
+
for (const checkId of expressCheckIds) {
|
|
27876
|
+
filtered[checkId] = fullMap[checkId] || checkId;
|
|
27877
|
+
}
|
|
27878
|
+
return filtered;
|
|
27879
|
+
})();
|
|
27254
27880
|
async function generateAllReports(client, nodeName = "node-01", onProgress) {
|
|
27255
27881
|
const reports = {};
|
|
27256
27882
|
const entries = Object.entries(REPORT_GENERATORS);
|
|
@@ -28229,12 +28855,16 @@ program2.command("prepare-db [conn]").description("prepare database for monitori
|
|
|
28229
28855
|
if (errAny.code === "42501") {
|
|
28230
28856
|
if (failedStep === "01.role") {
|
|
28231
28857
|
console.error(" Context: role creation/update requires CREATEROLE or superuser");
|
|
28232
|
-
} else if (failedStep === "
|
|
28858
|
+
} else if (failedStep === "03.permissions") {
|
|
28233
28859
|
console.error(" Context: grants/view/search_path require sufficient GRANT/DDL privileges");
|
|
28234
28860
|
}
|
|
28235
28861
|
console.error(" Fix: ensure your Supabase access token has sufficient permissions");
|
|
28236
28862
|
console.error(" Tip: run with --print-sql to review the exact SQL plan");
|
|
28237
28863
|
}
|
|
28864
|
+
if (errAny.code === "42P06" || message.includes("already exists") && failedStep === "03.permissions") {
|
|
28865
|
+
console.error(" Hint: postgres_ai schema or objects already exist from a previous setup.");
|
|
28866
|
+
console.error(" Fix: run 'postgresai unprepare-db <connection>' first to clean up, then retry prepare-db.");
|
|
28867
|
+
}
|
|
28238
28868
|
}
|
|
28239
28869
|
if (errAny && typeof errAny === "object" && typeof errAny.httpStatus === "number") {
|
|
28240
28870
|
if (errAny.httpStatus === 401) {
|
|
@@ -28498,13 +29128,258 @@ program2.command("prepare-db [conn]").description("prepare database for monitori
|
|
|
28498
29128
|
if (errAny.code === "42501") {
|
|
28499
29129
|
if (failedStep === "01.role") {
|
|
28500
29130
|
console.error(" Context: role creation/update requires CREATEROLE or superuser");
|
|
28501
|
-
} else if (failedStep === "
|
|
29131
|
+
} else if (failedStep === "03.permissions") {
|
|
28502
29132
|
console.error(" Context: grants/view/search_path require sufficient GRANT/DDL privileges");
|
|
28503
29133
|
}
|
|
28504
29134
|
console.error(" Fix: connect as a superuser (or a role with CREATEROLE and sufficient GRANT privileges)");
|
|
28505
29135
|
console.error(" Fix: on managed Postgres, use the provider's admin/master user");
|
|
28506
29136
|
console.error(" Tip: run with --print-sql to review the exact SQL plan");
|
|
28507
29137
|
}
|
|
29138
|
+
if (errAny.code === "42P06" || message.includes("already exists") && failedStep === "03.permissions") {
|
|
29139
|
+
console.error(" Hint: postgres_ai schema or objects already exist from a previous setup.");
|
|
29140
|
+
console.error(" Fix: run 'postgresai unprepare-db <connection>' first to clean up, then retry prepare-db.");
|
|
29141
|
+
}
|
|
29142
|
+
if (errAny.code === "ECONNREFUSED") {
|
|
29143
|
+
console.error(" Hint: check host/port and ensure Postgres is reachable from this machine");
|
|
29144
|
+
}
|
|
29145
|
+
if (errAny.code === "ENOTFOUND") {
|
|
29146
|
+
console.error(" Hint: DNS resolution failed; double-check the host name");
|
|
29147
|
+
}
|
|
29148
|
+
if (errAny.code === "ETIMEDOUT") {
|
|
29149
|
+
console.error(" Hint: connection timed out; check network/firewall rules");
|
|
29150
|
+
}
|
|
29151
|
+
}
|
|
29152
|
+
process.exitCode = 1;
|
|
29153
|
+
}
|
|
29154
|
+
} finally {
|
|
29155
|
+
if (client) {
|
|
29156
|
+
try {
|
|
29157
|
+
await client.end();
|
|
29158
|
+
} catch {}
|
|
29159
|
+
}
|
|
29160
|
+
}
|
|
29161
|
+
});
|
|
29162
|
+
program2.command("unprepare-db [conn]").description("remove monitoring setup: drop monitoring user, views, schema, and revoke permissions").option("--db-url <url>", "PostgreSQL connection URL (admin) (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 remove", DEFAULT_MONITORING_USER).option("--keep-role", "Keep the monitoring role (only revoke permissions and drop objects)", false).option("--provider <provider>", "Database provider (e.g., supabase). Affects which steps are executed.").option("--print-sql", "Print SQL plan and exit (no changes applied)", false).option("--force", "Skip confirmation prompt", false).option("--json", "Output result as JSON (machine-readable)", false).addHelpText("after", [
|
|
29163
|
+
"",
|
|
29164
|
+
"Examples:",
|
|
29165
|
+
" postgresai unprepare-db postgresql://admin@host:5432/dbname",
|
|
29166
|
+
' postgresai unprepare-db "dbname=dbname host=host user=admin"',
|
|
29167
|
+
" postgresai unprepare-db -h host -p 5432 -U admin -d dbname",
|
|
29168
|
+
"",
|
|
29169
|
+
"Admin password:",
|
|
29170
|
+
" --admin-password <password> or PGPASSWORD=... (libpq standard)",
|
|
29171
|
+
"",
|
|
29172
|
+
"Keep role but remove objects/permissions:",
|
|
29173
|
+
" postgresai unprepare-db <conn> --keep-role",
|
|
29174
|
+
"",
|
|
29175
|
+
"Inspect SQL without applying changes:",
|
|
29176
|
+
" postgresai unprepare-db <conn> --print-sql",
|
|
29177
|
+
"",
|
|
29178
|
+
"Offline SQL plan (no DB connection):",
|
|
29179
|
+
" postgresai unprepare-db --print-sql",
|
|
29180
|
+
"",
|
|
29181
|
+
"Skip confirmation prompt:",
|
|
29182
|
+
" postgresai unprepare-db <conn> --force"
|
|
29183
|
+
].join(`
|
|
29184
|
+
`)).action(async (conn, opts, cmd) => {
|
|
29185
|
+
const jsonOutput = opts.json;
|
|
29186
|
+
const outputJson = (data) => {
|
|
29187
|
+
console.log(JSON.stringify(data, null, 2));
|
|
29188
|
+
};
|
|
29189
|
+
const outputError = (error2) => {
|
|
29190
|
+
if (jsonOutput) {
|
|
29191
|
+
outputJson({
|
|
29192
|
+
success: false,
|
|
29193
|
+
error: error2
|
|
29194
|
+
});
|
|
29195
|
+
} else {
|
|
29196
|
+
console.error(`Error: unprepare-db: ${error2.message}`);
|
|
29197
|
+
if (error2.step)
|
|
29198
|
+
console.error(` Step: ${error2.step}`);
|
|
29199
|
+
if (error2.code)
|
|
29200
|
+
console.error(` Code: ${error2.code}`);
|
|
29201
|
+
if (error2.detail)
|
|
29202
|
+
console.error(` Detail: ${error2.detail}`);
|
|
29203
|
+
if (error2.hint)
|
|
29204
|
+
console.error(` Hint: ${error2.hint}`);
|
|
29205
|
+
}
|
|
29206
|
+
process.exitCode = 1;
|
|
29207
|
+
};
|
|
29208
|
+
const shouldPrintSql = !!opts.printSql;
|
|
29209
|
+
const dropRole = !opts.keepRole;
|
|
29210
|
+
const providerWarning = validateProvider(opts.provider);
|
|
29211
|
+
if (providerWarning) {
|
|
29212
|
+
console.warn(`\u26A0 ${providerWarning}`);
|
|
29213
|
+
}
|
|
29214
|
+
if (!conn && !opts.dbUrl && !opts.host && !opts.port && !opts.username && !opts.adminPassword) {
|
|
29215
|
+
if (shouldPrintSql) {
|
|
29216
|
+
const database = (opts.dbname ?? process.env.PGDATABASE ?? "postgres").trim();
|
|
29217
|
+
const plan = await buildUninitPlan({
|
|
29218
|
+
database,
|
|
29219
|
+
monitoringUser: opts.monitoringUser,
|
|
29220
|
+
dropRole,
|
|
29221
|
+
provider: opts.provider
|
|
29222
|
+
});
|
|
29223
|
+
console.log(`
|
|
29224
|
+
--- SQL plan (offline; not connected) ---`);
|
|
29225
|
+
console.log(`-- database: ${database}`);
|
|
29226
|
+
console.log(`-- monitoring user: ${opts.monitoringUser}`);
|
|
29227
|
+
console.log(`-- provider: ${opts.provider ?? "self-managed"}`);
|
|
29228
|
+
console.log(`-- drop role: ${dropRole}`);
|
|
29229
|
+
for (const step of plan.steps) {
|
|
29230
|
+
console.log(`
|
|
29231
|
+
-- ${step.name}`);
|
|
29232
|
+
console.log(step.sql);
|
|
29233
|
+
}
|
|
29234
|
+
console.log(`
|
|
29235
|
+
--- end SQL plan ---
|
|
29236
|
+
`);
|
|
29237
|
+
return;
|
|
29238
|
+
}
|
|
29239
|
+
}
|
|
29240
|
+
let adminConn;
|
|
29241
|
+
try {
|
|
29242
|
+
adminConn = resolveAdminConnection({
|
|
29243
|
+
conn,
|
|
29244
|
+
dbUrlFlag: opts.dbUrl,
|
|
29245
|
+
host: opts.host ?? process.env.PGHOST,
|
|
29246
|
+
port: opts.port ?? process.env.PGPORT,
|
|
29247
|
+
username: opts.username ?? process.env.PGUSER,
|
|
29248
|
+
dbname: opts.dbname ?? process.env.PGDATABASE,
|
|
29249
|
+
adminPassword: opts.adminPassword,
|
|
29250
|
+
envPassword: process.env.PGPASSWORD
|
|
29251
|
+
});
|
|
29252
|
+
} catch (e) {
|
|
29253
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
29254
|
+
if (jsonOutput) {
|
|
29255
|
+
outputError({ message: msg });
|
|
29256
|
+
} else {
|
|
29257
|
+
console.error(`Error: unprepare-db: ${msg}`);
|
|
29258
|
+
if (typeof msg === "string" && msg.startsWith("Connection is required.")) {
|
|
29259
|
+
console.error("");
|
|
29260
|
+
cmd.outputHelp({ error: true });
|
|
29261
|
+
}
|
|
29262
|
+
process.exitCode = 1;
|
|
29263
|
+
}
|
|
29264
|
+
return;
|
|
29265
|
+
}
|
|
29266
|
+
if (!jsonOutput) {
|
|
29267
|
+
console.log(`Connecting to: ${adminConn.display}`);
|
|
29268
|
+
console.log(`Monitoring user: ${opts.monitoringUser}`);
|
|
29269
|
+
console.log(`Drop role: ${dropRole}`);
|
|
29270
|
+
}
|
|
29271
|
+
if (!opts.force && !jsonOutput && !shouldPrintSql) {
|
|
29272
|
+
const answer = await new Promise((resolve6) => {
|
|
29273
|
+
const readline = getReadline();
|
|
29274
|
+
readline.question(`This will remove the monitoring setup for user "${opts.monitoringUser}"${dropRole ? " and drop the role" : ""}. Continue? [y/N] `, (ans) => resolve6(ans.trim().toLowerCase()));
|
|
29275
|
+
});
|
|
29276
|
+
if (answer !== "y" && answer !== "yes") {
|
|
29277
|
+
console.log("Aborted.");
|
|
29278
|
+
return;
|
|
29279
|
+
}
|
|
29280
|
+
}
|
|
29281
|
+
let client;
|
|
29282
|
+
try {
|
|
29283
|
+
const connResult = await connectWithSslFallback(Client, adminConn);
|
|
29284
|
+
client = connResult.client;
|
|
29285
|
+
const dbRes = await client.query("select current_database() as db");
|
|
29286
|
+
const database = dbRes.rows?.[0]?.db;
|
|
29287
|
+
if (typeof database !== "string" || !database) {
|
|
29288
|
+
throw new Error("Failed to resolve current database name");
|
|
29289
|
+
}
|
|
29290
|
+
const plan = await buildUninitPlan({
|
|
29291
|
+
database,
|
|
29292
|
+
monitoringUser: opts.monitoringUser,
|
|
29293
|
+
dropRole,
|
|
29294
|
+
provider: opts.provider
|
|
29295
|
+
});
|
|
29296
|
+
if (shouldPrintSql) {
|
|
29297
|
+
console.log(`
|
|
29298
|
+
--- SQL plan ---`);
|
|
29299
|
+
for (const step of plan.steps) {
|
|
29300
|
+
console.log(`
|
|
29301
|
+
-- ${step.name}`);
|
|
29302
|
+
console.log(step.sql);
|
|
29303
|
+
}
|
|
29304
|
+
console.log(`
|
|
29305
|
+
--- end SQL plan ---
|
|
29306
|
+
`);
|
|
29307
|
+
return;
|
|
29308
|
+
}
|
|
29309
|
+
const { applied, errors: errors3 } = await applyUninitPlan({ client, plan });
|
|
29310
|
+
if (jsonOutput) {
|
|
29311
|
+
outputJson({
|
|
29312
|
+
success: errors3.length === 0,
|
|
29313
|
+
action: "unprepare",
|
|
29314
|
+
database,
|
|
29315
|
+
monitoringUser: opts.monitoringUser,
|
|
29316
|
+
dropRole,
|
|
29317
|
+
applied,
|
|
29318
|
+
errors: errors3
|
|
29319
|
+
});
|
|
29320
|
+
if (errors3.length > 0) {
|
|
29321
|
+
process.exitCode = 1;
|
|
29322
|
+
}
|
|
29323
|
+
} else {
|
|
29324
|
+
if (errors3.length === 0) {
|
|
29325
|
+
console.log("\u2713 unprepare-db completed");
|
|
29326
|
+
console.log(`Applied ${applied.length} steps`);
|
|
29327
|
+
} else {
|
|
29328
|
+
console.log("\u26A0 unprepare-db completed with errors");
|
|
29329
|
+
console.log(`Applied ${applied.length} steps`);
|
|
29330
|
+
console.log("Errors:");
|
|
29331
|
+
for (const err of errors3) {
|
|
29332
|
+
console.log(` - ${err}`);
|
|
29333
|
+
}
|
|
29334
|
+
process.exitCode = 1;
|
|
29335
|
+
}
|
|
29336
|
+
}
|
|
29337
|
+
} catch (error2) {
|
|
29338
|
+
const errAny = error2;
|
|
29339
|
+
let message = "";
|
|
29340
|
+
if (error2 instanceof Error && error2.message) {
|
|
29341
|
+
message = error2.message;
|
|
29342
|
+
} else if (errAny && typeof errAny === "object" && typeof errAny.message === "string" && errAny.message) {
|
|
29343
|
+
message = errAny.message;
|
|
29344
|
+
} else {
|
|
29345
|
+
message = String(error2);
|
|
29346
|
+
}
|
|
29347
|
+
if (!message || message === "[object Object]") {
|
|
29348
|
+
message = "Unknown error";
|
|
29349
|
+
}
|
|
29350
|
+
const errorObj = { message };
|
|
29351
|
+
if (errAny && typeof errAny === "object") {
|
|
29352
|
+
if (typeof errAny.code === "string" && errAny.code)
|
|
29353
|
+
errorObj.code = errAny.code;
|
|
29354
|
+
if (typeof errAny.detail === "string" && errAny.detail)
|
|
29355
|
+
errorObj.detail = errAny.detail;
|
|
29356
|
+
if (typeof errAny.hint === "string" && errAny.hint)
|
|
29357
|
+
errorObj.hint = errAny.hint;
|
|
29358
|
+
}
|
|
29359
|
+
if (jsonOutput) {
|
|
29360
|
+
outputJson({
|
|
29361
|
+
success: false,
|
|
29362
|
+
error: errorObj
|
|
29363
|
+
});
|
|
29364
|
+
process.exitCode = 1;
|
|
29365
|
+
} else {
|
|
29366
|
+
console.error(`Error: unprepare-db: ${message}`);
|
|
29367
|
+
if (errAny && typeof errAny === "object") {
|
|
29368
|
+
if (typeof errAny.code === "string" && errAny.code) {
|
|
29369
|
+
console.error(` Code: ${errAny.code}`);
|
|
29370
|
+
}
|
|
29371
|
+
if (typeof errAny.detail === "string" && errAny.detail) {
|
|
29372
|
+
console.error(` Detail: ${errAny.detail}`);
|
|
29373
|
+
}
|
|
29374
|
+
if (typeof errAny.hint === "string" && errAny.hint) {
|
|
29375
|
+
console.error(` Hint: ${errAny.hint}`);
|
|
29376
|
+
}
|
|
29377
|
+
}
|
|
29378
|
+
if (errAny && typeof errAny === "object" && typeof errAny.code === "string") {
|
|
29379
|
+
if (errAny.code === "42501") {
|
|
29380
|
+
console.error(" Context: dropping roles/objects requires sufficient privileges");
|
|
29381
|
+
console.error(" Fix: connect as a superuser (or a role with appropriate DROP privileges)");
|
|
29382
|
+
}
|
|
28508
29383
|
if (errAny.code === "ECONNREFUSED") {
|
|
28509
29384
|
console.error(" Hint: check host/port and ensure Postgres is reachable from this machine");
|
|
28510
29385
|
}
|
|
@@ -28523,6 +29398,7 @@ program2.command("prepare-db [conn]").description("prepare database for monitori
|
|
|
28523
29398
|
await client.end();
|
|
28524
29399
|
} catch {}
|
|
28525
29400
|
}
|
|
29401
|
+
closeReadline();
|
|
28526
29402
|
}
|
|
28527
29403
|
});
|
|
28528
29404
|
program2.command("checkup [conn]").description("generate health check reports directly from PostgreSQL (express mode)").option("--check-id <id>", `specific check to run: ${Object.keys(CHECK_INFO).join(", ")}, or ALL`, "ALL").option("--node-name <name>", "node name for reports", "node-01").option("--output <path>", "output directory for JSON files").option("--[no-]upload", "upload JSON results to PostgresAI (default: enabled; requires API key)", undefined).option("--project <project>", "project name or ID for remote upload (used with --upload; defaults to config defaultProject; auto-generated on first run)").option("--json", "output JSON to stdout (implies --no-upload)").addHelpText("after", [
|
|
@@ -28651,14 +29527,14 @@ async function resolveOrInitPaths() {
|
|
|
28651
29527
|
}
|
|
28652
29528
|
function isDockerRunning() {
|
|
28653
29529
|
try {
|
|
28654
|
-
const result = spawnSync2("docker", ["info"], { stdio: "pipe" });
|
|
29530
|
+
const result = spawnSync2("docker", ["info"], { stdio: "pipe", timeout: 5000 });
|
|
28655
29531
|
return result.status === 0;
|
|
28656
29532
|
} catch {
|
|
28657
29533
|
return false;
|
|
28658
29534
|
}
|
|
28659
29535
|
}
|
|
28660
29536
|
function getComposeCmd() {
|
|
28661
|
-
const tryCmd = (cmd, args) => spawnSync2(cmd, args, { stdio: "ignore" }).status === 0;
|
|
29537
|
+
const tryCmd = (cmd, args) => spawnSync2(cmd, args, { stdio: "ignore", timeout: 5000 }).status === 0;
|
|
28662
29538
|
if (tryCmd("docker-compose", ["version"]))
|
|
28663
29539
|
return ["docker-compose"];
|
|
28664
29540
|
if (tryCmd("docker", ["compose", "version"]))
|
|
@@ -28667,7 +29543,7 @@ function getComposeCmd() {
|
|
|
28667
29543
|
}
|
|
28668
29544
|
function checkRunningContainers() {
|
|
28669
29545
|
try {
|
|
28670
|
-
const result = spawnSync2("docker", ["ps", "--filter", "name=grafana-with-datasources", "--filter", "name=pgwatch", "--format", "{{.Names}}"], { stdio: "pipe", encoding: "utf8" });
|
|
29546
|
+
const result = spawnSync2("docker", ["ps", "--filter", "name=grafana-with-datasources", "--filter", "name=pgwatch", "--format", "{{.Names}}"], { stdio: "pipe", encoding: "utf8", timeout: 5000 });
|
|
28671
29547
|
if (result.status === 0 && result.stdout) {
|
|
28672
29548
|
const containers = result.stdout.trim().split(`
|
|
28673
29549
|
`).filter(Boolean);
|