@nightlybuildgroup/vault 1.4.0 → 1.6.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/README.md +42 -0
- package/package.json +1 -1
- package/src/bw.js +20 -5
- package/src/cli.js +10 -4
- package/src/commands/add.js +19 -5
- package/src/commands/collections.js +12 -8
- package/src/commands/config.js +60 -0
- package/src/commands/folders.js +12 -8
- package/src/commands/items.js +10 -7
- package/src/commands/organizations.js +39 -0
- package/src/commands/status.js +4 -0
- package/src/config.js +7 -0
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ nbg-pw attach "GitHub" --file ./key.pem # add an attachment; --list/--get/--de
|
|
|
38
38
|
nbg-pw delete "GitHub" # soft-delete to trash (alias: rm; --permanent skips it)
|
|
39
39
|
nbg-pw items --search git # list item names; also: folders, collections
|
|
40
40
|
nbg-pw generate --length 24 --symbols # generate a password (no vault needed)
|
|
41
|
+
nbg-pw config set collection "Shared" # new items share into this org collection
|
|
41
42
|
nbg-pw serve --port 8087 # local bw REST API for fast repeated reads
|
|
42
43
|
nbg-pw status # presence + auth state (no secrets)
|
|
43
44
|
nbg-pw doctor # diagnose bw / Keychain / connectivity
|
|
@@ -110,6 +111,33 @@ for path in $(keepassxc-cli ls -R -f "$DB"); do
|
|
|
110
111
|
done
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
### Sharing new items with an organization (`config`)
|
|
115
|
+
|
|
116
|
+
By default `add` creates items in the API account's **personal** vault, which
|
|
117
|
+
other org members (and your own interactive login) can't see. To make every new
|
|
118
|
+
item land in a shared **organization collection** instead, set a default — it's
|
|
119
|
+
stored in your Keychain, never in this repo, so nothing org-specific is baked
|
|
120
|
+
into the tool:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
nbg-pw config set organization "Acme Inc" # by name or id
|
|
124
|
+
nbg-pw config set collection "Shared" # by name or id
|
|
125
|
+
nbg-pw config show # current defaults
|
|
126
|
+
nbg-pw config unset collection # back to personal-vault default
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Once a default collection is set, every `add` shares the new item into it:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
printf '%s' "$pw" | nbg-pw add "GitHub" --username octocat --password-stdin
|
|
133
|
+
# → Created "GitHub" (…) → shared to "Shared"
|
|
134
|
+
|
|
135
|
+
nbg-pw add "Personal Note" --personal # opt a single item out (personal vault)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
An explicit `--collection`/`--organization` on `add` overrides the default for
|
|
139
|
+
that call. `status` shows the active default share.
|
|
140
|
+
|
|
113
141
|
### Editing items (`edit`)
|
|
114
142
|
|
|
115
143
|
Fetch-merge-write: only the flags you pass are changed; everything else stays.
|
|
@@ -153,8 +181,22 @@ nbg-pw items --search git # filtered by search
|
|
|
153
181
|
nbg-pw items --folder "Agents" # filtered by folder
|
|
154
182
|
nbg-pw folders # folder names
|
|
155
183
|
nbg-pw collections # collection names
|
|
184
|
+
nbg-pw organizations # organization names
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Add `--ids` to any of them to print ids alongside names — useful for `config set`
|
|
188
|
+
or scripting. `collections --ids` also shows each collection's org id:
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
$ nbg-pw organizations --ids
|
|
192
|
+
dfff… Acme Inc
|
|
193
|
+
$ nbg-pw collections --ids
|
|
194
|
+
bf2b… Shared (org dfff…)
|
|
156
195
|
```
|
|
157
196
|
|
|
197
|
+
(You can pass either a **name or an id** to `config set` / `add --collection`, so
|
|
198
|
+
ids are optional — handy mainly when names are ambiguous.)
|
|
199
|
+
|
|
158
200
|
### Generating secrets (`generate`)
|
|
159
201
|
|
|
160
202
|
Pure generator — needs no vault session.
|
package/package.json
CHANGED
package/src/bw.js
CHANGED
|
@@ -144,14 +144,29 @@ export async function listOrganizations({ session }, deps = {}) {
|
|
|
144
144
|
return JSON.parse(stdout);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
export async function sync({ session }, deps = {}) {
|
|
148
|
+
const run = deps.run ?? realRun;
|
|
149
|
+
await run('bw', ['sync'], { env: { BW_SESSION: session } });
|
|
150
|
+
}
|
|
151
|
+
|
|
147
152
|
// Share an existing item into an organization's collection(s). This is bw's only
|
|
148
|
-
// path to put an item in a collection; the item must already exist.
|
|
153
|
+
// path to put an item in a collection; the item must already exist. bw's local
|
|
154
|
+
// cache can lag a just-created item ("client copy ... out of date"); a sync +
|
|
155
|
+
// single retry clears that.
|
|
149
156
|
export async function moveItemToCollection({ itemId, organizationId, collectionIds, session }, deps = {}) {
|
|
150
157
|
const run = deps.run ?? realRun;
|
|
151
|
-
|
|
152
|
-
env: { BW_SESSION: session },
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
const move = () =>
|
|
159
|
+
run('bw', ['move', itemId, organizationId], { env: { BW_SESSION: session }, input: encode(collectionIds) });
|
|
160
|
+
try {
|
|
161
|
+
await move();
|
|
162
|
+
} catch (e) {
|
|
163
|
+
if (/out of date/i.test(e.message || '')) {
|
|
164
|
+
await run('bw', ['sync'], { env: { BW_SESSION: session } });
|
|
165
|
+
await move();
|
|
166
|
+
} else {
|
|
167
|
+
throw e;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
155
170
|
}
|
|
156
171
|
|
|
157
172
|
export async function editItem({ id, item, session }, deps = {}) {
|
package/src/cli.js
CHANGED
|
@@ -10,10 +10,12 @@ Commands:
|
|
|
10
10
|
delete Delete an item or folder (alias: rm; --permanent skips trash)
|
|
11
11
|
attach Manage an item's attachments (--file/--list/--get/--delete)
|
|
12
12
|
list Show an item's field names + types, no values (nbg-pw list "My Visa")
|
|
13
|
-
items List item names (--search, --folder, --collection)
|
|
14
|
-
folders List folder names
|
|
15
|
-
collections List collection names
|
|
13
|
+
items List item names (--search, --folder, --collection; --ids)
|
|
14
|
+
folders List folder names (--ids)
|
|
15
|
+
collections List collection names (--ids shows collection + org ids)
|
|
16
|
+
organizations List organization names (--ids)
|
|
16
17
|
generate Generate a password or passphrase (bw generate)
|
|
18
|
+
config Show/set defaults, e.g. the org collection new items share into
|
|
17
19
|
serve Start a local bw API daemon (unlocked) for fast repeated reads
|
|
18
20
|
status Show config + auth state (no secret values)
|
|
19
21
|
doctor Diagnose bw / Keychain / server connectivity (--fix resets a wedged bw session)
|
|
@@ -54,7 +56,7 @@ export async function runCli(argv, deps = {}) {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
async function loadCommands() {
|
|
57
|
-
const [setup, get, add, edit, del, attach, list, items, folders, collections, generate, status, doctor, serve, reset] =
|
|
59
|
+
const [setup, get, add, edit, del, attach, list, items, folders, collections, organizations, generate, config, status, doctor, serve, reset] =
|
|
58
60
|
await Promise.all([
|
|
59
61
|
import('./commands/setup.js'),
|
|
60
62
|
import('./commands/get.js'),
|
|
@@ -66,7 +68,9 @@ async function loadCommands() {
|
|
|
66
68
|
import('./commands/items.js'),
|
|
67
69
|
import('./commands/folders.js'),
|
|
68
70
|
import('./commands/collections.js'),
|
|
71
|
+
import('./commands/organizations.js'),
|
|
69
72
|
import('./commands/generate.js'),
|
|
73
|
+
import('./commands/config.js'),
|
|
70
74
|
import('./commands/status.js'),
|
|
71
75
|
import('./commands/doctor.js'),
|
|
72
76
|
import('./commands/serve.js'),
|
|
@@ -84,7 +88,9 @@ async function loadCommands() {
|
|
|
84
88
|
items: items.default,
|
|
85
89
|
folders: folders.default,
|
|
86
90
|
collections: collections.default,
|
|
91
|
+
organizations: organizations.default,
|
|
87
92
|
generate: generate.default,
|
|
93
|
+
config: config.default,
|
|
88
94
|
status: status.default,
|
|
89
95
|
doctor: doctor.default,
|
|
90
96
|
serve: serve.default,
|
package/src/commands/add.js
CHANGED
|
@@ -5,7 +5,7 @@ const USAGE =
|
|
|
5
5
|
'usage: nbg-pw add <name> [--username <u>] [--url <uri>]... [--notes <n>] ' +
|
|
6
6
|
'[--folder <name>] [--collection <name>] [--organization <name|id>] ' +
|
|
7
7
|
'[--field <name>=<value>]... [--field-hidden <name>=<value>]... [--totp <secret>] ' +
|
|
8
|
-
'[--password-stdin]\n or: nbg-pw add --json (reads one entry as a JSON object on stdin)';
|
|
8
|
+
'[--password-stdin] [--personal]\n or: nbg-pw add --json (reads one entry as a JSON object on stdin)';
|
|
9
9
|
|
|
10
10
|
// `--field name=value` → { name, value, type }. Splits on the FIRST `=` so the
|
|
11
11
|
// value may itself contain `=`.
|
|
@@ -16,7 +16,7 @@ function parseField(raw, type) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function parseAddArgs(args) {
|
|
19
|
-
const opts = { uris: [], fields: [], passwordStdin: false, json: false };
|
|
19
|
+
const opts = { uris: [], fields: [], passwordStdin: false, json: false, personal: false };
|
|
20
20
|
let name = null;
|
|
21
21
|
for (let i = 0; i < args.length; i++) {
|
|
22
22
|
const a = args[i];
|
|
@@ -27,6 +27,7 @@ export function parseAddArgs(args) {
|
|
|
27
27
|
};
|
|
28
28
|
switch (a) {
|
|
29
29
|
case '--json': opts.json = true; break;
|
|
30
|
+
case '--personal': opts.personal = true; break;
|
|
30
31
|
case '--password-stdin': opts.passwordStdin = true; break;
|
|
31
32
|
case '--username': opts.username = need(); break;
|
|
32
33
|
case '--url': opts.uris.push(need()); break;
|
|
@@ -122,23 +123,36 @@ export async function runAdd(args, deps) {
|
|
|
122
123
|
if (opts.passwordStdin) entry.password = (await readStdin()).replace(/\r?\n$/, '');
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
// Where the item lands: an explicit --collection wins; otherwise the
|
|
127
|
+
// configured default shares every new item into the org collection, unless
|
|
128
|
+
// --personal (or a json `personal:true`) opts out into the personal vault.
|
|
129
|
+
const personal = opts.personal || entry.personal;
|
|
130
|
+
let collectionName = entry.collection;
|
|
131
|
+
let organizationName = entry.organization;
|
|
132
|
+
if (!personal && !collectionName && config.defaultCollection) {
|
|
133
|
+
collectionName = config.defaultCollection;
|
|
134
|
+
organizationName = organizationName ?? config.defaultOrganization;
|
|
135
|
+
}
|
|
136
|
+
|
|
125
137
|
const session = await bw.ensureSession(config);
|
|
126
138
|
|
|
127
139
|
if (entry.folder) entry.folderId = await resolveFolderId(bw, session, entry.folder);
|
|
128
140
|
|
|
129
141
|
const created = await bw.createItem({ item: buildLoginItem(entry), session });
|
|
130
142
|
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
let shared = '';
|
|
144
|
+
if (collectionName) {
|
|
145
|
+
const col = await resolveCollection(bw, session, collectionName, organizationName);
|
|
133
146
|
await bw.moveItemToCollection({
|
|
134
147
|
itemId: created.id,
|
|
135
148
|
organizationId: col.organizationId,
|
|
136
149
|
collectionIds: [col.id],
|
|
137
150
|
session,
|
|
138
151
|
});
|
|
152
|
+
shared = ` → shared to "${col.name}"`;
|
|
139
153
|
}
|
|
140
154
|
|
|
141
|
-
out(`Created "${created.name}" (${created.id})\n`);
|
|
155
|
+
out(`Created "${created.name}" (${created.id})${shared}\n`);
|
|
142
156
|
return 0;
|
|
143
157
|
}
|
|
144
158
|
|
|
@@ -2,13 +2,17 @@ import * as bwModule from '../bw.js';
|
|
|
2
2
|
import * as keychainModule from '../keychain.js';
|
|
3
3
|
|
|
4
4
|
export function parseCollectionsArgs(args) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let ids = false;
|
|
6
|
+
for (const a of args) {
|
|
7
|
+
if (a === '--ids') ids = true;
|
|
8
|
+
else throw new Error(`unexpected argument: ${a}`);
|
|
9
|
+
}
|
|
10
|
+
return { ids };
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export async function runCollections(args, deps) {
|
|
10
14
|
const { keychain, bw, out, err } = deps;
|
|
11
|
-
parseCollectionsArgs(args);
|
|
15
|
+
const { ids } = parseCollectionsArgs(args);
|
|
12
16
|
|
|
13
17
|
const config = await keychain.readConfig();
|
|
14
18
|
if (!config) {
|
|
@@ -17,11 +21,11 @@ export async function runCollections(args, deps) {
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const session = await bw.ensureSession(config);
|
|
20
|
-
const collections = await bw.listCollections({ session })
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const collections = (await bw.listCollections({ session }))
|
|
25
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
26
|
+
for (const c of collections) {
|
|
27
|
+
out(ids ? `${c.id} ${c.name} (org ${c.organizationId})\n` : c.name + '\n');
|
|
28
|
+
}
|
|
25
29
|
return 0;
|
|
26
30
|
}
|
|
27
31
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as keychainModule from '../keychain.js';
|
|
2
|
+
|
|
3
|
+
// User-facing key → stored config field.
|
|
4
|
+
const KEYS = { organization: 'defaultOrganization', collection: 'defaultCollection' };
|
|
5
|
+
|
|
6
|
+
const USAGE = 'usage: nbg-pw config [show | set <organization|collection> <value> | unset <organization|collection>]';
|
|
7
|
+
|
|
8
|
+
export function parseConfigArgs(args) {
|
|
9
|
+
const [sub, ...rest] = args;
|
|
10
|
+
if (!sub || sub === 'show') return { action: 'show' };
|
|
11
|
+
if (sub === 'set') {
|
|
12
|
+
const [key, value] = rest;
|
|
13
|
+
if (!key || !(key in KEYS)) throw new Error(USAGE);
|
|
14
|
+
if (value === undefined) throw new Error('config set requires a value');
|
|
15
|
+
return { action: 'set', key, value };
|
|
16
|
+
}
|
|
17
|
+
if (sub === 'unset') {
|
|
18
|
+
const [key] = rest;
|
|
19
|
+
if (!key || !(key in KEYS)) throw new Error(USAGE);
|
|
20
|
+
return { action: 'unset', key };
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`unknown config subcommand "${sub}"\n${USAGE}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function runConfig(args, deps) {
|
|
26
|
+
const { keychain, out, err } = deps;
|
|
27
|
+
const opts = parseConfigArgs(args);
|
|
28
|
+
|
|
29
|
+
const config = await keychain.readConfig();
|
|
30
|
+
if (!config) {
|
|
31
|
+
err('No stored credentials. Run "nbg-pw setup" first.\n');
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (opts.action === 'show') {
|
|
36
|
+
out(`default organization: ${config.defaultOrganization ?? '(not set)'}\n`);
|
|
37
|
+
out(`default collection: ${config.defaultCollection ?? '(not set)'}\n`);
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const field = KEYS[opts.key];
|
|
42
|
+
if (opts.action === 'set') {
|
|
43
|
+
config[field] = opts.value;
|
|
44
|
+
await keychain.writeConfig(config);
|
|
45
|
+
out(`Set default ${opts.key} = ${opts.value}\n`);
|
|
46
|
+
} else {
|
|
47
|
+
delete config[field];
|
|
48
|
+
await keychain.writeConfig(config);
|
|
49
|
+
out(`Unset default ${opts.key}\n`);
|
|
50
|
+
}
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default async function config(args) {
|
|
55
|
+
return runConfig(args, {
|
|
56
|
+
keychain: keychainModule,
|
|
57
|
+
out: (s) => process.stdout.write(s),
|
|
58
|
+
err: (s) => process.stderr.write(s),
|
|
59
|
+
});
|
|
60
|
+
}
|
package/src/commands/folders.js
CHANGED
|
@@ -2,13 +2,17 @@ import * as bwModule from '../bw.js';
|
|
|
2
2
|
import * as keychainModule from '../keychain.js';
|
|
3
3
|
|
|
4
4
|
export function parseFoldersArgs(args) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let ids = false;
|
|
6
|
+
for (const a of args) {
|
|
7
|
+
if (a === '--ids') ids = true;
|
|
8
|
+
else throw new Error(`unexpected argument: ${a}`);
|
|
9
|
+
}
|
|
10
|
+
return { ids };
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export async function runFolders(args, deps) {
|
|
10
14
|
const { keychain, bw, out, err } = deps;
|
|
11
|
-
parseFoldersArgs(args);
|
|
15
|
+
const { ids } = parseFoldersArgs(args);
|
|
12
16
|
|
|
13
17
|
const config = await keychain.readConfig();
|
|
14
18
|
if (!config) {
|
|
@@ -17,11 +21,11 @@ export async function runFolders(args, deps) {
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const session = await bw.ensureSession(config);
|
|
20
|
-
const folders = await bw.listFolders({ session })
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const folders = (await bw.listFolders({ session }))
|
|
25
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
26
|
+
for (const f of folders) {
|
|
27
|
+
out(ids ? `${f.id ?? '-'} ${f.name}\n` : f.name + '\n');
|
|
28
|
+
}
|
|
25
29
|
return 0;
|
|
26
30
|
}
|
|
27
31
|
|
package/src/commands/items.js
CHANGED
|
@@ -5,6 +5,7 @@ export function parseItemsArgs(args) {
|
|
|
5
5
|
let search = null;
|
|
6
6
|
let folder = null;
|
|
7
7
|
let collection = null;
|
|
8
|
+
let ids = false;
|
|
8
9
|
for (let i = 0; i < args.length; i++) {
|
|
9
10
|
if (args[i] === '--search') {
|
|
10
11
|
const v = args[++i];
|
|
@@ -18,16 +19,18 @@ export function parseItemsArgs(args) {
|
|
|
18
19
|
const v = args[++i];
|
|
19
20
|
if (v === undefined) throw new Error('--collection requires a value');
|
|
20
21
|
collection = v;
|
|
22
|
+
} else if (args[i] === '--ids') {
|
|
23
|
+
ids = true;
|
|
21
24
|
} else {
|
|
22
25
|
throw new Error(`unknown option: ${args[i]}`);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
|
-
return { search, folder, collection };
|
|
28
|
+
return { search, folder, collection, ids };
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export async function runItems(args, deps) {
|
|
29
32
|
const { keychain, bw, out, err } = deps;
|
|
30
|
-
const { search, folder, collection } = parseItemsArgs(args);
|
|
33
|
+
const { search, folder, collection, ids } = parseItemsArgs(args);
|
|
31
34
|
|
|
32
35
|
const config = await keychain.readConfig();
|
|
33
36
|
if (!config) {
|
|
@@ -58,11 +61,11 @@ export async function runItems(args, deps) {
|
|
|
58
61
|
if (folderId !== undefined) filters.folderId = folderId;
|
|
59
62
|
if (collectionId !== undefined) filters.collectionId = collectionId;
|
|
60
63
|
|
|
61
|
-
const items = await bw.listItems(filters)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
const items = (await bw.listItems(filters))
|
|
65
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
66
|
+
for (const i of items) {
|
|
67
|
+
out(ids ? `${i.id} ${i.name}\n` : i.name + '\n');
|
|
68
|
+
}
|
|
66
69
|
return 0;
|
|
67
70
|
}
|
|
68
71
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as bwModule from '../bw.js';
|
|
2
|
+
import * as keychainModule from '../keychain.js';
|
|
3
|
+
|
|
4
|
+
export function parseOrganizationsArgs(args) {
|
|
5
|
+
let ids = false;
|
|
6
|
+
for (const a of args) {
|
|
7
|
+
if (a === '--ids') ids = true;
|
|
8
|
+
else throw new Error(`unexpected argument: ${a}`);
|
|
9
|
+
}
|
|
10
|
+
return { ids };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function runOrganizations(args, deps) {
|
|
14
|
+
const { keychain, bw, out, err } = deps;
|
|
15
|
+
const { ids } = parseOrganizationsArgs(args);
|
|
16
|
+
|
|
17
|
+
const config = await keychain.readConfig();
|
|
18
|
+
if (!config) {
|
|
19
|
+
err('No stored credentials. Run "nbg-pw setup" first.\n');
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const session = await bw.ensureSession(config);
|
|
24
|
+
const orgs = (await bw.listOrganizations({ session }))
|
|
25
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
26
|
+
for (const o of orgs) {
|
|
27
|
+
out(ids ? `${o.id} ${o.name}\n` : o.name + '\n');
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default async function organizations(args) {
|
|
33
|
+
return runOrganizations(args, {
|
|
34
|
+
keychain: keychainModule,
|
|
35
|
+
bw: bwModule,
|
|
36
|
+
out: (s) => process.stdout.write(s),
|
|
37
|
+
err: (s) => process.stderr.write(s),
|
|
38
|
+
});
|
|
39
|
+
}
|
package/src/commands/status.js
CHANGED
|
@@ -12,6 +12,10 @@ export async function runStatus(args, deps) {
|
|
|
12
12
|
out(` stored creds: yes\n`);
|
|
13
13
|
out(` server URL: ${config.serverUrl}\n`);
|
|
14
14
|
out(` email: ${config.email}\n`);
|
|
15
|
+
if (config.defaultCollection) {
|
|
16
|
+
const org = config.defaultOrganization ? `${config.defaultOrganization} / ` : '';
|
|
17
|
+
out(` default share: ${org}${config.defaultCollection}\n`);
|
|
18
|
+
}
|
|
15
19
|
} else {
|
|
16
20
|
out(' stored creds: No credentials stored (run "nbg-pw setup")\n');
|
|
17
21
|
}
|
package/src/config.js
CHANGED
|
@@ -2,6 +2,8 @@ export const SERVICE = 'com.nightlybuild.vault';
|
|
|
2
2
|
export const ACCOUNT = 'default';
|
|
3
3
|
export const FIELDS = ['serverUrl', 'email', 'clientId', 'clientSecret', 'masterPassword'];
|
|
4
4
|
export const READ_FIELDS = [...FIELDS, 'savedAt'];
|
|
5
|
+
// Optional: when set, `add` shares new items into this org collection by default.
|
|
6
|
+
export const OPTIONAL_FIELDS = ['defaultOrganization', 'defaultCollection'];
|
|
5
7
|
|
|
6
8
|
export function isValidUrl(s) {
|
|
7
9
|
if (typeof s !== 'string' || s.length === 0) return false;
|
|
@@ -26,6 +28,11 @@ export function validateConfig(obj) {
|
|
|
26
28
|
}
|
|
27
29
|
if (!isValidUrl(obj.serverUrl)) throw new Error('config serverUrl is not a valid URL');
|
|
28
30
|
if (!isValidEmail(obj.email)) throw new Error('config email is not a valid email');
|
|
31
|
+
for (const f of OPTIONAL_FIELDS) {
|
|
32
|
+
if (obj[f] !== undefined && (typeof obj[f] !== 'string' || obj[f].length === 0)) {
|
|
33
|
+
throw new Error(`config field ${f} must be a non-empty string when set`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
29
36
|
return obj;
|
|
30
37
|
}
|
|
31
38
|
|