honeydo 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 +21 -0
- package/README.md +90 -0
- package/README.zh-CN.md +88 -0
- package/package.json +54 -0
- package/packages/cli/dist/index.d.ts +2 -0
- package/packages/cli/dist/index.js +171 -0
- package/packages/cli/dist/index.js.map +1 -0
- package/packages/doubao/dist/cli.d.ts +38 -0
- package/packages/doubao/dist/cli.d.ts.map +1 -0
- package/packages/doubao/dist/cli.js +206 -0
- package/packages/gcli/dist/cli.d.ts +465 -0
- package/packages/gcli/dist/cli.js +2017 -0
- package/packages/gcli/dist/cli.js.map +1 -0
- package/packages/lmedia/dist/index.d.ts +1 -0
- package/packages/lmedia/dist/index.js +1594 -0
- package/packages/lmedia/python/edit.py +107 -0
- package/packages/lmedia/python/esrgan_path.py +16 -0
- package/packages/lmedia/python/gen.py +131 -0
- package/packages/lmedia/python/serve.py +352 -0
- package/packages/lmedia/python/sfx.py +527 -0
- package/packages/lmedia/python/teacache.py +255 -0
- package/packages/lmedia/python/upscale.py +41 -0
- package/packages/minimax/dist/cli.d.ts +51 -0
- package/packages/minimax/dist/cli.js +307 -0
- package/packages/minimax/dist/cli.js.map +1 -0
- package/packages/minimax/dist/client.d.ts +20 -0
- package/packages/minimax/dist/client.js +55 -0
- package/packages/minimax/dist/client.js.map +1 -0
- package/packages/minimax/dist/tts.d.ts +33 -0
- package/packages/minimax/dist/tts.js +64 -0
- package/packages/minimax/dist/tts.js.map +1 -0
- package/packages/minimax/dist/validate.d.ts +29 -0
- package/packages/minimax/dist/validate.js +122 -0
- package/packages/minimax/dist/validate.js.map +1 -0
- package/packages/minimax/dist/voice-clone.d.ts +17 -0
- package/packages/minimax/dist/voice-clone.js +47 -0
- package/packages/minimax/dist/voice-clone.js.map +1 -0
- package/packages/minimax/dist/voices.d.ts +17 -0
- package/packages/minimax/dist/voices.js +20 -0
- package/packages/minimax/dist/voices.js.map +1 -0
- package/packages/qwen/dist/index.d.ts +1 -0
- package/packages/qwen/dist/index.js +311 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
|
|
5
|
+
// src/lib/config.ts
|
|
6
|
+
var config = {
|
|
7
|
+
apiUrl: process.env.QWEN_API_URL || "http://127.0.0.1:8001",
|
|
8
|
+
apiKey: process.env.QWEN_API_KEY || "qwen-local-key",
|
|
9
|
+
model: process.env.QWEN_MODEL || "qwen3.6-35b",
|
|
10
|
+
defaults: {
|
|
11
|
+
maxTokens: 1e3,
|
|
12
|
+
visionMaxTokens: 3e3,
|
|
13
|
+
temperature: 0.7,
|
|
14
|
+
// QWEN_TIMEOUT_MS 可覆盖:vision 长输出(-t 10000 实测 90-150s+)会撞默认 120s
|
|
15
|
+
timeout: Number(process.env.QWEN_TIMEOUT_MS) || 12e4
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/lib/api.ts
|
|
20
|
+
async function apiFetch(path, options, timeoutMs = config.defaults.timeout) {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
23
|
+
const url = `${config.apiUrl}${path}`;
|
|
24
|
+
const headers = {
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
27
|
+
};
|
|
28
|
+
const init = { headers, signal: controller.signal };
|
|
29
|
+
if (options.method) init.method = options.method;
|
|
30
|
+
if (options.body !== void 0) {
|
|
31
|
+
init.body = JSON.stringify(options.body);
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch(url, init);
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
return res;
|
|
37
|
+
} catch (err) {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
40
|
+
throw new Error(`\u8BF7\u6C42\u8D85\u65F6 (${timeoutMs / 1e3}s)`);
|
|
41
|
+
}
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function chatCompletions(prompt, options = {}) {
|
|
46
|
+
const body = {
|
|
47
|
+
model: options.model ?? config.model,
|
|
48
|
+
messages: [{ role: "user", content: prompt }],
|
|
49
|
+
max_tokens: options.maxTokens ?? config.defaults.maxTokens,
|
|
50
|
+
temperature: options.temperature ?? config.defaults.temperature,
|
|
51
|
+
stream: false
|
|
52
|
+
};
|
|
53
|
+
const res = await apiFetch("/v1/chat/completions", { method: "POST", body });
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const text = await res.text().catch(() => "");
|
|
56
|
+
let msg = `API \u9519\u8BEF (${res.status})`;
|
|
57
|
+
try {
|
|
58
|
+
const errJson = JSON.parse(text);
|
|
59
|
+
if (errJson.error?.message) msg += `: ${errJson.error.message}`;
|
|
60
|
+
else if (errJson.message) msg += `: ${errJson.message}`;
|
|
61
|
+
else msg += `: ${text.slice(0, 200)}`;
|
|
62
|
+
} catch {
|
|
63
|
+
if (text) msg += `: ${text.slice(0, 200)}`;
|
|
64
|
+
}
|
|
65
|
+
throw new Error(msg);
|
|
66
|
+
}
|
|
67
|
+
return await res.json();
|
|
68
|
+
}
|
|
69
|
+
async function imageToBase64(input) {
|
|
70
|
+
if (/^https?:\/\//i.test(input)) {
|
|
71
|
+
const res = await fetch(input);
|
|
72
|
+
if (!res.ok) throw new Error(`\u4E0B\u8F7D\u56FE\u7247\u5931\u8D25 (${res.status})`);
|
|
73
|
+
const contentType = res.headers.get("content-type") || "image/png";
|
|
74
|
+
const buffer2 = Buffer.from(await res.arrayBuffer());
|
|
75
|
+
const mime2 = contentType.split(";")[0].trim();
|
|
76
|
+
return { mime: mime2, data: buffer2.toString("base64") };
|
|
77
|
+
}
|
|
78
|
+
const buffer = readFileSync(input);
|
|
79
|
+
const ext = input.split(".").pop()?.toLowerCase();
|
|
80
|
+
const mimeMap = {
|
|
81
|
+
png: "image/png",
|
|
82
|
+
jpg: "image/jpeg",
|
|
83
|
+
jpeg: "image/jpeg",
|
|
84
|
+
gif: "image/gif",
|
|
85
|
+
webp: "image/webp",
|
|
86
|
+
bmp: "image/bmp"
|
|
87
|
+
};
|
|
88
|
+
const mime = mimeMap[ext ?? ""] ?? "image/png";
|
|
89
|
+
return { mime, data: buffer.toString("base64") };
|
|
90
|
+
}
|
|
91
|
+
async function visionCompletions(imageInput, prompt, options = {}) {
|
|
92
|
+
const { mime, data } = await imageToBase64(imageInput);
|
|
93
|
+
const dataUri = `data:${mime};base64,${data}`;
|
|
94
|
+
const content = [
|
|
95
|
+
{ type: "text", text: prompt },
|
|
96
|
+
{ type: "image_url", image_url: { url: dataUri } }
|
|
97
|
+
];
|
|
98
|
+
const body = {
|
|
99
|
+
model: options.model ?? config.model,
|
|
100
|
+
messages: [{ role: "user", content }],
|
|
101
|
+
max_tokens: options.maxTokens ?? config.defaults.visionMaxTokens,
|
|
102
|
+
temperature: config.defaults.temperature,
|
|
103
|
+
stream: false
|
|
104
|
+
};
|
|
105
|
+
const res = await apiFetch("/v1/chat/completions", { method: "POST", body });
|
|
106
|
+
if (!res.ok) {
|
|
107
|
+
const text = await res.text().catch(() => "");
|
|
108
|
+
let msg = `API \u9519\u8BEF (${res.status})`;
|
|
109
|
+
try {
|
|
110
|
+
const errJson = JSON.parse(text);
|
|
111
|
+
if (errJson.error?.message) msg += `: ${errJson.error.message}`;
|
|
112
|
+
else if (errJson.message) msg += `: ${errJson.message}`;
|
|
113
|
+
else msg += `: ${text.slice(0, 200)}`;
|
|
114
|
+
} catch {
|
|
115
|
+
if (text) msg += `: ${text.slice(0, 200)}`;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(msg);
|
|
118
|
+
}
|
|
119
|
+
return await res.json();
|
|
120
|
+
}
|
|
121
|
+
async function healthCheck() {
|
|
122
|
+
let health = { ok: false, error: "\u672A\u53D1\u8D77\u8BF7\u6C42" };
|
|
123
|
+
let models = { ok: false, error: "\u672A\u53D1\u8D77\u8BF7\u6C42" };
|
|
124
|
+
try {
|
|
125
|
+
const res = await apiFetch("/health", { method: "GET" }, 1e4);
|
|
126
|
+
if (res.ok) {
|
|
127
|
+
health = { ok: true, health: await res.json().catch(() => ({})) };
|
|
128
|
+
} else {
|
|
129
|
+
health = { ok: false, error: `HTTP ${res.status}` };
|
|
130
|
+
}
|
|
131
|
+
} catch (err) {
|
|
132
|
+
health = { ok: false, error: err.message };
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const res = await apiFetch("/v1/models", { method: "GET" }, 1e4);
|
|
136
|
+
if (res.ok) {
|
|
137
|
+
const data = await res.json();
|
|
138
|
+
models = { ok: true, models: data.data || [] };
|
|
139
|
+
} else {
|
|
140
|
+
models = { ok: false, error: `HTTP ${res.status}` };
|
|
141
|
+
}
|
|
142
|
+
} catch (err) {
|
|
143
|
+
models = { ok: false, error: err.message };
|
|
144
|
+
}
|
|
145
|
+
return { health, models };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/lib/format.ts
|
|
149
|
+
function extractContent(data) {
|
|
150
|
+
try {
|
|
151
|
+
const choices = data.choices;
|
|
152
|
+
if (!choices || !choices[0]) return "[\u65E0\u54CD\u5E94\u5185\u5BB9]";
|
|
153
|
+
const message = choices[0].message;
|
|
154
|
+
if (!message) return "[\u65E0 message \u5B57\u6BB5]";
|
|
155
|
+
if (message.content && message.content.trim()) {
|
|
156
|
+
return message.content;
|
|
157
|
+
}
|
|
158
|
+
const reasoning = message.reasoning_content;
|
|
159
|
+
if (reasoning && reasoning.trim()) {
|
|
160
|
+
return `[\u63A8\u7406\u94FE]
|
|
161
|
+
${reasoning}`;
|
|
162
|
+
}
|
|
163
|
+
return JSON.stringify(message, null, 2);
|
|
164
|
+
} catch {
|
|
165
|
+
return "[\u89E3\u6790\u54CD\u5E94\u5931\u8D25]";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function formatJson(data) {
|
|
169
|
+
return JSON.stringify(data, null, 2);
|
|
170
|
+
}
|
|
171
|
+
function formatError(err) {
|
|
172
|
+
if (err instanceof Error) {
|
|
173
|
+
const msg = err.message;
|
|
174
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
175
|
+
return `\u65E0\u6CD5\u8FDE\u63A5\u5230 Qwen \u670D\u52A1\uFF08${process.env.QWEN_API_URL || "http://127.0.0.1:8001"}\uFF09\u3002\u8BF7\u786E\u8BA4\u670D\u52A1\u5DF2\u542F\u52A8\u3002`;
|
|
176
|
+
}
|
|
177
|
+
if (msg.includes("timeout") || msg.includes("ETIMEDOUT") || msg.includes("aborted")) {
|
|
178
|
+
return "\u8BF7\u6C42\u8D85\u65F6\u3002vision \u6A21\u5F0F\u53EF\u80FD\u9700\u8981\u8F83\u957F\u65F6\u95F4\uFF0C\u5EFA\u8BAE\u68C0\u67E5\u670D\u52A1\u72B6\u6001\u3002";
|
|
179
|
+
}
|
|
180
|
+
if (msg.includes("ENOTFOUND") || msg.includes("getaddrinfo")) {
|
|
181
|
+
return `\u65E0\u6CD5\u89E3\u6790 Qwen \u670D\u52A1\u5730\u5740\u3002\u8BF7\u68C0\u67E5 QWEN_API_URL \u73AF\u5883\u53D8\u91CF\u3002`;
|
|
182
|
+
}
|
|
183
|
+
return `\u9519\u8BEF: ${msg}`;
|
|
184
|
+
}
|
|
185
|
+
return `\u672A\u77E5\u9519\u8BEF: ${String(err)}`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/commands/ask.ts
|
|
189
|
+
async function readStdin() {
|
|
190
|
+
if (process.stdin.isTTY) return "";
|
|
191
|
+
const chunks = [];
|
|
192
|
+
for await (const chunk of process.stdin) {
|
|
193
|
+
chunks.push(Buffer.from(chunk));
|
|
194
|
+
}
|
|
195
|
+
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
196
|
+
}
|
|
197
|
+
function registerAsk(program2) {
|
|
198
|
+
const cmd = new Command("ask").description("\u6587\u672C\u5BF9\u8BDD\uFF0C\u8C03\u7528 Qwen API \u8FD4\u56DE\u7EAF\u6587\u672C\u56DE\u590D").argument("[prompt]", "\u5BF9\u8BDD\u63D0\u793A\u8BCD").option("-t, --tokens <N>", "\u6700\u5927\u8F93\u51FA token \u6570\uFF0C\u9ED8\u8BA4 1000", (v) => parseInt(v, 10)).option("--json", "\u8F93\u51FA\u5B8C\u6574 JSON response").option("--stdin", "\u5F3A\u5236\u4ECE stdin \u8BFB\u53D6 prompt").action(async (prompt, options) => {
|
|
199
|
+
try {
|
|
200
|
+
let finalPrompt = prompt || "";
|
|
201
|
+
const useStdin = options.stdin || !process.stdin.isTTY;
|
|
202
|
+
if (useStdin) {
|
|
203
|
+
const stdinContent = await readStdin();
|
|
204
|
+
if (stdinContent) {
|
|
205
|
+
finalPrompt = finalPrompt ? `${finalPrompt}
|
|
206
|
+
${stdinContent}` : stdinContent;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!finalPrompt) {
|
|
210
|
+
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u5BF9\u8BDD\u5185\u5BB9\uFF08\u53C2\u6570\u6216\u7BA1\u9053\u8F93\u5165\uFF09");
|
|
211
|
+
console.error('\u7528\u6CD5: qwen ask "\u4F60\u7684\u95EE\u9898" \u6216 echo "\u4F60\u7684\u95EE\u9898" | qwen ask');
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
const data = await chatCompletions(finalPrompt, {
|
|
215
|
+
maxTokens: options.tokens
|
|
216
|
+
});
|
|
217
|
+
if (options.json) {
|
|
218
|
+
console.log(formatJson(data));
|
|
219
|
+
} else {
|
|
220
|
+
console.log(extractContent(data));
|
|
221
|
+
}
|
|
222
|
+
} catch (err) {
|
|
223
|
+
console.error(formatError(err));
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
program2.addCommand(cmd);
|
|
228
|
+
}
|
|
229
|
+
function registerVision(program2) {
|
|
230
|
+
const cmd = new Command("vision").description("\u56FE\u7247\u8BC6\u522B\uFF0C\u5206\u6790\u56FE\u7247\u5185\u5BB9\u5E76\u8FD4\u56DE\u6587\u672C\u56DE\u590D").requiredOption("-i, --image <file|url>", "\u56FE\u7247\u6587\u4EF6\u8DEF\u5F84\u6216 URL").argument("[prompt]", '\u8BC6\u522B\u63D0\u793A\u8BCD\uFF0C\u9ED8\u8BA4\u4E3A"\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9"').option("-t, --tokens <N>", "\u6700\u5927\u8F93\u51FA token \u6570\uFF0C\u9ED8\u8BA4 3000", (v) => parseInt(v, 10)).option("--json", "\u8F93\u51FA\u5B8C\u6574 JSON response").action(async (prompt, options) => {
|
|
231
|
+
try {
|
|
232
|
+
const imageInput = options.image;
|
|
233
|
+
const promptText = prompt || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
234
|
+
const data = await visionCompletions(imageInput, promptText, {
|
|
235
|
+
maxTokens: options.tokens
|
|
236
|
+
});
|
|
237
|
+
if (options.json) {
|
|
238
|
+
console.log(formatJson(data));
|
|
239
|
+
} else {
|
|
240
|
+
console.log(extractContent(data));
|
|
241
|
+
}
|
|
242
|
+
} catch (err) {
|
|
243
|
+
console.error(formatError(err));
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
program2.addCommand(cmd);
|
|
248
|
+
}
|
|
249
|
+
function registerStatus(program2) {
|
|
250
|
+
const cmd = new Command("status").description("\u670D\u52A1\u5065\u5EB7\u68C0\u67E5 + \u6A21\u578B\u4FE1\u606F").action(async () => {
|
|
251
|
+
console.log(`Qwen \u670D\u52A1: ${config.apiUrl}`);
|
|
252
|
+
console.log(`\u9ED8\u8BA4\u6A21\u578B: ${config.model}`);
|
|
253
|
+
console.log("");
|
|
254
|
+
const { health, models } = await healthCheck();
|
|
255
|
+
if (health.ok) {
|
|
256
|
+
console.log("\u5065\u5EB7\u68C0\u67E5: \u901A\u8FC7");
|
|
257
|
+
if (health.health) {
|
|
258
|
+
for (const [k, v] of Object.entries(health.health)) {
|
|
259
|
+
console.log(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
console.log(`\u5065\u5EB7\u68C0\u67E5: \u5931\u8D25 (${health.error})`);
|
|
264
|
+
}
|
|
265
|
+
console.log("");
|
|
266
|
+
if (models.ok && models.models) {
|
|
267
|
+
console.log(`\u53EF\u7528\u6A21\u578B: ${models.models.length} \u4E2A`);
|
|
268
|
+
for (const m of models.models) {
|
|
269
|
+
const marker = m.id === config.model ? " \u2190 \u9ED8\u8BA4" : "";
|
|
270
|
+
console.log(` - ${m.id}${marker}`);
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
console.log(`\u6A21\u578B\u5217\u8868: \u83B7\u53D6\u5931\u8D25 (${models.error})`);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
program2.addCommand(cmd);
|
|
277
|
+
}
|
|
278
|
+
function registerModels(program2) {
|
|
279
|
+
const cmd = new Command("models").description("\u5217\u51FA\u53EF\u7528\u6A21\u578B").action(async () => {
|
|
280
|
+
try {
|
|
281
|
+
const { models } = await healthCheck();
|
|
282
|
+
if (models.ok && models.models && models.models.length > 0) {
|
|
283
|
+
console.log(`\u53EF\u7528\u6A21\u578B (${models.models.length} \u4E2A):`);
|
|
284
|
+
console.log("");
|
|
285
|
+
for (const m of models.models) {
|
|
286
|
+
const marker = m.id === config.model ? " \u2605 \u9ED8\u8BA4" : "";
|
|
287
|
+
console.log(` ${m.id}${marker}`);
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
console.log(`\u83B7\u53D6\u6A21\u578B\u5217\u8868\u5931\u8D25: ${models.error || "\u672A\u77E5\u9519\u8BEF"}`);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
} catch (err) {
|
|
294
|
+
console.error(`\u8BF7\u6C42\u5931\u8D25: ${err.message}`);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
program2.addCommand(cmd);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/index.ts
|
|
302
|
+
var program = new Command();
|
|
303
|
+
program.name("qwen").description("Qwen API CLI \u2014 \u5C01\u88C5 Qwen API \u4E3A\u72EC\u7ACB\u547D\u4EE4\u884C\u5DE5\u5177").version("0.1.0");
|
|
304
|
+
registerAsk(program);
|
|
305
|
+
registerVision(program);
|
|
306
|
+
registerStatus(program);
|
|
307
|
+
registerModels(program);
|
|
308
|
+
process.stderr.write(
|
|
309
|
+
"[deprecated] `qwen` \u547D\u4EE4\u5C06\u8FC1\u79FB\u81F3 `honeydo`\uFF08hd vision/models/status\u3001hd ask --backend local\uFF09\uFF0C\u8BF7\u9010\u6B65\u5207\u6362\n"
|
|
310
|
+
);
|
|
311
|
+
program.parse();
|