rshow 0.0.1 → 0.0.2
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/main.js +851 -332
- package/package.json +4 -3
- package/skills/rshow/reference/cli.md +24 -3
package/dist/main.js
CHANGED
|
@@ -1,8 +1,115 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import path24 from "node:path";
|
|
5
|
+
import process17 from "node:process";
|
|
6
|
+
|
|
7
|
+
// src/binary.ts
|
|
8
|
+
import { mkdir, mkdtemp, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import process from "node:process";
|
|
12
|
+
|
|
13
|
+
// src/version.ts
|
|
14
|
+
import { readFileSync } from "node:fs";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
var VERSION = true ? "0.0.2" : versionFromPackage();
|
|
17
|
+
|
|
18
|
+
// src/binary.ts
|
|
19
|
+
var ASSET_MANIFEST = "assets.json";
|
|
20
|
+
var COMPLETE_MARKER = ".complete";
|
|
21
|
+
var sea;
|
|
22
|
+
function seaModule() {
|
|
23
|
+
if (sea === void 0) {
|
|
24
|
+
sea = import("node:sea").then(
|
|
25
|
+
(mod) => mod,
|
|
26
|
+
() => void 0
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return sea;
|
|
30
|
+
}
|
|
31
|
+
async function isBinary() {
|
|
32
|
+
const mod = await seaModule();
|
|
33
|
+
if (mod === void 0 || typeof mod.isSea !== "function") return false;
|
|
34
|
+
try {
|
|
35
|
+
return mod.isSea();
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function cacheRoot(env = process.env) {
|
|
41
|
+
const xdg = (env.XDG_CACHE_HOME ?? "").trim();
|
|
42
|
+
if (xdg !== "") return path.resolve(xdg);
|
|
43
|
+
const home = (env.HOME ?? "").trim();
|
|
44
|
+
return path.join(home !== "" ? home : homedir(), ".cache");
|
|
45
|
+
}
|
|
46
|
+
function binaryCacheDir(env = process.env, version = VERSION) {
|
|
47
|
+
return path.join(cacheRoot(env), "rshow", version);
|
|
48
|
+
}
|
|
49
|
+
async function exists(file) {
|
|
50
|
+
return await stat(file).catch(() => void 0) !== void 0;
|
|
51
|
+
}
|
|
52
|
+
async function extractAssets(bundle, dir) {
|
|
53
|
+
if (await exists(path.join(dir, COMPLETE_MARKER))) return;
|
|
54
|
+
const parent = path.dirname(dir);
|
|
55
|
+
await mkdir(parent, { recursive: true });
|
|
56
|
+
const tmp = await mkdtemp(path.join(parent, `${path.basename(dir)}.tmp-`));
|
|
57
|
+
try {
|
|
58
|
+
for (const key2 of bundle.keys) {
|
|
59
|
+
const file = path.join(tmp, ...key2.split("/"));
|
|
60
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
61
|
+
await writeFile(file, bundle.read(key2));
|
|
62
|
+
}
|
|
63
|
+
await writeFile(path.join(tmp, COMPLETE_MARKER), `${VERSION}
|
|
64
|
+
`);
|
|
65
|
+
await rm(dir, { recursive: true, force: true });
|
|
66
|
+
await rename(tmp, dir);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
await rm(tmp, { recursive: true, force: true });
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function safeKey(key2) {
|
|
73
|
+
return key2 !== "" && !path.isAbsolute(key2) && !key2.split("/").includes("..");
|
|
74
|
+
}
|
|
75
|
+
function manifestKeys(read2) {
|
|
76
|
+
try {
|
|
77
|
+
const doc = JSON.parse(new TextDecoder().decode(read2(ASSET_MANIFEST)));
|
|
78
|
+
if (doc !== null && typeof doc === "object" && Array.isArray(doc.keys)) {
|
|
79
|
+
const keys = doc.keys.filter((key2) => typeof key2 === "string");
|
|
80
|
+
if (keys.length > 0) return keys.filter(safeKey);
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
function bundleOf(mod) {
|
|
87
|
+
const raw = mod.getRawAsset;
|
|
88
|
+
const read2 = (key2) => new Uint8Array(raw(key2));
|
|
89
|
+
const withManifest = manifestKeys(read2);
|
|
90
|
+
return { keys: withManifest.length > 0 ? withManifest : (mod.getAssetKeys?.() ?? []).filter(safeKey), read: read2 };
|
|
91
|
+
}
|
|
92
|
+
async function unpack(env) {
|
|
93
|
+
if (!await isBinary()) return void 0;
|
|
94
|
+
const mod = await seaModule();
|
|
95
|
+
if (mod?.getRawAsset === void 0) throw new Error("this rshow binary carries no assets");
|
|
96
|
+
const dir = binaryCacheDir(env);
|
|
97
|
+
await extractAssets(bundleOf(mod), dir);
|
|
98
|
+
return dir;
|
|
99
|
+
}
|
|
100
|
+
var root;
|
|
101
|
+
function binaryRoot(env = process.env) {
|
|
102
|
+
if (root === void 0) root = unpack(env);
|
|
103
|
+
return root;
|
|
104
|
+
}
|
|
105
|
+
async function binaryThemeBases(env = process.env) {
|
|
106
|
+
const dir = await binaryRoot(env);
|
|
107
|
+
return dir === void 0 ? [] : [path.join(dir, "themes")];
|
|
108
|
+
}
|
|
109
|
+
async function binarySkillBases(env = process.env) {
|
|
110
|
+
const dir = await binaryRoot(env);
|
|
111
|
+
return dir === void 0 ? [] : [path.join(dir, "skills")];
|
|
112
|
+
}
|
|
6
113
|
|
|
7
114
|
// src/exit.ts
|
|
8
115
|
var EXIT = { ok: 0, usage: 2, auth: 4, api: 5, build: 5, check: 6, compare: 7 };
|
|
@@ -14,18 +121,18 @@ var UsageError = class extends Error {
|
|
|
14
121
|
};
|
|
15
122
|
var AuthError = class extends Error {
|
|
16
123
|
status;
|
|
17
|
-
constructor(message,
|
|
124
|
+
constructor(message, status2) {
|
|
18
125
|
super(message);
|
|
19
126
|
this.name = "AuthError";
|
|
20
|
-
this.status =
|
|
127
|
+
this.status = status2;
|
|
21
128
|
}
|
|
22
129
|
};
|
|
23
130
|
var ApiError = class extends Error {
|
|
24
131
|
status;
|
|
25
|
-
constructor(message,
|
|
132
|
+
constructor(message, status2 = 0) {
|
|
26
133
|
super(message);
|
|
27
134
|
this.name = "ApiError";
|
|
28
|
-
this.status =
|
|
135
|
+
this.status = status2;
|
|
29
136
|
}
|
|
30
137
|
};
|
|
31
138
|
var BuildError = class extends Error {
|
|
@@ -41,20 +148,15 @@ var CheckError = class extends Error {
|
|
|
41
148
|
}
|
|
42
149
|
};
|
|
43
150
|
|
|
44
|
-
// src/version.ts
|
|
45
|
-
import { readFileSync } from "node:fs";
|
|
46
|
-
import { fileURLToPath } from "node:url";
|
|
47
|
-
var VERSION = true ? "0.0.1" : versionFromPackage();
|
|
48
|
-
|
|
49
151
|
// src/client.ts
|
|
50
152
|
function messageOf(err) {
|
|
51
153
|
return err instanceof Error ? err.message : String(err);
|
|
52
154
|
}
|
|
53
|
-
function errorText(body,
|
|
155
|
+
function errorText(body, status2) {
|
|
54
156
|
if (body !== null && typeof body === "object" && typeof body.error === "string") {
|
|
55
157
|
return body.error;
|
|
56
158
|
}
|
|
57
|
-
return `HTTP ${
|
|
159
|
+
return `HTTP ${status2}`;
|
|
58
160
|
}
|
|
59
161
|
function parseBody(text) {
|
|
60
162
|
if (text.trim() === "") return void 0;
|
|
@@ -179,20 +281,20 @@ var Client = class {
|
|
|
179
281
|
|
|
180
282
|
// src/config.ts
|
|
181
283
|
import { readFile } from "node:fs/promises";
|
|
182
|
-
import { homedir } from "node:os";
|
|
183
|
-
import
|
|
284
|
+
import { homedir as homedir2 } from "node:os";
|
|
285
|
+
import path2 from "node:path";
|
|
184
286
|
|
|
185
287
|
// src/keychain.ts
|
|
186
288
|
import { spawn } from "node:child_process";
|
|
187
|
-
import
|
|
289
|
+
import process2 from "node:process";
|
|
188
290
|
var SERVICE = "rshow";
|
|
189
291
|
var MISSING = 44;
|
|
190
292
|
var NO_MACOS = "the keychain needs macOS; set RSHOW_TOKEN instead";
|
|
191
293
|
function bin() {
|
|
192
|
-
return
|
|
294
|
+
return process2.env.RSHOW_SECURITY_BIN ?? "security";
|
|
193
295
|
}
|
|
194
296
|
function usable() {
|
|
195
|
-
return
|
|
297
|
+
return process2.platform === "darwin" || process2.env.RSHOW_SECURITY_BIN !== void 0;
|
|
196
298
|
}
|
|
197
299
|
function run(args, input) {
|
|
198
300
|
return new Promise((resolve2, reject) => {
|
|
@@ -322,11 +424,11 @@ function withoutSlash(value) {
|
|
|
322
424
|
function globalConfigPath(env) {
|
|
323
425
|
const xdg = (env.XDG_CONFIG_HOME ?? "").trim();
|
|
324
426
|
const home = (env.HOME ?? "").trim();
|
|
325
|
-
const base = xdg !== "" ? xdg :
|
|
326
|
-
return
|
|
427
|
+
const base = xdg !== "" ? xdg : path2.join(home !== "" ? home : homedir2(), ".config");
|
|
428
|
+
return path2.join(base, GLOBAL_DIR, GLOBAL_FILE);
|
|
327
429
|
}
|
|
328
430
|
function localConfigPath(dir) {
|
|
329
|
-
return
|
|
431
|
+
return path2.join(path2.resolve(dir), LOCAL_FILE);
|
|
330
432
|
}
|
|
331
433
|
function layerOf(file, value) {
|
|
332
434
|
if (!isRecord(value)) throw new UsageError(`${file} must hold a JSON object`);
|
|
@@ -442,7 +544,7 @@ async function readToken(env, url) {
|
|
|
442
544
|
}
|
|
443
545
|
|
|
444
546
|
// src/output.ts
|
|
445
|
-
import
|
|
547
|
+
import process3 from "node:process";
|
|
446
548
|
var Output = class {
|
|
447
549
|
json;
|
|
448
550
|
constructor(json) {
|
|
@@ -450,16 +552,16 @@ var Output = class {
|
|
|
450
552
|
}
|
|
451
553
|
data(value) {
|
|
452
554
|
if (!this.json) return;
|
|
453
|
-
|
|
555
|
+
process3.stdout.write(`${JSON.stringify(value ?? null, null, 2)}
|
|
454
556
|
`);
|
|
455
557
|
}
|
|
456
558
|
line(text) {
|
|
457
559
|
if (this.json) return;
|
|
458
|
-
|
|
560
|
+
process3.stdout.write(`${text}
|
|
459
561
|
`);
|
|
460
562
|
}
|
|
461
563
|
raw(text) {
|
|
462
|
-
|
|
564
|
+
process3.stdout.write(`${text}
|
|
463
565
|
`);
|
|
464
566
|
}
|
|
465
567
|
};
|
|
@@ -475,12 +577,13 @@ commands:
|
|
|
475
577
|
preview <dir> serve a folder on 127.0.0.1 for a look, offline
|
|
476
578
|
config [dir] print the merged config as JSON
|
|
477
579
|
init --url u --art a write the global config file
|
|
478
|
-
doctor [dir] check node, config, token, api, art
|
|
580
|
+
doctor [dir] check node, config, token, api, art, theme, skills
|
|
479
581
|
theme new <name> write a preset folder from a built-in theme
|
|
480
582
|
theme check <dir> check a theme folder, preset or full
|
|
481
583
|
theme preview <dir> build a theme, serve it on 127.0.0.1 for a look
|
|
482
584
|
theme publish <dir> publish a theme folder as theme-<name>
|
|
483
585
|
theme list list theme artifacts
|
|
586
|
+
skills <cmd> install, remove or show the agent skills, per target
|
|
484
587
|
project <cmd> create, list, show, share or delete a project
|
|
485
588
|
tag <slug> add or remove tags on an artifact
|
|
486
589
|
list [filters] list artifacts you own or share
|
|
@@ -616,8 +719,9 @@ Prints the path it wrote and the next step: rshow login. --json prints
|
|
|
616
719
|
var DOCTOR_USAGE = `usage: rshow doctor [dir] [--json]
|
|
617
720
|
|
|
618
721
|
Checks the rshow version, the node version against the engines.node of the
|
|
619
|
-
package, the config files, the token, the api, the art host
|
|
620
|
-
line each: ok|warn|fail <check> <detail>. With [dir] it reads
|
|
722
|
+
package, the config files, the token, the api, the art host, the theme and the
|
|
723
|
+
skills, one line each: ok|warn|fail <check> <detail>. With [dir] it reads
|
|
724
|
+
<dir>/rshow.json too. A skill line warns and never fails.
|
|
621
725
|
Network calls time out after 5 s. Exit 0 when no check failed, 6 when one did.
|
|
622
726
|
The token is never printed.`;
|
|
623
727
|
var THEME_USAGE = `usage: rshow theme <new|check|preview|publish|list> [options]
|
|
@@ -676,6 +780,54 @@ Prints /_themes/<name>@<n>/.`;
|
|
|
676
780
|
var THEME_LIST_USAGE = `usage: rshow theme list
|
|
677
781
|
|
|
678
782
|
Lists artifacts whose slug starts with theme- and their served version.`;
|
|
783
|
+
var SKILLS_USAGE = `usage: rshow skills <install|uninstall|status> [target ...] [options]
|
|
784
|
+
|
|
785
|
+
skills install [target ...] link or copy the three skills into an agent
|
|
786
|
+
skills uninstall [target ...] remove the links and copies rshow made
|
|
787
|
+
skills status [target ...] say what each target holds, per skill
|
|
788
|
+
|
|
789
|
+
A target is where an agent reads its skills from:
|
|
790
|
+
claude $HOME/.claude/skills
|
|
791
|
+
pi $HOME/.pi/agent/skills
|
|
792
|
+
codex $HOME/.codex/skills
|
|
793
|
+
With no target, every target whose parent folder exists is used.`;
|
|
794
|
+
var SKILLS_INSTALL_USAGE = `usage: rshow skills install [target ...] [--link|--copy] [--dry-run]
|
|
795
|
+
|
|
796
|
+
Puts the three skills rshow ships, rshow, rshow-slides and rshow-implement, in
|
|
797
|
+
the skills folder of every target, so that claude, pi and codex read them:
|
|
798
|
+
$HOME/.claude/skills, $HOME/.pi/agent/skills and $HOME/.codex/skills. With no
|
|
799
|
+
target, every target whose parent folder exists is used; naming one of claude,
|
|
800
|
+
pi and codex picks it, and a usage error names them when none is detected.
|
|
801
|
+
Each skill is a symlink to the folder rshow reads it from, so an upgrade of
|
|
802
|
+
rshow is nothing to do. --copy copies the folder and writes a .rshow-skill
|
|
803
|
+
marker in it, {"version": <rshow version>, "source": "rshow"}, which is how a
|
|
804
|
+
newer rshow knows the copy is its own. The default is a link, and a copy when
|
|
805
|
+
that folder sits in an npx cache, which npm may delete under a link, or when
|
|
806
|
+
rshow runs as a single executable, whose files live in a per-version cache.
|
|
807
|
+
A skill that is ours and current prints ok, and one of ours from another path or
|
|
808
|
+
another version is replaced and prints updated. Anything else, a folder, a file
|
|
809
|
+
or a link that rshow did not make, is left alone: skip goes to stderr, the rest
|
|
810
|
+
is still installed, and the run exits 6.
|
|
811
|
+
--dry-run prints what would happen, with would, and changes nothing.
|
|
812
|
+
Prints one line per skill, then installed <n> skills for <targets>; --json
|
|
813
|
+
prints {targets, actions: [{target, skill, action, path}]}.
|
|
814
|
+
RSHOW_SKILLS_DIR reads the skills from another folder.`;
|
|
815
|
+
var SKILLS_UNINSTALL_USAGE = `usage: rshow skills uninstall [target ...] [--dry-run]
|
|
816
|
+
|
|
817
|
+
Removes the skills rshow put in every target: a symlink into a skills folder
|
|
818
|
+
that holds rshow skills, and a copy that carries the .rshow-skill marker.
|
|
819
|
+
It removes nothing else, with no line and no error. With no target, every
|
|
820
|
+
target whose parent folder exists is used.
|
|
821
|
+
Prints removed <path> per skill, then removed <n> skills for <targets>, and
|
|
822
|
+
--dry-run prints what would happen, with would, and changes nothing.`;
|
|
823
|
+
var SKILLS_STATUS_USAGE = `usage: rshow skills status [target ...] [--json]
|
|
824
|
+
|
|
825
|
+
Prints one line per target and skill, <target> <skill> <state>, where the
|
|
826
|
+
state is link -> <path>, copy v<version>, missing or foreign. It prints nothing
|
|
827
|
+
for a target whose parent folder does not exist, and --json prints {targets,
|
|
828
|
+
actions: [{target, skill, action, path}]}.
|
|
829
|
+
A copy of another version also warns on stderr, because rshow skills install
|
|
830
|
+
replaces it.`;
|
|
679
831
|
var LIST_USAGE = `usage: rshow list [--project p] [--tag t] [--q text]
|
|
680
832
|
|
|
681
833
|
Lists artifacts you own or that are shared with you.
|
|
@@ -830,8 +982,8 @@ function usageLine(usage) {
|
|
|
830
982
|
}
|
|
831
983
|
|
|
832
984
|
// src/commands/publish.ts
|
|
833
|
-
import { stat as
|
|
834
|
-
import
|
|
985
|
+
import { stat as stat4 } from "node:fs/promises";
|
|
986
|
+
import path7 from "node:path";
|
|
835
987
|
|
|
836
988
|
// src/args.ts
|
|
837
989
|
import { parseArgs } from "node:util";
|
|
@@ -905,7 +1057,7 @@ function emailList(args, name) {
|
|
|
905
1057
|
|
|
906
1058
|
// src/build.ts
|
|
907
1059
|
import { spawn as spawn2 } from "node:child_process";
|
|
908
|
-
import
|
|
1060
|
+
import process4 from "node:process";
|
|
909
1061
|
function messageOf4(err) {
|
|
910
1062
|
return err instanceof Error ? err.message : String(err);
|
|
911
1063
|
}
|
|
@@ -913,7 +1065,7 @@ function shellPath(env) {
|
|
|
913
1065
|
const shell = (env.SHELL ?? "").trim();
|
|
914
1066
|
return shell === "" ? "/bin/sh" : shell;
|
|
915
1067
|
}
|
|
916
|
-
async function runBuild(dir, command, env =
|
|
1068
|
+
async function runBuild(dir, command, env = process4.env) {
|
|
917
1069
|
const code = await new Promise((resolve2, reject) => {
|
|
918
1070
|
const child = spawn2(shellPath(env), ["-c", command], { cwd: dir, stdio: "inherit" });
|
|
919
1071
|
child.once("error", reject);
|
|
@@ -927,12 +1079,12 @@ async function runBuild(dir, command, env = process3.env) {
|
|
|
927
1079
|
}
|
|
928
1080
|
|
|
929
1081
|
// src/check.ts
|
|
930
|
-
import { readFile as readFile4, readdir, stat } from "node:fs/promises";
|
|
931
|
-
import
|
|
1082
|
+
import { readFile as readFile4, readdir, stat as stat2 } from "node:fs/promises";
|
|
1083
|
+
import path5 from "node:path";
|
|
932
1084
|
|
|
933
1085
|
// src/mockup.ts
|
|
934
1086
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
935
|
-
import
|
|
1087
|
+
import path3 from "node:path";
|
|
936
1088
|
var MAX_SCAN_BYTES = 4 * 1024 * 1024;
|
|
937
1089
|
var HTML_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".htm"]);
|
|
938
1090
|
var MIN_COLS = 80;
|
|
@@ -1127,7 +1279,7 @@ function htmlWarnings(rel, text) {
|
|
|
1127
1279
|
async function mockupWarnings(entries) {
|
|
1128
1280
|
const out = [];
|
|
1129
1281
|
for (const entry of entries) {
|
|
1130
|
-
const ext =
|
|
1282
|
+
const ext = path3.extname(entry.rel).toLowerCase();
|
|
1131
1283
|
if (!HTML_EXTENSIONS.has(ext) || entry.size > MAX_SCAN_BYTES) continue;
|
|
1132
1284
|
const text = await readFile2(entry.abs, "utf8").catch(() => void 0);
|
|
1133
1285
|
if (text === void 0) continue;
|
|
@@ -1138,7 +1290,7 @@ async function mockupWarnings(entries) {
|
|
|
1138
1290
|
|
|
1139
1291
|
// src/style.ts
|
|
1140
1292
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
1141
|
-
import
|
|
1293
|
+
import path4 from "node:path";
|
|
1142
1294
|
var MAX_SCAN_BYTES2 = 4 * 1024 * 1024;
|
|
1143
1295
|
var STYLE_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".htm", ".css"]);
|
|
1144
1296
|
var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
@@ -1535,7 +1687,7 @@ async function styleWarnings(entries) {
|
|
|
1535
1687
|
const loaded = /* @__PURE__ */ new Set();
|
|
1536
1688
|
const used = [];
|
|
1537
1689
|
for (const entry of entries) {
|
|
1538
|
-
const ext =
|
|
1690
|
+
const ext = path4.extname(entry.rel).toLowerCase();
|
|
1539
1691
|
if (!STYLE_EXTENSIONS.has(ext) || entry.size > MAX_SCAN_BYTES2) continue;
|
|
1540
1692
|
const text = await readFile3(entry.abs, "utf8").catch(() => void 0);
|
|
1541
1693
|
if (text === void 0) continue;
|
|
@@ -1665,7 +1817,7 @@ function resolvePath(file, ref) {
|
|
|
1665
1817
|
decoded = ref;
|
|
1666
1818
|
}
|
|
1667
1819
|
const wantsIndex = decoded.endsWith("/");
|
|
1668
|
-
const base =
|
|
1820
|
+
const base = path5.posix.dirname(file);
|
|
1669
1821
|
const parts2 = base === "." ? [] : base.split("/");
|
|
1670
1822
|
for (const segment of decoded.split("/")) {
|
|
1671
1823
|
if (segment === "" || segment === ".") continue;
|
|
@@ -1788,7 +1940,7 @@ function scanHtml(scan, file, text, facts) {
|
|
|
1788
1940
|
scanConnect(scan, file, masked, at);
|
|
1789
1941
|
}
|
|
1790
1942
|
async function scanFile(scan, entry, facts) {
|
|
1791
|
-
const ext =
|
|
1943
|
+
const ext = path5.extname(entry.rel).toLowerCase();
|
|
1792
1944
|
if (!SCAN_EXTENSIONS.has(ext) || entry.size > MAX_SCAN_BYTES3) return;
|
|
1793
1945
|
const text = await readFile4(entry.abs, "utf8");
|
|
1794
1946
|
if (ext === ".html" || ext === ".htm") {
|
|
@@ -1805,7 +1957,7 @@ async function collect(scan, dir, prefix, entries) {
|
|
|
1805
1957
|
for (const item of items) {
|
|
1806
1958
|
if (item.name.startsWith(".") || SKIPPED_NAMES.has(item.name)) continue;
|
|
1807
1959
|
const rel = prefix === "" ? item.name : `${prefix}/${item.name}`;
|
|
1808
|
-
const abs =
|
|
1960
|
+
const abs = path5.join(dir, item.name);
|
|
1809
1961
|
const problem = pathProblem(rel);
|
|
1810
1962
|
if (problem !== void 0) add(scan, rel, 1, "error", problem);
|
|
1811
1963
|
if (item.isDirectory()) {
|
|
@@ -1813,54 +1965,54 @@ async function collect(scan, dir, prefix, entries) {
|
|
|
1813
1965
|
continue;
|
|
1814
1966
|
}
|
|
1815
1967
|
if (!item.isFile()) continue;
|
|
1816
|
-
const info = await
|
|
1968
|
+
const info = await stat2(abs).catch(() => void 0);
|
|
1817
1969
|
if (info === void 0) continue;
|
|
1818
1970
|
entries.push({ rel, abs, size: info.size });
|
|
1819
1971
|
}
|
|
1820
1972
|
}
|
|
1821
1973
|
async function checkThemeFile(scan, key2, rel, where) {
|
|
1822
1974
|
const url = `${scan.art}/_themes/${key2}/${rel}`;
|
|
1823
|
-
let
|
|
1975
|
+
let status2;
|
|
1824
1976
|
try {
|
|
1825
1977
|
const reply = await scan.fetchImpl(url, { method: "HEAD" });
|
|
1826
|
-
|
|
1978
|
+
status2 = reply.status;
|
|
1827
1979
|
} catch (err) {
|
|
1828
1980
|
add(scan, where.file, where.line, "warning", `cannot check theme ${key2}/${rel}: ${messageOf5(err)}`);
|
|
1829
1981
|
return;
|
|
1830
1982
|
}
|
|
1831
|
-
if (
|
|
1832
|
-
else if (
|
|
1833
|
-
add(scan, where.file, where.line, "warning", `cannot check theme ${key2}/${rel}: HTTP ${
|
|
1983
|
+
if (status2 === 404) add(scan, where.file, where.line, "error", `theme ${key2} has no ${rel} (${url})`);
|
|
1984
|
+
else if (status2 < 200 || status2 > 399) {
|
|
1985
|
+
add(scan, where.file, where.line, "warning", `cannot check theme ${key2}/${rel}: HTTP ${status2}`);
|
|
1834
1986
|
}
|
|
1835
1987
|
}
|
|
1836
1988
|
async function checkTheme(scan, key2, where) {
|
|
1837
1989
|
if (scan.offline || scan.art === "") return;
|
|
1838
1990
|
const url = `${scan.art}/_themes/${key2}/tokens.css`;
|
|
1839
|
-
let
|
|
1991
|
+
let status2;
|
|
1840
1992
|
try {
|
|
1841
1993
|
const reply = await scan.fetchImpl(url, { method: "HEAD" });
|
|
1842
|
-
|
|
1994
|
+
status2 = reply.status;
|
|
1843
1995
|
} catch (err) {
|
|
1844
1996
|
add(scan, where.file, where.line, "warning", `cannot check theme ${key2}: ${messageOf5(err)}`);
|
|
1845
1997
|
return;
|
|
1846
1998
|
}
|
|
1847
|
-
if (
|
|
1999
|
+
if (status2 === 404) {
|
|
1848
2000
|
add(scan, where.file, where.line, "error", `theme ${key2} is not published (${url})`);
|
|
1849
2001
|
return;
|
|
1850
2002
|
}
|
|
1851
|
-
if (
|
|
1852
|
-
add(scan, where.file, where.line, "warning", `cannot check theme ${key2}: HTTP ${
|
|
2003
|
+
if (status2 < 200 || status2 > 399) {
|
|
2004
|
+
add(scan, where.file, where.line, "warning", `cannot check theme ${key2}: HTTP ${status2}`);
|
|
1853
2005
|
return;
|
|
1854
2006
|
}
|
|
1855
|
-
if (
|
|
2007
|
+
if (status2 > 299) return;
|
|
1856
2008
|
for (const [rel, ref] of where.files) {
|
|
1857
2009
|
if (rel === "tokens.css") continue;
|
|
1858
2010
|
await checkThemeFile(scan, key2, rel, ref);
|
|
1859
2011
|
}
|
|
1860
2012
|
}
|
|
1861
2013
|
async function checkFolder(input, opts = {}) {
|
|
1862
|
-
const
|
|
1863
|
-
const info = await
|
|
2014
|
+
const root2 = path5.resolve(input);
|
|
2015
|
+
const info = await stat2(root2).catch(() => void 0);
|
|
1864
2016
|
if (info === void 0 || !info.isDirectory()) throw new UsageError(`no such folder: ${input}`);
|
|
1865
2017
|
const scan = {
|
|
1866
2018
|
files: /* @__PURE__ */ new Set(),
|
|
@@ -1871,7 +2023,7 @@ async function checkFolder(input, opts = {}) {
|
|
|
1871
2023
|
themes: /* @__PURE__ */ new Map()
|
|
1872
2024
|
};
|
|
1873
2025
|
const entries = [];
|
|
1874
|
-
await collect(scan,
|
|
2026
|
+
await collect(scan, root2, "", entries);
|
|
1875
2027
|
let bytes = 0;
|
|
1876
2028
|
for (const entry of entries) {
|
|
1877
2029
|
scan.files.add(entry.rel);
|
|
@@ -1952,7 +2104,7 @@ function reportCheck(out, result) {
|
|
|
1952
2104
|
out.line(summaryLine(result));
|
|
1953
2105
|
}
|
|
1954
2106
|
async function enforceCheck(out, target, opts = {}) {
|
|
1955
|
-
const info = await
|
|
2107
|
+
const info = await stat2(target).catch(() => void 0);
|
|
1956
2108
|
if (info === void 0 || !info.isDirectory()) return;
|
|
1957
2109
|
const result = await checkFolder(target, opts);
|
|
1958
2110
|
if (result.findings.length > 0) reportCheck(out, result);
|
|
@@ -1964,8 +2116,8 @@ import { readFile as readFile6 } from "node:fs/promises";
|
|
|
1964
2116
|
|
|
1965
2117
|
// src/files.ts
|
|
1966
2118
|
import { createHash } from "node:crypto";
|
|
1967
|
-
import { readdir as readdir2, readFile as readFile5, stat as
|
|
1968
|
-
import
|
|
2119
|
+
import { readdir as readdir2, readFile as readFile5, stat as stat3 } from "node:fs/promises";
|
|
2120
|
+
import path6 from "node:path";
|
|
1969
2121
|
var CONTENT_TYPES = {
|
|
1970
2122
|
html: "text/html; charset=utf-8",
|
|
1971
2123
|
css: "text/css; charset=utf-8",
|
|
@@ -1997,7 +2149,7 @@ var CONTENT_TYPES = {
|
|
|
1997
2149
|
var DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
1998
2150
|
var SKIPPED_NAMES2 = /* @__PURE__ */ new Set(["node_modules"]);
|
|
1999
2151
|
function contentTypeFor(filePath) {
|
|
2000
|
-
const ext =
|
|
2152
|
+
const ext = path6.extname(filePath).replace(/^\./, "").toLowerCase();
|
|
2001
2153
|
return CONTENT_TYPES[ext] ?? DEFAULT_CONTENT_TYPE;
|
|
2002
2154
|
}
|
|
2003
2155
|
function sha256Hex(bytes) {
|
|
@@ -2013,7 +2165,7 @@ async function readEntry(abs, rel) {
|
|
|
2013
2165
|
type: contentTypeFor(rel)
|
|
2014
2166
|
};
|
|
2015
2167
|
}
|
|
2016
|
-
async function collectPaths(
|
|
2168
|
+
async function collectPaths(root2) {
|
|
2017
2169
|
const found = [];
|
|
2018
2170
|
async function walk2(dir, prefix) {
|
|
2019
2171
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
@@ -2022,7 +2174,7 @@ async function collectPaths(root) {
|
|
|
2022
2174
|
if (entry.name.startsWith(".")) continue;
|
|
2023
2175
|
if (SKIPPED_NAMES2.has(entry.name)) continue;
|
|
2024
2176
|
const rel = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
2025
|
-
const abs =
|
|
2177
|
+
const abs = path6.join(dir, entry.name);
|
|
2026
2178
|
if (entry.isDirectory()) {
|
|
2027
2179
|
await walk2(abs, rel);
|
|
2028
2180
|
continue;
|
|
@@ -2031,22 +2183,22 @@ async function collectPaths(root) {
|
|
|
2031
2183
|
found.push(rel);
|
|
2032
2184
|
}
|
|
2033
2185
|
}
|
|
2034
|
-
await walk2(
|
|
2186
|
+
await walk2(root2, "");
|
|
2035
2187
|
return found;
|
|
2036
2188
|
}
|
|
2037
2189
|
async function buildManifest(input, opts = {}) {
|
|
2038
|
-
const
|
|
2039
|
-
const info = await
|
|
2190
|
+
const root2 = path6.resolve(input);
|
|
2191
|
+
const info = await stat3(root2).catch(() => void 0);
|
|
2040
2192
|
if (info === void 0) throw new UsageError(`no such file or folder: ${input}`);
|
|
2041
2193
|
const entries = [];
|
|
2042
2194
|
if (info.isFile()) {
|
|
2043
|
-
entries.push(await readEntry(
|
|
2195
|
+
entries.push(await readEntry(root2, "index.html"));
|
|
2044
2196
|
} else if (info.isDirectory()) {
|
|
2045
|
-
const paths = await collectPaths(
|
|
2197
|
+
const paths = await collectPaths(root2);
|
|
2046
2198
|
if (opts.requireIndex !== false && !paths.includes("index.html")) {
|
|
2047
2199
|
throw new UsageError(`${input} has no index.html`);
|
|
2048
2200
|
}
|
|
2049
|
-
for (const rel of paths) entries.push(await readEntry(
|
|
2201
|
+
for (const rel of paths) entries.push(await readEntry(path6.join(root2, rel), rel));
|
|
2050
2202
|
} else {
|
|
2051
2203
|
throw new UsageError(`not a file or folder: ${input}`);
|
|
2052
2204
|
}
|
|
@@ -2192,7 +2344,7 @@ function sameFiles(local, remote) {
|
|
|
2192
2344
|
const paths = Object.keys(local);
|
|
2193
2345
|
const other = remote;
|
|
2194
2346
|
if (paths.length !== Object.keys(other).length) return false;
|
|
2195
|
-
return paths.every((
|
|
2347
|
+
return paths.every((path25) => other[path25]?.hash === local[path25]?.hash);
|
|
2196
2348
|
}
|
|
2197
2349
|
function reportUnchanged(out, result) {
|
|
2198
2350
|
out.data({ unchanged: true, slug: result.slug, version: result.version.n });
|
|
@@ -2221,23 +2373,23 @@ function validateSlug(slug) {
|
|
|
2221
2373
|
}
|
|
2222
2374
|
|
|
2223
2375
|
// src/warn.ts
|
|
2224
|
-
import
|
|
2376
|
+
import process5 from "node:process";
|
|
2225
2377
|
function warn2(message) {
|
|
2226
|
-
|
|
2378
|
+
process5.stderr.write(`rshow: warning: ${message}
|
|
2227
2379
|
`);
|
|
2228
2380
|
}
|
|
2229
2381
|
|
|
2230
2382
|
// src/commands/publish.ts
|
|
2231
2383
|
async function directoryOf(input) {
|
|
2232
|
-
const dir =
|
|
2233
|
-
const info = await
|
|
2384
|
+
const dir = path7.resolve(input);
|
|
2385
|
+
const info = await stat4(dir).catch(() => void 0);
|
|
2234
2386
|
return info !== void 0 && info.isDirectory() ? dir : void 0;
|
|
2235
2387
|
}
|
|
2236
2388
|
async function targetOf(input, dir, built, out) {
|
|
2237
|
-
if (dir === void 0) return
|
|
2238
|
-
if (built) return
|
|
2389
|
+
if (dir === void 0) return path7.resolve(input);
|
|
2390
|
+
if (built) return path7.resolve(dir, out);
|
|
2239
2391
|
const local = await readConfigFile(localConfigPath(dir));
|
|
2240
|
-
return local.out === void 0 ? dir :
|
|
2392
|
+
return local.out === void 0 ? dir : path7.resolve(dir, out);
|
|
2241
2393
|
}
|
|
2242
2394
|
async function run2(argv, ctx) {
|
|
2243
2395
|
const args = parseCommandArgs(
|
|
@@ -2266,7 +2418,7 @@ async function run2(argv, ctx) {
|
|
|
2266
2418
|
}
|
|
2267
2419
|
const target = await targetOf(input, dir, build !== void 0, config.out);
|
|
2268
2420
|
const slug = validateSlug(
|
|
2269
|
-
stringValue(args, "slug") ?? config.slug ?? deriveSlug(
|
|
2421
|
+
stringValue(args, "slug") ?? config.slug ?? deriveSlug(path7.basename(path7.resolve(input)))
|
|
2270
2422
|
);
|
|
2271
2423
|
const title = stringValue(args, "title") ?? config.title;
|
|
2272
2424
|
const label = stringValue(args, "label");
|
|
@@ -2301,8 +2453,8 @@ async function run2(argv, ctx) {
|
|
|
2301
2453
|
}
|
|
2302
2454
|
|
|
2303
2455
|
// src/commands/check.ts
|
|
2304
|
-
import { stat as
|
|
2305
|
-
import
|
|
2456
|
+
import { stat as stat5 } from "node:fs/promises";
|
|
2457
|
+
import path8 from "node:path";
|
|
2306
2458
|
async function run3(argv, ctx) {
|
|
2307
2459
|
const args = parseCommandArgs(
|
|
2308
2460
|
argv,
|
|
@@ -2312,8 +2464,8 @@ async function run3(argv, ctx) {
|
|
|
2312
2464
|
CHECK_USAGE
|
|
2313
2465
|
);
|
|
2314
2466
|
const input = args.positionals[0];
|
|
2315
|
-
const dir =
|
|
2316
|
-
const info = await
|
|
2467
|
+
const dir = path8.resolve(input);
|
|
2468
|
+
const info = await stat5(dir).catch(() => void 0);
|
|
2317
2469
|
if (info === void 0 || !info.isDirectory()) throw new UsageError(`no such folder: ${input}`);
|
|
2318
2470
|
const config = await ctx.config(dir);
|
|
2319
2471
|
const offline = boolValue(args, "offline");
|
|
@@ -2336,21 +2488,21 @@ async function run3(argv, ctx) {
|
|
|
2336
2488
|
}
|
|
2337
2489
|
|
|
2338
2490
|
// src/commands/preview.ts
|
|
2339
|
-
import { stat as
|
|
2340
|
-
import
|
|
2491
|
+
import { stat as stat8 } from "node:fs/promises";
|
|
2492
|
+
import path11 from "node:path";
|
|
2341
2493
|
|
|
2342
2494
|
// src/serve.ts
|
|
2343
2495
|
import { watch } from "node:fs";
|
|
2344
|
-
import { mkdtemp, readFile as readFile8, rm as
|
|
2496
|
+
import { mkdtemp as mkdtemp2, readFile as readFile8, rm as rm3, stat as stat7, writeFile as writeFile3 } from "node:fs/promises";
|
|
2345
2497
|
import { createHash as createHash2 } from "node:crypto";
|
|
2346
2498
|
import { createServer } from "node:http";
|
|
2347
2499
|
import { tmpdir } from "node:os";
|
|
2348
|
-
import
|
|
2349
|
-
import
|
|
2500
|
+
import path10 from "node:path";
|
|
2501
|
+
import process6 from "node:process";
|
|
2350
2502
|
|
|
2351
2503
|
// src/themes.ts
|
|
2352
|
-
import { cp, mkdir, readFile as readFile7, rm, stat as
|
|
2353
|
-
import
|
|
2504
|
+
import { cp, mkdir as mkdir2, readFile as readFile7, rm as rm2, stat as stat6, writeFile as writeFile2 } from "node:fs/promises";
|
|
2505
|
+
import path9 from "node:path";
|
|
2354
2506
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2355
2507
|
|
|
2356
2508
|
// src/color.ts
|
|
@@ -2682,11 +2834,21 @@ function flattenDtcg(tree) {
|
|
|
2682
2834
|
}
|
|
2683
2835
|
|
|
2684
2836
|
// src/themes.ts
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2837
|
+
function sourceDir() {
|
|
2838
|
+
try {
|
|
2839
|
+
const url = import.meta.url;
|
|
2840
|
+
return typeof url === "string" && url !== "" ? path9.dirname(fileURLToPath2(url)) : void 0;
|
|
2841
|
+
} catch {
|
|
2842
|
+
return void 0;
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
var HERE = sourceDir();
|
|
2846
|
+
function nearSource(...segments) {
|
|
2847
|
+
if (HERE === void 0) return [];
|
|
2848
|
+
return [path9.resolve(HERE, "..", "..", "..", ...segments), path9.resolve(HERE, "..", ...segments)];
|
|
2849
|
+
}
|
|
2850
|
+
var BUILTIN_BASES = nearSource("themes");
|
|
2851
|
+
var BUILTIN_SKILL_BASES = nearSource("skills");
|
|
2690
2852
|
var ROLE_TOKENS = ["--bg", "--fg", "--muted", "--accent", "--layer-01"];
|
|
2691
2853
|
var TEXT_ROLES = ["--fg", "--muted", "--accent"];
|
|
2692
2854
|
var GROUND_TOKENS = ["--bg", "--layer-01"];
|
|
@@ -2711,11 +2873,11 @@ function messageOf6(err) {
|
|
|
2711
2873
|
return err instanceof Error ? err.message : String(err);
|
|
2712
2874
|
}
|
|
2713
2875
|
async function isFile(file) {
|
|
2714
|
-
const info = await
|
|
2876
|
+
const info = await stat6(file).catch(() => void 0);
|
|
2715
2877
|
return info !== void 0 && info.isFile();
|
|
2716
2878
|
}
|
|
2717
2879
|
async function isDir(dir) {
|
|
2718
|
-
const info = await
|
|
2880
|
+
const info = await stat6(dir).catch(() => void 0);
|
|
2719
2881
|
return info !== void 0 && info.isDirectory();
|
|
2720
2882
|
}
|
|
2721
2883
|
function tokenMap(value) {
|
|
@@ -2750,7 +2912,7 @@ function themeValues(json) {
|
|
|
2750
2912
|
};
|
|
2751
2913
|
}
|
|
2752
2914
|
async function readThemeJson(dir) {
|
|
2753
|
-
const file =
|
|
2915
|
+
const file = path9.join(dir, "theme.json");
|
|
2754
2916
|
let text;
|
|
2755
2917
|
try {
|
|
2756
2918
|
text = await readFile7(file, "utf8");
|
|
@@ -2769,12 +2931,18 @@ async function readThemeJson(dir) {
|
|
|
2769
2931
|
}
|
|
2770
2932
|
async function builtinThemeDir(name, bases = BUILTIN_BASES) {
|
|
2771
2933
|
if (!THEME_NAME_PATTERN.test(name)) return void 0;
|
|
2772
|
-
for (const base of bases) {
|
|
2773
|
-
const dir =
|
|
2934
|
+
for (const base of [...await binaryThemeBases(), ...bases]) {
|
|
2935
|
+
const dir = path9.join(base, name);
|
|
2774
2936
|
if (await isDir(dir)) return dir;
|
|
2775
2937
|
}
|
|
2776
2938
|
return void 0;
|
|
2777
2939
|
}
|
|
2940
|
+
async function builtinSkillsDir(bases = BUILTIN_SKILL_BASES) {
|
|
2941
|
+
for (const base of [...await binarySkillBases(), ...bases]) {
|
|
2942
|
+
if (await isDir(base)) return base;
|
|
2943
|
+
}
|
|
2944
|
+
return void 0;
|
|
2945
|
+
}
|
|
2778
2946
|
function stripPrint(css) {
|
|
2779
2947
|
let out = "";
|
|
2780
2948
|
let index = 0;
|
|
@@ -2800,7 +2968,7 @@ function stripPrint(css) {
|
|
|
2800
2968
|
return out;
|
|
2801
2969
|
}
|
|
2802
2970
|
async function themeTokenSets(dir) {
|
|
2803
|
-
const file =
|
|
2971
|
+
const file = path9.join(dir, "tokens.css");
|
|
2804
2972
|
const text = await readFile7(file, "utf8").catch(() => void 0);
|
|
2805
2973
|
return extractTokens(text === void 0 ? [] : [stripPrint(text)]);
|
|
2806
2974
|
}
|
|
@@ -2823,9 +2991,9 @@ function declarations(values, indent) {
|
|
|
2823
2991
|
function presetTokenCss(name, values) {
|
|
2824
2992
|
const parts2 = [];
|
|
2825
2993
|
const light = { ...values.tokens, ...values.light };
|
|
2826
|
-
const
|
|
2827
|
-
if (
|
|
2828
|
-
${
|
|
2994
|
+
const root2 = declarations(light, " ");
|
|
2995
|
+
if (root2 !== "") parts2.push(`:root {
|
|
2996
|
+
${root2}
|
|
2829
2997
|
}`);
|
|
2830
2998
|
const dark = declarations(values.dark, " ");
|
|
2831
2999
|
if (dark !== "") {
|
|
@@ -2854,7 +3022,7 @@ function presetFontCss(fonts) {
|
|
|
2854
3022
|
lines.push(` font-weight: ${font.weight ?? "400"};`);
|
|
2855
3023
|
lines.push(" font-display: swap;");
|
|
2856
3024
|
const url = fontUrl(font.file);
|
|
2857
|
-
const format = FONT_FORMATS[
|
|
3025
|
+
const format = FONT_FORMATS[path9.extname(url).toLowerCase()];
|
|
2858
3026
|
const source = format === void 0 ? `url("${url}")` : `url("${url}") format("${format}")`;
|
|
2859
3027
|
lines.push(` src: ${source};`);
|
|
2860
3028
|
return `@font-face {
|
|
@@ -2881,13 +3049,13 @@ async function appendFile(file, addition) {
|
|
|
2881
3049
|
if (addition === "") return;
|
|
2882
3050
|
const before = await readFile7(file, "utf8").catch(() => "");
|
|
2883
3051
|
const glue = before === "" || before.endsWith("\n") ? "" : "\n";
|
|
2884
|
-
await
|
|
3052
|
+
await writeFile2(file, `${before}${glue}${addition}`);
|
|
2885
3053
|
}
|
|
2886
3054
|
async function buildPreset(presetDir, outDir) {
|
|
2887
3055
|
const preset = await readThemeJson(presetDir);
|
|
2888
3056
|
const baseName = stringField(preset.json.extends);
|
|
2889
|
-
await
|
|
2890
|
-
await
|
|
3057
|
+
await rm2(outDir, { recursive: true, force: true });
|
|
3058
|
+
await mkdir2(outDir, { recursive: true });
|
|
2891
3059
|
if (baseName === void 0) {
|
|
2892
3060
|
await cp(presetDir, outDir, { recursive: true });
|
|
2893
3061
|
return;
|
|
@@ -2898,15 +3066,15 @@ async function buildPreset(presetDir, outDir) {
|
|
|
2898
3066
|
await cp(baseDir, outDir, { recursive: true });
|
|
2899
3067
|
const values = themeValues(preset.json);
|
|
2900
3068
|
const name = stringField(preset.json.name) ?? baseName;
|
|
2901
|
-
await appendFile(
|
|
3069
|
+
await appendFile(path9.join(outDir, "tokens.css"), presetTokenCss(name, values));
|
|
2902
3070
|
const fonts = fontList(preset.json.fonts);
|
|
2903
3071
|
if (fonts.length > 0) {
|
|
2904
|
-
const from =
|
|
2905
|
-
if (await isDir(from)) await cp(from,
|
|
2906
|
-
await appendFile(
|
|
3072
|
+
const from = path9.join(presetDir, "fonts");
|
|
3073
|
+
if (await isDir(from)) await cp(from, path9.join(outDir, "fonts"), { recursive: true });
|
|
3074
|
+
await appendFile(path9.join(outDir, "fonts.css"), presetFontCss(fonts));
|
|
2907
3075
|
}
|
|
2908
3076
|
const merged = mergeTheme(base.json, preset.json, baseName);
|
|
2909
|
-
await
|
|
3077
|
+
await writeFile2(path9.join(outDir, "theme.json"), `${JSON.stringify(merged, null, 2)}
|
|
2910
3078
|
`);
|
|
2911
3079
|
}
|
|
2912
3080
|
function keyLine(text, key2, mode) {
|
|
@@ -2921,8 +3089,8 @@ function keyLine(text, key2, mode) {
|
|
|
2921
3089
|
}
|
|
2922
3090
|
return 0;
|
|
2923
3091
|
}
|
|
2924
|
-
function finding(
|
|
2925
|
-
return { path:
|
|
3092
|
+
function finding(path25, line, severity, message) {
|
|
3093
|
+
return { path: path25, line: Math.max(1, line), severity, message };
|
|
2926
3094
|
}
|
|
2927
3095
|
function resolveValue(name, map, fallback) {
|
|
2928
3096
|
let current = map[name] ?? fallback[name];
|
|
@@ -3020,13 +3188,13 @@ async function checkPreset(presetDir) {
|
|
|
3020
3188
|
}
|
|
3021
3189
|
const fonts = fontList(preset.json.fonts);
|
|
3022
3190
|
for (const font of fonts) {
|
|
3023
|
-
const file =
|
|
3191
|
+
const file = path9.join(presetDir, font.file);
|
|
3024
3192
|
if (!await isFile(file)) {
|
|
3025
3193
|
findings.push(finding("theme.json", keyLine(text, "file"), "error", `missing font file: ${font.file}`));
|
|
3026
3194
|
}
|
|
3027
3195
|
}
|
|
3028
3196
|
if (fonts.length > 0) {
|
|
3029
|
-
const found = await Promise.all(LICENCE_FILES.map((rel) => isFile(
|
|
3197
|
+
const found = await Promise.all(LICENCE_FILES.map((rel) => isFile(path9.join(presetDir, "fonts", rel))));
|
|
3030
3198
|
if (!found.some(Boolean)) {
|
|
3031
3199
|
findings.push(
|
|
3032
3200
|
finding(
|
|
@@ -3144,14 +3312,14 @@ function parseValue(text) {
|
|
|
3144
3312
|
function dataLimit(key2) {
|
|
3145
3313
|
return key2.startsWith(SKETCH_DATA_KEY_PREFIX) ? MAX_SKETCH_DATA_BYTES : MAX_DATA_BYTES;
|
|
3146
3314
|
}
|
|
3147
|
-
function jsonReply(res,
|
|
3148
|
-
res.writeHead(
|
|
3315
|
+
function jsonReply(res, status2, body, headers = {}) {
|
|
3316
|
+
res.writeHead(status2, { "content-type": JSON_TYPE, "cache-control": "no-store", ...headers });
|
|
3149
3317
|
res.end(JSON.stringify(body));
|
|
3150
3318
|
}
|
|
3151
|
-
function jsonError(res, message,
|
|
3152
|
-
jsonReply(res,
|
|
3319
|
+
function jsonError(res, message, status2) {
|
|
3320
|
+
jsonReply(res, status2, { error: message });
|
|
3153
3321
|
}
|
|
3154
|
-
async function resolveFile(
|
|
3322
|
+
async function resolveFile(root2, pathname) {
|
|
3155
3323
|
let decoded;
|
|
3156
3324
|
try {
|
|
3157
3325
|
decoded = decodeURIComponent(pathname);
|
|
@@ -3159,15 +3327,15 @@ async function resolveFile(root, pathname) {
|
|
|
3159
3327
|
return void 0;
|
|
3160
3328
|
}
|
|
3161
3329
|
if (decoded.includes("\0")) return void 0;
|
|
3162
|
-
const target =
|
|
3163
|
-
const rel =
|
|
3164
|
-
if (rel.startsWith("..") ||
|
|
3165
|
-
const info = await
|
|
3330
|
+
const target = path10.resolve(root2, decoded.replace(/^\/+/, ""));
|
|
3331
|
+
const rel = path10.relative(root2, target);
|
|
3332
|
+
if (rel.startsWith("..") || path10.isAbsolute(rel)) return void 0;
|
|
3333
|
+
const info = await stat7(target).catch(() => void 0);
|
|
3166
3334
|
if (info === void 0) return void 0;
|
|
3167
3335
|
if (info.isFile()) return target;
|
|
3168
3336
|
if (!info.isDirectory()) return void 0;
|
|
3169
|
-
const index =
|
|
3170
|
-
const indexInfo = await
|
|
3337
|
+
const index = path10.join(target, "index.html");
|
|
3338
|
+
const indexInfo = await stat7(index).catch(() => void 0);
|
|
3171
3339
|
if (indexInfo === void 0 || !indexInfo.isFile()) return void 0;
|
|
3172
3340
|
return index;
|
|
3173
3341
|
}
|
|
@@ -3193,10 +3361,10 @@ async function proxy(art, pathname, method, res) {
|
|
|
3193
3361
|
async function prepareTheme(dir) {
|
|
3194
3362
|
const doc = await readThemeJson(dir);
|
|
3195
3363
|
const declared = typeof doc.json.name === "string" ? doc.json.name.trim() : "";
|
|
3196
|
-
const name = declared === "" ?
|
|
3364
|
+
const name = declared === "" ? path10.basename(dir) : declared;
|
|
3197
3365
|
const base = typeof doc.json.extends === "string" ? doc.json.extends.trim() : "";
|
|
3198
3366
|
if (base === "") return { name, source: dir, built: dir, label: "local" };
|
|
3199
|
-
const built = await
|
|
3367
|
+
const built = await mkdtemp2(path10.join(tmpdir(), "rshow-theme-"));
|
|
3200
3368
|
await buildPreset(dir, built);
|
|
3201
3369
|
return { name, source: dir, built, label: "preset" };
|
|
3202
3370
|
}
|
|
@@ -3267,9 +3435,9 @@ function createDataStore() {
|
|
|
3267
3435
|
const values = /* @__PURE__ */ new Map();
|
|
3268
3436
|
let dir;
|
|
3269
3437
|
const persist = async () => {
|
|
3270
|
-
if (dir === void 0) dir = await
|
|
3438
|
+
if (dir === void 0) dir = await mkdtemp2(path10.join(tmpdir(), "rshow-data-"));
|
|
3271
3439
|
const doc = Object.fromEntries(values);
|
|
3272
|
-
await
|
|
3440
|
+
await writeFile3(path10.join(dir, "data.json"), `${JSON.stringify(doc, null, 2)}
|
|
3273
3441
|
`);
|
|
3274
3442
|
};
|
|
3275
3443
|
return {
|
|
@@ -3283,7 +3451,7 @@ function createDataStore() {
|
|
|
3283
3451
|
},
|
|
3284
3452
|
close: async () => {
|
|
3285
3453
|
if (dir === void 0) return;
|
|
3286
|
-
await
|
|
3454
|
+
await rm3(dir, { recursive: true, force: true });
|
|
3287
3455
|
dir = void 0;
|
|
3288
3456
|
}
|
|
3289
3457
|
};
|
|
@@ -3488,19 +3656,19 @@ async function handle(req, res, session) {
|
|
|
3488
3656
|
}
|
|
3489
3657
|
function closeOnSignals(preview2) {
|
|
3490
3658
|
const stop = () => {
|
|
3491
|
-
preview2.close().catch(() => void 0).finally(() =>
|
|
3659
|
+
preview2.close().catch(() => void 0).finally(() => process6.exit(0));
|
|
3492
3660
|
};
|
|
3493
|
-
|
|
3494
|
-
|
|
3661
|
+
process6.once("SIGINT", stop);
|
|
3662
|
+
process6.once("SIGTERM", stop);
|
|
3495
3663
|
}
|
|
3496
3664
|
async function startPreview(opts) {
|
|
3497
|
-
const dir =
|
|
3665
|
+
const dir = path10.resolve(opts.dir);
|
|
3498
3666
|
const art = (opts.art ?? "").replace(/\/+$/, "");
|
|
3499
3667
|
const host = opts.host ?? "127.0.0.1";
|
|
3500
|
-
const theme = opts.theme === void 0 ? void 0 : await prepareTheme(
|
|
3501
|
-
const
|
|
3668
|
+
const theme = opts.theme === void 0 ? void 0 : await prepareTheme(path10.resolve(opts.theme));
|
|
3669
|
+
const root2 = opts.root === "theme" && theme !== void 0 ? theme.built : dir;
|
|
3502
3670
|
const session = {
|
|
3503
|
-
root,
|
|
3671
|
+
root: root2,
|
|
3504
3672
|
art,
|
|
3505
3673
|
offline: opts.offline === true,
|
|
3506
3674
|
reload: opts.reload !== false,
|
|
@@ -3509,8 +3677,8 @@ async function startPreview(opts) {
|
|
|
3509
3677
|
clients: /* @__PURE__ */ new Set(),
|
|
3510
3678
|
builtin: /* @__PURE__ */ new Map()
|
|
3511
3679
|
};
|
|
3512
|
-
const targets = [{ dir:
|
|
3513
|
-
if (theme !== void 0 && theme.source !==
|
|
3680
|
+
const targets = [{ dir: root2, rebuild: false }];
|
|
3681
|
+
if (theme !== void 0 && theme.source !== root2) targets.push({ dir: theme.source, rebuild: true });
|
|
3514
3682
|
const watchers = startWatchers(session, targets);
|
|
3515
3683
|
const server = createServer((req, res) => {
|
|
3516
3684
|
handle(req, res, session).catch((err) => {
|
|
@@ -3525,7 +3693,7 @@ async function startPreview(opts) {
|
|
|
3525
3693
|
session.clients.clear();
|
|
3526
3694
|
await session.store.close();
|
|
3527
3695
|
if (theme !== void 0 && theme.built !== theme.source) {
|
|
3528
|
-
await
|
|
3696
|
+
await rm3(theme.built, { recursive: true, force: true });
|
|
3529
3697
|
}
|
|
3530
3698
|
};
|
|
3531
3699
|
try {
|
|
@@ -3565,8 +3733,8 @@ function portOf(value) {
|
|
|
3565
3733
|
return port;
|
|
3566
3734
|
}
|
|
3567
3735
|
async function requireFolder(input, what) {
|
|
3568
|
-
const dir =
|
|
3569
|
-
const info = await
|
|
3736
|
+
const dir = path11.resolve(input);
|
|
3737
|
+
const info = await stat8(dir).catch(() => void 0);
|
|
3570
3738
|
if (info === void 0 || !info.isDirectory()) throw new UsageError(`no such ${what}: ${input}`);
|
|
3571
3739
|
return dir;
|
|
3572
3740
|
}
|
|
@@ -3652,9 +3820,9 @@ async function run5(argv, ctx) {
|
|
|
3652
3820
|
}
|
|
3653
3821
|
|
|
3654
3822
|
// src/commands/init.ts
|
|
3655
|
-
import { mkdir as
|
|
3656
|
-
import
|
|
3657
|
-
import
|
|
3823
|
+
import { mkdir as mkdir3, readFile as readFile9, writeFile as writeFile4 } from "node:fs/promises";
|
|
3824
|
+
import path12 from "node:path";
|
|
3825
|
+
import process7 from "node:process";
|
|
3658
3826
|
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost"]);
|
|
3659
3827
|
function messageOf9(err) {
|
|
3660
3828
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -3706,7 +3874,7 @@ async function run6(argv, ctx) {
|
|
|
3706
3874
|
const url = serverUrl(stringValue(args, "url"), "url");
|
|
3707
3875
|
const art = serverUrl(stringValue(args, "art"), "art");
|
|
3708
3876
|
const theme = (stringValue(args, "theme") ?? "").trim();
|
|
3709
|
-
const file = globalConfigPath(
|
|
3877
|
+
const file = globalConfigPath(process7.env);
|
|
3710
3878
|
const current = await readConfigFile(file);
|
|
3711
3879
|
const same = (have, want) => have === void 0 || have.replace(/\/+$/, "") === want;
|
|
3712
3880
|
if (!boolValue(args, "force")) {
|
|
@@ -3721,8 +3889,8 @@ async function run6(argv, ctx) {
|
|
|
3721
3889
|
raw.url = url;
|
|
3722
3890
|
raw.art = art;
|
|
3723
3891
|
if (theme !== "") raw.theme = theme;
|
|
3724
|
-
await
|
|
3725
|
-
await
|
|
3892
|
+
await mkdir3(path12.dirname(file), { recursive: true });
|
|
3893
|
+
await writeFile4(file, `${JSON.stringify(raw, null, 2)}
|
|
3726
3894
|
`, "utf8");
|
|
3727
3895
|
ctx.out.data({ path: file, url, art });
|
|
3728
3896
|
ctx.out.line(`wrote ${file}`);
|
|
@@ -3730,24 +3898,226 @@ async function run6(argv, ctx) {
|
|
|
3730
3898
|
}
|
|
3731
3899
|
|
|
3732
3900
|
// src/commands/doctor.ts
|
|
3733
|
-
import { readFile as
|
|
3734
|
-
import
|
|
3735
|
-
import
|
|
3901
|
+
import { readFile as readFile11, stat as stat10 } from "node:fs/promises";
|
|
3902
|
+
import path14 from "node:path";
|
|
3903
|
+
import process8 from "node:process";
|
|
3736
3904
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
3905
|
+
|
|
3906
|
+
// src/skills.ts
|
|
3907
|
+
import { cp as cp2, lstat, mkdir as mkdir4, readFile as readFile10, readdir as readdir3, readlink, rm as rm4, stat as stat9, symlink, writeFile as writeFile5 } from "node:fs/promises";
|
|
3908
|
+
import { homedir as homedir3 } from "node:os";
|
|
3909
|
+
import path13 from "node:path";
|
|
3910
|
+
var SKILL_TARGETS = ["claude", "pi", "codex"];
|
|
3911
|
+
var SKILL_MARKER = ".rshow-skill";
|
|
3912
|
+
var SKILL_SOURCE = "rshow";
|
|
3913
|
+
var TARGET_SEGMENTS = {
|
|
3914
|
+
claude: [".claude"],
|
|
3915
|
+
pi: [".pi", "agent"],
|
|
3916
|
+
codex: [".codex"]
|
|
3917
|
+
};
|
|
3918
|
+
async function isDir2(dir) {
|
|
3919
|
+
const info = await stat9(dir).catch(() => void 0);
|
|
3920
|
+
return info !== void 0 && info.isDirectory();
|
|
3921
|
+
}
|
|
3922
|
+
async function isFile2(file) {
|
|
3923
|
+
const info = await stat9(file).catch(() => void 0);
|
|
3924
|
+
return info !== void 0 && info.isFile();
|
|
3925
|
+
}
|
|
3926
|
+
function isUnder(file, dir) {
|
|
3927
|
+
const rel = path13.relative(dir, file);
|
|
3928
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${path13.sep}`) && !path13.isAbsolute(rel);
|
|
3929
|
+
}
|
|
3930
|
+
function homeDir(env) {
|
|
3931
|
+
const home = (env.HOME ?? "").trim();
|
|
3932
|
+
return home !== "" ? home : homedir3();
|
|
3933
|
+
}
|
|
3934
|
+
function targetParent(home, target) {
|
|
3935
|
+
return path13.join(home, ...TARGET_SEGMENTS[target]);
|
|
3936
|
+
}
|
|
3937
|
+
function targetSkillsDir(home, target) {
|
|
3938
|
+
return path13.join(targetParent(home, target), "skills");
|
|
3939
|
+
}
|
|
3940
|
+
async function detectedTargets(home) {
|
|
3941
|
+
const out = [];
|
|
3942
|
+
for (const target of SKILL_TARGETS) {
|
|
3943
|
+
if (await isDir2(targetParent(home, target))) out.push(target);
|
|
3944
|
+
}
|
|
3945
|
+
return out;
|
|
3946
|
+
}
|
|
3947
|
+
function parseTargets(names) {
|
|
3948
|
+
const out = [];
|
|
3949
|
+
for (const name of names) {
|
|
3950
|
+
const target = SKILL_TARGETS.find((item) => item === name);
|
|
3951
|
+
if (target === void 0) {
|
|
3952
|
+
throw new UsageError(`unknown target: ${name} (use ${SKILL_TARGETS.join(", ")})`);
|
|
3953
|
+
}
|
|
3954
|
+
if (!out.includes(target)) out.push(target);
|
|
3955
|
+
}
|
|
3956
|
+
return out;
|
|
3957
|
+
}
|
|
3958
|
+
function targetHint() {
|
|
3959
|
+
return SKILL_TARGETS.join(", ");
|
|
3960
|
+
}
|
|
3961
|
+
function defaultModeOf(dir) {
|
|
3962
|
+
return dir.split(path13.sep).join("/").includes("/_npx/") ? "copy" : "link";
|
|
3963
|
+
}
|
|
3964
|
+
async function skillsSource(env) {
|
|
3965
|
+
const override = (env.RSHOW_SKILLS_DIR ?? "").trim();
|
|
3966
|
+
let dir;
|
|
3967
|
+
if (override !== "") {
|
|
3968
|
+
dir = path13.resolve(override);
|
|
3969
|
+
if (!await isDir2(dir)) throw new UsageError(`RSHOW_SKILLS_DIR is not a folder: ${dir}`);
|
|
3970
|
+
} else {
|
|
3971
|
+
dir = await builtinSkillsDir();
|
|
3972
|
+
}
|
|
3973
|
+
if (dir === void 0) return void 0;
|
|
3974
|
+
const skills = [];
|
|
3975
|
+
for (const entry of await readdir3(dir, { withFileTypes: true }).catch(() => [])) {
|
|
3976
|
+
if (entry.isDirectory() && await isFile2(path13.join(dir, entry.name, "SKILL.md"))) skills.push(entry.name);
|
|
3977
|
+
}
|
|
3978
|
+
skills.sort();
|
|
3979
|
+
return { dir, mode: await isBinary() ? "copy" : defaultModeOf(dir), skills };
|
|
3980
|
+
}
|
|
3981
|
+
function isOurLink(target, source) {
|
|
3982
|
+
const resolved = path13.resolve(target);
|
|
3983
|
+
if (isUnder(resolved, path13.resolve(source))) return true;
|
|
3984
|
+
return /(^|\/)rshow\/skills\//.test(resolved.split(path13.sep).join("/"));
|
|
3985
|
+
}
|
|
3986
|
+
async function linkTargetOf(dst) {
|
|
3987
|
+
const value = await readlink(dst);
|
|
3988
|
+
return path13.isAbsolute(value) ? value : path13.resolve(path13.dirname(dst), value);
|
|
3989
|
+
}
|
|
3990
|
+
async function readMarker(dir) {
|
|
3991
|
+
const text = await readFile10(path13.join(dir, SKILL_MARKER), "utf8").catch(() => void 0);
|
|
3992
|
+
if (text === void 0) return void 0;
|
|
3993
|
+
let parsed;
|
|
3994
|
+
try {
|
|
3995
|
+
parsed = JSON.parse(text);
|
|
3996
|
+
} catch {
|
|
3997
|
+
return void 0;
|
|
3998
|
+
}
|
|
3999
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
4000
|
+
const doc = parsed;
|
|
4001
|
+
if (doc.source !== SKILL_SOURCE || typeof doc.version !== "string" || doc.version === "") return void 0;
|
|
4002
|
+
return { version: doc.version, source: SKILL_SOURCE };
|
|
4003
|
+
}
|
|
4004
|
+
async function inspectSkill(dst, source, skill) {
|
|
4005
|
+
const info = await lstat(dst).catch(() => void 0);
|
|
4006
|
+
if (info === void 0) return { kind: "missing" };
|
|
4007
|
+
if (info.isSymbolicLink()) {
|
|
4008
|
+
const link = await linkTargetOf(dst);
|
|
4009
|
+
if (!isOurLink(link, source)) return { kind: "foreign" };
|
|
4010
|
+
return { kind: "link", path: link, current: path13.resolve(link) === path13.join(path13.resolve(source), skill) };
|
|
4011
|
+
}
|
|
4012
|
+
if (info.isDirectory()) {
|
|
4013
|
+
const marker = await readMarker(dst);
|
|
4014
|
+
return marker === void 0 ? { kind: "foreign" } : { kind: "copy", version: marker.version };
|
|
4015
|
+
}
|
|
4016
|
+
return { kind: "foreign" };
|
|
4017
|
+
}
|
|
4018
|
+
function skillIsCurrent(state, version) {
|
|
4019
|
+
if (state.kind === "link") return true;
|
|
4020
|
+
if (state.kind === "copy") return state.version === version;
|
|
4021
|
+
return false;
|
|
4022
|
+
}
|
|
4023
|
+
function skillStateLabel(state) {
|
|
4024
|
+
switch (state.kind) {
|
|
4025
|
+
case "missing":
|
|
4026
|
+
return "missing";
|
|
4027
|
+
case "foreign":
|
|
4028
|
+
return "foreign";
|
|
4029
|
+
case "link":
|
|
4030
|
+
return `link -> ${state.path}`;
|
|
4031
|
+
case "copy":
|
|
4032
|
+
return `copy v${state.version}`;
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
function actionFor(state, version) {
|
|
4036
|
+
if (state.kind === "missing") return "installed";
|
|
4037
|
+
if (state.kind === "foreign") return "skip";
|
|
4038
|
+
if (state.kind === "link") return state.current ? "ok" : "updated";
|
|
4039
|
+
return state.version === version ? "ok" : "updated";
|
|
4040
|
+
}
|
|
4041
|
+
async function writeSkill(dst, src, mode, version) {
|
|
4042
|
+
if (mode === "link") {
|
|
4043
|
+
await symlink(src, dst);
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
await cp2(src, dst, { recursive: true });
|
|
4047
|
+
const marker = { version, source: SKILL_SOURCE };
|
|
4048
|
+
await writeFile5(path13.join(dst, SKILL_MARKER), `${JSON.stringify(marker, null, 2)}
|
|
4049
|
+
`, "utf8");
|
|
4050
|
+
}
|
|
4051
|
+
async function installSkills(options) {
|
|
4052
|
+
const entries = [];
|
|
4053
|
+
for (const target of options.targets) {
|
|
4054
|
+
const dir = targetSkillsDir(options.home, target);
|
|
4055
|
+
if (!options.dryRun) await mkdir4(dir, { recursive: true });
|
|
4056
|
+
for (const skill of options.source.skills) {
|
|
4057
|
+
const dst = path13.join(dir, skill);
|
|
4058
|
+
const src = path13.join(options.source.dir, skill);
|
|
4059
|
+
const state = await inspectSkill(dst, options.source.dir, skill);
|
|
4060
|
+
const action = actionFor(state, options.version);
|
|
4061
|
+
if (!options.dryRun && (action === "installed" || action === "updated")) {
|
|
4062
|
+
if (action === "updated") await rm4(dst, { recursive: true, force: true });
|
|
4063
|
+
await writeSkill(dst, src, options.mode, options.version);
|
|
4064
|
+
}
|
|
4065
|
+
entries.push({ target, skill, action, path: dst });
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return entries;
|
|
4069
|
+
}
|
|
4070
|
+
async function uninstallSkills(options) {
|
|
4071
|
+
const entries = [];
|
|
4072
|
+
for (const target of options.targets) {
|
|
4073
|
+
const dir = targetSkillsDir(options.home, target);
|
|
4074
|
+
for (const skill of (await readdir3(dir).catch(() => [])).sort()) {
|
|
4075
|
+
const dst = path13.join(dir, skill);
|
|
4076
|
+
const state = await inspectSkill(dst, options.source.dir, skill);
|
|
4077
|
+
if (state.kind !== "link" && state.kind !== "copy") continue;
|
|
4078
|
+
if (!options.dryRun) await rm4(dst, { recursive: true, force: true });
|
|
4079
|
+
entries.push({ target, skill, action: "removed", path: dst });
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
return entries;
|
|
4083
|
+
}
|
|
4084
|
+
async function skillsStatus(options) {
|
|
4085
|
+
const out = [];
|
|
4086
|
+
for (const target of options.targets) {
|
|
4087
|
+
const dir = targetSkillsDir(options.home, target);
|
|
4088
|
+
for (const skill of options.source.skills) {
|
|
4089
|
+
const dst = path13.join(dir, skill);
|
|
4090
|
+
out.push({ target, skill, state: await inspectSkill(dst, options.source.dir, skill), path: dst });
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
return out;
|
|
4094
|
+
}
|
|
4095
|
+
|
|
4096
|
+
// src/commands/doctor.ts
|
|
3737
4097
|
var TIMEOUT_MS = 5e3;
|
|
3738
4098
|
function messageOf10(err) {
|
|
3739
4099
|
return err instanceof Error ? err.message : String(err);
|
|
3740
4100
|
}
|
|
3741
|
-
async function
|
|
3742
|
-
const info = await
|
|
4101
|
+
async function isFile3(file) {
|
|
4102
|
+
const info = await stat10(file).catch(() => void 0);
|
|
3743
4103
|
return info !== void 0 && info.isFile();
|
|
3744
4104
|
}
|
|
4105
|
+
function sourceDir2() {
|
|
4106
|
+
try {
|
|
4107
|
+
const url = import.meta.url;
|
|
4108
|
+
return typeof url === "string" && url !== "" ? path14.dirname(fileURLToPath3(url)) : void 0;
|
|
4109
|
+
} catch {
|
|
4110
|
+
return void 0;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
3745
4113
|
async function packageJsonPath() {
|
|
3746
|
-
|
|
4114
|
+
const start = sourceDir2();
|
|
4115
|
+
if (start === void 0) return void 0;
|
|
4116
|
+
let dir = start;
|
|
3747
4117
|
for (let i = 0; i < 6; i += 1) {
|
|
3748
|
-
const file =
|
|
3749
|
-
if (await
|
|
3750
|
-
const up =
|
|
4118
|
+
const file = path14.join(dir, "package.json");
|
|
4119
|
+
if (await isFile3(file)) return file;
|
|
4120
|
+
const up = path14.dirname(dir);
|
|
3751
4121
|
if (up === dir) break;
|
|
3752
4122
|
dir = up;
|
|
3753
4123
|
}
|
|
@@ -3771,23 +4141,26 @@ function minimumOf(range) {
|
|
|
3771
4141
|
}
|
|
3772
4142
|
async function nodeCheck() {
|
|
3773
4143
|
const name = "node";
|
|
3774
|
-
const current =
|
|
4144
|
+
const current = process8.versions.node;
|
|
3775
4145
|
const file = await packageJsonPath();
|
|
3776
|
-
if (file === void 0)
|
|
4146
|
+
if (file === void 0) {
|
|
4147
|
+
if (await isBinary()) return { name, status: "ok", detail: `v${current} (inside the binary)` };
|
|
4148
|
+
return { name, status: "warn", detail: `v${current} (package.json not found)` };
|
|
4149
|
+
}
|
|
3777
4150
|
let range;
|
|
3778
4151
|
try {
|
|
3779
|
-
const body = JSON.parse(await
|
|
4152
|
+
const body = JSON.parse(await readFile11(file, "utf8"));
|
|
3780
4153
|
range = typeof body.engines?.node === "string" ? body.engines.node : "";
|
|
3781
4154
|
} catch (err) {
|
|
3782
4155
|
return { name, status: "warn", detail: `v${current} (${messageOf10(err)})` };
|
|
3783
4156
|
}
|
|
3784
4157
|
const needed = minimumOf(range);
|
|
3785
4158
|
if (needed === void 0) return { name, status: "warn", detail: `v${current} (no engines.node)` };
|
|
3786
|
-
const
|
|
3787
|
-
return { name, status, detail: `v${current} (needs ${range})` };
|
|
4159
|
+
const status2 = atLeast(versionParts(current), needed) ? "ok" : "warn";
|
|
4160
|
+
return { name, status: status2, detail: `v${current} (needs ${range})` };
|
|
3788
4161
|
}
|
|
3789
4162
|
async function tokenOf(url) {
|
|
3790
|
-
const env = (
|
|
4163
|
+
const env = (process8.env.RSHOW_TOKEN ?? "").trim();
|
|
3791
4164
|
if (env !== "") return { token: env, source: "env" };
|
|
3792
4165
|
if (url === void 0) return { source: "none" };
|
|
3793
4166
|
try {
|
|
@@ -3872,12 +4245,28 @@ async function themeCheck(config) {
|
|
|
3872
4245
|
return { name, status: "warn", detail: messageOf10(err) };
|
|
3873
4246
|
}
|
|
3874
4247
|
}
|
|
4248
|
+
async function skillsCheck() {
|
|
4249
|
+
const name = "skills";
|
|
4250
|
+
const home = homeDir(process8.env);
|
|
4251
|
+
const targets = await detectedTargets(home);
|
|
4252
|
+
if (targets.length === 0) return { name, status: "ok", detail: "no target detected" };
|
|
4253
|
+
const source = await skillsSource(process8.env).catch(() => void 0);
|
|
4254
|
+
if (source === void 0) return { name, status: "warn", detail: "no skills folder next to rshow" };
|
|
4255
|
+
const list3 = await skillsStatus({ home, source, targets });
|
|
4256
|
+
const problems = [];
|
|
4257
|
+
for (const target of targets) {
|
|
4258
|
+
const bad = list3.filter((item) => item.target === target && !skillIsCurrent(item.state, VERSION)).map((item) => `${item.skill} ${skillStateLabel(item.state)}`);
|
|
4259
|
+
if (bad.length > 0) problems.push(`${target}: ${bad.join(", ")}`);
|
|
4260
|
+
}
|
|
4261
|
+
if (problems.length === 0) return { name, status: "ok", detail: targets.join(", ") };
|
|
4262
|
+
return { name, status: "warn", detail: `${problems.join("; ")} (run rshow skills install)` };
|
|
4263
|
+
}
|
|
3875
4264
|
async function run7(argv, ctx) {
|
|
3876
4265
|
const args = parseCommandArgs(argv, {}, 0, 1, DOCTOR_USAGE);
|
|
3877
4266
|
const dir = args.positionals[0];
|
|
3878
4267
|
const found = [];
|
|
3879
|
-
for (const file of [globalConfigPath(
|
|
3880
|
-
if (file !== void 0 && await
|
|
4268
|
+
for (const file of [globalConfigPath(process8.env), dir === void 0 ? void 0 : localConfigPath(dir)]) {
|
|
4269
|
+
if (file !== void 0 && await isFile3(file)) found.push(file);
|
|
3881
4270
|
}
|
|
3882
4271
|
let config;
|
|
3883
4272
|
let configError;
|
|
@@ -3888,7 +4277,7 @@ async function run7(argv, ctx) {
|
|
|
3888
4277
|
}
|
|
3889
4278
|
const token = await tokenOf(config?.url);
|
|
3890
4279
|
const checks = [
|
|
3891
|
-
{ name: "rshow", status: "ok", detail: VERSION },
|
|
4280
|
+
{ name: "rshow", status: "ok", detail: await isBinary() ? `${VERSION} (binary)` : VERSION },
|
|
3892
4281
|
await nodeCheck(),
|
|
3893
4282
|
configCheck(found, config, configError),
|
|
3894
4283
|
tokenCheck(token),
|
|
@@ -3897,19 +4286,20 @@ async function run7(argv, ctx) {
|
|
|
3897
4286
|
];
|
|
3898
4287
|
const theme = await themeCheck(config);
|
|
3899
4288
|
if (theme !== void 0) checks.push(theme);
|
|
4289
|
+
checks.push(await skillsCheck());
|
|
3900
4290
|
ctx.out.data({ checks });
|
|
3901
4291
|
for (const check2 of checks) ctx.out.line(`${check2.status} ${check2.name} ${check2.detail}`);
|
|
3902
4292
|
return checks.some((check2) => check2.status === "fail") ? EXIT.check : EXIT.ok;
|
|
3903
4293
|
}
|
|
3904
4294
|
|
|
3905
4295
|
// src/commands/theme.ts
|
|
3906
|
-
import { mkdir as
|
|
4296
|
+
import { mkdir as mkdir5, mkdtemp as mkdtemp3, rm as rm5, stat as stat12, writeFile as writeFile6 } from "node:fs/promises";
|
|
3907
4297
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
3908
|
-
import
|
|
4298
|
+
import path16 from "node:path";
|
|
3909
4299
|
|
|
3910
4300
|
// src/theme.ts
|
|
3911
|
-
import { readFile as
|
|
3912
|
-
import
|
|
4301
|
+
import { readFile as readFile12, stat as stat11 } from "node:fs/promises";
|
|
4302
|
+
import path15 from "node:path";
|
|
3913
4303
|
function themeSlug(name) {
|
|
3914
4304
|
return `theme-${name}`;
|
|
3915
4305
|
}
|
|
@@ -3917,10 +4307,10 @@ function themeUrl(name, n) {
|
|
|
3917
4307
|
return `/_themes/${name}@${n}/`;
|
|
3918
4308
|
}
|
|
3919
4309
|
async function readThemeMeta(dir) {
|
|
3920
|
-
const file =
|
|
4310
|
+
const file = path15.join(dir, "theme.json");
|
|
3921
4311
|
let text;
|
|
3922
4312
|
try {
|
|
3923
|
-
text = await
|
|
4313
|
+
text = await readFile12(file, "utf8");
|
|
3924
4314
|
} catch (err) {
|
|
3925
4315
|
if (err.code === "ENOENT") throw new UsageError(`${dir} has no theme.json`);
|
|
3926
4316
|
throw new UsageError(`cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -3941,8 +4331,8 @@ async function readThemeMeta(dir) {
|
|
|
3941
4331
|
return { name };
|
|
3942
4332
|
}
|
|
3943
4333
|
async function requireThemeDir(input) {
|
|
3944
|
-
const dir =
|
|
3945
|
-
const info = await
|
|
4334
|
+
const dir = path15.resolve(input);
|
|
4335
|
+
const info = await stat11(dir).catch(() => void 0);
|
|
3946
4336
|
if (info === void 0 || !info.isDirectory()) throw new UsageError(`no such folder: ${input}`);
|
|
3947
4337
|
return dir;
|
|
3948
4338
|
}
|
|
@@ -3950,7 +4340,7 @@ async function requireThemeDir(input) {
|
|
|
3950
4340
|
// src/commands/theme.ts
|
|
3951
4341
|
async function presetCheck(dir) {
|
|
3952
4342
|
const findings = await checkPreset(dir);
|
|
3953
|
-
const info = await
|
|
4343
|
+
const info = await stat12(path16.join(dir, "theme.json")).catch(() => void 0);
|
|
3954
4344
|
const errors = findings.filter((finding2) => finding2.severity === "error").length;
|
|
3955
4345
|
return { findings, errors, warnings: findings.length - errors, files: 1, bytes: info?.size ?? 0 };
|
|
3956
4346
|
}
|
|
@@ -3986,7 +4376,7 @@ async function publish(argv, ctx) {
|
|
|
3986
4376
|
let target = dir;
|
|
3987
4377
|
let temp;
|
|
3988
4378
|
if (preset) {
|
|
3989
|
-
temp = await
|
|
4379
|
+
temp = await mkdtemp3(path16.join(tmpdir2(), "rshow-theme-"));
|
|
3990
4380
|
await buildPreset(dir, temp);
|
|
3991
4381
|
target = temp;
|
|
3992
4382
|
}
|
|
@@ -4032,7 +4422,7 @@ async function publish(argv, ctx) {
|
|
|
4032
4422
|
reportPublish(ctx.out, { ...result, artifact });
|
|
4033
4423
|
ctx.out.line(url);
|
|
4034
4424
|
} finally {
|
|
4035
|
-
if (temp !== void 0) await
|
|
4425
|
+
if (temp !== void 0) await rm5(temp, { recursive: true, force: true });
|
|
4036
4426
|
}
|
|
4037
4427
|
}
|
|
4038
4428
|
async function list(argv, ctx) {
|
|
@@ -4074,14 +4464,14 @@ async function create(argv, ctx) {
|
|
|
4074
4464
|
const from = stringValue(args, "from") ?? "default";
|
|
4075
4465
|
const baseDir = await builtinThemeDir(from);
|
|
4076
4466
|
if (baseDir === void 0) throw new UsageError(`no built-in theme: ${from}`);
|
|
4077
|
-
const parent =
|
|
4078
|
-
const target =
|
|
4079
|
-
const
|
|
4080
|
-
if (
|
|
4467
|
+
const parent = path16.resolve(stringValue(args, "dir") ?? ".");
|
|
4468
|
+
const target = path16.join(parent, name);
|
|
4469
|
+
const exists2 = await stat12(target).catch(() => void 0);
|
|
4470
|
+
if (exists2 !== void 0) throw new UsageError(`${target} already exists`);
|
|
4081
4471
|
const values = await baseRoleValues(baseDir, ROLE_TOKENS);
|
|
4082
4472
|
const doc = { name, extends: from, tokens: {}, modeTokens: values };
|
|
4083
|
-
await
|
|
4084
|
-
await
|
|
4473
|
+
await mkdir5(target, { recursive: true });
|
|
4474
|
+
await writeFile6(path16.join(target, "theme.json"), `${JSON.stringify(doc, null, 2)}
|
|
4085
4475
|
`);
|
|
4086
4476
|
ctx.out.data({ path: target, name, extends: from });
|
|
4087
4477
|
ctx.out.line(target);
|
|
@@ -4147,8 +4537,124 @@ async function run8(argv, ctx) {
|
|
|
4147
4537
|
throw new UsageError(`unknown theme command: ${sub} (try: ${usageLine(THEME_USAGE)})`);
|
|
4148
4538
|
}
|
|
4149
4539
|
|
|
4150
|
-
// src/commands/
|
|
4540
|
+
// src/commands/skills.ts
|
|
4541
|
+
import path17 from "node:path";
|
|
4542
|
+
import process9 from "node:process";
|
|
4543
|
+
var CHANGE_ACTIONS = /* @__PURE__ */ new Set(["installed", "updated", "removed"]);
|
|
4544
|
+
async function sourceOf() {
|
|
4545
|
+
const source = await skillsSource(process9.env);
|
|
4546
|
+
if (source === void 0) {
|
|
4547
|
+
throw new UsageError(`no skills folder next to rshow (set RSHOW_SKILLS_DIR to one)`);
|
|
4548
|
+
}
|
|
4549
|
+
return source;
|
|
4550
|
+
}
|
|
4551
|
+
async function targetsWithDetection(names, home) {
|
|
4552
|
+
const chosen = parseTargets(names);
|
|
4553
|
+
if (chosen.length > 0) return chosen;
|
|
4554
|
+
return detectedTargets(home);
|
|
4555
|
+
}
|
|
4556
|
+
function noTargetsError(home) {
|
|
4557
|
+
return new UsageError(
|
|
4558
|
+
`no target detected (looked for ${path17.join(home, ".claude")}, ${path17.join(home, ".pi/agent")} and ${path17.join(home, ".codex")}); name one of ${targetHint()}`
|
|
4559
|
+
);
|
|
4560
|
+
}
|
|
4561
|
+
function actionLine(entry, dryRun) {
|
|
4562
|
+
const would = dryRun && CHANGE_ACTIONS.has(entry.action) ? "would " : "";
|
|
4563
|
+
return `${would}${entry.action} ${entry.path}`;
|
|
4564
|
+
}
|
|
4565
|
+
function countOf(entries) {
|
|
4566
|
+
return entries.filter((entry) => entry.action !== "skip").length;
|
|
4567
|
+
}
|
|
4568
|
+
var DRY_RUN_VERB = { installed: "install", removed: "remove" };
|
|
4569
|
+
function summaryLine2(verb, entries, targets, dryRun) {
|
|
4570
|
+
const phrase = dryRun ? `would ${DRY_RUN_VERB[verb] ?? verb}` : verb;
|
|
4571
|
+
return `${phrase} ${countOf(entries)} skills for ${targets.join(", ")}`;
|
|
4572
|
+
}
|
|
4573
|
+
async function install(argv, ctx) {
|
|
4574
|
+
const args = parseCommandArgs(
|
|
4575
|
+
argv,
|
|
4576
|
+
{ link: { type: "boolean" }, copy: { type: "boolean" }, "dry-run": { type: "boolean" } },
|
|
4577
|
+
0,
|
|
4578
|
+
3,
|
|
4579
|
+
SKILLS_INSTALL_USAGE
|
|
4580
|
+
);
|
|
4581
|
+
const link = boolValue(args, "link");
|
|
4582
|
+
const copy = boolValue(args, "copy");
|
|
4583
|
+
if (link && copy) throw new UsageError(`--link and --copy cannot be used together (try: ${usageLine(SKILLS_INSTALL_USAGE)})`);
|
|
4584
|
+
const home = homeDir(process9.env);
|
|
4585
|
+
const targets = await targetsWithDetection(args.positionals, home);
|
|
4586
|
+
if (targets.length === 0) throw noTargetsError(home);
|
|
4587
|
+
const source = await sourceOf();
|
|
4588
|
+
const mode = link ? "link" : copy ? "copy" : source.mode;
|
|
4589
|
+
const dryRun = boolValue(args, "dry-run");
|
|
4590
|
+
const entries = await installSkills({ home, source, targets, mode, dryRun, version: VERSION });
|
|
4591
|
+
ctx.out.data({ targets, actions: entries });
|
|
4592
|
+
const skipped = [];
|
|
4593
|
+
for (const entry of entries) {
|
|
4594
|
+
if (entry.action === "skip") skipped.push(entry.path);
|
|
4595
|
+
else ctx.out.line(actionLine(entry, dryRun));
|
|
4596
|
+
}
|
|
4597
|
+
ctx.out.line(summaryLine2("installed", entries, targets, dryRun));
|
|
4598
|
+
for (const dst of skipped) process9.stderr.write(`skip ${dst}: exists and is not from rshow
|
|
4599
|
+
`);
|
|
4600
|
+
return skipped.length > 0 ? EXIT.check : EXIT.ok;
|
|
4601
|
+
}
|
|
4602
|
+
async function uninstall(argv, ctx) {
|
|
4603
|
+
const args = parseCommandArgs(argv, { "dry-run": { type: "boolean" } }, 0, 3, SKILLS_UNINSTALL_USAGE);
|
|
4604
|
+
const home = homeDir(process9.env);
|
|
4605
|
+
const targets = await targetsWithDetection(args.positionals, home);
|
|
4606
|
+
if (targets.length === 0) return EXIT.ok;
|
|
4607
|
+
const source = await sourceOf();
|
|
4608
|
+
const dryRun = boolValue(args, "dry-run");
|
|
4609
|
+
const entries = await uninstallSkills({ home, source, targets, dryRun });
|
|
4610
|
+
ctx.out.data({ targets, actions: entries });
|
|
4611
|
+
for (const entry of entries) ctx.out.line(actionLine(entry, dryRun));
|
|
4612
|
+
ctx.out.line(summaryLine2("removed", entries, targets, dryRun));
|
|
4613
|
+
return EXIT.ok;
|
|
4614
|
+
}
|
|
4615
|
+
function actionOf(item) {
|
|
4616
|
+
if (item.state.kind === "link") return { action: "link", link: item.state.path };
|
|
4617
|
+
if (item.state.kind === "copy") return { action: "copy", version: item.state.version };
|
|
4618
|
+
return { action: item.state.kind };
|
|
4619
|
+
}
|
|
4620
|
+
async function status(argv, ctx) {
|
|
4621
|
+
const args = parseCommandArgs(argv, {}, 0, 3, SKILLS_STATUS_USAGE);
|
|
4622
|
+
const home = homeDir(process9.env);
|
|
4623
|
+
const targets = await targetsWithDetection(args.positionals, home);
|
|
4624
|
+
if (targets.length === 0) return EXIT.ok;
|
|
4625
|
+
const source = await sourceOf();
|
|
4626
|
+
const list3 = await skillsStatus({ home, source, targets });
|
|
4627
|
+
const targetWidth = Math.max(...targets.map((target) => target.length));
|
|
4628
|
+
const skillWidth = Math.max(...source.skills.map((skill) => skill.length));
|
|
4629
|
+
ctx.out.data({
|
|
4630
|
+
targets,
|
|
4631
|
+
actions: list3.map((item) => ({ target: item.target, skill: item.skill, path: item.path, ...actionOf(item) }))
|
|
4632
|
+
});
|
|
4633
|
+
for (const item of list3) {
|
|
4634
|
+
const name = `${item.target.padEnd(targetWidth)} ${item.skill.padEnd(skillWidth)}`;
|
|
4635
|
+
ctx.out.line(`${name} ${skillStateLabel(item.state)}`);
|
|
4636
|
+
if (item.state.kind === "copy" && !skillIsCurrent(item.state, VERSION)) {
|
|
4637
|
+
warn2(`${item.path} is a copy of v${item.state.version}, this rshow is ${VERSION}: run rshow skills install`);
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
return EXIT.ok;
|
|
4641
|
+
}
|
|
4151
4642
|
async function run9(argv, ctx) {
|
|
4643
|
+
const [sub, ...rest] = argv;
|
|
4644
|
+
switch (sub) {
|
|
4645
|
+
case "install":
|
|
4646
|
+
return install(rest, ctx);
|
|
4647
|
+
case "uninstall":
|
|
4648
|
+
return uninstall(rest, ctx);
|
|
4649
|
+
case "status":
|
|
4650
|
+
return status(rest, ctx);
|
|
4651
|
+
default:
|
|
4652
|
+
throw new UsageError(`skills needs a subcommand (try: ${usageLine(SKILLS_USAGE)})`);
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
|
|
4656
|
+
// src/commands/list.ts
|
|
4657
|
+
async function run10(argv, ctx) {
|
|
4152
4658
|
const args = parseCommandArgs(
|
|
4153
4659
|
argv,
|
|
4154
4660
|
{ project: { type: "string" }, tag: { type: "string" }, q: { type: "string" } },
|
|
@@ -4181,7 +4687,7 @@ async function run9(argv, ctx) {
|
|
|
4181
4687
|
}
|
|
4182
4688
|
|
|
4183
4689
|
// src/commands/show.ts
|
|
4184
|
-
async function
|
|
4690
|
+
async function run11(argv, ctx) {
|
|
4185
4691
|
const args = parseCommandArgs(argv, {}, 1, 1, SHOW_USAGE);
|
|
4186
4692
|
const slug = validateSlug(args.positionals[0]);
|
|
4187
4693
|
const api = await ctx.api();
|
|
@@ -4214,7 +4720,7 @@ async function run10(argv, ctx) {
|
|
|
4214
4720
|
|
|
4215
4721
|
// src/commands/share.ts
|
|
4216
4722
|
var MODES = /* @__PURE__ */ new Set(["private", "people", "public"]);
|
|
4217
|
-
async function
|
|
4723
|
+
async function run12(argv, ctx) {
|
|
4218
4724
|
const args = parseCommandArgs(
|
|
4219
4725
|
argv,
|
|
4220
4726
|
{
|
|
@@ -4254,7 +4760,7 @@ async function run11(argv, ctx) {
|
|
|
4254
4760
|
}
|
|
4255
4761
|
|
|
4256
4762
|
// src/commands/pin.ts
|
|
4257
|
-
async function
|
|
4763
|
+
async function run13(argv, ctx) {
|
|
4258
4764
|
const args = parseCommandArgs(argv, {}, 2, 2, PIN_USAGE);
|
|
4259
4765
|
const slug = validateSlug(args.positionals[0]);
|
|
4260
4766
|
const target = args.positionals[1];
|
|
@@ -4276,7 +4782,7 @@ async function run12(argv, ctx) {
|
|
|
4276
4782
|
}
|
|
4277
4783
|
|
|
4278
4784
|
// src/commands/delete.ts
|
|
4279
|
-
async function
|
|
4785
|
+
async function run14(argv, ctx) {
|
|
4280
4786
|
const args = parseCommandArgs(argv, { yes: { type: "boolean" } }, 1, 1, DELETE_USAGE);
|
|
4281
4787
|
const slug = validateSlug(args.positionals[0]);
|
|
4282
4788
|
if (!boolValue(args, "yes")) throw new UsageError(`delete needs --yes (try: ${usageLine(DELETE_USAGE)})`);
|
|
@@ -4401,7 +4907,7 @@ async function remove2(argv, ctx) {
|
|
|
4401
4907
|
ctx.out.data(null);
|
|
4402
4908
|
ctx.out.line(`deleted: ${slug}`);
|
|
4403
4909
|
}
|
|
4404
|
-
async function
|
|
4910
|
+
async function run15(argv, ctx) {
|
|
4405
4911
|
const [sub, ...rest] = argv;
|
|
4406
4912
|
if (sub === void 0) {
|
|
4407
4913
|
throw new UsageError(`expected a project command (try: ${usageLine(PROJECT_USAGE)})`);
|
|
@@ -4415,7 +4921,7 @@ async function run14(argv, ctx) {
|
|
|
4415
4921
|
}
|
|
4416
4922
|
|
|
4417
4923
|
// src/commands/tag.ts
|
|
4418
|
-
async function
|
|
4924
|
+
async function run16(argv, ctx) {
|
|
4419
4925
|
const args = parseCommandArgs(
|
|
4420
4926
|
argv,
|
|
4421
4927
|
{ add: { type: "string", multiple: true }, remove: { type: "string", multiple: true } },
|
|
@@ -4439,20 +4945,20 @@ async function run15(argv, ctx) {
|
|
|
4439
4945
|
}
|
|
4440
4946
|
|
|
4441
4947
|
// src/commands/handoff.ts
|
|
4442
|
-
import
|
|
4948
|
+
import process13 from "node:process";
|
|
4443
4949
|
|
|
4444
4950
|
// src/handoff.ts
|
|
4445
|
-
import { mkdir as
|
|
4446
|
-
import
|
|
4447
|
-
import
|
|
4951
|
+
import { mkdir as mkdir6, stat as stat13, writeFile as writeFile8 } from "node:fs/promises";
|
|
4952
|
+
import path20 from "node:path";
|
|
4953
|
+
import process12 from "node:process";
|
|
4448
4954
|
|
|
4449
4955
|
// src/cdp.ts
|
|
4450
4956
|
import { spawn as spawn3 } from "node:child_process";
|
|
4451
4957
|
import { existsSync, rmSync } from "node:fs";
|
|
4452
|
-
import { mkdtemp as
|
|
4958
|
+
import { mkdtemp as mkdtemp4, rm as rm6 } from "node:fs/promises";
|
|
4453
4959
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
4454
|
-
import
|
|
4455
|
-
import
|
|
4960
|
+
import path18 from "node:path";
|
|
4961
|
+
import process10 from "node:process";
|
|
4456
4962
|
var CHROME_CANDIDATES = [
|
|
4457
4963
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
4458
4964
|
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
@@ -4472,9 +4978,9 @@ var LOAD_TIMEOUT_MS = 2e4;
|
|
|
4472
4978
|
var liveBrowsers = /* @__PURE__ */ new Map();
|
|
4473
4979
|
var signalHandlersInstalled = false;
|
|
4474
4980
|
function killBrowser(child, signal) {
|
|
4475
|
-
if (
|
|
4981
|
+
if (process10.platform !== "win32" && child.pid !== void 0) {
|
|
4476
4982
|
try {
|
|
4477
|
-
|
|
4983
|
+
process10.kill(-child.pid, signal);
|
|
4478
4984
|
return;
|
|
4479
4985
|
} catch {
|
|
4480
4986
|
}
|
|
@@ -4499,24 +5005,24 @@ function reapBrowsers() {
|
|
|
4499
5005
|
}
|
|
4500
5006
|
function onInterrupt() {
|
|
4501
5007
|
reapBrowsers();
|
|
4502
|
-
|
|
5008
|
+
process10.exit(130);
|
|
4503
5009
|
}
|
|
4504
5010
|
function onTerminate() {
|
|
4505
5011
|
reapBrowsers();
|
|
4506
|
-
|
|
5012
|
+
process10.exit(143);
|
|
4507
5013
|
}
|
|
4508
5014
|
function dropSignalHandlers() {
|
|
4509
5015
|
if (!signalHandlersInstalled) return;
|
|
4510
5016
|
signalHandlersInstalled = false;
|
|
4511
|
-
|
|
4512
|
-
|
|
5017
|
+
process10.off("SIGINT", onInterrupt);
|
|
5018
|
+
process10.off("SIGTERM", onTerminate);
|
|
4513
5019
|
}
|
|
4514
5020
|
function trackBrowser(child, profile) {
|
|
4515
5021
|
liveBrowsers.set(child, profile);
|
|
4516
5022
|
if (!signalHandlersInstalled) {
|
|
4517
5023
|
signalHandlersInstalled = true;
|
|
4518
|
-
|
|
4519
|
-
|
|
5024
|
+
process10.on("SIGINT", onInterrupt);
|
|
5025
|
+
process10.on("SIGTERM", onTerminate);
|
|
4520
5026
|
}
|
|
4521
5027
|
}
|
|
4522
5028
|
function forgetBrowser(child) {
|
|
@@ -4527,20 +5033,20 @@ var exitHookInstalled = false;
|
|
|
4527
5033
|
function installExitHook() {
|
|
4528
5034
|
if (exitHookInstalled) return;
|
|
4529
5035
|
exitHookInstalled = true;
|
|
4530
|
-
|
|
5036
|
+
process10.once("exit", reapBrowsers);
|
|
4531
5037
|
}
|
|
4532
5038
|
function messageOf11(err) {
|
|
4533
5039
|
return err instanceof Error ? err.message : String(err);
|
|
4534
5040
|
}
|
|
4535
5041
|
function whichInPath(name, pathValue) {
|
|
4536
|
-
for (const dir of String(pathValue ?? "").split(
|
|
5042
|
+
for (const dir of String(pathValue ?? "").split(path18.delimiter)) {
|
|
4537
5043
|
if (dir === "") continue;
|
|
4538
|
-
const file =
|
|
5044
|
+
const file = path18.join(dir, name);
|
|
4539
5045
|
if (existsSync(file)) return file;
|
|
4540
5046
|
}
|
|
4541
5047
|
return void 0;
|
|
4542
5048
|
}
|
|
4543
|
-
function findChrome(env =
|
|
5049
|
+
function findChrome(env = process10.env) {
|
|
4544
5050
|
const explicit = (env.CHROME ?? "").trim();
|
|
4545
5051
|
if (explicit !== "") {
|
|
4546
5052
|
if (existsSync(explicit)) return explicit;
|
|
@@ -4711,7 +5217,7 @@ var Chrome = class _Chrome {
|
|
|
4711
5217
|
static async launch(opts = {}) {
|
|
4712
5218
|
const binary = opts.binary ?? findChrome();
|
|
4713
5219
|
if (binary === void 0) throw new UsageError("no Chrome or Chromium found, set CHROME");
|
|
4714
|
-
const profile = await
|
|
5220
|
+
const profile = await mkdtemp4(path18.join(tmpdir3(), "rshow-chrome-"));
|
|
4715
5221
|
const child = spawn3(
|
|
4716
5222
|
binary,
|
|
4717
5223
|
[
|
|
@@ -4731,7 +5237,7 @@ var Chrome = class _Chrome {
|
|
|
4731
5237
|
"--window-size=1440,900",
|
|
4732
5238
|
"about:blank"
|
|
4733
5239
|
],
|
|
4734
|
-
{ stdio: ["ignore", "pipe", "pipe"], detached:
|
|
5240
|
+
{ stdio: ["ignore", "pipe", "pipe"], detached: process10.platform !== "win32" }
|
|
4735
5241
|
);
|
|
4736
5242
|
installExitHook();
|
|
4737
5243
|
trackBrowser(child, profile);
|
|
@@ -4742,7 +5248,7 @@ var Chrome = class _Chrome {
|
|
|
4742
5248
|
} catch (err) {
|
|
4743
5249
|
forgetBrowser(child);
|
|
4744
5250
|
killBrowser(child, "SIGKILL");
|
|
4745
|
-
await
|
|
5251
|
+
await rm6(profile, { recursive: true, force: true }).catch(() => {
|
|
4746
5252
|
});
|
|
4747
5253
|
if (err instanceof ApiError || err instanceof UsageError) throw err;
|
|
4748
5254
|
throw new ApiError(`cannot start chrome: ${messageOf11(err)}`);
|
|
@@ -4757,7 +5263,7 @@ var Chrome = class _Chrome {
|
|
|
4757
5263
|
killBrowser(this.child, "SIGTERM");
|
|
4758
5264
|
await delay(50);
|
|
4759
5265
|
if (this.child.exitCode === null) killBrowser(this.child, "SIGKILL");
|
|
4760
|
-
await
|
|
5266
|
+
await rm6(this.profile, PROFILE_REMOVE).catch(() => {
|
|
4761
5267
|
});
|
|
4762
5268
|
forgetBrowser(this.child);
|
|
4763
5269
|
}
|
|
@@ -4863,9 +5369,9 @@ var Page = class _Page {
|
|
|
4863
5369
|
};
|
|
4864
5370
|
|
|
4865
5371
|
// src/map.ts
|
|
4866
|
-
import { readFile as
|
|
4867
|
-
import
|
|
4868
|
-
import
|
|
5372
|
+
import { readFile as readFile13, writeFile as writeFile7 } from "node:fs/promises";
|
|
5373
|
+
import path19 from "node:path";
|
|
5374
|
+
import process11 from "node:process";
|
|
4869
5375
|
var MAP_FILE = "rshow.map.json";
|
|
4870
5376
|
function isRecord5(value) {
|
|
4871
5377
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -4881,12 +5387,12 @@ function textMap(file, key2, value) {
|
|
|
4881
5387
|
return out;
|
|
4882
5388
|
}
|
|
4883
5389
|
function mapPath(dir) {
|
|
4884
|
-
return
|
|
5390
|
+
return path19.resolve(dir ?? process11.cwd(), MAP_FILE);
|
|
4885
5391
|
}
|
|
4886
5392
|
async function readMapFile(file) {
|
|
4887
5393
|
let text;
|
|
4888
5394
|
try {
|
|
4889
|
-
text = await
|
|
5395
|
+
text = await readFile13(file, "utf8");
|
|
4890
5396
|
} catch (err) {
|
|
4891
5397
|
if (err.code === "ENOENT") return void 0;
|
|
4892
5398
|
throw new UsageError(`cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -4905,7 +5411,7 @@ async function readMapFile(file) {
|
|
|
4905
5411
|
};
|
|
4906
5412
|
}
|
|
4907
5413
|
async function writeMapFile(file, map) {
|
|
4908
|
-
await
|
|
5414
|
+
await writeFile7(file, `${JSON.stringify(sortMap(map), null, 2)}
|
|
4909
5415
|
`);
|
|
4910
5416
|
}
|
|
4911
5417
|
function sortRecord(value) {
|
|
@@ -5349,14 +5855,14 @@ function parseTarget(input) {
|
|
|
5349
5855
|
if (remote !== null) {
|
|
5350
5856
|
return { kind: "remote", slug: remote[1], version: Number(remote[2]) };
|
|
5351
5857
|
}
|
|
5352
|
-
const dir =
|
|
5353
|
-
return { kind: "local", dir, slug: deriveSlug(
|
|
5858
|
+
const dir = path20.resolve(input);
|
|
5859
|
+
return { kind: "local", dir, slug: deriveSlug(path20.basename(dir)) || "handoff" };
|
|
5354
5860
|
}
|
|
5355
5861
|
async function requireLocalDir(target) {
|
|
5356
5862
|
if (target.kind !== "local") throw new UsageError("expected a local folder");
|
|
5357
|
-
const info = await
|
|
5863
|
+
const info = await stat13(target.dir).catch(() => void 0);
|
|
5358
5864
|
if (info === void 0 || !info.isDirectory()) throw new UsageError(`no such folder: ${target.dir}`);
|
|
5359
|
-
const index = await
|
|
5865
|
+
const index = await stat13(path20.join(target.dir, "index.html")).catch(() => void 0);
|
|
5360
5866
|
if (index === void 0 || !index.isFile()) throw new UsageError(`${target.dir} has no index.html`);
|
|
5361
5867
|
return target.dir;
|
|
5362
5868
|
}
|
|
@@ -5371,10 +5877,10 @@ function safeName(text) {
|
|
|
5371
5877
|
return cleaned === "" ? "screen" : cleaned;
|
|
5372
5878
|
}
|
|
5373
5879
|
function displayPath(target) {
|
|
5374
|
-
const resolved =
|
|
5375
|
-
const relative =
|
|
5880
|
+
const resolved = path20.resolve(target);
|
|
5881
|
+
const relative = path20.relative(process12.cwd(), resolved);
|
|
5376
5882
|
if (relative === "") return ".";
|
|
5377
|
-
if (relative.startsWith("..") ||
|
|
5883
|
+
if (relative.startsWith("..") || path20.isAbsolute(relative)) return resolved;
|
|
5378
5884
|
return relative;
|
|
5379
5885
|
}
|
|
5380
5886
|
function baselineName(screen, state, width, mode) {
|
|
@@ -5535,7 +6041,7 @@ async function runHandoff(target, opts) {
|
|
|
5535
6041
|
const version = target.kind === "remote" ? target.version : null;
|
|
5536
6042
|
const slug = target.slug;
|
|
5537
6043
|
const url = target.kind === "remote" ? `${art}/${slug}/v${version}/` : "";
|
|
5538
|
-
const out =
|
|
6044
|
+
const out = path20.resolve(opts.out);
|
|
5539
6045
|
let closePreview;
|
|
5540
6046
|
let pageUrl = url;
|
|
5541
6047
|
if (target.kind === "remote") {
|
|
@@ -5550,8 +6056,8 @@ async function runHandoff(target, opts) {
|
|
|
5550
6056
|
const styleTexts = await collectStyleTexts(pageUrl, fetchImpl);
|
|
5551
6057
|
const tokens = extractTokens(styleTexts);
|
|
5552
6058
|
say(`tokens: ${tokens.light.length} light, ${tokens.dark.length} dark`);
|
|
5553
|
-
await
|
|
5554
|
-
await
|
|
6059
|
+
await mkdir6(path20.join(out, "baseline"), { recursive: true });
|
|
6060
|
+
await writeFile8(path20.join(out, "tokens.json"), `${JSON.stringify(dtcgTokens(tokens), null, 2)}
|
|
5555
6061
|
`);
|
|
5556
6062
|
const screens = [];
|
|
5557
6063
|
const baselines = [];
|
|
@@ -5602,7 +6108,7 @@ async function runHandoff(target, opts) {
|
|
|
5602
6108
|
}
|
|
5603
6109
|
const png = await capture(page, screen);
|
|
5604
6110
|
const name = baselineName(screen.id, state, width, mode);
|
|
5605
|
-
await
|
|
6111
|
+
await writeFile8(path20.join(out, "baseline", name), png);
|
|
5606
6112
|
baselines.push({ screen: screen.id, state, width, mode, file: `baseline/${name}` });
|
|
5607
6113
|
}
|
|
5608
6114
|
}
|
|
@@ -5631,7 +6137,7 @@ async function runHandoff(target, opts) {
|
|
|
5631
6137
|
screens,
|
|
5632
6138
|
baselines
|
|
5633
6139
|
};
|
|
5634
|
-
await
|
|
6140
|
+
await writeFile8(path20.join(out, "spec.json"), `${JSON.stringify(spec, null, 2)}
|
|
5635
6141
|
`);
|
|
5636
6142
|
const mapFile = MAP_FILE;
|
|
5637
6143
|
let mapWritten = false;
|
|
@@ -5650,7 +6156,7 @@ async function runHandoff(target, opts) {
|
|
|
5650
6156
|
createdAt,
|
|
5651
6157
|
mapFile
|
|
5652
6158
|
});
|
|
5653
|
-
await
|
|
6159
|
+
await writeFile8(path20.join(out, "HANDOFF.md"), markdown);
|
|
5654
6160
|
return {
|
|
5655
6161
|
out,
|
|
5656
6162
|
url: pageUrl,
|
|
@@ -5691,7 +6197,7 @@ function modeList(text) {
|
|
|
5691
6197
|
}
|
|
5692
6198
|
return out;
|
|
5693
6199
|
}
|
|
5694
|
-
async function
|
|
6200
|
+
async function run17(argv, ctx) {
|
|
5695
6201
|
const args = parseCommandArgs(
|
|
5696
6202
|
argv,
|
|
5697
6203
|
{ out: { type: "string" }, widths: { type: "string" }, modes: { type: "string" } },
|
|
@@ -5714,7 +6220,7 @@ async function run16(argv, ctx) {
|
|
|
5714
6220
|
widths: widthList(stringValue(args, "widths")),
|
|
5715
6221
|
modes: modeList(stringValue(args, "modes")),
|
|
5716
6222
|
art: config.art ?? "",
|
|
5717
|
-
chrome:
|
|
6223
|
+
chrome: process13.env.CHROME,
|
|
5718
6224
|
onProgress: (line) => ctx.out.line(line)
|
|
5719
6225
|
});
|
|
5720
6226
|
const display = displayPath(result.out);
|
|
@@ -5740,11 +6246,11 @@ async function run16(argv, ctx) {
|
|
|
5740
6246
|
}
|
|
5741
6247
|
|
|
5742
6248
|
// src/commands/compare.ts
|
|
5743
|
-
import
|
|
6249
|
+
import process15 from "node:process";
|
|
5744
6250
|
|
|
5745
6251
|
// src/compare.ts
|
|
5746
|
-
import { copyFile, mkdir as
|
|
5747
|
-
import
|
|
6252
|
+
import { copyFile, mkdir as mkdir7, readFile as readFile15, writeFile as writeFile9 } from "node:fs/promises";
|
|
6253
|
+
import path22 from "node:path";
|
|
5748
6254
|
|
|
5749
6255
|
// src/diff.ts
|
|
5750
6256
|
var MAX_Y = 35215;
|
|
@@ -5906,10 +6412,10 @@ function diffImages(before, after, opts = {}) {
|
|
|
5906
6412
|
|
|
5907
6413
|
// src/native.ts
|
|
5908
6414
|
import { spawn as spawn4 } from "node:child_process";
|
|
5909
|
-
import { readFile as
|
|
6415
|
+
import { readFile as readFile14, unlink } from "node:fs/promises";
|
|
5910
6416
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
5911
|
-
import
|
|
5912
|
-
import
|
|
6417
|
+
import path21 from "node:path";
|
|
6418
|
+
import process14 from "node:process";
|
|
5913
6419
|
var FILE_PREFIX = "file:";
|
|
5914
6420
|
var WINDOW_PREFIX = "window:";
|
|
5915
6421
|
var TMUX_PREFIX = "tmux:";
|
|
@@ -5938,9 +6444,9 @@ function isNative(source) {
|
|
|
5938
6444
|
}
|
|
5939
6445
|
async function captureNative(source, mapDir, deps = {}) {
|
|
5940
6446
|
if (source.kind === "file") {
|
|
5941
|
-
const file =
|
|
6447
|
+
const file = path21.resolve(mapDir, source.file);
|
|
5942
6448
|
try {
|
|
5943
|
-
return new Uint8Array(await
|
|
6449
|
+
return new Uint8Array(await readFile14(file));
|
|
5944
6450
|
} catch {
|
|
5945
6451
|
throw new UsageError(`no screenshot at ${source.file}, which is relative to rshow.map.json`);
|
|
5946
6452
|
}
|
|
@@ -6011,9 +6517,9 @@ var systemRunner = {
|
|
|
6011
6517
|
})
|
|
6012
6518
|
};
|
|
6013
6519
|
async function captureWindow(title, deps = {}) {
|
|
6014
|
-
const platform = deps.platform ??
|
|
6520
|
+
const platform = deps.platform ?? process14.platform;
|
|
6015
6521
|
const runner = deps.runner ?? systemRunner;
|
|
6016
|
-
const scratch =
|
|
6522
|
+
const scratch = path21.join(tmpdir4(), `rshow-window-${process14.pid}-${Date.now()}.png`);
|
|
6017
6523
|
const wrap = (err) => err instanceof UsageError ? err : new UsageError(`window: ${title}: ${err instanceof Error ? err.message : String(err)}; ${NATIVE_HINT}`);
|
|
6018
6524
|
try {
|
|
6019
6525
|
if (platform === "darwin") {
|
|
@@ -6029,7 +6535,7 @@ async function captureWindow(title, deps = {}) {
|
|
|
6029
6535
|
} else {
|
|
6030
6536
|
throw new UsageError(`window: capture is not supported on ${platform}; ${NATIVE_HINT}`);
|
|
6031
6537
|
}
|
|
6032
|
-
return new Uint8Array(await
|
|
6538
|
+
return new Uint8Array(await readFile14(scratch));
|
|
6033
6539
|
} catch (err) {
|
|
6034
6540
|
throw wrap(err);
|
|
6035
6541
|
} finally {
|
|
@@ -6843,9 +7349,9 @@ var EMPTY_MAP = { components: {}, tokens: {}, screens: {} };
|
|
|
6843
7349
|
async function readJson(file, what) {
|
|
6844
7350
|
let text;
|
|
6845
7351
|
try {
|
|
6846
|
-
text = await
|
|
7352
|
+
text = await readFile15(file, "utf8");
|
|
6847
7353
|
} catch {
|
|
6848
|
-
throw new UsageError(`${what} is missing ${
|
|
7354
|
+
throw new UsageError(`${what} is missing ${path22.basename(file)} (run rshow handoff first)`);
|
|
6849
7355
|
}
|
|
6850
7356
|
try {
|
|
6851
7357
|
return JSON.parse(text);
|
|
@@ -6854,13 +7360,13 @@ async function readJson(file, what) {
|
|
|
6854
7360
|
}
|
|
6855
7361
|
}
|
|
6856
7362
|
async function readHandoff(dir) {
|
|
6857
|
-
const spec = await readJson(
|
|
7363
|
+
const spec = await readJson(path22.join(dir, "spec.json"), dir);
|
|
6858
7364
|
if (spec === null || typeof spec !== "object" || !Array.isArray(spec.screens)) {
|
|
6859
7365
|
throw new UsageError(`${dir}/spec.json does not hold a handoff spec`);
|
|
6860
7366
|
}
|
|
6861
7367
|
let tokens = { light: [], dark: [] };
|
|
6862
7368
|
try {
|
|
6863
|
-
tokens = flattenDtcg(JSON.parse(await
|
|
7369
|
+
tokens = flattenDtcg(JSON.parse(await readFile15(path22.join(dir, "tokens.json"), "utf8")));
|
|
6864
7370
|
} catch {
|
|
6865
7371
|
tokens = { light: [], dark: [] };
|
|
6866
7372
|
}
|
|
@@ -6883,15 +7389,15 @@ function clipOf(info, screenId) {
|
|
|
6883
7389
|
async function runCompare(opts) {
|
|
6884
7390
|
const say = opts.onProgress ?? (() => {
|
|
6885
7391
|
});
|
|
6886
|
-
const dir =
|
|
7392
|
+
const dir = path22.resolve(opts.against);
|
|
6887
7393
|
const { spec, tokens } = await readHandoff(dir);
|
|
6888
|
-
const mapFile = opts.map === void 0 ? mapPath() :
|
|
7394
|
+
const mapFile = opts.map === void 0 ? mapPath() : path22.resolve(opts.map);
|
|
6889
7395
|
const map = await readMapFile(mapFile) ?? EMPTY_MAP;
|
|
6890
|
-
const mapDir =
|
|
6891
|
-
const reportDir =
|
|
6892
|
-
const imagesDir =
|
|
6893
|
-
await
|
|
6894
|
-
await
|
|
7396
|
+
const mapDir = path22.dirname(mapFile);
|
|
7397
|
+
const reportDir = path22.join(dir, "compare-report");
|
|
7398
|
+
const imagesDir = path22.join(reportDir, "images");
|
|
7399
|
+
await mkdir7(imagesDir, { recursive: true });
|
|
7400
|
+
await mkdir7(path22.join(dir, "diff"), { recursive: true });
|
|
6895
7401
|
const results = [];
|
|
6896
7402
|
const collected = [];
|
|
6897
7403
|
let skipped = 0;
|
|
@@ -6926,16 +7432,16 @@ async function runCompare(opts) {
|
|
|
6926
7432
|
throw new UsageError(`tmux capture failed for ${target}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6927
7433
|
});
|
|
6928
7434
|
const text = opts.tuiColors ? captured : stripAnsi(captured);
|
|
6929
|
-
const baselineFile =
|
|
7435
|
+
const baselineFile = path22.join(dir, "baseline", `${safeName(screen)}.txt`);
|
|
6930
7436
|
let expected;
|
|
6931
7437
|
try {
|
|
6932
|
-
expected = await
|
|
7438
|
+
expected = await readFile15(baselineFile, "utf8");
|
|
6933
7439
|
} catch {
|
|
6934
7440
|
expected = void 0;
|
|
6935
7441
|
}
|
|
6936
7442
|
if (expected === void 0) {
|
|
6937
|
-
await
|
|
6938
|
-
await
|
|
7443
|
+
await mkdir7(path22.dirname(baselineFile), { recursive: true });
|
|
7444
|
+
await writeFile9(baselineFile, text);
|
|
6939
7445
|
results.push({
|
|
6940
7446
|
screen,
|
|
6941
7447
|
state: "default",
|
|
@@ -7010,7 +7516,7 @@ async function runCompare(opts) {
|
|
|
7010
7516
|
}
|
|
7011
7517
|
let expectedImage;
|
|
7012
7518
|
try {
|
|
7013
|
-
expectedImage = decodePng(new Uint8Array(await
|
|
7519
|
+
expectedImage = decodePng(new Uint8Array(await readFile15(path22.join(dir, baseline.file))));
|
|
7014
7520
|
} catch {
|
|
7015
7521
|
results.push({ ...base, note: `missing baseline ${baseline.file}` });
|
|
7016
7522
|
continue;
|
|
@@ -7022,10 +7528,10 @@ async function runCompare(opts) {
|
|
|
7022
7528
|
threshold: opts.threshold
|
|
7023
7529
|
});
|
|
7024
7530
|
const diffPng = encodePng(diff.image);
|
|
7025
|
-
await
|
|
7026
|
-
await
|
|
7027
|
-
await
|
|
7028
|
-
await copyFile(
|
|
7531
|
+
await writeFile9(path22.join(imagesDir, `${stem}-diff.png`), diffPng);
|
|
7532
|
+
await writeFile9(path22.join(dir, "diff", `${stem}.png`), diffPng);
|
|
7533
|
+
await writeFile9(path22.join(imagesDir, `${stem}-capture.png`), encodePng(scaled));
|
|
7534
|
+
await copyFile(path22.join(dir, baseline.file), path22.join(imagesDir, `${stem}-baseline.png`));
|
|
7029
7535
|
const expected = screenSpec === void 0 ? [] : normativeColors(screenSpec, tokens, baseline.state, baseline.mode);
|
|
7030
7536
|
const palette = checkPalette(extractPalette(scaled), expected);
|
|
7031
7537
|
const pixelOk = diff.ratio <= opts.nativeMaxDiffRatio;
|
|
@@ -7070,7 +7576,7 @@ async function runCompare(opts) {
|
|
|
7070
7576
|
});
|
|
7071
7577
|
}
|
|
7072
7578
|
if (screenSpec !== void 0) {
|
|
7073
|
-
await
|
|
7579
|
+
await writeFile9(path22.join(reportDir, rubricFile), `${rubricMarkdown({ screen: screenSpec, shots })}
|
|
7074
7580
|
`);
|
|
7075
7581
|
}
|
|
7076
7582
|
}
|
|
@@ -7127,12 +7633,12 @@ async function runCompare(opts) {
|
|
|
7127
7633
|
}
|
|
7128
7634
|
const clip = clipOf(info, screenId) ? await screenClip(page, screenId) : null;
|
|
7129
7635
|
const actual = await page.screenshot(clip ?? void 0);
|
|
7130
|
-
const actualFile =
|
|
7131
|
-
await
|
|
7132
|
-
const baselineFile =
|
|
7636
|
+
const actualFile = path22.join(imagesDir, `${stem}-actual.png`);
|
|
7637
|
+
await writeFile9(actualFile, actual);
|
|
7638
|
+
const baselineFile = path22.join(dir, baseline.file);
|
|
7133
7639
|
let expected;
|
|
7134
7640
|
try {
|
|
7135
|
-
expected = new Uint8Array(await
|
|
7641
|
+
expected = new Uint8Array(await readFile15(baselineFile));
|
|
7136
7642
|
} catch {
|
|
7137
7643
|
results.push({
|
|
7138
7644
|
screen: baseline.screen,
|
|
@@ -7147,14 +7653,14 @@ async function runCompare(opts) {
|
|
|
7147
7653
|
});
|
|
7148
7654
|
continue;
|
|
7149
7655
|
}
|
|
7150
|
-
const baselineCopy =
|
|
7656
|
+
const baselineCopy = path22.join(imagesDir, `${stem}-baseline.png`);
|
|
7151
7657
|
await copyFile(baselineFile, baselineCopy);
|
|
7152
7658
|
const left = decodePng(expected);
|
|
7153
7659
|
const right = decodePng(actual);
|
|
7154
7660
|
const diff = diffImages(left, right, { threshold: opts.threshold });
|
|
7155
7661
|
const diffPng = encodePng(diff.image);
|
|
7156
|
-
await
|
|
7157
|
-
await
|
|
7662
|
+
await writeFile9(path22.join(imagesDir, `${stem}-diff.png`), diffPng);
|
|
7663
|
+
await writeFile9(path22.join(dir, "diff", `${stem}.png`), diffPng);
|
|
7158
7664
|
const note = left.width === right.width && left.height === right.height ? void 0 : `size ${left.width}x${left.height} against ${right.width}x${right.height}`;
|
|
7159
7665
|
results.push({
|
|
7160
7666
|
screen: baseline.screen,
|
|
@@ -7223,8 +7729,8 @@ async function runCompare(opts) {
|
|
|
7223
7729
|
const checked = results.filter((item) => item.source !== "").length;
|
|
7224
7730
|
const review = results.filter((item) => item.status === "review").length;
|
|
7225
7731
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7226
|
-
await
|
|
7227
|
-
|
|
7732
|
+
await writeFile9(
|
|
7733
|
+
path22.join(reportDir, "report.json"),
|
|
7228
7734
|
`${JSON.stringify(
|
|
7229
7735
|
{
|
|
7230
7736
|
schema: 1,
|
|
@@ -7260,8 +7766,8 @@ async function runCompare(opts) {
|
|
|
7260
7766
|
)}
|
|
7261
7767
|
`
|
|
7262
7768
|
);
|
|
7263
|
-
await
|
|
7264
|
-
|
|
7769
|
+
await writeFile9(
|
|
7770
|
+
path22.join(reportDir, "index.html"),
|
|
7265
7771
|
reportHtml({
|
|
7266
7772
|
spec,
|
|
7267
7773
|
results,
|
|
@@ -7314,15 +7820,15 @@ function reportHtml(input) {
|
|
|
7314
7820
|
const nativeLimit = input.nativeMaxDiffRatio ?? 0.15;
|
|
7315
7821
|
const statusOf = (item) => item.status ?? (item.pass ? "pass" : "fail");
|
|
7316
7822
|
const rows = results.map((item) => {
|
|
7317
|
-
const
|
|
7318
|
-
const mark =
|
|
7823
|
+
const status2 = statusOf(item);
|
|
7824
|
+
const mark = status2 === "pass" ? '<span class="pass">pass</span>' : status2 === "review" ? '<span class="review">review</span>' : '<span class="fail">fail</span>';
|
|
7319
7825
|
const ratio2 = item.diffRatio === void 0 ? "-" : item.diffRatio.toFixed(4);
|
|
7320
7826
|
const rubric = item.rubric === void 0 ? "" : ` <a href="${escapeHtml(item.rubric)}">rubric</a>`;
|
|
7321
7827
|
return `<tr><td>${escapeHtml(item.screen)}</td><td>${escapeHtml(item.state)}</td><td>${escapeHtml(item.mode)}</td><td>${item.width === 0 ? "-" : item.width}</td><td>${mark}</td><td>${ratio2}</td><td>${item.mismatches.length}</td><td>${escapeHtml(item.note ?? "")}${rubric}</td></tr>`;
|
|
7322
7828
|
}).join("\n");
|
|
7323
7829
|
const sections = results.filter((item) => item.images !== void 0 || item.mismatches.length > 0).map((item) => {
|
|
7324
|
-
const
|
|
7325
|
-
const badge = `<span class="badge ${
|
|
7830
|
+
const status2 = statusOf(item);
|
|
7831
|
+
const badge = `<span class="badge ${status2}">${status2}</span>`;
|
|
7326
7832
|
const captureCaption = item.kind === "native" ? "capture" : "actual";
|
|
7327
7833
|
const shots = item.images === void 0 ? "" : `<div class="shots">
|
|
7328
7834
|
<figure><img src="${escapeHtml(item.images.baseline)}" alt="baseline"><figcaption>baseline</figcaption></figure>
|
|
@@ -7382,7 +7888,7 @@ function ratioValue(text, name, fallback) {
|
|
|
7382
7888
|
}
|
|
7383
7889
|
return value;
|
|
7384
7890
|
}
|
|
7385
|
-
async function
|
|
7891
|
+
async function run18(argv, ctx) {
|
|
7386
7892
|
const args = parseCommandArgs(
|
|
7387
7893
|
argv,
|
|
7388
7894
|
{
|
|
@@ -7412,7 +7918,7 @@ async function run17(argv, ctx) {
|
|
|
7412
7918
|
DEFAULT_NATIVE_MAX_DIFF_RATIO
|
|
7413
7919
|
),
|
|
7414
7920
|
tuiColors: boolValue(args, "tui-colors"),
|
|
7415
|
-
chrome:
|
|
7921
|
+
chrome: process15.env.CHROME,
|
|
7416
7922
|
onProgress: (line) => ctx.out.line(line)
|
|
7417
7923
|
});
|
|
7418
7924
|
const failing = result.results.filter((item) => !item.pass);
|
|
@@ -7427,8 +7933,8 @@ async function run17(argv, ctx) {
|
|
|
7427
7933
|
const where = item.width === 0 ? item.mode : `${item.mode} ${item.width}`;
|
|
7428
7934
|
const ratio2 = item.diffRatio === void 0 ? "" : ` diff ${item.diffRatio.toFixed(4)}`;
|
|
7429
7935
|
const note = item.note === void 0 ? "" : ` (${item.note})`;
|
|
7430
|
-
const
|
|
7431
|
-
ctx.out.line(`${
|
|
7936
|
+
const status2 = item.status ?? (item.pass ? "pass" : "fail");
|
|
7937
|
+
ctx.out.line(`${status2} ${item.screen} ${item.state} ${where}${ratio2}${note}`);
|
|
7432
7938
|
for (const mismatch of item.mismatches.slice(0, 20)) {
|
|
7433
7939
|
ctx.out.line(
|
|
7434
7940
|
` mismatch ${item.screen} ${item.state} ${mismatch.component}: ${mismatch.property} expected ${mismatch.expected} actual ${mismatch.actual}`
|
|
@@ -7460,8 +7966,8 @@ async function run17(argv, ctx) {
|
|
|
7460
7966
|
}
|
|
7461
7967
|
|
|
7462
7968
|
// src/commands/docs.ts
|
|
7463
|
-
import { mkdir as
|
|
7464
|
-
import
|
|
7969
|
+
import { mkdir as mkdir8, readFile as readFile16, rm as rm7, writeFile as writeFile10 } from "node:fs/promises";
|
|
7970
|
+
import path23 from "node:path";
|
|
7465
7971
|
var USAGE = {
|
|
7466
7972
|
publish: PUBLISH_USAGE,
|
|
7467
7973
|
check: CHECK_USAGE,
|
|
@@ -7477,6 +7983,10 @@ var USAGE = {
|
|
|
7477
7983
|
"theme preview": THEME_PREVIEW_USAGE,
|
|
7478
7984
|
"theme publish": THEME_PUBLISH_USAGE,
|
|
7479
7985
|
"theme list": THEME_LIST_USAGE,
|
|
7986
|
+
skills: SKILLS_USAGE,
|
|
7987
|
+
"skills install": SKILLS_INSTALL_USAGE,
|
|
7988
|
+
"skills uninstall": SKILLS_UNINSTALL_USAGE,
|
|
7989
|
+
"skills status": SKILLS_STATUS_USAGE,
|
|
7480
7990
|
project: PROJECT_USAGE,
|
|
7481
7991
|
"project create": PROJECT_CREATE_USAGE,
|
|
7482
7992
|
"project list": PROJECT_LIST_USAGE,
|
|
@@ -7498,7 +8008,7 @@ var WHEN = {
|
|
|
7498
8008
|
auth: "the API refused the token with 401 or 403.",
|
|
7499
8009
|
api: "the API returned another error.",
|
|
7500
8010
|
build: "the `--build` command failed.",
|
|
7501
|
-
check: "`rshow check`, `rshow publish` or `rshow theme publish` found an error.",
|
|
8011
|
+
check: "`rshow check`, `rshow publish` or `rshow theme publish` found an error, or `rshow skills install` found a skill folder that is not from rshow.",
|
|
7502
8012
|
compare: "`rshow compare` found a difference."
|
|
7503
8013
|
};
|
|
7504
8014
|
var ARG_WORD = /^(?:<[^>]+>|\[[^\]]*\]|[a-z0-9-]+@v<n>|--?\S+)$/;
|
|
@@ -7736,7 +8246,7 @@ function heading(label) {
|
|
|
7736
8246
|
async function themeTokensPage() {
|
|
7737
8247
|
const dir = await builtinThemeDir("default");
|
|
7738
8248
|
if (dir === void 0) throw new Error("no built-in theme: default");
|
|
7739
|
-
const css = await
|
|
8249
|
+
const css = await readFile16(path23.join(dir, "tokens.css"), "utf8");
|
|
7740
8250
|
const end = css.indexOf("*/");
|
|
7741
8251
|
const roles = parseRoles(end < 0 ? css : css.slice(0, end));
|
|
7742
8252
|
const sections = parseSections(rootBlock(css));
|
|
@@ -7784,45 +8294,45 @@ function exitCodesPage() {
|
|
|
7784
8294
|
`;
|
|
7785
8295
|
}
|
|
7786
8296
|
async function generate(out) {
|
|
7787
|
-
const dir =
|
|
7788
|
-
await
|
|
7789
|
-
await
|
|
8297
|
+
const dir = path23.join(out, "commands");
|
|
8298
|
+
await rm7(dir, { recursive: true, force: true });
|
|
8299
|
+
await mkdir8(dir, { recursive: true });
|
|
7790
8300
|
const rows = commandRows();
|
|
7791
8301
|
const files = [];
|
|
7792
8302
|
for (const row of rows) {
|
|
7793
|
-
const file =
|
|
7794
|
-
await
|
|
8303
|
+
const file = path23.join(dir, row.file);
|
|
8304
|
+
await writeFile10(file, commandPage(row, rows));
|
|
7795
8305
|
files.push(file);
|
|
7796
8306
|
}
|
|
7797
|
-
const index =
|
|
7798
|
-
await
|
|
8307
|
+
const index = path23.join(dir, "index.md");
|
|
8308
|
+
await writeFile10(index, indexPage(rows));
|
|
7799
8309
|
files.push(index);
|
|
7800
8310
|
for (const [name, text] of [
|
|
7801
8311
|
["theme-tokens.md", await themeTokensPage()],
|
|
7802
8312
|
["exit-codes.md", exitCodesPage()]
|
|
7803
8313
|
]) {
|
|
7804
|
-
const file =
|
|
7805
|
-
await
|
|
8314
|
+
const file = path23.join(out, name);
|
|
8315
|
+
await writeFile10(file, text);
|
|
7806
8316
|
files.push(file);
|
|
7807
8317
|
}
|
|
7808
|
-
return { commands: rows.length, files: files.map((file) =>
|
|
8318
|
+
return { commands: rows.length, files: files.map((file) => path23.relative(out, file)).sort() };
|
|
7809
8319
|
}
|
|
7810
|
-
async function
|
|
8320
|
+
async function run19(argv, ctx) {
|
|
7811
8321
|
const args = parseCommandArgs(argv, { out: { type: "string" } }, 0, 0, DOCS_USAGE);
|
|
7812
8322
|
const out = stringValue(args, "out");
|
|
7813
8323
|
if (out === void 0) throw new UsageError(`docs needs --out <dir> (try: ${usageLine(DOCS_USAGE)})`);
|
|
7814
8324
|
const result = await generate(out);
|
|
7815
8325
|
ctx.out.data(result);
|
|
7816
|
-
ctx.out.line(`wrote ${result.commands} command pages and the index under ${
|
|
7817
|
-
ctx.out.line(`wrote ${
|
|
7818
|
-
ctx.out.line(`wrote ${
|
|
8326
|
+
ctx.out.line(`wrote ${result.commands} command pages and the index under ${path23.join(out, "commands")}`);
|
|
8327
|
+
ctx.out.line(`wrote ${path23.join(out, "theme-tokens.md")}`);
|
|
8328
|
+
ctx.out.line(`wrote ${path23.join(out, "exit-codes.md")}`);
|
|
7819
8329
|
}
|
|
7820
8330
|
|
|
7821
8331
|
// src/commands/login.ts
|
|
7822
|
-
import
|
|
8332
|
+
import process16 from "node:process";
|
|
7823
8333
|
async function hiddenLine() {
|
|
7824
|
-
|
|
7825
|
-
const stdin =
|
|
8334
|
+
process16.stderr.write("token: ");
|
|
8335
|
+
const stdin = process16.stdin;
|
|
7826
8336
|
stdin.setRawMode(true);
|
|
7827
8337
|
stdin.resume();
|
|
7828
8338
|
stdin.setEncoding("utf8");
|
|
@@ -7832,7 +8342,7 @@ async function hiddenLine() {
|
|
|
7832
8342
|
stdin.off("data", onData);
|
|
7833
8343
|
stdin.setRawMode(false);
|
|
7834
8344
|
stdin.pause();
|
|
7835
|
-
|
|
8345
|
+
process16.stderr.write("\n");
|
|
7836
8346
|
if (err === void 0) resolve2(text);
|
|
7837
8347
|
else reject(err);
|
|
7838
8348
|
};
|
|
@@ -7855,16 +8365,16 @@ async function hiddenLine() {
|
|
|
7855
8365
|
}
|
|
7856
8366
|
async function pipedText() {
|
|
7857
8367
|
const chunks = [];
|
|
7858
|
-
for await (const chunk2 of
|
|
8368
|
+
for await (const chunk2 of process16.stdin) chunks.push(chunk2);
|
|
7859
8369
|
return Buffer.concat(chunks).toString("utf8").trim();
|
|
7860
8370
|
}
|
|
7861
8371
|
async function tokenFromStdin() {
|
|
7862
|
-
const token =
|
|
8372
|
+
const token = process16.stdin.isTTY === true ? await hiddenLine() : await pipedText();
|
|
7863
8373
|
if (token === "") throw new UsageError("expected a token on stdin");
|
|
7864
8374
|
if (!token.startsWith(TOKEN_PREFIX)) throw new UsageError(`that is not an rshow token: it must start with ${TOKEN_PREFIX}`);
|
|
7865
8375
|
return token;
|
|
7866
8376
|
}
|
|
7867
|
-
async function
|
|
8377
|
+
async function run20(argv, ctx) {
|
|
7868
8378
|
const args = parseCommandArgs(argv, { url: { type: "string" } }, 0, 0, LOGIN_USAGE);
|
|
7869
8379
|
const settings = await ctx.config();
|
|
7870
8380
|
const given = (stringValue(args, "url") ?? "").trim();
|
|
@@ -7879,7 +8389,7 @@ async function run19(argv, ctx) {
|
|
|
7879
8389
|
}
|
|
7880
8390
|
|
|
7881
8391
|
// src/commands/logout.ts
|
|
7882
|
-
async function
|
|
8392
|
+
async function run21(argv, ctx) {
|
|
7883
8393
|
const args = parseCommandArgs(argv, { url: { type: "string" } }, 0, 0, LOGOUT_USAGE);
|
|
7884
8394
|
const settings = await ctx.config();
|
|
7885
8395
|
const given = (stringValue(args, "url") ?? "").trim();
|
|
@@ -7892,28 +8402,29 @@ async function run20(argv, ctx) {
|
|
|
7892
8402
|
var COMMANDS = {
|
|
7893
8403
|
publish: { usage: PUBLISH_USAGE, run: run2 },
|
|
7894
8404
|
check: { usage: CHECK_USAGE, run: run3 },
|
|
7895
|
-
handoff: { usage: HANDOFF_USAGE, run:
|
|
7896
|
-
compare: { usage: COMPARE_USAGE, run:
|
|
8405
|
+
handoff: { usage: HANDOFF_USAGE, run: run17 },
|
|
8406
|
+
compare: { usage: COMPARE_USAGE, run: run18 },
|
|
7897
8407
|
preview: { usage: PREVIEW_USAGE, run: run4 },
|
|
7898
8408
|
config: { usage: CONFIG_USAGE, run: run5 },
|
|
7899
8409
|
init: { usage: INIT_USAGE, run: run6 },
|
|
7900
8410
|
doctor: { usage: DOCTOR_USAGE, run: run7 },
|
|
7901
8411
|
theme: { usage: THEME_USAGE, run: run8 },
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
8412
|
+
skills: { usage: SKILLS_USAGE, run: run9 },
|
|
8413
|
+
project: { usage: PROJECT_USAGE, run: run15 },
|
|
8414
|
+
tag: { usage: TAG_USAGE, run: run16 },
|
|
8415
|
+
list: { usage: LIST_USAGE, run: run10 },
|
|
8416
|
+
show: { usage: SHOW_USAGE, run: run11 },
|
|
8417
|
+
share: { usage: SHARE_USAGE, run: run12 },
|
|
8418
|
+
pin: { usage: PIN_USAGE, run: run13 },
|
|
8419
|
+
delete: { usage: DELETE_USAGE, run: run14 },
|
|
8420
|
+
login: { usage: LOGIN_USAGE, run: run20 },
|
|
8421
|
+
logout: { usage: LOGOUT_USAGE, run: run21 },
|
|
7911
8422
|
// Kept out of the general help: the docs site runs it, a person rarely does.
|
|
7912
|
-
docs: { usage: DOCS_USAGE, run:
|
|
8423
|
+
docs: { usage: DOCS_USAGE, run: run19 }
|
|
7913
8424
|
};
|
|
7914
8425
|
function fail(err) {
|
|
7915
8426
|
const message = err instanceof Error ? err.message : String(err);
|
|
7916
|
-
|
|
8427
|
+
process17.stderr.write(`rshow: ${message}
|
|
7917
8428
|
`);
|
|
7918
8429
|
if (err instanceof UsageError) return EXIT.usage;
|
|
7919
8430
|
if (err instanceof CheckError) return EXIT.check;
|
|
@@ -7930,17 +8441,18 @@ async function main(argv) {
|
|
|
7930
8441
|
const name = args.find((arg) => !arg.startsWith("-"));
|
|
7931
8442
|
const spec = name === void 0 ? void 0 : COMMANDS[name];
|
|
7932
8443
|
if (wantsVersion) {
|
|
7933
|
-
|
|
8444
|
+
const suffix = await isBinary() ? " (binary)" : "";
|
|
8445
|
+
process17.stdout.write(`rshow ${VERSION}${suffix}
|
|
7934
8446
|
`);
|
|
7935
8447
|
return EXIT.ok;
|
|
7936
8448
|
}
|
|
7937
8449
|
if (wantsHelp) {
|
|
7938
|
-
|
|
8450
|
+
process17.stdout.write(`${spec === void 0 ? GENERAL_USAGE : spec.usage}
|
|
7939
8451
|
`);
|
|
7940
8452
|
return EXIT.ok;
|
|
7941
8453
|
}
|
|
7942
8454
|
if (name === void 0) {
|
|
7943
|
-
|
|
8455
|
+
process17.stderr.write(`${GENERAL_USAGE}
|
|
7944
8456
|
`);
|
|
7945
8457
|
return EXIT.usage;
|
|
7946
8458
|
}
|
|
@@ -7951,12 +8463,12 @@ async function main(argv) {
|
|
|
7951
8463
|
const rest = [...args.slice(0, index), ...args.slice(index + 1)];
|
|
7952
8464
|
const configs = /* @__PURE__ */ new Map();
|
|
7953
8465
|
const clients = /* @__PURE__ */ new Map();
|
|
7954
|
-
const keyOf = (dir) => dir === void 0 ? "" :
|
|
8466
|
+
const keyOf = (dir) => dir === void 0 ? "" : path24.resolve(dir);
|
|
7955
8467
|
const configFor = (dir) => {
|
|
7956
8468
|
const key2 = keyOf(dir);
|
|
7957
8469
|
let pending = configs.get(key2);
|
|
7958
8470
|
if (pending === void 0) {
|
|
7959
|
-
pending = loadConfig(
|
|
8471
|
+
pending = loadConfig(process17.env, dir);
|
|
7960
8472
|
configs.set(key2, pending);
|
|
7961
8473
|
}
|
|
7962
8474
|
return pending;
|
|
@@ -7969,7 +8481,7 @@ async function main(argv) {
|
|
|
7969
8481
|
if (cached !== void 0) return cached;
|
|
7970
8482
|
const settings = await configFor(dir);
|
|
7971
8483
|
const url = requireUrl(settings);
|
|
7972
|
-
const client = new Client(url, await readToken(
|
|
8484
|
+
const client = new Client(url, await readToken(process17.env, url));
|
|
7973
8485
|
clients.set(key2, client);
|
|
7974
8486
|
return client;
|
|
7975
8487
|
},
|
|
@@ -7978,4 +8490,11 @@ async function main(argv) {
|
|
|
7978
8490
|
const code = await spec.run(rest, ctx);
|
|
7979
8491
|
return code ?? EXIT.ok;
|
|
7980
8492
|
}
|
|
7981
|
-
|
|
8493
|
+
main(process17.argv.slice(2)).then(
|
|
8494
|
+
(code) => {
|
|
8495
|
+
process17.exitCode = code;
|
|
8496
|
+
},
|
|
8497
|
+
(err) => {
|
|
8498
|
+
process17.exitCode = fail(err);
|
|
8499
|
+
}
|
|
8500
|
+
);
|