@kolatts/pncli 1.22.0 → 1.24.0

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/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  setRepoConfigValue,
9
9
  writeGlobalConfig,
10
10
  writeRepoConfig
11
- } from "./chunk-FXY262BR.js";
11
+ } from "./chunk-KRDKHGYS.js";
12
12
  import {
13
13
  ExitCode,
14
14
  PncliError,
@@ -20,7 +20,7 @@ import {
20
20
  success,
21
21
  warn,
22
22
  writeRawOutput
23
- } from "./chunk-PZWGOMIQ.js";
23
+ } from "./chunk-ABHLES5M.js";
24
24
  import {
25
25
  AdoCoreClient
26
26
  } from "./chunk-VDL32PSG.js";
@@ -2235,6 +2235,68 @@ var GitHubClient = class {
2235
2235
  }
2236
2236
  });
2237
2237
  }
2238
+ async listReviewThreads(owner, repo, pullNumber) {
2239
+ const query = `
2240
+ query($owner: String!, $repo: String!, $number: Int!) {
2241
+ repository(owner: $owner, name: $repo) {
2242
+ pullRequest(number: $number) {
2243
+ reviewThreads(first: 100) {
2244
+ pageInfo { hasNextPage }
2245
+ nodes {
2246
+ id
2247
+ isResolved
2248
+ isOutdated
2249
+ path
2250
+ line
2251
+ comments(first: 10) {
2252
+ nodes {
2253
+ id
2254
+ databaseId
2255
+ body
2256
+ author { login }
2257
+ createdAt
2258
+ }
2259
+ }
2260
+ }
2261
+ }
2262
+ }
2263
+ }
2264
+ }
2265
+ `;
2266
+ const data = await this.http.githubGraphQL(query, { owner, repo, number: pullNumber });
2267
+ const threads = data.repository.pullRequest.reviewThreads;
2268
+ if (threads.pageInfo?.hasNextPage) {
2269
+ process.stderr.write(`warning: more than ${threads.nodes.length} review threads on PR #${pullNumber}; only showing the first ${threads.nodes.length}
2270
+ `);
2271
+ }
2272
+ return threads.nodes;
2273
+ }
2274
+ async resolveReviewThread(threadId) {
2275
+ const mutation = `
2276
+ mutation($threadId: ID!) {
2277
+ resolveReviewThread(input: { threadId: $threadId }) {
2278
+ thread {
2279
+ id
2280
+ isResolved
2281
+ isOutdated
2282
+ path
2283
+ line
2284
+ comments(first: 10) {
2285
+ nodes {
2286
+ id
2287
+ databaseId
2288
+ body
2289
+ author { login }
2290
+ createdAt
2291
+ }
2292
+ }
2293
+ }
2294
+ }
2295
+ }
2296
+ `;
2297
+ const data = await this.http.githubGraphQL(mutation, { threadId });
2298
+ return data.resolveReviewThread.thread;
2299
+ }
2238
2300
  };
2239
2301
 
2240
2302
  // src/services/github/commands.ts
@@ -2541,6 +2603,26 @@ function registerGitHubCommands(program2) {
2541
2603
  fail(err, "github", "list-reviewers", start);
2542
2604
  }
2543
2605
  });
2606
+ gh.command("list-review-threads").description("List review threads on a pull request (includes thread node IDs for resolve-thread)").requiredOption("--number <n>", "Pull request number").action(async (opts) => {
2607
+ const start = Date.now();
2608
+ try {
2609
+ const { client, owner, repo } = getClient3(gh);
2610
+ const data = await client.listReviewThreads(owner, repo, parseInt(opts.number, 10));
2611
+ success(data, "github", "list-review-threads", start);
2612
+ } catch (err) {
2613
+ fail(err, "github", "list-review-threads", start);
2614
+ }
2615
+ });
2616
+ gh.command("resolve-thread").description("Mark a review thread as resolved (use list-review-threads to get the thread id)").requiredOption("--thread-id <id>", "GraphQL node ID of the review thread (e.g. PRT_kwDO...)").action(async (opts) => {
2617
+ const start = Date.now();
2618
+ try {
2619
+ const { client } = getClient3(gh);
2620
+ const data = await client.resolveReviewThread(opts.threadId);
2621
+ success(data, "github", "resolve-thread", start);
2622
+ } catch (err) {
2623
+ fail(err, "github", "resolve-thread", start);
2624
+ }
2625
+ });
2544
2626
  gh.command("get-status").description("Get combined commit status for a ref (branch, tag, or SHA)").requiredOption("--ref <ref>", "Git ref (branch, tag, or commit SHA)").action(async (opts) => {
2545
2627
  const start = Date.now();
2546
2628
  try {
@@ -2778,6 +2860,28 @@ function xmlParseHint(err, bodyContent, convertedFromMarkdown = false) {
2778
2860
  return `Offending line ${row}${suffix}: ${line}
2779
2861
  ${pointer}`;
2780
2862
  }
2863
+ function storageFormatHint(err, bodyContent) {
2864
+ if (!(err instanceof PncliError) || err.status !== 500) return null;
2865
+ const hints = [];
2866
+ if (/<ac:parameter[^>]*(?<![:\w])name\s*=/i.test(bodyContent)) {
2867
+ hints.push(
2868
+ '<ac:parameter name="..."> found \u2014 use ac:name="..." instead (e.g. <ac:parameter ac:name="language">text</ac:parameter>).'
2869
+ );
2870
+ }
2871
+ const macroTagRe = /<ac:structured-macro(?:\s[^>]*)?\s*>/gi;
2872
+ let macroMatch;
2873
+ while ((macroMatch = macroTagRe.exec(bodyContent)) !== null) {
2874
+ if (!/\bac:macro-id\b/.test(macroMatch[0])) {
2875
+ hints.push(
2876
+ '<ac:structured-macro> without ac:macro-id found \u2014 add a unique UUID: ac:macro-id="<uuid>" (e.g. ac:macro-id="a1b2c3d4-...").'
2877
+ );
2878
+ break;
2879
+ }
2880
+ }
2881
+ if (hints.length === 0) return null;
2882
+ return `HTTP 500 \u2014 possible storage-format causes:
2883
+ ${hints.map((h) => ` \u2022 ${h}`).join("\n")}`;
2884
+ }
2781
2885
  function getClient4(program2) {
2782
2886
  const opts = program2.optsWithGlobals();
2783
2887
  const config = loadConfig({ configPath: opts.config });
@@ -2873,7 +2977,9 @@ function registerConfluenceCommands(program2) {
2873
2977
  });
2874
2978
  success(data, "confluence", "create-page", start);
2875
2979
  } catch (err) {
2876
- const hint = bodyContent ? xmlParseHint(err, bodyContent, Boolean(opts.markdown)) : null;
2980
+ const xmlHint = bodyContent ? xmlParseHint(err, bodyContent, Boolean(opts.markdown)) : null;
2981
+ const sfHint = bodyContent ? storageFormatHint(err, bodyContent) : null;
2982
+ const hint = [xmlHint, sfHint].filter(Boolean).join("\n\n") || null;
2877
2983
  if (hint) {
2878
2984
  const msg = err instanceof Error ? err.message : String(err);
2879
2985
  const status = err instanceof PncliError ? err.status : 1;
@@ -2905,7 +3011,9 @@ ${hint}`, status, url), "confluence", "create-page", start);
2905
3011
  });
2906
3012
  success(data, "confluence", "update-page", start);
2907
3013
  } catch (err) {
2908
- const hint = bodyContent ? xmlParseHint(err, bodyContent, Boolean(opts.markdown)) : null;
3014
+ const xmlHint = bodyContent ? xmlParseHint(err, bodyContent, Boolean(opts.markdown)) : null;
3015
+ const sfHint = bodyContent ? storageFormatHint(err, bodyContent) : null;
3016
+ const hint = [xmlHint, sfHint].filter(Boolean).join("\n\n") || null;
2909
3017
  if (hint) {
2910
3018
  const msg = err instanceof Error ? err.message : String(err);
2911
3019
  const status = err instanceof PncliError ? err.status : 1;
@@ -5724,7 +5832,7 @@ function registerConfigCommands(program2) {
5724
5832
  }
5725
5833
  if (cfg.checkmarx.baseUrl && cfg.checkmarx.tenantName && (cfg.checkmarx.apiKey || cfg.checkmarx.clientId && cfg.checkmarx.clientSecret)) {
5726
5834
  try {
5727
- await http.checkmarx("/api/projects", { params: { limit: 1 } });
5835
+ await http.checkmarx("projects", { params: { limit: 1 } });
5728
5836
  results.checkmarx = { ok: true, message: "connected" };
5729
5837
  } catch (err) {
5730
5838
  results.checkmarx = { ok: false, message: err instanceof Error ? err.message : String(err) };
@@ -5804,6 +5912,16 @@ function registerConfigCommands(program2) {
5804
5912
  } else {
5805
5913
  results.dynatrace_platform = { ok: null, message: "not configured" };
5806
5914
  }
5915
+ if (cfg.logscale.baseUrl && cfg.logscale.token) {
5916
+ try {
5917
+ await http.logscale("/api/v1/repositories");
5918
+ results.logscale = { ok: true, message: "connected" };
5919
+ } catch (err) {
5920
+ results.logscale = { ok: false, message: err instanceof Error ? err.message : String(err) };
5921
+ }
5922
+ } else {
5923
+ results.logscale = { ok: null, message: "not configured" };
5924
+ }
5807
5925
  success(results, "config", "test", start);
5808
5926
  } catch (err) {
5809
5927
  fail(err, "config", "test", start);
@@ -5956,7 +6074,7 @@ function registerConfigCommands(program2) {
5956
6074
  results.checkmarx = { status: "error", message: "baseUrl not configured" };
5957
6075
  } else {
5958
6076
  try {
5959
- await http.checkmarx("/api/projects", { params: { limit: 1 }, timeoutMs: 1e4 });
6077
+ await http.checkmarx("projects", { params: { limit: 1 }, timeoutMs: 1e4 });
5960
6078
  results.checkmarx = { status: "valid", message: "ok" };
5961
6079
  } catch (err) {
5962
6080
  results.checkmarx = categorize2(err);
@@ -6048,11 +6166,23 @@ function registerConfigCommands(program2) {
6048
6166
  results.dynatrace_platform = categorize2(err);
6049
6167
  }
6050
6168
  }
6169
+ if (!cfg.logscale.token) {
6170
+ results.logscale = { status: "blank", message: "not configured" };
6171
+ } else if (!cfg.logscale.baseUrl) {
6172
+ results.logscale = { status: "error", message: "baseUrl not configured" };
6173
+ } else {
6174
+ try {
6175
+ await http.logscale("/api/v1/repositories", { timeoutMs: 1e4 });
6176
+ results.logscale = { status: "valid", message: "ok" };
6177
+ } catch (err) {
6178
+ results.logscale = categorize2(err);
6179
+ }
6180
+ }
6051
6181
  const statuses = Object.values(results).map((r) => r.status);
6052
6182
  if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
6053
6183
  else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
6054
6184
  if (cmdOpts.output === "table") {
6055
- const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform"];
6185
+ const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform", "logscale"];
6056
6186
  const labelWidth = 14;
6057
6187
  const statusWidth = 9;
6058
6188
  for (const svc of services) {
@@ -6066,7 +6196,7 @@ function registerConfigCommands(program2) {
6066
6196
  process.stdout.write("\n");
6067
6197
  } else {
6068
6198
  if (opts.pretty) {
6069
- const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform"];
6199
+ const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform", "logscale"];
6070
6200
  const labelWidth = 14;
6071
6201
  const statusWidth = 9;
6072
6202
  for (const svc of services) {
@@ -6689,6 +6819,40 @@ async function initGlobalConfig(start) {
6689
6819
  }
6690
6820
  }
6691
6821
  }
6822
+ process.stderr.write("\n\u2500\u2500 LogScale \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
6823
+ const useLogscale = await confirm({
6824
+ message: "Configure LogScale (Falcon LogScale / Humio) for log queries?",
6825
+ default: false
6826
+ });
6827
+ let logscaleBaseUrl = "";
6828
+ let logscaleToken = "";
6829
+ if (useLogscale) {
6830
+ logscaleBaseUrl = await input({
6831
+ message: "LogScale base URL (e.g. https://logscale.imagile.dev):",
6832
+ default: ""
6833
+ });
6834
+ logscaleToken = await password({
6835
+ message: "LogScale personal access token:"
6836
+ });
6837
+ if (logscaleBaseUrl && logscaleToken) {
6838
+ process.stderr.write("\n Verifying connection...\n");
6839
+ try {
6840
+ const tempConfig = {
6841
+ ...loadConfig(),
6842
+ logscale: {
6843
+ baseUrl: normalizeBaseUrl(logscaleBaseUrl),
6844
+ token: logscaleToken
6845
+ }
6846
+ };
6847
+ const tempHttp = createHttpClient(tempConfig);
6848
+ await tempHttp.logscale("/api/v1/repositories");
6849
+ process.stderr.write(" Connected.\n");
6850
+ } catch (err) {
6851
+ warn(`Could not connect to LogScale: ${err instanceof Error ? err.message : String(err)}`);
6852
+ warn("Config will be saved anyway. Check your URL and token and re-run pncli config init or pncli config test.");
6853
+ }
6854
+ }
6855
+ }
6692
6856
  process.stderr.write("\n\u2500\u2500 Defaults \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
6693
6857
  const jiraProject = await input({
6694
6858
  message: "Default Jira project key (optional):",
@@ -6828,6 +6992,12 @@ async function initGlobalConfig(start) {
6828
6992
  platformToken: dynatracePlatformToken || void 0
6829
6993
  }
6830
6994
  } : {},
6995
+ ...useLogscale && logscaleBaseUrl ? {
6996
+ logscale: {
6997
+ baseUrl: normalizeBaseUrl(logscaleBaseUrl),
6998
+ token: logscaleToken || void 0
6999
+ }
7000
+ } : {},
6831
7001
  defaults: {
6832
7002
  jira: {
6833
7003
  project: jiraProject || void 0
@@ -7917,11 +8087,11 @@ function registerAdoRepoCommands(ado) {
7917
8087
  const start = Date.now();
7918
8088
  try {
7919
8089
  const globalOpts = ado.optsWithGlobals();
7920
- const { loadConfig: lc } = await import("./config-CPRXY5SN.js");
8090
+ const { loadConfig: lc } = await import("./config-QN5FFAZY.js");
7921
8091
  const config = lc({ configPath: globalOpts.config });
7922
8092
  const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
7923
8093
  const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
7924
- const { createHttpClient: createHttpClient2 } = await import("./http-52YHD3ZL.js");
8094
+ const { createHttpClient: createHttpClient2 } = await import("./http-WCK6AZW6.js");
7925
8095
  const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
7926
8096
  const coreClient = new AdoCoreClient2(http);
7927
8097
  const me = await coreClient.getConnectionData(collection);
@@ -10627,6 +10797,65 @@ function registerDynatraceCommands(program2) {
10627
10797
  });
10628
10798
  }
10629
10799
 
10800
+ // src/services/logscale/commands.ts
10801
+ function getHttp4(program2) {
10802
+ const opts = program2.optsWithGlobals();
10803
+ return createHttpClient(
10804
+ loadConfig({ configPath: opts.config }),
10805
+ Boolean(opts.dryRun)
10806
+ );
10807
+ }
10808
+ function registerLogscaleCommands(program2) {
10809
+ const logscale = program2.command("logscale").description("LogScale log query operations");
10810
+ const repositories = logscale.command("repositories").description("Repository operations");
10811
+ repositories.command("list").description("List available repositories").action(async () => {
10812
+ const start = Date.now();
10813
+ try {
10814
+ const data = await getHttp4(program2).logscale("/api/v1/repositories");
10815
+ success({ count: data.length, repositories: data }, "logscale", "repositories list", start);
10816
+ } catch (err) {
10817
+ fail(err, "logscale", "repositories list", start);
10818
+ }
10819
+ });
10820
+ logscale.command("query").description("Execute a LogScale Query Language (LQL) search against a repository").requiredOption("--repository <name>", "Repository name to query").requiredOption("--query <lql>", 'LogScale Query Language expression (e.g. "error" or "level=error | count()")').option("--start <time>", "Start time (ISO 8601 or relative such as 1h or 24h)", "1h").option("--end <time>", "End time (ISO 8601 or relative)", "now").option("--limit <n>", "Maximum number of events to return", "200").option("--timeout-ms <ms>", "Client-side HTTP timeout in milliseconds", "30000").action(async (opts) => {
10821
+ const ts = Date.now();
10822
+ try {
10823
+ const body = {
10824
+ queryString: opts.query,
10825
+ start: opts.start,
10826
+ end: opts.end,
10827
+ limit: parseInt(opts.limit, 10),
10828
+ showQueryEventDistribution: false,
10829
+ isLive: false
10830
+ };
10831
+ const data = await getHttp4(program2).logscale(
10832
+ `/api/v1/repositories/${encodeURIComponent(opts.repository)}/query`,
10833
+ {
10834
+ method: "POST",
10835
+ body,
10836
+ timeoutMs: parseInt(opts.timeoutMs, 10)
10837
+ }
10838
+ );
10839
+ success(
10840
+ {
10841
+ repository: opts.repository,
10842
+ done: data.done,
10843
+ cancelled: data.cancelled,
10844
+ eventCount: data.events.length,
10845
+ events: data.events,
10846
+ warnings: data.warnings ?? [],
10847
+ metaData: data.metaData
10848
+ },
10849
+ "logscale",
10850
+ "query",
10851
+ ts
10852
+ );
10853
+ } catch (err) {
10854
+ fail(err, "logscale", "query", ts);
10855
+ }
10856
+ });
10857
+ }
10858
+
10630
10859
  // src/cli.ts
10631
10860
  if (!process.env.PNCLI_VERIFY_TLS) {
10632
10861
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
@@ -10671,6 +10900,7 @@ registerSkillsCommands(program);
10671
10900
  registerJwtCommands(program);
10672
10901
  registerOpenShiftCommands(program);
10673
10902
  registerDynatraceCommands(program);
10903
+ registerLogscaleCommands(program);
10674
10904
  program.addHelpText("after", `
10675
10905
  Services:
10676
10906
  git Local git operations (status, diff, log, branch)
@@ -10691,6 +10921,7 @@ Services:
10691
10921
  sonatypeiq Sonatype IQ Server (applications, organizations, policies)
10692
10922
  openshift OpenShift / Kubernetes (pods, events, logs, metrics)
10693
10923
  dynatrace Dynatrace (services, entities, problems, traces, Kubernetes workloads)
10924
+ logscale LogScale (log query, repositories)
10694
10925
  config Manage pncli configuration
10695
10926
  skills Download and manage Claude Code skills
10696
10927
  jwt JWT token utilities (decode header and payload)