@@ -2443,9 +2443,44 @@ interface MonitoringRegistration {
24432443 *
24442444 * Never throws — registration is best-effort; returns null on failure.
24452445 */
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+
24462481async function registerMonitoringInstance (
24472482 apiKey : string ,
2448- projectName : string ,
2483+ projectName : string | undefined ,
24492484 opts ?: { apiBaseUrl ?: string ; debug ?: boolean ; instanceId ?: string ; retries ?: number ; retryDelayMs ?: number }
24502485) : Promise < MonitoringRegistration | null > {
24512486 const { apiBaseUrl } = resolveBaseUrls ( opts ) ;
@@ -2457,16 +2492,25 @@ async function registerMonitoringInstance(
24572492 // moment to recover; skipped before the first attempt. Tests pass 0.
24582493 const retryDelayMs = opts ?. retryDelayMs ?? 400 ;
24592494
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+
24602500 if ( debug ) {
24612501 console . error ( `\nDebug: Registering monitoring instance...` ) ;
24622502 console . error ( `Debug: POST ${ url } ` ) ;
2463- console . error ( `Debug: project_name=${ projectName } ${ instanceId ? ` instance_id=${ instanceId } ` : "" } ` ) ;
2503+ console . error (
2504+ `Debug: ${ hasProjectName ? `project_name=${ projectName } ` : "project_name=(omitted)" } ${ instanceId ? ` instance_id=${ instanceId } ` : "" } `
2505+ ) ;
24642506 }
24652507
24662508 const requestBody : Record < string , string > = {
24672509 api_token : apiKey ,
2468- project_name : projectName ,
24692510 } ;
2511+ if ( hasProjectName ) {
2512+ requestBody . project_name = projectName as string ;
2513+ }
24702514 if ( instanceId ) {
24712515 requestBody . instance_id = instanceId ;
24722516 }
@@ -3137,8 +3181,11 @@ mon
31373181 // and persisted; the legacy self-registration stays fire-and-forget
31383182 // (issue platform-all#311).
31393183 if ( apiKey && ! opts . demo ) {
3140- const projectName = opts . project || "postgres-ai-monitoring" ;
31413184 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.
31423189 if ( instanceId ) {
31433190 const reg = await registerMonitoringInstance ( apiKey , projectName , {
31443191 apiBaseUrl : globalOpts . apiBaseUrl ,
@@ -3160,11 +3207,17 @@ mon
31603207 // Request succeeded but carried no usable project field — don't claim
31613208 // adoption, but don't report a hard failure either (no re-run needed).
31623209 console . error (
3163- `⚠ Adopted provisioned instance ${ instanceId } but the platform returned no project — reports will use project '${ projectName } '`
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>` )
31643214 ) ;
31653215 } else {
31663216 console . error (
3167- `⚠ Could not adopt provisioned instance ${ instanceId } — reports will use project '${ projectName } ' until 'postgresai mon local-install' is re-run`
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>` )
31683221 ) ;
31693222 }
31703223
@@ -3188,6 +3241,17 @@ mon
31883241 `⚠ AAS auto-collection not registered (${ aas . reason } ); it can be enabled later by re-running 'postgresai mon local-install'\n`
31893242 ) ;
31903243 }
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 ;
31913255 } else {
31923256 void registerMonitoringInstance ( apiKey , projectName , {
31933257 apiBaseUrl : globalOpts . apiBaseUrl ,
@@ -5488,3 +5552,4 @@ if (import.meta.main) {
54885552// same functions used by the `mon` commands).
54895553export { refreshBundledComposeIfStale , readDeployedTag , isValidComposeYaml } ;
54905554export { registerMonitoringInstance , resolveAdoptedProject , type MonitoringRegistration } ;
5555+ export { planMonitoringRegistration , type MonRegistrationPlan } ;
0 commit comments