postgresai 0.17.0-dev.0 → 0.17.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/README.md CHANGED
@@ -301,8 +301,8 @@ run `pgai orgs`) to discover the available ids.
301
301
  Tools exposed:
302
302
  - `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, hidden_only?, limit?, offset?, debug? }`).
303
303
  - `view_issue`: view a single issue with its comments (args: `{ issue_id, org_id, debug? }`).
304
- - `create_issue`: create a new issue (args: `{ title, description?, org_id, attachments?, debug? }`).
305
- - `update_issue`: update title/description/status/labels (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, debug? }`).
304
+ - `create_issue`: create a new issue (args: `{ title, description?, org_id, project_id?, labels?, attachments?, is_hidden?, debug? }`).
305
+ - `update_issue`: update title/description/status/labels/is_hidden (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, is_hidden?, debug? }`).
306
306
  - `post_issue_comment`: post a comment (args: `{ issue_id, org_id, content?, parent_comment_id?, attachments?, debug? }`).
307
307
  - `update_issue_comment`: update an existing comment (args: `{ comment_id, org_id, content?, attachments?, debug? }`).
308
308
  - `upload_file`: upload a local file and return the storage URL plus a ready-to-paste markdown link (args: `{ path, org_id, debug? }`).
@@ -353,8 +353,10 @@ sensitive.
353
353
  postgresai issues list # List issues (shows: id, title, status, created_at; is_hidden only when set)
354
354
  postgresai issues list --hidden-only # Only hidden issues (PostgresAI staff)
355
355
  postgresai issues view <issueId> # View issue details and comments
356
- postgresai issues create --org-id <id> --title <t> # Create a new issue
356
+ postgresai issues create --org-id <id> <title> # Create a new issue
357
+ postgresai issues create --org-id <id> <title> --hidden # Create a hidden issue (PostgresAI staff)
357
358
  postgresai issues update <issueId> [--title ... --status ...]# Update an existing issue
359
+ postgresai issues update <issueId> --hidden|--no-hidden # Hide / unhide an issue (PostgresAI staff)
358
360
  postgresai issues post-comment <issueId> <content> # Post a comment to an issue
359
361
  postgresai issues update-comment <commentId> <content> # Update an existing comment
360
362
  postgresai issues files upload <path> # Upload a file, print URL + markdown
@@ -370,7 +372,11 @@ postgresai issues files download <url> [-o <path>] # Download a file
370
372
  Hidden issues are staff-internal. `issues list` and `issues view` mark them
371
373
  with `is_hidden: true`; the key is omitted entirely otherwise, so ordinary
372
374
  issues look exactly as they always have. `--hidden-only` lists just the hidden
373
- ones, filtered server-side.
375
+ ones, filtered server-side. `issues create --hidden` creates one, and
376
+ `issues update --hidden` / `--no-hidden` hides or unhides an existing issue
377
+ (MCP: `is_hidden` on `create_issue` / `update_issue`). The CLI does no staff
378
+ check of its own: the platform refuses a visibility change for a non-staff
379
+ credential with a plain error, and the CLI prints it and exits non-zero.
374
380
 
375
381
  Staff access is granted per credential, not per person, and a credential that
376
382
  does not qualify simply sees nothing — `--hidden-only` returns an empty list
@@ -13524,7 +13524,7 @@ var {
13524
13524
  // package.json
13525
13525
  var package_default = {
13526
13526
  name: "postgresai",
13527
- version: "0.17.0-dev.0",
13527
+ version: "0.17.0-dev.2",
13528
13528
  description: "postgres_ai CLI",
13529
13529
  license: "Apache-2.0",
13530
13530
  private: false,
@@ -13721,6 +13721,23 @@ function deleteConfigKeys(keys) {
13721
13721
  }
13722
13722
  }
13723
13723
 
13724
+ // lib/stdout.ts
13725
+ function writeStdout(text) {
13726
+ return new Promise((resolve, reject) => {
13727
+ process.stdout.write(text, (err) => {
13728
+ if (err) {
13729
+ process.stdout.once("error", () => {});
13730
+ reject(err);
13731
+ return;
13732
+ }
13733
+ resolve();
13734
+ });
13735
+ });
13736
+ }
13737
+ function isEpipe(err) {
13738
+ return err?.code === "EPIPE";
13739
+ }
13740
+
13724
13741
  // node_modules/js-yaml/dist/js-yaml.mjs
13725
13742
  function getDefaultExportFromCjs(x) {
13726
13743
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -16867,7 +16884,7 @@ var Result = import_lib.default.Result;
16867
16884
  var TypeOverrides = import_lib.default.TypeOverrides;
16868
16885
  var defaults = import_lib.default.defaults;
16869
16886
  // package.json
16870
- var version = "0.17.0-dev.0";
16887
+ var version = "0.17.0-dev.2";
16871
16888
  var package_default2 = {
16872
16889
  name: "postgresai",
16873
16890
  version,
@@ -17228,9 +17245,9 @@ var ORG_ALIAS_HEADER = "x-pgai-org";
17228
17245
  var ORG_ID_HEADER = "x-pgai-org-id";
17229
17246
  class OrgScopeError extends Error {
17230
17247
  }
17231
- var activeOrgScope;
17248
+ var ACTIVE_ORG_SCOPE_KEY = Symbol.for("postgres-ai.cli.activeOrgScope");
17232
17249
  function getActiveOrgScope() {
17233
- return activeOrgScope;
17250
+ return globalThis[ACTIVE_ORG_SCOPE_KEY];
17234
17251
  }
17235
17252
  function orgScopeHeaders(scope) {
17236
17253
  if (!scope)
@@ -17246,7 +17263,7 @@ function buildAuthHeaders(apiKey, scope, extra = {}) {
17246
17263
  "access-token": apiKey,
17247
17264
  "Content-Type": "application/json",
17248
17265
  Connection: "close",
17249
- ...orgScopeHeaders(scope ?? activeOrgScope),
17266
+ ...orgScopeHeaders(scope ?? getActiveOrgScope()),
17250
17267
  ...extra
17251
17268
  };
17252
17269
  }
@@ -17456,7 +17473,7 @@ async function fetchIssue(params) {
17456
17473
  }
17457
17474
  }
17458
17475
  async function createIssue(params) {
17459
- const { apiKey, apiBaseUrl, orgScope, title, orgId, description, projectId, labels, debug } = params;
17476
+ const { apiKey, apiBaseUrl, orgScope, title, orgId, description, projectId, labels, hidden, debug } = params;
17460
17477
  if (!apiKey) {
17461
17478
  throw new Error("API key is required");
17462
17479
  }
@@ -17481,6 +17498,9 @@ async function createIssue(params) {
17481
17498
  if (labels && labels.length > 0) {
17482
17499
  bodyObj.labels = labels;
17483
17500
  }
17501
+ if (hidden === true) {
17502
+ bodyObj.is_hidden = true;
17503
+ }
17484
17504
  const body = JSON.stringify(bodyObj);
17485
17505
  const headers = issueRequestHeaders(apiKey, orgScope);
17486
17506
  if (debug) {
@@ -17562,15 +17582,15 @@ async function createIssueComment(params) {
17562
17582
  }
17563
17583
  }
17564
17584
  async function updateIssue(params) {
17565
- const { apiKey, apiBaseUrl, orgScope, issueId, title, description, status, labels, debug } = params;
17585
+ const { apiKey, apiBaseUrl, orgScope, issueId, title, description, status, labels, hidden, debug } = params;
17566
17586
  if (!apiKey) {
17567
17587
  throw new Error("API key is required");
17568
17588
  }
17569
17589
  if (!issueId) {
17570
17590
  throw new Error("issueId is required");
17571
17591
  }
17572
- if (title === undefined && description === undefined && status === undefined && labels === undefined) {
17573
- throw new Error("At least one field to update is required (title, description, status, or labels)");
17592
+ if (title === undefined && description === undefined && status === undefined && labels === undefined && hidden === undefined) {
17593
+ throw new Error("At least one field to update is required (title, description, status, labels, or hidden)");
17574
17594
  }
17575
17595
  const base = normalizeBaseUrl(apiBaseUrl);
17576
17596
  const url = new URL(`${base}/rpc/issue_update`);
@@ -17589,6 +17609,9 @@ async function updateIssue(params) {
17589
17609
  if (labels !== undefined) {
17590
17610
  bodyObj.p_labels = labels;
17591
17611
  }
17612
+ if (hidden !== undefined) {
17613
+ bodyObj.p_is_hidden = hidden;
17614
+ }
17592
17615
  const body = JSON.stringify(bodyObj);
17593
17616
  const headers = issueRequestHeaders(apiKey, orgScope);
17594
17617
  if (debug) {
@@ -24930,6 +24953,13 @@ var ORG_ID_TOOL_PROPERTY = {
24930
24953
  description: "Organization ID. Optional with a per-organization token (uses the stored org); REQUIRED with a global token, which can reach every organization the user belongs to and will not assume one. Use orgs_list / `pgai orgs` to discover valid values."
24931
24954
  }
24932
24955
  };
24956
+ function parseHiddenArg(raw) {
24957
+ if (raw === undefined || raw === null)
24958
+ return {};
24959
+ if (typeof raw === "boolean")
24960
+ return { value: raw };
24961
+ return { error: { content: [{ type: "text", text: "is_hidden must be a boolean" }], isError: true } };
24962
+ }
24933
24963
  async function handleToolCall(req, rootOpts, extra) {
24934
24964
  const toolName = req.params.name;
24935
24965
  const args = req.params.arguments || {};
@@ -25016,6 +25046,10 @@ async function handleToolCall(req, rootOpts, extra) {
25016
25046
  let description = rawDescription ? interpretEscapes(rawDescription) : undefined;
25017
25047
  const projectId = args.project_id !== undefined ? Number(args.project_id) : undefined;
25018
25048
  const labels = Array.isArray(args.labels) ? args.labels.map(String) : undefined;
25049
+ const hiddenArg = parseHiddenArg(args.is_hidden);
25050
+ if (hiddenArg.error)
25051
+ return hiddenArg.error;
25052
+ const hidden = hiddenArg.value === true;
25019
25053
  const attachments = Array.isArray(args.attachments) ? args.attachments.map(String).filter((p) => p.length > 0) : [];
25020
25054
  const scope = resolveMcpOrgScope(args, apiKey, cfg);
25021
25055
  if (scope.error)
@@ -25029,7 +25063,7 @@ async function handleToolCall(req, rootOpts, extra) {
25029
25063
  const uploaded = await uploadAttachments({ apiKey, orgScope: scope.orgScope, storageBaseUrl, attachmentPaths: attachments, debug });
25030
25064
  description = appendAttachmentsToContent(description ?? "", uploaded);
25031
25065
  }
25032
- const result = await createIssue({ apiKey, apiBaseUrl, title, orgId, orgScope: scope.orgScope, description, projectId, labels, debug });
25066
+ const result = await createIssue({ apiKey, apiBaseUrl, title, orgId, orgScope: scope.orgScope, description, projectId, labels, hidden, debug });
25033
25067
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25034
25068
  }
25035
25069
  if (toolName === "update_issue") {
@@ -25043,9 +25077,13 @@ async function handleToolCall(req, rootOpts, extra) {
25043
25077
  let description = rawDescription !== undefined ? interpretEscapes(rawDescription) : undefined;
25044
25078
  const status = args.status !== undefined ? Number(args.status) : undefined;
25045
25079
  const labels = Array.isArray(args.labels) ? args.labels.map(String) : undefined;
25080
+ const hiddenArg = parseHiddenArg(args.is_hidden);
25081
+ if (hiddenArg.error)
25082
+ return hiddenArg.error;
25083
+ const hidden = hiddenArg.value;
25046
25084
  const attachments = Array.isArray(args.attachments) ? args.attachments.map(String).filter((p) => p.length > 0) : [];
25047
- if (title === undefined && description === undefined && status === undefined && labels === undefined && attachments.length === 0) {
25048
- return { content: [{ type: "text", text: "At least one field to update is required (title, description, status, labels, or attachments)" }], isError: true };
25085
+ if (title === undefined && description === undefined && status === undefined && labels === undefined && hidden === undefined && attachments.length === 0) {
25086
+ return { content: [{ type: "text", text: "At least one field to update is required (title, description, status, labels, is_hidden, or attachments)" }], isError: true };
25049
25087
  }
25050
25088
  if (status !== undefined && (Number.isNaN(status) || status !== 0 && status !== 1)) {
25051
25089
  return { content: [{ type: "text", text: "status must be 0 (open) or 1 (closed)" }], isError: true };
@@ -25065,7 +25103,7 @@ async function handleToolCall(req, rootOpts, extra) {
25065
25103
  const uploaded = await uploadAttachments({ apiKey, orgScope: scope.orgScope, storageBaseUrl, attachmentPaths: attachments, debug });
25066
25104
  description = appendAttachmentsToContent(description, uploaded);
25067
25105
  }
25068
- const result = await updateIssue({ apiKey, apiBaseUrl, issueId, title, description, status, labels, orgScope: scope.orgScope, debug });
25106
+ const result = await updateIssue({ apiKey, apiBaseUrl, issueId, title, description, status, labels, hidden, orgScope: scope.orgScope, debug });
25069
25107
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25070
25108
  }
25071
25109
  if (toolName === "update_issue_comment") {
@@ -25340,6 +25378,7 @@ async function startMcpServer(rootOpts, extra) {
25340
25378
  items: { type: "string" },
25341
25379
  description: "Local file paths to upload and append as markdown links to the description (images render inline)"
25342
25380
  },
25381
+ is_hidden: { type: "boolean", description: "Create as a hidden, staff-internal issue (PostgresAI staff only; same as CLI --hidden)" },
25343
25382
  debug: { type: "boolean", description: "Enable verbose debug logs" }
25344
25383
  },
25345
25384
  required: ["title"],
@@ -25348,7 +25387,7 @@ async function startMcpServer(rootOpts, extra) {
25348
25387
  },
25349
25388
  {
25350
25389
  name: "update_issue",
25351
- description: "Update an existing issue (title, description, status, labels). Use status=1 to close, status=0 to reopen. Local files passed via 'attachments' are uploaded and appended to 'description'; if 'description' is omitted, the existing description is fetched first and appended to.",
25390
+ description: "Update an existing issue (title, description, status, labels, is_hidden). Use status=1 to close, status=0 to reopen. Local files passed via 'attachments' are uploaded and appended to 'description'; if 'description' is omitted, the existing description is fetched first and appended to.",
25352
25391
  inputSchema: {
25353
25392
  type: "object",
25354
25393
  properties: {
@@ -25367,6 +25406,7 @@ async function startMcpServer(rootOpts, extra) {
25367
25406
  items: { type: "string" },
25368
25407
  description: "Local file paths to upload and append as markdown links (images render inline). When provided without 'description', the existing description is fetched and appended to."
25369
25408
  },
25409
+ is_hidden: { type: "boolean", description: "true hides the issue, false unhides it (PostgresAI staff only; same as CLI --hidden / --no-hidden). Omit (or pass null) to leave unchanged." },
25370
25410
  debug: { type: "boolean", description: "Enable verbose debug logs" }
25371
25411
  },
25372
25412
  required: ["issue_id"],
@@ -25714,7 +25754,7 @@ async function fetchIssue2(params) {
25714
25754
  }
25715
25755
  }
25716
25756
  async function createIssue2(params) {
25717
- const { apiKey, apiBaseUrl, orgScope, title, orgId, description, projectId, labels, debug } = params;
25757
+ const { apiKey, apiBaseUrl, orgScope, title, orgId, description, projectId, labels, hidden, debug } = params;
25718
25758
  if (!apiKey) {
25719
25759
  throw new Error("API key is required");
25720
25760
  }
@@ -25739,6 +25779,9 @@ async function createIssue2(params) {
25739
25779
  if (labels && labels.length > 0) {
25740
25780
  bodyObj.labels = labels;
25741
25781
  }
25782
+ if (hidden === true) {
25783
+ bodyObj.is_hidden = true;
25784
+ }
25742
25785
  const body = JSON.stringify(bodyObj);
25743
25786
  const headers = issueRequestHeaders2(apiKey, orgScope);
25744
25787
  if (debug) {
@@ -25820,15 +25863,15 @@ async function createIssueComment2(params) {
25820
25863
  }
25821
25864
  }
25822
25865
  async function updateIssue2(params) {
25823
- const { apiKey, apiBaseUrl, orgScope, issueId, title, description, status, labels, debug } = params;
25866
+ const { apiKey, apiBaseUrl, orgScope, issueId, title, description, status, labels, hidden, debug } = params;
25824
25867
  if (!apiKey) {
25825
25868
  throw new Error("API key is required");
25826
25869
  }
25827
25870
  if (!issueId) {
25828
25871
  throw new Error("issueId is required");
25829
25872
  }
25830
- if (title === undefined && description === undefined && status === undefined && labels === undefined) {
25831
- throw new Error("At least one field to update is required (title, description, status, or labels)");
25873
+ if (title === undefined && description === undefined && status === undefined && labels === undefined && hidden === undefined) {
25874
+ throw new Error("At least one field to update is required (title, description, status, labels, or hidden)");
25832
25875
  }
25833
25876
  const base = normalizeBaseUrl(apiBaseUrl);
25834
25877
  const url = new URL(`${base}/rpc/issue_update`);
@@ -25847,6 +25890,9 @@ async function updateIssue2(params) {
25847
25890
  if (labels !== undefined) {
25848
25891
  bodyObj.p_labels = labels;
25849
25892
  }
25893
+ if (hidden !== undefined) {
25894
+ bodyObj.p_is_hidden = hidden;
25895
+ }
25850
25896
  const body = JSON.stringify(bodyObj);
25851
25897
  const headers = issueRequestHeaders2(apiKey, orgScope);
25852
25898
  if (debug) {
@@ -32145,12 +32191,12 @@ function requireOrgScope(opts = {}, apiKey) {
32145
32191
  }
32146
32192
  return { source: "token" };
32147
32193
  }
32148
- var activeOrgScope2;
32194
+ var ACTIVE_ORG_SCOPE_KEY2 = Symbol.for("postgres-ai.cli.activeOrgScope");
32149
32195
  function setActiveOrgScope(scope) {
32150
- activeOrgScope2 = scope;
32196
+ globalThis[ACTIVE_ORG_SCOPE_KEY2] = scope;
32151
32197
  }
32152
32198
  function getActiveOrgScope2() {
32153
- return activeOrgScope2;
32199
+ return globalThis[ACTIVE_ORG_SCOPE_KEY2];
32154
32200
  }
32155
32201
  function configOrgIdForBody(apiKey, cfgOrgId) {
32156
32202
  if (isGlobalTokenValue2(apiKey))
@@ -38522,16 +38568,32 @@ function addInstanceToFile(file, instance) {
38522
38568
  throw new Error(`Monitoring target '${instance.name}' already exists`);
38523
38569
  }
38524
38570
  existing.push(instance);
38525
- fs8.writeFileSync(file, dump2(existing), "utf8");
38571
+ writeInstancesFile(file, existing);
38526
38572
  }
38527
38573
  function removeInstanceFromFile(file, name) {
38528
38574
  const instances = loadInstances2(file);
38529
38575
  const filtered = instances.filter((i) => i.name !== name);
38530
38576
  if (filtered.length === instances.length)
38531
38577
  return false;
38532
- fs8.writeFileSync(file, dump2(filtered), "utf8");
38578
+ writeInstancesFile(file, filtered);
38533
38579
  return true;
38534
38580
  }
38581
+ function writeInstancesFile(file, instances) {
38582
+ const content = dump2(instances);
38583
+ const fd = fs8.openSync(file, "w", 384);
38584
+ try {
38585
+ try {
38586
+ fs8.fchmodSync(fd, 384);
38587
+ } catch (err) {
38588
+ const message = err instanceof Error ? err.message : String(err);
38589
+ console.error(`Warning: could not restrict permissions on ${file}: ${message}`);
38590
+ console.error(" It may be readable by other users on this machine.");
38591
+ }
38592
+ fs8.writeFileSync(fd, content, "utf8");
38593
+ } finally {
38594
+ fs8.closeSync(fd);
38595
+ }
38596
+ }
38535
38597
  function extractSslmode(connStr) {
38536
38598
  try {
38537
38599
  return (new URL(connStr).searchParams.get("sslmode") || "").toLowerCase();
@@ -40358,7 +40420,14 @@ Usage: postgresai checkup ${checkId} postgresql://user@host:5432/dbname
40358
40420
  }
40359
40421
  }
40360
40422
  if (shouldPrintJson && !outputPath) {
40361
- console.log(JSON.stringify(reports, null, 2));
40423
+ try {
40424
+ await writeStdout(JSON.stringify(reports, null, 2) + `
40425
+ `);
40426
+ } catch (err) {
40427
+ if (isEpipe(err))
40428
+ return;
40429
+ throw err;
40430
+ }
40362
40431
  }
40363
40432
  const hadOutput = shouldPrintJson || shouldConvertMarkdown || outputPath || uploadSummary;
40364
40433
  if (!hadOutput) {
@@ -42188,7 +42257,7 @@ withOrgOptions(issues.command("create <title>")).description("create a new issue
42188
42257
  }, []).option("--attach <path>", "attach a file (uploads to storage and appends a markdown link to the description; repeatable)", (value, previous) => {
42189
42258
  previous.push(value);
42190
42259
  return previous;
42191
- }, []).option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (rawTitle, opts) => {
42260
+ }, []).option("--hidden", "create as a hidden issue (PostgresAI staff)").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (rawTitle, opts) => {
42192
42261
  const rootOpts = program2.opts();
42193
42262
  const cfg = readConfig();
42194
42263
  const { apiKey } = getConfig(rootOpts);
@@ -42249,6 +42318,7 @@ withOrgOptions(issues.command("create <title>")).description("create a new issue
42249
42318
  description: augmentedDescription,
42250
42319
  projectId,
42251
42320
  labels,
42321
+ hidden: opts.hidden === true,
42252
42322
  debug: !!opts.debug
42253
42323
  });
42254
42324
  spinner.stop();
@@ -42266,7 +42336,7 @@ withOrgOptions(issues.command("update <issueId>")).description("update an existi
42266
42336
  }, []).option("--clear-labels", "set labels to an empty list").option("--attach <path>", "attach a file (uploads and appends a markdown link to --description; if --description is omitted the existing description is fetched and appended to; repeatable)", (value, previous) => {
42267
42337
  previous.push(value);
42268
42338
  return previous;
42269
- }, []).option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (issueId, opts) => {
42339
+ }, []).option("--hidden", "hide the issue (PostgresAI staff)").option("--no-hidden", "unhide the issue (PostgresAI staff)").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (issueId, opts) => {
42270
42340
  const rootOpts = program2.opts();
42271
42341
  const cfg = readConfig();
42272
42342
  const { apiKey } = getConfig(rootOpts);
@@ -42337,6 +42407,7 @@ withOrgOptions(issues.command("update <issueId>")).description("update an existi
42337
42407
  description,
42338
42408
  status,
42339
42409
  labels,
42410
+ hidden: typeof opts.hidden === "boolean" ? opts.hidden : undefined,
42340
42411
  debug: !!opts.debug
42341
42412
  });
42342
42413
  spinner.stop();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresai",
3
- "version": "0.17.0-dev.0",
3
+ "version": "0.17.0-dev.2",
4
4
  "description": "postgres_ai CLI",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,