@intentic/registry 1.222.0 → 1.224.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/package.json +1 -1
- package/src/registry.ts +20 -20
- package/src/source.ts +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentic/registry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.224.0",
|
|
4
4
|
"description": "The extension-registry file format — a git repo of sha-pinned pointers, shared by the daemon, the site gallery and the scanner",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/registry.ts
CHANGED
|
@@ -4,15 +4,15 @@ import { type RegistryInstall, RegistryInstallSchema, resolveSource } from "./so
|
|
|
4
4
|
/* THE EXTENSION REGISTRY: a git repo of pointers, and the two files in it.
|
|
5
5
|
*
|
|
6
6
|
* intentic hosts no extension code, builds none, and signs none. A registry is a repository whose
|
|
7
|
-
* `.claude-plugin/marketplace.json` lists pointers to other people's repositories at a commit
|
|
7
|
+
* `.claude-plugin/marketplace.json` lists pointers to other people's repositories at a commit, so listing
|
|
8
8
|
* costs a pull request, delisting deletes nothing, and anybody can run their own registry against a private
|
|
9
9
|
* one. That file is Claude Code's plugin-marketplace format deliberately: `kind` and `trust` are intentic's
|
|
10
10
|
* own fields and Claude Code ignores what it doesn't know, so one repo serves both consumers.
|
|
11
11
|
*
|
|
12
|
-
* The split into TWO files is the
|
|
12
|
+
* The split into TWO files is the part that matters. `marketplace.json` is hand-edited and is the only place a
|
|
13
13
|
* curated decision and source-bound admission are recorded; `registry.generated.json` is bot-written and holds
|
|
14
14
|
* nothing but facts read back off the source host. Keeping star counts out of the curated file is what stops every nightly refresh from
|
|
15
|
-
* being a merge conflict against an open pull request
|
|
15
|
+
* being a merge conflict against an open pull request, and it keeps the review diff to the thing being
|
|
16
16
|
* decided. A registry that carries no generated file is a registry with no stars, which renders fine. */
|
|
17
17
|
|
|
18
18
|
// Repo-relative, and the same two strings for the daemon (which clones), the scanner (which commits) and the
|
|
@@ -24,7 +24,7 @@ export const REGISTRY_FACTS_FILE = ".claude-plugin/registry.generated.json";
|
|
|
24
24
|
// field stays editable, so a company points it at an internal registry and never touches this one.
|
|
25
25
|
export const OFFICIAL_REGISTRY_URL = "https://github.com/intentic/registry";
|
|
26
26
|
|
|
27
|
-
// The GitHub topic an author adds to be found. This is discovery only
|
|
27
|
+
// The GitHub topic an author adds to be found. This is discovery only, appearing in the scan gets you a pull
|
|
28
28
|
// request against the registry, never a listing.
|
|
29
29
|
export const REGISTRY_TOPIC = "intentic-extension";
|
|
30
30
|
|
|
@@ -65,7 +65,7 @@ export type RegistrySecurityReview = z.infer<typeof RegistrySecurityReviewSchema
|
|
|
65
65
|
* - `verified` both automated checks passed and a human also read the source at that sha. Sorted first and badged.
|
|
66
66
|
* - `blocked` known-malicious or known-broken. It STAYS in the file with a reason rather than being deleted:
|
|
67
67
|
* removing the row hides it from people browsing and tells the people who already installed it
|
|
68
|
-
* nothing, which is backwards
|
|
68
|
+
* nothing, which is backwards, they are the ones at risk.
|
|
69
69
|
*
|
|
70
70
|
* Absent on a third-party registry ⇒ `listed`, because a registry that doesn't use the field hasn't asserted
|
|
71
71
|
* anything and shouldn't be read as if it had. */
|
|
@@ -74,10 +74,10 @@ export type RegistryTrust = z.infer<typeof RegistryTrustSchema>;
|
|
|
74
74
|
|
|
75
75
|
/* WHAT A LISTING COSTS. `free` is the default and the whole story for most rows. `premium` opts the listing
|
|
76
76
|
* into the creator pool: installing it requires an intentic membership and donates a published number of the
|
|
77
|
-
* member's credits to the publisher (once, deduped monthly
|
|
77
|
+
* member's credits to the publisher (once, deduped monthly, an update in a later month donates again), and
|
|
78
78
|
* both surfaces badge it so the price is visible before the click. No usage is ever metered or reported for
|
|
79
79
|
* this; the deliberate act of installing is the whole signal. On a third-party registry the field still
|
|
80
|
-
* parses but means nothing
|
|
80
|
+
* parses but means nothing, the pool only pays listings the platform's members actually install. */
|
|
81
81
|
export const RegistryTierSchema = z.enum(["free", "premium"]);
|
|
82
82
|
export type RegistryTier = z.infer<typeof RegistryTierSchema>;
|
|
83
83
|
|
|
@@ -89,27 +89,27 @@ const RegistryFileEntrySchema = z
|
|
|
89
89
|
// "extension" installs as the sha-pinned `extension` capability; absent/"plugin" is a Claude Code plugin.
|
|
90
90
|
kind: z.enum(["plugin", "extension"]).optional(),
|
|
91
91
|
trust: RegistryTrustSchema.optional(),
|
|
92
|
-
// Why it is blocked, or what was checked to verify it
|
|
92
|
+
// Why it is blocked, or what was checked to verify it, shown verbatim wherever the badge is, because a
|
|
93
93
|
// trust state with no stated reason is an opinion the reader can't weigh.
|
|
94
94
|
trustReason: z.string().optional(),
|
|
95
95
|
// Both automated checks' evidence, bound to the complete source identity below. It is deliberately
|
|
96
96
|
// distinct from `trust: verified`: passing admission admits code; verified additionally says a human
|
|
97
97
|
// read it. Keeping those claims apart stops automated verdicts being presented as human review.
|
|
98
98
|
securityReview: RegistrySecurityReviewSchema.optional(),
|
|
99
|
-
// This listing's pinned commit fixes a security problem in earlier commits
|
|
99
|
+
// This listing's pinned commit fixes a security problem in earlier commits, the fast lane: an installed
|
|
100
100
|
// sandbox promotes its "update available" badge from ambient to attention-demanding, because there the OLD
|
|
101
101
|
// version is the dangerous one. Asserted by the pull request like trust, and worth exactly that review.
|
|
102
102
|
securityFix: z.boolean().optional(),
|
|
103
103
|
tier: RegistryTierSchema.optional(),
|
|
104
104
|
category: z.string().optional(),
|
|
105
105
|
/* The mark the row is drawn with, copied off the extension's manifest exactly like the description and
|
|
106
|
-
* the version
|
|
106
|
+
* the version, the same three tiers the manifest declares (the author's own inline drawing, then a
|
|
107
107
|
* simple-icons slug, then a name from the app's icon set), and none is required.
|
|
108
108
|
*
|
|
109
109
|
* It rides the CURATED file rather than the generated one, which looks wrong for a derived value until you
|
|
110
110
|
* ask what a registry is for: a listing is what a human decided to publish, and the mark is part of how it
|
|
111
111
|
* presents itself, so it belongs in the row a reviewer reads and can strike out. The generated file holds
|
|
112
|
-
* only what a bot re-reads nightly and nobody reviews. It also has to be here to be of any use at all
|
|
112
|
+
* only what a bot re-reads nightly and nobody reviews. It also has to be here to be of any use at all,
|
|
113
113
|
* this is what the gallery and the in-app browse list render, and neither of them has the manifest: the
|
|
114
114
|
* whole point of the row is that the code has NOT been cloned yet.
|
|
115
115
|
*
|
|
@@ -166,15 +166,15 @@ export type RegistryFile = z.infer<typeof RegistryFileSchema>;
|
|
|
166
166
|
/* Facts read off the source host, keyed by the curated entry's name. Deliberately NOT the upstream head sha:
|
|
167
167
|
* the approved sha is the one that runs, updating is a pull request, and a file that advertised "there's a
|
|
168
168
|
* newer commit over there" would be inviting a click that skips the review the whole model rests on. */
|
|
169
|
-
/* What the scan re-derived COLD at the listing's pinned sha
|
|
169
|
+
/* What the scan re-derived COLD at the listing's pinned sha, the same questions the daemon's readiness check
|
|
170
170
|
* answers for an author before publishing, asked again by a stranger with nothing but the pointer. That
|
|
171
171
|
* re-derivation is the whole value: an author's own checks describe the directory they ran them in, and these
|
|
172
172
|
* describe what an installer actually gets. `sha` binds the answers to the commit they were read from, so a
|
|
173
|
-
* listing repointed since the last scan renders no stale verdicts
|
|
173
|
+
* listing repointed since the last scan renders no stale verdicts, the join drops checks whose sha no longer
|
|
174
174
|
* matches rather than letting yesterday's answer describe today's pointer. */
|
|
175
175
|
const RegistryChecksSchema = z.object({
|
|
176
176
|
sha: z.string(),
|
|
177
|
-
// "ok", or the reason it is not, verbatim
|
|
177
|
+
// "ok", or the reason it is not, verbatim, a verdict with no stated reason is an opinion.
|
|
178
178
|
manifest: z.string(),
|
|
179
179
|
// "ok" / "none" (no UI bundle) / the reason the bundle cannot load where it is installed.
|
|
180
180
|
bundle: z.string(),
|
|
@@ -187,7 +187,7 @@ export type RegistryChecks = z.infer<typeof RegistryChecksSchema>;
|
|
|
187
187
|
const RegistryFactsEntrySchema = z.object({
|
|
188
188
|
name: z.string(),
|
|
189
189
|
stars: z.number().int().nonnegative().optional(),
|
|
190
|
-
// ISO-8601, last push to the source repo's default branch
|
|
190
|
+
// ISO-8601, last push to the source repo's default branch, the tiebreaker that does the real work while
|
|
191
191
|
// every listing still has single-digit stars.
|
|
192
192
|
pushedAt: z.string().optional(),
|
|
193
193
|
checks: RegistryChecksSchema.optional(),
|
|
@@ -200,7 +200,7 @@ export const RegistryFactsSchema = z.object({
|
|
|
200
200
|
export type RegistryFacts = z.infer<typeof RegistryFactsSchema>;
|
|
201
201
|
|
|
202
202
|
// One row as every surface consumes it: the curated decision, the resolved pointer, and the upstream facts,
|
|
203
|
-
// already joined. This is also the daemon's browse wire shape
|
|
203
|
+
// already joined. This is also the daemon's browse wire shape, the app renders what the site renders.
|
|
204
204
|
export const RegistryEntrySchema = z.object({
|
|
205
205
|
name: z.string(),
|
|
206
206
|
description: z.string().optional(),
|
|
@@ -217,7 +217,7 @@ export const RegistryEntrySchema = z.object({
|
|
|
217
217
|
securityFix: z.boolean().optional(),
|
|
218
218
|
tier: RegistryTierSchema,
|
|
219
219
|
category: z.string().optional(),
|
|
220
|
-
// The mark, as the gallery and the app's browse list draw it
|
|
220
|
+
// The mark, as the gallery and the app's browse list draw it, see the curated file's fields above.
|
|
221
221
|
art: z.string().optional(),
|
|
222
222
|
logo: z.string().optional(),
|
|
223
223
|
icon: z.string().optional(),
|
|
@@ -265,7 +265,7 @@ export const isCurrentSecurityReview = (review: RegistrySecurityReview | undefin
|
|
|
265
265
|
review.deterministic.version === OFFICIAL_DETERMINISTIC_SCANNER_VERSION;
|
|
266
266
|
|
|
267
267
|
/* Join the curated file to the generated facts. `facts` is undefined for any registry that runs no scanner,
|
|
268
|
-
* which is most of them
|
|
268
|
+
* which is most of them, a private registry of six internal extensions wants the pointers and nothing else. */
|
|
269
269
|
export const resolveRegistry = (file: RegistryFile, facts: RegistryFacts | undefined, registryUrl: string): RegistryEntry[] => {
|
|
270
270
|
const byName = new Map(facts?.entries.map((entry) => [entry.name, entry]) ?? []);
|
|
271
271
|
const official = isOfficialRegistryUrl(registryUrl);
|
|
@@ -295,7 +295,7 @@ export const resolveRegistry = (file: RegistryFile, facts: RegistryFacts | undef
|
|
|
295
295
|
...(install !== undefined ? { install } : {}),
|
|
296
296
|
...(upstream?.stars !== undefined ? { stars: upstream.stars } : {}),
|
|
297
297
|
...(upstream?.pushedAt !== undefined ? { pushedAt: upstream.pushedAt } : {}),
|
|
298
|
-
// Only when derived from the sha this row still points at
|
|
298
|
+
// Only when derived from the sha this row still points at, a repointed listing renders no checks
|
|
299
299
|
// until the next scan, which is the honest gap rather than yesterday's verdict on today's pointer.
|
|
300
300
|
...(upstream?.checks !== undefined && upstream.checks.sha === install?.ref ? { checks: upstream.checks } : {}),
|
|
301
301
|
};
|
|
@@ -305,7 +305,7 @@ export const resolveRegistry = (file: RegistryFile, facts: RegistryFacts | undef
|
|
|
305
305
|
/* THE ORDER, and why it isn't just stars.
|
|
306
306
|
*
|
|
307
307
|
* Stars are the obvious sort and the wrong one on day one: every listing will sit at nought to three of them
|
|
308
|
-
* for months, so a pure star sort is a random order wearing a merit badge
|
|
308
|
+
* for months, so a pure star sort is a random order wearing a merit badge, and it is the single most
|
|
309
309
|
* purchasable number on GitHub. So: the one field a human actually asserted leads, stars rank within that,
|
|
310
310
|
* and recency breaks the ties that will be the overwhelmingly common case early on. Stars stay VISIBLE
|
|
311
311
|
* either way; a reader can weigh them, they just don't get to be the whole ranking. */
|
package/src/source.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { z } from "zod";
|
|
|
2
2
|
|
|
3
3
|
/* WHERE A REGISTRY ENTRY'S CODE LIVES, and how that resolves to something cloneable.
|
|
4
4
|
*
|
|
5
|
-
* A registry never hosts code
|
|
5
|
+
* A registry never hosts code, an entry is a pointer to somebody else's repository at a commit. `source` is
|
|
6
6
|
* that pointer in the shapes Claude Code's plugin-marketplace format already defines, so one registry repo
|
|
7
7
|
* serves both consumers; `resolveSource` maps the shapes we can clone onto the url/ref/path a capability
|
|
8
|
-
* install takes, and returns undefined for the ones we can't (npm, say) rather than dropping the entry
|
|
8
|
+
* install takes, and returns undefined for the ones we can't (npm, say) rather than dropping the entry,
|
|
9
9
|
* an entry that exists but can't be installed in a click is information, a missing row is a bug report. */
|
|
10
10
|
|
|
11
11
|
// The resolved pointer: exactly the fields a plugin- or extension-capability install needs.
|
|
@@ -46,14 +46,14 @@ export const resolveSource = (source: unknown, registryUrl: string, pluginRoot:
|
|
|
46
46
|
|
|
47
47
|
const FULL_SHA = /^[0-9a-f]{40}$/;
|
|
48
48
|
|
|
49
|
-
/* Whether this pointer names one immutable commit. An EXTENSION install requires it
|
|
49
|
+
/* Whether this pointer names one immutable commit. An EXTENSION install requires it, extension code runs
|
|
50
50
|
* trusted in the owner's browser, so the approved code and the running code have to be the same object, and a
|
|
51
51
|
* branch name is a promise the upstream can break with a force-push. A registry entry that gives only a branch
|
|
52
52
|
* is still listed and still readable; it just can't be a one-click install, which is the pressure that makes
|
|
53
53
|
* authors pin. Plugins are laxer by design: they load into the agent, not the browser. */
|
|
54
54
|
export const isShaPinned = (install: RegistryInstall | undefined): boolean => install?.ref !== undefined && FULL_SHA.test(install.ref);
|
|
55
55
|
|
|
56
|
-
// `owner/repo` for a GitHub pointer
|
|
56
|
+
// `owner/repo` for a GitHub pointer, what the scanner keys upstream facts by, and what the gallery links to.
|
|
57
57
|
// Undefined for any host that isn't GitHub, which is a listing we simply carry no stars for.
|
|
58
58
|
export const githubRepoOf = (install: RegistryInstall | undefined): string | undefined => {
|
|
59
59
|
if (install === undefined) {
|