batter-pixso-mcp 0.3.0 → 0.5.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/dist/index.js +1518 -1582
- package/dist/plugin/code.js +257 -128
- package/dist/plugin/ui.html +51 -49
- package/package.json +4 -4
- package/standalone/index.js +220 -99
- package/standalone/plugin/code.js +257 -128
- package/standalone/plugin/ui.html +51 -49
package/dist/index.js
CHANGED
|
@@ -1,50 +1,69 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
-
};
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// src/config.ts
|
|
13
|
-
function log(...args) {
|
|
14
|
-
console.error("[batter-pixso-mcp]", ...args);
|
|
15
|
-
}
|
|
16
|
-
var config, VERSION;
|
|
17
|
-
var init_config = __esm({
|
|
18
|
-
"src/config.ts"() {
|
|
19
|
-
"use strict";
|
|
20
|
-
config = {
|
|
21
|
-
/** 插件通信 HTTP 端口,仅监听 127.0.0.1 */
|
|
22
|
-
httpPort: Number(process.env.HTTP_PORT ?? 3210),
|
|
23
|
-
/** hub 空闲自毁阈值(无插件、无 agent 请求),默认 30 分钟 */
|
|
24
|
-
hubIdleMs: Number(process.env.HUB_IDLE_MS ?? 30 * 60 * 1e3),
|
|
25
|
-
/** 产物图片保留时长,默认 7 天 */
|
|
26
|
-
assetTtlMs: Number(process.env.ASSET_TTL_DAYS ?? 7) * 24 * 60 * 60 * 1e3
|
|
27
|
-
};
|
|
28
|
-
VERSION = "0.3.0";
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
2
|
|
|
32
|
-
// src/assets.ts
|
|
3
|
+
// src/plugin/assets.ts
|
|
33
4
|
import { createHash } from "node:crypto";
|
|
34
5
|
import { mkdir, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
6
|
+
import { join as join2 } from "node:path";
|
|
7
|
+
|
|
8
|
+
// src/config.ts
|
|
35
9
|
import { tmpdir } from "node:os";
|
|
36
10
|
import { join } from "node:path";
|
|
37
|
-
|
|
38
|
-
|
|
11
|
+
var VERSION = true ? "0.5.0" : "0.0.0-dev";
|
|
12
|
+
var HUB_NAME = "batter-pixso-mcp";
|
|
13
|
+
var ASSETS_DIR = join(tmpdir(), "batter-pixso-assets");
|
|
14
|
+
var config = {
|
|
15
|
+
/** 插件通信 HTTP 端口,仅监听 127.0.0.1。与插件 UI 的 DEFAULT_PORT 互为默认对端,改默认需同步 */
|
|
16
|
+
httpPort: Number(process.env.HTTP_PORT ?? 3210),
|
|
17
|
+
/** hub 空闲自毁阈值(无插件、无 agent 请求),默认 30 分钟 */
|
|
18
|
+
hubIdleMs: Number(process.env.HUB_IDLE_MS ?? 30 * 60 * 1e3),
|
|
19
|
+
/** 产物图片保留时长,默认 7 天 */
|
|
20
|
+
assetTtlMs: Number(process.env.ASSET_TTL_DAYS ?? 7) * 24 * 60 * 60 * 1e3
|
|
21
|
+
};
|
|
22
|
+
function hubBaseUrl() {
|
|
23
|
+
return `http://127.0.0.1:${config.httpPort}`;
|
|
39
24
|
}
|
|
25
|
+
function log(...args) {
|
|
26
|
+
console.error("[batter-pixso-mcp]", ...args);
|
|
27
|
+
}
|
|
28
|
+
function withResolvers() {
|
|
29
|
+
let resolve;
|
|
30
|
+
let reject;
|
|
31
|
+
const promise = new Promise((res, rej) => {
|
|
32
|
+
resolve = res;
|
|
33
|
+
reject = rej;
|
|
34
|
+
});
|
|
35
|
+
return { promise, resolve, reject };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/plugin/assets.ts
|
|
39
|
+
var EXT_BY_FORMAT = {
|
|
40
|
+
PNG: ".png",
|
|
41
|
+
SVG: ".svg",
|
|
42
|
+
JPG: ".jpg",
|
|
43
|
+
JPEG: ".jpg",
|
|
44
|
+
WEBP: ".webp",
|
|
45
|
+
PDF: ".pdf"
|
|
46
|
+
// export_image 尊重设计师导出标记,标记可能指定 PDF
|
|
47
|
+
};
|
|
48
|
+
var dirReady = null;
|
|
40
49
|
async function saveAsset(bytes, format) {
|
|
41
|
-
|
|
50
|
+
dirReady ??= mkdir(ASSETS_DIR, { recursive: true });
|
|
51
|
+
await dirReady;
|
|
42
52
|
const ext = EXT_BY_FORMAT[format.toUpperCase()] ?? ".bin";
|
|
43
53
|
const name = `${createHash("sha1").update(bytes).digest("hex").slice(0, 16)}${ext}`;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
await writeFile(join2(ASSETS_DIR, name), bytes);
|
|
55
|
+
log(`asset saved: ${name} (${bytes.length} bytes)`);
|
|
56
|
+
return name;
|
|
57
|
+
}
|
|
58
|
+
function substituteAssets(result, assets) {
|
|
59
|
+
if (!assets.length) return result;
|
|
60
|
+
const nameByKey = new Map(assets.map((a) => [a.key, a.name]));
|
|
61
|
+
return JSON.parse(
|
|
62
|
+
JSON.stringify(
|
|
63
|
+
result,
|
|
64
|
+
(_k, v) => typeof v === "string" ? nameByKey.get(v) ?? v : v
|
|
65
|
+
)
|
|
66
|
+
);
|
|
48
67
|
}
|
|
49
68
|
async function cleanOldAssets(ttlMs) {
|
|
50
69
|
let removed = 0;
|
|
@@ -57,7 +76,7 @@ async function cleanOldAssets(ttlMs) {
|
|
|
57
76
|
}
|
|
58
77
|
for (const name of names) {
|
|
59
78
|
try {
|
|
60
|
-
const p =
|
|
79
|
+
const p = join2(ASSETS_DIR, name);
|
|
61
80
|
const s = await stat(p);
|
|
62
81
|
if (s.isFile() && s.mtimeMs < cutoff) {
|
|
63
82
|
await unlink(p);
|
|
@@ -69,111 +88,125 @@ async function cleanOldAssets(ttlMs) {
|
|
|
69
88
|
if (removed > 0) log(`assets cleanup: removed ${removed} expired file(s)`);
|
|
70
89
|
return removed;
|
|
71
90
|
}
|
|
72
|
-
var ASSETS_DIR, EXT_BY_FORMAT;
|
|
73
|
-
var init_assets = __esm({
|
|
74
|
-
"src/assets.ts"() {
|
|
75
|
-
"use strict";
|
|
76
|
-
init_config();
|
|
77
|
-
ASSETS_DIR = join(tmpdir(), "batter-pixso-assets");
|
|
78
|
-
EXT_BY_FORMAT = {
|
|
79
|
-
PNG: ".png",
|
|
80
|
-
SVG: ".svg",
|
|
81
|
-
JPG: ".jpg",
|
|
82
|
-
JPEG: ".jpg",
|
|
83
|
-
WEBP: ".webp",
|
|
84
|
-
PDF: ".pdf"
|
|
85
|
-
// export_image 尊重设计师导出标记,标记可能指定 PDF
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
91
|
|
|
90
|
-
// src/
|
|
92
|
+
// src/plugin/sessions.ts
|
|
91
93
|
import { randomUUID } from "node:crypto";
|
|
92
|
-
var ONLINE_WINDOW_MS
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
s.connected = connected;
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
updateContext(id, meta) {
|
|
125
|
-
const s = this.touch(id);
|
|
126
|
-
if (!s) return false;
|
|
127
|
-
s.meta = { ...s.meta, ...meta };
|
|
128
|
-
log(
|
|
129
|
-
`context updated [${id.slice(0, 8)}]: ${s.meta.fileName ?? "?"} / ${s.meta.pageName ?? "?"} / ${s.meta.selection?.length ?? 0} selected`
|
|
130
|
-
);
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
list() {
|
|
134
|
-
return [...this.sessions.values()].map((s) => this.toInfo(s));
|
|
135
|
-
}
|
|
136
|
-
anyOnline() {
|
|
137
|
-
return [...this.sessions.values()].some((s) => this.isOnline(s));
|
|
94
|
+
var ONLINE_WINDOW_MS = 45e3;
|
|
95
|
+
function unknownTargetError(target) {
|
|
96
|
+
return new Error(`\u627E\u4E0D\u5230\u76EE\u6807\u63D2\u4EF6\u4F1A\u8BDD\uFF1A${target}\u3002\u53EF\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u8FDE\u63A5\u7684\u4F1A\u8BDD`);
|
|
97
|
+
}
|
|
98
|
+
var SessionRegistry = class {
|
|
99
|
+
sessions = /* @__PURE__ */ new Map();
|
|
100
|
+
lastActiveId = null;
|
|
101
|
+
/** 会话亲和:fileKey → 上一次路由使用的 sessionId(仍在线时优先,避免同文件多实例间漂移) */
|
|
102
|
+
lastUsedByFileKey = /* @__PURE__ */ new Map();
|
|
103
|
+
isOnline(s) {
|
|
104
|
+
return s.connected || Date.now() - s.lastSeen < ONLINE_WINDOW_MS;
|
|
105
|
+
}
|
|
106
|
+
/** fileKey 寻址候选:该文件的在线会话(谁先 pong 由 ping 竞速决定,此处不排序) */
|
|
107
|
+
candidatesForFileKey(fileKey) {
|
|
108
|
+
return [...this.sessions.values()].filter((s) => s.meta.fileKey === fileKey && this.isOnline(s));
|
|
109
|
+
}
|
|
110
|
+
/** 亲和查询:上一次为该 fileKey 服务的 sessionId */
|
|
111
|
+
affinityIdOf(fileKey) {
|
|
112
|
+
return this.lastUsedByFileKey.get(fileKey);
|
|
113
|
+
}
|
|
114
|
+
/** 亲和钉住:fileKey 寻址探活成功后记录(下次优先) */
|
|
115
|
+
pinAffinity(fileKey, sessionId) {
|
|
116
|
+
this.lastUsedByFileKey.set(fileKey, sessionId);
|
|
117
|
+
}
|
|
118
|
+
/** 剔除会话(幽灵剔除/ping 判死共用):清理亲和引用;存活但繁忙被误剔的插件会在 SSE 断开或上报 404 后自重注册 */
|
|
119
|
+
evict(id) {
|
|
120
|
+
if (this.sessions.delete(id)) {
|
|
121
|
+
for (const [k, v] of this.lastUsedByFileKey) {
|
|
122
|
+
if (v === id) this.lastUsedByFileKey.delete(k);
|
|
138
123
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
if (this.lastActiveId) {
|
|
149
|
-
const s = this.sessions.get(this.lastActiveId);
|
|
150
|
-
if (s && this.isOnline(s)) return s;
|
|
124
|
+
log(`session evicted: ${id.slice(0, 8)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
register(meta = {}) {
|
|
128
|
+
if (meta.fileKey) {
|
|
129
|
+
for (const s of [...this.sessions.values()]) {
|
|
130
|
+
if (s.meta.fileKey === meta.fileKey && !this.isOnline(s)) {
|
|
131
|
+
this.evict(s.id);
|
|
151
132
|
}
|
|
152
|
-
const fallback = [...this.sessions.values()].find((s) => this.isOnline(s));
|
|
153
|
-
if (fallback) return fallback;
|
|
154
|
-
throw new Error("Pixso \u63D2\u4EF6\u672A\u8FDE\u63A5\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6");
|
|
155
|
-
}
|
|
156
|
-
assertOnline(s) {
|
|
157
|
-
if (!this.isOnline(s)) throw new Error(`\u76EE\u6807\u63D2\u4EF6\u5DF2\u79BB\u7EBF\uFF1A${s.meta.fileName ?? s.id}`);
|
|
158
|
-
return s;
|
|
159
|
-
}
|
|
160
|
-
toInfo(s) {
|
|
161
|
-
return {
|
|
162
|
-
sessionId: s.id,
|
|
163
|
-
fileKey: s.meta.fileKey,
|
|
164
|
-
fileName: s.meta.fileName,
|
|
165
|
-
pageName: s.meta.pageName,
|
|
166
|
-
selectionCount: s.meta.selection?.length,
|
|
167
|
-
online: this.isOnline(s),
|
|
168
|
-
lastSeen: s.lastSeen
|
|
169
|
-
};
|
|
170
133
|
}
|
|
134
|
+
}
|
|
135
|
+
const session = { id: randomUUID(), meta, lastSeen: Date.now(), connected: false };
|
|
136
|
+
this.sessions.set(session.id, session);
|
|
137
|
+
this.lastActiveId = session.id;
|
|
138
|
+
log(`session registered: ${session.id} (${meta.fileName ?? "unknown file"})`);
|
|
139
|
+
return this.toInfo(session);
|
|
140
|
+
}
|
|
141
|
+
/** 心跳/连接状态用:只刷 lastSeen,不影响 lastActiveId("最近活跃"= 用户行为,心跳不算) */
|
|
142
|
+
touchSeen(id) {
|
|
143
|
+
const s = this.sessions.get(id);
|
|
144
|
+
if (!s) return null;
|
|
145
|
+
s.lastSeen = Date.now();
|
|
146
|
+
return s;
|
|
147
|
+
}
|
|
148
|
+
/** 用户行为(选区上报/命令服务/注册):刷 lastSeen 并标记最近活跃 */
|
|
149
|
+
touch(id) {
|
|
150
|
+
const s = this.touchSeen(id);
|
|
151
|
+
if (!s) return null;
|
|
152
|
+
this.lastActiveId = id;
|
|
153
|
+
return s;
|
|
154
|
+
}
|
|
155
|
+
setConnected(id, connected) {
|
|
156
|
+
const s = this.touchSeen(id);
|
|
157
|
+
if (!s) return false;
|
|
158
|
+
s.connected = connected;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
updateContext(id, meta) {
|
|
162
|
+
const s = this.touch(id);
|
|
163
|
+
if (!s) return false;
|
|
164
|
+
s.meta = { ...s.meta, ...meta };
|
|
165
|
+
log(
|
|
166
|
+
`context updated [${id.slice(0, 8)}]: ${s.meta.fileName ?? "?"} / ${s.meta.pageName ?? "?"} / ${s.meta.selection?.length ?? 0} selected`
|
|
167
|
+
);
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
list() {
|
|
171
|
+
return [...this.sessions.values()].map((s) => this.toInfo(s));
|
|
172
|
+
}
|
|
173
|
+
/** 纯查找(不判活):工具层寻址用——离线会话也可取到 fileKey 用于候选扩展与故障转移 */
|
|
174
|
+
get(id) {
|
|
175
|
+
return this.sessions.get(id);
|
|
176
|
+
}
|
|
177
|
+
/** 缺省路由:最近活跃在线会话 > 任意在线会话(显式 target 由工具层 get/candidatesForFileKey + ping 完成) */
|
|
178
|
+
resolve() {
|
|
179
|
+
if (this.lastActiveId) {
|
|
180
|
+
const s = this.sessions.get(this.lastActiveId);
|
|
181
|
+
if (s && this.isOnline(s)) return s;
|
|
182
|
+
}
|
|
183
|
+
const fallback = [...this.sessions.values()].find((s) => this.isOnline(s));
|
|
184
|
+
if (fallback) return fallback;
|
|
185
|
+
throw new Error("Pixso \u63D2\u4EF6\u672A\u8FDE\u63A5\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6");
|
|
186
|
+
}
|
|
187
|
+
toInfo(s) {
|
|
188
|
+
return {
|
|
189
|
+
sessionId: s.id,
|
|
190
|
+
fileKey: s.meta.fileKey,
|
|
191
|
+
fileName: s.meta.fileName,
|
|
192
|
+
pageName: s.meta.pageName,
|
|
193
|
+
selectionCount: s.meta.selection?.length,
|
|
194
|
+
online: this.isOnline(s),
|
|
195
|
+
lastSeen: s.lastSeen
|
|
171
196
|
};
|
|
172
|
-
sessions = new SessionRegistry();
|
|
173
197
|
}
|
|
174
|
-
}
|
|
198
|
+
};
|
|
199
|
+
var sessions = new SessionRegistry();
|
|
200
|
+
|
|
201
|
+
// src/http.ts
|
|
202
|
+
import { createServer } from "node:http";
|
|
203
|
+
|
|
204
|
+
// src/agent/http.ts
|
|
205
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
206
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
207
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
175
208
|
|
|
176
|
-
// src/
|
|
209
|
+
// src/http-body.ts
|
|
177
210
|
function sendJson(res, status, body) {
|
|
178
211
|
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
|
|
179
212
|
res.end(JSON.stringify(body));
|
|
@@ -183,769 +216,149 @@ async function readBody(req) {
|
|
|
183
216
|
for await (const chunk of req) chunks.push(chunk);
|
|
184
217
|
return Buffer.concat(chunks);
|
|
185
218
|
}
|
|
186
|
-
var
|
|
187
|
-
|
|
188
|
-
|
|
219
|
+
var JsonBodyError = class extends Error {
|
|
220
|
+
};
|
|
221
|
+
async function readJsonBody(req) {
|
|
222
|
+
const buf = await readBody(req);
|
|
223
|
+
try {
|
|
224
|
+
return buf.length ? JSON.parse(buf.toString("utf8")) : {};
|
|
225
|
+
} catch {
|
|
226
|
+
throw new JsonBodyError("invalid json");
|
|
189
227
|
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// src/install/dist-resolver.ts
|
|
193
|
-
import { existsSync } from "node:fs";
|
|
194
|
-
import { readFile } from "node:fs/promises";
|
|
195
|
-
import { join as join2 } from "node:path";
|
|
196
|
-
import { fileURLToPath } from "node:url";
|
|
197
|
-
function resolvePluginDir() {
|
|
198
|
-
const candidates = [
|
|
199
|
-
fileURLToPath(new URL("./plugin/", import.meta.url)),
|
|
200
|
-
// bundle: dist/index.js → dist/plugin/
|
|
201
|
-
fileURLToPath(new URL("../../plugin/dist/", import.meta.url)),
|
|
202
|
-
// bundle 开发: dist/index.js → packages/plugin/dist/
|
|
203
|
-
fileURLToPath(new URL("../../../plugin/dist/", import.meta.url))
|
|
204
|
-
// tsx 开发: src/install/ → packages/plugin/dist/
|
|
205
|
-
];
|
|
206
|
-
return candidates.find((p) => existsSync(join2(p, "manifest.json"))) ?? null;
|
|
207
228
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return Buffer.from(JSON.stringify(raw, null, 2));
|
|
229
|
+
var segsCache = /* @__PURE__ */ new Map();
|
|
230
|
+
function segsOf(template) {
|
|
231
|
+
let segs = segsCache.get(template);
|
|
232
|
+
if (!segs) {
|
|
233
|
+
segs = template.split("/");
|
|
234
|
+
segsCache.set(template, segs);
|
|
235
|
+
}
|
|
236
|
+
return segs;
|
|
217
237
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
238
|
+
function matchPath(template, pathname) {
|
|
239
|
+
const tplSegs = segsOf(template);
|
|
240
|
+
const pathSegs = segsOf(pathname);
|
|
241
|
+
if (tplSegs.length !== pathSegs.length) return null;
|
|
242
|
+
const params = {};
|
|
243
|
+
for (let i = 0; i < tplSegs.length; i++) {
|
|
244
|
+
const t = tplSegs[i];
|
|
245
|
+
if (t.startsWith(":")) {
|
|
246
|
+
params[t.slice(1)] = decodeURIComponent(pathSegs[i]);
|
|
247
|
+
} else if (t !== pathSegs[i]) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
224
250
|
}
|
|
251
|
+
return params;
|
|
225
252
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
253
|
+
async function dispatchRoute(routes, req, res, url) {
|
|
254
|
+
for (const route of routes) {
|
|
255
|
+
if (route.method !== req.method) continue;
|
|
256
|
+
const params = matchPath(route.path, url.pathname);
|
|
257
|
+
if (!params) continue;
|
|
258
|
+
try {
|
|
259
|
+
await route.handler({ req, res, url, params });
|
|
260
|
+
} catch (err) {
|
|
261
|
+
if (err instanceof JsonBodyError) {
|
|
262
|
+
if (!res.headersSent) sendJson(res, 400, { error: "invalid json" });
|
|
263
|
+
} else {
|
|
264
|
+
console.error("[batter-pixso-mcp] route handler error:", err);
|
|
265
|
+
if (!res.headersSent) sendJson(res, 500, { error: "internal error" });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
232
269
|
}
|
|
233
|
-
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
234
272
|
|
|
235
|
-
// src/
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
273
|
+
// src/agent/mcp.ts
|
|
274
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
275
|
+
import {
|
|
276
|
+
CallToolRequestSchema,
|
|
277
|
+
GetPromptRequestSchema,
|
|
278
|
+
ListPromptsRequestSchema,
|
|
279
|
+
ListResourceTemplatesRequestSchema,
|
|
280
|
+
ListToolsRequestSchema
|
|
281
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
242
282
|
|
|
243
|
-
// src/
|
|
244
|
-
var
|
|
245
|
-
var init_install_page = __esm({
|
|
246
|
-
"src/install/templates/install-page.html"() {
|
|
247
|
-
install_page_default = `<!DOCTYPE html>
|
|
248
|
-
<html lang="zh-CN">
|
|
249
|
-
<head>
|
|
250
|
-
<meta charset="utf-8">
|
|
251
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
252
|
-
<title>Batter Pixso Bridge \xB7 \u5B89\u88C5</title>
|
|
253
|
-
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect width='24' height='24' rx='6' fill='%23575BF2'/%3E%3Cpath d='M13 4 7.5 13.5H11L10 20l6.5-9.5H12.5Z' fill='%23fff'/%3E%3C/svg%3E">
|
|
254
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
255
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
256
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
257
|
-
<style>
|
|
258
|
-
:root {
|
|
259
|
-
--ink: #1e1e1e;
|
|
260
|
-
--sub: #6b6b6b;
|
|
261
|
-
--faint: #a3a3a3;
|
|
262
|
-
--line: #e8e8ea;
|
|
263
|
-
--panel: #f7f7f8;
|
|
264
|
-
--iris: #575bf2;
|
|
265
|
-
--iris-strong: #4346e0;
|
|
266
|
-
--iris-soft: #eef0fe;
|
|
267
|
-
--ok: #14ae5c;
|
|
268
|
-
--err: #f24822;
|
|
269
|
-
--radius: 6px;
|
|
270
|
-
--mono: ui-monospace, "SF Mono", Menlo, Consolas, "Cascadia Mono", monospace;
|
|
271
|
-
}
|
|
272
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
273
|
-
html { -webkit-text-size-adjust: 100%; }
|
|
274
|
-
body {
|
|
275
|
-
background: #fff;
|
|
276
|
-
color: var(--ink);
|
|
277
|
-
font-family: Inter, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
|
278
|
-
font-feature-settings: "tnum" 1, "cv05" 1;
|
|
279
|
-
line-height: 1.5;
|
|
280
|
-
min-height: 100vh;
|
|
281
|
-
}
|
|
282
|
-
::selection { background: var(--iris-soft); }
|
|
283
|
-
.page {
|
|
284
|
-
max-width: 680px;
|
|
285
|
-
margin: 0 auto;
|
|
286
|
-
padding: clamp(48px, 10vh, 96px) 24px 64px;
|
|
287
|
-
animation: enter 420ms cubic-bezier(0.2, 0.6, 0.2, 1) both;
|
|
288
|
-
}
|
|
289
|
-
@keyframes enter {
|
|
290
|
-
from { opacity: 0; transform: translateY(8px); }
|
|
291
|
-
to { opacity: 1; transform: none; }
|
|
292
|
-
}
|
|
293
|
-
a { color: var(--iris); text-decoration: none; }
|
|
294
|
-
a:focus-visible, button:focus-visible {
|
|
295
|
-
outline: 2px solid var(--iris);
|
|
296
|
-
outline-offset: 2px;
|
|
297
|
-
border-radius: 2px;
|
|
298
|
-
}
|
|
283
|
+
// src/agent/guides.ts
|
|
284
|
+
var INTRO = `# batter-pixso-mcp \u4F7F\u7528\u6307\u5357
|
|
299
285
|
|
|
300
|
-
|
|
301
|
-
.top {
|
|
302
|
-
display: flex;
|
|
303
|
-
align-items: center;
|
|
304
|
-
justify-content: space-between;
|
|
305
|
-
gap: 16px;
|
|
306
|
-
margin-bottom: clamp(40px, 7vh, 72px);
|
|
307
|
-
}
|
|
308
|
-
.brand {
|
|
309
|
-
display: flex;
|
|
310
|
-
align-items: center;
|
|
311
|
-
gap: 10px;
|
|
312
|
-
font-size: 13px;
|
|
313
|
-
font-weight: 600;
|
|
314
|
-
letter-spacing: -0.01em;
|
|
315
|
-
}
|
|
316
|
-
.brand svg { display: block; }
|
|
317
|
-
.brand .ver {
|
|
318
|
-
font-family: var(--mono);
|
|
319
|
-
font-weight: 400;
|
|
320
|
-
font-size: 11px;
|
|
321
|
-
color: var(--faint);
|
|
322
|
-
margin-left: 2px;
|
|
323
|
-
}
|
|
324
|
-
.status {
|
|
325
|
-
display: inline-flex;
|
|
326
|
-
align-items: center;
|
|
327
|
-
gap: 7px;
|
|
328
|
-
padding: 5px 10px;
|
|
329
|
-
border-radius: 999px;
|
|
330
|
-
background: var(--panel);
|
|
331
|
-
border: 1px solid var(--line);
|
|
332
|
-
font-size: 11px;
|
|
333
|
-
color: var(--sub);
|
|
334
|
-
white-space: nowrap;
|
|
335
|
-
}
|
|
336
|
-
.status .dot {
|
|
337
|
-
width: 6px;
|
|
338
|
-
height: 6px;
|
|
339
|
-
border-radius: 50%;
|
|
340
|
-
background: var(--faint);
|
|
341
|
-
flex-shrink: 0;
|
|
342
|
-
animation: pulse 2.2s ease-in-out infinite;
|
|
343
|
-
}
|
|
344
|
-
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
|
|
345
|
-
.status.ok { color: #0d7a3e; }
|
|
346
|
-
.status.ok .dot { background: var(--ok); }
|
|
347
|
-
.status.err { color: var(--err); }
|
|
348
|
-
.status.err .dot { background: var(--err); animation: none; }
|
|
286
|
+
## \u5DE5\u5177\u603B\u89C8
|
|
349
287
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
.sub {
|
|
359
|
-
font-size: 14px;
|
|
360
|
-
color: var(--sub);
|
|
361
|
-
max-width: 46em;
|
|
362
|
-
line-height: 1.7;
|
|
363
|
-
margin-bottom: clamp(40px, 7vh, 64px);
|
|
364
|
-
}
|
|
288
|
+
| \u5DE5\u5177 | \u7528\u9014 |
|
|
289
|
+
| --- | --- |
|
|
290
|
+
| \`get_selected\` | \u8BFB\u753B\u5E03\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\u3001\u5C3A\u5BF8\uFF09 |
|
|
291
|
+
| \`get_metadata\` | \u5168\u6811\u7ED3\u6784\u9AA8\u67B6\uFF08\u4EC5 id/name/type/kids\uFF0C\u65E0\u6837\u5F0F/\u5750\u6807/\u8D44\u6E90\uFF09\uFF0C\u9996\u6B21\u5B9A\u4F4D\u7528 |
|
|
292
|
+
| \`get_node_dsl\` | \u751F\u6210\u8282\u70B9\u7684\u5F00\u53D1\u5411\u7CBE\u7B80 DSL\uFF08\u7ED3\u6784 + \u6837\u5F0F + \u8D44\u6E90\u6587\u4EF6\u540D\uFF09\uFF0C\u8BBE\u8BA1\u8F6C\u4EE3\u7801\u7684\u6838\u5FC3\u5165\u53E3 |
|
|
293
|
+
| \`get_screenshot\` | \u5BFC\u51FA\u8282\u70B9\u622A\u56FE\uFF0C\u8FD4\u56DE\u56FE\u7247\u6587\u4EF6\u540D\uFF08\u672C\u673A\u8DEF\u5F84\u76F4\u8BFB\uFF09\uFF0C\u7528\u4E8E\u89C6\u89C9\u53C2\u7167/\u6821\u9A8C |
|
|
294
|
+
| \`export_image\` | \u6309 guid \u5BFC\u51FA\u56FE\u7247\u7D20\u6750\uFF08\u5207\u56FE\uFF09\uFF1A\u8282\u70B9\u5E26\u5BFC\u51FA\u6807\u8BB0\u65F6\u6309\u8BBE\u8BA1\u5E08\u8BBE\u7F6E\u9010\u4EFD\u5BFC\u51FA\uFF0C\u65E0\u6807\u8BB0\u6309\u53C2\u6570\u515C\u5E95 |
|
|
295
|
+
| \`list_sessions\` | \u5217\u51FA\u5168\u90E8\u63D2\u4EF6\u4F1A\u8BDD\uFF08\u591A\u6587\u4EF6\u573A\u666F\u8DEF\u7531\u7528\uFF09 |
|
|
365
296
|
|
|
366
|
-
|
|
367
|
-
.steps { border-top: 1px solid var(--line); }
|
|
368
|
-
.step {
|
|
369
|
-
display: grid;
|
|
370
|
-
grid-template-columns: 44px 1fr;
|
|
371
|
-
gap: 4px 16px;
|
|
372
|
-
padding: 26px 0 28px;
|
|
373
|
-
border-bottom: 1px solid var(--line);
|
|
374
|
-
}
|
|
375
|
-
.num {
|
|
376
|
-
grid-row: 1 / span 2;
|
|
377
|
-
font-family: var(--mono);
|
|
378
|
-
font-size: 12px;
|
|
379
|
-
color: var(--faint);
|
|
380
|
-
padding-top: 3px;
|
|
381
|
-
font-variant-numeric: tabular-nums;
|
|
382
|
-
}
|
|
383
|
-
.step-head {
|
|
384
|
-
display: flex;
|
|
385
|
-
align-items: center;
|
|
386
|
-
gap: 10px;
|
|
387
|
-
flex-wrap: wrap;
|
|
388
|
-
}
|
|
389
|
-
.step-title {
|
|
390
|
-
font-size: 14px;
|
|
391
|
-
font-weight: 600;
|
|
392
|
-
letter-spacing: -0.01em;
|
|
393
|
-
}
|
|
394
|
-
.tag {
|
|
395
|
-
font-size: 11px;
|
|
396
|
-
font-weight: 500;
|
|
397
|
-
color: var(--iris);
|
|
398
|
-
background: var(--iris-soft);
|
|
399
|
-
padding: 2px 8px;
|
|
400
|
-
border-radius: 999px;
|
|
401
|
-
}
|
|
402
|
-
.step-body { grid-column: 2; }
|
|
403
|
-
.note {
|
|
404
|
-
font-size: 12.5px;
|
|
405
|
-
color: var(--sub);
|
|
406
|
-
line-height: 1.75;
|
|
407
|
-
max-width: 60em;
|
|
408
|
-
}
|
|
409
|
-
.note strong { color: var(--ink); font-weight: 600; }
|
|
410
|
-
code {
|
|
411
|
-
font-family: var(--mono);
|
|
412
|
-
background: var(--panel);
|
|
413
|
-
border: 1px solid var(--line);
|
|
414
|
-
color: var(--ink);
|
|
415
|
-
padding: 0.5px 5px;
|
|
416
|
-
border-radius: 4px;
|
|
417
|
-
font-size: 11px;
|
|
418
|
-
}
|
|
419
|
-
.actions { display: flex; align-items: center; gap: 12px; margin: 14px 0 10px; }
|
|
297
|
+
## \u94FE\u63A5\u5373\u8F93\u5165\uFF08\u9AD8\u9891\u573A\u666F\uFF09
|
|
420
298
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
align-items: center;
|
|
425
|
-
gap: 8px;
|
|
426
|
-
padding: 9px 18px;
|
|
427
|
-
background: var(--iris);
|
|
428
|
-
color: #fff;
|
|
429
|
-
border-radius: var(--radius);
|
|
430
|
-
font-size: 13px;
|
|
431
|
-
font-weight: 600;
|
|
432
|
-
letter-spacing: -0.01em;
|
|
433
|
-
cursor: grab;
|
|
434
|
-
box-shadow: 0 1px 2px rgba(30, 30, 30, 0.08);
|
|
435
|
-
transition: background 140ms ease, transform 140ms ease;
|
|
436
|
-
user-select: none;
|
|
437
|
-
}
|
|
438
|
-
.bm:hover { background: var(--iris-strong); }
|
|
439
|
-
.bm:active { cursor: grabbing; transform: scale(0.985); }
|
|
440
|
-
.drag-hint {
|
|
441
|
-
font-size: 12px;
|
|
442
|
-
color: var(--faint);
|
|
443
|
-
}
|
|
299
|
+
\u7528\u6237\u63D0\u4F9B Pixso \u94FE\u63A5\u65F6\uFF08\u5B98\u65B9 \`pixso.cn\` \u6216\u79C1\u6709\u5316\u90E8\u7F72\uFF0C\u4EFB\u610F\u57DF\u540D\uFF09\uFF0C\u94FE\u63A5\u672C\u8EAB\u5C31\u662F\u4EFB\u52A1\u8F93\u5165\u2014\u2014
|
|
300
|
+
\u63D0\u53D6 \`item-id\` \u67E5\u8BE2\u53C2\u6570 \u2192 URL \u89E3\u7801 \u2192 \u5F97\u5230\u8282\u70B9 guid \u76F4\u63A5\u8C03\u7528\uFF0C\u65E0\u9700\u5148\u95EE\u7528\u6237\u9009\u4E2D\u4EC0\u4E48\u3002
|
|
301
|
+
\u89C4\u5219\u5168\u6587\u89C1 \`batter://guides/design-to-code\` \u786C\u6027\u89C4\u5219\u7B2C 2 \u6761\u3002
|
|
444
302
|
|
|
445
|
-
|
|
446
|
-
.btn {
|
|
447
|
-
display: inline-flex;
|
|
448
|
-
align-items: center;
|
|
449
|
-
gap: 7px;
|
|
450
|
-
padding: 8px 14px;
|
|
451
|
-
border: 1px solid var(--line);
|
|
452
|
-
border-radius: var(--radius);
|
|
453
|
-
background: #fff;
|
|
454
|
-
color: var(--ink);
|
|
455
|
-
font-size: 13px;
|
|
456
|
-
font-weight: 500;
|
|
457
|
-
letter-spacing: -0.01em;
|
|
458
|
-
text-decoration: none;
|
|
459
|
-
transition: border-color 140ms ease, background 140ms ease;
|
|
460
|
-
}
|
|
461
|
-
.btn:hover { border-color: #d2d2d6; background: var(--panel); }
|
|
462
|
-
.btn svg { display: block; color: var(--sub); }
|
|
303
|
+
## \u591A\u6587\u4EF6\u573A\u666F
|
|
463
304
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
margin: 14px 0 10px;
|
|
468
|
-
}
|
|
469
|
-
pre {
|
|
470
|
-
background: var(--panel);
|
|
471
|
-
border: 1px solid var(--line);
|
|
472
|
-
border-radius: 8px;
|
|
473
|
-
padding: 14px 16px;
|
|
474
|
-
font-family: var(--mono);
|
|
475
|
-
font-size: 12px;
|
|
476
|
-
line-height: 1.7;
|
|
477
|
-
color: #3c3c3c;
|
|
478
|
-
overflow-x: auto;
|
|
479
|
-
}
|
|
480
|
-
.copy {
|
|
481
|
-
position: absolute;
|
|
482
|
-
top: 8px;
|
|
483
|
-
right: 8px;
|
|
484
|
-
padding: 4px 10px;
|
|
485
|
-
font-family: inherit;
|
|
486
|
-
font-size: 11px;
|
|
487
|
-
font-weight: 500;
|
|
488
|
-
color: var(--sub);
|
|
489
|
-
background: #fff;
|
|
490
|
-
border: 1px solid var(--line);
|
|
491
|
-
border-radius: var(--radius);
|
|
492
|
-
cursor: pointer;
|
|
493
|
-
transition: color 140ms ease, border-color 140ms ease;
|
|
494
|
-
}
|
|
495
|
-
.copy:hover { color: var(--ink); border-color: #d2d2d6; }
|
|
496
|
-
.copy.done { color: var(--ok); border-color: var(--ok); }
|
|
305
|
+
\u6BCF\u4E2A\u8FD0\u884C\u63D2\u4EF6\u7684\u8BBE\u8BA1\u6587\u4EF6\u662F\u4E00\u4E2A\u4F1A\u8BDD\u3002\u591A\u4E2A\u6587\u4EF6\u540C\u65F6\u6253\u5F00\u65F6\uFF1A
|
|
306
|
+
\u5148\u7528 \`list_sessions\` \u786E\u8BA4\u76EE\u6807\u6587\u4EF6\uFF0C\u518D\u628A\u8FD4\u56DE\u7684 \`sessionId\` \u6216 \`fileKey\`
|
|
307
|
+
\u4F5C\u4E3A\u5176\u4ED6\u5DE5\u5177\u7684 \`target\` \u53C2\u6570\u4F20\u5165\uFF1B\u4E0D\u4F20 \`target\` \u65F6\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u7684\u6587\u4EF6\u3002
|
|
497
308
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
font-family: var(--mono);
|
|
503
|
-
display: flex;
|
|
504
|
-
gap: 8px;
|
|
505
|
-
flex-wrap: wrap;
|
|
506
|
-
}
|
|
309
|
+
**\u4F1A\u8BDD\u89E3\u6790\u4E0E\u4FE1\u5C01**\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u7684\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, data}\`\u2014\u2014\`sessionId\` \u662F\u672C\u6B21\u5B9E\u9645\u670D\u52A1\u7684\u4F1A\u8BDD
|
|
310
|
+
\uFF08\u6BCF\u6B21\u8C03\u7528\u7ECF ping/pong \u63A2\u6D3B\u91CD\u65B0\u786E\u8BA4\uFF0C\u4E0D\u662F\u53EF\u957F\u671F\u9489\u4F4F\u7684\u56FA\u5B9A\u6807\u8BC6\uFF09\u3002\u63A8\u8350 \`target\` \u7528 \`fileKey\`\uFF1A
|
|
311
|
+
\u6BCF\u6B21\u8C03\u7528\u6309 fileKey \u91CD\u65B0\u5BFB\u5740\u2014\u2014\u540C fileKey \u591A\u4F1A\u8BDD\u65F6\u4F18\u5148\u4E0A\u4E00\u6B21\u4F7F\u7528\u7684\uFF08\u4EB2\u548C\uFF09\uFF0C\u6587\u4EF6\u5173\u95ED\u91CD\u5F00\u3001\u63D2\u4EF6\u91CD\u8DD1\u540E
|
|
312
|
+
\u81EA\u52A8\u89E3\u6790\u5230\u5F53\u524D\u5B58\u6D3B\u5B9E\u4F8B\uFF0Cagent \u65E0\u9700\u5173\u5FC3 sessionId \u7684\u5931\u6548\u3002
|
|
507
313
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
@media (prefers-reduced-motion: reduce) {
|
|
513
|
-
.page { animation: none; }
|
|
514
|
-
.status .dot { animation: none; }
|
|
515
|
-
.bm, .btn, .copy { transition: none; }
|
|
516
|
-
}
|
|
517
|
-
</style>
|
|
518
|
-
</head>
|
|
519
|
-
<body>
|
|
520
|
-
<div class="page">
|
|
521
|
-
<header class="top">
|
|
522
|
-
<div class="brand">
|
|
523
|
-
<svg width="22" height="22" viewBox="0 0 24 24" aria-hidden="true"><rect width="24" height="24" rx="6" fill="#575BF2"/><path d="M13 4 7.5 13.5H11L10 20l6.5-9.5H12.5Z" fill="#fff"/></svg>
|
|
524
|
-
Batter Pixso Bridge
|
|
525
|
-
<span class="ver">v{{ version }}</span>
|
|
526
|
-
</div>
|
|
527
|
-
<div class="status" id="status">
|
|
528
|
-
<span class="dot"></span><span id="status-text">\u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u2026</span>
|
|
529
|
-
</div>
|
|
530
|
-
</header>
|
|
314
|
+
\u8BE6\u7EC6\u5DE5\u4F5C\u6D41\u89C1\u8D44\u6E90 \`batter://guides/design-to-code\`\u3002
|
|
315
|
+
`;
|
|
316
|
+
var DESIGN_TO_CODE = `# \u8BBE\u8BA1\u8F6C\u4EE3\u7801\u5DE5\u4F5C\u6D41
|
|
531
317
|
|
|
532
|
-
|
|
533
|
-
<p class="sub">\u4E00\u6B21\u5B89\u88C5\uFF0C\u8BA9 Claude\u3001Cursor \u7B49 AI \u7F16\u7A0B\u52A9\u624B\u76F4\u63A5\u8BFB\u53D6 Pixso / MasterGo \u753B\u5E03\u7684\u7ED3\u6784\u3001\u6837\u5F0F\u4E0E\u622A\u56FE\u3002\u6309\u4E0B\u9762\u56DB\u6B65\u5B8C\u6210\u8BBE\u7F6E\u3002</p>
|
|
318
|
+
## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011
|
|
534
319
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
\u4E66\u7B7E\u6307\u5411\u672C\u673A\u670D\u52A1 <code>{{ base }}</code>\uFF0C\u670D\u52A1\u91CD\u542F\u540E\u4F9D\u7136\u6709\u6548\u3002\u770B\u4E0D\u5230\u4E66\u7B7E\u680F\uFF1F\u6309 <code>Ctrl + Shift + B</code> \u6253\u5F00\u3002
|
|
550
|
-
</p>
|
|
551
|
-
</div>
|
|
552
|
-
</div>
|
|
320
|
+
1. **\u8D44\u6E90\u662F\u672C\u673A\u6587\u4EF6\uFF0C\u6309\u9700\u53D6\u7528**\uFF1ADSL \u4E0E\u622A\u56FE\u7ED3\u679C\u4E2D\u7684 \`img\` \u5B57\u6BB5\u662F\u4EA7\u7269**\u6587\u4EF6\u540D**
|
|
321
|
+
\uFF08\u5982 \`"ab12cd34.svg"\`\uFF09\uFF0C\u5B8C\u6574\u8DEF\u5F84 = \u4EA7\u7269\u76EE\u5F55 + \u6587\u4EF6\u540D\uFF08\u672C\u673A\u4EA7\u7269\u76EE\u5F55\uFF1A\`${ASSETS_DIR}\`\uFF09\u3002\u4EA7\u7269\u4E0E\u670D\u52A1\u540C\u673A\uFF0C\u76F4\u63A5\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF1A
|
|
322
|
+
- **\u622A\u56FE\u7B49\u89C6\u89C9\u53C2\u7167**\uFF1A\u7528 Read \u5DE5\u5177\u76F4\u63A5\u8BFB\u8BE5\u8DEF\u5F84\u67E5\u770B\uFF0C\u65E0\u9700\u590D\u5236\u3002
|
|
323
|
+
- **\u8981\u5199\u8FDB\u4EA4\u4ED8\u4EE3\u7801\u7684\u56FE\u6807/\u56FE\u7247**\uFF1A**\u5FC5\u987B**\u5148\u590D\u5236\u5230\u9879\u76EE\u8D44\u6E90\u76EE\u5F55
|
|
324
|
+
\uFF08PowerShell \`Copy-Item\` / \u7C7B Unix \`cp\`\uFF09\uFF0C\u518D\u4EE5\u672C\u5730\u76F8\u5BF9\u8DEF\u5F84\u5F15\u7528
|
|
325
|
+
\uFF08\`<img src>\`\u3001CSS \`background-image\` \u7B49\uFF09\u3002\u4EA7\u7269\u6587\u4EF6\u540D\u662F\u5185\u5BB9 hash\uFF0C\u6CA1\u6709\u8BED\u4E49\u2014\u2014
|
|
326
|
+
**\u590D\u5236\u65F6\u6309\u8282\u70B9\u7528\u9014\u81EA\u884C\u547D\u540D\u8BED\u4E49\u5316\u6587\u4EF6\u540D**\uFF08\u5982 \`icon-arrow.svg\`\u3001\`hero-banner.png\`\uFF1B
|
|
327
|
+
\`export_image\` \u8FD4\u56DE\u7684 \`suffix\` \u5982 \`@2x\` \u5EFA\u8BAE\u4FDD\u7559\uFF09\uFF0C\u4E25\u7981\u628A hash \u6587\u4EF6\u540D\u5E26\u8FDB\u4EA4\u4ED8\u4EE3\u7801\u3002
|
|
328
|
+
\u4E25\u7981\u76F4\u63A5\u5F15\u7528\u4EA7\u7269\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4EA7\u7269\u9ED8\u8BA4 7 \u5929\u540E\u81EA\u52A8\u6E05\u626B\uFF0C\u5F15\u7528\u4F1A\u5931\u6548\uFF09\uFF1B
|
|
329
|
+
\u4E25\u7981\u79FB\u52A8\u6216\u5220\u9664\u4EA7\u7269\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\uFF08\u591A\u4F1A\u8BDD\u5171\u4EAB\uFF0C\u6E05\u626B\u7531\u670D\u52A1\u7EDF\u4E00\u8D1F\u8D23\uFF09\u3002
|
|
330
|
+
2. **Pixso \u94FE\u63A5\u5FC5\u987B\u63D0\u53D6 \`item-id\` \u5F53 guid**\uFF1A\u4EFB\u610F\u57DF\u540D\u90FD\u6709\u6548\u2014\u2014\u5B98\u65B9 \`pixso.cn\` \u6216\u79C1\u6709\u5316\u90E8\u7F72\u5982
|
|
331
|
+
\`https://pixso.ky-tech.com.cn/app/design/<\u6587\u4EF6>?item-id=1%3A2\`\uFF08\u4E5F\u53EF\u80FD\u662F \`/app/editor/\` \u8DEF\u5F84\uFF09\u3002
|
|
332
|
+
\u4ECE \`item-id\` \u67E5\u8BE2\u53C2\u6570\u63D0\u53D6 guid \u5E76 URL \u89E3\u7801\uFF08\`1%3A2\` \u2192 \`"1:2"\`\uFF09\u540E\u4F5C\u4E3A \`guid\` \u4F20\u5165\u3002
|
|
333
|
+
\u4E25\u7981\u628A\u6574\u4E2A URL \u5F53 guid \u4F20\uFF1B\u65E0\u8BBA\u57DF\u540D\u662F\u5426\u8BA4\u8BC6\uFF0C\u90FD\u6309\u6B64\u89C4\u5219\u5904\u7406\u3002
|
|
553
334
|
|
|
554
|
-
|
|
555
|
-
<span class="num">02</span>
|
|
556
|
-
<div class="step-head">
|
|
557
|
-
<span class="step-title">\u5B89\u88C5\u5230 Pixso \u5BA2\u6237\u7AEF</span>
|
|
558
|
-
</div>
|
|
559
|
-
<div class="step-body">
|
|
560
|
-
<div class="actions">
|
|
561
|
-
<a class="btn" href="/plugin.zip" download="batter-pixso-bridge.zip">
|
|
562
|
-
<svg width="12" height="12" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><path d="m7 11 5 5 5-5"/><path d="M4 21h16"/></svg>
|
|
563
|
-
\u4E0B\u8F7D\u63D2\u4EF6\u5305\uFF08.zip\uFF09
|
|
564
|
-
</a>
|
|
565
|
-
</div>
|
|
566
|
-
<p class="note">\u89E3\u538B\u540E\uFF0C\u5728\u5BA2\u6237\u7AEF\u7684\u8BBE\u8BA1\u6587\u4EF6\u4E2D\uFF1A\u53F3\u952E \u2192 \u63D2\u4EF6 \u2192 \u5F00\u53D1\u63D2\u4EF6 \u2192 \u5BFC\u5165 <code>manifest.json</code>\u3002</p>
|
|
567
|
-
</div>
|
|
568
|
-
</div>
|
|
335
|
+
## \u63A8\u8350\u6B65\u9AA4
|
|
569
336
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
}</pre>
|
|
586
|
-
<button class="copy" id="copy" type="button">\u590D\u5236</button>
|
|
587
|
-
</div>
|
|
588
|
-
<p class="note">\u7C98\u8D34\u5230 Cursor / Claude Code \u7684 MCP \u914D\u7F6E\u6587\u4EF6\u3002\u7CFB\u7EDF Node \u4F4E\u4E8E 18 \u65F6\u6539\u7528 volta \u5305\u88F9\u542F\u52A8\uFF0C\u8BE6\u89C1 README\u3002</p>
|
|
589
|
-
</div>
|
|
590
|
-
</div>
|
|
337
|
+
1. **\u786E\u8BA4\u76EE\u6807\u8282\u70B9**
|
|
338
|
+
- \u7528\u6237\u7ED9\u4E86 Pixso \u94FE\u63A5\uFF1A\u6309\u4E0A\u65B9\u786C\u6027\u89C4\u5219\u7B2C 2 \u6761\u63D0\u53D6 guid\uFF0C\u76F4\u63A5\u7EE7\u7EED\u3002
|
|
339
|
+
- \u7528\u6237\u660E\u786E\u8BF4\u4E86"\u8FD9\u4E2A/\u6211\u9009\u4E2D\u7684"\uFF1A\u8C03 \`get_selected\` \u62FF guid\uFF0C\u76F4\u63A5\u7EE7\u7EED\u3002
|
|
340
|
+
- \u5176\u4F59\u4F9D\u8D56\u5F53\u524D\u9009\u533A\u7684\u573A\u666F\uFF1A**\u4E25\u7981\u9690\u5F0F\u4F7F\u7528\u9009\u533A guid**\u2014\u2014\u8C03 \`get_selected\` \u540E
|
|
341
|
+
\u5FC5\u987B\u5148\u628A\u9009\u4E2D\u56FE\u5C42\u540D\u79F0\u62A5\u7ED9\u7528\u6237\u786E\u8BA4\uFF08"\u5F53\u524D\u9009\u4E2D\u7684\u662F\u300CXX\u300D\uFF0C\u786E\u8BA4\u5904\u7406\u5B83\u5417\uFF1F"\uFF09\uFF0C\u786E\u8BA4\u540E\u518D\u7EE7\u7EED\u3002
|
|
342
|
+
\`get_node_dsl\` / \`get_screenshot\` \u7684 guid \u7F3A\u7701\u56DE\u9000\u540C\u6837\u9002\u7528\u672C\u89C4\u5219\u3002
|
|
343
|
+
2. **\u53D6\u7ED3\u6784**\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, data}\` \u4FE1\u5C01\u2014\u2014DSL \u5728 \`data\` \u5B57\u6BB5\u91CC\u3002
|
|
344
|
+
\`get_node_dsl(guid)\` \u62FF\u7CBE\u7B80 DSL\u3002
|
|
345
|
+
**\u9996\u6B21\u63A5\u89E6\u4E00\u4E2A\u8BBE\u8BA1\u7A3F/\u9875\u9762**\uFF08\u8FD8\u6CA1\u6709\u76EE\u6807\u8282\u70B9\u7684\u4E0A\u4E0B\u6587\u65F6\uFF09\uFF1A\u5148 \`get_metadata(guid)\`
|
|
346
|
+
\u62FF\u5168\u6811\u9AA8\u67B6\uFF08\u4EC5 id/name/type/kids\uFF0C\u65E0\u6837\u5F0F/\u5750\u6807/\u8D44\u6E90\uFF0C\u4F53\u79EF\u6781\u5C0F\uFF09+ \`get_screenshot(guid)\` \u62FF\u89C6\u89C9\u5168\u8C8C\uFF0C
|
|
347
|
+
\u636E\u6B64\u5B9A\u4F4D\u76EE\u6807\u8282\u70B9\uFF0C\u518D\u5BF9\u76EE\u6807\u6B63\u5E38\u53D6 DSL\u3002\u9AA8\u67B6\u4E2D ICON/IMAGE \u53F6\u5B50\u65E0 \`img\`\u2014\u2014\u9700\u8D44\u6E90\u65F6\u6309\u5176 \`id\` \u4E0B\u94BB\u5168\u91CF DSL\u3002
|
|
348
|
+
3. **\u53D6\u89C6\u89C9\u53C2\u7167**\uFF1A\`get_screenshot(guid)\` \u62FF\u622A\u56FE\u6587\u4EF6\u540D\uFF0C\u6309\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\u62FC\u8DEF\u5F84\u540E\u7528 Read \u76F4\u8BFB\uFF0C\u4E0E DSL \u5BF9\u7167\u7406\u89E3\u89C6\u89C9\u5C42\u7EA7\u3002
|
|
349
|
+
4. **\u751F\u6210\u4EE3\u7801**\uFF1A\u6309\u4E0B\u6587 DSL \u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002
|
|
350
|
+
5. **\u6821\u9A8C**\uFF1A\u751F\u6210\u540E\u518D\u53D6\u4E00\u6B21\u622A\u56FE\u5BF9\u6BD4\uFF0C\u6709\u504F\u5DEE\u5C31\u4FEE\u6B63\u540E\u518D\u6BD4\u4E00\u8F6E\u3002
|
|
591
351
|
|
|
592
|
-
|
|
593
|
-
<span class="num">04</span>
|
|
594
|
-
<div class="step-head">
|
|
595
|
-
<span class="step-title">\u5F00\u59CB\u4F7F\u7528</span>
|
|
596
|
-
</div>
|
|
597
|
-
<div class="step-body">
|
|
598
|
-
<p class="note" style="margin-top:10px">
|
|
599
|
-
\u63D2\u4EF6\u8FD0\u884C\u540E\uFF0C\u5728\u753B\u5E03\u4E2D\u9009\u4E2D\u56FE\u5C42\uFF0C\u5BF9 AI \u8BF4\u300C\u5B9E\u73B0\u8FD9\u4E2A\u8BBE\u8BA1\u300D\u3002
|
|
600
|
-
AI \u4F1A\u81EA\u52A8\u8C03\u7528 <code>get_node_dsl</code>\u3001<code>get_screenshot</code> \u7B49\u5DE5\u5177\u8BFB\u53D6\u8BBE\u8BA1\u5E76\u751F\u6210\u4EE3\u7801\u3002
|
|
601
|
-
</p>
|
|
602
|
-
</div>
|
|
603
|
-
</div>
|
|
604
|
-
</div>
|
|
352
|
+
## DSL \u5B57\u6BB5\u8BED\u4E49\uFF08\u7D27\u51D1\u683C\u5F0F\uFF09
|
|
605
353
|
|
|
606
|
-
|
|
607
|
-
<span>batter-pixso-mcp</span>
|
|
608
|
-
<span>\xB7</span>
|
|
609
|
-
<span>v{{ version }}</span>
|
|
610
|
-
<span>\xB7</span>
|
|
611
|
-
<span>{{ base }}</span>
|
|
612
|
-
</div>
|
|
613
|
-
</div>
|
|
614
|
-
<script>
|
|
615
|
-
fetch("/health").then(function (r) { return r.json(); }).then(function (h) {
|
|
616
|
-
var el = document.getElementById("status");
|
|
617
|
-
el.className = "status ok";
|
|
618
|
-
document.getElementById("status-text").textContent =
|
|
619
|
-
"\u670D\u52A1\u8FD0\u884C\u4E2D \xB7 \u63D2\u4EF6 " + h.pluginsOnline + " \u5728\u7EBF";
|
|
620
|
-
}).catch(function () {
|
|
621
|
-
var el = document.getElementById("status");
|
|
622
|
-
el.className = "status err";
|
|
623
|
-
document.getElementById("status-text").textContent = "\u670D\u52A1\u672A\u8FDE\u63A5";
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
var copyBtn = document.getElementById("copy");
|
|
627
|
-
copyBtn.addEventListener("click", function () {
|
|
628
|
-
var text = document.getElementById("cfg").textContent;
|
|
629
|
-
function done() {
|
|
630
|
-
copyBtn.textContent = "\u5DF2\u590D\u5236";
|
|
631
|
-
copyBtn.className = "copy done";
|
|
632
|
-
setTimeout(function () {
|
|
633
|
-
copyBtn.textContent = "\u590D\u5236";
|
|
634
|
-
copyBtn.className = "copy";
|
|
635
|
-
}, 1600);
|
|
636
|
-
}
|
|
637
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
638
|
-
navigator.clipboard.writeText(text).then(done, done);
|
|
639
|
-
} else {
|
|
640
|
-
var ta = document.createElement("textarea");
|
|
641
|
-
ta.value = text;
|
|
642
|
-
document.body.appendChild(ta);
|
|
643
|
-
ta.select();
|
|
644
|
-
try { document.execCommand("copy"); } catch (e) {}
|
|
645
|
-
document.body.removeChild(ta);
|
|
646
|
-
done();
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
</script>
|
|
650
|
-
</body>
|
|
651
|
-
</html>
|
|
652
|
-
`;
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
// src/install/template.ts
|
|
657
|
-
function escapeHtml(s) {
|
|
658
|
-
return s.replace(/[&<>"']/g, (c) => HTML_ESCAPES[c]);
|
|
659
|
-
}
|
|
660
|
-
function escapeJsString(s) {
|
|
661
|
-
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
662
|
-
}
|
|
663
|
-
function renderTemplate(tpl, vars, escape = escapeHtml) {
|
|
664
|
-
const lookup = (key) => {
|
|
665
|
-
const v = vars[key];
|
|
666
|
-
if (v === void 0) throw new Error(`\u6A21\u677F\u53D8\u91CF\u7F3A\u5931\uFF1A{{${key}}}`);
|
|
667
|
-
return String(v);
|
|
668
|
-
};
|
|
669
|
-
return tpl.replace(/\{\{\{\s*(\w+)\s*\}\}\}/g, (_, key) => lookup(key)).replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => escape(lookup(key)));
|
|
670
|
-
}
|
|
671
|
-
function loadTemplate(name) {
|
|
672
|
-
return TEMPLATES[name];
|
|
673
|
-
}
|
|
674
|
-
var HTML_ESCAPES, TEMPLATES;
|
|
675
|
-
var init_template = __esm({
|
|
676
|
-
"src/install/template.ts"() {
|
|
677
|
-
"use strict";
|
|
678
|
-
init_bookmarklet();
|
|
679
|
-
init_install_page();
|
|
680
|
-
HTML_ESCAPES = {
|
|
681
|
-
"&": "&",
|
|
682
|
-
"<": "<",
|
|
683
|
-
">": ">",
|
|
684
|
-
'"': """,
|
|
685
|
-
"'": "'"
|
|
686
|
-
};
|
|
687
|
-
TEMPLATES = {
|
|
688
|
-
"bookmarklet.txt": bookmarklet_default,
|
|
689
|
-
"install-page.html": install_page_default
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
// src/install/page.ts
|
|
695
|
-
function bookmarkletHref(base) {
|
|
696
|
-
const source = renderTemplate(loadTemplate("bookmarklet.txt"), { base }, escapeJsString);
|
|
697
|
-
return `javascript:${encodeURIComponent(source)}`;
|
|
698
|
-
}
|
|
699
|
-
function renderInstallPage(base, httpPort) {
|
|
700
|
-
return renderTemplate(loadTemplate("install-page.html"), {
|
|
701
|
-
version: VERSION,
|
|
702
|
-
href: bookmarkletHref(base),
|
|
703
|
-
base,
|
|
704
|
-
httpPort
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
var init_page = __esm({
|
|
708
|
-
"src/install/page.ts"() {
|
|
709
|
-
"use strict";
|
|
710
|
-
init_config();
|
|
711
|
-
init_template();
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
// src/install/zip.ts
|
|
716
|
-
import { Buffer as Buffer2 } from "node:buffer";
|
|
717
|
-
function crc32(buf) {
|
|
718
|
-
let crc = 4294967295;
|
|
719
|
-
for (const byte of buf) crc = CRC_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
720
|
-
return (crc ^ 4294967295) >>> 0;
|
|
721
|
-
}
|
|
722
|
-
function buildZip(entries) {
|
|
723
|
-
const chunks = [];
|
|
724
|
-
const central = [];
|
|
725
|
-
let offset = 0;
|
|
726
|
-
for (const { name, data } of entries) {
|
|
727
|
-
const nameBuf = Buffer2.from(name, "utf8");
|
|
728
|
-
const crc = crc32(data);
|
|
729
|
-
const local = Buffer2.alloc(30);
|
|
730
|
-
local.writeUInt32LE(67324752, 0);
|
|
731
|
-
local.writeUInt16LE(20, 4);
|
|
732
|
-
local.writeUInt16LE(2048, 6);
|
|
733
|
-
local.writeUInt16LE(0, 8);
|
|
734
|
-
local.writeUInt16LE(0, 10);
|
|
735
|
-
local.writeUInt16LE(33, 12);
|
|
736
|
-
local.writeUInt32LE(crc, 14);
|
|
737
|
-
local.writeUInt32LE(data.length, 18);
|
|
738
|
-
local.writeUInt32LE(data.length, 22);
|
|
739
|
-
local.writeUInt16LE(nameBuf.length, 26);
|
|
740
|
-
local.writeUInt16LE(0, 28);
|
|
741
|
-
chunks.push(local, nameBuf, data);
|
|
742
|
-
const cen = Buffer2.alloc(46);
|
|
743
|
-
cen.writeUInt32LE(33639248, 0);
|
|
744
|
-
cen.writeUInt16LE(20, 4);
|
|
745
|
-
cen.writeUInt16LE(20, 6);
|
|
746
|
-
cen.writeUInt16LE(2048, 8);
|
|
747
|
-
cen.writeUInt16LE(0, 10);
|
|
748
|
-
cen.writeUInt16LE(0, 12);
|
|
749
|
-
cen.writeUInt16LE(33, 14);
|
|
750
|
-
cen.writeUInt32LE(crc, 16);
|
|
751
|
-
cen.writeUInt32LE(data.length, 20);
|
|
752
|
-
cen.writeUInt32LE(data.length, 24);
|
|
753
|
-
cen.writeUInt16LE(nameBuf.length, 28);
|
|
754
|
-
cen.writeUInt16LE(0, 30);
|
|
755
|
-
cen.writeUInt16LE(0, 32);
|
|
756
|
-
cen.writeUInt16LE(0, 34);
|
|
757
|
-
cen.writeUInt16LE(0, 36);
|
|
758
|
-
cen.writeUInt32LE(0, 38);
|
|
759
|
-
cen.writeUInt32LE(offset, 42);
|
|
760
|
-
central.push(Buffer2.concat([cen, nameBuf]));
|
|
761
|
-
offset += 30 + nameBuf.length + data.length;
|
|
762
|
-
}
|
|
763
|
-
const centralBuf = Buffer2.concat(central);
|
|
764
|
-
const eocd = Buffer2.alloc(22);
|
|
765
|
-
eocd.writeUInt32LE(101010256, 0);
|
|
766
|
-
eocd.writeUInt16LE(entries.length, 8);
|
|
767
|
-
eocd.writeUInt16LE(entries.length, 10);
|
|
768
|
-
eocd.writeUInt32LE(centralBuf.length, 12);
|
|
769
|
-
eocd.writeUInt32LE(offset, 16);
|
|
770
|
-
return Buffer2.concat([...chunks, centralBuf, eocd]);
|
|
771
|
-
}
|
|
772
|
-
var CRC_TABLE;
|
|
773
|
-
var init_zip = __esm({
|
|
774
|
-
"src/install/zip.ts"() {
|
|
775
|
-
"use strict";
|
|
776
|
-
CRC_TABLE = (() => {
|
|
777
|
-
const table = new Uint32Array(256);
|
|
778
|
-
for (let n = 0; n < 256; n++) {
|
|
779
|
-
let c = n;
|
|
780
|
-
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
781
|
-
table[n] = c >>> 0;
|
|
782
|
-
}
|
|
783
|
-
return table;
|
|
784
|
-
})();
|
|
785
|
-
}
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
// src/routes/install.ts
|
|
789
|
-
async function handleInstall(req, res, url) {
|
|
790
|
-
if (req.method !== "GET") return false;
|
|
791
|
-
const path = url.pathname;
|
|
792
|
-
if (path === "/") {
|
|
793
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
794
|
-
res.end(renderInstallPage(`http://127.0.0.1:${config.httpPort}/`, config.httpPort));
|
|
795
|
-
return true;
|
|
796
|
-
}
|
|
797
|
-
if (path === "/manifest.json") {
|
|
798
|
-
const buf = await pluginManifest().catch(() => null);
|
|
799
|
-
if (!buf) {
|
|
800
|
-
sendJson(res, 404, { error: NO_DIST });
|
|
801
|
-
return true;
|
|
802
|
-
}
|
|
803
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store" });
|
|
804
|
-
res.end(buf);
|
|
805
|
-
return true;
|
|
806
|
-
}
|
|
807
|
-
if (path === "/code.js" || path === "/ui.html") {
|
|
808
|
-
const buf = await readPluginFile(path.slice(1));
|
|
809
|
-
if (!buf) {
|
|
810
|
-
sendJson(res, 404, { error: PLUGIN_DIR ? "not found" : NO_DIST });
|
|
811
|
-
return true;
|
|
812
|
-
}
|
|
813
|
-
res.writeHead(200, {
|
|
814
|
-
"Content-Type": path.endsWith(".js") ? "text/javascript; charset=utf-8" : "text/html; charset=utf-8",
|
|
815
|
-
"Cache-Control": "no-store"
|
|
816
|
-
});
|
|
817
|
-
res.end(buf);
|
|
818
|
-
return true;
|
|
819
|
-
}
|
|
820
|
-
if (path === "/plugin.zip") {
|
|
821
|
-
if (!PLUGIN_DIR) {
|
|
822
|
-
sendJson(res, 404, { error: NO_DIST });
|
|
823
|
-
return true;
|
|
824
|
-
}
|
|
825
|
-
try {
|
|
826
|
-
const [manifest, code, ui] = await Promise.all([
|
|
827
|
-
pluginManifest(),
|
|
828
|
-
readPluginFile("code.js"),
|
|
829
|
-
readPluginFile("ui.html")
|
|
830
|
-
]);
|
|
831
|
-
const zip = buildZip([
|
|
832
|
-
{ name: "manifest.json", data: manifest },
|
|
833
|
-
{ name: "code.js", data: code },
|
|
834
|
-
{ name: "ui.html", data: ui }
|
|
835
|
-
]);
|
|
836
|
-
res.writeHead(200, {
|
|
837
|
-
"Content-Type": "application/zip",
|
|
838
|
-
"Content-Disposition": 'attachment; filename="batter-pixso-bridge.zip"'
|
|
839
|
-
});
|
|
840
|
-
res.end(zip);
|
|
841
|
-
} catch {
|
|
842
|
-
sendJson(res, 500, { error: "zip build failed" });
|
|
843
|
-
}
|
|
844
|
-
return true;
|
|
845
|
-
}
|
|
846
|
-
return false;
|
|
847
|
-
}
|
|
848
|
-
var NO_DIST;
|
|
849
|
-
var init_install = __esm({
|
|
850
|
-
"src/routes/install.ts"() {
|
|
851
|
-
"use strict";
|
|
852
|
-
init_config();
|
|
853
|
-
init_dist_resolver();
|
|
854
|
-
init_page();
|
|
855
|
-
init_zip();
|
|
856
|
-
init_http_utils();
|
|
857
|
-
NO_DIST = "plugin dist not found\uFF08\u8FD0\u884C npm run build \u751F\u6210\uFF09";
|
|
858
|
-
}
|
|
859
|
-
});
|
|
860
|
-
|
|
861
|
-
// src/guides.ts
|
|
862
|
-
var INTRO, DESIGN_TO_CODE, GUIDES;
|
|
863
|
-
var init_guides = __esm({
|
|
864
|
-
"src/guides.ts"() {
|
|
865
|
-
"use strict";
|
|
866
|
-
INTRO = `# batter-pixso-mcp \u4F7F\u7528\u6307\u5357
|
|
867
|
-
|
|
868
|
-
\u672C\u670D\u52A1\u628A Pixso \u8BBE\u8BA1\u7A3F\u80FD\u529B\u4EE5 MCP \u5DE5\u5177\u66B4\u9732\u7ED9 AI\u3002\u6570\u636E\u94FE\u8DEF\uFF1A
|
|
869
|
-
|
|
870
|
-
\`\`\`
|
|
871
|
-
AI \u5BA2\u6237\u7AEF \u2500\u2500MCP(stdio/HTTP)\u2500\u2500\u25B6 batter-pixso-mcp \u2500\u2500HTTP \u957F\u8F6E\u8BE2\u2500\u2500\u25B6 Pixso \u63D2\u4EF6(\u8BBE\u8BA1\u6587\u4EF6\u5185)
|
|
872
|
-
\`\`\`
|
|
873
|
-
|
|
874
|
-
## \u524D\u63D0
|
|
875
|
-
|
|
876
|
-
1. batter-pixso-mcp \u670D\u52A1\u5DF2\u542F\u52A8\uFF08agent \u7684 MCP \u914D\u7F6E\u4F1A\u81EA\u52A8\u62C9\u8D77\uFF09\u3002
|
|
877
|
-
2. Pixso \u5BA2\u6237\u7AEF\u4E2D\u5DF2\u5BFC\u5165\u5E76\u8FD0\u884C **Batter Pixso Bridge** \u63D2\u4EF6\uFF0C\u63D2\u4EF6 UI \u663E\u793A\u300C\u5DF2\u8FDE\u63A5\u672C\u5730\u670D\u52A1\u300D\u3002
|
|
878
|
-
3. \u76EE\u6807\u8BBE\u8BA1\u6587\u4EF6\u5904\u4E8E\u6253\u5F00\u72B6\u6001\u3002
|
|
879
|
-
|
|
880
|
-
## \u5DE5\u5177\u603B\u89C8
|
|
881
|
-
|
|
882
|
-
| \u5DE5\u5177 | \u7528\u9014 |
|
|
883
|
-
| --- | --- |
|
|
884
|
-
| \`get_selected\` | \u8BFB\u753B\u5E03\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\u3001\u5C3A\u5BF8\uFF09 |
|
|
885
|
-
| \`get_node_dsl\` | \u751F\u6210\u8282\u70B9\u7684\u5F00\u53D1\u5411\u7CBE\u7B80 DSL\uFF08\u7ED3\u6784 + \u6837\u5F0F + \u8D44\u6E90\u6587\u4EF6\u540D\uFF09\uFF0C\u8BBE\u8BA1\u8F6C\u4EE3\u7801\u7684\u6838\u5FC3\u5165\u53E3 |
|
|
886
|
-
| \`get_screenshot\` | \u5BFC\u51FA\u8282\u70B9\u622A\u56FE\uFF0C\u8FD4\u56DE\u56FE\u7247\u6587\u4EF6\u540D\uFF08\u672C\u673A\u8DEF\u5F84\u76F4\u8BFB\uFF09\uFF0C\u7528\u4E8E\u89C6\u89C9\u53C2\u7167/\u6821\u9A8C |
|
|
887
|
-
| \`export_image\` | \u6309 guid \u5BFC\u51FA\u56FE\u7247\u7D20\u6750\uFF08\u5207\u56FE\uFF09\uFF1A\u8282\u70B9\u5E26\u5BFC\u51FA\u6807\u8BB0\u65F6\u6309\u8BBE\u8BA1\u5E08\u8BBE\u7F6E\u9010\u4EFD\u5BFC\u51FA\uFF0C\u65E0\u6807\u8BB0\u6309\u53C2\u6570\u515C\u5E95 |
|
|
888
|
-
| \`list_sessions\` | \u5217\u51FA\u5168\u90E8\u63D2\u4EF6\u4F1A\u8BDD\uFF08\u591A\u6587\u4EF6\u573A\u666F\u8DEF\u7531\u7528\uFF09 |
|
|
889
|
-
|
|
890
|
-
## \u94FE\u63A5\u5373\u8F93\u5165\uFF08\u9AD8\u9891\u573A\u666F\uFF09
|
|
891
|
-
|
|
892
|
-
\u7528\u6237\u63D0\u4F9B Pixso \u94FE\u63A5\u65F6\uFF08\u5B98\u65B9 \`pixso.cn\` \u6216\u79C1\u6709\u5316\u90E8\u7F72\u5982 \`pixso.ky-tech.com.cn\`\uFF0C\u4EFB\u610F\u57DF\u540D\uFF09\uFF0C
|
|
893
|
-
\u94FE\u63A5\u672C\u8EAB\u5C31\u662F\u4EFB\u52A1\u8F93\u5165\uFF1A**\u5FC5\u987B**\u63D0\u53D6 \`item-id\` \u67E5\u8BE2\u53C2\u6570 \u2192 URL \u89E3\u7801 \u2192 \u5F97\u5230\u8282\u70B9 guid\uFF0C
|
|
894
|
-
\u76F4\u63A5\u8C03 \`get_node_dsl(guid)\` / \`get_screenshot(guid)\`\uFF0C\u65E0\u9700\u5148\u95EE\u7528\u6237\u9009\u4E2D\u4EC0\u4E48\u3002
|
|
895
|
-
\u4E25\u7981\u628A\u6574\u4E2A URL \u5F53 guid \u4F20\u3002
|
|
896
|
-
|
|
897
|
-
## \u591A\u6587\u4EF6\u573A\u666F
|
|
898
|
-
|
|
899
|
-
\u6BCF\u4E2A\u8FD0\u884C\u63D2\u4EF6\u7684\u8BBE\u8BA1\u6587\u4EF6\u662F\u4E00\u4E2A\u4F1A\u8BDD\u3002\u591A\u4E2A\u6587\u4EF6\u540C\u65F6\u6253\u5F00\u65F6\uFF1A
|
|
900
|
-
\u5148\u7528 \`list_sessions\` \u786E\u8BA4\u76EE\u6807\u6587\u4EF6\uFF0C\u518D\u628A\u8FD4\u56DE\u7684 \`sessionId\` \u6216 \`fileKey\`
|
|
901
|
-
\u4F5C\u4E3A\u5176\u4ED6\u5DE5\u5177\u7684 \`target\` \u53C2\u6570\u4F20\u5165\uFF1B\u4E0D\u4F20 \`target\` \u65F6\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u7684\u6587\u4EF6\u3002
|
|
902
|
-
|
|
903
|
-
\u8BE6\u7EC6\u5DE5\u4F5C\u6D41\u89C1\u8D44\u6E90 \`batter://guides/design-to-code\`\u3002
|
|
904
|
-
`;
|
|
905
|
-
DESIGN_TO_CODE = `# \u8BBE\u8BA1\u8F6C\u4EE3\u7801\u5DE5\u4F5C\u6D41
|
|
906
|
-
|
|
907
|
-
## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011
|
|
908
|
-
|
|
909
|
-
1. **\u8D44\u6E90\u662F\u672C\u673A\u6587\u4EF6\uFF0C\u6309\u9700\u53D6\u7528**\uFF1ADSL \u4E0E\u622A\u56FE\u7ED3\u679C\u4E2D\u7684 \`img\` \u5B57\u6BB5\u662F\u4EA7\u7269**\u6587\u4EF6\u540D**
|
|
910
|
-
\uFF08\u5982 \`"ab12cd34.svg"\`\uFF09\uFF0C\u5B8C\u6574\u8DEF\u5F84 = \u4EA7\u7269\u76EE\u5F55 + \u6587\u4EF6\u540D\uFF08\u76EE\u5F55\u89C1\u5DE5\u5177 description
|
|
911
|
-
\u4E2D\u7684\u5B9E\u9645\u8DEF\u5F84\uFF0C\u901A\u5E38\u4E3A \`%TEMP%\\batter-pixso-assets\`\uFF09\u3002\u4EA7\u7269\u4E0E\u670D\u52A1\u540C\u673A\uFF0C\u76F4\u63A5\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF1A
|
|
912
|
-
- **\u622A\u56FE\u7B49\u89C6\u89C9\u53C2\u7167**\uFF1A\u7528 Read \u5DE5\u5177\u76F4\u63A5\u8BFB\u8BE5\u8DEF\u5F84\u67E5\u770B\uFF0C\u65E0\u9700\u590D\u5236\u3002
|
|
913
|
-
- **\u8981\u5199\u8FDB\u4EA4\u4ED8\u4EE3\u7801\u7684\u56FE\u6807/\u56FE\u7247**\uFF1A**\u5FC5\u987B**\u5148\u590D\u5236\u5230\u9879\u76EE\u8D44\u6E90\u76EE\u5F55
|
|
914
|
-
\uFF08PowerShell \`Copy-Item\` / \u7C7B Unix \`cp\`\uFF09\uFF0C\u518D\u4EE5\u672C\u5730\u76F8\u5BF9\u8DEF\u5F84\u5F15\u7528
|
|
915
|
-
\uFF08\`<img src>\`\u3001CSS \`background-image\` \u7B49\uFF09\u3002\u4EA7\u7269\u6587\u4EF6\u540D\u662F\u5185\u5BB9 hash\uFF0C\u6CA1\u6709\u8BED\u4E49\u2014\u2014
|
|
916
|
-
**\u590D\u5236\u65F6\u6309\u8282\u70B9\u7528\u9014\u81EA\u884C\u547D\u540D\u8BED\u4E49\u5316\u6587\u4EF6\u540D**\uFF08\u5982 \`icon-arrow.svg\`\u3001\`hero-banner.png\`\uFF1B
|
|
917
|
-
\`export_image\` \u8FD4\u56DE\u7684 \`suffix\` \u5982 \`@2x\` \u5EFA\u8BAE\u4FDD\u7559\uFF09\uFF0C\u4E25\u7981\u628A hash \u6587\u4EF6\u540D\u5E26\u8FDB\u4EA4\u4ED8\u4EE3\u7801\u3002
|
|
918
|
-
\u4E25\u7981\u76F4\u63A5\u5F15\u7528\u4EA7\u7269\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4EA7\u7269\u9ED8\u8BA4 7 \u5929\u540E\u81EA\u52A8\u6E05\u626B\uFF0C\u5F15\u7528\u4F1A\u5931\u6548\uFF09\uFF1B
|
|
919
|
-
\u4E25\u7981\u79FB\u52A8\u6216\u5220\u9664\u4EA7\u7269\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\uFF08\u591A\u4F1A\u8BDD\u5171\u4EAB\uFF0C\u6E05\u626B\u7531\u670D\u52A1\u7EDF\u4E00\u8D1F\u8D23\uFF09\u3002
|
|
920
|
-
2. **Pixso \u94FE\u63A5\u5FC5\u987B\u63D0\u53D6 \`item-id\` \u5F53 guid**\uFF1A\u4EFB\u610F\u57DF\u540D\u90FD\u6709\u6548\u2014\u2014\u5B98\u65B9 \`pixso.cn\` \u6216\u79C1\u6709\u5316\u90E8\u7F72\u5982
|
|
921
|
-
\`https://pixso.ky-tech.com.cn/app/design/<\u6587\u4EF6>?item-id=1%3A2\`\uFF08\u4E5F\u53EF\u80FD\u662F \`/app/editor/\` \u8DEF\u5F84\uFF09\u3002
|
|
922
|
-
\u4ECE \`item-id\` \u67E5\u8BE2\u53C2\u6570\u63D0\u53D6 guid \u5E76 URL \u89E3\u7801\uFF08\`1%3A2\` \u2192 \`"1:2"\`\uFF09\u540E\u4F5C\u4E3A \`guid\` \u4F20\u5165\u3002
|
|
923
|
-
\u4E25\u7981\u628A\u6574\u4E2A URL \u5F53 guid \u4F20\uFF1B\u65E0\u8BBA\u57DF\u540D\u662F\u5426\u8BA4\u8BC6\uFF0C\u90FD\u6309\u6B64\u89C4\u5219\u5904\u7406\u3002
|
|
924
|
-
|
|
925
|
-
## \u63A8\u8350\u6B65\u9AA4
|
|
926
|
-
|
|
927
|
-
1. **\u786E\u8BA4\u76EE\u6807\u8282\u70B9**
|
|
928
|
-
- \u7528\u6237\u7ED9\u4E86 Pixso \u94FE\u63A5\uFF1A\u6309\u4E0A\u65B9\u786C\u6027\u89C4\u5219\u7B2C 2 \u6761\u63D0\u53D6 guid\uFF0C\u76F4\u63A5\u7EE7\u7EED\u3002
|
|
929
|
-
- \u7528\u6237\u660E\u786E\u8BF4\u4E86"\u8FD9\u4E2A/\u6211\u9009\u4E2D\u7684"\uFF1A\u8C03 \`get_selected\` \u62FF guid\uFF0C\u76F4\u63A5\u7EE7\u7EED\u3002
|
|
930
|
-
- \u5176\u4F59\u4F9D\u8D56\u5F53\u524D\u9009\u533A\u7684\u573A\u666F\uFF1A**\u4E25\u7981\u9690\u5F0F\u4F7F\u7528\u9009\u533A guid**\u2014\u2014\u8C03 \`get_selected\` \u540E
|
|
931
|
-
\u5FC5\u987B\u5148\u628A\u9009\u4E2D\u56FE\u5C42\u540D\u79F0\u62A5\u7ED9\u7528\u6237\u786E\u8BA4\uFF08"\u5F53\u524D\u9009\u4E2D\u7684\u662F\u300CXX\u300D\uFF0C\u786E\u8BA4\u5904\u7406\u5B83\u5417\uFF1F"\uFF09\uFF0C\u786E\u8BA4\u540E\u518D\u7EE7\u7EED\u3002
|
|
932
|
-
\`get_node_dsl\` / \`get_screenshot\` \u7684 guid \u7F3A\u7701\u56DE\u9000\u540C\u6837\u9002\u7528\u672C\u89C4\u5219\u3002
|
|
933
|
-
2. **\u53D6\u7ED3\u6784**\uFF1A\`get_node_dsl(guid)\` \u62FF\u7CBE\u7B80 DSL\u3002
|
|
934
|
-
3. **\u53D6\u89C6\u89C9\u53C2\u7167**\uFF1A\`get_screenshot(guid)\` \u62FF\u622A\u56FE\u6587\u4EF6\u540D\uFF0C\u6309\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\u62FC\u8DEF\u5F84\u540E\u7528 Read \u76F4\u8BFB\uFF0C\u4E0E DSL \u5BF9\u7167\u7406\u89E3\u89C6\u89C9\u5C42\u7EA7\u3002
|
|
935
|
-
4. **\u751F\u6210\u4EE3\u7801**\uFF1A\u6309\u4E0B\u6587 DSL \u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002
|
|
936
|
-
5. **\u6821\u9A8C**\uFF1A\u751F\u6210\u540E\u518D\u53D6\u4E00\u6B21\u622A\u56FE\u5BF9\u6BD4\uFF0C\u6709\u504F\u5DEE\u5C31\u4FEE\u6B63\u540E\u518D\u6BD4\u4E00\u8F6E\u3002
|
|
937
|
-
|
|
938
|
-
## DSL \u5B57\u6BB5\u8BED\u4E49\uFF08\u7D27\u51D1\u683C\u5F0F\uFF09
|
|
939
|
-
|
|
940
|
-
DSL \u662F\u5355\u884C\u7D27\u51D1 JSON\uFF1A\u77ED key\u3001\u4F4D\u7F6E\u6570\u7EC4\u3001\u9ED8\u8BA4\u503C\u7701\u7565\u3002\u56FE\u4F8B\uFF1A
|
|
354
|
+
DSL \u662F\u5355\u884C\u7D27\u51D1 JSON\uFF1A\u77ED key\u3001\u4F4D\u7F6E\u6570\u7EC4\u3001\u9ED8\u8BA4\u503C\u7701\u7565\u3002\u56FE\u4F8B\uFF1A
|
|
941
355
|
|
|
942
356
|
\`\`\`jsonc
|
|
943
357
|
{
|
|
944
358
|
"id": "297:133032", // \u8282\u70B9 guid\uFF0C\u53EF\u56DE\u4F20\u4E3A guid \u53C2\u6570\u5BF9\u8BE5\u8282\u70B9\u4E0B\u94BB\u8C03\u7528
|
|
945
359
|
"name": "\u65B0\u5BA2", // \u56FE\u5C42\u540D\uFF1B\u81EA\u52A8\u547D\u540D\uFF08"Frame 3123" \u7C7B\uFF09\u5DF2\u7701\u7565
|
|
946
360
|
"type": "FRAME", // \u8282\u70B9\u7C7B\u578B\uFF1BICON/IMAGE \u4E3A\u584C\u7F29\u53F6\u5B50
|
|
947
|
-
"rect": [0, 0, 375, 200], // [x,y,w,h] \u7EDD\u5BF9\u5B9A\u4F4D
|
|
948
|
-
"size": [120, 40], // [w,h]\uFF1Aauto-layout \u5B50\u8282\u70B9\uFF08x/y \u7531\u7236\u7EA7 layout \u63A8\u5BFC\uFF0C\u7701\u7565\uFF09
|
|
361
|
+
"rect": [0, 0, 375, 200], // [x,y,w,h] \u7EDD\u5BF9\u5B9A\u4F4D
|
|
949
362
|
"layout": { // auto-layout \u2192 flex \u76F4\u8BD1\uFF08\u4EC5\u5BB9\u5668\u6709\uFF09
|
|
950
363
|
"dir": "row", // row | column
|
|
951
364
|
"gap": 12, // \u4E3A 0 \u65F6\u7701\u7565
|
|
@@ -970,672 +383,1255 @@ DSL \u662F\u5355\u884C\u7D27\u51D1 JSON\uFF1A\u77ED key\u3001\u4F4D\u7F6E\u6570\
|
|
|
970
383
|
"constraints": { "h": "right", "v": "bottom" }, // \u7EDD\u5BF9\u5B9A\u4F4D\u5BB9\u5668\u5185\u7684\u7EA6\u675F\uFF08h: left|center|right|stretch|scale\uFF09\uFF0C\u9ED8\u8BA4 left/top \u4E0D\u8F93\u51FA
|
|
971
384
|
"opacity": 0.9, // \u4E3A 1 \u65F6\u7701\u7565
|
|
972
385
|
"img": "ab12cd34.svg", // \u53F6\u5B50\u8D44\u6E90\u6587\u4EF6\u540D\uFF08\u53D6\u7528\u89C4\u5219\u89C1\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\uFF09
|
|
386
|
+
"notes": ["\u70B9\u51FB\u8DF3\u8F6C\u9996\u9875"], // \u8BBE\u8BA1\u6807\u6CE8\uFF08\u8BBE\u8BA1\u5E08\u5199\u5728\u8282\u70B9\u4E0A\u7684\u6CE8\u89E3\uFF09\uFF0C\u4EC5\u5B58\u5728\u65F6\u8F93\u51FA\u2014\u2014\u8FD8\u539F\u65F6\u4F18\u5148\u8003\u8651\u5176\u610F\u56FE
|
|
387
|
+
"comp": { "key": "abc123", "name": "Button/Large", "props": { "size": "large" } }, // \u7EC4\u4EF6\u5173\u8054\uFF08INSTANCE/COMPONENT/COMPONENT_SET\uFF09\uFF0C\u89C1\u4E0B\u6587\u300C\u7EC4\u4EF6\u5173\u8054\u300D
|
|
973
388
|
"palette": ["#1a73e8", "#f5222d"], // \u8C03\u8272\u677F\uFF08\u4EC5\u6839\u8282\u70B9\uFF09\uFF0C"p0"/"p1" \u4E3A\u4E0B\u6807\u5F15\u7528
|
|
974
389
|
"depth": 6, // \u6574\u68F5\u6811\u5B9E\u9645\u6700\u5927\u6DF1\u5EA6\uFF08\u4EC5\u6839\u8282\u70B9\uFF0C\u603B\u662F\u8F93\u51FA\uFF0C\u4E0E\u622A\u65AD\u65E0\u5173\uFF09
|
|
975
390
|
"more": 3, // \u4EC5\u56E0 maxDepth \u622A\u65AD\u65F6\u51FA\u73B0\uFF1A\u8BE5\u8282\u70B9\u4E0B\u8FD8\u6709 3 \u7EA7\u672A\u5C55\u5F00\uFF0C\u62FF id \u56DE\u4F20 guid \u7EE7\u7EED\u4E0B\u94BB
|
|
391
|
+
"kidsSummary": [{"id": "...", "name": "...", "type": "FRAME"}], // \u622A\u65AD\u5904\u6216\u584C\u7F29\u53F6\u5B50\uFF08img \u8282\u70B9\uFF09\u7684\u76F4\u63A5\u5B50\u8282\u70B9\u6D45\u9884\u89C8
|
|
976
392
|
"kids": [] // \u5B50\u8282\u70B9\u9012\u5F52
|
|
977
393
|
}
|
|
978
394
|
\`\`\`
|
|
979
395
|
|
|
980
|
-
**\u6DF1\u5EA6\u63A7\u5236**\uFF1A\`get_node_dsl\` \u7684 \`maxDepth\` \u53C2\u6570\u9650\u5236\u4E0B\u63A2\u5C42\u7EA7\uFF08\u6839\u4E3A\u7B2C 0 \u5C42\uFF0C\u7F3A\u7701\u4E0D\u9650\uFF09\u3002
|
|
981
|
-
\u5927\u6811\u5206\u6BB5\u7B56\u7565\uFF1A\u5148 \`maxDepth: 0\` \u62FF\u6839\u8282\u70B9\uFF08\u542B \`depth\` \u603B\u6DF1\u5EA6\uFF09\u9884\u5224\u89C4\u6A21\uFF0C\u518D\u6309\u9700\u5BF9\u5E26 \`more\` \u6807\u8BB0\u7684\u5B50\u6811\u7528\u5176 \`id\` \u5355\u72EC\u4E0B\u94BB\u3002
|
|
396
|
+
**\u6DF1\u5EA6\u63A7\u5236**\uFF1A\`get_node_dsl\` \u7684 \`maxDepth\` \u53C2\u6570\u9650\u5236\u4E0B\u63A2\u5C42\u7EA7\uFF08\u6839\u4E3A\u7B2C 0 \u5C42\uFF0C\u7F3A\u7701\u4E0D\u9650\uFF09\u3002
|
|
397
|
+
\u5927\u6811\u5206\u6BB5\u7B56\u7565\uFF1A\u5148 \`maxDepth: 0\` \u62FF\u6839\u8282\u70B9\uFF08\u542B \`depth\` \u603B\u6DF1\u5EA6\uFF09\u9884\u5224\u89C4\u6A21\uFF0C\u518D\u6309\u9700\u5BF9\u5E26 \`more\` \u6807\u8BB0\u7684\u5B50\u6811\u7528\u5176 \`id\` \u5355\u72EC\u4E0B\u94BB\u3002
|
|
398
|
+
\u622A\u65AD\u8282\u70B9\u5E26 \`kidsSummary\`\uFF08\u76F4\u63A5\u5B50\u8282\u70B9\u7684 \`{id, name?, type}\` \u6D45\u9884\u89C8\uFF09\u2014\u2014\u636E\u6B64\u5224\u65AD\u54EA\u4E9B\u5206\u652F\u503C\u5F97\u5C55\u5F00\uFF0C\u4E0D\u5FC5\u76F2\u4E0B\u94BB\u6BCF\u4E2A \`more\` \u8282\u70B9\u3002
|
|
399
|
+
|
|
400
|
+
**\u8C03\u8272\u677F\u89C4\u5219**\uFF1A\u540C\u4E00\u989C\u8272\uFF08\u5355\u8272\uFF0C\u542B rgba\uFF09\u51FA\u73B0 \u22652 \u6B21\u65F6\u63D0\u53D6\u5230\u6839\u8282\u70B9 \`palette\` \u6570\u7EC4\uFF0C
|
|
401
|
+
\`fills\`\u3001\`text.color\`\u3001\`segs[].color\`\u3001\`stroke\` \u4E2D\u4EE5 \`"p0"\`\u3001\`"p1"\` \u5F15\u7528\uFF08\u4E0B\u6807\u5373 palette \u4E0B\u6807\uFF09\u3002
|
|
402
|
+
\`shadow\`\u3001\`gradient\` \u7B49\u590D\u5408 CSS \u5B57\u7B26\u4E32\u5185\u90E8\u7684\u989C\u8272\u4FDD\u6301\u5B57\u9762\u503C\uFF0C\u4E0D\u67E5\u8868\u3002
|
|
403
|
+
**palette \u989C\u8272\u4F18\u5148\u6620\u5C04\u5230\u9879\u76EE\u73B0\u6709 CSS \u53D8\u91CF/Tailwind \u4E3B\u9898\u8272**\uFF08\u5982 \`var(--primary)\`\u3001\`text-blue-500\`\uFF09\uFF0C\u5339\u914D\u4E0D\u4E0A\u624D\u7528\u88F8\u503C\u3002
|
|
404
|
+
|
|
405
|
+
## \u9519\u8BEF\u6062\u590D
|
|
406
|
+
|
|
407
|
+
- **\u7F3A guid**\uFF1A\u5411\u7528\u6237\u8981\u5B8C\u6574\u7684 Pixso \u94FE\u63A5\uFF08\u542B item-id \u67E5\u8BE2\u53C2\u6570\uFF09\uFF0C\u4E0D\u8981\u731C\u6D4B\u8282\u70B9 id\u3002
|
|
408
|
+
- **\u547D\u4EE4\u8D85\u65F6/\u7ED3\u679C\u8FC7\u5927**\uFF1A\u5BF9\u76EE\u6807\u8282\u70B9\u7684\u5B50\u8282\u70B9\u7528 \`get_node_dsl\` + \u8F83\u4F4E \`maxDepth\` \u5206\u6BB5\u53D6\uFF0C\u4E0D\u8981\u6574\u6811\u4E00\u6B21\u62C9\u3002
|
|
409
|
+
- **\u7ED3\u679C\u7591\u4F3C\u8FC7\u671F**\uFF08\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\uFF09\uFF1A\u7ED9\u5BFC\u51FA\u7C7B\u5DE5\u5177\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\u3002
|
|
410
|
+
- **\u63D2\u4EF6\u65E0\u54CD\u5E94**\uFF08ping \u63A2\u6D4B\u8D85\u65F6\uFF09\uFF1A\u63D2\u4EF6\u672A\u8FD0\u884C\u6216\u6B63\u5728\u6267\u884C\u957F\u547D\u4EE4\u2014\u2014\u7A0D\u540E\u91CD\u8BD5\uFF1B\u6587\u4EF6\u91CD\u5F00\u573A\u666F \`target\` \u7528 fileKey \u4F1A\u81EA\u52A8\u89E3\u6790\u5230\u65B0\u5B9E\u4F8B\u3002
|
|
411
|
+
- **\u7981\u6B62\u4EC5\u51ED\u622A\u56FE\u786C\u7F16\u7801**\uFF1A\u622A\u56FE\u662F\u6821\u9A8C\u4E0E\u610F\u56FE\u53C2\u7167\uFF0C\u4E0D\u662F\u7ED3\u6784\u6765\u6E90\u2014\u2014\u7ED3\u6784\u4E00\u5F8B\u8D70 DSL\u3002
|
|
412
|
+
|
|
413
|
+
## \u7EC4\u4EF6\u5173\u8054\uFF08comp \u5B57\u6BB5\uFF09
|
|
414
|
+
|
|
415
|
+
INSTANCE / COMPONENT / COMPONENT_SET \u8282\u70B9\u5E26 \`comp\` \u5B57\u6BB5\uFF0C\u7528\u4E8E\u628A\u8BBE\u8BA1\u7A3F\u7EC4\u4EF6\u6620\u5C04\u5230\u524D\u7AEF\u7EC4\u4EF6\u5E93\uFF1A
|
|
416
|
+
- \`comp.key\`\uFF1A\u7EC4\u4EF6\u8DE8\u6587\u4EF6\u7A33\u5B9A\u6807\u8BC6\uFF08\u53D1\u5E03\u5230\u56E2\u961F\u5E93\u540E\u4E0D\u53D8\uFF09\uFF0C\u662F\u540C\u7EC4\u4EF6\u4E0D\u540C\u5B9E\u4F8B\u7684\u552F\u4E00\u53EF\u9760\u5173\u8054\u952E\u3002
|
|
417
|
+
- \`comp.name\`\uFF1A\u7EC4\u4EF6\u540D\uFF08\u5982 \`"Button/Large"\`\uFF09\uFF0C**\u6620\u5C04\u6743\u91CD\u6700\u9AD8\u7684\u4F9D\u636E**\u2014\u2014\u636E\u6B64\u5728\u9879\u76EE\u7EC4\u4EF6\u5E93\u4E2D\u627E\u5BF9\u5E94\u524D\u7AEF\u7EC4\u4EF6\uFF08\u540D\u5B57\u4E0D\u5FC5\u9010\u5B57\u76F8\u540C\uFF0C\u8BED\u4E49\u5BF9\u9F50\u5373\u53EF\uFF09\uFF0C\u627E\u4E0D\u5230\u624D\u624B\u5199\u3002
|
|
418
|
+
- \`comp.props\`\uFF1Avariant \u5C5E\u6027\uFF08\u5982 \`{"size":"large","state":"hover"}\`\uFF09\uFF0C\u4EC5\u5B58\u5728\u65F6\u8F93\u51FA\u2014\u2014\u6620\u5C04\u5230\u524D\u7AEF\u7EC4\u4EF6\u7684 props\u3002
|
|
419
|
+
- \`comp.set\`\uFF1A\u672C\u8282\u70B9\u662F variant \u7EC4\uFF08COMPONENT_SET\uFF09\uFF0C\u5176\u5B50\u8282\u70B9\u5404\u81EA\u5E26 \`comp.key\`\u3002
|
|
420
|
+
\u584C\u7F29\u4E3A ICON \u7684\u7EC4\u4EF6\u5B9E\u4F8B\u540C\u6837\u5E26 \`comp\`\uFF08\u8D44\u6E90\u4E0E\u7EC4\u4EF6\u8EAB\u4EFD\u4E0D\u51B2\u7A81\uFF09\u3002
|
|
421
|
+
|
|
422
|
+
## \u53F6\u5B50\u8282\u70B9\u4E0E\u56FE\u7247\u8D44\u6E90
|
|
423
|
+
|
|
424
|
+
- \`type: "ICON"\`\uFF1A\u7EAF\u77E2\u91CF\u7EC4\u4EF6\u5B9E\u4F8B\uFF08icon\uFF09\uFF0C\`img\` \u4E3A **SVG** \u6587\u4EF6\u540D\u3002
|
|
425
|
+
- \`type: "IMAGE"\`\uFF1A\u56FE\u7247\u586B\u5145\u8282\u70B9\uFF0C\`img\` \u4E3A **PNG** \u6587\u4EF6\u540D\u3002
|
|
426
|
+
- \u6587\u4EF6\u540D\u62FC\u8DEF\u5F84\u4E0E\u53D6\u7528\u8981\u6C42\u89C1\u5F00\u5934\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\u3002
|
|
427
|
+
- \u5BB9\u5668\u81EA\u8EAB\u6709\u5BFC\u51FA\u8BBE\u7F6E\u6807\u8BB0\u65F6\u4E5F\u4F1A\u584C\u7F29\u4E3A\u53F6\u5B50\uFF08\u5C0A\u91CD\u8BBE\u8BA1\u5E08\u7684\u5BFC\u51FA\u610F\u56FE\uFF09\u3002
|
|
428
|
+
- **\u584C\u7F29\u53F6\u5B50\u5E26 \`kidsSummary\`**\uFF08\u76F4\u63A5\u5B50\u8282\u70B9\u6D45\u9884\u89C8\uFF09\uFF1A\u5185\u90E8\u7ED3\u6784\u9ED8\u8BA4\u584C\u7F29\u9690\u85CF\uFF0C\u9700\u8981\u4E0B\u63A2\u65F6
|
|
429
|
+
\uFF08\u5982\u5B9E\u4F8B\u5185\u8FD8\u6709\u6587\u672C/\u5FBD\u6807/\u5355\u72EC\u6807\u8BB0\u5BFC\u51FA\u7684\u5B50\u8282\u70B9\uFF09\u62FF\u5B50\u8282\u70B9 \`id\` \u56DE\u4F20 \`guid\` \u53D6\u5168\u91CF DSL\u3002
|
|
430
|
+
- **\u5DF2\u5E26 \`img\` \u7684\u8282\u70B9\u5728\u53D6 DSL \u65F6\u5DF2\u5B8C\u6210\u5BFC\u51FA**\uFF1A\u76F4\u63A5\u4F7F\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E25\u7981\u5BF9\u5B83\u4EEC\u518D\u8C03 \`export_image\`\uFF08\u91CD\u590D\u5BFC\u51FA\u540C\u4E00\u4EFD\u5185\u5BB9\uFF09\u3002
|
|
431
|
+
\`export_image\` \u4EC5\u7528\u4E8E DSL \u4E2D\u672A\u584C\u7F29\u7684\u8282\u70B9\uFF08\u4E3B\u52A8\u5207\u56FE\uFF09\u3002
|
|
432
|
+
|
|
433
|
+
## \u6CE8\u610F
|
|
434
|
+
|
|
435
|
+
- DSL \u662F**\u6709\u635F\u7CBE\u7B80**\uFF1A\u7701\u7565\u4E86\u5E03\u5C14\u8FD0\u7B97\u8DEF\u5F84\u3001\u8499\u7248\u7B49\u7EAF\u8BBE\u8BA1\u7EC6\u8282\uFF0C\u89C6\u89C9\u590D\u6742\u8282\u70B9\u4EE5\u622A\u56FE\u4E3A\u51C6\u3002
|
|
436
|
+
- \u5BFC\u51FA\u7C7B\u5DE5\u5177\uFF08\`get_node_dsl\`/\`get_screenshot\`/\`export_image\`\uFF09\u5E26 15 \u5206\u949F\u7ED3\u679C\u7F13\u5B58\uFF08\u63D2\u4EF6\u4FA7 TTL\uFF09\uFF1B
|
|
437
|
+
\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\u4F46\u7ED3\u679C\u5BF9\u4E0D\u4E0A\u65F6\uFF0C\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\u3002
|
|
438
|
+
- \u591A\u6587\u4EF6\u573A\u666F\u6240\u6709\u5DE5\u5177\u90FD\u652F\u6301 \`target\` \u53C2\u6570\uFF08sessionId/fileKey\uFF09\uFF0C\u89C1 \`batter://guides/intro\`\u3002
|
|
439
|
+
`;
|
|
440
|
+
var GUIDES = [
|
|
441
|
+
{
|
|
442
|
+
name: "intro",
|
|
443
|
+
uri: "batter://guides/intro",
|
|
444
|
+
description: "batter-pixso-mcp \u6982\u89C8\uFF1A\u5DE5\u5177\u6E05\u5355\u3001\u94FE\u63A5\u63D0\u53D6 item-id\u3001\u591A\u6587\u4EF6\u4F1A\u8BDD\u8DEF\u7531\u3002",
|
|
445
|
+
text: INTRO
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: "design-to-code",
|
|
449
|
+
uri: "batter://guides/design-to-code",
|
|
450
|
+
description: "\u8BBE\u8BA1\u8F6C\u4EE3\u7801\uFF08\u6837\u5F0F\u8FD8\u539F/\u8BBE\u8BA1\u8FD8\u539F\uFF09\u5DE5\u4F5C\u6D41\uFF1A\u786C\u6027\u89C4\u5219\uFF08\u8D44\u6E90\u6309\u672C\u673A\u8DEF\u5F84\u53D6\u7528\u3001\u94FE\u63A5\u63D0\u53D6 item-id\uFF09\u3001DSL \u5B57\u6BB5\u8BED\u4E49\u3001\u89C6\u89C9\u6821\u9A8C\u3002",
|
|
451
|
+
text: DESIGN_TO_CODE
|
|
452
|
+
}
|
|
453
|
+
];
|
|
454
|
+
|
|
455
|
+
// src/agent/prompts.ts
|
|
456
|
+
var DESIGN_TO_CODE2 = {
|
|
457
|
+
name: "design-to-code",
|
|
458
|
+
description: "\u8BBE\u8BA1\u7A3F\u8F6C\u524D\u7AEF\u4EE3\u7801\uFF08\u6837\u5F0F\u8FD8\u539F/\u8BBE\u8BA1\u8FD8\u539F/UI \u8FD8\u539F\uFF09\uFF1A\u4ECE Pixso \u94FE\u63A5\u6216\u5F53\u524D\u9009\u533A\u53D6 DSL + \u622A\u56FE \u2192 \u751F\u6210 \u2192 \u622A\u56FE\u6821\u9A8C\u3002",
|
|
459
|
+
arguments: [
|
|
460
|
+
{
|
|
461
|
+
name: "url",
|
|
462
|
+
description: "Pixso \u94FE\u63A5\uFF08\u542B item-id \u67E5\u8BE2\u53C2\u6570\uFF0C\u4EFB\u610F\u57DF\u540D\uFF1A\u5B98\u65B9 pixso.cn \u6216\u79C1\u6709\u5316\u90E8\u7F72\uFF09\u3002\u7559\u7A7A\u5219\u4F7F\u7528\u753B\u5E03\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\u3002",
|
|
463
|
+
required: false
|
|
464
|
+
}
|
|
465
|
+
],
|
|
466
|
+
render: (args) => {
|
|
467
|
+
const targetLine = args.url?.trim() ? `\u76EE\u6807\u8282\u70B9\u6765\u81EA\u94FE\u63A5\uFF1A${args.url.trim()}\u3002\u5FC5\u987B\u4ECE\u67E5\u8BE2\u53C2\u6570 \`item-id\` \u63D0\u53D6\u8282\u70B9 guid \u5E76 URL \u89E3\u7801\uFF08\`1%3A2\` \u2192 \`"1:2"\`\uFF09\u540E\u4F5C\u4E3A guid \u4F20\u5165\uFF1B\u4E25\u7981\u628A\u6574\u4E2A URL \u5F53 guid \u4F20\u3002\u65E0\u8BBA\u57DF\u540D\u662F\u5426\u8BA4\u8BC6\uFF0C\u90FD\u6309\u6B64\u89C4\u5219\u5904\u7406\u3002` : `\u76EE\u6807\u8282\u70B9\u4E3A\u5F53\u524D\u9009\u533A\uFF1A\u8C03 \`get_selected\` \u62FF\u9009\u4E2D\u56FE\u5C42\u3002\u7981\u6B62\u9690\u5F0F\u4F7F\u7528\u5176 guid\u2014\u2014\u9664\u975E\u7528\u6237\u660E\u786E\u8BF4\u4E86"\u6211\u9009\u4E2D\u7684/\u5F53\u524D\u9009\u4E2D\u7684"\uFF0C\u5148\u628A\u9009\u4E2D\u56FE\u5C42\u540D\u79F0\u62A5\u7ED9\u7528\u6237\u786E\u8BA4\uFF08"\u5F53\u524D\u9009\u4E2D\u7684\u662F\u300CXX\u300D\uFF0C\u786E\u8BA4\u5904\u7406\u5B83\u5417\uFF1F"\uFF09\uFF0C\u786E\u8BA4\u540E\u518D\u7EE7\u7EED\u3002`;
|
|
468
|
+
return [
|
|
469
|
+
{
|
|
470
|
+
role: "user",
|
|
471
|
+
content: {
|
|
472
|
+
type: "text",
|
|
473
|
+
text: [
|
|
474
|
+
`\u628A Pixso \u8BBE\u8BA1\u7A3F\u8FD8\u539F\u4E3A\u524D\u7AEF\u4EE3\u7801\u3002`,
|
|
475
|
+
``,
|
|
476
|
+
`## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011`,
|
|
477
|
+
``,
|
|
478
|
+
`**\u5199\u4EE3\u7801\u524D\u5FC5\u987B\u5148\u8BFB**\u8D44\u6E90 \`batter://guides/design-to-code\` \u5E76\u9075\u5B88\u5176\u5168\u90E8\u786C\u6027\u89C4\u5219\uFF08\u8D44\u6E90\u53D6\u7528/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D/\u7981\u6B62\u622A\u56FE\u786C\u7F16\u7801\uFF09\uFF1B\u603B\u89C8\u89C1 \`batter://guides/intro\`\u3002`,
|
|
479
|
+
``,
|
|
480
|
+
`## \u6267\u884C\u6D41\u7A0B`,
|
|
481
|
+
``,
|
|
482
|
+
`1. \u786E\u8BA4\u76EE\u6807\u8282\u70B9\uFF1A${targetLine}`,
|
|
483
|
+
`2. \u53D6\u6570\u636E\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, data}\` \u4FE1\u5C01\uFF0C\u53D6 \`data\` \u5B57\u6BB5\u3002\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\uFF1A\u5148 \`get_metadata(guid)\` \u62FF\u5168\u6811\u9AA8\u67B6\u5B9A\u4F4D\u76EE\u6807 + \`get_screenshot(guid)\` \u62FF\u89C6\u89C9\u5168\u8C8C\uFF1B\u968F\u540E \`get_node_dsl(guid)\` \u62FF\u5168\u91CF DSL\uFF08\u7ED3\u6784\u5F88\u5927\u65F6\u7528 \`maxDepth\` \u5206\u6BB5\u4E0B\u63A2\u2014\u2014\u622A\u65AD\u5904\u8282\u70B9\u5E26 \`more\` \u6807\u8BB0\u4E0E \`kidsSummary\` \u6D45\u9884\u89C8\uFF0C\u62FF\u5176 \`id\` \u56DE\u4F20 \`guid\` \u4E0B\u94BB\uFF09\u3002DSL \u4E2D\u5DF2\u5E26 \`img\` \u7684\u8282\u70B9\uFF08ICON/IMAGE \u53F6\u5B50\uFF09\u5DF2\u5B8C\u6210\u5BFC\u51FA\uFF0C\u76F4\u63A5\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E0D\u8981\u518D\u8C03 \`export_image\`\u3002`,
|
|
484
|
+
`3. \u751F\u6210\u4EE3\u7801\uFF1A\u6309\u6307\u5357\u4E2D\u7684 DSL \u5B57\u6BB5\u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002`,
|
|
485
|
+
`4. \u81EA\u68C0\uFF1A\u518D\u6B21\u8C03 \`get_screenshot(guid)\`\uFF0C\u4E0E\u4EE3\u7801\u6E32\u67D3\u7ED3\u679C\u5BF9\u6BD4\uFF0C\u6709\u504F\u5DEE\u5C31\u4FEE\u6B63\u540E\u518D\u6BD4\u4E00\u8F6E\uFF08\u5BFC\u51FA\u7C7B\u5DE5\u5177\u5E26 15 \u5206\u949F\u7F13\u5B58\uFF1B\u7ED3\u679C\u7591\u4F3C\u8FC7\u671F\u65F6\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\uFF09\u3002`,
|
|
486
|
+
``,
|
|
487
|
+
`**\u6700\u540E\u91CD\u7533**\uFF1A\u4EA4\u4ED8\u4EE3\u7801\u4E2D\u4E0D\u5141\u8BB8\u51FA\u73B0\u4EA7\u7269\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5168\u90E8\u56FE\u7247\u5FC5\u987B\u662F\u5DF2\u590D\u5236\u5230\u9879\u76EE\u7684\u672C\u5730\u76F8\u5BF9\u8DEF\u5F84\uFF08\u7EC6\u5219\u89C1\u6307\u5357\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\uFF09\u3002`
|
|
488
|
+
].join("\n")
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
];
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
var PROMPTS = [DESIGN_TO_CODE2];
|
|
495
|
+
|
|
496
|
+
// src/plugin/command-bus.ts
|
|
497
|
+
var CommandBus = class {
|
|
498
|
+
sseListeners = /* @__PURE__ */ new Map();
|
|
499
|
+
pendingById = /* @__PURE__ */ new Map();
|
|
500
|
+
seq = 0;
|
|
501
|
+
/** AI 侧:执行命令 */
|
|
502
|
+
execute(type, args, opts) {
|
|
503
|
+
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
504
|
+
const { promise, resolve, reject } = withResolvers();
|
|
505
|
+
const command = { id: `cmd-${++this.seq}`, type, args };
|
|
506
|
+
const entry = {
|
|
507
|
+
command,
|
|
508
|
+
sessionId: opts.sessionId,
|
|
509
|
+
resolve,
|
|
510
|
+
reject,
|
|
511
|
+
dispatched: false,
|
|
512
|
+
timer: setTimeout(() => {
|
|
513
|
+
this.drop(entry);
|
|
514
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u8D85\u65F6\uFF1A\u63D2\u4EF6 ${timeoutMs / 1e3}s \u5185\u672A\u8FD4\u56DE\u7ED3\u679C`));
|
|
515
|
+
}, timeoutMs)
|
|
516
|
+
};
|
|
517
|
+
opts.signal?.addEventListener("abort", () => {
|
|
518
|
+
if (!this.pendingById.has(command.id)) return;
|
|
519
|
+
this.drop(entry);
|
|
520
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u5DF2\u88AB\u8C03\u7528\u65B9\u53D6\u6D88`));
|
|
521
|
+
});
|
|
522
|
+
this.pendingById.set(command.id, entry);
|
|
523
|
+
this.dispatch(opts.sessionId);
|
|
524
|
+
return promise;
|
|
525
|
+
}
|
|
526
|
+
/** 插件侧:订阅即时推送(SSE 通道),返回退订函数;补发订阅前已入队的命令 */
|
|
527
|
+
subscribe(sessionId, listener) {
|
|
528
|
+
let subs = this.sseListeners.get(sessionId);
|
|
529
|
+
if (!subs) {
|
|
530
|
+
subs = /* @__PURE__ */ new Set();
|
|
531
|
+
this.sseListeners.set(sessionId, subs);
|
|
532
|
+
}
|
|
533
|
+
subs.add(listener);
|
|
534
|
+
this.dispatch(sessionId);
|
|
535
|
+
return () => {
|
|
536
|
+
subs.delete(listener);
|
|
537
|
+
if (subs.size === 0) this.sseListeners.delete(sessionId);
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
/** 插件侧:回传结果 */
|
|
541
|
+
complete(id, ok, resultOrError) {
|
|
542
|
+
const entry = this.pendingById.get(id);
|
|
543
|
+
if (!entry) return false;
|
|
544
|
+
this.drop(entry);
|
|
545
|
+
if (ok) {
|
|
546
|
+
entry.resolve(resultOrError);
|
|
547
|
+
} else {
|
|
548
|
+
entry.reject(resultOrError instanceof Error ? resultOrError : new Error(String(resultOrError)));
|
|
549
|
+
}
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
/** 排空该会话全部未派发命令(无订阅者则留待 subscribe 补发) */
|
|
553
|
+
dispatch(sessionId) {
|
|
554
|
+
const subs = this.sseListeners.get(sessionId);
|
|
555
|
+
if (!subs || subs.size === 0) return;
|
|
556
|
+
for (const entry of this.pendingById.values()) {
|
|
557
|
+
if (entry.sessionId !== sessionId || entry.dispatched) continue;
|
|
558
|
+
entry.dispatched = true;
|
|
559
|
+
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)}`);
|
|
560
|
+
for (const fn of subs) fn(entry.command);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
/** 从 pending 表移除(完成/超时/取消共用) */
|
|
564
|
+
drop(entry) {
|
|
565
|
+
clearTimeout(entry.timer);
|
|
566
|
+
this.pendingById.delete(entry.command.id);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
var commandBus = new CommandBus();
|
|
570
|
+
|
|
571
|
+
// src/agent/tools/shared.ts
|
|
572
|
+
var PING_TIMEOUT_MS = 800;
|
|
573
|
+
async function run(type, args, signal) {
|
|
574
|
+
const { target, ...rest } = args;
|
|
575
|
+
try {
|
|
576
|
+
const session = await resolveAndPing(target, signal);
|
|
577
|
+
const data = await commandBus.execute(type, rest, { sessionId: session.id, signal });
|
|
578
|
+
sessions.touch(session.id);
|
|
579
|
+
return jsonResult({ sessionId: session.id, data });
|
|
580
|
+
} catch (err) {
|
|
581
|
+
return errorResult(err);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
async function pingSession(sessionId, signal) {
|
|
585
|
+
try {
|
|
586
|
+
await commandBus.execute("ping", {}, { sessionId, timeoutMs: PING_TIMEOUT_MS, signal });
|
|
587
|
+
return true;
|
|
588
|
+
} catch (err) {
|
|
589
|
+
if (signal?.aborted) throw err;
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async function pingCandidates(candidates, affinityId, signal) {
|
|
594
|
+
const check = async (c) => {
|
|
595
|
+
if (await pingSession(c.id, signal)) return c;
|
|
596
|
+
throw new Error("no pong");
|
|
597
|
+
};
|
|
598
|
+
const affinity = candidates.find((c) => c.id === affinityId);
|
|
599
|
+
try {
|
|
600
|
+
if (affinity && await pingSession(affinity.id, signal)) return affinity;
|
|
601
|
+
const rest = affinity ? candidates.filter((c) => c !== affinity) : candidates;
|
|
602
|
+
return await Promise.any(rest.map(check));
|
|
603
|
+
} catch {
|
|
604
|
+
if (signal?.aborted) throw new Error("\u547D\u4EE4\u5DF2\u88AB\u8C03\u7528\u65B9\u53D6\u6D88");
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async function resolveAndPing(target, signal) {
|
|
609
|
+
const s = target ? sessions.get(target) : sessions.resolve();
|
|
610
|
+
const fileKey = s ? s.meta.fileKey : target;
|
|
611
|
+
const candidates = s && !fileKey ? [s] : sessions.candidatesForFileKey(fileKey);
|
|
612
|
+
if (candidates.length === 0) {
|
|
613
|
+
if (s) throw new Error(`\u76EE\u6807\u63D2\u4EF6\u5DF2\u79BB\u7EBF\uFF1A${s.meta.fileName ?? s.id}\uFF08\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u5E76\u8FD0\u884C\u63D2\u4EF6\u540E\u91CD\u8BD5\uFF09`);
|
|
614
|
+
throw unknownTargetError(target);
|
|
615
|
+
}
|
|
616
|
+
const winner = await pingCandidates(candidates, fileKey ? sessions.affinityIdOf(fileKey) : void 0, signal);
|
|
617
|
+
if (!winner) {
|
|
618
|
+
for (const c of candidates) sessions.evict(c.id);
|
|
619
|
+
throw new Error(
|
|
620
|
+
`\u63D2\u4EF6\u65E0\u54CD\u5E94\uFF08${PING_TIMEOUT_MS}ms \u672A\u56DE pong\uFF09\uFF1A${fileKey ?? target}\u2014\u2014\u63D2\u4EF6\u672A\u8FD0\u884C\uFF0C\u6216\u6B63\u5728\u6267\u884C\u957F\u547D\u4EE4\uFF08\u7A0D\u540E\u91CD\u8BD5\uFF09`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
if (fileKey) sessions.pinAffinity(fileKey, winner.id);
|
|
624
|
+
return winner;
|
|
625
|
+
}
|
|
626
|
+
var ENVELOPE_NOTE = "\u8FD4\u56DE\u5C01\u88C5\u4E3A {sessionId, data}\uFF08\u9519\u8BEF\u4E3A isError \u6587\u672C\uFF0C\u4E0D\u542B\u4FE1\u5C01\uFF09\uFF1AsessionId \u662F ping/pong \u786E\u8BA4\u7684\u5B9E\u9645\u670D\u52A1\u4F1A\u8BDD\uFF1Btarget \u7528 fileKey \u6700\u7A33\u2014\u2014\u6587\u4EF6\u91CD\u5F00\u540E\u81EA\u52A8\u89E3\u6790\u5230\u5B58\u6D3B\u5B9E\u4F8B\uFF08\u591A\u5B9E\u4F8B\u4F18\u5148\u4E0A\u4E00\u6B21\u4F7F\u7528\u7684\uFF09\u3002";
|
|
627
|
+
var GUIDES_PTR = "\u5B8C\u6574\u89C4\u5219\uFF08\u8D44\u6E90\u53D6\u7528/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D/\u7981\u6B62\u622A\u56FE\u786C\u7F16\u7801\uFF09\u89C1\u8D44\u6E90 batter://guides/design-to-code\u3002";
|
|
628
|
+
var TARGET_PROP = {
|
|
629
|
+
type: "string",
|
|
630
|
+
description: "\u76EE\u6807\u63D2\u4EF6\u4F1A\u8BDD\uFF08sessionId \u6216 fileKey\uFF09\u3002\u591A\u4E2A Pixso \u6587\u4EF6\u540C\u65F6\u5F00\u63D2\u4EF6\u65F6\u624D\u9700\u8981\uFF1B\u7F3A\u7701\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u6587\u4EF6\u3002\u53EF\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u3002"
|
|
631
|
+
};
|
|
632
|
+
var CACHE_PROP = {
|
|
633
|
+
type: "string",
|
|
634
|
+
enum: ["first", "refresh"],
|
|
635
|
+
description: "\u7F13\u5B58\u7B56\u7565\uFF1Afirst=\u7F13\u5B58\u4F18\u5148\uFF08\u9ED8\u8BA4\uFF0C15 \u5206\u949F\u5185\u76F8\u540C\u8C03\u7528\u76F4\u63A5\u8FD4\u56DE\uFF09\uFF1Brefresh=\u5F3A\u5236\u91CD\u65B0\u6784\u5EFA/\u5BFC\u51FA\uFF08\u4ECD\u5199\u5165\u7F13\u5B58\uFF09\u3002\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\u4E14\u7ED3\u679C\u5BF9\u4E0D\u4E0A\u65F6\u7528 refresh\u3002"
|
|
636
|
+
};
|
|
637
|
+
var GUID_PROP = {
|
|
638
|
+
type: "string",
|
|
639
|
+
description: '\u8282\u70B9 GUID\uFF08\u5982 "297:133032"\uFF09\u3002\u94FE\u63A5\u8F93\u5165\uFF1A\u4ECE item-id \u63D0\u53D6\u5E76 URL \u89E3\u7801\u540E\u4F20\u5165\uFF1B\u7F3A\u7701\u56DE\u9000\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08\u7981\u6B62\u9690\u5F0F\u4F7F\u7528\u2014\u2014\u9009\u4E2D\u786E\u8BA4\u89C4\u5219\u89C1 get_selected\uFF09\u3002'
|
|
640
|
+
};
|
|
641
|
+
function jsonResult(data) {
|
|
642
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
643
|
+
}
|
|
644
|
+
function errorResult(err) {
|
|
645
|
+
return {
|
|
646
|
+
content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
|
|
647
|
+
isError: true
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/agent/tools/export_image.ts
|
|
652
|
+
var exportImage = {
|
|
653
|
+
description: '\u3010\u7528\u9014\u3011\u5BFC\u51FA\u8282\u70B9\u7684\u56FE\u7247\u7D20\u6750\uFF08\u5207\u56FE\uFF09\uFF0C\u4F9B\u4EA4\u4ED8\u4EE3\u7801\u4F7F\u7528\u3002\u5355\u8282\u70B9\u4F20 guid\uFF1B\u591A\u8282\u70B9\u4F20 guids \u6570\u7EC4\u6279\u91CF\u3002DSL \u4E2D\u5DF2\u5E26 img \u7684\u8282\u70B9\uFF08ICON/IMAGE \u53F6\u5B50\uFF09\u5728\u53D6 DSL \u65F6\u5DF2\u5BFC\u51FA\uFF0C\u76F4\u63A5\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E25\u7981\u5BF9\u672C\u7C7B\u8282\u70B9\u91CD\u590D\u8C03\u7528\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011"\u5207\u56FE/\u5BFC\u51FA\u56FE\u6807/\u5BFC\u51FA\u56FE\u7247\u7D20\u6750/\u628A\u67D0\u4E2A\u8282\u70B9\u5B58\u6210\u56FE\u7247"\u3002\u8981\u89C6\u89C9\u53C2\u7167\u7528 get_screenshot\u3002\u3010\u8FD4\u56DE\u3011\u5355\u8282\u70B9 {width, height, exports: [{img, format, scale?, suffix?}]}\uFF1B\u6279\u91CF {items: [{guid, width, height, exports}]}\u3002\u8282\u70B9\u5E26\u8BBE\u8BA1\u5E08\u5BFC\u51FA\u8BBE\u7F6E\u65F6\u6309\u5176\u8BBE\u7F6E\u9010\u4EFD\u5BFC\u51FA\uFF08format/scale \u53C2\u6570\u5931\u6548\uFF09\uFF1B\u65E0\u6807\u8BB0\u6309\u53C2\u6570\u5BFC\u51FA\u5355\u4EFD\uFF08\u9ED8\u8BA4 PNG\u30012x\uFF09\u3002suffix\uFF08\u5982 "@2x"\uFF09\u590D\u5236\u8FDB\u9879\u76EE\u65F6\u4FDD\u7559\u547D\u540D\u3002\u3010\u786C\u6027\u89C4\u5219\u3011' + GUIDES_PTR + " " + ENVELOPE_NOTE,
|
|
654
|
+
inputSchema: {
|
|
655
|
+
type: "object",
|
|
656
|
+
properties: {
|
|
657
|
+
guid: GUID_PROP,
|
|
658
|
+
guids: {
|
|
659
|
+
type: "array",
|
|
660
|
+
items: { type: "string" },
|
|
661
|
+
description: "\u6279\u91CF\u5BFC\u51FA\uFF1A\u8282\u70B9 GUID \u6570\u7EC4\uFF08\u4E0E guid \u4E8C\u9009\u4E00\uFF09\u3002\u591A\u8282\u70B9\u573A\u666F\u4F18\u5148\u7528\u6279\u91CF\uFF0C\u907F\u514D\u9010\u4E2A\u8C03\u7528\u3002"
|
|
662
|
+
},
|
|
663
|
+
format: {
|
|
664
|
+
type: "string",
|
|
665
|
+
enum: ["PNG", "SVG", "JPG"],
|
|
666
|
+
description: "\u65E0\u5BFC\u51FA\u6807\u8BB0\u65F6\u7684\u515C\u5E95\u683C\u5F0F\uFF0C\u9ED8\u8BA4 PNG\u3002\u8282\u70B9\u5E26\u5BFC\u51FA\u6807\u8BB0\u65F6\u6B64\u53C2\u6570\u65E0\u6548\u3002"
|
|
667
|
+
},
|
|
668
|
+
scale: {
|
|
669
|
+
type: "number",
|
|
670
|
+
description: "\u65E0\u5BFC\u51FA\u6807\u8BB0\u65F6\u7684\u515C\u5E95\u500D\u7387\uFF0C\u9ED8\u8BA4 2\u3002\u8282\u70B9\u5E26\u5BFC\u51FA\u6807\u8BB0\u65F6\u6B64\u53C2\u6570\u65E0\u6548\u3002"
|
|
671
|
+
},
|
|
672
|
+
cache: CACHE_PROP,
|
|
673
|
+
target: TARGET_PROP
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// src/agent/tools/get_metadata.ts
|
|
679
|
+
var getMetadata = {
|
|
680
|
+
description: '\u3010\u7528\u9014\u3011\u5168\u6811\u7ED3\u6784\u9AA8\u67B6\uFF08\u4EC5 id/name/type/kids\uFF0C\u65E0\u6837\u5F0F/\u5750\u6807/\u8D44\u6E90\uFF0C\u4F53\u79EF\u6781\u5C0F\uFF09\u3002\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\u65F6\uFF1A\u672C\u5DE5\u5177 + get_screenshot \u5B9A\u4F4D\u76EE\u6807\u8282\u70B9\uFF0C\u518D\u5BF9\u76EE\u6807\u8C03 get_node_dsl \u53D6\u5168\u91CF\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\uFF1B"\u9875\u9762\u91CC\u6709\u6CA1\u6709 XX"\uFF1B\u6309\u540D\u5B57\u627E\u8282\u70B9\uFF1B\u53EA\u8981\u7ED3\u6784\u6982\u89C8\u4E0D\u8981\u6837\u5F0F\u3002\u3010\u8FD4\u56DE\u3011\u7D27\u51D1 JSON\uFF1A{id, name?, type, kids: \u9012\u5F52}\uFF1BICON/IMAGE \u53F6\u5B50\u65E0 img\uFF08\u9700\u8D44\u6E90\u65F6\u6309 id \u8C03 get_node_dsl\uFF09\u3002\u3010\u786C\u6027\u89C4\u5219\u3011' + GUIDES_PTR + " " + ENVELOPE_NOTE,
|
|
681
|
+
inputSchema: {
|
|
682
|
+
type: "object",
|
|
683
|
+
properties: {
|
|
684
|
+
guid: GUID_PROP,
|
|
685
|
+
cache: CACHE_PROP,
|
|
686
|
+
target: TARGET_PROP
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
// src/agent/tools/get_node_dsl.ts
|
|
692
|
+
var URL_RULE = '1. \u94FE\u63A5\u8F93\u5165\uFF1A\u5FC5\u987B\u4ECE item-id \u67E5\u8BE2\u53C2\u6570\u63D0\u53D6\u8282\u70B9 guid \u5E76 URL \u89E3\u7801\uFF081%3A2 \u2192 "1:2"\uFF09\uFF0C\u628A\u89E3\u7801\u503C\u4F5C\u4E3A guid \u4F20\u5165\uFF1B\u4E25\u7981\u628A\u6574\u4E2A URL \u5F53 guid \u4F20\u3002\u4EFB\u610F\u57DF\u540D\u90FD\u6709\u6548\uFF1A\u5B98\u65B9 pixso.cn \u6216\u79C1\u6709\u5316\u90E8\u7F72\uFF08\u5982 pixso.ky-tech.com.cn\uFF09\uFF0C\u683C\u5F0F\u5F62\u5982 https://<\u57DF\u540D>/app/design/<\u6587\u4EF6>?item-id=1%3A2\uFF08\u4E5F\u53EF\u80FD\u662F /app/editor/ \u8DEF\u5F84\uFF09\u3002';
|
|
693
|
+
var DSL_LEGEND = '\u5B57\u6BB5\u56FE\u4F8B\uFF1Aid=\u8282\u70B9 guid\uFF08\u53EF\u56DE\u4F20 guid \u4E0B\u94BB\uFF09\uFF1Bname=\u56FE\u5C42\u540D\uFF08\u81EA\u52A8\u547D\u540D\u5DF2\u7701\u7565\uFF09\uFF1Btype=\u8282\u70B9\u7C7B\u578B\uFF08ICON/IMAGE \u4E3A\u584C\u7F29\u53F6\u5B50\uFF09\uFF1Brect=[x,y,w,h] \u7EDD\u5BF9\u5B9A\u4F4D\uFF1Blayout={dir:row|column, gap, pad:[\u4E0A,\u53F3,\u4E0B,\u5DE6], align:[\u4E3B\u8F74,\u4EA4\u53C9\u8F74], sizing:[hug|fixed|fill]\xD72}\uFF1Bconstraints={h,v}\uFF08\u9ED8\u8BA4 left/top \u7701\u7565\uFF09\uFF1Btext={content, size, font, weight, lh, align, color, deco, ls, link, segs=\u6DF7\u6392\u5206\u6BB5\uFF08\u4EC5\u5DEE\u5F02\u5B57\u6BB5\uFF0C\u987A\u5E8F\u5373\u5B57\u7B26\u504F\u79FB\uFF09}\uFF1Bimg=\u8D44\u6E90\u6587\u4EF6\u540D\uFF1Bkids=\u5B50\u8282\u70B9\u9012\u5F52\uFF1Bpalette=\u8C03\u8272\u677F\uFF08\u4EC5\u6839\u8282\u70B9\uFF09\uFF1A\u22652 \u6B21\u7684\u5355\u8272\u63D0\u53D6\u4E3A\u6570\u7EC4\uFF0Cfills/text.color/stroke \u4E2D "p0""p1" \u4E3A\u4E0B\u6807\u5F15\u7528\uFF08\u590D\u5408 CSS \u4E32\u5185\u989C\u8272\u4E0D\u67E5\u8868\uFF09\uFF1Bnotes=\u8BBE\u8BA1\u6807\u6CE8\u6587\u672C\uFF08\u4EC5\u5B58\u5728\u65F6\uFF09\uFF1Bcomp=\u7EC4\u4EF6\u5173\u8054\uFF08INSTANCE/COMPONENT/COMPONENT_SET\uFF09\uFF1Akey=\u8DE8\u6587\u4EF6\u7A33\u5B9A\u6807\u8BC6\uFF0Cname=\u7EC4\u4EF6\u540D\uFF08\u6620\u5C04\u6743\u91CD\u6700\u9AD8\u7684\u4F9D\u636E\u2014\u2014\u5728\u9879\u76EE\u7EC4\u4EF6\u5E93\u627E\u5BF9\u5E94\uFF0C\u4E0D\u9010\u5B57\u76F4\u7528\uFF09\uFF0Cprops=variant \u5C5E\u6027\uFF0Cset=variant \u7EC4\uFF1Bdepth=\u6574\u6811\u5B9E\u9645\u6DF1\u5EA6\uFF08\u4EC5\u6839\u8282\u70B9\uFF0C\u603B\u662F\u8F93\u51FA\uFF09\uFF1Bmore=\u622A\u65AD\u5904\u5269\u4F59\u5C42\u7EA7\u6570\uFF08\u4EC5 maxDepth \u622A\u65AD\u65F6\uFF0C\u62FF id \u56DE\u4F20 guid \u4E0B\u94BB\uFF09\uFF1BkidsSummary=\u622A\u65AD\u5904\u6216\u584C\u7F29\u53F6\u5B50\uFF08img \u8282\u70B9\uFF09\u7684\u76F4\u63A5\u5B50\u8282\u70B9\u6D45\u9884\u89C8 [{id, name?, type}]\uFF08\u8F85\u52A9\u9009\u62E9\u4E0B\u94BB\u5206\u652F\uFF09\u3002\u7701\u7565\u89C4\u5219\uFF1A\u9ED8\u8BA4\u503C\u4E0D\u8F93\u51FA\uFF08text.align=left\u3001lh=normal\u3001weight=Regular\u3001gap=0\u3001pad \u5168 0\uFF09\u3002';
|
|
694
|
+
var getNodeDsl = {
|
|
695
|
+
description: '\u3010\u7528\u9014\u3011\u751F\u6210\u8282\u70B9\u7684\u5F00\u53D1\u5411\u7D27\u51D1 DSL\uFF08\u7ED3\u6784+\u6837\u5F0F+\u8D44\u6E90\u6587\u4EF6\u540D\uFF09\uFF0C\u8BBE\u8BA1\u8F6C\u4EE3\u7801\u7684\u6838\u5FC3\u5165\u53E3\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011"\u6837\u5F0F\u8FD8\u539F/\u8BBE\u8BA1\u8FD8\u539F/UI \u8FD8\u539F/\u8BBE\u8BA1\u8F6C\u4EE3\u7801/\u6839\u636E\u8BBE\u8BA1\u7A3F\u5199\u9875\u9762\u3001\u7EC4\u4EF6"\uFF0C\u6216\u7ED9\u51FA Pixso \u94FE\u63A5\uFF08\u4EFB\u610F\u57DF\u540D\uFF09\u65F6\uFF0C\u9ED8\u8BA4\u4ECE\u672C\u5DE5\u5177\u5F00\u59CB\u3002\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\uFF1A\u5148 get_metadata \u9AA8\u67B6 + get_screenshot \u89C6\u89C9\uFF0C\u5B9A\u4F4D\u76EE\u6807\u540E\u518D\u7528\u672C\u5DE5\u5177\u3002\u7ED3\u6784\u590D\u6742\uFF1AmaxDepth \u4F20 0 \u9884\u5224\u89C4\u6A21\uFF08\u6839\u8282\u70B9 depth=\u6574\u6811\u6DF1\u5EA6\uFF09\uFF0C\u518D\u5BF9\u5E26 more \u6807\u8BB0\u7684\u5B50\u6811\u6309 id \u4E0B\u94BB\uFF08kidsSummary \u8F85\u52A9\u9009\u5206\u652F\uFF09\u3002\u3010\u8FD4\u56DE\u3011\u5355\u884C\u7D27\u51D1 JSON\u3002' + DSL_LEGEND + "\u584C\u7F29\u53F6\u5B50\uFF1A\u7EAF\u77E2\u91CF\u7EC4\u4EF6\u5B9E\u4F8B\u2192SVG\u3001\u56FE\u7247\u586B\u5145\u2192PNG\u3001\u8BBE\u8BA1\u5E08\u6807\u8BB0\u6309\u5176\u8BBE\u7F6E\uFF1B\u5DF2\u5E26 img \u7684\u8282\u70B9\u5DF2\u5B8C\u6210\u5BFC\u51FA\uFF0C\u76F4\u63A5\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u52FF\u518D\u8C03 export_image\u3002DSL \u6709\u635F\u7CBE\u7B80\uFF08\u7701\u7565\u8499\u7248/\u5E03\u5C14\u8DEF\u5F84\u7B49\uFF09\uFF0C\u89C6\u89C9\u590D\u6742\u8282\u70B9\u914D\u5408 get_screenshot \u5BF9\u7167\u3002\u3010\u786C\u6027\u89C4\u5219\u3011" + URL_RULE + " " + GUIDES_PTR + " " + ENVELOPE_NOTE,
|
|
696
|
+
inputSchema: {
|
|
697
|
+
type: "object",
|
|
698
|
+
properties: {
|
|
699
|
+
guid: GUID_PROP,
|
|
700
|
+
maxDepth: {
|
|
701
|
+
type: "number",
|
|
702
|
+
description: "\u4E0B\u63A2\u5B50\u8282\u70B9\u7684\u6700\u5927\u6DF1\u5EA6\uFF08\u6839\u8282\u70B9\u4E3A\u7B2C 0 \u5C42\uFF09\uFF0C\u7F3A\u7701\u4E0D\u9650\u3002\u622A\u65AD\u8282\u70B9\u5E26 more\uFF08\u5269\u4F59\u5C42\u7EA7\u6570\uFF09+ kidsSummary\uFF08\u5B50\u8282\u70B9\u6D45\u9884\u89C8\uFF09\uFF0C\u62FF\u5176 id \u56DE\u4F20 guid \u7EE7\u7EED\u4E0B\u94BB\uFF1B\u4F20 0 \u53EA\u8FD4\u56DE\u6839\u8282\u70B9\uFF08\u914D\u5408 depth \u5B57\u6BB5\u9884\u5224\u89C4\u6A21\uFF09\u3002"
|
|
703
|
+
},
|
|
704
|
+
cache: CACHE_PROP,
|
|
705
|
+
target: TARGET_PROP
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// src/agent/tools/get_screenshot.ts
|
|
711
|
+
var getScreenshot = {
|
|
712
|
+
description: '\u3010\u7528\u9014\u3011\u5BFC\u51FA\u5355\u4E2A\u8282\u70B9\u7684\u622A\u56FE\uFF0C\u4F5C\u89C6\u89C9\u53C2\u7167\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011"\u770B\u770B\u8FD9\u4E2A\u8BBE\u8BA1\u7A3F\u957F\u4EC0\u4E48\u6837"/"\u7ED9\u6211\u622A\u56FE"\uFF1B\u8FD8\u539F\u4EFB\u52A1\u9700\u8981\u89C6\u89C9\u53C2\u7167\u6216\u8FD8\u539F\u5EA6\u6821\u9A8C\u3002\u8981\u5207\u56FE\u7D20\u6750\u7528 export_image\u3002\u751F\u6210\u4EE3\u7801\u540E\u53EF\u518D\u6B21\u8C03\u7528\u672C\u5DE5\u5177\u5BF9\u6BD4\u8FD8\u539F\u5EA6\uFF1B\u5EFA\u8BAE\u4E0E get_node_dsl \u914D\u5408\uFF1ADSL \u5B9A\u7ED3\u6784\uFF0C\u622A\u56FE\u6821\u9A8C\u89C6\u89C9\u3002\u3010\u8FD4\u56DE\u3011{img: \u4EA7\u7269\u6587\u4EF6\u540D, width, height, format}\u3002\u3010\u786C\u6027\u89C4\u5219\u3011' + GUIDES_PTR + " " + ENVELOPE_NOTE,
|
|
713
|
+
inputSchema: {
|
|
714
|
+
type: "object",
|
|
715
|
+
properties: {
|
|
716
|
+
guid: GUID_PROP,
|
|
717
|
+
format: {
|
|
718
|
+
type: "string",
|
|
719
|
+
enum: ["PNG", "SVG"],
|
|
720
|
+
description: "\u5BFC\u51FA\u683C\u5F0F\uFF0C\u9ED8\u8BA4 PNG\u3002\u622A\u56FE\u7528\u9014\u7528 PNG\uFF1B\u7EAF\u56FE\u5F62\u60F3\u62FF\u77E2\u91CF\u518D\u9009 SVG\u3002"
|
|
721
|
+
},
|
|
722
|
+
scale: {
|
|
723
|
+
type: "number",
|
|
724
|
+
description: "\u5BFC\u51FA\u500D\u7387\uFF0C\u9ED8\u8BA4 2\u3002"
|
|
725
|
+
},
|
|
726
|
+
cache: CACHE_PROP,
|
|
727
|
+
target: TARGET_PROP
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// src/agent/tools/get_selected.ts
|
|
733
|
+
var getSelected = {
|
|
734
|
+
description: '\u3010\u7528\u9014\u3011\u5B9E\u65F6\u8BFB\u53D6 Pixso \u753B\u5E03\u5F53\u524D\u9009\u4E2D\u7684\u56FE\u5C42\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\uFF09\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u7528\u6237\u8BF4"\u8FD9\u4E2A/\u5F53\u524D\u9009\u4E2D\u7684"\u56FE\u5C42\u3001\u7EC4\u4EF6\u3002\u5DF2\u7ED9 Pixso \u94FE\u63A5\u65F6\u4E0D\u8981\u7528\u672C\u5DE5\u5177\u2014\u2014\u76F4\u63A5\u4ECE\u94FE\u63A5\u63D0\u53D6 guid\uFF08\u89C1 get_node_dsl\uFF09\u3002\u3010\u8FD4\u56DE\u3011{fileKey, fileName, pageName, selection: [{id, name, type}]}\u3002\u3010\u786C\u6027\u89C4\u5219\u3011\u4E25\u7981\u9690\u5F0F\u4F7F\u7528\u8FD4\u56DE\u7684 guid\uFF1A\u9664\u975E\u7528\u6237\u660E\u786E\u8BF4\u4E86"\u6211\u9009\u4E2D\u7684/\u5F53\u524D\u9009\u4E2D\u7684"\uFF0C\u5FC5\u987B\u5148\u628A\u9009\u4E2D\u56FE\u5C42\u540D\u79F0\u62A5\u7ED9\u7528\u6237\u786E\u8BA4\uFF08"\u5F53\u524D\u9009\u4E2D\u7684\u662F\u300CXX\u300D\uFF0C\u786E\u8BA4\u5904\u7406\u5B83\u5417\uFF1F"\uFF09\uFF0C\u786E\u8BA4\u540E\u624D\u80FD\u4F20\u7ED9\u5176\u4ED6\u5DE5\u5177\u3002 ' + ENVELOPE_NOTE,
|
|
735
|
+
inputSchema: { type: "object", properties: { target: TARGET_PROP } }
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
// src/agent/tools/list_sessions.ts
|
|
739
|
+
var listSessions = {
|
|
740
|
+
description: "\u3010\u7528\u9014\u3011\u5217\u51FA\u5B58\u6D3B\u7684\u8BBE\u8BA1\u7A3F\u63D2\u4EF6\u4F1A\u8BDD\uFF08sessionId\u3001\u6587\u4EF6\u3001\u9875\u9762\u3001\u9009\u4E2D\u6570\uFF09\u2014\u2014\u6BCF\u6B21\u8C03\u7528\u7ECF ping/pong \u5B9E\u6D4B\uFF0C\u65E0\u54CD\u5E94\u7684\u4E0D\u51FA\u73B0\u3002\u6BCF\u4E2A\u8FD0\u884C\u63D2\u4EF6\u7684\u8BBE\u8BA1\u6587\u4EF6\u5BF9\u5E94\u4E00\u4E2A\u4F1A\u8BDD\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u591A\u4E2A Pixso \u6587\u4EF6\u540C\u65F6\u5F00\u63D2\u4EF6\u65F6\uFF0C\u5148\u7528\u672C\u5DE5\u5177\u786E\u8BA4\u76EE\u6807\u6587\u4EF6\uFF1B\u53EA\u6709\u4E00\u4E2A\u4F1A\u8BDD\u65F6\u65E0\u9700\u5173\u6CE8\u672C\u5DE5\u5177\u3002\u3010\u8FD4\u56DE\u3011\u4F1A\u8BDD\u5217\u8868 JSON\u3002\u628A sessionId \u6216 fileKey \u4F5C\u4E3A\u5176\u4ED6\u5DE5\u5177\u7684 target \u53C2\u6570\u4F20\u5165\u5373\u8DEF\u7531\u5230\u5BF9\u5E94\u6587\u4EF6\uFF1B\u4E0D\u4F20 target \u65F6\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u7684\u6587\u4EF6\u3002\u8BE6\u89C1\u8D44\u6E90 batter://guides/intro\u3002",
|
|
741
|
+
inputSchema: { type: "object", properties: {} },
|
|
742
|
+
handler: async () => {
|
|
743
|
+
const list = sessions.list();
|
|
744
|
+
if (!list.length) {
|
|
745
|
+
return {
|
|
746
|
+
content: [{ type: "text", text: "\u5F53\u524D\u6CA1\u6709\u63D2\u4EF6\u8FDE\u63A5\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6\u3002" }]
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const checks = await Promise.all(
|
|
750
|
+
list.map(async (info) => info.online && await pingSession(info.sessionId) ? info : null)
|
|
751
|
+
);
|
|
752
|
+
const alive = checks.filter((c) => c !== null);
|
|
753
|
+
for (const info of list) {
|
|
754
|
+
if (!alive.includes(info)) sessions.evict(info.sessionId);
|
|
755
|
+
}
|
|
756
|
+
if (!alive.length) {
|
|
757
|
+
return {
|
|
758
|
+
content: [{ type: "text", text: "\u5F53\u524D\u6CA1\u6709\u5B58\u6D3B\u7684\u63D2\u4EF6\u8FDE\u63A5\uFF08\u5DF2\u6E05\u7406\u65E0\u54CD\u5E94\u4F1A\u8BDD\uFF09\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6\u3002" }]
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
return jsonResult(
|
|
762
|
+
alive.map(({ sessionId, fileKey, fileName, pageName, selectionCount }) => ({
|
|
763
|
+
sessionId,
|
|
764
|
+
fileKey,
|
|
765
|
+
fileName,
|
|
766
|
+
pageName,
|
|
767
|
+
selectionCount
|
|
768
|
+
}))
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
// src/agent/tools/index.ts
|
|
774
|
+
var TOOL_REGISTRY = {
|
|
775
|
+
get_selected: getSelected,
|
|
776
|
+
get_node_dsl: getNodeDsl,
|
|
777
|
+
get_metadata: getMetadata,
|
|
778
|
+
export_image: exportImage,
|
|
779
|
+
get_screenshot: getScreenshot,
|
|
780
|
+
list_sessions: listSessions
|
|
781
|
+
};
|
|
782
|
+
var TOOLS = Object.fromEntries(
|
|
783
|
+
Object.entries(TOOL_REGISTRY).map(([name, def]) => {
|
|
784
|
+
const handler = def.handler ?? ((args, ctx) => run(name, args, ctx?.signal));
|
|
785
|
+
const resolvedDef = { description: def.description, inputSchema: def.inputSchema, handler };
|
|
786
|
+
return [name, { def: resolvedDef, tool: { name, description: def.description, inputSchema: def.inputSchema } }];
|
|
787
|
+
})
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
// src/agent/mcp.ts
|
|
791
|
+
var INSTRUCTIONS = `Pixso \u8BBE\u8BA1\u7A3F\u8F6C\u4EE3\u7801\u670D\u52A1\u3002\u6309\u7528\u6237\u8F93\u5165\u8DEF\u7531\uFF1A1. \u7ED9\u51FA Pixso \u94FE\u63A5\uFF08\u4EFB\u610F\u57DF\u540D\uFF09\u2192 \u4ECE item-id \u63D0\u53D6 guid\uFF08URL \u89E3\u7801\uFF09\uFF0C\u76F4\u63A5\u8C03 get_node_dsl / get_screenshot / export_image\uFF1B2. \u8BF4"\u6211\u9009\u4E2D\u7684/\u5F53\u524D\u9009\u4E2D\u7684"\u2192 get_selected \u62FF guid\uFF1B\u5176\u4ED6\u4F9D\u8D56\u9009\u533A\u7684\u573A\u666F\u5148 get_selected \u518D\u5411\u7528\u6237\u62A5\u540D\u79F0\u786E\u8BA4\uFF1B3. "\u6837\u5F0F\u8FD8\u539F/\u8BBE\u8BA1\u8FD8\u539F/UI \u8FD8\u539F/\u8BBE\u8BA1\u8F6C\u4EE3\u7801"\u2192 get_node_dsl \u53D6\u7ED3\u6784 + get_screenshot \u53D6\u89C6\u89C9\u53C2\u7167\uFF1B4. "\u5207\u56FE/\u5BFC\u51FA\u56FE\u6807\u6216\u56FE\u7247\u7D20\u6750"\u2192 export_image\uFF08DSL \u91CC\u5DF2\u5E26 img \u7684\u8282\u70B9\u76F4\u63A5\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E0D\u8981\u91CD\u590D\u5BFC\u51FA\uFF09\uFF1B5. \u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F/\u53EA\u8981\u7ED3\u6784\u6982\u89C8 \u2192 get_metadata \u62FF\u5168\u6811\u9AA8\u67B6\uFF08\u65E0\u6837\u5F0F\u65E0\u8D44\u6E90\uFF0C\u4F53\u79EF\u6781\u5C0F\uFF09\u5B9A\u4F4D\u76EE\u6807\u8282\u70B9\uFF1B\u7ED3\u6784\u590D\u6742 \u2192 get_node_dsl \u7528 maxDepth \u5206\u6BB5\u4E0B\u63A2\uFF08\u622A\u65AD\u5904 more \u6807\u8BB0 + kidsSummary \u6D45\u9884\u89C8\uFF0C\u6309 id \u4E0B\u94BB\uFF09\uFF1B6. \u751F\u6210\u540E\u5BF9\u6BD4\u8FD8\u539F\u5EA6 \u2192 get_screenshot \u6821\u9A8C\u5FAA\u73AF\uFF1B7. \u591A\u4E2A Pixso \u6587\u4EF6\u540C\u65F6\u6253\u5F00 \u2192 list_sessions + target \u53C2\u6570\u8DEF\u7531\u3002\u786C\u6027\u89C4\u5219\uFF08\u8D44\u6E90\u53D6\u7528/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D/\u7981\u6B62\u622A\u56FE\u786C\u7F16\u7801\uFF09\u89C1\u8D44\u6E90 batter://guides/design-to-code\u2014\u2014\u5199\u4EE3\u7801\u524D\u5FC5\u8BFB\u3002`;
|
|
792
|
+
function createMcpServer() {
|
|
793
|
+
const server = new McpServer(
|
|
794
|
+
{ name: HUB_NAME, version: VERSION },
|
|
795
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} }, instructions: INSTRUCTIONS }
|
|
796
|
+
);
|
|
797
|
+
for (const guide of GUIDES) {
|
|
798
|
+
server.registerResource(
|
|
799
|
+
guide.name,
|
|
800
|
+
guide.uri,
|
|
801
|
+
{
|
|
802
|
+
description: guide.description,
|
|
803
|
+
mimeType: "text/markdown",
|
|
804
|
+
// 纯 agent 消费的工作流文档,高优先级提示读取
|
|
805
|
+
annotations: { audience: ["assistant"], priority: 0.9 }
|
|
806
|
+
},
|
|
807
|
+
async (uri) => ({
|
|
808
|
+
contents: [{ uri: uri.href, mimeType: "text/markdown", text: guide.text }]
|
|
809
|
+
})
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
server.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
813
|
+
resourceTemplates: []
|
|
814
|
+
}));
|
|
815
|
+
server.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
816
|
+
tools: Object.values(TOOLS).map((t) => t.tool)
|
|
817
|
+
}));
|
|
818
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
819
|
+
const { name, arguments: args = {} } = request.params;
|
|
820
|
+
const entry = TOOLS[name];
|
|
821
|
+
if (!entry) {
|
|
822
|
+
throw new Error(`unknown tool: ${name}`);
|
|
823
|
+
}
|
|
824
|
+
return entry.def.handler(args, { signal: extra.signal });
|
|
825
|
+
});
|
|
826
|
+
server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
827
|
+
prompts: PROMPTS.map((p) => ({
|
|
828
|
+
name: p.name,
|
|
829
|
+
description: p.description,
|
|
830
|
+
arguments: p.arguments
|
|
831
|
+
}))
|
|
832
|
+
}));
|
|
833
|
+
server.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
834
|
+
const entry = PROMPTS.find((p) => p.name === request.params.name);
|
|
835
|
+
if (!entry) {
|
|
836
|
+
throw new Error(`unknown prompt: ${request.params.name}`);
|
|
837
|
+
}
|
|
838
|
+
const args = request.params.arguments ?? {};
|
|
839
|
+
return { description: entry.description, messages: entry.render(args) };
|
|
840
|
+
});
|
|
841
|
+
return server;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// src/agent/sessions.ts
|
|
845
|
+
var transports = /* @__PURE__ */ new Map();
|
|
846
|
+
function getMcpTransport(sessionId) {
|
|
847
|
+
return transports.get(sessionId);
|
|
848
|
+
}
|
|
849
|
+
function registerMcpTransport(sessionId, transport) {
|
|
850
|
+
transports.set(sessionId, transport);
|
|
851
|
+
log(`MCP HTTP session initialized: ${sessionId.slice(0, 8)} (total ${transports.size})`);
|
|
852
|
+
transport.onclose = () => {
|
|
853
|
+
if (transport.sessionId) transports.delete(transport.sessionId);
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/agent/http.ts
|
|
858
|
+
function handleMcp(req, res, url) {
|
|
859
|
+
return dispatchRoute(MCP_ROUTES, req, res, url);
|
|
860
|
+
}
|
|
861
|
+
async function sessionRequestHandler({ req, res }) {
|
|
862
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
863
|
+
const transport = sessionId ? getMcpTransport(sessionId) : void 0;
|
|
864
|
+
if (!transport) {
|
|
865
|
+
sendJson(res, 400, { error: "missing or unknown MCP session" });
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
await transport.handleRequest(req, res);
|
|
869
|
+
}
|
|
870
|
+
var MCP_ROUTES = [
|
|
871
|
+
{
|
|
872
|
+
method: "POST",
|
|
873
|
+
path: "/mcp",
|
|
874
|
+
handler: async ({ req, res }) => {
|
|
875
|
+
let body;
|
|
876
|
+
try {
|
|
877
|
+
body = await readJsonBody(req);
|
|
878
|
+
} catch {
|
|
879
|
+
sendJson(res, 400, { jsonrpc: "2.0", error: { code: -32700, message: "Parse error" }, id: null });
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
883
|
+
let transport = sessionId ? getMcpTransport(sessionId) : void 0;
|
|
884
|
+
if (!transport) {
|
|
885
|
+
if (!sessionId && isInitializeRequest(body)) {
|
|
886
|
+
const t = new StreamableHTTPServerTransport({
|
|
887
|
+
sessionIdGenerator: () => randomUUID2(),
|
|
888
|
+
onsessioninitialized: (id) => registerMcpTransport(id, t)
|
|
889
|
+
});
|
|
890
|
+
await createMcpServer().connect(t);
|
|
891
|
+
transport = t;
|
|
892
|
+
} else {
|
|
893
|
+
sendJson(res, 400, {
|
|
894
|
+
jsonrpc: "2.0",
|
|
895
|
+
error: { code: -32e3, message: "Bad Request: missing or unknown MCP session" },
|
|
896
|
+
id: null
|
|
897
|
+
});
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
await transport.handleRequest(req, res, body);
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
{ method: "GET", path: "/mcp", handler: sessionRequestHandler },
|
|
905
|
+
{ method: "DELETE", path: "/mcp", handler: sessionRequestHandler }
|
|
906
|
+
];
|
|
907
|
+
|
|
908
|
+
// src/install/dist-resolver.ts
|
|
909
|
+
import { existsSync } from "node:fs";
|
|
910
|
+
import { readFile } from "node:fs/promises";
|
|
911
|
+
import { join as join3 } from "node:path";
|
|
912
|
+
import { fileURLToPath } from "node:url";
|
|
913
|
+
function resolvePluginDir() {
|
|
914
|
+
const candidates = [
|
|
915
|
+
fileURLToPath(new URL("./plugin/", import.meta.url)),
|
|
916
|
+
// bundle: dist/index.js → dist/plugin/
|
|
917
|
+
fileURLToPath(new URL("../../plugin/dist/", import.meta.url)),
|
|
918
|
+
// bundle 开发: dist/index.js → packages/plugin/dist/
|
|
919
|
+
fileURLToPath(new URL("../../../plugin/dist/", import.meta.url))
|
|
920
|
+
// tsx 开发: src/install/ → packages/plugin/dist/
|
|
921
|
+
];
|
|
922
|
+
return candidates.find((p) => existsSync(join3(p, "manifest.json"))) ?? null;
|
|
923
|
+
}
|
|
924
|
+
var resolved = null;
|
|
925
|
+
function pluginDir() {
|
|
926
|
+
resolved ??= resolvePluginDir();
|
|
927
|
+
return resolved;
|
|
928
|
+
}
|
|
929
|
+
function hasPluginDist() {
|
|
930
|
+
return pluginDir() !== null;
|
|
931
|
+
}
|
|
932
|
+
async function pluginManifest() {
|
|
933
|
+
const dir = pluginDir();
|
|
934
|
+
if (!dir) return null;
|
|
935
|
+
const raw = JSON.parse(await readFile(join3(dir, "manifest.json"), "utf8"));
|
|
936
|
+
raw.networkAccess = {
|
|
937
|
+
allowedDomains: [`http://localhost:${config.httpPort}`, `http://127.0.0.1:${config.httpPort}`]
|
|
938
|
+
};
|
|
939
|
+
return Buffer.from(JSON.stringify(raw, null, 2));
|
|
940
|
+
}
|
|
941
|
+
async function readPluginFile(name) {
|
|
942
|
+
const dir = pluginDir();
|
|
943
|
+
if (!dir) return null;
|
|
944
|
+
try {
|
|
945
|
+
return await readFile(join3(dir, name));
|
|
946
|
+
} catch {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/install/template.ts
|
|
952
|
+
var HTML_ESCAPES = {
|
|
953
|
+
"&": "&",
|
|
954
|
+
"<": "<",
|
|
955
|
+
">": ">",
|
|
956
|
+
'"': """,
|
|
957
|
+
"'": "'"
|
|
958
|
+
};
|
|
959
|
+
function escapeHtml(s) {
|
|
960
|
+
return s.replace(/[&<>"']/g, (c) => HTML_ESCAPES[c]);
|
|
961
|
+
}
|
|
962
|
+
function escapeJsString(s) {
|
|
963
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
964
|
+
}
|
|
965
|
+
function renderTemplate(tpl, vars, escape = escapeHtml) {
|
|
966
|
+
const lookup = (key) => {
|
|
967
|
+
const v = vars[key];
|
|
968
|
+
if (v === void 0) throw new Error(`\u6A21\u677F\u53D8\u91CF\u7F3A\u5931\uFF1A{{${key}}}`);
|
|
969
|
+
return String(v);
|
|
970
|
+
};
|
|
971
|
+
return tpl.replace(/\{\{\{\s*(\w+)\s*\}\}\}/g, (_, key) => lookup(key)).replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => escape(lookup(key)));
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// src/install/templates/bookmarklet.txt
|
|
975
|
+
var bookmarklet_default = '// Batter Bridge \u4E66\u7B7E\u6CE8\u5165\u811A\u672C\n// \u5728 Pixso / MasterGo \u7F51\u9875\u7248\u8BBE\u8BA1\u6587\u4EF6\u9875\u9762\u70B9\u51FB\u540E\u6267\u884C\uFF1A\n// \u4ECE\u672C\u670D\u52A1\u62C9\u53D6 manifest + code\uFF0C\u901A\u8FC7\u5BBF\u4E3B\u6CE8\u5165 API \u8FD0\u884C\u63D2\u4EF6\u3002\n// \u670D\u52A1\u4E0D\u53EF\u8FBE\u65F6\u56DE\u9000 localStorage \u7F13\u5B58\uFF08\u4E0A\u6B21\u6210\u529F\u62C9\u53D6\u7684\u63D2\u4EF6\u5305\uFF09\u3002\n// \u6E32\u67D3\u65F6 {{ base }} \u66FF\u6362\u4E3A\u670D\u52A1\u5730\u5740\uFF08JS \u5B57\u7B26\u4E32\u8F6C\u4E49\uFF09\uFF0C\u6574\u4F53 encodeURIComponent \u540E\u4F5C\u4E3A\u4E66\u7B7E href\u3002\n(function () {\n var SERVER_BASE = "{{ base }}";\n var CACHE_KEY = "batter-bridge-cache";\n\n /** \u7528\u5BBF\u4E3B\u9875\u9762\u7684\u6CE8\u5165 API \u542F\u52A8\u63D2\u4EF6 */\n function startPlugin(codeText, manifestText) {\n try {\n var manifest = JSON.parse(manifestText);\n if (window._FicAppObj && window._FicAppObj.runTestPlugin) {\n // MasterGo \u5BBF\u4E3B\n window._FicAppObj.runTestPlugin(manifest, codeText, "", null, { startup: "DEBUG" });\n return;\n }\n if (window.runNewPlugin) {\n // Pixso \u5BBF\u4E3B\n window.runNewPlugin(codeText, null, null, { manifest: manifest, plugin: manifest });\n return;\n }\n alert("\u8BF7\u5728 Pixso / MasterGo \u8BBE\u8BA1\u6587\u4EF6\u9875\u9762\u70B9\u51FB\u6B64\u4E66\u7B7E");\n } catch (err) {\n alert("\u63D2\u4EF6\u542F\u52A8\u5931\u8D25\uFF1A" + ((err && err.message) || err));\n }\n }\n\n /** \u5C1D\u8BD5\u7528\u7F13\u5B58\u7684\u63D2\u4EF6\u5305\u542F\u52A8\uFF1B\u65E0\u7F13\u5B58\u8FD4\u56DE false */\n function startFromCache() {\n try {\n var cached = JSON.parse(localStorage.getItem(CACHE_KEY) || "null");\n if (cached && cached.manifest && cached.code) {\n startPlugin(cached.code, cached.manifest);\n return true;\n }\n } catch (err) {\n // \u7F13\u5B58\u635F\u574F\uFF1A\u89C6\u4E3A\u65E0\u7F13\u5B58\n }\n return false;\n }\n\n function saveCache(manifestText, codeText) {\n try {\n localStorage.setItem(CACHE_KEY, JSON.stringify({ manifest: manifestText, code: codeText }));\n } catch (err) {\n // \u5B58\u50A8\u8D85\u9650\u7B49\uFF1A\u7F13\u5B58\u53EA\u662F\u79BB\u7EBF\u515C\u5E95\uFF0C\u5931\u8D25\u53EF\u5FFD\u7565\n }\n }\n\n function fetchText(path) {\n return fetch(SERVER_BASE + path + "?t=" + Date.now(), { cache: "no-store" }).then(\n function (res) {\n if (!res.ok) throw new Error("HTTP " + res.status);\n return res.text();\n },\n );\n }\n\n Promise.all([fetchText("manifest.json"), fetchText("code.js")])\n .then(function (texts) {\n var manifestText = texts[0];\n var codeText = texts[1];\n saveCache(manifestText, codeText);\n startPlugin(codeText, manifestText);\n })\n .catch(function () {\n if (!startFromCache()) {\n alert("\u65E0\u6CD5\u8FDE\u63A5 batter-pixso-mcp \u670D\u52A1\uFF08" + SERVER_BASE + "\uFF09\uFF0C\u8BF7\u786E\u8BA4\u670D\u52A1\u5DF2\u542F\u52A8");\n }\n });\n})();\n';
|
|
976
|
+
|
|
977
|
+
// src/install/templates/install-page.html
|
|
978
|
+
var install_page_default = `<!DOCTYPE html>
|
|
979
|
+
<html lang="zh-CN">
|
|
980
|
+
<head>
|
|
981
|
+
<meta charset="utf-8">
|
|
982
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
983
|
+
<title>Batter Pixso Bridge \xB7 \u5B89\u88C5</title>
|
|
984
|
+
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect width='24' height='24' rx='6' fill='%23575BF2'/%3E%3Cpath d='M13 4 7.5 13.5H11L10 20l6.5-9.5H12.5Z' fill='%23fff'/%3E%3C/svg%3E">
|
|
985
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
986
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
987
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
988
|
+
<style>
|
|
989
|
+
:root {
|
|
990
|
+
--ink: #1e1e1e;
|
|
991
|
+
--sub: #6b6b6b;
|
|
992
|
+
--faint: #a3a3a3;
|
|
993
|
+
--line: #e8e8ea;
|
|
994
|
+
--panel: #f7f7f8;
|
|
995
|
+
--iris: #575bf2;
|
|
996
|
+
--iris-strong: #4346e0;
|
|
997
|
+
--iris-soft: #eef0fe;
|
|
998
|
+
--ok: #14ae5c;
|
|
999
|
+
--err: #f24822;
|
|
1000
|
+
--radius: 6px;
|
|
1001
|
+
--mono: ui-monospace, "SF Mono", Menlo, Consolas, "Cascadia Mono", monospace;
|
|
1002
|
+
}
|
|
1003
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1004
|
+
html { -webkit-text-size-adjust: 100%; }
|
|
1005
|
+
body {
|
|
1006
|
+
background: #fff;
|
|
1007
|
+
color: var(--ink);
|
|
1008
|
+
font-family: Inter, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
|
1009
|
+
font-feature-settings: "tnum" 1, "cv05" 1;
|
|
1010
|
+
line-height: 1.5;
|
|
1011
|
+
min-height: 100vh;
|
|
1012
|
+
}
|
|
1013
|
+
::selection { background: var(--iris-soft); }
|
|
1014
|
+
.page {
|
|
1015
|
+
max-width: 680px;
|
|
1016
|
+
margin: 0 auto;
|
|
1017
|
+
padding: clamp(48px, 10vh, 96px) 24px 64px;
|
|
1018
|
+
animation: enter 420ms cubic-bezier(0.2, 0.6, 0.2, 1) both;
|
|
1019
|
+
}
|
|
1020
|
+
@keyframes enter {
|
|
1021
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
1022
|
+
to { opacity: 1; transform: none; }
|
|
1023
|
+
}
|
|
1024
|
+
a { color: var(--iris); text-decoration: none; }
|
|
1025
|
+
a:focus-visible, button:focus-visible {
|
|
1026
|
+
outline: 2px solid var(--iris);
|
|
1027
|
+
outline-offset: 2px;
|
|
1028
|
+
border-radius: 2px;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/* \u2500\u2500 \u9876\u680F\uFF1A\u54C1\u724C + \u670D\u52A1\u72B6\u6001 \u2500\u2500 */
|
|
1032
|
+
.top {
|
|
1033
|
+
display: flex;
|
|
1034
|
+
align-items: center;
|
|
1035
|
+
justify-content: space-between;
|
|
1036
|
+
gap: 16px;
|
|
1037
|
+
margin-bottom: clamp(40px, 7vh, 72px);
|
|
1038
|
+
}
|
|
1039
|
+
.brand {
|
|
1040
|
+
display: flex;
|
|
1041
|
+
align-items: center;
|
|
1042
|
+
gap: 10px;
|
|
1043
|
+
font-size: 13px;
|
|
1044
|
+
font-weight: 600;
|
|
1045
|
+
letter-spacing: -0.01em;
|
|
1046
|
+
}
|
|
1047
|
+
.brand svg { display: block; }
|
|
1048
|
+
.brand .ver {
|
|
1049
|
+
font-family: var(--mono);
|
|
1050
|
+
font-weight: 400;
|
|
1051
|
+
font-size: 11px;
|
|
1052
|
+
color: var(--faint);
|
|
1053
|
+
margin-left: 2px;
|
|
1054
|
+
}
|
|
1055
|
+
.status {
|
|
1056
|
+
display: inline-flex;
|
|
1057
|
+
align-items: center;
|
|
1058
|
+
gap: 7px;
|
|
1059
|
+
padding: 5px 10px;
|
|
1060
|
+
border-radius: 999px;
|
|
1061
|
+
background: var(--panel);
|
|
1062
|
+
border: 1px solid var(--line);
|
|
1063
|
+
font-size: 11px;
|
|
1064
|
+
color: var(--sub);
|
|
1065
|
+
white-space: nowrap;
|
|
1066
|
+
}
|
|
1067
|
+
.status .dot {
|
|
1068
|
+
width: 6px;
|
|
1069
|
+
height: 6px;
|
|
1070
|
+
border-radius: 50%;
|
|
1071
|
+
background: var(--faint);
|
|
1072
|
+
flex-shrink: 0;
|
|
1073
|
+
animation: pulse 2.2s ease-in-out infinite;
|
|
1074
|
+
}
|
|
1075
|
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
|
|
1076
|
+
.status.ok { color: #0d7a3e; }
|
|
1077
|
+
.status.ok .dot { background: var(--ok); }
|
|
1078
|
+
.status.err { color: var(--err); }
|
|
1079
|
+
.status.err .dot { background: var(--err); animation: none; }
|
|
1080
|
+
|
|
1081
|
+
/* \u2500\u2500 \u4E3B\u6807\u9898\u533A \u2500\u2500 */
|
|
1082
|
+
h1 {
|
|
1083
|
+
font-size: clamp(28px, 5vw, 38px);
|
|
1084
|
+
font-weight: 650;
|
|
1085
|
+
letter-spacing: -0.025em;
|
|
1086
|
+
line-height: 1.2;
|
|
1087
|
+
margin-bottom: 12px;
|
|
1088
|
+
}
|
|
1089
|
+
.sub {
|
|
1090
|
+
font-size: 14px;
|
|
1091
|
+
color: var(--sub);
|
|
1092
|
+
max-width: 46em;
|
|
1093
|
+
line-height: 1.7;
|
|
1094
|
+
margin-bottom: clamp(40px, 7vh, 64px);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/* \u2500\u2500 \u6B65\u9AA4\u5217\u8868\uFF1A\u53D1\u4E1D\u7EBF\u5206\u9694 \u2500\u2500 */
|
|
1098
|
+
.steps { border-top: 1px solid var(--line); }
|
|
1099
|
+
.step {
|
|
1100
|
+
display: grid;
|
|
1101
|
+
grid-template-columns: 44px 1fr;
|
|
1102
|
+
gap: 4px 16px;
|
|
1103
|
+
padding: 26px 0 28px;
|
|
1104
|
+
border-bottom: 1px solid var(--line);
|
|
1105
|
+
}
|
|
1106
|
+
.num {
|
|
1107
|
+
grid-row: 1 / span 2;
|
|
1108
|
+
font-family: var(--mono);
|
|
1109
|
+
font-size: 12px;
|
|
1110
|
+
color: var(--faint);
|
|
1111
|
+
padding-top: 3px;
|
|
1112
|
+
font-variant-numeric: tabular-nums;
|
|
1113
|
+
}
|
|
1114
|
+
.step-head {
|
|
1115
|
+
display: flex;
|
|
1116
|
+
align-items: center;
|
|
1117
|
+
gap: 10px;
|
|
1118
|
+
flex-wrap: wrap;
|
|
1119
|
+
}
|
|
1120
|
+
.step-title {
|
|
1121
|
+
font-size: 14px;
|
|
1122
|
+
font-weight: 600;
|
|
1123
|
+
letter-spacing: -0.01em;
|
|
1124
|
+
}
|
|
1125
|
+
.tag {
|
|
1126
|
+
font-size: 11px;
|
|
1127
|
+
font-weight: 500;
|
|
1128
|
+
color: var(--iris);
|
|
1129
|
+
background: var(--iris-soft);
|
|
1130
|
+
padding: 2px 8px;
|
|
1131
|
+
border-radius: 999px;
|
|
1132
|
+
}
|
|
1133
|
+
.step-body { grid-column: 2; }
|
|
1134
|
+
.note {
|
|
1135
|
+
font-size: 12.5px;
|
|
1136
|
+
color: var(--sub);
|
|
1137
|
+
line-height: 1.75;
|
|
1138
|
+
max-width: 60em;
|
|
1139
|
+
}
|
|
1140
|
+
.note strong { color: var(--ink); font-weight: 600; }
|
|
1141
|
+
code {
|
|
1142
|
+
font-family: var(--mono);
|
|
1143
|
+
background: var(--panel);
|
|
1144
|
+
border: 1px solid var(--line);
|
|
1145
|
+
color: var(--ink);
|
|
1146
|
+
padding: 0.5px 5px;
|
|
1147
|
+
border-radius: 4px;
|
|
1148
|
+
font-size: 11px;
|
|
1149
|
+
}
|
|
1150
|
+
.actions { display: flex; align-items: center; gap: 12px; margin: 14px 0 10px; }
|
|
1151
|
+
|
|
1152
|
+
/* \u4E3B\u6309\u94AE\uFF1A\u53EF\u62D6\u62FD\u7684\u4E66\u7B7E */
|
|
1153
|
+
.bm {
|
|
1154
|
+
display: inline-flex;
|
|
1155
|
+
align-items: center;
|
|
1156
|
+
gap: 8px;
|
|
1157
|
+
padding: 9px 18px;
|
|
1158
|
+
background: var(--iris);
|
|
1159
|
+
color: #fff;
|
|
1160
|
+
border-radius: var(--radius);
|
|
1161
|
+
font-size: 13px;
|
|
1162
|
+
font-weight: 600;
|
|
1163
|
+
letter-spacing: -0.01em;
|
|
1164
|
+
cursor: grab;
|
|
1165
|
+
box-shadow: 0 1px 2px rgba(30, 30, 30, 0.08);
|
|
1166
|
+
transition: background 140ms ease, transform 140ms ease;
|
|
1167
|
+
user-select: none;
|
|
1168
|
+
}
|
|
1169
|
+
.bm:hover { background: var(--iris-strong); }
|
|
1170
|
+
.bm:active { cursor: grabbing; transform: scale(0.985); }
|
|
1171
|
+
.drag-hint {
|
|
1172
|
+
font-size: 12px;
|
|
1173
|
+
color: var(--faint);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/* \u6B21\u7EA7\u6309\u94AE */
|
|
1177
|
+
.btn {
|
|
1178
|
+
display: inline-flex;
|
|
1179
|
+
align-items: center;
|
|
1180
|
+
gap: 7px;
|
|
1181
|
+
padding: 8px 14px;
|
|
1182
|
+
border: 1px solid var(--line);
|
|
1183
|
+
border-radius: var(--radius);
|
|
1184
|
+
background: #fff;
|
|
1185
|
+
color: var(--ink);
|
|
1186
|
+
font-size: 13px;
|
|
1187
|
+
font-weight: 500;
|
|
1188
|
+
letter-spacing: -0.01em;
|
|
1189
|
+
text-decoration: none;
|
|
1190
|
+
transition: border-color 140ms ease, background 140ms ease;
|
|
1191
|
+
}
|
|
1192
|
+
.btn:hover { border-color: #d2d2d6; background: var(--panel); }
|
|
1193
|
+
.btn svg { display: block; color: var(--sub); }
|
|
1194
|
+
|
|
1195
|
+
/* MCP \u914D\u7F6E\u4EE3\u7801\u5757 */
|
|
1196
|
+
.cfg {
|
|
1197
|
+
position: relative;
|
|
1198
|
+
margin: 14px 0 10px;
|
|
1199
|
+
}
|
|
1200
|
+
pre {
|
|
1201
|
+
background: var(--panel);
|
|
1202
|
+
border: 1px solid var(--line);
|
|
1203
|
+
border-radius: 8px;
|
|
1204
|
+
padding: 14px 16px;
|
|
1205
|
+
font-family: var(--mono);
|
|
1206
|
+
font-size: 12px;
|
|
1207
|
+
line-height: 1.7;
|
|
1208
|
+
color: #3c3c3c;
|
|
1209
|
+
overflow-x: auto;
|
|
1210
|
+
}
|
|
1211
|
+
.copy {
|
|
1212
|
+
position: absolute;
|
|
1213
|
+
top: 8px;
|
|
1214
|
+
right: 8px;
|
|
1215
|
+
padding: 4px 10px;
|
|
1216
|
+
font-family: inherit;
|
|
1217
|
+
font-size: 11px;
|
|
1218
|
+
font-weight: 500;
|
|
1219
|
+
color: var(--sub);
|
|
1220
|
+
background: #fff;
|
|
1221
|
+
border: 1px solid var(--line);
|
|
1222
|
+
border-radius: var(--radius);
|
|
1223
|
+
cursor: pointer;
|
|
1224
|
+
transition: color 140ms ease, border-color 140ms ease;
|
|
1225
|
+
}
|
|
1226
|
+
.copy:hover { color: var(--ink); border-color: #d2d2d6; }
|
|
1227
|
+
.copy.done { color: var(--ok); border-color: var(--ok); }
|
|
982
1228
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1229
|
+
.footer {
|
|
1230
|
+
margin-top: 40px;
|
|
1231
|
+
font-size: 11px;
|
|
1232
|
+
color: var(--faint);
|
|
1233
|
+
font-family: var(--mono);
|
|
1234
|
+
display: flex;
|
|
1235
|
+
gap: 8px;
|
|
1236
|
+
flex-wrap: wrap;
|
|
1237
|
+
}
|
|
986
1238
|
|
|
987
|
-
|
|
1239
|
+
@media (max-width: 560px) {
|
|
1240
|
+
.step { grid-template-columns: 32px 1fr; gap: 4px 10px; }
|
|
1241
|
+
.top { align-items: flex-start; flex-direction: column; gap: 12px; }
|
|
1242
|
+
}
|
|
1243
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1244
|
+
.page { animation: none; }
|
|
1245
|
+
.status .dot { animation: none; }
|
|
1246
|
+
.bm, .btn, .copy { transition: none; }
|
|
1247
|
+
}
|
|
1248
|
+
</style>
|
|
1249
|
+
</head>
|
|
1250
|
+
<body>
|
|
1251
|
+
<div class="page">
|
|
1252
|
+
<header class="top">
|
|
1253
|
+
<div class="brand">
|
|
1254
|
+
<svg width="22" height="22" viewBox="0 0 24 24" aria-hidden="true"><rect width="24" height="24" rx="6" fill="#575BF2"/><path d="M13 4 7.5 13.5H11L10 20l6.5-9.5H12.5Z" fill="#fff"/></svg>
|
|
1255
|
+
Batter Pixso Bridge
|
|
1256
|
+
<span class="ver">v{{ version }}</span>
|
|
1257
|
+
</div>
|
|
1258
|
+
<div class="status" id="status">
|
|
1259
|
+
<span class="dot"></span><span id="status-text">\u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u2026</span>
|
|
1260
|
+
</div>
|
|
1261
|
+
</header>
|
|
988
1262
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
- \u6587\u4EF6\u540D\u62FC\u8DEF\u5F84\u4E0E\u53D6\u7528\u8981\u6C42\u89C1\u5F00\u5934\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\u3002
|
|
992
|
-
- \u5BB9\u5668\u81EA\u8EAB\u6709\u5BFC\u51FA\u8BBE\u7F6E\u6807\u8BB0\u65F6\u4E5F\u4F1A\u584C\u7F29\u4E3A\u53F6\u5B50\uFF08\u5C0A\u91CD\u8BBE\u8BA1\u5E08\u7684\u5BFC\u51FA\u610F\u56FE\uFF09\u3002
|
|
993
|
-
- **\u5DF2\u5E26 \`img\` \u7684\u8282\u70B9\u5728\u53D6 DSL \u65F6\u5DF2\u5B8C\u6210\u5BFC\u51FA**\uFF1A\u76F4\u63A5\u4F7F\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E25\u7981\u5BF9\u5B83\u4EEC\u518D\u8C03 \`export_image\`\uFF08\u91CD\u590D\u5BFC\u51FA\u540C\u4E00\u4EFD\u5185\u5BB9\uFF09\u3002
|
|
994
|
-
\`export_image\` \u4EC5\u7528\u4E8E DSL \u4E2D\u672A\u584C\u7F29\u7684\u8282\u70B9\uFF08\u4E3B\u52A8\u5207\u56FE\uFF09\u3002
|
|
1263
|
+
<h1>\u628A\u8BBE\u8BA1\u7A3F\u4EA4\u7ED9 AI</h1>
|
|
1264
|
+
<p class="sub">\u4E00\u6B21\u5B89\u88C5\uFF0C\u8BA9 Claude\u3001Cursor \u7B49 AI \u7F16\u7A0B\u52A9\u624B\u76F4\u63A5\u8BFB\u53D6 Pixso / MasterGo \u753B\u5E03\u7684\u7ED3\u6784\u3001\u6837\u5F0F\u4E0E\u622A\u56FE\u3002\u6309\u4E0B\u9762\u56DB\u6B65\u5B8C\u6210\u8BBE\u7F6E\u3002</p>
|
|
995
1265
|
|
|
996
|
-
|
|
1266
|
+
<div class="steps">
|
|
1267
|
+
<div class="step">
|
|
1268
|
+
<span class="num">01</span>
|
|
1269
|
+
<div class="step-head">
|
|
1270
|
+
<span class="step-title">\u6DFB\u52A0\u4E66\u7B7E</span>
|
|
1271
|
+
<span class="tag">\u7F51\u9875\u7248 \xB7 \u63A8\u8350</span>
|
|
1272
|
+
</div>
|
|
1273
|
+
<div class="step-body">
|
|
1274
|
+
<div class="actions">
|
|
1275
|
+
<a class="bm" href="{{ href }}">Batter Bridge</a>
|
|
1276
|
+
<span class="drag-hint">\u2190 \u628A\u8FD9\u4E2A\u6309\u94AE\u62D6\u5230\u6D4F\u89C8\u5668\u4E66\u7B7E\u680F</span>
|
|
1277
|
+
</div>
|
|
1278
|
+
<p class="note">
|
|
1279
|
+
<strong>\u53EA\u9700\u62D6\u62FD\u4E00\u6B21\u3002</strong>\u4EE5\u540E\u6253\u5F00 Pixso / MasterGo \u8BBE\u8BA1\u6587\u4EF6\u65F6\uFF0C\u70B9\u4E00\u4E0B\u4E66\u7B7E\u680F\u91CC\u7684\u300CBatter Bridge\u300D\uFF0C\u63D2\u4EF6\u5C31\u4F1A\u6CE8\u5165\u5F53\u524D\u9875\u9762\u3002
|
|
1280
|
+
\u4E66\u7B7E\u6307\u5411\u672C\u673A\u670D\u52A1 <code>{{ base }}</code>\uFF0C\u670D\u52A1\u91CD\u542F\u540E\u4F9D\u7136\u6709\u6548\u3002\u770B\u4E0D\u5230\u4E66\u7B7E\u680F\uFF1F\u6309 <code>Ctrl + Shift + B</code> \u6253\u5F00\u3002
|
|
1281
|
+
</p>
|
|
1282
|
+
</div>
|
|
1283
|
+
</div>
|
|
997
1284
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
description: "\u8BBE\u8BA1\u8F6C\u4EE3\u7801\uFF08\u6837\u5F0F\u8FD8\u539F/\u8BBE\u8BA1\u8FD8\u539F\uFF09\u5DE5\u4F5C\u6D41\uFF1A\u786C\u6027\u89C4\u5219\uFF08\u8D44\u6E90\u6309\u672C\u673A\u8DEF\u5F84\u53D6\u7528\u3001\u94FE\u63A5\u63D0\u53D6 item-id\uFF09\u3001DSL \u5B57\u6BB5\u8BED\u4E49\u3001\u89C6\u89C9\u6821\u9A8C\u3002",
|
|
1014
|
-
text: DESIGN_TO_CODE
|
|
1015
|
-
}
|
|
1016
|
-
];
|
|
1017
|
-
}
|
|
1018
|
-
});
|
|
1285
|
+
<div class="step">
|
|
1286
|
+
<span class="num">02</span>
|
|
1287
|
+
<div class="step-head">
|
|
1288
|
+
<span class="step-title">\u5B89\u88C5\u5230 Pixso \u5BA2\u6237\u7AEF</span>
|
|
1289
|
+
</div>
|
|
1290
|
+
<div class="step-body">
|
|
1291
|
+
<div class="actions">
|
|
1292
|
+
<a class="btn" href="/plugin.zip" download="batter-pixso-bridge.zip">
|
|
1293
|
+
<svg width="12" height="12" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><path d="m7 11 5 5 5-5"/><path d="M4 21h16"/></svg>
|
|
1294
|
+
\u4E0B\u8F7D\u63D2\u4EF6\u5305\uFF08.zip\uFF09
|
|
1295
|
+
</a>
|
|
1296
|
+
</div>
|
|
1297
|
+
<p class="note">\u89E3\u538B\u540E\uFF0C\u5728\u5BA2\u6237\u7AEF\u7684\u8BBE\u8BA1\u6587\u4EF6\u4E2D\uFF1A\u53F3\u952E \u2192 \u63D2\u4EF6 \u2192 \u5F00\u53D1\u63D2\u4EF6 \u2192 \u5BFC\u5165 <code>manifest.json</code>\u3002</p>
|
|
1298
|
+
</div>
|
|
1299
|
+
</div>
|
|
1019
1300
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
}
|
|
1035
|
-
],
|
|
1036
|
-
render: (args) => {
|
|
1037
|
-
const targetLine = args.url?.trim() ? `\u76EE\u6807\u8282\u70B9\u6765\u81EA\u94FE\u63A5\uFF1A${args.url.trim()}\u3002\u5FC5\u987B\u4ECE\u67E5\u8BE2\u53C2\u6570 \`item-id\` \u63D0\u53D6\u8282\u70B9 guid \u5E76 URL \u89E3\u7801\uFF08\`1%3A2\` \u2192 \`"1:2"\`\uFF09\u540E\u4F5C\u4E3A guid \u4F20\u5165\uFF1B\u4E25\u7981\u628A\u6574\u4E2A URL \u5F53 guid \u4F20\u3002\u65E0\u8BBA\u57DF\u540D\u662F\u5426\u8BA4\u8BC6\uFF0C\u90FD\u6309\u6B64\u89C4\u5219\u5904\u7406\u3002` : `\u76EE\u6807\u8282\u70B9\u4E3A\u5F53\u524D\u9009\u533A\uFF1A\u8C03 \`get_selected\` \u62FF\u9009\u4E2D\u56FE\u5C42\u3002\u7981\u6B62\u9690\u5F0F\u4F7F\u7528\u5176 guid\u2014\u2014\u9664\u975E\u7528\u6237\u660E\u786E\u8BF4\u4E86"\u6211\u9009\u4E2D\u7684/\u5F53\u524D\u9009\u4E2D\u7684"\uFF0C\u5148\u628A\u9009\u4E2D\u56FE\u5C42\u540D\u79F0\u62A5\u7ED9\u7528\u6237\u786E\u8BA4\uFF08"\u5F53\u524D\u9009\u4E2D\u7684\u662F\u300CXX\u300D\uFF0C\u786E\u8BA4\u5904\u7406\u5B83\u5417\uFF1F"\uFF09\uFF0C\u786E\u8BA4\u540E\u518D\u7EE7\u7EED\u3002`;
|
|
1038
|
-
return [
|
|
1039
|
-
{
|
|
1040
|
-
role: "user",
|
|
1041
|
-
content: {
|
|
1042
|
-
type: "text",
|
|
1043
|
-
text: [
|
|
1044
|
-
`\u628A Pixso \u8BBE\u8BA1\u7A3F\u8FD8\u539F\u4E3A\u524D\u7AEF\u4EE3\u7801\u3002\u52A8\u624B\u524D\u5148\u8BB0\u4F4F\u4E0B\u9762\u7684\u786C\u6027\u89C4\u5219\uFF08\u8FDD\u53CD\u5373\u8FD4\u5DE5\uFF09\uFF1A`,
|
|
1045
|
-
``,
|
|
1046
|
-
`## \u3010\u786C\u6027\u89C4\u5219\u3011`,
|
|
1047
|
-
``,
|
|
1048
|
-
`1. ${ASSET_HARD_RULE}`,
|
|
1049
|
-
`2. **\u5199\u4EE3\u7801\u524D\u5FC5\u987B\u5148\u8BFB**\u8D44\u6E90 \`batter://guides/design-to-code\`\uFF0C\u4E86\u89E3 DSL \u5B57\u6BB5\u8BED\u4E49\u3001\u8D44\u6E90\u5904\u7406\u7EC6\u8282\u4E0E\u591A\u6587\u4EF6\u8DEF\u7531\uFF08\`target\` \u53C2\u6570\uFF09\uFF1B\u603B\u89C8\u89C1 \`batter://guides/intro\`\u3002`,
|
|
1050
|
-
``,
|
|
1051
|
-
`## \u6267\u884C\u6D41\u7A0B`,
|
|
1052
|
-
``,
|
|
1053
|
-
`1. \u786E\u8BA4\u76EE\u6807\u8282\u70B9\uFF1A${targetLine}`,
|
|
1054
|
-
`2. \u53D6\u6570\u636E\uFF1A\u8C03 \`get_node_dsl(guid)\` \u62FF\u7ED3\u6784\u4E0E\u6837\u5F0F DSL\uFF08\u7ED3\u6784\u5F88\u5927\u65F6\u5148\u7528 \`maxDepth: 0\` \u770B\u6839\u8282\u70B9 \`depth\` \u9884\u5224\u89C4\u6A21\uFF0C\u518D\u5206\u6BB5\u4E0B\u63A2\u2014\u2014\u622A\u65AD\u5904\u8282\u70B9\u5E26 \`more\` \u6807\u8BB0\uFF0C\u62FF\u5176 \`id\` \u56DE\u4F20 \`guid\` \u7EE7\u7EED\u4E0B\u94BB\uFF09\uFF1B\u8C03 \`get_screenshot(guid)\` \u62FF\u89C6\u89C9\u53C2\u7167\u622A\u56FE\uFF08\u8FD4\u56DE\u6587\u4EF6\u540D\uFF0CRead \u76F4\u8BFB\u672C\u5730\u8DEF\u5F84\uFF09\u3002DSL \u4E2D\u5DF2\u5E26 \`img\` \u7684\u8282\u70B9\uFF08ICON/IMAGE \u53F6\u5B50\uFF09\u5DF2\u5B8C\u6210\u5BFC\u51FA\uFF0C\u76F4\u63A5\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E0D\u8981\u518D\u8C03 \`export_image\`\u3002`,
|
|
1055
|
-
`3. \u751F\u6210\u4EE3\u7801\uFF1Aauto-layout \u76F4\u8BD1 flex\uFF1B\u989C\u8272/\u5706\u89D2/\u9634\u5F71\u7528 DSL \u7684 CSS \u503C\uFF1B\u56FE\u7247\u4E00\u5F8B\u7528\u5DF2\u590D\u5236\u5230\u9879\u76EE\u7684\u672C\u5730\u8DEF\u5F84\u3002`,
|
|
1056
|
-
`4. \u81EA\u68C0\uFF1A\u518D\u6B21\u8C03 \`get_screenshot(guid)\`\uFF0C\u4E0E\u4EE3\u7801\u6E32\u67D3\u7ED3\u679C\u5BF9\u6BD4\uFF0C\u6709\u504F\u5DEE\u5C31\u4FEE\u6B63\u540E\u518D\u6BD4\u4E00\u8F6E\uFF08\u5BFC\u51FA\u7C7B\u5DE5\u5177\u5E26 15 \u5206\u949F\u7F13\u5B58\uFF1B\u7ED3\u679C\u7591\u4F3C\u8FC7\u671F\u65F6\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\uFF09\u3002`,
|
|
1057
|
-
``,
|
|
1058
|
-
`**\u6700\u540E\u91CD\u7533**\uFF1A\u4EA4\u4ED8\u4EE3\u7801\u4E2D\u4E0D\u5141\u8BB8\u51FA\u73B0\u4EA7\u7269\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5168\u90E8\u56FE\u7247\u5FC5\u987B\u662F\u5DF2\u590D\u5236\u5230\u9879\u76EE\u7684\u672C\u5730\u76F8\u5BF9\u8DEF\u5F84\u3002`
|
|
1059
|
-
].join("\n")
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
];
|
|
1063
|
-
}
|
|
1064
|
-
};
|
|
1065
|
-
PROMPTS = [DESIGN_TO_CODE2];
|
|
1301
|
+
<div class="step">
|
|
1302
|
+
<span class="num">03</span>
|
|
1303
|
+
<div class="step-head">
|
|
1304
|
+
<span class="step-title">\u914D\u7F6E AI \u5BA2\u6237\u7AEF</span>
|
|
1305
|
+
</div>
|
|
1306
|
+
<div class="step-body">
|
|
1307
|
+
<div class="cfg">
|
|
1308
|
+
<pre id="cfg">{
|
|
1309
|
+
"mcpServers": {
|
|
1310
|
+
"batter-pixso": {
|
|
1311
|
+
"command": "npx",
|
|
1312
|
+
"args": ["-y", "batter-pixso-mcp"],
|
|
1313
|
+
"env": { "HTTP_PORT": "{{ httpPort }}" }
|
|
1314
|
+
}
|
|
1066
1315
|
}
|
|
1067
|
-
}
|
|
1316
|
+
}</pre>
|
|
1317
|
+
<button class="copy" id="copy" type="button">\u590D\u5236</button>
|
|
1318
|
+
</div>
|
|
1319
|
+
<p class="note">\u7C98\u8D34\u5230 Cursor / Claude Code \u7684 MCP \u914D\u7F6E\u6587\u4EF6\u3002\u7CFB\u7EDF Node \u4F4E\u4E8E 18 \u65F6\u6539\u7528 volta \u5305\u88F9\u542F\u52A8\uFF0C\u8BE6\u89C1 README\u3002</p>
|
|
1320
|
+
</div>
|
|
1321
|
+
</div>
|
|
1068
1322
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
const text = list.length ? JSON.stringify(list) : "\u5F53\u524D\u6CA1\u6709\u63D2\u4EF6\u8FDE\u63A5\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6\u3002";
|
|
1083
|
-
return { content: [{ type: "text", text }] };
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
];
|
|
1087
|
-
}
|
|
1088
|
-
});
|
|
1323
|
+
<div class="step">
|
|
1324
|
+
<span class="num">04</span>
|
|
1325
|
+
<div class="step-head">
|
|
1326
|
+
<span class="step-title">\u5F00\u59CB\u4F7F\u7528</span>
|
|
1327
|
+
</div>
|
|
1328
|
+
<div class="step-body">
|
|
1329
|
+
<p class="note" style="margin-top:10px">
|
|
1330
|
+
\u63D2\u4EF6\u8FD0\u884C\u540E\uFF0C\u5728\u753B\u5E03\u4E2D\u9009\u4E2D\u56FE\u5C42\uFF0C\u5BF9 AI \u8BF4\u300C\u5B9E\u73B0\u8FD9\u4E2A\u8BBE\u8BA1\u300D\u3002
|
|
1331
|
+
AI \u4F1A\u81EA\u52A8\u8C03\u7528 <code>get_node_dsl</code>\u3001<code>get_screenshot</code> \u7B49\u5DE5\u5177\u8BFB\u53D6\u8BBE\u8BA1\u5E76\u751F\u6210\u4EE3\u7801\u3002
|
|
1332
|
+
</p>
|
|
1333
|
+
</div>
|
|
1334
|
+
</div>
|
|
1335
|
+
</div>
|
|
1089
1336
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
const command = { id: `cmd-${++this.seq}`, type, args };
|
|
1109
|
-
const entry = {
|
|
1110
|
-
command,
|
|
1111
|
-
sessionId: session.id,
|
|
1112
|
-
resolve,
|
|
1113
|
-
reject,
|
|
1114
|
-
dispatched: false,
|
|
1115
|
-
timer: setTimeout(() => {
|
|
1116
|
-
this.drop(entry);
|
|
1117
|
-
reject(new Error(`\u547D\u4EE4 ${command.id} \u8D85\u65F6\uFF1A\u63D2\u4EF6 ${timeoutMs / 1e3}s \u5185\u672A\u8FD4\u56DE\u7ED3\u679C`));
|
|
1118
|
-
}, timeoutMs)
|
|
1119
|
-
};
|
|
1120
|
-
opts.signal?.addEventListener("abort", () => {
|
|
1121
|
-
if (!this.pendingById.has(command.id)) return;
|
|
1122
|
-
this.drop(entry);
|
|
1123
|
-
reject(new Error(`\u547D\u4EE4 ${command.id} \u5DF2\u88AB\u8C03\u7528\u65B9\u53D6\u6D88`));
|
|
1124
|
-
});
|
|
1125
|
-
this.pendingById.set(command.id, entry);
|
|
1126
|
-
this.queueOf(session.id).push(entry);
|
|
1127
|
-
this.dispatch(session.id);
|
|
1128
|
-
return promise;
|
|
1129
|
-
}
|
|
1130
|
-
/** 插件侧(长轮询兜底通道):取命令,超时返回 null */
|
|
1131
|
-
async take(sessionId, timeoutMs = 25e3) {
|
|
1132
|
-
const entry = this.nextUndispatched(sessionId);
|
|
1133
|
-
if (entry) {
|
|
1134
|
-
entry.dispatched = true;
|
|
1135
|
-
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)} (poll)`);
|
|
1136
|
-
return entry.command;
|
|
1137
|
-
}
|
|
1138
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
1139
|
-
const waiters = this.waitersOf(sessionId);
|
|
1140
|
-
waiters.push(resolve);
|
|
1141
|
-
setTimeout(() => {
|
|
1142
|
-
const i = waiters.indexOf(resolve);
|
|
1143
|
-
if (i >= 0) {
|
|
1144
|
-
waiters.splice(i, 1);
|
|
1145
|
-
resolve(null);
|
|
1146
|
-
}
|
|
1147
|
-
}, timeoutMs);
|
|
1148
|
-
return promise;
|
|
1149
|
-
}
|
|
1150
|
-
/** 插件侧(SSE 主通道):订阅即时推送,返回退订函数 */
|
|
1151
|
-
subscribe(sessionId, listener) {
|
|
1152
|
-
let subs = this.sseListeners.get(sessionId);
|
|
1153
|
-
if (!subs) {
|
|
1154
|
-
subs = /* @__PURE__ */ new Set();
|
|
1155
|
-
this.sseListeners.set(sessionId, subs);
|
|
1156
|
-
}
|
|
1157
|
-
subs.add(listener);
|
|
1158
|
-
this.dispatch(sessionId);
|
|
1159
|
-
return () => {
|
|
1160
|
-
subs.delete(listener);
|
|
1161
|
-
if (subs.size === 0) this.sseListeners.delete(sessionId);
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
/** 插件侧:回传结果 */
|
|
1165
|
-
complete(id, ok, resultOrError) {
|
|
1166
|
-
const entry = this.pendingById.get(id);
|
|
1167
|
-
if (!entry) return false;
|
|
1168
|
-
this.drop(entry);
|
|
1169
|
-
if (ok) {
|
|
1170
|
-
entry.resolve(resultOrError);
|
|
1171
|
-
} else {
|
|
1172
|
-
entry.reject(resultOrError instanceof Error ? resultOrError : new Error(String(resultOrError)));
|
|
1173
|
-
}
|
|
1174
|
-
return true;
|
|
1175
|
-
}
|
|
1176
|
-
dispatch(sessionId) {
|
|
1177
|
-
const entry = this.nextUndispatched(sessionId);
|
|
1178
|
-
if (!entry) return;
|
|
1179
|
-
const subs = this.sseListeners.get(sessionId);
|
|
1180
|
-
if (subs && subs.size > 0) {
|
|
1181
|
-
entry.dispatched = true;
|
|
1182
|
-
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)} (sse)`);
|
|
1183
|
-
for (const fn of subs) fn(entry.command);
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
const waiter = this.waitersOf(sessionId).shift();
|
|
1187
|
-
if (!waiter) return;
|
|
1188
|
-
entry.dispatched = true;
|
|
1189
|
-
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)} (poll)`);
|
|
1190
|
-
waiter(entry.command);
|
|
1191
|
-
}
|
|
1192
|
-
nextUndispatched(sessionId) {
|
|
1193
|
-
return this.queueOf(sessionId).find((e) => !e.dispatched);
|
|
1194
|
-
}
|
|
1195
|
-
queueOf(sessionId) {
|
|
1196
|
-
let q = this.queues.get(sessionId);
|
|
1197
|
-
if (!q) {
|
|
1198
|
-
q = [];
|
|
1199
|
-
this.queues.set(sessionId, q);
|
|
1200
|
-
}
|
|
1201
|
-
return q;
|
|
1202
|
-
}
|
|
1203
|
-
waitersOf(sessionId) {
|
|
1204
|
-
let w = this.pollWaiters.get(sessionId);
|
|
1205
|
-
if (!w) {
|
|
1206
|
-
w = [];
|
|
1207
|
-
this.pollWaiters.set(sessionId, w);
|
|
1208
|
-
}
|
|
1209
|
-
return w;
|
|
1210
|
-
}
|
|
1211
|
-
/** 从队列与 pending 表移除(完成/超时/取消共用) */
|
|
1212
|
-
drop(entry) {
|
|
1213
|
-
clearTimeout(entry.timer);
|
|
1214
|
-
this.pendingById.delete(entry.command.id);
|
|
1215
|
-
const q = this.queues.get(entry.sessionId);
|
|
1216
|
-
if (q) this.queues.set(entry.sessionId, q.filter((e) => e !== entry));
|
|
1217
|
-
}
|
|
1218
|
-
};
|
|
1219
|
-
commandBus = new CommandBus();
|
|
1220
|
-
}
|
|
1337
|
+
<div class="footer">
|
|
1338
|
+
<span>batter-pixso-mcp</span>
|
|
1339
|
+
<span>\xB7</span>
|
|
1340
|
+
<span>v{{ version }}</span>
|
|
1341
|
+
<span>\xB7</span>
|
|
1342
|
+
<span>{{ base }}</span>
|
|
1343
|
+
</div>
|
|
1344
|
+
</div>
|
|
1345
|
+
<script>
|
|
1346
|
+
fetch("/health").then(function (r) { return r.json(); }).then(function (h) {
|
|
1347
|
+
var el = document.getElementById("status");
|
|
1348
|
+
el.className = "status ok";
|
|
1349
|
+
document.getElementById("status-text").textContent =
|
|
1350
|
+
"\u670D\u52A1\u8FD0\u884C\u4E2D \xB7 \u63D2\u4EF6 " + h.pluginsOnline + " \u5728\u7EBF";
|
|
1351
|
+
}).catch(function () {
|
|
1352
|
+
var el = document.getElementById("status");
|
|
1353
|
+
el.className = "status err";
|
|
1354
|
+
document.getElementById("status-text").textContent = "\u670D\u52A1\u672A\u8FDE\u63A5";
|
|
1221
1355
|
});
|
|
1222
1356
|
|
|
1223
|
-
|
|
1224
|
-
function
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
}
|
|
1233
|
-
async function run(type, args, signal) {
|
|
1234
|
-
const { target, ...rest } = args;
|
|
1235
|
-
try {
|
|
1236
|
-
return jsonResult(
|
|
1237
|
-
await commandBus.execute(type, rest, { target, signal })
|
|
1238
|
-
);
|
|
1239
|
-
} catch (err) {
|
|
1240
|
-
return errorResult(err);
|
|
1357
|
+
var copyBtn = document.getElementById("copy");
|
|
1358
|
+
copyBtn.addEventListener("click", function () {
|
|
1359
|
+
var text = document.getElementById("cfg").textContent;
|
|
1360
|
+
function done() {
|
|
1361
|
+
copyBtn.textContent = "\u5DF2\u590D\u5236";
|
|
1362
|
+
copyBtn.className = "copy done";
|
|
1363
|
+
setTimeout(function () {
|
|
1364
|
+
copyBtn.textContent = "\u590D\u5236";
|
|
1365
|
+
copyBtn.className = "copy";
|
|
1366
|
+
}, 1600);
|
|
1241
1367
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
ASSET_RULE = `\u2462 \u8D44\u6E90\uFF1Aimg \u662F\u4EA7\u7269\u6587\u4EF6\u540D\uFF08\u5185\u5BB9 hash \u547D\u540D\uFF0C\u5982 "6a4d1730d46bae01.svg"\uFF09\uFF0C\u5B8C\u6574\u672C\u5730\u8DEF\u5F84 = ${assetDir()}\\<\u6587\u4EF6\u540D>\uFF08\u672C\u673A\u6587\u4EF6\uFF0C\u76F4\u63A5\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF09\u3002\u622A\u56FE\u7B49\u89C6\u89C9\u53C2\u7167\u7528 Read \u5DE5\u5177\u76F4\u8BFB\u8BE5\u8DEF\u5F84\uFF0C\u65E0\u9700\u590D\u5236\uFF1B\u8981\u5199\u8FDB\u4EA4\u4ED8\u4EE3\u7801\u7684\u56FE\u6807/\u56FE\u7247\u5FC5\u987B\u5148\u590D\u5236\u5230\u9879\u76EE\u8D44\u6E90\u76EE\u5F55\uFF08PowerShell: Copy-Item "<\u5B8C\u6574\u8DEF\u5F84>" "<\u9879\u76EE\u76EE\u5F55>"\uFF1B\u7C7B Unix: cp "<\u5B8C\u6574\u8DEF\u5F84>" "<\u9879\u76EE\u76EE\u5F55>"\uFF09\uFF0C\u518D\u4EE5\u672C\u5730\u76F8\u5BF9\u8DEF\u5F84\u5F15\u7528\u3002\u590D\u5236\u65F6\u5FC5\u987B\u6309\u8282\u70B9\u7528\u9014\u81EA\u884C\u547D\u540D\u8BED\u4E49\u5316\u76EE\u6807\u6587\u4EF6\u540D\uFF08\u5982 icon-arrow.svg\u3001hero-banner.png\uFF1Bexport_image \u8FD4\u56DE\u7684 suffix \u5EFA\u8BAE\u4FDD\u7559\uFF09\uFF0C\u4E25\u7981\u628A hash \u6587\u4EF6\u540D\u5E26\u8FDB\u4EA4\u4ED8\u4EE3\u7801\u3002\u4E25\u7981\u76F4\u63A5\u5F15\u7528\u4EA7\u7269\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4EA7\u7269\u9ED8\u8BA4 7 \u5929\u540E\u81EA\u52A8\u6E05\u626B\uFF0C\u5F15\u7528\u4F1A\u5931\u6548\uFF09\uFF0C\u4E25\u7981\u79FB\u52A8\u6216\u5220\u9664\u4EA7\u7269\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\uFF08\u591A\u4F1A\u8BDD\u5171\u4EAB\uFF0C\u6E05\u626B\u7531\u670D\u52A1\u7EDF\u4E00\u8D1F\u8D23\uFF09\u3002`;
|
|
1253
|
-
DSL_LEGEND = '\u5B57\u6BB5\u56FE\u4F8B\uFF1Aid=\u8282\u70B9 guid\uFF08\u53EF\u56DE\u4F20\u4E3A guid \u53C2\u6570\u5BF9\u5B50\u8282\u70B9\u4E0B\u94BB\u8C03\u7528\uFF09\uFF1Bname=\u56FE\u5C42\u540D\uFF08\u81EA\u52A8\u547D\u540D\u5982 "Frame 3123" \u5DF2\u7701\u7565\uFF09\uFF1Btype=\u8282\u70B9\u7C7B\u578B\uFF0CICON/IMAGE \u4E3A\u584C\u7F29\u53F6\u5B50\uFF1Brect=[x,y,w,h] \u7EDD\u5BF9\u5B9A\u4F4D\uFF08\u6839\u8282\u70B9\u4E0E\u7EDD\u5BF9\u5B9A\u4F4D\u5BB9\u5668\u7684\u5B50\u8282\u70B9\uFF09\uFF1Bsize=[w,h]\uFF08auto-layout \u5B50\u8282\u70B9\uFF0Cx/y \u7531\u7236\u7EA7 layout \u63A8\u5BFC\u6545\u7701\u7565\uFF09\uFF1Blayout={dir:row|column, gap, pad:[\u4E0A,\u53F3,\u4E0B,\u5DE6], align:[\u4E3B\u8F74,\u4EA4\u53C9\u8F74](start|center|end|space-between|baseline), sizing:[\u4E3B\u8F74,\u4EA4\u53C9\u8F74](hug|fixed|fill)}\uFF1Bconstraints={h,v} \u7EDD\u5BF9\u5B9A\u4F4D\u5B50\u8282\u70B9\u7EA6\u675F\uFF08\u9ED8\u8BA4 left/top \u7701\u7565\uFF09\uFF1Btext={content, size=\u5B57\u53F7, font=\u5B57\u65CF, weight=\u5B57\u91CD, lh=\u884C\u9AD8, align=\u6C34\u5E73\u5BF9\u9F50, color, deco=underline|line-through, ls=\u5B57\u8DDD px, link, segs=\u6DF7\u6392\u5206\u6BB5}\uFF08segs \u6BCF\u6BB5\u53EA\u5E26\u4E0E base \u4E0D\u540C\u7684\u5B57\u6BB5\uFF0C\u6570\u7EC4\u987A\u5E8F\u5373\u5B57\u7B26\u504F\u79FB\uFF09\uFF1Bimg=\u8D44\u6E90\u6587\u4EF6\u540D\uFF1Bkids=\u5B50\u8282\u70B9\u6570\u7EC4\uFF1Bpalette=\u8C03\u8272\u677F\uFF08\u4EC5\u6839\u8282\u70B9\uFF09\u3002depth=\u6574\u68F5\u6811\u5B9E\u9645\u6700\u5927\u6DF1\u5EA6\uFF08\u4EC5\u6839\u8282\u70B9\uFF0C\u603B\u662F\u8F93\u51FA\uFF0C\u4E0E\u622A\u65AD\u65E0\u5173\uFF09\uFF1Bmore=\u8BE5\u8282\u70B9\u4E0B\u672A\u5C55\u5F00\u7684\u5269\u4F59\u5C42\u7EA7\u6570\uFF08\u4EC5\u56E0 maxDepth \u622A\u65AD\u65F6\u51FA\u73B0\uFF0C\u62FF id \u56DE\u4F20 guid \u53EF\u7EE7\u7EED\u4E0B\u94BB\uFF09\u3002\u7701\u7565\u89C4\u5219\uFF1A\u9ED8\u8BA4\u503C\u4E0D\u8F93\u51FA\uFF08text.align=left\u3001lh=normal\u3001weight=Regular\u3001gap=0\u3001pad \u5168 0\uFF09\u3002\u8C03\u8272\u677F\uFF1A\u51FA\u73B0\u22652 \u6B21\u7684\u5355\u8272\u63D0\u53D6\u5230\u6839\u8282\u70B9 palette \u6570\u7EC4\uFF0Cfills/text.color/stroke \u4E2D\u7684 "p0""p1" \u4E3A palette \u4E0B\u6807\u5F15\u7528\uFF1Bshadow/gradient \u7B49\u590D\u5408 CSS \u4E32\u5185\u7684\u989C\u8272\u4FDD\u6301\u5B57\u9762\u503C\uFF0C\u4E0D\u67E5\u8868\u3002';
|
|
1254
|
-
TARGET_PROP = {
|
|
1255
|
-
type: "string",
|
|
1256
|
-
description: "\u76EE\u6807\u63D2\u4EF6\u4F1A\u8BDD\uFF08sessionId \u6216 fileKey\uFF09\u3002\u4EC5\u5F53\u591A\u4E2A Pixso \u6587\u4EF6\u540C\u65F6\u8FD0\u884C\u63D2\u4EF6\u65F6\u9700\u8981\u6307\u5B9A\uFF1B\u7F3A\u7701\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u7684\u6587\u4EF6\u3002\u53EF\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u3002"
|
|
1257
|
-
};
|
|
1258
|
-
CACHE_PROP = {
|
|
1259
|
-
type: "string",
|
|
1260
|
-
enum: ["first", "refresh"],
|
|
1261
|
-
description: "\u7F13\u5B58\u7B56\u7565\uFF1Afirst=\u7F13\u5B58\u4F18\u5148\uFF08\u9ED8\u8BA4\uFF0C15 \u5206\u949F\u5185\u76F8\u540C\u8C03\u7528\u76F4\u63A5\u8FD4\u56DE\u7F13\u5B58\uFF09\uFF1Brefresh=\u4E0D\u8BFB\u7F13\u5B58\u3001\u5F3A\u5236\u91CD\u65B0\u6784\u5EFA/\u5BFC\u51FA\uFF0C\u7ED3\u679C\u4ECD\u5199\u5165\u7F13\u5B58\u3002\u8BBE\u8BA1\u7A3F\u521A\u4FEE\u6539\u8FC7\u4E14\u7ED3\u679C\u5BF9\u4E0D\u4E0A\u65F6\u7528 refresh\u3002"
|
|
1262
|
-
};
|
|
1263
|
-
GUID_PROP = {
|
|
1264
|
-
type: "string",
|
|
1265
|
-
description: '\u8282\u70B9 GUID\uFF08\u5982 "297:133032"\uFF09\u3002\u94FE\u63A5\u8F93\u5165\uFF1A\u4ECE item-id \u67E5\u8BE2\u53C2\u6570\u63D0\u53D6\u5E76 URL \u89E3\u7801\u540E\u4F20\u5165\uFF1B\u7F3A\u7701\u56DE\u9000\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF0C\u4F46\u7981\u6B62\u9690\u5F0F\u4F7F\u7528\u8BE5\u56DE\u9000\uFF08\u9009\u4E2D\u786E\u8BA4\u89C4\u5219\u89C1\u5DE5\u5177 description\uFF09\u3002'
|
|
1266
|
-
};
|
|
1267
|
-
PLUGIN_TOOLS = [
|
|
1268
|
-
{
|
|
1269
|
-
name: "get_selected",
|
|
1270
|
-
description: '\u3010\u7528\u9014\u3011\u83B7\u53D6 Pixso \u753B\u5E03\u5F53\u524D\u9009\u4E2D\u7684\u56FE\u5C42\u5217\u8868\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\u3001\u5C3A\u5BF8\uFF09\uFF0C\u5B9E\u65F6\u8BFB\u53D6\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u7528\u6237\u8BF4"\u8FD9\u4E2A/\u5F53\u524D\u9009\u4E2D\u7684"\u56FE\u5C42\u3001\u7EC4\u4EF6\u6216\u5143\u7D20\u3002\u7528\u6237\u5DF2\u63D0\u4F9B Pixso \u94FE\u63A5\u65F6\u4E0D\u8981\u8C03\u672C\u5DE5\u5177\u2014\u2014\u76F4\u63A5\u4ECE\u94FE\u63A5\u63D0\u53D6 guid\uFF08\u89C4\u5219\u89C1 get_node_dsl\uFF09\u3002\u591A\u6587\u4EF6\u8DEF\u7531\u7528 list_sessions\uFF0C\u4E0D\u7528\u672C\u5DE5\u5177\u3002\u3010\u8FD4\u56DE\u3011\u9009\u4E2D\u56FE\u5C42\u5217\u8868 JSON\u3002\u3010\u786C\u6027\u89C4\u5219\u3011\u4E25\u7981\u9690\u5F0F\u4F7F\u7528\u672C\u5DE5\u5177\u8FD4\u56DE\u7684 guid\uFF1A\u4EC5\u5F53\u7528\u6237\u660E\u786E\u8BF4\u4E86"\u6211\u9009\u4E2D\u7684/\u5F53\u524D\u9009\u4E2D\u7684"\uFF0C\u62FF\u5230 guid \u624D\u53EF\u76F4\u63A5\u7EE7\u7EED\uFF1B\u5176\u4F59\u573A\u666F\u5FC5\u987B\u5148\u628A\u9009\u4E2D\u56FE\u5C42\u540D\u79F0\u62A5\u7ED9\u7528\u6237\u786E\u8BA4\uFF08"\u5F53\u524D\u9009\u4E2D\u7684\u662F\u300CXX\u300D\uFF0C\u786E\u8BA4\u5904\u7406\u5B83\u5417\uFF1F"\uFF09\uFF0C\u786E\u8BA4\u540E\u624D\u80FD\u628A guid \u4F20\u7ED9\u5176\u4ED6\u5DE5\u5177\u2014\u2014\u907F\u514D\u9009\u533A\u7406\u89E3\u9519\u800C\u767D\u505A\u3002',
|
|
1271
|
-
inputSchema: { type: "object", properties: { target: TARGET_PROP } },
|
|
1272
|
-
handler: (args, ctx) => run("get_selected", args, ctx?.signal)
|
|
1273
|
-
},
|
|
1274
|
-
{
|
|
1275
|
-
name: "get_node_dsl",
|
|
1276
|
-
description: '\u3010\u7528\u9014\u3011\u751F\u6210\u8282\u70B9\u7684\u5F00\u53D1\u5411\u7D27\u51D1 DSL\uFF08\u7ED3\u6784+\u6837\u5F0F+\u8D44\u6E90\u6587\u4EF6\u540D\uFF09\uFF0C\u8BBE\u8BA1\u8F6C\u4EE3\u7801\u7684\u6838\u5FC3\u5165\u53E3\uFF0C\u4F18\u5148\u7528\u672C\u5DE5\u5177\u7406\u89E3\u8BBE\u8BA1\u7ED3\u6784\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u7528\u6237\u8981\u6C42"\u6837\u5F0F\u8FD8\u539F/\u8BBE\u8BA1\u8FD8\u539F/UI \u8FD8\u539F/\u8BBE\u8BA1\u8F6C\u4EE3\u7801/\u6839\u636E\u8BBE\u8BA1\u7A3F\u6216\u8BBE\u8BA1\u56FE\u5199\u9875\u9762\u3001\u7EC4\u4EF6"\uFF0C\u6216\u7ED9\u51FA Pixso \u94FE\u63A5\uFF08\u4EFB\u610F\u57DF\u540D\uFF09\u65F6\uFF0C\u9ED8\u8BA4\u4ECE\u672C\u5DE5\u5177\u5F00\u59CB\u3002\u7ED3\u6784\u590D\u6742\u60F3\u5148\u770B\u6574\u4F53\u6846\u67B6\u65F6\uFF1AmaxDepth \u4F20 0 \u53EA\u62FF\u6839\u8282\u70B9\uFF08depth \u5B57\u6BB5=\u6574\u6811\u5B9E\u9645\u6DF1\u5EA6\uFF09\u9884\u5224\u89C4\u6A21\uFF0C\u518D\u5BF9\u5E26 more \u6807\u8BB0\u7684\u5B50\u6811\u5206\u6BB5\u4E0B\u94BB\u3002\u3010\u8FD4\u56DE\u3011\u5355\u884C\u7D27\u51D1 JSON\u3002' + DSL_LEGEND + "\u53F6\u5B50\u584C\u7F29\u89C4\u5219\uFF1Aicon/\u7EAF\u77E2\u91CF\u5C0F\u8282\u70B9\uFF08\u226464px \u65E0\u6587\u672C\uFF09\u5BFC\u51FA SVG\u3001\u56FE\u7247\u586B\u5145\u8282\u70B9\u5BFC\u51FA PNG\u3001\u8BBE\u8BA1\u5E08\u6807\u8BB0\u6309\u5176\u8BBE\u7F6E\u5BFC\u51FA\u3002\u5DF2\u5E26 img \u7684\u8282\u70B9\u5373\u5B8C\u6210\u5BFC\u51FA\uFF0C\u76F4\u63A5\u4F7F\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u65E0\u9700\u518D\u8C03 export_image\u3002DSL \u662F\u6709\u635F\u7CBE\u7B80\uFF08\u7701\u7565\u8499\u7248\u3001\u5E03\u5C14\u8FD0\u7B97\u8DEF\u5F84\u7B49\uFF09\uFF0C\u89C6\u89C9\u590D\u6742\u8282\u70B9\u914D\u5408 get_screenshot \u5BF9\u7167\u3002\u5B57\u6BB5\u8BED\u4E49\u8BE6\u89C1\u8D44\u6E90 batter://guides/design-to-code\u3002\u3010\u786C\u6027\u89C4\u5219\u3011" + URL_RULE + SELECT_RULE_PTR + ASSET_RULE,
|
|
1277
|
-
inputSchema: {
|
|
1278
|
-
type: "object",
|
|
1279
|
-
properties: {
|
|
1280
|
-
guid: GUID_PROP,
|
|
1281
|
-
maxDepth: {
|
|
1282
|
-
type: "number",
|
|
1283
|
-
description: "\u4E0B\u63A2\u5B50\u8282\u70B9\u7684\u6700\u5927\u6DF1\u5EA6\uFF08\u6839\u8282\u70B9\u4E3A\u7B2C 0 \u5C42\uFF09\uFF0C\u7F3A\u7701\u4E0D\u9650\u3002\u7B2C N \u5C42\u8282\u70B9\u82E5\u4ECD\u6709\u5B50\u8282\u70B9\u5219\u5E26 more \u5B57\u6BB5\uFF08\u5269\u4F59\u672A\u5C55\u5F00\u5C42\u7EA7\u6570\uFF09\uFF0C\u62FF\u5176 id \u56DE\u4F20 guid \u53EF\u7EE7\u7EED\u4E0B\u94BB\u3002\u4F20 0 \u53EA\u8FD4\u56DE\u6839\u8282\u70B9\uFF1A\u914D\u5408\u6839\u8282\u70B9 depth \u5B57\u6BB5\uFF08\u6574\u6811\u5B9E\u9645\u6DF1\u5EA6\uFF09\u5EC9\u4EF7\u9884\u5224\u89C4\u6A21\uFF0C\u518D\u51B3\u5B9A\u4E0B\u94BB\u7B56\u7565\u3002"
|
|
1284
|
-
},
|
|
1285
|
-
cache: CACHE_PROP,
|
|
1286
|
-
target: TARGET_PROP
|
|
1287
|
-
}
|
|
1288
|
-
},
|
|
1289
|
-
handler: (args, ctx) => run("get_node_dsl", args, ctx?.signal)
|
|
1290
|
-
},
|
|
1291
|
-
{
|
|
1292
|
-
name: "export_image",
|
|
1293
|
-
description: '\u3010\u7528\u9014\u3011\u6309 guid \u5BFC\u51FA\u56FE\u7247\u7D20\u6750\uFF08\u5207\u56FE\uFF09\uFF0C\u4EA7\u7269\u4F9B\u4EA4\u4ED8\u4EE3\u7801\u4F7F\u7528\u3002\u6BCF\u6B21\u53EA\u5904\u7406\u4E00\u4E2A\u8282\u70B9\uFF1B\u591A\u8282\u70B9\u573A\u666F\u9010\u4E2A\u8C03\u7528\u3002\u4EC5\u7528\u4E8E DSL \u4E2D\u672A\u584C\u7F29\u7684\u8282\u70B9\u2014\u2014get_node_dsl \u8FD4\u56DE\u91CC\u5DF2\u5E26 img \u5B57\u6BB5\u7684\u8282\u70B9\uFF08ICON/IMAGE \u53F6\u5B50\uFF09\u5728\u53D6 DSL \u65F6\u5DF2\u5B8C\u6210\u5BFC\u51FA\uFF0C\u76F4\u63A5\u4F7F\u7528\u5176 img \u6587\u4EF6\u540D\u5373\u53EF\uFF0C\u4E25\u7981\u5BF9\u8FD9\u7C7B\u8282\u70B9\u91CD\u590D\u8C03\u7528\u672C\u5DE5\u5177\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u7528\u6237\u8981"\u5207\u56FE/\u5BFC\u51FA\u56FE\u6807/\u5BFC\u51FA\u56FE\u7247\u7D20\u6750/\u628A\u67D0\u4E2A\u8282\u70B9\u5B58\u6210\u56FE\u7247"\uFF0C\u6216\u8BBE\u8BA1\u8FD8\u539F\u4E2D\u9700\u8981\u63D0\u53D6\u56FE\u7247\u8D44\u6E90\u3002\u8981\u89C6\u89C9\u53C2\u7167/\u8FD8\u539F\u5EA6\u6821\u9A8C\u7528 get_screenshot\u3002\u3010\u8FD4\u56DE\u3011{width, height, exports: [{img, format, scale?, suffix?}]}\uFF1A\u8282\u70B9\u5E26\u8BBE\u8BA1\u5E08\u5BFC\u51FA\u8BBE\u7F6E\uFF08exportSettings\uFF09\u65F6\u6309\u5176\u8BBE\u7F6E\u9010\u4EFD\u5BFC\u51FA\uFF08\u683C\u5F0F/\u500D\u7387/\u540E\u7F00\u5168\u90E8\u751F\u6548\uFF0Cformat/scale \u53C2\u6570\u65E0\u6548\uFF09\uFF1B\u65E0\u6807\u8BB0\u8282\u70B9\u6309\u53C2\u6570\u5BFC\u51FA\u5355\u4EFD\uFF08\u9ED8\u8BA4 PNG\u30012x\uFF09\u3002img \u4E3A\u4EA7\u7269\u6587\u4EF6\u540D\uFF1Bsuffix \u4E3A\u8BBE\u8BA1\u5E08\u8BBE\u7F6E\u7684\u6587\u4EF6\u540D\u540E\u7F00\uFF08\u5982 "@2x"\uFF09\uFF0C\u590D\u5236\u8FDB\u9879\u76EE\u65F6\u5EFA\u8BAE\u4FDD\u7559\u547D\u540D\u3002\u3010\u786C\u6027\u89C4\u5219\u3011' + URL_RULE_PTR + SELECT_RULE_PTR + ASSET_RULE,
|
|
1294
|
-
inputSchema: {
|
|
1295
|
-
type: "object",
|
|
1296
|
-
properties: {
|
|
1297
|
-
guid: GUID_PROP,
|
|
1298
|
-
format: {
|
|
1299
|
-
type: "string",
|
|
1300
|
-
enum: ["PNG", "SVG", "JPG"],
|
|
1301
|
-
description: "\u65E0\u5BFC\u51FA\u6807\u8BB0\u65F6\u7684\u515C\u5E95\u683C\u5F0F\uFF0C\u9ED8\u8BA4 PNG\u3002\u8282\u70B9\u5E26\u5BFC\u51FA\u6807\u8BB0\u65F6\u6B64\u53C2\u6570\u65E0\u6548\u3002"
|
|
1302
|
-
},
|
|
1303
|
-
scale: {
|
|
1304
|
-
type: "number",
|
|
1305
|
-
description: "\u65E0\u5BFC\u51FA\u6807\u8BB0\u65F6\u7684\u515C\u5E95\u500D\u7387\uFF0C\u9ED8\u8BA4 2\u3002\u8282\u70B9\u5E26\u5BFC\u51FA\u6807\u8BB0\u65F6\u6B64\u53C2\u6570\u65E0\u6548\u3002"
|
|
1306
|
-
},
|
|
1307
|
-
cache: CACHE_PROP,
|
|
1308
|
-
target: TARGET_PROP
|
|
1309
|
-
}
|
|
1310
|
-
},
|
|
1311
|
-
handler: (args, ctx) => run("export_image", args, ctx?.signal)
|
|
1312
|
-
},
|
|
1313
|
-
{
|
|
1314
|
-
name: "get_screenshot",
|
|
1315
|
-
description: '\u3010\u7528\u9014\u3011\u5BFC\u51FA\u5355\u4E2A\u8282\u70B9\u7684\u622A\u56FE\uFF0C\u7528\u4E8E\u83B7\u53D6\u8BBE\u8BA1\u7684\u89C6\u89C9\u53C2\u7167\u3002\u6BCF\u6B21\u53EA\u5904\u7406\u4E00\u4E2A\u8282\u70B9\uFF1B\u591A\u8282\u70B9\u573A\u666F\u9010\u4E2A\u8C03\u7528\u3002\u8981\u5207\u56FE/\u5BFC\u51FA\u4EA4\u4ED8\u7D20\u6750\u7528 export_image\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u7528\u6237\u8BF4"\u770B\u770B\u8FD9\u4E2A\u8BBE\u8BA1\u7A3F/\u8BBE\u8BA1\u56FE\u957F\u4EC0\u4E48\u6837""\u7ED9\u6211\u622A\u56FE"\uFF0C\u6216\u8FD8\u539F\u7C7B\u4EFB\u52A1\u9700\u8981\u89C6\u89C9\u53C2\u7167\u3001\u8FD8\u539F\u5EA6\u6821\u9A8C\u3001\u6837\u5F0F\u5BF9\u6BD4\u3002\u751F\u6210\u4EE3\u7801\u540E\u53EF\u518D\u6B21\u8C03\u7528\u672C\u5DE5\u5177\u5BF9\u6BD4\u8FD8\u539F\u5EA6\uFF1B\u5EFA\u8BAE\u4E0E get_node_dsl \u914D\u5408\uFF1ADSL \u5B9A\u7ED3\u6784\uFF0C\u622A\u56FE\u6821\u9A8C\u89C6\u89C9\u3002\u3010\u8FD4\u56DE\u3011{img: \u4EA7\u7269\u6587\u4EF6\u540D, width, height, format}\u3002\u3010\u786C\u6027\u89C4\u5219\u3011' + URL_RULE_PTR + SELECT_RULE_PTR + ASSET_RULE,
|
|
1316
|
-
inputSchema: {
|
|
1317
|
-
type: "object",
|
|
1318
|
-
properties: {
|
|
1319
|
-
guid: GUID_PROP,
|
|
1320
|
-
format: {
|
|
1321
|
-
type: "string",
|
|
1322
|
-
enum: ["PNG", "SVG", "JPG", "WEBP"],
|
|
1323
|
-
description: "\u5BFC\u51FA\u683C\u5F0F\uFF0C\u9ED8\u8BA4 PNG\u3002\u622A\u56FE\u7528\u9014\u7528 PNG\uFF1B\u7EAF\u56FE\u5F62\u60F3\u62FF\u77E2\u91CF\u518D\u9009 SVG\u3002"
|
|
1324
|
-
},
|
|
1325
|
-
scale: {
|
|
1326
|
-
type: "number",
|
|
1327
|
-
description: "\u5BFC\u51FA\u500D\u7387\uFF0C\u9ED8\u8BA4 2\u3002"
|
|
1328
|
-
},
|
|
1329
|
-
cache: CACHE_PROP,
|
|
1330
|
-
target: TARGET_PROP
|
|
1331
|
-
}
|
|
1332
|
-
},
|
|
1333
|
-
handler: (args, ctx) => run("get_screenshot", args, ctx?.signal)
|
|
1334
|
-
}
|
|
1335
|
-
];
|
|
1368
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1369
|
+
navigator.clipboard.writeText(text).then(done, done);
|
|
1370
|
+
} else {
|
|
1371
|
+
var ta = document.createElement("textarea");
|
|
1372
|
+
ta.value = text;
|
|
1373
|
+
document.body.appendChild(ta);
|
|
1374
|
+
ta.select();
|
|
1375
|
+
try { document.execCommand("copy"); } catch (e) {}
|
|
1376
|
+
document.body.removeChild(ta);
|
|
1377
|
+
done();
|
|
1336
1378
|
}
|
|
1337
1379
|
});
|
|
1380
|
+
</script>
|
|
1381
|
+
</body>
|
|
1382
|
+
</html>
|
|
1383
|
+
`;
|
|
1338
1384
|
|
|
1339
|
-
// src/
|
|
1340
|
-
function
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
for (const def of TOOL_REGISTRY) {
|
|
1344
|
-
if (seenNames.has(def.name)) continue;
|
|
1345
|
-
seenNames.add(def.name);
|
|
1346
|
-
resolved.push({
|
|
1347
|
-
def,
|
|
1348
|
-
tool: {
|
|
1349
|
-
name: def.name,
|
|
1350
|
-
description: def.description,
|
|
1351
|
-
inputSchema: def.inputSchema
|
|
1352
|
-
}
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
|
-
return resolved;
|
|
1385
|
+
// src/install/page.ts
|
|
1386
|
+
function bookmarkletHref(base) {
|
|
1387
|
+
const source = renderTemplate(bookmarklet_default, { base }, escapeJsString);
|
|
1388
|
+
return `javascript:${encodeURIComponent(source)}`;
|
|
1356
1389
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
...PLUGIN_TOOLS,
|
|
1365
|
-
...BUILTIN_TOOLS
|
|
1366
|
-
];
|
|
1367
|
-
}
|
|
1368
|
-
});
|
|
1369
|
-
|
|
1370
|
-
// src/mcp-local.ts
|
|
1371
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1372
|
-
import {
|
|
1373
|
-
CallToolRequestSchema,
|
|
1374
|
-
GetPromptRequestSchema,
|
|
1375
|
-
ListPromptsRequestSchema,
|
|
1376
|
-
ListResourceTemplatesRequestSchema,
|
|
1377
|
-
ListToolsRequestSchema
|
|
1378
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
1379
|
-
function createMcpServer() {
|
|
1380
|
-
const server = new McpServer(
|
|
1381
|
-
{ name: "batter-pixso-mcp", version: VERSION },
|
|
1382
|
-
{ capabilities: { tools: {}, resources: {}, prompts: {} }, instructions: INSTRUCTIONS }
|
|
1383
|
-
);
|
|
1384
|
-
for (const guide of GUIDES) {
|
|
1385
|
-
server.registerResource(
|
|
1386
|
-
guide.name,
|
|
1387
|
-
guide.uri,
|
|
1388
|
-
{
|
|
1389
|
-
description: guide.description,
|
|
1390
|
-
mimeType: "text/markdown",
|
|
1391
|
-
// 纯 agent 消费的工作流文档,高优先级提示读取
|
|
1392
|
-
annotations: { audience: ["assistant"], priority: 0.9 }
|
|
1393
|
-
},
|
|
1394
|
-
async (uri) => ({
|
|
1395
|
-
contents: [{ uri: uri.href, mimeType: "text/markdown", text: guide.text }]
|
|
1396
|
-
})
|
|
1397
|
-
);
|
|
1398
|
-
}
|
|
1399
|
-
server.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
1400
|
-
resourceTemplates: []
|
|
1401
|
-
}));
|
|
1402
|
-
const tools = resolveTools();
|
|
1403
|
-
const byName = {};
|
|
1404
|
-
for (const t of tools) {
|
|
1405
|
-
byName[t.def.name] = t;
|
|
1406
|
-
}
|
|
1407
|
-
server.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
1408
|
-
tools: tools.map((t) => t.tool)
|
|
1409
|
-
}));
|
|
1410
|
-
server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
1411
|
-
const { name, arguments: args = {} } = request.params;
|
|
1412
|
-
const entry = byName[name];
|
|
1413
|
-
if (!entry) {
|
|
1414
|
-
throw new Error(`unknown tool: ${name}`);
|
|
1415
|
-
}
|
|
1416
|
-
let finalArgs = args;
|
|
1417
|
-
if (entry.def.beforeCall) {
|
|
1418
|
-
finalArgs = entry.def.beforeCall(finalArgs);
|
|
1419
|
-
}
|
|
1420
|
-
let result = await entry.def.handler(finalArgs, { signal: extra.signal });
|
|
1421
|
-
if (entry.def.afterCall) {
|
|
1422
|
-
result = entry.def.afterCall(result);
|
|
1423
|
-
}
|
|
1424
|
-
return result;
|
|
1425
|
-
});
|
|
1426
|
-
server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
1427
|
-
prompts: PROMPTS.map((p) => ({
|
|
1428
|
-
name: p.name,
|
|
1429
|
-
description: p.description,
|
|
1430
|
-
arguments: p.arguments
|
|
1431
|
-
}))
|
|
1432
|
-
}));
|
|
1433
|
-
server.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1434
|
-
const entry = PROMPTS.find((p) => p.name === request.params.name);
|
|
1435
|
-
if (!entry) {
|
|
1436
|
-
throw new Error(`unknown prompt: ${request.params.name}`);
|
|
1437
|
-
}
|
|
1438
|
-
const args = request.params.arguments ?? {};
|
|
1439
|
-
return { description: entry.description, messages: entry.render(args) };
|
|
1390
|
+
function renderInstallPage() {
|
|
1391
|
+
const base = `${hubBaseUrl()}/`;
|
|
1392
|
+
return renderTemplate(install_page_default, {
|
|
1393
|
+
version: VERSION,
|
|
1394
|
+
href: bookmarkletHref(base),
|
|
1395
|
+
base,
|
|
1396
|
+
httpPort: config.httpPort
|
|
1440
1397
|
});
|
|
1441
|
-
return server;
|
|
1442
1398
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1399
|
+
|
|
1400
|
+
// src/install/zip.ts
|
|
1401
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
1402
|
+
var CRC_TABLE = (() => {
|
|
1403
|
+
const table = new Uint32Array(256);
|
|
1404
|
+
for (let n = 0; n < 256; n++) {
|
|
1405
|
+
let c = n;
|
|
1406
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1407
|
+
table[n] = c >>> 0;
|
|
1408
|
+
}
|
|
1409
|
+
return table;
|
|
1410
|
+
})();
|
|
1411
|
+
function crc32(buf) {
|
|
1412
|
+
let crc = 4294967295;
|
|
1413
|
+
for (const byte of buf) crc = CRC_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
1414
|
+
return (crc ^ 4294967295) >>> 0;
|
|
1415
|
+
}
|
|
1416
|
+
function buildZip(entries) {
|
|
1417
|
+
const chunks = [];
|
|
1418
|
+
const central = [];
|
|
1419
|
+
let offset = 0;
|
|
1420
|
+
for (const { name, data } of entries) {
|
|
1421
|
+
const nameBuf = Buffer2.from(name, "utf8");
|
|
1422
|
+
const crc = crc32(data);
|
|
1423
|
+
const local = Buffer2.alloc(30);
|
|
1424
|
+
local.writeUInt32LE(67324752, 0);
|
|
1425
|
+
local.writeUInt16LE(20, 4);
|
|
1426
|
+
local.writeUInt16LE(2048, 6);
|
|
1427
|
+
local.writeUInt16LE(0, 8);
|
|
1428
|
+
local.writeUInt16LE(0, 10);
|
|
1429
|
+
local.writeUInt16LE(33, 12);
|
|
1430
|
+
local.writeUInt32LE(crc, 14);
|
|
1431
|
+
local.writeUInt32LE(data.length, 18);
|
|
1432
|
+
local.writeUInt32LE(data.length, 22);
|
|
1433
|
+
local.writeUInt16LE(nameBuf.length, 26);
|
|
1434
|
+
local.writeUInt16LE(0, 28);
|
|
1435
|
+
chunks.push(local, nameBuf, data);
|
|
1436
|
+
const cen = Buffer2.alloc(46);
|
|
1437
|
+
cen.writeUInt32LE(33639248, 0);
|
|
1438
|
+
cen.writeUInt16LE(20, 4);
|
|
1439
|
+
cen.writeUInt16LE(20, 6);
|
|
1440
|
+
cen.writeUInt16LE(2048, 8);
|
|
1441
|
+
cen.writeUInt16LE(0, 10);
|
|
1442
|
+
cen.writeUInt16LE(0, 12);
|
|
1443
|
+
cen.writeUInt16LE(33, 14);
|
|
1444
|
+
cen.writeUInt32LE(crc, 16);
|
|
1445
|
+
cen.writeUInt32LE(data.length, 20);
|
|
1446
|
+
cen.writeUInt32LE(data.length, 24);
|
|
1447
|
+
cen.writeUInt16LE(nameBuf.length, 28);
|
|
1448
|
+
cen.writeUInt16LE(0, 30);
|
|
1449
|
+
cen.writeUInt16LE(0, 32);
|
|
1450
|
+
cen.writeUInt16LE(0, 34);
|
|
1451
|
+
cen.writeUInt16LE(0, 36);
|
|
1452
|
+
cen.writeUInt32LE(0, 38);
|
|
1453
|
+
cen.writeUInt32LE(offset, 42);
|
|
1454
|
+
central.push(Buffer2.concat([cen, nameBuf]));
|
|
1455
|
+
offset += 30 + nameBuf.length + data.length;
|
|
1452
1456
|
}
|
|
1453
|
-
|
|
1457
|
+
const centralBuf = Buffer2.concat(central);
|
|
1458
|
+
const eocd = Buffer2.alloc(22);
|
|
1459
|
+
eocd.writeUInt32LE(101010256, 0);
|
|
1460
|
+
eocd.writeUInt16LE(entries.length, 8);
|
|
1461
|
+
eocd.writeUInt16LE(entries.length, 10);
|
|
1462
|
+
eocd.writeUInt32LE(centralBuf.length, 12);
|
|
1463
|
+
eocd.writeUInt32LE(offset, 16);
|
|
1464
|
+
return Buffer2.concat([...chunks, centralBuf, eocd]);
|
|
1465
|
+
}
|
|
1454
1466
|
|
|
1455
|
-
// src/
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1467
|
+
// src/install/api.ts
|
|
1468
|
+
var NO_DIST = "plugin dist not found\uFF08\u8FD0\u884C npm run build \u751F\u6210\uFF09";
|
|
1469
|
+
function handleInstall(req, res, url) {
|
|
1470
|
+
return dispatchRoute(INSTALL_ROUTES, req, res, url);
|
|
1471
|
+
}
|
|
1472
|
+
function sendBinary(res, buf, contentType) {
|
|
1473
|
+
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "no-store" });
|
|
1474
|
+
res.end(buf);
|
|
1475
|
+
}
|
|
1476
|
+
var PLUGIN_FILE_ROUTES = [
|
|
1477
|
+
{ path: "/code.js", file: "code.js", type: "text/javascript; charset=utf-8" },
|
|
1478
|
+
{ path: "/ui.html", file: "ui.html", type: "text/html; charset=utf-8" }
|
|
1479
|
+
].map(({ path, file, type }) => ({
|
|
1480
|
+
method: "GET",
|
|
1481
|
+
path,
|
|
1482
|
+
handler: async ({ res }) => {
|
|
1483
|
+
const buf = await readPluginFile(file);
|
|
1484
|
+
if (!buf) {
|
|
1485
|
+
sendJson(res, 404, { error: hasPluginDist() ? "not found" : NO_DIST });
|
|
1486
|
+
return;
|
|
1470
1487
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1488
|
+
sendBinary(res, buf, type);
|
|
1489
|
+
}
|
|
1490
|
+
}));
|
|
1491
|
+
var INSTALL_ROUTES = [
|
|
1492
|
+
{
|
|
1493
|
+
method: "GET",
|
|
1494
|
+
path: "/",
|
|
1495
|
+
handler: ({ res }) => {
|
|
1496
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1497
|
+
res.end(renderInstallPage());
|
|
1498
|
+
}
|
|
1499
|
+
},
|
|
1500
|
+
{
|
|
1501
|
+
method: "GET",
|
|
1502
|
+
path: "/manifest.json",
|
|
1503
|
+
handler: async ({ res }) => {
|
|
1504
|
+
const buf = await pluginManifest();
|
|
1505
|
+
if (!buf) {
|
|
1506
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
sendBinary(res, buf, "application/json; charset=utf-8");
|
|
1510
|
+
}
|
|
1511
|
+
},
|
|
1512
|
+
...PLUGIN_FILE_ROUTES,
|
|
1513
|
+
{
|
|
1514
|
+
method: "GET",
|
|
1515
|
+
path: "/plugin.zip",
|
|
1516
|
+
handler: async ({ res }) => {
|
|
1517
|
+
if (!hasPluginDist()) {
|
|
1518
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
try {
|
|
1522
|
+
const [manifest, code, ui] = await Promise.all([
|
|
1523
|
+
pluginManifest(),
|
|
1524
|
+
readPluginFile("code.js"),
|
|
1525
|
+
readPluginFile("ui.html")
|
|
1526
|
+
]);
|
|
1527
|
+
const zip = buildZip([
|
|
1528
|
+
{ name: "manifest.json", data: manifest },
|
|
1529
|
+
{ name: "code.js", data: code },
|
|
1530
|
+
{ name: "ui.html", data: ui }
|
|
1531
|
+
]);
|
|
1532
|
+
res.writeHead(200, {
|
|
1533
|
+
"Content-Type": "application/zip",
|
|
1534
|
+
"Content-Disposition": 'attachment; filename="batter-pixso-bridge.zip"'
|
|
1491
1535
|
});
|
|
1492
|
-
|
|
1536
|
+
res.end(zip);
|
|
1537
|
+
} catch {
|
|
1538
|
+
sendJson(res, 500, { error: "zip build failed" });
|
|
1493
1539
|
}
|
|
1494
1540
|
}
|
|
1495
|
-
await transport2.handleRequest(req, res, body);
|
|
1496
|
-
return true;
|
|
1497
1541
|
}
|
|
1498
|
-
|
|
1499
|
-
if (!transport) {
|
|
1500
|
-
sendJson(res, 400, { error: "missing or unknown MCP session" });
|
|
1501
|
-
return true;
|
|
1502
|
-
}
|
|
1503
|
-
await transport.handleRequest(req, res);
|
|
1504
|
-
return true;
|
|
1505
|
-
}
|
|
1506
|
-
var mcpTransports;
|
|
1507
|
-
var init_mcp_http = __esm({
|
|
1508
|
-
"src/routes/mcp-http.ts"() {
|
|
1509
|
-
"use strict";
|
|
1510
|
-
init_config();
|
|
1511
|
-
init_mcp_local();
|
|
1512
|
-
init_http_utils();
|
|
1513
|
-
mcpTransports = /* @__PURE__ */ new Map();
|
|
1514
|
-
}
|
|
1515
|
-
});
|
|
1542
|
+
];
|
|
1516
1543
|
|
|
1517
|
-
// src/
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
sendJson(res, 400, { error: "invalid json" });
|
|
1528
|
-
}
|
|
1529
|
-
return true;
|
|
1530
|
-
}
|
|
1531
|
-
if (req.method === "POST" && path === "/context") {
|
|
1532
|
-
if (!session) {
|
|
1533
|
-
sendJson(res, 400, { error: "missing session" });
|
|
1534
|
-
return true;
|
|
1544
|
+
// src/plugin/api.ts
|
|
1545
|
+
function handlePluginApi(req, res, url) {
|
|
1546
|
+
return dispatchRoute(PLUGIN_ROUTES, req, res, url);
|
|
1547
|
+
}
|
|
1548
|
+
var PLUGIN_ROUTES = [
|
|
1549
|
+
{
|
|
1550
|
+
method: "POST",
|
|
1551
|
+
path: "/sessions/register",
|
|
1552
|
+
handler: async ({ req, res }) => {
|
|
1553
|
+
sendJson(res, 201, sessions.register(await readJsonBody(req)));
|
|
1535
1554
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
method: "POST",
|
|
1558
|
+
path: "/context",
|
|
1559
|
+
handler: async ({ req, res, url }) => {
|
|
1560
|
+
const session = url.searchParams.get("session");
|
|
1561
|
+
if (!session) {
|
|
1562
|
+
sendJson(res, 400, { error: "missing session" });
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const meta = await readJsonBody(req);
|
|
1539
1566
|
if (!sessions.updateContext(session, meta)) {
|
|
1540
1567
|
sendJson(res, 404, { error: "unknown_session" });
|
|
1541
|
-
return
|
|
1568
|
+
return;
|
|
1542
1569
|
}
|
|
1543
1570
|
sendJson(res, 200, { ok: true });
|
|
1544
|
-
} catch {
|
|
1545
|
-
sendJson(res, 400, { error: "invalid json" });
|
|
1546
1571
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1572
|
+
},
|
|
1573
|
+
{
|
|
1574
|
+
method: "GET",
|
|
1575
|
+
path: "/commands/stream",
|
|
1576
|
+
handler: ({ req, res, url }) => {
|
|
1577
|
+
const session = url.searchParams.get("session");
|
|
1578
|
+
if (!session || !sessions.setConnected(session, true)) {
|
|
1579
|
+
sendJson(res, 404, { error: "unknown_session" });
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
res.writeHead(200, {
|
|
1583
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
1584
|
+
"Cache-Control": "no-store",
|
|
1585
|
+
Connection: "keep-alive"
|
|
1586
|
+
});
|
|
1587
|
+
res.write(": connected\n\n");
|
|
1588
|
+
const unsubscribe = commandBus.subscribe(session, (command) => {
|
|
1589
|
+
res.write(`data: ${JSON.stringify({ command })}
|
|
1562
1590
|
|
|
1563
1591
|
`);
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
return true;
|
|
1576
|
-
}
|
|
1577
|
-
if (req.method === "GET" && path === "/commands/next") {
|
|
1578
|
-
if (!session) {
|
|
1579
|
-
sendJson(res, 400, { error: "missing session" });
|
|
1580
|
-
return true;
|
|
1581
|
-
}
|
|
1582
|
-
if (!sessions.touch(session)) {
|
|
1583
|
-
sendJson(res, 404, { error: "unknown_session" });
|
|
1584
|
-
return true;
|
|
1585
|
-
}
|
|
1586
|
-
try {
|
|
1587
|
-
const command = await commandBus.take(session);
|
|
1588
|
-
if (command === null) {
|
|
1589
|
-
res.writeHead(204);
|
|
1590
|
-
res.end();
|
|
1591
|
-
return true;
|
|
1592
|
-
}
|
|
1593
|
-
sendJson(res, 200, { command });
|
|
1594
|
-
} catch {
|
|
1595
|
-
sendJson(res, 500, { error: "internal error" });
|
|
1592
|
+
});
|
|
1593
|
+
const heartbeat = setInterval(() => {
|
|
1594
|
+
sessions.touchSeen(session);
|
|
1595
|
+
res.write(": ping\n\n");
|
|
1596
|
+
}, 15e3);
|
|
1597
|
+
req.on("close", () => {
|
|
1598
|
+
clearInterval(heartbeat);
|
|
1599
|
+
unsubscribe();
|
|
1600
|
+
sessions.setConnected(session, false);
|
|
1601
|
+
log(`SSE channel closed: session ${session.slice(0, 8)}`);
|
|
1602
|
+
});
|
|
1596
1603
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
const
|
|
1604
|
-
const found = commandBus.complete(
|
|
1604
|
+
},
|
|
1605
|
+
{
|
|
1606
|
+
method: "POST",
|
|
1607
|
+
path: "/commands/:id/result",
|
|
1608
|
+
handler: async ({ req, res, params }) => {
|
|
1609
|
+
const payload = await readJsonBody(req);
|
|
1610
|
+
const result = payload.ok && payload.assets?.length ? substituteAssets(payload.result, payload.assets) : payload.result;
|
|
1611
|
+
const found = commandBus.complete(params["id"], payload.ok, payload.ok ? result : payload.error);
|
|
1605
1612
|
sendJson(res, found ? 200 : 404, found ? { ok: true } : { error: "unknown command" });
|
|
1606
|
-
} catch {
|
|
1607
|
-
sendJson(res, 400, { error: "invalid json" });
|
|
1608
1613
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
method: "POST",
|
|
1617
|
+
path: "/assets",
|
|
1618
|
+
handler: async ({ req, res, url }) => {
|
|
1619
|
+
try {
|
|
1620
|
+
const name = await saveAsset(await readBody(req), url.searchParams.get("format") ?? "PNG");
|
|
1621
|
+
sendJson(res, 201, { name });
|
|
1622
|
+
} catch {
|
|
1623
|
+
sendJson(res, 500, { error: "asset save failed" });
|
|
1624
|
+
}
|
|
1618
1625
|
}
|
|
1619
|
-
return true;
|
|
1620
|
-
}
|
|
1621
|
-
return false;
|
|
1622
|
-
}
|
|
1623
|
-
var init_plugin_api = __esm({
|
|
1624
|
-
"src/routes/plugin-api.ts"() {
|
|
1625
|
-
"use strict";
|
|
1626
|
-
init_assets();
|
|
1627
|
-
init_commands();
|
|
1628
|
-
init_sessions();
|
|
1629
|
-
init_config();
|
|
1630
|
-
init_http_utils();
|
|
1631
1626
|
}
|
|
1632
|
-
|
|
1627
|
+
];
|
|
1633
1628
|
|
|
1634
|
-
// src/
|
|
1635
|
-
|
|
1629
|
+
// src/http.ts
|
|
1630
|
+
var lastActivity = Date.now();
|
|
1636
1631
|
function getLastActivity() {
|
|
1637
1632
|
return lastActivity;
|
|
1638
1633
|
}
|
|
1634
|
+
var ROUTES = [handleInstall, handleMcp, handlePluginApi];
|
|
1639
1635
|
function startHttpServer() {
|
|
1640
1636
|
const server = createServer((req, res) => {
|
|
1641
1637
|
lastActivity = Date.now();
|
|
@@ -1653,23 +1649,19 @@ function startHttpServer() {
|
|
|
1653
1649
|
const all = sessions.list();
|
|
1654
1650
|
sendJson(res, 200, {
|
|
1655
1651
|
ok: true,
|
|
1656
|
-
name:
|
|
1652
|
+
name: HUB_NAME,
|
|
1657
1653
|
pid: process.pid,
|
|
1658
1654
|
version: VERSION,
|
|
1659
|
-
sessions: all.length,
|
|
1660
1655
|
pluginsOnline: all.filter((s) => s.online).length
|
|
1661
1656
|
});
|
|
1662
1657
|
return;
|
|
1663
1658
|
}
|
|
1664
|
-
(async () => {
|
|
1659
|
+
void (async () => {
|
|
1665
1660
|
for (const handler of ROUTES) {
|
|
1666
1661
|
if (await handler(req, res, url)) return;
|
|
1667
1662
|
}
|
|
1668
1663
|
sendJson(res, 404, { error: "not found" });
|
|
1669
|
-
})()
|
|
1670
|
-
log("route error:", err);
|
|
1671
|
-
if (!res.headersSent) sendJson(res, 500, { error: "internal error" });
|
|
1672
|
-
});
|
|
1664
|
+
})();
|
|
1673
1665
|
});
|
|
1674
1666
|
return new Promise((resolve, reject) => {
|
|
1675
1667
|
server.once("error", (err) => {
|
|
@@ -1685,22 +1677,8 @@ function startHttpServer() {
|
|
|
1685
1677
|
});
|
|
1686
1678
|
});
|
|
1687
1679
|
}
|
|
1688
|
-
var lastActivity, ROUTES;
|
|
1689
|
-
var init_server = __esm({
|
|
1690
|
-
"src/server.ts"() {
|
|
1691
|
-
"use strict";
|
|
1692
|
-
init_config();
|
|
1693
|
-
init_sessions();
|
|
1694
|
-
init_http_utils();
|
|
1695
|
-
init_install();
|
|
1696
|
-
init_mcp_http();
|
|
1697
|
-
init_plugin_api();
|
|
1698
|
-
lastActivity = Date.now();
|
|
1699
|
-
ROUTES = [handleInstall, handleMcp, handlePluginApi];
|
|
1700
|
-
}
|
|
1701
|
-
});
|
|
1702
1680
|
|
|
1703
|
-
// src/
|
|
1681
|
+
// src/process/hub.ts
|
|
1704
1682
|
async function runHub() {
|
|
1705
1683
|
const server = await startHttpServer();
|
|
1706
1684
|
if (!server) {
|
|
@@ -1708,46 +1686,46 @@ async function runHub() {
|
|
|
1708
1686
|
process.exit(0);
|
|
1709
1687
|
}
|
|
1710
1688
|
log(`hub ready (idle TTL ${config.hubIdleMs / 6e4}min)`);
|
|
1689
|
+
void cleanOldAssets(config.assetTtlMs);
|
|
1690
|
+
let tick = 0;
|
|
1711
1691
|
const watchdog = setInterval(() => {
|
|
1712
1692
|
const idle = Date.now() - getLastActivity();
|
|
1713
|
-
|
|
1714
|
-
|
|
1693
|
+
const anyPluginOnline = sessions.list().some((s) => s.online);
|
|
1694
|
+
if (idle > config.hubIdleMs && !anyPluginOnline) {
|
|
1695
|
+
log(`\u7A7A\u95F2 ${Math.round(idle / 6e4)} \u5206\u949F\u4E14\u65E0\u5728\u7EBF\u63D2\u4EF6\uFF0Chub \u81EA\u52A8\u9000\u51FA`);
|
|
1715
1696
|
process.exit(0);
|
|
1716
1697
|
}
|
|
1717
|
-
void cleanOldAssets(config.assetTtlMs);
|
|
1698
|
+
if (++tick % 60 === 0) void cleanOldAssets(config.assetTtlMs);
|
|
1718
1699
|
}, 6e4);
|
|
1719
1700
|
watchdog.unref();
|
|
1720
1701
|
}
|
|
1721
|
-
var init_hub_mode = __esm({
|
|
1722
|
-
"src/lifecycle/hub-mode.ts"() {
|
|
1723
|
-
"use strict";
|
|
1724
|
-
init_assets();
|
|
1725
|
-
init_config();
|
|
1726
|
-
init_server();
|
|
1727
|
-
}
|
|
1728
|
-
});
|
|
1729
1702
|
|
|
1730
|
-
// src/
|
|
1703
|
+
// src/process/stdio-proxy.ts
|
|
1704
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1705
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
1706
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1707
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1708
|
+
import { ResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1709
|
+
|
|
1710
|
+
// src/process/hub-process.ts
|
|
1731
1711
|
import { spawn } from "node:child_process";
|
|
1732
|
-
import { openSync } from "node:fs";
|
|
1712
|
+
import { closeSync, openSync } from "node:fs";
|
|
1733
1713
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
1734
|
-
import { join as
|
|
1714
|
+
import { join as join4 } from "node:path";
|
|
1735
1715
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1736
|
-
function hubBaseUrl() {
|
|
1737
|
-
return `http://127.0.0.1:${config.httpPort}`;
|
|
1738
|
-
}
|
|
1739
1716
|
async function probeHub() {
|
|
1740
1717
|
try {
|
|
1741
1718
|
const res = await fetch(`${hubBaseUrl()}/health`, { signal: AbortSignal.timeout(1500) });
|
|
1742
1719
|
if (!res.ok) return "foreign";
|
|
1743
1720
|
const body = await res.json();
|
|
1744
|
-
if (body.ok === true && body.name ===
|
|
1721
|
+
if (body.ok === true && body.name === HUB_NAME) return "alive";
|
|
1745
1722
|
if (body.ok === true) return "legacy";
|
|
1746
1723
|
return "foreign";
|
|
1747
1724
|
} catch {
|
|
1748
1725
|
return "down";
|
|
1749
1726
|
}
|
|
1750
1727
|
}
|
|
1728
|
+
var PORT_FIX_HINT = `\u7528 netstat -ano | findstr :${config.httpPort} \u67E5\u5230 PID \u540E taskkill /PID <pid> /F \u7ED3\u675F\uFF0C\u518D\u91CD\u8BD5`;
|
|
1751
1729
|
function hubStateError(state) {
|
|
1752
1730
|
if (state === "legacy") {
|
|
1753
1731
|
return new Error(
|
|
@@ -1756,6 +1734,7 @@ function hubStateError(state) {
|
|
|
1756
1734
|
}
|
|
1757
1735
|
return new Error(`\u7AEF\u53E3 ${config.httpPort} \u88AB\u5176\u4ED6\u7A0B\u5E8F\u5360\u7528\u3002${PORT_FIX_HINT}\uFF0C\u6216\u4FEE\u6539 HTTP_PORT \u6362\u7AEF\u53E3`);
|
|
1758
1736
|
}
|
|
1737
|
+
var HUB_LOG = join4(tmpdir2(), "batter-pixso-mcp-hub.log");
|
|
1759
1738
|
function spawnHub() {
|
|
1760
1739
|
const out = openSync(HUB_LOG, "a");
|
|
1761
1740
|
const entry = process.argv[1] ?? fileURLToPath2(import.meta.url);
|
|
@@ -1770,6 +1749,7 @@ function spawnHub() {
|
|
|
1770
1749
|
env: { ...process.env }
|
|
1771
1750
|
});
|
|
1772
1751
|
child.unref();
|
|
1752
|
+
closeSync(out);
|
|
1773
1753
|
log(`hub spawned (pid ${child.pid}), log: ${HUB_LOG}`);
|
|
1774
1754
|
}
|
|
1775
1755
|
async function waitForHub(timeoutMs = 15e3) {
|
|
@@ -1782,22 +1762,8 @@ async function waitForHub(timeoutMs = 15e3) {
|
|
|
1782
1762
|
await new Promise((r) => setTimeout(r, 200));
|
|
1783
1763
|
}
|
|
1784
1764
|
}
|
|
1785
|
-
var PORT_FIX_HINT, HUB_LOG;
|
|
1786
|
-
var init_hub_process = __esm({
|
|
1787
|
-
"src/lifecycle/hub-process.ts"() {
|
|
1788
|
-
"use strict";
|
|
1789
|
-
init_config();
|
|
1790
|
-
PORT_FIX_HINT = `\u7528 netstat -ano | findstr :${config.httpPort} \u67E5\u5230 PID \u540E taskkill /PID <pid> /F \u7ED3\u675F\uFF0C\u518D\u91CD\u8BD5`;
|
|
1791
|
-
HUB_LOG = join3(tmpdir2(), "batter-pixso-mcp-hub.log");
|
|
1792
|
-
}
|
|
1793
|
-
});
|
|
1794
1765
|
|
|
1795
|
-
// src/stdio-proxy.ts
|
|
1796
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1797
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
1798
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1799
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1800
|
-
import { ResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1766
|
+
// src/process/stdio-proxy.ts
|
|
1801
1767
|
async function startStdioProxy() {
|
|
1802
1768
|
const mcpUrl = `${hubBaseUrl()}/mcp`;
|
|
1803
1769
|
let upstream = null;
|
|
@@ -1810,13 +1776,7 @@ async function startStdioProxy() {
|
|
|
1810
1776
|
return client;
|
|
1811
1777
|
}
|
|
1812
1778
|
async function ensureUpstream() {
|
|
1813
|
-
if (upstream)
|
|
1814
|
-
try {
|
|
1815
|
-
await upstream.ping();
|
|
1816
|
-
return upstream;
|
|
1817
|
-
} catch {
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1779
|
+
if (upstream) return upstream;
|
|
1820
1780
|
const state = await probeHub();
|
|
1821
1781
|
if (state === "down") spawnHub();
|
|
1822
1782
|
if (state === "legacy" || state === "foreign") throw hubStateError(state);
|
|
@@ -1830,8 +1790,12 @@ async function startStdioProxy() {
|
|
|
1830
1790
|
try {
|
|
1831
1791
|
return await fn(client);
|
|
1832
1792
|
} catch (err) {
|
|
1833
|
-
|
|
1834
|
-
|
|
1793
|
+
const alive = await client.ping().then(
|
|
1794
|
+
() => true,
|
|
1795
|
+
() => false
|
|
1796
|
+
);
|
|
1797
|
+
if (alive) throw err;
|
|
1798
|
+
log("hub \u4F1A\u8BDD\u5931\u8054\uFF0C\u91CD\u5EFA\u540E\u91CD\u8BD5\u2026");
|
|
1835
1799
|
upstream = null;
|
|
1836
1800
|
const rebuilt = await ensureUpstream();
|
|
1837
1801
|
return await fn(rebuilt);
|
|
@@ -1839,7 +1803,7 @@ async function startStdioProxy() {
|
|
|
1839
1803
|
}
|
|
1840
1804
|
const upstreamClient = await ensureUpstream();
|
|
1841
1805
|
const server = new Server(
|
|
1842
|
-
{ name:
|
|
1806
|
+
{ name: HUB_NAME, version: VERSION },
|
|
1843
1807
|
{
|
|
1844
1808
|
capabilities: upstreamClient.getServerCapabilities() ?? {}
|
|
1845
1809
|
}
|
|
@@ -1852,19 +1816,8 @@ async function startStdioProxy() {
|
|
|
1852
1816
|
await server.connect(new StdioServerTransport());
|
|
1853
1817
|
log("proxy ready on stdio");
|
|
1854
1818
|
}
|
|
1855
|
-
var init_stdio_proxy = __esm({
|
|
1856
|
-
"src/stdio-proxy.ts"() {
|
|
1857
|
-
"use strict";
|
|
1858
|
-
init_config();
|
|
1859
|
-
init_hub_process();
|
|
1860
|
-
}
|
|
1861
|
-
});
|
|
1862
1819
|
|
|
1863
|
-
// src/main.ts
|
|
1864
|
-
var main_exports = {};
|
|
1865
|
-
__export(main_exports, {
|
|
1866
|
-
main: () => main
|
|
1867
|
-
});
|
|
1820
|
+
// src/process/main.ts
|
|
1868
1821
|
async function main() {
|
|
1869
1822
|
if (process.argv.includes("--hub")) {
|
|
1870
1823
|
await runHub();
|
|
@@ -1872,26 +1825,9 @@ async function main() {
|
|
|
1872
1825
|
}
|
|
1873
1826
|
await startStdioProxy();
|
|
1874
1827
|
}
|
|
1875
|
-
var init_main = __esm({
|
|
1876
|
-
"src/main.ts"() {
|
|
1877
|
-
"use strict";
|
|
1878
|
-
init_hub_mode();
|
|
1879
|
-
init_stdio_proxy();
|
|
1880
|
-
}
|
|
1881
|
-
});
|
|
1882
1828
|
|
|
1883
|
-
// src/index.ts
|
|
1884
|
-
|
|
1885
|
-
if (major < 18) {
|
|
1886
|
-
console.error(
|
|
1887
|
-
`[batter-pixso-mcp] \u9700\u8981 Node \u2265 18\uFF08\u5F53\u524D ${process.version}\uFF09\u3002
|
|
1888
|
-
\u7CFB\u7EDF Node \u8FC7\u65E7\u65F6\u53EF\u7528 Volta \u6307\u5B9A\u8FD0\u884C\u65F6\uFF1A
|
|
1889
|
-
MCP \u914D\u7F6E: { "command": "volta", "args": ["run", "--node", "24", "npx", "-y", "batter-pixso-mcp"] }`
|
|
1890
|
-
);
|
|
1891
|
-
process.exit(1);
|
|
1892
|
-
}
|
|
1893
|
-
var { main: main2 } = await Promise.resolve().then(() => (init_main(), main_exports));
|
|
1894
|
-
main2().catch((err) => {
|
|
1829
|
+
// src/process/index.ts
|
|
1830
|
+
main().catch((err) => {
|
|
1895
1831
|
console.error("[batter-pixso-mcp] fatal:", err);
|
|
1896
1832
|
process.exit(1);
|
|
1897
1833
|
});
|