marvelous-ui-library 1.0.1 → 1.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/README.md +1 -1
- package/bin.mjs +30 -7
- package/lib/agents.mjs +69 -0
- package/lib/installer.mjs +10 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ npx marvelous-ui-library@latest init
|
|
|
7
7
|
npx marvelous-ui-library@latest add dialog tabs toast
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
`init` detects the project and supported agent configuration. In an interactive terminal it asks whether to install the Pack in this project or in your user profile. Enter a Polar license key in the hidden prompt for Pro, or press Enter for Free. For automation, set `MARVELOUS_LICENSE_KEY` for Pro and
|
|
10
|
+
`init` detects the project and supported agent configuration. In an interactive terminal it asks whether to install the Pack in this project or in your user profile. Enter a Polar license key in the hidden prompt for Pro, or press Enter for Free. User setup then asks which agents to connect and registers Codex and Claude Code directly. For automation, set `MARVELOUS_LICENSE_KEY` for Pro and use `--scope user --clients codex,claude` to choose agents; `--clients none` installs only the shared Pack. Rerunning user setup removes a previously selected plugin when you deselect that agent. A noninteractive invocation without a key defaults to Free in the current project. `--edition free|pro` remains available for existing scripts. Other agents can use project setup or the manual instructions in the Pack's `docs/MCP.md`.
|
|
11
11
|
|
|
12
12
|
The Pro Pack requires a Polar license key for the first download and for newer releases. A supplied invalid key causes an error. The key is not written to a project file. A project Pack remains at its installed version until you run:
|
|
13
13
|
|
package/bin.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { join, resolve } from "node:path";
|
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
6
|
import { Writable } from "node:stream";
|
|
7
7
|
import { locations, readState, latestRelease, downloadRelease, installPack, addComponents, detectFramework, listProArchives, readProArchive } from "./lib/installer.mjs";
|
|
8
|
+
import { checkUserAgents, configureUserAgents, parseUserAgents, readUserAgents } from "./lib/agents.mjs";
|
|
8
9
|
|
|
9
10
|
const HELP = `Marvelous UI
|
|
10
11
|
|
|
@@ -13,7 +14,7 @@ const HELP = `Marvelous UI
|
|
|
13
14
|
npx marvelous-ui-library@latest update [--scope project|user]
|
|
14
15
|
npx marvelous-ui-library@latest restore [--scope project|user] [--version 1.1.0]
|
|
15
16
|
|
|
16
|
-
Options: --project path, --clients names. At init, press Enter for Free or enter a Polar key for Pro. In automation, set MARVELOUS_LICENSE_KEY for Pro.
|
|
17
|
+
Options: --project path, --clients names. User setup supports codex,claude,none. At init, press Enter for Free or enter a Polar key for Pro. In automation, set MARVELOUS_LICENSE_KEY for Pro.
|
|
17
18
|
`;
|
|
18
19
|
|
|
19
20
|
function options(args) {
|
|
@@ -69,8 +70,22 @@ function chosenOut(project, supplied, componentState) {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
function pluginRefresh(place) {
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
if (place.scope !== "user") return;
|
|
74
|
+
const agents = readUserAgents(place);
|
|
75
|
+
if (agents.length) {
|
|
76
|
+
const names = configureUserAgents(place, agents);
|
|
77
|
+
console.log(`Updated agent plugins: ${names.join(", ")}.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function defaultUserAgents(place) {
|
|
82
|
+
const previous = readUserAgents(place);
|
|
83
|
+
if (existsSync(join(place.base, "agents.json"))) return previous.length ? previous.join(",") : "none";
|
|
84
|
+
for (const name of ["codex", "claude"]) {
|
|
85
|
+
try { execFileSync(name, ["--version"], { stdio: "ignore" }); return name; }
|
|
86
|
+
catch {}
|
|
87
|
+
}
|
|
88
|
+
return "none";
|
|
74
89
|
}
|
|
75
90
|
|
|
76
91
|
async function init(opts, project) {
|
|
@@ -82,9 +97,17 @@ async function init(opts, project) {
|
|
|
82
97
|
const edition = opts.edition ?? (key || current?.edition === "pro" ? "pro" : "free");
|
|
83
98
|
if (!["project", "user"].includes(scope) || !["free", "pro"].includes(edition)) throw new Error("Choose --scope project|user and --edition free|pro.");
|
|
84
99
|
if (opts.edition === "free" && key) throw new Error("A Polar key was provided with --edition free. Remove the key or choose Pro.");
|
|
100
|
+
let selectedAgents = null;
|
|
101
|
+
if (scope === "user") {
|
|
102
|
+
const choice = opts.clients ?? (process.stdin.isTTY
|
|
103
|
+
? await ask("Which agents should be connected? [codex,claude,none] (comma-separated) ", defaultUserAgents(place))
|
|
104
|
+
: existsSync(join(place.base, "agents.json")) ? defaultUserAgents(place) : "none");
|
|
105
|
+
selectedAgents = parseUserAgents(choice);
|
|
106
|
+
}
|
|
107
|
+
if (selectedAgents) checkUserAgents(selectedAgents);
|
|
85
108
|
const componentState = readState(place.components);
|
|
86
109
|
const hasComponents = !!(current && componentState?.slugs?.length && componentState.sourceScope === scope);
|
|
87
|
-
console.log(`Marvelous UI: ${edition} Pack, ${scope
|
|
110
|
+
console.log(`Marvelous UI: ${edition} Pack, ${scope === "user" ? "user profile" : `${detectFramework(project)} project`}${key ? ", Polar key received" : ""}.`);
|
|
88
111
|
if (!current || current.edition !== edition) {
|
|
89
112
|
const meta = await latestRelease(edition);
|
|
90
113
|
const bytes = await downloadRelease(edition, meta, edition === "pro" ? key || await licenseKey() : "");
|
|
@@ -109,10 +132,10 @@ async function init(opts, project) {
|
|
|
109
132
|
if (opts.clients) args.push("--clients", opts.clients);
|
|
110
133
|
execFileSync(process.execPath, args, { stdio: "inherit" });
|
|
111
134
|
} else {
|
|
112
|
-
|
|
113
|
-
|
|
135
|
+
const names = configureUserAgents(place, selectedAgents);
|
|
136
|
+
console.log(names.length ? `Connected for all projects: ${names.join(", ")}.` : "No agent connected by choice.");
|
|
114
137
|
}
|
|
115
|
-
console.log("Ready.
|
|
138
|
+
console.log(scope === "user" && !selectedAgents.length ? "Pack installed." : "Ready. Restart your agent to load Marvelous UI.");
|
|
116
139
|
}
|
|
117
140
|
|
|
118
141
|
async function restore(opts, project) {
|
package/lib/agents.mjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const SUPPORTED = ["codex", "claude"];
|
|
6
|
+
const LABELS = { codex: "Codex", claude: "Claude Code" };
|
|
7
|
+
|
|
8
|
+
export function parseUserAgents(value) {
|
|
9
|
+
const names = [...new Set(value.toLowerCase().split(",").map((name) => name.trim()).filter(Boolean))];
|
|
10
|
+
if (names.length === 1 && names[0] === "none") return [];
|
|
11
|
+
const unsupported = names.filter((name) => !SUPPORTED.includes(name));
|
|
12
|
+
if (unsupported.length) throw new Error(`User setup supports Codex and Claude Code. Unsupported choice: ${unsupported.join(", ")}. Other agents can use project setup or the manual guide.`);
|
|
13
|
+
if (!names.length) throw new Error("Choose codex, claude, both, or none.");
|
|
14
|
+
return names;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function readUserAgents(place) {
|
|
18
|
+
const file = join(place.base, "agents.json");
|
|
19
|
+
if (!existsSync(file)) return [];
|
|
20
|
+
const state = JSON.parse(readFileSync(file, "utf8"));
|
|
21
|
+
if (state.schema !== 1 || !Array.isArray(state.clients) || state.clients.some((name) => !SUPPORTED.includes(name))) {
|
|
22
|
+
throw new Error(`Unsupported Marvelous UI agent state: ${file}`);
|
|
23
|
+
}
|
|
24
|
+
return state.clients;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function runAgent(command, args, runner) {
|
|
28
|
+
try { runner(command, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); }
|
|
29
|
+
catch (error) {
|
|
30
|
+
const detail = String(error.stderr ?? error.message ?? "").trim().split(/\r?\n/).at(-1);
|
|
31
|
+
throw new Error(`${LABELS[command]} setup failed${detail ? `: ${detail}` : "."} The Pack remains installed. Rerun init to retry agent setup.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function checkUserAgents(clients, runner = execFileSync) {
|
|
36
|
+
for (const client of clients) runAgent(client, ["--version"], runner);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function configureUserAgents(place, clients, runner = execFileSync) {
|
|
40
|
+
const prior = readUserAgents(place);
|
|
41
|
+
for (const client of clients) {
|
|
42
|
+
if (client === "codex") {
|
|
43
|
+
runAgent("codex", ["plugin", "marketplace", "add", place.pack], runner);
|
|
44
|
+
runAgent("codex", ["plugin", "add", "marvelous-ui@marvelous-ui"], runner);
|
|
45
|
+
} else {
|
|
46
|
+
if (prior.includes("claude")) {
|
|
47
|
+
runAgent("claude", ["plugin", "marketplace", "update", "marvelous-ui"], runner);
|
|
48
|
+
runAgent("claude", ["plugin", "update", "marvelous-ui@marvelous-ui"], runner);
|
|
49
|
+
} else {
|
|
50
|
+
runAgent("claude", ["plugin", "marketplace", "add", place.pack], runner);
|
|
51
|
+
runAgent("claude", ["plugin", "install", "marvelous-ui@marvelous-ui", "--scope", "user"], runner);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const client of prior.filter((name) => !clients.includes(name))) {
|
|
56
|
+
if (client === "codex") runAgent("codex", ["plugin", "remove", "marvelous-ui@marvelous-ui"], runner);
|
|
57
|
+
else runAgent("claude", ["plugin", "uninstall", "marvelous-ui@marvelous-ui", "--scope", "user"], runner);
|
|
58
|
+
}
|
|
59
|
+
saveUserAgents(place, clients);
|
|
60
|
+
return clients.map((name) => LABELS[name]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function saveUserAgents(place, clients) {
|
|
64
|
+
const file = join(place.base, "agents.json");
|
|
65
|
+
mkdirSync(place.base, { recursive: true });
|
|
66
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
67
|
+
writeFileSync(temporary, JSON.stringify({ schema: 1, clients }, null, 2) + "\n", { mode: 0o600 });
|
|
68
|
+
renameSync(temporary, file);
|
|
69
|
+
}
|
package/lib/installer.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFileSync } from "node:child_process";
|
|
1
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
2
2
|
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir, tmpdir } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -193,9 +193,15 @@ export function installPack(bytes, edition, meta, place, { installDependencies =
|
|
|
193
193
|
validPack(stage, edition, meta.version);
|
|
194
194
|
if (edition === "pro") saveProArchive(bytes, meta, place);
|
|
195
195
|
if (installDependencies) {
|
|
196
|
-
const npmArgs = ["ci", "--omit=dev", "--ignore-scripts", "--no-audit"];
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
const npmArgs = ["ci", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund", "--silent"];
|
|
197
|
+
try {
|
|
198
|
+
if (process.env.npm_execpath?.endsWith(".js")) execFileSync(process.execPath, [process.env.npm_execpath, ...npmArgs], { cwd: stage, stdio: ["ignore", "pipe", "pipe"] });
|
|
199
|
+
else if (process.platform === "win32") execSync(`npm ${npmArgs.join(" ")}`, { cwd: stage, stdio: ["ignore", "pipe", "pipe"] });
|
|
200
|
+
else execFileSync("npm", npmArgs, { cwd: stage, stdio: ["ignore", "pipe", "pipe"] });
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const detail = String(error.stderr ?? error.message ?? "").trim().split(/\r?\n/).at(-1);
|
|
203
|
+
throw new Error(`The Pack dependencies could not be installed${detail ? `: ${detail}` : "."}`);
|
|
204
|
+
}
|
|
199
205
|
}
|
|
200
206
|
beforeCommit?.(stage);
|
|
201
207
|
if (existsSync(backup)) throw new Error("A previous installation is still being recovered.");
|