@bitspark/archon-cli 0.6.1

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,482 @@
1
+ // archon key add|list|rm|default|export — the password-protected seed store of
2
+ // ADR 0007 §A. The format lives in ../keystore.js; this file is the command: flags,
3
+ // prompts, paths, and the lines we print about what we did.
4
+ //
5
+ // Every operation that destroys, reveals or creates key material says so IN SCOPE
6
+ // (docs/keystore.md §6): archon speaks for its own store and never for anyone else's, so
7
+ // an empty result means "nothing visible here", never "nothing exists". The wording is
8
+ // pinned in cli/smoke.mjs.
9
+ import { randomBytes } from "node:crypto";
10
+ import { chmodSync, existsSync, fstatSync, mkdirSync, readdirSync, readFileSync, readSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
11
+ import { dirname, join } from "node:path";
12
+ import { homedir } from "node:os";
13
+ import { encodeKey, getPublicKey, pkcs8PemToSeed, pubkeyFromHex, seedFromHex, seedToPkcs8Pem } from "@bitspark/archon";
14
+ import * as keystore from "../keystore.js";
15
+ export const USAGE = "usage: archon key <add <name> [--seed <hex>|--seed-file <file>|--pkcs8 <file>]|" +
16
+ "list [--json]|rm <name> [--force]|default [<name>]|export <name> --reveal --out <file>>\n " +
17
+ "the password-protected seed store; keys live in $ARCHON_HOME/keys (default ~/.archon). " +
18
+ "Password: interactive prompt, or ARCHON_KEY_PASSWORD / --password-fd <n>, never argv.";
19
+ // ---------------------------------------------------------------------------
20
+ // Where the store lives — docs/keystore.md §1.
21
+ // ---------------------------------------------------------------------------
22
+ const archonHome = () => {
23
+ const env = process.env["ARCHON_HOME"];
24
+ return env !== undefined && env !== "" ? env : join(homedir(), ".archon");
25
+ };
26
+ const storeDir = () => join(archonHome(), "keys");
27
+ function keyPath(name) {
28
+ keystore.validateName(name);
29
+ return join(storeDir(), name);
30
+ }
31
+ /**
32
+ * Writes atomically: a temp file in the SAME directory, then a rename, so a crash
33
+ * mid-write can never leave a half key where a whole one was.
34
+ */
35
+ function writeKeyFile(path, blob) {
36
+ const dir = dirname(path);
37
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
38
+ const tmp = join(dir, `.tmp-${process.pid}`);
39
+ writeFileSync(tmp, blob, { mode: 0o600 });
40
+ try {
41
+ chmodSync(tmp, 0o600);
42
+ }
43
+ catch {
44
+ // Windows has no mode bits; the file inherits the ACL of the user's own profile
45
+ // directory. Failing here would be pretending 0600 was ever possible.
46
+ }
47
+ renameSync(tmp, path);
48
+ }
49
+ function listKeyNames() {
50
+ try {
51
+ return readdirSync(storeDir())
52
+ .filter((n) => !n.startsWith(".tmp-"))
53
+ .filter((n) => {
54
+ try {
55
+ return statSync(join(storeDir(), n)).isFile();
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ })
61
+ .sort();
62
+ }
63
+ catch {
64
+ // A missing store directory is an empty store, not an error: nothing added yet.
65
+ return [];
66
+ }
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Passwords — docs/keystore.md §4. Never argv.
70
+ // ---------------------------------------------------------------------------
71
+ /**
72
+ * Scans for `--password-fd <n>` and removes it. The password never travels in argv, so
73
+ * this carries a descriptor number rather than the secret.
74
+ */
75
+ export function takePasswordFd(args) {
76
+ const rest = [];
77
+ let fd;
78
+ for (let i = 0; i < args.length; i++) {
79
+ if (args[i] === "--password-fd") {
80
+ const v = args[i + 1];
81
+ if (v === undefined)
82
+ throw new Error('flag "--password-fd" needs a value');
83
+ const n = Number.parseInt(v, 10);
84
+ if (!Number.isInteger(n) || n < 0) {
85
+ throw new Error(`--password-fd: ${JSON.stringify(v)} is not a file descriptor`);
86
+ }
87
+ fd = n;
88
+ i++;
89
+ continue;
90
+ }
91
+ rest.push(args[i]);
92
+ }
93
+ return { rest, fd };
94
+ }
95
+ const trimNewline = (s) => s.replace(/[\r\n]+$/u, "");
96
+ /** The real terminal. The fd read is injectable too, so the byte-reading closure — the one
97
+ * place this file touches fd 0 — can be exercised by a test without a TTY. */
98
+ export const terminalIo = (readOne = (into) => readSync(0, into, 0, 1, null)) => {
99
+ const chunk = Buffer.alloc(1);
100
+ return {
101
+ setRawMode: (raw) => {
102
+ process.stdin.setRawMode(raw);
103
+ },
104
+ readByte: () => {
105
+ let n = 0;
106
+ try {
107
+ n = readOne(chunk);
108
+ }
109
+ catch {
110
+ return -1;
111
+ }
112
+ return n === 0 ? -1 : chunk[0];
113
+ },
114
+ write: (text) => {
115
+ process.stderr.write(text);
116
+ },
117
+ };
118
+ };
119
+ /** A prompt that does not echo. Node has no rpassword, so raw mode is done by hand. */
120
+ export function promptHidden(label, io = terminalIo()) {
121
+ io.write(label);
122
+ const wasRaw = process.stdin.isRaw === true;
123
+ io.setRawMode(true);
124
+ const buf = [];
125
+ for (;;) {
126
+ const c = io.readByte();
127
+ if (c < 0 || c === 0x0d || c === 0x0a)
128
+ break;
129
+ if (c === 0x03) {
130
+ io.setRawMode(wasRaw);
131
+ io.write("\n");
132
+ throw new Error("interrupted");
133
+ }
134
+ if (c === 0x7f || c === 0x08) {
135
+ buf.pop();
136
+ continue;
137
+ }
138
+ buf.push(String.fromCharCode(c));
139
+ }
140
+ io.setRawMode(wasRaw);
141
+ io.write("\n");
142
+ return buf.join("");
143
+ }
144
+ /**
145
+ * Sources the password: --password-fd, then ARCHON_KEY_PASSWORD, then an interactive
146
+ * prompt. `confirm` asks twice when a key is being created, where a typo would otherwise
147
+ * be discovered only at the next unlock.
148
+ */
149
+ export function readPassword(fd, confirm) {
150
+ if (fd !== undefined) {
151
+ refuseLoosePasswordFile(fd);
152
+ return trimNewline(readFileSync(fd, "utf8"));
153
+ }
154
+ const env = process.env["ARCHON_KEY_PASSWORD"];
155
+ if (env !== undefined)
156
+ return env;
157
+ if (process.stdin.isTTY !== true) {
158
+ throw new Error("no password: stdin is not a terminal — set ARCHON_KEY_PASSWORD or pass --password-fd <n>");
159
+ }
160
+ const first = promptHidden("password: ");
161
+ if (confirm) {
162
+ const second = promptHidden("password (again): ");
163
+ if (first !== second)
164
+ throw new Error("the two passwords differ");
165
+ }
166
+ return first;
167
+ }
168
+ // ---------------------------------------------------------------------------
169
+ // What the store lets another command ask of it — `login --key`'s seams.
170
+ // ---------------------------------------------------------------------------
171
+ /**
172
+ * The store's own "is there a key called that": a validated name, then a stat — opening
173
+ * nothing, asking for no password. Shared by `key default <name>` and by `login`'s
174
+ * pre-network check, so both refuse a missing key with one wording.
175
+ */
176
+ export function requireNamedKey(name) {
177
+ if (!existsSync(keyPath(name))) {
178
+ throw new Error(`no key named ${JSON.stringify(name)} in archon's store`);
179
+ }
180
+ }
181
+ /**
182
+ * The ONE reader of the default pointer ($ARCHON_HOME/default), shared by `key default`
183
+ * and by `login`'s fallback so the two can never disagree about what "the default" is.
184
+ * Absent, unreadable, or present-but-empty all read as undefined — there is no default —
185
+ * and the caller says what that means for it.
186
+ */
187
+ export function readDefaultKeyName() {
188
+ let raw;
189
+ try {
190
+ raw = readFileSync(join(archonHome(), "default"), "utf8");
191
+ }
192
+ catch {
193
+ return undefined;
194
+ }
195
+ const name = raw.trim();
196
+ return name === "" ? undefined : name;
197
+ }
198
+ /**
199
+ * Opens the key called `name`: the path, the file, the password — sourced the store's one
200
+ * way (--password-fd, then ARCHON_KEY_PASSWORD, then a prompt) — and the seal. It is the
201
+ * ONE unlock path, shared by `key export` and `login --key`, so no two commands can ask
202
+ * for a password differently.
203
+ */
204
+ export function unlockNamedKey(name, fd) {
205
+ const path = keyPath(name);
206
+ let raw;
207
+ try {
208
+ raw = new Uint8Array(readFileSync(path));
209
+ }
210
+ catch {
211
+ throw new Error(`no key named ${JSON.stringify(name)} in archon's store`);
212
+ }
213
+ return keystore.open(raw, readPassword(fd, false));
214
+ }
215
+ // ---------------------------------------------------------------------------
216
+ // The commands.
217
+ // ---------------------------------------------------------------------------
218
+ /**
219
+ * The one place a key is written, shared by `key add` and `keygen --store` so the two
220
+ * cannot drift apart. Salt and nonce are drawn HERE, in the command: the format takes them
221
+ * as arguments and never sources randomness, which is what makes it pinnable.
222
+ */
223
+ export function sealAndWrite(path, seed, password) {
224
+ const salt = new Uint8Array(randomBytes(keystore.SALT_SIZE));
225
+ const nonce = new Uint8Array(randomBytes(keystore.NONCE_SIZE));
226
+ const blob = keystore.seal(seed, password, salt, nonce, keystore.defaultParams());
227
+ writeKeyFile(path, blob);
228
+ return encodeKey(getPublicKey(seed));
229
+ }
230
+ /** `keygen --store <name>`: the same seal path as `key add`. */
231
+ export function storeGenerated(name, seed, fd) {
232
+ keystore.validateName(name);
233
+ const path = keyPath(name);
234
+ if (existsSync(path)) {
235
+ throw new Error(`a key named ${JSON.stringify(name)} already exists; remove it first (archon key rm ${name})`);
236
+ }
237
+ const password = readPassword(fd, true);
238
+ const principal = sealAndWrite(path, seed, password);
239
+ process.stdout.write(`generated and stored ${name} (${principal}).\n`);
240
+ }
241
+ /**
242
+ * Accepts the two shapes ADR 0007 §A names: a 32-byte seed as 64 hex, and the first consumer's
243
+ * ed25519.PrivateKey shape as 128 hex (seed ‖ public key). The public half is CHECKED
244
+ * against the seed rather than trusted — a mismatch means the file is not what its owner
245
+ * thinks it is, and storing it would carry the confusion forward.
246
+ */
247
+ function seedFromHexish(s) {
248
+ const t = s.trim();
249
+ if (t.length === 64)
250
+ return seedFromHex(t);
251
+ if (t.length === 128) {
252
+ const seed = seedFromHex(t.slice(0, 64));
253
+ const claimed = pubkeyFromHex(t.slice(64));
254
+ const derived = getPublicKey(seed);
255
+ if (claimed.length !== derived.length || claimed.some((b, i) => b !== derived[i])) {
256
+ throw new Error("the public half does not match the seed: this is not a consistent private key");
257
+ }
258
+ return seed;
259
+ }
260
+ throw new Error(`expected 64 hex characters (a seed) or 128 (seed then public key), got ${t.length}`);
261
+ }
262
+ export function runAdd(args) {
263
+ const name = args[0];
264
+ if (name === undefined)
265
+ throw new Error(USAGE);
266
+ keystore.validateName(name);
267
+ const { rest, fd } = takePasswordFd(args.slice(1));
268
+ let seedHex;
269
+ let seedFile;
270
+ let pkcs8File;
271
+ for (let i = 0; i < rest.length; i += 2) {
272
+ const value = rest[i + 1];
273
+ if (value === undefined)
274
+ throw new Error(`flag ${JSON.stringify(rest[i])} needs a value\n${USAGE}`);
275
+ switch (rest[i]) {
276
+ case "--seed":
277
+ seedHex = value;
278
+ break;
279
+ case "--seed-file":
280
+ seedFile = value;
281
+ break;
282
+ case "--pkcs8":
283
+ pkcs8File = value;
284
+ break;
285
+ default: throw new Error(`unknown flag ${JSON.stringify(rest[i])}\n${USAGE}`);
286
+ }
287
+ }
288
+ if ([seedHex, seedFile, pkcs8File].filter((v) => v !== undefined).length > 1) {
289
+ throw new Error(`--seed, --seed-file and --pkcs8 are mutually exclusive\n${USAGE}`);
290
+ }
291
+ const path = keyPath(name);
292
+ // Refused BEFORE a password is asked for: a key is never silently replaced, and there is
293
+ // no reason to make someone type a password to find that out.
294
+ if (existsSync(path)) {
295
+ throw new Error(`a key named ${JSON.stringify(name)} already exists; remove it first (archon key rm ${name})`);
296
+ }
297
+ let seed;
298
+ let from;
299
+ if (seedHex !== undefined) {
300
+ seed = seedFromHexish(seedHex);
301
+ from = "--seed";
302
+ }
303
+ else if (seedFile !== undefined) {
304
+ seed = seedFromHexish(readFileSync(seedFile, "utf8"));
305
+ from = seedFile;
306
+ }
307
+ else if (pkcs8File !== undefined) {
308
+ seed = pkcs8PemToSeed(readFileSync(pkcs8File, "utf8"));
309
+ from = pkcs8File;
310
+ }
311
+ else {
312
+ seed = new Uint8Array(randomBytes(keystore.SEED_SIZE));
313
+ }
314
+ const password = readPassword(fd, true);
315
+ const principal = sealAndWrite(path, seed, password);
316
+ if (from === undefined) {
317
+ process.stdout.write(`generated and stored ${name} (${principal}).\n`);
318
+ }
319
+ else {
320
+ process.stdout.write(`stored ${name} (${principal}) from ${from}; the source file is untouched.\n`);
321
+ }
322
+ }
323
+ /**
324
+ * Prints what each header CLAIMS. The claim is only proven at unlock, which is why this
325
+ * never opens a key and never asks for a password.
326
+ */
327
+ export function runList(args) {
328
+ let asJson = false;
329
+ for (const a of args) {
330
+ if (a !== "--json")
331
+ throw new Error(`unknown flag ${JSON.stringify(a)}\n${USAGE}`);
332
+ asJson = true;
333
+ }
334
+ const rows = [];
335
+ for (const name of listKeyNames()) {
336
+ try {
337
+ const raw = new Uint8Array(readFileSync(keyPath(name)));
338
+ // The magic is what keeps a stray file out of this list.
339
+ const h = keystore.parseHeader(raw);
340
+ rows.push({ name, principal: encodeKey(h.publicKey) });
341
+ }
342
+ catch {
343
+ continue;
344
+ }
345
+ }
346
+ if (asJson) {
347
+ process.stdout.write(`${JSON.stringify(rows)}\n`);
348
+ return;
349
+ }
350
+ for (const r of rows)
351
+ process.stdout.write(`${r.name}\t${r.principal}\n`);
352
+ }
353
+ /**
354
+ * Removes a key and says exactly what it removed, from where, and what it does NOT speak
355
+ * for. An unparsable file is refused unless --force: deleting an unrecognised file inside
356
+ * the store silently is what this rule exists to prevent.
357
+ */
358
+ export function runRm(args) {
359
+ const name = args[0];
360
+ if (name === undefined)
361
+ throw new Error(USAGE);
362
+ let force = false;
363
+ for (const a of args.slice(1)) {
364
+ if (a !== "--force")
365
+ throw new Error(`unknown flag ${JSON.stringify(a)}\n${USAGE}`);
366
+ force = true;
367
+ }
368
+ const path = keyPath(name);
369
+ let raw;
370
+ try {
371
+ raw = new Uint8Array(readFileSync(path));
372
+ }
373
+ catch {
374
+ throw new Error(`no key named ${JSON.stringify(name)} in archon's store`);
375
+ }
376
+ let header;
377
+ let why = "";
378
+ try {
379
+ header = keystore.parseHeader(raw);
380
+ }
381
+ catch (e) {
382
+ why = e instanceof Error ? e.message : String(e);
383
+ if (!force)
384
+ throw new Error(`not an archon key file: ${path}: ${why}`);
385
+ }
386
+ unlinkSync(path);
387
+ const what = header === undefined ? `unreadable header: ${why}` : encodeKey(header.publicKey);
388
+ process.stdout.write(`removed ${name} (${what}) from archon's store at ${path}; ` +
389
+ "any copy of this key outside it is untouched.\n");
390
+ }
391
+ /**
392
+ * Sets or shows the default key. Selection is by NAME, never by principal text
393
+ * (ADR 0007 §A). The pointer lives beside keys/, not in it, so it can never collide with
394
+ * a key name.
395
+ */
396
+ export function runDefault(args) {
397
+ const pointer = join(archonHome(), "default");
398
+ if (args.length === 0) {
399
+ const name = readDefaultKeyName();
400
+ if (name === undefined)
401
+ throw new Error("no default key is set");
402
+ process.stdout.write(`${name}\n`);
403
+ return;
404
+ }
405
+ if (args.length !== 1)
406
+ throw new Error(USAGE);
407
+ const name = args[0];
408
+ requireNamedKey(name);
409
+ mkdirSync(dirname(pointer), { recursive: true, mode: 0o700 });
410
+ writeFileSync(pointer, `${name}\n`, { mode: 0o600 });
411
+ process.stdout.write(`default key is now ${name}.\n`);
412
+ }
413
+ /**
414
+ * Writes the seed out. Refuses without --reveal, and refuses stdout unless `--out -` says
415
+ * so: a seed should never land in a pipe by accident.
416
+ */
417
+ export function runExport(args) {
418
+ const name = args[0];
419
+ if (name === undefined)
420
+ throw new Error(USAGE);
421
+ const { rest, fd } = takePasswordFd(args.slice(1));
422
+ let reveal = false;
423
+ let out = "";
424
+ for (let i = 0; i < rest.length; i++) {
425
+ if (rest[i] === "--reveal") {
426
+ reveal = true;
427
+ continue;
428
+ }
429
+ if (rest[i] === "--out") {
430
+ const v = rest[i + 1];
431
+ if (v === undefined)
432
+ throw new Error(`flag "--out" needs a value\n${USAGE}`);
433
+ out = v;
434
+ i++;
435
+ continue;
436
+ }
437
+ throw new Error(`unknown flag ${JSON.stringify(rest[i])}\n${USAGE}`);
438
+ }
439
+ if (!reveal)
440
+ throw new Error("refusing to export a seed without --reveal");
441
+ if (out === "") {
442
+ throw new Error("refusing to write a seed to stdout: pass --out <file>, or --out - to mean it");
443
+ }
444
+ const seed = unlockNamedKey(name, fd);
445
+ const pem = seedToPkcs8Pem(seed);
446
+ if (out === "-") {
447
+ process.stdout.write(pem);
448
+ process.stderr.write(`wrote the seed of ${name} to stdout; the store's copy remains.\n`);
449
+ return;
450
+ }
451
+ writeFileSync(out, pem, { mode: 0o600 });
452
+ process.stdout.write(`wrote the seed of ${name} to ${out}; the store's copy remains.\n`);
453
+ }
454
+ /**
455
+ * Refuses a password file that anyone but its owner can read (ADR 0007 §A). Only a REGULAR
456
+ * file is checked: a pipe, a terminal or a process substitution has no meaningful mode, and
457
+ * `--password-fd 0` fed by a heredoc is a pipe, so checking those would refuse the ordinary
458
+ * non-interactive case for nothing.
459
+ *
460
+ * Windows has no mode bits and Node reports a synthetic 0666, so there is nothing to check
461
+ * and nothing is claimed — the same honesty the store keeps about 0600 elsewhere.
462
+ */
463
+ function refuseLoosePasswordFile(fd) {
464
+ if (process.platform === "win32")
465
+ return;
466
+ let st;
467
+ try {
468
+ st = fstatSync(fd);
469
+ }
470
+ catch {
471
+ // Undecidable, so this refuses nothing rather than guessing; the read below will
472
+ // produce the real error if the descriptor is unusable.
473
+ return;
474
+ }
475
+ if (!st.isFile())
476
+ return;
477
+ const perm = st.mode & 0o777;
478
+ if ((perm & 0o077) !== 0) {
479
+ throw new Error(`--password-fd: the password file is readable by others (mode ${perm.toString(8).padStart(4, "0")}); chmod 600 it`);
480
+ }
481
+ }
482
+ //# sourceMappingURL=key_store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key_store.js","sourceRoot":"","sources":["../../../src/cmd/key_store.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,oFAAoF;AACpF,4DAA4D;AAC5D,EAAE;AACF,kFAAkF;AAClF,yFAAyF;AACzF,uFAAuF;AACvF,2BAA2B;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,UAAU,EACV,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MACvF,kBAAkB,CAAC;AAE1B,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,MAAM,CAAC,MAAM,KAAK,GAChB,iFAAiF;IACjF,6FAA6F;IAC7F,yFAAyF;IACzF,uFAAuF,CAAC;AAE1F,8EAA8E;AAC9E,+CAA+C;AAC/C,8EAA8E;AAE9E,MAAM,UAAU,GAAG,GAAW,EAAE;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC5E,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC;AAE1D,SAAS,OAAO,CAAC,IAAY;IAC3B,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAY,EAAE,IAAgB;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7C,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,gFAAgF;QAChF,sEAAsE;IACxE,CAAC;IACD,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,YAAY;IACnB,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,QAAQ,EAAE,CAAC;aAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;aACrC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACZ,IAAI,CAAC;gBACH,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC,CAAC;aACD,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,gFAAgF;QAChF,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,+CAA+C;AAC/C,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,EAAsB,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,eAAe,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;YAC3E,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC;YAClF,CAAC;YACD,EAAE,GAAG,CAAC,CAAC;YACP,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAW,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AACtB,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAgBtE;+EAC+E;AAC/E,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,UAAoC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,EAC3D,EAAE;IAClB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO;QACL,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE;YAClB,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE;YACb,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,IAAI,CAAC;gBACH,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,CAAC,CAAC;YACZ,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,KAAK,CAAC,CAAC,CAAY,CAAC;QAC7C,CAAC;QACD,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,uFAAuF;AACvF,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,KAAqB,UAAU,EAAE;IAC3E,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC;IAC5C,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACpB,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,SAAS,CAAC;QACR,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;YAAE,MAAM;QAC7C,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACf,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACtB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAC7B,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACtB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACf,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,EAAsB,EAAE,OAAgB;IACnE,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACrB,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAC5B,OAAO,WAAW,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC/C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAClC,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACzC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,YAAY,CAAC,oBAAoB,CAAC,CAAC;QAClD,IAAI,KAAK,KAAK,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,yEAAyE;AACzE,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACxB,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,EAAsB;IACjE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,GAAe,CAAC;IACpB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAgB,EAAE,QAAgB;IAC3E,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAClF,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACzB,OAAO,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAgB,EAAE,EAAsB;IACnF,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,eAAe,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,mDAAmD,IAAI,GAAG,CAC9F,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,IAAI,KAAK,SAAS,MAAM,CAAC,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,CAAS;IAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnB,IAAI,CAAC,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAClF,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,KAAK,CACb,0EAA0E,CAAC,CAAC,MAAM,EAAE,CACrF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAc;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnD,IAAI,OAA2B,CAAC;IAChC,IAAI,QAA4B,CAAC;IACjC,IAAI,SAA6B,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC;QACpG,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,QAAQ;gBAAE,OAAO,GAAG,KAAK,CAAC;gBAAC,MAAM;YACtC,KAAK,aAAa;gBAAE,QAAQ,GAAG,KAAK,CAAC;gBAAC,MAAM;YAC5C,KAAK,SAAS;gBAAE,SAAS,GAAG,KAAK,CAAC;gBAAC,MAAM;YACzC,OAAO,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7E,MAAM,IAAI,KAAK,CAAC,2DAA2D,KAAK,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,yFAAyF;IACzF,8DAA8D;IAC9D,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,eAAe,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,mDAAmD,IAAI,GAAG,CAC9F,CAAC;IACJ,CAAC;IAED,IAAI,IAAgB,CAAC;IACrB,IAAI,IAAwB,CAAC;IAC7B,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,GAAG,QAAQ,CAAC;IAClB,CAAC;SAAM,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,GAAG,cAAc,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QACtD,IAAI,GAAG,QAAQ,CAAC;IAClB,CAAC;SAAM,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,IAAI,GAAG,cAAc,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;QACvD,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,IAAI,KAAK,SAAS,MAAM,CAAC,CAAC;IACzE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,UAAU,IAAI,KAAK,SAAS,UAAU,IAAI,mCAAmC,CAC9E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,IAAc;IACpC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;QACnF,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,IAAI,GAA0C,EAAE,CAAC;IACvD,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxD,yDAAyD;YACzD,MAAM,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC;AAC5E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK,CAAC,IAAc;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;QACpF,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,GAAe,CAAC;IACpB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,MAAsC,CAAC;IAC3C,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,CAAC;QACH,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,UAAU,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,IAAI,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,sBAAsB,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9F,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,WAAW,IAAI,KAAK,IAAI,4BAA4B,IAAI,IAAI;QAC1D,iDAAiD,CACpD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;QAClC,IAAI,IAAI,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;IAC/B,eAAe,CAAC,IAAI,CAAC,CAAC;IACtB,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9D,aAAa,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,IAAI,KAAK,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC;YAC3B,MAAM,GAAG,IAAI,CAAC;YACd,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;YAC7E,GAAG,GAAG,CAAC,CAAC;YACR,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC3E,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAClG,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,IAAI,yCAAyC,CAAC,CAAC;QACzF,OAAO;IACT,CAAC;IACD,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,IAAI,OAAO,GAAG,+BAA+B,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,uBAAuB,CAAC,EAAU;IACzC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO;IACzC,IAAI,EAAE,CAAC;IACP,IAAI,CAAC;QACH,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,iFAAiF;QACjF,wDAAwD;QACxD,OAAO;IACT,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO;IACzB,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC;IAC7B,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,gEAAgE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,iBAAiB,CACnH,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function run(args: string[]): void;
@@ -0,0 +1,114 @@
1
+ // archon keygen — generate (or deterministically derive) an Ed25519 key pair: the
2
+ // canonical public key to stdout — the shareable part — and the private key as a PKCS#8
3
+ // v1 PEM, never the raw seed hex, either to --out <file> (the recommended path) or to
4
+ // stderr behind a clear warning.
5
+ //
6
+ // The RNG is the one deliberately-unpinned edge, and it lives here, in the CLI: the
7
+ // library takes a seed it is given and never invents one (ADR 0002). This is not custody
8
+ // in stele's sense — nothing is named, stored or managed; a file you name is written and
9
+ // forgotten.
10
+ import { randomBytes } from "node:crypto";
11
+ import { writeFileSync } from "node:fs";
12
+ import { encodeKey, getPublicKey, seedFromHex, seedToPkcs8Pem, SEED_SIZE } from "@bitspark/archon";
13
+ import { wantsHelp } from "../io.js";
14
+ import * as keystore from "../keystore.js";
15
+ import * as store from "./key_store.js";
16
+ import { parsePubFormat, renderPub } from "../pubrender.js";
17
+ const USAGE = "usage: archon keygen [--seed <hex>] [--store <name>] [--out <file>] [--pub-out <file>] [--pub-format spki|text|hex]\n " +
18
+ "prints the public key (canonical text) on stdout; writes the PKCS#8 PEM private key to " +
19
+ "<file> (--out) or, by default, to stderr behind a SECRET warning; --pub-out writes the " +
20
+ "public key (--pub-format spki|text|hex, default spki) to a file. --seed derives the " +
21
+ "key deterministically.";
22
+ export function run(args) {
23
+ if (wantsHelp(args)) {
24
+ process.stdout.write(`${USAGE}\n`);
25
+ return;
26
+ }
27
+ const { rest: afterStore, store: storeName } = takeStoreFlag(args);
28
+ const { rest: parsedArgs, fd: pwFd } = store.takePasswordFd(afterStore);
29
+ // Validated before anything is generated or printed: a bad name should cost nothing.
30
+ if (storeName !== undefined)
31
+ keystore.validateName(storeName);
32
+ const opts = parseOpts(parsedArgs);
33
+ if (storeName !== undefined && opts.out !== undefined) {
34
+ throw new Error("--store and --out are mutually exclusive: one keeps the seed, the other writes it out");
35
+ }
36
+ const pub = getPublicKey(opts.seed);
37
+ process.stdout.write(`${encodeKey(pub)}\n`);
38
+ // --store: the seed stays in the store and no PEM is produced at all. Same seal path
39
+ // `key add` uses (sealAndWrite), reached from the command that owns the CSPRNG.
40
+ if (storeName !== undefined) {
41
+ store.storeGenerated(storeName, opts.seed, pwFd);
42
+ if (opts.pubOut !== undefined) {
43
+ writeFileSync(opts.pubOut, renderPub(pub, opts.pubFormat));
44
+ process.stderr.write(`wrote public key (${opts.pubFormat}) to ${opts.pubOut}\n`);
45
+ }
46
+ return;
47
+ }
48
+ const pem = seedToPkcs8Pem(opts.seed);
49
+ if (opts.out !== undefined) {
50
+ writeFileSync(opts.out, pem, { mode: 0o600 });
51
+ process.stderr.write(`wrote PKCS#8 private key PEM to ${opts.out}\n`);
52
+ }
53
+ else {
54
+ process.stderr.write("SECRET — do not share. Anyone with this private key controls the identity:\n");
55
+ process.stderr.write(pem);
56
+ }
57
+ if (opts.pubOut !== undefined) {
58
+ writeFileSync(opts.pubOut, renderPub(pub, opts.pubFormat));
59
+ process.stderr.write(`wrote public key (${opts.pubFormat}) to ${opts.pubOut}\n`);
60
+ }
61
+ }
62
+ function parseOpts(args) {
63
+ let seed;
64
+ let out;
65
+ let pubOut;
66
+ let pubFormat = "spki";
67
+ for (let i = 0; i < args.length; i += 2) {
68
+ const flag = args[i];
69
+ if (i + 1 >= args.length)
70
+ throw new Error(`flag ${JSON.stringify(flag)} needs a value\n${USAGE}`);
71
+ const value = args[i + 1];
72
+ switch (flag) {
73
+ case "--seed":
74
+ try {
75
+ seed = seedFromHex(value);
76
+ }
77
+ catch (e) {
78
+ throw new Error(`--seed: ${e.message}`);
79
+ }
80
+ break;
81
+ case "--out":
82
+ out = value;
83
+ break;
84
+ case "--pub-out":
85
+ pubOut = value;
86
+ break;
87
+ case "--pub-format":
88
+ // Validated at parse time so a bad format never leaves a private key behind.
89
+ pubFormat = parsePubFormat(value);
90
+ break;
91
+ default:
92
+ throw new Error(`unknown flag ${JSON.stringify(flag)}\n${USAGE}`);
93
+ }
94
+ }
95
+ return { seed: seed ?? new Uint8Array(randomBytes(SEED_SIZE)), out, pubOut, pubFormat };
96
+ }
97
+ /** Scans for `--store <name>` and removes it, the same shape as takeInFlag. */
98
+ function takeStoreFlag(args) {
99
+ const rest = [];
100
+ let store;
101
+ for (let i = 0; i < args.length; i++) {
102
+ if (args[i] === "--store") {
103
+ const v = args[i + 1];
104
+ if (v === undefined || v === "")
105
+ throw new Error('flag "--store" needs a value');
106
+ store = v;
107
+ i++;
108
+ continue;
109
+ }
110
+ rest.push(args[i]);
111
+ }
112
+ return { rest, store };
113
+ }
114
+ //# sourceMappingURL=keygen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keygen.js","sourceRoot":"","sources":["../../../src/cmd/keygen.ts"],"names":[],"mappings":"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"}