@adea-ai/plugins 1.0.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/LICENSE +201 -0
- package/README.md +74 -0
- package/dist/agent-plugins-catalog.d.ts +26 -0
- package/dist/agent-plugins-catalog.js +699 -0
- package/dist/agent-plugins-catalog.js.map +1 -0
- package/dist/agent-plugins-harness.d.ts +54 -0
- package/dist/agent-plugins-harness.js +212 -0
- package/dist/agent-plugins-harness.js.map +1 -0
- package/dist/agent-plugins-schema.d.ts +402 -0
- package/dist/agent-plugins-schema.js +200 -0
- package/dist/agent-plugins-schema.js.map +1 -0
- package/dist/consumer-index.d.ts +217 -0
- package/dist/consumer-index.js +358 -0
- package/dist/consumer-index.js.map +1 -0
- package/dist/display-name.d.ts +8 -0
- package/dist/display-name.js +160 -0
- package/dist/display-name.js.map +1 -0
- package/dist/harness.d.ts +13 -0
- package/dist/harness.js +169 -0
- package/dist/harness.js.map +1 -0
- package/dist/icon-mirror.d.ts +69 -0
- package/dist/icon-mirror.js +147 -0
- package/dist/icon-mirror.js.map +1 -0
- package/dist/icons.d.ts +65 -0
- package/dist/icons.js +268 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +304 -0
- package/dist/index.js +1877 -0
- package/dist/index.js.map +1 -0
- package/dist/portable-catalog.d.ts +9 -0
- package/dist/portable-catalog.js +175 -0
- package/dist/portable-catalog.js.map +1 -0
- package/dist/publication.d.ts +98 -0
- package/dist/publication.js +121 -0
- package/dist/publication.js.map +1 -0
- package/dist/schema.d.ts +705 -0
- package/dist/schema.js +267 -0
- package/dist/schema.js.map +1 -0
- package/dist/site-icons.d.ts +55 -0
- package/dist/site-icons.js +155 -0
- package/dist/site-icons.js.map +1 -0
- package/dist/sources.d.ts +54 -0
- package/dist/sources.js +306 -0
- package/dist/sources.js.map +1 -0
- package/package.json +102 -0
- package/schemas/agent-package.v1.json +387 -0
- package/schemas/catalog.v1.schema.json +407 -0
- package/schemas/compatibility.v1.schema.json +23 -0
- package/schemas/harness-profile.v1.json +75 -0
- package/schemas/installation-plan.v2.json +695 -0
- package/schemas/sources-lock.v1.schema.json +61 -0
- package/src/agent-plugins-catalog.test.ts +402 -0
- package/src/agent-plugins-catalog.ts +800 -0
- package/src/agent-plugins-harness.test.ts +272 -0
- package/src/agent-plugins-harness.ts +281 -0
- package/src/agent-plugins-schema.test.ts +27 -0
- package/src/agent-plugins-schema.ts +219 -0
- package/src/artifacts.test.ts +545 -0
- package/src/catalog-installation.test.ts +42 -0
- package/src/consumer-index.test.ts +465 -0
- package/src/consumer-index.ts +592 -0
- package/src/display-name.test.ts +56 -0
- package/src/display-name.ts +157 -0
- package/src/harness.ts +202 -0
- package/src/icon-mirror.ts +196 -0
- package/src/icons.test.ts +185 -0
- package/src/icons.ts +297 -0
- package/src/index.test.ts +888 -0
- package/src/index.ts +2538 -0
- package/src/portable-catalog.test.ts +186 -0
- package/src/portable-catalog.ts +203 -0
- package/src/publication.test.ts +117 -0
- package/src/publication.ts +171 -0
- package/src/schema.ts +309 -0
- package/src/site-icon-fallback.test.ts +267 -0
- package/src/site-icons.test.ts +171 -0
- package/src/site-icons.ts +161 -0
- package/src/sources.ts +360 -0
- package/src/upstream-headers.test.ts +148 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const DIGEST_RE = /^sha256:[a-f0-9]{64}$/;
|
|
3
|
+
export const SHA_RE = /^[a-f0-9]{40}$/;
|
|
4
|
+
export const DigestSchema = z.string().regex(DIGEST_RE);
|
|
5
|
+
export const ShaSchema = z.string().regex(SHA_RE);
|
|
6
|
+
export const TimestampSchema = z.string().datetime({ offset: true });
|
|
7
|
+
export const MarketplaceDialectSchema = z.enum(['openai', 'cursor', 'claude']);
|
|
8
|
+
export const TrustClassificationSchema = z.enum(['official']);
|
|
9
|
+
export const SyncStatusSchema = z.enum(['synchronized', 'unchanged', 'failed']);
|
|
10
|
+
export const CapabilityTypeSchema = z.enum([
|
|
11
|
+
'skill',
|
|
12
|
+
'mcp-server',
|
|
13
|
+
'connector',
|
|
14
|
+
'command',
|
|
15
|
+
'agent',
|
|
16
|
+
'hook',
|
|
17
|
+
'rule',
|
|
18
|
+
'browser',
|
|
19
|
+
'scheduled-task',
|
|
20
|
+
'ui-component',
|
|
21
|
+
'executable',
|
|
22
|
+
'unknown',
|
|
23
|
+
]);
|
|
24
|
+
export const HarnessSchema = z.enum([
|
|
25
|
+
'codex',
|
|
26
|
+
'claude-code',
|
|
27
|
+
'cursor',
|
|
28
|
+
'pi',
|
|
29
|
+
'hermes',
|
|
30
|
+
'opencode',
|
|
31
|
+
'generic-skill-mcp',
|
|
32
|
+
]);
|
|
33
|
+
export const CompatibilityStatusSchema = z.enum([
|
|
34
|
+
'native',
|
|
35
|
+
'portable',
|
|
36
|
+
'requires-translation',
|
|
37
|
+
'partially-supported',
|
|
38
|
+
'unsupported',
|
|
39
|
+
'blocked-by-policy',
|
|
40
|
+
'unknown',
|
|
41
|
+
]);
|
|
42
|
+
const JsonValueSchema = z.lazy(() => z.union([
|
|
43
|
+
z.string(),
|
|
44
|
+
z.number().finite(),
|
|
45
|
+
z.boolean(),
|
|
46
|
+
z.null(),
|
|
47
|
+
z.array(JsonValueSchema),
|
|
48
|
+
z.record(z.string(), JsonValueSchema),
|
|
49
|
+
]));
|
|
50
|
+
export const JsonObjectSchema = z.record(z.string(), JsonValueSchema);
|
|
51
|
+
export const CatalogSourceSchema = z
|
|
52
|
+
.object({
|
|
53
|
+
sourceId: z.string().regex(/^[a-z0-9][a-z0-9-]{1,63}$/),
|
|
54
|
+
displayName: z.string().min(1).max(160),
|
|
55
|
+
repositoryUrl: z.string().url(),
|
|
56
|
+
marketplaceDialect: MarketplaceDialectSchema,
|
|
57
|
+
manifestPath: z.string().min(1).max(512),
|
|
58
|
+
defaultBranch: z.string().regex(/^[A-Za-z0-9._/-]+$/),
|
|
59
|
+
resolvedCommitSha: ShaSchema,
|
|
60
|
+
retrievalTimestamp: TimestampSchema,
|
|
61
|
+
trustClassification: TrustClassificationSchema,
|
|
62
|
+
sourceManifestDigest: DigestSchema,
|
|
63
|
+
synchronizationStatus: SyncStatusSchema,
|
|
64
|
+
})
|
|
65
|
+
.strict();
|
|
66
|
+
export const CapabilitySchema = z
|
|
67
|
+
.object({
|
|
68
|
+
type: CapabilityTypeSchema,
|
|
69
|
+
name: z.string().min(1).max(200),
|
|
70
|
+
paths: z.array(z.string().min(1).max(512)).max(512),
|
|
71
|
+
metadata: JsonObjectSchema,
|
|
72
|
+
securityImpact: z.enum(['none', 'review', 'sensitive']),
|
|
73
|
+
})
|
|
74
|
+
.strict();
|
|
75
|
+
export const HarnessCompatibilitySchema = z
|
|
76
|
+
.object({
|
|
77
|
+
status: CompatibilityStatusSchema,
|
|
78
|
+
reasons: z.array(z.string().min(1).max(500)).max(64),
|
|
79
|
+
responsibleCapabilities: z.array(CapabilityTypeSchema).max(32),
|
|
80
|
+
})
|
|
81
|
+
.strict();
|
|
82
|
+
export const LicenseMetadataSchema = z
|
|
83
|
+
.object({
|
|
84
|
+
name: z.string().min(1).max(200),
|
|
85
|
+
spdxId: z.string().min(1).max(100).optional(),
|
|
86
|
+
url: z.string().url().optional(),
|
|
87
|
+
source: z.enum(['plugin-manifest', 'package-manifest', 'marketplace-entry', 'unknown']),
|
|
88
|
+
})
|
|
89
|
+
.strict();
|
|
90
|
+
export const SecurityClassificationSchema = z
|
|
91
|
+
.object({
|
|
92
|
+
level: z.enum(['low', 'review', 'sensitive']),
|
|
93
|
+
reasons: z.array(z.string().min(1).max(500)).max(64),
|
|
94
|
+
permissionSensitiveChanges: z.array(z.string().min(1).max(200)).max(64),
|
|
95
|
+
contentResolution: z.enum(['complete', 'metadata-only']),
|
|
96
|
+
})
|
|
97
|
+
.strict();
|
|
98
|
+
export const PluginProvenanceSchema = z
|
|
99
|
+
.object({
|
|
100
|
+
sourceId: z.string().min(1),
|
|
101
|
+
repositoryUrl: z.string().url(),
|
|
102
|
+
manifestPath: z.string().min(1),
|
|
103
|
+
pluginSubdirectory: z.string().min(1),
|
|
104
|
+
resolvedCommitSha: ShaSchema,
|
|
105
|
+
sourceManifestDigest: DigestSchema,
|
|
106
|
+
upstreamEntryDigest: DigestSchema,
|
|
107
|
+
})
|
|
108
|
+
.strict();
|
|
109
|
+
export const IconContentTypeSchema = z.enum([
|
|
110
|
+
'image/png',
|
|
111
|
+
'image/jpeg',
|
|
112
|
+
'image/gif',
|
|
113
|
+
'image/webp',
|
|
114
|
+
'image/svg+xml',
|
|
115
|
+
'image/x-icon',
|
|
116
|
+
]);
|
|
117
|
+
export const PluginReleaseSchema = z
|
|
118
|
+
.object({
|
|
119
|
+
releaseId: z.string().regex(/^release:[a-f0-9]{64}$/),
|
|
120
|
+
upstreamVersion: z.string().min(1).max(128).optional(),
|
|
121
|
+
resolvedRepositoryUrl: z.string().url(),
|
|
122
|
+
resolvedCommitSha: ShaSchema,
|
|
123
|
+
pluginSubdirectory: z.string().min(1).max(512),
|
|
124
|
+
canonicalContentDigest: DigestSchema,
|
|
125
|
+
manifestDigest: DigestSchema,
|
|
126
|
+
contentResolution: z.enum(['complete', 'metadata-only']),
|
|
127
|
+
releaseMetadata: JsonObjectSchema,
|
|
128
|
+
capabilities: z.array(CapabilitySchema).max(1024),
|
|
129
|
+
requiredConnectors: z.array(z.string().min(1).max(200)).max(128),
|
|
130
|
+
requiredCredentials: z.array(z.string().min(1).max(200)).max(128),
|
|
131
|
+
permissionSensitiveChanges: z.array(z.string().min(1).max(200)).max(128),
|
|
132
|
+
fileIndex: z.array(z.string().min(1).max(512)).max(4096),
|
|
133
|
+
publicationTimestamp: TimestampSchema,
|
|
134
|
+
/**
|
|
135
|
+
* Brand mark resolved at compile time. `content` comes from the release's
|
|
136
|
+
* own files; `favicon` comes from the product's declared homepage, for
|
|
137
|
+
* products that ship no mark of their own.
|
|
138
|
+
*/
|
|
139
|
+
icon: z
|
|
140
|
+
.discriminatedUnion('kind', [
|
|
141
|
+
z
|
|
142
|
+
.object({
|
|
143
|
+
kind: z.literal('content'),
|
|
144
|
+
path: z.string().min(1).max(512),
|
|
145
|
+
contentType: IconContentTypeSchema,
|
|
146
|
+
digest: DigestSchema,
|
|
147
|
+
bytes: z.number().int().positive().max(1048576),
|
|
148
|
+
})
|
|
149
|
+
.strict(),
|
|
150
|
+
z
|
|
151
|
+
.object({
|
|
152
|
+
kind: z.literal('favicon'),
|
|
153
|
+
url: z.string().url().max(1024),
|
|
154
|
+
homepage: z.string().url().max(1024),
|
|
155
|
+
contentType: IconContentTypeSchema,
|
|
156
|
+
digest: DigestSchema,
|
|
157
|
+
bytes: z.number().int().positive().max(1048576),
|
|
158
|
+
})
|
|
159
|
+
.strict(),
|
|
160
|
+
])
|
|
161
|
+
.optional(),
|
|
162
|
+
})
|
|
163
|
+
.strict();
|
|
164
|
+
export const PluginSchema = z
|
|
165
|
+
.object({
|
|
166
|
+
pluginId: z.string().regex(/^plugin:[a-z0-9-]+:[a-z0-9][a-z0-9-]{1,127}$/),
|
|
167
|
+
displayName: z.string().min(1).max(200),
|
|
168
|
+
description: z.string().max(4096),
|
|
169
|
+
productGroupingKey: z.string().regex(/^[a-z0-9][a-z0-9-]{1,127}$/),
|
|
170
|
+
categories: z.array(z.string().regex(/^[a-z0-9][a-z0-9-]{1,63}$/)).max(32),
|
|
171
|
+
keywords: z.array(z.string().min(1).max(100)).max(128),
|
|
172
|
+
authors: z.array(z.string().min(1).max(200)).max(32),
|
|
173
|
+
homepage: z.string().url().optional(),
|
|
174
|
+
icons: z.array(z.string().min(1).max(1024)).max(16),
|
|
175
|
+
sourceId: z.string().min(1),
|
|
176
|
+
upstreamPluginName: z.string().min(1).max(200),
|
|
177
|
+
currentReleaseId: z.string().regex(/^release:[a-f0-9]{64}$/),
|
|
178
|
+
availableReleases: z.array(PluginReleaseSchema).min(1).max(128),
|
|
179
|
+
capabilitySummary: z.partialRecord(CapabilityTypeSchema, z.number().int().nonnegative()),
|
|
180
|
+
harnessCompatibility: z.record(HarnessSchema, HarnessCompatibilitySchema),
|
|
181
|
+
license: LicenseMetadataSchema,
|
|
182
|
+
provenance: PluginProvenanceSchema,
|
|
183
|
+
securityClassification: SecurityClassificationSchema,
|
|
184
|
+
})
|
|
185
|
+
.strict();
|
|
186
|
+
export const CatalogSchema = z
|
|
187
|
+
.object({
|
|
188
|
+
schemaVersion: z.literal(1),
|
|
189
|
+
catalogId: z.string().regex(/^catalog:[a-f0-9]{64}$/),
|
|
190
|
+
generatedAt: TimestampSchema,
|
|
191
|
+
sources: z.array(CatalogSourceSchema).min(1).max(64),
|
|
192
|
+
plugins: z.array(PluginSchema).max(4096),
|
|
193
|
+
})
|
|
194
|
+
.strict();
|
|
195
|
+
export const SourceLockEntrySchema = z
|
|
196
|
+
.object({
|
|
197
|
+
sourceId: z.string().min(1),
|
|
198
|
+
repositoryUrl: z.string().url(),
|
|
199
|
+
defaultBranch: z.string().min(1),
|
|
200
|
+
resolvedCommitSha: ShaSchema,
|
|
201
|
+
manifestPath: z.string().min(1),
|
|
202
|
+
marketplaceDialect: MarketplaceDialectSchema,
|
|
203
|
+
sourceManifestDigest: DigestSchema,
|
|
204
|
+
retrievedAt: TimestampSchema,
|
|
205
|
+
synchronizationStatus: SyncStatusSchema,
|
|
206
|
+
pluginPins: z
|
|
207
|
+
.array(z
|
|
208
|
+
.object({
|
|
209
|
+
pluginName: z.string().min(1).max(200),
|
|
210
|
+
repositoryUrl: z.string().url(),
|
|
211
|
+
pluginSubdirectory: z.string().max(512),
|
|
212
|
+
resolvedCommitSha: ShaSchema,
|
|
213
|
+
})
|
|
214
|
+
.strict())
|
|
215
|
+
.max(4096),
|
|
216
|
+
})
|
|
217
|
+
.strict();
|
|
218
|
+
export const SourcesLockSchema = z
|
|
219
|
+
.object({
|
|
220
|
+
schemaVersion: z.literal(1),
|
|
221
|
+
lockId: z.string().regex(/^lock:[a-f0-9]{64}$/),
|
|
222
|
+
sources: z.array(SourceLockEntrySchema).min(1).max(64),
|
|
223
|
+
})
|
|
224
|
+
.strict();
|
|
225
|
+
export const MaterializationPlanSchema = z
|
|
226
|
+
.object({
|
|
227
|
+
planVersion: z.literal(1),
|
|
228
|
+
pluginId: PluginSchema.shape.pluginId,
|
|
229
|
+
releaseId: PluginReleaseSchema.shape.releaseId,
|
|
230
|
+
harness: HarnessSchema,
|
|
231
|
+
source: z
|
|
232
|
+
.object({
|
|
233
|
+
repositoryUrl: z.string().url(),
|
|
234
|
+
commitSha: ShaSchema,
|
|
235
|
+
pluginSubdirectory: z.string().min(1),
|
|
236
|
+
contentDigest: DigestSchema,
|
|
237
|
+
})
|
|
238
|
+
.strict(),
|
|
239
|
+
targetLayout: z
|
|
240
|
+
.object({
|
|
241
|
+
root: z.string().min(1),
|
|
242
|
+
notes: z.array(z.string().min(1)).max(32),
|
|
243
|
+
})
|
|
244
|
+
.strict(),
|
|
245
|
+
files: z
|
|
246
|
+
.array(z
|
|
247
|
+
.object({
|
|
248
|
+
sourcePath: z.string().min(1),
|
|
249
|
+
targetPath: z.string().min(1),
|
|
250
|
+
action: z.enum(['copy', 'translate', 'ignore', 'unsupported']),
|
|
251
|
+
reason: z.string().min(1).max(500),
|
|
252
|
+
})
|
|
253
|
+
.strict())
|
|
254
|
+
.max(4096),
|
|
255
|
+
configuration: JsonObjectSchema,
|
|
256
|
+
requiredConnectors: z.array(z.string().min(1)).max(128),
|
|
257
|
+
requiredCredentials: z.array(z.string().min(1)).max(128),
|
|
258
|
+
policyConstraints: z.array(z.string().min(1)).max(128),
|
|
259
|
+
})
|
|
260
|
+
.strict();
|
|
261
|
+
export function parseCatalog(input) {
|
|
262
|
+
return CatalogSchema.parse(input);
|
|
263
|
+
}
|
|
264
|
+
export function parseSourcesLock(input) {
|
|
265
|
+
return SourcesLockSchema.parse(input);
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type IconContentType } from './icons.js';
|
|
2
|
+
/**
|
|
3
|
+
* Site icon discovery, for products that ship no brand mark of their own.
|
|
4
|
+
*
|
|
5
|
+
* Many plugins are a thin wrapper over a vendor's API: the content carries no
|
|
6
|
+
* logo, and the declared homepage is the only pointer to the vendor. Fetching
|
|
7
|
+
* that site's icon once at publish time, verifying it and mirroring it as a
|
|
8
|
+
* release asset replaces a per-render favicon lookup in the client.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SITE_ICON_MAX_HTML_BYTES: number;
|
|
11
|
+
/**
|
|
12
|
+
* Only the document head is parsed. Icon links are declared there, and vendor
|
|
13
|
+
* landing pages are frequently several megabytes of markup and script.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SITE_ICON_HEAD_BYTES: number;
|
|
16
|
+
export interface SiteIcon {
|
|
17
|
+
readonly url: string;
|
|
18
|
+
/** The product homepage the icon was discovered from. */
|
|
19
|
+
readonly homepage: string;
|
|
20
|
+
readonly bytes: Uint8Array;
|
|
21
|
+
readonly contentType: IconContentType;
|
|
22
|
+
readonly digest: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Whether a homepage can stand in for a product brand.
|
|
26
|
+
*
|
|
27
|
+
* Only HTTPS sites qualify: a repository URL would yield the forge's own logo,
|
|
28
|
+
* and a non-routable host is a request forgery risk rather than a vendor site.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isVendorHomepage(homepage: string | undefined): boolean;
|
|
31
|
+
/** Rejects loopback, link-local, private and internal hostnames. */
|
|
32
|
+
export declare function isPublicHostname(hostname: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Extracts icon URLs from a document head, best candidate first.
|
|
35
|
+
*
|
|
36
|
+
* `apple-touch-icon` is preferred because it is a square PNG sized for a home
|
|
37
|
+
* screen; declared `icon` links follow in declared order; `/favicon.ico` is the
|
|
38
|
+
* conventional last resort when nothing is declared.
|
|
39
|
+
*/
|
|
40
|
+
export declare function discoverSiteIcons(html: string, baseUrl: string): readonly string[];
|
|
41
|
+
export interface SiteIconFetcher {
|
|
42
|
+
readonly fetchText: (url: string) => Promise<string>;
|
|
43
|
+
readonly fetchBytes: (url: string) => Promise<Uint8Array>;
|
|
44
|
+
readonly digest: (bytes: Uint8Array) => string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolves the best usable icon for a vendor homepage.
|
|
48
|
+
*
|
|
49
|
+
* Every candidate is inspected with the same gate as an in-content mark, so a
|
|
50
|
+
* site that serves an oversized, unsupported or scripted image yields no icon
|
|
51
|
+
* rather than a mirrored liability.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveSiteIcon(homepage: string, fetcher: SiteIconFetcher): Promise<SiteIcon | {
|
|
54
|
+
readonly failed: string;
|
|
55
|
+
}>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { inspectIconBytes, MAX_ICON_BYTES } from './icons.js';
|
|
2
|
+
/**
|
|
3
|
+
* Site icon discovery, for products that ship no brand mark of their own.
|
|
4
|
+
*
|
|
5
|
+
* Many plugins are a thin wrapper over a vendor's API: the content carries no
|
|
6
|
+
* logo, and the declared homepage is the only pointer to the vendor. Fetching
|
|
7
|
+
* that site's icon once at publish time, verifying it and mirroring it as a
|
|
8
|
+
* release asset replaces a per-render favicon lookup in the client.
|
|
9
|
+
*/
|
|
10
|
+
export const SITE_ICON_MAX_HTML_BYTES = 4 * 1024 * 1024;
|
|
11
|
+
/**
|
|
12
|
+
* Only the document head is parsed. Icon links are declared there, and vendor
|
|
13
|
+
* landing pages are frequently several megabytes of markup and script.
|
|
14
|
+
*/
|
|
15
|
+
export const SITE_ICON_HEAD_BYTES = 256 * 1024;
|
|
16
|
+
/** Hosts that serve repository chrome rather than a product brand. */
|
|
17
|
+
const REPOSITORY_HOSTS = /(^|\.)(github\.com|githubusercontent\.com|gitlab\.com|bitbucket\.org|codeberg\.org|sourceforge\.net|gitee\.com)$/;
|
|
18
|
+
/**
|
|
19
|
+
* Whether a homepage can stand in for a product brand.
|
|
20
|
+
*
|
|
21
|
+
* Only HTTPS sites qualify: a repository URL would yield the forge's own logo,
|
|
22
|
+
* and a non-routable host is a request forgery risk rather than a vendor site.
|
|
23
|
+
*/
|
|
24
|
+
export function isVendorHomepage(homepage) {
|
|
25
|
+
if (!homepage)
|
|
26
|
+
return false;
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = new URL(homepage);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (parsed.protocol !== 'https:')
|
|
35
|
+
return false;
|
|
36
|
+
return isPublicHostname(parsed.hostname) && !REPOSITORY_HOSTS.test(parsed.hostname);
|
|
37
|
+
}
|
|
38
|
+
/** Rejects loopback, link-local, private and internal hostnames. */
|
|
39
|
+
export function isPublicHostname(hostname) {
|
|
40
|
+
const host = hostname.toLowerCase().replace(/^\[|\]$/g, '');
|
|
41
|
+
if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local'))
|
|
42
|
+
return false;
|
|
43
|
+
if (host.endsWith('.internal') || host.endsWith('.home.arpa'))
|
|
44
|
+
return false;
|
|
45
|
+
if (host === '::1' || host.startsWith('fe80:') || host.startsWith('fc') || host.startsWith('fd'))
|
|
46
|
+
return false;
|
|
47
|
+
const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
48
|
+
if (ipv4) {
|
|
49
|
+
const [a, b] = ipv4.slice(1).map(Number);
|
|
50
|
+
if (a === 0 || a === 10 || a === 127 || a >= 224)
|
|
51
|
+
return false;
|
|
52
|
+
if (a === 169 && b === 254)
|
|
53
|
+
return false;
|
|
54
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
55
|
+
return false;
|
|
56
|
+
if (a === 192 && b === 168)
|
|
57
|
+
return false;
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// A bare hostname label has no public site behind it.
|
|
61
|
+
return host.includes('.');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Extracts icon URLs from a document head, best candidate first.
|
|
65
|
+
*
|
|
66
|
+
* `apple-touch-icon` is preferred because it is a square PNG sized for a home
|
|
67
|
+
* screen; declared `icon` links follow in declared order; `/favicon.ico` is the
|
|
68
|
+
* conventional last resort when nothing is declared.
|
|
69
|
+
*/
|
|
70
|
+
export function discoverSiteIcons(html, baseUrl) {
|
|
71
|
+
const candidates = [];
|
|
72
|
+
const linkTags = html.match(/<link\b[^>]*>/gi) ?? [];
|
|
73
|
+
for (const tag of linkTags) {
|
|
74
|
+
const rel = /\brel\s*=\s*["']?([^"'>]+)/i.exec(tag)?.[1]?.toLowerCase() ?? '';
|
|
75
|
+
if (!/\b(apple-touch-icon|icon|shortcut|mask-icon)\b/.test(rel))
|
|
76
|
+
continue;
|
|
77
|
+
const href = /\bhref\s*=\s*["']([^"']+)["']/i.exec(tag)?.[1];
|
|
78
|
+
if (!href)
|
|
79
|
+
continue;
|
|
80
|
+
const sizes = /\bsizes\s*=\s*["']?([0-9]+x[0-9]+)/i.exec(tag)?.[1];
|
|
81
|
+
const edge = sizes ? Number.parseInt(sizes, 10) : 180;
|
|
82
|
+
// Apple touch icons lead; larger declared sizes lead within a group.
|
|
83
|
+
const rank = rel.includes('apple-touch-icon') ? 0 : rel.includes('mask-icon') ? 2 : 1;
|
|
84
|
+
candidates.push({ href, rank: rank * 1000 + Math.max(0, 512 - (edge || 180)) });
|
|
85
|
+
}
|
|
86
|
+
const resolved = [];
|
|
87
|
+
for (const candidate of candidates.toSorted((left, right) => left.rank - right.rank)) {
|
|
88
|
+
try {
|
|
89
|
+
const url = new URL(candidate.href, baseUrl);
|
|
90
|
+
if (url.protocol !== 'https:' || !isPublicHostname(url.hostname))
|
|
91
|
+
continue;
|
|
92
|
+
resolved.push({ url: url.toString(), rank: candidate.rank });
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// The conventional location is always the last candidate: a site whose
|
|
99
|
+
// declared icon turns out to be unusable can still resolve through it.
|
|
100
|
+
try {
|
|
101
|
+
const fallback = new URL('/favicon.ico', baseUrl);
|
|
102
|
+
if (fallback.protocol === 'https:' && isPublicHostname(fallback.hostname))
|
|
103
|
+
resolved.push({ url: fallback.toString(), rank: 5000 });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// An unparseable base leaves the declared candidates only.
|
|
107
|
+
}
|
|
108
|
+
return [...new Set(resolved.toSorted((l, r) => l.rank - r.rank).map((item) => item.url))];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Resolves the best usable icon for a vendor homepage.
|
|
112
|
+
*
|
|
113
|
+
* Every candidate is inspected with the same gate as an in-content mark, so a
|
|
114
|
+
* site that serves an oversized, unsupported or scripted image yields no icon
|
|
115
|
+
* rather than a mirrored liability.
|
|
116
|
+
*/
|
|
117
|
+
export async function resolveSiteIcon(homepage, fetcher) {
|
|
118
|
+
if (!isVendorHomepage(homepage))
|
|
119
|
+
return { failed: 'NOT_VENDOR_HOMEPAGE' };
|
|
120
|
+
let html;
|
|
121
|
+
try {
|
|
122
|
+
html = await fetcher.fetchText(homepage);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return { failed: `HOMEPAGE_FETCH_FAILED: ${message(error)}` };
|
|
126
|
+
}
|
|
127
|
+
if (html.length > SITE_ICON_MAX_HTML_BYTES)
|
|
128
|
+
return { failed: 'HOMEPAGE_TOO_LARGE' };
|
|
129
|
+
for (const url of discoverSiteIcons(html.slice(0, SITE_ICON_HEAD_BYTES), homepage)) {
|
|
130
|
+
let bytes;
|
|
131
|
+
try {
|
|
132
|
+
bytes = await fetcher.fetchBytes(url);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (bytes.byteLength > MAX_ICON_BYTES)
|
|
138
|
+
continue;
|
|
139
|
+
const inspected = inspectIconBytes(bytes);
|
|
140
|
+
if (!('contentType' in inspected))
|
|
141
|
+
continue;
|
|
142
|
+
return {
|
|
143
|
+
url,
|
|
144
|
+
homepage,
|
|
145
|
+
bytes,
|
|
146
|
+
contentType: inspected.contentType,
|
|
147
|
+
digest: fetcher.digest(bytes),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return { failed: 'NO_USABLE_ICON' };
|
|
151
|
+
}
|
|
152
|
+
function message(error) {
|
|
153
|
+
return error instanceof Error ? error.message : String(error);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=site-icons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-icons.js","sourceRoot":"","sources":["../src/site-icons.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type MarketplaceDialect = 'openai' | 'cursor' | 'claude';
|
|
2
|
+
export interface SourceConfig {
|
|
3
|
+
readonly sourceId: string;
|
|
4
|
+
readonly displayName: string;
|
|
5
|
+
readonly repositoryUrl: string;
|
|
6
|
+
readonly marketplaceDialect: MarketplaceDialect;
|
|
7
|
+
readonly manifestPath: string;
|
|
8
|
+
readonly defaultBranch: string;
|
|
9
|
+
readonly trustClassification: 'official';
|
|
10
|
+
readonly fixturePath?: string;
|
|
11
|
+
readonly fixtureCommitSha?: string;
|
|
12
|
+
}
|
|
13
|
+
export type PluginSourceSpec = {
|
|
14
|
+
readonly kind: 'local';
|
|
15
|
+
readonly path: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'git';
|
|
18
|
+
readonly repositoryUrl: string;
|
|
19
|
+
readonly subdirectory: string;
|
|
20
|
+
readonly ref?: string;
|
|
21
|
+
readonly sha?: string;
|
|
22
|
+
};
|
|
23
|
+
export interface MarketplacePluginEntry {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly displayName?: string | undefined;
|
|
26
|
+
readonly description: string;
|
|
27
|
+
readonly categories: readonly string[];
|
|
28
|
+
readonly keywords: readonly string[];
|
|
29
|
+
readonly authors: readonly string[];
|
|
30
|
+
readonly homepage?: string | undefined;
|
|
31
|
+
readonly icons: readonly string[];
|
|
32
|
+
readonly source: PluginSourceSpec;
|
|
33
|
+
readonly policy: Record<string, unknown>;
|
|
34
|
+
readonly raw: Record<string, unknown>;
|
|
35
|
+
readonly entryDigest: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ParsedMarketplace {
|
|
38
|
+
readonly marketplaceName: string;
|
|
39
|
+
readonly marketplaceDescription: string;
|
|
40
|
+
readonly owner: readonly string[];
|
|
41
|
+
readonly entries: readonly MarketplacePluginEntry[];
|
|
42
|
+
readonly raw: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
export declare class SourceAdapterError extends Error {
|
|
45
|
+
readonly code: string;
|
|
46
|
+
readonly details: readonly string[];
|
|
47
|
+
constructor(code: string, details?: readonly string[]);
|
|
48
|
+
}
|
|
49
|
+
export declare function parseMarketplaceManifest(input: unknown, source: SourceConfig): ParsedMarketplace;
|
|
50
|
+
/** Parse JSON while rejecting duplicate object keys instead of accepting last-key-wins input. */
|
|
51
|
+
export declare function parseJsonDocument(text: string): unknown;
|
|
52
|
+
export declare function parseSourceSpec(value: unknown, source: SourceConfig, pluginName?: string): PluginSourceSpec;
|
|
53
|
+
export declare function canonicalRepositoryUrl(input: string): string;
|
|
54
|
+
export declare function safeRelativePath(input: string, code?: string): string;
|