@veilio-inc/cli 0.1.0 → 0.3.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 +30 -5
- package/README.md +42 -3
- package/dist/args.d.ts +35 -1
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +82 -1
- package/dist/args.js.map +1 -1
- package/dist/cloud-commands.d.ts +90 -0
- package/dist/cloud-commands.d.ts.map +1 -0
- package/dist/cloud-commands.js +656 -0
- package/dist/cloud-commands.js.map +1 -0
- package/dist/cloud.d.ts +172 -0
- package/dist/cloud.d.ts.map +1 -0
- package/dist/cloud.js +212 -0
- package/dist/cloud.js.map +1 -0
- package/dist/commands.d.ts +14 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +35 -0
- package/dist/commands.js.map +1 -1
- package/dist/credential.d.ts +52 -0
- package/dist/credential.d.ts.map +1 -0
- package/dist/credential.js +135 -0
- package/dist/credential.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +158 -1
- package/dist/index.js.map +1 -1
- package/dist/rules.d.ts +35 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +103 -0
- package/dist/rules.js.map +1 -0
- package/dist/store.d.ts +25 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +26 -1
- package/dist/store.js.map +1 -1
- package/dist/team-unlock.d.ts +49 -0
- package/dist/team-unlock.d.ts.map +1 -0
- package/dist/team-unlock.js +134 -0
- package/dist/team-unlock.js.map +1 -0
- package/package.json +10 -6
package/dist/store.js
CHANGED
|
@@ -38,6 +38,28 @@ function isSymbolMap(value) {
|
|
|
38
38
|
return false;
|
|
39
39
|
return Object.values(value).every((v) => typeof v === 'string');
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The Cloud provenance recorded alongside the local map, if any.
|
|
43
|
+
*
|
|
44
|
+
* Separate from `loadMap` because every existing caller wants the map and
|
|
45
|
+
* nothing else, and because a store written before this existed simply has no
|
|
46
|
+
* `remote` — which reads correctly as "this map did not come from Cloud".
|
|
47
|
+
*/
|
|
48
|
+
export function loadRemote(path) {
|
|
49
|
+
if (!existsSync(path))
|
|
50
|
+
return undefined;
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
53
|
+
const remote = parsed.remote;
|
|
54
|
+
if (!remote || typeof remote.id !== 'string' || typeof remote.updatedAt !== 'string') {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return remote;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
41
63
|
/** Read the stored map, or an empty map when there is none. Throws only when a
|
|
42
64
|
* file exists but cannot be understood — silently starting from empty there
|
|
43
65
|
* would orphan every placeholder the user already has in flight. */
|
|
@@ -98,7 +120,10 @@ export function saveMap(path, map, options = {}) {
|
|
|
98
120
|
if (dir.endsWith(STORE_DIR) && !existsSync(ignore)) {
|
|
99
121
|
writeFileSync(ignore, '*\n', { mode: 0o600 });
|
|
100
122
|
}
|
|
101
|
-
|
|
123
|
+
// Carried forward when the caller does not supply one, so an ordinary `scrub`
|
|
124
|
+
// does not quietly erase the provenance a `pull` recorded.
|
|
125
|
+
const remote = options.remote ?? loadRemote(path);
|
|
126
|
+
const payload = { version: 1, map, ...(remote ? { remote } : {}) };
|
|
102
127
|
writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, { mode: 0o600 });
|
|
103
128
|
// writeFileSync only applies `mode` when creating the file; enforce it on
|
|
104
129
|
// rewrite too so a pre-existing world-readable map gets locked down.
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,4EAA4E;AAC5E,kEAAkE;AAClE,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,+BAA+B;AAE/B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC/F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,SAAS,EAAkB,MAAM,oBAAoB,CAAA;AAE9D,OAAO,EAAE,SAAS,EAAE,CAAA;AACpB,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAA;AAEpC;;oCAEoC;AACpC,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,SAAS,CAAC;QACR,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,GAAG,CAAA;QACjF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAA;QACxC,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,QAAuB,EAAE,GAAW;IACjE,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IACpD,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;AAC1D,CAAC;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const TEAM_UNLOCK_FILE = "team-keys.json";
|
|
2
|
+
/** How long an unlock lasts unless asked for something shorter. */
|
|
3
|
+
export declare const DEFAULT_UNLOCK_DAYS = 7;
|
|
4
|
+
export interface StoredTeamKey {
|
|
5
|
+
version: number;
|
|
6
|
+
/** Raw AES-256 key, base64. */
|
|
7
|
+
key: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TeamUnlock {
|
|
10
|
+
v: 1;
|
|
11
|
+
/** Bound to the instance and account that unlocked it, so a file left behind
|
|
12
|
+
* by one sign-in is never used by another. The same reasoning as the wrap
|
|
13
|
+
* context: material that is valid somewhere must not be valid everywhere. */
|
|
14
|
+
instance: string;
|
|
15
|
+
account: string;
|
|
16
|
+
teamId: string;
|
|
17
|
+
/** ISO-8601. Past this, reads return null and the file is ignored. */
|
|
18
|
+
expiresAt: string;
|
|
19
|
+
keys: StoredTeamKey[];
|
|
20
|
+
}
|
|
21
|
+
/** Where the unlocked keys live: beside the credential, not in a project. */
|
|
22
|
+
export declare function teamUnlockPath(home?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* The unlocked keys, or null.
|
|
25
|
+
*
|
|
26
|
+
* Null means "not unlocked", for every reason it can mean that. `now` is
|
|
27
|
+
* injectable so the expiry can be tested without waiting a week.
|
|
28
|
+
*/
|
|
29
|
+
export declare function readTeamUnlock(binding: {
|
|
30
|
+
instance: string;
|
|
31
|
+
account: string;
|
|
32
|
+
}, home?: string, now?: Date): TeamUnlock | null;
|
|
33
|
+
/** Write the unlocked keys, readable only by this user. */
|
|
34
|
+
export declare function writeTeamUnlock(unlock: TeamUnlock, home?: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Remove the unlocked keys. Returns whether there were any to remove.
|
|
37
|
+
*
|
|
38
|
+
* Called by `team lock` and by `logout` — signing out must not leave live team
|
|
39
|
+
* keys behind, which would be exactly the gap between what a person believes
|
|
40
|
+
* they revoked and what is still readable on disk.
|
|
41
|
+
*
|
|
42
|
+
* A failure to DELETE throws rather than returning false, for the reason
|
|
43
|
+
* `removeCredential` gives: reporting "nothing to remove" while key material
|
|
44
|
+
* sits there inverts the whole point of the call.
|
|
45
|
+
*/
|
|
46
|
+
export declare function removeTeamUnlock(home?: string): boolean;
|
|
47
|
+
/** When an unlock made now should expire. */
|
|
48
|
+
export declare function expiryFrom(now: Date, days?: number): string;
|
|
49
|
+
//# sourceMappingURL=team-unlock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-unlock.d.ts","sourceRoot":"","sources":["../src/team-unlock.ts"],"names":[],"mappings":"AAwCA,eAAO,MAAM,gBAAgB,mBAAmB,CAAA;AAEhD,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,+BAA+B;IAC/B,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,CAAC,CAAA;IACJ;;kFAE8E;IAC9E,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,aAAa,EAAE,CAAA;CACtB;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAE/D;AAiBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC9C,IAAI,GAAE,MAAkB,EACxB,GAAG,GAAE,IAAiB,GACrB,UAAU,GAAG,IAAI,CAyBnB;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,GAAE,MAAkB,GAAG,IAAI,CAQlF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,MAAkB,GAAG,OAAO,CASlE;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,GAAE,MAA4B,GAAG,MAAM,CAEhF"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// The team keys, unlocked for processes that have nobody to ask.
|
|
2
|
+
//
|
|
3
|
+
// An MCP server starts inside a coding agent. There is no terminal attached and
|
|
4
|
+
// no one present to type a vault passphrase, so a key it needs must have been
|
|
5
|
+
// put here by an earlier run in a terminal where somebody was. `veilio team
|
|
6
|
+
// unlock` is that run.
|
|
7
|
+
//
|
|
8
|
+
// WHAT THIS FILE HOLDS, AND WHY IT IS THE NARROWER OF TWO BAD OPTIONS
|
|
9
|
+
//
|
|
10
|
+
// It holds unwrapped TEAM keys. The obvious alternative — storing the vault key
|
|
11
|
+
// and re-deriving everything per process — is worse in every direction that
|
|
12
|
+
// matters: the vault key also opens every personal map, and unwraps the private
|
|
13
|
+
// key, which in turn opens any wrap ever addressed to this account, including
|
|
14
|
+
// ones granted after the file was written. A team key opens team maps and
|
|
15
|
+
// nothing else, and a team already shares them by construction.
|
|
16
|
+
//
|
|
17
|
+
// It is still real key material at rest. Whoever holds this file can read the
|
|
18
|
+
// team's maps until the keys rotate. It is written 0600 beside the session
|
|
19
|
+
// token, which is the same posture the token itself gets, and it expires — so a
|
|
20
|
+
// machine compromised weeks after somebody ran `unlock` does not hand over a
|
|
21
|
+
// live key for a forgotten session.
|
|
22
|
+
//
|
|
23
|
+
// WHAT IT DOES NOT SURVIVE
|
|
24
|
+
//
|
|
25
|
+
// A rotation. Removing a member mints a new version, and the copy here is of
|
|
26
|
+
// the old one, so maps rewritten under the new key stop opening. That is
|
|
27
|
+
// deliberate: the stale file degrades into "some maps cannot be read", which
|
|
28
|
+
// `decryptTeamMapWithAny` already handles by skipping them, rather than into
|
|
29
|
+
// continued access a rotation was meant to end. Re-running `unlock` picks up
|
|
30
|
+
// the new version.
|
|
31
|
+
//
|
|
32
|
+
// Reads tolerate everything, exactly as `credential.ts` does: a missing file,
|
|
33
|
+
// a corrupt one, an expired one and one belonging to another account all mean
|
|
34
|
+
// the same thing to every caller — not unlocked, resolve locally.
|
|
35
|
+
import { chmodSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
36
|
+
import { dirname, join } from 'node:path';
|
|
37
|
+
import { homedir } from 'node:os';
|
|
38
|
+
import { STORE_DIR } from '@veilio-inc/engine';
|
|
39
|
+
export const TEAM_UNLOCK_FILE = 'team-keys.json';
|
|
40
|
+
/** How long an unlock lasts unless asked for something shorter. */
|
|
41
|
+
export const DEFAULT_UNLOCK_DAYS = 7;
|
|
42
|
+
/** Where the unlocked keys live: beside the credential, not in a project. */
|
|
43
|
+
export function teamUnlockPath(home = homedir()) {
|
|
44
|
+
return join(home, STORE_DIR, TEAM_UNLOCK_FILE);
|
|
45
|
+
}
|
|
46
|
+
function isTeamUnlock(value) {
|
|
47
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
48
|
+
return false;
|
|
49
|
+
const u = value;
|
|
50
|
+
if (u.v !== 1)
|
|
51
|
+
return false;
|
|
52
|
+
for (const field of ['instance', 'account', 'teamId', 'expiresAt']) {
|
|
53
|
+
if (typeof u[field] !== 'string' || u[field] === '')
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (!Array.isArray(u.keys) || u.keys.length === 0)
|
|
57
|
+
return false;
|
|
58
|
+
return u.keys.every((k) => {
|
|
59
|
+
if (typeof k !== 'object' || k === null)
|
|
60
|
+
return false;
|
|
61
|
+
const key = k;
|
|
62
|
+
return typeof key.version === 'number' && typeof key.key === 'string' && key.key !== '';
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The unlocked keys, or null.
|
|
67
|
+
*
|
|
68
|
+
* Null means "not unlocked", for every reason it can mean that. `now` is
|
|
69
|
+
* injectable so the expiry can be tested without waiting a week.
|
|
70
|
+
*/
|
|
71
|
+
export function readTeamUnlock(binding, home = homedir(), now = new Date()) {
|
|
72
|
+
let raw;
|
|
73
|
+
try {
|
|
74
|
+
raw = readFileSync(teamUnlockPath(home), 'utf8');
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(raw);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (!isTeamUnlock(parsed))
|
|
87
|
+
return null;
|
|
88
|
+
// Someone else's unlock, or one from another instance. Not an error — a
|
|
89
|
+
// machine can be signed in to a self-hosted instance today and to the hosted
|
|
90
|
+
// one tomorrow, and the leftover file is simply not ours.
|
|
91
|
+
if (parsed.instance !== binding.instance || parsed.account !== binding.account)
|
|
92
|
+
return null;
|
|
93
|
+
const expiresAt = Date.parse(parsed.expiresAt);
|
|
94
|
+
if (Number.isNaN(expiresAt) || expiresAt <= now.getTime())
|
|
95
|
+
return null;
|
|
96
|
+
return parsed;
|
|
97
|
+
}
|
|
98
|
+
/** Write the unlocked keys, readable only by this user. */
|
|
99
|
+
export function writeTeamUnlock(unlock, home = homedir()) {
|
|
100
|
+
const path = teamUnlockPath(home);
|
|
101
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
102
|
+
// `chmodSync` after the write for the same reason `writeCredential` does it:
|
|
103
|
+
// `mode` applies only when the file is created, so re-unlocking over a
|
|
104
|
+
// world-readable file left from an earlier mistake would keep it that way.
|
|
105
|
+
writeFileSync(path, `${JSON.stringify(unlock, null, 2)}\n`, { mode: 0o600 });
|
|
106
|
+
chmodSync(path, 0o600);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Remove the unlocked keys. Returns whether there were any to remove.
|
|
110
|
+
*
|
|
111
|
+
* Called by `team lock` and by `logout` — signing out must not leave live team
|
|
112
|
+
* keys behind, which would be exactly the gap between what a person believes
|
|
113
|
+
* they revoked and what is still readable on disk.
|
|
114
|
+
*
|
|
115
|
+
* A failure to DELETE throws rather than returning false, for the reason
|
|
116
|
+
* `removeCredential` gives: reporting "nothing to remove" while key material
|
|
117
|
+
* sits there inverts the whole point of the call.
|
|
118
|
+
*/
|
|
119
|
+
export function removeTeamUnlock(home = homedir()) {
|
|
120
|
+
const path = teamUnlockPath(home);
|
|
121
|
+
try {
|
|
122
|
+
statSync(path);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
rmSync(path, { recursive: true, force: true });
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
/** When an unlock made now should expire. */
|
|
131
|
+
export function expiryFrom(now, days = DEFAULT_UNLOCK_DAYS) {
|
|
132
|
+
return new Date(now.getTime() + days * 24 * 60 * 60 * 1000).toISOString();
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=team-unlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-unlock.js","sourceRoot":"","sources":["../src/team-unlock.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veilio-inc/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Anonymize identifiers and redact credentials in source code before it reaches an LLM
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Anonymize identifiers and redact credentials in source code before it reaches an LLM \u2014 from the terminal, a pipe, or a pre-commit hook.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"veilio": "dist/index.js"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"license": "SEE LICENSE IN LICENSE",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@veilio-inc/engine": "^1.
|
|
31
|
+
"@veilio-inc/engine": "^1.6.2"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc",
|
|
@@ -39,13 +39,17 @@
|
|
|
39
39
|
"test:coverage": "vitest run --coverage"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@vitest/coverage-v8": "^
|
|
42
|
+
"@vitest/coverage-v8": "^5.0.1",
|
|
43
43
|
"typescript": "^5.4.0",
|
|
44
|
-
"vitest": "^
|
|
44
|
+
"vitest": "^5.0.1"
|
|
45
45
|
},
|
|
46
46
|
"exports": {
|
|
47
47
|
".": "./dist/index.js",
|
|
48
|
-
"./store": "./dist/store.js"
|
|
48
|
+
"./store": "./dist/store.js",
|
|
49
|
+
"./credential": "./dist/credential.js",
|
|
50
|
+
"./cloud": "./dist/cloud.js",
|
|
51
|
+
"./team-unlock": "./dist/team-unlock.js",
|
|
52
|
+
"./rules": "./dist/rules.js"
|
|
49
53
|
},
|
|
50
54
|
"repository": {
|
|
51
55
|
"type": "git",
|