@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.
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +79 -0
- package/dist/args.d.ts +30 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +72 -0
- package/dist/args.js.map +1 -0
- package/dist/commands.d.ts +82 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +540 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +70 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +27 -0
- package/dist/main.js.map +1 -0
- package/dist/run.d.ts +25 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +104 -0
- package/dist/run.js.map +1 -0
- package/package.json +53 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
/** `$XDG_CONFIG_HOME/nacre/config.json`, or the usual place under `$HOME`. */
|
|
5
|
+
export function configPath(env = process.env) {
|
|
6
|
+
const xdg = env.XDG_CONFIG_HOME;
|
|
7
|
+
const base = xdg !== undefined && xdg !== '' ? xdg : join(env.HOME ?? homedir(), '.config');
|
|
8
|
+
return join(base, 'nacre', 'config.json');
|
|
9
|
+
}
|
|
10
|
+
function readStored(path) {
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
13
|
+
return typeof parsed === 'object' && parsed !== null ? parsed : {};
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// A missing file is the ordinary case on a first run, and an unreadable or
|
|
17
|
+
// corrupt one is not worth failing a `search` over — `login` rewrites it.
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The session to use, from the environment first and the file second.
|
|
23
|
+
*
|
|
24
|
+
* Returns `undefined` rather than throwing so a command can say what to do
|
|
25
|
+
* about it. There is exactly one message for this and it names both ways in,
|
|
26
|
+
* because an operator reaching for the CLI in CI wants the variables and one on
|
|
27
|
+
* a laptop wants `nacre login`.
|
|
28
|
+
*/
|
|
29
|
+
export function loadSession(env = process.env) {
|
|
30
|
+
const stored = readStored(configPath(env));
|
|
31
|
+
const baseUrl = env.NACRE_API_URL ?? stored.baseUrl;
|
|
32
|
+
const token = env.NACRE_TOKEN ?? stored.token;
|
|
33
|
+
if (baseUrl === undefined || baseUrl === '' || token === undefined || token === '')
|
|
34
|
+
return undefined;
|
|
35
|
+
// Only from the file. A refresh token in the environment beside an access
|
|
36
|
+
// token would be a second credential to leak for a session the environment
|
|
37
|
+
// cannot renew anyway — nothing here would have anywhere to write the new one.
|
|
38
|
+
const refreshToken = env.NACRE_TOKEN === undefined ? stored.refreshToken : undefined;
|
|
39
|
+
return { baseUrl, token, ...(refreshToken === undefined ? {} : { refreshToken }) };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Write the session, readable by nobody else.
|
|
43
|
+
*
|
|
44
|
+
* `0600` on the file **and** `0700` on the directory, set explicitly rather
|
|
45
|
+
* than left to the umask: this holds a refresh token, and a refresh token is a
|
|
46
|
+
* session that renews itself, so it outlives the access token beside it by as
|
|
47
|
+
* long as the deployment allows. A default umask of `022` would leave it world
|
|
48
|
+
* readable on a shared machine, which is the one place a CLI config actually
|
|
49
|
+
* gets read by somebody else.
|
|
50
|
+
*/
|
|
51
|
+
export function saveSession(session, env = process.env) {
|
|
52
|
+
const path = configPath(env);
|
|
53
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
54
|
+
writeFileSync(path, `${JSON.stringify(session, null, 2)}\n`, { mode: 0o600 });
|
|
55
|
+
// mkdirSync's mode is ignored when the directory already exists, and
|
|
56
|
+
// writeFileSync's is ignored when the file does — so neither call above is
|
|
57
|
+
// enough on its own for the second run, which is the run that matters.
|
|
58
|
+
chmodSync(dirname(path), 0o700);
|
|
59
|
+
chmodSync(path, 0o600);
|
|
60
|
+
return path;
|
|
61
|
+
}
|
|
62
|
+
/** Forget the session. Not a sign-out — that revokes, and this only forgets. */
|
|
63
|
+
export function clearSession(env = process.env) {
|
|
64
|
+
const path = configPath(env);
|
|
65
|
+
const stored = readStored(path);
|
|
66
|
+
if (stored.baseUrl === undefined)
|
|
67
|
+
return;
|
|
68
|
+
saveSession({ baseUrl: stored.baseUrl, token: '' }, env);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { run, HELP } from './run.js';
|
|
2
|
+
export { parse, flag, option, integer, UsageError } from './args.js';
|
|
3
|
+
export type { Parsed } from './args.js';
|
|
4
|
+
export { clearSession, configPath, loadSession, saveSession } from './config.js';
|
|
5
|
+
export type { Session } from './config.js';
|
|
6
|
+
export { ask, collect, eligible, evaluate, grant, ingest, layers, login, search, whoami } from './commands.js';
|
|
7
|
+
export type { Context, Terminal } from './commands.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACpE,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAChF,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAC9G,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { run, HELP } from './run.js';
|
|
2
|
+
export { parse, flag, option, integer, UsageError } from './args.js';
|
|
3
|
+
export { clearSession, configPath, loadSession, saveSession } from './config.js';
|
|
4
|
+
export { ask, collect, eligible, evaluate, grant, ingest, layers, login, search, whoami } from './commands.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEpE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEhF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The `nacre` entry point.
|
|
4
|
+
*
|
|
5
|
+
* Everything that decides anything is in `run.ts`; this is the shell around it,
|
|
6
|
+
* and it is thin on purpose — a program whose logic lives in its entry point is
|
|
7
|
+
* a program whose logic is tested by spawning it.
|
|
8
|
+
*/
|
|
9
|
+
import { run } from './run.js';
|
|
10
|
+
const { output, code, stdout } = await run(process.argv.slice(2));
|
|
11
|
+
// Diagnostics to stderr, the answer to stdout. `nacre search q --json | jq`
|
|
12
|
+
// has to work, and it cannot if a refusal is printed into the same stream as
|
|
13
|
+
// the results.
|
|
14
|
+
//
|
|
15
|
+
// `stdout` is the third case: a command that partly worked, where the output is
|
|
16
|
+
// still the answer and the code still has to say so. An ingest of a hundred
|
|
17
|
+
// files with two failures is that, and putting its summary on stderr would make
|
|
18
|
+
// the failure unreadable to the script that has to act on it.
|
|
19
|
+
if (code === 0 || stdout === true) {
|
|
20
|
+
if (output !== '')
|
|
21
|
+
process.stdout.write(`${output}\n`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
process.stderr.write(`${output}\n`);
|
|
25
|
+
}
|
|
26
|
+
process.exitCode = code;
|
|
27
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AAEjE,4EAA4E;AAC5E,6EAA6E;AAC7E,eAAe;AACf,EAAE;AACF,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,8DAA8D;AAC9D,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;IAClC,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,CAAA;AACxD,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,CAAA;AACrC,CAAC;AAED,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Context } from './commands.js';
|
|
2
|
+
export declare const HELP = "nacre \u2014 the command line client for a Nacre installation\n\n nacre login --url <https://api.example> sign in, and remember the session\n nacre whoami which principal this session is\n nacre layers layers you can read\n nacre layers create <slug> [--name ...] create one [--workspace <slug>]\n nacre grant <permission> <scope> --to <principal>\n nacre ingest <file|dir>... --layer <slug> index text files, and wait for them\n --watch keep indexing a directory as it changes\n nacre search <query> [--layer <slug>] [--top-k <n>]\n nacre eval --layer <slug> [--top-k <n>] [--floor <0..1>]\n score the layer's reference queries\n\n --json print the response as JSON, for a script rather than a person\n --help this\n\nA scope is layer:<slug>, workspace:<slug> or document:<id>. A principal is\nuser:<id>, group:<id> or service_account:<id>.\n\nNACRE_API_URL and NACRE_TOKEN override the stored session, which is what a\nservice account in CI uses \u2014 there is no terminal there to log in from.\n\nPermissions are not a ladder: write does not imply read, and admin implies both.\nAn ingest-only service account holds write and cannot search back what it wrote.\n";
|
|
3
|
+
/**
|
|
4
|
+
* What a command hands back: text when it worked, or text and a code when it
|
|
5
|
+
* partly did. Ingest is the only one that needs the second form today.
|
|
6
|
+
*/
|
|
7
|
+
export type Outcome = string | {
|
|
8
|
+
readonly output: string;
|
|
9
|
+
readonly code: number;
|
|
10
|
+
};
|
|
11
|
+
export interface Result {
|
|
12
|
+
readonly output: string;
|
|
13
|
+
readonly code: number;
|
|
14
|
+
/** Print on stdout despite a non-zero code — the output is still the answer. */
|
|
15
|
+
readonly stdout?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One command, returning what to print and the code to exit with.
|
|
19
|
+
*
|
|
20
|
+
* Split from the entry point so the whole surface is testable without spawning
|
|
21
|
+
* a process: everything the program touches that is not the network arrives
|
|
22
|
+
* through `Context`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function run(argv: readonly string[], overrides?: Partial<Context>): Promise<Result>;
|
|
25
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAGA,OAAO,EAA+D,KAAK,OAAO,EAAE,MAAM,eAAe,CAAA;AAGzG,eAAO,MAAM,IAAI,izCAwBhB,CAAA;AAYD;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjF,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;GAMG;AACH,wBAAsB,GAAG,CACvB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,SAAS,GAAE,OAAO,CAAC,OAAO,CAAM,GAC/B,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { NacreClient } from '@nacre.work/sdk';
|
|
2
|
+
import { parse, UsageError } from './args.js';
|
|
3
|
+
import { ask, evaluate, grant, ingest, layers, login, search, whoami } from './commands.js';
|
|
4
|
+
import { saveSession } from './config.js';
|
|
5
|
+
export const HELP = `nacre — the command line client for a Nacre installation
|
|
6
|
+
|
|
7
|
+
nacre login --url <https://api.example> sign in, and remember the session
|
|
8
|
+
nacre whoami which principal this session is
|
|
9
|
+
nacre layers layers you can read
|
|
10
|
+
nacre layers create <slug> [--name ...] create one [--workspace <slug>]
|
|
11
|
+
nacre grant <permission> <scope> --to <principal>
|
|
12
|
+
nacre ingest <file|dir>... --layer <slug> index text files, and wait for them
|
|
13
|
+
--watch keep indexing a directory as it changes
|
|
14
|
+
nacre search <query> [--layer <slug>] [--top-k <n>]
|
|
15
|
+
nacre eval --layer <slug> [--top-k <n>] [--floor <0..1>]
|
|
16
|
+
score the layer's reference queries
|
|
17
|
+
|
|
18
|
+
--json print the response as JSON, for a script rather than a person
|
|
19
|
+
--help this
|
|
20
|
+
|
|
21
|
+
A scope is layer:<slug>, workspace:<slug> or document:<id>. A principal is
|
|
22
|
+
user:<id>, group:<id> or service_account:<id>.
|
|
23
|
+
|
|
24
|
+
NACRE_API_URL and NACRE_TOKEN override the stored session, which is what a
|
|
25
|
+
service account in CI uses — there is no terminal there to log in from.
|
|
26
|
+
|
|
27
|
+
Permissions are not a ladder: write does not imply read, and admin implies both.
|
|
28
|
+
An ingest-only service account holds write and cannot search back what it wrote.
|
|
29
|
+
`;
|
|
30
|
+
const COMMANDS = {
|
|
31
|
+
login,
|
|
32
|
+
whoami,
|
|
33
|
+
layers,
|
|
34
|
+
grant,
|
|
35
|
+
ingest,
|
|
36
|
+
search,
|
|
37
|
+
eval: evaluate,
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* One command, returning what to print and the code to exit with.
|
|
41
|
+
*
|
|
42
|
+
* Split from the entry point so the whole surface is testable without spawning
|
|
43
|
+
* a process: everything the program touches that is not the network arrives
|
|
44
|
+
* through `Context`.
|
|
45
|
+
*/
|
|
46
|
+
export async function run(argv, overrides = {}) {
|
|
47
|
+
const parsed = parse(argv);
|
|
48
|
+
const name = parsed.positional[0];
|
|
49
|
+
if (name === undefined || name === 'help' || parsed.options.has('help')) {
|
|
50
|
+
// Help is a success when it was asked for and a usage error when it is the
|
|
51
|
+
// answer to a command nobody named — a script piping `nacre` with no
|
|
52
|
+
// arguments must not read exit 0 as "it worked".
|
|
53
|
+
return { output: HELP, code: name === undefined && !parsed.options.has('help') ? 2 : 0 };
|
|
54
|
+
}
|
|
55
|
+
const command = COMMANDS[name];
|
|
56
|
+
if (command === undefined) {
|
|
57
|
+
return { output: `Unknown command ${JSON.stringify(name)}.\n\n${HELP}`, code: 2 };
|
|
58
|
+
}
|
|
59
|
+
const context = {
|
|
60
|
+
parsed,
|
|
61
|
+
env: process.env,
|
|
62
|
+
prompt: ask,
|
|
63
|
+
clientFor: defaultClient,
|
|
64
|
+
...overrides,
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
const outcome = await command(context);
|
|
68
|
+
return typeof outcome === 'string'
|
|
69
|
+
? { output: outcome, code: 0 }
|
|
70
|
+
: // A command that partly worked reports on **stdout** and still exits
|
|
71
|
+
// non-zero. Ingest is the one: a directory where every document failed
|
|
72
|
+
// is not a success, and a script reading exit 0 there is the "reported
|
|
73
|
+
// success having done nothing" failure this repository keeps finding —
|
|
74
|
+
// but the per-document summary is the answer, so `--json | jq` has to
|
|
75
|
+
// keep working while the code says not everything landed.
|
|
76
|
+
{ output: outcome.output, code: outcome.code, stdout: true };
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
// Two codes, and the difference is whose fault it is: `2` means this
|
|
80
|
+
// invocation was wrong and repeating it will fail the same way, `1` means
|
|
81
|
+
// the request was refused or the network was. A script that cannot tell
|
|
82
|
+
// them apart retries the first one forever.
|
|
83
|
+
const code = error instanceof UsageError ? 2 : 1;
|
|
84
|
+
return { output: error instanceof Error ? error.message : String(error), code };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function defaultClient(session) {
|
|
88
|
+
return new NacreClient({
|
|
89
|
+
baseUrl: session.baseUrl,
|
|
90
|
+
token: session.token,
|
|
91
|
+
...(session.refreshToken === undefined ? {} : { refreshToken: session.refreshToken }),
|
|
92
|
+
// The session renews itself and the renewal has to be written down, or the
|
|
93
|
+
// next invocation is a fresh process holding the token that just expired.
|
|
94
|
+
// This is the whole reason the CLI keeps a file rather than an env var.
|
|
95
|
+
onTokens: (tokens) => {
|
|
96
|
+
saveSession({
|
|
97
|
+
baseUrl: session.baseUrl,
|
|
98
|
+
token: tokens.accessToken,
|
|
99
|
+
refreshToken: tokens.refreshToken,
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nacre.work/cli",
|
|
3
|
+
"version": "0.14.3",
|
|
4
|
+
"description": "Command line client for Nacre: layers, grants, ingest and search",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/nacre-work/nacre.git",
|
|
8
|
+
"directory": "packages/cli"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://nacre.work",
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -b"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"nacre": "./dist/main.js"
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE",
|
|
34
|
+
"NOTICE"
|
|
35
|
+
],
|
|
36
|
+
"keywords": [
|
|
37
|
+
"nacre",
|
|
38
|
+
"cli",
|
|
39
|
+
"rag",
|
|
40
|
+
"permissions",
|
|
41
|
+
"search"
|
|
42
|
+
],
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/nacre-work/nacre/issues"
|
|
45
|
+
},
|
|
46
|
+
"author": "Nacre",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@nacre.work/sdk": "workspace:*"
|
|
52
|
+
}
|
|
53
|
+
}
|