@scrthq/runlog 0.0.37 → 0.0.38

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.
Files changed (2) hide show
  1. package/dist/runlog.js +273 -47
  2. package/package.json +1 -1
package/dist/runlog.js CHANGED
@@ -7368,9 +7368,9 @@ var require_dist = __commonJS({
7368
7368
  });
7369
7369
 
7370
7370
  // packages/cli/src/bin.ts
7371
- var import_yaml3 = __toESM(require_dist(), 1);
7372
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2 } from "node:fs";
7373
- import { basename, dirname, resolve as resolve2 } from "node:path";
7371
+ var import_yaml4 = __toESM(require_dist(), 1);
7372
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "node:fs";
7373
+ import { basename, dirname, resolve as resolve3 } from "node:path";
7374
7374
  import { createHash } from "node:crypto";
7375
7375
 
7376
7376
  // node_modules/zod/v4/classic/external.js
@@ -27813,6 +27813,11 @@ async function signPack(document, privateKey, signedBy) {
27813
27813
  ...signedBy ? { signedBy } : {}
27814
27814
  };
27815
27815
  }
27816
+ async function signBytes(bytes2, privateKey) {
27817
+ const key = await crypto.subtle.importKey("pkcs8", fromBase64Url(privateKey), ALGORITHM, false, ["sign"]);
27818
+ const value = await crypto.subtle.sign(SIGN, key, bytes2);
27819
+ return toBase64Url(new Uint8Array(value));
27820
+ }
27816
27821
  async function derivePublicKey(privateKey) {
27817
27822
  const key = await crypto.subtle.importKey(
27818
27823
  "pkcs8",
@@ -28203,9 +28208,199 @@ function runFixtures(pack) {
28203
28208
  }
28204
28209
 
28205
28210
  // packages/cli/src/sign.ts
28211
+ var import_yaml3 = __toESM(require_dist(), 1);
28212
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, chmodSync as chmodSync2 } from "node:fs";
28213
+ import { resolve as resolve2 } from "node:path";
28214
+
28215
+ // packages/cli/src/account.ts
28206
28216
  var import_yaml2 = __toESM(require_dist(), 1);
28207
- import { readFileSync, writeFileSync, existsSync, chmodSync } from "node:fs";
28208
- import { resolve } from "node:path";
28217
+ import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
28218
+ import { homedir } from "node:os";
28219
+ import { join, resolve } from "node:path";
28220
+ import { createInterface } from "node:readline";
28221
+ var DEFAULT_API = "https://runlog.scrthq.com/api";
28222
+ function configDir() {
28223
+ const base = process.env["RUNLOG_CONFIG_DIR"] ?? (process.platform === "win32" ? join(process.env["APPDATA"] ?? join(homedir(), "AppData", "Roaming"), "runlog") : join(process.env["XDG_CONFIG_HOME"] ?? join(homedir(), ".config"), "runlog"));
28224
+ return base;
28225
+ }
28226
+ var credentialsPath = () => join(configDir(), "credentials.json");
28227
+ function credentials() {
28228
+ const fromEnv = process.env["RUNLOG_API_KEY"];
28229
+ if (fromEnv) return { api: process.env["RUNLOG_API"] ?? DEFAULT_API, key: fromEnv, savedAt: "" };
28230
+ try {
28231
+ const raw = JSON.parse(readFileSync(credentialsPath(), "utf8"));
28232
+ if (typeof raw.key !== "string" || typeof raw.api !== "string") return null;
28233
+ return { api: raw.api, key: raw.key, savedAt: raw.savedAt ?? "" };
28234
+ } catch {
28235
+ return null;
28236
+ }
28237
+ }
28238
+ async function ask(prompt, hidden = false) {
28239
+ const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
28240
+ return new Promise((done) => {
28241
+ if (hidden) {
28242
+ const out = process.stdout;
28243
+ process.stdout.write(prompt);
28244
+ const original = out.write.bind(out);
28245
+ out.write = ((chunk, ...rest) => {
28246
+ if (typeof chunk === "string" && chunk !== "\n" && chunk !== "\r\n") return true;
28247
+ return original(chunk, ...rest);
28248
+ });
28249
+ rl.question("", (answer) => {
28250
+ out.write = original;
28251
+ process.stdout.write("\n");
28252
+ rl.close();
28253
+ done(answer.trim());
28254
+ });
28255
+ } else {
28256
+ rl.question(prompt, (answer) => {
28257
+ rl.close();
28258
+ done(answer.trim());
28259
+ });
28260
+ }
28261
+ });
28262
+ }
28263
+ async function api(method, path, body) {
28264
+ const creds = credentials();
28265
+ if (!creds) throw new Error("not signed in: run `runlog login` with a key from your profile page");
28266
+ const response = await fetch(`${creds.api.replace(/\/$/, "")}${path}`, {
28267
+ method,
28268
+ headers: { authorization: `Bearer ${creds.key}`, ...body !== void 0 ? { "content-type": "application/json" } : {} },
28269
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
28270
+ });
28271
+ const text = await response.text();
28272
+ let parsed = null;
28273
+ try {
28274
+ parsed = JSON.parse(text);
28275
+ } catch {
28276
+ throw new Error(`the API at ${creds.api} did not answer as expected (${response.status})`);
28277
+ }
28278
+ if (response.status === 401) throw new Error("that key is not accepted; make a new one on your profile page and `runlog login` again");
28279
+ if (response.status >= 400) {
28280
+ const said = parsed?.error;
28281
+ throw new Error(said ?? `the API said ${response.status}`);
28282
+ }
28283
+ return parsed;
28284
+ }
28285
+ async function cmdLogin(args) {
28286
+ const apiUrl = flag(args, "--api") ?? process.env["RUNLOG_API"] ?? DEFAULT_API;
28287
+ console.log("Paste a command-line key from your profile page in Runlog. It is not shown as you type.");
28288
+ const key = await ask("key: ", true);
28289
+ if (!key.startsWith("rl_")) {
28290
+ console.error("that does not look like a Runlog key; they begin with rl_");
28291
+ return 1;
28292
+ }
28293
+ const dir = configDir();
28294
+ mkdirSync(dir, { recursive: true });
28295
+ const saved = { api: apiUrl, key, savedAt: (/* @__PURE__ */ new Date()).toISOString() };
28296
+ writeFileSync(credentialsPath(), `${JSON.stringify(saved, null, 2)}
28297
+ `, "utf8");
28298
+ try {
28299
+ chmodSync(credentialsPath(), 384);
28300
+ } catch {
28301
+ }
28302
+ try {
28303
+ const me = await api("GET", "/me");
28304
+ const who = me.profile?.name ?? me.profile?.email ?? "you";
28305
+ console.log(`signed in as ${who} at ${apiUrl}`);
28306
+ return 0;
28307
+ } catch (error61) {
28308
+ unlinkSync(credentialsPath());
28309
+ console.error(error61 instanceof Error ? error61.message : String(error61));
28310
+ return 1;
28311
+ }
28312
+ }
28313
+ function cmdLogout() {
28314
+ if (existsSync(credentialsPath())) {
28315
+ unlinkSync(credentialsPath());
28316
+ console.log("signed out; the key is still valid until you revoke it on your profile page");
28317
+ } else {
28318
+ console.log("not signed in");
28319
+ }
28320
+ return 0;
28321
+ }
28322
+ async function cmdWhoami() {
28323
+ try {
28324
+ const me = await api("GET", "/me");
28325
+ const claims = await api("GET", "/claims");
28326
+ console.log(`${me.profile?.name ?? "(no name)"} <${me.profile?.email ?? "?"}> ${me.sub}`);
28327
+ console.log(claims.claims.length > 0 ? `claimed signing keys: ${claims.claims.map((c) => c.fingerprint).join(", ")}` : "no claimed signing keys yet: `runlog claim key.json`");
28328
+ return 0;
28329
+ } catch (error61) {
28330
+ console.error(error61 instanceof Error ? error61.message : String(error61));
28331
+ return 1;
28332
+ }
28333
+ }
28334
+ function readKey(path) {
28335
+ const key = JSON.parse(readFileSync(resolve(path), "utf8"));
28336
+ if (!key.privateKey || !key.publicKey) throw new Error(`${path} is not a runlog signing key`);
28337
+ return { publicKey: key.publicKey, privateKey: key.privateKey, fingerprint: key.fingerprint ?? "" };
28338
+ }
28339
+ async function cmdClaim(args) {
28340
+ const keyPath = args.filter((a) => !a.startsWith("-"))[0];
28341
+ if (!keyPath) {
28342
+ console.error("usage: runlog claim key.json");
28343
+ return 2;
28344
+ }
28345
+ try {
28346
+ const key = readKey(keyPath);
28347
+ const { nonce } = await api("POST", "/claims/nonce");
28348
+ const signature = await signBytes(new TextEncoder().encode(nonce), key.privateKey);
28349
+ const { claim: claim2 } = await api("POST", "/claims", { publicKey: key.publicKey, nonce, signature });
28350
+ console.log(`claimed ${claim2.fingerprint}${claim2.name ? ` as ${claim2.name}` : ""}`);
28351
+ console.log("packs signed with this key now show your name in the app");
28352
+ return 0;
28353
+ } catch (error61) {
28354
+ console.error(error61 instanceof Error ? error61.message : String(error61));
28355
+ return 1;
28356
+ }
28357
+ }
28358
+ async function requireClaimed(publicKey) {
28359
+ const fp = await fingerprint(publicKey);
28360
+ const { claims } = await api("GET", "/claims");
28361
+ if (!claims.some((c) => c.fingerprint === fp)) {
28362
+ throw new Error(`this key (${fp}) is not claimed by your account: run \`runlog claim key.json\` first, so the app can name you`);
28363
+ }
28364
+ }
28365
+ async function cmdPublish(args) {
28366
+ const input2 = args.filter((a) => !a.startsWith("-"))[0];
28367
+ if (!input2) {
28368
+ console.error("usage: runlog publish <pack.yaml|pack.json>");
28369
+ return 2;
28370
+ }
28371
+ try {
28372
+ const path = resolve(input2);
28373
+ const text = readFileSync(path, "utf8");
28374
+ const format = detectFormat(path);
28375
+ const doc = format === "json" ? JSON.parse(text) : import_yaml2.default.parse(text);
28376
+ const id = String(doc["id"] ?? "");
28377
+ if (!id) throw new Error("the pack has no id");
28378
+ const { createHash: createHash2 } = await import("node:crypto");
28379
+ const hash2 = createHash2("sha256").update(text).digest("hex").slice(0, 16);
28380
+ const at = (/* @__PURE__ */ new Date()).toISOString();
28381
+ await api("PUT", `/packs/${encodeURIComponent(id)}`, {
28382
+ title: String(doc["title"] ?? id),
28383
+ version: String(doc["version"] ?? ""),
28384
+ format,
28385
+ filename: input2.split(/[\\/]/).pop(),
28386
+ importedAt: at,
28387
+ updatedAt: at,
28388
+ hash: hash2,
28389
+ source: text
28390
+ });
28391
+ console.log(`published ${id} to your library; it reaches your devices on their next sync`);
28392
+ return 0;
28393
+ } catch (error61) {
28394
+ console.error(error61 instanceof Error ? error61.message : String(error61));
28395
+ return 1;
28396
+ }
28397
+ }
28398
+ function flag(args, name) {
28399
+ const at = args.indexOf(name);
28400
+ return at === -1 ? void 0 : args[at + 1];
28401
+ }
28402
+
28403
+ // packages/cli/src/sign.ts
28209
28404
  var RED = "\x1B[31m";
28210
28405
  var GREEN = "\x1B[32m";
28211
28406
  var YELLOW = "\x1B[33m";
@@ -28213,13 +28408,13 @@ var DIM = "\x1B[2m";
28213
28408
  var RESET = "\x1B[0m";
28214
28409
  var useColor = process.stdout.isTTY && !process.env.NO_COLOR;
28215
28410
  var paint = (code, text) => useColor ? `${code}${text}${RESET}` : text;
28216
- function flag(args, name) {
28411
+ function flag2(args, name) {
28217
28412
  const at = args.indexOf(name);
28218
28413
  return at === -1 ? void 0 : args[at + 1];
28219
28414
  }
28220
28415
  async function cmdKeygen(args) {
28221
- const out = resolve(flag(args, "-o") ?? flag(args, "--out") ?? "runlog-key.json");
28222
- if (existsSync(out)) {
28416
+ const out = resolve2(flag2(args, "-o") ?? flag2(args, "--out") ?? "runlog-key.json");
28417
+ if (existsSync2(out)) {
28223
28418
  console.error(paint(RED, `refusing to overwrite ${out}`));
28224
28419
  console.error(paint(DIM, " a signing key cannot be recovered; delete it yourself if you mean to"));
28225
28420
  return 1;
@@ -28232,10 +28427,10 @@ async function cmdKeygen(args) {
28232
28427
  fingerprint: await fingerprint(pair.publicKey),
28233
28428
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
28234
28429
  };
28235
- writeFileSync(out, `${JSON.stringify(key, null, 2)}
28430
+ writeFileSync2(out, `${JSON.stringify(key, null, 2)}
28236
28431
  `, "utf8");
28237
28432
  try {
28238
- chmodSync(out, 384);
28433
+ chmodSync2(out, 384);
28239
28434
  } catch {
28240
28435
  }
28241
28436
  console.log(`wrote ${out}`);
@@ -28253,14 +28448,14 @@ async function cmdKeygen(args) {
28253
28448
  }
28254
28449
  async function cmdSign(args) {
28255
28450
  const input2 = args.filter((a) => !a.startsWith("-"))[0];
28256
- const keyPath = flag(args, "--key") ?? flag(args, "-k");
28451
+ const keyPath = flag2(args, "--key") ?? flag2(args, "-k");
28257
28452
  if (!input2 || !keyPath) {
28258
28453
  console.error('usage: runlog sign <pack.yaml> --key runlog-key.json [--as "Your Name"]');
28259
28454
  return 2;
28260
28455
  }
28261
28456
  let key;
28262
28457
  try {
28263
- key = JSON.parse(readFileSync(resolve(keyPath), "utf8"));
28458
+ key = JSON.parse(readFileSync2(resolve2(keyPath), "utf8"));
28264
28459
  } catch {
28265
28460
  console.error(paint(RED, `could not read the key at ${keyPath}`));
28266
28461
  return 1;
@@ -28269,16 +28464,22 @@ async function cmdSign(args) {
28269
28464
  console.error(paint(RED, `${keyPath} has no private key in it`));
28270
28465
  return 1;
28271
28466
  }
28272
- const path = resolve(input2);
28273
- const text = readFileSync(path, "utf8");
28467
+ try {
28468
+ await requireClaimed(key.publicKey);
28469
+ } catch (error61) {
28470
+ console.error(paint(RED, error61 instanceof Error ? error61.message : String(error61)));
28471
+ return 1;
28472
+ }
28473
+ const path = resolve2(input2);
28474
+ const text = readFileSync2(path, "utf8");
28274
28475
  const format = detectFormat(path);
28275
- const document = import_yaml2.default.parse(text);
28276
- const signature = await signPack(document, key.privateKey, flag(args, "--as"));
28476
+ const document = import_yaml3.default.parse(text);
28477
+ const signature = await signPack(document, key.privateKey, flag2(args, "--as"));
28277
28478
  const signed = { ...document, signature };
28278
- writeFileSync(
28479
+ writeFileSync2(
28279
28480
  path,
28280
28481
  format === "json" ? `${JSON.stringify(signed, null, 2)}
28281
- ` : import_yaml2.default.stringify(signed, { lineWidth: 90 }),
28482
+ ` : import_yaml3.default.stringify(signed, { lineWidth: 90 }),
28282
28483
  "utf8"
28283
28484
  );
28284
28485
  console.log(`${paint(GREEN, "signed")} ${input2}`);
@@ -28292,65 +28493,71 @@ async function cmdSign(args) {
28292
28493
  }
28293
28494
  async function cmdIssue(args) {
28294
28495
  const input2 = args.filter((a) => !a.startsWith("-"))[0];
28295
- const to = flag(args, "--to");
28496
+ const to = flag2(args, "--to");
28296
28497
  if (!input2 || !to) {
28297
28498
  console.error(
28298
28499
  'usage: runlog issue <pack.yaml> --to "Buyer Name" [--ref order-123] [--key key.json] [-o out.yaml]'
28299
28500
  );
28300
28501
  return 2;
28301
28502
  }
28302
- const path = resolve(input2);
28303
- const text = readFileSync(path, "utf8");
28503
+ const path = resolve2(input2);
28504
+ const text = readFileSync2(path, "utf8");
28304
28505
  const format = detectFormat(path);
28305
- const document = import_yaml2.default.parse(text);
28506
+ const document = import_yaml3.default.parse(text);
28306
28507
  const { signature: _old, ...base } = document;
28307
28508
  const stamped = {
28308
28509
  ...base,
28309
28510
  issue: {
28310
28511
  to,
28311
- ...flag(args, "--ref") ? { reference: flag(args, "--ref") } : {},
28512
+ ...flag2(args, "--ref") ? { reference: flag2(args, "--ref") } : {},
28312
28513
  issuedAt: (/* @__PURE__ */ new Date()).toISOString()
28313
28514
  }
28314
28515
  };
28315
- const keyPath = flag(args, "--key") ?? flag(args, "-k");
28516
+ const keyPath = flag2(args, "--key") ?? flag2(args, "-k");
28316
28517
  let fingerprintText = "";
28317
28518
  if (keyPath) {
28318
28519
  let key;
28319
28520
  try {
28320
- key = JSON.parse(readFileSync(resolve(keyPath), "utf8"));
28521
+ key = JSON.parse(readFileSync2(resolve2(keyPath), "utf8"));
28321
28522
  } catch {
28322
28523
  console.error(paint(RED, `could not read the key at ${keyPath}`));
28323
28524
  return 1;
28324
28525
  }
28325
- stamped.signature = await signPack(stamped, key.privateKey, flag(args, "--as"));
28526
+ try {
28527
+ await requireClaimed(key.publicKey);
28528
+ } catch (error61) {
28529
+ console.error(paint(RED, error61 instanceof Error ? error61.message : String(error61)));
28530
+ return 1;
28531
+ }
28532
+ stamped.signature = await signPack(stamped, key.privateKey, flag2(args, "--as"));
28326
28533
  fingerprintText = key.fingerprint;
28327
28534
  }
28328
28535
  const sealing = args.includes("--seal");
28329
- const licenseKey = sealing ? flag(args, "--license") ?? generateLicenseKey() : null;
28536
+ const licenseKey = sealing ? flag2(args, "--license") ?? generateLicenseKey() : null;
28330
28537
  const extension = sealing ? "rlpack" : format;
28331
- const out = resolve(
28332
- flag(args, "-o") ?? flag(args, "--out") ?? defaultIssueName(document, to, extension)
28538
+ const out = resolve2(
28539
+ flag2(args, "-o") ?? flag2(args, "--out") ?? defaultIssueName(document, to, extension)
28333
28540
  );
28334
- if (existsSync(out)) {
28541
+ if (existsSync2(out)) {
28335
28542
  console.error(paint(RED, `refusing to overwrite ${out}`));
28336
28543
  return 1;
28337
28544
  }
28338
28545
  if (licenseKey) {
28339
28546
  const bytes2 = await seal(stamped, licenseKey, {
28340
- ...flag(args, "--ref") ? { ref: flag(args, "--ref") } : {},
28547
+ ...flag2(args, "--ref") ? { ref: flag2(args, "--ref") } : {},
28341
28548
  ...typeof stamped.title === "string" ? { title: stamped.title } : {}
28342
28549
  });
28343
- writeFileSync(out, bytes2);
28550
+ writeFileSync2(out, bytes2);
28344
28551
  } else {
28345
- writeFileSync(
28552
+ writeFileSync2(
28346
28553
  out,
28347
28554
  format === "json" ? `${JSON.stringify(stamped, null, 2)}
28348
- ` : import_yaml2.default.stringify(stamped, { lineWidth: 90 }),
28555
+ ` : import_yaml3.default.stringify(stamped, { lineWidth: 90 }),
28349
28556
  "utf8"
28350
28557
  );
28351
28558
  }
28352
28559
  console.log(`${paint(GREEN, "issued")} ${out}`);
28353
- console.log(paint(DIM, ` to ${to}${flag(args, "--ref") ? ` \xB7 ref ${flag(args, "--ref")}` : ""}`));
28560
+ console.log(paint(DIM, ` to ${to}${flag2(args, "--ref") ? ` \xB7 ref ${flag2(args, "--ref")}` : ""}`));
28354
28561
  if (keyPath) {
28355
28562
  console.log(paint(DIM, ` signed \xB7 fingerprint ${fingerprintText}`));
28356
28563
  console.log(
@@ -28440,12 +28647,12 @@ function printDiagnostics(diagnostics, file2) {
28440
28647
  console.error(` ${parts.join(", ")} in ${file2}`);
28441
28648
  }
28442
28649
  function read(path) {
28443
- const abs = resolve2(path);
28444
- if (!existsSync2(abs)) {
28650
+ const abs = resolve3(path);
28651
+ if (!existsSync3(abs)) {
28445
28652
  console.error(paint2(RED2, `no such file: ${path}`));
28446
28653
  process.exit(2);
28447
28654
  }
28448
- const text = readFileSync2(abs, "utf8");
28655
+ const text = readFileSync3(abs, "utf8");
28449
28656
  const result = loadPackText(text, detectFormat(abs));
28450
28657
  return { text, pack: result.pack, diagnostics: result.diagnostics };
28451
28658
  }
@@ -28476,7 +28683,7 @@ async function cmdValidate(args) {
28476
28683
  console.log(
28477
28684
  `${paint2(GREEN2, "ok")} ${paint2(BOLD, pack.title)} ${paint2(DIM2, `v${pack.version}`)} \u2014 ${tables} table${tables === 1 ? "" : "s"}, ${entries} entries, ${Object.keys(pack.modes).length} mode(s)` + (diagnostics.length ? paint2(YELLOW2, `, ${diagnostics.length} warning(s)`) : "")
28478
28685
  );
28479
- await reportSignature(import_yaml3.default.parse(readFileSync2(resolve2(file2), "utf8")));
28686
+ await reportSignature(import_yaml4.default.parse(readFileSync3(resolve3(file2), "utf8")));
28480
28687
  }
28481
28688
  }
28482
28689
  return worst;
@@ -28501,9 +28708,9 @@ function cmdBundle(args) {
28501
28708
  const body = JSON.stringify(pack, null, 2);
28502
28709
  const hash2 = createHash("sha256").update(body).digest("hex").slice(0, 16);
28503
28710
  const bundled = { ...pack, bundledAt: (/* @__PURE__ */ new Date()).toISOString(), contentHash: hash2 };
28504
- const out = outIndex >= 0 && args[outIndex + 1] ? resolve2(args[outIndex + 1]) : resolve2(`${pack.id}-${pack.version}.pack.json`);
28505
- mkdirSync(dirname(out), { recursive: true });
28506
- writeFileSync2(out, `${JSON.stringify(bundled, null, 2)}
28711
+ const out = outIndex >= 0 && args[outIndex + 1] ? resolve3(args[outIndex + 1]) : resolve3(`${pack.id}-${pack.version}.pack.json`);
28712
+ mkdirSync2(dirname(out), { recursive: true });
28713
+ writeFileSync3(out, `${JSON.stringify(bundled, null, 2)}
28507
28714
  `, "utf8");
28508
28715
  console.log(`${paint2(GREEN2, "bundled")} ${out} ${paint2(DIM2, `sha256:${hash2}`)}`);
28509
28716
  if (!pack.license.redistributable) {
@@ -28556,12 +28763,12 @@ function cmdTest(args) {
28556
28763
  }
28557
28764
  function cmdInit(args) {
28558
28765
  const name = args.filter((a) => !a.startsWith("-"))[0] ?? "my-pack";
28559
- const out = resolve2(`${name}.yaml`);
28560
- if (existsSync2(out)) {
28766
+ const out = resolve3(`${name}.yaml`);
28767
+ if (existsSync3(out)) {
28561
28768
  console.error(paint2(RED2, `refusing to overwrite ${out}`));
28562
28769
  return 1;
28563
28770
  }
28564
- writeFileSync2(out, SKELETON.replaceAll("__NAME__", name), "utf8");
28771
+ writeFileSync3(out, SKELETON.replaceAll("__NAME__", name), "utf8");
28565
28772
  console.log(`${paint2(GREEN2, "created")} ${out}`);
28566
28773
  console.log(paint2(DIM2, ` next: runlog validate ${name}.yaml`));
28567
28774
  return 0;
@@ -28628,11 +28835,20 @@ usage:
28628
28835
  runlog issue <pack> --to "Name" stamp a copy with a buyer's name and sign it
28629
28836
  [--seal] \u2026and seal it, so it needs a license key to open
28630
28837
 
28631
- --strict makes warnings fail, which is what you want in CI.
28838
+ runlog login [--api URL] paste a key from your profile page, once
28839
+ runlog whoami who the command line is acting as
28840
+ runlog claim key.json prove a signing key is yours; the app then names you
28841
+ runlog publish <pack> put a pack in your library, on every device
28842
+ runlog logout forget the saved key
28843
+
28844
+ --strict makes warnings fail, which is what you want in CI. In CI, set
28845
+ RUNLOG_API_KEY instead of running login.
28632
28846
 
28633
28847
  Signing proves authorship. It does not restrict copying and cannot: the app
28634
28848
  has to read every word of a pack to play it. What it gives you is that an
28635
- altered copy can no longer claim to be yours.
28849
+ altered copy can no longer claim to be yours \u2014 and, once the key is claimed
28850
+ by your account, that the app names you beside it. sign and issue refuse a
28851
+ key that is not claimed.
28636
28852
  `;
28637
28853
  async function main(argv) {
28638
28854
  const [command, ...args] = argv;
@@ -28649,6 +28865,16 @@ async function main(argv) {
28649
28865
  return cmdSign(args);
28650
28866
  case "issue":
28651
28867
  return cmdIssue(args);
28868
+ case "login":
28869
+ return cmdLogin(args);
28870
+ case "logout":
28871
+ return cmdLogout();
28872
+ case "whoami":
28873
+ return cmdWhoami();
28874
+ case "claim":
28875
+ return cmdClaim(args);
28876
+ case "publish":
28877
+ return cmdPublish(args);
28652
28878
  case "init":
28653
28879
  return cmdInit(args);
28654
28880
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrthq/runlog",
3
- "version": "0.0.37",
3
+ "version": "0.0.38",
4
4
  "description": "Validate, lint, bundle and test Runlog rule packs.",
5
5
  "type": "module",
6
6
  "license": "MIT",