batter-pixso-mcp 0.3.0 → 0.4.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 +1515 -1582
- package/dist/plugin/code.js +251 -129
- package/dist/plugin/ui.html +51 -49
- package/package.json +4 -4
- package/standalone/index.js +97 -99
- package/standalone/plugin/code.js +251 -129
- 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.4.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\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
|
|
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,6 +383,8 @@ 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
|
|
@@ -977,665 +392,1243 @@ DSL \u662F\u5355\u884C\u7D27\u51D1 JSON\uFF1A\u77ED key\u3001\u4F4D\u7F6E\u6570\
|
|
|
977
392
|
}
|
|
978
393
|
\`\`\`
|
|
979
394
|
|
|
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
|
|
395
|
+
**\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
|
|
396
|
+
\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
|
|
397
|
+
\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
|
|
398
|
+
|
|
399
|
+
**\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
|
|
400
|
+
\`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
|
|
401
|
+
\`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
|
|
402
|
+
**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
|
|
403
|
+
|
|
404
|
+
## \u9519\u8BEF\u6062\u590D
|
|
405
|
+
|
|
406
|
+
- **\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
|
|
407
|
+
- **\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
|
|
408
|
+
- **\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
|
|
409
|
+
- **\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
|
|
410
|
+
- **\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
|
|
411
|
+
|
|
412
|
+
## \u7EC4\u4EF6\u5173\u8054\uFF08comp \u5B57\u6BB5\uFF09
|
|
413
|
+
|
|
414
|
+
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
|
|
415
|
+
- \`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
|
|
416
|
+
- \`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
|
|
417
|
+
- \`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
|
|
418
|
+
- \`comp.set\`\uFF1A\u672C\u8282\u70B9\u662F variant \u7EC4\uFF08COMPONENT_SET\uFF09\uFF0C\u5176\u5B50\u8282\u70B9\u5404\u81EA\u5E26 \`comp.key\`\u3002
|
|
419
|
+
\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
|
|
420
|
+
|
|
421
|
+
## \u53F6\u5B50\u8282\u70B9\u4E0E\u56FE\u7247\u8D44\u6E90
|
|
422
|
+
|
|
423
|
+
- \`type: "ICON"\`\uFF1A\u7EAF\u77E2\u91CF\u7EC4\u4EF6\u5B9E\u4F8B\uFF08icon\uFF09\uFF0C\`img\` \u4E3A **SVG** \u6587\u4EF6\u540D\u3002
|
|
424
|
+
- \`type: "IMAGE"\`\uFF1A\u56FE\u7247\u586B\u5145\u8282\u70B9\uFF0C\`img\` \u4E3A **PNG** \u6587\u4EF6\u540D\u3002
|
|
425
|
+
- \u6587\u4EF6\u540D\u62FC\u8DEF\u5F84\u4E0E\u53D6\u7528\u8981\u6C42\u89C1\u5F00\u5934\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\u3002
|
|
426
|
+
- \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
|
|
427
|
+
- **\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
|
|
428
|
+
\`export_image\` \u4EC5\u7528\u4E8E DSL \u4E2D\u672A\u584C\u7F29\u7684\u8282\u70B9\uFF08\u4E3B\u52A8\u5207\u56FE\uFF09\u3002
|
|
429
|
+
|
|
430
|
+
## \u6CE8\u610F
|
|
431
|
+
|
|
432
|
+
- 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
|
|
433
|
+
- \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
|
|
434
|
+
\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\u4F46\u7ED3\u679C\u5BF9\u4E0D\u4E0A\u65F6\uFF0C\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\u3002
|
|
435
|
+
- \u591A\u6587\u4EF6\u573A\u666F\u6240\u6709\u5DE5\u5177\u90FD\u652F\u6301 \`target\` \u53C2\u6570\uFF08sessionId/fileKey\uFF09\uFF0C\u89C1 \`batter://guides/intro\`\u3002
|
|
436
|
+
`;
|
|
437
|
+
var GUIDES = [
|
|
438
|
+
{
|
|
439
|
+
name: "intro",
|
|
440
|
+
uri: "batter://guides/intro",
|
|
441
|
+
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",
|
|
442
|
+
text: INTRO
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: "design-to-code",
|
|
446
|
+
uri: "batter://guides/design-to-code",
|
|
447
|
+
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",
|
|
448
|
+
text: DESIGN_TO_CODE
|
|
449
|
+
}
|
|
450
|
+
];
|
|
451
|
+
|
|
452
|
+
// src/agent/prompts.ts
|
|
453
|
+
var DESIGN_TO_CODE2 = {
|
|
454
|
+
name: "design-to-code",
|
|
455
|
+
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",
|
|
456
|
+
arguments: [
|
|
457
|
+
{
|
|
458
|
+
name: "url",
|
|
459
|
+
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",
|
|
460
|
+
required: false
|
|
461
|
+
}
|
|
462
|
+
],
|
|
463
|
+
render: (args) => {
|
|
464
|
+
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`;
|
|
465
|
+
return [
|
|
466
|
+
{
|
|
467
|
+
role: "user",
|
|
468
|
+
content: {
|
|
469
|
+
type: "text",
|
|
470
|
+
text: [
|
|
471
|
+
`\u628A Pixso \u8BBE\u8BA1\u7A3F\u8FD8\u539F\u4E3A\u524D\u7AEF\u4EE3\u7801\u3002`,
|
|
472
|
+
``,
|
|
473
|
+
`## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011`,
|
|
474
|
+
``,
|
|
475
|
+
`**\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`,
|
|
476
|
+
``,
|
|
477
|
+
`## \u6267\u884C\u6D41\u7A0B`,
|
|
478
|
+
``,
|
|
479
|
+
`1. \u786E\u8BA4\u76EE\u6807\u8282\u70B9\uFF1A${targetLine}`,
|
|
480
|
+
`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`,
|
|
481
|
+
`3. \u751F\u6210\u4EE3\u7801\uFF1A\u6309\u6307\u5357\u4E2D\u7684 DSL \u5B57\u6BB5\u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002`,
|
|
482
|
+
`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`,
|
|
483
|
+
``,
|
|
484
|
+
`**\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`
|
|
485
|
+
].join("\n")
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
];
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
var PROMPTS = [DESIGN_TO_CODE2];
|
|
492
|
+
|
|
493
|
+
// src/plugin/command-bus.ts
|
|
494
|
+
var CommandBus = class {
|
|
495
|
+
sseListeners = /* @__PURE__ */ new Map();
|
|
496
|
+
pendingById = /* @__PURE__ */ new Map();
|
|
497
|
+
seq = 0;
|
|
498
|
+
/** AI 侧:执行命令 */
|
|
499
|
+
execute(type, args, opts) {
|
|
500
|
+
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
501
|
+
const { promise, resolve, reject } = withResolvers();
|
|
502
|
+
const command = { id: `cmd-${++this.seq}`, type, args };
|
|
503
|
+
const entry = {
|
|
504
|
+
command,
|
|
505
|
+
sessionId: opts.sessionId,
|
|
506
|
+
resolve,
|
|
507
|
+
reject,
|
|
508
|
+
dispatched: false,
|
|
509
|
+
timer: setTimeout(() => {
|
|
510
|
+
this.drop(entry);
|
|
511
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u8D85\u65F6\uFF1A\u63D2\u4EF6 ${timeoutMs / 1e3}s \u5185\u672A\u8FD4\u56DE\u7ED3\u679C`));
|
|
512
|
+
}, timeoutMs)
|
|
513
|
+
};
|
|
514
|
+
opts.signal?.addEventListener("abort", () => {
|
|
515
|
+
if (!this.pendingById.has(command.id)) return;
|
|
516
|
+
this.drop(entry);
|
|
517
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u5DF2\u88AB\u8C03\u7528\u65B9\u53D6\u6D88`));
|
|
518
|
+
});
|
|
519
|
+
this.pendingById.set(command.id, entry);
|
|
520
|
+
this.dispatch(opts.sessionId);
|
|
521
|
+
return promise;
|
|
522
|
+
}
|
|
523
|
+
/** 插件侧:订阅即时推送(SSE 通道),返回退订函数;补发订阅前已入队的命令 */
|
|
524
|
+
subscribe(sessionId, listener) {
|
|
525
|
+
let subs = this.sseListeners.get(sessionId);
|
|
526
|
+
if (!subs) {
|
|
527
|
+
subs = /* @__PURE__ */ new Set();
|
|
528
|
+
this.sseListeners.set(sessionId, subs);
|
|
529
|
+
}
|
|
530
|
+
subs.add(listener);
|
|
531
|
+
this.dispatch(sessionId);
|
|
532
|
+
return () => {
|
|
533
|
+
subs.delete(listener);
|
|
534
|
+
if (subs.size === 0) this.sseListeners.delete(sessionId);
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
/** 插件侧:回传结果 */
|
|
538
|
+
complete(id, ok, resultOrError) {
|
|
539
|
+
const entry = this.pendingById.get(id);
|
|
540
|
+
if (!entry) return false;
|
|
541
|
+
this.drop(entry);
|
|
542
|
+
if (ok) {
|
|
543
|
+
entry.resolve(resultOrError);
|
|
544
|
+
} else {
|
|
545
|
+
entry.reject(resultOrError instanceof Error ? resultOrError : new Error(String(resultOrError)));
|
|
546
|
+
}
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
/** 排空该会话全部未派发命令(无订阅者则留待 subscribe 补发) */
|
|
550
|
+
dispatch(sessionId) {
|
|
551
|
+
const subs = this.sseListeners.get(sessionId);
|
|
552
|
+
if (!subs || subs.size === 0) return;
|
|
553
|
+
for (const entry of this.pendingById.values()) {
|
|
554
|
+
if (entry.sessionId !== sessionId || entry.dispatched) continue;
|
|
555
|
+
entry.dispatched = true;
|
|
556
|
+
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)}`);
|
|
557
|
+
for (const fn of subs) fn(entry.command);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/** 从 pending 表移除(完成/超时/取消共用) */
|
|
561
|
+
drop(entry) {
|
|
562
|
+
clearTimeout(entry.timer);
|
|
563
|
+
this.pendingById.delete(entry.command.id);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
var commandBus = new CommandBus();
|
|
567
|
+
|
|
568
|
+
// src/agent/tools/shared.ts
|
|
569
|
+
var PING_TIMEOUT_MS = 800;
|
|
570
|
+
async function run(type, args, signal) {
|
|
571
|
+
const { target, ...rest } = args;
|
|
572
|
+
try {
|
|
573
|
+
const session = await resolveAndPing(target, signal);
|
|
574
|
+
const data = await commandBus.execute(type, rest, { sessionId: session.id, signal });
|
|
575
|
+
sessions.touch(session.id);
|
|
576
|
+
return jsonResult({ sessionId: session.id, data });
|
|
577
|
+
} catch (err) {
|
|
578
|
+
return errorResult(err);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async function pingSession(sessionId, signal) {
|
|
582
|
+
try {
|
|
583
|
+
await commandBus.execute("ping", {}, { sessionId, timeoutMs: PING_TIMEOUT_MS, signal });
|
|
584
|
+
return true;
|
|
585
|
+
} catch (err) {
|
|
586
|
+
if (signal?.aborted) throw err;
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
async function pingCandidates(candidates, affinityId, signal) {
|
|
591
|
+
const check = async (c) => {
|
|
592
|
+
if (await pingSession(c.id, signal)) return c;
|
|
593
|
+
throw new Error("no pong");
|
|
594
|
+
};
|
|
595
|
+
const affinity = candidates.find((c) => c.id === affinityId);
|
|
596
|
+
try {
|
|
597
|
+
if (affinity && await pingSession(affinity.id, signal)) return affinity;
|
|
598
|
+
const rest = affinity ? candidates.filter((c) => c !== affinity) : candidates;
|
|
599
|
+
return await Promise.any(rest.map(check));
|
|
600
|
+
} catch {
|
|
601
|
+
if (signal?.aborted) throw new Error("\u547D\u4EE4\u5DF2\u88AB\u8C03\u7528\u65B9\u53D6\u6D88");
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async function resolveAndPing(target, signal) {
|
|
606
|
+
const s = target ? sessions.get(target) : sessions.resolve();
|
|
607
|
+
const fileKey = s ? s.meta.fileKey : target;
|
|
608
|
+
const candidates = s && !fileKey ? [s] : sessions.candidatesForFileKey(fileKey);
|
|
609
|
+
if (candidates.length === 0) {
|
|
610
|
+
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`);
|
|
611
|
+
throw unknownTargetError(target);
|
|
612
|
+
}
|
|
613
|
+
const winner = await pingCandidates(candidates, fileKey ? sessions.affinityIdOf(fileKey) : void 0, signal);
|
|
614
|
+
if (!winner) {
|
|
615
|
+
for (const c of candidates) sessions.evict(c.id);
|
|
616
|
+
throw new Error(
|
|
617
|
+
`\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`
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
if (fileKey) sessions.pinAffinity(fileKey, winner.id);
|
|
621
|
+
return winner;
|
|
622
|
+
}
|
|
623
|
+
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";
|
|
624
|
+
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";
|
|
625
|
+
var TARGET_PROP = {
|
|
626
|
+
type: "string",
|
|
627
|
+
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"
|
|
628
|
+
};
|
|
629
|
+
var CACHE_PROP = {
|
|
630
|
+
type: "string",
|
|
631
|
+
enum: ["first", "refresh"],
|
|
632
|
+
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"
|
|
633
|
+
};
|
|
634
|
+
var GUID_PROP = {
|
|
635
|
+
type: "string",
|
|
636
|
+
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'
|
|
637
|
+
};
|
|
638
|
+
function jsonResult(data) {
|
|
639
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
640
|
+
}
|
|
641
|
+
function errorResult(err) {
|
|
642
|
+
return {
|
|
643
|
+
content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
|
|
644
|
+
isError: true
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// src/agent/tools/export_image.ts
|
|
649
|
+
var exportImage = {
|
|
650
|
+
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,
|
|
651
|
+
inputSchema: {
|
|
652
|
+
type: "object",
|
|
653
|
+
properties: {
|
|
654
|
+
guid: GUID_PROP,
|
|
655
|
+
guids: {
|
|
656
|
+
type: "array",
|
|
657
|
+
items: { type: "string" },
|
|
658
|
+
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"
|
|
659
|
+
},
|
|
660
|
+
format: {
|
|
661
|
+
type: "string",
|
|
662
|
+
enum: ["PNG", "SVG", "JPG"],
|
|
663
|
+
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"
|
|
664
|
+
},
|
|
665
|
+
scale: {
|
|
666
|
+
type: "number",
|
|
667
|
+
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"
|
|
668
|
+
},
|
|
669
|
+
cache: CACHE_PROP,
|
|
670
|
+
target: TARGET_PROP
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
// src/agent/tools/get_metadata.ts
|
|
676
|
+
var getMetadata = {
|
|
677
|
+
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,
|
|
678
|
+
inputSchema: {
|
|
679
|
+
type: "object",
|
|
680
|
+
properties: {
|
|
681
|
+
guid: GUID_PROP,
|
|
682
|
+
cache: CACHE_PROP,
|
|
683
|
+
target: TARGET_PROP
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
// src/agent/tools/get_node_dsl.ts
|
|
689
|
+
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';
|
|
690
|
+
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\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';
|
|
691
|
+
var getNodeDsl = {
|
|
692
|
+
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,
|
|
693
|
+
inputSchema: {
|
|
694
|
+
type: "object",
|
|
695
|
+
properties: {
|
|
696
|
+
guid: GUID_PROP,
|
|
697
|
+
maxDepth: {
|
|
698
|
+
type: "number",
|
|
699
|
+
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"
|
|
700
|
+
},
|
|
701
|
+
cache: CACHE_PROP,
|
|
702
|
+
target: TARGET_PROP
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
// src/agent/tools/get_screenshot.ts
|
|
708
|
+
var getScreenshot = {
|
|
709
|
+
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,
|
|
710
|
+
inputSchema: {
|
|
711
|
+
type: "object",
|
|
712
|
+
properties: {
|
|
713
|
+
guid: GUID_PROP,
|
|
714
|
+
format: {
|
|
715
|
+
type: "string",
|
|
716
|
+
enum: ["PNG", "SVG"],
|
|
717
|
+
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"
|
|
718
|
+
},
|
|
719
|
+
scale: {
|
|
720
|
+
type: "number",
|
|
721
|
+
description: "\u5BFC\u51FA\u500D\u7387\uFF0C\u9ED8\u8BA4 2\u3002"
|
|
722
|
+
},
|
|
723
|
+
cache: CACHE_PROP,
|
|
724
|
+
target: TARGET_PROP
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// src/agent/tools/get_selected.ts
|
|
730
|
+
var getSelected = {
|
|
731
|
+
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,
|
|
732
|
+
inputSchema: { type: "object", properties: { target: TARGET_PROP } }
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// src/agent/tools/list_sessions.ts
|
|
736
|
+
var listSessions = {
|
|
737
|
+
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",
|
|
738
|
+
inputSchema: { type: "object", properties: {} },
|
|
739
|
+
handler: async () => {
|
|
740
|
+
const list = sessions.list();
|
|
741
|
+
if (!list.length) {
|
|
742
|
+
return {
|
|
743
|
+
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" }]
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
const checks = await Promise.all(
|
|
747
|
+
list.map(async (info) => info.online && await pingSession(info.sessionId) ? info : null)
|
|
748
|
+
);
|
|
749
|
+
const alive = checks.filter((c) => c !== null);
|
|
750
|
+
for (const info of list) {
|
|
751
|
+
if (!alive.includes(info)) sessions.evict(info.sessionId);
|
|
752
|
+
}
|
|
753
|
+
if (!alive.length) {
|
|
754
|
+
return {
|
|
755
|
+
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" }]
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
return jsonResult(
|
|
759
|
+
alive.map(({ sessionId, fileKey, fileName, pageName, selectionCount }) => ({
|
|
760
|
+
sessionId,
|
|
761
|
+
fileKey,
|
|
762
|
+
fileName,
|
|
763
|
+
pageName,
|
|
764
|
+
selectionCount
|
|
765
|
+
}))
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// src/agent/tools/index.ts
|
|
771
|
+
var TOOL_REGISTRY = {
|
|
772
|
+
get_selected: getSelected,
|
|
773
|
+
get_node_dsl: getNodeDsl,
|
|
774
|
+
get_metadata: getMetadata,
|
|
775
|
+
export_image: exportImage,
|
|
776
|
+
get_screenshot: getScreenshot,
|
|
777
|
+
list_sessions: listSessions
|
|
778
|
+
};
|
|
779
|
+
var TOOLS = Object.fromEntries(
|
|
780
|
+
Object.entries(TOOL_REGISTRY).map(([name, def]) => {
|
|
781
|
+
const handler = def.handler ?? ((args, ctx) => run(name, args, ctx?.signal));
|
|
782
|
+
const resolvedDef = { description: def.description, inputSchema: def.inputSchema, handler };
|
|
783
|
+
return [name, { def: resolvedDef, tool: { name, description: def.description, inputSchema: def.inputSchema } }];
|
|
784
|
+
})
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
// src/agent/mcp.ts
|
|
788
|
+
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`;
|
|
789
|
+
function createMcpServer() {
|
|
790
|
+
const server = new McpServer(
|
|
791
|
+
{ name: HUB_NAME, version: VERSION },
|
|
792
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} }, instructions: INSTRUCTIONS }
|
|
793
|
+
);
|
|
794
|
+
for (const guide of GUIDES) {
|
|
795
|
+
server.registerResource(
|
|
796
|
+
guide.name,
|
|
797
|
+
guide.uri,
|
|
798
|
+
{
|
|
799
|
+
description: guide.description,
|
|
800
|
+
mimeType: "text/markdown",
|
|
801
|
+
// 纯 agent 消费的工作流文档,高优先级提示读取
|
|
802
|
+
annotations: { audience: ["assistant"], priority: 0.9 }
|
|
803
|
+
},
|
|
804
|
+
async (uri) => ({
|
|
805
|
+
contents: [{ uri: uri.href, mimeType: "text/markdown", text: guide.text }]
|
|
806
|
+
})
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
server.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
810
|
+
resourceTemplates: []
|
|
811
|
+
}));
|
|
812
|
+
server.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
813
|
+
tools: Object.values(TOOLS).map((t) => t.tool)
|
|
814
|
+
}));
|
|
815
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
816
|
+
const { name, arguments: args = {} } = request.params;
|
|
817
|
+
const entry = TOOLS[name];
|
|
818
|
+
if (!entry) {
|
|
819
|
+
throw new Error(`unknown tool: ${name}`);
|
|
820
|
+
}
|
|
821
|
+
return entry.def.handler(args, { signal: extra.signal });
|
|
822
|
+
});
|
|
823
|
+
server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
824
|
+
prompts: PROMPTS.map((p) => ({
|
|
825
|
+
name: p.name,
|
|
826
|
+
description: p.description,
|
|
827
|
+
arguments: p.arguments
|
|
828
|
+
}))
|
|
829
|
+
}));
|
|
830
|
+
server.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
831
|
+
const entry = PROMPTS.find((p) => p.name === request.params.name);
|
|
832
|
+
if (!entry) {
|
|
833
|
+
throw new Error(`unknown prompt: ${request.params.name}`);
|
|
834
|
+
}
|
|
835
|
+
const args = request.params.arguments ?? {};
|
|
836
|
+
return { description: entry.description, messages: entry.render(args) };
|
|
837
|
+
});
|
|
838
|
+
return server;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// src/agent/sessions.ts
|
|
842
|
+
var transports = /* @__PURE__ */ new Map();
|
|
843
|
+
function getMcpTransport(sessionId) {
|
|
844
|
+
return transports.get(sessionId);
|
|
845
|
+
}
|
|
846
|
+
function registerMcpTransport(sessionId, transport) {
|
|
847
|
+
transports.set(sessionId, transport);
|
|
848
|
+
log(`MCP HTTP session initialized: ${sessionId.slice(0, 8)} (total ${transports.size})`);
|
|
849
|
+
transport.onclose = () => {
|
|
850
|
+
if (transport.sessionId) transports.delete(transport.sessionId);
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// src/agent/http.ts
|
|
855
|
+
function handleMcp(req, res, url) {
|
|
856
|
+
return dispatchRoute(MCP_ROUTES, req, res, url);
|
|
857
|
+
}
|
|
858
|
+
async function sessionRequestHandler({ req, res }) {
|
|
859
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
860
|
+
const transport = sessionId ? getMcpTransport(sessionId) : void 0;
|
|
861
|
+
if (!transport) {
|
|
862
|
+
sendJson(res, 400, { error: "missing or unknown MCP session" });
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
await transport.handleRequest(req, res);
|
|
866
|
+
}
|
|
867
|
+
var MCP_ROUTES = [
|
|
868
|
+
{
|
|
869
|
+
method: "POST",
|
|
870
|
+
path: "/mcp",
|
|
871
|
+
handler: async ({ req, res }) => {
|
|
872
|
+
let body;
|
|
873
|
+
try {
|
|
874
|
+
body = await readJsonBody(req);
|
|
875
|
+
} catch {
|
|
876
|
+
sendJson(res, 400, { jsonrpc: "2.0", error: { code: -32700, message: "Parse error" }, id: null });
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
880
|
+
let transport = sessionId ? getMcpTransport(sessionId) : void 0;
|
|
881
|
+
if (!transport) {
|
|
882
|
+
if (!sessionId && isInitializeRequest(body)) {
|
|
883
|
+
const t = new StreamableHTTPServerTransport({
|
|
884
|
+
sessionIdGenerator: () => randomUUID2(),
|
|
885
|
+
onsessioninitialized: (id) => registerMcpTransport(id, t)
|
|
886
|
+
});
|
|
887
|
+
await createMcpServer().connect(t);
|
|
888
|
+
transport = t;
|
|
889
|
+
} else {
|
|
890
|
+
sendJson(res, 400, {
|
|
891
|
+
jsonrpc: "2.0",
|
|
892
|
+
error: { code: -32e3, message: "Bad Request: missing or unknown MCP session" },
|
|
893
|
+
id: null
|
|
894
|
+
});
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
await transport.handleRequest(req, res, body);
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
{ method: "GET", path: "/mcp", handler: sessionRequestHandler },
|
|
902
|
+
{ method: "DELETE", path: "/mcp", handler: sessionRequestHandler }
|
|
903
|
+
];
|
|
904
|
+
|
|
905
|
+
// src/install/dist-resolver.ts
|
|
906
|
+
import { existsSync } from "node:fs";
|
|
907
|
+
import { readFile } from "node:fs/promises";
|
|
908
|
+
import { join as join3 } from "node:path";
|
|
909
|
+
import { fileURLToPath } from "node:url";
|
|
910
|
+
function resolvePluginDir() {
|
|
911
|
+
const candidates = [
|
|
912
|
+
fileURLToPath(new URL("./plugin/", import.meta.url)),
|
|
913
|
+
// bundle: dist/index.js → dist/plugin/
|
|
914
|
+
fileURLToPath(new URL("../../plugin/dist/", import.meta.url)),
|
|
915
|
+
// bundle 开发: dist/index.js → packages/plugin/dist/
|
|
916
|
+
fileURLToPath(new URL("../../../plugin/dist/", import.meta.url))
|
|
917
|
+
// tsx 开发: src/install/ → packages/plugin/dist/
|
|
918
|
+
];
|
|
919
|
+
return candidates.find((p) => existsSync(join3(p, "manifest.json"))) ?? null;
|
|
920
|
+
}
|
|
921
|
+
var resolved = null;
|
|
922
|
+
function pluginDir() {
|
|
923
|
+
resolved ??= resolvePluginDir();
|
|
924
|
+
return resolved;
|
|
925
|
+
}
|
|
926
|
+
function hasPluginDist() {
|
|
927
|
+
return pluginDir() !== null;
|
|
928
|
+
}
|
|
929
|
+
async function pluginManifest() {
|
|
930
|
+
const dir = pluginDir();
|
|
931
|
+
if (!dir) return null;
|
|
932
|
+
const raw = JSON.parse(await readFile(join3(dir, "manifest.json"), "utf8"));
|
|
933
|
+
raw.networkAccess = {
|
|
934
|
+
allowedDomains: [`http://localhost:${config.httpPort}`, `http://127.0.0.1:${config.httpPort}`]
|
|
935
|
+
};
|
|
936
|
+
return Buffer.from(JSON.stringify(raw, null, 2));
|
|
937
|
+
}
|
|
938
|
+
async function readPluginFile(name) {
|
|
939
|
+
const dir = pluginDir();
|
|
940
|
+
if (!dir) return null;
|
|
941
|
+
try {
|
|
942
|
+
return await readFile(join3(dir, name));
|
|
943
|
+
} catch {
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/install/template.ts
|
|
949
|
+
var HTML_ESCAPES = {
|
|
950
|
+
"&": "&",
|
|
951
|
+
"<": "<",
|
|
952
|
+
">": ">",
|
|
953
|
+
'"': """,
|
|
954
|
+
"'": "'"
|
|
955
|
+
};
|
|
956
|
+
function escapeHtml(s) {
|
|
957
|
+
return s.replace(/[&<>"']/g, (c) => HTML_ESCAPES[c]);
|
|
958
|
+
}
|
|
959
|
+
function escapeJsString(s) {
|
|
960
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
961
|
+
}
|
|
962
|
+
function renderTemplate(tpl, vars, escape = escapeHtml) {
|
|
963
|
+
const lookup = (key) => {
|
|
964
|
+
const v = vars[key];
|
|
965
|
+
if (v === void 0) throw new Error(`\u6A21\u677F\u53D8\u91CF\u7F3A\u5931\uFF1A{{${key}}}`);
|
|
966
|
+
return String(v);
|
|
967
|
+
};
|
|
968
|
+
return tpl.replace(/\{\{\{\s*(\w+)\s*\}\}\}/g, (_, key) => lookup(key)).replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => escape(lookup(key)));
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/install/templates/bookmarklet.txt
|
|
972
|
+
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';
|
|
973
|
+
|
|
974
|
+
// src/install/templates/install-page.html
|
|
975
|
+
var install_page_default = `<!DOCTYPE html>
|
|
976
|
+
<html lang="zh-CN">
|
|
977
|
+
<head>
|
|
978
|
+
<meta charset="utf-8">
|
|
979
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
980
|
+
<title>Batter Pixso Bridge \xB7 \u5B89\u88C5</title>
|
|
981
|
+
<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">
|
|
982
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
983
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
984
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
985
|
+
<style>
|
|
986
|
+
:root {
|
|
987
|
+
--ink: #1e1e1e;
|
|
988
|
+
--sub: #6b6b6b;
|
|
989
|
+
--faint: #a3a3a3;
|
|
990
|
+
--line: #e8e8ea;
|
|
991
|
+
--panel: #f7f7f8;
|
|
992
|
+
--iris: #575bf2;
|
|
993
|
+
--iris-strong: #4346e0;
|
|
994
|
+
--iris-soft: #eef0fe;
|
|
995
|
+
--ok: #14ae5c;
|
|
996
|
+
--err: #f24822;
|
|
997
|
+
--radius: 6px;
|
|
998
|
+
--mono: ui-monospace, "SF Mono", Menlo, Consolas, "Cascadia Mono", monospace;
|
|
999
|
+
}
|
|
1000
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1001
|
+
html { -webkit-text-size-adjust: 100%; }
|
|
1002
|
+
body {
|
|
1003
|
+
background: #fff;
|
|
1004
|
+
color: var(--ink);
|
|
1005
|
+
font-family: Inter, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
|
1006
|
+
font-feature-settings: "tnum" 1, "cv05" 1;
|
|
1007
|
+
line-height: 1.5;
|
|
1008
|
+
min-height: 100vh;
|
|
1009
|
+
}
|
|
1010
|
+
::selection { background: var(--iris-soft); }
|
|
1011
|
+
.page {
|
|
1012
|
+
max-width: 680px;
|
|
1013
|
+
margin: 0 auto;
|
|
1014
|
+
padding: clamp(48px, 10vh, 96px) 24px 64px;
|
|
1015
|
+
animation: enter 420ms cubic-bezier(0.2, 0.6, 0.2, 1) both;
|
|
1016
|
+
}
|
|
1017
|
+
@keyframes enter {
|
|
1018
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
1019
|
+
to { opacity: 1; transform: none; }
|
|
1020
|
+
}
|
|
1021
|
+
a { color: var(--iris); text-decoration: none; }
|
|
1022
|
+
a:focus-visible, button:focus-visible {
|
|
1023
|
+
outline: 2px solid var(--iris);
|
|
1024
|
+
outline-offset: 2px;
|
|
1025
|
+
border-radius: 2px;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* \u2500\u2500 \u9876\u680F\uFF1A\u54C1\u724C + \u670D\u52A1\u72B6\u6001 \u2500\u2500 */
|
|
1029
|
+
.top {
|
|
1030
|
+
display: flex;
|
|
1031
|
+
align-items: center;
|
|
1032
|
+
justify-content: space-between;
|
|
1033
|
+
gap: 16px;
|
|
1034
|
+
margin-bottom: clamp(40px, 7vh, 72px);
|
|
1035
|
+
}
|
|
1036
|
+
.brand {
|
|
1037
|
+
display: flex;
|
|
1038
|
+
align-items: center;
|
|
1039
|
+
gap: 10px;
|
|
1040
|
+
font-size: 13px;
|
|
1041
|
+
font-weight: 600;
|
|
1042
|
+
letter-spacing: -0.01em;
|
|
1043
|
+
}
|
|
1044
|
+
.brand svg { display: block; }
|
|
1045
|
+
.brand .ver {
|
|
1046
|
+
font-family: var(--mono);
|
|
1047
|
+
font-weight: 400;
|
|
1048
|
+
font-size: 11px;
|
|
1049
|
+
color: var(--faint);
|
|
1050
|
+
margin-left: 2px;
|
|
1051
|
+
}
|
|
1052
|
+
.status {
|
|
1053
|
+
display: inline-flex;
|
|
1054
|
+
align-items: center;
|
|
1055
|
+
gap: 7px;
|
|
1056
|
+
padding: 5px 10px;
|
|
1057
|
+
border-radius: 999px;
|
|
1058
|
+
background: var(--panel);
|
|
1059
|
+
border: 1px solid var(--line);
|
|
1060
|
+
font-size: 11px;
|
|
1061
|
+
color: var(--sub);
|
|
1062
|
+
white-space: nowrap;
|
|
1063
|
+
}
|
|
1064
|
+
.status .dot {
|
|
1065
|
+
width: 6px;
|
|
1066
|
+
height: 6px;
|
|
1067
|
+
border-radius: 50%;
|
|
1068
|
+
background: var(--faint);
|
|
1069
|
+
flex-shrink: 0;
|
|
1070
|
+
animation: pulse 2.2s ease-in-out infinite;
|
|
1071
|
+
}
|
|
1072
|
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
|
|
1073
|
+
.status.ok { color: #0d7a3e; }
|
|
1074
|
+
.status.ok .dot { background: var(--ok); }
|
|
1075
|
+
.status.err { color: var(--err); }
|
|
1076
|
+
.status.err .dot { background: var(--err); animation: none; }
|
|
1077
|
+
|
|
1078
|
+
/* \u2500\u2500 \u4E3B\u6807\u9898\u533A \u2500\u2500 */
|
|
1079
|
+
h1 {
|
|
1080
|
+
font-size: clamp(28px, 5vw, 38px);
|
|
1081
|
+
font-weight: 650;
|
|
1082
|
+
letter-spacing: -0.025em;
|
|
1083
|
+
line-height: 1.2;
|
|
1084
|
+
margin-bottom: 12px;
|
|
1085
|
+
}
|
|
1086
|
+
.sub {
|
|
1087
|
+
font-size: 14px;
|
|
1088
|
+
color: var(--sub);
|
|
1089
|
+
max-width: 46em;
|
|
1090
|
+
line-height: 1.7;
|
|
1091
|
+
margin-bottom: clamp(40px, 7vh, 64px);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/* \u2500\u2500 \u6B65\u9AA4\u5217\u8868\uFF1A\u53D1\u4E1D\u7EBF\u5206\u9694 \u2500\u2500 */
|
|
1095
|
+
.steps { border-top: 1px solid var(--line); }
|
|
1096
|
+
.step {
|
|
1097
|
+
display: grid;
|
|
1098
|
+
grid-template-columns: 44px 1fr;
|
|
1099
|
+
gap: 4px 16px;
|
|
1100
|
+
padding: 26px 0 28px;
|
|
1101
|
+
border-bottom: 1px solid var(--line);
|
|
1102
|
+
}
|
|
1103
|
+
.num {
|
|
1104
|
+
grid-row: 1 / span 2;
|
|
1105
|
+
font-family: var(--mono);
|
|
1106
|
+
font-size: 12px;
|
|
1107
|
+
color: var(--faint);
|
|
1108
|
+
padding-top: 3px;
|
|
1109
|
+
font-variant-numeric: tabular-nums;
|
|
1110
|
+
}
|
|
1111
|
+
.step-head {
|
|
1112
|
+
display: flex;
|
|
1113
|
+
align-items: center;
|
|
1114
|
+
gap: 10px;
|
|
1115
|
+
flex-wrap: wrap;
|
|
1116
|
+
}
|
|
1117
|
+
.step-title {
|
|
1118
|
+
font-size: 14px;
|
|
1119
|
+
font-weight: 600;
|
|
1120
|
+
letter-spacing: -0.01em;
|
|
1121
|
+
}
|
|
1122
|
+
.tag {
|
|
1123
|
+
font-size: 11px;
|
|
1124
|
+
font-weight: 500;
|
|
1125
|
+
color: var(--iris);
|
|
1126
|
+
background: var(--iris-soft);
|
|
1127
|
+
padding: 2px 8px;
|
|
1128
|
+
border-radius: 999px;
|
|
1129
|
+
}
|
|
1130
|
+
.step-body { grid-column: 2; }
|
|
1131
|
+
.note {
|
|
1132
|
+
font-size: 12.5px;
|
|
1133
|
+
color: var(--sub);
|
|
1134
|
+
line-height: 1.75;
|
|
1135
|
+
max-width: 60em;
|
|
1136
|
+
}
|
|
1137
|
+
.note strong { color: var(--ink); font-weight: 600; }
|
|
1138
|
+
code {
|
|
1139
|
+
font-family: var(--mono);
|
|
1140
|
+
background: var(--panel);
|
|
1141
|
+
border: 1px solid var(--line);
|
|
1142
|
+
color: var(--ink);
|
|
1143
|
+
padding: 0.5px 5px;
|
|
1144
|
+
border-radius: 4px;
|
|
1145
|
+
font-size: 11px;
|
|
1146
|
+
}
|
|
1147
|
+
.actions { display: flex; align-items: center; gap: 12px; margin: 14px 0 10px; }
|
|
1148
|
+
|
|
1149
|
+
/* \u4E3B\u6309\u94AE\uFF1A\u53EF\u62D6\u62FD\u7684\u4E66\u7B7E */
|
|
1150
|
+
.bm {
|
|
1151
|
+
display: inline-flex;
|
|
1152
|
+
align-items: center;
|
|
1153
|
+
gap: 8px;
|
|
1154
|
+
padding: 9px 18px;
|
|
1155
|
+
background: var(--iris);
|
|
1156
|
+
color: #fff;
|
|
1157
|
+
border-radius: var(--radius);
|
|
1158
|
+
font-size: 13px;
|
|
1159
|
+
font-weight: 600;
|
|
1160
|
+
letter-spacing: -0.01em;
|
|
1161
|
+
cursor: grab;
|
|
1162
|
+
box-shadow: 0 1px 2px rgba(30, 30, 30, 0.08);
|
|
1163
|
+
transition: background 140ms ease, transform 140ms ease;
|
|
1164
|
+
user-select: none;
|
|
1165
|
+
}
|
|
1166
|
+
.bm:hover { background: var(--iris-strong); }
|
|
1167
|
+
.bm:active { cursor: grabbing; transform: scale(0.985); }
|
|
1168
|
+
.drag-hint {
|
|
1169
|
+
font-size: 12px;
|
|
1170
|
+
color: var(--faint);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/* \u6B21\u7EA7\u6309\u94AE */
|
|
1174
|
+
.btn {
|
|
1175
|
+
display: inline-flex;
|
|
1176
|
+
align-items: center;
|
|
1177
|
+
gap: 7px;
|
|
1178
|
+
padding: 8px 14px;
|
|
1179
|
+
border: 1px solid var(--line);
|
|
1180
|
+
border-radius: var(--radius);
|
|
1181
|
+
background: #fff;
|
|
1182
|
+
color: var(--ink);
|
|
1183
|
+
font-size: 13px;
|
|
1184
|
+
font-weight: 500;
|
|
1185
|
+
letter-spacing: -0.01em;
|
|
1186
|
+
text-decoration: none;
|
|
1187
|
+
transition: border-color 140ms ease, background 140ms ease;
|
|
1188
|
+
}
|
|
1189
|
+
.btn:hover { border-color: #d2d2d6; background: var(--panel); }
|
|
1190
|
+
.btn svg { display: block; color: var(--sub); }
|
|
1191
|
+
|
|
1192
|
+
/* MCP \u914D\u7F6E\u4EE3\u7801\u5757 */
|
|
1193
|
+
.cfg {
|
|
1194
|
+
position: relative;
|
|
1195
|
+
margin: 14px 0 10px;
|
|
1196
|
+
}
|
|
1197
|
+
pre {
|
|
1198
|
+
background: var(--panel);
|
|
1199
|
+
border: 1px solid var(--line);
|
|
1200
|
+
border-radius: 8px;
|
|
1201
|
+
padding: 14px 16px;
|
|
1202
|
+
font-family: var(--mono);
|
|
1203
|
+
font-size: 12px;
|
|
1204
|
+
line-height: 1.7;
|
|
1205
|
+
color: #3c3c3c;
|
|
1206
|
+
overflow-x: auto;
|
|
1207
|
+
}
|
|
1208
|
+
.copy {
|
|
1209
|
+
position: absolute;
|
|
1210
|
+
top: 8px;
|
|
1211
|
+
right: 8px;
|
|
1212
|
+
padding: 4px 10px;
|
|
1213
|
+
font-family: inherit;
|
|
1214
|
+
font-size: 11px;
|
|
1215
|
+
font-weight: 500;
|
|
1216
|
+
color: var(--sub);
|
|
1217
|
+
background: #fff;
|
|
1218
|
+
border: 1px solid var(--line);
|
|
1219
|
+
border-radius: var(--radius);
|
|
1220
|
+
cursor: pointer;
|
|
1221
|
+
transition: color 140ms ease, border-color 140ms ease;
|
|
1222
|
+
}
|
|
1223
|
+
.copy:hover { color: var(--ink); border-color: #d2d2d6; }
|
|
1224
|
+
.copy.done { color: var(--ok); border-color: var(--ok); }
|
|
982
1225
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1226
|
+
.footer {
|
|
1227
|
+
margin-top: 40px;
|
|
1228
|
+
font-size: 11px;
|
|
1229
|
+
color: var(--faint);
|
|
1230
|
+
font-family: var(--mono);
|
|
1231
|
+
display: flex;
|
|
1232
|
+
gap: 8px;
|
|
1233
|
+
flex-wrap: wrap;
|
|
1234
|
+
}
|
|
986
1235
|
|
|
987
|
-
|
|
1236
|
+
@media (max-width: 560px) {
|
|
1237
|
+
.step { grid-template-columns: 32px 1fr; gap: 4px 10px; }
|
|
1238
|
+
.top { align-items: flex-start; flex-direction: column; gap: 12px; }
|
|
1239
|
+
}
|
|
1240
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1241
|
+
.page { animation: none; }
|
|
1242
|
+
.status .dot { animation: none; }
|
|
1243
|
+
.bm, .btn, .copy { transition: none; }
|
|
1244
|
+
}
|
|
1245
|
+
</style>
|
|
1246
|
+
</head>
|
|
1247
|
+
<body>
|
|
1248
|
+
<div class="page">
|
|
1249
|
+
<header class="top">
|
|
1250
|
+
<div class="brand">
|
|
1251
|
+
<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>
|
|
1252
|
+
Batter Pixso Bridge
|
|
1253
|
+
<span class="ver">v{{ version }}</span>
|
|
1254
|
+
</div>
|
|
1255
|
+
<div class="status" id="status">
|
|
1256
|
+
<span class="dot"></span><span id="status-text">\u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u2026</span>
|
|
1257
|
+
</div>
|
|
1258
|
+
</header>
|
|
988
1259
|
|
|
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
|
|
1260
|
+
<h1>\u628A\u8BBE\u8BA1\u7A3F\u4EA4\u7ED9 AI</h1>
|
|
1261
|
+
<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
1262
|
|
|
996
|
-
|
|
1263
|
+
<div class="steps">
|
|
1264
|
+
<div class="step">
|
|
1265
|
+
<span class="num">01</span>
|
|
1266
|
+
<div class="step-head">
|
|
1267
|
+
<span class="step-title">\u6DFB\u52A0\u4E66\u7B7E</span>
|
|
1268
|
+
<span class="tag">\u7F51\u9875\u7248 \xB7 \u63A8\u8350</span>
|
|
1269
|
+
</div>
|
|
1270
|
+
<div class="step-body">
|
|
1271
|
+
<div class="actions">
|
|
1272
|
+
<a class="bm" href="{{ href }}">Batter Bridge</a>
|
|
1273
|
+
<span class="drag-hint">\u2190 \u628A\u8FD9\u4E2A\u6309\u94AE\u62D6\u5230\u6D4F\u89C8\u5668\u4E66\u7B7E\u680F</span>
|
|
1274
|
+
</div>
|
|
1275
|
+
<p class="note">
|
|
1276
|
+
<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
|
|
1277
|
+
\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
|
|
1278
|
+
</p>
|
|
1279
|
+
</div>
|
|
1280
|
+
</div>
|
|
997
1281
|
|
|
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
|
-
});
|
|
1282
|
+
<div class="step">
|
|
1283
|
+
<span class="num">02</span>
|
|
1284
|
+
<div class="step-head">
|
|
1285
|
+
<span class="step-title">\u5B89\u88C5\u5230 Pixso \u5BA2\u6237\u7AEF</span>
|
|
1286
|
+
</div>
|
|
1287
|
+
<div class="step-body">
|
|
1288
|
+
<div class="actions">
|
|
1289
|
+
<a class="btn" href="/plugin.zip" download="batter-pixso-bridge.zip">
|
|
1290
|
+
<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>
|
|
1291
|
+
\u4E0B\u8F7D\u63D2\u4EF6\u5305\uFF08.zip\uFF09
|
|
1292
|
+
</a>
|
|
1293
|
+
</div>
|
|
1294
|
+
<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>
|
|
1295
|
+
</div>
|
|
1296
|
+
</div>
|
|
1019
1297
|
|
|
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];
|
|
1298
|
+
<div class="step">
|
|
1299
|
+
<span class="num">03</span>
|
|
1300
|
+
<div class="step-head">
|
|
1301
|
+
<span class="step-title">\u914D\u7F6E AI \u5BA2\u6237\u7AEF</span>
|
|
1302
|
+
</div>
|
|
1303
|
+
<div class="step-body">
|
|
1304
|
+
<div class="cfg">
|
|
1305
|
+
<pre id="cfg">{
|
|
1306
|
+
"mcpServers": {
|
|
1307
|
+
"batter-pixso": {
|
|
1308
|
+
"command": "npx",
|
|
1309
|
+
"args": ["-y", "batter-pixso-mcp"],
|
|
1310
|
+
"env": { "HTTP_PORT": "{{ httpPort }}" }
|
|
1311
|
+
}
|
|
1066
1312
|
}
|
|
1067
|
-
}
|
|
1313
|
+
}</pre>
|
|
1314
|
+
<button class="copy" id="copy" type="button">\u590D\u5236</button>
|
|
1315
|
+
</div>
|
|
1316
|
+
<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>
|
|
1317
|
+
</div>
|
|
1318
|
+
</div>
|
|
1068
1319
|
|
|
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
|
-
});
|
|
1320
|
+
<div class="step">
|
|
1321
|
+
<span class="num">04</span>
|
|
1322
|
+
<div class="step-head">
|
|
1323
|
+
<span class="step-title">\u5F00\u59CB\u4F7F\u7528</span>
|
|
1324
|
+
</div>
|
|
1325
|
+
<div class="step-body">
|
|
1326
|
+
<p class="note" style="margin-top:10px">
|
|
1327
|
+
\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
|
|
1328
|
+
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
|
|
1329
|
+
</p>
|
|
1330
|
+
</div>
|
|
1331
|
+
</div>
|
|
1332
|
+
</div>
|
|
1089
1333
|
|
|
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
|
-
}
|
|
1334
|
+
<div class="footer">
|
|
1335
|
+
<span>batter-pixso-mcp</span>
|
|
1336
|
+
<span>\xB7</span>
|
|
1337
|
+
<span>v{{ version }}</span>
|
|
1338
|
+
<span>\xB7</span>
|
|
1339
|
+
<span>{{ base }}</span>
|
|
1340
|
+
</div>
|
|
1341
|
+
</div>
|
|
1342
|
+
<script>
|
|
1343
|
+
fetch("/health").then(function (r) { return r.json(); }).then(function (h) {
|
|
1344
|
+
var el = document.getElementById("status");
|
|
1345
|
+
el.className = "status ok";
|
|
1346
|
+
document.getElementById("status-text").textContent =
|
|
1347
|
+
"\u670D\u52A1\u8FD0\u884C\u4E2D \xB7 \u63D2\u4EF6 " + h.pluginsOnline + " \u5728\u7EBF";
|
|
1348
|
+
}).catch(function () {
|
|
1349
|
+
var el = document.getElementById("status");
|
|
1350
|
+
el.className = "status err";
|
|
1351
|
+
document.getElementById("status-text").textContent = "\u670D\u52A1\u672A\u8FDE\u63A5";
|
|
1221
1352
|
});
|
|
1222
1353
|
|
|
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);
|
|
1354
|
+
var copyBtn = document.getElementById("copy");
|
|
1355
|
+
copyBtn.addEventListener("click", function () {
|
|
1356
|
+
var text = document.getElementById("cfg").textContent;
|
|
1357
|
+
function done() {
|
|
1358
|
+
copyBtn.textContent = "\u5DF2\u590D\u5236";
|
|
1359
|
+
copyBtn.className = "copy done";
|
|
1360
|
+
setTimeout(function () {
|
|
1361
|
+
copyBtn.textContent = "\u590D\u5236";
|
|
1362
|
+
copyBtn.className = "copy";
|
|
1363
|
+
}, 1600);
|
|
1241
1364
|
}
|
|
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
|
-
];
|
|
1365
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1366
|
+
navigator.clipboard.writeText(text).then(done, done);
|
|
1367
|
+
} else {
|
|
1368
|
+
var ta = document.createElement("textarea");
|
|
1369
|
+
ta.value = text;
|
|
1370
|
+
document.body.appendChild(ta);
|
|
1371
|
+
ta.select();
|
|
1372
|
+
try { document.execCommand("copy"); } catch (e) {}
|
|
1373
|
+
document.body.removeChild(ta);
|
|
1374
|
+
done();
|
|
1336
1375
|
}
|
|
1337
1376
|
});
|
|
1377
|
+
</script>
|
|
1378
|
+
</body>
|
|
1379
|
+
</html>
|
|
1380
|
+
`;
|
|
1338
1381
|
|
|
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;
|
|
1382
|
+
// src/install/page.ts
|
|
1383
|
+
function bookmarkletHref(base) {
|
|
1384
|
+
const source = renderTemplate(bookmarklet_default, { base }, escapeJsString);
|
|
1385
|
+
return `javascript:${encodeURIComponent(source)}`;
|
|
1356
1386
|
}
|
|
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) };
|
|
1387
|
+
function renderInstallPage() {
|
|
1388
|
+
const base = `${hubBaseUrl()}/`;
|
|
1389
|
+
return renderTemplate(install_page_default, {
|
|
1390
|
+
version: VERSION,
|
|
1391
|
+
href: bookmarkletHref(base),
|
|
1392
|
+
base,
|
|
1393
|
+
httpPort: config.httpPort
|
|
1440
1394
|
});
|
|
1441
|
-
return server;
|
|
1442
1395
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1396
|
+
|
|
1397
|
+
// src/install/zip.ts
|
|
1398
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
1399
|
+
var CRC_TABLE = (() => {
|
|
1400
|
+
const table = new Uint32Array(256);
|
|
1401
|
+
for (let n = 0; n < 256; n++) {
|
|
1402
|
+
let c = n;
|
|
1403
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1404
|
+
table[n] = c >>> 0;
|
|
1405
|
+
}
|
|
1406
|
+
return table;
|
|
1407
|
+
})();
|
|
1408
|
+
function crc32(buf) {
|
|
1409
|
+
let crc = 4294967295;
|
|
1410
|
+
for (const byte of buf) crc = CRC_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
1411
|
+
return (crc ^ 4294967295) >>> 0;
|
|
1412
|
+
}
|
|
1413
|
+
function buildZip(entries) {
|
|
1414
|
+
const chunks = [];
|
|
1415
|
+
const central = [];
|
|
1416
|
+
let offset = 0;
|
|
1417
|
+
for (const { name, data } of entries) {
|
|
1418
|
+
const nameBuf = Buffer2.from(name, "utf8");
|
|
1419
|
+
const crc = crc32(data);
|
|
1420
|
+
const local = Buffer2.alloc(30);
|
|
1421
|
+
local.writeUInt32LE(67324752, 0);
|
|
1422
|
+
local.writeUInt16LE(20, 4);
|
|
1423
|
+
local.writeUInt16LE(2048, 6);
|
|
1424
|
+
local.writeUInt16LE(0, 8);
|
|
1425
|
+
local.writeUInt16LE(0, 10);
|
|
1426
|
+
local.writeUInt16LE(33, 12);
|
|
1427
|
+
local.writeUInt32LE(crc, 14);
|
|
1428
|
+
local.writeUInt32LE(data.length, 18);
|
|
1429
|
+
local.writeUInt32LE(data.length, 22);
|
|
1430
|
+
local.writeUInt16LE(nameBuf.length, 26);
|
|
1431
|
+
local.writeUInt16LE(0, 28);
|
|
1432
|
+
chunks.push(local, nameBuf, data);
|
|
1433
|
+
const cen = Buffer2.alloc(46);
|
|
1434
|
+
cen.writeUInt32LE(33639248, 0);
|
|
1435
|
+
cen.writeUInt16LE(20, 4);
|
|
1436
|
+
cen.writeUInt16LE(20, 6);
|
|
1437
|
+
cen.writeUInt16LE(2048, 8);
|
|
1438
|
+
cen.writeUInt16LE(0, 10);
|
|
1439
|
+
cen.writeUInt16LE(0, 12);
|
|
1440
|
+
cen.writeUInt16LE(33, 14);
|
|
1441
|
+
cen.writeUInt32LE(crc, 16);
|
|
1442
|
+
cen.writeUInt32LE(data.length, 20);
|
|
1443
|
+
cen.writeUInt32LE(data.length, 24);
|
|
1444
|
+
cen.writeUInt16LE(nameBuf.length, 28);
|
|
1445
|
+
cen.writeUInt16LE(0, 30);
|
|
1446
|
+
cen.writeUInt16LE(0, 32);
|
|
1447
|
+
cen.writeUInt16LE(0, 34);
|
|
1448
|
+
cen.writeUInt16LE(0, 36);
|
|
1449
|
+
cen.writeUInt32LE(0, 38);
|
|
1450
|
+
cen.writeUInt32LE(offset, 42);
|
|
1451
|
+
central.push(Buffer2.concat([cen, nameBuf]));
|
|
1452
|
+
offset += 30 + nameBuf.length + data.length;
|
|
1452
1453
|
}
|
|
1453
|
-
|
|
1454
|
+
const centralBuf = Buffer2.concat(central);
|
|
1455
|
+
const eocd = Buffer2.alloc(22);
|
|
1456
|
+
eocd.writeUInt32LE(101010256, 0);
|
|
1457
|
+
eocd.writeUInt16LE(entries.length, 8);
|
|
1458
|
+
eocd.writeUInt16LE(entries.length, 10);
|
|
1459
|
+
eocd.writeUInt32LE(centralBuf.length, 12);
|
|
1460
|
+
eocd.writeUInt32LE(offset, 16);
|
|
1461
|
+
return Buffer2.concat([...chunks, centralBuf, eocd]);
|
|
1462
|
+
}
|
|
1454
1463
|
|
|
1455
|
-
// src/
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1464
|
+
// src/install/api.ts
|
|
1465
|
+
var NO_DIST = "plugin dist not found\uFF08\u8FD0\u884C npm run build \u751F\u6210\uFF09";
|
|
1466
|
+
function handleInstall(req, res, url) {
|
|
1467
|
+
return dispatchRoute(INSTALL_ROUTES, req, res, url);
|
|
1468
|
+
}
|
|
1469
|
+
function sendBinary(res, buf, contentType) {
|
|
1470
|
+
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "no-store" });
|
|
1471
|
+
res.end(buf);
|
|
1472
|
+
}
|
|
1473
|
+
var PLUGIN_FILE_ROUTES = [
|
|
1474
|
+
{ path: "/code.js", file: "code.js", type: "text/javascript; charset=utf-8" },
|
|
1475
|
+
{ path: "/ui.html", file: "ui.html", type: "text/html; charset=utf-8" }
|
|
1476
|
+
].map(({ path, file, type }) => ({
|
|
1477
|
+
method: "GET",
|
|
1478
|
+
path,
|
|
1479
|
+
handler: async ({ res }) => {
|
|
1480
|
+
const buf = await readPluginFile(file);
|
|
1481
|
+
if (!buf) {
|
|
1482
|
+
sendJson(res, 404, { error: hasPluginDist() ? "not found" : NO_DIST });
|
|
1483
|
+
return;
|
|
1470
1484
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1485
|
+
sendBinary(res, buf, type);
|
|
1486
|
+
}
|
|
1487
|
+
}));
|
|
1488
|
+
var INSTALL_ROUTES = [
|
|
1489
|
+
{
|
|
1490
|
+
method: "GET",
|
|
1491
|
+
path: "/",
|
|
1492
|
+
handler: ({ res }) => {
|
|
1493
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1494
|
+
res.end(renderInstallPage());
|
|
1495
|
+
}
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
method: "GET",
|
|
1499
|
+
path: "/manifest.json",
|
|
1500
|
+
handler: async ({ res }) => {
|
|
1501
|
+
const buf = await pluginManifest();
|
|
1502
|
+
if (!buf) {
|
|
1503
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
sendBinary(res, buf, "application/json; charset=utf-8");
|
|
1507
|
+
}
|
|
1508
|
+
},
|
|
1509
|
+
...PLUGIN_FILE_ROUTES,
|
|
1510
|
+
{
|
|
1511
|
+
method: "GET",
|
|
1512
|
+
path: "/plugin.zip",
|
|
1513
|
+
handler: async ({ res }) => {
|
|
1514
|
+
if (!hasPluginDist()) {
|
|
1515
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
try {
|
|
1519
|
+
const [manifest, code, ui] = await Promise.all([
|
|
1520
|
+
pluginManifest(),
|
|
1521
|
+
readPluginFile("code.js"),
|
|
1522
|
+
readPluginFile("ui.html")
|
|
1523
|
+
]);
|
|
1524
|
+
const zip = buildZip([
|
|
1525
|
+
{ name: "manifest.json", data: manifest },
|
|
1526
|
+
{ name: "code.js", data: code },
|
|
1527
|
+
{ name: "ui.html", data: ui }
|
|
1528
|
+
]);
|
|
1529
|
+
res.writeHead(200, {
|
|
1530
|
+
"Content-Type": "application/zip",
|
|
1531
|
+
"Content-Disposition": 'attachment; filename="batter-pixso-bridge.zip"'
|
|
1491
1532
|
});
|
|
1492
|
-
|
|
1533
|
+
res.end(zip);
|
|
1534
|
+
} catch {
|
|
1535
|
+
sendJson(res, 500, { error: "zip build failed" });
|
|
1493
1536
|
}
|
|
1494
1537
|
}
|
|
1495
|
-
await transport2.handleRequest(req, res, body);
|
|
1496
|
-
return true;
|
|
1497
1538
|
}
|
|
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
|
-
});
|
|
1539
|
+
];
|
|
1516
1540
|
|
|
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;
|
|
1541
|
+
// src/plugin/api.ts
|
|
1542
|
+
function handlePluginApi(req, res, url) {
|
|
1543
|
+
return dispatchRoute(PLUGIN_ROUTES, req, res, url);
|
|
1544
|
+
}
|
|
1545
|
+
var PLUGIN_ROUTES = [
|
|
1546
|
+
{
|
|
1547
|
+
method: "POST",
|
|
1548
|
+
path: "/sessions/register",
|
|
1549
|
+
handler: async ({ req, res }) => {
|
|
1550
|
+
sendJson(res, 201, sessions.register(await readJsonBody(req)));
|
|
1535
1551
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1552
|
+
},
|
|
1553
|
+
{
|
|
1554
|
+
method: "POST",
|
|
1555
|
+
path: "/context",
|
|
1556
|
+
handler: async ({ req, res, url }) => {
|
|
1557
|
+
const session = url.searchParams.get("session");
|
|
1558
|
+
if (!session) {
|
|
1559
|
+
sendJson(res, 400, { error: "missing session" });
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
const meta = await readJsonBody(req);
|
|
1539
1563
|
if (!sessions.updateContext(session, meta)) {
|
|
1540
1564
|
sendJson(res, 404, { error: "unknown_session" });
|
|
1541
|
-
return
|
|
1565
|
+
return;
|
|
1542
1566
|
}
|
|
1543
1567
|
sendJson(res, 200, { ok: true });
|
|
1544
|
-
} catch {
|
|
1545
|
-
sendJson(res, 400, { error: "invalid json" });
|
|
1546
1568
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
method: "GET",
|
|
1572
|
+
path: "/commands/stream",
|
|
1573
|
+
handler: ({ req, res, url }) => {
|
|
1574
|
+
const session = url.searchParams.get("session");
|
|
1575
|
+
if (!session || !sessions.setConnected(session, true)) {
|
|
1576
|
+
sendJson(res, 404, { error: "unknown_session" });
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
res.writeHead(200, {
|
|
1580
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
1581
|
+
"Cache-Control": "no-store",
|
|
1582
|
+
Connection: "keep-alive"
|
|
1583
|
+
});
|
|
1584
|
+
res.write(": connected\n\n");
|
|
1585
|
+
const unsubscribe = commandBus.subscribe(session, (command) => {
|
|
1586
|
+
res.write(`data: ${JSON.stringify({ command })}
|
|
1562
1587
|
|
|
1563
1588
|
`);
|
|
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" });
|
|
1589
|
+
});
|
|
1590
|
+
const heartbeat = setInterval(() => {
|
|
1591
|
+
sessions.touchSeen(session);
|
|
1592
|
+
res.write(": ping\n\n");
|
|
1593
|
+
}, 15e3);
|
|
1594
|
+
req.on("close", () => {
|
|
1595
|
+
clearInterval(heartbeat);
|
|
1596
|
+
unsubscribe();
|
|
1597
|
+
sessions.setConnected(session, false);
|
|
1598
|
+
log(`SSE channel closed: session ${session.slice(0, 8)}`);
|
|
1599
|
+
});
|
|
1596
1600
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
const
|
|
1604
|
-
const found = commandBus.complete(
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
method: "POST",
|
|
1604
|
+
path: "/commands/:id/result",
|
|
1605
|
+
handler: async ({ req, res, params }) => {
|
|
1606
|
+
const payload = await readJsonBody(req);
|
|
1607
|
+
const result = payload.ok && payload.assets?.length ? substituteAssets(payload.result, payload.assets) : payload.result;
|
|
1608
|
+
const found = commandBus.complete(params["id"], payload.ok, payload.ok ? result : payload.error);
|
|
1605
1609
|
sendJson(res, found ? 200 : 404, found ? { ok: true } : { error: "unknown command" });
|
|
1606
|
-
} catch {
|
|
1607
|
-
sendJson(res, 400, { error: "invalid json" });
|
|
1608
1610
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
method: "POST",
|
|
1614
|
+
path: "/assets",
|
|
1615
|
+
handler: async ({ req, res, url }) => {
|
|
1616
|
+
try {
|
|
1617
|
+
const name = await saveAsset(await readBody(req), url.searchParams.get("format") ?? "PNG");
|
|
1618
|
+
sendJson(res, 201, { name });
|
|
1619
|
+
} catch {
|
|
1620
|
+
sendJson(res, 500, { error: "asset save failed" });
|
|
1621
|
+
}
|
|
1618
1622
|
}
|
|
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
1623
|
}
|
|
1632
|
-
|
|
1624
|
+
];
|
|
1633
1625
|
|
|
1634
|
-
// src/
|
|
1635
|
-
|
|
1626
|
+
// src/http.ts
|
|
1627
|
+
var lastActivity = Date.now();
|
|
1636
1628
|
function getLastActivity() {
|
|
1637
1629
|
return lastActivity;
|
|
1638
1630
|
}
|
|
1631
|
+
var ROUTES = [handleInstall, handleMcp, handlePluginApi];
|
|
1639
1632
|
function startHttpServer() {
|
|
1640
1633
|
const server = createServer((req, res) => {
|
|
1641
1634
|
lastActivity = Date.now();
|
|
@@ -1653,23 +1646,19 @@ function startHttpServer() {
|
|
|
1653
1646
|
const all = sessions.list();
|
|
1654
1647
|
sendJson(res, 200, {
|
|
1655
1648
|
ok: true,
|
|
1656
|
-
name:
|
|
1649
|
+
name: HUB_NAME,
|
|
1657
1650
|
pid: process.pid,
|
|
1658
1651
|
version: VERSION,
|
|
1659
|
-
sessions: all.length,
|
|
1660
1652
|
pluginsOnline: all.filter((s) => s.online).length
|
|
1661
1653
|
});
|
|
1662
1654
|
return;
|
|
1663
1655
|
}
|
|
1664
|
-
(async () => {
|
|
1656
|
+
void (async () => {
|
|
1665
1657
|
for (const handler of ROUTES) {
|
|
1666
1658
|
if (await handler(req, res, url)) return;
|
|
1667
1659
|
}
|
|
1668
1660
|
sendJson(res, 404, { error: "not found" });
|
|
1669
|
-
})()
|
|
1670
|
-
log("route error:", err);
|
|
1671
|
-
if (!res.headersSent) sendJson(res, 500, { error: "internal error" });
|
|
1672
|
-
});
|
|
1661
|
+
})();
|
|
1673
1662
|
});
|
|
1674
1663
|
return new Promise((resolve, reject) => {
|
|
1675
1664
|
server.once("error", (err) => {
|
|
@@ -1685,22 +1674,8 @@ function startHttpServer() {
|
|
|
1685
1674
|
});
|
|
1686
1675
|
});
|
|
1687
1676
|
}
|
|
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
1677
|
|
|
1703
|
-
// src/
|
|
1678
|
+
// src/process/hub.ts
|
|
1704
1679
|
async function runHub() {
|
|
1705
1680
|
const server = await startHttpServer();
|
|
1706
1681
|
if (!server) {
|
|
@@ -1708,46 +1683,46 @@ async function runHub() {
|
|
|
1708
1683
|
process.exit(0);
|
|
1709
1684
|
}
|
|
1710
1685
|
log(`hub ready (idle TTL ${config.hubIdleMs / 6e4}min)`);
|
|
1686
|
+
void cleanOldAssets(config.assetTtlMs);
|
|
1687
|
+
let tick = 0;
|
|
1711
1688
|
const watchdog = setInterval(() => {
|
|
1712
1689
|
const idle = Date.now() - getLastActivity();
|
|
1713
|
-
|
|
1714
|
-
|
|
1690
|
+
const anyPluginOnline = sessions.list().some((s) => s.online);
|
|
1691
|
+
if (idle > config.hubIdleMs && !anyPluginOnline) {
|
|
1692
|
+
log(`\u7A7A\u95F2 ${Math.round(idle / 6e4)} \u5206\u949F\u4E14\u65E0\u5728\u7EBF\u63D2\u4EF6\uFF0Chub \u81EA\u52A8\u9000\u51FA`);
|
|
1715
1693
|
process.exit(0);
|
|
1716
1694
|
}
|
|
1717
|
-
void cleanOldAssets(config.assetTtlMs);
|
|
1695
|
+
if (++tick % 60 === 0) void cleanOldAssets(config.assetTtlMs);
|
|
1718
1696
|
}, 6e4);
|
|
1719
1697
|
watchdog.unref();
|
|
1720
1698
|
}
|
|
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
1699
|
|
|
1730
|
-
// src/
|
|
1700
|
+
// src/process/stdio-proxy.ts
|
|
1701
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1702
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
1703
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1704
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1705
|
+
import { ResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1706
|
+
|
|
1707
|
+
// src/process/hub-process.ts
|
|
1731
1708
|
import { spawn } from "node:child_process";
|
|
1732
|
-
import { openSync } from "node:fs";
|
|
1709
|
+
import { closeSync, openSync } from "node:fs";
|
|
1733
1710
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
1734
|
-
import { join as
|
|
1711
|
+
import { join as join4 } from "node:path";
|
|
1735
1712
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1736
|
-
function hubBaseUrl() {
|
|
1737
|
-
return `http://127.0.0.1:${config.httpPort}`;
|
|
1738
|
-
}
|
|
1739
1713
|
async function probeHub() {
|
|
1740
1714
|
try {
|
|
1741
1715
|
const res = await fetch(`${hubBaseUrl()}/health`, { signal: AbortSignal.timeout(1500) });
|
|
1742
1716
|
if (!res.ok) return "foreign";
|
|
1743
1717
|
const body = await res.json();
|
|
1744
|
-
if (body.ok === true && body.name ===
|
|
1718
|
+
if (body.ok === true && body.name === HUB_NAME) return "alive";
|
|
1745
1719
|
if (body.ok === true) return "legacy";
|
|
1746
1720
|
return "foreign";
|
|
1747
1721
|
} catch {
|
|
1748
1722
|
return "down";
|
|
1749
1723
|
}
|
|
1750
1724
|
}
|
|
1725
|
+
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
1726
|
function hubStateError(state) {
|
|
1752
1727
|
if (state === "legacy") {
|
|
1753
1728
|
return new Error(
|
|
@@ -1756,6 +1731,7 @@ function hubStateError(state) {
|
|
|
1756
1731
|
}
|
|
1757
1732
|
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
1733
|
}
|
|
1734
|
+
var HUB_LOG = join4(tmpdir2(), "batter-pixso-mcp-hub.log");
|
|
1759
1735
|
function spawnHub() {
|
|
1760
1736
|
const out = openSync(HUB_LOG, "a");
|
|
1761
1737
|
const entry = process.argv[1] ?? fileURLToPath2(import.meta.url);
|
|
@@ -1770,6 +1746,7 @@ function spawnHub() {
|
|
|
1770
1746
|
env: { ...process.env }
|
|
1771
1747
|
});
|
|
1772
1748
|
child.unref();
|
|
1749
|
+
closeSync(out);
|
|
1773
1750
|
log(`hub spawned (pid ${child.pid}), log: ${HUB_LOG}`);
|
|
1774
1751
|
}
|
|
1775
1752
|
async function waitForHub(timeoutMs = 15e3) {
|
|
@@ -1782,22 +1759,8 @@ async function waitForHub(timeoutMs = 15e3) {
|
|
|
1782
1759
|
await new Promise((r) => setTimeout(r, 200));
|
|
1783
1760
|
}
|
|
1784
1761
|
}
|
|
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
1762
|
|
|
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";
|
|
1763
|
+
// src/process/stdio-proxy.ts
|
|
1801
1764
|
async function startStdioProxy() {
|
|
1802
1765
|
const mcpUrl = `${hubBaseUrl()}/mcp`;
|
|
1803
1766
|
let upstream = null;
|
|
@@ -1810,13 +1773,7 @@ async function startStdioProxy() {
|
|
|
1810
1773
|
return client;
|
|
1811
1774
|
}
|
|
1812
1775
|
async function ensureUpstream() {
|
|
1813
|
-
if (upstream)
|
|
1814
|
-
try {
|
|
1815
|
-
await upstream.ping();
|
|
1816
|
-
return upstream;
|
|
1817
|
-
} catch {
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1776
|
+
if (upstream) return upstream;
|
|
1820
1777
|
const state = await probeHub();
|
|
1821
1778
|
if (state === "down") spawnHub();
|
|
1822
1779
|
if (state === "legacy" || state === "foreign") throw hubStateError(state);
|
|
@@ -1830,8 +1787,12 @@ async function startStdioProxy() {
|
|
|
1830
1787
|
try {
|
|
1831
1788
|
return await fn(client);
|
|
1832
1789
|
} catch (err) {
|
|
1833
|
-
|
|
1834
|
-
|
|
1790
|
+
const alive = await client.ping().then(
|
|
1791
|
+
() => true,
|
|
1792
|
+
() => false
|
|
1793
|
+
);
|
|
1794
|
+
if (alive) throw err;
|
|
1795
|
+
log("hub \u4F1A\u8BDD\u5931\u8054\uFF0C\u91CD\u5EFA\u540E\u91CD\u8BD5\u2026");
|
|
1835
1796
|
upstream = null;
|
|
1836
1797
|
const rebuilt = await ensureUpstream();
|
|
1837
1798
|
return await fn(rebuilt);
|
|
@@ -1839,7 +1800,7 @@ async function startStdioProxy() {
|
|
|
1839
1800
|
}
|
|
1840
1801
|
const upstreamClient = await ensureUpstream();
|
|
1841
1802
|
const server = new Server(
|
|
1842
|
-
{ name:
|
|
1803
|
+
{ name: HUB_NAME, version: VERSION },
|
|
1843
1804
|
{
|
|
1844
1805
|
capabilities: upstreamClient.getServerCapabilities() ?? {}
|
|
1845
1806
|
}
|
|
@@ -1852,19 +1813,8 @@ async function startStdioProxy() {
|
|
|
1852
1813
|
await server.connect(new StdioServerTransport());
|
|
1853
1814
|
log("proxy ready on stdio");
|
|
1854
1815
|
}
|
|
1855
|
-
var init_stdio_proxy = __esm({
|
|
1856
|
-
"src/stdio-proxy.ts"() {
|
|
1857
|
-
"use strict";
|
|
1858
|
-
init_config();
|
|
1859
|
-
init_hub_process();
|
|
1860
|
-
}
|
|
1861
|
-
});
|
|
1862
1816
|
|
|
1863
|
-
// src/main.ts
|
|
1864
|
-
var main_exports = {};
|
|
1865
|
-
__export(main_exports, {
|
|
1866
|
-
main: () => main
|
|
1867
|
-
});
|
|
1817
|
+
// src/process/main.ts
|
|
1868
1818
|
async function main() {
|
|
1869
1819
|
if (process.argv.includes("--hub")) {
|
|
1870
1820
|
await runHub();
|
|
@@ -1872,26 +1822,9 @@ async function main() {
|
|
|
1872
1822
|
}
|
|
1873
1823
|
await startStdioProxy();
|
|
1874
1824
|
}
|
|
1875
|
-
var init_main = __esm({
|
|
1876
|
-
"src/main.ts"() {
|
|
1877
|
-
"use strict";
|
|
1878
|
-
init_hub_mode();
|
|
1879
|
-
init_stdio_proxy();
|
|
1880
|
-
}
|
|
1881
|
-
});
|
|
1882
1825
|
|
|
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) => {
|
|
1826
|
+
// src/process/index.ts
|
|
1827
|
+
main().catch((err) => {
|
|
1895
1828
|
console.error("[batter-pixso-mcp] fatal:", err);
|
|
1896
1829
|
process.exit(1);
|
|
1897
1830
|
});
|