batter-pixso-mcp 0.6.0 → 0.8.0-beta.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 +1275 -937
- package/dist/plugin/code.js +26 -2
- package/dist/plugin/ui.html +124 -133
- package/package.json +9 -4
- package/standalone/README.txt +6 -3
- package/standalone/index.js +67 -65
- package/standalone/plugin/code.js +26 -2
- package/standalone/plugin/ui.html +124 -133
package/dist/index.js
CHANGED
|
@@ -1,233 +1,322 @@
|
|
|
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
|
+
};
|
|
2
11
|
|
|
3
12
|
// src/config.ts
|
|
4
13
|
import { tmpdir } from "node:os";
|
|
5
14
|
import { join } from "node:path";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// hub 常驻(不自毁):升级由 proxy 版本比对自动换血,手动停止用 --stop
|
|
13
|
-
/** MCP HTTP 会话空闲 TTL:超时清扫防泄漏(proxy 对失效会话有自动重建兜底),默认 30 分钟 */
|
|
14
|
-
mcpSessionTtlMs: Number(process.env.MCP_SESSION_TTL_MS ?? 30 * 60 * 1e3),
|
|
15
|
-
/** 产物图片保留时长,默认 7 天 */
|
|
16
|
-
assetTtlMs: Number(process.env.ASSET_TTL_DAYS ?? 7) * 24 * 60 * 60 * 1e3
|
|
17
|
-
};
|
|
18
|
-
function hubBaseUrl() {
|
|
15
|
+
function sleep(ms) {
|
|
16
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
17
|
+
setTimeout(resolve, ms);
|
|
18
|
+
return promise;
|
|
19
|
+
}
|
|
20
|
+
function serviceBaseUrl() {
|
|
19
21
|
return `http://127.0.0.1:${config.httpPort}`;
|
|
20
22
|
}
|
|
23
|
+
function mcpUrl() {
|
|
24
|
+
return `${serviceBaseUrl()}/mcp`;
|
|
25
|
+
}
|
|
26
|
+
function claudeAddCommand() {
|
|
27
|
+
return `claude mcp add --transport http batter-pixso ${mcpUrl()}`;
|
|
28
|
+
}
|
|
21
29
|
function log(...args) {
|
|
22
30
|
console.error("[batter-pixso-mcp]", ...args);
|
|
23
31
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
var VERSION, DEFAULT_PORT, SERVICE_NAME, ASSETS_DIR, config, PING_TIMEOUT_MS, SESSION_ONLINE_WINDOW_MS, COMMAND_TIMEOUT_MS, SSE_HEARTBEAT_MS, MCP_SESSION_SWEEP_MS, PROBE_TIMEOUT_MS, ASSET_SWEEP_INTERVAL_MS;
|
|
33
|
+
var init_config = __esm({
|
|
34
|
+
"src/config.ts"() {
|
|
35
|
+
"use strict";
|
|
36
|
+
VERSION = true ? "0.8.0-beta.0" : "0.0.0-dev";
|
|
37
|
+
DEFAULT_PORT = true ? 3210 : 3210;
|
|
38
|
+
SERVICE_NAME = "batter-pixso-mcp";
|
|
39
|
+
ASSETS_DIR = join(tmpdir(), "batter-pixso-assets");
|
|
40
|
+
config = {
|
|
41
|
+
/** 插件通信 HTTP 端口,仅监听 127.0.0.1。默认值见上方 DEFAULT_PORT(HTTP_PORT 环境变量优先) */
|
|
42
|
+
httpPort: Number(process.env.HTTP_PORT ?? DEFAULT_PORT),
|
|
43
|
+
/** MCP HTTP 会话空闲 TTL:超时清扫防泄漏(客户端重连重新 initialize 即可恢复),默认 30 分钟 */
|
|
44
|
+
mcpSessionTtlMs: Number(process.env.MCP_SESSION_TTL_MS ?? 30 * 60 * 1e3),
|
|
45
|
+
/** 产物图片保留时长,默认 7 天 */
|
|
46
|
+
assetTtlMs: Number(process.env.ASSET_TTL_DAYS ?? 7) * 24 * 60 * 60 * 1e3
|
|
47
|
+
};
|
|
48
|
+
PING_TIMEOUT_MS = 800;
|
|
49
|
+
SESSION_ONLINE_WINDOW_MS = 45e3;
|
|
50
|
+
COMMAND_TIMEOUT_MS = 6e4;
|
|
51
|
+
SSE_HEARTBEAT_MS = 15e3;
|
|
52
|
+
MCP_SESSION_SWEEP_MS = 6e4;
|
|
53
|
+
PROBE_TIMEOUT_MS = 1500;
|
|
54
|
+
ASSET_SWEEP_INTERVAL_MS = 60 * 6e4;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
33
57
|
|
|
34
|
-
// src/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
PDF: ".pdf"
|
|
45
|
-
// export_image 尊重设计师导出标记,标记可能指定 PDF
|
|
46
|
-
};
|
|
47
|
-
var dirReady = null;
|
|
48
|
-
async function saveAsset(bytes, format) {
|
|
49
|
-
dirReady ??= mkdir(ASSETS_DIR, { recursive: true });
|
|
50
|
-
await dirReady;
|
|
51
|
-
const ext = EXT_BY_FORMAT[format.toUpperCase()] ?? ".bin";
|
|
52
|
-
const name = `${createHash("sha1").update(bytes).digest("hex").slice(0, 16)}${ext}`;
|
|
53
|
-
await writeFile(join2(ASSETS_DIR, name), bytes);
|
|
54
|
-
log(`asset saved: ${name} (${bytes.length} bytes)`);
|
|
55
|
-
return name;
|
|
56
|
-
}
|
|
57
|
-
function substituteAssets(result, assets) {
|
|
58
|
-
if (!assets.length) return result;
|
|
59
|
-
const nameByKey = new Map(assets.map((a) => [a.key, a.name]));
|
|
60
|
-
return JSON.parse(
|
|
61
|
-
JSON.stringify(
|
|
62
|
-
result,
|
|
63
|
-
(_k, v) => typeof v === "string" ? nameByKey.get(v) ?? v : v
|
|
64
|
-
)
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
async function cleanOldAssets(ttlMs) {
|
|
68
|
-
let removed = 0;
|
|
69
|
-
const cutoff = Date.now() - ttlMs;
|
|
70
|
-
let names;
|
|
58
|
+
// src/cli/service.ts
|
|
59
|
+
var service_exports = {};
|
|
60
|
+
__export(service_exports, {
|
|
61
|
+
PORT_FIX_HINT: () => PORT_FIX_HINT,
|
|
62
|
+
probeService: () => probeService,
|
|
63
|
+
serviceStateError: () => serviceStateError,
|
|
64
|
+
stopRunningService: () => stopRunningService,
|
|
65
|
+
stopService: () => stopService
|
|
66
|
+
});
|
|
67
|
+
async function probeService() {
|
|
71
68
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
for (const name of names) {
|
|
69
|
+
const res = await fetch(`${serviceBaseUrl()}/health`, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
|
|
70
|
+
if (!res.ok) return { state: "foreign" };
|
|
71
|
+
let body;
|
|
77
72
|
try {
|
|
78
|
-
|
|
79
|
-
const s = await stat(p);
|
|
80
|
-
if (s.isFile() && s.mtimeMs < cutoff) {
|
|
81
|
-
await unlink(p);
|
|
82
|
-
removed++;
|
|
83
|
-
}
|
|
73
|
+
body = await res.json();
|
|
84
74
|
} catch {
|
|
75
|
+
return { state: "foreign" };
|
|
85
76
|
}
|
|
86
|
-
|
|
87
|
-
if (removed > 0) log(`assets cleanup: removed ${removed} expired file(s)`);
|
|
88
|
-
return removed;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// src/process/hub-process.ts
|
|
92
|
-
import { spawn } from "node:child_process";
|
|
93
|
-
import { closeSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
94
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
95
|
-
import { join as join3 } from "node:path";
|
|
96
|
-
import { fileURLToPath } from "node:url";
|
|
97
|
-
async function probeHub() {
|
|
98
|
-
try {
|
|
99
|
-
const res = await fetch(`${hubBaseUrl()}/health`, { signal: AbortSignal.timeout(1500) });
|
|
100
|
-
if (!res.ok) return { state: "foreign" };
|
|
101
|
-
const body = await res.json();
|
|
102
|
-
if (body.ok === true && body.name === HUB_NAME) return { state: "alive", health: body };
|
|
77
|
+
if (body.ok === true && body.name === SERVICE_NAME) return { state: "alive", health: body };
|
|
103
78
|
return { state: "foreign" };
|
|
104
79
|
} catch (err) {
|
|
105
80
|
const hung = err instanceof Error && err.name === "TimeoutError";
|
|
106
81
|
return { state: hung ? "hung" : "down" };
|
|
107
82
|
}
|
|
108
83
|
}
|
|
109
|
-
|
|
110
|
-
function hubStateError(state) {
|
|
84
|
+
function serviceStateError(state) {
|
|
111
85
|
if (state === "hung") {
|
|
112
|
-
return new Error(`\u7AEF\u53E3 ${config.httpPort} \u4E0A\u7684\u670D\u52A1\u65E0\u54CD\u5E94\uFF08\u53EF\u80FD\u6302\u6B7B\uFF09\
|
|
86
|
+
return new Error(`\u7AEF\u53E3 ${config.httpPort} \u4E0A\u7684\u670D\u52A1\u65E0\u54CD\u5E94\uFF08\u53EF\u80FD\u6302\u6B7B\uFF09\u3002${PORT_FIX_HINT}`);
|
|
113
87
|
}
|
|
114
88
|
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`);
|
|
115
89
|
}
|
|
116
|
-
|
|
117
|
-
var PID_FILE = join3(tmpdir2(), "batter-pixso-mcp-hub.pid");
|
|
118
|
-
function writePidFile(pid) {
|
|
90
|
+
function killServiceByPid(pid) {
|
|
119
91
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
rmSync(PID_FILE);
|
|
124
|
-
} catch {
|
|
125
|
-
}
|
|
126
|
-
});
|
|
92
|
+
process.kill(pid);
|
|
93
|
+
return true;
|
|
127
94
|
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function stopRunningService() {
|
|
99
|
+
const { state, health } = await probeService();
|
|
100
|
+
if (state !== "alive") return state;
|
|
101
|
+
return killServiceByPid(health.pid) ? "stopped" : "failed";
|
|
102
|
+
}
|
|
103
|
+
async function stopService() {
|
|
104
|
+
const result = await stopRunningService();
|
|
105
|
+
if (result === "down") {
|
|
106
|
+
log(`\u670D\u52A1\u672A\u5728\u8FD0\u884C\uFF08\u7AEF\u53E3 ${config.httpPort} \u65E0\u670D\u52A1\uFF09`);
|
|
107
|
+
} else if (result === "foreign") {
|
|
108
|
+
log(`\u7AEF\u53E3 ${config.httpPort} \u4E0A\u4E0D\u662F batter-pixso-mcp\uFF0C\u672A\u5904\u7406`);
|
|
109
|
+
} else if (result === "stopped") {
|
|
110
|
+
log("\u670D\u52A1\u5DF2\u505C\u6B62");
|
|
111
|
+
} else {
|
|
112
|
+
log(`\u505C\u6B62\u5931\u8D25${result === "hung" ? "\uFF08\u670D\u52A1\u65E0\u54CD\u5E94\uFF0C\u53EF\u80FD\u6302\u6B7B\uFF09" : ""}\u3002${PORT_FIX_HINT}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
var PORT_FIX_HINT;
|
|
116
|
+
var init_service = __esm({
|
|
117
|
+
"src/cli/service.ts"() {
|
|
118
|
+
"use strict";
|
|
119
|
+
init_config();
|
|
120
|
+
PORT_FIX_HINT = `\u7528 netstat -ano | findstr :${config.httpPort} \u67E5\u5230 PID \u540E taskkill /PID <pid> /F \u7ED3\u675F\uFF0C\u518D\u91CD\u8BD5`;
|
|
128
121
|
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// src/install/dist-resolver.ts
|
|
125
|
+
import { existsSync } from "node:fs";
|
|
126
|
+
import { readFile } from "node:fs/promises";
|
|
127
|
+
import { join as join2 } from "node:path";
|
|
128
|
+
import { fileURLToPath } from "node:url";
|
|
129
|
+
function resolvePluginDir() {
|
|
130
|
+
const candidates = [
|
|
131
|
+
fileURLToPath(new URL("./plugin/", import.meta.url)),
|
|
132
|
+
// bundle: dist/index.js → dist/plugin/
|
|
133
|
+
fileURLToPath(new URL("../../plugin/dist/", import.meta.url)),
|
|
134
|
+
// bundle 开发: dist/index.js → packages/plugin/dist/
|
|
135
|
+
fileURLToPath(new URL("../../../plugin/dist/", import.meta.url))
|
|
136
|
+
// tsx 开发: src/install/ → packages/plugin/dist/
|
|
137
|
+
];
|
|
138
|
+
return candidates.find((p) => existsSync(join2(p, "manifest.json"))) ?? null;
|
|
139
|
+
}
|
|
140
|
+
function pluginDistDir() {
|
|
141
|
+
resolved ??= resolvePluginDir();
|
|
142
|
+
return resolved;
|
|
129
143
|
}
|
|
130
|
-
function
|
|
144
|
+
function hasPluginDist() {
|
|
145
|
+
return pluginDistDir() !== null;
|
|
146
|
+
}
|
|
147
|
+
async function pluginManifest() {
|
|
148
|
+
const dir = pluginDistDir();
|
|
149
|
+
if (!dir) return null;
|
|
150
|
+
const raw = JSON.parse(await readFile(join2(dir, "manifest.json"), "utf8"));
|
|
151
|
+
raw.networkAccess = {
|
|
152
|
+
allowedDomains: [`http://localhost:${config.httpPort}`, `http://127.0.0.1:${config.httpPort}`]
|
|
153
|
+
};
|
|
154
|
+
return Buffer.from(JSON.stringify(raw, null, 2));
|
|
155
|
+
}
|
|
156
|
+
async function readPluginFile(name) {
|
|
157
|
+
const dir = pluginDistDir();
|
|
158
|
+
if (!dir) return null;
|
|
131
159
|
try {
|
|
132
|
-
|
|
133
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
160
|
+
return await readFile(join2(dir, name));
|
|
134
161
|
} catch {
|
|
135
162
|
return null;
|
|
136
163
|
}
|
|
137
164
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
165
|
+
var resolved;
|
|
166
|
+
var init_dist_resolver = __esm({
|
|
167
|
+
"src/install/dist-resolver.ts"() {
|
|
168
|
+
"use strict";
|
|
169
|
+
init_config();
|
|
170
|
+
resolved = null;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// src/cli/setup.ts
|
|
175
|
+
var setup_exports = {};
|
|
176
|
+
__export(setup_exports, {
|
|
177
|
+
setupAutostart: () => setupAutostart,
|
|
178
|
+
uninstallAutostart: () => uninstallAutostart
|
|
179
|
+
});
|
|
180
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
181
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
182
|
+
import { copyFile, cp } from "node:fs/promises";
|
|
183
|
+
import { dirname, join as join3 } from "node:path";
|
|
184
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
185
|
+
function serviceCmd() {
|
|
186
|
+
return [
|
|
187
|
+
"@echo off",
|
|
188
|
+
`set HTTP_PORT=${config.httpPort}`,
|
|
189
|
+
`"%~dp0node.exe" "%~dp0index.js" >> "%TEMP%\\${SERVICE_LOG_NAME}" 2>&1`
|
|
190
|
+
].join("\r\n");
|
|
191
|
+
}
|
|
192
|
+
async function withTransientLockRetry(what, action) {
|
|
193
|
+
for (let i = 0; ; i++) {
|
|
194
|
+
try {
|
|
195
|
+
await action();
|
|
196
|
+
return;
|
|
197
|
+
} catch (err) {
|
|
198
|
+
const code = err.code ?? "";
|
|
199
|
+
if (!TRANSIENT_LOCK_ERRORS.has(code) || i >= 4) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`\u62F7\u8D1D\u5931\u8D25 ${what}\uFF08${code || err}\uFF09\uFF1A\u6587\u4EF6\u88AB\u5360\u7528\u2014\u2014\u82E5\u81EA\u542F\u5B9E\u4F8B\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148 --stop\uFF1B\u5426\u5219\u591A\u4E3A\u6740\u6BD2\u8F6F\u4EF6\u626B\u63CF\u9501\u5B9A\uFF0C\u7A0D\u5019\u91CD\u8BD5`,
|
|
202
|
+
{ cause: err }
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
await sleep(500);
|
|
206
|
+
}
|
|
142
207
|
}
|
|
143
208
|
}
|
|
144
|
-
function
|
|
209
|
+
async function copyWithRetry(src, dest) {
|
|
210
|
+
await withTransientLockRetry(dest, () => copyFile(src, dest));
|
|
211
|
+
}
|
|
212
|
+
async function materializeAutostart() {
|
|
213
|
+
if (!DIR) throw new Error("\u65E0\u6CD5\u5B9A\u4F4D\u7269\u5316\u76EE\u5F55\uFF1A\u73AF\u5883\u53D8\u91CF LOCALAPPDATA \u672A\u8BBE\u7F6E");
|
|
214
|
+
const bundle = fileURLToPath2(import.meta.url);
|
|
215
|
+
if (!bundle.endsWith(".js")) {
|
|
216
|
+
throw new Error("--setup \u4EC5\u652F\u6301\u53D1\u5E03\u5305\u5F62\u6001\uFF08dist bundle\uFF09\uFF1Btsx \u5F00\u53D1\u6001\u8BF7\u5148 npm run build \u540E\u7528 dist \u5165\u53E3\u6267\u884C");
|
|
217
|
+
}
|
|
218
|
+
const standalone = join3(dirname(bundle), "..", "standalone", "index.js");
|
|
219
|
+
if (!existsSync2(standalone)) {
|
|
220
|
+
throw new Error("\u672A\u627E\u5230 standalone bundle\uFF08standalone/index.js\uFF09\uFF1B\u4ED3\u5E93\u5185\u8BF7\u5148\u6267\u884C npm run pack:standalone -w batter-pixso-mcp");
|
|
221
|
+
}
|
|
222
|
+
const fresh = autostartVersion() === VERSION && existsSync2(join3(DIR, "node.exe")) && existsSync2(join3(DIR, "index.js")) && existsSync2(join3(DIR, "plugin", "manifest.json"));
|
|
223
|
+
mkdirSync(DIR, { recursive: true });
|
|
224
|
+
if (!fresh) {
|
|
225
|
+
const plugin = pluginDistDir();
|
|
226
|
+
await Promise.all([
|
|
227
|
+
copyWithRetry(process.execPath, join3(DIR, "node.exe")),
|
|
228
|
+
copyWithRetry(standalone, join3(DIR, "index.js")),
|
|
229
|
+
...plugin ? [withTransientLockRetry(join3(DIR, "plugin"), () => cp(plugin, join3(DIR, "plugin"), { recursive: true }))] : []
|
|
230
|
+
]);
|
|
231
|
+
}
|
|
232
|
+
writeFileSync(join3(DIR, "service.cmd"), serviceCmd(), "utf8");
|
|
233
|
+
writeFileSync(join3(DIR, "run.vbs"), RUN_VBS, "utf8");
|
|
234
|
+
writeFileSync(join3(DIR, "version.txt"), VERSION, "utf8");
|
|
235
|
+
}
|
|
236
|
+
function autostartVersion() {
|
|
237
|
+
if (!DIR) return null;
|
|
145
238
|
try {
|
|
146
|
-
|
|
147
|
-
return true;
|
|
239
|
+
return readFileSync(join3(DIR, "version.txt"), "utf8").trim() || null;
|
|
148
240
|
} catch {
|
|
149
|
-
return
|
|
241
|
+
return null;
|
|
150
242
|
}
|
|
151
243
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
244
|
+
async function setupAutostart() {
|
|
245
|
+
if (process.platform !== "win32") {
|
|
246
|
+
throw new Error("--setup \u5F53\u524D\u4EC5\u652F\u6301 Windows\uFF1Bmac/Linux \u8BF7\u7528\u7CFB\u7EDF\u5F00\u673A\u542F\u52A8\u673A\u5236\u914D\u7F6E npx batter-pixso-mcp");
|
|
247
|
+
}
|
|
248
|
+
await materializeAutostart();
|
|
249
|
+
const target = `wscript.exe "${join3(DIR, "run.vbs")}"`;
|
|
250
|
+
const r = spawnSync("reg", ["add", RUN_KEY, "/v", RUN_VALUE, "/t", "REG_SZ", "/d", target, "/f"]);
|
|
251
|
+
if (r.status !== 0) {
|
|
252
|
+
throw new Error(`\u6CE8\u518C\u5F00\u673A\u81EA\u542F\u5931\u8D25\uFF1A${r.stderr?.toString().trim() || `reg exit ${r.status}`}`);
|
|
253
|
+
}
|
|
254
|
+
log(`\u5F00\u673A\u81EA\u542F\u5DF2\u6CE8\u518C\uFF1A\u767B\u5F55\u540E\u9759\u9ED8\u542F\u52A8\u670D\u52A1\uFF08\u526F\u672C\u5728 ${DIR}\uFF0C\u4E0E nvm/npm \u72B6\u6001\u65E0\u5173\uFF09`);
|
|
255
|
+
await startHiddenNow();
|
|
158
256
|
}
|
|
159
|
-
function
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const child = spawn(process.execPath, [...execArgv, entry, "--hub"], {
|
|
168
|
-
detached: true,
|
|
169
|
-
stdio: ["ignore", out, out],
|
|
170
|
-
windowsHide: true,
|
|
171
|
-
env: { ...process.env }
|
|
172
|
-
});
|
|
173
|
-
child.on("error", (err) => log("hub spawn \u5931\u8D25:", err.message));
|
|
174
|
-
child.unref();
|
|
175
|
-
closeSync(out);
|
|
176
|
-
log(`hub spawned (pid ${child.pid}), log: ${HUB_LOG}`);
|
|
177
|
-
}
|
|
178
|
-
async function waitForHub(timeoutMs = 15e3) {
|
|
179
|
-
const deadline = Date.now() + timeoutMs;
|
|
180
|
-
for (; ; ) {
|
|
181
|
-
const { state } = await probeHub();
|
|
182
|
-
if (state === "alive") return;
|
|
183
|
-
if (state !== "down") throw hubStateError(state);
|
|
184
|
-
if (Date.now() > deadline) throw new Error(`\u7B49\u5F85 hub \u5C31\u7EEA\u8D85\u65F6\uFF08${timeoutMs / 1e3}s\uFF09`);
|
|
185
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
257
|
+
async function startHiddenNow() {
|
|
258
|
+
const { state, health } = await probeService();
|
|
259
|
+
if (state === "alive") {
|
|
260
|
+
if (health?.version === VERSION) {
|
|
261
|
+
log("\u670D\u52A1\u5DF2\u5728\u8FD0\u884C\u4E14\u7248\u672C\u4E00\u81F4\uFF0C\u65E0\u9700\u91CD\u590D\u62C9\u8D77");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
throw new Error(`\u670D\u52A1\u5DF2\u5728\u8FD0\u884C\u4E14\u7248\u672C\u4E0D\u7B26\uFF08\u8FD0\u884C ${health?.version} / \u5F53\u524D ${VERSION}\uFF09\u2014\u2014\u5148\u6267\u884C --stop \u518D\u91CD\u8DD1 --setup`);
|
|
186
265
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
266
|
+
if (state === "hung" || state === "foreign") {
|
|
267
|
+
throw serviceStateError(state);
|
|
268
|
+
}
|
|
269
|
+
spawn("wscript.exe", [join3(DIR, "run.vbs")], { detached: true, stdio: "ignore" }).unref();
|
|
270
|
+
const deadline = Date.now() + 1e4;
|
|
190
271
|
for (; ; ) {
|
|
191
|
-
|
|
192
|
-
if (state === "
|
|
193
|
-
|
|
194
|
-
|
|
272
|
+
await sleep(300);
|
|
273
|
+
if ((await probeService()).state === "alive") {
|
|
274
|
+
log(`\u670D\u52A1\u5DF2\u5728\u540E\u53F0\u542F\u52A8\uFF08\u65E5\u5FD7\uFF1A%TEMP%\\${SERVICE_LOG_NAME}\uFF09`);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (Date.now() > deadline) {
|
|
278
|
+
throw new Error(`\u540E\u53F0\u670D\u52A1 10s \u5185\u672A\u5C31\u7EEA\u2014\u2014\u67E5\u770B\u65E5\u5FD7 %TEMP%\\${SERVICE_LOG_NAME}`);
|
|
279
|
+
}
|
|
195
280
|
}
|
|
196
281
|
}
|
|
197
|
-
async function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
log(
|
|
201
|
-
return;
|
|
282
|
+
async function uninstallAutostart() {
|
|
283
|
+
if (process.platform === "win32") {
|
|
284
|
+
spawnSync("reg", ["delete", RUN_KEY, "/v", RUN_VALUE, "/f"]);
|
|
285
|
+
log("\u5DF2\u79FB\u9664\u5F00\u673A\u81EA\u542F\uFF08\u82E5\u66FE\u6CE8\u518C\uFF09");
|
|
202
286
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
287
|
+
const result = await stopRunningService();
|
|
288
|
+
if (result === "failed" || result === "hung") {
|
|
289
|
+
log(`\u670D\u52A1\u505C\u6B62\u5931\u8D25\uFF0C\u7269\u5316\u76EE\u5F55\u53EF\u80FD\u5220\u4E0D\u51C0\u3002${PORT_FIX_HINT}`);
|
|
206
290
|
}
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
291
|
+
if (DIR && existsSync2(DIR)) {
|
|
292
|
+
try {
|
|
293
|
+
rmSync(DIR, { recursive: true, force: true, maxRetries: 4, retryDelay: 500 });
|
|
294
|
+
} catch {
|
|
211
295
|
}
|
|
212
|
-
log(
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const pid = health.pid;
|
|
216
|
-
if (killHubByPid(pid)) {
|
|
217
|
-
removePidFile();
|
|
218
|
-
log(`hub \u5DF2\u505C\u6B62\uFF08pid ${pid}\uFF09\u3002\u4E0B\u6B21 agent \u8BF7\u6C42\u4F1A\u81EA\u52A8\u62C9\u8D77\u65B0\u7248\u672C`);
|
|
219
|
-
} else {
|
|
220
|
-
log(`\u505C\u6B62 pid ${pid} \u5931\u8D25\u3002${PORT_FIX_HINT}`);
|
|
296
|
+
log(existsSync2(DIR) ? `\u76EE\u5F55\u5220\u9664\u5931\u8D25\uFF0C\u53EF\u624B\u52A8\u5220\u9664\uFF1A${DIR}` : `\u5DF2\u5220\u9664\u7269\u5316\u76EE\u5F55 ${DIR}`);
|
|
221
297
|
}
|
|
298
|
+
log("\u5378\u8F7D\u5B8C\u6210\uFF08npm \u5168\u5C40/npx \u7F13\u5B58\u4E2D\u7684\u5305\u5982\u9700\u6E05\u7406\u8BF7\u81EA\u884C npm uninstall\uFF09");
|
|
222
299
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
300
|
+
var DIR, RUN_KEY, RUN_VALUE, SERVICE_LOG_NAME, RUN_VBS, TRANSIENT_LOCK_ERRORS;
|
|
301
|
+
var init_setup = __esm({
|
|
302
|
+
"src/cli/setup.ts"() {
|
|
303
|
+
"use strict";
|
|
304
|
+
init_config();
|
|
305
|
+
init_dist_resolver();
|
|
306
|
+
init_service();
|
|
307
|
+
DIR = process.env.LOCALAPPDATA ? join3(process.env.LOCALAPPDATA, "batter-pixso-mcp") : null;
|
|
308
|
+
RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
309
|
+
RUN_VALUE = "BatterPixsoMcp";
|
|
310
|
+
SERVICE_LOG_NAME = "batter-pixso-mcp-service.log";
|
|
311
|
+
RUN_VBS = [
|
|
312
|
+
"Set fso = CreateObject(" + JSON.stringify("Scripting.FileSystemObject") + ")",
|
|
313
|
+
"dir = fso.GetParentFolderName(WScript.ScriptFullName)",
|
|
314
|
+
// 路径由脚本自身位置推导,不写死绝对路径(用户名含中文/空格时 VBS 字面量会翻车)
|
|
315
|
+
'CreateObject("Wscript.Shell").Run """" & dir & "\\service.cmd""", 0, False'
|
|
316
|
+
].join("\r\n");
|
|
317
|
+
TRANSIENT_LOCK_ERRORS = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
231
320
|
|
|
232
321
|
// src/http-body.ts
|
|
233
322
|
function sendJson(res, status, body) {
|
|
@@ -239,8 +328,6 @@ async function readBody(req) {
|
|
|
239
328
|
for await (const chunk of req) chunks.push(chunk);
|
|
240
329
|
return Buffer.concat(chunks);
|
|
241
330
|
}
|
|
242
|
-
var JsonBodyError = class extends Error {
|
|
243
|
-
};
|
|
244
331
|
async function readJsonBody(req) {
|
|
245
332
|
const buf = await readBody(req);
|
|
246
333
|
try {
|
|
@@ -249,7 +336,6 @@ async function readJsonBody(req) {
|
|
|
249
336
|
throw new JsonBodyError("invalid json");
|
|
250
337
|
}
|
|
251
338
|
}
|
|
252
|
-
var segsCache = /* @__PURE__ */ new Map();
|
|
253
339
|
function segsOf(template) {
|
|
254
340
|
let segs = segsCache.get(template);
|
|
255
341
|
if (!segs) {
|
|
@@ -284,7 +370,7 @@ async function dispatchRoute(routes, req, res, url) {
|
|
|
284
370
|
if (err instanceof JsonBodyError) {
|
|
285
371
|
if (!res.headersSent) sendJson(res, 400, { error: "invalid json" });
|
|
286
372
|
} else {
|
|
287
|
-
|
|
373
|
+
log("route handler error:", err);
|
|
288
374
|
if (!res.headersSent) sendJson(res, 500, { error: "internal error" });
|
|
289
375
|
}
|
|
290
376
|
}
|
|
@@ -292,26 +378,32 @@ async function dispatchRoute(routes, req, res, url) {
|
|
|
292
378
|
}
|
|
293
379
|
return false;
|
|
294
380
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
381
|
+
var JsonBodyError, segsCache;
|
|
382
|
+
var init_http_body = __esm({
|
|
383
|
+
"src/http-body.ts"() {
|
|
384
|
+
"use strict";
|
|
385
|
+
init_config();
|
|
386
|
+
JsonBodyError = class extends Error {
|
|
387
|
+
};
|
|
388
|
+
segsCache = /* @__PURE__ */ new Map();
|
|
389
|
+
}
|
|
390
|
+
});
|
|
305
391
|
|
|
306
392
|
// src/agent/guides.ts
|
|
307
|
-
var INTRO
|
|
393
|
+
var INTRO, DESIGN_TO_CODE, GUIDES;
|
|
394
|
+
var init_guides = __esm({
|
|
395
|
+
"src/agent/guides.ts"() {
|
|
396
|
+
"use strict";
|
|
397
|
+
init_config();
|
|
398
|
+
INTRO = `# batter-pixso-mcp \u4F7F\u7528\u6307\u5357
|
|
308
399
|
|
|
309
400
|
## \u5DE5\u5177\u603B\u89C8
|
|
310
401
|
|
|
311
402
|
| \u5DE5\u5177 | \u7528\u9014 |
|
|
312
403
|
| --- | --- |
|
|
313
|
-
| \`get_selected\` | \u8BFB\u753B\u5E03\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\
|
|
404
|
+
| \`get_selected\` | \u8BFB\u753B\u5E03\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\uFF09 |
|
|
314
405
|
| \`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 |
|
|
406
|
+
| \`get_page_metadata\` | \u5F53\u524D\u9875\u9876\u5C42\u9AA8\u67B6\uFF08\u9875\u9762 id/name/depth + \u9876\u5C42\u56FE\u5C42\u5217\u8868\uFF09\uFF0C\u65E0 guid \u65F6\u7684\u51B7\u542F\u52A8\u5165\u53E3 |
|
|
315
407
|
| \`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 |
|
|
316
408
|
| \`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 |
|
|
317
409
|
| \`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 |
|
|
@@ -327,7 +419,7 @@ var INTRO = `# batter-pixso-mcp \u4F7F\u7528\u6307\u5357
|
|
|
327
419
|
|
|
328
420
|
\u6BCF\u4E2A\u8FD0\u884C\u63D2\u4EF6\u7684\u8BBE\u8BA1\u6587\u4EF6\u662F\u4E00\u4E2A\u4F1A\u8BDD\u3002\u8282\u70B9\u7C7B\u5DE5\u5177\uFF08\`get_node_dsl\`/\`get_metadata\`/\`get_screenshot\`/\`export_image\`\uFF09
|
|
329
421
|
\u7684 \`guid\` \u5FC5\u4F20\uFF0C\u4E14 \`fileKey\`/\`sessionId\` \u4E8C\u9009\u4E00\u5FC5\u4F20\uFF1A\u5148\u7528 \`list_sessions\` \u786E\u8BA4\u76EE\u6807\u6587\u4EF6\uFF0C\u628A\u8FD4\u56DE\u7684 \`fileKey\`
|
|
330
|
-
\u4F5C\u4E3A\u8DEF\u7531\u53C2\u6570\u4F20\u5165\uFF08\u8DE8\u91CD\u5F00\u7A33\u5B9A\uFF0C\u4F18\u5148\u4E8E \`sessionId\`\uFF09\uFF1B\u4EC5 \`get_selected\`/\`list_sessions\` \u53EF\u7F3A\u7701\u8DEF\u7531\u53C2\u6570\u505A\u63A2\u67E5\u3002
|
|
422
|
+
\u4F5C\u4E3A\u8DEF\u7531\u53C2\u6570\u4F20\u5165\uFF08\u8DE8\u91CD\u5F00\u7A33\u5B9A\uFF0C\u4F18\u5148\u4E8E \`sessionId\`\uFF09\uFF1B\u4EC5 \`get_selected\`/\`get_page_metadata\`/\`list_sessions\` \u53EF\u7F3A\u7701\u8DEF\u7531\u53C2\u6570\u505A\u63A2\u67E5\u3002
|
|
331
423
|
|
|
332
424
|
**\u4F1A\u8BDD\u89E3\u6790\u4E0E\u4FE1\u5C01**\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u7684\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, fileKey, data}\`\u2014\u2014\`sessionId\` \u662F\u672C\u6B21\u5B9E\u9645\u670D\u52A1\u7684\u4F1A\u8BDD
|
|
333
425
|
\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\uFF1B\`fileKey\` \u662F\u8DE8\u91CD\u5F00\u7A33\u5B9A\u7684\u53E5\u67C4\uFF0C\u540E\u7EED\u8C03\u7528
|
|
@@ -336,7 +428,7 @@ var INTRO = `# batter-pixso-mcp \u4F7F\u7528\u6307\u5357
|
|
|
336
428
|
|
|
337
429
|
\u8BE6\u7EC6\u5DE5\u4F5C\u6D41\u89C1\u8D44\u6E90 \`batter://guides/design-to-code\`\u3002
|
|
338
430
|
`;
|
|
339
|
-
|
|
431
|
+
DESIGN_TO_CODE = `# \u8BBE\u8BA1\u8F6C\u4EE3\u7801\u5DE5\u4F5C\u6D41
|
|
340
432
|
|
|
341
433
|
## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011
|
|
342
434
|
|
|
@@ -354,6 +446,11 @@ var DESIGN_TO_CODE = `# \u8BBE\u8BA1\u8F6C\u4EE3\u7801\u5DE5\u4F5C\u6D41
|
|
|
354
446
|
\`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
|
|
355
447
|
\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
|
|
356
448
|
\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
|
|
449
|
+
3. **DSL \u662F\u7ED3\u6784\u7684\u552F\u4E00\u4E8B\u5B9E\u6E90**\uFF1A\u4F4D\u7F6E/\u5C3A\u5BF8/\u5C42\u7EA7/\u95F4\u8DDD/\u989C\u8272\u4E00\u5F8B\u4EE5 DSL \u5B57\u6BB5\u4E3A\u51C6\u3002\u622A\u56FE\u53EA\u7528\u4E8E\u89C6\u89C9\u6821\u9A8C\u4E0E\u610F\u56FE\u53C2\u7167\u2014\u2014
|
|
450
|
+
\u7981\u6B62\u526A\u88C1/\u653E\u5927\u622A\u56FE\u91CF\u53D6\u50CF\u7D20\u6765\u63A8\u6D4B\u5E03\u5C40\u6216\u8BA1\u7B97\u4F4D\u7F6E\uFF1B\u7981\u6B62\u8131\u79BB DSL \u51ED\u622A\u56FE\u786C\u7F16\u7801\u7ED3\u6784\u3002
|
|
451
|
+
4. **\u8D44\u6E90\u5B8C\u6574\u6027**\uFF1A\u8282\u70B9\u5E26 \`img\` \u65F6\u5FC5\u987B\u5F15\u7528\u8BE5\u5BFC\u51FA\u6587\u4EF6\uFF0C\u7981\u6B62\u6309 DSL \u7684\u5C3A\u5BF8/\u989C\u8272\u624B\u5199 SVG/Canvas/CSS \u5F62\u72B6\u53BB"\u8FD1\u4F3C"\u5B83\uFF1B
|
|
452
|
+
\u5BFC\u51FA\u4EA7\u7269\u5373\u4EA4\u4ED8\u7D20\u6750\u672C\u4F53\u2014\u2014\u7981\u6B62\u7F29\u653E/\u538B\u7F29/\u8F6C\u683C\u5F0F/\u88C1\u5207/\u5408\u6210\u7B49\u4EFB\u4F55\u4E8C\u6B21\u5904\u7406\uFF0C
|
|
453
|
+
\u9700\u8981\u4E0D\u540C\u5C3A\u5BF8\u3001\u500D\u6570\u6216\u683C\u5F0F\u65F6\u6309\u8282\u70B9 id \u91CD\u8C03 \`export_image\` \u6307\u5B9A \`scale\`/\`format\` \u56DE\u6E90\u83B7\u53D6\u3002
|
|
357
454
|
|
|
358
455
|
## \u63A8\u8350\u6B65\u9AA4
|
|
359
456
|
|
|
@@ -368,6 +465,8 @@ var DESIGN_TO_CODE = `# \u8BBE\u8BA1\u8F6C\u4EE3\u7801\u5DE5\u4F5C\u6D41
|
|
|
368
465
|
**\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)\`
|
|
369
466
|
\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
|
|
370
467
|
\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
|
|
468
|
+
**\u8FD8\u6CA1\u6709\u4EFB\u4F55 guid**\uFF08\u7528\u6237\u6CA1\u7ED9\u94FE\u63A5\u4E5F\u6CA1\u9009\u4E2D\u56FE\u5C42\uFF09\uFF1A\u5148 \`get_page_metadata()\` \u62FF\u5F53\u524D\u9875\u9876\u5C42\u9AA8\u67B6
|
|
469
|
+
\uFF08\u9875\u9762 id/name/depth + \u9876\u5C42\u56FE\u5C42 \`kids\`\uFF09\uFF0C\u628A\u76EE\u6807\u56FE\u5C42\u7684 \`id\` \u4F5C\u4E3A guid \u7EE7\u7EED\u4E0A\u8FF0\u6D41\u7A0B\uFF1B\u8FD4\u56DE\u7684\u9875\u9762 id \u4E0D\u53EF\u56DE\u4F20\u8282\u70B9\u7C7B\u5DE5\u5177\u3002
|
|
371
470
|
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
|
|
372
471
|
4. **\u751F\u6210\u4EE3\u7801**\uFF1A\u6309\u4E0B\u6587 DSL \u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002
|
|
373
472
|
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
|
|
@@ -416,7 +515,7 @@ DSL \u662F\u5355\u884C\u7D27\u51D1 JSON\uFF1A\u77ED key\u3001\u4F4D\u7F6E\u6570\
|
|
|
416
515
|
}
|
|
417
516
|
\`\`\`
|
|
418
517
|
|
|
419
|
-
**\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
|
|
518
|
+
**\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 4 \u5C42\uFF0C\u4F20 -1 \u4E0D\u9650\uFF09\u3002
|
|
420
519
|
\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
|
|
421
520
|
\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
|
|
422
521
|
|
|
@@ -427,11 +526,10 @@ DSL \u662F\u5355\u884C\u7D27\u51D1 JSON\uFF1A\u77ED key\u3001\u4F4D\u7F6E\u6570\
|
|
|
427
526
|
|
|
428
527
|
## \u9519\u8BEF\u6062\u590D
|
|
429
528
|
|
|
430
|
-
- **\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
|
|
529
|
+
- **\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\uFF1B\u53EA\u8981\u63A2\u7D22\u5F53\u524D\u9875\u5185\u5BB9\u65F6\u7528 \`get_page_metadata\` \u81EA\u4E3E\uFF0C\u65E0\u9700 guid\u3002
|
|
431
530
|
- **\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
|
|
432
531
|
- **\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
|
|
433
532
|
- **\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\u7528 fileKey \u5BFB\u5740\u4F1A\u81EA\u52A8\u89E3\u6790\u5230\u65B0\u5B9E\u4F8B\u3002
|
|
434
|
-
- **\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
|
|
435
533
|
|
|
436
534
|
## \u7EC4\u4EF6\u5173\u8054\uFF08comp \u5B57\u6BB5\uFF09
|
|
437
535
|
|
|
@@ -460,252 +558,272 @@ INSTANCE / COMPONENT / COMPONENT_SET \u8282\u70B9\u5E26 \`comp\` \u5B57\u6BB5\uF
|
|
|
460
558
|
\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\u4F46\u7ED3\u679C\u5BF9\u4E0D\u4E0A\u65F6\uFF0C\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\u3002
|
|
461
559
|
- \u591A\u6587\u4EF6\u573A\u666F\u8282\u70B9\u7C7B\u5DE5\u5177\u7528 \`fileKey\`/\`sessionId\` \u53C2\u6570\u8DEF\u7531\uFF08\u4E8C\u9009\u4E00\u5FC5\u4F20\uFF09\uFF0C\u89C1 \`batter://guides/intro\`\u3002
|
|
462
560
|
`;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
561
|
+
GUIDES = [
|
|
562
|
+
{
|
|
563
|
+
name: "intro",
|
|
564
|
+
uri: "batter://guides/intro",
|
|
565
|
+
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",
|
|
566
|
+
text: INTRO
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
name: "design-to-code",
|
|
570
|
+
uri: "batter://guides/design-to-code",
|
|
571
|
+
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",
|
|
572
|
+
text: DESIGN_TO_CODE
|
|
573
|
+
}
|
|
574
|
+
];
|
|
475
575
|
}
|
|
476
|
-
|
|
576
|
+
});
|
|
477
577
|
|
|
478
578
|
// src/agent/prompts.ts
|
|
479
|
-
var DESIGN_TO_CODE2
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
{
|
|
484
|
-
name: "
|
|
485
|
-
description: "
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
return [
|
|
492
|
-
{
|
|
493
|
-
role: "user",
|
|
494
|
-
content: {
|
|
495
|
-
type: "text",
|
|
496
|
-
text: [
|
|
497
|
-
`\u628A Pixso \u8BBE\u8BA1\u7A3F\u8FD8\u539F\u4E3A\u524D\u7AEF\u4EE3\u7801\u3002`,
|
|
498
|
-
``,
|
|
499
|
-
`## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011`,
|
|
500
|
-
``,
|
|
501
|
-
`**\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`,
|
|
502
|
-
``,
|
|
503
|
-
`## \u6267\u884C\u6D41\u7A0B`,
|
|
504
|
-
``,
|
|
505
|
-
`1. \u786E\u8BA4\u76EE\u6807\u8282\u70B9\uFF1A${targetLine}`,
|
|
506
|
-
`2. \u53D6\u6570\u636E\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, fileKey, data}\` \u4FE1\u5C01\uFF0C\u53D6 \`data\` \u5B57\u6BB5\u3002\u8282\u70B9\u7C7B\u5DE5\u5177\u7684 guid \u5FC5\u4F20\uFF0CfileKey/sessionId \u5FC5\u4F20\u5176\u4E00\uFF08\u4F18\u5148 fileKey\uFF1B\u9009\u533A\u573A\u666F\u7528 \`get_selected\` \u8FD4\u56DE\u7684 fileKey\uFF09\u3002\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\uFF1A\u5148 \`get_metadata(guid, fileKey)\` \u62FF\u5168\u6811\u9AA8\u67B6\u5B9A\u4F4D\u76EE\u6807 + \`get_screenshot(guid, fileKey)\` \u62FF\u89C6\u89C9\u5168\u8C8C\uFF1B\u968F\u540E \`get_node_dsl(guid, fileKey)\` \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`,
|
|
507
|
-
`3. \u751F\u6210\u4EE3\u7801\uFF1A\u6309\u6307\u5357\u4E2D\u7684 DSL \u5B57\u6BB5\u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002`,
|
|
508
|
-
`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`,
|
|
509
|
-
``,
|
|
510
|
-
`**\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`
|
|
511
|
-
].join("\n")
|
|
579
|
+
var DESIGN_TO_CODE2, PROMPTS;
|
|
580
|
+
var init_prompts = __esm({
|
|
581
|
+
"src/agent/prompts.ts"() {
|
|
582
|
+
"use strict";
|
|
583
|
+
DESIGN_TO_CODE2 = {
|
|
584
|
+
name: "design-to-code",
|
|
585
|
+
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",
|
|
586
|
+
arguments: [
|
|
587
|
+
{
|
|
588
|
+
name: "url",
|
|
589
|
+
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",
|
|
590
|
+
required: false
|
|
512
591
|
}
|
|
592
|
+
],
|
|
593
|
+
render: (args) => {
|
|
594
|
+
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`;
|
|
595
|
+
return [
|
|
596
|
+
{
|
|
597
|
+
role: "user",
|
|
598
|
+
content: {
|
|
599
|
+
type: "text",
|
|
600
|
+
text: [
|
|
601
|
+
`\u628A Pixso \u8BBE\u8BA1\u7A3F\u8FD8\u539F\u4E3A\u524D\u7AEF\u4EE3\u7801\u3002`,
|
|
602
|
+
``,
|
|
603
|
+
`## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011`,
|
|
604
|
+
``,
|
|
605
|
+
`**\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\u4E0E\u5B8C\u6574\u6027/DSL \u4E3A\u7ED3\u6784\u4E8B\u5B9E\u6E90/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D\uFF09\uFF1B\u603B\u89C8\u89C1 \`batter://guides/intro\`\u3002`,
|
|
606
|
+
``,
|
|
607
|
+
`## \u6267\u884C\u6D41\u7A0B`,
|
|
608
|
+
``,
|
|
609
|
+
`1. \u786E\u8BA4\u76EE\u6807\u8282\u70B9\uFF1A${targetLine}`,
|
|
610
|
+
`2. \u53D6\u6570\u636E\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, fileKey, data}\` \u4FE1\u5C01\uFF0C\u53D6 \`data\` \u5B57\u6BB5\u3002\u8282\u70B9\u7C7B\u5DE5\u5177\u7684 guid \u5FC5\u4F20\uFF0CfileKey/sessionId \u5FC5\u4F20\u5176\u4E00\uFF08\u4F18\u5148 fileKey\uFF1B\u9009\u533A\u573A\u666F\u7528 \`get_selected\` \u8FD4\u56DE\u7684 fileKey\uFF09\u3002\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\uFF1A\u6709 guid \u5148 \`get_metadata(guid, fileKey)\` \u62FF\u5168\u6811\u9AA8\u67B6\u5B9A\u4F4D\u76EE\u6807 + \`get_screenshot(guid, fileKey)\` \u62FF\u89C6\u89C9\u5168\u8C8C\uFF1B\u6CA1\u6709\u4EFB\u4F55 guid\uFF08\u6CA1\u7ED9\u94FE\u63A5\u4E5F\u6CA1\u9009\u4E2D\uFF09\u5148 \`get_page_metadata()\` \u62FF\u5F53\u524D\u9875\u9876\u5C42\u9AA8\u67B6\uFF0C\u7528\u5176 kids \u7684 id \u4F5C guid \u7EE7\u7EED\uFF1B\u968F\u540E \`get_node_dsl(guid, fileKey)\` \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`,
|
|
611
|
+
`3. \u751F\u6210\u4EE3\u7801\uFF1A\u6309\u6307\u5357\u4E2D\u7684 DSL \u5B57\u6BB5\u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002`,
|
|
612
|
+
`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`,
|
|
613
|
+
``,
|
|
614
|
+
`**\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\uFF1B\u4F4D\u7F6E/\u5C3A\u5BF8/\u5C42\u7EA7\u53EA\u4FE1 DSL\uFF0C\u7981\u6B62\u526A\u88C1\u6216\u653E\u5927\u622A\u56FE\u91CF\u53D6\u50CF\u7D20\u63A8\u5E03\u5C40\uFF08\u7B2C 3 \u6761\uFF09\uFF1B\u5E26 \`img\` \u7684\u8282\u70B9\u5FC5\u987B\u5F15\u7528\u5BFC\u51FA\u6587\u4EF6\uFF0C\u7981\u6B62\u624B\u5199 SVG \u8FD1\u4F3C\uFF0C\u5BFC\u51FA\u56FE\u7247\u7981\u6B62\u7F29\u653E/\u8F6C\u683C\u5F0F/\u88C1\u5207\u7B49\u4E8C\u6B21\u5904\u7406\u2014\u2014\u8981\u522B\u7684\u5C3A\u5BF8\u6216\u683C\u5F0F\u5C31\u91CD\u8C03 \`export_image\`\uFF08\u7B2C 4 \u6761\uFF09\u3002`
|
|
615
|
+
].join("\n")
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
];
|
|
513
619
|
}
|
|
514
|
-
|
|
620
|
+
};
|
|
621
|
+
PROMPTS = [DESIGN_TO_CODE2];
|
|
515
622
|
}
|
|
516
|
-
};
|
|
517
|
-
var PROMPTS = [DESIGN_TO_CODE2];
|
|
623
|
+
});
|
|
518
624
|
|
|
519
625
|
// src/plugin/command-bus.ts
|
|
520
|
-
var CommandBus
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
626
|
+
var CommandBus, commandBus;
|
|
627
|
+
var init_command_bus = __esm({
|
|
628
|
+
"src/plugin/command-bus.ts"() {
|
|
629
|
+
"use strict";
|
|
630
|
+
init_config();
|
|
631
|
+
CommandBus = class {
|
|
632
|
+
sseListeners = /* @__PURE__ */ new Map();
|
|
633
|
+
pendingById = /* @__PURE__ */ new Map();
|
|
634
|
+
seq = 0;
|
|
635
|
+
/** AI 侧:执行命令 */
|
|
636
|
+
execute(type, args, opts) {
|
|
637
|
+
const timeoutMs = opts.timeoutMs ?? COMMAND_TIMEOUT_MS;
|
|
638
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
639
|
+
const command = { id: `cmd-${++this.seq}`, type, args };
|
|
640
|
+
const entry = {
|
|
641
|
+
command,
|
|
642
|
+
sessionId: opts.sessionId,
|
|
643
|
+
resolve,
|
|
644
|
+
reject,
|
|
645
|
+
dispatched: false,
|
|
646
|
+
timer: setTimeout(() => {
|
|
647
|
+
this.drop(entry);
|
|
648
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u8D85\u65F6\uFF1A\u63D2\u4EF6 ${timeoutMs / 1e3}s \u5185\u672A\u8FD4\u56DE\u7ED3\u679C`));
|
|
649
|
+
}, timeoutMs)
|
|
650
|
+
};
|
|
651
|
+
opts.signal?.addEventListener("abort", () => {
|
|
652
|
+
if (!this.pendingById.has(command.id)) return;
|
|
653
|
+
this.drop(entry);
|
|
654
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u5DF2\u88AB\u8C03\u7528\u65B9\u53D6\u6D88`));
|
|
655
|
+
});
|
|
656
|
+
this.pendingById.set(command.id, entry);
|
|
657
|
+
this.dispatch(opts.sessionId);
|
|
658
|
+
return promise;
|
|
659
|
+
}
|
|
660
|
+
/** 插件侧:订阅即时推送(SSE 通道),返回退订函数;补发订阅前已入队的命令 */
|
|
661
|
+
subscribe(sessionId, listener) {
|
|
662
|
+
let subs = this.sseListeners.get(sessionId);
|
|
663
|
+
if (!subs) {
|
|
664
|
+
subs = /* @__PURE__ */ new Set();
|
|
665
|
+
this.sseListeners.set(sessionId, subs);
|
|
666
|
+
}
|
|
667
|
+
subs.add(listener);
|
|
668
|
+
this.dispatch(sessionId);
|
|
669
|
+
return () => {
|
|
670
|
+
subs.delete(listener);
|
|
671
|
+
if (subs.size === 0) this.sseListeners.delete(sessionId);
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
/** 插件侧:回传结果 */
|
|
675
|
+
complete(id, ok, resultOrError) {
|
|
676
|
+
const entry = this.pendingById.get(id);
|
|
677
|
+
if (!entry) return false;
|
|
536
678
|
this.drop(entry);
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (subs.size === 0) this.sseListeners.delete(sessionId);
|
|
679
|
+
if (ok) {
|
|
680
|
+
entry.resolve(resultOrError);
|
|
681
|
+
} else {
|
|
682
|
+
entry.reject(resultOrError instanceof Error ? resultOrError : new Error(String(resultOrError)));
|
|
683
|
+
}
|
|
684
|
+
return true;
|
|
685
|
+
}
|
|
686
|
+
/** 排空该会话全部未派发命令(无订阅者则留待 subscribe 补发) */
|
|
687
|
+
dispatch(sessionId) {
|
|
688
|
+
const subs = this.sseListeners.get(sessionId);
|
|
689
|
+
if (!subs || subs.size === 0) return;
|
|
690
|
+
for (const entry of this.pendingById.values()) {
|
|
691
|
+
if (entry.sessionId !== sessionId || entry.dispatched) continue;
|
|
692
|
+
entry.dispatched = true;
|
|
693
|
+
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)}`);
|
|
694
|
+
for (const fn of subs) fn(entry.command);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
/** 从 pending 表移除(完成/超时/取消共用) */
|
|
698
|
+
drop(entry) {
|
|
699
|
+
clearTimeout(entry.timer);
|
|
700
|
+
this.pendingById.delete(entry.command.id);
|
|
701
|
+
}
|
|
561
702
|
};
|
|
703
|
+
commandBus = new CommandBus();
|
|
562
704
|
}
|
|
563
|
-
|
|
564
|
-
complete(id, ok, resultOrError) {
|
|
565
|
-
const entry = this.pendingById.get(id);
|
|
566
|
-
if (!entry) return false;
|
|
567
|
-
this.drop(entry);
|
|
568
|
-
if (ok) {
|
|
569
|
-
entry.resolve(resultOrError);
|
|
570
|
-
} else {
|
|
571
|
-
entry.reject(resultOrError instanceof Error ? resultOrError : new Error(String(resultOrError)));
|
|
572
|
-
}
|
|
573
|
-
return true;
|
|
574
|
-
}
|
|
575
|
-
/** 排空该会话全部未派发命令(无订阅者则留待 subscribe 补发) */
|
|
576
|
-
dispatch(sessionId) {
|
|
577
|
-
const subs = this.sseListeners.get(sessionId);
|
|
578
|
-
if (!subs || subs.size === 0) return;
|
|
579
|
-
for (const entry of this.pendingById.values()) {
|
|
580
|
-
if (entry.sessionId !== sessionId || entry.dispatched) continue;
|
|
581
|
-
entry.dispatched = true;
|
|
582
|
-
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)}`);
|
|
583
|
-
for (const fn of subs) fn(entry.command);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
/** 从 pending 表移除(完成/超时/取消共用) */
|
|
587
|
-
drop(entry) {
|
|
588
|
-
clearTimeout(entry.timer);
|
|
589
|
-
this.pendingById.delete(entry.command.id);
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
var commandBus = new CommandBus();
|
|
705
|
+
});
|
|
593
706
|
|
|
594
707
|
// src/plugin/sessions.ts
|
|
595
708
|
import { randomUUID } from "node:crypto";
|
|
596
|
-
var ONLINE_WINDOW_MS = 45e3;
|
|
597
709
|
function unknownRouteError(route) {
|
|
598
710
|
if (route.fileKey) {
|
|
599
711
|
return new Error(`\u627E\u4E0D\u5230 fileKey \u4E3A ${route.fileKey} \u7684\u5B58\u6D3B\u63D2\u4EF6\u4F1A\u8BDD\u3002\u53EF\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u8FDE\u63A5\u7684\u4F1A\u8BDD`);
|
|
600
712
|
}
|
|
601
713
|
return new Error(`\u627E\u4E0D\u5230 sessionId \u4E3A ${route.sessionId} \u7684\u5B58\u6D3B\u63D2\u4EF6\u4F1A\u8BDD\uFF08\u53EF\u80FD\u5DF2\u88AB\u5254\u9664\uFF09\u3002\u53EF\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u8FDE\u63A5\u7684\u4F1A\u8BDD`);
|
|
602
714
|
}
|
|
603
|
-
var SessionRegistry
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
/** 亲和查询:上一次为该 fileKey 服务的 sessionId */
|
|
616
|
-
affinityIdOf(fileKey) {
|
|
617
|
-
return this.lastUsedByFileKey.get(fileKey);
|
|
618
|
-
}
|
|
619
|
-
/** 亲和钉住:fileKey 寻址探活成功后记录(下次优先) */
|
|
620
|
-
pinAffinity(fileKey, sessionId) {
|
|
621
|
-
this.lastUsedByFileKey.set(fileKey, sessionId);
|
|
622
|
-
}
|
|
623
|
-
/** 剔除会话(幽灵剔除/ping 判死共用):清理亲和引用;存活但繁忙被误剔的插件会在 SSE 断开或上报 404 后自重注册;
|
|
624
|
-
* 失效 sessionId 的 failover 不靠墓碑——工具结果信封带 fileKey,agent 手里的稳定句柄一直是 fileKey */
|
|
625
|
-
evict(id) {
|
|
626
|
-
if (this.sessions.delete(id)) {
|
|
627
|
-
for (const [k, v] of this.lastUsedByFileKey) {
|
|
628
|
-
if (v === id) this.lastUsedByFileKey.delete(k);
|
|
715
|
+
var SessionRegistry, sessions;
|
|
716
|
+
var init_sessions = __esm({
|
|
717
|
+
"src/plugin/sessions.ts"() {
|
|
718
|
+
"use strict";
|
|
719
|
+
init_config();
|
|
720
|
+
SessionRegistry = class {
|
|
721
|
+
sessions = /* @__PURE__ */ new Map();
|
|
722
|
+
lastActiveId = null;
|
|
723
|
+
/** 会话亲和:fileKey → 上一次路由使用的 sessionId(仍在线时优先,避免同文件多实例间漂移) */
|
|
724
|
+
lastUsedByFileKey = /* @__PURE__ */ new Map();
|
|
725
|
+
isOnline(s) {
|
|
726
|
+
return s.connected || Date.now() - s.lastSeen < SESSION_ONLINE_WINDOW_MS;
|
|
629
727
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
728
|
+
/** fileKey 寻址候选:该文件的在线会话(谁先 pong 由 ping 竞速决定,此处不排序) */
|
|
729
|
+
candidatesForFileKey(fileKey) {
|
|
730
|
+
return [...this.sessions.values()].filter((s) => s.meta.fileKey === fileKey && this.isOnline(s));
|
|
731
|
+
}
|
|
732
|
+
/** 亲和查询:上一次为该 fileKey 服务的 sessionId */
|
|
733
|
+
affinityIdOf(fileKey) {
|
|
734
|
+
return this.lastUsedByFileKey.get(fileKey);
|
|
735
|
+
}
|
|
736
|
+
/** 亲和钉住:fileKey 寻址探活成功后记录(下次优先) */
|
|
737
|
+
pinAffinity(fileKey, sessionId) {
|
|
738
|
+
this.lastUsedByFileKey.set(fileKey, sessionId);
|
|
739
|
+
}
|
|
740
|
+
/** 剔除会话(幽灵剔除/ping 判死共用):清理亲和引用;存活但繁忙被误剔的插件会在 SSE 断开或上报 404 后自重注册;
|
|
741
|
+
* 失效 sessionId 的 failover 不靠墓碑——工具结果信封带 fileKey,agent 手里的稳定句柄一直是 fileKey */
|
|
742
|
+
evict(id) {
|
|
743
|
+
if (this.sessions.delete(id)) {
|
|
744
|
+
for (const [k, v] of this.lastUsedByFileKey) {
|
|
745
|
+
if (v === id) this.lastUsedByFileKey.delete(k);
|
|
746
|
+
}
|
|
747
|
+
log(`session evicted: ${id.slice(0, 8)}`);
|
|
638
748
|
}
|
|
639
749
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
750
|
+
register(meta = {}) {
|
|
751
|
+
if (meta.fileKey) {
|
|
752
|
+
for (const s of [...this.sessions.values()]) {
|
|
753
|
+
if (s.meta.fileKey === meta.fileKey && !this.isOnline(s)) {
|
|
754
|
+
this.evict(s.id);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
const session = { id: randomUUID(), meta, lastSeen: Date.now(), connected: false };
|
|
759
|
+
this.sessions.set(session.id, session);
|
|
760
|
+
this.lastActiveId = session.id;
|
|
761
|
+
log(`session registered: ${session.id} (${meta.fileName ?? "unknown file"})`);
|
|
762
|
+
return this.toInfo(session);
|
|
763
|
+
}
|
|
764
|
+
/** 心跳/连接状态用:只刷 lastSeen,不影响 lastActiveId("最近活跃"= 用户行为,心跳不算) */
|
|
765
|
+
touchSeen(id) {
|
|
766
|
+
const s = this.sessions.get(id);
|
|
767
|
+
if (!s) return null;
|
|
768
|
+
s.lastSeen = Date.now();
|
|
769
|
+
return s;
|
|
770
|
+
}
|
|
771
|
+
/** 用户行为(选区上报/命令服务/注册):刷 lastSeen 并标记最近活跃 */
|
|
772
|
+
touch(id) {
|
|
773
|
+
const s = this.touchSeen(id);
|
|
774
|
+
if (!s) return null;
|
|
775
|
+
this.lastActiveId = id;
|
|
776
|
+
return s;
|
|
777
|
+
}
|
|
778
|
+
setConnected(id, connected) {
|
|
779
|
+
const s = this.touchSeen(id);
|
|
780
|
+
if (!s) return false;
|
|
781
|
+
s.connected = connected;
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
updateContext(id, meta) {
|
|
785
|
+
const s = this.touch(id);
|
|
786
|
+
if (!s) return false;
|
|
787
|
+
s.meta = { ...s.meta, ...meta };
|
|
788
|
+
log(
|
|
789
|
+
`context updated [${id.slice(0, 8)}]: ${s.meta.fileName ?? "?"} / ${s.meta.pageName ?? "?"} / ${s.meta.selection?.length ?? 0} selected`
|
|
790
|
+
);
|
|
791
|
+
return true;
|
|
792
|
+
}
|
|
793
|
+
list() {
|
|
794
|
+
return [...this.sessions.values()].map((s) => this.toInfo(s));
|
|
795
|
+
}
|
|
796
|
+
/** 纯查找(不判活):工具层寻址用——离线会话也可取到 fileKey 用于候选扩展与故障转移 */
|
|
797
|
+
get(id) {
|
|
798
|
+
return this.sessions.get(id);
|
|
799
|
+
}
|
|
800
|
+
/** 缺省路由:最近活跃在线会话 > 任意在线会话(显式 fileKey/sessionId 由工具层 get/candidatesForFileKey + ping 完成) */
|
|
801
|
+
resolve() {
|
|
802
|
+
if (this.lastActiveId) {
|
|
803
|
+
const s = this.sessions.get(this.lastActiveId);
|
|
804
|
+
if (s && this.isOnline(s)) return s;
|
|
805
|
+
}
|
|
806
|
+
const fallback = [...this.sessions.values()].find((s) => this.isOnline(s));
|
|
807
|
+
if (fallback) return fallback;
|
|
808
|
+
throw new Error("Pixso \u63D2\u4EF6\u672A\u8FDE\u63A5\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6");
|
|
809
|
+
}
|
|
810
|
+
toInfo(s) {
|
|
811
|
+
return {
|
|
812
|
+
sessionId: s.id,
|
|
813
|
+
fileKey: s.meta.fileKey,
|
|
814
|
+
fileName: s.meta.fileName,
|
|
815
|
+
pageName: s.meta.pageName,
|
|
816
|
+
selectionCount: s.meta.selection?.length,
|
|
817
|
+
online: this.isOnline(s),
|
|
818
|
+
lastSeen: s.lastSeen
|
|
819
|
+
};
|
|
820
|
+
}
|
|
702
821
|
};
|
|
822
|
+
sessions = new SessionRegistry();
|
|
703
823
|
}
|
|
704
|
-
};
|
|
705
|
-
var sessions = new SessionRegistry();
|
|
824
|
+
});
|
|
706
825
|
|
|
707
826
|
// src/agent/tools/shared.ts
|
|
708
|
-
var PING_TIMEOUT_MS = 800;
|
|
709
827
|
async function run(type, args, signal) {
|
|
710
828
|
const { fileKey, sessionId, ...rest } = args;
|
|
711
829
|
try {
|
|
@@ -763,28 +881,6 @@ async function resolveAndPing(route, signal) {
|
|
|
763
881
|
if (fileKey) sessions.pinAffinity(fileKey, winner.id);
|
|
764
882
|
return winner;
|
|
765
883
|
}
|
|
766
|
-
var ENVELOPE_NOTE = "\u8FD4\u56DE\u5C01\u88C5\u4E3A {sessionId, fileKey, 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\uFF0CfileKey \u662F\u8DE8\u91CD\u5F00\u7A33\u5B9A\u7684\u53E5\u67C4\u2014\u2014\u540E\u7EED\u8C03\u7528\u7528 fileKey \u5BFB\u5740\u3002";
|
|
767
|
-
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";
|
|
768
|
-
var FILE_KEY_PROP = {
|
|
769
|
-
type: "string",
|
|
770
|
-
minLength: 1,
|
|
771
|
-
description: "\u76EE\u6807\u8BBE\u8BA1\u6587\u4EF6\u7684 fileKey\uFF08\u8DE8\u91CD\u5F00\u7A33\u5B9A\uFF0C\u4F18\u5148\uFF09\u3002\u4E0E sessionId \u4E8C\u9009\u4E00\uFF1B\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u3002"
|
|
772
|
-
};
|
|
773
|
-
var SESSION_ID_PROP = {
|
|
774
|
-
type: "string",
|
|
775
|
-
minLength: 1,
|
|
776
|
-
description: "\u76EE\u6807\u63D2\u4EF6\u4F1A\u8BDD\u7684 sessionId\uFF08ping/pong \u786E\u8BA4\u7684\u5B58\u6D3B\u5B9E\u4F8B\uFF09\u3002\u4E0E fileKey \u4E8C\u9009\u4E00\u3002"
|
|
777
|
-
};
|
|
778
|
-
var CACHE_PROP = {
|
|
779
|
-
type: "string",
|
|
780
|
-
enum: ["first", "refresh"],
|
|
781
|
-
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"
|
|
782
|
-
};
|
|
783
|
-
var GUID_PROP = {
|
|
784
|
-
type: "string",
|
|
785
|
-
minLength: 1,
|
|
786
|
-
description: '\u8282\u70B9 GUID\uFF08\u5982 "297:133032"\uFF09\uFF0C\u5FC5\u4F20\u3002\u94FE\u63A5\u8F93\u5165\uFF1A\u4ECE item-id \u63D0\u53D6\u5E76 URL \u89E3\u7801\u540E\u4F20\u5165\uFF1B\u8981\u64CD\u4F5C\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\u65F6\uFF0C\u5148\u7528 get_selected \u53D6\u5176 guid \u518D\u4F20\u5165\u3002'
|
|
787
|
-
};
|
|
788
884
|
function jsonResult(data) {
|
|
789
885
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
790
886
|
}
|
|
@@ -794,138 +890,217 @@ function errorResult(err) {
|
|
|
794
890
|
isError: true
|
|
795
891
|
};
|
|
796
892
|
}
|
|
893
|
+
var ENVELOPE_NOTE, GUIDES_PTR, FILE_KEY_PROP, SESSION_ID_PROP, CACHE_PROP, GUID_PROP;
|
|
894
|
+
var init_shared = __esm({
|
|
895
|
+
"src/agent/tools/shared.ts"() {
|
|
896
|
+
"use strict";
|
|
897
|
+
init_command_bus();
|
|
898
|
+
init_sessions();
|
|
899
|
+
init_config();
|
|
900
|
+
ENVELOPE_NOTE = "\u8FD4\u56DE\u5C01\u88C5\u4E3A {sessionId, fileKey, 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\uFF0CfileKey \u662F\u8DE8\u91CD\u5F00\u7A33\u5B9A\u7684\u53E5\u67C4\u2014\u2014\u540E\u7EED\u8C03\u7528\u7528 fileKey \u5BFB\u5740\u3002";
|
|
901
|
+
GUIDES_PTR = "\u5B8C\u6574\u89C4\u5219\uFF08\u8D44\u6E90\u53D6\u7528\u4E0E\u5B8C\u6574\u6027/DSL \u4E3A\u7ED3\u6784\u4E8B\u5B9E\u6E90/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D\uFF09\u89C1\u8D44\u6E90 batter://guides/design-to-code\u3002";
|
|
902
|
+
FILE_KEY_PROP = {
|
|
903
|
+
type: "string",
|
|
904
|
+
minLength: 1,
|
|
905
|
+
description: "\u76EE\u6807\u8BBE\u8BA1\u6587\u4EF6\u7684 fileKey\uFF08\u8DE8\u91CD\u5F00\u7A33\u5B9A\uFF0C\u4F18\u5148\uFF09\u3002\u4E0E sessionId \u4E8C\u9009\u4E00\uFF1B\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u3002"
|
|
906
|
+
};
|
|
907
|
+
SESSION_ID_PROP = {
|
|
908
|
+
type: "string",
|
|
909
|
+
minLength: 1,
|
|
910
|
+
description: "\u76EE\u6807\u63D2\u4EF6\u4F1A\u8BDD\u7684 sessionId\uFF08ping/pong \u786E\u8BA4\u7684\u5B58\u6D3B\u5B9E\u4F8B\uFF09\u3002\u4E0E fileKey \u4E8C\u9009\u4E00\u3002"
|
|
911
|
+
};
|
|
912
|
+
CACHE_PROP = {
|
|
913
|
+
type: "string",
|
|
914
|
+
enum: ["first", "refresh"],
|
|
915
|
+
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"
|
|
916
|
+
};
|
|
917
|
+
GUID_PROP = {
|
|
918
|
+
type: "string",
|
|
919
|
+
minLength: 1,
|
|
920
|
+
description: '\u8282\u70B9 GUID\uFF08\u5982 "297:133032"\uFF09\uFF0C\u5FC5\u4F20\u3002\u94FE\u63A5\u8F93\u5165\uFF1A\u4ECE item-id \u63D0\u53D6\u5E76 URL \u89E3\u7801\u540E\u4F20\u5165\uFF1B\u8981\u64CD\u4F5C\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\u65F6\uFF0C\u5148\u7528 get_selected \u53D6\u5176 guid \u518D\u4F20\u5165\u3002'
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
});
|
|
797
924
|
|
|
798
925
|
// src/agent/tools/export_image.ts
|
|
799
|
-
var exportImage
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
926
|
+
var exportImage;
|
|
927
|
+
var init_export_image = __esm({
|
|
928
|
+
"src/agent/tools/export_image.ts"() {
|
|
929
|
+
"use strict";
|
|
930
|
+
init_shared();
|
|
931
|
+
exportImage = {
|
|
932
|
+
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,
|
|
933
|
+
// 两组互斥各取其一:guid|guids(单节点|批量)× fileKey|sessionId(路由)
|
|
934
|
+
requires: [
|
|
935
|
+
["guid", "guids"],
|
|
936
|
+
["fileKey", "sessionId"]
|
|
937
|
+
],
|
|
938
|
+
properties: {
|
|
939
|
+
guid: GUID_PROP,
|
|
940
|
+
guids: {
|
|
941
|
+
type: "array",
|
|
942
|
+
items: { type: "string" },
|
|
943
|
+
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"
|
|
944
|
+
},
|
|
945
|
+
format: {
|
|
946
|
+
type: "string",
|
|
947
|
+
enum: ["PNG", "SVG", "JPG"],
|
|
948
|
+
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"
|
|
949
|
+
},
|
|
950
|
+
scale: {
|
|
951
|
+
type: "number",
|
|
952
|
+
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"
|
|
953
|
+
},
|
|
954
|
+
cache: CACHE_PROP,
|
|
955
|
+
fileKey: FILE_KEY_PROP,
|
|
956
|
+
sessionId: SESSION_ID_PROP
|
|
957
|
+
}
|
|
958
|
+
};
|
|
825
959
|
}
|
|
826
|
-
};
|
|
960
|
+
});
|
|
827
961
|
|
|
828
962
|
// src/agent/tools/get_metadata.ts
|
|
829
|
-
var getMetadata
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
963
|
+
var getMetadata;
|
|
964
|
+
var init_get_metadata = __esm({
|
|
965
|
+
"src/agent/tools/get_metadata.ts"() {
|
|
966
|
+
"use strict";
|
|
967
|
+
init_shared();
|
|
968
|
+
getMetadata = {
|
|
969
|
+
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,
|
|
970
|
+
requires: ["guid", ["fileKey", "sessionId"]],
|
|
971
|
+
properties: {
|
|
972
|
+
guid: GUID_PROP,
|
|
973
|
+
cache: CACHE_PROP,
|
|
974
|
+
fileKey: FILE_KEY_PROP,
|
|
975
|
+
sessionId: SESSION_ID_PROP
|
|
976
|
+
}
|
|
977
|
+
};
|
|
837
978
|
}
|
|
838
|
-
};
|
|
979
|
+
});
|
|
839
980
|
|
|
840
981
|
// src/agent/tools/get_node_dsl.ts
|
|
841
|
-
var URL_RULE
|
|
842
|
-
var
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
guid:
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
982
|
+
var URL_RULE, DSL_LEGEND, getNodeDsl;
|
|
983
|
+
var init_get_node_dsl = __esm({
|
|
984
|
+
"src/agent/tools/get_node_dsl.ts"() {
|
|
985
|
+
"use strict";
|
|
986
|
+
init_shared();
|
|
987
|
+
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';
|
|
988
|
+
DSL_LEGEND = '\u5B57\u6BB5\u56FE\u4F8B\uFF1Aid=\u8282\u70B9 guid\uFF08\u53EF\u56DE\u4F20 guid \u4E0B\u94BB\uFF09\uFF1Bname=\u56FE\u5C42\u540D\uFF08\u81EA\u52A8\u547D\u540D\u5DF2\u7701\u7565\uFF09\uFF1Btype=\u8282\u70B9\u7C7B\u578B\uFF08ICON/IMAGE \u4E3A\u584C\u7F29\u53F6\u5B50\uFF09\uFF1Brect=[x,y,w,h] \u7EDD\u5BF9\u5B9A\u4F4D\uFF1Blayout={dir:row|column, gap, pad:[\u4E0A,\u53F3,\u4E0B,\u5DE6], align:[\u4E3B\u8F74,\u4EA4\u53C9\u8F74], sizing:[hug|fixed|fill]\xD72}\uFF1Bconstraints={h,v}\uFF08\u9ED8\u8BA4 left/top \u7701\u7565\uFF09\uFF1Btext={content, size, font, weight, lh, align, color, deco, ls, link, segs=\u6DF7\u6392\u5206\u6BB5\uFF08\u4EC5\u5DEE\u5F02\u5B57\u6BB5\uFF0C\u987A\u5E8F\u5373\u5B57\u7B26\u504F\u79FB\uFF09}\uFF1Bimg=\u8D44\u6E90\u6587\u4EF6\u540D\uFF1Bkids=\u5B50\u8282\u70B9\u9012\u5F52\uFF1Bpalette=\u8C03\u8272\u677F\uFF08\u4EC5\u6839\u8282\u70B9\uFF09\uFF1A\u22652 \u6B21\u7684\u5355\u8272\u63D0\u53D6\u4E3A\u6570\u7EC4\uFF0Cfills/text.color/stroke \u4E2D "p0""p1" \u4E3A\u4E0B\u6807\u5F15\u7528\uFF08\u590D\u5408 CSS \u4E32\u5185\u989C\u8272\u4E0D\u67E5\u8868\uFF09\uFF1Bnotes=\u8BBE\u8BA1\u6807\u6CE8\u6587\u672C\uFF08\u4EC5\u5B58\u5728\u65F6\uFF09\uFF1Bcomp=\u7EC4\u4EF6\u5173\u8054\uFF08INSTANCE/COMPONENT/COMPONENT_SET\uFF09\uFF1Akey=\u8DE8\u6587\u4EF6\u7A33\u5B9A\u6807\u8BC6\uFF0Cname=\u7EC4\u4EF6\u540D\uFF08\u6620\u5C04\u6743\u91CD\u6700\u9AD8\u7684\u4F9D\u636E\u2014\u2014\u5728\u9879\u76EE\u7EC4\u4EF6\u5E93\u627E\u5BF9\u5E94\uFF0C\u4E0D\u9010\u5B57\u76F4\u7528\uFF09\uFF0Cprops=variant \u5C5E\u6027\uFF0Cset=variant \u7EC4\uFF1Bdepth=\u6574\u6811\u5B9E\u9645\u6DF1\u5EA6\uFF08\u4EC5\u6839\u8282\u70B9\uFF0C\u603B\u662F\u8F93\u51FA\uFF09\uFF1Bmore=\u622A\u65AD\u5904\u5269\u4F59\u5C42\u7EA7\u6570\uFF08\u4EC5 maxDepth \u622A\u65AD\u65F6\uFF0C\u62FF id \u56DE\u4F20 guid \u4E0B\u94BB\uFF09\uFF1BkidsSummary=\u622A\u65AD\u5904\u6216\u584C\u7F29\u53F6\u5B50\uFF08img \u8282\u70B9\uFF09\u7684\u76F4\u63A5\u5B50\u8282\u70B9\u6D45\u9884\u89C8 [{id, name?, type}]\uFF08\u8F85\u52A9\u9009\u62E9\u4E0B\u94BB\u5206\u652F\uFF09\u3002\u7701\u7565\u89C4\u5219\uFF1A\u9ED8\u8BA4\u503C\u4E0D\u8F93\u51FA\uFF08text.align=left\u3001lh=normal\u3001weight=Regular\u3001gap=0\u3001pad \u5168 0\uFF09\u3002';
|
|
989
|
+
getNodeDsl = {
|
|
990
|
+
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,
|
|
991
|
+
requires: ["guid", ["fileKey", "sessionId"]],
|
|
992
|
+
properties: {
|
|
993
|
+
guid: GUID_PROP,
|
|
994
|
+
maxDepth: {
|
|
995
|
+
type: "number",
|
|
996
|
+
description: "\u4E0B\u63A2\u5B50\u8282\u70B9\u7684\u6700\u5927\u6DF1\u5EA6\uFF08\u6839\u8282\u70B9\u4E3A\u7B2C 0 \u5C42\uFF09\uFF0C\u7F3A\u7701 4 \u5C42\uFF0C\u4F20 -1 \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"
|
|
997
|
+
},
|
|
998
|
+
cache: CACHE_PROP,
|
|
999
|
+
fileKey: FILE_KEY_PROP,
|
|
1000
|
+
sessionId: SESSION_ID_PROP
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
855
1003
|
}
|
|
856
|
-
};
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// src/agent/tools/get_page_metadata.ts
|
|
1007
|
+
var getPageMetadata;
|
|
1008
|
+
var init_get_page_metadata = __esm({
|
|
1009
|
+
"src/agent/tools/get_page_metadata.ts"() {
|
|
1010
|
+
"use strict";
|
|
1011
|
+
init_shared();
|
|
1012
|
+
getPageMetadata = {
|
|
1013
|
+
description: '\u3010\u7528\u9014\u3011\u5F53\u524D\u9875\u9762\u9876\u5C42\u9AA8\u67B6\uFF08\u9875\u9762 id/name\u3001\u6574\u9875\u6DF1\u5EA6 depth\u3001\u9876\u5C42\u56FE\u5C42\u5217\u8868\uFF09\uFF0C\u51B7\u542F\u52A8\u63A2\u7D22\u5165\u53E3\uFF0C\u65E0\u9700 guid\u3002\u3010\u547D\u4E2D\u4FE1\u53F7\u3011\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\u4F46\u6CA1\u7ED9\u94FE\u63A5\u4E5F\u6CA1\u9009\u4E2D\u56FE\u5C42\uFF1B"\u9875\u9762\u91CC\u6709\u4EC0\u4E48/\u6709\u54EA\u4E9B\u56FE\u5C42"\uFF1B\u9700\u8981\u5148\u5B9A\u4F4D\u76EE\u6807\u518D\u4E0B\u94BB\u3002\u3010\u8FD4\u56DE\u3011{id, name, depth, kids: [{id, name?, type}]}\uFF08kids \u4EC5\u9876\u5C42\u4E00\u5C42\uFF09\u3002\u4E0B\u94BB\uFF1A\u628A kids \u91CC\u76EE\u6807\u56FE\u5C42\u7684 id \u4F5C\u4E3A guid \u4F20\u7ED9 get_metadata/get_node_dsl/get_screenshot\uFF1B\u8FD4\u56DE\u7684\u9875\u9762 id \u4E0D\u53EF\u56DE\u4F20\u8282\u70B9\u7C7B\u5DE5\u5177\uFF08\u9875\u9762\u4E0D\u652F\u6301\u5BFC\u51FA/DSL\uFF09\u3002\u672C\u5DE5\u5177\u7684 fileKey/sessionId \u53EF\u7F3A\u7701\uFF08\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u6587\u4EF6\uFF09\uFF1B\u4E0B\u94BB\u8C03\u7528\u4E24\u8005\u5FC5\u4F20\u5176\u4E00\u3002\u3010\u786C\u6027\u89C4\u5219\u3011' + GUIDES_PTR + " " + ENVELOPE_NOTE,
|
|
1014
|
+
properties: { fileKey: FILE_KEY_PROP, sessionId: SESSION_ID_PROP }
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
857
1018
|
|
|
858
1019
|
// src/agent/tools/get_screenshot.ts
|
|
859
|
-
var getScreenshot
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1020
|
+
var getScreenshot;
|
|
1021
|
+
var init_get_screenshot = __esm({
|
|
1022
|
+
"src/agent/tools/get_screenshot.ts"() {
|
|
1023
|
+
"use strict";
|
|
1024
|
+
init_shared();
|
|
1025
|
+
getScreenshot = {
|
|
1026
|
+
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,
|
|
1027
|
+
requires: ["guid", ["fileKey", "sessionId"]],
|
|
1028
|
+
properties: {
|
|
1029
|
+
guid: GUID_PROP,
|
|
1030
|
+
format: {
|
|
1031
|
+
type: "string",
|
|
1032
|
+
enum: ["PNG", "SVG"],
|
|
1033
|
+
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"
|
|
1034
|
+
},
|
|
1035
|
+
scale: {
|
|
1036
|
+
type: "number",
|
|
1037
|
+
description: "\u5BFC\u51FA\u500D\u7387\uFF0C\u9ED8\u8BA4 2\u3002"
|
|
1038
|
+
},
|
|
1039
|
+
cache: CACHE_PROP,
|
|
1040
|
+
fileKey: FILE_KEY_PROP,
|
|
1041
|
+
sessionId: SESSION_ID_PROP
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
876
1044
|
}
|
|
877
|
-
};
|
|
1045
|
+
});
|
|
878
1046
|
|
|
879
1047
|
// src/agent/tools/get_selected.ts
|
|
880
|
-
var getSelected
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
);
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1048
|
+
var getSelected;
|
|
1049
|
+
var init_get_selected = __esm({
|
|
1050
|
+
"src/agent/tools/get_selected.ts"() {
|
|
1051
|
+
"use strict";
|
|
1052
|
+
init_shared();
|
|
1053
|
+
getSelected = {
|
|
1054
|
+
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\u672C\u5DE5\u5177\u7684 fileKey/sessionId \u53EF\u7F3A\u7701\uFF08\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u6587\u4EF6\uFF09\uFF1B\u5176\u4ED6\u5DE5\u5177\u4E24\u8005\u5FC5\u4F20\u5176\u4E00\u3002 ' + ENVELOPE_NOTE,
|
|
1055
|
+
properties: { fileKey: FILE_KEY_PROP, sessionId: SESSION_ID_PROP }
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
// src/agent/tools/list_sessions.ts
|
|
1061
|
+
var listSessions;
|
|
1062
|
+
var init_list_sessions = __esm({
|
|
1063
|
+
"src/agent/tools/list_sessions.ts"() {
|
|
1064
|
+
"use strict";
|
|
1065
|
+
init_sessions();
|
|
1066
|
+
init_shared();
|
|
1067
|
+
listSessions = {
|
|
1068
|
+
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 fileKey\uFF08\u8DE8\u91CD\u5F00\u7A33\u5B9A\uFF0C\u4F18\u5148\uFF09\u6216 sessionId \u4F5C\u4E3A\u5176\u4ED6\u5DE5\u5177\u7684\u540C\u540D\u53C2\u6570\u4F20\u5165\u5373\u8DEF\u7531\u5230\u5BF9\u5E94\u6587\u4EF6\uFF08\u8282\u70B9\u7C7B\u5DE5\u5177\u4E8C\u8005\u5FC5\u4F20\u5176\u4E00\uFF1B\u4EC5 get_selected \u4E0E\u672C\u5DE5\u5177\u53EF\u7F3A\u7701\uFF09\u3002\u8BE6\u89C1\u8D44\u6E90 batter://guides/intro\u3002",
|
|
1069
|
+
properties: {},
|
|
1070
|
+
handler: async () => {
|
|
1071
|
+
const list = sessions.list();
|
|
1072
|
+
if (!list.length) {
|
|
1073
|
+
return {
|
|
1074
|
+
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" }]
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
const checks = await Promise.all(
|
|
1078
|
+
list.map(async (info) => info.online && await pingSession(info.sessionId) ? info : null)
|
|
1079
|
+
);
|
|
1080
|
+
const alive = checks.filter((c) => c !== null);
|
|
1081
|
+
for (const info of list) {
|
|
1082
|
+
if (!alive.includes(info)) sessions.evict(info.sessionId);
|
|
1083
|
+
}
|
|
1084
|
+
if (!alive.length) {
|
|
1085
|
+
return {
|
|
1086
|
+
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" }]
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
return jsonResult(
|
|
1090
|
+
alive.map(({ sessionId, fileKey, fileName, pageName, selectionCount }) => ({
|
|
1091
|
+
sessionId,
|
|
1092
|
+
fileKey,
|
|
1093
|
+
fileName,
|
|
1094
|
+
pageName,
|
|
1095
|
+
selectionCount
|
|
1096
|
+
}))
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
917
1100
|
}
|
|
918
|
-
};
|
|
1101
|
+
});
|
|
919
1102
|
|
|
920
1103
|
// src/agent/tools/index.ts
|
|
921
|
-
var TOOL_REGISTRY = {
|
|
922
|
-
get_selected: getSelected,
|
|
923
|
-
get_node_dsl: getNodeDsl,
|
|
924
|
-
get_metadata: getMetadata,
|
|
925
|
-
export_image: exportImage,
|
|
926
|
-
get_screenshot: getScreenshot,
|
|
927
|
-
list_sessions: listSessions
|
|
928
|
-
};
|
|
929
1104
|
function buildInputSchema(requires, properties) {
|
|
930
1105
|
const singles = requires.filter((r) => typeof r === "string");
|
|
931
1106
|
const xorGroups = requires.filter((r) => Array.isArray(r));
|
|
@@ -951,26 +1126,56 @@ function validateArgs(requires, args) {
|
|
|
951
1126
|
}
|
|
952
1127
|
return null;
|
|
953
1128
|
}
|
|
954
|
-
var TOOLS
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1129
|
+
var TOOL_REGISTRY, TOOLS;
|
|
1130
|
+
var init_tools = __esm({
|
|
1131
|
+
"src/agent/tools/index.ts"() {
|
|
1132
|
+
"use strict";
|
|
1133
|
+
init_export_image();
|
|
1134
|
+
init_get_metadata();
|
|
1135
|
+
init_get_node_dsl();
|
|
1136
|
+
init_get_page_metadata();
|
|
1137
|
+
init_get_screenshot();
|
|
1138
|
+
init_get_selected();
|
|
1139
|
+
init_list_sessions();
|
|
1140
|
+
init_shared();
|
|
1141
|
+
TOOL_REGISTRY = {
|
|
1142
|
+
get_selected: getSelected,
|
|
1143
|
+
get_node_dsl: getNodeDsl,
|
|
1144
|
+
get_metadata: getMetadata,
|
|
1145
|
+
get_page_metadata: getPageMetadata,
|
|
1146
|
+
export_image: exportImage,
|
|
1147
|
+
get_screenshot: getScreenshot,
|
|
1148
|
+
list_sessions: listSessions
|
|
963
1149
|
};
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
);
|
|
1150
|
+
TOOLS = Object.fromEntries(
|
|
1151
|
+
Object.entries(TOOL_REGISTRY).map(([name, def]) => {
|
|
1152
|
+
const requires = def.requires ?? [];
|
|
1153
|
+
const inputSchema = buildInputSchema(requires, def.properties);
|
|
1154
|
+
const base = def.handler ?? ((args, ctx) => run(name, args, ctx?.signal));
|
|
1155
|
+
const handler = (args, ctx) => {
|
|
1156
|
+
const err = requires.length ? validateArgs(requires, args) : null;
|
|
1157
|
+
if (err) return Promise.resolve(errorResult(err));
|
|
1158
|
+
return base(args, ctx);
|
|
1159
|
+
};
|
|
1160
|
+
const resolvedDef = { description: def.description, inputSchema, handler };
|
|
1161
|
+
return [name, { def: resolvedDef, tool: { name, description: def.description, inputSchema } }];
|
|
1162
|
+
})
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
968
1166
|
|
|
969
1167
|
// src/agent/mcp.ts
|
|
970
|
-
|
|
1168
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1169
|
+
import {
|
|
1170
|
+
CallToolRequestSchema,
|
|
1171
|
+
GetPromptRequestSchema,
|
|
1172
|
+
ListPromptsRequestSchema,
|
|
1173
|
+
ListResourceTemplatesRequestSchema,
|
|
1174
|
+
ListToolsRequestSchema
|
|
1175
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
971
1176
|
function createMcpServer() {
|
|
972
1177
|
const server = new McpServer(
|
|
973
|
-
{ name:
|
|
1178
|
+
{ name: SERVICE_NAME, version: VERSION },
|
|
974
1179
|
{ capabilities: { tools: {}, resources: {}, prompts: {} }, instructions: INSTRUCTIONS }
|
|
975
1180
|
);
|
|
976
1181
|
for (const guide of GUIDES) {
|
|
@@ -1019,9 +1224,19 @@ function createMcpServer() {
|
|
|
1019
1224
|
});
|
|
1020
1225
|
return server;
|
|
1021
1226
|
}
|
|
1227
|
+
var INSTRUCTIONS;
|
|
1228
|
+
var init_mcp = __esm({
|
|
1229
|
+
"src/agent/mcp.ts"() {
|
|
1230
|
+
"use strict";
|
|
1231
|
+
init_config();
|
|
1232
|
+
init_guides();
|
|
1233
|
+
init_prompts();
|
|
1234
|
+
init_tools();
|
|
1235
|
+
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 \u6709 guid \u7528 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\u6CA1\u7ED9\u94FE\u63A5\u4E5F\u6CA1\u9009\u4E2D \u2192 get_page_metadata \u62FF\u5F53\u524D\u9875\u9876\u5C42\u9AA8\u67B6\uFF08\u65E0\u9700 guid\uFF09\uFF0C\u4ECE\u5176 kids \u7684 id \u4E0B\u94BB\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 + fileKey/sessionId \u53C2\u6570\u8DEF\u7531\uFF08\u8282\u70B9\u7C7B\u5DE5\u5177\u4E8C\u8005\u5FC5\u4F20\u5176\u4E00\uFF09\u3002\u786C\u6027\u89C4\u5219\uFF08\u8D44\u6E90\u53D6\u7528\u4E0E\u5B8C\u6574\u6027/DSL \u4E3A\u7ED3\u6784\u4E8B\u5B9E\u6E90/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D\uFF09\u89C1\u8D44\u6E90 batter://guides/design-to-code\u2014\u2014\u5199\u4EE3\u7801\u524D\u5FC5\u8BFB\u3002`;
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1022
1238
|
|
|
1023
1239
|
// src/agent/sessions.ts
|
|
1024
|
-
var transports = /* @__PURE__ */ new Map();
|
|
1025
1240
|
function getMcpTransport(sessionId) {
|
|
1026
1241
|
const entry = transports.get(sessionId);
|
|
1027
1242
|
if (entry) entry.lastSeen = Date.now();
|
|
@@ -1034,19 +1249,30 @@ function registerMcpTransport(sessionId, transport) {
|
|
|
1034
1249
|
if (transport.sessionId) transports.delete(transport.sessionId);
|
|
1035
1250
|
};
|
|
1036
1251
|
}
|
|
1037
|
-
var sweeper
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1252
|
+
var transports, sweeper;
|
|
1253
|
+
var init_sessions2 = __esm({
|
|
1254
|
+
"src/agent/sessions.ts"() {
|
|
1255
|
+
"use strict";
|
|
1256
|
+
init_config();
|
|
1257
|
+
transports = /* @__PURE__ */ new Map();
|
|
1258
|
+
sweeper = setInterval(() => {
|
|
1259
|
+
const now = Date.now();
|
|
1260
|
+
for (const [id, entry] of transports) {
|
|
1261
|
+
if (now - entry.lastSeen <= config.mcpSessionTtlMs) continue;
|
|
1262
|
+
transports.delete(id);
|
|
1263
|
+
void entry.transport.close().catch(() => {
|
|
1264
|
+
});
|
|
1265
|
+
log(`MCP HTTP session expired: ${id.slice(0, 8)} (total ${transports.size})`);
|
|
1266
|
+
}
|
|
1267
|
+
}, MCP_SESSION_SWEEP_MS);
|
|
1268
|
+
sweeper.unref();
|
|
1045
1269
|
}
|
|
1046
|
-
}
|
|
1047
|
-
sweeper.unref();
|
|
1270
|
+
});
|
|
1048
1271
|
|
|
1049
1272
|
// src/agent/http.ts
|
|
1273
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1274
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
1275
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
1050
1276
|
function handleMcp(req, res, url) {
|
|
1051
1277
|
return dispatchRoute(MCP_ROUTES, req, res, url);
|
|
1052
1278
|
}
|
|
@@ -1059,95 +1285,54 @@ async function sessionRequestHandler({ req, res }) {
|
|
|
1059
1285
|
}
|
|
1060
1286
|
await transport.handleRequest(req, res);
|
|
1061
1287
|
}
|
|
1062
|
-
var MCP_ROUTES
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
transport
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1288
|
+
var MCP_ROUTES;
|
|
1289
|
+
var init_http = __esm({
|
|
1290
|
+
"src/agent/http.ts"() {
|
|
1291
|
+
"use strict";
|
|
1292
|
+
init_http_body();
|
|
1293
|
+
init_mcp();
|
|
1294
|
+
init_sessions2();
|
|
1295
|
+
MCP_ROUTES = [
|
|
1296
|
+
{
|
|
1297
|
+
method: "POST",
|
|
1298
|
+
path: "/mcp",
|
|
1299
|
+
handler: async ({ req, res }) => {
|
|
1300
|
+
let body;
|
|
1301
|
+
try {
|
|
1302
|
+
body = await readJsonBody(req);
|
|
1303
|
+
} catch {
|
|
1304
|
+
sendJson(res, 400, { jsonrpc: "2.0", error: { code: -32700, message: "Parse error" }, id: null });
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
1308
|
+
let transport = sessionId ? getMcpTransport(sessionId) : void 0;
|
|
1309
|
+
if (!transport) {
|
|
1310
|
+
if (!sessionId && isInitializeRequest(body)) {
|
|
1311
|
+
const t = new StreamableHTTPServerTransport({
|
|
1312
|
+
sessionIdGenerator: () => randomUUID2(),
|
|
1313
|
+
onsessioninitialized: (id) => registerMcpTransport(id, t)
|
|
1314
|
+
});
|
|
1315
|
+
await createMcpServer().connect(t);
|
|
1316
|
+
transport = t;
|
|
1317
|
+
} else {
|
|
1318
|
+
sendJson(res, 400, {
|
|
1319
|
+
jsonrpc: "2.0",
|
|
1320
|
+
error: { code: -32e3, message: "Bad Request: missing or unknown MCP session" },
|
|
1321
|
+
id: null
|
|
1322
|
+
});
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
await transport.handleRequest(req, res, body);
|
|
1091
1327
|
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
{ method: "GET", path: "/mcp", handler: sessionRequestHandler },
|
|
1097
|
-
{ method: "DELETE", path: "/mcp", handler: sessionRequestHandler }
|
|
1098
|
-
];
|
|
1099
|
-
|
|
1100
|
-
// src/install/dist-resolver.ts
|
|
1101
|
-
import { existsSync } from "node:fs";
|
|
1102
|
-
import { readFile } from "node:fs/promises";
|
|
1103
|
-
import { join as join4 } from "node:path";
|
|
1104
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1105
|
-
function resolvePluginDir() {
|
|
1106
|
-
const candidates = [
|
|
1107
|
-
fileURLToPath2(new URL("./plugin/", import.meta.url)),
|
|
1108
|
-
// bundle: dist/index.js → dist/plugin/
|
|
1109
|
-
fileURLToPath2(new URL("../../plugin/dist/", import.meta.url)),
|
|
1110
|
-
// bundle 开发: dist/index.js → packages/plugin/dist/
|
|
1111
|
-
fileURLToPath2(new URL("../../../plugin/dist/", import.meta.url))
|
|
1112
|
-
// tsx 开发: src/install/ → packages/plugin/dist/
|
|
1113
|
-
];
|
|
1114
|
-
return candidates.find((p) => existsSync(join4(p, "manifest.json"))) ?? null;
|
|
1115
|
-
}
|
|
1116
|
-
var resolved = null;
|
|
1117
|
-
function pluginDir() {
|
|
1118
|
-
resolved ??= resolvePluginDir();
|
|
1119
|
-
return resolved;
|
|
1120
|
-
}
|
|
1121
|
-
function hasPluginDist() {
|
|
1122
|
-
return pluginDir() !== null;
|
|
1123
|
-
}
|
|
1124
|
-
async function pluginManifest() {
|
|
1125
|
-
const dir = pluginDir();
|
|
1126
|
-
if (!dir) return null;
|
|
1127
|
-
const raw = JSON.parse(await readFile(join4(dir, "manifest.json"), "utf8"));
|
|
1128
|
-
raw.networkAccess = {
|
|
1129
|
-
allowedDomains: [`http://localhost:${config.httpPort}`, `http://127.0.0.1:${config.httpPort}`]
|
|
1130
|
-
};
|
|
1131
|
-
return Buffer.from(JSON.stringify(raw, null, 2));
|
|
1132
|
-
}
|
|
1133
|
-
async function readPluginFile(name) {
|
|
1134
|
-
const dir = pluginDir();
|
|
1135
|
-
if (!dir) return null;
|
|
1136
|
-
try {
|
|
1137
|
-
return await readFile(join4(dir, name));
|
|
1138
|
-
} catch {
|
|
1139
|
-
return null;
|
|
1328
|
+
},
|
|
1329
|
+
{ method: "GET", path: "/mcp", handler: sessionRequestHandler },
|
|
1330
|
+
{ method: "DELETE", path: "/mcp", handler: sessionRequestHandler }
|
|
1331
|
+
];
|
|
1140
1332
|
}
|
|
1141
|
-
}
|
|
1333
|
+
});
|
|
1142
1334
|
|
|
1143
1335
|
// src/install/template.ts
|
|
1144
|
-
var HTML_ESCAPES = {
|
|
1145
|
-
"&": "&",
|
|
1146
|
-
"<": "<",
|
|
1147
|
-
">": ">",
|
|
1148
|
-
'"': """,
|
|
1149
|
-
"'": "'"
|
|
1150
|
-
};
|
|
1151
1336
|
function escapeHtml(s) {
|
|
1152
1337
|
return s.replace(/[&<>"']/g, (c) => HTML_ESCAPES[c]);
|
|
1153
1338
|
}
|
|
@@ -1162,12 +1347,33 @@ function renderTemplate(tpl, vars, escape = escapeHtml) {
|
|
|
1162
1347
|
};
|
|
1163
1348
|
return tpl.replace(/\{\{\{\s*(\w+)\s*\}\}\}/g, (_, key) => lookup(key)).replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => escape(lookup(key)));
|
|
1164
1349
|
}
|
|
1350
|
+
var HTML_ESCAPES;
|
|
1351
|
+
var init_template = __esm({
|
|
1352
|
+
"src/install/template.ts"() {
|
|
1353
|
+
"use strict";
|
|
1354
|
+
HTML_ESCAPES = {
|
|
1355
|
+
"&": "&",
|
|
1356
|
+
"<": "<",
|
|
1357
|
+
">": ">",
|
|
1358
|
+
'"': """,
|
|
1359
|
+
"'": "'"
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1165
1363
|
|
|
1166
1364
|
// src/install/templates/bookmarklet.txt
|
|
1167
|
-
var bookmarklet_default
|
|
1365
|
+
var bookmarklet_default;
|
|
1366
|
+
var init_bookmarklet = __esm({
|
|
1367
|
+
"src/install/templates/bookmarklet.txt"() {
|
|
1368
|
+
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';
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1168
1371
|
|
|
1169
1372
|
// src/install/templates/install-page.html
|
|
1170
|
-
var install_page_default
|
|
1373
|
+
var install_page_default;
|
|
1374
|
+
var init_install_page = __esm({
|
|
1375
|
+
"src/install/templates/install-page.html"() {
|
|
1376
|
+
install_page_default = `<!DOCTYPE html>
|
|
1171
1377
|
<html lang="zh-CN">
|
|
1172
1378
|
<head>
|
|
1173
1379
|
<meta charset="utf-8">
|
|
@@ -1500,15 +1706,17 @@ var install_page_default = `<!DOCTYPE html>
|
|
|
1500
1706
|
<pre id="cfg">{
|
|
1501
1707
|
"mcpServers": {
|
|
1502
1708
|
"batter-pixso": {
|
|
1503
|
-
"
|
|
1504
|
-
"args": ["-y", "batter-pixso-mcp"],
|
|
1505
|
-
"env": { "HTTP_PORT": "{{ httpPort }}" }
|
|
1709
|
+
"url": "{{ mcpUrl }}"
|
|
1506
1710
|
}
|
|
1507
1711
|
}
|
|
1508
1712
|
}</pre>
|
|
1509
1713
|
<button class="copy" id="copy" type="button">\u590D\u5236</button>
|
|
1510
1714
|
</div>
|
|
1511
|
-
<p class="note"
|
|
1715
|
+
<p class="note">
|
|
1716
|
+
\u7C98\u8D34\u5230 Cursor / Cherry Studio \u7B49\u7684 MCP \u914D\u7F6E\u6587\u4EF6\uFF1BClaude Code \u6267\u884C
|
|
1717
|
+
<code>{{ claudeAddCommand }}</code>\u3002
|
|
1718
|
+
\u670D\u52A1\u672A\u542F\u52A8\u65F6\u5148\u5728\u7EC8\u7AEF\u6267\u884C <code>npx batter-pixso-mcp</code>\uFF08Windows \u53EF\u7528 <code>--setup</code> \u6CE8\u518C\u5F00\u673A\u81EA\u542F\uFF09\u3002
|
|
1719
|
+
</p>
|
|
1512
1720
|
</div>
|
|
1513
1721
|
</div>
|
|
1514
1722
|
|
|
@@ -1560,6 +1768,7 @@ copyBtn.addEventListener("click", function () {
|
|
|
1560
1768
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1561
1769
|
navigator.clipboard.writeText(text).then(done, done);
|
|
1562
1770
|
} else {
|
|
1771
|
+
// \u515C\u5E95\uFF1A\u65E0 clipboard API \u7684\u73AF\u5883\uFF08\u53CC\u6838\u6D4F\u89C8\u5668\u517C\u5BB9\u6A21\u5F0F\u7B49\uFF09
|
|
1563
1772
|
var ta = document.createElement("textarea");
|
|
1564
1773
|
ta.value = text;
|
|
1565
1774
|
document.body.appendChild(ta);
|
|
@@ -1573,6 +1782,8 @@ copyBtn.addEventListener("click", function () {
|
|
|
1573
1782
|
</body>
|
|
1574
1783
|
</html>
|
|
1575
1784
|
`;
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1576
1787
|
|
|
1577
1788
|
// src/install/page.ts
|
|
1578
1789
|
function bookmarkletHref(base) {
|
|
@@ -1580,26 +1791,27 @@ function bookmarkletHref(base) {
|
|
|
1580
1791
|
return `javascript:${encodeURIComponent(source)}`;
|
|
1581
1792
|
}
|
|
1582
1793
|
function renderInstallPage() {
|
|
1583
|
-
const base = `${
|
|
1794
|
+
const base = `${serviceBaseUrl()}/`;
|
|
1584
1795
|
return renderTemplate(install_page_default, {
|
|
1585
1796
|
version: VERSION,
|
|
1586
1797
|
href: bookmarkletHref(base),
|
|
1587
1798
|
base,
|
|
1588
|
-
|
|
1799
|
+
mcpUrl: mcpUrl(),
|
|
1800
|
+
claudeAddCommand: claudeAddCommand()
|
|
1589
1801
|
});
|
|
1590
1802
|
}
|
|
1803
|
+
var init_page = __esm({
|
|
1804
|
+
"src/install/page.ts"() {
|
|
1805
|
+
"use strict";
|
|
1806
|
+
init_config();
|
|
1807
|
+
init_template();
|
|
1808
|
+
init_bookmarklet();
|
|
1809
|
+
init_install_page();
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1591
1812
|
|
|
1592
1813
|
// src/install/zip.ts
|
|
1593
1814
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
1594
|
-
var CRC_TABLE = (() => {
|
|
1595
|
-
const table = new Uint32Array(256);
|
|
1596
|
-
for (let n = 0; n < 256; n++) {
|
|
1597
|
-
let c = n;
|
|
1598
|
-
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1599
|
-
table[n] = c >>> 0;
|
|
1600
|
-
}
|
|
1601
|
-
return table;
|
|
1602
|
-
})();
|
|
1603
1815
|
function crc32(buf) {
|
|
1604
1816
|
let crc = 4294967295;
|
|
1605
1817
|
for (const byte of buf) crc = CRC_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
@@ -1655,9 +1867,23 @@ function buildZip(entries) {
|
|
|
1655
1867
|
eocd.writeUInt32LE(offset, 16);
|
|
1656
1868
|
return Buffer2.concat([...chunks, centralBuf, eocd]);
|
|
1657
1869
|
}
|
|
1870
|
+
var CRC_TABLE;
|
|
1871
|
+
var init_zip = __esm({
|
|
1872
|
+
"src/install/zip.ts"() {
|
|
1873
|
+
"use strict";
|
|
1874
|
+
CRC_TABLE = (() => {
|
|
1875
|
+
const table = new Uint32Array(256);
|
|
1876
|
+
for (let n = 0; n < 256; n++) {
|
|
1877
|
+
let c = n;
|
|
1878
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1879
|
+
table[n] = c >>> 0;
|
|
1880
|
+
}
|
|
1881
|
+
return table;
|
|
1882
|
+
})();
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1658
1885
|
|
|
1659
1886
|
// src/install/api.ts
|
|
1660
|
-
var NO_DIST = "plugin dist not found\uFF08\u8FD0\u884C npm run build \u751F\u6210\uFF09";
|
|
1661
1887
|
function handleInstall(req, res, url) {
|
|
1662
1888
|
return dispatchRoute(INSTALL_ROUTES, req, res, url);
|
|
1663
1889
|
}
|
|
@@ -1665,169 +1891,269 @@ function sendBinary(res, buf, contentType) {
|
|
|
1665
1891
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "no-store" });
|
|
1666
1892
|
res.end(buf);
|
|
1667
1893
|
}
|
|
1668
|
-
var PLUGIN_FILE_ROUTES
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
}
|
|
1691
|
-
},
|
|
1692
|
-
{
|
|
1693
|
-
method: "GET",
|
|
1694
|
-
path: "/manifest.json",
|
|
1695
|
-
handler: async ({ res }) => {
|
|
1696
|
-
const buf = await pluginManifest();
|
|
1697
|
-
if (!buf) {
|
|
1698
|
-
sendJson(res, 404, { error: NO_DIST });
|
|
1699
|
-
return;
|
|
1894
|
+
var NO_DIST, PLUGIN_FILE_ROUTES, INSTALL_ROUTES;
|
|
1895
|
+
var init_api = __esm({
|
|
1896
|
+
"src/install/api.ts"() {
|
|
1897
|
+
"use strict";
|
|
1898
|
+
init_http_body();
|
|
1899
|
+
init_dist_resolver();
|
|
1900
|
+
init_page();
|
|
1901
|
+
init_zip();
|
|
1902
|
+
NO_DIST = "plugin dist not found\uFF08\u8FD0\u884C npm run build \u751F\u6210\uFF09";
|
|
1903
|
+
PLUGIN_FILE_ROUTES = [
|
|
1904
|
+
{ path: "/code.js", file: "code.js", type: "text/javascript; charset=utf-8" },
|
|
1905
|
+
{ path: "/ui.html", file: "ui.html", type: "text/html; charset=utf-8" }
|
|
1906
|
+
].map(({ path, file, type }) => ({
|
|
1907
|
+
method: "GET",
|
|
1908
|
+
path,
|
|
1909
|
+
handler: async ({ res }) => {
|
|
1910
|
+
const buf = await readPluginFile(file);
|
|
1911
|
+
if (!buf) {
|
|
1912
|
+
sendJson(res, 404, { error: hasPluginDist() ? "not found" : NO_DIST });
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
sendBinary(res, buf, type);
|
|
1700
1916
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1917
|
+
}));
|
|
1918
|
+
INSTALL_ROUTES = [
|
|
1919
|
+
{
|
|
1920
|
+
method: "GET",
|
|
1921
|
+
path: "/",
|
|
1922
|
+
handler: ({ res }) => {
|
|
1923
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1924
|
+
res.end(renderInstallPage());
|
|
1925
|
+
}
|
|
1926
|
+
},
|
|
1927
|
+
{
|
|
1928
|
+
method: "GET",
|
|
1929
|
+
path: "/manifest.json",
|
|
1930
|
+
handler: async ({ res }) => {
|
|
1931
|
+
const buf = await pluginManifest();
|
|
1932
|
+
if (!buf) {
|
|
1933
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
sendBinary(res, buf, "application/json; charset=utf-8");
|
|
1937
|
+
}
|
|
1938
|
+
},
|
|
1939
|
+
...PLUGIN_FILE_ROUTES,
|
|
1940
|
+
{
|
|
1941
|
+
method: "GET",
|
|
1942
|
+
path: "/plugin.zip",
|
|
1943
|
+
handler: async ({ res }) => {
|
|
1944
|
+
if (!hasPluginDist()) {
|
|
1945
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
try {
|
|
1949
|
+
const [manifest, code, ui] = await Promise.all([
|
|
1950
|
+
pluginManifest(),
|
|
1951
|
+
readPluginFile("code.js"),
|
|
1952
|
+
readPluginFile("ui.html")
|
|
1953
|
+
]);
|
|
1954
|
+
const zip = buildZip([
|
|
1955
|
+
{ name: "manifest.json", data: manifest },
|
|
1956
|
+
{ name: "code.js", data: code },
|
|
1957
|
+
{ name: "ui.html", data: ui }
|
|
1958
|
+
]);
|
|
1959
|
+
res.writeHead(200, {
|
|
1960
|
+
"Content-Type": "application/zip",
|
|
1961
|
+
"Content-Disposition": 'attachment; filename="batter-pixso-bridge.zip"'
|
|
1962
|
+
});
|
|
1963
|
+
res.end(zip);
|
|
1964
|
+
} catch {
|
|
1965
|
+
sendJson(res, 500, { error: "zip build failed" });
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
];
|
|
1970
|
+
}
|
|
1971
|
+
});
|
|
1972
|
+
|
|
1973
|
+
// src/plugin/assets.ts
|
|
1974
|
+
import { createHash } from "node:crypto";
|
|
1975
|
+
import { mkdir, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
1976
|
+
import { join as join4 } from "node:path";
|
|
1977
|
+
async function saveAsset(bytes, format) {
|
|
1978
|
+
dirReady ??= mkdir(ASSETS_DIR, { recursive: true });
|
|
1979
|
+
await dirReady;
|
|
1980
|
+
const ext = EXT_BY_FORMAT[format.toUpperCase()] ?? ".bin";
|
|
1981
|
+
const name = `${createHash("sha1").update(bytes).digest("hex").slice(0, 16)}${ext}`;
|
|
1982
|
+
await writeFile(join4(ASSETS_DIR, name), bytes);
|
|
1983
|
+
log(`asset saved: ${name} (${bytes.length} bytes)`);
|
|
1984
|
+
return name;
|
|
1985
|
+
}
|
|
1986
|
+
function substituteAssets(result, assets) {
|
|
1987
|
+
if (!assets.length) return result;
|
|
1988
|
+
const nameByKey = new Map(assets.map((a) => [a.key, a.name]));
|
|
1989
|
+
return JSON.parse(
|
|
1990
|
+
JSON.stringify(
|
|
1991
|
+
result,
|
|
1992
|
+
(_k, v) => typeof v === "string" ? nameByKey.get(v) ?? v : v
|
|
1993
|
+
)
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
async function cleanOldAssets(ttlMs) {
|
|
1997
|
+
let removed = 0;
|
|
1998
|
+
const cutoff = Date.now() - ttlMs;
|
|
1999
|
+
let names;
|
|
2000
|
+
try {
|
|
2001
|
+
names = await readdir(ASSETS_DIR);
|
|
2002
|
+
} catch {
|
|
2003
|
+
return 0;
|
|
2004
|
+
}
|
|
2005
|
+
const expired = await Promise.all(
|
|
2006
|
+
names.map(async (name) => {
|
|
2007
|
+
try {
|
|
2008
|
+
const p = join4(ASSETS_DIR, name);
|
|
2009
|
+
const s = await stat(p);
|
|
2010
|
+
return s.isFile() && s.mtimeMs < cutoff ? p : null;
|
|
2011
|
+
} catch {
|
|
2012
|
+
return null;
|
|
1712
2013
|
}
|
|
2014
|
+
})
|
|
2015
|
+
);
|
|
2016
|
+
await Promise.all(
|
|
2017
|
+
expired.filter((p) => p !== null).map(async (p) => {
|
|
1713
2018
|
try {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
readPluginFile("code.js"),
|
|
1717
|
-
readPluginFile("ui.html")
|
|
1718
|
-
]);
|
|
1719
|
-
const zip = buildZip([
|
|
1720
|
-
{ name: "manifest.json", data: manifest },
|
|
1721
|
-
{ name: "code.js", data: code },
|
|
1722
|
-
{ name: "ui.html", data: ui }
|
|
1723
|
-
]);
|
|
1724
|
-
res.writeHead(200, {
|
|
1725
|
-
"Content-Type": "application/zip",
|
|
1726
|
-
"Content-Disposition": 'attachment; filename="batter-pixso-bridge.zip"'
|
|
1727
|
-
});
|
|
1728
|
-
res.end(zip);
|
|
2019
|
+
await unlink(p);
|
|
2020
|
+
removed++;
|
|
1729
2021
|
} catch {
|
|
1730
|
-
sendJson(res, 500, { error: "zip build failed" });
|
|
1731
2022
|
}
|
|
1732
|
-
}
|
|
2023
|
+
})
|
|
2024
|
+
);
|
|
2025
|
+
if (removed > 0) log(`assets cleanup: removed ${removed} expired file(s)`);
|
|
2026
|
+
return removed;
|
|
2027
|
+
}
|
|
2028
|
+
function scheduleAssetsSweep(ttlMs) {
|
|
2029
|
+
void cleanOldAssets(ttlMs);
|
|
2030
|
+
const sweeper2 = setInterval(() => void cleanOldAssets(ttlMs), ASSET_SWEEP_INTERVAL_MS);
|
|
2031
|
+
sweeper2.unref();
|
|
2032
|
+
}
|
|
2033
|
+
var EXT_BY_FORMAT, dirReady;
|
|
2034
|
+
var init_assets = __esm({
|
|
2035
|
+
"src/plugin/assets.ts"() {
|
|
2036
|
+
"use strict";
|
|
2037
|
+
init_config();
|
|
2038
|
+
EXT_BY_FORMAT = {
|
|
2039
|
+
PNG: ".png",
|
|
2040
|
+
SVG: ".svg",
|
|
2041
|
+
JPG: ".jpg",
|
|
2042
|
+
JPEG: ".jpg",
|
|
2043
|
+
WEBP: ".webp",
|
|
2044
|
+
PDF: ".pdf"
|
|
2045
|
+
// export_image 尊重设计师导出标记,标记可能指定 PDF
|
|
2046
|
+
};
|
|
2047
|
+
dirReady = null;
|
|
1733
2048
|
}
|
|
1734
|
-
|
|
2049
|
+
});
|
|
1735
2050
|
|
|
1736
2051
|
// src/plugin/api.ts
|
|
1737
2052
|
function handlePluginApi(req, res, url) {
|
|
1738
2053
|
return dispatchRoute(PLUGIN_ROUTES, req, res, url);
|
|
1739
2054
|
}
|
|
1740
|
-
var PLUGIN_ROUTES
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
2055
|
+
var PLUGIN_ROUTES;
|
|
2056
|
+
var init_api2 = __esm({
|
|
2057
|
+
"src/plugin/api.ts"() {
|
|
2058
|
+
"use strict";
|
|
2059
|
+
init_config();
|
|
2060
|
+
init_http_body();
|
|
2061
|
+
init_assets();
|
|
2062
|
+
init_command_bus();
|
|
2063
|
+
init_sessions();
|
|
2064
|
+
PLUGIN_ROUTES = [
|
|
2065
|
+
{
|
|
2066
|
+
method: "POST",
|
|
2067
|
+
path: "/sessions/register",
|
|
2068
|
+
handler: async ({ req, res }) => {
|
|
2069
|
+
sendJson(res, 201, sessions.register(await readJsonBody(req)));
|
|
2070
|
+
}
|
|
2071
|
+
},
|
|
2072
|
+
{
|
|
2073
|
+
method: "POST",
|
|
2074
|
+
path: "/context",
|
|
2075
|
+
handler: async ({ req, res, url }) => {
|
|
2076
|
+
const session = url.searchParams.get("session");
|
|
2077
|
+
if (!session) {
|
|
2078
|
+
sendJson(res, 400, { error: "missing session" });
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
const meta = await readJsonBody(req);
|
|
2082
|
+
if (!sessions.updateContext(session, meta)) {
|
|
2083
|
+
sendJson(res, 404, { error: "unknown_session" });
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
sendJson(res, 200, { ok: true });
|
|
2087
|
+
}
|
|
2088
|
+
},
|
|
2089
|
+
{
|
|
2090
|
+
method: "GET",
|
|
2091
|
+
path: "/commands/stream",
|
|
2092
|
+
handler: ({ req, res, url }) => {
|
|
2093
|
+
const session = url.searchParams.get("session");
|
|
2094
|
+
if (!session || !sessions.setConnected(session, true)) {
|
|
2095
|
+
sendJson(res, 404, { error: "unknown_session" });
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
res.writeHead(200, {
|
|
2099
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
2100
|
+
"Cache-Control": "no-store",
|
|
2101
|
+
Connection: "keep-alive"
|
|
2102
|
+
});
|
|
2103
|
+
res.write(": connected\n\n");
|
|
2104
|
+
const unsubscribe = commandBus.subscribe(session, (command) => {
|
|
2105
|
+
res.write(`data: ${JSON.stringify({ command })}
|
|
1782
2106
|
|
|
1783
2107
|
`);
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
2108
|
+
});
|
|
2109
|
+
const teardown = () => {
|
|
2110
|
+
clearInterval(heartbeat);
|
|
2111
|
+
unsubscribe();
|
|
2112
|
+
sessions.setConnected(session, false);
|
|
2113
|
+
};
|
|
2114
|
+
const heartbeat = setInterval(() => {
|
|
2115
|
+
if (!res.write(": ping\n\n")) {
|
|
2116
|
+
teardown();
|
|
2117
|
+
res.end();
|
|
2118
|
+
log(`SSE ghost reaped: session ${session.slice(0, 8)}`);
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
sessions.touchSeen(session);
|
|
2122
|
+
}, SSE_HEARTBEAT_MS);
|
|
2123
|
+
req.on("close", () => {
|
|
2124
|
+
teardown();
|
|
2125
|
+
log(`SSE channel closed: session ${session.slice(0, 8)}`);
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
method: "POST",
|
|
2131
|
+
path: "/commands/:id/result",
|
|
2132
|
+
handler: async ({ req, res, params }) => {
|
|
2133
|
+
const payload = await readJsonBody(req);
|
|
2134
|
+
const result = payload.ok && payload.assets?.length ? substituteAssets(payload.result, payload.assets) : payload.result;
|
|
2135
|
+
const found = commandBus.complete(params["id"], payload.ok, payload.ok ? result : payload.error);
|
|
2136
|
+
sendJson(res, found ? 200 : 404, found ? { ok: true } : { error: "unknown command" });
|
|
2137
|
+
}
|
|
2138
|
+
},
|
|
2139
|
+
{
|
|
2140
|
+
method: "POST",
|
|
2141
|
+
path: "/assets",
|
|
2142
|
+
handler: async ({ req, res, url }) => {
|
|
2143
|
+
try {
|
|
2144
|
+
const name = await saveAsset(await readBody(req), url.searchParams.get("format") ?? "PNG");
|
|
2145
|
+
sendJson(res, 201, { name });
|
|
2146
|
+
} catch {
|
|
2147
|
+
sendJson(res, 500, { error: "asset save failed" });
|
|
2148
|
+
}
|
|
1796
2149
|
}
|
|
1797
|
-
sessions.touchSeen(session);
|
|
1798
|
-
}, 15e3);
|
|
1799
|
-
req.on("close", () => {
|
|
1800
|
-
teardown();
|
|
1801
|
-
log(`SSE channel closed: session ${session.slice(0, 8)}`);
|
|
1802
|
-
});
|
|
1803
|
-
}
|
|
1804
|
-
},
|
|
1805
|
-
{
|
|
1806
|
-
method: "POST",
|
|
1807
|
-
path: "/commands/:id/result",
|
|
1808
|
-
handler: async ({ req, res, params }) => {
|
|
1809
|
-
const payload = await readJsonBody(req);
|
|
1810
|
-
const result = payload.ok && payload.assets?.length ? substituteAssets(payload.result, payload.assets) : payload.result;
|
|
1811
|
-
const found = commandBus.complete(params["id"], payload.ok, payload.ok ? result : payload.error);
|
|
1812
|
-
sendJson(res, found ? 200 : 404, found ? { ok: true } : { error: "unknown command" });
|
|
1813
|
-
}
|
|
1814
|
-
},
|
|
1815
|
-
{
|
|
1816
|
-
method: "POST",
|
|
1817
|
-
path: "/assets",
|
|
1818
|
-
handler: async ({ req, res, url }) => {
|
|
1819
|
-
try {
|
|
1820
|
-
const name = await saveAsset(await readBody(req), url.searchParams.get("format") ?? "PNG");
|
|
1821
|
-
sendJson(res, 201, { name });
|
|
1822
|
-
} catch {
|
|
1823
|
-
sendJson(res, 500, { error: "asset save failed" });
|
|
1824
2150
|
}
|
|
1825
|
-
|
|
2151
|
+
];
|
|
1826
2152
|
}
|
|
1827
|
-
|
|
2153
|
+
});
|
|
1828
2154
|
|
|
1829
2155
|
// src/http.ts
|
|
1830
|
-
|
|
2156
|
+
import { createServer } from "node:http";
|
|
1831
2157
|
function startHttpServer() {
|
|
1832
2158
|
const server = createServer((req, res) => {
|
|
1833
2159
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
@@ -1844,7 +2170,7 @@ function startHttpServer() {
|
|
|
1844
2170
|
const all = sessions.list();
|
|
1845
2171
|
sendJson(res, 200, {
|
|
1846
2172
|
ok: true,
|
|
1847
|
-
name:
|
|
2173
|
+
name: SERVICE_NAME,
|
|
1848
2174
|
pid: process.pid,
|
|
1849
2175
|
version: VERSION,
|
|
1850
2176
|
pluginsOnline: all.filter((s) => s.online).length
|
|
@@ -1868,128 +2194,140 @@ function startHttpServer() {
|
|
|
1868
2194
|
});
|
|
1869
2195
|
server.listen(config.httpPort, "127.0.0.1", () => {
|
|
1870
2196
|
log(`plugin HTTP + MCP server on http://127.0.0.1:${config.httpPort} (MCP endpoint: /mcp, pid ${process.pid})`);
|
|
2197
|
+
scheduleAssetsSweep(config.assetTtlMs);
|
|
1871
2198
|
resolve(server);
|
|
1872
2199
|
});
|
|
1873
2200
|
});
|
|
1874
2201
|
}
|
|
2202
|
+
var ROUTES;
|
|
2203
|
+
var init_http2 = __esm({
|
|
2204
|
+
"src/http.ts"() {
|
|
2205
|
+
"use strict";
|
|
2206
|
+
init_http();
|
|
2207
|
+
init_config();
|
|
2208
|
+
init_http_body();
|
|
2209
|
+
init_api();
|
|
2210
|
+
init_api2();
|
|
2211
|
+
init_assets();
|
|
2212
|
+
init_sessions();
|
|
2213
|
+
ROUTES = [handleInstall, handleMcp, handlePluginApi];
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
1875
2216
|
|
|
1876
|
-
// src/
|
|
1877
|
-
|
|
2217
|
+
// src/cli/serve.ts
|
|
2218
|
+
var serve_exports = {};
|
|
2219
|
+
__export(serve_exports, {
|
|
2220
|
+
printUsage: () => printUsage,
|
|
2221
|
+
serve: () => serve
|
|
2222
|
+
});
|
|
2223
|
+
async function serve() {
|
|
2224
|
+
const { state, health } = await probeService();
|
|
2225
|
+
if (state === "alive" && health?.version === VERSION) {
|
|
2226
|
+
printUsage();
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
if (state === "alive") {
|
|
2230
|
+
throw new Error(
|
|
2231
|
+
`\u670D\u52A1\u5DF2\u5728\u8FD0\u884C\u4E14\u7248\u672C\u4E0D\u7B26\uFF08\u8FD0\u884C ${health?.version} / \u5F53\u524D ${VERSION}\uFF09\u2014\u2014\u5148\u6267\u884C --stop \u505C\u6B62\u65E7\u7248\u672C\u518D\u542F\u52A8`
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
if (state === "hung" || state === "foreign") {
|
|
2235
|
+
throw serviceStateError(state);
|
|
2236
|
+
}
|
|
1878
2237
|
const server = await startHttpServer();
|
|
1879
2238
|
if (!server) {
|
|
1880
|
-
|
|
1881
|
-
|
|
2239
|
+
throw new Error(
|
|
2240
|
+
`\u7AEF\u53E3 ${config.httpPort} \u5728\u542F\u52A8\u77AC\u95F4\u88AB\u5360\u7528\uFF08\u53EF\u80FD\u662F\u767B\u5F55\u81EA\u542F\u5B9E\u4F8B\u62A2\u5148\u542F\u52A8\u2014\u2014\u91CD\u8DD1\u672C\u547D\u4EE4\u5373\u53EF\u81EA\u52A8\u8BC6\u522B\uFF1B\u82E5\u6301\u7EED\u88AB\u5360\uFF0C${PORT_FIX_HINT}\uFF09`
|
|
2241
|
+
);
|
|
1882
2242
|
}
|
|
1883
|
-
|
|
1884
|
-
log("hub ready\uFF08\u5E38\u9A7B\uFF0C\u4E0D\u81EA\u6BC1\uFF1B\u7248\u672C\u4E0D\u7B26\u65F6\u7531 proxy \u81EA\u52A8\u6362\u8840\uFF0C\u6216 --stop \u624B\u52A8\u505C\u6B62\uFF09");
|
|
1885
|
-
void cleanOldAssets(config.assetTtlMs);
|
|
1886
|
-
const sweeper2 = setInterval(() => void cleanOldAssets(config.assetTtlMs), 60 * 6e4);
|
|
1887
|
-
sweeper2.unref();
|
|
2243
|
+
printUsage();
|
|
1888
2244
|
}
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
spawnHub();
|
|
1915
|
-
} else if (state === "hung") {
|
|
1916
|
-
log("hub \u65E0\u54CD\u5E94\uFF08\u50F5\u5C38\u8FDB\u7A0B\uFF09\uFF0C\u5F3A\u6740\u540E\u91CD\u5EFA\u2026");
|
|
1917
|
-
if (!killHubByPidFile()) throw hubStateError("hung");
|
|
1918
|
-
await waitForHubDown(1e4);
|
|
1919
|
-
spawnHub();
|
|
1920
|
-
} else if (state === "down") {
|
|
1921
|
-
spawnHub();
|
|
1922
|
-
} else if (state === "foreign") {
|
|
1923
|
-
throw hubStateError(state);
|
|
1924
|
-
}
|
|
1925
|
-
await waitForHub(3e4);
|
|
1926
|
-
upstream = await connectUpstream();
|
|
1927
|
-
log(`proxy connected to ${mcpUrl}`);
|
|
1928
|
-
return upstream;
|
|
1929
|
-
}
|
|
1930
|
-
let rebuilding = null;
|
|
1931
|
-
function rebuildUpstream() {
|
|
1932
|
-
rebuilding ??= (async () => {
|
|
1933
|
-
const stale = upstream;
|
|
1934
|
-
upstream = null;
|
|
1935
|
-
if (stale) await stale.close().catch(() => {
|
|
1936
|
-
});
|
|
1937
|
-
return ensureUpstream();
|
|
1938
|
-
})().finally(() => {
|
|
1939
|
-
rebuilding = null;
|
|
1940
|
-
});
|
|
1941
|
-
return rebuilding;
|
|
2245
|
+
function printUsage() {
|
|
2246
|
+
const url = mcpUrl();
|
|
2247
|
+
console.log(
|
|
2248
|
+
[
|
|
2249
|
+
``,
|
|
2250
|
+
`MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\uFF08URL \u76F4\u8FDE\uFF0C\u4E00\u4E2A\u5730\u5740\u5168\u5BA2\u6237\u7AEF\u901A\u7528\uFF09\uFF1A`,
|
|
2251
|
+
``,
|
|
2252
|
+
` Claude Code\uFF08\u4E00\u884C\u547D\u4EE4\uFF09\uFF1A`,
|
|
2253
|
+
` ${claudeAddCommand()}`,
|
|
2254
|
+
``,
|
|
2255
|
+
` \u901A\u7528 JSON\uFF08Cursor / Cherry Studio \u7B49\uFF09\uFF1A`,
|
|
2256
|
+
` { "mcpServers": { "batter-pixso": { "url": "${url}" } } }`,
|
|
2257
|
+
``,
|
|
2258
|
+
`\u63D2\u4EF6\u5B89\u88C5\uFF1A\u6D4F\u89C8\u5668\u6253\u5F00 ${serviceBaseUrl()}/ \u6309\u5F15\u5BFC\u5B89\u88C5`,
|
|
2259
|
+
`\u5F00\u673A\u81EA\u542F\uFF1Anpx batter-pixso-mcp --setup\uFF08Windows\uFF09`,
|
|
2260
|
+
`\u505C\u6B62\u670D\u52A1\uFF1Anpx batter-pixso-mcp --stop`
|
|
2261
|
+
].join("\n")
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
var init_serve = __esm({
|
|
2265
|
+
"src/cli/serve.ts"() {
|
|
2266
|
+
"use strict";
|
|
2267
|
+
init_config();
|
|
2268
|
+
init_http2();
|
|
2269
|
+
init_service();
|
|
1942
2270
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
2271
|
+
});
|
|
2272
|
+
|
|
2273
|
+
// src/cli/index.ts
|
|
2274
|
+
init_config();
|
|
2275
|
+
|
|
2276
|
+
// src/cli/commands.ts
|
|
2277
|
+
init_config();
|
|
2278
|
+
var COMMANDS = [
|
|
2279
|
+
{
|
|
2280
|
+
flags: ["--stop"],
|
|
2281
|
+
summary: "\u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1",
|
|
2282
|
+
run: async () => (await Promise.resolve().then(() => (init_service(), service_exports))).stopService()
|
|
2283
|
+
},
|
|
2284
|
+
{
|
|
2285
|
+
flags: ["--version", "-v"],
|
|
2286
|
+
summary: "\u6253\u5370\u7248\u672C\u53F7",
|
|
2287
|
+
run: async () => log(VERSION)
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
flags: ["--uninstall"],
|
|
2291
|
+
summary: "\u79FB\u9664\u767B\u5F55\u81EA\u542F + \u505C\u6B62\u670D\u52A1 + \u5220\u9664\u7269\u5316\u76EE\u5F55",
|
|
2292
|
+
run: async () => (await Promise.resolve().then(() => (init_setup(), setup_exports))).uninstallAutostart()
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
flags: ["--setup"],
|
|
2296
|
+
summary: "\uFF08Windows\uFF09\u7269\u5316\u81EA\u5305\u542B\u526F\u672C\u5230 %LOCALAPPDATA% \u5E76\u6CE8\u518C\u767B\u5F55\u81EA\u542F\uFF0C\u968F\u540E\u7ACB\u5373\u540E\u53F0\u542F\u52A8\u670D\u52A1\uFF08\u4E0D\u5360\u524D\u53F0\uFF09",
|
|
2297
|
+
run: async () => {
|
|
2298
|
+
await (await Promise.resolve().then(() => (init_setup(), setup_exports))).setupAutostart();
|
|
2299
|
+
(await Promise.resolve().then(() => (init_serve(), serve_exports))).printUsage();
|
|
1956
2300
|
}
|
|
2301
|
+
},
|
|
2302
|
+
{
|
|
2303
|
+
flags: ["--help", "-h"],
|
|
2304
|
+
summary: "\u6253\u5370\u672C\u5E2E\u52A9",
|
|
2305
|
+
run: async () => printHelp()
|
|
1957
2306
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
2307
|
+
];
|
|
2308
|
+
function printHelp() {
|
|
2309
|
+
console.log(
|
|
2310
|
+
[
|
|
2311
|
+
`batter-pixso-mcp v${VERSION}`,
|
|
2312
|
+
``,
|
|
2313
|
+
`\u7528\u6CD5\uFF1Abatter-pixso-mcp [\u547D\u4EE4]\uFF08\u4E0D\u5E26\u547D\u4EE4 = \u524D\u53F0\u542F\u52A8\u670D\u52A1\uFF09`,
|
|
2314
|
+
``,
|
|
2315
|
+
...COMMANDS.map((c) => ` ${c.flags.join(", ").padEnd(18)}${c.summary}`)
|
|
2316
|
+
].join("\n")
|
|
1964
2317
|
);
|
|
1965
|
-
server.fallbackRequestHandler = (req, extra) => callWithRecovery((c) => c.request(req, ResultSchema, { signal: extra.signal }));
|
|
1966
|
-
server.onclose = () => {
|
|
1967
|
-
log("agent disconnected, proxy exiting");
|
|
1968
|
-
process.exit(0);
|
|
1969
|
-
};
|
|
1970
|
-
await server.connect(new StdioServerTransport());
|
|
1971
|
-
log("proxy ready on stdio");
|
|
1972
2318
|
}
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
await runHub();
|
|
1978
|
-
return;
|
|
1979
|
-
}
|
|
1980
|
-
if (process.argv.includes("--stop")) {
|
|
1981
|
-
await stopHub();
|
|
1982
|
-
return;
|
|
1983
|
-
}
|
|
1984
|
-
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
1985
|
-
log(VERSION);
|
|
2319
|
+
async function dispatch(argv) {
|
|
2320
|
+
const hit = COMMANDS.find((c) => c.flags.some((f) => argv.includes(f)));
|
|
2321
|
+
if (hit) {
|
|
2322
|
+
await hit.run();
|
|
1986
2323
|
return;
|
|
1987
2324
|
}
|
|
1988
|
-
await
|
|
2325
|
+
const { serve: serve2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
|
|
2326
|
+
await serve2();
|
|
1989
2327
|
}
|
|
1990
2328
|
|
|
1991
|
-
// src/
|
|
1992
|
-
|
|
1993
|
-
|
|
2329
|
+
// src/cli/index.ts
|
|
2330
|
+
dispatch(process.argv).catch((err) => {
|
|
2331
|
+
log("fatal:", err);
|
|
1994
2332
|
process.exit(1);
|
|
1995
2333
|
});
|