postgresai 0.14.0-dev.7 → 0.14.0-dev.70
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 +161 -61
- package/bin/postgres-ai.ts +1957 -404
- package/bun.lock +258 -0
- package/bunfig.toml +20 -0
- package/dist/bin/postgres-ai.js +29351 -1576
- package/dist/sql/01.role.sql +16 -0
- package/dist/sql/02.permissions.sql +37 -0
- package/dist/sql/03.optional_rds.sql +6 -0
- package/dist/sql/04.optional_self_managed.sql +8 -0
- package/dist/sql/05.helpers.sql +439 -0
- package/dist/sql/sql/01.role.sql +16 -0
- package/dist/sql/sql/02.permissions.sql +37 -0
- package/dist/sql/sql/03.optional_rds.sql +6 -0
- package/dist/sql/sql/04.optional_self_managed.sql +8 -0
- package/dist/sql/sql/05.helpers.sql +439 -0
- package/lib/auth-server.ts +124 -106
- package/lib/checkup-api.ts +386 -0
- package/lib/checkup.ts +1396 -0
- package/lib/config.ts +6 -3
- package/lib/init.ts +512 -156
- package/lib/issues.ts +400 -191
- package/lib/mcp-server.ts +213 -90
- package/lib/metrics-embedded.ts +79 -0
- package/lib/metrics-loader.ts +127 -0
- package/lib/supabase.ts +769 -0
- package/lib/util.ts +61 -0
- package/package.json +20 -10
- package/packages/postgres-ai/README.md +26 -0
- package/packages/postgres-ai/bin/postgres-ai.js +27 -0
- package/packages/postgres-ai/package.json +27 -0
- package/scripts/embed-metrics.ts +154 -0
- package/sql/01.role.sql +16 -0
- package/sql/02.permissions.sql +37 -0
- package/sql/03.optional_rds.sql +6 -0
- package/sql/04.optional_self_managed.sql +8 -0
- package/sql/05.helpers.sql +439 -0
- package/test/auth.test.ts +258 -0
- package/test/checkup.integration.test.ts +321 -0
- package/test/checkup.test.ts +1117 -0
- package/test/init.integration.test.ts +500 -0
- package/test/init.test.ts +527 -0
- package/test/issues.cli.test.ts +314 -0
- package/test/issues.test.ts +456 -0
- package/test/mcp-server.test.ts +988 -0
- package/test/schema-validation.test.ts +81 -0
- package/test/supabase.test.ts +568 -0
- package/test/test-utils.ts +128 -0
- package/tsconfig.json +12 -20
- package/dist/bin/postgres-ai.d.ts +0 -3
- package/dist/bin/postgres-ai.d.ts.map +0 -1
- package/dist/bin/postgres-ai.js.map +0 -1
- package/dist/lib/auth-server.d.ts +0 -31
- package/dist/lib/auth-server.d.ts.map +0 -1
- package/dist/lib/auth-server.js +0 -263
- package/dist/lib/auth-server.js.map +0 -1
- package/dist/lib/config.d.ts +0 -45
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/config.js +0 -181
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/init.d.ts +0 -61
- package/dist/lib/init.d.ts.map +0 -1
- package/dist/lib/init.js +0 -359
- package/dist/lib/init.js.map +0 -1
- package/dist/lib/issues.d.ts +0 -75
- package/dist/lib/issues.d.ts.map +0 -1
- package/dist/lib/issues.js +0 -336
- package/dist/lib/issues.js.map +0 -1
- package/dist/lib/mcp-server.d.ts +0 -9
- package/dist/lib/mcp-server.d.ts.map +0 -1
- package/dist/lib/mcp-server.js +0 -168
- package/dist/lib/mcp-server.js.map +0 -1
- package/dist/lib/pkce.d.ts +0 -32
- package/dist/lib/pkce.d.ts.map +0 -1
- package/dist/lib/pkce.js +0 -101
- package/dist/lib/pkce.js.map +0 -1
- package/dist/lib/util.d.ts +0 -27
- package/dist/lib/util.d.ts.map +0 -1
- package/dist/lib/util.js +0 -46
- package/dist/lib/util.js.map +0 -1
- package/dist/package.json +0 -46
- package/test/init.integration.test.cjs +0 -269
- package/test/init.test.cjs +0 -69
package/lib/issues.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { formatHttpError, maskSecret, normalizeBaseUrl } from "./util";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Issue status constants.
|
|
5
|
+
* Used in updateIssue to change issue state.
|
|
6
|
+
*/
|
|
7
|
+
export const IssueStatus = {
|
|
8
|
+
/** Issue is open and active */
|
|
9
|
+
OPEN: 0,
|
|
10
|
+
/** Issue is closed/resolved */
|
|
11
|
+
CLOSED: 1,
|
|
12
|
+
} as const;
|
|
4
13
|
|
|
5
14
|
export interface IssueActionItem {
|
|
6
15
|
id: string;
|
|
@@ -71,63 +80,38 @@ export async function fetchIssues(params: FetchIssuesParams): Promise<IssueListI
|
|
|
71
80
|
"access-token": apiKey,
|
|
72
81
|
"Prefer": "return=representation",
|
|
73
82
|
"Content-Type": "application/json",
|
|
83
|
+
"Connection": "close",
|
|
74
84
|
};
|
|
75
85
|
|
|
76
86
|
if (debug) {
|
|
77
87
|
const debugHeaders: Record<string, string> = { ...headers, "access-token": maskSecret(apiKey) };
|
|
78
|
-
// eslint-disable-next-line no-console
|
|
79
88
|
console.log(`Debug: Resolved API base URL: ${base}`);
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
89
|
console.log(`Debug: GET URL: ${url.toString()}`);
|
|
82
|
-
// eslint-disable-next-line no-console
|
|
83
90
|
console.log(`Debug: Auth scheme: access-token`);
|
|
84
|
-
// eslint-disable-next-line no-console
|
|
85
91
|
console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
method: "GET",
|
|
93
|
-
headers,
|
|
94
|
-
},
|
|
95
|
-
(res) => {
|
|
96
|
-
let data = "";
|
|
97
|
-
res.on("data", (chunk) => (data += chunk));
|
|
98
|
-
res.on("end", () => {
|
|
99
|
-
if (debug) {
|
|
100
|
-
// eslint-disable-next-line no-console
|
|
101
|
-
console.log(`Debug: Response status: ${res.statusCode}`);
|
|
102
|
-
// eslint-disable-next-line no-console
|
|
103
|
-
console.log(`Debug: Response headers: ${JSON.stringify(res.headers)}`);
|
|
104
|
-
}
|
|
105
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
106
|
-
try {
|
|
107
|
-
const parsed = JSON.parse(data) as IssueListItem[];
|
|
108
|
-
resolve(parsed);
|
|
109
|
-
} catch {
|
|
110
|
-
reject(new Error(`Failed to parse issues response: ${data}`));
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
let errMsg = `Failed to fetch issues: HTTP ${res.statusCode}`;
|
|
114
|
-
if (data) {
|
|
115
|
-
try {
|
|
116
|
-
const errObj = JSON.parse(data);
|
|
117
|
-
errMsg += `\n${JSON.stringify(errObj, null, 2)}`;
|
|
118
|
-
} catch {
|
|
119
|
-
errMsg += `\n${data}`;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
reject(new Error(errMsg));
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
req.on("error", (err: Error) => reject(err));
|
|
129
|
-
req.end();
|
|
94
|
+
const response = await fetch(url.toString(), {
|
|
95
|
+
method: "GET",
|
|
96
|
+
headers,
|
|
130
97
|
});
|
|
98
|
+
|
|
99
|
+
if (debug) {
|
|
100
|
+
console.log(`Debug: Response status: ${response.status}`);
|
|
101
|
+
console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const data = await response.text();
|
|
105
|
+
|
|
106
|
+
if (response.ok) {
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(data) as IssueListItem[];
|
|
109
|
+
} catch {
|
|
110
|
+
throw new Error(`Failed to parse issues response: ${data}`);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
throw new Error(formatHttpError("Failed to fetch issues", response.status, data));
|
|
114
|
+
}
|
|
131
115
|
}
|
|
132
116
|
|
|
133
117
|
|
|
@@ -154,63 +138,38 @@ export async function fetchIssueComments(params: FetchIssueCommentsParams): Prom
|
|
|
154
138
|
"access-token": apiKey,
|
|
155
139
|
"Prefer": "return=representation",
|
|
156
140
|
"Content-Type": "application/json",
|
|
141
|
+
"Connection": "close",
|
|
157
142
|
};
|
|
158
143
|
|
|
159
144
|
if (debug) {
|
|
160
145
|
const debugHeaders: Record<string, string> = { ...headers, "access-token": maskSecret(apiKey) };
|
|
161
|
-
// eslint-disable-next-line no-console
|
|
162
146
|
console.log(`Debug: Resolved API base URL: ${base}`);
|
|
163
|
-
// eslint-disable-next-line no-console
|
|
164
147
|
console.log(`Debug: GET URL: ${url.toString()}`);
|
|
165
|
-
// eslint-disable-next-line no-console
|
|
166
148
|
console.log(`Debug: Auth scheme: access-token`);
|
|
167
|
-
// eslint-disable-next-line no-console
|
|
168
149
|
console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
169
150
|
}
|
|
170
151
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{
|
|
175
|
-
method: "GET",
|
|
176
|
-
headers,
|
|
177
|
-
},
|
|
178
|
-
(res) => {
|
|
179
|
-
let data = "";
|
|
180
|
-
res.on("data", (chunk) => (data += chunk));
|
|
181
|
-
res.on("end", () => {
|
|
182
|
-
if (debug) {
|
|
183
|
-
// eslint-disable-next-line no-console
|
|
184
|
-
console.log(`Debug: Response status: ${res.statusCode}`);
|
|
185
|
-
// eslint-disable-next-line no-console
|
|
186
|
-
console.log(`Debug: Response headers: ${JSON.stringify(res.headers)}`);
|
|
187
|
-
}
|
|
188
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
189
|
-
try {
|
|
190
|
-
const parsed = JSON.parse(data) as IssueComment[];
|
|
191
|
-
resolve(parsed);
|
|
192
|
-
} catch {
|
|
193
|
-
reject(new Error(`Failed to parse issue comments response: ${data}`));
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
let errMsg = `Failed to fetch issue comments: HTTP ${res.statusCode}`;
|
|
197
|
-
if (data) {
|
|
198
|
-
try {
|
|
199
|
-
const errObj = JSON.parse(data);
|
|
200
|
-
errMsg += `\n${JSON.stringify(errObj, null, 2)}`;
|
|
201
|
-
} catch {
|
|
202
|
-
errMsg += `\n${data}`;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
reject(new Error(errMsg));
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
req.on("error", (err: Error) => reject(err));
|
|
212
|
-
req.end();
|
|
152
|
+
const response = await fetch(url.toString(), {
|
|
153
|
+
method: "GET",
|
|
154
|
+
headers,
|
|
213
155
|
});
|
|
156
|
+
|
|
157
|
+
if (debug) {
|
|
158
|
+
console.log(`Debug: Response status: ${response.status}`);
|
|
159
|
+
console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const data = await response.text();
|
|
163
|
+
|
|
164
|
+
if (response.ok) {
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(data) as IssueComment[];
|
|
167
|
+
} catch {
|
|
168
|
+
throw new Error(`Failed to parse issue comments response: ${data}`);
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
throw new Error(formatHttpError("Failed to fetch issue comments", response.status, data));
|
|
172
|
+
}
|
|
214
173
|
}
|
|
215
174
|
|
|
216
175
|
export interface FetchIssueParams {
|
|
@@ -239,67 +198,149 @@ export async function fetchIssue(params: FetchIssueParams): Promise<IssueDetail
|
|
|
239
198
|
"access-token": apiKey,
|
|
240
199
|
"Prefer": "return=representation",
|
|
241
200
|
"Content-Type": "application/json",
|
|
201
|
+
"Connection": "close",
|
|
242
202
|
};
|
|
243
203
|
|
|
244
204
|
if (debug) {
|
|
245
205
|
const debugHeaders: Record<string, string> = { ...headers, "access-token": maskSecret(apiKey) };
|
|
246
|
-
// eslint-disable-next-line no-console
|
|
247
206
|
console.log(`Debug: Resolved API base URL: ${base}`);
|
|
248
|
-
// eslint-disable-next-line no-console
|
|
249
207
|
console.log(`Debug: GET URL: ${url.toString()}`);
|
|
250
|
-
// eslint-disable-next-line no-console
|
|
251
208
|
console.log(`Debug: Auth scheme: access-token`);
|
|
252
|
-
// eslint-disable-next-line no-console
|
|
253
209
|
console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
254
210
|
}
|
|
255
211
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const parsed = JSON.parse(data);
|
|
276
|
-
if (Array.isArray(parsed)) {
|
|
277
|
-
resolve((parsed[0] as IssueDetail) ?? null);
|
|
278
|
-
} else {
|
|
279
|
-
resolve(parsed as IssueDetail);
|
|
280
|
-
}
|
|
281
|
-
} catch {
|
|
282
|
-
reject(new Error(`Failed to parse issue response: ${data}`));
|
|
283
|
-
}
|
|
284
|
-
} else {
|
|
285
|
-
let errMsg = `Failed to fetch issue: HTTP ${res.statusCode}`;
|
|
286
|
-
if (data) {
|
|
287
|
-
try {
|
|
288
|
-
const errObj = JSON.parse(data);
|
|
289
|
-
errMsg += `\n${JSON.stringify(errObj, null, 2)}`;
|
|
290
|
-
} catch {
|
|
291
|
-
errMsg += `\n${data}`;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
reject(new Error(errMsg));
|
|
295
|
-
}
|
|
296
|
-
});
|
|
212
|
+
const response = await fetch(url.toString(), {
|
|
213
|
+
method: "GET",
|
|
214
|
+
headers,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (debug) {
|
|
218
|
+
console.log(`Debug: Response status: ${response.status}`);
|
|
219
|
+
console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const data = await response.text();
|
|
223
|
+
|
|
224
|
+
if (response.ok) {
|
|
225
|
+
try {
|
|
226
|
+
const parsed = JSON.parse(data);
|
|
227
|
+
if (Array.isArray(parsed)) {
|
|
228
|
+
return (parsed[0] as IssueDetail) ?? null;
|
|
229
|
+
} else {
|
|
230
|
+
return parsed as IssueDetail;
|
|
297
231
|
}
|
|
298
|
-
|
|
232
|
+
} catch {
|
|
233
|
+
throw new Error(`Failed to parse issue response: ${data}`);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
throw new Error(formatHttpError("Failed to fetch issue", response.status, data));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface CreateIssueParams {
|
|
241
|
+
apiKey: string;
|
|
242
|
+
apiBaseUrl: string;
|
|
243
|
+
title: string;
|
|
244
|
+
orgId: number;
|
|
245
|
+
description?: string;
|
|
246
|
+
projectId?: number;
|
|
247
|
+
labels?: string[];
|
|
248
|
+
debug?: boolean;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface CreatedIssue {
|
|
252
|
+
id: string;
|
|
253
|
+
title: string;
|
|
254
|
+
description: string | null;
|
|
255
|
+
created_at: string;
|
|
256
|
+
status: number;
|
|
257
|
+
project_id: number | null;
|
|
258
|
+
labels: string[] | null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Create a new issue in the PostgresAI platform.
|
|
263
|
+
*
|
|
264
|
+
* @param params - The parameters for creating an issue
|
|
265
|
+
* @param params.apiKey - API key for authentication
|
|
266
|
+
* @param params.apiBaseUrl - Base URL for the API
|
|
267
|
+
* @param params.title - Issue title (required)
|
|
268
|
+
* @param params.orgId - Organization ID (required)
|
|
269
|
+
* @param params.description - Optional issue description
|
|
270
|
+
* @param params.projectId - Optional project ID to associate with
|
|
271
|
+
* @param params.labels - Optional array of label strings
|
|
272
|
+
* @param params.debug - Enable debug logging
|
|
273
|
+
* @returns The created issue object
|
|
274
|
+
* @throws Error if API key, title, or orgId is missing, or if the API call fails
|
|
275
|
+
*/
|
|
276
|
+
export async function createIssue(params: CreateIssueParams): Promise<CreatedIssue> {
|
|
277
|
+
const { apiKey, apiBaseUrl, title, orgId, description, projectId, labels, debug } = params;
|
|
278
|
+
if (!apiKey) {
|
|
279
|
+
throw new Error("API key is required");
|
|
280
|
+
}
|
|
281
|
+
if (!title) {
|
|
282
|
+
throw new Error("title is required");
|
|
283
|
+
}
|
|
284
|
+
if (typeof orgId !== "number") {
|
|
285
|
+
throw new Error("orgId is required");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const base = normalizeBaseUrl(apiBaseUrl);
|
|
289
|
+
const url = new URL(`${base}/rpc/issue_create`);
|
|
299
290
|
|
|
300
|
-
|
|
301
|
-
|
|
291
|
+
const bodyObj: Record<string, unknown> = {
|
|
292
|
+
title: title,
|
|
293
|
+
org_id: orgId,
|
|
294
|
+
};
|
|
295
|
+
if (description !== undefined) {
|
|
296
|
+
bodyObj.description = description;
|
|
297
|
+
}
|
|
298
|
+
if (projectId !== undefined) {
|
|
299
|
+
bodyObj.project_id = projectId;
|
|
300
|
+
}
|
|
301
|
+
if (labels && labels.length > 0) {
|
|
302
|
+
bodyObj.labels = labels;
|
|
303
|
+
}
|
|
304
|
+
const body = JSON.stringify(bodyObj);
|
|
305
|
+
|
|
306
|
+
const headers: Record<string, string> = {
|
|
307
|
+
"access-token": apiKey,
|
|
308
|
+
"Prefer": "return=representation",
|
|
309
|
+
"Content-Type": "application/json",
|
|
310
|
+
"Connection": "close",
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
if (debug) {
|
|
314
|
+
const debugHeaders: Record<string, string> = { ...headers, "access-token": maskSecret(apiKey) };
|
|
315
|
+
console.log(`Debug: Resolved API base URL: ${base}`);
|
|
316
|
+
console.log(`Debug: POST URL: ${url.toString()}`);
|
|
317
|
+
console.log(`Debug: Auth scheme: access-token`);
|
|
318
|
+
console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
319
|
+
console.log(`Debug: Request body: ${body}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const response = await fetch(url.toString(), {
|
|
323
|
+
method: "POST",
|
|
324
|
+
headers,
|
|
325
|
+
body,
|
|
302
326
|
});
|
|
327
|
+
|
|
328
|
+
if (debug) {
|
|
329
|
+
console.log(`Debug: Response status: ${response.status}`);
|
|
330
|
+
console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const data = await response.text();
|
|
334
|
+
|
|
335
|
+
if (response.ok) {
|
|
336
|
+
try {
|
|
337
|
+
return JSON.parse(data) as CreatedIssue;
|
|
338
|
+
} catch {
|
|
339
|
+
throw new Error(`Failed to parse create issue response: ${data}`);
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
throw new Error(formatHttpError("Failed to create issue", response.status, data));
|
|
343
|
+
}
|
|
303
344
|
}
|
|
304
345
|
|
|
305
346
|
export interface CreateIssueCommentParams {
|
|
@@ -339,67 +380,235 @@ export async function createIssueComment(params: CreateIssueCommentParams): Prom
|
|
|
339
380
|
"access-token": apiKey,
|
|
340
381
|
"Prefer": "return=representation",
|
|
341
382
|
"Content-Type": "application/json",
|
|
342
|
-
"
|
|
383
|
+
"Connection": "close",
|
|
343
384
|
};
|
|
344
385
|
|
|
345
386
|
if (debug) {
|
|
346
387
|
const debugHeaders: Record<string, string> = { ...headers, "access-token": maskSecret(apiKey) };
|
|
347
|
-
// eslint-disable-next-line no-console
|
|
348
388
|
console.log(`Debug: Resolved API base URL: ${base}`);
|
|
349
|
-
// eslint-disable-next-line no-console
|
|
350
389
|
console.log(`Debug: POST URL: ${url.toString()}`);
|
|
351
|
-
// eslint-disable-next-line no-console
|
|
352
390
|
console.log(`Debug: Auth scheme: access-token`);
|
|
353
|
-
// eslint-disable-next-line no-console
|
|
354
391
|
console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
355
|
-
// eslint-disable-next-line no-console
|
|
356
392
|
console.log(`Debug: Request body: ${body}`);
|
|
357
393
|
}
|
|
358
394
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
// eslint-disable-next-line no-console
|
|
372
|
-
console.log(`Debug: Response status: ${res.statusCode}`);
|
|
373
|
-
// eslint-disable-next-line no-console
|
|
374
|
-
console.log(`Debug: Response headers: ${JSON.stringify(res.headers)}`);
|
|
375
|
-
}
|
|
376
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
377
|
-
try {
|
|
378
|
-
const parsed = JSON.parse(data) as IssueComment;
|
|
379
|
-
resolve(parsed);
|
|
380
|
-
} catch {
|
|
381
|
-
reject(new Error(`Failed to parse create comment response: ${data}`));
|
|
382
|
-
}
|
|
383
|
-
} else {
|
|
384
|
-
let errMsg = `Failed to create issue comment: HTTP ${res.statusCode}`;
|
|
385
|
-
if (data) {
|
|
386
|
-
try {
|
|
387
|
-
const errObj = JSON.parse(data);
|
|
388
|
-
errMsg += `\n${JSON.stringify(errObj, null, 2)}`;
|
|
389
|
-
} catch {
|
|
390
|
-
errMsg += `\n${data}`;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
reject(new Error(errMsg));
|
|
394
|
-
}
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
);
|
|
395
|
+
const response = await fetch(url.toString(), {
|
|
396
|
+
method: "POST",
|
|
397
|
+
headers,
|
|
398
|
+
body,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
if (debug) {
|
|
402
|
+
console.log(`Debug: Response status: ${response.status}`);
|
|
403
|
+
console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const data = await response.text();
|
|
398
407
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
408
|
+
if (response.ok) {
|
|
409
|
+
try {
|
|
410
|
+
return JSON.parse(data) as IssueComment;
|
|
411
|
+
} catch {
|
|
412
|
+
throw new Error(`Failed to parse create comment response: ${data}`);
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
throw new Error(formatHttpError("Failed to create issue comment", response.status, data));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export interface UpdateIssueParams {
|
|
420
|
+
apiKey: string;
|
|
421
|
+
apiBaseUrl: string;
|
|
422
|
+
issueId: string;
|
|
423
|
+
title?: string;
|
|
424
|
+
description?: string;
|
|
425
|
+
status?: number;
|
|
426
|
+
labels?: string[];
|
|
427
|
+
debug?: boolean;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export interface UpdatedIssue {
|
|
431
|
+
id: string;
|
|
432
|
+
title: string;
|
|
433
|
+
description: string | null;
|
|
434
|
+
status: number;
|
|
435
|
+
updated_at: string;
|
|
436
|
+
labels: string[] | null;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Update an existing issue in the PostgresAI platform.
|
|
441
|
+
*
|
|
442
|
+
* @param params - The parameters for updating an issue
|
|
443
|
+
* @param params.apiKey - API key for authentication
|
|
444
|
+
* @param params.apiBaseUrl - Base URL for the API
|
|
445
|
+
* @param params.issueId - ID of the issue to update (required)
|
|
446
|
+
* @param params.title - New title (optional)
|
|
447
|
+
* @param params.description - New description (optional)
|
|
448
|
+
* @param params.status - New status: 0 = open, 1 = closed (optional)
|
|
449
|
+
* @param params.labels - New labels array (optional, replaces existing)
|
|
450
|
+
* @param params.debug - Enable debug logging
|
|
451
|
+
* @returns The updated issue object
|
|
452
|
+
* @throws Error if API key or issueId is missing, if no fields to update are provided, or if the API call fails
|
|
453
|
+
*/
|
|
454
|
+
export async function updateIssue(params: UpdateIssueParams): Promise<UpdatedIssue> {
|
|
455
|
+
const { apiKey, apiBaseUrl, issueId, title, description, status, labels, debug } = params;
|
|
456
|
+
if (!apiKey) {
|
|
457
|
+
throw new Error("API key is required");
|
|
458
|
+
}
|
|
459
|
+
if (!issueId) {
|
|
460
|
+
throw new Error("issueId is required");
|
|
461
|
+
}
|
|
462
|
+
if (title === undefined && description === undefined && status === undefined && labels === undefined) {
|
|
463
|
+
throw new Error("At least one field to update is required (title, description, status, or labels)");
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const base = normalizeBaseUrl(apiBaseUrl);
|
|
467
|
+
const url = new URL(`${base}/rpc/issue_update`);
|
|
468
|
+
|
|
469
|
+
// Prod RPC expects p_* argument names (see OpenAPI at /api/general/).
|
|
470
|
+
const bodyObj: Record<string, unknown> = {
|
|
471
|
+
p_id: issueId,
|
|
472
|
+
};
|
|
473
|
+
if (title !== undefined) {
|
|
474
|
+
bodyObj.p_title = title;
|
|
475
|
+
}
|
|
476
|
+
if (description !== undefined) {
|
|
477
|
+
bodyObj.p_description = description;
|
|
478
|
+
}
|
|
479
|
+
if (status !== undefined) {
|
|
480
|
+
bodyObj.p_status = status;
|
|
481
|
+
}
|
|
482
|
+
if (labels !== undefined) {
|
|
483
|
+
bodyObj.p_labels = labels;
|
|
484
|
+
}
|
|
485
|
+
const body = JSON.stringify(bodyObj);
|
|
486
|
+
|
|
487
|
+
const headers: Record<string, string> = {
|
|
488
|
+
"access-token": apiKey,
|
|
489
|
+
"Prefer": "return=representation",
|
|
490
|
+
"Content-Type": "application/json",
|
|
491
|
+
"Connection": "close",
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
if (debug) {
|
|
495
|
+
const debugHeaders: Record<string, string> = { ...headers, "access-token": maskSecret(apiKey) };
|
|
496
|
+
console.log(`Debug: Resolved API base URL: ${base}`);
|
|
497
|
+
console.log(`Debug: POST URL: ${url.toString()}`);
|
|
498
|
+
console.log(`Debug: Auth scheme: access-token`);
|
|
499
|
+
console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
500
|
+
console.log(`Debug: Request body: ${body}`);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const response = await fetch(url.toString(), {
|
|
504
|
+
method: "POST",
|
|
505
|
+
headers,
|
|
506
|
+
body,
|
|
402
507
|
});
|
|
508
|
+
|
|
509
|
+
if (debug) {
|
|
510
|
+
console.log(`Debug: Response status: ${response.status}`);
|
|
511
|
+
console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const data = await response.text();
|
|
515
|
+
|
|
516
|
+
if (response.ok) {
|
|
517
|
+
try {
|
|
518
|
+
return JSON.parse(data) as UpdatedIssue;
|
|
519
|
+
} catch {
|
|
520
|
+
throw new Error(`Failed to parse update issue response: ${data}`);
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
throw new Error(formatHttpError("Failed to update issue", response.status, data));
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export interface UpdateIssueCommentParams {
|
|
528
|
+
apiKey: string;
|
|
529
|
+
apiBaseUrl: string;
|
|
530
|
+
commentId: string;
|
|
531
|
+
content: string;
|
|
532
|
+
debug?: boolean;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export interface UpdatedIssueComment {
|
|
536
|
+
id: string;
|
|
537
|
+
issue_id: string;
|
|
538
|
+
content: string;
|
|
539
|
+
updated_at: string;
|
|
403
540
|
}
|
|
404
541
|
|
|
542
|
+
/**
|
|
543
|
+
* Update an existing issue comment in the PostgresAI platform.
|
|
544
|
+
*
|
|
545
|
+
* @param params - The parameters for updating a comment
|
|
546
|
+
* @param params.apiKey - API key for authentication
|
|
547
|
+
* @param params.apiBaseUrl - Base URL for the API
|
|
548
|
+
* @param params.commentId - ID of the comment to update (required)
|
|
549
|
+
* @param params.content - New comment content (required)
|
|
550
|
+
* @param params.debug - Enable debug logging
|
|
551
|
+
* @returns The updated comment object
|
|
552
|
+
* @throws Error if API key, commentId, or content is missing, or if the API call fails
|
|
553
|
+
*/
|
|
554
|
+
export async function updateIssueComment(params: UpdateIssueCommentParams): Promise<UpdatedIssueComment> {
|
|
555
|
+
const { apiKey, apiBaseUrl, commentId, content, debug } = params;
|
|
556
|
+
if (!apiKey) {
|
|
557
|
+
throw new Error("API key is required");
|
|
558
|
+
}
|
|
559
|
+
if (!commentId) {
|
|
560
|
+
throw new Error("commentId is required");
|
|
561
|
+
}
|
|
562
|
+
if (!content) {
|
|
563
|
+
throw new Error("content is required");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const base = normalizeBaseUrl(apiBaseUrl);
|
|
567
|
+
const url = new URL(`${base}/rpc/issue_comment_update`);
|
|
568
|
+
|
|
569
|
+
const bodyObj: Record<string, unknown> = {
|
|
570
|
+
// Prod RPC expects p_* argument names (see OpenAPI at /api/general/).
|
|
571
|
+
p_id: commentId,
|
|
572
|
+
p_content: content,
|
|
573
|
+
};
|
|
574
|
+
const body = JSON.stringify(bodyObj);
|
|
575
|
+
|
|
576
|
+
const headers: Record<string, string> = {
|
|
577
|
+
"access-token": apiKey,
|
|
578
|
+
"Prefer": "return=representation",
|
|
579
|
+
"Content-Type": "application/json",
|
|
580
|
+
"Connection": "close",
|
|
581
|
+
};
|
|
405
582
|
|
|
583
|
+
if (debug) {
|
|
584
|
+
const debugHeaders: Record<string, string> = { ...headers, "access-token": maskSecret(apiKey) };
|
|
585
|
+
console.log(`Debug: Resolved API base URL: ${base}`);
|
|
586
|
+
console.log(`Debug: POST URL: ${url.toString()}`);
|
|
587
|
+
console.log(`Debug: Auth scheme: access-token`);
|
|
588
|
+
console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
|
|
589
|
+
console.log(`Debug: Request body: ${body}`);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const response = await fetch(url.toString(), {
|
|
593
|
+
method: "POST",
|
|
594
|
+
headers,
|
|
595
|
+
body,
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
if (debug) {
|
|
599
|
+
console.log(`Debug: Response status: ${response.status}`);
|
|
600
|
+
console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const data = await response.text();
|
|
604
|
+
|
|
605
|
+
if (response.ok) {
|
|
606
|
+
try {
|
|
607
|
+
return JSON.parse(data) as UpdatedIssueComment;
|
|
608
|
+
} catch {
|
|
609
|
+
throw new Error(`Failed to parse update comment response: ${data}`);
|
|
610
|
+
}
|
|
611
|
+
} else {
|
|
612
|
+
throw new Error(formatHttpError("Failed to update issue comment", response.status, data));
|
|
613
|
+
}
|
|
614
|
+
}
|