@great-arrow/cli 0.1.51 → 0.1.53

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.
Files changed (2) hide show
  1. package/dist/index.js +75 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9327,6 +9327,7 @@ function mapErrorToExitCode(error) {
9327
9327
  return 2 /* NETWORK_ERROR */;
9328
9328
  }
9329
9329
  var FAILURE_HINTS = {
9330
+ permission: "Review this connection's scopes and workspace permissions at /account/connections.",
9330
9331
  auth: "Run `gad login`, then reconnect the client: /mcp \u2192 great-arrow \u2192 reconnect.",
9331
9332
  network: "Check this machine's connectivity (VPN, proxy, DNS), then try again.",
9332
9333
  timeout: "The request timed out. Check connectivity and retry; if it persists, see https://www.greatarrow.ai/status.",
@@ -9337,9 +9338,11 @@ var FAILURE_HINTS = {
9337
9338
  function classifyRequestFailure(error) {
9338
9339
  const message = error instanceof Error ? error.message : String(error ?? "unknown error");
9339
9340
  let kind = "unknown";
9340
- if (error instanceof CliError && error.code === 1 /* AUTH_ERROR */) {
9341
+ if (/\b403\b/.test(message)) {
9342
+ kind = "permission";
9343
+ } else if (error instanceof CliError && error.code === 1 /* AUTH_ERROR */) {
9341
9344
  kind = "auth";
9342
- } else if (/\b(401|403)\b/.test(message)) {
9345
+ } else if (/\b401\b/.test(message)) {
9343
9346
  kind = "auth";
9344
9347
  } else if (/HTTP 5\d\d|Server error/i.test(message)) {
9345
9348
  kind = "server";
@@ -9399,7 +9402,7 @@ function isNodeError(value) {
9399
9402
 
9400
9403
  // src/lib/version.ts
9401
9404
  var CLI_PACKAGE_NAME = "@great-arrow/cli";
9402
- var CLI_VERSION = "0.1.51";
9405
+ var CLI_VERSION = "0.1.53";
9403
9406
  var GAD_INSTALL_VERSION = "2026.08.08";
9404
9407
  var GAD_INSTALL_MIN_VERSION = "2026.06.22";
9405
9408
  var GAD_INSTALL_HEADER = "X-GAD-Install-Version";
@@ -10675,6 +10678,42 @@ function parseMcpPayload(result) {
10675
10678
  }
10676
10679
  }
10677
10680
 
10681
+ // ../../src/lib/observability/scrub-pii.ts
10682
+ var EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
10683
+ var SSN_RE = /\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/g;
10684
+ var CREDIT_CARD_RE = /\b\d(?:[ -]?\d){12,18}\b/g;
10685
+ var PHONE_RE = /(?:\+?\b1[-.\s]?)?(?:\([2-9]\d{2}\)\s?|\b[2-9]\d{2}[-.\s]?)\d{3}[-.\s]?\d{4}\b/g;
10686
+ function luhnValid(candidate) {
10687
+ const digits2 = candidate.replace(/\D/g, "");
10688
+ if (digits2.length < 13 || digits2.length > 19) return false;
10689
+ let sum = 0;
10690
+ let double = false;
10691
+ for (let i = digits2.length - 1; i >= 0; i--) {
10692
+ let n = digits2.charCodeAt(i) - 48;
10693
+ if (double) {
10694
+ n *= 2;
10695
+ if (n > 9) n -= 9;
10696
+ }
10697
+ sum += n;
10698
+ double = !double;
10699
+ }
10700
+ return sum % 10 === 0;
10701
+ }
10702
+ var SECRET_TOKEN_RE = /\b(?:gad_[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9-]{20,}|Bearer\s+[A-Za-z0-9._~+/=-]{8,})\b/gi;
10703
+ var SENSITIVE_QUERY_VALUE_RE = /((?:^|[?&])(?:code|token|access_token|refresh_token|id_token|api_key|apikey|api_secret|apisecret|key|secret|client_secret|password|state)=)[^&#\s]+/gi;
10704
+ function scrubPiiFromText(s) {
10705
+ return s.replace(EMAIL_RE, "[redacted-email]").replace(
10706
+ CREDIT_CARD_RE,
10707
+ (m) => luhnValid(m) ? "[redacted-credit-card]" : m
10708
+ ).replace(SSN_RE, "[redacted-ssn]").replace(PHONE_RE, "[redacted-phone]");
10709
+ }
10710
+ function scrubTelemetryText(s) {
10711
+ return scrubPiiFromText(s).replace(SENSITIVE_QUERY_VALUE_RE, "$1[redacted]").replace(
10712
+ SECRET_TOKEN_RE,
10713
+ (match) => match.toLowerCase().startsWith("bearer ") ? "Bearer [redacted]" : "[redacted-secret]"
10714
+ );
10715
+ }
10716
+
10678
10717
  // src/lib/telemetry.ts
10679
10718
  var MAX_EVENTS_PER_PROCESS = 20;
10680
10719
  var MAX_STRING = 240;
@@ -10682,12 +10721,14 @@ var sent = 0;
10682
10721
  function enabled() {
10683
10722
  if (process.env.GAD_TELEMETRY === "0" || process.env.GAD_TELEMETRY === "false")
10684
10723
  return false;
10685
- return process.env.GAD_TELEMETRY === "1" || process.env.GAD_TELEMETRY === "true";
10724
+ if (process.env.GAD_TELEMETRY === "1" || process.env.GAD_TELEMETRY === "true")
10725
+ return true;
10726
+ return void 0;
10686
10727
  }
10687
10728
  function redact(value, depth = 0) {
10688
10729
  if (depth > 3) return "[redacted]";
10689
10730
  if (typeof value === "string") {
10690
- const scrubbed = value.replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(
10731
+ const scrubbed = scrubTelemetryText(value).replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(
10691
10732
  /(token|secret|password|authorization)[=:]\s*[^\s,]+/gi,
10692
10733
  "$1=[redacted]"
10693
10734
  );
@@ -10706,7 +10747,9 @@ function redact(value, depth = 0) {
10706
10747
  }
10707
10748
  async function captureCliEvent(input) {
10708
10749
  if (sent >= MAX_EVENTS_PER_PROCESS) return;
10709
- if (!enabled()) {
10750
+ const envConsent = enabled();
10751
+ if (envConsent === false) return;
10752
+ if (envConsent === void 0) {
10710
10753
  try {
10711
10754
  const config = await loadConfig();
10712
10755
  if (config.telemetry_enabled !== true) return;
@@ -10714,28 +10757,33 @@ async function captureCliEvent(input) {
10714
10757
  return;
10715
10758
  }
10716
10759
  }
10760
+ if (sent >= MAX_EVENTS_PER_PROCESS) return;
10717
10761
  sent++;
10718
10762
  const error = input.error instanceof Error ? { name: input.error.name, message: input.error.message } : input.error;
10719
10763
  const payload = {
10720
10764
  platform: "cli",
10721
10765
  event: input.name,
10722
10766
  level: input.error ? "error" : "info",
10723
- message: error && typeof error === "object" && "message" in error ? String(error.message) : input.name,
10767
+ message: error && typeof error === "object" && "message" in error ? redact(String(error.message)) : redact(input.name),
10724
10768
  version: process.env.GAD_CLI_VERSION,
10725
10769
  code: input.context?.code,
10726
10770
  context: redact(input.context ?? {}),
10727
10771
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
10728
10772
  };
10729
10773
  try {
10730
- await fetch(`${input.apiUrl.replace(/\/$/, "")}/api/telemetry`, {
10731
- method: "POST",
10732
- headers: {
10733
- "content-type": "application/json",
10734
- ...input.token ? { authorization: `Bearer ${input.token}` } : {}
10735
- },
10736
- body: JSON.stringify(payload),
10737
- signal: AbortSignal.timeout(1500)
10738
- });
10774
+ const response = await fetch(
10775
+ `${input.apiUrl.replace(/\/$/, "")}/api/telemetry`,
10776
+ {
10777
+ method: "POST",
10778
+ headers: {
10779
+ "content-type": "application/json"
10780
+ },
10781
+ body: JSON.stringify(redact(payload)),
10782
+ signal: AbortSignal.timeout(1500)
10783
+ }
10784
+ );
10785
+ if (!response.ok)
10786
+ debugSwallow("telemetry.send", new Error(`HTTP ${response.status}`));
10739
10787
  } catch (err) {
10740
10788
  debugSwallow("telemetry.send", err);
10741
10789
  }
@@ -10921,7 +10969,14 @@ var GadClient = class {
10921
10969
  }
10922
10970
  const status = response.status;
10923
10971
  const body = await response.text().catch(() => "");
10924
- if (status === 401 || status === 403) {
10972
+ if (status === 403) {
10973
+ throw new CliError(
10974
+ "Permission denied (HTTP 403).",
10975
+ 1 /* AUTH_ERROR */,
10976
+ "Check this connection's scopes and workspace permissions at /account/connections."
10977
+ );
10978
+ }
10979
+ if (status === 401) {
10925
10980
  throw authError(
10926
10981
  `Authentication failed (HTTP ${status}).`,
10927
10982
  "Run 'gad login' to re-authenticate, or check your GAD_TOKEN."
@@ -14210,7 +14265,8 @@ function codexTransportFields(body) {
14210
14265
 
14211
14266
  // src/lib/mcp-doctor.ts
14212
14267
  var MCP_FAILURE_MESSAGE = {
14213
- auth: "MCP rejected this machine's credential (401/403)",
14268
+ auth: "MCP rejected this machine's credential (401)",
14269
+ permission: "MCP denied this connection's scopes or workspace access (403)",
14214
14270
  network: "Could not reach the MCP endpoint",
14215
14271
  timeout: "The MCP endpoint did not answer in time",
14216
14272
  server: "The MCP endpoint returned a server error",
@@ -14219,6 +14275,7 @@ var MCP_FAILURE_MESSAGE = {
14219
14275
  };
14220
14276
  var MCP_STATUS_WORD = {
14221
14277
  auth: "unauthenticated",
14278
+ permission: "forbidden",
14222
14279
  network: "unreachable",
14223
14280
  timeout: "timeout",
14224
14281
  server: "server error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@great-arrow/cli",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "description": "GreatArrow.ai CLI tool",
5
5
  "type": "module",
6
6
  "bin": {