@kevin5251984/guild 0.2.12
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/bin/guildd.mjs +20 -0
- package/cordis.yml +24 -0
- package/package.json +52 -0
- package/src/agent-file.ts +125 -0
- package/src/browser.ts +668 -0
- package/src/catalog/default-bots.ts +263 -0
- package/src/catalog/skills.ts +128 -0
- package/src/catalog/subagents.ts +70 -0
- package/src/chat-parts.ts +71 -0
- package/src/cli-args.ts +75 -0
- package/src/cli.ts +60 -0
- package/src/compact.ts +355 -0
- package/src/cordis.d.ts +40 -0
- package/src/db.ts +653 -0
- package/src/generate.ts +673 -0
- package/src/handlers.ts +1623 -0
- package/src/harness.ts +326 -0
- package/src/host-agents.ts +137 -0
- package/src/host-browse.ts +199 -0
- package/src/host-skills.ts +150 -0
- package/src/image-gen.ts +270 -0
- package/src/index.ts +12 -0
- package/src/llm.ts +993 -0
- package/src/mcp.ts +563 -0
- package/src/memory.ts +159 -0
- package/src/mention.ts +176 -0
- package/src/oauth.ts +1474 -0
- package/src/plugins/api.ts +8 -0
- package/src/plugins/chat.ts +31 -0
- package/src/plugins/harness.ts +77 -0
- package/src/plugins/llm.ts +50 -0
- package/src/plugins/mcp.ts +58 -0
- package/src/plugins/memory.ts +42 -0
- package/src/plugins/oauth.ts +47 -0
- package/src/plugins/server.ts +126 -0
- package/src/plugins/store.ts +29 -0
- package/src/plugins/tools.ts +79 -0
- package/src/public/buddy.js +432 -0
- package/src/public/chat.css +3045 -0
- package/src/public/chat.html +5834 -0
- package/src/public/favicon-16.png +0 -0
- package/src/public/favicon-16.svg +10 -0
- package/src/public/favicon-32.png +0 -0
- package/src/public/favicon.ico +0 -0
- package/src/public/favicon.svg +13 -0
- package/src/public/i18n.js +663 -0
- package/src/public/index.html +143 -0
- package/src/public/library.html +678 -0
- package/src/public/mcp-add.html +126 -0
- package/src/public/md.js +332 -0
- package/src/public/rpg/inn-street.jpg +0 -0
- package/src/public/settings.html +795 -0
- package/src/public/skills-add.html +212 -0
- package/src/public/studio.html +1181 -0
- package/src/public/style.css +1678 -0
- package/src/public/subagents-add.html +152 -0
- package/src/router.ts +978 -0
- package/src/send-budget.ts +52 -0
- package/src/server.ts +1 -0
- package/src/skill-import.ts +250 -0
- package/src/slash.ts +15 -0
- package/src/start.ts +103 -0
- package/src/store.ts +1208 -0
- package/src/subagent.ts +355 -0
- package/src/tools.ts +818 -0
- package/src/trajectory.ts +339 -0
- package/src/usage.ts +111 -0
- package/vendor/protocol/package.json +19 -0
- package/vendor/protocol/src/index.ts +159 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { parseSkillMarkdown } from "./skill-import.ts";
|
|
5
|
+
|
|
6
|
+
export type HostSkill = {
|
|
7
|
+
id: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
body: string;
|
|
12
|
+
source: "host";
|
|
13
|
+
host: string;
|
|
14
|
+
hostName: string;
|
|
15
|
+
path: string;
|
|
16
|
+
tags: string[];
|
|
17
|
+
createdAt: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type HostTool = {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
dirs: string[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const HOST_TOOLS: HostTool[] = [
|
|
27
|
+
{ id: "claude", name: "Claude", dirs: [".claude/skills"] },
|
|
28
|
+
{ id: "codex", name: "Codex", dirs: [".codex/skills", ".agents/skills"] },
|
|
29
|
+
{ id: "pi", name: "Pi", dirs: [".pi/agent/skills", ".pi/skills"] },
|
|
30
|
+
{ id: "grok", name: "Grok", dirs: [".grok/skills", ".grok/bundled/skills"] },
|
|
31
|
+
{ id: "cursor", name: "Cursor", dirs: [".cursor/skills", ".cursor/skills-cursor"] },
|
|
32
|
+
{ id: "dsh", name: "DSH", dirs: [".dsh/skills"] },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const BODY_CAP = 80_000;
|
|
36
|
+
const LIST_CAP = 400;
|
|
37
|
+
|
|
38
|
+
function skillFilesIn(dir: string): string[] {
|
|
39
|
+
if (!existsSync(dir)) return [];
|
|
40
|
+
try {
|
|
41
|
+
if (!statSync(dir).isDirectory()) return [];
|
|
42
|
+
} catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const found: string[] = [];
|
|
46
|
+
const direct = join(dir, "SKILL.md");
|
|
47
|
+
try {
|
|
48
|
+
if (existsSync(direct) && statSync(direct).isFile()) found.push(direct);
|
|
49
|
+
} catch {
|
|
50
|
+
/* skip */
|
|
51
|
+
}
|
|
52
|
+
let entries: { name: string; isDirectory: () => boolean }[] = [];
|
|
53
|
+
try {
|
|
54
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
55
|
+
} catch {
|
|
56
|
+
return found;
|
|
57
|
+
}
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (entry.name.startsWith(".")) continue;
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
const skill = join(dir, entry.name, "SKILL.md");
|
|
62
|
+
try {
|
|
63
|
+
if (existsSync(skill) && statSync(skill).isFile()) found.push(skill);
|
|
64
|
+
} catch {
|
|
65
|
+
/* skip */
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (entry.isFile() && /\.md$/i.test(entry.name) && entry.name.toLowerCase() !== "skill.md") {
|
|
70
|
+
found.push(join(dir, entry.name));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return found;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readSkill(file: string, tool: HostTool, home: string): HostSkill | null {
|
|
77
|
+
try {
|
|
78
|
+
const raw = readFileSync(file, "utf8");
|
|
79
|
+
const fileName = basename(file);
|
|
80
|
+
const isBundle = fileName.toLowerCase() === "skill.md";
|
|
81
|
+
const slug = isBundle
|
|
82
|
+
? basename(dirname(file))
|
|
83
|
+
: fileName.replace(/\.md$/i, "");
|
|
84
|
+
const parsed = parseSkillMarkdown(raw, slug);
|
|
85
|
+
const st = statSync(file);
|
|
86
|
+
const rel = file.startsWith(home)
|
|
87
|
+
? `~${file.slice(home.length)}`
|
|
88
|
+
: file;
|
|
89
|
+
const body = parsed.body.length > BODY_CAP
|
|
90
|
+
? parsed.body.slice(0, BODY_CAP)
|
|
91
|
+
: parsed.body;
|
|
92
|
+
return {
|
|
93
|
+
id: `host:${tool.id}:${slug}`,
|
|
94
|
+
slug,
|
|
95
|
+
name: parsed.name || slug,
|
|
96
|
+
description: parsed.description || "",
|
|
97
|
+
body,
|
|
98
|
+
source: "host",
|
|
99
|
+
host: tool.id,
|
|
100
|
+
hostName: tool.name,
|
|
101
|
+
path: rel,
|
|
102
|
+
tags: [tool.id],
|
|
103
|
+
createdAt: st.mtime.toISOString(),
|
|
104
|
+
};
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function listHostSkills(opts?: {
|
|
111
|
+
home?: string;
|
|
112
|
+
cwd?: string;
|
|
113
|
+
includeBody?: boolean;
|
|
114
|
+
}): HostSkill[] {
|
|
115
|
+
const home = opts?.home || homedir();
|
|
116
|
+
const cwd = opts?.cwd || process.cwd();
|
|
117
|
+
const includeBody = opts?.includeBody !== false;
|
|
118
|
+
const seen = new Set<string>();
|
|
119
|
+
const out: HostSkill[] = [];
|
|
120
|
+
for (const tool of HOST_TOOLS) {
|
|
121
|
+
const dirs = tool.dirs.flatMap((rel) => [join(home, rel), join(cwd, rel)]);
|
|
122
|
+
for (const dir of dirs) {
|
|
123
|
+
for (const file of skillFilesIn(dir)) {
|
|
124
|
+
let key = file;
|
|
125
|
+
try {
|
|
126
|
+
key = realpathSync(file);
|
|
127
|
+
} catch {
|
|
128
|
+
/* keep file */
|
|
129
|
+
}
|
|
130
|
+
if (seen.has(key)) continue;
|
|
131
|
+
const item = readSkill(file, tool, home);
|
|
132
|
+
if (!item) continue;
|
|
133
|
+
if (!includeBody) item.body = "";
|
|
134
|
+
seen.add(key);
|
|
135
|
+
const clash = out.some((row) => row.id === item.id);
|
|
136
|
+
if (clash) item.id = `${item.id}:${out.length}`;
|
|
137
|
+
out.push(item);
|
|
138
|
+
if (out.length >= LIST_CAP) return out;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return out.sort(
|
|
143
|
+
(a, b) =>
|
|
144
|
+
a.hostName.localeCompare(b.hostName) || a.name.localeCompare(b.name),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function hostSkillTools(): { id: string; name: string }[] {
|
|
149
|
+
return HOST_TOOLS.map((tool) => ({ id: tool.id, name: tool.name }));
|
|
150
|
+
}
|
package/src/image-gen.ts
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { grokCliHeaders, storedAccessToken } from "./oauth.ts";
|
|
5
|
+
import { defaultDataDir } from "./store.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One Imagine HTTP call. Codex waits on the same stream idle as chat:
|
|
9
|
+
* `DEFAULT_STREAM_IDLE_TIMEOUT_MS` = 300_000. DSH community `dsh-image-gen`
|
|
10
|
+
* uses requestTimeoutMs 120_000; official DSH leaves tools undeadlined unless
|
|
11
|
+
* they declare timeoutMs. Pi image generateImages uses the OpenAI SDK 10 min
|
|
12
|
+
* default when timeoutMs is omitted.
|
|
13
|
+
*/
|
|
14
|
+
export const IMAGE_GEN_TIMEOUT_MS = 300_000;
|
|
15
|
+
const ATTEMPT_MS = IMAGE_GEN_TIMEOUT_MS;
|
|
16
|
+
/** DSH `filesApiTimeoutMs` default: one minute to fetch a resolved image. */
|
|
17
|
+
const DOWNLOAD_MS = 60_000;
|
|
18
|
+
|
|
19
|
+
const ASPECT = new Set([
|
|
20
|
+
"auto",
|
|
21
|
+
"1:1",
|
|
22
|
+
"16:9",
|
|
23
|
+
"9:16",
|
|
24
|
+
"4:3",
|
|
25
|
+
"3:4",
|
|
26
|
+
"3:2",
|
|
27
|
+
"2:3",
|
|
28
|
+
"2:1",
|
|
29
|
+
"1:2",
|
|
30
|
+
"19.5:9",
|
|
31
|
+
"9:19.5",
|
|
32
|
+
"20:9",
|
|
33
|
+
"9:20",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
export function isSafeGeneratedName(name: string): boolean {
|
|
37
|
+
return /^[A-Za-z0-9._-]+$/.test(name) && !name.includes("..");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function generatedDir(dataDir: string): string {
|
|
41
|
+
return join(dataDir, "generated");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function generatedPublicPath(name: string): string {
|
|
45
|
+
return `/generated/${name}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveKey(raw: unknown, env: NodeJS.ProcessEnv): string {
|
|
49
|
+
if (typeof raw !== "string") return "";
|
|
50
|
+
const value = raw.trim();
|
|
51
|
+
if (!value) return "";
|
|
52
|
+
if (value.startsWith("$")) return (env[value.slice(1)] ?? "").trim();
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readProviderKey(
|
|
57
|
+
dataDir: string,
|
|
58
|
+
env: NodeJS.ProcessEnv,
|
|
59
|
+
id: string,
|
|
60
|
+
): string {
|
|
61
|
+
try {
|
|
62
|
+
const file = JSON.parse(readFileSync(join(dataDir, "models.json"), "utf8")) as {
|
|
63
|
+
providers?: Record<string, { apiKey?: string }>;
|
|
64
|
+
};
|
|
65
|
+
return resolveKey(file.providers?.[id]?.apiKey, env);
|
|
66
|
+
} catch {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function extForMime(mime: string): string {
|
|
72
|
+
if (mime.includes("png")) return "png";
|
|
73
|
+
if (mime.includes("webp")) return "webp";
|
|
74
|
+
if (mime.includes("gif")) return "gif";
|
|
75
|
+
return "jpg";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type Route = {
|
|
79
|
+
url: string;
|
|
80
|
+
token: string;
|
|
81
|
+
headers: Record<string, string>;
|
|
82
|
+
model: string;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function routes(dataDir: string, env: NodeJS.ProcessEnv): Route[] {
|
|
86
|
+
const grokHeaders = grokCliHeaders();
|
|
87
|
+
const oauth = storedAccessToken(dataDir, "xai") || "";
|
|
88
|
+
const xaiKey =
|
|
89
|
+
readProviderKey(dataDir, env, "xai") || (env.XAI_API_KEY ?? "").trim();
|
|
90
|
+
const openaiKey =
|
|
91
|
+
readProviderKey(dataDir, env, "openai") || (env.OPENAI_API_KEY ?? "").trim();
|
|
92
|
+
const out: Route[] = [];
|
|
93
|
+
if (oauth) {
|
|
94
|
+
out.push({
|
|
95
|
+
url: "https://api.x.ai/v1/images/generations",
|
|
96
|
+
token: oauth,
|
|
97
|
+
headers: grokHeaders,
|
|
98
|
+
model: "grok-imagine-image-2.0",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (xaiKey && xaiKey !== oauth) {
|
|
102
|
+
out.push({
|
|
103
|
+
url: "https://api.x.ai/v1/images/generations",
|
|
104
|
+
token: xaiKey,
|
|
105
|
+
headers: {},
|
|
106
|
+
model: "grok-imagine-image-2.0",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (openaiKey) {
|
|
110
|
+
out.push({
|
|
111
|
+
url: "https://api.openai.com/v1/images/generations",
|
|
112
|
+
token: openaiKey,
|
|
113
|
+
headers: {},
|
|
114
|
+
model: "gpt-image-1",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function saveBytes(
|
|
121
|
+
dataDir: string,
|
|
122
|
+
bytes: Buffer,
|
|
123
|
+
mime: string,
|
|
124
|
+
): { abs: string; name: string; publicPath: string } {
|
|
125
|
+
const name = `${randomUUID()}.${extForMime(mime)}`;
|
|
126
|
+
const dir = generatedDir(dataDir);
|
|
127
|
+
mkdirSync(dir, { recursive: true });
|
|
128
|
+
const abs = join(dir, name);
|
|
129
|
+
writeFileSync(abs, bytes);
|
|
130
|
+
return { abs, name, publicPath: generatedPublicPath(name) };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function downloadImage(
|
|
134
|
+
url: string,
|
|
135
|
+
token: string,
|
|
136
|
+
headers: Record<string, string>,
|
|
137
|
+
): Promise<{ bytes: Buffer; mime: string } | null> {
|
|
138
|
+
const attempts: Record<string, string>[] = [
|
|
139
|
+
{},
|
|
140
|
+
{ authorization: `Bearer ${token}`, ...headers },
|
|
141
|
+
];
|
|
142
|
+
for (const extra of attempts) {
|
|
143
|
+
try {
|
|
144
|
+
const res = await fetch(url, {
|
|
145
|
+
headers: extra,
|
|
146
|
+
signal: AbortSignal.timeout(DOWNLOAD_MS),
|
|
147
|
+
});
|
|
148
|
+
if (!res.ok) continue;
|
|
149
|
+
const mime = (res.headers.get("content-type") || "image/jpeg").split(";")[0];
|
|
150
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
151
|
+
if (bytes.length < 32) continue;
|
|
152
|
+
return { bytes, mime };
|
|
153
|
+
} catch {
|
|
154
|
+
/* try next auth mode */
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type ImageHit = { bytes: Buffer; mime: string; model: string };
|
|
161
|
+
|
|
162
|
+
async function postGenerate(
|
|
163
|
+
route: Route,
|
|
164
|
+
prompt: string,
|
|
165
|
+
aspect: string,
|
|
166
|
+
): Promise<ImageHit | { error: string }> {
|
|
167
|
+
const body: Record<string, unknown> = {
|
|
168
|
+
model: route.model,
|
|
169
|
+
prompt,
|
|
170
|
+
n: 1,
|
|
171
|
+
};
|
|
172
|
+
if (route.model.startsWith("gpt-image")) body.response_format = "b64_json";
|
|
173
|
+
if (aspect && aspect !== "auto") body.aspect_ratio = aspect;
|
|
174
|
+
const res = await fetch(route.url, {
|
|
175
|
+
method: "POST",
|
|
176
|
+
headers: {
|
|
177
|
+
authorization: `Bearer ${route.token}`,
|
|
178
|
+
"content-type": "application/json",
|
|
179
|
+
...route.headers,
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify(body),
|
|
182
|
+
signal: AbortSignal.timeout(ATTEMPT_MS),
|
|
183
|
+
});
|
|
184
|
+
const raw = await res.text();
|
|
185
|
+
if (!res.ok) {
|
|
186
|
+
return { error: `${res.status} ${raw.slice(0, 180)}` };
|
|
187
|
+
}
|
|
188
|
+
let parsed: {
|
|
189
|
+
data?: {
|
|
190
|
+
url?: string;
|
|
191
|
+
b64_json?: string;
|
|
192
|
+
mime_type?: string;
|
|
193
|
+
}[];
|
|
194
|
+
};
|
|
195
|
+
try {
|
|
196
|
+
parsed = JSON.parse(raw) as typeof parsed;
|
|
197
|
+
} catch {
|
|
198
|
+
return { error: "unparseable image response" };
|
|
199
|
+
}
|
|
200
|
+
const item = parsed.data?.[0];
|
|
201
|
+
if (item?.b64_json) {
|
|
202
|
+
return {
|
|
203
|
+
bytes: Buffer.from(item.b64_json, "base64"),
|
|
204
|
+
mime: item.mime_type || "image/png",
|
|
205
|
+
model: route.model,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (item?.url) {
|
|
209
|
+
const got = await downloadImage(item.url, route.token, route.headers);
|
|
210
|
+
if (!got) return { error: "failed to download image url" };
|
|
211
|
+
return { ...got, mime: item.mime_type || got.mime, model: route.model };
|
|
212
|
+
}
|
|
213
|
+
return { error: "image response had no url or b64" };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export async function generateImage(input: {
|
|
217
|
+
prompt: string;
|
|
218
|
+
aspectRatio?: string;
|
|
219
|
+
dataDir?: string;
|
|
220
|
+
env?: NodeJS.ProcessEnv;
|
|
221
|
+
}): Promise<{ text: string; isError: boolean; publicPath?: string }> {
|
|
222
|
+
const prompt = String(input.prompt || "").trim();
|
|
223
|
+
if (!prompt) return { text: "prompt is required", isError: true };
|
|
224
|
+
const aspect = String(input.aspectRatio || "").trim();
|
|
225
|
+
if (aspect && !ASPECT.has(aspect)) {
|
|
226
|
+
return {
|
|
227
|
+
text: `unknown aspect_ratio ${aspect}. Use auto, 1:1, 16:9, 9:16, …`,
|
|
228
|
+
isError: true,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const env = input.env ?? process.env;
|
|
232
|
+
const dataDir = input.dataDir || defaultDataDir(env);
|
|
233
|
+
const list = routes(dataDir, env);
|
|
234
|
+
if (!list.length) {
|
|
235
|
+
return {
|
|
236
|
+
text: "沒有可用的生圖模型。到模型頁連接 xAI Grok 訂閱,或填 xAI / OpenAI API key。",
|
|
237
|
+
isError: true,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const errors: string[] = [];
|
|
241
|
+
const deadline = Date.now() + IMAGE_GEN_TIMEOUT_MS;
|
|
242
|
+
for (const route of list) {
|
|
243
|
+
if (Date.now() >= deadline) break;
|
|
244
|
+
try {
|
|
245
|
+
const hit = await postGenerate(route, prompt, aspect);
|
|
246
|
+
if ("bytes" in hit) {
|
|
247
|
+
const saved = saveBytes(dataDir, hit.bytes, hit.mime);
|
|
248
|
+
return {
|
|
249
|
+
text: [
|
|
250
|
+
`generated with ${hit.model}`,
|
|
251
|
+
`path: ${saved.abs}`,
|
|
252
|
+
`markdown: `,
|
|
253
|
+
].join("\n"),
|
|
254
|
+
isError: false,
|
|
255
|
+
publicPath: saved.publicPath,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
errors.push(`${route.model} @ ${route.url}: ${hit.error}`);
|
|
259
|
+
if (/timeout|aborted/i.test(hit.error)) break;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
262
|
+
errors.push(`${route.model} @ ${route.url}: ${message}`);
|
|
263
|
+
if (/timeout|aborted/i.test(message)) break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
text: `生圖失敗:${errors.slice(0, 3).join(" | ") || "timeout"}`,
|
|
268
|
+
isError: true,
|
|
269
|
+
};
|
|
270
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export {
|
|
2
|
+
healthPayload,
|
|
3
|
+
listBench,
|
|
4
|
+
createBot,
|
|
5
|
+
createLibraryItem,
|
|
6
|
+
generateKind,
|
|
7
|
+
importSkills,
|
|
8
|
+
} from "./handlers.ts";
|
|
9
|
+
export { handleRequest } from "./router.ts";
|
|
10
|
+
export { listenGuildServer } from "./plugins/server.ts";
|
|
11
|
+
export { createGuildContext, startGuildDaemon } from "./start.ts";
|
|
12
|
+
export { GuildStore, defaultDataDir } from "./store.ts";
|