@klingai/plugin-global 0.0.0-stage → 0.0.2-beta
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/.claude-plugin/plugin.json +20 -0
- package/.codex-plugin/plugin.json +40 -0
- package/.mcp.json +13 -0
- package/.workbuddy-plugin/plugin.json +20 -0
- package/README.cn.md +48 -0
- package/README.global.md +48 -0
- package/README.md +47 -2
- package/archives/claude-skill.zip +0 -0
- package/assets/kling-logo.png +0 -0
- package/bin/plugin.mjs +179 -0
- package/bin/postinstall.mjs +16 -0
- package/dist/cli.js +22255 -0
- package/lib/install.mjs +910 -0
- package/lib/region-data.mjs +48 -0
- package/lib/region.mjs +16 -0
- package/lib/uninstall.mjs +236 -0
- package/mcp.json +24 -0
- package/package.json +67 -4
- package/plugin.json +44 -0
- package/skills/kling-ai-plugin/SKILL.md +154 -0
- package/skills/kling-ai-plugin/agents/openai.yaml +6 -0
- package/skills/kling-ai-plugin/api-examples.md +328 -0
- package/skills/kling-ai-plugin/dist/cli.mjs +22255 -0
- package/skills/kling-ai-plugin/package.json +9 -0
- package/skills/kling-ai-plugin/reference.md +90 -0
- package/skills/kling-ai-plugin/region.md +9 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const PLUGIN_REGIONS = Object.freeze({
|
|
2
|
+
"cn": {
|
|
3
|
+
"id": "cn",
|
|
4
|
+
"packageName": "@klingai/plugin-cn",
|
|
5
|
+
"baseUrl": "https://klingai.com",
|
|
6
|
+
"mcpUrl": "https://klingai.com/mcp/plugin",
|
|
7
|
+
"marketplaceName": "kling-internal",
|
|
8
|
+
"displayName": "可灵 AI",
|
|
9
|
+
"description": "Kling AI image and video creation with Kling 4.0, reusable subjects, and motion control",
|
|
10
|
+
"manifestDescription": "使用可灵 AI 生成和编辑图片、创作可灵 4.0 视频,支持关键帧、全能参考、角色主体与动作控制。",
|
|
11
|
+
"hostDescription": "使用可灵 AI 生成和编辑图片、创作可灵 4.0 视频,支持关键帧、全能参考、角色主体与动作控制。",
|
|
12
|
+
"shortDescription": "一句话,让灵感从想法变成大片",
|
|
13
|
+
"longDescription": "使用可灵 AI 生成和编辑图片,以可灵 4.0、4.0 Flash 等可用模型创作视频,支持关键帧、全能参考、角色主体与动作控制,并管理素材、任务和灵感值。",
|
|
14
|
+
"defaultPrompts": [
|
|
15
|
+
"生成一张电影感图片:一只小萨摩耶在开满野花的草地上快乐奔跑,阳光逆光,毛发细节清晰",
|
|
16
|
+
"使用可灵 4.0 生成一段 5 秒电影感视频:机甲战士从空中落地,碎石与尘土飞散,镜头快速推进",
|
|
17
|
+
"查询我的可灵任务状态,只查询,不创建新的生成任务"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"registry": "https://registry.npmjs.org/",
|
|
22
|
+
"tag": "latest"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"global": {
|
|
26
|
+
"id": "global",
|
|
27
|
+
"packageName": "@klingai/plugin-global",
|
|
28
|
+
"baseUrl": "https://kling.ai",
|
|
29
|
+
"mcpUrl": "https://kling.ai/mcp/plugin",
|
|
30
|
+
"marketplaceName": "kling-global",
|
|
31
|
+
"displayName": "Kling AI",
|
|
32
|
+
"description": "Kling AI image and video creation with Kling 4.0, reusable subjects, and motion control",
|
|
33
|
+
"manifestDescription": "Create and edit images and Kling 4.0 videos with keyframes, Omni-Ref, reusable subjects, and motion control.",
|
|
34
|
+
"hostDescription": "Create and edit images and Kling 4.0 videos with keyframes, Omni-Ref, reusable subjects, and motion control.",
|
|
35
|
+
"shortDescription": "Turn ideas into cinematic art",
|
|
36
|
+
"longDescription": "Create and edit images and videos with available Kling 4.0 and 4.0 Flash models, keyframes, Omni-Ref, reusable subjects, and motion control. Manage assets, tasks, and credits.",
|
|
37
|
+
"defaultPrompts": [
|
|
38
|
+
"Generate a cinematic image of a Samoyed running through a wildflower meadow in warm backlight with detailed fur",
|
|
39
|
+
"Use Kling 4.0 to generate a 5-second cinematic video of a mech warrior landing as rocks and dust scatter and the camera pushes in",
|
|
40
|
+
"Check the status of my Kling AI tasks without creating a new generation"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"registry": "https://registry.npmjs.org/",
|
|
45
|
+
"tag": "latest"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
package/lib/region.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PLUGIN_REGIONS } from './region-data.mjs';
|
|
2
|
+
|
|
3
|
+
export { PLUGIN_REGIONS };
|
|
4
|
+
|
|
5
|
+
export function pluginRegion(id) {
|
|
6
|
+
const region = PLUGIN_REGIONS[id];
|
|
7
|
+
if (!region) throw new Error(`Unknown plugin region: ${id}`);
|
|
8
|
+
return region;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function pluginRegionForPackage(packageName) {
|
|
12
|
+
if (packageName === '@klingai/plugin') return PLUGIN_REGIONS.cn;
|
|
13
|
+
const region = Object.values(PLUGIN_REGIONS).find((item) => item.packageName === packageName);
|
|
14
|
+
if (!region) throw new Error(`Unsupported plugin package name: ${packageName}`);
|
|
15
|
+
return region;
|
|
16
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, lstatSync, readFileSync, readlinkSync, rmSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
5
|
+
import {
|
|
6
|
+
ALL_SKILL_HOST_PATHS, LEGACY_PLUGIN_NAME, LEGACY_SKILL_NAMES, PLUGIN_NAME,
|
|
7
|
+
SKILL_NAMES, isManagedSkill, readInstallState, removeExposedCommand, uninstallSkillDirectory,
|
|
8
|
+
} from './install.mjs';
|
|
9
|
+
import { PLUGIN_REGIONS } from './region.mjs';
|
|
10
|
+
|
|
11
|
+
const PACKAGE_NAMES = ['@klingai/plugin', ...Object.values(PLUGIN_REGIONS).map((item) => item.packageName)];
|
|
12
|
+
|
|
13
|
+
function readJsonFile(path) {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
16
|
+
} catch {
|
|
17
|
+
throw new Error(`Cannot read JSON configuration: ${path}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function manifest(path) {
|
|
22
|
+
const file = join(path, 'package.json');
|
|
23
|
+
return existsSync(file) ? readJsonFile(file) : undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function npm(args, options) {
|
|
27
|
+
if (options.runNpm) return options.runNpm(args);
|
|
28
|
+
const cli = process.env.npm_execpath && join(dirname(process.env.npm_execpath), 'npm-cli.js');
|
|
29
|
+
return cli && existsSync(cli)
|
|
30
|
+
? spawnSync(process.execPath, [cli, ...args], { encoding: 'utf8' })
|
|
31
|
+
: spawnSync('npm', args, { encoding: 'utf8' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function nativeHomes(userHome, options) {
|
|
35
|
+
const env = options.userHome === undefined ? process.env : {};
|
|
36
|
+
return {
|
|
37
|
+
codex: resolve(options.codexHome ?? env.CODEX_HOME ?? join(userHome, '.codex')),
|
|
38
|
+
claude: resolve(options.claudeHome ?? env.CLAUDE_CONFIG_DIR ?? join(userHome, '.claude')),
|
|
39
|
+
pi: resolve(options.piHome ?? env.PI_CODING_AGENT_DIR ?? join(userHome, '.pi', 'agent')),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function nativeEnvironment(userHome, homes) {
|
|
44
|
+
return { ...process.env, HOME: userHome, CODEX_HOME: homes.codex, CLAUDE_CONFIG_DIR: homes.claude, PI_CODING_AGENT_DIR: homes.pi };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function nativeJson(command, args, userHome, homes, options) {
|
|
48
|
+
const result = options.captureNative
|
|
49
|
+
? options.captureNative(command, args)
|
|
50
|
+
: spawnSync(command, args, { encoding: 'utf8', cwd: options.cwd, env: nativeEnvironment(userHome, homes) });
|
|
51
|
+
if (result.status !== 0) throw new Error(`Cannot inspect ${command} registrations; no shared files were removed`);
|
|
52
|
+
try { return JSON.parse(result.stdout); } catch { throw new Error(`Invalid registration response from ${command}`); }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function nativeActions(userHome, homes, pluginRoot, options, verify = false) {
|
|
56
|
+
const actions = [];
|
|
57
|
+
const marketplaces = Object.values(PLUGIN_REGIONS).map((item) => item.marketplaceName);
|
|
58
|
+
const ids = marketplaces.flatMap((marketplace) => [PLUGIN_NAME, LEGACY_PLUGIN_NAME].map((name) => `${name}@${marketplace}`));
|
|
59
|
+
const codexConfig = join(homes.codex, 'config.toml');
|
|
60
|
+
const codex = existsSync(codexConfig) ? readFileSync(codexConfig, 'utf8') : '';
|
|
61
|
+
const codexCache = marketplaces.some((marketplace) => [PLUGIN_NAME, LEGACY_PLUGIN_NAME].some((name) => existsSync(join(homes.codex, 'plugins', 'cache', marketplace, name))));
|
|
62
|
+
if (codexCache || marketplaces.some((name) => codex.includes(name))) {
|
|
63
|
+
const inventory = nativeJson('codex', ['plugin', 'list', '--json'], userHome, homes, options);
|
|
64
|
+
const sources = nativeJson('codex', ['plugin', 'marketplace', 'list', '--json'], userHome, homes, options);
|
|
65
|
+
if (!Array.isArray(inventory.installed) || !Array.isArray(sources.marketplaces)) throw new Error('Cannot verify Codex registration inventory');
|
|
66
|
+
const installed = new Set(inventory.installed.filter((item) => item.installed).map((item) => item.pluginId));
|
|
67
|
+
// Codex itself parses/removes TOML keys, including quoted and dotted forms.
|
|
68
|
+
for (const id of ids) {
|
|
69
|
+
if (installed.has(id) || (!verify && codex.includes(id))) actions.push({ command: 'codex', args: ['plugin', 'remove', id, '--json'] });
|
|
70
|
+
}
|
|
71
|
+
for (const item of sources.marketplaces) {
|
|
72
|
+
if (marketplaces.includes(item.name)) actions.push({ command: 'codex', args: ['plugin', 'marketplace', 'remove', item.name] });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const claudeSettings = join(homes.claude, 'settings.json');
|
|
76
|
+
const claude = existsSync(claudeSettings) ? readJsonFile(claudeSettings).enabledPlugins ?? {} : {};
|
|
77
|
+
const marketplaceFile = join(homes.claude, 'plugins', 'known_marketplaces.json');
|
|
78
|
+
const claudeMarketplaces = existsSync(marketplaceFile) ? readJsonFile(marketplaceFile) : {};
|
|
79
|
+
const installedFile = join(homes.claude, 'plugins', 'installed_plugins.json');
|
|
80
|
+
const claudeInstalled = existsSync(installedFile) ? readJsonFile(installedFile).plugins ?? {} : {};
|
|
81
|
+
if (ids.some((id) => Object.hasOwn(claude, id) || Object.hasOwn(claudeInstalled, id)) || marketplaces.some((name) => Object.hasOwn(claudeMarketplaces, name))) {
|
|
82
|
+
const inventory = nativeJson('claude', ['plugin', 'list', '--json'], userHome, homes, options);
|
|
83
|
+
if (!Array.isArray(inventory)) throw new Error('Cannot verify Claude registration inventory');
|
|
84
|
+
for (const item of inventory) {
|
|
85
|
+
if (ids.includes(item.id) && (item.scope ?? 'user') === 'user') actions.push({ command: 'claude', args: ['plugin', 'uninstall', item.id, '--scope', 'user'] });
|
|
86
|
+
}
|
|
87
|
+
for (const marketplace of marketplaces) {
|
|
88
|
+
if (Object.hasOwn(claudeMarketplaces, marketplace)) actions.push({ command: 'claude', args: ['plugin', 'marketplace', 'remove', marketplace] });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const piSettings = join(homes.pi, 'settings.json');
|
|
92
|
+
if (existsSync(piSettings)) {
|
|
93
|
+
const packages = readJsonFile(piSettings).packages ?? [];
|
|
94
|
+
for (const item of packages) {
|
|
95
|
+
const source = typeof item === 'string' ? item : item.source;
|
|
96
|
+
const npmSource = PACKAGE_NAMES.some((name) => source === `npm:${name}` || source?.startsWith(`npm:${name}@`));
|
|
97
|
+
const localSource = typeof source === 'string' && resolve(homes.pi, source.startsWith('~/') ? join(userHome, source.slice(2)) : source) === pluginRoot;
|
|
98
|
+
if (localSource || npmSource) {
|
|
99
|
+
actions.push({ command: 'pi', args: ['remove', localSource ? pluginRoot : source] });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return actions;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function npmLinks(item) {
|
|
107
|
+
const bin = item.global ? (process.platform === 'win32' ? item.prefix : join(item.prefix, 'bin')) : join(item.prefix, 'node_modules', '.bin');
|
|
108
|
+
const entries = [
|
|
109
|
+
[item.name.split('/').at(-1), 'bin/plugin.mjs'], ['kling-agent-plugin', 'bin/plugin.mjs'],
|
|
110
|
+
['kling-ai-plugin', 'dist/cli.js'], ['kling', 'dist/cli.js'],
|
|
111
|
+
];
|
|
112
|
+
return entries.flatMap(([name, target]) => {
|
|
113
|
+
const path = join(bin, name);
|
|
114
|
+
return lstatSync(path, { throwIfNoEntry: false })?.isSymbolicLink()
|
|
115
|
+
&& resolve(dirname(path), readlinkSync(path)) === join(item.path, target) ? [path] : [];
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const DEPENDENCY_FIELDS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
|
|
120
|
+
function declared(prefix, name, options, cache = new Map()) {
|
|
121
|
+
if (!cache.has(prefix)) {
|
|
122
|
+
const project = manifest(prefix);
|
|
123
|
+
let projects = [project];
|
|
124
|
+
if (project?.workspaces) {
|
|
125
|
+
const result = npm(['pkg', 'get', ...DEPENDENCY_FIELDS, '--prefix', prefix, '--workspaces', '--include-workspace-root', '--json'], options);
|
|
126
|
+
if (result.status !== 0) throw new Error(`Cannot inspect workspace dependencies: ${prefix}`);
|
|
127
|
+
try { projects = Object.values(JSON.parse(result.stdout)); } catch { throw new Error(`Invalid workspace dependency response: ${prefix}`); }
|
|
128
|
+
}
|
|
129
|
+
cache.set(prefix, new Set(projects.flatMap((item) => DEPENDENCY_FIELDS.flatMap((field) => Object.keys(item?.[field] ?? {})))));
|
|
130
|
+
}
|
|
131
|
+
return cache.get(prefix).has(name);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function createUninstallPlan(options = {}) {
|
|
135
|
+
const userHome = resolve(options.userHome ?? homedir());
|
|
136
|
+
const klingHome = resolve(options.klingHome ?? process.env.KLING_HOME ?? join(userHome, '.kling'));
|
|
137
|
+
const base = join(klingHome, 'plugins', PLUGIN_NAME);
|
|
138
|
+
const pluginRoot = join(base, 'current');
|
|
139
|
+
const state = readInstallState(klingHome);
|
|
140
|
+
if (existsSync(pluginRoot) && !PACKAGE_NAMES.includes(manifest(pluginRoot)?.name)) {
|
|
141
|
+
throw new Error(`Refusing to remove an unmanaged package: ${pluginRoot}`);
|
|
142
|
+
}
|
|
143
|
+
const homes = nativeHomes(userHome, options);
|
|
144
|
+
const skillsRoots = [...new Set([
|
|
145
|
+
...Object.values(ALL_SKILL_HOST_PATHS).map((parts) => join(userHome, ...parts)),
|
|
146
|
+
join(homes.codex, 'skills'), join(homes.claude, 'skills'), join(homes.pi, 'skills'),
|
|
147
|
+
...state.skills,
|
|
148
|
+
])];
|
|
149
|
+
const npmPackages = new Map();
|
|
150
|
+
const declarations = new Map();
|
|
151
|
+
function add(path, global = false, prefix, recordedName) {
|
|
152
|
+
const data = manifest(path);
|
|
153
|
+
if (data && !PACKAGE_NAMES.includes(data.name)) return;
|
|
154
|
+
if (basename(dirname(dirname(path))) !== 'node_modules') return;
|
|
155
|
+
const parent = dirname(dirname(dirname(path)));
|
|
156
|
+
const expectedPrefix = global && basename(parent) === 'lib' ? dirname(parent) : parent;
|
|
157
|
+
if (prefix && resolve(prefix) !== expectedPrefix) throw new Error(`Invalid npm install prefix: ${path}`);
|
|
158
|
+
const name = data?.name ?? recordedName ?? PACKAGE_NAMES.find((name) => path.endsWith(join(...name.split('/'))));
|
|
159
|
+
if (!PACKAGE_NAMES.includes(name)) return;
|
|
160
|
+
const item = { name, path: resolve(path), prefix: expectedPrefix, global };
|
|
161
|
+
const cliName = name.replace('/plugin-', '/cli-');
|
|
162
|
+
const recordedCli = recordedName && !global && cliName !== name
|
|
163
|
+
&& manifest(join(expectedPrefix, 'node_modules', ...cliName.split('/')))?.name === cliName;
|
|
164
|
+
if (!data && (global || !declared(expectedPrefix, name, options, declarations)) && npmLinks(item).length === 0 && !recordedCli) return;
|
|
165
|
+
npmPackages.set(resolve(path), item);
|
|
166
|
+
}
|
|
167
|
+
for (const item of state.npm) add(item.path, item.global, item.prefix, item.name);
|
|
168
|
+
// Existing releases did not record npm installs. Find the current project,
|
|
169
|
+
// its ancestors, and npm's active global prefix without scanning user files.
|
|
170
|
+
for (let cwd = resolve(options.cwd ?? process.cwd()); ; cwd = dirname(cwd)) {
|
|
171
|
+
for (const name of PACKAGE_NAMES) add(join(cwd, 'node_modules', ...name.split('/')));
|
|
172
|
+
if (dirname(cwd) === cwd) break;
|
|
173
|
+
}
|
|
174
|
+
const global = npm(['root', '--global'], options);
|
|
175
|
+
if (global.status !== 0 || !global.stdout.trim()) throw new Error('Cannot locate global npm packages; no files were removed');
|
|
176
|
+
for (const name of PACKAGE_NAMES) add(join(global.stdout.trim(), ...name.split('/')), true);
|
|
177
|
+
return {
|
|
178
|
+
userHome, klingHome, base, pluginRoot, skillsRoots, nativeHomes: homes,
|
|
179
|
+
npmPackages: [...npmPackages.values()], nativeActions: nativeActions(userHome, homes, pluginRoot, options),
|
|
180
|
+
preserved: [join(klingHome, '.credentials'), join(klingHome, 'logs')],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function uninstallAll(options = {}) {
|
|
185
|
+
const plan = createUninstallPlan(options);
|
|
186
|
+
if (options.dryRun) return { dryRun: true, ...plan };
|
|
187
|
+
for (const action of plan.nativeActions) {
|
|
188
|
+
const result = options.runNative
|
|
189
|
+
? options.runNative(action.command, action.args)
|
|
190
|
+
: spawnSync(action.command, action.args, { encoding: 'utf8', cwd: options.cwd, env: nativeEnvironment(plan.userHome, plan.nativeHomes) });
|
|
191
|
+
if (result.status !== 0) throw new Error(`Unable to unregister ${action.command}; shared CLI and Skills were preserved`);
|
|
192
|
+
}
|
|
193
|
+
if (plan.nativeActions.length > 0 && nativeActions(plan.userHome, plan.nativeHomes, plan.pluginRoot, options, true).length > 0) {
|
|
194
|
+
throw new Error('Native registrations remain after uninstall; shared CLI and Skills were preserved');
|
|
195
|
+
}
|
|
196
|
+
// npm failures leave the durable installer and all Skills available for retry.
|
|
197
|
+
for (const item of plan.npmPackages) {
|
|
198
|
+
const args = ['uninstall', item.name, '--prefix', item.prefix, '--ignore-scripts', '--no-audit', '--no-fund'];
|
|
199
|
+
args.push(item.global ? '--global' : '--no-global');
|
|
200
|
+
if (!item.global && manifest(item.prefix)?.workspaces) args.push('--workspaces', '--include-workspace-root');
|
|
201
|
+
if (npm(args, options).status !== 0 || PACKAGE_NAMES.includes(manifest(item.path)?.name) || (!item.global && declared(item.prefix, item.name, options))) {
|
|
202
|
+
throw new Error(`npm uninstall failed: ${item.name} at ${item.prefix}; shared CLI and Skills were preserved`);
|
|
203
|
+
}
|
|
204
|
+
for (const path of npmLinks(item)) rmSync(path);
|
|
205
|
+
// A manually deleted workspace Plugin can leave its hoisted CLI behind.
|
|
206
|
+
// Let npm retain shared dependencies; never remove a declared standalone CLI.
|
|
207
|
+
const cliName = item.name.replace('/plugin-', '/cli-');
|
|
208
|
+
const cliPath = join(item.prefix, 'node_modules', ...cliName.split('/'));
|
|
209
|
+
if (!item.global && cliName !== item.name && manifest(cliPath)?.name === cliName && !declared(item.prefix, cliName, options)) {
|
|
210
|
+
const result = npm(['uninstall', cliName, '--prefix', item.prefix, '--no-global', '--no-save', '--ignore-scripts', '--no-audit', '--no-fund'], options);
|
|
211
|
+
if (result.status !== 0) throw new Error(`npm CLI dependency cleanup failed at ${item.prefix}; shared CLI and Skills were preserved`);
|
|
212
|
+
if (!existsSync(cliPath)) for (const path of npmLinks({ ...item, name: cliName, path: cliPath })) rmSync(path);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
for (const skillsRoot of plan.skillsRoots) {
|
|
216
|
+
uninstallSkillDirectory(skillsRoot, { userHome: plan.userHome, klingHome: plan.klingHome });
|
|
217
|
+
}
|
|
218
|
+
for (const skillsRoot of plan.skillsRoots) {
|
|
219
|
+
for (const name of [...SKILL_NAMES, ...LEGACY_SKILL_NAMES]) {
|
|
220
|
+
if (isManagedSkill(join(skillsRoot, name), plan.pluginRoot)) throw new Error(`Managed Skill remains: ${join(skillsRoot, name)}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const preservedExecutables = [];
|
|
224
|
+
for (const name of ['kling-plugin', 'kling-plugin.cmd', 'kling-ai-plugin', 'kling-ai-plugin.cmd', 'kling', 'kling.cmd']) {
|
|
225
|
+
const path = join(plan.klingHome, 'bin', name);
|
|
226
|
+
if (!existsSync(path)) continue;
|
|
227
|
+
if (/managed-by: @klingai\/plugin(?:-family)?/.test(readFileSync(path, 'utf8'))) rmSync(path);
|
|
228
|
+
else preservedExecutables.push(path);
|
|
229
|
+
}
|
|
230
|
+
removeExposedCommand(plan.klingHome, options);
|
|
231
|
+
rmSync(plan.base, { recursive: true, force: true });
|
|
232
|
+
return {
|
|
233
|
+
status: 'uninstalled', skillDirectories: plan.skillsRoots.length,
|
|
234
|
+
npmPackages: plan.npmPackages, preserved: plan.preserved, preservedExecutables,
|
|
235
|
+
};
|
|
236
|
+
}
|
package/mcp.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
|
3
|
+
"mcpServers": {
|
|
4
|
+
"kling-ai": {
|
|
5
|
+
"type": "streamable-http",
|
|
6
|
+
"url": "https://kling.ai/mcp/plugin",
|
|
7
|
+
"extensions": {
|
|
8
|
+
"com.openai": {
|
|
9
|
+
"auth": {
|
|
10
|
+
"type": "oauth",
|
|
11
|
+
"client": {
|
|
12
|
+
"mode": "dcr"
|
|
13
|
+
},
|
|
14
|
+
"baseScopes": [
|
|
15
|
+
"generation.create",
|
|
16
|
+
"generation.read",
|
|
17
|
+
"account.credit.read"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,69 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@klingai/plugin-global",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.0.2-beta",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Kling AI image and video creation with Kling 4.0, reusable subjects, and motion control",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"plugin-global": "bin/plugin.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
".mcp.json",
|
|
12
|
+
".claude-plugin",
|
|
13
|
+
".codex-plugin",
|
|
14
|
+
".workbuddy-plugin",
|
|
15
|
+
"archives",
|
|
16
|
+
"assets",
|
|
17
|
+
"bin",
|
|
18
|
+
"dist",
|
|
19
|
+
"lib",
|
|
20
|
+
"mcp.json",
|
|
21
|
+
"plugin.json",
|
|
22
|
+
"README.md",
|
|
23
|
+
"skills"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"postinstall": "node bin/postinstall.mjs"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public",
|
|
33
|
+
"registry": "https://registry.npmjs.org/",
|
|
34
|
+
"tag": "latest"
|
|
35
|
+
},
|
|
36
|
+
"license": "UNLICENSED",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"agent-plugin",
|
|
39
|
+
"kling-ai",
|
|
40
|
+
"kling-universal",
|
|
41
|
+
"skill",
|
|
42
|
+
"cli",
|
|
43
|
+
"codex",
|
|
44
|
+
"claude-code",
|
|
45
|
+
"cursor",
|
|
46
|
+
"gemini-cli",
|
|
47
|
+
"github-copilot",
|
|
48
|
+
"opencode",
|
|
49
|
+
"qwen-code",
|
|
50
|
+
"cline",
|
|
51
|
+
"roo-code",
|
|
52
|
+
"windsurf",
|
|
53
|
+
"agent-skills",
|
|
54
|
+
"kiro",
|
|
55
|
+
"junie",
|
|
56
|
+
"factory-droid",
|
|
57
|
+
"trae",
|
|
58
|
+
"qoder",
|
|
59
|
+
"codebuddy",
|
|
60
|
+
"kimi-code",
|
|
61
|
+
"pi",
|
|
62
|
+
"workbuddy"
|
|
63
|
+
],
|
|
64
|
+
"pi": {
|
|
65
|
+
"skills": [
|
|
66
|
+
"./skills"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "kling-universal",
|
|
4
|
+
"version": "0.0.2-beta",
|
|
5
|
+
"description": "Create and edit images and Kling 4.0 videos with keyframes, Omni-Ref, reusable subjects, and motion control.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Kling AI"
|
|
8
|
+
},
|
|
9
|
+
"license": "Proprietary",
|
|
10
|
+
"homepage": "https://kling.ai",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"kling",
|
|
13
|
+
"image generation",
|
|
14
|
+
"video generation",
|
|
15
|
+
"agent skill",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
18
|
+
"extensions": {
|
|
19
|
+
"com.openai": {
|
|
20
|
+
"interface": {
|
|
21
|
+
"displayName": "Kling AI",
|
|
22
|
+
"shortDescription": "Turn ideas into cinematic art",
|
|
23
|
+
"longDescription": "Create and edit images and videos with available Kling 4.0 and 4.0 Flash models, keyframes, Omni-Ref, reusable subjects, and motion control. Manage assets, tasks, and credits.",
|
|
24
|
+
"developerName": "Kling AI",
|
|
25
|
+
"category": "Creativity",
|
|
26
|
+
"capabilities": [
|
|
27
|
+
"Interactive",
|
|
28
|
+
"Read",
|
|
29
|
+
"Write"
|
|
30
|
+
],
|
|
31
|
+
"websiteURL": "https://kling.ai",
|
|
32
|
+
"defaultPrompt": [
|
|
33
|
+
"Generate a cinematic image of a Samoyed running through a wildflower meadow in warm backlight with detailed fur",
|
|
34
|
+
"Use Kling 4.0 to generate a 5-second cinematic video of a mech warrior landing as rocks and dust scatter and the camera pushes in",
|
|
35
|
+
"Check the status of my Kling AI tasks without creating a new generation"
|
|
36
|
+
],
|
|
37
|
+
"brandColor": "#000000",
|
|
38
|
+
"logo": "./assets/kling-logo.png",
|
|
39
|
+
"logoDark": "./assets/kling-logo.png",
|
|
40
|
+
"composerIcon": "./assets/kling-logo.png"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kling-ai-plugin
|
|
3
|
+
description: >-
|
|
4
|
+
使用可灵 AI 生成和编辑图片、创作视频、复用角色主体、控制动作,并管理素材、任务和灵感值。
|
|
5
|
+
支持可灵 4.0、4.0 Flash 等账号实时可用模型,涵盖文生图、图生图、文生视频、图生视频、关键帧和全能参考。
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Kling AI Plugin
|
|
9
|
+
|
|
10
|
+
本安装包向原生插件 Host 注册当前账号区域的 `kling-ai` 远端 MCP,并为不支持原生 MCP 的 Host 提供同区域 Kling Plugin。安装包名称、网站和固定 MCP 端点见 [region.md](region.md);该远端端点是工具、结果和 MCP App UI 的唯一运行时来源,npm 包不维护第二份 UI。业务调用必须通过 Host 暴露的 MCP 工具或 CLI 完成;不要自行请求网页接口、业务 HTTP API 或绕过 Host 直连远端 MCP 端点。
|
|
11
|
+
|
|
12
|
+
## 单一展示所有者(所有 Host,优先级最高)
|
|
13
|
+
|
|
14
|
+
同一次生成只能有一个“展示和刷新所有者”,否则会出现两张结果卡片或重复文字。
|
|
15
|
+
|
|
16
|
+
**1. MCP App widget 已挂载时,widget 是唯一所有者。** 走 MCP 通道,生成工具结果带 `_meta.ui.resourceUri` 且 Host 实现 MCP Apps iframe(卡片实际显示)时视为已挂载。**ChatGPT 例外:一律按未挂载处理**(视频卡片曾出现空白,模型无法确认卡片是否真的显示),走第 2 条;宁可多一份文字链接,也不能让用户拿不到成品。其余已挂载情形:
|
|
17
|
+
- 不为同一次提交调用 `query_tasks`、`query_result`,也不用 `--poll`;widget 在内部查询并原位刷新。
|
|
18
|
+
- 不再追加第二张卡片、缩略图、重复预览或重复的结果链接;只补一句简短状态(已提交/处理中),并把 `generationId` 显示为任务编号。
|
|
19
|
+
- 用户之后明确要求“查看结果/进度”时,才对已有任务查询一次,并按「成品交付」给出链接。
|
|
20
|
+
|
|
21
|
+
**2. widget 未挂载时,才由文字结果承担展示。** CLI 通道、Host 不支持 MCP Apps、ChatGPT、卡片空白或加载失败时视为未挂载:按下文轮询原任务,并按「成品交付」提供链接,不因此重新生成或换通道。
|
|
22
|
+
|
|
23
|
+
**3. 通道唯一。** 一次任务只用一条通道,不并行走 CLI 或其他连接器,避免重复生成与重复扣费。额度预检(`query_membership_and_credits` / `account`)是只读查询,不产生展示,可照常执行。
|
|
24
|
+
|
|
25
|
+
## 选择执行通道
|
|
26
|
+
|
|
27
|
+
- Host 已启用本插件的 `kling-ai` MCP Server:优先调用该 Server 暴露的 MCP 工具。若 Host 实现 MCP Apps iframe,则根据工具的 `_meta.ui.resourceUri` 读取 `resources/read` 返回的官方 MCP App;否则展示同一工具的文本或结构化结果。
|
|
28
|
+
- Codex/ChatGPT 等 Host 安装或升级 MCP 清单后必须重启并新开会话;已有会话没有出现组件时,不要把 basic-host 截图当作 Host 验收。ChatGPT 云端 UI 还需要已注册的远端 MCP 插件/连接器,不能由本地 `npx` 安装替代。
|
|
29
|
+
- 用户明确要求 CLI,或 Host 没有可用的 MCP 工具:使用下方的内置 CLI。缺少 MCP Apps UI 渲染能力不等于缺少 MCP 工具能力。
|
|
30
|
+
- 运行时只保留一个 `kling-ai` MCP 身份;一次任务只选择一个通道,禁止对同一生成请求同时调用 CLI 和其他连接器(见「单一展示所有者」第 3 条)。
|
|
31
|
+
|
|
32
|
+
## 选择 CLI
|
|
33
|
+
|
|
34
|
+
插件自带对应区域的 CLI,持久化命令为 `kling-plugin`,固定使用 `/mcp/plugin`。必须选择下述插件持久化命令或 Skill 自带封装;PATH 中独立 CLI 的 `kling` 走 `/mcp`,不能代替插件封装。
|
|
35
|
+
|
|
36
|
+
按以下顺序选择命令,找到第一个可执行项后在本次任务中保持一致:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
test -x "${KLING_HOME:-$HOME/.kling}/bin/kling-plugin" && printf '%s\n' "${KLING_HOME:-$HOME/.kling}/bin/kling-plugin"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
记住选中的可执行文件路径。下文和示例文档中的 `kling-plugin` 只是命令占位符;实际调用必须替换为该路径(优先 `~/.kling/bin/kling-plugin` 的实际展开路径),不要假设安装器已把它加入 `PATH`,也不要误用其他区域的同名 CLI。
|
|
43
|
+
|
|
44
|
+
若持久化命令不可用(例如 Host 将技能复制到独立沙箱),使用本 Skill 自带的 [dist/cli.mjs](dist/cli.mjs):`node "<当前 Skill 实际目录>/dist/cli.mjs" <命令与参数>`。该文件是与安装包相同的区域 CLI,已内置全部依赖,单个文件即可运行;无需重新下载。以 Host 加载技能时给出的实际目录为准,不使用桌面用户的绝对路径。
|
|
45
|
+
|
|
46
|
+
若沙箱缺少 Node 或随 Skill 附带的 CLI 文件,停止并报告具体缺项。不要声称版本检查成功,也不要自动安装运行环境。普通本机安装损坏时,读取 [region.md](region.md) 并使用其中的 `Reinstall` 命令重新安装当前区域插件。不要把中国区 `@klingai/plugin-cn` 与海外区 `@klingai/plugin-global` 混装。
|
|
47
|
+
|
|
48
|
+
不要自动改装其他区域的 Plugin 或 CLI,也不要在运行时覆盖端点。
|
|
49
|
+
|
|
50
|
+
## 登录与能力发现
|
|
51
|
+
|
|
52
|
+
首次使用或登录失效时运行:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
kling-plugin login
|
|
56
|
+
kling-plugin who_am_i
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`login` 会打开浏览器完成 OAuth。不得读取浏览器 Cookie、让用户粘贴 token、请求 AK/SK,或打印凭据。
|
|
60
|
+
|
|
61
|
+
每个新会话先用 `who_am_i` 获取当前可用模型和参数。服务端能力不清楚时用 `kling-plugin tool_list`;单个命令参数用 `kling-plugin <command> --help`。
|
|
62
|
+
|
|
63
|
+
## 生成前检查
|
|
64
|
+
|
|
65
|
+
- 生成前读取实时 `tools/list` 和 `who_am_i`(CLI 使用 `tool_list` 与 `who_am_i`);同一会话已读取且没有不兼容迹象时可复用。`mcpVersion` 较新本身不阻断,也不要求新会话或重新登录;只有目标工具缺失、实时 schema 冲突或宿主明确提示工具列表过期时刷新声明,仍不兼容则报告并停止,保留当前需求与参数。
|
|
66
|
+
- 紧邻每次收费生成(包括多步骤任务的每一步及用户授权的重试)前,调用 `query_membership_and_credits`,CLI 使用 `account`。查询失败、工具不可用、无法确认余额、明确零余额、已知额度不足或返回的会员/模型资格被拒绝时停止,不提交。
|
|
67
|
+
- 资格仅按服务端明确返回的契约判断,不凭会员等级推断;未返回资格字段不等于无资格,也不证明权限。不得编造价格或把正余额当作足额保证,最终资格、额度和扣费由服务端校验。不得为绕过限制静默换模型、降低规格或重试,改变计费设置须先取得用户确认。
|
|
68
|
+
- 只读查询、上传和素材管理无需生成额度预检;保持原有计费授权与单次提交规则。
|
|
69
|
+
|
|
70
|
+
## 调用约束
|
|
71
|
+
|
|
72
|
+
- 生成调用必须显式指定 `model`,CLI 使用 `--model <名称>`;模型名以 `who_am_i` 为准。
|
|
73
|
+
- 生成会消耗灵感值;提交前确认 prompt、模型和素材,不要自行改参重投。
|
|
74
|
+
- 生成命令返回 `generationId` 后:MCP widget 已挂载时遵循「单一展示所有者」,不轮询;否则用 `kling-plugin query_tasks <generationId>` 轮询,或提交时加 `--poll [秒数]`。
|
|
75
|
+
- 完成后按下方「成品交付」展示预览和可点击的结果链接;需要无水印且响应提供时使用 `works[].urlWithoutWatermark`。
|
|
76
|
+
- 本地图片可直接传给图生类命令,CLI 会负责上传。
|
|
77
|
+
- 非交互环境缺少必填参数时应报错,不要等待 stdin。
|
|
78
|
+
|
|
79
|
+
常用命令:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
who_am_i
|
|
83
|
+
text_to_image
|
|
84
|
+
image_to_image
|
|
85
|
+
text_to_video
|
|
86
|
+
image_to_video
|
|
87
|
+
omni_ref_video
|
|
88
|
+
motion_control
|
|
89
|
+
motion_library_list
|
|
90
|
+
element_create / element_list / element_get / element_update / element_delete
|
|
91
|
+
query_tasks
|
|
92
|
+
file_upload
|
|
93
|
+
account
|
|
94
|
+
feedback
|
|
95
|
+
logout
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
完整参数和示例见 [reference.md](reference.md) 与 [api-examples.md](api-examples.md)。
|
|
99
|
+
|
|
100
|
+
## 成品交付(所有 Host,包括 ChatGPT)
|
|
101
|
+
|
|
102
|
+
本节适用于 widget 未挂载,或用户明确要求查看已有任务结果时;widget 已挂载的新提交以「单一展示所有者」为准,不重复追加链接或卡片。
|
|
103
|
+
|
|
104
|
+
- 从原任务 `query_tasks` 的真实返回中提取每个已完成作品的 `works[].url`。最终回复必须逐项提供普通 Markdown 文字链接,例如 `[观看视频](实际返回的作品 URL)` 或 `[查看图片](实际返回的作品 URL)`;无论原生组件或内嵌预览是否显示,都保留这些链接,不能只说「已生成」、只给 generationId 或只显示预览卡片。多作品按序编号。
|
|
105
|
+
- 图片可同时使用图片预览;视频优先使用 Host 支持的原生媒体展示或官方 MCP App 播放器。不要把视频 URL 写成以 `!` 开头的 Markdown 图片语法;封面只能标为封面,不能冒充可播放成品。普通文字链接不依赖 Host 的媒体预览能力。
|
|
106
|
+
- ChatGPT 等 Host 不支持预览、出现空白卡片或用户反馈加载失败时,明确提示「可通过下方链接打开查看」,再次提供原作品链接。不因展示失败重新生成、切换执行通道或额外扣费。
|
|
107
|
+
- 用户要求无水印且响应提供 `urlWithoutWatermark` 时链接该字段;否则使用 `url`。服务端若另有明确返回的可灵作品详情页/分享链接,可同时附上并标为「可灵作品页」;媒体直链标为「观看视频」或「查看图片」。不得根据 generationId、作品 ID、官网首页或区域域名猜测、拼接作品页地址,不把媒体直链称为作品页。
|
|
108
|
+
- 成功状态但没有可用作品 URL 时,明确说明「任务已完成,但未返回可用成品链接」,保留 generationId 便于后续只读查询;不得编造链接或宣称成品已可查看。部分完成时只交付实际返回的可用作品,并说明其余作品的实际状态。
|
|
109
|
+
|
|
110
|
+
## 图片生成与编辑
|
|
111
|
+
|
|
112
|
+
- 新建图片用 `text_to_image`;素材需要控制主体、结构、构图或风格时用 `image_to_image`。
|
|
113
|
+
- 使用 `who_am_i` 返回的实时能力指定 `model`(CLI 使用 `--model`);不使用已移除的模型简写。
|
|
114
|
+
- 参考图要明确各自的身份、结构、风格或构图作用,并保持重复 `--image` 参数的顺序。
|
|
115
|
+
- 编辑请求要同时写明“改什么”和“保留什么”;保留人物身份、商品几何、Logo 和用户提供的准确文字。
|
|
116
|
+
- 默认只生成一张,除非用户明确要求多张。提交前查询灵感值(MCP 使用 `query_membership_and_credits`,CLI 使用 `account`),付费请求最多提交一次,不得因质量问题自动重生成。
|
|
117
|
+
- 拿到 `generationId` 后,在 widget 未挂载时用 `query_tasks` 或 `--poll` 等待原任务(widget 已挂载时不轮询,见「单一展示所有者」),不另起新任务。
|
|
118
|
+
|
|
119
|
+
图片调用示例:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
kling-plugin text_to_image --model <who_am_i 返回的模型> "提示词"
|
|
123
|
+
kling-plugin image_to_image --model <who_am_i 返回的模型> --image ./reference.png "修改要求与保留项"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 视频生成与动作控制
|
|
127
|
+
|
|
128
|
+
- 无控制素材时用 `text_to_video`;动画化图片或使用首尾帧时用 `image_to_video`。
|
|
129
|
+
- 混合图片、视频或 Element 主体参考时用 `omni_ref_video`;用视频或已保存动作驱动主体时用 `motion_control`。
|
|
130
|
+
- 使用 `who_am_i` 返回的实时能力指定 `model`(CLI 使用 `--model`);不使用 `--omni` 或 `--flash` 简写。
|
|
131
|
+
- 提示词按时间写清开场状态、主体动作、镜头运动和结束状态。保留人物身份、服饰、商品几何和画面方向。
|
|
132
|
+
- 只在模型实时声明支持时传递时长、比例、音频和多镜头参数;不复用其他模型的参数。
|
|
133
|
+
- 提交前查询灵感值(MCP 使用 `query_membership_and_credits`,CLI 使用 `account`),付费请求最多提交一次,不自动换模型、改素材或重生成。
|
|
134
|
+
- 拿到 `generationId` 后,在 widget 未挂载时用 `query_tasks` 或 `--poll` 等待原任务(widget 已挂载时不轮询,见「单一展示所有者」);用户只要查状态时不创建生成。
|
|
135
|
+
|
|
136
|
+
### 可灵 4.0 与 4.0 Flash
|
|
137
|
+
|
|
138
|
+
- 先用 `who_am_i` 查询目标工具的模型清单;标准版为 `kling-video-v4_0`,Flash 为 `kling-video-v4_0_flash`。用户指定 4.0 时不自动换成 Flash;模型未出现在该工具清单时报告不可用,不换模型提交。
|
|
139
|
+
- 纯文本创作走 `text_to_video`;首帧、尾帧和中间关键帧走 `image_to_video`;图、视频、音频及主体混合参考走 `omni_ref_video`,不要把混合参考塞进关键帧入口。
|
|
140
|
+
- 标准版关键帧按实时声明使用 `first_image`、`tail_image`、`keyframe_*`。CLI 的中间帧使用 `--input <槽位名>=<url或路径>`,不可与 `--image` / `--tailImage` 混用。Flash 的 `image_to_video` 只接首帧,不接尾帧或主体;其全能参考能力以 `omni_ref_video` 的单独声明为准。
|
|
141
|
+
- 时长、分辨率、素材数量及音频支持均读取目标模型声明。全能参考按秒计费,提交前告知用户参考视频时长也计入总计费时长;保留参考原声时,`keepOriginalSound=true` 与 `enable_audio=false` 配套使用。
|
|
142
|
+
|
|
143
|
+
视频调用示例:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
kling-plugin text_to_video --model <who_am_i 返回的模型> "提示词"
|
|
147
|
+
kling-plugin image_to_video --model <who_am_i 返回的模型> --image ./first-frame.png "运动与镜头要求"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## MCP 与 UI 边界
|
|
151
|
+
|
|
152
|
+
这个 npm 插件提供 **CLI + Skill + MCP Apps 原生远端注册**。插件清单让 Host 直接连接远端 MCP 并获取 `tools/list`、`tools/call`、`resources/list`、`resources/read` 与 UI 元数据。只有实现 MCP Apps iframe 的 Host 才会渲染原生 UI;其他 Host 使用同一工具调用的文本/结构化结果或 CLI。ChatGPT 云端对话内 UI 需要另外注册远端 MCP 插件/连接器。本地 `npx` 安装不能给 Host 补充 UI 渲染能力。
|
|
153
|
+
|
|
154
|
+
技能目录名、frontmatter 和菜单名称统一为 `kling-ai-plugin`,不再为单个 Host 安装不同目录别名,避免 Cursor 等跨目录发现时产生两个同名入口。MCP Server 统一使用 `kling-ai`,正式环境不保留两套可灵 MCP。
|