@let5sne/kimi-video 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenClaw Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # Kimi Video Plugin for OpenClaw
2
+
3
+ OpenClaw 插件,用于使用 Kimi K2.5 API 分析视频内容。
4
+
5
+ ## 功能
6
+
7
+ - 分析本地视频文件或远程视频 URL
8
+ - 使用 Kimi K2.5 模型进行智能视频内容分析
9
+ - 支持自定义分析提示词
10
+
11
+ ## 安装
12
+
13
+ 插件已自动安装在 `~/.openclaw/extensions/kimi-video/` 目录下。
14
+
15
+ ## 配置
16
+
17
+ 在 `config.json5` 中添加配置:
18
+
19
+ ```json5
20
+ {
21
+ plugins: {
22
+ entries: {
23
+ "kimi-video": {
24
+ enabled: true,
25
+ config: {
26
+ // API Key (可选,默认使用 MOONSHOT_API_KEY 环境变量)
27
+ apiKey: "your-moonshot-api-key",
28
+ // 模型选择 (可选,默认 kimi-k2.5)
29
+ model: "kimi-k2.5",
30
+ // API 基础 URL (可选)
31
+ baseUrl: "https://api.moonshot.cn/v1"
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ 或者只使用环境变量:
40
+
41
+ ```bash
42
+ export MOONSHOT_API_KEY="your-moonshot-api-key"
43
+ ```
44
+
45
+ ## 使用
46
+
47
+ 工具名称:`video_analyze`
48
+
49
+ 参数:
50
+ - `videoPath` (可选): 本地视频文件路径
51
+ - `videoUrl` (可选): 远程视频 URL
52
+ - `prompt` (可选): 自定义分析提示词
53
+
54
+ 注意:`videoPath` 和 `videoUrl` 至少提供一个。
55
+
56
+ ### 示例
57
+
58
+ 分析本地视频:
59
+ ```json
60
+ {
61
+ "videoPath": "/path/to/video.mp4"
62
+ }
63
+ ```
64
+
65
+ 分析远程视频:
66
+ ```json
67
+ {
68
+ "videoUrl": "https://example.com/video.mp4"
69
+ }
70
+ ```
71
+
72
+ 使用自定义提示词:
73
+ ```json
74
+ {
75
+ "videoUrl": "https://example.com/video.mp4",
76
+ "prompt": "请分析这个视频中的主要人物和对话内容"
77
+ }
78
+ ```
79
+
80
+ ## 工具启用
81
+
82
+ 由于这是插件工具,需要在 agent 配置中启用:
83
+
84
+ ```json5
85
+ {
86
+ agents: {
87
+ list: [
88
+ {
89
+ id: "main",
90
+ tools: {
91
+ allow: [
92
+ "video_analyze", // 启用视频分析工具
93
+ "kimi-video" // 或启用整个插件的所有工具
94
+ ]
95
+ }
96
+ }
97
+ ]
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## 文件结构
103
+
104
+ ```
105
+ ~/.openclaw/extensions/kimi-video/
106
+ ├── package.json # 包配置
107
+ ├── openclaw.plugin.json # 插件清单
108
+ ├── index.ts # 插件入口
109
+ ├── src/
110
+ │ ├── tool.ts # video_analyze 工具实现
111
+ │ └── kimi-api.ts # Kimi API 调用
112
+ └── README.md # 本文件
113
+ ```
114
+
115
+ ## 依赖
116
+
117
+ - OpenClaw >= 2026.1.0
118
+ - Node.js >= 22.0.0
119
+
120
+ ## API 参考
121
+
122
+ - Base URL: https://api.moonshot.cn/v1
123
+ - 模型: kimi-k2.5
124
+ - 能力: video_in (视频输入)
package/index.ts ADDED
@@ -0,0 +1,57 @@
1
+ import type { ClawdbotPluginApi } from "openclaw/plugin-sdk";
2
+ import { registerVideoAnalyzeTool, type ToolConfig } from "./src/tool.js";
3
+
4
+ function resolveConfig(pluginConfig: unknown): ToolConfig {
5
+ const raw =
6
+ pluginConfig && typeof pluginConfig === "object" && !Array.isArray(pluginConfig)
7
+ ? (pluginConfig as Record<string, unknown>)
8
+ : {};
9
+
10
+ // Get API key from config or environment variable
11
+ const apiKey =
12
+ typeof raw.apiKey === "string" && raw.apiKey.trim()
13
+ ? raw.apiKey.trim()
14
+ : process.env.MOONSHOT_API_KEY || "";
15
+
16
+ if (!apiKey) {
17
+ throw new Error(
18
+ "Moonshot API key is required. Set it in plugin config or MOONSHOT_API_KEY environment variable."
19
+ );
20
+ }
21
+
22
+ return {
23
+ apiKey,
24
+ model:
25
+ typeof raw.model === "string" && raw.model.trim()
26
+ ? raw.model.trim()
27
+ : "kimi-k2.5",
28
+ baseUrl:
29
+ typeof raw.baseUrl === "string" && raw.baseUrl.trim()
30
+ ? raw.baseUrl.trim()
31
+ : "https://api.moonshot.cn/v1",
32
+ };
33
+ }
34
+
35
+ const kimiVideoPlugin = {
36
+ id: "kimi-video",
37
+ name: "Kimi Video",
38
+ description: "Video analysis plugin using Kimi K2.5 API",
39
+ register(api: ClawdbotPluginApi) {
40
+ try {
41
+ const config = resolveConfig(api.pluginConfig);
42
+
43
+ api.logger.info(`[kimi-video] Initializing with model: ${config.model}`);
44
+
45
+ registerVideoAnalyzeTool(api, config);
46
+
47
+ api.logger.info("[kimi-video] Video analyze tool registered successfully");
48
+ } catch (err) {
49
+ const errorMessage = err instanceof Error ? err.message : String(err);
50
+ api.logger.error(`[kimi-video] Failed to initialize: ${errorMessage}`);
51
+ throw err;
52
+ }
53
+ },
54
+ };
55
+
56
+ export default kimiVideoPlugin;
57
+ export { registerVideoAnalyzeTool };
@@ -0,0 +1,28 @@
1
+ {
2
+ "id": "kimi-video",
3
+ "configSchema": {
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "properties": {
7
+ "apiKey": {
8
+ "type": "string",
9
+ "description": "Moonshot API Key (optional, defaults to MOONSHOT_API_KEY env var)"
10
+ },
11
+ "model": {
12
+ "type": "string",
13
+ "description": "Model to use for video analysis",
14
+ "default": "kimi-k2.5"
15
+ },
16
+ "baseUrl": {
17
+ "type": "string",
18
+ "description": "Base URL for Moonshot API",
19
+ "default": "https://api.moonshot.cn/v1"
20
+ }
21
+ }
22
+ },
23
+ "uiHints": {
24
+ "apiKey": { "label": "API Key", "sensitive": true },
25
+ "model": { "label": "Model" },
26
+ "baseUrl": { "label": "Base URL" }
27
+ }
28
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@let5sne/kimi-video",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "OpenClaw plugin for video analysis using Kimi K2.5 API",
6
+ "license": "MIT",
7
+ "files": [
8
+ "index.ts",
9
+ "src",
10
+ "openclaw.plugin.json"
11
+ ],
12
+ "openclaw": {
13
+ "extensions": [
14
+ "./index.ts"
15
+ ]
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^22.0.0",
19
+ "openclaw": "^2026.1.0",
20
+ "typescript": "^5.7.0"
21
+ },
22
+ "peerDependencies": {
23
+ "openclaw": ">=2026.1.0"
24
+ }
25
+ }
@@ -0,0 +1,101 @@
1
+ import { readFile, stat } from "fs/promises";
2
+ import { execSync } from "child_process";
3
+ import { tmpdir } from "os";
4
+ import { join } from "path";
5
+
6
+ export interface KimiVideoConfig {
7
+ apiKey: string;
8
+ model: string;
9
+ baseUrl: string;
10
+ }
11
+
12
+ export interface VideoAnalyzeRequest {
13
+ videoPath?: string;
14
+ videoUrl?: string;
15
+ prompt?: string;
16
+ }
17
+
18
+ export interface VideoAnalyzeResponse {
19
+ content: string;
20
+ usage?: {
21
+ prompt_tokens: number;
22
+ completion_tokens: number;
23
+ total_tokens: number;
24
+ };
25
+ }
26
+
27
+ const DEFAULT_PROMPT = "请详细分析这个视频的内容,包括:1. 视频主题 2. 画面内容 3. 关键信息";
28
+
29
+ export class KimiVideoAnalyzer {
30
+ private config: KimiVideoConfig;
31
+
32
+ constructor(config: KimiVideoConfig) {
33
+ this.config = config;
34
+ }
35
+
36
+ async analyzeVideo(request: VideoAnalyzeRequest): Promise<VideoAnalyzeResponse> {
37
+ const { videoPath, videoUrl, prompt = DEFAULT_PROMPT } = request;
38
+
39
+ if (!videoPath && !videoUrl) {
40
+ throw new Error("Either videoPath or videoUrl must be provided");
41
+ }
42
+
43
+ // Use kimi CLI for video analysis (it handles video_in properly)
44
+ if (videoPath) {
45
+ return this.analyzeWithKimiCli(videoPath, prompt);
46
+ } else if (videoUrl) {
47
+ // Download URL to temp file first
48
+ const tempPath = join(tmpdir(), `kimi-video-${Date.now()}.mp4`);
49
+ execSync(`curl -sL "${videoUrl}" -o "${tempPath}"`);
50
+ const result = await this.analyzeWithKimiCli(tempPath, prompt);
51
+ execSync(`rm -f "${tempPath}"`);
52
+ return result;
53
+ }
54
+
55
+ throw new Error("No video source provided");
56
+ }
57
+
58
+ private async analyzeWithKimiCli(
59
+ filePath: string,
60
+ prompt: string
61
+ ): Promise<VideoAnalyzeResponse> {
62
+ // Verify file exists
63
+ const stats = await stat(filePath);
64
+ if (!stats.isFile()) {
65
+ throw new Error("Path is not a file");
66
+ }
67
+
68
+ // Copy to /tmp for kimi to access
69
+ const tempPath = join(tmpdir(), `video-${Date.now()}.mp4`);
70
+ execSync(`cp "${filePath}" "${tempPath}"`);
71
+
72
+ try {
73
+ // Use kimi CLI with --quiet for clean output
74
+ const cmd = `cd /tmp && kimi -w /tmp -y -p "${prompt.replace(/"/g, '\\"')} 文件路径: ${tempPath}" --print --final-message-only 2>&1`;
75
+
76
+ const output = execSync(cmd, {
77
+ encoding: "utf-8",
78
+ timeout: 120000, // 2 minutes timeout
79
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
80
+ });
81
+
82
+ // Clean up temp file
83
+ execSync(`rm -f "${tempPath}"`);
84
+
85
+ return {
86
+ content: output.trim(),
87
+ usage: undefined, // kimi CLI doesn't expose token usage
88
+ };
89
+ } catch (error) {
90
+ // Clean up on error
91
+ execSync(`rm -f "${tempPath}"`).toString();
92
+
93
+ const errorMessage = error instanceof Error ? error.message : String(error);
94
+ throw new Error(`Kimi CLI error: ${errorMessage}`);
95
+ }
96
+ }
97
+ }
98
+
99
+ export function createKimiVideoAnalyzer(config: KimiVideoConfig): KimiVideoAnalyzer {
100
+ return new KimiVideoAnalyzer(config);
101
+ }
package/src/tool.ts ADDED
@@ -0,0 +1,79 @@
1
+ import type { ClawdbotPluginApi } from "openclaw/plugin-sdk";
2
+ import {
3
+ KimiVideoAnalyzer,
4
+ createKimiVideoAnalyzer,
5
+ type KimiVideoConfig,
6
+ type VideoAnalyzeRequest,
7
+ } from "./kimi-api.js";
8
+
9
+ // Use plain JSON schema instead of TypeBox
10
+ const VideoAnalyzeToolSchema = {
11
+ type: "object",
12
+ properties: {
13
+ videoPath: {
14
+ type: "string",
15
+ description: "Local file path to the video file to analyze",
16
+ },
17
+ videoUrl: {
18
+ type: "string",
19
+ description: "URL of the video to analyze",
20
+ },
21
+ prompt: {
22
+ type: "string",
23
+ description: "Custom prompt for video analysis (optional)",
24
+ },
25
+ },
26
+ };
27
+
28
+ export interface ToolConfig {
29
+ apiKey: string;
30
+ model: string;
31
+ baseUrl: string;
32
+ }
33
+
34
+ export function registerVideoAnalyzeTool(
35
+ api: ClawdbotPluginApi,
36
+ config: ToolConfig
37
+ ) {
38
+ const analyzer = createKimiVideoAnalyzer({
39
+ apiKey: config.apiKey,
40
+ model: config.model,
41
+ baseUrl: config.baseUrl,
42
+ });
43
+
44
+ api.registerTool({
45
+ name: "video_analyze",
46
+ label: "Video Analysis",
47
+ description:
48
+ "Analyze video content using Kimi K2.5 AI. Provide either a local file path (videoPath) or a URL (videoUrl). The tool will analyze the video and return detailed information about its content, scenes, objects, and key events.",
49
+ parameters: VideoAnalyzeToolSchema,
50
+ async execute(_toolCallId, params) {
51
+ const json = (payload: unknown) => ({
52
+ content: [{ type: "text" as const, text: JSON.stringify(payload, null, 2) }],
53
+ details: payload,
54
+ });
55
+
56
+ try {
57
+ const request: VideoAnalyzeRequest = {
58
+ videoPath: params.videoPath,
59
+ videoUrl: params.videoUrl,
60
+ prompt: params.prompt,
61
+ };
62
+
63
+ const result = await analyzer.analyzeVideo(request);
64
+
65
+ return json({
66
+ success: true,
67
+ analysis: result.content,
68
+ usage: result.usage,
69
+ });
70
+ } catch (err) {
71
+ const errorMessage = err instanceof Error ? err.message : String(err);
72
+ return json({
73
+ success: false,
74
+ error: errorMessage,
75
+ });
76
+ }
77
+ },
78
+ });
79
+ }