@privacyscrubber/sdk 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,8 @@
6
6
  [![Air-Gapped](https://img.shields.io/badge/network_calls-0-blueviolet.svg)](https://privacyscrubber.com)
7
7
  [![TypeScript Typings](https://img.shields.io/badge/types-TypeScript-blue.svg)](https://www.npmjs.com/package/@privacyscrubber/sdk)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
+ [![Patent Pending](https://img.shields.io/badge/Patent-Pending%20(ILPO%20331905)-06b6d4.svg)](https://privacyscrubber.com/patents/)
10
+ [![GitHub Stars](https://img.shields.io/github/stars/moxno/privacyscrubber-mcp?style=social)](https://github.com/moxno/privacyscrubber-mcp)
9
11
 
10
12
  > **Zero-Trust Data Sanitization (ZTDS) & PII Redaction Engine**
11
13
  > 100% In-Memory Execution. Zero Remote Network Calls. Zero Disk Writes. Sub-millisecond Latency.
@@ -26,7 +28,7 @@ The official Node.js / TypeScript programmatic SDK for [PrivacyScrubber.com](htt
26
28
  | **DevOps Secrets Scanning** | **Built-in (AWS, JWT, DBs)** | Custom regex rules | Custom detectors | Custom classifiers |
27
29
  | **Cost Predictability** | **Free Tier / Flat $199/mo** | DevOps server maintenance | Pay-per-GB cloud fees | Pay-per-unit API fees |
28
30
 
29
- > 📊 **Full Technical Benchmark**: Read the in-depth latency analysis, cold-start profiles, and architecture deep-dive in [docs/benchmarks/sdk-vs-presidio.md](https://github.com/moxno/PrivacyScrubber/blob/main/docs/benchmarks/sdk-vs-presidio.md).
31
+ > 📊 **Full Technical Benchmark**: Read the in-depth latency analysis, cold-start profiles, and architecture deep-dive in [docs/benchmarks/sdk-vs-presidio.md](https://github.com/moxno/privacyscrubber-mcp/blob/main/docs/benchmarks/sdk-vs-presidio.md).
30
32
 
31
33
  ### 💡 The Breach Math vs SDK Math
32
34
 
@@ -92,6 +94,7 @@ console.log(auditReceipt);
92
94
  // > * **Risk Level:** 🟡 MODERATE EXPOSURE
93
95
  // > * **Compliance Enforced:** ZTDS Standard, GDPR (Art. 4), CCPA/CPRA
94
96
  // > * **Tokens Masked:** 2 (1 NAME, 1 PHONE)
97
+ // > * ⭐ **Star on GitHub:** [moxno/privacyscrubber-mcp](https://github.com/moxno/privacyscrubber-mcp) | **SDK & Enterprise:** [privacyscrubber.com/pricing](https://privacyscrubber.com/pricing)
95
98
 
96
99
  // 2. Deterministically restore LLM response
97
100
  const aiResponse = "I have queued an SMS notification for [NAME_1] at [PHONE_1].";
@@ -103,7 +106,157 @@ console.log(restoredText);
103
106
 
104
107
  ---
105
108
 
106
- ### 3. RAG & Vector Database Ingestion (Pinecone, Chroma, pgvector)
109
+ ### 3. Vercel AI SDK Integration (`generateText` & `streamText`)
110
+
111
+ Drop-in sanitization for Next.js and Node.js applications built with the Vercel AI SDK (`ai`):
112
+
113
+ ```typescript
114
+ import { generateText } from 'ai';
115
+ import { openai } from '@ai-sdk/openai';
116
+ import { sanitize, restore } from '@privacyscrubber/sdk';
117
+
118
+ export async function POST(req: Request) {
119
+ const { prompt } = await req.json();
120
+
121
+ // 1. Sanitize user prompt in local RAM (<1ms)
122
+ const { scrubbedText, tokenMap } = sanitize(prompt, {
123
+ profile: 'General',
124
+ detectSecrets: true
125
+ });
126
+
127
+ // 2. Outbound call to LLM contains zero PII or credentials
128
+ const { text } = await generateText({
129
+ model: openai('gpt-4o'),
130
+ prompt: scrubbedText,
131
+ });
132
+
133
+ // 3. Deterministically restore authentic tokens locally
134
+ const { restoredText } = restore(text, tokenMap);
135
+
136
+ return Response.json({ response: restoredText });
137
+ }
138
+ ```
139
+
140
+ ---
141
+
142
+ ### 3b. Real-Time LLM Streaming De-tokenization (`wrapAiStream`, `createStreamTokenRestorer`, `createNodeStreamTokenRestorer`)
143
+
144
+ When LLMs stream responses via Server-Sent Events (SSE) or WebSockets, tokens can be split across arbitrary chunk boundaries (e.g. Chunk 1: `"Contact [EM"`, Chunk 2: `"AIL_1] at once"`). `StreamingDetokenizer` uses a zero-latency sliding window buffer to stitch and restore tokens on the fly without delaying stream output:
145
+
146
+ ```typescript
147
+ import { streamText } from 'ai';
148
+ import { openai } from '@ai-sdk/openai';
149
+ import { sanitize, wrapAiStream } from '@privacyscrubber/sdk';
150
+
151
+ export async function POST(req: Request) {
152
+ const { prompt } = await req.json();
153
+
154
+ // 1. Sanitize prompt in RAM
155
+ const { scrubbedText, tokenMap } = sanitize(prompt, { profile: 'General' });
156
+
157
+ // 2. Stream from LLM
158
+ const { textStream } = streamText({
159
+ model: openai('gpt-4o'),
160
+ prompt: scrubbedText,
161
+ });
162
+
163
+ // 3. Wrap stream — split tokens ([EM + AIL_1]) are rehydrated in real-time!
164
+ const restoredStream = wrapAiStream(textStream, tokenMap);
165
+
166
+ // 4. Return SSE stream to client with authentic values restored
167
+ return new Response(restoredStream);
168
+ }
169
+ ```
170
+
171
+ #### Transparent OpenAI Streaming (`wrapOpenAI` with `stream: true`):
172
+ ```typescript
173
+ import OpenAI from 'openai';
174
+ import { wrapOpenAI } from '@privacyscrubber/sdk';
175
+
176
+ const client = wrapOpenAI(new OpenAI(), { profile: 'Dev' });
177
+
178
+ const stream = await client.chat.completions.create({
179
+ model: 'gpt-4o',
180
+ messages: [{ role: 'user', content: 'Debug AWS_KEY=AKIAIOSFODNN7EXAMPLE for user alice@acme.com' }],
181
+ stream: true, // Transparent real-time chunk de-tokenization!
182
+ });
183
+
184
+ for await (const chunk of stream) {
185
+ process.stdout.write(chunk.choices[0]?.delta?.content || '');
186
+ }
187
+ ```
188
+
189
+ #### Node.js Pipeline Streaming (`createNodeStreamTokenRestorer`):
190
+ ```javascript
191
+ import { pipeline } from 'node:stream/promises';
192
+ import { createNodeStreamTokenRestorer } from '@privacyscrubber/sdk';
193
+
194
+ const restorer = createNodeStreamTokenRestorer(tokenMap);
195
+ await pipeline(readableLlmStream, restorer, clientResponseStream);
196
+ ```
197
+
198
+ ---
199
+
200
+ ### 4. LangChain (LCEL Pipelines & `createLangChainTransform`)
201
+
202
+ Use the official middleware helper or compose inside LangChain Expression Language (LCEL) chains:
203
+
204
+ ```typescript
205
+ import { ChatOpenAI } from '@langchain/openai';
206
+ import { createLangChainTransform, PrivacyScrubberEngine } from '@privacyscrubber/sdk';
207
+
208
+ // Option A: 1-line middleware helper
209
+ const transform = createLangChainTransform({ defaultProfile: 'Healthcare' });
210
+ const { scrubbedText, tokenMap } = transform.preprocess("Patient John Doe MRN-99120 prescribed Lipitor.");
211
+ // ... invoke LLM with scrubbedText ...
212
+ const restoredOutput = transform.postprocess(llmOutput, tokenMap);
213
+
214
+ // Option B: Composable LCEL Runnable chain
215
+ const engine = new PrivacyScrubberEngine({ defaultProfile: 'Dev' });
216
+ const model = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });
217
+
218
+ const secureChain = async (rawQuestion: string) => {
219
+ const { scrubbedText, tokenMap } = engine.sanitize(rawQuestion);
220
+ const response = await model.invoke(scrubbedText);
221
+ return engine.restore(response.content as string, tokenMap);
222
+ };
223
+
224
+ const result = await secureChain("Debug AWS_KEY=AKIAIOSFODNN7EXAMPLE for user alice@acme.com");
225
+ ```
226
+
227
+ ---
228
+
229
+ ### 5. LlamaIndex.TS (RAG Node Sanitizer)
230
+
231
+ Prevent PII poisoning in vector stores by sanitizing documents prior to indexing:
232
+
233
+ ```typescript
234
+ import { Document } from 'llamaindex';
235
+ import { sanitize } from '@privacyscrubber/sdk';
236
+
237
+ // Sanitize documents before vector index ingestion (HIPAA & GDPR safe)
238
+ export function sanitizeDocumentsForIndex(docs: Document[]): Document[] {
239
+ return docs.map(doc => {
240
+ const { scrubbedText, telemetry } = sanitize(doc.text, {
241
+ profile: 'Healthcare', // Masks 18 HIPAA identifiers
242
+ detectSecrets: true
243
+ });
244
+
245
+ return new Document({
246
+ text: scrubbedText,
247
+ metadata: {
248
+ ...doc.metadata,
249
+ psRiskLevel: telemetry.riskLevel,
250
+ psCompliant: telemetry.frameworksList.join(',')
251
+ }
252
+ });
253
+ });
254
+ }
255
+ ```
256
+
257
+ ---
258
+
259
+ ### 6. RAG & Vector Database Ingestion (Pinecone, Chroma, pgvector)
107
260
 
108
261
  Storing raw PII inside vector embeddings is irreversible and violates GDPR Article 17 (Right to be Forgotten). Sanitize document chunks prior to vectorization:
109
262
 
@@ -127,7 +280,7 @@ function prepareVectorChunk(rawDocumentChunk) {
127
280
 
128
281
  ---
129
282
 
130
- ### 4. Stateful Multi-Turn Agent Engine (`PrivacyScrubberEngine`)
283
+ ### 7. Stateful Multi-Turn Agent Engine (`PrivacyScrubberEngine`)
131
284
 
132
285
  Maintain token bindings across multi-step autonomous agent loops (LangChain, AutoGen, CrewAI):
133
286
 
@@ -149,7 +302,7 @@ const restored = engine.restore(agentOutputText);
149
302
 
150
303
  ---
151
304
 
152
- ### 5. High-Throughput Stream Sanitization (`createSanitizeStream`)
305
+ ### 8. High-Throughput Stream Sanitization (`createSanitizeStream`)
153
306
 
154
307
  Sanitize massive log files, database dumps, and SSE payloads on-the-fly with <1MB heap memory. Automatically handles split chunk boundaries:
155
308
 
@@ -173,7 +326,7 @@ sanitizeStream.on('finish', () => {
173
326
 
174
327
  ---
175
328
 
176
- ### 6. AI Agent Guard & Function Calling (`createGuardedTools`, `applyPatch`)
329
+ ### 9. AI Agent Guard & Function Calling (`createGuardedTools`, `applyPatch`)
177
330
 
178
331
  Equip autonomous agents (Cursor, Vercel AI SDK, OpenAI, LangChain) with safe tools that intercept secrets and restore authentic data on disk:
179
332
 
@@ -202,7 +355,7 @@ const patchResult = await guardApplyPatch.execute({
202
355
 
203
356
  ---
204
357
 
205
- ### 7. Automated PR Security Verification Stamp (CI/CD)
358
+ ### 10. Automated PR Security Verification Stamp (CI/CD)
206
359
 
207
360
  Enforce zero plaintext PII and credential leaks in your CI/CD pipeline (GitHub Actions, GitLab CI, Jenkins) before pull requests are merged into production.
208
361
 
@@ -312,10 +465,29 @@ To acquire a commercial Developer SDK license:
312
465
  - 🔌 **MCP Server**: [@privacyscrubber/mcp-server](https://www.npmjs.com/package/@privacyscrubber/mcp-server)
313
466
  - 🧩 **Chrome Extension**: [Chrome Web Store](https://chromewebstore.google.com/detail/privacyscrubber-%E2%80%94-zero-tr/pimoejgefeilajmmbpghifdmhdlkgjol)
314
467
  - 📖 **Developer SDK Specs**: [https://privacyscrubber.com/features/developer-sdk/](https://privacyscrubber.com/features/developer-sdk/)
468
+ - ⭐ **GitHub Repository**: [moxno/privacyscrubber-mcp](https://github.com/moxno/privacyscrubber-mcp) (Star us on GitHub!)
315
469
  - 🧪 **DLP Latency Benchmark**: [https://privacyscrubber.com/dlp-speed-test/](https://privacyscrubber.com/dlp-speed-test/)
316
470
 
471
+ ### 📚 Production Architecture Guides
472
+ - 🛡️ **RAG & Vector Databases**: [Sanitizing PII Before Vector DB Ingestion (Pinecone, Chroma, Qdrant)](https://privacyscrubber.com/solutions/dev/rag-vector-database-pii-masking/)
473
+ - 🤖 **LangChain & LlamaIndex**: [In-Memory PII Middleware for AI Agent Pipelines](https://privacyscrubber.com/solutions/agents/langchain-llamaindex-pii-anonymizer/)
474
+ - ⚡ **AWS Comprehend Alternative**: [In-Memory Redaction Without Egress or Cloud Overhead](https://privacyscrubber.com/solutions/dev/aws-comprehend-pii-alternative/)
475
+
317
476
  ---
318
477
 
319
478
  ## ⚖️ License
320
479
 
321
480
  MIT License © 2026 PrivacyScrubber. Commercial production usage without quota limits requires an active Developer SDK or Enterprise license.
481
+
482
+ ---
483
+
484
+ ## 🏛️ Intellectual Property & Virtual Patent Marking
485
+
486
+ The Zero-Trust Data Sanitization (ZTDS) architecture, in-memory deterministic tokenization, cryptographic session handoff, and headless execution methods implemented in this SDK are proprietary technology of Ilya Sibiryakov (BrandMeWeb) and are protected under **Patent Pending** status:
487
+
488
+ - **Patent Office:** State of Israel Ministry of Justice, Patent Office (ILPO)
489
+ - **Application Number:** `331905` (Tracking ID: `94221`)
490
+ - **Filing / Priority Date:** September 14, 2026 (Paris Convention Art. 4 & 35 U.S.C. § 119 Priority)
491
+ - **Official Title:** *SYSTEM AND METHOD FOR CLIENT-SIDE ZERO-TRUST DATA SANITIZATION AND CRYPTOGRAPHIC SESSION HANDOFF IN ARTIFICIAL INTELLIGENCE WORKFLOWS*
492
+ - **Virtual Patent Marking:** [privacyscrubber.com/patents/](https://privacyscrubber.com/patents/) in accordance with 35 U.S.C. § 287(a).
493
+
package/cli.js CHANGED
@@ -13,7 +13,7 @@ import fs from 'node:fs';
13
13
  import path from 'node:path';
14
14
  import crypto from 'node:crypto';
15
15
  import { fileURLToPath } from 'node:url';
16
- import { sanitize } from './index.js';
16
+ import { sanitize, restore, generateProcurementMemo, StreamingDetokenizer } from './index.js';
17
17
 
18
18
  const __filename = fileURLToPath(import.meta.url);
19
19
  const __dirname = path.dirname(__filename);
@@ -21,7 +21,7 @@ const __dirname = path.dirname(__filename);
21
21
  const args = process.argv.slice(2);
22
22
 
23
23
  if (args.includes('--version') || args.includes('-v')) {
24
- let version = '2.2.0';
24
+ let version = '2.2.2';
25
25
  try {
26
26
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
27
27
  version = pkg.version || version;
@@ -30,6 +30,76 @@ if (args.includes('--version') || args.includes('-v')) {
30
30
  process.exit(0);
31
31
  }
32
32
 
33
+ // ── Memo Command: Generate CISO / DPO Procurement Memorandum ──────
34
+ if (args.includes('memo') || args.includes('--memo') || args.includes('--procurement-memo')) {
35
+ let format = 'markdown';
36
+ const formatIdx = args.indexOf('--format');
37
+ if (formatIdx !== -1 && args[formatIdx + 1]) {
38
+ format = args[formatIdx + 1];
39
+ }
40
+ let appName = 'Internal AI Applications & LLM Pipelines';
41
+ const appIdx = args.indexOf('--app');
42
+ if (appIdx !== -1 && args[appIdx + 1]) {
43
+ appName = args[appIdx + 1];
44
+ }
45
+ let developer = 'Engineering / AI Platform Team';
46
+ const devIdx = args.indexOf('--developer') !== -1 ? args.indexOf('--developer') : args.indexOf('--dev');
47
+ if (devIdx !== -1 && args[devIdx + 1]) {
48
+ developer = args[devIdx + 1];
49
+ }
50
+
51
+ const memo = generateProcurementMemo({ appName, developer, format });
52
+ process.stdout.write(memo.endsWith('\n') ? memo : memo + '\n');
53
+ process.exit(0);
54
+ }
55
+
56
+ // ── Demo Command: Live In-Memory Benchmark & Zero-Trust Inspection ──
57
+ if (args.includes('demo') || args.includes('--demo')) {
58
+ const sampleInput = `DevOps Incident Report #4829
59
+ Reporter: Sarah Connor (sarah.connor@cyberdyne.io, +1-415-555-0199, SSN 987-65-4321)
60
+ Host: prod-api-04.us-east-1.internal (10.0.42.15)
61
+ AWS Credentials: AKIAIOSFODNN7EXAMPLE / wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
62
+ Stripe API Key: sk_live_51OzTestDummyStripeKey998877665544332211
63
+ Database URI: postgresql://admin:SuperSecretPassword123@db-prod.internal:5432/production
64
+ Status: Database connection timeout during batch export.`;
65
+
66
+ const start = process.hrtime.bigint();
67
+ const res = sanitize(sampleInput, { profile: 'Dev', detectSecrets: true });
68
+ const end = process.hrtime.bigint();
69
+ const durationUs = Number(end - start) / 1000;
70
+ const durationMs = (durationUs / 1000).toFixed(3);
71
+
72
+ const maskedCount = Object.keys(res.tokenMap || {}).length;
73
+ const border = '═'.repeat(66);
74
+ const thin = '─'.repeat(66);
75
+
76
+ console.log(`
77
+ ╔${border}╗
78
+ ║ PrivacyScrubber Developer SDK — Live In-Memory Benchmark ║
79
+ ║ Standard: Zero-Trust Data Sanitization (ZTDS) ║
80
+ ╠${border}╣
81
+ ║ Input Characters : ${sampleInput.length.toString().padEnd(44)} ║
82
+ ║ Protected Tokens : ${maskedCount.toString().padEnd(44)} ║
83
+ ║ Execution Latency : ${(durationMs + ' ms (' + durationUs.toFixed(0) + ' µs)').padEnd(44)} ║
84
+ ║ Network Egress : 0 bytes (Pure in-memory process RAM) ║
85
+ ║ Risk Level : ${(res.telemetry?.riskLevel || 'CLEAN').padEnd(44)} ║
86
+ ║ Frameworks : ${(res.telemetry?.frameworksList?.slice(0, 3).join(', ') || 'GDPR, SOC 2, NIST').padEnd(44)} ║
87
+ ╟${thin}╢
88
+ ║ RAW INPUT PREVIEW: ║
89
+ ${sampleInput.split('\n').map(l => `║ ${l.slice(0, 62).padEnd(64)}║`).join('\n')}
90
+ ╟${thin}╢
91
+ ║ SANITIZED OUTPUT (Safe for LLM Egress): ║
92
+ ${res.scrubbedText.split('\n').map(l => `║ ${l.slice(0, 62).padEnd(64)}║`).join('\n')}
93
+ ╟${thin}╢
94
+ ║ NEXT STEPS & PROCUREMENT: ║
95
+ ║ * Copy CISO Approval Memo : npx @privacyscrubber/sdk memo ║
96
+ ║ * CI/CD PR Stamp : cat prompt.txt | sdk --verify-ci ║
97
+ ║ * Unlimited SDK ($199/mo) : https://privacyscrubber.com/pricing ║
98
+ ╚${border}╝
99
+ `);
100
+ process.exit(0);
101
+ }
102
+
33
103
  if (args.includes('--help') || args.includes('-h') || (args.length === 0 && process.stdin.isTTY)) {
34
104
  console.log(`@privacyscrubber/sdk — Zero-Trust Data Sanitization & CI Security Verification
35
105
 
@@ -37,18 +107,37 @@ Usage:
37
107
  npx @privacyscrubber/sdk <fileOrText> [options]
38
108
  cat prompt.txt | npx @privacyscrubber/sdk [options]
39
109
  npx @privacyscrubber/sdk --verify-ci <fileOrText> [options]
110
+ npx @privacyscrubber/sdk memo [options]
111
+ npx @privacyscrubber/sdk demo
112
+
113
+ Commands:
114
+ memo Generate executive CISO & DPO procurement memo
115
+ demo Run live in-memory benchmark (<0.1ms latency check)
40
116
 
41
117
  Options:
42
- --verify-ci Generate CI/CD PR verification stamp
43
- --profile <name> Specify detection profile (default: Dev)
118
+ --verify-ci Generate CI/CD PR verification stamp (audit receipt)
119
+ --json Output structured JSON (scrubbedText, tokenMap, telemetry)
120
+ --restore, --unscrub Restore masked tokens in text back to original values
121
+ --stream Stream de-tokenization chunks in real-time from stdin
122
+ --token-map <path> Path to JSON file or inline JSON string of token mappings
123
+ --profile <name> Specify detection profile (default: Dev, 30 profiles available)
44
124
  --key <licKey> PrivacyScrubber PRO/TEAMS/SDK license key
45
125
  -v, --version Show SDK version
46
126
  -h, --help Show help screen
47
127
 
48
128
  Examples:
49
129
  cat prompt.txt | npx @privacyscrubber/sdk
130
+ cat prompt.txt | npx @privacyscrubber/sdk --json
50
131
  cat build.log | npx @privacyscrubber/sdk --verify-ci
132
+ npx @privacyscrubber/sdk memo > CISO_APPROVAL_MEMO.md
133
+ npx @privacyscrubber/sdk demo
51
134
  npx @privacyscrubber/sdk "Contact Alice at alice@acme.com" --profile dev
135
+ echo "Hello [NAME_1]" | npx @privacyscrubber/sdk --restore --token-map '{"[NAME_1]":"Alice"}'
136
+ cat stream.txt | npx @privacyscrubber/sdk --restore --stream --token-map tokens.json
137
+
138
+ Commercial Licensing:
139
+ Developer SDK ($199/mo flat): https://privacyscrubber.com/pricing?tier=sdk&src=cli_help
140
+ Teams ($99/mo flat): https://privacyscrubber.com/pricing?tier=teams&src=cli_help
52
141
  `);
53
142
  process.exit(0);
54
143
  }
@@ -66,30 +155,60 @@ if (keyIdx !== -1 && args[keyIdx + 1]) {
66
155
  }
67
156
 
68
157
  const isVerifyCi = args.includes('--verify-ci');
158
+ const isJson = args.includes('--json');
159
+ const isRestore = args.includes('--restore') || args.includes('--unscrub');
160
+ const isStream = args.includes('--stream');
161
+
162
+ let tokenMap = {};
163
+ const mapIdx = args.indexOf('--token-map');
164
+ if (mapIdx !== -1 && args[mapIdx + 1]) {
165
+ const mapSource = args[mapIdx + 1];
166
+ try {
167
+ if (fs.existsSync(mapSource)) {
168
+ tokenMap = JSON.parse(fs.readFileSync(mapSource, 'utf8'));
169
+ } else {
170
+ tokenMap = JSON.parse(mapSource);
171
+ }
172
+ } catch (err) {
173
+ console.error(`[PrivacyScrubber CLI Error] Failed to parse --token-map: ${err.message}`);
174
+ process.exit(1);
175
+ }
176
+ }
69
177
 
70
178
  // Locate input argument (first positional parameter that is not a flag or flag value)
71
179
  const skipIndices = new Set();
72
180
  if (profIdx !== -1) { skipIndices.add(profIdx); skipIndices.add(profIdx + 1); }
73
181
  if (keyIdx !== -1) { skipIndices.add(keyIdx); skipIndices.add(keyIdx + 1); }
182
+ if (mapIdx !== -1) { skipIndices.add(mapIdx); skipIndices.add(mapIdx + 1); }
74
183
 
75
184
  let inputArg = null;
76
185
  for (let i = 0; i < args.length; i++) {
77
186
  if (skipIndices.has(i)) continue;
78
- if (args[i] === '--verify-ci') continue;
187
+ if (args[i] === '--verify-ci' || args[i] === '--json' || args[i] === '--restore' || args[i] === '--unscrub') continue;
79
188
  if (args[i].startsWith('-')) continue;
80
189
  inputArg = args[i];
81
190
  break;
82
191
  }
83
192
 
84
193
  function handleExecution(text) {
194
+ if (isRestore) {
195
+ const { restoredText, restoredCount } = restore(text, tokenMap);
196
+ if (isJson) {
197
+ console.log(JSON.stringify({ restoredText, restoredCount }, null, 2));
198
+ } else {
199
+ process.stdout.write(restoredText.endsWith('\n') ? restoredText : restoredText + '\n');
200
+ }
201
+ process.exit(0);
202
+ }
203
+
85
204
  if (isVerifyCi) {
86
205
  if (!text || !text.trim()) {
87
- console.log(`> 🛡️ **Verified by PrivacyScrubber (ZTDS Standard)**\n> * **Status:** ✅ PASSED · 0 PII / 0 Secrets detected in prompt pipeline (Empty payload)\n> * **Execution:** Local RAM (<1ms) · 0 Network Egress Bytes`);
206
+ console.log(`> 🛡️ **Verified by PrivacyScrubber (ZTDS Standard)**\n> * **Status:** ✅ PASSED · 0 PII / 0 Secrets detected in prompt pipeline (Empty payload)\n> * **Execution:** Local RAM (<1ms) · 0 Network Egress Bytes\n> * **Compliance Spec:** [Zero-Trust Data Sanitization (ZTDS)](https://privacyscrubber.com/solutions/engineering/zero-trust-ai-data-sanitization?src=ci_stamp) | [Developer SDK ($199/mo)](https://privacyscrubber.com/pricing?tier=sdk&src=ci_stamp)`);
88
207
  process.exit(0);
89
208
  }
90
209
 
91
- const { scrubbedText, tokenMap, telemetry } = sanitize(text, { profile, detectSecrets: true });
92
- const maskedCount = Object.keys(tokenMap || {}).length;
210
+ const { scrubbedText, tokenMap: resMap, telemetry } = sanitize(text, { profile, detectSecrets: true });
211
+ const maskedCount = Object.keys(resMap || {}).length;
93
212
  const hash = crypto.createHash('sha256').update(scrubbedText).digest('hex').slice(0, 16);
94
213
 
95
214
  const stamp = `
@@ -97,17 +216,31 @@ function handleExecution(text) {
97
216
  > * **Status:** ✅ PASSED · ${maskedCount} item(s) protected (${telemetry?.riskLevel || 'CLEAN'})
98
217
  > * **Execution:** Local RAM (<1ms) · 0 Network Egress Bytes
99
218
  > * **Audit Hash:** \`${hash}\` (Zero-Trust Verified)
219
+ > * **Compliance Spec:** [Zero-Trust Data Sanitization (ZTDS)](https://privacyscrubber.com/solutions/engineering/zero-trust-ai-data-sanitization?src=ci_stamp) | [Developer SDK ($199/mo)](https://privacyscrubber.com/pricing?tier=sdk&src=ci_stamp)
100
220
  `;
101
221
  console.log(stamp.trim());
102
222
  process.exit(0);
103
- } else {
104
- if (!text) {
105
- process.exit(0);
223
+ }
224
+
225
+ if (!text) {
226
+ if (isJson) {
227
+ console.log(JSON.stringify({ scrubbedText: '', tokenMap: {}, count: 0 }, null, 2));
106
228
  }
107
- const { scrubbedText } = sanitize(text, { profile, detectSecrets: true });
108
- process.stdout.write(scrubbedText.endsWith('\n') ? scrubbedText : scrubbedText + '\n');
109
229
  process.exit(0);
110
230
  }
231
+
232
+ const res = sanitize(text, { profile, detectSecrets: true });
233
+ if (isJson) {
234
+ console.log(JSON.stringify({
235
+ scrubbedText: res.scrubbedText,
236
+ tokenMap: res.tokenMap,
237
+ count: res.count,
238
+ telemetry: res.telemetry
239
+ }, null, 2));
240
+ } else {
241
+ process.stdout.write(res.scrubbedText.endsWith('\n') ? res.scrubbedText : res.scrubbedText + '\n');
242
+ }
243
+ process.exit(0);
111
244
  }
112
245
 
113
246
  if (inputArg) {
@@ -118,12 +251,26 @@ if (inputArg) {
118
251
  handleExecution(inputArg);
119
252
  }
120
253
  } else if (!process.stdin.isTTY) {
121
- const chunks = [];
122
- process.stdin.on('data', c => chunks.push(c));
123
- process.stdin.on('end', () => {
124
- const content = Buffer.concat(chunks).toString('utf8');
125
- handleExecution(content);
126
- });
254
+ if (isStream && isRestore) {
255
+ const detok = new StreamingDetokenizer(tokenMap);
256
+ process.stdin.setEncoding('utf8');
257
+ process.stdin.on('data', chunk => {
258
+ const out = detok.feed(chunk);
259
+ if (out) process.stdout.write(out);
260
+ });
261
+ process.stdin.on('end', () => {
262
+ const finalOut = detok.flush();
263
+ if (finalOut) process.stdout.write(finalOut);
264
+ process.exit(0);
265
+ });
266
+ } else {
267
+ const chunks = [];
268
+ process.stdin.on('data', c => chunks.push(c));
269
+ process.stdin.on('end', () => {
270
+ const content = Buffer.concat(chunks).toString('utf8');
271
+ handleExecution(content);
272
+ });
273
+ }
127
274
  } else {
128
275
  console.error(`Error: No input provided. Use: npx @privacyscrubber/sdk <fileOrText> or pipe stdin.`);
129
276
  process.exit(1);