@scrthq/runlog 0.0.36 → 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.
- package/dist/runlog.js +273 -47
- 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
|
|
7372
|
-
import { readFileSync as
|
|
7373
|
-
import { basename, dirname, resolve as
|
|
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 {
|
|
28208
|
-
import {
|
|
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
|
|
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 =
|
|
28222
|
-
if (
|
|
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
|
-
|
|
28430
|
+
writeFileSync2(out, `${JSON.stringify(key, null, 2)}
|
|
28236
28431
|
`, "utf8");
|
|
28237
28432
|
try {
|
|
28238
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
28273
|
-
|
|
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 =
|
|
28276
|
-
const signature = await signPack(document, key.privateKey,
|
|
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
|
-
|
|
28479
|
+
writeFileSync2(
|
|
28279
28480
|
path,
|
|
28280
28481
|
format === "json" ? `${JSON.stringify(signed, null, 2)}
|
|
28281
|
-
` :
|
|
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 =
|
|
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 =
|
|
28303
|
-
const text =
|
|
28503
|
+
const path = resolve2(input2);
|
|
28504
|
+
const text = readFileSync2(path, "utf8");
|
|
28304
28505
|
const format = detectFormat(path);
|
|
28305
|
-
const document =
|
|
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
|
-
...
|
|
28512
|
+
...flag2(args, "--ref") ? { reference: flag2(args, "--ref") } : {},
|
|
28312
28513
|
issuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
28313
28514
|
}
|
|
28314
28515
|
};
|
|
28315
|
-
const keyPath =
|
|
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(
|
|
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
|
-
|
|
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 ?
|
|
28536
|
+
const licenseKey = sealing ? flag2(args, "--license") ?? generateLicenseKey() : null;
|
|
28330
28537
|
const extension = sealing ? "rlpack" : format;
|
|
28331
|
-
const out =
|
|
28332
|
-
|
|
28538
|
+
const out = resolve2(
|
|
28539
|
+
flag2(args, "-o") ?? flag2(args, "--out") ?? defaultIssueName(document, to, extension)
|
|
28333
28540
|
);
|
|
28334
|
-
if (
|
|
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
|
-
...
|
|
28547
|
+
...flag2(args, "--ref") ? { ref: flag2(args, "--ref") } : {},
|
|
28341
28548
|
...typeof stamped.title === "string" ? { title: stamped.title } : {}
|
|
28342
28549
|
});
|
|
28343
|
-
|
|
28550
|
+
writeFileSync2(out, bytes2);
|
|
28344
28551
|
} else {
|
|
28345
|
-
|
|
28552
|
+
writeFileSync2(
|
|
28346
28553
|
out,
|
|
28347
28554
|
format === "json" ? `${JSON.stringify(stamped, null, 2)}
|
|
28348
|
-
` :
|
|
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}${
|
|
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 =
|
|
28444
|
-
if (!
|
|
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 =
|
|
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(
|
|
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] ?
|
|
28505
|
-
|
|
28506
|
-
|
|
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 =
|
|
28560
|
-
if (
|
|
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
|
-
|
|
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
|
-
--
|
|
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":
|