@klingai/plugin-global 0.0.0-stage → 0.0.1-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.md +92 -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,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "kling-universal",
|
|
4
|
+
"displayName": "Kling AI",
|
|
5
|
+
"version": "0.0.1-beta",
|
|
6
|
+
"description": "Create and edit images and Kling 4.0 videos with keyframes, Omni-Ref, reusable subjects, and motion control.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Kling AI"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://kling.ai",
|
|
11
|
+
"license": "Proprietary",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"kling",
|
|
14
|
+
"image-generation",
|
|
15
|
+
"video-generation",
|
|
16
|
+
"claude-code"
|
|
17
|
+
],
|
|
18
|
+
"mcpServers": "./.mcp.json",
|
|
19
|
+
"skills": "./skills/"
|
|
20
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kling-universal",
|
|
3
|
+
"version": "0.0.1-beta",
|
|
4
|
+
"description": "Create and edit images and Kling 4.0 videos with keyframes, Omni-Ref, reusable subjects, and motion control.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Kling AI"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://kling.ai",
|
|
9
|
+
"license": "Proprietary",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"kling",
|
|
12
|
+
"image-generation",
|
|
13
|
+
"video-generation",
|
|
14
|
+
"codex"
|
|
15
|
+
],
|
|
16
|
+
"mcpServers": "./.mcp.json",
|
|
17
|
+
"skills": "./skills/",
|
|
18
|
+
"interface": {
|
|
19
|
+
"displayName": "Kling AI",
|
|
20
|
+
"shortDescription": "Turn ideas into cinematic art",
|
|
21
|
+
"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.",
|
|
22
|
+
"developerName": "Kling AI",
|
|
23
|
+
"category": "Creativity",
|
|
24
|
+
"capabilities": [
|
|
25
|
+
"Interactive",
|
|
26
|
+
"Read",
|
|
27
|
+
"Write"
|
|
28
|
+
],
|
|
29
|
+
"websiteURL": "https://kling.ai",
|
|
30
|
+
"defaultPrompt": [
|
|
31
|
+
"Generate a cinematic image of a Samoyed running through a wildflower meadow in warm backlight with detailed fur",
|
|
32
|
+
"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",
|
|
33
|
+
"Check the status of my Kling AI tasks without creating a new generation"
|
|
34
|
+
],
|
|
35
|
+
"brandColor": "#000000",
|
|
36
|
+
"logo": "./assets/kling-logo.png",
|
|
37
|
+
"logoDark": "./assets/kling-logo.png",
|
|
38
|
+
"composerIcon": "./assets/kling-logo.png"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/.mcp.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "kling-universal",
|
|
4
|
+
"displayName": "Kling AI",
|
|
5
|
+
"version": "0.0.1-beta",
|
|
6
|
+
"description": "Create and edit images and Kling 4.0 videos with keyframes, Omni-Ref, reusable subjects, and motion control.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Kling AI"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://kling.ai",
|
|
11
|
+
"license": "Proprietary",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"kling",
|
|
14
|
+
"image-generation",
|
|
15
|
+
"video-generation",
|
|
16
|
+
"workbuddy"
|
|
17
|
+
],
|
|
18
|
+
"mcpServers": "./.mcp.json",
|
|
19
|
+
"skills": "./skills/"
|
|
20
|
+
}
|
package/README.md
CHANGED
|
@@ -1,3 +1,93 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Kling AI 通用安装包 / Universal Plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
可灵 AI Agent 安装包按账号区域发布为两个独立 npm 包,二者共享同一份源码,但端点在发布时固定,运行时不可切换:
|
|
4
|
+
|
|
5
|
+
| 账号区域 | npm 包 | registry | 固定 MCP |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| 中国区 | `@klingai/plugin-cn` | `https://registry.npmjs.org` | `https://klingai.com/mcp/plugin` |
|
|
8
|
+
| 海外区 | `@klingai/plugin-global` | `https://registry.npmjs.org` | `https://kling.ai/mcp/plugin` |
|
|
9
|
+
|
|
10
|
+
支持可灵 4.0、4.0 Flash 等账号实时可用模型,覆盖图片生成与编辑、文生视频、图生视频、关键帧、全能参考、角色主体和动作控制。模型、参数和素材限制以 `who_am_i` 的实时声明为准。通用 Skill 的列表描述直接介绍创作用途,MCP 与 CLI 的执行通道由 Skill 正文选择。
|
|
11
|
+
|
|
12
|
+
Skill 的目录和 frontmatter 内部名称统一为 `kling-ai-plugin`,`agents/openai.yaml` 中的显示名为 **Kling AI Plugin**。所有默认技能目录与 frontmatter 名称一致,不安装不同目录别名,避免跨目录发现产生重复菜单。复制和软连接均携带同一区域的 `dist/cli.mjs`,独立沙箱可用 Node 直接运行,无需访问桌面用户的 `~/.kling`。
|
|
13
|
+
|
|
14
|
+
两个包都是分发和兼容层,不是第二个可灵产品或第二套 MCP App UI。每个包同时提供:
|
|
15
|
+
|
|
16
|
+
- 与所选账号区域一致的预构建 `kling` CLI;
|
|
17
|
+
- 原生 Host 使用的 `kling-ai` 远端 MCP 注册(域名带 `/mcp/plugin` 后缀);
|
|
18
|
+
- 可被 Agent 自动触发的一个 Skill:`kling-ai-plugin`,包含图片、视频和素材管理;
|
|
19
|
+
- Agent Plugins 1.0、Codex、Claude Code、WorkBuddy 兼容清单;
|
|
20
|
+
- 面向主流本地 Agent Host 与开放 Agent Skills 生态的 `npx` 安装器,并提供任意 Skill 目录兜底。
|
|
21
|
+
|
|
22
|
+
## 云端应用导入
|
|
23
|
+
|
|
24
|
+
Manus Studio 与 Grok Bot 的执行电脑不在桌面用户目录中,不能用本地 `--skills-dir` 自动安装到云端。当前 CN PRT 已实际导入并执行随 Skill 附带的 CLI,版本为 `0.4.13-beta.6`;不代表收费业务、Global 或 MCP App UI 已验收。
|
|
25
|
+
|
|
26
|
+
内网正式端点构建同时输出两份可导入 ZIP,路径见构建结果 JSON:
|
|
27
|
+
|
|
28
|
+
| 字段 | 导入目标 | ZIP 布局 |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `skillArchive` | Manus 等要求根目录技能文件的宿主 | 根目录包含 `SKILL.md`、对应区域 `dist/cli.mjs` 和版本元数据 |
|
|
31
|
+
| `claudeSkillArchive` | Claude 主界面的 `Customize → Skills` | 顶层 `kling-ai-plugin/` 文件夹,完整运行时位于 `lib/kling-cli/`,封装使用相对导入,避免 Claude 拒绝含 `@` 的 npm 路径 |
|
|
32
|
+
|
|
33
|
+
发布构建机需提供 `zip`;这是构建工具,不是安装包运行时依赖。已构建的 npm 包也可单独导出,Claude 无需再手动调整 ZIP:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
node plugin/scripts/export-skill.mjs /绝对路径/已构建的plugin /绝对路径/新建的skill.zip
|
|
37
|
+
node plugin/scripts/export-skill.mjs /绝对路径/已构建的plugin /绝对路径/新建的claude-skill.zip --format claude
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
普通 `pnpm build:plugin` 也会生成 `plugin/archives/claude-skill.zip`;发布的 npm 包携带同一路径 `archives/claude-skill.zip`,可直接上传到 Claude。该 ZIP 是构建时的区域 CLI 快照;重新执行本地安装器不会更新已导入 Claude 的账号侧副本,需要用新版 ZIP 替换。
|
|
41
|
+
|
|
42
|
+
- Manus Studio:更多 → 技能 → 创建 → 上传技能。导入 ZIP 后在“我的技能”启用自动生效,再新建会话。实际菜单仅一项 `kling-ai-plugin`,加载 `/home/ubuntu/skills/kling-ai-plugin/SKILL.md`,便携 CLI 已在应用终端执行成功。仅导入但未挂载时,菜单可见不等于可调用。
|
|
43
|
+
- Grok Bot:通过附件上传 ZIP,在 Bot 云端安装到其用户 Skill 目录;本次由应用确认目录为 `/home/box/agent-data/workflows/kling-ai-plugin`,frontmatter 保留 `kling-ai-plugin`。已检查正常目录索引加载及可见终端的版本/退出码;Grok 没有本地 slash 技能菜单,本轮不能宣称菜单名称验收通过。不要据此新增猜测的桌面目录。
|
|
44
|
+
- TRAE CN:本机应用未成功启动出窗口;Antigravity:账号地区资格限制。两者尚未完成应用调用验收,不新增默认安装目标。
|
|
45
|
+
|
|
46
|
+
## 安装
|
|
47
|
+
|
|
48
|
+
插件自带对应区域的 CLI:构建时由 `kling-cli/src` 打包为 `dist/cli.js`,区域端点在构建时写死,不依赖 `@klingai/cli-*` 包,也不随它们的 `latest` 变化。持久化命令为 `~/.kling/bin/kling-plugin`(私有 wrapper,不是 npm bin),固定使用 `/mcp/plugin`;Skill 另带同一份单文件 CLI(`dist/cli.mjs`),无需 `node_modules`。升级会移除本插件托管的旧 `kling`、`kling-ai-plugin` wrapper。安装后安装器会把 `kling-plugin` 链接到 PATH 中一个已有且可写的目录(优先 `~/.local/bin`、`/opt/homebrew/bin`、`/usr/local/bin`),不改 shell 配置、不覆盖非本包托管的同名文件;没有合适目录时会提示把 `~/.kling/bin` 加入 PATH,卸载时移除该链接。Skill 只使用插件持久化封装或自带封装;PATH 中独立 `kling` 不作为插件回退,独立 CLI 也不会被插件覆盖。
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx --yes --registry=https://registry.npmjs.org @klingai/plugin-cn@beta doctor --host codex
|
|
52
|
+
~/.kling/bin/kling-plugin --version
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Codex 的 `doctor` 会额外报告旧版 Kling 插件、marketplace 和远端 MCP 注册。Codex、Claude Code 的 `doctor` 还会核对 Host 实际启用的插件版本,Pi 会核对包级注册;目录型 Host 则核对通用 Skill 的托管标记与版本。普通 `install --host codex` 会自动执行同一套有备份的迁移;也可以预先查看或单独执行。
|
|
56
|
+
|
|
57
|
+
Claude Code 若启用了其他提供 `kling-ai` MCP 的插件,安装预检会阻止显式 `--host claude` 安装;批量安装会跳过 Claude 并继续其他 Host。`doctor` 也会报告该冲突,但不会擅自卸载不属于本包管理的插件。所有 Host 的 `doctor` 都不能证明账号已登录、当前会话已刷新或 AI 已调用 Skill。请在对应 Host 的技能列表中新开会话核对通用 Skill;无法登录的 Host 不能视为通过界面验收。
|
|
58
|
+
|
|
59
|
+
Cursor 同时发现共享目录和 Claude 用户技能目录。不同目录标识可以显示为相同的技能名称,形成两个入口;本包现在统一目录名与 frontmatter 为 `kling-ai-plugin`,升级时移除托管的 `kling-universal` 与 `Kling AI Plugin` 旧标识。旧版跨 Host 的实测记录见验收文档;此次改名后的菜单和调用显示需要重新验收。独立旧插件与非托管副本不会被清理;其他重复项须先确认实际来源,`doctor` 不替代菜单唯一性验收。
|
|
60
|
+
|
|
61
|
+
完整卸载使用一条命令,无需区分 npm 本地或全局安装:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx --yes --registry=https://registry.npmjs.org @klingai/plugin-cn@beta uninstall
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
开发验收可运行 `node plugin/scripts/verify-uninstall.mjs /绝对路径/package.tgz`,在隔离目录验证多个项目、全局 prefix、保留用户数据和重复卸载。`verify-host-discovery.mjs` 还会检查真实 Host 在安装后能发现 Skill、完整卸载后不再发现 Skill;追加旧版压缩包路径作为第二个参数,可验证从三个入口升级为一个入口以及旧入口不再被 Host 发现。
|
|
68
|
+
|
|
69
|
+
该命令移除本包托管的所有 Host Skill(含记录的自定义目录)、持久化 `kling` CLI、插件文件与找到的本地/全局 npm 包;凭据、日志、其他 Skill 和非本包的同名可执行文件保留。支持 npm workspace 的根项目与子项目依赖声明;即使 `node_modules` 或包目录已删除,也会清理声明及指向本包的失效 npm 命令链接。原生注册通过 Host 的插件清单核对,尊重 `CODEX_HOME`、`CLAUDE_CONFIG_DIR`、`PI_CODING_AGENT_DIR`,注销后再次检查。可先使用 `uninstall --dry-run` 查看计划。失败会非零退出并保留持久化安装器与记录,方便重试,不会报告“已完整卸载”。
|
|
70
|
+
|
|
71
|
+
从 0.4.11 起,安装器记录 npm 包与 Skill 目录,支持清理多个项目或自定义全局 prefix 的安装。对于没有记录的旧版本,请在原 npm 安装项目目录执行;命令会检查当前目录的父级项目及 npm 当前全局位置,不扫描整块磁盘。npx 下载缓存由 npm 管理,不作为已安装插件清理。
|
|
72
|
+
|
|
73
|
+
显式指定 Host 仍只卸载对应集成,不移除 npm 包;卸载不会删除 `~/.kling/.credentials` 或历史日志:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx --yes --registry=https://registry.npmjs.org @klingai/plugin-cn@beta uninstall --host codex
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## MCP 与 Skill
|
|
80
|
+
|
|
81
|
+
每个区域包只做两件事:
|
|
82
|
+
|
|
83
|
+
1. **原生远端 MCP 注册**:插件清单中的 `kling-ai` 直接连接该区域固定的、带 `plugin` 后缀的 Streamable HTTP MCP(中国区 `https://klingai.com/mcp/plugin`,海外区 `https://kling.ai/mcp/plugin`)。npm 包不复制、重写或发布 HTML,也不提供 stdio Bridge。
|
|
84
|
+
2. **给各 Host 安装 Skill**:通用 Skill 的目录名和 frontmatter 统一为 `kling-ai-plugin`,用户显示名为 `Kling AI Plugin`;内部插件注册 ID 为 `kling-universal`。
|
|
85
|
+
|
|
86
|
+
UI 是否出现由 Host 决定,npm/CLI 不能替 Host 增加 iframe 渲染能力。一次生成只能选择原生 MCP 或 CLI 其中一条通道,避免重复扣费。
|
|
87
|
+
|
|
88
|
+
## 运行限制
|
|
89
|
+
|
|
90
|
+
- `@klingai/plugin-cn` 固定连接中国区 `https://klingai.com`;`@klingai/plugin-global` 固定连接海外区 `https://kling.ai`。
|
|
91
|
+
- 两个包不提供环境变量或配置文件切区;换区必须安装另一个包并重新完成对应账号授权。
|
|
92
|
+
- Host 的网络或文件沙箱仍然生效;受限模式下可能需要用户批准访问网络与 `~/.kling`。
|
|
93
|
+
- Node.js 18 或更高版本。
|
|
Binary file
|
|
Binary file
|
package/bin/plugin.mjs
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
PACKAGE_NAME,
|
|
5
|
+
PLUGIN_BASE_URL,
|
|
6
|
+
PLUGIN_DISPLAY_NAME,
|
|
7
|
+
SUPPORTED_HOSTS,
|
|
8
|
+
DEFAULT_SKILL_HOSTS,
|
|
9
|
+
createInstallPlan,
|
|
10
|
+
displayPath,
|
|
11
|
+
doctor,
|
|
12
|
+
doctorSkillDirectory,
|
|
13
|
+
exposeCommand,
|
|
14
|
+
installHost,
|
|
15
|
+
installSkillDirectory,
|
|
16
|
+
parseArgs,
|
|
17
|
+
preflightInstallHosts,
|
|
18
|
+
resolveInstallHosts,
|
|
19
|
+
removeManagedObsoleteSkills,
|
|
20
|
+
stagePackage,
|
|
21
|
+
uninstallHost,
|
|
22
|
+
uninstallSkillDirectory,
|
|
23
|
+
} from '../lib/install.mjs';
|
|
24
|
+
import { uninstallAll } from '../lib/uninstall.mjs';
|
|
25
|
+
|
|
26
|
+
function reportCommand(wrapper) {
|
|
27
|
+
console.log(`CLI: ${displayPath(wrapper)}`);
|
|
28
|
+
if (process.platform === 'win32') return;
|
|
29
|
+
const result = exposeCommand(wrapper);
|
|
30
|
+
if (result.status === 'linked') console.log(`Command: ${displayPath(result.path)} -> kling-plugin is available in new terminals`);
|
|
31
|
+
else if (result.status === 'conflict') console.log(`Command: kling-plugin was not linked (${result.reason}); run ${displayPath(wrapper)} directly or remove that file`);
|
|
32
|
+
else if (result.status === 'not-on-path') console.log(`Command: add ~/.kling/bin to PATH to run kling-plugin, e.g. echo 'export PATH="$HOME/.kling/bin:$PATH"' >> ~/.zshrc`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function usage() {
|
|
36
|
+
const installerName = PACKAGE_NAME.split('/').at(-1);
|
|
37
|
+
return [
|
|
38
|
+
`${PLUGIN_DISPLAY_NAME} installer`,
|
|
39
|
+
'',
|
|
40
|
+
'Usage:',
|
|
41
|
+
` ${installerName} install # Install Skills for the checked Host set`,
|
|
42
|
+
` ${installerName} install --host <${DEFAULT_SKILL_HOSTS.join('|')}|all>`,
|
|
43
|
+
` ${installerName} install --skills-dir <path>`,
|
|
44
|
+
` ${installerName} install --host <directory-host> --link-skills`,
|
|
45
|
+
` ${installerName} install [--host <host>] --dry-run`,
|
|
46
|
+
` ${installerName} doctor --host <host|all>`,
|
|
47
|
+
` ${installerName} doctor --skills-dir <path>`,
|
|
48
|
+
` ${installerName} uninstall [--dry-run] # Remove all managed Skills, CLI and npm installs`,
|
|
49
|
+
` ${installerName} uninstall --host <host> [--purge]`,
|
|
50
|
+
` ${installerName} uninstall --skills-dir <path> [--purge]`,
|
|
51
|
+
'',
|
|
52
|
+
`${PACKAGE_NAME} has a fixed endpoint: ${PLUGIN_BASE_URL}.`,
|
|
53
|
+
].join('\n');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const { command, host, skillsDir, purge, dryRun, linkSkills } = parseArgs(process.argv.slice(2));
|
|
58
|
+
const installOptions = { skillsOnly: !host || host === 'all' };
|
|
59
|
+
if (command === 'help' || command === '--help' || command === '-h') {
|
|
60
|
+
console.log(usage());
|
|
61
|
+
process.exitCode = 0;
|
|
62
|
+
} else if (!host && !skillsDir && command !== 'install' && command !== 'uninstall') {
|
|
63
|
+
throw new Error(`--host or --skills-dir is required. Choose: ${SUPPORTED_HOSTS.join(', ')}, all`);
|
|
64
|
+
} else if (command === 'install') {
|
|
65
|
+
if (skillsDir) {
|
|
66
|
+
if (dryRun) {
|
|
67
|
+
console.log(JSON.stringify({
|
|
68
|
+
...createInstallPlan([]),
|
|
69
|
+
customSkillsDir: displayPath(skillsDir),
|
|
70
|
+
}, null, 2));
|
|
71
|
+
process.exitCode = 0;
|
|
72
|
+
} else {
|
|
73
|
+
const staged = stagePackage();
|
|
74
|
+
const result = installSkillDirectory(skillsDir, staged, { linkSkills });
|
|
75
|
+
console.log(`[written] custom: ${displayPath(result.path)}`);
|
|
76
|
+
reportCommand(staged.wrapper);
|
|
77
|
+
console.log('Skill files written; Host discovery is not verified. Restart the Host or reload Skills, then start a new session.');
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
const hosts = resolveInstallHosts(host);
|
|
81
|
+
if (host && host !== 'all' && !DEFAULT_SKILL_HOSTS.includes(host) && host !== 'agents') {
|
|
82
|
+
console.error(`Unverified Host target: ${host}. This writes Skill files only; application discovery and invocation have not passed acceptance.`);
|
|
83
|
+
}
|
|
84
|
+
const preflight = preflightInstallHosts(hosts, { ...installOptions, linkSkills });
|
|
85
|
+
if (dryRun) {
|
|
86
|
+
console.log(JSON.stringify({ ...createInstallPlan(hosts, installOptions), preflight }, null, 2));
|
|
87
|
+
process.exitCode = 0;
|
|
88
|
+
} else {
|
|
89
|
+
const failedPreflight = preflight.filter((result) => result.status === 'failed');
|
|
90
|
+
const unavailable = preflight.filter((result) => result.status === 'unavailable');
|
|
91
|
+
const conflicts = preflight.filter((result) => result.status === 'conflict');
|
|
92
|
+
if (failedPreflight.length > 0 || (host !== 'all' && host !== undefined && (unavailable.length > 0 || conflicts.length > 0))) {
|
|
93
|
+
const reasons = [...failedPreflight, ...unavailable, ...conflicts]
|
|
94
|
+
.map((result) => `${result.host}: ${result.reason}`)
|
|
95
|
+
.join('; ');
|
|
96
|
+
throw new Error(`Install preflight failed: ${reasons}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const installableHosts = preflight
|
|
100
|
+
.filter((result) => result.status === 'ready')
|
|
101
|
+
.map((result) => result.host);
|
|
102
|
+
if (installableHosts.length === 0) {
|
|
103
|
+
throw new Error('No available Host remained after install preflight');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const staged = stagePackage();
|
|
107
|
+
const results = [...unavailable, ...conflicts].map((result) => ({
|
|
108
|
+
host: result.host,
|
|
109
|
+
status: 'skipped',
|
|
110
|
+
reason: result.reason,
|
|
111
|
+
}));
|
|
112
|
+
for (const item of installableHosts) {
|
|
113
|
+
try {
|
|
114
|
+
const result = installHost(item, staged, { ...installOptions, linkSkills });
|
|
115
|
+
results.push({ host: item, status: result.status, path: result.path });
|
|
116
|
+
} catch (error) {
|
|
117
|
+
results.push({ host: item, status: 'failed', reason: error.message });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log('Install summary:');
|
|
122
|
+
for (const result of results) {
|
|
123
|
+
const detail = result.path ? displayPath(result.path) : result.reason;
|
|
124
|
+
const status = result.status === 'installed' && (installOptions.skillsOnly || !['codex', 'claude', 'pi'].includes(result.host))
|
|
125
|
+
? 'written' : result.status;
|
|
126
|
+
console.log(` [${status}] ${result.host}: ${detail}`);
|
|
127
|
+
}
|
|
128
|
+
reportCommand(staged.wrapper);
|
|
129
|
+
console.log('Host discovery and invocation are not verified by installation. Restart the Host or reload Skills, then start a new session.');
|
|
130
|
+
if (results.some((result) => result.host === 'workbuddy' && result.status === 'installed')) {
|
|
131
|
+
console.log(`WorkBuddy: look for Kling AI Plugin under Installed Skills (user-defined); the plugin marketplace is separate.`);
|
|
132
|
+
}
|
|
133
|
+
if (!installOptions.skillsOnly && results.some((result) => result.host === 'codex' && result.status === 'installed')) {
|
|
134
|
+
console.log('Codex MCP OAuth is separate from kling login. Check: codex mcp list; if kling-ai is Not logged in, run: codex mcp login kling-ai. Then restart Codex and open a new session. UI rendering still depends on the Host.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const failures = results.filter((result) => result.status === 'failed');
|
|
138
|
+
if (failures.length > 0) {
|
|
139
|
+
throw new Error(`${failures.length} Host installation${failures.length === 1 ? '' : 's'} failed`);
|
|
140
|
+
}
|
|
141
|
+
if (installOptions.skillsOnly) removeManagedObsoleteSkills(staged);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else if (command === 'doctor') {
|
|
145
|
+
if (skillsDir) {
|
|
146
|
+
const result = doctorSkillDirectory(skillsDir);
|
|
147
|
+
console.log(JSON.stringify(result, null, 2));
|
|
148
|
+
if (!result.ok) process.exitCode = 1;
|
|
149
|
+
} else {
|
|
150
|
+
let result;
|
|
151
|
+
if (host === 'all') {
|
|
152
|
+
const plan = createInstallPlan(resolveInstallHosts('all'), { skillsOnly: true });
|
|
153
|
+
const results = plan.hosts.map(({ host: item, target }) => ({
|
|
154
|
+
host: item, path: target, ...doctor(item, { skillsOnly: true }),
|
|
155
|
+
}));
|
|
156
|
+
result = { ok: results.every((item) => item.ok), hosts: results };
|
|
157
|
+
} else {
|
|
158
|
+
result = doctor(host);
|
|
159
|
+
}
|
|
160
|
+
console.log(JSON.stringify(result, null, 2));
|
|
161
|
+
if (!result.ok) process.exitCode = 1;
|
|
162
|
+
}
|
|
163
|
+
} else if (command === 'uninstall') {
|
|
164
|
+
if (!host && !skillsDir) {
|
|
165
|
+
console.log(JSON.stringify(uninstallAll({ dryRun }), null, 2));
|
|
166
|
+
} else if (skillsDir) {
|
|
167
|
+
uninstallSkillDirectory(skillsDir, { purge });
|
|
168
|
+
} else {
|
|
169
|
+
const hosts = host === 'all' ? resolveInstallHosts('all', { includeUnverified: true }) : [host];
|
|
170
|
+
for (const item of hosts) uninstallHost(item, { skillsOnly: host === 'all', purge: purge && item === hosts.at(-1) });
|
|
171
|
+
}
|
|
172
|
+
if (host || skillsDir) console.log('Plugin registration removed. OAuth credentials and logs were preserved.');
|
|
173
|
+
} else {
|
|
174
|
+
throw new Error(`Unknown command: ${command}\n\n${usage()}`);
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(`Error: ${error.message}`);
|
|
178
|
+
process.exitCode = 1;
|
|
179
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { recordNpmInstallation } from '../lib/install.mjs';
|
|
4
|
+
|
|
5
|
+
// npm exec / npx runs the user's installer arguments after fetching the package.
|
|
6
|
+
// Installing here would turn --dry-run and single-Host operations into full installs.
|
|
7
|
+
if (process.env.npm_command !== 'exec') {
|
|
8
|
+
const result = spawnSync(
|
|
9
|
+
process.execPath,
|
|
10
|
+
[fileURLToPath(new URL('./plugin.mjs', import.meta.url)), 'install'],
|
|
11
|
+
{ stdio: 'inherit' },
|
|
12
|
+
);
|
|
13
|
+
if (result.error) throw result.error;
|
|
14
|
+
process.exitCode = result.status ?? 1;
|
|
15
|
+
if (result.status === 0) recordNpmInstallation(fileURLToPath(new URL('..', import.meta.url)));
|
|
16
|
+
}
|