@veilio-inc/cli 0.2.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Veilio Community License
2
2
 
3
- _Version 1.0 · Effective 2026-08-08_
3
+ _Version 1.1 · Effective 2026-09-25_
4
4
 
5
5
  **Copyright © 2026 Veilio**
6
6
 
@@ -10,7 +10,8 @@ _Version 1.0 · Effective 2026-08-08_
10
10
  > The one thing you may not do without written permission is sell Veilio itself:
11
11
  > reselling it, white-labeling it, or running it as a hosted service for other
12
12
  > people. Contributions are welcome. For a commercial or redistribution license,
13
- > email `hello@veilio.dev`.
13
+ > email `hello@veilio.dev`. Veilio is not licensed for use in or from Russia or
14
+ > Belarus, or by sanctioned persons (Section 5A).
14
15
 
15
16
  ---
16
17
 
@@ -48,11 +49,17 @@ For the purposes of this License:
48
49
 
49
50
  **"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
+ **"Applicable Sanctions"** means any economic, financial, trade or other restrictive measures applicable to Veilio or the relevant transaction under the laws of the European Union or the Republic of Poland, including applicable asset-freezing and prohibition-on-making-available measures, and any other sanctions or trade restrictions that are legally binding on Veilio.
53
+
54
+ **"Restricted Person"** means any natural or legal person, entity or body that: (a) is designated or otherwise subject to restrictive measures under Applicable Sanctions, (b) is directly or indirectly owned or controlled by such a person, entity or body, (c) acts on behalf of or at the direction of such a person, entity or body; or (d) is otherwise a person to whom Veilio is prohibited under Applicable Sanctions from providing the Software, funds or economic resources.
55
+
56
+ **"Restricted Territory"** means the Russian Federation and the Republic of Belarus.
57
+
51
58
  ---
52
59
 
53
60
  ## 3. Permissions
54
61
 
55
- Subject to the restrictions in this License, you are granted a worldwide, royalty-free, non-exclusive license to:
62
+ Subject to the restrictions in this License, including Section 5A, you are granted a worldwide, royalty-free, non-exclusive license to:
56
63
 
57
64
  * use the Software for personal purposes;
58
65
  * use the Software for internal commercial purposes;
@@ -114,6 +121,24 @@ Requests for written permission may be sent to `hello@veilio.dev`.
114
121
 
115
122
  ---
116
123
 
124
+ ## 5A. Restricted Territories and Sanctions
125
+
126
+ **5A.1.** No license or other right to the Software is granted to:
127
+
128
+ * persons or entities incorporated, registered, established or headquartered in a Restricted Territory;
129
+ * natural persons whose principal place of business or ordinary residence is in a Restricted Territory; or
130
+ * Restricted Persons.
131
+
132
+ **5A.2.** You must not access or use the Software from a Restricted Territory, make the Software available to any person located in a Restricted Territory, or use the Software directly or indirectly for the benefit of a Restricted Person or a person or entity incorporated, registered, established or headquartered in a Restricted Territory.
133
+
134
+ **5A.3.** By using the Software, you represent and warrant, both when you first use it and throughout your use of it, that you are not a Restricted Person and do not fall within any of the territorial restrictions set out in this Section.
135
+
136
+ **5A.4.** You must not use an intermediary, affiliate, nominee, proxy, VPN or other arrangement for the purpose of circumventing the restrictions set out in this Section or Applicable Sanctions.
137
+
138
+ **5A.5.** Any breach of this Section is a material violation of this License and terminates your rights under it (Section 14). Veilio does not grant written permission for exceptions to this Section.
139
+
140
+ ---
141
+
117
142
  ## 6. Cloud Edition
118
143
 
119
144
  The Cloud Edition is the official commercial hosted offering of the project.
@@ -218,7 +243,7 @@ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHORS
218
243
 
219
244
  ## 14. Termination
220
245
 
221
- Your rights under this License terminate automatically if you materially violate its terms.
246
+ Your rights under this License terminate automatically if you materially violate its terms. Any breach of Section 5A is a material violation.
222
247
 
223
248
  Upon termination, you must cease the activities that are prohibited by this License.
224
249
 
@@ -244,5 +269,5 @@ Each version will be identified by a version number. Unless explicitly stated ot
244
269
 
245
270
  **Project:** Veilio
246
271
  **Copyright:** © 2026 Veilio
247
- **License:** Veilio Community License, Version 1.0
272
+ **License:** Veilio Community License, Version 1.1
248
273
  **Contact:** `hello@veilio.dev`
package/README.md CHANGED
@@ -8,6 +8,8 @@ pbpaste | veilio restore # bring the answer back
8
8
  git diff --cached | veilio scan # refuse to commit a live key
9
9
  ```
10
10
 
11
+ Full guide - terminal, Cloud sign-in, and connecting AI assistants (Claude Code, Claude Desktop, Xcode, Cursor, VS Code): [docs/USING-THE-CLI-AND-MCP.md](../../docs/USING-THE-CLI-AND-MCP.md).
12
+
11
13
  ## Install
12
14
 
13
15
  ```bash
@@ -32,17 +34,15 @@ behavior; it only unlocks the four commands below that name Cloud explicitly.
32
34
 
33
35
  ## Cloud sync (Individual plan and above)
34
36
 
35
- > **None of this is in `0.1.0`, which is what npm currently has.** These
36
- > commands are merged here and ship with the next release; until then they run
37
- > from a clone — `npm run build:packages`, then
38
- > `node packages/cli/dist/index.js --help`.
39
-
40
37
  ```bash
41
- veilio login # prompts for email/password; --instance for self-hosted
38
+ veilio login # email, password, and the authentication code if 2FA is on
42
39
  veilio whoami # who you're signed in as, no request made
43
40
  veilio maps list # ids, scope and symbol counts for everything in Cloud
44
41
  veilio maps pull <id> # decrypt locally, write second — never a partial write
45
42
  veilio maps push <name> # upload the local map under a new Cloud name
43
+ veilio team unlock # open the team key with your vault passphrase (kept 7 days)
44
+ veilio team lock # remove the unlocked team key from this machine
45
+ veilio rules pull # fetch your custom rules; scrub then applies them offline
46
46
  veilio logout # revokes server-side, then removes the local credential
47
47
  ```
48
48
 
@@ -53,10 +53,16 @@ accepted the sign-in. `0600` is the floor, not the final answer — the OS
53
53
  keychain is a stronger option and a planned follow-up. A personal map is
54
54
  zero-knowledge: pulling or pushing one prompts for the vault passphrase and
55
55
  derives the key here, on your machine — the passphrase and the key never
56
- leave it. A team map arrives already open, because the server can read those
57
- (stated in the privacy policy) and there is nothing local left to decrypt.
56
+ leave it. A team map is sealed under the team key, which the server cannot
57
+ read either; `veilio team unlock` opens it here with your vault passphrase.
58
+
59
+ Custom rules (whitelist and replace, set in the web app) are applied by
60
+ `scrub` from a local copy that `veilio rules pull` writes to
61
+ `~/.veilio/rules.json` (`0600`). `scrub` never fetches them itself - it stays
62
+ offline - and says on every run how many rules it applied and how old the copy
63
+ is. Pull again after the rules change; `logout` removes the copy.
58
64
 
59
- Every one of these six is a client of `GET /api/maps`, `GET /api/maps/:id` and
65
+ The map commands are clients of `GET /api/maps`, `GET /api/maps/:id` and
60
66
  `POST /api/maps` — the exact same routes the browser uses, behind the exact
61
67
  same entitlement check. There is no CLI-shaped API and nothing here can reach
62
68
  a plan feature the web app couldn't.
package/dist/args.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type LanguageOption } from '@veilio-inc/engine';
2
2
  import type { SecretPolicy } from '@veilio-inc/engine';
3
- export type Command = 'scrub' | 'restore' | 'scan' | 'map' | 'login' | 'logout' | 'whoami' | 'maps' | 'help' | 'version';
3
+ export type Command = 'scrub' | 'restore' | 'scan' | 'map' | 'login' | 'logout' | 'whoami' | 'maps' | 'team' | 'rules' | 'help' | 'version';
4
4
  export interface ParsedArgs {
5
5
  command: Command;
6
6
  /** Positional file paths. Empty means read stdin (or, for `scan`, error). */
@@ -32,6 +32,16 @@ export interface ParsedArgs {
32
32
  * visibly separate from the offline one.
33
33
  */
34
34
  mapsAction: 'list' | 'pull' | 'push' | null;
35
+ /**
36
+ * `team unlock` / `team lock`.
37
+ *
38
+ * A subcommand for the same reason `maps` is one: `veilio unlock` alone would
39
+ * not say what it unlocks, and what it unlocks is specifically the team's
40
+ * keys rather than the account or the local store.
41
+ */
42
+ teamAction: 'unlock' | 'lock' | null;
43
+ /** `rules pull`. */
44
+ rulesAction: 'pull' | null;
35
45
  /**
36
46
  * Base URL of the Veilio instance to sign in to. Null means the public Cloud.
37
47
  *
@@ -1 +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,GACjB,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;AAEpG,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;IACd;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAE3C;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAexC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,cAA0D,CAAA;AAWrF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAkI7D"}
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,GACf,OAAO,GACP,SAAS,GACT,MAAM,GACN,KAAK,GACL,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,CAAA;AAEb,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;IACd;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAE3C;;;;;;OAMG;IACH,UAAU,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IACpC,oBAAoB;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAE1B;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAiBxC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,cAOzB,CAAA;AAWF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA0J7D"}
package/dist/args.js CHANGED
@@ -13,6 +13,8 @@ const COMMANDS = new Set([
13
13
  'logout',
14
14
  'whoami',
15
15
  'maps',
16
+ 'team',
17
+ 'rules',
16
18
  'help',
17
19
  'version',
18
20
  ]);
@@ -22,7 +24,14 @@ const COMMANDS = new Set([
22
24
  * command without deciding which side of the line it falls on is a compile
23
25
  * error rather than an accident.
24
26
  */
25
- export const CLOUD_COMMANDS = new Set(['login', 'logout', 'whoami', 'maps']);
27
+ export const CLOUD_COMMANDS = new Set([
28
+ 'login',
29
+ 'logout',
30
+ 'whoami',
31
+ 'maps',
32
+ 'team',
33
+ 'rules',
34
+ ]);
26
35
  const SECRET_POLICIES = new Set(['redact', 'warn', 'off']);
27
36
  const LANGUAGE_VALUES = new Set([...LANGUAGES, 'auto']);
28
37
  function requireValue(flag, value) {
@@ -47,6 +56,8 @@ export function parseArgs(argv) {
47
56
  force: false,
48
57
  instance: null,
49
58
  mapsAction: null,
59
+ teamAction: null,
60
+ rulesAction: null,
50
61
  };
51
62
  if (argv.length === 0)
52
63
  return parsed;
@@ -70,6 +81,26 @@ export function parseArgs(argv) {
70
81
  i = 2;
71
82
  }
72
83
  }
84
+ if (parsed.command === 'team') {
85
+ const verb = argv[1];
86
+ if (verb !== undefined && !verb.startsWith('-')) {
87
+ if (verb !== 'unlock' && verb !== 'lock') {
88
+ throw new UsageError(`unknown team action "${verb}" — expected unlock or lock`);
89
+ }
90
+ parsed.teamAction = verb;
91
+ i = 2;
92
+ }
93
+ }
94
+ if (parsed.command === 'rules') {
95
+ const verb = argv[1];
96
+ if (verb !== undefined && !verb.startsWith('-')) {
97
+ if (verb !== 'pull') {
98
+ throw new UsageError(`unknown rules action "${verb}" — expected pull`);
99
+ }
100
+ parsed.rulesAction = verb;
101
+ i = 2;
102
+ }
103
+ }
73
104
  }
74
105
  for (; i < argv.length; i++) {
75
106
  const arg = argv[i];
package/dist/args.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,2EAA2E;AAC3E,8DAA8D;AAE9D,OAAO,EAAE,SAAS,EAAsC,MAAM,oBAAoB,CAAA;AAsElF,MAAM,OAAO,UAAW,SAAQ,KAAK;CAAG;AAExC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAU;IAChC,OAAO;IACP,SAAS;IACT,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,SAAS;CACV,CAAC,CAAA;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAU;IAC7C,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,MAAM;IACN,OAAO;CACR,CAAC,CAAA;AACF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;AACxE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS,CAAC,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC,CAAA;AAE/D,SAAS,YAAY,CAAC,IAAY,EAAE,KAAyB;IAC3D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,UAAU,CAAC,GAAG,IAAI,mBAAmB,CAAC,CAAA;IAClD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,MAAM,MAAM,GAAe;QACzB,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,QAAQ;QACjB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,KAAK;QACZ,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,KAAK;QACb,QAAQ,EAAE,KAAK;QACf,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,IAAI;KAClB,CAAA;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAA;IAEpC,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IACrB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAgB,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,UAAU,CAAC,oBAAoB,KAAK,GAAG,CAAC,CAAA;QACpD,CAAC;QACD,MAAM,CAAC,OAAO,GAAG,KAAgB,CAAA;QACjC,CAAC,GAAG,CAAC,CAAA;QAEL,4EAA4E;QAC5E,uEAAuE;QACvE,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACpB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChD,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC1D,MAAM,IAAI,UAAU,CAAC,wBAAwB,IAAI,iCAAiC,CAAC,CAAA;gBACrF,CAAC;gBACD,MAAM,CAAC,UAAU,GAAG,IAAI,CAAA;gBACxB,CAAC,GAAG,CAAC,CAAA;YACP,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACpB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACzC,MAAM,IAAI,UAAU,CAAC,wBAAwB,IAAI,6BAA6B,CAAC,CAAA;gBACjF,CAAC;gBACD,MAAM,CAAC,UAAU,GAAG,IAAI,CAAA;gBACxB,CAAC,GAAG,CAAC,CAAA;YACP,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACpB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACpB,MAAM,IAAI,UAAU,CAAC,yBAAyB,IAAI,mBAAmB,CAAC,CAAA;gBACxE,CAAC;gBACD,MAAM,CAAC,WAAW,GAAG,IAAI,CAAA;gBACzB,CAAC,GAAG,CAAC,CAAA;YACP,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QACnB,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACX,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;gBACvB,MAAK;YACP,KAAK,IAAI,CAAC;YACV,KAAK,WAAW;gBACd,MAAM,CAAC,OAAO,GAAG,SAAS,CAAA;gBAC1B,MAAK;YACP,KAAK,IAAI,CAAC;YACV,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;gBAC1C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;oBAChC,MAAM,IAAI,UAAU,CAClB,qBAAqB,KAAK,gCAAgC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACjF,CAAA;gBACH,CAAC;gBACD,MAAM,CAAC,QAAQ,GAAG,KAA0B,CAAA;gBAC5C,MAAK;YACP,CAAC;YACD,KAAK,IAAI,CAAC;YACV,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;gBAC1C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAqB,CAAC,EAAE,CAAC;oBAChD,MAAM,IAAI,UAAU,CAAC,gDAAgD,KAAK,IAAI,CAAC,CAAA;gBACjF,CAAC;gBACD,MAAM,CAAC,OAAO,GAAG,KAAqB,CAAA;gBACtC,MAAK;YACP,CAAC;YACD,KAAK,IAAI,CAAC;YACV,KAAK,OAAO;gBACV,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;gBAC7C,MAAK;YACP,KAAK,IAAI,CAAC;YACV,KAAK,YAAY;gBACf,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAA;gBACtB,MAAK;YACP,KAAK,QAAQ;gBACX,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;gBAClB,MAAK;YACP,KAAK,IAAI,CAAC;YACV,KAAK,SAAS;gBACZ,MAAM,CAAC,KAAK,GAAG,IAAI,CAAA;gBACnB,MAAK;YACP,KAAK,SAAS;gBACZ,MAAM,CAAC,KAAK,GAAG,IAAI,CAAA;gBACnB,MAAK;YACP,KAAK,UAAU;gBACb,MAAM,CAAC,MAAM,GAAG,IAAI,CAAA;gBACpB,MAAK;YACP,KAAK,aAAa;gBAChB,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAA;gBACtB,MAAK;YACP,KAAK,IAAI,CAAC;YACV,KAAK,SAAS;gBACZ,MAAM,CAAC,KAAK,GAAG,IAAI,CAAA;gBACnB,MAAK;YACP,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;gBAC1C,wEAAwE;gBACxE,2DAA2D;gBAC3D,IAAI,GAAQ,CAAA;gBACZ,IAAI,CAAC;oBACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;gBACtB,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,IAAI,UAAU,CAAC,kCAAkC,KAAK,IAAI,CAAC,CAAA;gBACnE,CAAC;gBACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;oBAC9D,MAAM,IAAI,UAAU,CAClB,kCAAkC,KAAK,2CAA2C;wBAChF,wFAAwF,CAC3F,CAAA;gBACH,CAAC;gBACD,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAA;gBACvB,MAAK;YACP,CAAC;YACD;gBACE,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;oBACvC,MAAM,IAAI,UAAU,CAAC,iBAAiB,GAAG,GAAG,CAAC,CAAA;gBAC/C,CAAC;gBACD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -67,4 +67,24 @@ export declare function runMapsPull(id: string | null, mapPath: string, io: Io,
67
67
  }): Promise<number>;
68
68
  /** Upload the local map. Same endpoint, same quotas, same validation. */
69
69
  export declare function runMapsPush(name: string | null, mapPath: string, io: Io): Promise<number>;
70
+ /**
71
+ * Fetch the account's custom rules so `veilio scrub` can apply them offline.
72
+ *
73
+ * Merged here, in the web app's order, so every later scrub applies exactly
74
+ * what the browser would. A plan without custom rules REMOVES any cache left
75
+ * from before: rules the account can no longer use must not go on shaping
76
+ * its output. Any other failure keeps the cache and says so.
77
+ */
78
+ export declare function runRulesPull(io: Io): Promise<number>;
79
+ /**
80
+ * Unlock this team's keys for processes that cannot ask for a passphrase.
81
+ *
82
+ * The MCP server starts inside a coding agent with no terminal attached, so the
83
+ * keys it needs have to be put on disk by a run like this one, where a person
84
+ * is present to type the passphrase. See `team-unlock.ts` for what is stored
85
+ * and why it is the team key rather than the vault key.
86
+ */
87
+ export declare function runTeamUnlock(io: Io, days?: number): Promise<number>;
88
+ /** Remove the unlocked team keys. */
89
+ export declare function runTeamLock(io: Io): number;
70
90
  //# sourceMappingURL=cloud-commands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-commands.d.ts","sourceRoot":"","sources":["../src/cloud-commands.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAuB,KAAK,EAAE,EAAE,MAAM,eAAe,CAAA;AAW5D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C/E;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgDvD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,CAQxC;AAyDD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBzD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,MAAM,GAAG,IAAI,EACjB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,EAAE,EACN,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,OAAO,CAAC,MAAM,CAAC,CAgGjB;AAED,yEAAyE;AACzE,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgE/F"}
1
+ {"version":3,"file":"cloud-commands.d.ts","sourceRoot":"","sources":["../src/cloud-commands.ts"],"names":[],"mappings":"AAkDA,OAAO,EAAuB,KAAK,EAAE,EAAE,MAAM,eAAe,CAAA;AAc5D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2E/E;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgFvD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,CAQxC;AAyDD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBzD;AA+CD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,MAAM,GAAG,IAAI,EACjB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,EAAE,EACN,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,OAAO,CAAC,MAAM,CAAC,CA+GjB;AAED,yEAAyE;AACzE,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgE/F;AAcD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqC1D;AAID;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmH1E;AAED,qCAAqC;AACrC,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,CAgB1C"}
@@ -6,10 +6,12 @@
6
6
  // with no account touches nothing (FR-002). Putting these here is what keeps
7
7
  // that walk meaningful instead of something to be deleted the moment it becomes
8
8
  // inconvenient.
9
- import { CloudError, createMap, getMap, getVault, listMaps, request, } from './cloud.js';
10
- import { checkVaultVerifier, decryptMapFromVault, deriveVaultKey, encryptMapForVault, fromBase64, parseVaultEnvelope, } from '@veilio-inc/engine';
9
+ import { CloudError, createMap, getMap, getVault, getUserKeys, getTeamKeyWraps, listMaps, listRules, request, } from './cloud.js';
10
+ import { checkVaultVerifier, decryptMapFromVault, deriveVaultKey, unwrapPrivateKey, unwrapTeamKey, exportTeamKey, encryptMapForVault, fromBase64, parseVaultEnvelope, importTeamKey, decryptTeamMapWithAny, } from '@veilio-inc/engine';
11
11
  import { loadMap, loadRemote, saveMap } from './store.js';
12
+ import { readTeamUnlock, removeTeamUnlock, writeTeamUnlock, teamUnlockPath, expiryFrom, } from './team-unlock.js';
12
13
  import { readCredential, removeCredential, writeCredential } from './credential.js';
14
+ import { mergeRules, removeRules, rulesPath, writeRules } from './rules.js';
13
15
  import { EXIT_ERROR, EXIT_OK } from './commands.js';
14
16
  import { DEFAULT_INSTANCE } from './cloud.js';
15
17
  /** The endpoint a sign-in is checked against. See `runLogin`. */
@@ -61,6 +63,32 @@ export async function runLogin(instance, io) {
61
63
  catch (err) {
62
64
  return reportLoginFailure(err, io);
63
65
  }
66
+ // A second factor. The token that came back is a challenge, not a session:
67
+ // storing it made every later command fail, and the failure read as a wrong
68
+ // password (found on staging, 2026-09-26). Ask for the code and complete the
69
+ // sign-in the way the web app does.
70
+ if (session.secondFactorRequired === true) {
71
+ const code = (await requirePrompt(io, 'Authentication code (or a recovery code): ')).trim();
72
+ if (code === '') {
73
+ io.stderr('veilio: no authentication code given\n');
74
+ return EXIT_ERROR;
75
+ }
76
+ try {
77
+ session = await request('/api/auth/2fa/verify', {
78
+ credential: { token: session.token, account, instance: base },
79
+ method: 'POST',
80
+ body: { code },
81
+ });
82
+ }
83
+ catch (err) {
84
+ if (err instanceof CloudError && err.kind === 'unauthenticated') {
85
+ io.stderr('veilio: that authentication code was not accepted, or it expired. ' +
86
+ 'Run `veilio login` again with a fresh code.\n');
87
+ return EXIT_ERROR;
88
+ }
89
+ return reportLoginFailure(err, io);
90
+ }
91
+ }
64
92
  if (typeof session.token !== 'string' || session.token === '') {
65
93
  // A 200 with no token is not a sign-in. Writing an empty credential here
66
94
  // would produce a "signed in" state that fails on every use.
@@ -104,7 +132,14 @@ export async function runLogout(io) {
104
132
  if (err instanceof CloudError && err.kind === 'unauthenticated') {
105
133
  // Already revoked, or expired. The server-side state is what logout
106
134
  // wanted, so finishing the job locally is correct rather than an error.
135
+ //
136
+ // "Locally" has to include the team keys. They outlive the session that
137
+ // fetched them — a revoked token stops working, an unlocked team key goes
138
+ // on opening the team's maps — so leaving them here would be the one
139
+ // branch where signing out quietly kept the more dangerous half.
107
140
  try {
141
+ removeTeamUnlock(io.home);
142
+ removeRules(io.home);
108
143
  removeCredential(io.home);
109
144
  }
110
145
  catch (removeErr) {
@@ -121,6 +156,28 @@ export async function runLogout(io) {
121
156
  'To end the session now, sign out from the web app.\n');
122
157
  return EXIT_ERROR;
123
158
  }
159
+ // Before the credential, because this is key material rather than a token
160
+ // the server has already revoked. Signing out while the team's keys stay
161
+ // readable on disk is exactly the gap between what a person believes they
162
+ // gave up and what is still there.
163
+ try {
164
+ removeTeamUnlock(io.home);
165
+ }
166
+ catch (err) {
167
+ io.stderr(`veilio: the session was revoked, but the unlocked team keys could not be removed ` +
168
+ `(${err instanceof Error ? err.message : String(err)}). Delete ${teamUnlockPath(io.home)} by hand.\n`);
169
+ return EXIT_ERROR;
170
+ }
171
+ // The rules are the account's too, and a whitelist among them keeps names
172
+ // readable: they go with the session rather than outliving it.
173
+ try {
174
+ removeRules(io.home);
175
+ }
176
+ catch (err) {
177
+ io.stderr(`veilio: the session was revoked, but the cached custom rules could not be removed ` +
178
+ `(${err instanceof Error ? err.message : String(err)}). Delete ${rulesPath(io.home)} by hand.\n`);
179
+ return EXIT_ERROR;
180
+ }
124
181
  try {
125
182
  removeCredential(io.home);
126
183
  }
@@ -229,6 +286,41 @@ export async function runMapsList(io) {
229
286
  }
230
287
  return EXIT_OK;
231
288
  }
289
+ /**
290
+ * Open a team map with the team keys `veilio team unlock` stored.
291
+ *
292
+ * The same keys the MCP server reads, for the same reason: this machine may
293
+ * have nobody present to type a vault passphrase, and the team key - not the
294
+ * vault key - is what a team map is sealed under. Reports and returns null
295
+ * when it cannot, so the caller writes nothing.
296
+ */
297
+ async function openTeamEnvelope(credential, mapData, io) {
298
+ const unlock = readTeamUnlock({ instance: credential.instance, account: credential.account }, io.home);
299
+ if (!unlock || unlock.keys.length === 0) {
300
+ io.stderr('veilio: that is a team map, and no team key is unlocked on this machine. ' +
301
+ 'Run `veilio team unlock` first. Nothing was written.\n');
302
+ return null;
303
+ }
304
+ const keys = [];
305
+ for (const stored of unlock.keys) {
306
+ try {
307
+ keys.push({ version: stored.version, key: await importTeamKey(stored.key) });
308
+ }
309
+ catch {
310
+ // A damaged entry; the others may still open the map.
311
+ continue;
312
+ }
313
+ }
314
+ try {
315
+ return await decryptTeamMapWithAny(keys, JSON.parse(mapData));
316
+ }
317
+ catch (err) {
318
+ io.stderr(`veilio: that team map could not be opened with the unlocked team keys — ` +
319
+ `${err instanceof Error ? err.message : String(err)}. ` +
320
+ 'If the team key was rotated since, run `veilio team unlock` again. Nothing was written.\n');
321
+ return null;
322
+ }
323
+ }
232
324
  /**
233
325
  * Pull a map into the local store: decrypt FIRST, write second.
234
326
  *
@@ -256,11 +348,27 @@ export async function runMapsPull(id, mapPath, io, options = {}) {
256
348
  catch (err) {
257
349
  return reportCloudFailure(err, io);
258
350
  }
259
- // A team map arrives already open — the server can read those, and says so in
260
- // the privacy policy. Only a client envelope is ours to decrypt.
261
- let map;
351
+ // Every map is an envelope the server cannot open: a personal map under the
352
+ // vault key, a team map under the team key. Which one is written on the
353
+ // envelope itself.
262
354
  if (typeof remote.map_data !== 'string') {
263
- map = remote.map_data;
355
+ io.stderr('veilio: that map is not an encrypted envelope, so it was not written.\n');
356
+ return EXIT_ERROR;
357
+ }
358
+ let envelope;
359
+ try {
360
+ envelope = JSON.parse(remote.map_data);
361
+ }
362
+ catch {
363
+ io.stderr('veilio: that map is not an encrypted envelope, so it was not written.\n');
364
+ return EXIT_ERROR;
365
+ }
366
+ let map;
367
+ if (envelope.alg === 'AES-256-GCM-TEAM') {
368
+ const opened = await openTeamEnvelope(credential, remote.map_data, io);
369
+ if (!opened)
370
+ return EXIT_ERROR;
371
+ map = opened;
264
372
  }
265
373
  else {
266
374
  let vault;
@@ -391,4 +499,158 @@ function notSignedIn(io) {
391
499
  function reportCloudFailure(err, io) {
392
500
  return reportLoginFailure(err, io);
393
501
  }
502
+ // ─── rules: pull ──────────────────────────────────────────────────────────────
503
+ /**
504
+ * Fetch the account's custom rules so `veilio scrub` can apply them offline.
505
+ *
506
+ * Merged here, in the web app's order, so every later scrub applies exactly
507
+ * what the browser would. A plan without custom rules REMOVES any cache left
508
+ * from before: rules the account can no longer use must not go on shaping
509
+ * its output. Any other failure keeps the cache and says so.
510
+ */
511
+ export async function runRulesPull(io) {
512
+ const credential = readCredential(io.home);
513
+ if (!credential)
514
+ return notSignedIn(io);
515
+ let fetched;
516
+ try {
517
+ fetched = await listRules(credential);
518
+ }
519
+ catch (err) {
520
+ if (err instanceof CloudError && err.kind === 'unentitled') {
521
+ const removed = removeRules(io.home);
522
+ io.stderr(`veilio: this account's plan does not include custom rules${removed ? ' - the cached rules were removed' : ''}.\n`);
523
+ return EXIT_ERROR;
524
+ }
525
+ return reportCloudFailure(err, io);
526
+ }
527
+ const personal = fetched.rules ?? [];
528
+ const team = fetched.teamRules ?? [];
529
+ const rules = mergeRules(personal, team);
530
+ writeRules({
531
+ instance: credential.instance,
532
+ account: credential.account,
533
+ pulledAt: new Date().toISOString(),
534
+ rules,
535
+ }, io.home);
536
+ const skipped = personal.length + team.length - rules.length;
537
+ io.stdout(`Pulled ${rules.length} custom rule${rules.length === 1 ? '' : 's'} ` +
538
+ `(${personal.length} personal, ${team.length} team${skipped > 0 ? `, ${skipped} disabled and skipped` : ''}). ` +
539
+ '`veilio scrub` applies them.\n');
540
+ return EXIT_OK;
541
+ }
542
+ // ─── team: unlock, lock ──────────────────────────────────────────────────────
543
+ /**
544
+ * Unlock this team's keys for processes that cannot ask for a passphrase.
545
+ *
546
+ * The MCP server starts inside a coding agent with no terminal attached, so the
547
+ * keys it needs have to be put on disk by a run like this one, where a person
548
+ * is present to type the passphrase. See `team-unlock.ts` for what is stored
549
+ * and why it is the team key rather than the vault key.
550
+ */
551
+ export async function runTeamUnlock(io, days) {
552
+ const credential = readCredential(io.home);
553
+ if (!credential) {
554
+ io.stderr('veilio: not signed in. Run `veilio login` first.\n');
555
+ return EXIT_ERROR;
556
+ }
557
+ try {
558
+ const list = await listMaps(credential);
559
+ const teamId = list.team?.id;
560
+ if (!teamId) {
561
+ io.stderr('veilio: this account is not in a team, so there are no team keys to unlock. ' +
562
+ 'Shared placeholders are a Team-plan feature.\n');
563
+ return EXIT_ERROR;
564
+ }
565
+ const mine = await getUserKeys(credential);
566
+ if (!mine.initialized) {
567
+ io.stderr('veilio: this account has no keypair yet. Open the web app once — that is where a ' +
568
+ 'keypair is created and where a teammate grants you the team key.\n');
569
+ return EXIT_ERROR;
570
+ }
571
+ const { wraps } = await getTeamKeyWraps(credential, teamId);
572
+ if (wraps.length === 0) {
573
+ io.stderr('veilio: no teammate has granted you the team key yet. Ask someone already in the ' +
574
+ 'team to open the web app, which is what performs the grant.\n');
575
+ return EXIT_ERROR;
576
+ }
577
+ const vault = await getVault(credential);
578
+ if (!vault.initialized) {
579
+ io.stderr('veilio: this account has no vault, so there is no key to unwrap anything with. ' +
580
+ 'Create one in the web app first.\n');
581
+ return EXIT_ERROR;
582
+ }
583
+ const passphrase = io.password ? await io.password('Vault passphrase: ') : '';
584
+ if (passphrase === '') {
585
+ io.stderr('veilio: no passphrase given\n');
586
+ return EXIT_ERROR;
587
+ }
588
+ const vaultKey = await deriveVaultKey(passphrase, fromBase64(vault.salt), vault.kdf ? { name: 'PBKDF2-SHA256', iterations: vault.kdf.iterations } : undefined);
589
+ // Named as a wrong passphrase here rather than surfacing later as an
590
+ // authentication-tag failure that reads like corruption.
591
+ if (!(await checkVaultVerifier(vaultKey, vault.verifier))) {
592
+ io.stderr('veilio: that vault passphrase is not right. Nothing was written.\n');
593
+ return EXIT_ERROR;
594
+ }
595
+ const privateKey = await unwrapPrivateKey(vaultKey, mine.privateKeyEncrypted);
596
+ const keys = [];
597
+ for (const wrap of wraps) {
598
+ try {
599
+ const teamKey = await unwrapTeamKey(wrap.wrapped_key, privateKey, {
600
+ teamId,
601
+ version: wrap.version,
602
+ myPublicKey: mine.publicKey,
603
+ });
604
+ keys.push({ version: wrap.version, key: await exportTeamKey(teamKey) });
605
+ }
606
+ catch {
607
+ // A wrap this account cannot open — from a granter whose key has since
608
+ // changed, most likely. Skipped rather than fatal: another version may
609
+ // still open most of the team's maps, and none opening is reported below.
610
+ continue;
611
+ }
612
+ }
613
+ if (keys.length === 0) {
614
+ io.stderr('veilio: none of the team key wraps held for this account could be opened. ' +
615
+ 'Ask a teammate to grant access again from the web app.\n');
616
+ return EXIT_ERROR;
617
+ }
618
+ const now = new Date();
619
+ const expiresAt = expiryFrom(now, days);
620
+ writeTeamUnlock({
621
+ v: 1,
622
+ instance: credential.instance,
623
+ account: credential.account,
624
+ teamId,
625
+ expiresAt,
626
+ keys,
627
+ }, io.home);
628
+ const versions = keys.map((k) => `v${k.version}`).join(', ');
629
+ io.stdout(`Unlocked ${keys.length === 1 ? 'the team key' : 'team keys'} (${versions}) until ` +
630
+ `${expiresAt}.\n` +
631
+ `Stored in ${teamUnlockPath(io.home)}, readable only by you. ` +
632
+ `Run \`veilio team lock\` to remove them.\n`);
633
+ return EXIT_OK;
634
+ }
635
+ catch (err) {
636
+ return reportCloudFailure(err, io);
637
+ }
638
+ }
639
+ /** Remove the unlocked team keys. */
640
+ export function runTeamLock(io) {
641
+ try {
642
+ if (!removeTeamUnlock(io.home)) {
643
+ io.stdout('No team keys were unlocked.\n');
644
+ return EXIT_OK;
645
+ }
646
+ }
647
+ catch (err) {
648
+ io.stderr(`veilio: the unlocked team keys could not be removed ` +
649
+ `(${err instanceof Error ? err.message : String(err)}). ` +
650
+ `Delete ${teamUnlockPath(io.home)} by hand.\n`);
651
+ return EXIT_ERROR;
652
+ }
653
+ io.stdout('Team keys locked.\n');
654
+ return EXIT_OK;
655
+ }
394
656
  //# sourceMappingURL=cloud-commands.js.map