layro-cli 0.1.0 → 0.3.0
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 +13 -11
- package/dist/layro.mjs +82 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# layro-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Find and publish React components on [Layro](https://layro.dev) from your terminal.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
npx layro-cli search pricing toggle
|
|
6
7
|
npx layro-cli login
|
|
7
8
|
npx layro-cli publish components/pricing-card.tsx
|
|
8
9
|
```
|
|
@@ -11,23 +12,24 @@ Requires Node 20 or newer. No dependencies.
|
|
|
11
12
|
|
|
12
13
|
## Commands
|
|
13
14
|
|
|
14
|
-
| Command | Does
|
|
15
|
-
| -------------------------- |
|
|
16
|
-
| `layro
|
|
17
|
-
| `layro
|
|
18
|
-
| `layro
|
|
19
|
-
| `layro
|
|
20
|
-
| `layro
|
|
21
|
-
| `layro
|
|
15
|
+
| Command | Does |
|
|
16
|
+
| -------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `layro search <words…>` | Find components, best match first (typos are fine, no sign-in). `--category`, `--author`, `--limit`, `--json`. |
|
|
18
|
+
| `layro login` | Sign in with GitHub in your browser (no API keys). `--no-browser` prints the link. |
|
|
19
|
+
| `layro whoami` | Who you're signed in as, and submissions left today. |
|
|
20
|
+
| `layro publish <files…>` | Send a new component, or a new version of one you already have (same name). The first file is the entry. |
|
|
21
|
+
| `layro status [component]` | Where your submissions stand, with reviewers' reasons. `component` is a slug or `author/slug`. |
|
|
22
|
+
| `layro add <author/slug>` | Prints the shadcn command that installs a component. |
|
|
23
|
+
| `layro logout` | Revokes the session and forgets it on this computer. |
|
|
22
24
|
|
|
23
25
|
### Publish options
|
|
24
26
|
|
|
25
|
-
`--name`, `--description` (20 to 200 characters), `--category` (a slug from layro.dev/components), `--license` (MIT, Apache-2.0, ISC or BSD-3-Clause; default MIT), `--version` (new versions: defaults to a patch bump), `--notes` (what changed), `--dark-only`, `--preview <plan.json>` (how to film the preview), `-y, --yes` (don't ask; for CI).
|
|
27
|
+
`--name`, `--description` (20 to 200 characters), `--category` (a slug from layro.dev/components), `--license` (MIT, Apache-2.0, ISC or BSD-3-Clause; default MIT), `--version` (new versions: defaults to a patch bump), `--notes` (what changed), `--dark-only`, `--preview <plan.json>` (how to film the preview), `--if-changed` (new versions: unchanged code or a version still in review exits 0 with "Nothing sent"; for CI), `-y, --yes` (don't ask; for CI).
|
|
26
28
|
|
|
27
29
|
Everything goes through review on Layro before it's live. The CLI and coding agents (MCP) share one daily allowance of submissions per account.
|
|
28
30
|
|
|
29
31
|
## Where tokens live
|
|
30
32
|
|
|
31
|
-
`~/.config/layro/credentials.json` (or `$XDG_CONFIG_HOME/layro`), readable only by you. Access tokens last an hour and renew themselves. `LAYRO_TOKEN` uses
|
|
33
|
+
`~/.config/layro/credentials.json` (or `$XDG_CONFIG_HOME/layro`), readable only by you. Access tokens last an hour and renew themselves. `LAYRO_TOKEN` uses a token instead: for CI, make a long-lived one at https://layro.dev/settings/tokens (publish only; a GitHub Actions example is at https://layro.dev/docs/cli); `LAYRO_URL` points at another Layro server.
|
|
32
34
|
|
|
33
35
|
Docs: https://layro.dev/docs/cli
|
package/dist/layro.mjs
CHANGED
|
@@ -340,7 +340,7 @@ async function logout(server2) {
|
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
// src/api.ts
|
|
343
|
-
var VERSION = true ? "0.
|
|
343
|
+
var VERSION = true ? "0.3.0" : "dev";
|
|
344
344
|
var ApiFailure = class extends CliError {
|
|
345
345
|
constructor(detail) {
|
|
346
346
|
super(detail.error);
|
|
@@ -376,13 +376,16 @@ async function api(server2, path, init = {}) {
|
|
|
376
376
|
if (response.ok && body) return body;
|
|
377
377
|
if (response.status === 401) {
|
|
378
378
|
if (process.env.LAYRO_TOKEN)
|
|
379
|
-
throw new CliError(
|
|
379
|
+
throw new CliError(
|
|
380
|
+
`LAYRO_TOKEN was refused: it's invalid, revoked or expired. Make a new one at ${server2}/settings/tokens.`
|
|
381
|
+
);
|
|
380
382
|
throw notSignedIn();
|
|
381
383
|
}
|
|
382
384
|
throw new ApiFailure({
|
|
383
385
|
status: response.status,
|
|
384
386
|
error: body?.error ?? `${server2} answered ${response.status} for ${path}.`,
|
|
385
|
-
errors: body?.errors
|
|
387
|
+
errors: body?.errors,
|
|
388
|
+
code: body?.code
|
|
386
389
|
});
|
|
387
390
|
}
|
|
388
391
|
|
|
@@ -461,14 +464,25 @@ async function publish(server2, paths, flags) {
|
|
|
461
464
|
const notes = flags.notes ?? (interactive ? await ask("What changed", "") : "");
|
|
462
465
|
if (interactive && !await confirm(`Send ${existing.id} ${version} for review?`))
|
|
463
466
|
return say("Nothing sent.");
|
|
464
|
-
|
|
467
|
+
const sent = await api(
|
|
465
468
|
server2,
|
|
466
469
|
`/components/${existing.id}/versions`,
|
|
467
470
|
{
|
|
468
471
|
method: "POST",
|
|
469
472
|
body: { version, notes, files, preview }
|
|
470
473
|
}
|
|
471
|
-
).catch(
|
|
474
|
+
).catch((error) => {
|
|
475
|
+
if (flags.ifChanged && error instanceof ApiFailure && (error.detail.code === "unchanged" || error.detail.code === "pending"))
|
|
476
|
+
return error;
|
|
477
|
+
return explain(error);
|
|
478
|
+
});
|
|
479
|
+
if (sent instanceof ApiFailure) {
|
|
480
|
+
say(
|
|
481
|
+
`Nothing sent: ${Object.values(sent.detail.errors ?? {})[0] ?? sent.message}`
|
|
482
|
+
);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
result = sent;
|
|
472
486
|
} else {
|
|
473
487
|
if (flags.version && flags.version !== "1.0.0")
|
|
474
488
|
warn(
|
|
@@ -520,6 +534,36 @@ async function publish(server2, paths, flags) {
|
|
|
520
534
|
say(dim(` Submissions left today: ${result.submissionsLeftToday}`));
|
|
521
535
|
}
|
|
522
536
|
|
|
537
|
+
// src/search.ts
|
|
538
|
+
async function search(server2, words, flags) {
|
|
539
|
+
const q = words.join(" ").trim();
|
|
540
|
+
if (!q) throw new CliError("Give some words: layro search <words\u2026>");
|
|
541
|
+
const params = new URLSearchParams({ q });
|
|
542
|
+
if (flags.category) params.set("category", flags.category);
|
|
543
|
+
if (flags.author) params.set("author", flags.author);
|
|
544
|
+
if (flags.limit) params.set("limit", flags.limit);
|
|
545
|
+
const found = await api(server2, `/search?${params}`, { auth: false });
|
|
546
|
+
if (flags.json) return say(JSON.stringify(found, null, 2));
|
|
547
|
+
if (found.results.length === 0) {
|
|
548
|
+
say(`Nothing matches "${q}".`);
|
|
549
|
+
if (found.suggestion)
|
|
550
|
+
say(
|
|
551
|
+
`The closest category is ${bold(found.suggestion.name)}: ${found.suggestion.url}`
|
|
552
|
+
);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
for (const c of found.results) {
|
|
556
|
+
say(`${bold(c.name)} ${dim(c.id)}`);
|
|
557
|
+
say(` ${c.description}`);
|
|
558
|
+
say(dim(` ${c.install}`));
|
|
559
|
+
}
|
|
560
|
+
say(
|
|
561
|
+
dim(
|
|
562
|
+
`${found.results.length} ${found.results.length === 1 ? "result" : "results"}. Install one with the command under it.`
|
|
563
|
+
)
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
523
567
|
// src/status.ts
|
|
524
568
|
var LABEL = {
|
|
525
569
|
live: "live",
|
|
@@ -574,9 +618,10 @@ async function status(server2, target) {
|
|
|
574
618
|
}
|
|
575
619
|
|
|
576
620
|
// src/index.ts
|
|
577
|
-
var HELP = `${bold("layro")} ${dim(VERSION)}: publish React components
|
|
621
|
+
var HELP = `${bold("layro")} ${dim(VERSION)}: find and publish React components on Layro
|
|
578
622
|
|
|
579
623
|
Usage
|
|
624
|
+
layro search <words\u2026> Find components (typos are fine; no sign-in needed)
|
|
580
625
|
layro login Sign in with GitHub in your browser
|
|
581
626
|
layro whoami Who you're signed in as
|
|
582
627
|
layro publish <files\u2026> Send a new component, or a new version of yours
|
|
@@ -593,13 +638,21 @@ Publish options
|
|
|
593
638
|
--notes <text> New versions: what changed
|
|
594
639
|
--dark-only It only works on a dark page
|
|
595
640
|
--preview <plan.json> How to film its preview (see the docs)
|
|
641
|
+
--if-changed Unchanged code, or a version still in review: say so, exit 0 (CI)
|
|
596
642
|
-y, --yes Don't ask; send what's given (for CI)
|
|
597
643
|
|
|
644
|
+
Search options
|
|
645
|
+
--category <slug> Only this category
|
|
646
|
+
--author <handle> Only this author's components
|
|
647
|
+
--limit <n> 1 to 50 (default 20)
|
|
648
|
+
--json Print the API's JSON
|
|
649
|
+
|
|
598
650
|
Login options
|
|
599
651
|
--no-browser Print the sign-in link instead of opening it
|
|
600
652
|
|
|
601
653
|
Environment
|
|
602
|
-
LAYRO_TOKEN
|
|
654
|
+
LAYRO_TOKEN A token to use instead of signing in: an access
|
|
655
|
+
token from https://layro.dev/settings/tokens (CI)
|
|
603
656
|
LAYRO_URL Another Layro server (default https://layro.dev)
|
|
604
657
|
|
|
605
658
|
Docs: https://layro.dev/docs/cli`;
|
|
@@ -661,14 +714,34 @@ async function main(argv) {
|
|
|
661
714
|
notes: { type: "string" },
|
|
662
715
|
preview: { type: "string" },
|
|
663
716
|
"dark-only": { type: "boolean" },
|
|
717
|
+
"if-changed": { type: "boolean" },
|
|
664
718
|
yes: { type: "boolean", short: "y" }
|
|
665
719
|
}
|
|
666
720
|
});
|
|
667
|
-
const {
|
|
721
|
+
const {
|
|
722
|
+
"dark-only": darkOnly,
|
|
723
|
+
"if-changed": ifChanged,
|
|
724
|
+
...flags
|
|
725
|
+
} = values;
|
|
668
726
|
await publish(server, positionals, {
|
|
669
727
|
...flags,
|
|
670
|
-
darkOnly
|
|
728
|
+
darkOnly,
|
|
729
|
+
ifChanged
|
|
730
|
+
});
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
case "search": {
|
|
734
|
+
const { values, positionals } = parseArgs({
|
|
735
|
+
args: rest,
|
|
736
|
+
allowPositionals: true,
|
|
737
|
+
options: {
|
|
738
|
+
category: { type: "string" },
|
|
739
|
+
author: { type: "string" },
|
|
740
|
+
limit: { type: "string" },
|
|
741
|
+
json: { type: "boolean" }
|
|
742
|
+
}
|
|
671
743
|
});
|
|
744
|
+
await search(server, positionals, values);
|
|
672
745
|
return;
|
|
673
746
|
}
|
|
674
747
|
case "status": {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "layro-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Find and publish React components on Layro (layro.dev) from your terminal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|