@xiaoqiong0v0/opencode-file-tool 1.0.8 → 1.1.1
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 +49 -49
- package/dist/cache.js +222 -0
- package/dist/config.js +168 -0
- package/dist/file-tool.js +18 -413
- package/dist/i18n.js +76 -0
- package/dist/providers.js +61 -0
- package/dist/tools/analyze.js +94 -0
- package/dist/tools/file_tool.js +105 -0
- package/dist/tools/file_tool_helpers.js +59 -0
- package/dist/tools/generate.js +107 -0
- package/dist/utils.js +32 -0
- package/package.json +1 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { DESC, T, LANG } from "../i18n.js";
|
|
5
|
+
import { requireModelCfg, getCfg } from "../config.js";
|
|
6
|
+
import { findFileInChain, readFileData } from "../cache.js";
|
|
7
|
+
const CACHE_TYPES_LIST = ["input", "image", "video", "tts"];
|
|
8
|
+
export const analyzeImageTool = tool({
|
|
9
|
+
description: DESC.analyze_image[LANG],
|
|
10
|
+
args: {
|
|
11
|
+
source: tool.schema.enum(["file_path", "base64"]).describe(DESC.analyze_args_source[LANG]),
|
|
12
|
+
data: tool.schema.string().describe(DESC.analyze_args_data[LANG]),
|
|
13
|
+
prompt: tool.schema.string().optional().describe(DESC.analyze_args_prompt[LANG]),
|
|
14
|
+
},
|
|
15
|
+
execute: async ({ source, data, prompt }, context) => {
|
|
16
|
+
let imageUrl, fileName = "";
|
|
17
|
+
if (source === "file_path" && data.startsWith("file_id:")) {
|
|
18
|
+
const segs = data.slice(8).split(":");
|
|
19
|
+
let type = "input";
|
|
20
|
+
let fid;
|
|
21
|
+
if (segs.length === 2 && CACHE_TYPES_LIST.includes(segs[0])) {
|
|
22
|
+
type = segs[0];
|
|
23
|
+
fid = parseInt(segs[1], 10);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
fid = parseInt(segs[0], 10);
|
|
27
|
+
}
|
|
28
|
+
const found = findFileInChain(context.sessionID, type, fid);
|
|
29
|
+
if (!found) {
|
|
30
|
+
context.metadata?.({ title: T("meta_failed") });
|
|
31
|
+
return T("file_id_not_found", { id: `${type}:${fid}` });
|
|
32
|
+
}
|
|
33
|
+
const file = found.file;
|
|
34
|
+
if (!file.cached) {
|
|
35
|
+
context.metadata?.({ title: T("meta_skip") });
|
|
36
|
+
return T("uncached_hint", { id: `${type}:${fid}` });
|
|
37
|
+
}
|
|
38
|
+
if (!file.mime.startsWith("image/")) {
|
|
39
|
+
context.metadata?.({ title: T("meta_skip") });
|
|
40
|
+
return T("not_an_image", { name: file.filename, mime: file.mime });
|
|
41
|
+
}
|
|
42
|
+
fileName = file.filename;
|
|
43
|
+
imageUrl = readFileData(context.sessionID, type, fid) || "";
|
|
44
|
+
if (!imageUrl) {
|
|
45
|
+
context.metadata?.({ title: T("meta_failed") });
|
|
46
|
+
return T("file_data_not_found", { id: `${type}:${fid}` });
|
|
47
|
+
}
|
|
48
|
+
prompt = prompt || T("describe_image", { name: fileName });
|
|
49
|
+
}
|
|
50
|
+
else if (source === "file_path") {
|
|
51
|
+
if (!existsSync(data)) {
|
|
52
|
+
const tryPath = join(context.directory, data);
|
|
53
|
+
if (existsSync(tryPath))
|
|
54
|
+
data = tryPath;
|
|
55
|
+
}
|
|
56
|
+
if (!existsSync(data)) {
|
|
57
|
+
context.metadata?.({ title: T("meta_not_found") });
|
|
58
|
+
return T("file_not_found", { path: data });
|
|
59
|
+
}
|
|
60
|
+
const ext = data.split(".").pop()?.toLowerCase() || "";
|
|
61
|
+
const mimeMap = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", bmp: "image/bmp", gif: "image/gif", webp: "image/webp" };
|
|
62
|
+
const mime = mimeMap[ext] || "image/png";
|
|
63
|
+
fileName = data.split(/[/\\]/).pop() || "";
|
|
64
|
+
imageUrl = `data:${mime};base64,${readFileSync(data).toString("base64")}`;
|
|
65
|
+
}
|
|
66
|
+
else if (source === "base64") {
|
|
67
|
+
imageUrl = `data:image/png;base64,${data.replace(/^data:image\/\w+;base64,/, "")}`;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return T("unsupported_source", { source });
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const cfg = requireModelCfg("vision");
|
|
74
|
+
const resp = await fetch(`${cfg.baseURL}/chat/completions`, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json" },
|
|
77
|
+
body: JSON.stringify({ model: cfg.modelId, messages: [{ role: "user", content: [{ type: "text", text: prompt || T("vision_prompt_default") }, { type: "image_url", image_url: { url: imageUrl } }] }], max_tokens: getCfg().maxTokens }),
|
|
78
|
+
signal: AbortSignal.timeout(getCfg().timeout || 60000),
|
|
79
|
+
});
|
|
80
|
+
if (!resp.ok)
|
|
81
|
+
throw new Error(T("err_api", { status: String(resp.status), msg: (await resp.text().catch(() => "unknown")).slice(0, 200) }));
|
|
82
|
+
const data = await resp.json();
|
|
83
|
+
const msg = data.choices?.[0]?.message;
|
|
84
|
+
const result = msg?.content || msg?.reasoning_content || T("empty_response");
|
|
85
|
+
context.metadata?.({ title: `[Vision] ${fileName || T("meta_image")}`, metadata: { sessionID: context.sessionID, messageID: context.messageID } });
|
|
86
|
+
return "[Vision] " + result;
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
90
|
+
context.metadata?.({ title: T("meta_error") });
|
|
91
|
+
return `[Vision Error] ${msg}`;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { DESC, T, LANG } from "../i18n.js";
|
|
4
|
+
import { CONFIG_PATH, MODEL_TYPES, CACHE_TYPES, DEFAULT_CFG, getCfg, getProviderNames, readJsonc, ENABLED, setEnabled, saveCfg } from "../config.js";
|
|
5
|
+
import { readTypeStore, getRootSession } from "../cache.js";
|
|
6
|
+
import { localModelList, listModelsApi, currentModelSummary } from "./file_tool_helpers.js";
|
|
7
|
+
export const fileTool = tool({
|
|
8
|
+
description: DESC.file_tool[LANG],
|
|
9
|
+
args: { command: tool.schema.string().describe(DESC.file_tool_args[LANG]) },
|
|
10
|
+
execute: async ({ command }, context) => {
|
|
11
|
+
const cmd = command.trim();
|
|
12
|
+
if (cmd === "list-provider") {
|
|
13
|
+
const providerNames = getProviderNames();
|
|
14
|
+
const models = await listModelsApi(providerNames);
|
|
15
|
+
const fallbackModels = localModelList();
|
|
16
|
+
for (const m of fallbackModels)
|
|
17
|
+
if (!models.includes(m))
|
|
18
|
+
models.push(m);
|
|
19
|
+
const modelLines = models.map(m => " " + m).join("\n");
|
|
20
|
+
return T("current_model", { model: currentModelSummary(), list: modelLines });
|
|
21
|
+
}
|
|
22
|
+
if (cmd.startsWith("set-provider ")) {
|
|
23
|
+
const arg = cmd.slice(13).trim();
|
|
24
|
+
if (!arg)
|
|
25
|
+
return T("specify_model");
|
|
26
|
+
let type = "vision";
|
|
27
|
+
let model = arg;
|
|
28
|
+
const colon = arg.indexOf(":");
|
|
29
|
+
if (colon > 0 && MODEL_TYPES.includes(arg.slice(0, colon))) {
|
|
30
|
+
type = arg.slice(0, colon);
|
|
31
|
+
model = arg.slice(colon + 1).trim();
|
|
32
|
+
}
|
|
33
|
+
if (!model)
|
|
34
|
+
return T("specify_model");
|
|
35
|
+
const cfg = existsSync(CONFIG_PATH) ? readJsonc(CONFIG_PATH) : {};
|
|
36
|
+
const models = { ...DEFAULT_CFG.models, ...((cfg.models && typeof cfg.models === "object") ? cfg.models : {}) };
|
|
37
|
+
models[type] = model;
|
|
38
|
+
saveCfg({ models });
|
|
39
|
+
return T("model_switched", { type, model });
|
|
40
|
+
}
|
|
41
|
+
if (cmd === "disable") {
|
|
42
|
+
setEnabled(false);
|
|
43
|
+
return T("disabled");
|
|
44
|
+
}
|
|
45
|
+
if (cmd === "enable") {
|
|
46
|
+
setEnabled(true);
|
|
47
|
+
return T("enabled");
|
|
48
|
+
}
|
|
49
|
+
if (cmd === "disable-save") {
|
|
50
|
+
saveCfg({ enabled: false });
|
|
51
|
+
return T("disabled");
|
|
52
|
+
}
|
|
53
|
+
if (cmd === "enable-save") {
|
|
54
|
+
saveCfg({ enabled: true });
|
|
55
|
+
return T("enabled");
|
|
56
|
+
}
|
|
57
|
+
if (cmd === "status") {
|
|
58
|
+
const c = getCfg();
|
|
59
|
+
const fmt = (t) => c.models[t]?.model || T("model_not_set");
|
|
60
|
+
return T("status", { s: ENABLED ? T("enabled") : T("disabled"), m: fmt("vision"), i: fmt("image"), v: fmt("video"), t: fmt("tts") });
|
|
61
|
+
}
|
|
62
|
+
if (cmd === "list-cache" || cmd.startsWith("list-cache ")) {
|
|
63
|
+
const rest = cmd === "list-cache" ? "" : cmd.slice(11).trim();
|
|
64
|
+
let targetSid = context.sessionID;
|
|
65
|
+
let arg = rest;
|
|
66
|
+
if (arg === "main" || arg.startsWith("main ")) {
|
|
67
|
+
targetSid = getRootSession(context.sessionID);
|
|
68
|
+
arg = arg === "main" ? "" : arg.slice(5).trim();
|
|
69
|
+
}
|
|
70
|
+
const tokens = arg.split(/\s+/).filter(Boolean);
|
|
71
|
+
let filter = null;
|
|
72
|
+
let countStr = "1";
|
|
73
|
+
if (tokens[0] && CACHE_TYPES.includes(tokens[0])) {
|
|
74
|
+
filter = tokens[0];
|
|
75
|
+
countStr = tokens[1] || "all";
|
|
76
|
+
}
|
|
77
|
+
else if (tokens[0]) {
|
|
78
|
+
countStr = tokens[0];
|
|
79
|
+
}
|
|
80
|
+
const types = filter ? [filter] : CACHE_TYPES;
|
|
81
|
+
const lines = [];
|
|
82
|
+
for (const type of types) {
|
|
83
|
+
const store = readTypeStore(targetSid, type);
|
|
84
|
+
const entries = Object.values(store.files);
|
|
85
|
+
if (entries.length === 0)
|
|
86
|
+
continue;
|
|
87
|
+
let show = entries;
|
|
88
|
+
if (countStr !== "all") {
|
|
89
|
+
const n = parseInt(countStr, 10);
|
|
90
|
+
if (!isNaN(n) && n > 0)
|
|
91
|
+
show = entries.slice(-n);
|
|
92
|
+
}
|
|
93
|
+
lines.push(` ${type}:`);
|
|
94
|
+
for (const f of show) {
|
|
95
|
+
const src = f.msgId ? `msg_${f.msgId.slice(-8)}` : type === "input" ? "input" : "generated";
|
|
96
|
+
lines.push(` ${f.filename} (${type}:${f.id}) [${src}]`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (lines.length === 0)
|
|
100
|
+
return `${targetSid}: ${T("no_cache")}`;
|
|
101
|
+
return `${targetSid}:\n${lines.join("\n")}`;
|
|
102
|
+
}
|
|
103
|
+
return T("unknown_cmd", { cmd });
|
|
104
|
+
},
|
|
105
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { T } from "../i18n.js";
|
|
4
|
+
import { OPENCODE_CONFIG, CONFIG_DIR, MODEL_TYPES, getProviderCreds, getCfg, log } from "../config.js";
|
|
5
|
+
export function localModelList() {
|
|
6
|
+
const models = [];
|
|
7
|
+
try {
|
|
8
|
+
const oc = JSON.parse(readFileSync(OPENCODE_CONFIG, "utf-8"));
|
|
9
|
+
for (const [pName, pVal] of Object.entries(oc.provider || {}))
|
|
10
|
+
for (const mId of Object.keys(pVal.models || {}))
|
|
11
|
+
models.push(`${pName}/${mId}`);
|
|
12
|
+
}
|
|
13
|
+
catch { }
|
|
14
|
+
const modelsJsonPath = join(CONFIG_DIR, ".cache/opencode/models.json");
|
|
15
|
+
if (existsSync(modelsJsonPath)) {
|
|
16
|
+
try {
|
|
17
|
+
const mc = JSON.parse(readFileSync(modelsJsonPath, "utf-8"));
|
|
18
|
+
for (const [pName, pVal] of Object.entries(mc)) {
|
|
19
|
+
const entry = pVal;
|
|
20
|
+
if (entry && typeof entry === "object" && entry.models && typeof entry.models === "object")
|
|
21
|
+
for (const mId of Object.keys(entry.models))
|
|
22
|
+
models.push(`${pName}/${mId}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
log.error(`Failed to read model cache ${modelsJsonPath}`, e instanceof Error ? e : Error(String(e)));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return models;
|
|
30
|
+
}
|
|
31
|
+
export async function listModelsApi(providerNames) {
|
|
32
|
+
const results = await Promise.allSettled([...providerNames].map(async (pName) => {
|
|
33
|
+
const creds = getProviderCreds(pName);
|
|
34
|
+
if (!creds)
|
|
35
|
+
return [];
|
|
36
|
+
const base = creds.baseURL.replace(/\/+$/, "");
|
|
37
|
+
const resp = await fetch(`${base}/models`, {
|
|
38
|
+
headers: { Authorization: `Bearer ${creds.apiKey}` },
|
|
39
|
+
signal: AbortSignal.timeout(8000),
|
|
40
|
+
});
|
|
41
|
+
if (!resp.ok)
|
|
42
|
+
return [];
|
|
43
|
+
const data = await resp.json();
|
|
44
|
+
const list = Array.isArray(data?.data) ? data.data.map((m) => m?.id).filter(Boolean) : [];
|
|
45
|
+
return list.map((m) => `${pName}/${m}`);
|
|
46
|
+
}));
|
|
47
|
+
const models = [];
|
|
48
|
+
for (const r of results)
|
|
49
|
+
if (r.status === "fulfilled")
|
|
50
|
+
for (const m of r.value)
|
|
51
|
+
if (!models.includes(m))
|
|
52
|
+
models.push(m);
|
|
53
|
+
return models;
|
|
54
|
+
}
|
|
55
|
+
export function currentModelSummary() {
|
|
56
|
+
const c = getCfg();
|
|
57
|
+
const lines = MODEL_TYPES.map(t => ` ${t}: ${c.models[t]?.model || T("model_not_set")}`);
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { DESC, T, LANG } from "../i18n.js";
|
|
3
|
+
import { requireModelCfg } from "../config.js";
|
|
4
|
+
import { readTypeStore, registerGeneratedFile, fetchToBuffer, buildGenFilename, extForMime } from "../cache.js";
|
|
5
|
+
import { generateVideo } from "../providers.js";
|
|
6
|
+
import { genError } from "../utils.js";
|
|
7
|
+
const GEN_TIMEOUT = 300000;
|
|
8
|
+
export const textToImageTool = tool({
|
|
9
|
+
description: DESC.text_to_image[LANG],
|
|
10
|
+
args: {
|
|
11
|
+
prompt: tool.schema.string().describe(DESC.gen_args_prompt[LANG]),
|
|
12
|
+
size: tool.schema.string().optional().describe(DESC.gen_args_size[LANG]),
|
|
13
|
+
},
|
|
14
|
+
execute: async ({ prompt, size }, context) => {
|
|
15
|
+
try {
|
|
16
|
+
const cfg = requireModelCfg("image");
|
|
17
|
+
const body = { model: cfg.modelId, prompt };
|
|
18
|
+
if (size)
|
|
19
|
+
body.size = size;
|
|
20
|
+
const resp = await fetch(`${cfg.baseURL}/images/generations`, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json" },
|
|
23
|
+
body: JSON.stringify(body),
|
|
24
|
+
signal: AbortSignal.timeout(GEN_TIMEOUT),
|
|
25
|
+
});
|
|
26
|
+
if (!resp.ok)
|
|
27
|
+
throw new Error(T("err_api", { status: String(resp.status), msg: (await resp.text().catch(() => "unknown")).slice(0, 200) }));
|
|
28
|
+
const data = await resp.json();
|
|
29
|
+
const item = data?.data?.[0];
|
|
30
|
+
if (!item)
|
|
31
|
+
throw new Error(T("gen_no_result"));
|
|
32
|
+
let buffer, mime = "image/png";
|
|
33
|
+
if (item.b64_json)
|
|
34
|
+
buffer = Buffer.from(item.b64_json, "base64");
|
|
35
|
+
else if (item.url) {
|
|
36
|
+
const d = await fetchToBuffer(item.url);
|
|
37
|
+
buffer = d.buffer;
|
|
38
|
+
mime = d.mime;
|
|
39
|
+
}
|
|
40
|
+
else
|
|
41
|
+
throw new Error(T("gen_no_result"));
|
|
42
|
+
const store = readTypeStore(context.sessionID, "image");
|
|
43
|
+
const r = registerGeneratedFile(context.sessionID, "image", buildGenFilename("image", mime, store.nextId), mime, context.messageID, buffer);
|
|
44
|
+
context.metadata?.({ title: "[ImageGen]", metadata: { sessionID: context.sessionID, messageID: context.messageID } });
|
|
45
|
+
return T("image_generated", { name: `image_${r.id}.${extForMime(mime)}`, id: `image:${r.id}`, path: r.path });
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
context.metadata?.({ title: T("meta_error") });
|
|
49
|
+
return genError(e);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
export const textToVideoTool = tool({
|
|
54
|
+
description: DESC.text_to_video[LANG],
|
|
55
|
+
args: {
|
|
56
|
+
prompt: tool.schema.string().describe(DESC.gen_args_prompt[LANG]),
|
|
57
|
+
duration: tool.schema.number().optional().describe(DESC.gen_args_duration[LANG]),
|
|
58
|
+
},
|
|
59
|
+
execute: async ({ prompt, duration }, context) => {
|
|
60
|
+
try {
|
|
61
|
+
const cfg = requireModelCfg("video");
|
|
62
|
+
const url = await generateVideo(cfg, prompt, duration);
|
|
63
|
+
const d = await fetchToBuffer(url);
|
|
64
|
+
const store = readTypeStore(context.sessionID, "video");
|
|
65
|
+
const r = registerGeneratedFile(context.sessionID, "video", buildGenFilename("video", d.mime, store.nextId), d.mime, context.messageID, d.buffer);
|
|
66
|
+
context.metadata?.({ title: "[VideoGen]", metadata: { sessionID: context.sessionID, messageID: context.messageID } });
|
|
67
|
+
return T("video_generated", { name: `video_${r.id}.${extForMime(d.mime)}`, id: `video:${r.id}`, path: r.path });
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
context.metadata?.({ title: T("meta_error") });
|
|
71
|
+
return genError(e);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
export const textToSpeechTool = tool({
|
|
76
|
+
description: DESC.text_to_speech[LANG],
|
|
77
|
+
args: {
|
|
78
|
+
text: tool.schema.string().describe(DESC.gen_args_prompt[LANG]),
|
|
79
|
+
voice: tool.schema.string().optional().describe(DESC.gen_args_voice[LANG]),
|
|
80
|
+
},
|
|
81
|
+
execute: async ({ text, voice }, context) => {
|
|
82
|
+
try {
|
|
83
|
+
const cfg = requireModelCfg("tts");
|
|
84
|
+
const body = { model: cfg.modelId, input: text };
|
|
85
|
+
if (voice)
|
|
86
|
+
body.voice = voice;
|
|
87
|
+
const resp = await fetch(`${cfg.baseURL}/audio/speech`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json" },
|
|
90
|
+
body: JSON.stringify(body),
|
|
91
|
+
signal: AbortSignal.timeout(GEN_TIMEOUT),
|
|
92
|
+
});
|
|
93
|
+
if (!resp.ok)
|
|
94
|
+
throw new Error(T("err_api", { status: String(resp.status), msg: (await resp.text().catch(() => "unknown")).slice(0, 200) }));
|
|
95
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
96
|
+
const mime = resp.headers.get("content-type")?.split(";")[0] || "audio/mpeg";
|
|
97
|
+
const store = readTypeStore(context.sessionID, "tts");
|
|
98
|
+
const r = registerGeneratedFile(context.sessionID, "tts", buildGenFilename("tts", mime, store.nextId), mime, context.messageID, buf);
|
|
99
|
+
context.metadata?.({ title: "[TTS]", metadata: { sessionID: context.sessionID, messageID: context.messageID } });
|
|
100
|
+
return T("tts_generated", { name: `tts_${r.id}.${extForMime(mime)}`, id: `tts:${r.id}`, path: r.path });
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
context.metadata?.({ title: T("meta_error") });
|
|
104
|
+
return genError(e);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { T } from "./i18n.js";
|
|
2
|
+
import { fetchToBuffer, registerGeneratedFile, buildGenFilename, readTypeStore } from "./cache.js";
|
|
3
|
+
export function isAgnesProvider(baseURL) {
|
|
4
|
+
return /agnes/i.test(baseURL);
|
|
5
|
+
}
|
|
6
|
+
export async function postJson(url, apiKey, body, timeout = 300000) {
|
|
7
|
+
const resp = await fetch(url, {
|
|
8
|
+
method: "POST",
|
|
9
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
10
|
+
body: JSON.stringify(body),
|
|
11
|
+
signal: AbortSignal.timeout(timeout),
|
|
12
|
+
});
|
|
13
|
+
if (!resp.ok)
|
|
14
|
+
throw new Error(T("err_api", { status: String(resp.status), msg: (await resp.text().catch(() => "unknown")).slice(0, 200) }));
|
|
15
|
+
return resp.json();
|
|
16
|
+
}
|
|
17
|
+
export async function getJson(url, apiKey, timeout = 30000) {
|
|
18
|
+
const resp = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` }, signal: AbortSignal.timeout(timeout) });
|
|
19
|
+
if (!resp.ok)
|
|
20
|
+
throw new Error(T("err_api", { status: String(resp.status), msg: (await resp.text().catch(() => "unknown")).slice(0, 200) }));
|
|
21
|
+
return resp.json();
|
|
22
|
+
}
|
|
23
|
+
export async function downloadAndRegister(context, type, url) {
|
|
24
|
+
const d = await fetchToBuffer(url);
|
|
25
|
+
const store = readTypeStore(context.sessionID, type);
|
|
26
|
+
const r = registerGeneratedFile(context.sessionID, type, buildGenFilename(type, d.mime, store.nextId), d.mime, context.messageID, d.buffer);
|
|
27
|
+
return { id: r.id, path: r.path, mime: d.mime };
|
|
28
|
+
}
|
|
29
|
+
export function genError(e) {
|
|
30
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
31
|
+
return `[Gen Error] ${msg}`;
|
|
32
|
+
}
|
package/package.json
CHANGED