@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/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
- const payload = { version: 1, map };
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":"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"}
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.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.",
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.4.0"
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": "^4.1.10",
42
+ "@vitest/coverage-v8": "^5.0.1",
43
43
  "typescript": "^5.4.0",
44
- "vitest": "^4.1.10"
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",