@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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/bin/guildd.mjs +20 -0
  3. package/cordis.yml +24 -0
  4. package/package.json +52 -0
  5. package/src/agent-file.ts +125 -0
  6. package/src/browser.ts +668 -0
  7. package/src/catalog/default-bots.ts +263 -0
  8. package/src/catalog/skills.ts +128 -0
  9. package/src/catalog/subagents.ts +70 -0
  10. package/src/chat-parts.ts +71 -0
  11. package/src/cli-args.ts +75 -0
  12. package/src/cli.ts +60 -0
  13. package/src/compact.ts +355 -0
  14. package/src/cordis.d.ts +40 -0
  15. package/src/db.ts +653 -0
  16. package/src/generate.ts +673 -0
  17. package/src/handlers.ts +1623 -0
  18. package/src/harness.ts +326 -0
  19. package/src/host-agents.ts +137 -0
  20. package/src/host-browse.ts +199 -0
  21. package/src/host-skills.ts +150 -0
  22. package/src/image-gen.ts +270 -0
  23. package/src/index.ts +12 -0
  24. package/src/llm.ts +993 -0
  25. package/src/mcp.ts +563 -0
  26. package/src/memory.ts +159 -0
  27. package/src/mention.ts +176 -0
  28. package/src/oauth.ts +1474 -0
  29. package/src/plugins/api.ts +8 -0
  30. package/src/plugins/chat.ts +31 -0
  31. package/src/plugins/harness.ts +77 -0
  32. package/src/plugins/llm.ts +50 -0
  33. package/src/plugins/mcp.ts +58 -0
  34. package/src/plugins/memory.ts +42 -0
  35. package/src/plugins/oauth.ts +47 -0
  36. package/src/plugins/server.ts +126 -0
  37. package/src/plugins/store.ts +29 -0
  38. package/src/plugins/tools.ts +79 -0
  39. package/src/public/buddy.js +432 -0
  40. package/src/public/chat.css +3045 -0
  41. package/src/public/chat.html +5834 -0
  42. package/src/public/favicon-16.png +0 -0
  43. package/src/public/favicon-16.svg +10 -0
  44. package/src/public/favicon-32.png +0 -0
  45. package/src/public/favicon.ico +0 -0
  46. package/src/public/favicon.svg +13 -0
  47. package/src/public/i18n.js +663 -0
  48. package/src/public/index.html +143 -0
  49. package/src/public/library.html +678 -0
  50. package/src/public/mcp-add.html +126 -0
  51. package/src/public/md.js +332 -0
  52. package/src/public/rpg/inn-street.jpg +0 -0
  53. package/src/public/settings.html +795 -0
  54. package/src/public/skills-add.html +212 -0
  55. package/src/public/studio.html +1181 -0
  56. package/src/public/style.css +1678 -0
  57. package/src/public/subagents-add.html +152 -0
  58. package/src/router.ts +978 -0
  59. package/src/send-budget.ts +52 -0
  60. package/src/server.ts +1 -0
  61. package/src/skill-import.ts +250 -0
  62. package/src/slash.ts +15 -0
  63. package/src/start.ts +103 -0
  64. package/src/store.ts +1208 -0
  65. package/src/subagent.ts +355 -0
  66. package/src/tools.ts +818 -0
  67. package/src/trajectory.ts +339 -0
  68. package/src/usage.ts +111 -0
  69. package/vendor/protocol/package.json +19 -0
  70. package/vendor/protocol/src/index.ts +159 -0
@@ -0,0 +1,126 @@
1
+ <!doctype html>
2
+ <html lang="zh-Hant">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" href="/favicon.ico" sizes="32x32" />
7
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
8
+ <link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" />
9
+ <link rel="icon" href="/favicon-16.png" type="image/png" sizes="16x16" />
10
+ <title>Guild — 連接 MCP</title>
11
+ <link rel="stylesheet" href="/style.css" />
12
+ <script src="/i18n.js"></script>
13
+ </head>
14
+ <body class="app skills-add-page" data-i18n-page="mcpAdd">
15
+ <aside class="sidebar">
16
+ <div class="side-brand"><a class="logo" href="/">Guild</a></div>
17
+ <nav class="side-nav">
18
+ <a href="/" data-i18n="nav.chat">大廳</a>
19
+ <a href="/studio" data-i18n="nav.studio">編制</a>
20
+ <a class="on" href="/mcp" data-i18n="nav.library">工坊</a>
21
+ <a href="/settings" data-i18n="nav.settings">模型</a>
22
+ </nav>
23
+ <div class="sidebar-resizer" data-i18n-title="sidebar.resize" title="拖曳調整寬度"></div>
24
+ </aside>
25
+ <section class="pane">
26
+ <header class="pane-head">
27
+ <h1 data-i18n="mcpAdd.title">連接 MCP</h1>
28
+ </header>
29
+ <div class="page-body">
30
+ <p class="lede" data-i18n="mcpAdd.lede">stdio 伺服器(command + args),跟 Codex config.toml / Claude MCP JSON 同一套。這不是技能,不要寫進技能庫。</p>
31
+ <p id="flash" class="muted"></p>
32
+ <section class="card">
33
+ <label data-i18n="mcpAdd.name">名稱</label>
34
+ <input id="name" placeholder="context7" />
35
+ <label data-i18n="mcpAdd.command">命令</label>
36
+ <input id="command" placeholder="npx" />
37
+ <label data-i18n="mcpAdd.args">參數(空白分隔)</label>
38
+ <input id="args" placeholder="-y @upstash/context7-mcp" />
39
+ <label data-i18n="mcpAdd.env">環境變數 KEY=value,一行一個</label>
40
+ <textarea id="env" class="body" rows="4" placeholder="API_KEY=…"></textarea>
41
+ <button type="button" id="save" data-i18n="mcpAdd.save">連接</button>
42
+ </section>
43
+ <section class="card">
44
+ <h2 data-i18n="mcpAdd.hostTitle">本機已發現</h2>
45
+ <p class="sub" data-i18n="mcp.host">本機 CLI,對話可直接用</p>
46
+ <div id="host-list" class="host-list"></div>
47
+ </section>
48
+ </div>
49
+ </section>
50
+ <script>
51
+ const flash = document.getElementById("flash");
52
+ function setFlash(kind, text) {
53
+ flash.className = kind;
54
+ flash.textContent = text;
55
+ }
56
+ function parseEnv(text) {
57
+ const env = {};
58
+ String(text || "")
59
+ .split(/\n/)
60
+ .forEach((line) => {
61
+ const eq = line.indexOf("=");
62
+ if (eq <= 0) return;
63
+ env[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
64
+ });
65
+ return env;
66
+ }
67
+ document.getElementById("save").addEventListener("click", async () => {
68
+ const name = document.getElementById("name").value.trim();
69
+ const command = document.getElementById("command").value.trim();
70
+ if (!name || !command) {
71
+ setFlash("error", t("mcpAdd.needName"));
72
+ return;
73
+ }
74
+ const args = document.getElementById("args").value.trim()
75
+ ? document.getElementById("args").value.trim().split(/\s+/)
76
+ : [];
77
+ const env = parseEnv(document.getElementById("env").value);
78
+ const res = await fetch("/mcp/servers", {
79
+ method: "POST",
80
+ headers: { "content-type": "application/json" },
81
+ body: JSON.stringify({ name, command, args, env }),
82
+ });
83
+ const body = await res.json().catch(() => ({}));
84
+ if (!res.ok) {
85
+ setFlash("error", body.error || t("saveFailed"));
86
+ return;
87
+ }
88
+ setFlash("health", t("mcpAdd.saved", { name: body.name || name }));
89
+ });
90
+ function escapeHtml(value) {
91
+ return String(value || "")
92
+ .replace(/&/g, "&amp;")
93
+ .replace(/</g, "&lt;")
94
+ .replace(/"/g, "&quot;");
95
+ }
96
+ fetch("/mcp/host")
97
+ .then((r) => r.json())
98
+ .then((rows) => {
99
+ const root = document.getElementById("host-list");
100
+ if (!Array.isArray(rows) || !rows.length) {
101
+ root.innerHTML = '<p class="muted">' + t("mcpAdd.noHost") + "</p>";
102
+ return;
103
+ }
104
+ root.innerHTML = rows
105
+ .map((row) => {
106
+ const cmd = [row.launch && row.launch.command]
107
+ .concat((row.launch && row.launch.args) || [])
108
+ .filter(Boolean)
109
+ .join(" ");
110
+ const meta = [row.host, cmd].filter(Boolean).join(" · ");
111
+ return (
112
+ '<div class="host-row"><div class="host-copy"><strong>' +
113
+ escapeHtml(row.name) +
114
+ '</strong><p class="sub" title="' +
115
+ escapeHtml(meta) +
116
+ '">' +
117
+ escapeHtml(meta) +
118
+ "</p></div></div>"
119
+ );
120
+ })
121
+ .join("");
122
+ })
123
+ .catch(() => {});
124
+ </script>
125
+ </body>
126
+ </html>
@@ -0,0 +1,332 @@
1
+ /** Small Markdown renderer for assistant replies. Escapes HTML first. */
2
+ function escapeMd(value) {
3
+ return String(value)
4
+ .replace(/&/g, "&amp;")
5
+ .replace(/</g, "&lt;")
6
+ .replace(/>/g, "&gt;");
7
+ }
8
+
9
+ function isFileCode(text) {
10
+ const s = String(text).trim();
11
+ if (!s || s.length > 180 || /\s/.test(s) || /^https?:/i.test(s)) return false;
12
+ if (
13
+ /\.(?:ts|tsx|js|jsx|mjs|cjs|json|md|html|css|scss|rs|py|go|kt|java|swift|toml|ya?ml|sh|bash|sql|proto|txt|xml|svg|lock)$/i.test(
14
+ s,
15
+ )
16
+ ) {
17
+ return true;
18
+ }
19
+ return /[A-Za-z0-9]\/[A-Za-z0-9._@+-]/.test(s);
20
+ }
21
+
22
+ function inlineMd(text) {
23
+ let out = escapeMd(text);
24
+ out = out.replace(
25
+ /!\[([^\]]*)\]\((\/generated\/[A-Za-z0-9._-]+|https?:[^)\s]+)\)/g,
26
+ function (_m, alt, href) {
27
+ const src = String(href).replace(/"/g, "");
28
+ const label = alt || "image";
29
+ return (
30
+ '<img class="md-img" src="' +
31
+ src +
32
+ '" alt="' +
33
+ label +
34
+ '" loading="lazy">'
35
+ );
36
+ },
37
+ );
38
+ out = out.replace(/\[([^\]]+)\]\((https?:[^)\s]+)\)/g, function (_m, label, href) {
39
+ return (
40
+ '<a href="' +
41
+ href.replace(/"/g, "") +
42
+ '" target="_blank" rel="noopener noreferrer">' +
43
+ label +
44
+ "</a>"
45
+ );
46
+ });
47
+ out = out.replace(/`([^`]+)`/g, function (_m, code) {
48
+ const cls = isFileCode(code) ? ' class="md-file"' : "";
49
+ return "<code" + cls + ">" + code + "</code>";
50
+ });
51
+ out = out.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
52
+ out = out.replace(/__([^_]+)__/g, "<strong>$1</strong>");
53
+ out = out.replace(/(^|[^\w*])\*([^*\n]+)\*(?=[^\w*]|$)/g, "$1<em>$2</em>");
54
+ return out;
55
+ }
56
+
57
+ function fenceIconFile() {
58
+ return '<svg class="md-fence-ico" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 1.6h4.1L9.2 3.7v6.7H3V1.6Z" stroke="currentColor" stroke-width="1.1"/><path d="M7 1.6v2.1h2.2" stroke="currentColor" stroke-width="1.1"/></svg>';
59
+ }
60
+ function fenceIconAt() {
61
+ return '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><circle cx="7" cy="7" r="4.4" stroke="currentColor" stroke-width="1.2"/><path d="M9.15 7v1.05a1.35 1.35 0 0 0 2.5.45" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><path d="M9.15 5.35v3.35" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>';
62
+ }
63
+ function fenceIconCopy() {
64
+ return '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><rect x="4.6" y="4.6" width="6.8" height="6.8" rx="1.3" stroke="currentColor" stroke-width="1.2"/><path d="M9.4 4.25V3.5A1.4 1.4 0 0 0 8 2.1H3.5A1.4 1.4 0 0 0 2.1 3.5V8A1.4 1.4 0 0 0 3.5 9.4H4.3" stroke="currentColor" stroke-width="1.2"/></svg>';
65
+ }
66
+ function fenceIconExpand() {
67
+ return '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M8.2 2.2h3.6v3.6M5.8 11.8H2.2V8.2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.5 2.5 8.2 5.8M2.5 11.5 5.8 8.2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>';
68
+ }
69
+ function splitTableRow(line) {
70
+ let s = String(line).trim();
71
+ if (s.startsWith("|")) s = s.slice(1);
72
+ if (s.endsWith("|") && !/(^|[^\\])\\\|$/.test(s)) s = s.slice(0, -1);
73
+ const cells = [];
74
+ let cur = "";
75
+ for (let i = 0; i < s.length; i += 1) {
76
+ if (s[i] === "\\" && s[i + 1] === "|") {
77
+ cur += "|";
78
+ i += 1;
79
+ } else if (s[i] === "|") {
80
+ cells.push(cur.trim());
81
+ cur = "";
82
+ } else {
83
+ cur += s[i];
84
+ }
85
+ }
86
+ cells.push(cur.trim());
87
+ return cells;
88
+ }
89
+
90
+ function isTableSep(line) {
91
+ const cells = splitTableRow(line);
92
+ return (
93
+ cells.length > 0 &&
94
+ cells.every((cell) => /^:?-{3,}:?$/.test(cell.replace(/\s/g, "")))
95
+ );
96
+ }
97
+
98
+ function isTableRow(line) {
99
+ return /\|/.test(line) && !/^%%FENCE\d+%%$/.test(String(line).trim());
100
+ }
101
+
102
+ function isTableStart(lines, i) {
103
+ return (
104
+ i + 1 < lines.length &&
105
+ isTableRow(lines[i]) &&
106
+ !isTableSep(lines[i]) &&
107
+ isTableSep(lines[i + 1])
108
+ );
109
+ }
110
+
111
+ function alignClass(spec) {
112
+ const s = String(spec).replace(/\s/g, "");
113
+ const left = s.startsWith(":");
114
+ const right = s.endsWith(":");
115
+ if (left && right) return "md-al-center";
116
+ if (right) return "md-al-right";
117
+ if (left) return "md-al-left";
118
+ return "";
119
+ }
120
+
121
+ function renderTableCell(tag, text, align) {
122
+ return (
123
+ "<" +
124
+ tag +
125
+ (align ? ' class="' + align + '"' : "") +
126
+ ">" +
127
+ inlineMd(text) +
128
+ "</" +
129
+ tag +
130
+ ">"
131
+ );
132
+ }
133
+
134
+ function renderTable(header, aligns, rows) {
135
+ const cols = Math.max(
136
+ header.length,
137
+ aligns.length,
138
+ ...rows.map((row) => row.length),
139
+ 1,
140
+ );
141
+ const head = [];
142
+ for (let c = 0; c < cols; c += 1) {
143
+ head.push(renderTableCell("th", header[c] || "", aligns[c] || ""));
144
+ }
145
+ const body = rows
146
+ .map((row) => {
147
+ const cells = [];
148
+ for (let c = 0; c < cols; c += 1) {
149
+ cells.push(renderTableCell("td", row[c] || "", aligns[c] || ""));
150
+ }
151
+ return "<tr>" + cells.join("") + "</tr>";
152
+ })
153
+ .join("");
154
+ return (
155
+ '<div class="md-table-wrap"><table class="md-table"><thead><tr>' +
156
+ head.join("") +
157
+ "</tr></thead>" +
158
+ (body ? "<tbody>" + body + "</tbody>" : "") +
159
+ "</table></div>"
160
+ );
161
+ }
162
+
163
+ function isHtmlPreviewLang(lang) {
164
+ return /^(html|htm|svg)$/i.test(String(lang || ""));
165
+ }
166
+
167
+ function tt(key, fallback) {
168
+ return typeof t === "function" ? t(key) : fallback;
169
+ }
170
+
171
+ function renderFence(lang, code) {
172
+ const label = lang || "text";
173
+ const body = escapeMd(String(code).replace(/\n$/, ""));
174
+ const preview = isHtmlPreviewLang(lang);
175
+ const tabs = preview
176
+ ? '<span class="md-fence-tabs">' +
177
+ '<button type="button" class="md-fence-tab on" data-html-view="preview">' +
178
+ tt("html.preview", "預覽") +
179
+ "</button>" +
180
+ '<button type="button" class="md-fence-tab" data-html-view="code">' +
181
+ tt("html.code", "原始碼") +
182
+ "</button>" +
183
+ "</span>"
184
+ : "";
185
+ const frame = preview
186
+ ? '<iframe class="md-html-frame" sandbox="allow-scripts" title="HTML preview"></iframe>' +
187
+ '<textarea class="md-html-src" hidden>' +
188
+ body +
189
+ "</textarea>"
190
+ : "";
191
+ return (
192
+ '<div class="md-fence' +
193
+ (preview ? " md-html-preview" : "") +
194
+ '"' +
195
+ (preview ? ' data-view="preview"' : "") +
196
+ ">" +
197
+ '<div class="md-fence-bar">' +
198
+ '<span class="md-fence-lang">' +
199
+ fenceIconFile() +
200
+ escapeMd(label) +
201
+ "</span>" +
202
+ tabs +
203
+ '<span class="md-fence-acts">' +
204
+ (preview
205
+ ? '<button type="button" class="md-fence-btn" data-html-expand title="' +
206
+ tt("html.expand", "放大預覽") +
207
+ '" aria-label="' +
208
+ tt("html.expand", "放大預覽") +
209
+ '">' +
210
+ fenceIconExpand() +
211
+ "</button>"
212
+ : "") +
213
+ '<button type="button" class="md-fence-btn" data-fence-input title="' +
214
+ tt("fence.toInput", "加入輸入框") +
215
+ '" aria-label="' +
216
+ tt("fence.toInput", "加入輸入框") +
217
+ '">' +
218
+ fenceIconAt() +
219
+ "</button>" +
220
+ '<button type="button" class="md-fence-btn" data-fence-copy title="' +
221
+ tt("copy", "複製") +
222
+ '" aria-label="' +
223
+ tt("copy", "複製") +
224
+ '">' +
225
+ fenceIconCopy() +
226
+ "</button>" +
227
+ "</span></div>" +
228
+ frame +
229
+ '<pre class="md-pre"' +
230
+ (preview ? " hidden" : "") +
231
+ "><code>" +
232
+ body +
233
+ "</code></pre></div>"
234
+ );
235
+ }
236
+
237
+ function renderMarkdown(raw) {
238
+ const source = String(raw ?? "").replace(/\r\n/g, "\n");
239
+ const fences = [];
240
+ const withFences = source.replace(/```([a-zA-Z0-9_-]*)\n?([\s\S]*?)```/g, function (_m, lang, code) {
241
+ const i = fences.length;
242
+ fences.push(renderFence(lang, code));
243
+ return "\n\n%%FENCE" + i + "%%\n\n";
244
+ });
245
+ const lines = withFences.split("\n");
246
+ const html = [];
247
+ let i = 0;
248
+ while (i < lines.length) {
249
+ const line = lines[i];
250
+ const fence = line.trim().match(/^%%FENCE(\d+)%%$/);
251
+ if (fence) {
252
+ html.push(fences[Number(fence[1])] || "");
253
+ i += 1;
254
+ continue;
255
+ }
256
+ if (/^\s*$/.test(line)) {
257
+ i += 1;
258
+ continue;
259
+ }
260
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
261
+ if (heading) {
262
+ const level = heading[1].length;
263
+ html.push("<h" + level + ">" + inlineMd(heading[2]) + "</h" + level + ">");
264
+ i += 1;
265
+ continue;
266
+ }
267
+ if (/^>\s?/.test(line)) {
268
+ const quoted = [];
269
+ while (i < lines.length && /^>\s?/.test(lines[i])) {
270
+ quoted.push(lines[i].replace(/^>\s?/, ""));
271
+ i += 1;
272
+ }
273
+ html.push("<blockquote>" + inlineMd(quoted.join(" ")) + "</blockquote>");
274
+ continue;
275
+ }
276
+ if (/^[-*]\s+/.test(line)) {
277
+ const items = [];
278
+ while (i < lines.length && /^[-*]\s+/.test(lines[i])) {
279
+ items.push("<li>" + inlineMd(lines[i].replace(/^[-*]\s+/, "")) + "</li>");
280
+ i += 1;
281
+ }
282
+ html.push("<ul>" + items.join("") + "</ul>");
283
+ continue;
284
+ }
285
+ if (/^\d+\.\s+/.test(line)) {
286
+ const items = [];
287
+ while (i < lines.length && /^\d+\.\s+/.test(lines[i])) {
288
+ items.push("<li>" + inlineMd(lines[i].replace(/^\d+\.\s+/, "")) + "</li>");
289
+ i += 1;
290
+ }
291
+ html.push("<ol>" + items.join("") + "</ol>");
292
+ continue;
293
+ }
294
+ if (isTableStart(lines, i)) {
295
+ const header = splitTableRow(lines[i]);
296
+ const aligns = splitTableRow(lines[i + 1]).map(alignClass);
297
+ i += 2;
298
+ const rows = [];
299
+ while (
300
+ i < lines.length &&
301
+ isTableRow(lines[i]) &&
302
+ !isTableSep(lines[i]) &&
303
+ !/^\s*$/.test(lines[i])
304
+ ) {
305
+ rows.push(splitTableRow(lines[i]));
306
+ i += 1;
307
+ }
308
+ html.push(renderTable(header, aligns, rows));
309
+ continue;
310
+ }
311
+ const para = [];
312
+ while (
313
+ i < lines.length &&
314
+ !/^\s*$/.test(lines[i]) &&
315
+ !/^#{1,3}\s+/.test(lines[i]) &&
316
+ !/^[-*]\s+/.test(lines[i]) &&
317
+ !/^\d+\.\s+/.test(lines[i]) &&
318
+ !/^>\s?/.test(lines[i]) &&
319
+ !/^%%FENCE\d+%%$/.test(lines[i].trim()) &&
320
+ !isTableStart(lines, i)
321
+ ) {
322
+ para.push(lines[i]);
323
+ i += 1;
324
+ }
325
+ html.push("<p>" + inlineMd(para.join("\n")).replace(/\n/g, "<br>") + "</p>");
326
+ }
327
+ return html.join("");
328
+ }
329
+
330
+ if (typeof module !== "undefined" && module.exports) {
331
+ module.exports = { renderMarkdown, inlineMd };
332
+ }
Binary file