@vetta-org/plugin-cli 0.1.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/CHANGELOG.md +18 -0
- package/README.md +108 -0
- package/dist/agents-template.d.ts +16 -0
- package/dist/agents-template.d.ts.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +20181 -0
- package/dist/command.d.ts +101 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/hub-template.d.ts +18 -0
- package/dist/hub-template.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20204 -0
- package/dist/init.d.ts +31 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/npm-package.d.ts +27 -0
- package/dist/npm-package.d.ts.map +1 -0
- package/dist/sync.d.ts +50 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/workspace.d.ts +30 -0
- package/dist/workspace.d.ts.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type ResolvedNpmPluginArchive } from "./npm-package.js";
|
|
2
|
+
export type PluginAddCommand = {
|
|
3
|
+
type: "help";
|
|
4
|
+
} | {
|
|
5
|
+
type: "error";
|
|
6
|
+
message: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: "add";
|
|
9
|
+
source: string;
|
|
10
|
+
json: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type PluginReloadCommand = {
|
|
13
|
+
type: "help";
|
|
14
|
+
} | {
|
|
15
|
+
type: "error";
|
|
16
|
+
message: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: "reload";
|
|
19
|
+
pluginId: string;
|
|
20
|
+
json: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type PluginDocsCommand = {
|
|
23
|
+
type: "help";
|
|
24
|
+
} | {
|
|
25
|
+
type: "error";
|
|
26
|
+
message: string;
|
|
27
|
+
} | {
|
|
28
|
+
type: "docs";
|
|
29
|
+
json: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type PluginInitCommand = {
|
|
32
|
+
type: "help";
|
|
33
|
+
} | {
|
|
34
|
+
type: "error";
|
|
35
|
+
message: string;
|
|
36
|
+
} | {
|
|
37
|
+
type: "init";
|
|
38
|
+
targetDir?: string;
|
|
39
|
+
pluginId: string;
|
|
40
|
+
displayName?: string;
|
|
41
|
+
json: boolean;
|
|
42
|
+
} | {
|
|
43
|
+
type: "init-hub";
|
|
44
|
+
targetDir?: string;
|
|
45
|
+
name: string;
|
|
46
|
+
repository: string;
|
|
47
|
+
minAppVersion: string;
|
|
48
|
+
json: boolean;
|
|
49
|
+
};
|
|
50
|
+
export type PluginWatchCommand = {
|
|
51
|
+
type: "help";
|
|
52
|
+
} | {
|
|
53
|
+
type: "error";
|
|
54
|
+
message: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: "watch";
|
|
57
|
+
dir?: string;
|
|
58
|
+
stop: boolean;
|
|
59
|
+
json: boolean;
|
|
60
|
+
};
|
|
61
|
+
export type PluginUninstallCommand = {
|
|
62
|
+
type: "help";
|
|
63
|
+
} | {
|
|
64
|
+
type: "error";
|
|
65
|
+
message: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: "uninstall";
|
|
68
|
+
pluginId?: string;
|
|
69
|
+
json: boolean;
|
|
70
|
+
};
|
|
71
|
+
export type PluginSyncCommand = {
|
|
72
|
+
type: "help";
|
|
73
|
+
} | {
|
|
74
|
+
type: "error";
|
|
75
|
+
message: string;
|
|
76
|
+
} | {
|
|
77
|
+
type: "sync";
|
|
78
|
+
check: boolean;
|
|
79
|
+
json: boolean;
|
|
80
|
+
};
|
|
81
|
+
export type PluginCommand = PluginAddCommand | PluginSyncCommand | PluginUninstallCommand | PluginReloadCommand | PluginDocsCommand | PluginInitCommand | PluginWatchCommand;
|
|
82
|
+
export interface PluginCommandDependencies {
|
|
83
|
+
resolveNpmArchive(packageSpec: string): Promise<ResolvedNpmPluginArchive>;
|
|
84
|
+
/** 命令执行时所在目录;缺省用 process.cwd(),测试与非交互调用方可以覆盖。 */
|
|
85
|
+
cwd?(): string;
|
|
86
|
+
runAction(actionId: string, input: unknown): Promise<unknown>;
|
|
87
|
+
writeStdout(value: string): void;
|
|
88
|
+
writeStderr(value: string): void;
|
|
89
|
+
}
|
|
90
|
+
export type PluginAddCommandDependencies = PluginCommandDependencies;
|
|
91
|
+
export declare function parsePluginAddCommand(argv: string[]): PluginAddCommand | undefined;
|
|
92
|
+
export declare function parsePluginReloadCommand(argv: string[]): PluginReloadCommand | undefined;
|
|
93
|
+
export declare function parsePluginDocsCommand(argv: string[]): PluginDocsCommand | undefined;
|
|
94
|
+
export declare function parsePluginInitCommand(argv: string[]): PluginInitCommand | undefined;
|
|
95
|
+
export declare function parsePluginWatchCommand(argv: string[]): PluginWatchCommand | undefined;
|
|
96
|
+
export declare function parsePluginUninstallCommand(argv: string[]): PluginUninstallCommand | undefined;
|
|
97
|
+
export declare function parsePluginSyncCommand(argv: string[]): PluginSyncCommand | undefined;
|
|
98
|
+
export declare function runPluginAddCommand(command: PluginAddCommand, dependencies?: PluginAddCommandDependencies): Promise<number>;
|
|
99
|
+
export declare function runPluginCommand(command: PluginCommand, dependencies?: PluginCommandDependencies): Promise<number>;
|
|
100
|
+
export declare function runPluginCli(argv: string[]): Promise<number>;
|
|
101
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { ActionRpcError, createActionRpcClient, readActionRpcEndpoint } from \"@vetta/action-rpc\";\nimport { resolveNpmPluginArchive, type ResolvedNpmPluginArchive } from \"./npm-package.js\";\nimport { initHubRepository, initPluginProject } from \"./init.js\";\nimport { describeIndexDrift, syncMarketplaceIndex } from \"./sync.js\";\nimport { findPluginHub, findPluginProject, type PluginProject, readManualSdkVersion, resolveManualDir } from \"./workspace.js\";\n\nexport type PluginAddCommand =\n\t| { type: \"help\" }\n\t| { type: \"error\"; message: string }\n\t| { type: \"add\"; source: string; json: boolean };\n\nexport type PluginReloadCommand =\n\t| { type: \"help\" }\n\t| { type: \"error\"; message: string }\n\t| { type: \"reload\"; pluginId: string; json: boolean };\n\nexport type PluginDocsCommand =\n\t| { type: \"help\" }\n\t| { type: \"error\"; message: string }\n\t| { type: \"docs\"; json: boolean };\n\nexport type PluginInitCommand =\n\t| { type: \"help\" }\n\t| { type: \"error\"; message: string }\n\t| { type: \"init\"; targetDir?: string; pluginId: string; displayName?: string; json: boolean }\n\t| { type: \"init-hub\"; targetDir?: string; name: string; repository: string; minAppVersion: string; json: boolean };\n\nexport type PluginWatchCommand =\n\t| { type: \"help\" }\n\t| { type: \"error\"; message: string }\n\t| { type: \"watch\"; dir?: string; stop: boolean; json: boolean };\n\nexport type PluginUninstallCommand =\n\t| { type: \"help\" }\n\t| { type: \"error\"; message: string }\n\t| { type: \"uninstall\"; pluginId?: string; json: boolean };\n\nexport type PluginSyncCommand =\n\t| { type: \"help\" }\n\t| { type: \"error\"; message: string }\n\t| { type: \"sync\"; check: boolean; json: boolean };\n\nexport type PluginCommand =\n\t| PluginAddCommand\n\t| PluginSyncCommand\n\t| PluginUninstallCommand\n\t| PluginReloadCommand\n\t| PluginDocsCommand\n\t| PluginInitCommand\n\t| PluginWatchCommand;\n\nexport interface PluginCommandDependencies {\n\tresolveNpmArchive(packageSpec: string): Promise<ResolvedNpmPluginArchive>;\n\t/** 命令执行时所在目录;缺省用 process.cwd(),测试与非交互调用方可以覆盖。 */\n\tcwd?(): string;\n\trunAction(actionId: string, input: unknown): Promise<unknown>;\n\twriteStdout(value: string): void;\n\twriteStderr(value: string): void;\n}\n\nexport type PluginAddCommandDependencies = PluginCommandDependencies;\n\nconst HELP_TEXT = `Vetta plugin manager\n\nUsage:\n vetta-plugin-cli add <npm-package|zip-path|http-url> [--json]\n vetta-plugin-cli reload <plugin-id> [--json]\n vetta-plugin-cli docs [--json]\n vetta-plugin-cli init --id <plugin-id> [--name <display>] [dir] [--json]\n vetta-plugin-cli init hub --name <slug> --repository <url> --min-app-version <x.y.z> [dir]\n vetta-plugin-cli watch [dir] [--stop] [--json]\n vetta-plugin-cli uninstall [plugin-id] [--json]\n vetta-plugin-cli sync [--check] [--json]\n\nExamples:\n npx @vetta-org/plugin-cli add @example/vetta-plugin-demo\n npx @vetta-org/plugin-cli add @example/vetta-plugin-demo@1.2.0\n npx @vetta-org/plugin-cli add . # 当前插件工程(先 pack)\n npx @vetta-org/plugin-cli add ./release/demo-1.2.0.zip\n npx @vetta-org/plugin-cli reload demo\n npx @vetta-org/plugin-cli docs\n npx @vetta-org/plugin-cli init --id my-plugin --name \"My Plugin\"\n npx @vetta-org/plugin-cli init hub --name my-market --repository https://github.com/me/my-market --min-app-version 0.55.0\n npx @vetta-org/plugin-cli watch # 让宿主改从工程目录加载,改完即生效\n npx @vetta-org/plugin-cli uninstall # 卸载当前插件工程对应的插件\n npx @vetta-org/plugin-cli sync # 在市场仓库根对账 .vetta/marketplace.json\n npx @vetta-org/plugin-cli sync --check # 只报不写,给 CI 用\n`;\n\nfunction formatParseError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function parsePluginAddCommand(argv: string[]): PluginAddCommand | undefined {\n\tif (argv[0] !== \"add\") return undefined;\n\tif (argv[1] === \"-h\" || argv[1] === \"--help\") return { type: \"help\" };\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({ args: argv.slice(1), allowPositionals: true, strict: true, options: { json: { type: \"boolean\" } } });\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst [source, unexpected] = parsed.positionals;\n\tif (!source) return { type: \"error\", message: \"Missing <npm-package|zip-path|http-url>\" };\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\treturn { type: \"add\", source, json: parsed.values.json === true };\n}\n\nexport function parsePluginReloadCommand(argv: string[]): PluginReloadCommand | undefined {\n\tif (argv[0] !== \"reload\") return undefined;\n\tif (argv[1] === \"-h\" || argv[1] === \"--help\") return { type: \"help\" };\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({ args: argv.slice(1), allowPositionals: true, strict: true, options: { json: { type: \"boolean\" } } });\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst [pluginId, unexpected] = parsed.positionals;\n\tif (!pluginId) return { type: \"error\", message: \"Missing <plugin-id>\" };\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\treturn { type: \"reload\", pluginId, json: parsed.values.json === true };\n}\n\nexport function parsePluginDocsCommand(argv: string[]): PluginDocsCommand | undefined {\n\tif (argv[0] !== \"docs\") return undefined;\n\tif (argv[1] === \"-h\" || argv[1] === \"--help\") return { type: \"help\" };\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({ args: argv.slice(1), allowPositionals: true, strict: true, options: { json: { type: \"boolean\" } } });\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst [unexpected] = parsed.positionals;\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\treturn { type: \"docs\", json: parsed.values.json === true };\n}\n\nexport function parsePluginInitCommand(argv: string[]): PluginInitCommand | undefined {\n\tif (argv[0] !== \"init\") return undefined;\n\tif (argv[1] === \"-h\" || argv[1] === \"--help\") return { type: \"help\" };\n\tif (argv[1] === \"hub\") return parseInitHubCommand(argv.slice(2));\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({\n\t\t\targs: argv.slice(1),\n\t\t\tallowPositionals: true,\n\t\t\tstrict: true,\n\t\t\toptions: {\n\t\t\t\tid: { type: \"string\" },\n\t\t\t\tname: { type: \"string\" },\n\t\t\t\tjson: { type: \"boolean\" },\n\t\t\t},\n\t\t});\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst pluginId = parsed.values.id;\n\tif (typeof pluginId !== \"string\" || pluginId.length === 0) {\n\t\treturn { type: \"error\", message: \"Missing --id <plugin-id>\" };\n\t}\n\tconst [targetDir, unexpected] = parsed.positionals;\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\treturn {\n\t\ttype: \"init\",\n\t\t...(targetDir ? { targetDir } : {}),\n\t\tpluginId,\n\t\t...(typeof parsed.values.name === \"string\" ? { displayName: parsed.values.name } : {}),\n\t\tjson: parsed.values.json === true,\n\t};\n}\n\nfunction parseInitHubCommand(argv: string[]): PluginInitCommand {\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({\n\t\t\targs: argv,\n\t\t\tallowPositionals: true,\n\t\t\tstrict: true,\n\t\t\toptions: {\n\t\t\t\tname: { type: \"string\" },\n\t\t\t\trepository: { type: \"string\" },\n\t\t\t\t\"min-app-version\": { type: \"string\" },\n\t\t\t\tjson: { type: \"boolean\" },\n\t\t\t},\n\t\t});\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst name = parsed.values.name;\n\tif (typeof name !== \"string\" || name.length === 0) return { type: \"error\", message: \"Missing --name <slug>\" };\n\tconst repository = parsed.values.repository;\n\tif (typeof repository !== \"string\" || repository.length === 0) {\n\t\treturn { type: \"error\", message: \"Missing --repository <https url>\" };\n\t}\n\t// 刻意不给默认值:太低会让装不动新 schema 的旧客户端也去激活快照,太高则部分用户直接\n\t// 看不到这个市场。这是发布决定,不该由工具替作者猜。\n\tconst minAppVersion = parsed.values[\"min-app-version\"];\n\tif (typeof minAppVersion !== \"string\" || minAppVersion.length === 0) {\n\t\treturn {\n\t\t\ttype: \"error\",\n\t\t\tmessage: \"Missing --min-app-version <x.y.z> (the oldest Vetta Desktop version your abilities support)\",\n\t\t};\n\t}\n\tconst [targetDir, unexpected] = parsed.positionals;\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\treturn {\n\t\ttype: \"init-hub\",\n\t\t...(targetDir ? { targetDir } : {}),\n\t\tname,\n\t\trepository,\n\t\tminAppVersion,\n\t\tjson: parsed.values.json === true,\n\t};\n}\n\nexport function parsePluginWatchCommand(argv: string[]): PluginWatchCommand | undefined {\n\tif (argv[0] !== \"watch\") return undefined;\n\tif (argv[1] === \"-h\" || argv[1] === \"--help\") return { type: \"help\" };\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({\n\t\t\targs: argv.slice(1),\n\t\t\tallowPositionals: true,\n\t\t\tstrict: true,\n\t\t\toptions: { json: { type: \"boolean\" }, stop: { type: \"boolean\" } },\n\t\t});\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst [dir, unexpected] = parsed.positionals;\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\treturn {\n\t\ttype: \"watch\",\n\t\t...(dir ? { dir } : {}),\n\t\tstop: parsed.values.stop === true,\n\t\tjson: parsed.values.json === true,\n\t};\n}\n\nexport function parsePluginUninstallCommand(argv: string[]): PluginUninstallCommand | undefined {\n\tif (argv[0] !== \"uninstall\") return undefined;\n\tif (argv[1] === \"-h\" || argv[1] === \"--help\") return { type: \"help\" };\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({\n\t\t\targs: argv.slice(1),\n\t\t\tallowPositionals: true,\n\t\t\tstrict: true,\n\t\t\toptions: { json: { type: \"boolean\" } },\n\t\t});\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst [pluginId, unexpected] = parsed.positionals;\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\t// 省略 id 时按 cwd 推断,语义与 add . / watch 一致:站在哪个插件里就作用于哪个。\n\treturn { type: \"uninstall\", ...(pluginId ? { pluginId } : {}), json: parsed.values.json === true };\n}\n\nexport function parsePluginSyncCommand(argv: string[]): PluginSyncCommand | undefined {\n\tif (argv[0] !== \"sync\") return undefined;\n\tif (argv[1] === \"-h\" || argv[1] === \"--help\") return { type: \"help\" };\n\tlet parsed: ReturnType<typeof parseArgs>;\n\ttry {\n\t\tparsed = parseArgs({\n\t\t\targs: argv.slice(1),\n\t\t\tallowPositionals: true,\n\t\t\tstrict: true,\n\t\t\toptions: { json: { type: \"boolean\" }, check: { type: \"boolean\" } },\n\t\t});\n\t} catch (error) {\n\t\treturn { type: \"error\", message: formatParseError(error) };\n\t}\n\tconst [unexpected] = parsed.positionals;\n\tif (unexpected) return { type: \"error\", message: `Unexpected argument: ${unexpected}` };\n\treturn { type: \"sync\", check: parsed.values.check === true, json: parsed.values.json === true };\n}\n\nasync function defaultRunAction(actionId: string, input: unknown): Promise<unknown> {\n\tconst client = createActionRpcClient(await readActionRpcEndpoint());\n\treturn client.run(actionId, input);\n}\n\nconst defaultDependencies: PluginCommandDependencies = {\n\tresolveNpmArchive: resolveNpmPluginArchive,\n\tcwd: () => process.cwd(),\n\trunAction: defaultRunAction,\n\twriteStdout: (value) => process.stdout.write(value),\n\twriteStderr: (value) => process.stderr.write(value),\n};\n\nfunction isHttpUrl(source: string): boolean {\n\ttry {\n\t\tconst url = new URL(source);\n\t\treturn url.protocol === \"http:\" || url.protocol === \"https:\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isLocalZip(source: string): boolean {\n\tif (source.toLowerCase().endsWith(\".zip\")) return true;\n\tconst path = resolve(source);\n\t// 目录不是压缩包:它是一个插件工程,走 resolveProjectArchive 先找它打出来的产物。\n\treturn existsSync(path) && !statSync(path).isDirectory();\n}\n\nfunction isDirectorySource(source: string): boolean {\n\tconst path = resolve(source);\n\treturn existsSync(path) && statSync(path).isDirectory();\n}\n\n/**\n * 把「装当前这个工程」翻译成一个具体的归档路径。\n *\n * 这条路径是给 `install:vetta` 这类脚本用的:作者(或 Agent)在插件目录里跑一条命令就\n * 装进 Vetta,不必记住产物叫什么名字。找不到产物时给出该跑的那条命令,而不是报一个\n * 「文件不存在」让人自己猜。\n */\nfunction resolveProjectArchive(source: string): { archivePath: string; project: PluginProject } {\n\tconst from = resolve(source);\n\tconst project = findPluginProject(from);\n\tif (!project) {\n\t\tconst hub = findPluginHub(from);\n\t\tif (hub) {\n\t\t\tthrow new Error(\n\t\t\t\t`${from} indexes plugins but is not one itself. Run this from a plugin directory, or pass its path: vetta-plugin-cli add ./path/to/plugin`,\n\t\t\t);\n\t\t}\n\t\tthrow new Error(`No plugin.json found in ${from} or any parent directory.`);\n\t}\n\tconst archivePath = join(project.root, \"release\", `${project.pluginId}-${project.version}.zip`);\n\tif (!existsSync(archivePath)) {\n\t\tthrow new Error(\n\t\t\t`Packaged archive not found: ${archivePath}\\nBuild it first: npm run build && npx vetta-plugin pack`,\n\t\t);\n\t}\n\treturn { archivePath, project };\n}\n\n/** 装完立刻检查索引是否还停在旧版本;不在市场仓库里时什么也不说。 */\nfunction indexDriftHint(project: PluginProject): string | undefined {\n\tconst hub = findPluginHub(project.root);\n\tif (!hub) return undefined;\n\treturn describeIndexDrift({\n\t\thubRoot: hub.root,\n\t\tmanifestPath: hub.manifestPath,\n\t\tslug: project.pluginId,\n\t\tversion: project.version,\n\t});\n}\n\nfunction npmInstallInput(resolved: ResolvedNpmPluginArchive): Record<string, unknown> {\n\treturn {\n\t\toperation: \"install-from-path\",\n\t\tpath: resolved.archivePath,\n\t\tenable: true,\n\t\tsource: \"npm\",\n\t\texpectedSha256: resolved.expectedSha256,\n\t\texpectedId: resolved.packageManifest.vetta.pluginId,\n\t\texpectedVersion: resolved.packageManifest.version,\n\t\tnpm: {\n\t\t\tpackageName: resolved.packageManifest.name,\n\t\t\trequestedSpec: resolved.requestedSpec,\n\t\t\tresolvedVersion: resolved.packageManifest.version,\n\t\t\t...(resolved.integrity ? { integrity: resolved.integrity } : {}),\n\t\t},\n\t};\n}\n\nfunction resultSummary(result: unknown): string {\n\tif (typeof result !== \"object\" || result === null || Array.isArray(result)) return \"Plugin installed.\\n\";\n\tconst response = result as Record<string, unknown>;\n\tconst plugin =\n\t\ttypeof response.plugin === \"object\" && response.plugin !== null && !Array.isArray(response.plugin)\n\t\t\t? (response.plugin as Record<string, unknown>)\n\t\t\t: undefined;\n\tif (!plugin) return \"Plugin installed.\\n\";\n\tconst id = typeof plugin.id === \"string\" ? plugin.id : \"plugin\";\n\tconst version = typeof plugin.version === \"string\" ? `@${plugin.version}` : \"\";\n\tconst pending = typeof plugin.pendingVersion === \"string\"\n\t\t? ` Update ${plugin.pendingVersion} is pending reload. Run \\`vetta-plugin-cli reload ${id}\\` to apply it.`\n\t\t: \"\";\n\treturn `Installed ${id}${version}.${pending}\\n`;\n}\n\nfunction reloadResultSummary(result: unknown, requestedPluginId: string): string {\n\tif (typeof result !== \"object\" || result === null || Array.isArray(result)) {\n\t\treturn `Reloaded ${requestedPluginId}.\\n`;\n\t}\n\tconst response = result as Record<string, unknown>;\n\tconst plugin =\n\t\ttypeof response.plugin === \"object\" && response.plugin !== null && !Array.isArray(response.plugin)\n\t\t\t? (response.plugin as Record<string, unknown>)\n\t\t\t: undefined;\n\tconst id = typeof plugin?.id === \"string\" ? plugin.id : requestedPluginId;\n\tconst version = typeof plugin?.activeVersion === \"string\" ? `@${plugin.activeVersion}` : \"\";\n\treturn `Reloaded ${id}${version}.\\n`;\n}\n\nfunction isConnectionError(error: unknown): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst code = (error as NodeJS.ErrnoException).code;\n\treturn (\n\t\tcode === \"ENOENT\" ||\n\t\tcode === \"ECONNREFUSED\" ||\n\t\tcode === \"ECONNRESET\" ||\n\t\terror.message.includes(\"ECONNREFUSED\") ||\n\t\terror.message.includes(\"fetch failed\")\n\t);\n}\n\nexport async function runPluginAddCommand(\n\tcommand: PluginAddCommand,\n\tdependencies: PluginAddCommandDependencies = defaultDependencies,\n): Promise<number> {\n\treturn runPluginCommand(command, dependencies);\n}\n\nexport async function runPluginCommand(\n\tcommand: PluginCommand,\n\tdependencies: PluginCommandDependencies = defaultDependencies,\n): Promise<number> {\n\tif (command.type === \"help\") {\n\t\tdependencies.writeStdout(HELP_TEXT);\n\t\treturn 0;\n\t}\n\tif (command.type === \"error\") {\n\t\tdependencies.writeStderr(`${command.message}\\n`);\n\t\treturn 2;\n\t}\n\n\tif (command.type === \"docs\") {\n\t\treturn runDocsCommand(command, dependencies);\n\t}\n\tif (command.type === \"init\") {\n\t\treturn runInitCommand(command, dependencies);\n\t}\n\tif (command.type === \"init-hub\") {\n\t\treturn runInitHubCommand(command, dependencies);\n\t}\n\tif (command.type === \"sync\") {\n\t\treturn runSyncCommand(command, dependencies);\n\t}\n\n\tif (command.type === \"watch\") {\n\t\treturn runWatchCommand(command, dependencies);\n\t}\n\tif (command.type === \"uninstall\") {\n\t\treturn runUninstallCommand(command, dependencies);\n\t}\n\n\tlet resolvedNpm: ResolvedNpmPluginArchive | undefined;\n\tlet driftHint: string | undefined;\n\ttry {\n\t\tlet result: unknown;\n\t\tif (command.type === \"reload\") {\n\t\t\tresult = await dependencies.runAction(\"plugins.manage\", {\n\t\t\t\toperation: \"reload\",\n\t\t\t\tid: command.pluginId,\n\t\t\t});\n\t\t} else if (isHttpUrl(command.source)) {\n\t\t\tresult = await dependencies.runAction(\"plugins.manage\", {\n\t\t\t\toperation: \"install-from-url\",\n\t\t\t\turl: command.source,\n\t\t\t});\n\t\t} else if (isDirectorySource(command.source)) {\n\t\t\tconst { archivePath, project } = resolveProjectArchive(command.source);\n\t\t\tresult = await dependencies.runAction(\"plugins.manage\", {\n\t\t\t\toperation: \"install-from-path\",\n\t\t\t\tpath: archivePath,\n\t\t\t\tenable: true,\n\t\t\t});\n\t\t\tdriftHint = indexDriftHint(project);\n\t\t} else if (isLocalZip(command.source)) {\n\t\t\tresult = await dependencies.runAction(\"plugins.manage\", {\n\t\t\t\toperation: \"install-from-path\",\n\t\t\t\tpath: resolve(command.source),\n\t\t\t\tenable: true,\n\t\t\t});\n\t\t} else {\n\t\t\tresolvedNpm = await dependencies.resolveNpmArchive(command.source);\n\t\t\tresult = await dependencies.runAction(\"plugins.manage\", npmInstallInput(resolvedNpm));\n\t\t}\n\t\tdependencies.writeStdout(\n\t\t\tcommand.json\n\t\t\t\t? `${JSON.stringify({ ok: true, result, ...(driftHint ? { warning: driftHint } : {}) })}\\n`\n\t\t\t\t: command.type === \"reload\"\n\t\t\t\t\t? reloadResultSummary(result, command.pluginId)\n\t\t\t\t\t: `${resultSummary(result)}${driftHint ? `${driftHint}\\n` : \"\"}`,\n\t\t);\n\t\treturn 0;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(\n\t\t\t\t`${JSON.stringify({ ok: false, error: { code: error instanceof ActionRpcError ? error.code : command.type === \"reload\" ? \"PLUGIN_RELOAD_FAILED\" : \"PLUGIN_ADD_FAILED\", message } })}\\n`,\n\t\t\t);\n\t\t} else {\n\t\t\tdependencies.writeStderr(`${message}\\n`);\n\t\t}\n\t\tif (error instanceof ActionRpcError) return 4;\n\t\treturn isConnectionError(error) ? 3 : 5;\n\t} finally {\n\t\tawait resolvedNpm?.cleanup();\n\t}\n}\n\n/**\n * 打印随 SDK 发布的手册目录。\n *\n * 存在的理由是「不要让任何人硬编码 node_modules 路径」:工作区会把依赖提升到仓库根,\n * 一仓多插件的 hub 里每个插件也可能各装一份。Agent 只需记住这一条命令,拿回来的永远是\n * 当前工程实际编译所针对的那个 SDK 版本的手册。\n */\nfunction runDocsCommand(command: { json: boolean }, dependencies: PluginCommandDependencies): number {\n\tconst cwd = dependencies.cwd?.() ?? process.cwd();\n\tconst manualDir = resolveManualDir(cwd);\n\tif (!manualDir) {\n\t\tconst message =\n\t\t\t\"Plugin manual not found. Install the SDK first: npm i -D @vetta-org/plugin-sdk\\n\";\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(\n\t\t\t\t`${JSON.stringify({ ok: false, error: { code: \"MANUAL_NOT_FOUND\", message: message.trim() } })}\\n`,\n\t\t\t);\n\t\t} else {\n\t\t\tdependencies.writeStderr(message);\n\t\t}\n\t\treturn 6;\n\t}\n\tconst project = findPluginProject(cwd);\n\tconst hub = findPluginHub(cwd);\n\tconst sdkVersion = readManualSdkVersion(manualDir);\n\tif (command.json) {\n\t\tdependencies.writeStdout(\n\t\t\t`${JSON.stringify({\n\t\t\t\tok: true,\n\t\t\t\tmanualDir,\n\t\t\t\tentry: join(manualDir, \"README.md\"),\n\t\t\t\tsdkVersion,\n\t\t\t\tproject: project ? { root: project.root, pluginId: project.pluginId, version: project.version } : undefined,\n\t\t\t\thub: hub\n\t\t\t\t\t? {\n\t\t\t\t\t\t\troot: hub.root,\n\t\t\t\t\t\t\tmanifestPath: hub.manifestPath,\n\t\t\t\t\t\t\tsyncHint: \"After changing version/permissions, run `vetta-plugin-cli sync` at the repository root.\",\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t})}\\n`,\n\t\t);\n\t\treturn 0;\n\t}\n\tconst lines = [\n\t\t`Plugin manual (@vetta-org/plugin-sdk${sdkVersion ? `@${sdkVersion}` : \"\"}):`,\n\t\t` ${manualDir}`,\n\t\t`Start here: ${join(manualDir, \"README.md\")}`,\n\t];\n\tif (project) lines.push(`Current plugin: ${project.pluginId} (${project.root})`);\n\tif (hub) {\n\t\tlines.push(`Marketplace index: ${hub.manifestPath}`);\n\t\t// Agent 几乎一定会先跑 docs,所以这是告诉它「索引要对账」的最佳时机。\n\t\tlines.push(\"After changing version/permissions, run `vetta-plugin-cli sync` at the repository root.\");\n\t}\n\tdependencies.writeStdout(`${lines.join(\"\\n\")}\\n`);\n\treturn 0;\n}\n\n/** 在陌生目录里生成一个可直接开工的插件工程,并留下让任意 Agent 自举的 AGENTS.md。 */\nfunction runInitCommand(\n\tcommand: Extract<PluginCommand, { type: \"init\" }>,\n\tdependencies: PluginCommandDependencies,\n): number {\n\tconst cwd = dependencies.cwd?.() ?? process.cwd();\n\ttry {\n\t\tconst result = initPluginProject({\n\t\t\ttargetDir: resolve(cwd, command.targetDir ?? command.pluginId),\n\t\t\tpluginId: command.pluginId,\n\t\t\tdisplayName: command.displayName ?? command.pluginId,\n\t\t});\n\t\tdependencies.writeStdout(\n\t\t\tcommand.json\n\t\t\t\t? `${JSON.stringify({ ok: true, ...result })}\\n`\n\t\t\t\t: [\n\t\t\t\t\t\t`Created ${result.pluginId} at ${result.root}`,\n\t\t\t\t\t\t\"Next: npm install && npm run install:vetta\",\n\t\t\t\t\t\t\"The agent brief is in AGENTS.md; the manual is at `npx vetta-plugin-cli docs`.\",\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\"\\n\")\n\t\t\t\t\t\t.concat(\"\\n\"),\n\t\t);\n\t\treturn 0;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(`${JSON.stringify({ ok: false, error: { code: \"PLUGIN_INIT_FAILED\", message } })}\\n`);\n\t\t} else {\n\t\t\tdependencies.writeStderr(`${message}\\n`);\n\t\t}\n\t\treturn 5;\n\t}\n}\n\n/**\n * 让宿主改从工程目录加载本插件,之后改源码即时生效,不必每次 build → pack → install。\n *\n * 目标插件按 cwd 向上找,理由同 `add .`:一仓多插件时「我正站在哪个插件里」是唯一不会\n * 弄错的意图,而 id 靠人重复输入迟早会错配到另一个插件上。\n */\nasync function runWatchCommand(\n\tcommand: Extract<PluginCommand, { type: \"watch\" }>,\n\tdependencies: PluginCommandDependencies,\n): Promise<number> {\n\tconst cwd = dependencies.cwd?.() ?? process.cwd();\n\tconst from = resolve(cwd, command.dir ?? \".\");\n\ttry {\n\t\tconst project = findPluginProject(from);\n\t\tif (!project) {\n\t\t\tconst hub = findPluginHub(from);\n\t\t\tthrow new Error(\n\t\t\t\thub\n\t\t\t\t\t? `${from} indexes plugins but is not one itself. Run this from a plugin directory, or pass its path.`\n\t\t\t\t\t: `No plugin.json found in ${from} or any parent directory.`,\n\t\t\t);\n\t\t}\n\t\tconst result = command.stop\n\t\t\t? await dependencies.runAction(\"plugins.manage\", { operation: \"dev-watch-stop\", id: project.pluginId })\n\t\t\t: await dependencies.runAction(\"plugins.manage\", {\n\t\t\t\t\toperation: \"dev-watch\",\n\t\t\t\t\tid: project.pluginId,\n\t\t\t\t\tprojectDir: project.root,\n\t\t\t\t});\n\t\tdependencies.writeStdout(\n\t\t\tcommand.json\n\t\t\t\t? `${JSON.stringify({ ok: true, result })}\\n`\n\t\t\t\t: command.stop\n\t\t\t\t\t? `Stopped hot reload for ${project.pluginId}.\\n`\n\t\t\t\t\t: `Hot reload on for ${project.pluginId}. Vetta now loads it from ${project.root}.\\n`,\n\t\t);\n\t\treturn 0;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(\n\t\t\t\t`${JSON.stringify({ ok: false, error: { code: error instanceof ActionRpcError ? error.code : \"PLUGIN_WATCH_FAILED\", message } })}\\n`,\n\t\t\t);\n\t\t} else {\n\t\t\tdependencies.writeStderr(`${message}\\n`);\n\t\t}\n\t\tif (error instanceof ActionRpcError) return 4;\n\t\treturn isConnectionError(error) ? 3 : 5;\n\t}\n}\n\n/**\n * 卸载一个插件。省略 id 时按 cwd 推断,语义与 `add .` / `watch` 一致。\n *\n * 刻意不在这里做二次确认:宿主自己会为写操作弹审批,CLI 再问一遍只是噪音。系统插件由\n * 宿主拒绝,这里不重复判断——那份名单不该有第二个真相源。\n */\nasync function runUninstallCommand(\n\tcommand: Extract<PluginCommand, { type: \"uninstall\" }>,\n\tdependencies: PluginCommandDependencies,\n): Promise<number> {\n\tconst cwd = dependencies.cwd?.() ?? process.cwd();\n\ttry {\n\t\tlet pluginId = command.pluginId;\n\t\tif (!pluginId) {\n\t\t\tconst project = findPluginProject(cwd);\n\t\t\tif (!project) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No plugin.json found in ${cwd} or any parent directory. Pass the id: vetta-plugin-cli uninstall <plugin-id>`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpluginId = project.pluginId;\n\t\t}\n\t\tconst result = await dependencies.runAction(\"plugins.manage\", { operation: \"uninstall\", id: pluginId });\n\t\tdependencies.writeStdout(\n\t\t\tcommand.json ? `${JSON.stringify({ ok: true, result })}\\n` : `Uninstalled ${pluginId}.\\n`,\n\t\t);\n\t\treturn 0;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(\n\t\t\t\t`${JSON.stringify({ ok: false, error: { code: error instanceof ActionRpcError ? error.code : \"PLUGIN_UNINSTALL_FAILED\", message } })}\\n`,\n\t\t\t);\n\t\t} else {\n\t\t\tdependencies.writeStderr(`${message}\\n`);\n\t\t}\n\t\tif (error instanceof ActionRpcError) return 4;\n\t\treturn isConnectionError(error) ? 3 : 5;\n\t}\n}\n\n/**\n * 对账能力市场索引。定位靠向上找 `.vetta/marketplace.json`,因此在仓库任何位置都能跑。\n *\n * `--check` 只报不写并以非零退出,给 CI 用:索引漂移的三种后果里,两种不在作者机器上复现,\n * 一种压根不报错,光靠人自觉看不住。\n */\nfunction runSyncCommand(\n\tcommand: Extract<PluginCommand, { type: \"sync\" }>,\n\tdependencies: PluginCommandDependencies,\n): number {\n\tconst cwd = dependencies.cwd?.() ?? process.cwd();\n\tconst hub = findPluginHub(cwd);\n\tif (!hub) {\n\t\tconst message = `No .vetta/marketplace.json found in ${cwd} or any parent directory. sync is for marketplace repositories.\\n`;\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(`${JSON.stringify({ ok: false, error: { code: \"HUB_NOT_FOUND\", message: message.trim() } })}\\n`);\n\t\t} else {\n\t\t\tdependencies.writeStderr(message);\n\t\t}\n\t\treturn 6;\n\t}\n\ttry {\n\t\tconst result = syncMarketplaceIndex({ hubRoot: hub.root, manifestPath: hub.manifestPath, apply: !command.check });\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(`${JSON.stringify({ ok: result.problems.length === 0, ...result })}\\n`);\n\t\t} else {\n\t\t\tdependencies.writeStdout(formatSyncReport(result, command.check));\n\t\t}\n\t\tif (result.problems.length > 0) return 7;\n\t\t// --check 的职责就是「有漂移就红」,否则 CI 拦不住任何东西。\n\t\treturn command.check && result.changes.length > 0 ? 7 : 0;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(`${JSON.stringify({ ok: false, error: { code: \"SYNC_FAILED\", message } })}\\n`);\n\t\t} else {\n\t\t\tdependencies.writeStderr(`${message}\\n`);\n\t\t}\n\t\treturn 5;\n\t}\n}\n\nfunction formatSyncReport(result: ReturnType<typeof syncMarketplaceIndex>, check: boolean): string {\n\tconst lines: string[] = [];\n\tfor (const change of result.changes) {\n\t\tlines.push(` ${change.slug}: ${change.field} ${JSON.stringify(change.from)} -> ${JSON.stringify(change.to)}`);\n\t}\n\tif (lines.length > 0) {\n\t\tlines.unshift(check ? \"Index is out of date:\" : \"Updated the index:\");\n\t}\n\tif (result.problems.length > 0) {\n\t\tlines.push(\"Problems:\");\n\t\tfor (const problem of result.problems) lines.push(` ${problem.slug}: ${problem.message}`);\n\t}\n\tif (result.unlisted.length > 0) {\n\t\tlines.push(\"Ability directories not listed in the index (add them by hand when ready to publish):\");\n\t\tfor (const dir of result.unlisted) lines.push(` ${dir}`);\n\t}\n\tif (lines.length === 0) return \"Index is in sync.\\n\";\n\tif (check && result.changes.length > 0) lines.push(\"Run `vetta-plugin-cli sync` to apply.\");\n\treturn `${lines.join(\"\\n\")}\\n`;\n}\n\n/** 生成一个合规的能力市场仓库骨架,连同仓库级 AGENTS.md 与对账用的 CI。 */\nfunction runInitHubCommand(\n\tcommand: Extract<PluginCommand, { type: \"init-hub\" }>,\n\tdependencies: PluginCommandDependencies,\n): number {\n\tconst cwd = dependencies.cwd?.() ?? process.cwd();\n\ttry {\n\t\tconst result = initHubRepository({\n\t\t\ttargetDir: resolve(cwd, command.targetDir ?? command.name),\n\t\t\tname: command.name,\n\t\t\trepository: command.repository,\n\t\t\tminAppVersion: command.minAppVersion,\n\t\t});\n\t\tdependencies.writeStdout(\n\t\t\tcommand.json\n\t\t\t\t? `${JSON.stringify({ ok: true, ...result })}\\n`\n\t\t\t\t: [\n\t\t\t\t\t\t`Created marketplace ${result.name} at ${result.root}`,\n\t\t\t\t\t\t\"Add an ability: npx vetta-plugin-cli init --id <slug> --name \\\"<Display>\\\" abilities/plugins/<slug>\",\n\t\t\t\t\t\t\"Then list it in .vetta/marketplace.json and run: npx vetta-plugin-cli sync\",\n\t\t\t\t\t\t\"The working agreement for agents is in AGENTS.md.\",\n\t\t\t\t\t].join(\"\\n\") + \"\\n\",\n\t\t);\n\t\treturn 0;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (command.json) {\n\t\t\tdependencies.writeStdout(`${JSON.stringify({ ok: false, error: { code: \"HUB_INIT_FAILED\", message } })}\\n`);\n\t\t} else {\n\t\t\tdependencies.writeStderr(`${message}\\n`);\n\t\t}\n\t\treturn 5;\n\t}\n}\n\nexport async function runPluginCli(argv: string[]): Promise<number> {\n\tif (argv.length === 0 || argv[0] === \"-h\" || argv[0] === \"--help\") {\n\t\treturn runPluginAddCommand({ type: \"help\" });\n\t}\n\tconst command = parsePluginAddCommand(argv) ?? parsePluginReloadCommand(argv) ?? parsePluginDocsCommand(argv) ?? parsePluginInitCommand(argv) ?? parsePluginWatchCommand(argv) ?? parsePluginUninstallCommand(argv) ?? parsePluginSyncCommand(argv);\n\tif (!command) {\n\t\tprocess.stderr.write(`Unknown command: ${argv[0]}\\n`);\n\t\treturn 2;\n\t}\n\treturn runPluginCommand(command);\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 能力市场仓库的骨架。
|
|
3
|
+
*
|
|
4
|
+
* 手写一个合规的 hub 成本不低:`.vetta/marketplace.json` 的必填字段、目录约定、以及那几条
|
|
5
|
+
* 只在别人机器上复现的发布约束,都得先读一遍文档才知道。这里把它变成一条命令。
|
|
6
|
+
*
|
|
7
|
+
* 仓库级 `AGENTS.md` 是关键的一半:落在仓库根的 Agent 需要知道「能力目录才是开发单位、
|
|
8
|
+
* 索引由 sync 对账」,否则它会去手改 marketplace.json。
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderHubAgentsGuide(input: {
|
|
11
|
+
name: string;
|
|
12
|
+
}): string;
|
|
13
|
+
export declare function renderHubWorkflow(): string;
|
|
14
|
+
export declare function renderHubReadme(input: {
|
|
15
|
+
name: string;
|
|
16
|
+
repository: string;
|
|
17
|
+
}): string;
|
|
18
|
+
//# sourceMappingURL=hub-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-template.d.ts","sourceRoot":"","sources":["../src/hub-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CA4DpE;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAmB1C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAYnF","sourcesContent":["/**\n * 能力市场仓库的骨架。\n *\n * 手写一个合规的 hub 成本不低:`.vetta/marketplace.json` 的必填字段、目录约定、以及那几条\n * 只在别人机器上复现的发布约束,都得先读一遍文档才知道。这里把它变成一条命令。\n *\n * 仓库级 `AGENTS.md` 是关键的一半:落在仓库根的 Agent 需要知道「能力目录才是开发单位、\n * 索引由 sync 对账」,否则它会去手改 marketplace.json。\n */\n\nexport function renderHubAgentsGuide(input: { name: string }): string {\n\treturn `# ${input.name}\n\nVetta 能力市场仓库。本仓库索引若干**能力**(plugin / mcp / skill / scene / bundle),\n每个能力是 \\`abilities/\\` 下的一个自包含目录。\n\n## 开发时站在能力目录里,不是站在这里\n\n\\`\\`\\`bash\ncd abilities/plugins/<slug> # ← 开发单位是这个目录\nnpx vetta-plugin-cli docs # 手册(随该目录装的 SDK 版本)\nnpm run install:vetta # 装进正在运行的 Vetta\nnpx vetta-plugin-cli watch # 热更新\n\\`\\`\\`\n\n每个插件目录自带 \\`AGENTS.md\\`,里面有该读哪些手册、以及不可违反的几条。**先 \\`cd\\` 进去再动手**:\n所有开发命令都作用于「最近的那个能力目录」,站在仓库根上它们不知道你指的是哪一个。\n\n新建一个插件:\n\n\\`\\`\\`bash\nnpx vetta-plugin-cli init --id <slug> --name \"<Display Name>\" abilities/plugins/<slug>\n\\`\\`\\`\n\n它只创建目录,**不会**动索引——新能力什么时候上架是人的决定。想好了再按下面的方式登记。\n\n## 索引由工具对账,不要手改派生字段\n\n\\`.vetta/marketplace.json\\` 里能力的 \\`version\\`、\\`config.api_version\\`、\\`config.permissions\\`、\n\\`config.commands\\` 全都是从能力包推导出来的。改完能力后:\n\n\\`\\`\\`bash\nnpx vetta-plugin-cli sync # 回填派生字段,并推进 marketplaceVersion\nnpx vetta-plugin-cli sync --check # 只报不写,非零退出(CI 用)\n\\`\\`\\`\n\n要**手写**的只有身份与展示:\\`slug\\`、\\`name\\`、\\`description\\`、\\`source.path\\`、\\`category\\`、\\`tags\\`、\n\\`detail\\`。新能力上架时手动加一条这样的条目,其余字段交给 \\`sync\\`。\n\n三条容易踩的约束,\\`sync --check\\` 会替你守住:\n\n| 约束 | 漏了会怎样 |\n| --- | --- |\n| 条目 \\`version\\` 必须 == 能力包里的版本 | 宿主同步**直接失败** |\n| \\`plugin.json\\` 的 \\`entry\\` / \\`styles\\` 必须在已发布目录里真实存在 | 本地能装,市场上装不了 |\n| 改了任何内容必须换 \\`marketplaceVersion\\` | 客户端不报错、也不更新,用户永远收不到 |\n\n第三条最阴险——它不报错。\n\n## 为什么插件目录里要提交 \\`dist/\\`\n\n客户端按 \\`source.path\\` 直接读目录并安装,**它不会替你构建**。所以构建产物必须在仓库里。\n脚手架生成的插件目录因此不忽略 \\`dist/\\`。\n\n## 发布\n\n1. 改能力 → 在能力目录里 build\n2. 回仓库根 \\`npx vetta-plugin-cli sync\\`\n3. 提交并推送;客户端在 \\`marketplaceVersion\\` 变化时拉新快照\n`;\n}\n\nexport function renderHubWorkflow(): string {\n\treturn `name: marketplace\n\non:\n pull_request:\n push:\n branches: [main]\n\njobs:\n index:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n # 索引与能力包漂移的后果有两种不在作者机器上复现、一种压根不报错,所以在这里拦。\n - run: npx --yes @vetta-org/plugin-cli sync --check\n`;\n}\n\nexport function renderHubReadme(input: { name: string; repository: string }): string {\n\treturn `# ${input.name}\n\nA Vetta ability marketplace. Add it in Vetta Desktop under **能力市场 → 添加来源**:\n\n\\`\\`\\`\n${input.repository}\n\\`\\`\\`\n\nAbilities live under \\`abilities/\\`. The index is \\`.vetta/marketplace.json\\`; its derived fields are\nmaintained by \\`npx @vetta-org/plugin-cli sync\\`. See \\`AGENTS.md\\` for the working agreement.\n`;\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { parsePluginAddCommand, parsePluginDocsCommand, parsePluginInitCommand, parsePluginSyncCommand, parsePluginUninstallCommand, parsePluginWatchCommand, parsePluginReloadCommand, type PluginAddCommand, type PluginAddCommandDependencies, type PluginCommand, type PluginCommandDependencies, type PluginDocsCommand, type PluginInitCommand, type PluginSyncCommand, type PluginUninstallCommand, type PluginWatchCommand, type PluginReloadCommand, runPluginAddCommand, runPluginCommand, runPluginCli, } from "./command.js";
|
|
2
|
+
export { DEFAULT_SDK_RANGE, DEFAULT_VITE_RANGE, initHubRepository, initPluginProject, type InitHubInput, type InitHubResult, type InitPluginInput, type InitPluginResult, } from "./init.js";
|
|
3
|
+
export { renderAgentsGuide } from "./agents-template.js";
|
|
4
|
+
export { renderHubAgentsGuide, renderHubReadme, renderHubWorkflow } from "./hub-template.js";
|
|
5
|
+
export { type SyncChange, type SyncChangeKind, type SyncInput, type SyncProblem, type SyncResult, syncMarketplaceIndex, } from "./sync.js";
|
|
6
|
+
export { findPluginHub, findPluginProject, type PluginHub, type PluginProject, readManualSdkVersion, resolveManualDir, } from "./workspace.js";
|
|
7
|
+
export { type NpmPackResult, type NpmPackRunner, type NpmPluginPackageManifest, type ResolvedNpmPluginArchive, resolveNpmPluginArchive, runNpmPack, } from "./npm-package.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,2BAA2B,EAC3B,uBAAuB,EACvB,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,aAAa,EAClB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACrB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EACN,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,oBAAoB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,aAAa,EACb,iBAAiB,EACjB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,uBAAuB,EACvB,UAAU,GACV,MAAM,kBAAkB,CAAC","sourcesContent":["export {\n\tparsePluginAddCommand,\n\tparsePluginDocsCommand,\n\tparsePluginInitCommand,\n\tparsePluginSyncCommand,\n\tparsePluginUninstallCommand,\n\tparsePluginWatchCommand,\n\tparsePluginReloadCommand,\n\ttype PluginAddCommand,\n\ttype PluginAddCommandDependencies,\n\ttype PluginCommand,\n\ttype PluginCommandDependencies,\n\ttype PluginDocsCommand,\n\ttype PluginInitCommand,\n\ttype PluginSyncCommand,\n\ttype PluginUninstallCommand,\n\ttype PluginWatchCommand,\n\ttype PluginReloadCommand,\n\trunPluginAddCommand,\n\trunPluginCommand,\n\trunPluginCli,\n} from \"./command.js\";\nexport {\n\tDEFAULT_SDK_RANGE,\n\tDEFAULT_VITE_RANGE,\n\tinitHubRepository,\n\tinitPluginProject,\n\ttype InitHubInput,\n\ttype InitHubResult,\n\ttype InitPluginInput,\n\ttype InitPluginResult,\n} from \"./init.js\";\nexport { renderAgentsGuide } from \"./agents-template.js\";\nexport { renderHubAgentsGuide, renderHubReadme, renderHubWorkflow } from \"./hub-template.js\";\nexport {\n\ttype SyncChange,\n\ttype SyncChangeKind,\n\ttype SyncInput,\n\ttype SyncProblem,\n\ttype SyncResult,\n\tsyncMarketplaceIndex,\n} from \"./sync.js\";\nexport {\n\tfindPluginHub,\n\tfindPluginProject,\n\ttype PluginHub,\n\ttype PluginProject,\n\treadManualSdkVersion,\n\tresolveManualDir,\n} from \"./workspace.js\";\nexport {\n\ttype NpmPackResult,\n\ttype NpmPackRunner,\n\ttype NpmPluginPackageManifest,\n\ttype ResolvedNpmPluginArchive,\n\tresolveNpmPluginArchive,\n\trunNpmPack,\n} from \"./npm-package.js\";\n"]}
|