postgresai 0.16.0-dev.1 → 0.16.0-dev.2
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 +71 -6
- package/dist/bin/postgres-ai.js +26 -8
- package/package.json +1 -1
- package/test/monitoring.test.ts +54 -3
package/bin/postgres-ai.ts
CHANGED
|
@@ -2443,9 +2443,44 @@ interface MonitoringRegistration {
|
|
|
2443
2443
|
*
|
|
2444
2444
|
* Never throws — registration is best-effort; returns null on failure.
|
|
2445
2445
|
*/
|
|
2446
|
+
|
|
2447
|
+
/**
|
|
2448
|
+
* Classify how `mon local-install` should register the monitoring instance,
|
|
2449
|
+
* given the raw `--project` value and the resolved instance id.
|
|
2450
|
+
*
|
|
2451
|
+
* - With an instance id: ADOPT the provisioned instance. No project name is
|
|
2452
|
+
* required (the platform returns the real project); a provided name is
|
|
2453
|
+
* normalized and carried through for messaging/fallback.
|
|
2454
|
+
* - No instance id and no project name: ERROR. The hardcoded
|
|
2455
|
+
* "postgres-ai-monitoring" default was removed, and a nameless legacy
|
|
2456
|
+
* self-registration is rejected by v1.monitoring_instance_register (PT400).
|
|
2457
|
+
* - No instance id but a project name: legacy SELF-REGISTER.
|
|
2458
|
+
*
|
|
2459
|
+
* Pure (no I/O) so the decision is unit-testable independently of the large
|
|
2460
|
+
* install command. `projectName` is the trimmed value, or undefined when empty.
|
|
2461
|
+
*/
|
|
2462
|
+
type MonRegistrationPlan =
|
|
2463
|
+
| { kind: "adopt"; projectName: string | undefined }
|
|
2464
|
+
| { kind: "self-register"; projectName: string }
|
|
2465
|
+
| { kind: "error-missing-project"; projectName: undefined };
|
|
2466
|
+
|
|
2467
|
+
function planMonitoringRegistration(args: {
|
|
2468
|
+
project?: string;
|
|
2469
|
+
instanceId?: string;
|
|
2470
|
+
}): MonRegistrationPlan {
|
|
2471
|
+
const projectName = args.project?.trim() || undefined;
|
|
2472
|
+
if (args.instanceId) {
|
|
2473
|
+
return { kind: "adopt", projectName };
|
|
2474
|
+
}
|
|
2475
|
+
if (!projectName) {
|
|
2476
|
+
return { kind: "error-missing-project", projectName: undefined };
|
|
2477
|
+
}
|
|
2478
|
+
return { kind: "self-register", projectName };
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2446
2481
|
async function registerMonitoringInstance(
|
|
2447
2482
|
apiKey: string,
|
|
2448
|
-
projectName: string,
|
|
2483
|
+
projectName: string | undefined,
|
|
2449
2484
|
opts?: { apiBaseUrl?: string; debug?: boolean; instanceId?: string; retries?: number; retryDelayMs?: number }
|
|
2450
2485
|
): Promise<MonitoringRegistration | null> {
|
|
2451
2486
|
const { apiBaseUrl } = resolveBaseUrls(opts);
|
|
@@ -2457,16 +2492,25 @@ async function registerMonitoringInstance(
|
|
|
2457
2492
|
// moment to recover; skipped before the first attempt. Tests pass 0.
|
|
2458
2493
|
const retryDelayMs = opts?.retryDelayMs ?? 400;
|
|
2459
2494
|
|
|
2495
|
+
// Omit project_name entirely when empty: the adopt path (instance_id present)
|
|
2496
|
+
// relies on the platform returning the real project, and a nameless legacy
|
|
2497
|
+
// self-registration is rejected by v1.monitoring_instance_register (PT400).
|
|
2498
|
+
const hasProjectName = !!(projectName && projectName.trim());
|
|
2499
|
+
|
|
2460
2500
|
if (debug) {
|
|
2461
2501
|
console.error(`\nDebug: Registering monitoring instance...`);
|
|
2462
2502
|
console.error(`Debug: POST ${url}`);
|
|
2463
|
-
console.error(
|
|
2503
|
+
console.error(
|
|
2504
|
+
`Debug: ${hasProjectName ? `project_name=${projectName}` : "project_name=(omitted)"}${instanceId ? ` instance_id=${instanceId}` : ""}`
|
|
2505
|
+
);
|
|
2464
2506
|
}
|
|
2465
2507
|
|
|
2466
2508
|
const requestBody: Record<string, string> = {
|
|
2467
2509
|
api_token: apiKey,
|
|
2468
|
-
project_name: projectName,
|
|
2469
2510
|
};
|
|
2511
|
+
if (hasProjectName) {
|
|
2512
|
+
requestBody.project_name = projectName as string;
|
|
2513
|
+
}
|
|
2470
2514
|
if (instanceId) {
|
|
2471
2515
|
requestBody.instance_id = instanceId;
|
|
2472
2516
|
}
|
|
@@ -3137,8 +3181,11 @@ mon
|
|
|
3137
3181
|
// and persisted; the legacy self-registration stays fire-and-forget
|
|
3138
3182
|
// (issue platform-all#311).
|
|
3139
3183
|
if (apiKey && !opts.demo) {
|
|
3140
|
-
const projectName = opts.project || "postgres-ai-monitoring";
|
|
3141
3184
|
const instanceId = opts.instanceId || process.env.PGAI_INSTANCE_ID;
|
|
3185
|
+
const plan = planMonitoringRegistration({ project: opts.project, instanceId });
|
|
3186
|
+
const projectName = plan.projectName;
|
|
3187
|
+
// `instanceId` truthy ⟺ plan.kind === "adopt"; branch on it directly so
|
|
3188
|
+
// TypeScript narrows instanceId to a defined string in the adopt path.
|
|
3142
3189
|
if (instanceId) {
|
|
3143
3190
|
const reg = await registerMonitoringInstance(apiKey, projectName, {
|
|
3144
3191
|
apiBaseUrl: globalOpts.apiBaseUrl,
|
|
@@ -3160,11 +3207,17 @@ mon
|
|
|
3160
3207
|
// Request succeeded but carried no usable project field — don't claim
|
|
3161
3208
|
// adoption, but don't report a hard failure either (no re-run needed).
|
|
3162
3209
|
console.error(
|
|
3163
|
-
`⚠ Adopted provisioned instance ${instanceId} but the platform returned no project
|
|
3210
|
+
`⚠ Adopted provisioned instance ${instanceId} but the platform returned no project` +
|
|
3211
|
+
(projectName
|
|
3212
|
+
? ` — reports will use project '${projectName}'`
|
|
3213
|
+
: ` — reports will have no project until 'postgresai mon local-install' is re-run with --project <name>`)
|
|
3164
3214
|
);
|
|
3165
3215
|
} else {
|
|
3166
3216
|
console.error(
|
|
3167
|
-
`⚠ Could not adopt provisioned instance ${instanceId}
|
|
3217
|
+
`⚠ Could not adopt provisioned instance ${instanceId}` +
|
|
3218
|
+
(projectName
|
|
3219
|
+
? ` — reports will use project '${projectName}' until 'postgresai mon local-install' is re-run`
|
|
3220
|
+
: ` — reports will have no project until 'postgresai mon local-install' is re-run with --project <name>`)
|
|
3168
3221
|
);
|
|
3169
3222
|
}
|
|
3170
3223
|
|
|
@@ -3188,6 +3241,17 @@ mon
|
|
|
3188
3241
|
`⚠ AAS auto-collection not registered (${aas.reason}); it can be enabled later by re-running 'postgresai mon local-install'\n`
|
|
3189
3242
|
);
|
|
3190
3243
|
}
|
|
3244
|
+
} else if (plan.kind === "error-missing-project") {
|
|
3245
|
+
// Legacy self-registration (no --instance-id) now requires a project
|
|
3246
|
+
// name: the hardcoded "postgres-ai-monitoring" default was removed, and
|
|
3247
|
+
// v1.monitoring_instance_register raises PT400 for a nameless legacy
|
|
3248
|
+
// registration. Console-provisioned installs should adopt with
|
|
3249
|
+
// --instance-id instead.
|
|
3250
|
+
console.error(
|
|
3251
|
+
"✗ A project name is required for self-registration (the 'postgres-ai-monitoring' default was removed). " +
|
|
3252
|
+
"Re-run with --project <name>, or adopt a console-provisioned instance with --instance-id <uuid>."
|
|
3253
|
+
);
|
|
3254
|
+
process.exitCode = 1;
|
|
3191
3255
|
} else {
|
|
3192
3256
|
void registerMonitoringInstance(apiKey, projectName, {
|
|
3193
3257
|
apiBaseUrl: globalOpts.apiBaseUrl,
|
|
@@ -5488,3 +5552,4 @@ if (import.meta.main) {
|
|
|
5488
5552
|
// same functions used by the `mon` commands).
|
|
5489
5553
|
export { refreshBundledComposeIfStale, readDeployedTag, isValidComposeYaml };
|
|
5490
5554
|
export { registerMonitoringInstance, resolveAdoptedProject, type MonitoringRegistration };
|
|
5555
|
+
export { planMonitoringRegistration, type MonRegistrationPlan };
|
package/dist/bin/postgres-ai.js
CHANGED
|
@@ -13425,7 +13425,7 @@ var {
|
|
|
13425
13425
|
// package.json
|
|
13426
13426
|
var package_default = {
|
|
13427
13427
|
name: "postgresai",
|
|
13428
|
-
version: "0.16.0-dev.
|
|
13428
|
+
version: "0.16.0-dev.2",
|
|
13429
13429
|
description: "postgres_ai CLI",
|
|
13430
13430
|
license: "Apache-2.0",
|
|
13431
13431
|
private: false,
|
|
@@ -16256,7 +16256,7 @@ var Result = import_lib.default.Result;
|
|
|
16256
16256
|
var TypeOverrides = import_lib.default.TypeOverrides;
|
|
16257
16257
|
var defaults = import_lib.default.defaults;
|
|
16258
16258
|
// package.json
|
|
16259
|
-
var version = "0.16.0-dev.
|
|
16259
|
+
var version = "0.16.0-dev.2";
|
|
16260
16260
|
var package_default2 = {
|
|
16261
16261
|
name: "postgresai",
|
|
16262
16262
|
version,
|
|
@@ -35659,6 +35659,16 @@ function checkRunningContainers() {
|
|
|
35659
35659
|
return { running: false, containers: [] };
|
|
35660
35660
|
}
|
|
35661
35661
|
}
|
|
35662
|
+
function planMonitoringRegistration(args) {
|
|
35663
|
+
const projectName = args.project?.trim() || undefined;
|
|
35664
|
+
if (args.instanceId) {
|
|
35665
|
+
return { kind: "adopt", projectName };
|
|
35666
|
+
}
|
|
35667
|
+
if (!projectName) {
|
|
35668
|
+
return { kind: "error-missing-project", projectName: undefined };
|
|
35669
|
+
}
|
|
35670
|
+
return { kind: "self-register", projectName };
|
|
35671
|
+
}
|
|
35662
35672
|
async function registerMonitoringInstance(apiKey, projectName, opts) {
|
|
35663
35673
|
const { apiBaseUrl } = resolveBaseUrls2(opts);
|
|
35664
35674
|
const url = `${apiBaseUrl}/rpc/monitoring_instance_register`;
|
|
@@ -35666,16 +35676,19 @@ async function registerMonitoringInstance(apiKey, projectName, opts) {
|
|
|
35666
35676
|
const instanceId = opts?.instanceId;
|
|
35667
35677
|
const retries = opts?.retries ?? (instanceId ? 1 : 0);
|
|
35668
35678
|
const retryDelayMs = opts?.retryDelayMs ?? 400;
|
|
35679
|
+
const hasProjectName = !!(projectName && projectName.trim());
|
|
35669
35680
|
if (debug) {
|
|
35670
35681
|
console.error(`
|
|
35671
35682
|
Debug: Registering monitoring instance...`);
|
|
35672
35683
|
console.error(`Debug: POST ${url}`);
|
|
35673
|
-
console.error(`Debug: project_name=${projectName}${instanceId ? ` instance_id=${instanceId}` : ""}`);
|
|
35684
|
+
console.error(`Debug: ${hasProjectName ? `project_name=${projectName}` : "project_name=(omitted)"}${instanceId ? ` instance_id=${instanceId}` : ""}`);
|
|
35674
35685
|
}
|
|
35675
35686
|
const requestBody = {
|
|
35676
|
-
api_token: apiKey
|
|
35677
|
-
project_name: projectName
|
|
35687
|
+
api_token: apiKey
|
|
35678
35688
|
};
|
|
35689
|
+
if (hasProjectName) {
|
|
35690
|
+
requestBody.project_name = projectName;
|
|
35691
|
+
}
|
|
35679
35692
|
if (instanceId) {
|
|
35680
35693
|
requestBody.instance_id = instanceId;
|
|
35681
35694
|
}
|
|
@@ -36218,8 +36231,9 @@ Searched: ${demoCandidates.join(", ")}
|
|
|
36218
36231
|
console.log(`\u2713 Services started
|
|
36219
36232
|
`);
|
|
36220
36233
|
if (apiKey && !opts.demo) {
|
|
36221
|
-
const projectName = opts.project || "postgres-ai-monitoring";
|
|
36222
36234
|
const instanceId = opts.instanceId || process.env.PGAI_INSTANCE_ID;
|
|
36235
|
+
const plan = planMonitoringRegistration({ project: opts.project, instanceId });
|
|
36236
|
+
const projectName = plan.projectName;
|
|
36223
36237
|
if (instanceId) {
|
|
36224
36238
|
const reg = await registerMonitoringInstance(apiKey, projectName, {
|
|
36225
36239
|
apiBaseUrl: globalOpts.apiBaseUrl,
|
|
@@ -36235,9 +36249,9 @@ Searched: ${demoCandidates.join(", ")}
|
|
|
36235
36249
|
console.log(`\u2713 ${verb} monitoring instance (project: ${adoptedProject})
|
|
36236
36250
|
`);
|
|
36237
36251
|
} else if (reg) {
|
|
36238
|
-
console.error(`\u26A0 Adopted provisioned instance ${instanceId} but the platform returned no project \u2014 reports will use project '${projectName}'`);
|
|
36252
|
+
console.error(`\u26A0 Adopted provisioned instance ${instanceId} but the platform returned no project` + (projectName ? ` \u2014 reports will use project '${projectName}'` : ` \u2014 reports will have no project until 'postgresai mon local-install' is re-run with --project <name>`));
|
|
36239
36253
|
} else {
|
|
36240
|
-
console.error(`\u26A0 Could not adopt provisioned instance ${instanceId} \u2014 reports will use project '${projectName}' until 'postgresai mon local-install' is re-run`);
|
|
36254
|
+
console.error(`\u26A0 Could not adopt provisioned instance ${instanceId}` + (projectName ? ` \u2014 reports will use project '${projectName}' until 'postgresai mon local-install' is re-run` : ` \u2014 reports will have no project until 'postgresai mon local-install' is re-run with --project <name>`));
|
|
36241
36255
|
}
|
|
36242
36256
|
const aas = await registerAasCollection(apiKey, instanceId, {
|
|
36243
36257
|
grafanaPassword,
|
|
@@ -36253,6 +36267,9 @@ Searched: ${demoCandidates.join(", ")}
|
|
|
36253
36267
|
console.error(`\u26A0 AAS auto-collection not registered (${aas.reason}); it can be enabled later by re-running 'postgresai mon local-install'
|
|
36254
36268
|
`);
|
|
36255
36269
|
}
|
|
36270
|
+
} else if (plan.kind === "error-missing-project") {
|
|
36271
|
+
console.error("\u2717 A project name is required for self-registration (the 'postgres-ai-monitoring' default was removed). " + "Re-run with --project <name>, or adopt a console-provisioned instance with --instance-id <uuid>.");
|
|
36272
|
+
process.exitCode = 1;
|
|
36256
36273
|
} else {
|
|
36257
36274
|
registerMonitoringInstance(apiKey, projectName, {
|
|
36258
36275
|
apiBaseUrl: globalOpts.apiBaseUrl,
|
|
@@ -38018,5 +38035,6 @@ export {
|
|
|
38018
38035
|
registerMonitoringInstance,
|
|
38019
38036
|
refreshBundledComposeIfStale,
|
|
38020
38037
|
readDeployedTag,
|
|
38038
|
+
planMonitoringRegistration,
|
|
38021
38039
|
isValidComposeYaml
|
|
38022
38040
|
};
|
package/package.json
CHANGED
package/test/monitoring.test.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
registerMonitoringInstance,
|
|
21
21
|
resolveAdoptedProject,
|
|
22
|
+
planMonitoringRegistration,
|
|
22
23
|
} from "../bin/postgres-ai";
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -241,12 +242,13 @@ describe("registerMonitoringInstance", () => {
|
|
|
241
242
|
});
|
|
242
243
|
|
|
243
244
|
// Issue platform-all#311: console-provisioned installs pass instance_id so
|
|
244
|
-
// the platform adopts the provisioned instance
|
|
245
|
-
//
|
|
245
|
+
// the platform adopts the provisioned instance and returns its real project
|
|
246
|
+
// — the CLI sends NO project_name on the adopt path (the hardcoded
|
|
247
|
+
// "postgres-ai-monitoring" default was removed).
|
|
246
248
|
test("includes instance_id in body when adopting a provisioned instance", async () => {
|
|
247
249
|
const instanceId = "019eb300-3f2a-7a75-b54d-4f10572b25b8";
|
|
248
250
|
|
|
249
|
-
await registerMonitoringInstance("key",
|
|
251
|
+
await registerMonitoringInstance("key", undefined, opts({ instanceId }));
|
|
250
252
|
|
|
251
253
|
const body = JSON.parse(fetchCalls[0].options.body as string);
|
|
252
254
|
expect(body.instance_id).toBe(instanceId);
|
|
@@ -255,6 +257,22 @@ describe("registerMonitoringInstance", () => {
|
|
|
255
257
|
expect(headers["instance-id"]).toBeUndefined();
|
|
256
258
|
});
|
|
257
259
|
|
|
260
|
+
test("omits project_name from the body when undefined (adopt path)", async () => {
|
|
261
|
+
await registerMonitoringInstance("key", undefined, opts({ instanceId: "i" }));
|
|
262
|
+
|
|
263
|
+
const body = JSON.parse(fetchCalls[0].options.body as string);
|
|
264
|
+
// The adopt path sends no name; the platform returns the real project.
|
|
265
|
+
expect("project_name" in body).toBe(false);
|
|
266
|
+
expect(body.api_token).toBe("key");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("omits project_name from the body when empty/whitespace", async () => {
|
|
270
|
+
await registerMonitoringInstance("key", " ", opts({ instanceId: "i" }));
|
|
271
|
+
|
|
272
|
+
const body = JSON.parse(fetchCalls[0].options.body as string);
|
|
273
|
+
expect("project_name" in body).toBe(false);
|
|
274
|
+
});
|
|
275
|
+
|
|
258
276
|
test("omits instance_id from the body for legacy self-registration", async () => {
|
|
259
277
|
await registerMonitoringInstance("key", "my-project", opts());
|
|
260
278
|
|
|
@@ -262,6 +280,8 @@ describe("registerMonitoringInstance", () => {
|
|
|
262
280
|
// PostgREST matches the 3-arg function via its default — the key must be
|
|
263
281
|
// ABSENT (not null) so legacy CLIs and the new one hit the same overload.
|
|
264
282
|
expect("instance_id" in body).toBe(false);
|
|
283
|
+
// A real project name still rides in the body for legacy registration.
|
|
284
|
+
expect(body.project_name).toBe("my-project");
|
|
265
285
|
});
|
|
266
286
|
|
|
267
287
|
test("a 200 with {project_id, project_name} returns a populated result", async () => {
|
|
@@ -323,6 +343,37 @@ describe("registerMonitoringInstance", () => {
|
|
|
323
343
|
});
|
|
324
344
|
});
|
|
325
345
|
|
|
346
|
+
describe("planMonitoringRegistration — mon local-install registration decision", () => {
|
|
347
|
+
test("instance id present → adopt, no project name required", () => {
|
|
348
|
+
const plan = planMonitoringRegistration({ instanceId: "i-123" });
|
|
349
|
+
expect(plan.kind).toBe("adopt");
|
|
350
|
+
expect(plan.projectName).toBeUndefined();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("instance id present + project → adopt, carries the trimmed name", () => {
|
|
354
|
+
const plan = planMonitoringRegistration({ instanceId: "i-123", project: " prod-db " });
|
|
355
|
+
expect(plan.kind).toBe("adopt");
|
|
356
|
+
expect(plan.projectName).toBe("prod-db");
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("no instance id + no project → error-missing-project (exit 1 path)", () => {
|
|
360
|
+
const plan = planMonitoringRegistration({});
|
|
361
|
+
expect(plan.kind).toBe("error-missing-project");
|
|
362
|
+
expect(plan.projectName).toBeUndefined();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("no instance id + empty/whitespace project → error-missing-project", () => {
|
|
366
|
+
expect(planMonitoringRegistration({ project: "" }).kind).toBe("error-missing-project");
|
|
367
|
+
expect(planMonitoringRegistration({ project: " " }).kind).toBe("error-missing-project");
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("no instance id + real project → legacy self-register with the trimmed name", () => {
|
|
371
|
+
const plan = planMonitoringRegistration({ project: " my-project " });
|
|
372
|
+
expect(plan.kind).toBe("self-register");
|
|
373
|
+
expect(plan.projectName).toBe("my-project");
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
326
377
|
describe("resolveAdoptedProject — what gets persisted to .pgwatch-config", () => {
|
|
327
378
|
test("prefers the numeric project_id over the name (survives renames)", () => {
|
|
328
379
|
expect(resolveAdoptedProject({ projectId: 42, projectName: "prod-db" })).toBe("42");
|