@zyun_skill/skills-runtime 1.0.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/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # @zyun_skill/skills-runtime
2
+
3
+ Zhiyun Skills Runtime
4
+
5
+ ## Example
6
+
7
+ ```bash
8
+ npx @zyun_skill/skills-runtime@latest serve douyin
9
+ ```
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import { argv } from "node:process";
4
+ import { createApiClient } from "../src/client.js";
5
+ import { getApiKey } from "../src/env.js";
6
+ import { executeDouyinAction } from "../src/skills/douyin.js";
7
+
8
+ const args = argv.slice(2);
9
+ const command = args[0];
10
+ const skillName = args[1];
11
+ const action = args[2];
12
+
13
+ function parsePayload(cliArgs) {
14
+ const payloadFileIndex = cliArgs.indexOf("--payload-file");
15
+ if (payloadFileIndex !== -1) {
16
+ const payloadFile = cliArgs[payloadFileIndex + 1];
17
+ if (!payloadFile) {
18
+ throw new Error("缺少 --payload-file 对应的文件路径");
19
+ }
20
+
21
+ try {
22
+ return JSON.parse(fs.readFileSync(payloadFile, "utf-8"));
23
+ } catch (error) {
24
+ throw new Error("无法解析 --payload-file 中的 JSON");
25
+ }
26
+ }
27
+
28
+ const payloadIndex = cliArgs.indexOf("--payload");
29
+ if (payloadIndex === -1) {
30
+ return {};
31
+ }
32
+
33
+ const payloadRaw = cliArgs[payloadIndex + 1];
34
+ if (!payloadRaw) {
35
+ throw new Error("缺少 --payload 对应的 JSON 内容");
36
+ }
37
+
38
+ try {
39
+ return JSON.parse(payloadRaw);
40
+ } catch (error) {
41
+ throw new Error("无法解析 --payload JSON");
42
+ }
43
+ }
44
+
45
+ if (command === "serve") {
46
+ try {
47
+ const apiKey = getApiKey();
48
+ createApiClient();
49
+
50
+ if (skillName !== "douyin") {
51
+ throw new Error(`暂不支持的 Skill: ${skillName}`);
52
+ }
53
+
54
+ if (!action) {
55
+ console.log(`启动 Runtime: ${skillName}`);
56
+ console.log(`读取到 API_KEY,长度: ${apiKey.length}`);
57
+ process.exit(0);
58
+ }
59
+
60
+ const payload = parsePayload(args);
61
+ const result = await executeDouyinAction(action, payload);
62
+ console.log(JSON.stringify(result, null, 2));
63
+ process.exit(0);
64
+ } catch (error) {
65
+ console.error(error.message);
66
+ process.exit(1);
67
+ }
68
+ }
69
+
70
+ console.log("用法: npx @zyun_skill/skills-runtime@latest serve douyin");
71
+ console.log("示例: npx @zyun_skill/skills-runtime@latest serve douyin fetch_video_search_v2 --payload '{\"keyword\":\"HappyHorse\"}'");
72
+ console.log("示例: npx @zyun_skill/skills-runtime@latest serve douyin fetch_video_search_v2 --payload-file .\\payloads\\search.json");
73
+ process.exit(1);
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@zyun_skill/skills-runtime",
3
+ "version": "1.0.0",
4
+ "description": "Zhiyun skills runtime",
5
+ "type": "module",
6
+ "bin": {
7
+ "zhiyun-skills-runtime": "./bin/zhiyun-skills-runtime.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "README.md"
13
+ ],
14
+ "dependencies": {
15
+ "axios": "^1.9.0",
16
+ "dotenv": "^16.5.0"
17
+ },
18
+ "license": "MIT"
19
+ }
package/src/client.js ADDED
@@ -0,0 +1,31 @@
1
+ import axios from "axios";
2
+ import { getApiKey, getBaseUrl } from "./env.js";
3
+
4
+ export function createApiClient() {
5
+ const apiKey = getApiKey();
6
+
7
+ return axios.create({
8
+ baseURL: getBaseUrl(),
9
+ timeout: 60000,
10
+ headers: {
11
+ Authorization: `Bearer ${apiKey}`
12
+ }
13
+ });
14
+ }
15
+
16
+ export async function requestApi({ method, path, payloadMode = "query", payload = {} }) {
17
+ const client = createApiClient();
18
+ const requestConfig = {
19
+ method,
20
+ url: path
21
+ };
22
+
23
+ if (payloadMode === "body") {
24
+ requestConfig.data = payload;
25
+ } else {
26
+ requestConfig.params = payload;
27
+ }
28
+
29
+ const response = await client.request(requestConfig);
30
+ return response.data;
31
+ }
package/src/env.js ADDED
@@ -0,0 +1,18 @@
1
+ import dotenv from "dotenv";
2
+
3
+ dotenv.config();
4
+
5
+ export function getApiKey() {
6
+ const apiKey = process.env.API_KEY || process.env.ZYUN_API_KEY || "";
7
+
8
+ if (!apiKey) {
9
+ throw new Error("未找到 API_KEY,请在 .env 中配置");
10
+ }
11
+
12
+ return apiKey;
13
+ }
14
+
15
+ export function getBaseUrl() {
16
+ const baseUrl = process.env.ZYUN_BASE_URL || "https://www.zyunaigc.com";
17
+ return baseUrl.replace(/\/+$/, "");
18
+ }
@@ -0,0 +1,90 @@
1
+ import { requestApi } from "../client.js";
2
+
3
+ export const douyinSkill = {
4
+ slug: "douyin",
5
+ name: "抖音工具箱"
6
+ };
7
+
8
+ export const douyinEndpoints = {
9
+ fetch_one_video: {
10
+ method: "GET",
11
+ path: "/api/v1/douyin/web/fetch_one_video",
12
+ payloadMode: "query",
13
+ params: ["aweme_id", "video_url"]
14
+ },
15
+ fetch_video_search_v2: {
16
+ method: "POST",
17
+ path: "/api/v1/douyin/search/fetch_video_search_v2",
18
+ payloadMode: "body",
19
+ params: [
20
+ "keyword",
21
+ "cursor",
22
+ "offset",
23
+ "sort_type",
24
+ "publish_time",
25
+ "filter_duration",
26
+ "content_type",
27
+ "search_id"
28
+ ]
29
+ },
30
+ handler_user_profile: {
31
+ method: "GET",
32
+ path: "/api/v1/douyin/web/handler_user_profile",
33
+ payloadMode: "query",
34
+ params: ["sec_user_id", "user_url"]
35
+ }
36
+ };
37
+
38
+ function pickAllowedParams(payload, allowedParams) {
39
+ const result = {};
40
+
41
+ for (const key of allowedParams) {
42
+ if (payload[key] !== undefined) {
43
+ result[key] = payload[key];
44
+ }
45
+ }
46
+
47
+ return result;
48
+ }
49
+
50
+ function normalizePayload(action, payload = {}) {
51
+ if (action === "fetch_video_search_v2") {
52
+ return {
53
+ keyword: payload.keyword ?? "",
54
+ cursor: payload.cursor ?? 0,
55
+ offset: payload.offset ?? 0,
56
+ sort_type: payload.sort_type ?? "0",
57
+ publish_time: payload.publish_time ?? "0",
58
+ filter_duration: payload.filter_duration ?? "0",
59
+ content_type: payload.content_type ?? "0",
60
+ search_id: payload.search_id ?? ""
61
+ };
62
+ }
63
+
64
+ const endpoint = douyinEndpoints[action];
65
+ return pickAllowedParams(payload, endpoint.params);
66
+ }
67
+
68
+ export async function executeDouyinAction(action, payload = {}) {
69
+ const endpoint = douyinEndpoints[action];
70
+
71
+ if (!endpoint) {
72
+ throw new Error(`未定义的抖音 Skill 动作: ${action}`);
73
+ }
74
+
75
+ const normalizedPayload = normalizePayload(action, payload);
76
+ const result = await requestApi({
77
+ method: endpoint.method,
78
+ path: endpoint.path,
79
+ payloadMode: endpoint.payloadMode,
80
+ payload: normalizedPayload
81
+ });
82
+
83
+ return {
84
+ action,
85
+ method: endpoint.method,
86
+ path: endpoint.path,
87
+ payload: normalizedPayload,
88
+ result
89
+ };
90
+ }