@capybara-hq/plugin-sdk 0.0.0-stage → 0.1.0-rc.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.
Files changed (70) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +27 -2
  4. package/dist/agent-template.d.ts +47 -0
  5. package/dist/agent-template.js +123 -0
  6. package/dist/bootstrap.d.ts +67 -0
  7. package/dist/bootstrap.js +109 -0
  8. package/dist/cards.d.ts +64 -0
  9. package/dist/cards.js +217 -0
  10. package/dist/channel.d.ts +230 -0
  11. package/dist/channel.js +26 -0
  12. package/dist/connector-auth.d.ts +64 -0
  13. package/dist/connector-auth.js +96 -0
  14. package/dist/connector.d.ts +154 -0
  15. package/dist/connector.js +1 -0
  16. package/dist/ctx.d.ts +176 -0
  17. package/dist/ctx.js +1 -0
  18. package/dist/define-extension.d.ts +41 -0
  19. package/dist/define-extension.js +85 -0
  20. package/dist/directory.d.ts +29 -0
  21. package/dist/directory.js +1 -0
  22. package/dist/extension-point.d.ts +29 -0
  23. package/dist/extension-point.js +36 -0
  24. package/dist/file-preview.d.ts +64 -0
  25. package/dist/file-preview.js +72 -0
  26. package/dist/frontmatter.d.ts +12 -0
  27. package/dist/frontmatter.js +36 -0
  28. package/dist/hooks.d.ts +194 -0
  29. package/dist/hooks.js +14 -0
  30. package/dist/identity.d.ts +44 -0
  31. package/dist/identity.js +1 -0
  32. package/dist/index.d.ts +27 -0
  33. package/dist/index.js +27 -0
  34. package/dist/marketplace.d.ts +99 -0
  35. package/dist/marketplace.js +256 -0
  36. package/dist/model-params.d.ts +82 -0
  37. package/dist/model-params.js +222 -0
  38. package/dist/model.d.ts +227 -0
  39. package/dist/model.js +152 -0
  40. package/dist/permissions.d.ts +67 -0
  41. package/dist/permissions.js +271 -0
  42. package/dist/plugin-manifest.d.ts +141 -0
  43. package/dist/plugin-manifest.js +322 -0
  44. package/dist/plugin-package.d.ts +139 -0
  45. package/dist/plugin-package.js +448 -0
  46. package/dist/sandbox.d.ts +152 -0
  47. package/dist/sandbox.js +96 -0
  48. package/dist/secret.d.ts +5 -0
  49. package/dist/secret.js +8 -0
  50. package/dist/semver-range.d.ts +6 -0
  51. package/dist/semver-range.js +12 -0
  52. package/dist/skill-invocation.d.ts +83 -0
  53. package/dist/skill-invocation.js +201 -0
  54. package/dist/skill-keys.d.ts +9 -0
  55. package/dist/skill-keys.js +16 -0
  56. package/dist/skill.d.ts +77 -0
  57. package/dist/skill.js +244 -0
  58. package/dist/storage.d.ts +96 -0
  59. package/dist/storage.js +9 -0
  60. package/dist/tool.d.ts +154 -0
  61. package/dist/tool.js +218 -0
  62. package/dist/ui.d.ts +265 -0
  63. package/dist/ui.js +358 -0
  64. package/dist/url.d.ts +2 -0
  65. package/dist/url.js +12 -0
  66. package/package.json +40 -3
  67. package/schemas/bootstrap.v1.schema.json +125 -0
  68. package/schemas/connector-template.v1.schema.json +116 -0
  69. package/schemas/marketplace.v1.schema.json +270 -0
  70. package/schemas/plugin.v1.schema.json +330 -0
@@ -0,0 +1,322 @@
1
+ import semver from "semver";
2
+ import { z } from "zod";
3
+ import { AGENT_TEMPLATE_LIMITS } from "./agent-template.js";
4
+ import { defineExtension, EXTENSION_NAME_RE } from "./define-extension.js";
5
+ import { CATALOG_ITEM_NAME_RE, CONNECTOR_TEMPLATE_LIMITS, isSafePackagePath, SEMVER_VERSION_RE, validateConnectorTemplate, } from "./plugin-package.js";
6
+ import { hasUpperBound } from "./semver-range.js";
7
+ /**
8
+ * The plugin manifest `.capybara-plugin/plugin.json` (package ecosystem spec §4.1). A plugin is a
9
+ * bundle of at most one extension, skills and connector templates, packed as an npm-pack tgz:
10
+ *
11
+ * ```
12
+ * package/package.json npm metadata; version equals plugin.json version
13
+ * package/.capybara-plugin/plugin.json this manifest (the packer writes it into every tgz)
14
+ * package/skills/<name>/SKILL.md skills
15
+ * package/connectors/<id>.json connector templates ({@link ConnectorTemplateFile})
16
+ * package/agents/<name>.md agent templates (parsed by `parseAgentTemplate`)
17
+ * package/dist/index.mjs extension entry (only with an extension)
18
+ * package/migrations/*.sql, package/ui/**
19
+ * ```
20
+ *
21
+ * Paths start with `./` and stay inside the plugin root. Omitting `skills` / `connectorTemplates`
22
+ * / `agents` means "scan `skills/*\/SKILL.md` / `connectors/*.json` / `agents/*.md`" (done by the
23
+ * kernel when it reads the package). `extension` is written by the packer, never by hand.
24
+ *
25
+ * The committed JSON Schema (`schemas/plugin.v1.schema.json`) is generated from
26
+ * {@link PluginManifestSchema} by `pnpm gen:package-schemas` and drift-checked by
27
+ * `check:package-schemas`. Rules JSON Schema cannot express (path safety and nesting, uniqueness,
28
+ * no self-dependency, extension name and version equal to the plugin's, the extension manifest
29
+ * rules of defineExtension) are decided only by {@link parsePluginManifest}. Unknown keys are
30
+ * rejected (the manifest is written by the author; a typo should fail, not be ignored).
31
+ */
32
+ /** Path of the manifest inside the plugin root (and under `package/` in a tgz). */
33
+ export const PLUGIN_MANIFEST_FILE = ".capybara-plugin/plugin.json";
34
+ /** Manifest format version. */
35
+ export const PLUGIN_MANIFEST_SCHEMA_VERSION = 1;
36
+ /** Limits of the plugin manifest. */
37
+ export const PLUGIN_MANIFEST_LIMITS = {
38
+ /** Characters of the `plugin.json` text (larger text is not parsed). */
39
+ manifestChars: 256 * 1024,
40
+ dependencies: 20,
41
+ skills: 50,
42
+ connectorTemplates: 20,
43
+ /** Agent templates ({@link AGENT_TEMPLATE_LIMITS}`.perPlugin`). */
44
+ agents: AGENT_TEMPLATE_LIMITS.perPlugin,
45
+ descriptionChars: 1024,
46
+ publisherChars: 100,
47
+ displayNameChars: 100,
48
+ pathChars: 512,
49
+ };
50
+ const L = PLUGIN_MANIFEST_LIMITS;
51
+ const MAX_ERRORS = 50;
52
+ const ICON_RE = /\.(?:svg|png)$/i;
53
+ const HTTPS_URL_RE = /^https:\/\/\S+$/;
54
+ const text = (max, what) => z
55
+ .string()
56
+ .min(1)
57
+ .max(max)
58
+ .regex(/\S/, { message: `${what} must not be blank` });
59
+ const localized = z.strictObject({
60
+ zh: text(L.displayNameChars, "displayName.zh"),
61
+ en: text(L.displayNameChars, "displayName.en"),
62
+ });
63
+ /** `./<safe path>` (the part after `./` passes {@link isSafePackagePath}). */
64
+ const isDotPath = (p) => p.startsWith("./") && isSafePackagePath(p.slice(2));
65
+ const dotPath = (what) => z
66
+ .string()
67
+ .min(3)
68
+ .max(L.pathChars)
69
+ .regex(/^\.\//, { message: `${what} must start with "./"` })
70
+ .refine(isDotPath, {
71
+ message: `${what} must be a relative path inside the plugin (no "..", no leading "/")`,
72
+ });
73
+ const stripDot = (p) => p.replace(/^\.\//, "");
74
+ /** `connectors/crm.json` → `crm`. */
75
+ const baseId = (p) => (p.split("/").pop() ?? "").replace(/\.json$/, "");
76
+ const configFields = z
77
+ .array(z.strictObject({ path: z.string().max(1000), secret: z.boolean(), hot: z.boolean() }))
78
+ .max(1000);
79
+ const staticExtensionManifest = z.strictObject({
80
+ name: z.string(),
81
+ version: z.string(),
82
+ sdk: z.string().max(256),
83
+ provides: z.array(z.string()),
84
+ requires: z.array(z.string()),
85
+ optional: z.array(z.string()),
86
+ permissions: z.record(z.string(), z.unknown()),
87
+ ui: z.array(z.record(z.string(), z.unknown())).optional(),
88
+ config: z.strictObject({ deployment: configFields, org: configFields }),
89
+ });
90
+ const extensionSection = z.strictObject({
91
+ entry: dotPath("entry").refine((p) => p.endsWith(".mjs"), {
92
+ message: "entry must be a .mjs file",
93
+ }),
94
+ manifest: staticExtensionManifest,
95
+ migrations: dotPath("migrations").optional(),
96
+ ui: dotPath("ui").optional(),
97
+ });
98
+ const schema = z
99
+ .strictObject({
100
+ $schema: z.string().max(L.pathChars).optional(),
101
+ schemaVersion: z.literal(PLUGIN_MANIFEST_SCHEMA_VERSION),
102
+ name: z.string().regex(CATALOG_ITEM_NAME_RE, {
103
+ message: "name must match ^[a-z0-9][a-z0-9-]{0,63}$",
104
+ }),
105
+ version: z
106
+ .string()
107
+ .max(256)
108
+ .regex(SEMVER_VERSION_RE, { message: "version must be an exact semver version" }),
109
+ publisher: text(L.publisherChars, "publisher"),
110
+ description: text(L.descriptionChars, "description"),
111
+ displayName: localized.optional(),
112
+ icon: dotPath("icon")
113
+ .regex(ICON_RE, { message: "icon must be a .svg or .png file" })
114
+ .optional(),
115
+ license: text(100, "license").optional(),
116
+ homepage: z
117
+ .string()
118
+ .max(2000)
119
+ .regex(HTTPS_URL_RE, { message: "homepage must be an https URL" })
120
+ .optional(),
121
+ dependencies: z
122
+ .array(z.strictObject({
123
+ name: z.string().regex(CATALOG_ITEM_NAME_RE, {
124
+ message: "name must match ^[a-z0-9][a-z0-9-]{0,63}$",
125
+ }),
126
+ version: z
127
+ .string()
128
+ .min(1)
129
+ .max(256)
130
+ .refine((r) => semver.validRange(r) !== null, {
131
+ message: "version must be a valid semver range",
132
+ }),
133
+ }))
134
+ .max(L.dependencies)
135
+ .optional(),
136
+ skills: z
137
+ .array(z.strictObject({ path: dotPath("path"), pinned: z.boolean().optional() }))
138
+ .max(L.skills)
139
+ .optional(),
140
+ connectorTemplates: z
141
+ .array(z.strictObject({
142
+ path: dotPath("path").refine((p) => p.endsWith(".json") && CATALOG_ITEM_NAME_RE.test(baseId(p)), { message: "path must be a <id>.json file with id matching ^[a-z0-9][a-z0-9-]{0,63}$" }),
143
+ }))
144
+ .max(L.connectorTemplates)
145
+ .optional(),
146
+ agents: z
147
+ .array(z.strictObject({
148
+ path: dotPath("path").regex(/\.md$/, { message: "path must be a .md file" }),
149
+ }))
150
+ .max(L.agents)
151
+ .optional(),
152
+ extension: extensionSection.optional(),
153
+ })
154
+ .superRefine((m, ctx) => {
155
+ const issue = (path, message) => ctx.addIssue({ code: "custom", path, message });
156
+ const names = new Set();
157
+ m.dependencies?.forEach((d, i) => {
158
+ if (d.name === m.name)
159
+ issue(["dependencies", i, "name"], "a plugin cannot depend on itself");
160
+ if (names.has(d.name))
161
+ issue(["dependencies", i, "name"], `duplicate dependency ${d.name}`);
162
+ names.add(d.name);
163
+ });
164
+ const dirs = [];
165
+ m.skills?.forEach((s, i) => {
166
+ const p = stripDot(s.path);
167
+ if (dirs.includes(p)) {
168
+ issue(["skills", i, "path"], `duplicate skill path ${s.path}`);
169
+ return;
170
+ }
171
+ const other = dirs.find((d) => p.startsWith(`${d}/`) || d.startsWith(`${p}/`));
172
+ if (other !== undefined) {
173
+ issue(["skills", i, "path"], `skill path ${s.path} is inside another skill (./${other}) or contains it`);
174
+ }
175
+ dirs.push(p);
176
+ });
177
+ const ids = new Set();
178
+ m.connectorTemplates?.forEach((c, i) => {
179
+ const id = baseId(c.path);
180
+ if (ids.has(id))
181
+ issue(["connectorTemplates", i, "path"], `duplicate connector template id ${id}`);
182
+ ids.add(id);
183
+ });
184
+ const agentPaths = new Set();
185
+ m.agents?.forEach((a, i) => {
186
+ if (agentPaths.has(a.path))
187
+ issue(["agents", i, "path"], `duplicate agent template path ${a.path}`);
188
+ agentPaths.add(a.path);
189
+ });
190
+ if (m.extension === undefined &&
191
+ m.skills?.length === 0 &&
192
+ m.connectorTemplates?.length === 0 &&
193
+ (m.agents === undefined || m.agents.length === 0)) {
194
+ issue([], "a plugin must contain an extension, a skill, a connector template or an agent template");
195
+ }
196
+ const ext = m.extension;
197
+ if (ext === undefined)
198
+ return;
199
+ const em = ext.manifest;
200
+ if (!EXTENSION_NAME_RE.test(m.name)) {
201
+ issue(["name"], `a plugin with an extension needs an extension name (${EXTENSION_NAME_RE})`);
202
+ }
203
+ if (em.name !== m.name) {
204
+ issue(["extension", "manifest", "name"], `extension name "${em.name}" must equal the plugin name "${m.name}"`);
205
+ }
206
+ if (em.version !== m.version) {
207
+ issue(["extension", "manifest", "version"], `extension version "${em.version}" must equal the plugin version "${m.version}"`);
208
+ }
209
+ if (semver.validRange(em.sdk) !== null && !hasUpperBound(em.sdk)) {
210
+ issue(["extension", "manifest", "sdk"], `"${em.sdk}" has no upper bound: use a range such as ^0.1.0 so a future major SDK is not assumed compatible`);
211
+ }
212
+ for (const k of ["deployment", "org"]) {
213
+ const seen = new Set();
214
+ for (const f of em.config[k]) {
215
+ if (seen.has(f.path)) {
216
+ issue(["extension", "manifest", "config", k], `duplicate config path "${f.path}"`);
217
+ }
218
+ seen.add(f.path);
219
+ }
220
+ }
221
+ // Name, version, sdk range, extension point refs, permissions, UI contributions: the same
222
+ // rules as defineExtension (with a placeholder setup).
223
+ try {
224
+ defineExtension({
225
+ ...em,
226
+ config: undefined,
227
+ migrations: undefined,
228
+ setup() { },
229
+ });
230
+ }
231
+ catch (e) {
232
+ issue(["extension", "manifest"], e instanceof Error ? e.message : String(e));
233
+ }
234
+ });
235
+ /**
236
+ * The zod schema of `plugin.json` (strict objects: unknown keys rejected, `$schema` allowed).
237
+ * The JSON Schema is generated with `z.toJSONSchema(PluginManifestSchema, { io: "input" })`.
238
+ */
239
+ export const PluginManifestSchema = schema;
240
+ const template = z.strictObject({
241
+ plugin: z.string().max(100),
242
+ pluginVersion: z.string().max(256).optional(),
243
+ connector: z.string().max(100),
244
+ endpoint: z.record(z.string(), z.unknown()),
245
+ editableEndpoint: z
246
+ .array(z.string().max(1000))
247
+ .max(CONNECTOR_TEMPLATE_LIMITS.editablePaths)
248
+ .optional(),
249
+ auth: z.record(z.string(), z.unknown()),
250
+ ownership: z.enum(["org", "personal"]).optional(),
251
+ defaultToolPolicies: z.record(z.string(), z.enum(["allow", "ask", "deny"])).optional(),
252
+ icon: z.string().max(L.pathChars).optional(),
253
+ });
254
+ const templateFile = z
255
+ .strictObject({
256
+ displayName: localized,
257
+ description: text(CONNECTOR_TEMPLATE_LIMITS.descriptionChars, "description").optional(),
258
+ icon: z
259
+ .string()
260
+ .max(L.pathChars)
261
+ .refine((p) => isSafePackagePath(stripDot(p)) && ICON_RE.test(p), {
262
+ message: "icon must be a relative .svg or .png path inside the plugin",
263
+ })
264
+ .optional(),
265
+ template,
266
+ })
267
+ // Field rules (no credentials, auth presets, editableEndpoint, ...) belong to validateConnectorTemplate.
268
+ .superRefine((f, ctx) => {
269
+ for (const e of validateConnectorTemplate(f.template)) {
270
+ ctx.addIssue({ code: "custom", path: [], message: e });
271
+ }
272
+ });
273
+ /**
274
+ * The zod schema of a connector template file (strict). The JSON Schema is generated with
275
+ * `z.toJSONSchema(ConnectorTemplateFileSchema, { io: "input" })`.
276
+ */
277
+ export const ConnectorTemplateFileSchema = templateFile;
278
+ function parseWith(zodSchema, json, file) {
279
+ let data = json;
280
+ if (typeof json === "string") {
281
+ if (json.length > L.manifestChars) {
282
+ return { errors: [`${file} is larger than ${L.manifestChars} characters`] };
283
+ }
284
+ try {
285
+ data = JSON.parse(json.startsWith("\uFEFF") ? json.slice(1) : json);
286
+ }
287
+ catch (e) {
288
+ const first = (e instanceof Error ? e.message : String(e)).split("\n")[0];
289
+ return { errors: [`${file} is not valid JSON: ${first}`] };
290
+ }
291
+ }
292
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
293
+ return { errors: [`${file} must be a JSON object`] };
294
+ }
295
+ const r = zodSchema.safeParse(data);
296
+ if (r.success)
297
+ return r.data;
298
+ return {
299
+ errors: r.error.issues.slice(0, MAX_ERRORS).map((i) => {
300
+ const path = i.path.map(String).join(".");
301
+ return path ? `${path}: ${i.message}` : i.message;
302
+ }),
303
+ };
304
+ }
305
+ /**
306
+ * Parses and validates `plugin.json`: either the parsed JSON value or the file text (text larger
307
+ * than {@link PLUGIN_MANIFEST_LIMITS}`.manifestChars` is not parsed). Invalid input returns
308
+ * `{ errors }` (`<path>: <reason>`, English, at most 50); never throws. Whether the listed paths
309
+ * exist, and the "contains something" rule after scanning, are checked by the kernel when it reads
310
+ * the package.
311
+ */
312
+ export function parsePluginManifest(json) {
313
+ return parseWith(PluginManifestSchema, json, "plugin.json");
314
+ }
315
+ /**
316
+ * Parses and validates a connector template file (`connectors/<id>.json`): the parsed JSON value
317
+ * or the file text. Template problems come from {@link validateConnectorTemplate} (paths relative
318
+ * to the template root, values never echoed). Returns `{ errors }` (at most 50); never throws.
319
+ */
320
+ export function parseConnectorTemplateFile(json) {
321
+ return parseWith(ConnectorTemplateFileSchema, json, "connector template file");
322
+ }
@@ -0,0 +1,139 @@
1
+ import { type ExtensionManifest } from "./define-extension.js";
2
+ import type { ExtensionPermissions } from "./permissions.js";
3
+ import type { PluginManifest } from "./plugin-manifest.js";
4
+ /**
5
+ * Plugin package (npm-pack tgz) rules shared by the SDK, the packer and the kernel (spec §4.1). Pure
6
+ * functions only: the kernel reads the tgz and scans imports, `packages/plugin-tools` packs it. The
7
+ * manifest is `.capybara-plugin/plugin.json` (plugin-manifest.ts); an extension's static manifest
8
+ * ({@link StaticExtensionManifest}) is written into it by the packer and compared with the real
9
+ * module's {@link staticManifestOf} at load (a difference is `manifest_mismatch`). The extension
10
+ * entry is single-file ESM with third-party dependencies bundled; only {@link HOST_PROVIDED_PACKAGES}
11
+ * come from the host.
12
+ */
13
+ /** 宿主提供的包(peerDependencies,安装时链接到宿主副本);其余三方依赖必须打进 entry。 */
14
+ export declare const HOST_PROVIDED_PACKAGES: readonly ["@capybara-hq/plugin-sdk", "zod"];
15
+ /** npm 安装时会自动执行的脚本:插件包一律不许有(安装不跑脚本,有也是误导)。 */
16
+ export declare const FORBIDDEN_INSTALL_SCRIPTS: readonly ["preinstall", "install", "postinstall", "prepare"];
17
+ /** 静态清单里的一个配置字段(点路径,根即叶子时为空串)。 */
18
+ export interface StaticConfigField {
19
+ path: string;
20
+ secret: boolean;
21
+ hot: boolean;
22
+ }
23
+ /** 插件清单的静态部分(可 JSON 序列化;不含 setup、zod schema、migrations 路径)。 */
24
+ export interface StaticExtensionManifest {
25
+ name: string;
26
+ version: string;
27
+ sdk: string;
28
+ provides: string[];
29
+ requires: string[];
30
+ optional: string[];
31
+ permissions: ExtensionPermissions;
32
+ ui?: ExtensionManifest["ui"];
33
+ config: {
34
+ deployment: StaticConfigField[];
35
+ org: StaticConfigField[];
36
+ };
37
+ }
38
+ /** 入口模块里的一个 import:`name` 是 specifier(非字面量的动态 import 时为空串)。 */
39
+ export interface EntryImport {
40
+ name: string;
41
+ dynamic: boolean;
42
+ literal: boolean;
43
+ }
44
+ /** npm 包名规则(简化):可带 scope,小写,≤ 214 字符(长度另行检查)。 */
45
+ export declare const NPM_PACKAGE_NAME_RE: RegExp;
46
+ /**
47
+ * 包内相对路径是否安全:非空、不以 `/` 开头、不含 `\`、控制字符与盘符,没有空段、`.` 与 `..` 段,≤ 1024 字符。
48
+ */
49
+ export declare function isSafePackagePath(p: string): boolean;
50
+ /**
51
+ * 核对入口模块的 import(C11):静态与字面量动态 import 只许 `node:*`、{@link HOST_PROVIDED_PACKAGES}(含子路径)
52
+ * 与包内相对路径(`./`、`../`;是否越出包外由调用方按文件位置判断);非字面量的动态 import 一律拒绝。
53
+ * 返回问题列表(空表示合法)。
54
+ */
55
+ export declare function checkEntryImports(specifiers: EntryImport[]): string[];
56
+ /**
57
+ * 真实清单的静态部分:打包时写进 `capybara.manifest`,加载时与包内的比对(`manifest_mismatch`)。
58
+ * 结果可 JSON 往返(undefined 字段去掉);配置字段按路径排序。
59
+ */
60
+ export declare function staticManifestOf(m: ExtensionManifest): StaticExtensionManifest;
61
+ /**
62
+ * Validates the `package.json` of a plugin tgz against its parsed `.capybara-plugin/plugin.json`
63
+ * ({@link PluginManifest}), returning the problems (empty when valid): the rules shared with every
64
+ * plugin package (npm name, semver version, no install scripts, no `gypfile`, no dependency fields,
65
+ * `peerDependencies` only host packages, a valid `zod` peer range satisfied by `hostZodVersion`),
66
+ * no `capybara` section, the version equal to the manifest's, and:
67
+ * - with an extension: `peerDependencies["@capybara-hq/plugin-sdk"]` present and equal to
68
+ * `extension.manifest.sdk`, which the host SDK version satisfies (includePrerelease), and
69
+ * `main` / `exports` absent or naming `extension.entry` (the packer writes both);
70
+ * - without one: no `peerDependencies`, `main` or `exports` at all.
71
+ */
72
+ export declare function validatePluginPackageJsonFor(pkg: unknown, manifest: PluginManifest, hostSdkVersion: string, opts?: {
73
+ hostZodVersion?: string;
74
+ }): string[];
75
+ /**
76
+ * Whether the host SDK satisfies an extension's `extension.manifest.sdk` range: the problem
77
+ * (English), or undefined when it does (an invalid range is reported by the manifest parser).
78
+ */
79
+ export declare function hostSdkProblem(sdk: string, hostSdkVersion: string): string | undefined;
80
+ /**
81
+ * 连接器模板(C30):连接器插件引用 + `Connector.kind` + 端点预设 + 组织可改的端点路径 + 认证预设(不含凭据)+
82
+ * 默认 ownership + 各工具默认策略 + 图标。组织安装时只能填凭据、客户端 id / secret 与 `editableEndpoint` 里的字段。
83
+ */
84
+ export interface ConnectorTemplateSpec {
85
+ /** 连接器插件名(如 `connector-mcp`)。 */
86
+ plugin: string;
87
+ /** 连接器插件的 semver 范围,可选。 */
88
+ pluginVersion?: string;
89
+ /** `Connector.kind`(如 `"mcp"`)。 */
90
+ connector: string;
91
+ /** 端点预设;导入时内核按该连接器的 `endpointSchema` 校验。 */
92
+ endpoint: Record<string, unknown>;
93
+ /** 组织安装时允许改的点路径(如 `"url"`);其余以模板为准。路径必须存在于连接器的端点 schema 中。 */
94
+ editableEndpoint?: string[];
95
+ /** 与连接器安装接口的认证方式同形({@link ConnectorAuthSchema}),不含任何凭据。 */
96
+ auth: Record<string, unknown>;
97
+ /** 缺省取 `auth.ownership`;给了就必须与之一致。 */
98
+ ownership?: "org" | "personal";
99
+ /** 工具名 → 默认策略(安装后记 `policy_source 'template'`)。 */
100
+ defaultToolPolicies?: Record<string, "allow" | "ask" | "deny">;
101
+ /** 包内路径,svg / png,≤ {@link CONNECTOR_TEMPLATE_LIMITS}`.iconBytes`。 */
102
+ icon?: string;
103
+ }
104
+ /** 双语显示名。 */
105
+ export interface LocalizedName {
106
+ zh: string;
107
+ en: string;
108
+ }
109
+ /** 连接器模板与目录段的上限。 */
110
+ export declare const CONNECTOR_TEMPLATE_LIMITS: {
111
+ /** 图标字节数上限。 */
112
+ readonly iconBytes: number;
113
+ /** 端点预设 JSON 序列化后的字符数上限。 */
114
+ readonly endpointChars: number;
115
+ /** `editableEndpoint` 条数上限。 */
116
+ readonly editablePaths: 50;
117
+ /** `defaultToolPolicies` 条数上限。 */
118
+ readonly toolPolicies: 1000;
119
+ /** publisher 字符数上限。 */
120
+ readonly publisherChars: 100;
121
+ /** description 字符数上限。 */
122
+ readonly descriptionChars: 1024;
123
+ /** displayName 每种语言的字符数上限。 */
124
+ readonly displayNameChars: 100;
125
+ };
126
+ /** 目录条目名(与清单条目名同一规则):小写字母或数字开头,之后小写字母、数字、连字符,≤ 64。 */
127
+ export declare const CATALOG_ITEM_NAME_RE: RegExp;
128
+ /** An exact semver version (a regex JSON Schema can use). */
129
+ export declare const SEMVER_VERSION_RE: RegExp;
130
+ /** The sha512 SRI of an npm package tgz: `sha512-<86 base64 chars>==`. */
131
+ export declare const SHA512_INTEGRITY_RE: RegExp;
132
+ /**
133
+ * 校验连接器模板({@link ConnectorTemplateSpec})的静态部分,返回问题列表(空表示合法;路径相对模板根,不回显值):
134
+ * 字段齐全、无未知字段;`auth` 符合 {@link ConnectorAuthSchema}(strict:夹带 `token` 等多余字段即拒绝);
135
+ * 端点预设里任何层级都不许有凭据样式的键;`editableEndpoint` 是不重复的点路径;`ownership` 与 `auth.ownership`
136
+ * 一致;默认策略只许 allow / ask / deny;icon 是包内 .svg / .png 路径。
137
+ * 端点是否符合连接器的 `endpointSchema`、`editableEndpoint` 是否存在于其中,由内核在导入时按已加载的连接器校验。
138
+ */
139
+ export declare function validateConnectorTemplate(t: unknown): string[];