@swarmdo/cli 1.8.0 → 1.10.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.
Files changed (36) hide show
  1. package/README.md +3 -1
  2. package/dist/src/commands/index.d.ts.map +1 -1
  3. package/dist/src/commands/index.js +9 -3
  4. package/dist/src/commands/index.js.map +1 -1
  5. package/dist/src/commands/pack.d.ts +18 -0
  6. package/dist/src/commands/pack.d.ts.map +1 -0
  7. package/dist/src/commands/pack.js +184 -0
  8. package/dist/src/commands/pack.js.map +1 -0
  9. package/dist/src/commands/redact.d.ts +18 -0
  10. package/dist/src/commands/redact.d.ts.map +1 -0
  11. package/dist/src/commands/redact.js +128 -0
  12. package/dist/src/commands/redact.js.map +1 -0
  13. package/dist/src/mcp-client.d.ts.map +1 -1
  14. package/dist/src/mcp-client.js +2 -0
  15. package/dist/src/mcp-client.js.map +1 -1
  16. package/dist/src/mcp-tools/index.d.ts +1 -0
  17. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  18. package/dist/src/mcp-tools/index.js +1 -0
  19. package/dist/src/mcp-tools/index.js.map +1 -1
  20. package/dist/src/mcp-tools/profiles.d.ts.map +1 -1
  21. package/dist/src/mcp-tools/profiles.js +1 -0
  22. package/dist/src/mcp-tools/profiles.js.map +1 -1
  23. package/dist/src/mcp-tools/redact-tools.d.ts +11 -0
  24. package/dist/src/mcp-tools/redact-tools.d.ts.map +1 -0
  25. package/dist/src/mcp-tools/redact-tools.js +69 -0
  26. package/dist/src/mcp-tools/redact-tools.js.map +1 -0
  27. package/dist/src/pack/pack.d.ts +59 -0
  28. package/dist/src/pack/pack.d.ts.map +1 -0
  29. package/dist/src/pack/pack.js +178 -0
  30. package/dist/src/pack/pack.js.map +1 -0
  31. package/dist/src/redact/redact.d.ts +68 -0
  32. package/dist/src/redact/redact.d.ts.map +1 -0
  33. package/dist/src/redact/redact.js +153 -0
  34. package/dist/src/redact/redact.js.map +1 -0
  35. package/dist/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -15,6 +15,7 @@ export { workflowTools } from './workflow-tools.js';
15
15
  export { coverageRouterTools } from '../swarmvector/coverage-tools.js';
16
16
  export { analyzeTools } from './analyze-tools.js';
17
17
  export { codegraphTools } from './codegraph-tools.js';
18
+ export { redactTools } from './redact-tools.js';
18
19
  export { progressTools } from './progress-tools.js';
19
20
  export { transferTools } from './transfer-tools.js';
20
21
  export { securityTools } from './security-tools.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp-tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,yFAAyF;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,iEAAiE;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp-tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,yFAAyF;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,iEAAiE;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AAyB5D,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAI9E,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,gBAAgB,CAEnE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,KAAK,CAIxF"}
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AA0B5D,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAI9E,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,gBAAgB,CAEnE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,KAAK,CAIxF"}
@@ -23,6 +23,7 @@ const LEAN_GROUPS = [
23
23
  'agentdb', // agentdb pattern store/search
24
24
  'embeddings', // embeddings_generate / search / compare
25
25
  'codegraph', // codegraph_query / file / index — "where is X defined?"
26
+ 'redact', // redact_text / redact_scan — secret guard on the data path
26
27
  ];
27
28
  /** Balanced = lean + orchestration/session/system the average user reaches for. */
28
29
  const BALANCED_EXTRA = [
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../src/mcp-tools/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,8EAA8E;AAC9E,MAAM,WAAW,GAAG;IAClB,OAAO,EAAO,2DAA2D;IACzE,OAAO,EAAO,gCAAgC;IAC9C,QAAQ,EAAM,0CAA0C;IACxD,OAAO,EAAO,mDAAmD;IACjE,SAAS,EAAK,+BAA+B;IAC7C,YAAY,EAAE,yCAAyC;IACvD,WAAW,EAAG,yDAAyD;CAC/D,CAAC;AAEX,mFAAmF;AACnF,MAAM,cAAc,GAAG;IACrB,UAAU;IACV,MAAM;IACN,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,cAAc;IACd,QAAQ;IACR,aAAa;CACL,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAwD;IACjF,IAAI,EAAE,WAAW;IACjB,QAAQ,EAAE,CAAC,GAAG,WAAW,EAAE,GAAG,cAAc,CAAC;IAC7C,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,CAAS;IAC1C,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,MAAM,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAwB;IAC3D,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,kBAAkB,CAAC,IAAI,CAAC;QAAE,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../src/mcp-tools/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,8EAA8E;AAC9E,MAAM,WAAW,GAAG;IAClB,OAAO,EAAO,2DAA2D;IACzE,OAAO,EAAO,gCAAgC;IAC9C,QAAQ,EAAM,0CAA0C;IACxD,OAAO,EAAO,mDAAmD;IACjE,SAAS,EAAK,+BAA+B;IAC7C,YAAY,EAAE,yCAAyC;IACvD,WAAW,EAAG,yDAAyD;IACvE,QAAQ,EAAM,4DAA4D;CAClE,CAAC;AAEX,mFAAmF;AACnF,MAAM,cAAc,GAAG;IACrB,UAAU;IACV,MAAM;IACN,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,cAAc;IACd,QAAQ;IACR,aAAa;CACL,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAwD;IACjF,IAAI,EAAE,WAAW;IACjB,QAAQ,EAAE,CAAC,GAAG,WAAW,EAAE,GAAG,cAAc,CAAC;IAC7C,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,CAAS;IAC1C,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,MAAM,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAwB;IAC3D,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,kBAAkB,CAAC,IAAI,CAAC;QAAE,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Redact MCP Tools
3
+ *
4
+ * Give agents a secret guard on the data path: before writing tool output or
5
+ * context into memory / a message / a file, an agent can mask secrets
6
+ * (redact_text) or check for their presence (redact_scan). Deterministic —
7
+ * shares the pure engine in ../redact/redact.ts with the CLI command.
8
+ */
9
+ import type { MCPTool } from './types.js';
10
+ export declare const redactTools: MCPTool[];
11
+ //# sourceMappingURL=redact-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/redact-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AA4D1C,eAAO,MAAM,WAAW,EAAE,OAAO,EAAqC,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Redact MCP Tools
3
+ *
4
+ * Give agents a secret guard on the data path: before writing tool output or
5
+ * context into memory / a message / a file, an agent can mask secrets
6
+ * (redact_text) or check for their presence (redact_scan). Deterministic —
7
+ * shares the pure engine in ../redact/redact.ts with the CLI command.
8
+ */
9
+ import { redactText, scanText } from '../redact/redact.js';
10
+ function optsOf(params) {
11
+ const opts = { entropy: params.entropy !== false };
12
+ if (typeof params.keepPrefix === 'number')
13
+ opts.keepPrefix = params.keepPrefix;
14
+ if (typeof params.token === 'string')
15
+ opts.token = params.token;
16
+ if (typeof params.entropyThreshold === 'number')
17
+ opts.entropyThreshold = params.entropyThreshold;
18
+ if (Array.isArray(params.allowlist))
19
+ opts.allowlist = params.allowlist.filter((a) => typeof a === 'string');
20
+ return opts;
21
+ }
22
+ const redactTextTool = {
23
+ name: 'redact_text',
24
+ description: 'Mask secrets (API keys, tokens, private keys) in a string before you store it in memory, put it in a message, or write it to a file. Returns the redacted text plus what was found. Deterministic; run this on any untrusted or command-derived content you are about to persist or forward.',
25
+ category: 'redact',
26
+ tags: ['security', 'secrets', 'redaction', 'safety'],
27
+ inputSchema: {
28
+ type: 'object',
29
+ properties: {
30
+ text: { type: 'string', description: 'The content to redact' },
31
+ keepPrefix: { type: 'number', description: 'Keep this many leading chars of each secret (default 3; 0 = full mask)' },
32
+ token: { type: 'string', description: 'Replacement token after the kept prefix (default [REDACTED])' },
33
+ entropy: { type: 'boolean', description: 'Enable the high-entropy keyword=value fallback (default true)' },
34
+ entropyThreshold: { type: 'number', description: 'Entropy threshold in bits/char for the fallback (default 3.5)' },
35
+ allowlist: { type: 'array', items: { type: 'string' }, description: 'Substrings; matching secrets are left untouched' },
36
+ },
37
+ required: ['text'],
38
+ },
39
+ handler: async (params) => {
40
+ if (typeof params.text !== 'string')
41
+ return { error: true, message: 'text is required' };
42
+ const { output, findings } = redactText(params.text, optsOf(params));
43
+ return { redacted: output, count: findings.length, findings };
44
+ },
45
+ };
46
+ const redactScanTool = {
47
+ name: 'redact_scan',
48
+ description: 'Check a string for secrets WITHOUT rewriting it — returns the list of findings (rule, line, column, description). Use to decide whether content is safe to store/forward, or to gate on it. Pair with redact_text when you actually need the masked output.',
49
+ category: 'redact',
50
+ tags: ['security', 'secrets', 'scan', 'safety'],
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: {
54
+ text: { type: 'string', description: 'The content to scan' },
55
+ entropy: { type: 'boolean', description: 'Enable the high-entropy keyword=value fallback (default true)' },
56
+ entropyThreshold: { type: 'number', description: 'Entropy threshold in bits/char for the fallback (default 3.5)' },
57
+ allowlist: { type: 'array', items: { type: 'string' }, description: 'Substrings to ignore' },
58
+ },
59
+ required: ['text'],
60
+ },
61
+ handler: async (params) => {
62
+ if (typeof params.text !== 'string')
63
+ return { error: true, message: 'text is required' };
64
+ const findings = scanText(params.text, optsOf(params));
65
+ return { clean: findings.length === 0, count: findings.length, findings };
66
+ },
67
+ };
68
+ export const redactTools = [redactTextTool, redactScanTool];
69
+ //# sourceMappingURL=redact-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact-tools.js","sourceRoot":"","sources":["../../../src/mcp-tools/redact-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * pack.ts — bundle a repo (or a subset) into one AI-friendly context blob.
3
+ *
4
+ * swarmdo has no context-assembly primitive: `codegraph` indexes symbols for
5
+ * *querying* and `compact` de-noises a *stream*, but neither emits promptable
6
+ * source for *feeding a model*. `pack` walks the tree (respecting default skips
7
+ * + a minimal .gitignore + glob include/exclude), and writes a bundle in
8
+ * markdown / xml / json / plain with a directory tree and per-file + total
9
+ * token estimates. Deterministic — same inputs, byte-identical output.
10
+ *
11
+ * Pure engine: the fs walk is injected as a file list + reader by the caller
12
+ * (../commands/pack.ts), so this module has no fs dependency and is trivially
13
+ * testable. Optional secret redaction reuses ../redact/redact.ts.
14
+ */
15
+ export type PackFormat = 'md' | 'xml' | 'json' | 'plain';
16
+ export interface PackFile {
17
+ /** repo-relative path, POSIX separators */
18
+ path: string;
19
+ content: string;
20
+ }
21
+ export interface PackOptions {
22
+ format?: PackFormat;
23
+ /** include a directory tree at the top (default true) */
24
+ tree?: boolean;
25
+ /** optional per-file transform (e.g. secret redaction) applied before bundling */
26
+ transform?: (content: string, path: string) => string;
27
+ }
28
+ export interface PackFileStat {
29
+ path: string;
30
+ bytes: number;
31
+ tokens: number;
32
+ }
33
+ export interface PackResult {
34
+ output: string;
35
+ stats: {
36
+ files: number;
37
+ bytes: number;
38
+ tokens: number;
39
+ perFile: PackFileStat[];
40
+ };
41
+ }
42
+ /**
43
+ * Cheap, deterministic token estimate (~4 chars/token, the widely-used rule of
44
+ * thumb for English + code). Not a real BPE tokenizer — good enough for budget
45
+ * warnings, and dependency-free.
46
+ */
47
+ export declare function estimateTokens(text: string): number;
48
+ /** Build an ASCII directory tree from a sorted file list. */
49
+ export declare function buildTree(paths: string[]): string;
50
+ /** Bundle files into a single context blob per `format`. */
51
+ export declare function packFiles(input: PackFile[], opts?: PackOptions): PackResult;
52
+ /**
53
+ * Minimal .gitignore-style matcher. Handles the common cases — plain names,
54
+ * `dir/`, `*.ext` globs, leading `/` anchors, and `!` negation — not the full
55
+ * spec (no `**` depth semantics beyond substring, no nested ignore files).
56
+ * Good enough to keep obvious noise out; full semantics is a follow-up.
57
+ */
58
+ export declare function makeIgnoreMatcher(patterns: string[]): (relPath: string) => boolean;
59
+ //# sourceMappingURL=pack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../../../src/pack/pack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,yDAAyD;IACzD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kFAAkF;IAClF,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACvD;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,YAAY,EAAE,CAAC;KACzB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CA8BjD;AAuDD,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,GAAE,WAAgB,GAAG,UAAU,CA4B/E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CA8BlF"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * pack.ts — bundle a repo (or a subset) into one AI-friendly context blob.
3
+ *
4
+ * swarmdo has no context-assembly primitive: `codegraph` indexes symbols for
5
+ * *querying* and `compact` de-noises a *stream*, but neither emits promptable
6
+ * source for *feeding a model*. `pack` walks the tree (respecting default skips
7
+ * + a minimal .gitignore + glob include/exclude), and writes a bundle in
8
+ * markdown / xml / json / plain with a directory tree and per-file + total
9
+ * token estimates. Deterministic — same inputs, byte-identical output.
10
+ *
11
+ * Pure engine: the fs walk is injected as a file list + reader by the caller
12
+ * (../commands/pack.ts), so this module has no fs dependency and is trivially
13
+ * testable. Optional secret redaction reuses ../redact/redact.ts.
14
+ */
15
+ /**
16
+ * Cheap, deterministic token estimate (~4 chars/token, the widely-used rule of
17
+ * thumb for English + code). Not a real BPE tokenizer — good enough for budget
18
+ * warnings, and dependency-free.
19
+ */
20
+ export function estimateTokens(text) {
21
+ if (!text)
22
+ return 0;
23
+ return Math.ceil(text.length / 4);
24
+ }
25
+ /** Build an ASCII directory tree from a sorted file list. */
26
+ export function buildTree(paths) {
27
+ const root = {};
28
+ for (const p of [...paths].sort()) {
29
+ const parts = p.split('/');
30
+ let node = root;
31
+ for (let i = 0; i < parts.length; i++) {
32
+ const part = parts[i];
33
+ const isFile = i === parts.length - 1;
34
+ if (isFile) {
35
+ if (!node['\0files'])
36
+ node['\0files'] = [];
37
+ node['\0files'].push(part);
38
+ }
39
+ else {
40
+ node[part] = node[part] ?? {};
41
+ node = node[part];
42
+ }
43
+ }
44
+ }
45
+ const lines = [];
46
+ const walk = (node, prefix) => {
47
+ const dirs = Object.keys(node).filter((k) => k !== '\0files').sort();
48
+ const files = (node['\0files'] ?? []).sort();
49
+ const entries = [...dirs.map((d) => ['dir', d]), ...files.map((f) => ['file', f])];
50
+ entries.forEach(([kind, name], i) => {
51
+ const last = i === entries.length - 1;
52
+ lines.push(`${prefix}${last ? '└── ' : '├── '}${name}${kind === 'dir' ? '/' : ''}`);
53
+ if (kind === 'dir')
54
+ walk(node[name], prefix + (last ? ' ' : '│ '));
55
+ });
56
+ };
57
+ walk(root, '');
58
+ return lines.join('\n');
59
+ }
60
+ /** Pick a fenced-code language hint from a file extension. */
61
+ function langOf(path) {
62
+ const ext = path.slice(path.lastIndexOf('.') + 1).toLowerCase();
63
+ const map = {
64
+ ts: 'typescript', tsx: 'tsx', js: 'javascript', jsx: 'jsx', mjs: 'javascript', cjs: 'javascript',
65
+ json: 'json', md: 'markdown', py: 'python', rs: 'rust', go: 'go', sh: 'bash', yml: 'yaml', yaml: 'yaml',
66
+ html: 'html', css: 'css', sql: 'sql', toml: 'toml',
67
+ };
68
+ return map[ext] ?? '';
69
+ }
70
+ function renderMarkdown(files, opts) {
71
+ const parts = ['# Repository context', ''];
72
+ if (opts.tree !== false) {
73
+ parts.push('## Files', '', '```', buildTree(files.map((f) => f.path)), '```', '');
74
+ }
75
+ parts.push('## Contents', '');
76
+ for (const f of files) {
77
+ const lang = langOf(f.path);
78
+ parts.push(`### ${f.path}`, '', '```' + lang, f.content.replace(/\n$/, ''), '```', '');
79
+ }
80
+ return parts.join('\n');
81
+ }
82
+ function escapeXml(s) {
83
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
84
+ }
85
+ function renderXml(files, opts) {
86
+ const parts = ['<repository>'];
87
+ if (opts.tree !== false) {
88
+ parts.push(' <tree>', escapeXml(buildTree(files.map((f) => f.path))), ' </tree>');
89
+ }
90
+ parts.push(' <files>');
91
+ for (const f of files) {
92
+ parts.push(` <file path="${escapeXml(f.path)}">`, escapeXml(f.content.replace(/\n$/, '')), ' </file>');
93
+ }
94
+ parts.push(' </files>', '</repository>');
95
+ return parts.join('\n');
96
+ }
97
+ function renderJson(files) {
98
+ return JSON.stringify({ files: files.map((f) => ({ path: f.path, content: f.content })) }, null, 2);
99
+ }
100
+ function renderPlain(files) {
101
+ const parts = [];
102
+ for (const f of files) {
103
+ parts.push('='.repeat(8) + ' ' + f.path + ' ' + '='.repeat(8), f.content.replace(/\n$/, ''), '');
104
+ }
105
+ return parts.join('\n');
106
+ }
107
+ /** Bundle files into a single context blob per `format`. */
108
+ export function packFiles(input, opts = {}) {
109
+ const files = input
110
+ .map((f) => ({ path: f.path, content: opts.transform ? opts.transform(f.content, f.path) : f.content }))
111
+ .sort((a, b) => a.path.localeCompare(b.path));
112
+ const format = opts.format ?? 'md';
113
+ let output;
114
+ switch (format) {
115
+ case 'xml':
116
+ output = renderXml(files, opts);
117
+ break;
118
+ case 'json':
119
+ output = renderJson(files);
120
+ break;
121
+ case 'plain':
122
+ output = renderPlain(files);
123
+ break;
124
+ default: output = renderMarkdown(files, opts);
125
+ }
126
+ const perFile = files.map((f) => ({
127
+ path: f.path,
128
+ bytes: Buffer.byteLength(f.content, 'utf8'),
129
+ tokens: estimateTokens(f.content),
130
+ }));
131
+ return {
132
+ output,
133
+ stats: {
134
+ files: files.length,
135
+ bytes: perFile.reduce((n, f) => n + f.bytes, 0),
136
+ tokens: estimateTokens(output),
137
+ perFile,
138
+ },
139
+ };
140
+ }
141
+ /**
142
+ * Minimal .gitignore-style matcher. Handles the common cases — plain names,
143
+ * `dir/`, `*.ext` globs, leading `/` anchors, and `!` negation — not the full
144
+ * spec (no `**` depth semantics beyond substring, no nested ignore files).
145
+ * Good enough to keep obvious noise out; full semantics is a follow-up.
146
+ */
147
+ export function makeIgnoreMatcher(patterns) {
148
+ const rules = patterns
149
+ .map((p) => p.trim())
150
+ .filter((p) => p && !p.startsWith('#'))
151
+ .map((p) => {
152
+ const negate = p.startsWith('!');
153
+ let body = negate ? p.slice(1) : p;
154
+ const dirOnly = body.endsWith('/');
155
+ if (dirOnly)
156
+ body = body.slice(0, -1);
157
+ const anchored = body.startsWith('/');
158
+ if (anchored)
159
+ body = body.slice(1);
160
+ const re = new RegExp('^' +
161
+ body
162
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
163
+ .replace(/\*/g, '[^/]*')
164
+ .replace(/\?/g, '[^/]') +
165
+ (dirOnly ? '(/|$)' : '($|/)'));
166
+ return { negate, anchored, re };
167
+ });
168
+ return (relPath) => {
169
+ let ignored = false;
170
+ for (const r of rules) {
171
+ const candidates = r.anchored ? [relPath] : [relPath, ...relPath.split('/').map((_, i, a) => a.slice(i).join('/'))];
172
+ if (candidates.some((c) => r.re.test(c)))
173
+ ignored = !r.negate;
174
+ }
175
+ return ignored;
176
+ };
177
+ }
178
+ //# sourceMappingURL=pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../../../src/pack/pack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * redact.ts — deterministic secret detection + redaction for the agent data
3
+ * path. swarmdo pipes tool output, memory, and context into LLMs constantly;
4
+ * `compact` de-noises that stream but never redacts, and `security scan` is
5
+ * CVE-oriented, not a content filter. This engine is the missing guard: it
6
+ * catches API keys / tokens / private keys via a high-confidence rule catalog
7
+ * plus a Shannon-entropy fallback for keyword-prefixed assignments, and masks
8
+ * them before content reaches a model, a log, or memory.
9
+ *
10
+ * Pure + LLM-free by design — known-secret fixtures in, deterministic masked
11
+ * bytes out. The CLI wrapper (../commands/redact.ts) and an MCP tool share it.
12
+ */
13
+ export interface RedactRule {
14
+ /** stable slug, e.g. 'aws-access-key' */
15
+ id: string;
16
+ /** human description for --scan output */
17
+ description: string;
18
+ /** global regex; the whole match (or capture group `group`) is the secret */
19
+ regex: RegExp;
20
+ /** if set, redact this capture group rather than the whole match */
21
+ group?: number;
22
+ }
23
+ export interface Finding {
24
+ ruleId: string;
25
+ description: string;
26
+ /** 1-based line number */
27
+ line: number;
28
+ /** 1-based column of the secret within the line */
29
+ column: number;
30
+ /** the raw secret text that matched */
31
+ match: string;
32
+ /** what it was replaced with */
33
+ redacted: string;
34
+ }
35
+ export interface RedactOptions {
36
+ /** keep this many leading chars of a secret, mask the rest (default 3) */
37
+ keepPrefix?: number;
38
+ /** the replacement token appended after the kept prefix (default '[REDACTED]') */
39
+ token?: string;
40
+ /** enable the entropy fallback for keyword=value assignments (default true) */
41
+ entropy?: boolean;
42
+ /** Shannon-entropy threshold (bits/char) above which a keyworded value is a secret (default 3.5) */
43
+ entropyThreshold?: number;
44
+ /** substrings/regexes; any secret whose match includes one is left untouched */
45
+ allowlist?: Array<string | RegExp>;
46
+ }
47
+ export interface RedactResult {
48
+ output: string;
49
+ findings: Finding[];
50
+ }
51
+ /**
52
+ * High-confidence catalog. Ordered — more specific rules (e.g. Anthropic
53
+ * `sk-ant-`) come before broader ones (`sk-` OpenAI) so the specific rule wins
54
+ * and we don't double-count. Patterns are conservative to keep false positives
55
+ * low; the entropy fallback covers the long tail of custom secrets.
56
+ */
57
+ export declare const RULES: RedactRule[];
58
+ /** Shannon entropy in bits/char. Pure; used by the assignment fallback. */
59
+ export declare function shannonEntropy(s: string): number;
60
+ /** Mask a secret: keep `keepPrefix` leading chars, then the token. */
61
+ export declare function maskSecret(secret: string, opts?: RedactOptions): string;
62
+ /** Scan without rewriting — returns findings only (for --scan / CI gating). */
63
+ export declare function scanText(text: string, opts?: RedactOptions): Finding[];
64
+ /** Redact secrets in `text`, returning the rewritten output + findings. */
65
+ export declare function redactText(text: string, opts?: RedactOptions): RedactResult;
66
+ /** One-line human summary for stderr. */
67
+ export declare function formatFindingsSummary(findings: Finding[]): string;
68
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/redact/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,UAAU,EAiB7B,CAAC;AAMF,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAUhD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAI3E;AAmED,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,EAAE,CAK1E;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,YAAY,CAc/E;AAED,yCAAyC;AACzC,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAMjE"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * redact.ts — deterministic secret detection + redaction for the agent data
3
+ * path. swarmdo pipes tool output, memory, and context into LLMs constantly;
4
+ * `compact` de-noises that stream but never redacts, and `security scan` is
5
+ * CVE-oriented, not a content filter. This engine is the missing guard: it
6
+ * catches API keys / tokens / private keys via a high-confidence rule catalog
7
+ * plus a Shannon-entropy fallback for keyword-prefixed assignments, and masks
8
+ * them before content reaches a model, a log, or memory.
9
+ *
10
+ * Pure + LLM-free by design — known-secret fixtures in, deterministic masked
11
+ * bytes out. The CLI wrapper (../commands/redact.ts) and an MCP tool share it.
12
+ */
13
+ /**
14
+ * High-confidence catalog. Ordered — more specific rules (e.g. Anthropic
15
+ * `sk-ant-`) come before broader ones (`sk-` OpenAI) so the specific rule wins
16
+ * and we don't double-count. Patterns are conservative to keep false positives
17
+ * low; the entropy fallback covers the long tail of custom secrets.
18
+ */
19
+ export const RULES = [
20
+ { id: 'private-key', description: 'Private key block header', regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/g },
21
+ { id: 'aws-access-key', description: 'AWS access key ID', regex: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASCA)[0-9A-Z]{16}\b/g },
22
+ { id: 'github-pat', description: 'GitHub personal access token', regex: /\bghp_[0-9A-Za-z]{36}\b/g },
23
+ { id: 'github-oauth', description: 'GitHub OAuth/app token', regex: /\b(?:gho|ghu|ghs|ghr)_[0-9A-Za-z]{36}\b/g },
24
+ { id: 'github-fine-pat', description: 'GitHub fine-grained PAT', regex: /\bgithub_pat_[0-9A-Za-z_]{82}\b/g },
25
+ { id: 'gitlab-pat', description: 'GitLab personal access token', regex: /\bglpat-[0-9A-Za-z_-]{20}\b/g },
26
+ { id: 'anthropic-key', description: 'Anthropic API key', regex: /\bsk-ant-[0-9A-Za-z-]{16,}\b/g },
27
+ { id: 'openai-key', description: 'OpenAI API key', regex: /\bsk-(?:proj-)?[0-9A-Za-z]{20,}\b/g },
28
+ { id: 'google-api-key', description: 'Google API key', regex: /\bAIza[0-9A-Za-z_-]{35}\b/g },
29
+ { id: 'slack-token', description: 'Slack token', regex: /\bxox[baprs]-[0-9A-Za-z-]{10,}\b/g },
30
+ { id: 'slack-webhook', description: 'Slack incoming webhook', regex: /https:\/\/hooks\.slack\.com\/services\/T[0-9A-Za-z_]+\/B[0-9A-Za-z_]+\/[0-9A-Za-z_]+/g },
31
+ { id: 'stripe-key', description: 'Stripe secret/restricted key', regex: /\b(?:sk|rk)_live_[0-9A-Za-z]{24,}\b/g },
32
+ { id: 'sendgrid-key', description: 'SendGrid API key', regex: /\bSG\.[0-9A-Za-z_-]{22}\.[0-9A-Za-z_-]{43}\b/g },
33
+ { id: 'twilio-key', description: 'Twilio API key SID', regex: /\bSK[0-9a-fA-F]{32}\b/g },
34
+ { id: 'npm-token', description: 'npm access token', regex: /\bnpm_[0-9A-Za-z]{36}\b/g },
35
+ { id: 'jwt', description: 'JSON Web Token', regex: /\beyJ[0-9A-Za-z_-]{10,}\.eyJ[0-9A-Za-z_-]{10,}\.[0-9A-Za-z_-]{10,}\b/g },
36
+ ];
37
+ /** Keyword-prefixed assignment, e.g. `api_key = "..."` — the value is group 1. */
38
+ const ASSIGNMENT_RE = /(?:pass(?:word|wd)?|pwd|secret|token|api[_-]?key|apikey|access[_-]?key|auth[_-]?token|client[_-]?secret)["']?\s*[:=]\s*["']?([^\s"'`,;]{8,})/gi;
39
+ /** Shannon entropy in bits/char. Pure; used by the assignment fallback. */
40
+ export function shannonEntropy(s) {
41
+ if (!s)
42
+ return 0;
43
+ const freq = new Map();
44
+ for (const ch of s)
45
+ freq.set(ch, (freq.get(ch) ?? 0) + 1);
46
+ let bits = 0;
47
+ for (const n of freq.values()) {
48
+ const p = n / s.length;
49
+ bits -= p * Math.log2(p);
50
+ }
51
+ return bits;
52
+ }
53
+ /** Mask a secret: keep `keepPrefix` leading chars, then the token. */
54
+ export function maskSecret(secret, opts = {}) {
55
+ const keep = Math.max(0, Math.min(opts.keepPrefix ?? 3, secret.length));
56
+ const token = opts.token ?? '[REDACTED]';
57
+ return secret.slice(0, keep) + token;
58
+ }
59
+ function isAllowlisted(match, allowlist) {
60
+ if (!allowlist || allowlist.length === 0)
61
+ return false;
62
+ return allowlist.some((a) => (typeof a === 'string' ? match.includes(a) : a.test(match)));
63
+ }
64
+ /** Collect all rule + entropy hits across the text, deduped by offset. */
65
+ function collectHits(text, opts) {
66
+ const hits = [];
67
+ const claimed = []; // [start,end) ranges already taken
68
+ const overlaps = (start, end) => claimed.some(([s, e]) => start < e && end > s);
69
+ for (const rule of RULES) {
70
+ rule.regex.lastIndex = 0;
71
+ let m;
72
+ while ((m = rule.regex.exec(text)) !== null) {
73
+ const secret = rule.group != null ? m[rule.group] : m[0];
74
+ if (secret == null)
75
+ continue;
76
+ const start = rule.group != null ? m.index + m[0].indexOf(secret) : m.index;
77
+ const end = start + secret.length;
78
+ if (overlaps(start, end))
79
+ continue;
80
+ if (isAllowlisted(secret, opts.allowlist))
81
+ continue;
82
+ claimed.push([start, end]);
83
+ hits.push({ ruleId: rule.id, description: rule.description, index: start, match: secret });
84
+ if (m.index === rule.regex.lastIndex)
85
+ rule.regex.lastIndex++; // guard zero-width
86
+ }
87
+ }
88
+ if (opts.entropy !== false) {
89
+ const threshold = opts.entropyThreshold ?? 3.5;
90
+ ASSIGNMENT_RE.lastIndex = 0;
91
+ let m;
92
+ while ((m = ASSIGNMENT_RE.exec(text)) !== null) {
93
+ const value = m[1];
94
+ const start = m.index + m[0].indexOf(value);
95
+ const end = start + value.length;
96
+ if (overlaps(start, end))
97
+ continue;
98
+ if (isAllowlisted(value, opts.allowlist))
99
+ continue;
100
+ if (shannonEntropy(value) < threshold)
101
+ continue;
102
+ claimed.push([start, end]);
103
+ hits.push({ ruleId: 'high-entropy-assignment', description: 'High-entropy secret assignment', index: start, match: value });
104
+ }
105
+ }
106
+ return hits.sort((a, b) => a.index - b.index);
107
+ }
108
+ /** Map an absolute offset to 1-based {line, column}. */
109
+ function lineColOf(text, index) {
110
+ let line = 1;
111
+ let last = -1;
112
+ for (let i = 0; i < index; i++) {
113
+ if (text[i] === '\n') {
114
+ line++;
115
+ last = i;
116
+ }
117
+ }
118
+ return { line, column: index - last };
119
+ }
120
+ /** Scan without rewriting — returns findings only (for --scan / CI gating). */
121
+ export function scanText(text, opts = {}) {
122
+ return collectHits(text, opts).map((h) => {
123
+ const { line, column } = lineColOf(text, h.index);
124
+ return { ruleId: h.ruleId, description: h.description, line, column, match: h.match, redacted: maskSecret(h.match, opts) };
125
+ });
126
+ }
127
+ /** Redact secrets in `text`, returning the rewritten output + findings. */
128
+ export function redactText(text, opts = {}) {
129
+ const hits = collectHits(text, opts);
130
+ const findings = [];
131
+ let out = '';
132
+ let cursor = 0;
133
+ for (const h of hits) {
134
+ const { line, column } = lineColOf(text, h.index);
135
+ const redacted = maskSecret(h.match, opts);
136
+ out += text.slice(cursor, h.index) + redacted;
137
+ cursor = h.index + h.match.length;
138
+ findings.push({ ruleId: h.ruleId, description: h.description, line, column, match: h.match, redacted });
139
+ }
140
+ out += text.slice(cursor);
141
+ return { output: out, findings };
142
+ }
143
+ /** One-line human summary for stderr. */
144
+ export function formatFindingsSummary(findings) {
145
+ if (findings.length === 0)
146
+ return 'redact: no secrets found';
147
+ const byRule = new Map();
148
+ for (const f of findings)
149
+ byRule.set(f.ruleId, (byRule.get(f.ruleId) ?? 0) + 1);
150
+ const parts = [...byRule.entries()].sort((a, b) => b[1] - a[1]).map(([r, n]) => `${r}:${n}`);
151
+ return `redact: ${findings.length} secret${findings.length === 1 ? '' : 's'} redacted (${parts.join(', ')})`;
152
+ }
153
+ //# sourceMappingURL=redact.js.map