@akasecurity/cli 0.9.5 → 0.9.6

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 (96) hide show
  1. package/dist/cli.js +129 -50
  2. package/extension/manifest.json +1 -1
  3. package/native-host/host.js +90 -35
  4. package/native-host/scan-worker.js +61 -12
  5. package/package.json +6 -6
  6. package/web-ui/package.json +5 -2
  7. package/web-ui/packages/detections/package.json +3 -2
  8. package/web-ui/packages/persistence/package.json +3 -2
  9. package/web-ui/packages/plugin-sdk/package.json +2 -1
  10. package/web-ui/web-ui/.next/BUILD_ID +1 -1
  11. package/web-ui/web-ui/.next/app-build-manifest.json +66 -66
  12. package/web-ui/web-ui/.next/app-path-routes-manifest.json +7 -7
  13. package/web-ui/web-ui/.next/build-manifest.json +2 -2
  14. package/web-ui/web-ui/.next/prerender-manifest.json +14 -14
  15. package/web-ui/web-ui/.next/server/app/(app)/activity/page.js +1 -1
  16. package/web-ui/web-ui/.next/server/app/(app)/activity/page_client-reference-manifest.js +1 -1
  17. package/web-ui/web-ui/.next/server/app/(app)/data-shares/page.js +1 -1
  18. package/web-ui/web-ui/.next/server/app/(app)/data-shares/page_client-reference-manifest.js +1 -1
  19. package/web-ui/web-ui/.next/server/app/(app)/detections/page.js +2 -2
  20. package/web-ui/web-ui/.next/server/app/(app)/detections/page_client-reference-manifest.js +1 -1
  21. package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js +2 -2
  22. package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js.nft.json +1 -1
  23. package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page_client-reference-manifest.js +1 -1
  24. package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js +2 -2
  25. package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js.nft.json +1 -1
  26. package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page_client-reference-manifest.js +1 -1
  27. package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js +2 -2
  28. package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js.nft.json +1 -1
  29. package/web-ui/web-ui/.next/server/app/(app)/exceptions/page_client-reference-manifest.js +1 -1
  30. package/web-ui/web-ui/.next/server/app/(app)/findings/page.js +2 -2
  31. package/web-ui/web-ui/.next/server/app/(app)/findings/page_client-reference-manifest.js +1 -1
  32. package/web-ui/web-ui/.next/server/app/(app)/inventory/page.js +2 -2
  33. package/web-ui/web-ui/.next/server/app/(app)/inventory/page_client-reference-manifest.js +1 -1
  34. package/web-ui/web-ui/.next/server/app/(app)/policies/page_client-reference-manifest.js +1 -1
  35. package/web-ui/web-ui/.next/server/app/(app)/scan/page.js +2 -2
  36. package/web-ui/web-ui/.next/server/app/(app)/scan/page.js.nft.json +1 -1
  37. package/web-ui/web-ui/.next/server/app/(app)/scan/page_client-reference-manifest.js +1 -1
  38. package/web-ui/web-ui/.next/server/app/(app)/security/page_client-reference-manifest.js +1 -1
  39. package/web-ui/web-ui/.next/server/app/(app)/settings/page.js +2 -2
  40. package/web-ui/web-ui/.next/server/app/(app)/settings/page_client-reference-manifest.js +1 -1
  41. package/web-ui/web-ui/.next/server/app/(app)/updates/page.js +2 -2
  42. package/web-ui/web-ui/.next/server/app/(app)/updates/page.js.nft.json +1 -1
  43. package/web-ui/web-ui/.next/server/app/(app)/updates/page_client-reference-manifest.js +1 -1
  44. package/web-ui/web-ui/.next/server/app/(app)/vault/page.js +2 -2
  45. package/web-ui/web-ui/.next/server/app/(app)/vault/page.js.nft.json +1 -1
  46. package/web-ui/web-ui/.next/server/app/(app)/vault/page_client-reference-manifest.js +1 -1
  47. package/web-ui/web-ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  48. package/web-ui/web-ui/.next/server/app/_not-found.html +1 -1
  49. package/web-ui/web-ui/.next/server/app/_not-found.rsc +2 -2
  50. package/web-ui/web-ui/.next/server/app/index.html +1 -1
  51. package/web-ui/web-ui/.next/server/app/index.rsc +2 -2
  52. package/web-ui/web-ui/.next/server/app/page_client-reference-manifest.js +1 -1
  53. package/web-ui/web-ui/.next/server/app-paths-manifest.json +7 -7
  54. package/web-ui/web-ui/.next/server/chunks/160.js +2 -2
  55. package/web-ui/web-ui/.next/server/chunks/243.js +1 -1
  56. package/web-ui/web-ui/.next/server/chunks/333.js +1 -1
  57. package/web-ui/web-ui/.next/server/chunks/{330.js → 413.js} +1 -1
  58. package/web-ui/web-ui/.next/server/chunks/557.js +2 -2
  59. package/web-ui/web-ui/.next/server/functions-config-manifest.json +5 -5
  60. package/web-ui/web-ui/.next/server/middleware-manifest.json +5 -5
  61. package/web-ui/web-ui/.next/server/pages/404.html +1 -1
  62. package/web-ui/web-ui/.next/server/pages/500.html +1 -1
  63. package/web-ui/web-ui/.next/server/server-reference-manifest.js +1 -1
  64. package/web-ui/web-ui/.next/server/server-reference-manifest.json +1 -1
  65. package/web-ui/web-ui/.next/static/chunks/133-66e4c8e40b72a3e8.js +1 -0
  66. package/web-ui/web-ui/.next/static/chunks/app/(app)/activity/page-1151de62cd9386c8.js +1 -0
  67. package/web-ui/web-ui/.next/static/chunks/app/(app)/data-shares/{page-d96aa1158bb1c630.js → page-eaee9ff5ba1056e3.js} +1 -1
  68. package/web-ui/web-ui/.next/static/chunks/app/(app)/detections/{page-269fa2d4f47e6125.js → page-7fbcf876de9f09de.js} +1 -1
  69. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-8f33eba279e45c1d.js +1 -0
  70. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/new/page-bf9c74a239afdab2.js +1 -0
  71. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-e17e9d7c8547bc32.js +1 -0
  72. package/web-ui/web-ui/.next/static/chunks/app/(app)/findings/page-4bdf3d2ede57b3c7.js +1 -0
  73. package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-88b768ca343a8524.js +1 -0
  74. package/web-ui/web-ui/.next/static/chunks/app/(app)/{layout-d7a290e9b0dd8a15.js → layout-a346cd78f5f28936.js} +1 -1
  75. package/web-ui/web-ui/.next/static/chunks/app/(app)/policies/{page-1d1a93a1e561ca82.js → page-f79874ebd61fbf46.js} +1 -1
  76. package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-86e7e860ce98d0a4.js +1 -0
  77. package/web-ui/web-ui/.next/static/chunks/app/(app)/security/{page-a7fa0e028623b7cb.js → page-557e1703a106d286.js} +1 -1
  78. package/web-ui/web-ui/.next/static/chunks/app/(app)/settings/page-a1956abf86b48b8b.js +1 -0
  79. package/web-ui/web-ui/.next/static/chunks/app/(app)/updates/page-fe71f1fd0ab6cde6.js +1 -0
  80. package/web-ui/web-ui/.next/static/chunks/app/(app)/vault/page-2473d9063bd5eb69.js +1 -0
  81. package/web-ui/web-ui/.next/static/css/2eca310301b96d98.css +2 -0
  82. package/web-ui/web-ui/dist/scan-worker.js +61 -12
  83. package/web-ui/web-ui/.next/static/chunks/133-14f0f297836736b9.js +0 -1
  84. package/web-ui/web-ui/.next/static/chunks/app/(app)/activity/page-90ea4cb1f1956a03.js +0 -1
  85. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-4ad5b60f476d7c6e.js +0 -1
  86. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/new/page-2bf76696fbe6e76d.js +0 -1
  87. package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-5d2511a7c06dd645.js +0 -1
  88. package/web-ui/web-ui/.next/static/chunks/app/(app)/findings/page-1891ec610b461455.js +0 -1
  89. package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-bc090490b3416d48.js +0 -1
  90. package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-7a1a29a230626f96.js +0 -1
  91. package/web-ui/web-ui/.next/static/chunks/app/(app)/settings/page-9729632fc8128534.js +0 -1
  92. package/web-ui/web-ui/.next/static/chunks/app/(app)/updates/page-bda9e826a1ac1a00.js +0 -1
  93. package/web-ui/web-ui/.next/static/chunks/app/(app)/vault/page-28116d3ce827e152.js +0 -1
  94. package/web-ui/web-ui/.next/static/css/0af7782f7a750368.css +0 -2
  95. /package/web-ui/web-ui/.next/static/{x4ixG3_q4UeyC_WNmSFdI → scT_YT84DnOMXJI2dA75N}/_buildManifest.js +0 -0
  96. /package/web-ui/web-ui/.next/static/{x4ixG3_q4UeyC_WNmSFdI → scT_YT84DnOMXJI2dA75N}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -854,7 +854,7 @@ var updateCodexPlugin = manager2.update;
854
854
 
855
855
  // ../packages/local-ops/src/egress-record.ts
856
856
  import { statSync as statSync7 } from "fs";
857
- import { dirname as dirname5, relative as relative2, resolve } from "path";
857
+ import { dirname as dirname5, relative, resolve } from "path";
858
858
 
859
859
  // ../packages/detections/src/egress/registry.ts
860
860
  var EXTRACTOR_VERSION = "1";
@@ -2263,6 +2263,40 @@ function escapeRegExp2(value) {
2263
2263
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2264
2264
  }
2265
2265
 
2266
+ // ../packages/detections/src/regex-cache.ts
2267
+ var singles = /* @__PURE__ */ new WeakMap();
2268
+ var keywordLists = /* @__PURE__ */ new WeakMap();
2269
+ var labelLists = /* @__PURE__ */ new WeakMap();
2270
+ function listCache(kind) {
2271
+ return kind === "keyword" ? keywordLists : labelLists;
2272
+ }
2273
+ function memoizedRegExp(owner, build) {
2274
+ const cached2 = singles.get(owner);
2275
+ if (cached2 !== void 0) {
2276
+ cached2.lastIndex = 0;
2277
+ return cached2;
2278
+ }
2279
+ const compiled = build();
2280
+ singles.set(owner, compiled);
2281
+ return compiled;
2282
+ }
2283
+ function memoizedRegExpList(kind, owner, build) {
2284
+ const cache = listCache(kind);
2285
+ const cached2 = cache.get(owner);
2286
+ if (cached2 !== void 0) {
2287
+ if (cached2.stateful) {
2288
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
2289
+ }
2290
+ return cached2.entries;
2291
+ }
2292
+ const entries = build();
2293
+ cache.set(owner, {
2294
+ entries,
2295
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
2296
+ });
2297
+ return entries;
2298
+ }
2299
+
2266
2300
  // ../packages/detections/src/matchers/limits.ts
2267
2301
  var MAX_MATCHES_PER_RULE = 1e4;
2268
2302
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -2273,10 +2307,17 @@ var KeywordMatcher = class {
2273
2307
  if (rule.matcher.type !== "keyword") return [];
2274
2308
  const { keywords, caseSensitive } = rule.matcher;
2275
2309
  const spans = [];
2276
- for (const kw of keywords) {
2277
- if (kw.length === 0) continue;
2310
+ const compiled = memoizedRegExpList(
2311
+ "keyword",
2312
+ rule.matcher,
2313
+ () => keywords.map((kw) => {
2314
+ if (kw.length === 0) return void 0;
2315
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
2316
+ })
2317
+ );
2318
+ for (const re of compiled) {
2319
+ if (re === void 0) continue;
2278
2320
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
2279
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
2280
2321
  let m;
2281
2322
  while ((m = re.exec(text)) !== null) {
2282
2323
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -2292,7 +2333,10 @@ var RegexMatcher = class {
2292
2333
  match(text, rule) {
2293
2334
  if (rule.matcher.type !== "regex") return [];
2294
2335
  const { pattern, flags, captureGroup } = rule.matcher;
2295
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
2336
+ const re = memoizedRegExp(
2337
+ rule.matcher,
2338
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
2339
+ );
2296
2340
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
2297
2341
  const spans = [];
2298
2342
  let m;
@@ -2400,11 +2444,15 @@ function isCorroborated(candidate, candidates, text) {
2400
2444
  const labels = req.labels;
2401
2445
  if (labels && labels.length > 0) {
2402
2446
  const haystack = text.slice(Math.max(0, winStart), winEnd);
2403
- for (const label of labels) {
2404
- const trimmed = label.trim();
2405
- if (trimmed.length === 0) continue;
2406
- const re = new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
2407
- if (re.test(haystack)) return true;
2447
+ for (const re of memoizedRegExpList(
2448
+ "label",
2449
+ req,
2450
+ () => labels.map((label) => {
2451
+ const trimmed = label.trim();
2452
+ return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
2453
+ })
2454
+ )) {
2455
+ if (re?.test(haystack)) return true;
2408
2456
  }
2409
2457
  }
2410
2458
  return false;
@@ -30956,7 +31004,7 @@ function resolveNonGitProject(startDir, recognizeMarker) {
30956
31004
  // ../packages/plugin-sdk/src/project-files.ts
30957
31005
  var import_ignore = __toESM(require_ignore(), 1);
30958
31006
  import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
30959
- import { basename as basename5, join as join13, relative, sep as sep4 } from "path";
31007
+ import { basename as basename5, join as join13 } from "path";
30960
31008
  var SKIP_DIRS = /* @__PURE__ */ new Set([
30961
31009
  ".git",
30962
31010
  "node_modules",
@@ -30974,23 +31022,28 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
30974
31022
  ".cache"
30975
31023
  ]);
30976
31024
  var MAX_FILES = 2e4;
30977
- function readIgnoreLayer(dir) {
31025
+ function readIgnoreLayer(dir, anchorLen) {
30978
31026
  try {
30979
31027
  const content = readFileSync9(join13(dir, ".gitignore"), "utf8");
30980
- return { base: dir, matcher: (0, import_ignore.default)().add(content) };
31028
+ return { matcher: (0, import_ignore.default)().add(content), anchorLen };
30981
31029
  } catch {
30982
31030
  return void 0;
30983
31031
  }
30984
31032
  }
30985
- function isIgnored(layers, absPath, isDir) {
30986
- let ignored = false;
30987
- for (const layer of layers) {
30988
- const rel = relative(layer.base, absPath).split(sep4).join("/") + (isDir ? "/" : "");
30989
- const verdict = layer.matcher.test(rel);
30990
- if (verdict.ignored) ignored = true;
30991
- else if (verdict.unignored) ignored = false;
31033
+ function relToAnchor(dirRel, anchorLen, name) {
31034
+ if (anchorLen === dirRel.length) return name;
31035
+ return `${dirRel.slice(anchorLen === 0 ? 0 : anchorLen + 1)}/${name}`;
31036
+ }
31037
+ function isIgnored(layers, dirRel, name, isDir) {
31038
+ const suffix = isDir ? "/" : "";
31039
+ for (let i = layers.length - 1; i >= 0; i--) {
31040
+ const layer = layers[i];
31041
+ if (layer === void 0) continue;
31042
+ const verdict = layer.matcher.test(relToAnchor(dirRel, layer.anchorLen, name) + suffix);
31043
+ if (verdict.ignored) return true;
31044
+ if (verdict.unignored) return false;
30992
31045
  }
30993
- return ignored;
31046
+ return false;
30994
31047
  }
30995
31048
  var GENERATED_FILES = /* @__PURE__ */ new Set([
30996
31049
  "pnpm-lock.yaml",
@@ -31043,7 +31096,7 @@ function classifyOrigin(relPath, name) {
31043
31096
  }
31044
31097
  function resolveProjectFiles(cwd) {
31045
31098
  try {
31046
- let visit3 = function(dir, layers) {
31099
+ let visit3 = function(dir, dirRel, layers) {
31047
31100
  let dirents;
31048
31101
  try {
31049
31102
  dirents = readdirSync5(dir, { withFileTypes: true, encoding: "utf8" });
@@ -31051,21 +31104,22 @@ function resolveProjectFiles(cwd) {
31051
31104
  walk.lostSubtree = true;
31052
31105
  return false;
31053
31106
  }
31054
- const layer = readIgnoreLayer(dir);
31107
+ const layer = readIgnoreLayer(dir, dirRel.length);
31055
31108
  const dirLayers = layer ? [...layers, layer] : layers;
31056
31109
  for (const entry of dirents) {
31057
- const fullPath = join13(dir, entry.name);
31058
31110
  if (entry.isDirectory()) {
31059
- if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, fullPath, true)) continue;
31111
+ if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, dirRel, entry.name, true)) continue;
31112
+ const fullPath = join13(dir, entry.name);
31060
31113
  if (existsSync9(join13(fullPath, ".git"))) continue;
31061
- if (visit3(fullPath, dirLayers)) return true;
31114
+ const childRel = dirRel === "" ? entry.name : `${dirRel}/${entry.name}`;
31115
+ if (visit3(fullPath, childRel, dirLayers)) return true;
31062
31116
  continue;
31063
31117
  }
31064
31118
  if (!entry.isFile()) continue;
31065
31119
  if (entry.name === ".git") continue;
31066
- if (isIgnored(dirLayers, fullPath, false)) continue;
31120
+ if (isIgnored(dirLayers, dirRel, entry.name, false)) continue;
31067
31121
  if (files.length >= MAX_FILES) return true;
31068
- const relPath = relative(root, fullPath).split(sep4).join("/");
31122
+ const relPath = dirRel === "" ? entry.name : `${dirRel}/${entry.name}`;
31069
31123
  files.push({
31070
31124
  path: relPath,
31071
31125
  name: basename5(entry.name),
@@ -31082,7 +31136,7 @@ function resolveProjectFiles(cwd) {
31082
31136
  const isLinkedCheckout = resolveHeadRoot(cwd) !== worktree;
31083
31137
  const files = [];
31084
31138
  const walk = { lostSubtree: false };
31085
- const truncated = visit3(root, []) || walk.lostSubtree || isLinkedCheckout;
31139
+ const truncated = visit3(root, "", []) || walk.lostSubtree || isLinkedCheckout;
31086
31140
  if (files.length === 0) return void 0;
31087
31141
  files.sort((a, b) => a.path.localeCompare(b.path));
31088
31142
  return { files, truncated, scannedAt: (/* @__PURE__ */ new Date()).toISOString() };
@@ -31154,7 +31208,7 @@ function recordProjectEgress(db, target, egress, base = defaultDataDir()) {
31154
31208
  }
31155
31209
  const files = [];
31156
31210
  for (const hit of egress.files) {
31157
- const file2 = toPosix(relative2(root, hit.file));
31211
+ const file2 = toPosix(relative(root, hit.file));
31158
31212
  if (file2 === "" || file2.startsWith("../")) continue;
31159
31213
  if (file2.startsWith(".") || file2.includes("/.")) continue;
31160
31214
  files.push({ ...hit, file: file2, vendored: isVendoredPath(file2) });
@@ -31163,7 +31217,7 @@ function recordProjectEgress(db, target, egress, base = defaultDataDir()) {
31163
31217
  projectKey,
31164
31218
  project,
31165
31219
  projectId,
31166
- reconcile: { mode: "walk", walkedPrefix: toPosix(relative2(root, abs)) },
31220
+ reconcile: { mode: "walk", walkedPrefix: toPosix(relative(root, abs)) },
31167
31221
  hits: resolveEgress(files)
31168
31222
  });
31169
31223
  return { ...summary, project };
@@ -31175,7 +31229,7 @@ function recordProjectEgress(db, target, egress, base = defaultDataDir()) {
31175
31229
  // ../packages/local-ops/src/fs-scan.ts
31176
31230
  import { createHash as createHash5, randomUUID as randomUUID15 } from "crypto";
31177
31231
  import { readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
31178
- import { basename as basename6, extname, join as join15, relative as relative3, resolve as resolve2 } from "path";
31232
+ import { basename as basename6, extname, join as join15, relative as relative2, resolve as resolve2 } from "path";
31179
31233
  var import_ignore2 = __toESM(require_ignore(), 1);
31180
31234
  var AKAIGNORE_FILENAME = ".akaignore";
31181
31235
  var SKIP_DIRS2 = /* @__PURE__ */ new Set([
@@ -31200,7 +31254,7 @@ function readIgnoreLayer2(dir, filename) {
31200
31254
  function evaluate(layers, absPath, isDir) {
31201
31255
  let state = "unmatched";
31202
31256
  for (const layer of layers) {
31203
- const rel = toPosix(relative3(layer.base, absPath)) + (isDir ? "/" : "");
31257
+ const rel = toPosix(relative2(layer.base, absPath)) + (isDir ? "/" : "");
31204
31258
  const verdict = layer.matcher.test(rel);
31205
31259
  if (verdict.ignored) state = "ignored";
31206
31260
  else if (verdict.unignored) state = "unignored";
@@ -31777,12 +31831,19 @@ import { parseArgs as parseArgs2 } from "util";
31777
31831
 
31778
31832
  // src/lib/open-url.ts
31779
31833
  import { spawn as spawn2 } from "child_process";
31780
- function openUrl(url2) {
31781
- const platform2 = process.platform;
31782
- const command = platform2 === "darwin" ? "open" : platform2 === "win32" ? "cmd" : "xdg-open";
31783
- const args = platform2 === "win32" ? ["/c", "start", "", url2] : [url2];
31834
+ function openUrlCommand(platform2, url2) {
31835
+ if (platform2 === "darwin") return { command: "open", args: [url2] };
31836
+ if (platform2 === "win32") return { command: "cmd", args: ["/c", "start", "", url2] };
31837
+ return { command: "xdg-open", args: [url2] };
31838
+ }
31839
+ var DETACHED = Object.freeze({ stdio: "ignore", detached: true });
31840
+ function openUrl(url2, deps = {}) {
31841
+ const { command, args } = openUrlCommand(deps.platform ?? process.platform, url2);
31784
31842
  try {
31785
- spawn2(command, args, { stdio: "ignore", detached: true }).unref();
31843
+ const child = (deps.spawn ?? spawn2)(command, [...args], DETACHED);
31844
+ child.on("error", () => {
31845
+ });
31846
+ child.unref();
31786
31847
  } catch {
31787
31848
  }
31788
31849
  }
@@ -32126,18 +32187,36 @@ function processHiddenChar(state, ch) {
32126
32187
  return { ...state, value: state.value + ch };
32127
32188
  }
32128
32189
  var CLEANUP_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
32129
- function terminalPrompter() {
32190
+ function processIo() {
32191
+ return {
32192
+ input: process.stdin,
32193
+ output: process.stdout,
32194
+ errorOutput: process.stderr,
32195
+ signals: {
32196
+ once: (signal, handler) => {
32197
+ process.once(signal, handler);
32198
+ },
32199
+ off: (signal, handler) => {
32200
+ process.removeListener(signal, handler);
32201
+ },
32202
+ raise: (signal) => {
32203
+ process.kill(process.pid, signal);
32204
+ }
32205
+ }
32206
+ };
32207
+ }
32208
+ function terminalPrompter(io = processIo()) {
32130
32209
  return {
32131
32210
  out: (text) => {
32132
- process.stdout.write(text);
32211
+ io.output.write(text);
32133
32212
  },
32134
32213
  err: (text) => {
32135
- process.stderr.write(text);
32214
+ io.errorOutput.write(text);
32136
32215
  },
32137
- isInteractive: process.stdin.isTTY && process.stdout.isTTY,
32216
+ isInteractive: io.input.isTTY && io.output.isTTY,
32138
32217
  ask(question) {
32139
32218
  return new Promise((resolve6) => {
32140
- const rl = createInterface({ input: process.stdin, output: process.stdout });
32219
+ const rl = createInterface({ input: io.input, output: io.output });
32141
32220
  rl.question(question, (answer) => {
32142
32221
  rl.close();
32143
32222
  resolve6(answer);
@@ -32152,8 +32231,8 @@ function terminalPrompter() {
32152
32231
  // invoked from.
32153
32232
  askHidden(question) {
32154
32233
  return new Promise((resolve6, reject) => {
32155
- const stdin = process.stdin;
32156
- process.stdout.write(question);
32234
+ const stdin = io.input;
32235
+ io.output.write(question);
32157
32236
  stdin.setRawMode(true);
32158
32237
  stdin.resume();
32159
32238
  let state = { value: "", esc: "none" };
@@ -32163,7 +32242,7 @@ function terminalPrompter() {
32163
32242
  restored = true;
32164
32243
  stdin.removeListener("data", onData);
32165
32244
  stdin.removeListener("error", onError);
32166
- for (const sig of CLEANUP_SIGNALS) process.removeListener(sig, onSignal);
32245
+ for (const sig of CLEANUP_SIGNALS) io.signals.off(sig, onSignal);
32167
32246
  try {
32168
32247
  stdin.setRawMode(false);
32169
32248
  } catch {
@@ -32172,7 +32251,7 @@ function terminalPrompter() {
32172
32251
  };
32173
32252
  const finish = (err) => {
32174
32253
  restore();
32175
- process.stdout.write("\n");
32254
+ io.output.write("\n");
32176
32255
  if (err) reject(err);
32177
32256
  else resolve6(state.value);
32178
32257
  };
@@ -32181,7 +32260,7 @@ function terminalPrompter() {
32181
32260
  };
32182
32261
  const onSignal = (sig) => {
32183
32262
  restore();
32184
- process.kill(process.pid, sig);
32263
+ io.signals.raise(sig);
32185
32264
  };
32186
32265
  const onData = (chunk) => {
32187
32266
  try {
@@ -32202,12 +32281,12 @@ function terminalPrompter() {
32202
32281
  };
32203
32282
  stdin.on("data", onData);
32204
32283
  stdin.once("error", onError);
32205
- for (const sig of CLEANUP_SIGNALS) process.once(sig, onSignal);
32284
+ for (const sig of CLEANUP_SIGNALS) io.signals.once(sig, onSignal);
32206
32285
  });
32207
32286
  },
32208
32287
  async readAllStdin() {
32209
32288
  const chunks = [];
32210
- for await (const chunk of process.stdin) {
32289
+ for await (const chunk of io.input) {
32211
32290
  chunks.push(chunk);
32212
32291
  }
32213
32292
  return Buffer.concat(chunks).toString("utf8");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "AI Traffic Control",
4
- "version": "0.9.5",
4
+ "version": "0.9.6",
5
5
  "description": "Local secret detection and redaction for ChatGPT and Claude.ai web chat.",
6
6
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqnwx5Snbi3oZ7mDBC78NaTHO6dTKYRtCqr29YnOImR47UM21tP1us3KF6FjQMtR6tAsffflRok1PThQpjBNwYdRM+stvPIUr5GqBRQL1Ge///kZaVz7eCptCjKg00ygk2HwelaCNt6nWy6czIcM3/HssrNflw42IKsBORW7qQ+KhhKqlABjkRUDvBcuhwOU34HHMJxeP2XdyI5T5msqVJN1nVA+X9T/18dP7cCSzkp9RwLN1hvQMS6n3A1R0HUCmfra1lb1KqQhy3EBdm2VwxTDGVPfGYbTdNSyRT70wzcZy1IdQkfT6YOy1M//+yT7Vu9p/aajs+dNNn/zUn0qJyQIDAQAB",
7
7
  "icons": {
@@ -26197,6 +26197,40 @@ function escapeRegExp2(value) {
26197
26197
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26198
26198
  }
26199
26199
 
26200
+ // ../../packages/detections/src/regex-cache.ts
26201
+ var singles = /* @__PURE__ */ new WeakMap();
26202
+ var keywordLists = /* @__PURE__ */ new WeakMap();
26203
+ var labelLists = /* @__PURE__ */ new WeakMap();
26204
+ function listCache(kind) {
26205
+ return kind === "keyword" ? keywordLists : labelLists;
26206
+ }
26207
+ function memoizedRegExp(owner, build) {
26208
+ const cached2 = singles.get(owner);
26209
+ if (cached2 !== void 0) {
26210
+ cached2.lastIndex = 0;
26211
+ return cached2;
26212
+ }
26213
+ const compiled = build();
26214
+ singles.set(owner, compiled);
26215
+ return compiled;
26216
+ }
26217
+ function memoizedRegExpList(kind, owner, build) {
26218
+ const cache = listCache(kind);
26219
+ const cached2 = cache.get(owner);
26220
+ if (cached2 !== void 0) {
26221
+ if (cached2.stateful) {
26222
+ for (const re of cached2.entries) if (re !== void 0) re.lastIndex = 0;
26223
+ }
26224
+ return cached2.entries;
26225
+ }
26226
+ const entries = build();
26227
+ cache.set(owner, {
26228
+ entries,
26229
+ stateful: entries.some((re) => re !== void 0 && (re.global || re.sticky))
26230
+ });
26231
+ return entries;
26232
+ }
26233
+
26200
26234
  // ../../packages/detections/src/matchers/limits.ts
26201
26235
  var MAX_MATCHES_PER_RULE = 1e4;
26202
26236
  var MAX_REGEX_INPUT_LENGTH = 2e5;
@@ -26207,10 +26241,17 @@ var KeywordMatcher2 = class {
26207
26241
  if (rule.matcher.type !== "keyword") return [];
26208
26242
  const { keywords, caseSensitive } = rule.matcher;
26209
26243
  const spans = [];
26210
- for (const kw of keywords) {
26211
- if (kw.length === 0) continue;
26244
+ const compiled = memoizedRegExpList(
26245
+ "keyword",
26246
+ rule.matcher,
26247
+ () => keywords.map((kw) => {
26248
+ if (kw.length === 0) return void 0;
26249
+ return new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26250
+ })
26251
+ );
26252
+ for (const re of compiled) {
26253
+ if (re === void 0) continue;
26212
26254
  if (spans.length >= MAX_MATCHES_PER_RULE) break;
26213
- const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
26214
26255
  let m;
26215
26256
  while ((m = re.exec(text)) !== null) {
26216
26257
  spans.push({ start: m.index, end: m.index + m[0].length });
@@ -26226,7 +26267,10 @@ var RegexMatcher2 = class {
26226
26267
  match(text, rule) {
26227
26268
  if (rule.matcher.type !== "regex") return [];
26228
26269
  const { pattern, flags, captureGroup } = rule.matcher;
26229
- const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
26270
+ const re = memoizedRegExp(
26271
+ rule.matcher,
26272
+ () => new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`)
26273
+ );
26230
26274
  const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
26231
26275
  const spans = [];
26232
26276
  let m;
@@ -26334,11 +26378,15 @@ function isCorroborated(candidate, candidates, text) {
26334
26378
  const labels = req.labels;
26335
26379
  if (labels && labels.length > 0) {
26336
26380
  const haystack = text.slice(Math.max(0, winStart), winEnd);
26337
- for (const label of labels) {
26338
- const trimmed = label.trim();
26339
- if (trimmed.length === 0) continue;
26340
- const re = new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
26341
- if (re.test(haystack)) return true;
26381
+ for (const re of memoizedRegExpList(
26382
+ "label",
26383
+ req,
26384
+ () => labels.map((label) => {
26385
+ const trimmed = label.trim();
26386
+ return trimmed.length === 0 ? void 0 : new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
26387
+ })
26388
+ )) {
26389
+ if (re?.test(haystack)) return true;
26342
26390
  }
26343
26391
  }
26344
26392
  return false;
@@ -26688,13 +26736,14 @@ function probesFor(rule) {
26688
26736
  const derived = rule.matcher.type === "regex" ? derivedProbes(rule.matcher.pattern) : [];
26689
26737
  return [...derived, ...EXPONENTIAL_PROBES, ...POLYNOMIAL_PROBES];
26690
26738
  }
26691
- function worstProbeMs(rule) {
26739
+ var wallClock = () => performance.now();
26740
+ function worstProbeMs(rule, now = wallClock) {
26692
26741
  let ms = 0;
26693
26742
  let probe = "";
26694
26743
  for (const text of probesFor(rule)) {
26695
- const start = performance.now();
26744
+ const start = now();
26696
26745
  scan(text, [rule]);
26697
- const elapsed = performance.now() - start;
26746
+ const elapsed = now() - start;
26698
26747
  if (elapsed > ms) {
26699
26748
  ms = elapsed;
26700
26749
  probe = text;
@@ -29252,10 +29301,10 @@ function parseFrontmatter(raw) {
29252
29301
  if (lines[0]?.trim() !== "---") return out;
29253
29302
  for (const line of lines.slice(1)) {
29254
29303
  if (line.trim() === "---") break;
29255
- const sep5 = line.indexOf(":");
29256
- if (sep5 === -1) continue;
29257
- const key = line.slice(0, sep5).trim();
29258
- const value = line.slice(sep5 + 1).trim().replace(/^['"]|['"]$/g, "");
29304
+ const sep4 = line.indexOf(":");
29305
+ if (sep4 === -1) continue;
29306
+ const key = line.slice(0, sep4).trim();
29307
+ const value = line.slice(sep4 + 1).trim().replace(/^['"]|['"]$/g, "");
29259
29308
  if (value === "") continue;
29260
29309
  if (key === "name") out.name = value;
29261
29310
  else if (key === "description") out.description = value;
@@ -29941,7 +29990,7 @@ import { basename as basename5, dirname as dirname3, sep as sep3 } from "path";
29941
29990
  // ../../packages/plugin-sdk/src/project-files.ts
29942
29991
  var import_ignore = __toESM(require_ignore(), 1);
29943
29992
  import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
29944
- import { basename as basename6, join as join12, relative, sep as sep4 } from "path";
29993
+ import { basename as basename6, join as join12 } from "path";
29945
29994
  var SKIP_DIRS = /* @__PURE__ */ new Set([
29946
29995
  ".git",
29947
29996
  "node_modules",
@@ -29959,23 +30008,28 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
29959
30008
  ".cache"
29960
30009
  ]);
29961
30010
  var MAX_FILES = 2e4;
29962
- function readIgnoreLayer(dir) {
30011
+ function readIgnoreLayer(dir, anchorLen) {
29963
30012
  try {
29964
30013
  const content = readFileSync8(join12(dir, ".gitignore"), "utf8");
29965
- return { base: dir, matcher: (0, import_ignore.default)().add(content) };
30014
+ return { matcher: (0, import_ignore.default)().add(content), anchorLen };
29966
30015
  } catch {
29967
30016
  return void 0;
29968
30017
  }
29969
30018
  }
29970
- function isIgnored(layers, absPath, isDir) {
29971
- let ignored = false;
29972
- for (const layer of layers) {
29973
- const rel = relative(layer.base, absPath).split(sep4).join("/") + (isDir ? "/" : "");
29974
- const verdict = layer.matcher.test(rel);
29975
- if (verdict.ignored) ignored = true;
29976
- else if (verdict.unignored) ignored = false;
30019
+ function relToAnchor(dirRel, anchorLen, name) {
30020
+ if (anchorLen === dirRel.length) return name;
30021
+ return `${dirRel.slice(anchorLen === 0 ? 0 : anchorLen + 1)}/${name}`;
30022
+ }
30023
+ function isIgnored(layers, dirRel, name, isDir) {
30024
+ const suffix = isDir ? "/" : "";
30025
+ for (let i = layers.length - 1; i >= 0; i--) {
30026
+ const layer = layers[i];
30027
+ if (layer === void 0) continue;
30028
+ const verdict = layer.matcher.test(relToAnchor(dirRel, layer.anchorLen, name) + suffix);
30029
+ if (verdict.ignored) return true;
30030
+ if (verdict.unignored) return false;
29977
30031
  }
29978
- return ignored;
30032
+ return false;
29979
30033
  }
29980
30034
  var GENERATED_FILES = /* @__PURE__ */ new Set([
29981
30035
  "pnpm-lock.yaml",
@@ -30028,7 +30082,7 @@ function classifyOrigin(relPath, name) {
30028
30082
  }
30029
30083
  function resolveProjectFiles(cwd) {
30030
30084
  try {
30031
- let visit2 = function(dir, layers) {
30085
+ let visit2 = function(dir, dirRel, layers) {
30032
30086
  let dirents;
30033
30087
  try {
30034
30088
  dirents = readdirSync4(dir, { withFileTypes: true, encoding: "utf8" });
@@ -30036,21 +30090,22 @@ function resolveProjectFiles(cwd) {
30036
30090
  walk.lostSubtree = true;
30037
30091
  return false;
30038
30092
  }
30039
- const layer = readIgnoreLayer(dir);
30093
+ const layer = readIgnoreLayer(dir, dirRel.length);
30040
30094
  const dirLayers = layer ? [...layers, layer] : layers;
30041
30095
  for (const entry of dirents) {
30042
- const fullPath = join12(dir, entry.name);
30043
30096
  if (entry.isDirectory()) {
30044
- if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, fullPath, true)) continue;
30097
+ if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, dirRel, entry.name, true)) continue;
30098
+ const fullPath = join12(dir, entry.name);
30045
30099
  if (existsSync8(join12(fullPath, ".git"))) continue;
30046
- if (visit2(fullPath, dirLayers)) return true;
30100
+ const childRel = dirRel === "" ? entry.name : `${dirRel}/${entry.name}`;
30101
+ if (visit2(fullPath, childRel, dirLayers)) return true;
30047
30102
  continue;
30048
30103
  }
30049
30104
  if (!entry.isFile()) continue;
30050
30105
  if (entry.name === ".git") continue;
30051
- if (isIgnored(dirLayers, fullPath, false)) continue;
30106
+ if (isIgnored(dirLayers, dirRel, entry.name, false)) continue;
30052
30107
  if (files.length >= MAX_FILES) return true;
30053
- const relPath = relative(root, fullPath).split(sep4).join("/");
30108
+ const relPath = dirRel === "" ? entry.name : `${dirRel}/${entry.name}`;
30054
30109
  files.push({
30055
30110
  path: relPath,
30056
30111
  name: basename6(entry.name),
@@ -30067,7 +30122,7 @@ function resolveProjectFiles(cwd) {
30067
30122
  const isLinkedCheckout = resolveHeadRoot(cwd) !== worktree;
30068
30123
  const files = [];
30069
30124
  const walk = { lostSubtree: false };
30070
- const truncated = visit2(root, []) || walk.lostSubtree || isLinkedCheckout;
30125
+ const truncated = visit2(root, "", []) || walk.lostSubtree || isLinkedCheckout;
30071
30126
  if (files.length === 0) return void 0;
30072
30127
  files.sort((a, b) => a.path.localeCompare(b.path));
30073
30128
  return { files, truncated, scannedAt: (/* @__PURE__ */ new Date()).toISOString() };