@nacre.work/cli 0.14.3

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.
@@ -0,0 +1,540 @@
1
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
2
+ import { basename, join, relative, sep } from 'node:path';
3
+ import { createInterface } from 'node:readline/promises';
4
+ import { NacreClient } from '@nacre.work/sdk';
5
+ import { flag, integer, option, UsageError } from './args.js';
6
+ import { loadSession, saveSession } from './config.js';
7
+ const UNAUTHENTICATED = 'unauthenticated';
8
+ /**
9
+ * The one message for "there is no session".
10
+ *
11
+ * It names both ways in, because the two readers want different halves: on a
12
+ * laptop it is `nacre login`, and in CI it is the variables, where there is no
13
+ * terminal to log in from and nowhere to write a config file that survives.
14
+ */
15
+ const NO_SESSION = 'Not signed in. Run `nacre login --url https://your-installation` — or set ' +
16
+ 'NACRE_API_URL and NACRE_TOKEN, which is what a service account in CI uses.';
17
+ function requireSession(context) {
18
+ const session = loadSession(context.env);
19
+ if (session === undefined)
20
+ throw new Error(NO_SESSION);
21
+ return session;
22
+ }
23
+ function requireClient(context) {
24
+ return context.clientFor(requireSession(context));
25
+ }
26
+ /** `--json` prints the object; otherwise the caller's own rendering. */
27
+ function render(context, value, human) {
28
+ return flag(context.parsed, 'json') ? JSON.stringify(value, null, 2) : human();
29
+ }
30
+ export async function login(context) {
31
+ const url = option(context.parsed, 'url') ?? context.env.NACRE_API_URL;
32
+ if (url === undefined) {
33
+ throw new UsageError('nacre login --url <https://your-installation>');
34
+ }
35
+ const email = option(context.parsed, 'email') ?? (await context.prompt('Email: ', false));
36
+ const password = await context.prompt('Password: ', true);
37
+ const organization = option(context.parsed, 'org');
38
+ const bare = context.clientFor({ baseUrl: url, token: UNAUTHENTICATED });
39
+ const tokens = await bare.auth.login({
40
+ email,
41
+ password,
42
+ ...(organization === undefined ? {} : { organization }),
43
+ });
44
+ // `undefined` is a refused sign-in, and it does not say which of the several
45
+ // reasons applied — that is invariant 4 reaching the login endpoint, and the
46
+ // CLI must not invent a more helpful message than the server was willing to
47
+ // give.
48
+ if (tokens === undefined)
49
+ throw new Error('Sign-in refused.');
50
+ const path = saveSession({ baseUrl: url, token: tokens.accessToken, refreshToken: tokens.refreshToken }, context.env);
51
+ const who = await context.clientFor({ baseUrl: url, token: tokens.accessToken }).me();
52
+ return `Signed in to ${url} as ${who.principalId} (${who.role}) in ${who.organization}.\nSession written to ${path}, readable only by you.`;
53
+ }
54
+ export async function whoami(context) {
55
+ const session = requireSession(context);
56
+ const self = await context.clientFor(session).me();
57
+ return render(context, self, () => [
58
+ `endpoint ${session.baseUrl}`,
59
+ `organization ${self.organization}`,
60
+ `principal ${self.principalId} (${self.principalType})`,
61
+ `role ${self.role}`,
62
+ ].join('\n'));
63
+ }
64
+ export async function layers(context) {
65
+ const client = requireClient(context);
66
+ const [verb] = context.parsed.positional.slice(1);
67
+ if (verb === 'create')
68
+ return createLayer(context, client);
69
+ if (verb !== undefined)
70
+ throw new UsageError(`Unknown: nacre layers ${verb}`);
71
+ const list = await client.layers.list();
72
+ return render(context, list, () => {
73
+ // Not an error and not an empty table: a caller with no readable layer is
74
+ // the ordinary state of a member nobody has granted anything yet, and the
75
+ // useful thing to say is what to do about it.
76
+ if (list.length === 0)
77
+ return 'No layers you can read. An org_admin grants access with `nacre grant`.';
78
+ const width = Math.max(...list.map((layer) => layer.slug.length));
79
+ return list
80
+ .map((layer) => {
81
+ const failed = layer.failedCount > 0 ? `, ${layer.failedCount} failed` : '';
82
+ return `${layer.slug.padEnd(width)} ${layer.documentCount} docs${failed} ${layer.name}`;
83
+ })
84
+ .join('\n');
85
+ });
86
+ }
87
+ async function createLayer(context, client) {
88
+ const slug = context.parsed.positional[2];
89
+ if (slug === undefined)
90
+ throw new UsageError('nacre layers create <slug> [--name ...] [--workspace <slug>]');
91
+ const wanted = option(context.parsed, 'workspace');
92
+ const workspaces = await client.workspaces.list();
93
+ // The gap this closes: creating a layer takes a workspace id, and the only
94
+ // way to have one used to be the line `init` printed. Naming one is optional
95
+ // here because almost every installation has exactly one — and where there
96
+ // are several, guessing is refused rather than resolved by picking the first.
97
+ const workspace = wanted === undefined
98
+ ? workspaces.length === 1
99
+ ? workspaces[0]
100
+ : undefined
101
+ : workspaces.find((candidate) => candidate.slug === wanted);
102
+ if (workspace === undefined) {
103
+ const known = workspaces.map((candidate) => candidate.slug).join(', ');
104
+ throw new UsageError(wanted === undefined
105
+ ? `This organization has ${workspaces.length} workspaces, so --workspace is required: ${known}`
106
+ : `No workspace ${JSON.stringify(wanted)} you can reach. Yours: ${known || 'none'}`);
107
+ }
108
+ const created = await client.layers.create({
109
+ workspaceId: workspace.id,
110
+ slug,
111
+ name: option(context.parsed, 'name') ?? slug,
112
+ ...(option(context.parsed, 'description') === undefined
113
+ ? {}
114
+ : { description: option(context.parsed, 'description') }),
115
+ ...(option(context.parsed, 'provider') === undefined
116
+ ? {}
117
+ : { providerId: option(context.parsed, 'provider') }),
118
+ });
119
+ // A `404` here is the workspace being invisible or the caller not holding
120
+ // admin on it, and those are deliberately the same answer.
121
+ if (created === undefined)
122
+ throw new Error(`Refused: no workspace ${workspace.slug} you may create a layer in.`);
123
+ return render(context, created, () => `Created layer ${created.slug} in ${workspace.slug}.`);
124
+ }
125
+ export async function grant(context) {
126
+ const client = requireClient(context);
127
+ const [, permission, scope] = context.parsed.positional;
128
+ const to = option(context.parsed, 'to');
129
+ if (permission === undefined || scope === undefined || to === undefined) {
130
+ throw new UsageError('nacre grant <read|write|admin> <layer:slug|workspace:slug|document:id> --to <user|group|service_account>:<id>');
131
+ }
132
+ if (permission !== 'read' && permission !== 'write' && permission !== 'admin') {
133
+ throw new UsageError(`Permission is read, write or admin — not ${JSON.stringify(permission)}.`);
134
+ }
135
+ const [scopeType, scopeRef] = split(scope, 'scope', ['workspace', 'layer', 'document']);
136
+ const [principalType, principalId] = split(to, '--to', ['user', 'group', 'service_account']);
137
+ // A slug is what a person has and an id is what the API takes, for the two
138
+ // scope types that have slugs. Resolving it here rather than making the
139
+ // reader run a listing first is most of why this command exists.
140
+ const scopeId = await resolveScope(client, scopeType, scopeRef);
141
+ const issued = await client.grants.issue({
142
+ principalType: principalType,
143
+ principalId,
144
+ scopeType: scopeType,
145
+ scopeId,
146
+ permission,
147
+ });
148
+ if (issued === undefined) {
149
+ throw new Error(`Refused: no ${scopeType} ${JSON.stringify(scopeRef)} you hold admin on. ` +
150
+ 'Not finding it and not being allowed to grant on it are the same answer here.');
151
+ }
152
+ return render(context, issued, () => `Granted ${permission} on ${scope} to ${to}.`);
153
+ }
154
+ function split(value, what, allowed) {
155
+ const at = value.indexOf(':');
156
+ const type = at === -1 ? '' : value.slice(0, at);
157
+ const rest = at === -1 ? '' : value.slice(at + 1);
158
+ if (!allowed.includes(type) || rest === '') {
159
+ throw new UsageError(`${what} is <${allowed.join('|')}>:<slug or id>, not ${JSON.stringify(value)}`);
160
+ }
161
+ return [type, rest];
162
+ }
163
+ async function resolveScope(client, type, ref) {
164
+ if (type === 'document')
165
+ return ref;
166
+ if (type === 'layer') {
167
+ const found = (await client.layers.list()).find((layer) => layer.slug === ref || layer.id === ref);
168
+ if (found === undefined)
169
+ throw new Error(`No layer ${JSON.stringify(ref)} you can see.`);
170
+ return found.id;
171
+ }
172
+ const found = (await client.workspaces.list()).find((one) => one.slug === ref || one.id === ref);
173
+ if (found === undefined)
174
+ throw new Error(`No workspace ${JSON.stringify(ref)} you can see.`);
175
+ return found.id;
176
+ }
177
+ /**
178
+ * Whether a path is one this command would have ingested.
179
+ *
180
+ * Split out because the watcher needs the same answer for a single path that
181
+ * `collect` reaches by walking, and two spellings of "is this a file we index"
182
+ * is how a watcher comes to index a `.git` object nobody asked for.
183
+ */
184
+ export function eligible(path) {
185
+ const parts = path.split(sep);
186
+ if (parts.includes('.git') || parts.includes('node_modules'))
187
+ return false;
188
+ return TEXT.has(extensionOf(basename(path)));
189
+ }
190
+ /** Extensions read as text. Everything else is refused rather than mangled. */
191
+ const TEXT = new Set(['.md', '.markdown', '.txt', '.rst', '.adoc', '.csv', '.json', '.yaml', '.yml', '.html']);
192
+ export function collect(target) {
193
+ const stats = statSync(target);
194
+ if (stats.isFile()) {
195
+ return [{ path: target, externalId: basename(target) }];
196
+ }
197
+ const found = [];
198
+ const walk = (dir) => {
199
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
200
+ // Not a general ignore file: the two that matter are a checkout's own
201
+ // metadata and an installed dependency tree, and both are large enough
202
+ // that indexing them by accident is the difference between a minute and
203
+ // an afternoon.
204
+ if (entry.name === '.git' || entry.name === 'node_modules')
205
+ continue;
206
+ const path = join(dir, entry.name);
207
+ if (entry.isDirectory())
208
+ walk(path);
209
+ else if (TEXT.has(extensionOf(entry.name))) {
210
+ // Relative and slash-separated, so the same tree ingested from Windows
211
+ // and from Linux produces the same external ids — and ingest is
212
+ // idempotent on (layer, external id), so getting that wrong means a
213
+ // second copy of every document rather than an error.
214
+ found.push({ path, externalId: relative(target, path).split(sep).join('/') });
215
+ }
216
+ }
217
+ };
218
+ walk(target);
219
+ return found.sort((a, b) => a.externalId.localeCompare(b.externalId));
220
+ }
221
+ function extensionOf(name) {
222
+ const dot = name.lastIndexOf('.');
223
+ return dot === -1 ? '' : name.slice(dot).toLowerCase();
224
+ }
225
+ export async function ingest(context) {
226
+ const client = requireClient(context);
227
+ const layer = option(context.parsed, 'layer');
228
+ const targets = context.parsed.positional.slice(1);
229
+ if (layer === undefined || targets.length === 0) {
230
+ throw new UsageError('nacre ingest <file or directory>... --layer <slug>');
231
+ }
232
+ const files = targets.flatMap((target) => collect(target));
233
+ if (files.length === 0) {
234
+ throw new UsageError(`Nothing to ingest: no text files under ${targets.join(', ')}. ` +
235
+ `Read as text: ${[...TEXT].sort().join(' ')}. A PDF goes through POST /v1/documents as multipart, which this command does not do yet.`);
236
+ }
237
+ // Submitted in order and waited on afterwards, rather than each one driven to
238
+ // `indexed` before the next is sent. The worker is the thing that scales out
239
+ // here, so serialising the whole pipeline behind one client would leave every
240
+ // replica but one idle — and sending them all at once is how a directory of a
241
+ // thousand files meets the ingest rate limit as a wall of 429s.
242
+ const queued = [];
243
+ let unchanged = 0;
244
+ for (const file of files) {
245
+ const outcome = await client.documents.add({
246
+ layer,
247
+ externalId: file.externalId,
248
+ title: basename(file.path),
249
+ content: readFileSync(file.path, 'utf8'),
250
+ });
251
+ if (outcome.unchanged)
252
+ unchanged += 1;
253
+ else
254
+ queued.push({ externalId: file.externalId, jobId: outcome.jobId });
255
+ }
256
+ const results = await Promise.all(queued.map(async (item) => ({ ...item, job: await client.jobs.wait(item.jobId) })));
257
+ const failed = results.filter((item) => item.job?.status !== 'indexed');
258
+ const output = render(context, { indexed: results.length - failed.length, unchanged, failed: failed.map(describe) }, () => {
259
+ const lines = [
260
+ `${results.length - failed.length} indexed, ${unchanged} unchanged, ${failed.length} failed.`,
261
+ ];
262
+ // Every failure by name. A count alone is the version of this that gets
263
+ // ignored, and the reason is on the job rather than in a log the person
264
+ // running a CLI has no access to.
265
+ for (const item of failed)
266
+ lines.push(` ${item.externalId}: ${reasonOf(item.job)}`);
267
+ return lines.join('\n');
268
+ });
269
+ if (flag(context.parsed, 'watch')) {
270
+ const directories = targets.filter((target) => statSync(target).isDirectory());
271
+ if (directories.length === 0) {
272
+ throw new UsageError('--watch needs a directory to watch, not only files');
273
+ }
274
+ return watch(context, client, layer, directories, output);
275
+ }
276
+ // **A failed document is a non-zero exit**, and finding that out is what
277
+ // running this against a real stack was for: a misconfigured embedding
278
+ // provider failed every document in the directory, the summary said `2
279
+ // failed`, and the process exited 0. A pipeline that ingests a corpus
280
+ // nightly and checks the exit code would have reported success for weeks
281
+ // while the index stayed empty — which is the shape of defect this
282
+ // repository has found more often than any other.
283
+ //
284
+ // The summary still goes to stdout: it is the answer, and a script needs to
285
+ // read which documents failed rather than only that some did.
286
+ return failed.length === 0 ? output : { output, code: 1 };
287
+ }
288
+ /**
289
+ * Keep ingesting a directory as it changes.
290
+ *
291
+ * The gap between "no connectors" — which is the right decision and stays — and
292
+ * "write your own ingest", which is what everybody trying this actually has to
293
+ * do first. It is not a connector: no Confluence, no Drive, no credentials for
294
+ * somebody else's system. It is the directory already on the machine.
295
+ *
296
+ * **It never deletes.** A file disappearing looks identical to the first half
297
+ * of how every editor saves — write a temporary file, rename it over the
298
+ * original — so a watcher that removed a document on an unlink event would
299
+ * delete documents on save, intermittently, depending on the editor. Removing a
300
+ * document is `DELETE /v1/documents/{id}`, deliberately by hand.
301
+ *
302
+ * Re-ingest of unchanged bytes is free: ingest is idempotent on
303
+ * `(layer, external_id)` plus the content hash, so a save that changes nothing
304
+ * costs one request and no embedding.
305
+ */
306
+ async function watch(context, client, layer, directories, first) {
307
+ const { watch: watchDirectory } = await import('node:fs');
308
+ const lines = [first, `Watching ${directories.join(', ')} for changes. Ctrl-C to stop.`];
309
+ // Editors emit several events for one save — the temporary file, the rename,
310
+ // a metadata touch — so a pass is scheduled rather than run per event, and a
311
+ // second event inside the window joins the pass already coming.
312
+ const pending = new Set();
313
+ let timer;
314
+ const flush = async () => {
315
+ timer = undefined;
316
+ const batch = [...pending];
317
+ pending.clear();
318
+ for (const path of batch) {
319
+ const target = directories.find((dir) => path.startsWith(dir + sep) || path.startsWith(dir));
320
+ if (target === undefined)
321
+ continue;
322
+ try {
323
+ const outcome = await client.documents.add({
324
+ layer,
325
+ externalId: relative(target, path).split(sep).join('/'),
326
+ title: basename(path),
327
+ content: readFileSync(path, 'utf8'),
328
+ });
329
+ if (outcome.unchanged)
330
+ continue;
331
+ const job = await client.jobs.wait(outcome.jobId);
332
+ process.stderr.write(`${job?.status === 'indexed' ? 'indexed' : `failed: ${reasonOf(job)}`} ${relative(target, path)}\n`);
333
+ }
334
+ catch (error) {
335
+ // A file removed between the event and the read is the ordinary case,
336
+ // and so is a partially written one. Neither is worth stopping a
337
+ // watcher that is meant to run all day.
338
+ process.stderr.write(`skipped ${path}: ${error instanceof Error ? error.message : String(error)}\n`);
339
+ }
340
+ }
341
+ };
342
+ for (const directory of directories) {
343
+ watchDirectory(directory, { recursive: true }, (_event, name) => {
344
+ if (name === null)
345
+ return;
346
+ const path = join(directory, name.toString());
347
+ if (!eligible(path))
348
+ return;
349
+ pending.add(path);
350
+ if (timer === undefined)
351
+ timer = setTimeout(() => void flush(), 300);
352
+ });
353
+ }
354
+ // Progress goes to stderr above, so this resolves only on Ctrl-C and the
355
+ // summary of the first pass stays the thing on stdout.
356
+ await new Promise((resolve) => {
357
+ process.on('SIGINT', () => resolve());
358
+ process.on('SIGTERM', () => resolve());
359
+ });
360
+ return lines.join('\n');
361
+ }
362
+ const describe = (item) => ({
363
+ externalId: item.externalId,
364
+ status: item.job?.status ?? 'unknown',
365
+ error: item.job?.error ?? null,
366
+ });
367
+ const reasonOf = (job) => job === undefined ? 'the job disappeared' : (job.error ?? job.status);
368
+ export async function search(context) {
369
+ const client = requireClient(context);
370
+ const query = context.parsed.positional.slice(1).join(' ');
371
+ if (query === '')
372
+ throw new UsageError('nacre search <query> [--layer <slug>] [--top-k <n>]');
373
+ const layer = option(context.parsed, 'layer');
374
+ const hits = await client.search(query, {
375
+ ...(integer(context.parsed, 'top-k') === undefined ? {} : { topK: integer(context.parsed, 'top-k') }),
376
+ ...(layer === undefined ? {} : { layers: [layer] }),
377
+ });
378
+ return render(context, hits, () => {
379
+ // An empty result is an answer, not an error: it is what a caller with no
380
+ // grant on anything matching sees, and it is indistinguishable from there
381
+ // being nothing to match. Saying so is invariant 4 in a terminal.
382
+ if (hits.length === 0)
383
+ return 'No results you can see.';
384
+ return hits.map(one).join('\n\n');
385
+ });
386
+ }
387
+ /**
388
+ * Score a layer's reference query set, from outside the worker.
389
+ *
390
+ * The same measurement the reindex gate makes and for a different purpose. That
391
+ * one asks "can the new model still answer?" at a moment nobody chose; this
392
+ * asks "how is retrieval doing?" at a moment somebody did — before and after a
393
+ * chunking change, a reranker being switched on, a corpus growing by a
394
+ * quarter. Until now the answer to "what is your recall?" was a number that
395
+ * existed only inside a migration nobody was running.
396
+ *
397
+ * **Recall@k against documents the deployment picked**, averaged per query,
398
+ * which is the core's definition and deliberately not agreement with a previous
399
+ * model — a better model disagrees with the worse one it replaces.
400
+ *
401
+ * It is a read-only measurement: nothing here writes a verdict, moves a layer
402
+ * or touches the index. A number leaves, and that is all.
403
+ */
404
+ export async function evaluate(context) {
405
+ const client = requireClient(context);
406
+ const slug = option(context.parsed, 'layer');
407
+ if (slug === undefined)
408
+ throw new UsageError('nacre eval --layer <slug> [--top-k <n>] [--floor <0..1>]');
409
+ const layer = (await client.layers.list()).find((one) => one.slug === slug || one.id === slug);
410
+ if (layer === undefined)
411
+ throw new Error(`No layer ${JSON.stringify(slug)} you can see.`);
412
+ const queries = await client.layers.referenceQueries(layer.id);
413
+ if (queries === undefined || queries.length === 0) {
414
+ // Not an error and not a zero. A layer without a reference set has no gate
415
+ // by design — the core says so — and reporting 0.00 would read as terrible
416
+ // retrieval rather than as no measurement.
417
+ throw new UsageError(`Layer ${slug} has no reference queries, so there is nothing to score. ` +
418
+ 'Write a set with PUT /v1/layers/{id}/reference-queries, or on the layer screen in the admin UI.');
419
+ }
420
+ const topK = integer(context.parsed, 'top-k') ?? 10;
421
+ const floor = Number(option(context.parsed, 'floor') ?? '0');
422
+ if (!Number.isFinite(floor) || floor < 0 || floor > 1) {
423
+ throw new UsageError('--floor is a fraction between 0 and 1');
424
+ }
425
+ const scored = await Promise.all(queries.map(async (query) => {
426
+ const hits = await client.search(query.query, { topK, layers: [layer.slug] });
427
+ // Hits carry document ids and a reference set names external ids, so the
428
+ // two are joined by reading the documents back. That read is why
429
+ // `external_id` had to stop being write-only: a client could name a
430
+ // document on the way in and never ask about it by that name again.
431
+ const found = new Set((await Promise.all([...new Set(hits.map((hit) => hit.documentId))].map(async (id) => (await client.documents.get(id))?.externalId ?? undefined))).filter((id) => id !== undefined));
432
+ const hit = query.expected.filter((id) => found.has(id));
433
+ return {
434
+ query: query.query,
435
+ expected: query.expected.length,
436
+ found: hit.length,
437
+ // A query expecting nothing scores zero rather than one. The core's
438
+ // gate makes the same choice: an empty expectation is a reference set
439
+ // that was not finished, and calling it perfect hides that.
440
+ recall: query.expected.length === 0 ? 0 : hit.length / query.expected.length,
441
+ missing: query.expected.filter((id) => !found.has(id)),
442
+ };
443
+ }));
444
+ // The mean is over queries, not over documents. Otherwise one query naming
445
+ // ten documents outvotes five naming one each, which is the core's argument
446
+ // and it is the same arithmetic here.
447
+ const recall = scored.reduce((total, item) => total + item.recall, 0) / scored.length;
448
+ const output = render(context, { layer: layer.slug, topK, recall, queries: scored }, () => {
449
+ const lines = [`recall@${topK} ${recall.toFixed(3)} over ${scored.length} quer${scored.length === 1 ? 'y' : 'ies'}`];
450
+ for (const item of scored) {
451
+ lines.push(` ${item.recall.toFixed(2)} ${item.found}/${item.expected} ${item.query}`);
452
+ // The missing documents by name, because "0.60" tells nobody what to
453
+ // look at and the whole point of running this is to look at something.
454
+ if (item.missing.length > 0)
455
+ lines.push(` missing: ${item.missing.join(', ')}`);
456
+ }
457
+ return lines.join('\n');
458
+ });
459
+ // `--floor` makes it a gate: exit 1 below the number, so this can be the step
460
+ // in a pipeline that refuses to promote an index. Without one it only
461
+ // reports, because a bare `nacre eval` failing on a corpus somebody is still
462
+ // building would teach them to stop running it.
463
+ return floor > 0 && recall < floor ? { output, code: 1 } : output;
464
+ }
465
+ function one(hit) {
466
+ const head = `${hit.score.toFixed(3)} ${hit.layer} ${hit.title ?? hit.documentId}`;
467
+ const body = hit.text.replace(/\s+/g, ' ').trim();
468
+ return `${head}\n ${body.length > 240 ? `${body.slice(0, 240)}…` : body}`;
469
+ }
470
+ /**
471
+ * Ask for something, and for a password ask without echoing it.
472
+ *
473
+ * The first version of this drove `readline` with `terminal: true` and
474
+ * intercepted its private `_writeToOutput`. It printed the password. Found by
475
+ * running it rather than by reading it: `terminal: true` makes readline echo
476
+ * whatever it reads, the interception is against an API with a leading
477
+ * underscore for a reason, and the case it failed on is the one a script uses —
478
+ * stdin is a pipe, where readline echoes the piped bytes back out because it
479
+ * was told there was a terminal.
480
+ *
481
+ * So readline is not involved in the hidden path at all. A pipe does not echo
482
+ * on its own, and a terminal does it in the driver, which raw mode turns off —
483
+ * two cases, each handled by the mechanism that actually governs it.
484
+ *
485
+ * This is not a security control. Anyone who can read this process can read the
486
+ * password whatever it does. What it buys is that the password is not in the
487
+ * scrollback, not in a screen recording, and not on the projector.
488
+ */
489
+ export async function ask(question, hidden, io = { input: process.stdin, output: process.stderr }) {
490
+ // The prompt goes to stderr, never stdout: `nacre search q --json | jq` has
491
+ // to work, and a prompt in that stream is a parse error.
492
+ io.output.write(question);
493
+ if (!hidden) {
494
+ const rl = createInterface({ input: io.input, output: io.output, terminal: io.input.isTTY === true });
495
+ try {
496
+ return (await rl.question('')).trim();
497
+ }
498
+ finally {
499
+ rl.close();
500
+ }
501
+ }
502
+ const answer = await readSecret(io);
503
+ io.output.write('\n');
504
+ return answer.trim();
505
+ }
506
+ function readSecret(io) {
507
+ return new Promise((resolve, reject) => {
508
+ const raw = io.input.isTTY === true && typeof io.input.setRawMode === 'function';
509
+ if (raw)
510
+ io.input.setRawMode?.(true);
511
+ let collected = '';
512
+ const done = (finish) => {
513
+ io.input.off('data', onData);
514
+ io.input.off('end', onEnd);
515
+ if (raw)
516
+ io.input.setRawMode?.(false);
517
+ io.input.pause();
518
+ finish();
519
+ };
520
+ const onData = (chunk) => {
521
+ for (const character of chunk.toString('utf8')) {
522
+ if (character === '\n' || character === '\r')
523
+ return done(() => resolve(collected));
524
+ // Raw mode means this process sees Ctrl-C rather than a signal, so it
525
+ // has to be honoured here or the prompt cannot be got out of.
526
+ if (character === '')
527
+ return done(() => reject(new Error('Cancelled.')));
528
+ if (character === '' || character === '\b')
529
+ collected = collected.slice(0, -1);
530
+ else if (character >= ' ')
531
+ collected += character;
532
+ }
533
+ };
534
+ const onEnd = () => done(() => resolve(collected));
535
+ io.input.on('data', onData);
536
+ io.input.on('end', onEnd);
537
+ io.input.resume();
538
+ });
539
+ }
540
+ //# 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,39 @@
1
+ /**
2
+ * Where the CLI keeps an endpoint and a session.
3
+ *
4
+ * On disk rather than in the environment, because the alternative is what the
5
+ * quickstart used to ask for: a token pasted into a shell, which ends up in a
6
+ * history file with no expiry and no way to tell it was ever there. The
7
+ * environment still wins when it is set — that is what CI uses, and a variable
8
+ * a process was handed is not a variable this program wrote down.
9
+ */
10
+ export interface Session {
11
+ readonly baseUrl: string;
12
+ readonly token: string;
13
+ readonly refreshToken?: string;
14
+ }
15
+ /** `$XDG_CONFIG_HOME/nacre/config.json`, or the usual place under `$HOME`. */
16
+ export declare function configPath(env?: NodeJS.ProcessEnv): string;
17
+ /**
18
+ * The session to use, from the environment first and the file second.
19
+ *
20
+ * Returns `undefined` rather than throwing so a command can say what to do
21
+ * about it. There is exactly one message for this and it names both ways in,
22
+ * because an operator reaching for the CLI in CI wants the variables and one on
23
+ * a laptop wants `nacre login`.
24
+ */
25
+ export declare function loadSession(env?: NodeJS.ProcessEnv): Session | undefined;
26
+ /**
27
+ * Write the session, readable by nobody else.
28
+ *
29
+ * `0600` on the file **and** `0700` on the directory, set explicitly rather
30
+ * than left to the umask: this holds a refresh token, and a refresh token is a
31
+ * session that renews itself, so it outlives the access token beside it by as
32
+ * long as the deployment allows. A default umask of `022` would leave it world
33
+ * readable on a shared machine, which is the one place a CLI config actually
34
+ * gets read by somebody else.
35
+ */
36
+ export declare function saveSession(session: Session, env?: NodeJS.ProcessEnv): string;
37
+ /** Forget the session. Not a sign-out — that revokes, and this only forgets. */
38
+ export declare function clearSession(env?: NodeJS.ProcessEnv): void;
39
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAQD,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAIvE;AAaD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,GAAG,SAAS,CAarF;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAU1F;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAKvE"}