@veilio-inc/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,248 @@
1
+ # Veilio Community License
2
+
3
+ _Version 1.0 · Effective 2026-08-08_
4
+
5
+ **Copyright © 2026 Veilio**
6
+
7
+ > **Plain-language summary (non-binding; the numbered terms below control).**
8
+ > Use Veilio for anything you like — personally, or inside your company, including
9
+ > to make money. Read it, fork it, change it, self-host it, ship products with it.
10
+ > The one thing you may not do without written permission is sell Veilio itself:
11
+ > reselling it, white-labeling it, or running it as a hosted service for other
12
+ > people. Contributions are welcome. For a commercial or redistribution license,
13
+ > email `hello@veilio.dev`.
14
+
15
+ ---
16
+
17
+ ## 1. Project Model
18
+
19
+ This project follows a **source-available and community-driven development model** with a separate commercial Cloud Edition. The source is public and contributions are welcome, but this is deliberately **not an OSI-approved open-source license**: the commercial restrictions in Section 5 go beyond what that definition permits.
20
+
21
+ The **Community Core** is publicly available and intended to be freely used, inspected, modified, forked, and improved by the community. Contributions from the community are welcome and are an important part of the project's development.
22
+
23
+ The **Cloud Edition** is a separate commercial offering operated by Veilio. It provides hosted and managed functionality and may include additional proprietary features, infrastructure, services, and support.
24
+
25
+ The Software may be used for personal purposes as well as for internal commercial purposes. Organizations may deploy and use the Software within their own businesses without requiring a commercial license.
26
+
27
+ However, the Software may not be commercially redistributed, resold, white-labeled, or offered as a third-party hosted service without prior written permission from Veilio.
28
+
29
+ The purpose of this model is to keep the core technology open and community-driven while allowing the project owner to maintain and commercially operate the official Cloud Edition.
30
+
31
+ ---
32
+
33
+ ## 2. Definitions
34
+
35
+ For the purposes of this License:
36
+
37
+ **"Software"** means the source code, binaries, documentation, configuration, and other materials distributed as part of this project.
38
+
39
+ **"Community Core"** means the publicly available portion of the Software developed and distributed under this License.
40
+
41
+ **"Modified Version"** means any work based on, derived from, or incorporating substantial portions of the Software.
42
+
43
+ **"Cloud Edition"** means the hosted, managed, or subscription-based version of the Software operated and commercially provided by Veilio, including any proprietary services, infrastructure, features, or components associated with it.
44
+
45
+ **"Commercial Distribution"** means selling, licensing, renting, leasing, sublicensing, or otherwise providing the Software or a Modified Version to third parties in exchange for payment or other commercial consideration.
46
+
47
+ **"Hosted Service"** means making the Software or a Modified Version available to third parties over a network, including as a SaaS, PaaS, managed service, hosted application, or similar commercial offering.
48
+
49
+ **"Competing Product"** means a commercially offered product or service whose primary purpose and core functionality substantially overlap with the Software or Cloud Edition.
50
+
51
+ ---
52
+
53
+ ## 3. Permissions
54
+
55
+ Subject to the restrictions in this License, you are granted a worldwide, royalty-free, non-exclusive license to:
56
+
57
+ * use the Software for personal purposes;
58
+ * use the Software for internal commercial purposes;
59
+ * inspect and study the source code;
60
+ * modify the Software;
61
+ * create forks and derivative works;
62
+ * submit contributions, patches, bug fixes, and improvements to the project;
63
+ * use modified versions for personal purposes or internal commercial operations;
64
+ * share the Software and Modified Versions for non-commercial purposes.
65
+
66
+ These permissions are intended to enable community development, experimentation, self-hosting, internal business use, and collaboration.
67
+
68
+ ---
69
+
70
+ ## 4. Internal Commercial Use
71
+
72
+ Commercial use of the Software is permitted when the Software is used as an internal tool or component of your own business activities.
73
+
74
+ For example, an organization may deploy the Software on its own infrastructure and use it to:
75
+
76
+ * operate internal business processes;
77
+ * support its employees;
78
+ * develop its own products;
79
+ * provide its own services;
80
+ * manage its own operations; or
81
+ * otherwise conduct its own commercial activities.
82
+
83
+ The fact that an organization generates revenue through its business does not, by itself, constitute a violation of this License.
84
+
85
+ The restriction applies to **commercializing the Software itself**, rather than using the Software as part of your own business.
86
+
87
+ ---
88
+
89
+ ## 5. Commercial Restrictions
90
+
91
+ Without prior written permission from Veilio, you may not:
92
+
93
+ ### 5.1 Sell or Resell the Software
94
+
95
+ Sell, license, rent, lease, sublicense, or otherwise commercially distribute the Software or a Modified Version as a standalone product.
96
+
97
+ ### 5.2 Offer the Software as a Hosted Service
98
+
99
+ Operate the Software or a Modified Version as a commercial SaaS, PaaS, hosted application, managed service, or similar service for third parties.
100
+
101
+ ### 5.3 Create a Competing Commercial Product
102
+
103
+ Use the Software or a Modified Version as the basis for a commercially offered product or service that substantially competes with the Cloud Edition.
104
+
105
+ ### 5.4 White-Label or Rebrand the Software
106
+
107
+ Repackage, white-label, rebrand, or otherwise present the Software or a Modified Version as your own commercial software product.
108
+
109
+ ### 5.5 Commercially Redistribute Modified Versions
110
+
111
+ Commercially distribute a Modified Version where the primary value or functionality being sold is provided by the Software.
112
+
113
+ Requests for written permission may be sent to `hello@veilio.dev`.
114
+
115
+ ---
116
+
117
+ ## 6. Cloud Edition
118
+
119
+ The Cloud Edition is the official commercial hosted offering of the project.
120
+
121
+ The Cloud Edition may be provided through paid subscriptions and may include:
122
+
123
+ * managed hosting;
124
+ * automatic updates;
125
+ * backups;
126
+ * infrastructure and scalability;
127
+ * monitoring;
128
+ * enterprise functionality;
129
+ * technical support;
130
+ * proprietary features;
131
+ * other hosted or managed services.
132
+
133
+ The Community Core and Cloud Edition may share common code, concepts, or functionality. However, access to the Community Core does not grant any right to commercially operate a competing hosted version of the Cloud Edition.
134
+
135
+ The Cloud Edition may be governed by separate Terms of Service, subscription agreements, privacy policies, and commercial licenses.
136
+
137
+ ---
138
+
139
+ ## 7. Forks and Modified Versions
140
+
141
+ You are permitted to create forks and Modified Versions of the Software for:
142
+
143
+ * personal use;
144
+ * internal business use;
145
+ * research;
146
+ * education;
147
+ * development;
148
+ * experimentation; and
149
+ * other purposes permitted by this License.
150
+
151
+ Forks and Modified Versions must retain applicable copyright notices and a copy of this License.
152
+
153
+ Creating a fork does not grant permission to commercially distribute that fork, offer it as a commercial Hosted Service, or use it to create a Competing Product.
154
+
155
+ ---
156
+
157
+ ## 8. Contributions
158
+
159
+ Contributions to the Community Core are welcome.
160
+
161
+ By submitting a contribution, you represent that you have the necessary rights to submit that contribution and agree that it may be incorporated into the project and distributed under this License.
162
+
163
+ Unless otherwise agreed in writing, contributions will be made available under the same licensing terms as the Software.
164
+
165
+ Contributors retain ownership of their original contributions to the extent permitted by applicable law.
166
+
167
+ ---
168
+
169
+ ## 9. Redistribution
170
+
171
+ You may redistribute the Software and Modified Versions for non-commercial purposes, provided that:
172
+
173
+ * applicable copyright notices are retained;
174
+ * the original project is appropriately credited;
175
+ * a copy of this License is included;
176
+ * recipients are informed of the applicable licensing terms; and
177
+ * the redistribution does not imply endorsement by Veilio.
178
+
179
+ Commercial redistribution requires prior written permission from Veilio.
180
+
181
+ ---
182
+
183
+ ## 10. Attribution
184
+
185
+ Redistributions of the Software or Modified Versions must retain:
186
+
187
+ * applicable copyright notices;
188
+ * notices identifying the original project; and
189
+ * a copy of this License.
190
+
191
+ You may add your own copyright notices for your modifications, provided that they do not imply ownership of the original Software.
192
+
193
+ ---
194
+
195
+ ## 11. Trademarks
196
+
197
+ This License does not grant permission to use the project's name, logo, trademarks, service marks, domain names, or branding except as reasonably necessary to identify the original Software.
198
+
199
+ You may not use the project's trademarks to imply endorsement, sponsorship, affiliation, or official status without prior written permission.
200
+
201
+ Creating a fork does not grant the right to use the project's trademarks for that fork.
202
+
203
+ ---
204
+
205
+ ## 12. No Warranty
206
+
207
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
208
+
209
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE AUTHORS AND COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
210
+
211
+ ---
212
+
213
+ ## 13. Limitation of Liability
214
+
215
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHORS, COPYRIGHT HOLDERS, CONTRIBUTORS, OR PROJECT OWNERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM OR RELATED TO THE SOFTWARE OR THE USE OR INABILITY TO USE THE SOFTWARE.
216
+
217
+ ---
218
+
219
+ ## 14. Termination
220
+
221
+ Your rights under this License terminate automatically if you materially violate its terms.
222
+
223
+ Upon termination, you must cease the activities that are prohibited by this License.
224
+
225
+ Permissions granted to recipients who are not in violation of this License remain unaffected.
226
+
227
+ ---
228
+
229
+ ## 15. Reservation of Rights
230
+
231
+ All rights not expressly granted by this License are reserved by Veilio.
232
+
233
+ In particular, this License does not grant rights to commercially sell, redistribute, host, white-label, or operate a competing commercial service based on the Software beyond the permissions expressly stated above.
234
+
235
+ ---
236
+
237
+ ## 16. License Versions
238
+
239
+ The project owner may publish future versions of this License.
240
+
241
+ Each version will be identified by a version number. Unless explicitly stated otherwise, versions of the Software already distributed under a particular version of this License remain subject to that version.
242
+
243
+ ---
244
+
245
+ **Project:** Veilio
246
+ **Copyright:** © 2026 Veilio
247
+ **License:** Veilio Community License, Version 1.0
248
+ **Contact:** `hello@veilio.dev`
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @veilio-inc/cli
2
+
3
+ Anonymize code before it reaches an LLM, restore it after — from a terminal, a pipe, or a pre-commit hook.
4
+
5
+ ```bash
6
+ veilio scrub src/billing.ts | pbcopy # mask, then paste anywhere
7
+ pbpaste | veilio restore # bring the answer back
8
+ git diff --cached | veilio scan # refuse to commit a live key
9
+ ```
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install -g @veilio-inc/cli # or run it without installing:
15
+ npx @veilio-inc/cli scrub src/billing.ts
16
+ ```
17
+
18
+ Requires Node 24 or newer. No account, no API key, and no network call on any of
19
+ the commands below — `packages/cli/tests/purity.test.ts` traps the network
20
+ globals and fails if one is ever introduced.
21
+
22
+ ## Commands
23
+
24
+ | Command | Purpose |
25
+ | -------------------- | --------------------------------------------------------------------- |
26
+ | `scrub [files...]` | Mask identifiers, redact credentials. Reads stdin when given no file. |
27
+ | `restore [files...]` | Swap placeholders back, strip AI-generated noise. |
28
+ | `scan [files...]` | Detect credentials only. Never rewrites. Exits 1 on findings. |
29
+ | `map` | Show the symbol map (`--clear` to wipe it). |
30
+
31
+ ## Options
32
+
33
+ ```
34
+ -l, --language <lang> auto (default), typescript, python, go, java, csharp,
35
+ rust, ruby, php, c, sql
36
+ -s, --secrets <policy> redact (default) | warn | off
37
+ -p, --preamble Prepend the downstream-AI note and placeholder legend
38
+ -m, --map <path> Use a specific map file
39
+ --json Machine-readable output (scan, map)
40
+ --strict scan: also fail on advisory findings
41
+ -q, --quiet Suppress the stderr summary
42
+ ```
43
+
44
+ ## Piping
45
+
46
+ Transformed code goes to **stdout**; summaries, warnings and errors go to **stderr**. That split is what makes the CLI composable — the summary stays visible without corrupting the pipe.
47
+
48
+ ## Exit codes
49
+
50
+ | Code | Meaning |
51
+ | ---- | -------------------------------------- |
52
+ | 0 | Clean |
53
+ | 1 | Findings that should stop the pipeline |
54
+ | 2 | Usage or IO error |
55
+
56
+ As a pre-commit hook:
57
+
58
+ ```bash
59
+ #!/bin/sh
60
+ git diff --cached | veilio scan || {
61
+ echo "Commit blocked: credentials detected." >&2
62
+ exit 1
63
+ }
64
+ ```
65
+
66
+ ## The symbol map
67
+
68
+ `scrub` writes `.veilio/map.json` at the project root and `restore` reads it, so placeholders stay stable across runs and across a whole session. The store is created `0600` with its own `.gitignore` — it holds the real identifier names, so committing it would undo the anonymization for anyone reading the repo.
69
+
70
+ Redacted credentials are **not** in the map. `restore` cannot bring them back, by construction.
71
+
72
+ ## In CI
73
+
74
+ ### GitHub Action
75
+
76
+ ```yaml
77
+ - uses: veilio-inc/veilio/packages/cli@v0.1.0
78
+ with:
79
+ strict: false # also fail on emails / private IPs
80
+ sarif: veilio.sarif # optional: upload to code scanning
81
+ ```
82
+
83
+ **This example does not work yet, and needs two things that do not exist.** The
84
+ `@v0.1.0` ref is a git tag on this repository — the only tags here are
85
+ `engine-v*`, cut by semantic-release for the engine — and the Action's own script
86
+ runs `npx --yes @veilio-inc/cli@<version>`, which needs the package on npm.
87
+ Publishing supplies the second; the first is a tag someone has to cut, and note
88
+ that `v*.*.*` is also what triggers the Community Edition release workflow, so
89
+ the two are not independent.
90
+
91
+ Scans the **pull-request diff** by default, not the whole tree. A repo adopting
92
+ this mid-life almost always has a historical finding somewhere; blocking every PR
93
+ on that is how a security check gets switched off in week one. Pass `paths:` to
94
+ scan a fixed set instead.
95
+
96
+ Findings land in the job summary and, with `sarif:`, in GitHub code scanning.
97
+ Reports carry truncated previews only — a stored, shareable artifact must never
98
+ contain the credential it is reporting.
99
+
100
+ ### Pre-commit
101
+
102
+ Via the [pre-commit framework](https://pre-commit.com):
103
+
104
+ ```yaml
105
+ repos:
106
+ - repo: https://github.com/veilio-inc/veilio
107
+ rev: v0.1.0
108
+ hooks:
109
+ - id: veilio-scan
110
+ ```
111
+
112
+ Or a plain git hook:
113
+
114
+ ```bash
115
+ cp packages/cli/hooks/pre-commit .git/hooks/pre-commit
116
+ chmod +x .git/hooks/pre-commit
117
+ ```
118
+
119
+ It scans the **staged** diff — what you are about to commit is what matters, and
120
+ a finding in an unstaged scratch file shouldn't block you. `--no-verify`
121
+ overrides, as usual.
package/dist/args.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { type LanguageOption } from '@veilio-inc/engine';
2
+ import type { SecretPolicy } from '@veilio-inc/engine';
3
+ export type Command = 'scrub' | 'restore' | 'scan' | 'map' | 'help' | 'version';
4
+ export interface ParsedArgs {
5
+ command: Command;
6
+ /** Positional file paths. Empty means read stdin (or, for `scan`, error). */
7
+ files: string[];
8
+ language: LanguageOption;
9
+ secrets: SecretPolicy;
10
+ /** Prepend the downstream-AI preamble and legend to scrub output. */
11
+ preamble: boolean;
12
+ /** Override the map store location. */
13
+ mapPath: string | null;
14
+ /** Machine-readable output (scan). */
15
+ json: boolean;
16
+ /** Suppress the human-readable summary on stderr. */
17
+ quiet: boolean;
18
+ /** `map --clear` wipes the store. */
19
+ clear: boolean;
20
+ /** Treat medium-severity findings as failures too (scan). */
21
+ strict: boolean;
22
+ /** Keep JSDoc blocks when restoring. */
23
+ keepDocs: boolean;
24
+ /** Allow a map write that would drop entries already on disk. */
25
+ force: boolean;
26
+ }
27
+ export declare class UsageError extends Error {
28
+ }
29
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs;
30
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAIA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAClF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;AAE/E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,EAAE,cAAc,CAAA;IACxB,OAAO,EAAE,YAAY,CAAA;IACrB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAA;IACjB,uCAAuC;IACvC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,sCAAsC;IACtC,IAAI,EAAE,OAAO,CAAA;IACb,qDAAqD;IACrD,KAAK,EAAE,OAAO,CAAA;IACd,qCAAqC;IACrC,KAAK,EAAE,OAAO,CAAA;IACd,6DAA6D;IAC7D,MAAM,EAAE,OAAO,CAAA;IACf,wCAAwC;IACxC,QAAQ,EAAE,OAAO,CAAA;IACjB,iEAAiE;IACjE,KAAK,EAAE,OAAO,CAAA;CACf;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAaxC,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAgG7D"}
package/dist/args.js ADDED
@@ -0,0 +1,108 @@
1
+ // Hand-rolled argument parsing. The engine ships zero runtime dependencies as a
2
+ // privacy invariant (an audited supply chain is the point), and a CLI that
3
+ // wraps it should not undo that for the sake of flag parsing.
4
+ import { LANGUAGES } from '@veilio-inc/engine';
5
+ export class UsageError extends Error {
6
+ }
7
+ const COMMANDS = new Set(['scrub', 'restore', 'scan', 'map', 'help', 'version']);
8
+ const SECRET_POLICIES = new Set(['redact', 'warn', 'off']);
9
+ const LANGUAGE_VALUES = new Set([...LANGUAGES, 'auto']);
10
+ function requireValue(flag, value) {
11
+ if (value === undefined || value.startsWith('-')) {
12
+ throw new UsageError(`${flag} requires a value`);
13
+ }
14
+ return value;
15
+ }
16
+ export function parseArgs(argv) {
17
+ const parsed = {
18
+ command: 'help',
19
+ files: [],
20
+ language: 'auto',
21
+ secrets: 'redact',
22
+ preamble: false,
23
+ mapPath: null,
24
+ json: false,
25
+ quiet: false,
26
+ clear: false,
27
+ strict: false,
28
+ keepDocs: false,
29
+ force: false,
30
+ };
31
+ if (argv.length === 0)
32
+ return parsed;
33
+ let i = 0;
34
+ const first = argv[0];
35
+ if (!first.startsWith('-')) {
36
+ if (!COMMANDS.has(first)) {
37
+ throw new UsageError(`unknown command "${first}"`);
38
+ }
39
+ parsed.command = first;
40
+ i = 1;
41
+ }
42
+ for (; i < argv.length; i++) {
43
+ const arg = argv[i];
44
+ switch (arg) {
45
+ case '-h':
46
+ case '--help':
47
+ parsed.command = 'help';
48
+ break;
49
+ case '-v':
50
+ case '--version':
51
+ parsed.command = 'version';
52
+ break;
53
+ case '-l':
54
+ case '--language': {
55
+ const value = requireValue(arg, argv[++i]);
56
+ if (!LANGUAGE_VALUES.has(value)) {
57
+ throw new UsageError(`unknown language "${value}" — expected auto or one of: ${LANGUAGES.join(', ')}`);
58
+ }
59
+ parsed.language = value;
60
+ break;
61
+ }
62
+ case '-s':
63
+ case '--secrets': {
64
+ const value = requireValue(arg, argv[++i]);
65
+ if (!SECRET_POLICIES.has(value)) {
66
+ throw new UsageError(`--secrets must be redact, warn, or off (got "${value}")`);
67
+ }
68
+ parsed.secrets = value;
69
+ break;
70
+ }
71
+ case '-m':
72
+ case '--map':
73
+ parsed.mapPath = requireValue(arg, argv[++i]);
74
+ break;
75
+ case '-p':
76
+ case '--preamble':
77
+ parsed.preamble = true;
78
+ break;
79
+ case '--json':
80
+ parsed.json = true;
81
+ break;
82
+ case '-q':
83
+ case '--quiet':
84
+ parsed.quiet = true;
85
+ break;
86
+ case '--clear':
87
+ parsed.clear = true;
88
+ break;
89
+ case '--strict':
90
+ parsed.strict = true;
91
+ break;
92
+ case '--keep-docs':
93
+ parsed.keepDocs = true;
94
+ break;
95
+ case '-f':
96
+ case '--force':
97
+ parsed.force = true;
98
+ break;
99
+ default:
100
+ if (arg.startsWith('-') && arg !== '-') {
101
+ throw new UsageError(`unknown flag "${arg}"`);
102
+ }
103
+ parsed.files.push(arg);
104
+ }
105
+ }
106
+ return parsed;
107
+ }
108
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ParsedArgs } from './args.js';
2
+ export interface Io {
3
+ cwd: string;
4
+ stdin: () => Promise<string>;
5
+ stdout: (text: string) => void;
6
+ stderr: (text: string) => void;
7
+ }
8
+ /** Exit codes are the contract for CI and pre-commit hooks:
9
+ * 0 clean, 1 findings that should stop the pipeline, 2 usage/IO failure. */
10
+ export declare const EXIT_OK = 0;
11
+ export declare const EXIT_FINDINGS = 1;
12
+ export declare const EXIT_ERROR = 2;
13
+ export declare function runScrub(args: ParsedArgs, io: Io): Promise<number>;
14
+ export declare function runRestore(args: ParsedArgs, io: Io): Promise<number>;
15
+ /** Detect-only pass for pre-commit hooks and CI. Never rewrites anything. */
16
+ export declare function runScan(args: ParsedArgs, io: Io): Promise<number>;
17
+ export declare function runMap(args: ParsedArgs, io: Io): Promise<number>;
18
+ export declare const HELP = "veilio \u2014 anonymize code before it reaches an LLM, restore it after.\n\nUSAGE\n veilio <command> [files...] [options]\n\nCOMMANDS\n scrub [files...] Mask identifiers and redact credentials. Reads stdin when\n no file is given. Writes the symbol map to .veilio/map.json.\n restore [files...] Swap placeholders back and strip AI-generated noise.\n scan [files...] Detect credentials only; never rewrites. Exits 1 when it\n finds something \u2014 use it in a pre-commit hook or CI.\n map Show the current symbol map (--clear to wipe it).\n\nOPTIONS\n -l, --language <lang> auto (default), typescript, python, go, java, csharp,\n rust, ruby, php, c, sql\n -s, --secrets <policy> redact (default) | warn | off\n -p, --preamble Prepend the downstream-AI note and placeholder legend\n -m, --map <path> Use a specific map file\n --json Machine-readable output (scan, map)\n --strict scan: also fail on advisory findings\n --keep-docs restore: keep JSDoc blocks the model wrote\n -f, --force Allow a map write that would drop existing entries\n -q, --quiet Suppress the all-clear summary (findings always show)\n -h, --help Show this help\n -v, --version Show the version\n\nEXIT CODES\n 0 clean 1 findings that should stop the pipeline 2 usage or IO error\n\nEXAMPLES\n veilio scrub src/billing.ts | pbcopy\n pbpaste | veilio restore\n git diff --cached | veilio scan # pre-commit gate\n veilio scan 'src/**/*.ts' --json > findings.json\n\nTransformed code goes to stdout; everything else goes to stderr, so the CLI\ncomposes in a pipe.\n";
19
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAG3C,MAAM,WAAW,EAAE;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/B;AAED;6EAC6E;AAC7E,eAAO,MAAM,OAAO,IAAI,CAAA;AACxB,eAAO,MAAM,aAAa,IAAI,CAAA;AAC9B,eAAO,MAAM,UAAU,IAAI,CAAA;AAsB3B,wBAAsB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyDxE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAsD1E;AAED,6EAA6E;AAC7E,wBAAsB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiCvE;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBtE;AAED,eAAO,MAAM,IAAI,svDAsChB,CAAA"}
@@ -0,0 +1,216 @@
1
+ // Command implementations.
2
+ //
3
+ // Two rules hold everywhere here:
4
+ // 1. Transformed code goes to stdout and nothing else does, so the CLI can sit
5
+ // in a pipe (`veilio scrub src/billing.ts | pbcopy`).
6
+ // 2. Summaries, warnings and errors go to stderr, so they stay visible without
7
+ // corrupting that pipe.
8
+ import { readFileSync } from 'node:fs';
9
+ import { resolve } from 'node:path';
10
+ import { anonymize, detectSecrets, hasBlockingSecrets, restore, summarizeSecrets, withAiPreamble, LANGUAGE_LABELS, STRIPPABLE_TYPES, BIN_NAME, STORE_DIR, } from '@veilio-inc/engine';
11
+ import { clearMap, loadMap, resolveMapPath, saveMap } from './store.js';
12
+ /** Exit codes are the contract for CI and pre-commit hooks:
13
+ * 0 clean, 1 findings that should stop the pipeline, 2 usage/IO failure. */
14
+ export const EXIT_OK = 0;
15
+ export const EXIT_FINDINGS = 1;
16
+ export const EXIT_ERROR = 2;
17
+ async function readInput(args, io) {
18
+ if (args.files.length === 0)
19
+ return io.stdin();
20
+ return args.files.map((f) => readFileSync(resolve(io.cwd, f), 'utf8')).join('\n');
21
+ }
22
+ function formatFinding(f, file) {
23
+ const where = file ? `${file}:${f.line}:${f.column}` : `line ${f.line}:${f.column}`;
24
+ const state = f.redacted ? 'redacted' : 'left in place';
25
+ return ` ${f.severity.padEnd(8)} ${where} ${f.label} — ${f.preview} (${state})`;
26
+ }
27
+ function summaryLine(findings) {
28
+ const s = summarizeSecrets(findings);
29
+ const parts = [];
30
+ if (s.critical)
31
+ parts.push(`${s.critical} critical`);
32
+ if (s.high)
33
+ parts.push(`${s.high} high`);
34
+ if (s.medium)
35
+ parts.push(`${s.medium} advisory`);
36
+ return parts.join(', ');
37
+ }
38
+ export async function runScrub(args, io) {
39
+ const source = await readInput(args, io);
40
+ const mapPath = resolveMapPath(args.mapPath, io.cwd);
41
+ const existingMap = loadMap(mapPath);
42
+ const result = anonymize(source, {
43
+ existingMap,
44
+ language: args.language,
45
+ secrets: args.secrets,
46
+ });
47
+ saveMap(mapPath, result.map, { force: args.force });
48
+ io.stdout(args.preamble ? withAiPreamble(result.anonymized, result.map) : result.anonymized);
49
+ // Survives --quiet. Everything else here describes work that went right; this
50
+ // says the masking itself may be wrong. No marker matched, so the file was
51
+ // tokenised with TypeScript rules — in a Ruby file that means `#` comments
52
+ // were read as code and their prose masked, and in the other direction a
53
+ // language whose comment syntax we did not apply had its prose left in the
54
+ // clear. Output that looks anonymised and is not is the one thing a pipeline
55
+ // must not swallow.
56
+ if (result.languageFallback) {
57
+ io.stderr(`${BIN_NAME}: no language marker matched — masked as ${LANGUAGE_LABELS[result.language]}, which may be wrong. ` +
58
+ `Pass --language to be sure.\n`);
59
+ }
60
+ if (!args.quiet) {
61
+ const added = Object.keys(result.map).length - Object.keys(existingMap).length;
62
+ io.stderr(`${BIN_NAME}: ${LANGUAGE_LABELS[result.language]} — ${added} new placeholder${added === 1 ? '' : 's'}, ${Object.keys(result.map).length} in map\n`);
63
+ // Under --quiet, unlike the language warning above. Nearly every real file
64
+ // has a comment beside code, so this fires on almost every run; surviving
65
+ // --quiet would defeat the flag and teach people to stop passing it. The
66
+ // engine reports it on every result either way, so nothing is hidden from a
67
+ // caller that wants it.
68
+ if (result.comments.total > 0) {
69
+ const { total, inline, characters } = result.comments;
70
+ const where = inline === 0 ? 'above the code' : `${inline} inside the body`;
71
+ io.stderr(`${BIN_NAME}: ${total} comment${total === 1 ? '' : 's'} (${where}) left as written — ` +
72
+ `${characters} characters of prose are NOT masked. Names and ticket numbers in them go out as typed.\n`);
73
+ }
74
+ if (result.secrets.length > 0) {
75
+ io.stderr(`${BIN_NAME}: credentials detected — ${summaryLine(result.secrets)}\n`);
76
+ for (const f of result.secrets)
77
+ io.stderr(`${formatFinding(f)}\n`);
78
+ if (result.secrets.some((f) => f.redacted)) {
79
+ io.stderr(`${BIN_NAME}: redacted values are NOT recoverable on restore. Rotate anything real.\n`);
80
+ }
81
+ }
82
+ }
83
+ return EXIT_OK;
84
+ }
85
+ export async function runRestore(args, io) {
86
+ const source = await readInput(args, io);
87
+ const mapPath = resolveMapPath(args.mapPath, io.cwd);
88
+ const map = loadMap(mapPath);
89
+ if (Object.keys(map).length === 0) {
90
+ // Restoring against an empty map returns the input verbatim, which looks
91
+ // like success. Say so instead.
92
+ io.stderr(`${BIN_NAME}: no symbol map at ${mapPath} — run "${BIN_NAME} scrub" first.\n`);
93
+ return EXIT_ERROR;
94
+ }
95
+ // Without --keep-docs the default strips JSDoc along with the narration and
96
+ // TODOs. That is right for noise, wrong when the model was asked to document
97
+ // its output — so the escape hatch is a flag, not a code change.
98
+ const strip = args.keepDocs ? STRIPPABLE_TYPES.filter((t) => t !== 'jsdoc') : 'all';
99
+ const result = restore(source, map, { strip });
100
+ io.stdout(result.restored);
101
+ const { resolved, missing, unresolved } = result.report;
102
+ // A token the map cannot explain is a finding, not a summary line: the text on
103
+ // stdout now contains something that means nothing, and the user is about to
104
+ // paste it into an editor. --quiet suppresses the all-clear, never findings —
105
+ // the same contract `scan` follows.
106
+ if (unresolved.length > 0) {
107
+ io.stderr(`${BIN_NAME}: ${unresolved.length} placeholder-shaped token${unresolved.length === 1 ? '' : 's'} not in the map — ` +
108
+ `${unresolved.join(', ')}\n`);
109
+ io.stderr(`${BIN_NAME}: the AI invented or altered these; they are still in the output above.\n`);
110
+ }
111
+ if (!args.quiet) {
112
+ io.stderr(`${BIN_NAME}: restored ${resolved.length} of ${Object.keys(map).length} placeholders, stripped ${result.strippedCount} AI artifact${result.strippedCount === 1 ? '' : 's'}\n`);
113
+ // Usually innocent — an answer about one function omits the rest of the
114
+ // file — so this sits under --quiet with the summary rather than above it.
115
+ if (missing.length > 0) {
116
+ io.stderr(`${BIN_NAME}: ${missing.length} placeholder${missing.length === 1 ? '' : 's'} did not appear in the input; ` +
117
+ `if the AI covered the whole file, it renamed them and those names are not recoverable from this text.\n`);
118
+ }
119
+ }
120
+ // Deliberately still EXIT_OK. `restore` writes usable text to stdout even when
121
+ // a token is unexplained, and exiting non-zero would break every
122
+ // `... | veilio restore > file` pipeline under `set -e` for a warning the user
123
+ // can act on. Gating that behind a flag is a separate decision from reporting.
124
+ return EXIT_OK;
125
+ }
126
+ /** Detect-only pass for pre-commit hooks and CI. Never rewrites anything. */
127
+ export async function runScan(args, io) {
128
+ const targets = args.files.length > 0
129
+ ? args.files.map((f) => ({ name: f, text: readFileSync(resolve(io.cwd, f), 'utf8') }))
130
+ : [{ name: '<stdin>', text: await io.stdin() }];
131
+ const all = [];
132
+ for (const target of targets) {
133
+ for (const finding of detectSecrets(target.text)) {
134
+ all.push({ file: target.name, finding });
135
+ }
136
+ }
137
+ if (args.json) {
138
+ io.stdout(`${JSON.stringify(all.map((r) => ({ file: r.file, ...r.finding })), null, 2)}\n`);
139
+ }
140
+ else if (all.length === 0) {
141
+ if (!args.quiet)
142
+ io.stderr(`${BIN_NAME}: no credentials detected\n`);
143
+ }
144
+ else {
145
+ io.stderr(`${BIN_NAME}: ${summaryLine(all.map((r) => r.finding))}\n`);
146
+ for (const { file, finding } of all)
147
+ io.stderr(`${formatFinding(finding, file)}\n`);
148
+ }
149
+ const findings = all.map((r) => r.finding);
150
+ // Default gate is critical/high. --strict also fails on advisory findings,
151
+ // for teams whose policy covers customer emails and internal hostnames.
152
+ const fail = args.strict ? findings.length > 0 : hasBlockingSecrets(findings);
153
+ return fail ? EXIT_FINDINGS : EXIT_OK;
154
+ }
155
+ export async function runMap(args, io) {
156
+ const mapPath = resolveMapPath(args.mapPath, io.cwd);
157
+ if (args.clear) {
158
+ const removed = clearMap(mapPath);
159
+ io.stderr(removed ? `${BIN_NAME}: cleared ${mapPath}\n` : `${BIN_NAME}: no map at ${mapPath}\n`);
160
+ return EXIT_OK;
161
+ }
162
+ const map = loadMap(mapPath);
163
+ const entries = Object.entries(map);
164
+ if (args.json) {
165
+ io.stdout(`${JSON.stringify(map, null, 2)}\n`);
166
+ return EXIT_OK;
167
+ }
168
+ if (entries.length === 0) {
169
+ io.stderr(`${BIN_NAME}: no map at ${mapPath}\n`);
170
+ return EXIT_OK;
171
+ }
172
+ io.stderr(`${BIN_NAME}: ${entries.length} placeholders at ${mapPath}\n`);
173
+ for (const [placeholder, real] of entries)
174
+ io.stdout(`${placeholder}\t${real}\n`);
175
+ return EXIT_OK;
176
+ }
177
+ export const HELP = `${BIN_NAME} — anonymize code before it reaches an LLM, restore it after.
178
+
179
+ USAGE
180
+ ${BIN_NAME} <command> [files...] [options]
181
+
182
+ COMMANDS
183
+ scrub [files...] Mask identifiers and redact credentials. Reads stdin when
184
+ no file is given. Writes the symbol map to ${STORE_DIR}/map.json.
185
+ restore [files...] Swap placeholders back and strip AI-generated noise.
186
+ scan [files...] Detect credentials only; never rewrites. Exits 1 when it
187
+ finds something — use it in a pre-commit hook or CI.
188
+ map Show the current symbol map (--clear to wipe it).
189
+
190
+ OPTIONS
191
+ -l, --language <lang> auto (default), typescript, python, go, java, csharp,
192
+ rust, ruby, php, c, sql
193
+ -s, --secrets <policy> redact (default) | warn | off
194
+ -p, --preamble Prepend the downstream-AI note and placeholder legend
195
+ -m, --map <path> Use a specific map file
196
+ --json Machine-readable output (scan, map)
197
+ --strict scan: also fail on advisory findings
198
+ --keep-docs restore: keep JSDoc blocks the model wrote
199
+ -f, --force Allow a map write that would drop existing entries
200
+ -q, --quiet Suppress the all-clear summary (findings always show)
201
+ -h, --help Show this help
202
+ -v, --version Show the version
203
+
204
+ EXIT CODES
205
+ 0 clean 1 findings that should stop the pipeline 2 usage or IO error
206
+
207
+ EXAMPLES
208
+ ${BIN_NAME} scrub src/billing.ts | pbcopy
209
+ pbpaste | ${BIN_NAME} restore
210
+ git diff --cached | ${BIN_NAME} scan # pre-commit gate
211
+ ${BIN_NAME} scan 'src/**/*.ts' --json > findings.json
212
+
213
+ Transformed code goes to stdout; everything else goes to stderr, so the CLI
214
+ composes in a pipe.
215
+ `;
216
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { type Io } from './commands.js';
3
+ export declare const VERSION = "0.1.0";
4
+ /** Testable entry point: everything that touches the process is injected. */
5
+ export declare function main(argv: readonly string[], io: Io): Promise<number>;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAA2D,KAAK,EAAE,EAAE,MAAM,eAAe,CAAA;AAIhG,eAAO,MAAM,OAAO,UAAU,CAAA;AAE9B,6EAA6E;AAC7E,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiC3E"}
package/dist/index.js ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs, UsageError } from './args.js';
3
+ import { EXIT_ERROR, HELP, runMap, runRestore, runScan, runScrub } from './commands.js';
4
+ import { realpathSync } from 'node:fs';
5
+ import { pathToFileURL } from 'node:url';
6
+ export const VERSION = '0.1.0';
7
+ /** Testable entry point: everything that touches the process is injected. */
8
+ export async function main(argv, io) {
9
+ let args;
10
+ try {
11
+ args = parseArgs(argv);
12
+ }
13
+ catch (err) {
14
+ if (err instanceof UsageError) {
15
+ io.stderr(`veilio: ${err.message}\n\nRun "veilio --help" for usage.\n`);
16
+ return EXIT_ERROR;
17
+ }
18
+ throw err;
19
+ }
20
+ try {
21
+ switch (args.command) {
22
+ case 'help':
23
+ io.stdout(HELP);
24
+ return 0;
25
+ case 'version':
26
+ io.stdout(`${VERSION}\n`);
27
+ return 0;
28
+ case 'scrub':
29
+ return await runScrub(args, io);
30
+ case 'restore':
31
+ return await runRestore(args, io);
32
+ case 'scan':
33
+ return await runScan(args, io);
34
+ case 'map':
35
+ return await runMap(args, io);
36
+ }
37
+ }
38
+ catch (err) {
39
+ io.stderr(`veilio: ${err instanceof Error ? err.message : String(err)}\n`);
40
+ return EXIT_ERROR;
41
+ }
42
+ }
43
+ function readStdin() {
44
+ return new Promise((resolvePromise, rejectPromise) => {
45
+ // A TTY with no piped input would hang forever waiting for EOF. Fail with
46
+ // usage guidance instead of appearing to freeze.
47
+ if (process.stdin.isTTY) {
48
+ rejectPromise(new Error('no input — pass a file or pipe to stdin'));
49
+ return;
50
+ }
51
+ let data = '';
52
+ process.stdin.setEncoding('utf8');
53
+ process.stdin.on('data', (chunk) => (data += chunk));
54
+ process.stdin.on('end', () => resolvePromise(data));
55
+ process.stdin.on('error', rejectPromise);
56
+ });
57
+ }
58
+ /**
59
+ * Was this file run as a program, or imported?
60
+ *
61
+ * The obvious form of this check — comparing `import.meta.url` to
62
+ * `file://${process.argv[1]}` — is wrong in the one way that matters, and it
63
+ * shipped: npm installs a binary as a SYMLINK in `node_modules/.bin`, so
64
+ * `argv[1]` is the link while `import.meta.url` is the file it resolves to. The
65
+ * two never match, the branch below never runs, and `veilio --version` prints
66
+ * nothing and exits 0. Installed fine, did nothing, said nothing.
67
+ *
68
+ * `realpathSync` resolves the link. `pathToFileURL` handles the rest of what
69
+ * string interpolation got wrong: a path containing a space or any non-ASCII
70
+ * character needs percent-encoding, and a Windows path needs `file:///C:/...`
71
+ * rather than `file://C:\...`.
72
+ *
73
+ * Wrapped because `argv[1]` may name something unstattable — `node --eval`, or a
74
+ * file deleted mid-run. Not a program then, which is the safe reading.
75
+ */
76
+ function invokedAsProgram() {
77
+ const argv1 = process.argv[1];
78
+ if (argv1 === undefined)
79
+ return false;
80
+ try {
81
+ return import.meta.url === pathToFileURL(realpathSync(argv1)).href;
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ if (invokedAsProgram()) {
88
+ const io = {
89
+ cwd: process.cwd(),
90
+ stdin: readStdin,
91
+ stdout: (text) => process.stdout.write(text),
92
+ stderr: (text) => process.stderr.write(text),
93
+ };
94
+ main(process.argv.slice(2), io).then((code) => {
95
+ process.exitCode = code;
96
+ }, (err) => {
97
+ process.stderr.write(`veilio: ${err instanceof Error ? err.message : String(err)}\n`);
98
+ process.exitCode = EXIT_ERROR;
99
+ });
100
+ }
101
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAW,MAAM,eAAe,CAAA;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA;AAE9B,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAuB,EAAE,EAAM;IACxD,IAAI,IAAI,CAAA;IACR,IAAI,CAAC;QACH,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;YAC9B,EAAE,CAAC,MAAM,CAAC,WAAW,GAAG,CAAC,OAAO,sCAAsC,CAAC,CAAA;YACvE,OAAO,UAAU,CAAA;QACnB,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,KAAK,MAAM;gBACT,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACf,OAAO,CAAC,CAAA;YACV,KAAK,SAAS;gBACZ,EAAE,CAAC,MAAM,CAAC,GAAG,OAAO,IAAI,CAAC,CAAA;gBACzB,OAAO,CAAC,CAAA;YACV,KAAK,OAAO;gBACV,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YACjC,KAAK,SAAS;gBACZ,OAAO,MAAM,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YACnC,KAAK,MAAM;gBACT,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YAChC,KAAK,KAAK;gBACR,OAAO,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,EAAE,CAAC,MAAM,CAAC,WAAW,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC1E,OAAO,UAAU,CAAA;IACnB,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,IAAI,OAAO,CAAC,CAAC,cAAc,EAAE,aAAa,EAAE,EAAE;QACnD,0EAA0E;QAC1E,iDAAiD;QACjD,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAA;YACnE,OAAM;QACR,CAAC;QACD,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACjC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAA;QACpD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAA;QACnD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAC1C,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,gBAAgB;IACvB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC7B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACrC,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,IAAI,gBAAgB,EAAE,EAAE,CAAC;IACvB,MAAM,EAAE,GAAO;QACb,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QAC5C,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;KAC7C,CAAA;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAClC,CAAC,IAAI,EAAE,EAAE;QACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA;IACzB,CAAC,EACD,CAAC,GAAY,EAAE,EAAE;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACrF,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAA;IAC/B,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { STORE_DIR, type SymbolMap } from '@veilio-inc/engine';
2
+ export { STORE_DIR };
3
+ export declare const STORE_FILE = "map.json";
4
+ /** Walk up from `from` looking for an existing store or a repo root, so running
5
+ * the CLI in a subdirectory still finds the project's map. Falls back to
6
+ * `from` when neither is found. */
7
+ export declare function findProjectRoot(from: string): string;
8
+ /** Resolve the map file path: explicit override, else the project store. */
9
+ export declare function resolveMapPath(override: string | null, cwd: string): string;
10
+ export interface StoredMap {
11
+ map: SymbolMap;
12
+ /** Schema marker so a future format change can be detected rather than
13
+ * silently misread as a map. */
14
+ version: 1;
15
+ }
16
+ /** Read the stored map, or an empty map when there is none. Throws only when a
17
+ * file exists but cannot be understood — silently starting from empty there
18
+ * would orphan every placeholder the user already has in flight. */
19
+ export declare function loadMap(path: string): SymbolMap;
20
+ export declare class MapOverwriteError extends Error {
21
+ }
22
+ /**
23
+ * Write the map, refusing a write that would lose entries already on disk.
24
+ *
25
+ * Not a guard on every write. `scrub` loads the existing map, hands it to
26
+ * `anonymize` as `existingMap` and saves the union, so the normal path only ever
27
+ * grows the file — demanding a flag for that would mean typing `--force` on
28
+ * every second command, which is how a safety prompt becomes muscle memory and
29
+ * stops being read.
30
+ *
31
+ * What it refuses is the write that actually destroys something: one whose map
32
+ * no longer explains a placeholder the file already had. That map is the only
33
+ * means of restoring output produced with it, and once the entry is gone the
34
+ * text that used it can never be read back. Reachable by pointing `--map` at
35
+ * another session's store, or by a future caller that forgets to load first.
36
+ */
37
+ export declare function saveMap(path: string, map: SymbolMap, options?: {
38
+ force?: boolean;
39
+ }): void;
40
+ export declare function clearMap(path: string): boolean;
41
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9D,OAAO,EAAE,SAAS,EAAE,CAAA;AACpB,eAAO,MAAM,UAAU,aAAa,CAAA;AAEpC;;oCAEoC;AACpC,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG3E;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,SAAS,CAAA;IACd;qCACiC;IACjC,OAAO,EAAE,CAAC,CAAA;CACX;AAOD;;qEAEqE;AACrE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAgB/C;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAE/C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,IAAI,CA8B7F;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAI9C"}
package/dist/store.js ADDED
@@ -0,0 +1,113 @@
1
+ // Symbol-map persistence.
2
+ //
3
+ // The map is what makes the round trip work: scrub writes it, restore reads it.
4
+ // It lives in the project, not a global cache, because placeholder numbering is
5
+ // only meaningful within one codebase — sharing `__CLS__1` across unrelated
6
+ // projects would collide names and restore the wrong identifiers.
7
+ //
8
+ // The map holds real identifier names, so it is written 0600 and the store
9
+ // directory carries a .gitignore. Committing it would undo the anonymization
10
+ // for anyone reading the repo.
11
+ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
12
+ import { dirname, join, resolve } from 'node:path';
13
+ import { STORE_DIR } from '@veilio-inc/engine';
14
+ export { STORE_DIR };
15
+ export const STORE_FILE = 'map.json';
16
+ /** Walk up from `from` looking for an existing store or a repo root, so running
17
+ * the CLI in a subdirectory still finds the project's map. Falls back to
18
+ * `from` when neither is found. */
19
+ export function findProjectRoot(from) {
20
+ let dir = resolve(from);
21
+ for (;;) {
22
+ if (existsSync(join(dir, STORE_DIR)) || existsSync(join(dir, '.git')))
23
+ return dir;
24
+ const parent = dirname(dir);
25
+ if (parent === dir)
26
+ return resolve(from);
27
+ dir = parent;
28
+ }
29
+ }
30
+ /** Resolve the map file path: explicit override, else the project store. */
31
+ export function resolveMapPath(override, cwd) {
32
+ if (override !== null)
33
+ return resolve(cwd, override);
34
+ return join(findProjectRoot(cwd), STORE_DIR, STORE_FILE);
35
+ }
36
+ function isSymbolMap(value) {
37
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
38
+ return false;
39
+ return Object.values(value).every((v) => typeof v === 'string');
40
+ }
41
+ /** Read the stored map, or an empty map when there is none. Throws only when a
42
+ * file exists but cannot be understood — silently starting from empty there
43
+ * would orphan every placeholder the user already has in flight. */
44
+ export function loadMap(path) {
45
+ if (!existsSync(path))
46
+ return {};
47
+ let parsed;
48
+ try {
49
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
50
+ }
51
+ catch {
52
+ throw new Error(`map file at ${path} is not valid JSON`);
53
+ }
54
+ if (typeof parsed !== 'object' || parsed === null) {
55
+ throw new Error(`map file at ${path} is malformed`);
56
+ }
57
+ const map = parsed.map;
58
+ if (!isSymbolMap(map)) {
59
+ throw new Error(`map file at ${path} is malformed`);
60
+ }
61
+ return map;
62
+ }
63
+ export class MapOverwriteError extends Error {
64
+ }
65
+ /**
66
+ * Write the map, refusing a write that would lose entries already on disk.
67
+ *
68
+ * Not a guard on every write. `scrub` loads the existing map, hands it to
69
+ * `anonymize` as `existingMap` and saves the union, so the normal path only ever
70
+ * grows the file — demanding a flag for that would mean typing `--force` on
71
+ * every second command, which is how a safety prompt becomes muscle memory and
72
+ * stops being read.
73
+ *
74
+ * What it refuses is the write that actually destroys something: one whose map
75
+ * no longer explains a placeholder the file already had. That map is the only
76
+ * means of restoring output produced with it, and once the entry is gone the
77
+ * text that used it can never be read back. Reachable by pointing `--map` at
78
+ * another session's store, or by a future caller that forgets to load first.
79
+ */
80
+ export function saveMap(path, map, options = {}) {
81
+ if (!options.force && existsSync(path)) {
82
+ const dropped = Object.entries(loadMap(path)).filter(([placeholder, real]) => map[placeholder] !== real);
83
+ if (dropped.length > 0) {
84
+ const shown = dropped
85
+ .slice(0, 3)
86
+ .map(([p]) => p)
87
+ .join(', ');
88
+ throw new MapOverwriteError(`refusing to overwrite ${path}: ${dropped.length} existing placeholder${dropped.length === 1 ? '' : 's'} ` +
89
+ `(${shown}${dropped.length > 3 ? ', …' : ''}) would be lost, and text already anonymized with them ` +
90
+ `could never be restored. Pass --force to overwrite anyway.`);
91
+ }
92
+ }
93
+ const dir = dirname(path);
94
+ mkdirSync(dir, { recursive: true });
95
+ // A store directory inside a repo must never be committed — it holds the real
96
+ // names the anonymization exists to hide.
97
+ const ignore = join(dir, '.gitignore');
98
+ if (dir.endsWith(STORE_DIR) && !existsSync(ignore)) {
99
+ writeFileSync(ignore, '*\n', { mode: 0o600 });
100
+ }
101
+ const payload = { version: 1, map };
102
+ writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, { mode: 0o600 });
103
+ // writeFileSync only applies `mode` when creating the file; enforce it on
104
+ // rewrite too so a pre-existing world-readable map gets locked down.
105
+ chmodSync(path, 0o600);
106
+ }
107
+ export function clearMap(path) {
108
+ if (!existsSync(path))
109
+ return false;
110
+ rmSync(path);
111
+ return true;
112
+ }
113
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@veilio-inc/cli",
3
+ "version": "0.1.0",
4
+ "description": "Anonymize identifiers and redact credentials in source code before it reaches an LLM — from the terminal, a pipe, or a pre-commit hook.",
5
+ "type": "module",
6
+ "bin": {
7
+ "veilio": "dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "engines": {
17
+ "node": ">=24"
18
+ },
19
+ "keywords": [
20
+ "anonymizer",
21
+ "llm",
22
+ "ai",
23
+ "privacy",
24
+ "secret-scanning",
25
+ "pre-commit",
26
+ "cli",
27
+ "veilio"
28
+ ],
29
+ "license": "SEE LICENSE IN LICENSE",
30
+ "dependencies": {
31
+ "@veilio-inc/engine": "^1.4.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "clean": "rm -rf dist",
36
+ "prepublishOnly": "npm run clean && npm run typecheck && npm run build && npm run test",
37
+ "typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
38
+ "test": "vitest run",
39
+ "test:coverage": "vitest run --coverage"
40
+ },
41
+ "devDependencies": {
42
+ "@vitest/coverage-v8": "^4.1.10",
43
+ "typescript": "^5.4.0",
44
+ "vitest": "^4.1.10"
45
+ },
46
+ "exports": {
47
+ ".": "./dist/index.js",
48
+ "./store": "./dist/store.js"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/veilio-inc/veilio.git",
53
+ "directory": "packages/cli"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/veilio-inc/veilio/issues"
57
+ },
58
+ "homepage": "https://github.com/veilio-inc/veilio/tree/main/packages/cli#readme",
59
+ "publishConfig": {
60
+ "access": "public"
61
+ }
62
+ }