batter-pixso-mcp 0.5.0 → 0.7.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 +1306 -853
- package/dist/plugin/code.js +5 -9
- package/dist/plugin/ui.html +124 -133
- package/package.json +9 -4
- package/standalone/README.txt +6 -3
- package/standalone/index.js +64 -68
- package/standalone/plugin/code.js +5 -9
- package/standalone/plugin/ui.html +124 -133
package/dist/index.js
CHANGED
|
@@ -1,210 +1,296 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
+
};
|
|
7
11
|
|
|
8
12
|
// src/config.ts
|
|
9
13
|
import { tmpdir } from "node:os";
|
|
10
14
|
import { join } from "node:path";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/** hub 空闲自毁阈值(无插件、无 agent 请求),默认 30 分钟 */
|
|
18
|
-
hubIdleMs: Number(process.env.HUB_IDLE_MS ?? 30 * 60 * 1e3),
|
|
19
|
-
/** 产物图片保留时长,默认 7 天 */
|
|
20
|
-
assetTtlMs: Number(process.env.ASSET_TTL_DAYS ?? 7) * 24 * 60 * 60 * 1e3
|
|
21
|
-
};
|
|
22
|
-
function hubBaseUrl() {
|
|
15
|
+
function sleep(ms) {
|
|
16
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
17
|
+
setTimeout(resolve, ms);
|
|
18
|
+
return promise;
|
|
19
|
+
}
|
|
20
|
+
function serviceBaseUrl() {
|
|
23
21
|
return `http://127.0.0.1:${config.httpPort}`;
|
|
24
22
|
}
|
|
23
|
+
function mcpUrl() {
|
|
24
|
+
return `${serviceBaseUrl()}/mcp`;
|
|
25
|
+
}
|
|
26
|
+
function claudeAddCommand() {
|
|
27
|
+
return `claude mcp add --transport http batter-pixso ${mcpUrl()}`;
|
|
28
|
+
}
|
|
25
29
|
function log(...args) {
|
|
26
30
|
console.error("[batter-pixso-mcp]", ...args);
|
|
27
31
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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.7.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
|
+
});
|
|
57
|
+
|
|
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() {
|
|
68
|
+
try {
|
|
69
|
+
const res = await fetch(`${serviceBaseUrl()}/health`, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
|
|
70
|
+
if (!res.ok) return { state: "foreign" };
|
|
71
|
+
let body;
|
|
72
|
+
try {
|
|
73
|
+
body = await res.json();
|
|
74
|
+
} catch {
|
|
75
|
+
return { state: "foreign" };
|
|
76
|
+
}
|
|
77
|
+
if (body.ok === true && body.name === SERVICE_NAME) return { state: "alive", health: body };
|
|
78
|
+
return { state: "foreign" };
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const hung = err instanceof Error && err.name === "TimeoutError";
|
|
81
|
+
return { state: hung ? "hung" : "down" };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function serviceStateError(state) {
|
|
85
|
+
if (state === "hung") {
|
|
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}`);
|
|
87
|
+
}
|
|
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`);
|
|
89
|
+
}
|
|
90
|
+
function killServiceByPid(pid) {
|
|
91
|
+
try {
|
|
92
|
+
process.kill(pid);
|
|
93
|
+
return true;
|
|
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";
|
|
36
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`;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
37
123
|
|
|
38
|
-
// src/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const name = `${createHash("sha1").update(bytes).digest("hex").slice(0, 16)}${ext}`;
|
|
54
|
-
await writeFile(join2(ASSETS_DIR, name), bytes);
|
|
55
|
-
log(`asset saved: ${name} (${bytes.length} bytes)`);
|
|
56
|
-
return name;
|
|
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;
|
|
57
139
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return JSON.parse(
|
|
62
|
-
JSON.stringify(
|
|
63
|
-
result,
|
|
64
|
-
(_k, v) => typeof v === "string" ? nameByKey.get(v) ?? v : v
|
|
65
|
-
)
|
|
66
|
-
);
|
|
140
|
+
function pluginDistDir() {
|
|
141
|
+
resolved ??= resolvePluginDir();
|
|
142
|
+
return resolved;
|
|
67
143
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
|
72
159
|
try {
|
|
73
|
-
|
|
160
|
+
return await readFile(join2(dir, name));
|
|
74
161
|
} catch {
|
|
75
|
-
return
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
var resolved;
|
|
166
|
+
var init_dist_resolver = __esm({
|
|
167
|
+
"src/install/dist-resolver.ts"() {
|
|
168
|
+
"use strict";
|
|
169
|
+
init_config();
|
|
170
|
+
resolved = null;
|
|
76
171
|
}
|
|
77
|
-
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// src/cli/setup.ts
|
|
175
|
+
var setup_exports = {};
|
|
176
|
+
__export(setup_exports, {
|
|
177
|
+
setupAutostart: () => setupAutostart,
|
|
178
|
+
uninstallAutostart: () => uninstallAutostart
|
|
179
|
+
});
|
|
180
|
+
import { 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++) {
|
|
78
194
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
+
);
|
|
84
204
|
}
|
|
85
|
-
|
|
205
|
+
await sleep(500);
|
|
86
206
|
}
|
|
87
207
|
}
|
|
88
|
-
if (removed > 0) log(`assets cleanup: removed ${removed} expired file(s)`);
|
|
89
|
-
return removed;
|
|
90
208
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
import { randomUUID } from "node:crypto";
|
|
94
|
-
var ONLINE_WINDOW_MS = 45e3;
|
|
95
|
-
function unknownTargetError(target) {
|
|
96
|
-
return new Error(`\u627E\u4E0D\u5230\u76EE\u6807\u63D2\u4EF6\u4F1A\u8BDD\uFF1A${target}\u3002\u53EF\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u8FDE\u63A5\u7684\u4F1A\u8BDD`);
|
|
209
|
+
async function copyWithRetry(src, dest) {
|
|
210
|
+
await withTransientLockRetry(dest, () => copyFile(src, dest));
|
|
97
211
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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;
|
|
238
|
+
try {
|
|
239
|
+
return readFileSync(join3(DIR, "version.txt"), "utf8").trim() || null;
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
126
242
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.evict(s.id);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
const session = { id: randomUUID(), meta, lastSeen: Date.now(), connected: false };
|
|
136
|
-
this.sessions.set(session.id, session);
|
|
137
|
-
this.lastActiveId = session.id;
|
|
138
|
-
log(`session registered: ${session.id} (${meta.fileName ?? "unknown file"})`);
|
|
139
|
-
return this.toInfo(session);
|
|
140
|
-
}
|
|
141
|
-
/** 心跳/连接状态用:只刷 lastSeen,不影响 lastActiveId("最近活跃"= 用户行为,心跳不算) */
|
|
142
|
-
touchSeen(id) {
|
|
143
|
-
const s = this.sessions.get(id);
|
|
144
|
-
if (!s) return null;
|
|
145
|
-
s.lastSeen = Date.now();
|
|
146
|
-
return s;
|
|
147
|
-
}
|
|
148
|
-
/** 用户行为(选区上报/命令服务/注册):刷 lastSeen 并标记最近活跃 */
|
|
149
|
-
touch(id) {
|
|
150
|
-
const s = this.touchSeen(id);
|
|
151
|
-
if (!s) return null;
|
|
152
|
-
this.lastActiveId = id;
|
|
153
|
-
return s;
|
|
154
|
-
}
|
|
155
|
-
setConnected(id, connected) {
|
|
156
|
-
const s = this.touchSeen(id);
|
|
157
|
-
if (!s) return false;
|
|
158
|
-
s.connected = connected;
|
|
159
|
-
return true;
|
|
243
|
+
}
|
|
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");
|
|
160
247
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
`context updated [${id.slice(0, 8)}]: ${s.meta.fileName ?? "?"} / ${s.meta.pageName ?? "?"} / ${s.meta.selection?.length ?? 0} selected`
|
|
167
|
-
);
|
|
168
|
-
return true;
|
|
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}`}`);
|
|
169
253
|
}
|
|
170
|
-
|
|
171
|
-
|
|
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
|
+
}
|
|
256
|
+
async function uninstallAutostart() {
|
|
257
|
+
if (process.platform === "win32") {
|
|
258
|
+
spawnSync("reg", ["delete", RUN_KEY, "/v", RUN_VALUE, "/f"]);
|
|
259
|
+
log("\u5DF2\u79FB\u9664\u5F00\u673A\u81EA\u542F\uFF08\u82E5\u66FE\u6CE8\u518C\uFF09");
|
|
172
260
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
261
|
+
const result = await stopRunningService();
|
|
262
|
+
if (result === "failed" || result === "hung") {
|
|
263
|
+
log(`\u670D\u52A1\u505C\u6B62\u5931\u8D25\uFF0C\u7269\u5316\u76EE\u5F55\u53EF\u80FD\u5220\u4E0D\u51C0\u3002${PORT_FIX_HINT}`);
|
|
176
264
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (s && this.isOnline(s)) return s;
|
|
265
|
+
if (DIR && existsSync2(DIR)) {
|
|
266
|
+
try {
|
|
267
|
+
rmSync(DIR, { recursive: true, force: true, maxRetries: 4, retryDelay: 500 });
|
|
268
|
+
} catch {
|
|
182
269
|
}
|
|
183
|
-
|
|
184
|
-
if (fallback) return fallback;
|
|
185
|
-
throw new Error("Pixso \u63D2\u4EF6\u672A\u8FDE\u63A5\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6");
|
|
186
|
-
}
|
|
187
|
-
toInfo(s) {
|
|
188
|
-
return {
|
|
189
|
-
sessionId: s.id,
|
|
190
|
-
fileKey: s.meta.fileKey,
|
|
191
|
-
fileName: s.meta.fileName,
|
|
192
|
-
pageName: s.meta.pageName,
|
|
193
|
-
selectionCount: s.meta.selection?.length,
|
|
194
|
-
online: this.isOnline(s),
|
|
195
|
-
lastSeen: s.lastSeen
|
|
196
|
-
};
|
|
270
|
+
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}`);
|
|
197
271
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
272
|
+
log("\u5378\u8F7D\u5B8C\u6210\uFF08npm \u5168\u5C40/npx \u7F13\u5B58\u4E2D\u7684\u5305\u5982\u9700\u6E05\u7406\u8BF7\u81EA\u884C npm uninstall\uFF09");
|
|
273
|
+
}
|
|
274
|
+
var DIR, RUN_KEY, RUN_VALUE, SERVICE_LOG_NAME, RUN_VBS, TRANSIENT_LOCK_ERRORS;
|
|
275
|
+
var init_setup = __esm({
|
|
276
|
+
"src/cli/setup.ts"() {
|
|
277
|
+
"use strict";
|
|
278
|
+
init_config();
|
|
279
|
+
init_dist_resolver();
|
|
280
|
+
init_service();
|
|
281
|
+
DIR = process.env.LOCALAPPDATA ? join3(process.env.LOCALAPPDATA, "batter-pixso-mcp") : null;
|
|
282
|
+
RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
283
|
+
RUN_VALUE = "BatterPixsoMcp";
|
|
284
|
+
SERVICE_LOG_NAME = "batter-pixso-mcp-service.log";
|
|
285
|
+
RUN_VBS = [
|
|
286
|
+
"Set fso = CreateObject(" + JSON.stringify("Scripting.FileSystemObject") + ")",
|
|
287
|
+
"dir = fso.GetParentFolderName(WScript.ScriptFullName)",
|
|
288
|
+
// 路径由脚本自身位置推导,不写死绝对路径(用户名含中文/空格时 VBS 字面量会翻车)
|
|
289
|
+
'CreateObject("Wscript.Shell").Run """" & dir & "\\service.cmd""", 0, False'
|
|
290
|
+
].join("\r\n");
|
|
291
|
+
TRANSIENT_LOCK_ERRORS = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
208
294
|
|
|
209
295
|
// src/http-body.ts
|
|
210
296
|
function sendJson(res, status, body) {
|
|
@@ -216,8 +302,6 @@ async function readBody(req) {
|
|
|
216
302
|
for await (const chunk of req) chunks.push(chunk);
|
|
217
303
|
return Buffer.concat(chunks);
|
|
218
304
|
}
|
|
219
|
-
var JsonBodyError = class extends Error {
|
|
220
|
-
};
|
|
221
305
|
async function readJsonBody(req) {
|
|
222
306
|
const buf = await readBody(req);
|
|
223
307
|
try {
|
|
@@ -226,7 +310,6 @@ async function readJsonBody(req) {
|
|
|
226
310
|
throw new JsonBodyError("invalid json");
|
|
227
311
|
}
|
|
228
312
|
}
|
|
229
|
-
var segsCache = /* @__PURE__ */ new Map();
|
|
230
313
|
function segsOf(template) {
|
|
231
314
|
let segs = segsCache.get(template);
|
|
232
315
|
if (!segs) {
|
|
@@ -261,7 +344,7 @@ async function dispatchRoute(routes, req, res, url) {
|
|
|
261
344
|
if (err instanceof JsonBodyError) {
|
|
262
345
|
if (!res.headersSent) sendJson(res, 400, { error: "invalid json" });
|
|
263
346
|
} else {
|
|
264
|
-
|
|
347
|
+
log("route handler error:", err);
|
|
265
348
|
if (!res.headersSent) sendJson(res, 500, { error: "internal error" });
|
|
266
349
|
}
|
|
267
350
|
}
|
|
@@ -269,25 +352,30 @@ async function dispatchRoute(routes, req, res, url) {
|
|
|
269
352
|
}
|
|
270
353
|
return false;
|
|
271
354
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
355
|
+
var JsonBodyError, segsCache;
|
|
356
|
+
var init_http_body = __esm({
|
|
357
|
+
"src/http-body.ts"() {
|
|
358
|
+
"use strict";
|
|
359
|
+
init_config();
|
|
360
|
+
JsonBodyError = class extends Error {
|
|
361
|
+
};
|
|
362
|
+
segsCache = /* @__PURE__ */ new Map();
|
|
363
|
+
}
|
|
364
|
+
});
|
|
282
365
|
|
|
283
366
|
// src/agent/guides.ts
|
|
284
|
-
var INTRO
|
|
367
|
+
var INTRO, DESIGN_TO_CODE, GUIDES;
|
|
368
|
+
var init_guides = __esm({
|
|
369
|
+
"src/agent/guides.ts"() {
|
|
370
|
+
"use strict";
|
|
371
|
+
init_config();
|
|
372
|
+
INTRO = `# batter-pixso-mcp \u4F7F\u7528\u6307\u5357
|
|
285
373
|
|
|
286
374
|
## \u5DE5\u5177\u603B\u89C8
|
|
287
375
|
|
|
288
376
|
| \u5DE5\u5177 | \u7528\u9014 |
|
|
289
377
|
| --- | --- |
|
|
290
|
-
| \`get_selected\` | \u8BFB\u753B\u5E03\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\
|
|
378
|
+
| \`get_selected\` | \u8BFB\u753B\u5E03\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08guid\u3001\u540D\u79F0\u3001\u7C7B\u578B\uFF09 |
|
|
291
379
|
| \`get_metadata\` | \u5168\u6811\u7ED3\u6784\u9AA8\u67B6\uFF08\u4EC5 id/name/type/kids\uFF0C\u65E0\u6837\u5F0F/\u5750\u6807/\u8D44\u6E90\uFF09\uFF0C\u9996\u6B21\u5B9A\u4F4D\u7528 |
|
|
292
380
|
| \`get_node_dsl\` | \u751F\u6210\u8282\u70B9\u7684\u5F00\u53D1\u5411\u7CBE\u7B80 DSL\uFF08\u7ED3\u6784 + \u6837\u5F0F + \u8D44\u6E90\u6587\u4EF6\u540D\uFF09\uFF0C\u8BBE\u8BA1\u8F6C\u4EE3\u7801\u7684\u6838\u5FC3\u5165\u53E3 |
|
|
293
381
|
| \`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 |
|
|
@@ -302,18 +390,18 @@ var INTRO = `# batter-pixso-mcp \u4F7F\u7528\u6307\u5357
|
|
|
302
390
|
|
|
303
391
|
## \u591A\u6587\u4EF6\u573A\u666F
|
|
304
392
|
|
|
305
|
-
\u6BCF\u4E2A\u8FD0\u884C\u63D2\u4EF6\u7684\u8BBE\u8BA1\u6587\u4EF6\u662F\u4E00\u4E2A\u4F1A\u8BDD\u3002\
|
|
306
|
-
\u5148\u7528 \`list_sessions\` \u786E\u8BA4\u76EE\u6807\u6587\u4EF6\uFF0C\
|
|
307
|
-
\u4F5C\u4E3A\
|
|
393
|
+
\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
|
|
394
|
+
\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\`
|
|
395
|
+
\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
|
|
308
396
|
|
|
309
|
-
**\u4F1A\u8BDD\u89E3\u6790\u4E0E\u4FE1\u5C01**\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u7684\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, data}\`\u2014\u2014\`sessionId\` \u662F\u672C\u6B21\u5B9E\u9645\u670D\u52A1\u7684\u4F1A\u8BDD
|
|
310
|
-
\uFF08\u6BCF\u6B21\u8C03\u7528\u7ECF ping/pong \u63A2\u6D3B\u91CD\u65B0\u786E\u8BA4\uFF0C\u4E0D\u662F\u53EF\u957F\u671F\u9489\u4F4F\u7684\u56FA\u5B9A\u6807\u8BC6\uFF09\
|
|
311
|
-
\u6BCF\u6B21\u8C03\u7528\u6309 fileKey \u91CD\u65B0\u5BFB\u5740\u2014\u2014\u540C fileKey \u591A\u4F1A\u8BDD\u65F6\u4F18\u5148\u4E0A\u4E00\u6B21\u4F7F\u7528\u7684\uFF08\u4EB2\u548C\uFF09\uFF0C\u6587\u4EF6\u5173\u95ED\u91CD\u5F00\u3001\u63D2\u4EF6\u91CD\u8DD1\u540E
|
|
397
|
+
**\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
|
|
398
|
+
\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
|
|
399
|
+
\u7528\u5B83\u505A\u8DEF\u7531\u53C2\u6570\uFF1A\u6BCF\u6B21\u8C03\u7528\u6309 fileKey \u91CD\u65B0\u5BFB\u5740\u2014\u2014\u540C fileKey \u591A\u4F1A\u8BDD\u65F6\u4F18\u5148\u4E0A\u4E00\u6B21\u4F7F\u7528\u7684\uFF08\u4EB2\u548C\uFF09\uFF0C\u6587\u4EF6\u5173\u95ED\u91CD\u5F00\u3001\u63D2\u4EF6\u91CD\u8DD1\u540E
|
|
312
400
|
\u81EA\u52A8\u89E3\u6790\u5230\u5F53\u524D\u5B58\u6D3B\u5B9E\u4F8B\uFF0Cagent \u65E0\u9700\u5173\u5FC3 sessionId \u7684\u5931\u6548\u3002
|
|
313
401
|
|
|
314
402
|
\u8BE6\u7EC6\u5DE5\u4F5C\u6D41\u89C1\u8D44\u6E90 \`batter://guides/design-to-code\`\u3002
|
|
315
403
|
`;
|
|
316
|
-
|
|
404
|
+
DESIGN_TO_CODE = `# \u8BBE\u8BA1\u8F6C\u4EE3\u7801\u5DE5\u4F5C\u6D41
|
|
317
405
|
|
|
318
406
|
## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011
|
|
319
407
|
|
|
@@ -336,12 +424,12 @@ var DESIGN_TO_CODE = `# \u8BBE\u8BA1\u8F6C\u4EE3\u7801\u5DE5\u4F5C\u6D41
|
|
|
336
424
|
|
|
337
425
|
1. **\u786E\u8BA4\u76EE\u6807\u8282\u70B9**
|
|
338
426
|
- \u7528\u6237\u7ED9\u4E86 Pixso \u94FE\u63A5\uFF1A\u6309\u4E0A\u65B9\u786C\u6027\u89C4\u5219\u7B2C 2 \u6761\u63D0\u53D6 guid\uFF0C\u76F4\u63A5\u7EE7\u7EED\u3002
|
|
339
|
-
- \u7528\u6237\u660E\u786E\u8BF4\u4E86"\u8FD9\u4E2A/\u6211\u9009\u4E2D\u7684"\uFF1A\u8C03 \`get_selected\` \u62FF guid\uFF0C\u76F4\u63A5\u7EE7\u7EED\u3002
|
|
427
|
+
- \u7528\u6237\u660E\u786E\u8BF4\u4E86"\u8FD9\u4E2A/\u6211\u9009\u4E2D\u7684"\uFF1A\u8C03 \`get_selected\` \u62FF guid \u4E0E fileKey\uFF0C\u76F4\u63A5\u7EE7\u7EED\u3002
|
|
340
428
|
- \u5176\u4F59\u4F9D\u8D56\u5F53\u524D\u9009\u533A\u7684\u573A\u666F\uFF1A**\u4E25\u7981\u9690\u5F0F\u4F7F\u7528\u9009\u533A guid**\u2014\u2014\u8C03 \`get_selected\` \u540E
|
|
341
429
|
\u5FC5\u987B\u5148\u628A\u9009\u4E2D\u56FE\u5C42\u540D\u79F0\u62A5\u7ED9\u7528\u6237\u786E\u8BA4\uFF08"\u5F53\u524D\u9009\u4E2D\u7684\u662F\u300CXX\u300D\uFF0C\u786E\u8BA4\u5904\u7406\u5B83\u5417\uFF1F"\uFF09\uFF0C\u786E\u8BA4\u540E\u518D\u7EE7\u7EED\u3002
|
|
342
|
-
|
|
343
|
-
2. **\u53D6\u7ED3\u6784**\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, data}\` \u4FE1\u5C01\u2014\u2014DSL \u5728 \`data\` \u5B57\u6BB5\u91CC\u3002
|
|
344
|
-
\`get_node_dsl(guid)\` \u62FF\u7CBE\u7B80 DSL\u3002
|
|
430
|
+
\u786E\u8BA4\u540E\u7684\u8C03\u7528\u91CC guid \u7528\u8FD4\u56DE\u7684\u9009\u4E2D\u56FE\u5C42 id\u3001\u8DEF\u7531\u53C2\u6570\u7528\u8FD4\u56DE\u7684 fileKey\uFF08guid \u5FC5\u4F20\u3001fileKey/sessionId \u5FC5\u4F20\u5176\u4E00\uFF09\u3002
|
|
431
|
+
2. **\u53D6\u7ED3\u6784**\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, fileKey, data}\` \u4FE1\u5C01\u2014\u2014DSL \u5728 \`data\` \u5B57\u6BB5\u91CC\u3002
|
|
432
|
+
\`get_node_dsl(guid, fileKey)\` \u62FF\u7CBE\u7B80 DSL\uFF08guid \u5FC5\u4F20\uFF0CfileKey/sessionId \u5FC5\u4F20\u5176\u4E00\uFF09\u3002
|
|
345
433
|
**\u9996\u6B21\u63A5\u89E6\u4E00\u4E2A\u8BBE\u8BA1\u7A3F/\u9875\u9762**\uFF08\u8FD8\u6CA1\u6709\u76EE\u6807\u8282\u70B9\u7684\u4E0A\u4E0B\u6587\u65F6\uFF09\uFF1A\u5148 \`get_metadata(guid)\`
|
|
346
434
|
\u62FF\u5168\u6811\u9AA8\u67B6\uFF08\u4EC5 id/name/type/kids\uFF0C\u65E0\u6837\u5F0F/\u5750\u6807/\u8D44\u6E90\uFF0C\u4F53\u79EF\u6781\u5C0F\uFF09+ \`get_screenshot(guid)\` \u62FF\u89C6\u89C9\u5168\u8C8C\uFF0C
|
|
347
435
|
\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
|
|
@@ -407,7 +495,7 @@ DSL \u662F\u5355\u884C\u7D27\u51D1 JSON\uFF1A\u77ED key\u3001\u4F4D\u7F6E\u6570\
|
|
|
407
495
|
- **\u7F3A guid**\uFF1A\u5411\u7528\u6237\u8981\u5B8C\u6574\u7684 Pixso \u94FE\u63A5\uFF08\u542B item-id \u67E5\u8BE2\u53C2\u6570\uFF09\uFF0C\u4E0D\u8981\u731C\u6D4B\u8282\u70B9 id\u3002
|
|
408
496
|
- **\u547D\u4EE4\u8D85\u65F6/\u7ED3\u679C\u8FC7\u5927**\uFF1A\u5BF9\u76EE\u6807\u8282\u70B9\u7684\u5B50\u8282\u70B9\u7528 \`get_node_dsl\` + \u8F83\u4F4E \`maxDepth\` \u5206\u6BB5\u53D6\uFF0C\u4E0D\u8981\u6574\u6811\u4E00\u6B21\u62C9\u3002
|
|
409
497
|
- **\u7ED3\u679C\u7591\u4F3C\u8FC7\u671F**\uFF08\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\uFF09\uFF1A\u7ED9\u5BFC\u51FA\u7C7B\u5DE5\u5177\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\u3002
|
|
410
|
-
- **\u63D2\u4EF6\u65E0\u54CD\u5E94**\uFF08ping \u63A2\u6D4B\u8D85\u65F6\uFF09\uFF1A\u63D2\u4EF6\u672A\u8FD0\u884C\u6216\u6B63\u5728\u6267\u884C\u957F\u547D\u4EE4\u2014\u2014\u7A0D\u540E\u91CD\u8BD5\uFF1B\u6587\u4EF6\u91CD\u5F00\u573A\u666F
|
|
498
|
+
- **\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
|
|
411
499
|
- **\u7981\u6B62\u4EC5\u51ED\u622A\u56FE\u786C\u7F16\u7801**\uFF1A\u622A\u56FE\u662F\u6821\u9A8C\u4E0E\u610F\u56FE\u53C2\u7167\uFF0C\u4E0D\u662F\u7ED3\u6784\u6765\u6E90\u2014\u2014\u7ED3\u6784\u4E00\u5F8B\u8D70 DSL\u3002
|
|
412
500
|
|
|
413
501
|
## \u7EC4\u4EF6\u5173\u8054\uFF08comp \u5B57\u6BB5\uFF09
|
|
@@ -435,148 +523,284 @@ INSTANCE / COMPONENT / COMPONENT_SET \u8282\u70B9\u5E26 \`comp\` \u5B57\u6BB5\uF
|
|
|
435
523
|
- DSL \u662F**\u6709\u635F\u7CBE\u7B80**\uFF1A\u7701\u7565\u4E86\u5E03\u5C14\u8FD0\u7B97\u8DEF\u5F84\u3001\u8499\u7248\u7B49\u7EAF\u8BBE\u8BA1\u7EC6\u8282\uFF0C\u89C6\u89C9\u590D\u6742\u8282\u70B9\u4EE5\u622A\u56FE\u4E3A\u51C6\u3002
|
|
436
524
|
- \u5BFC\u51FA\u7C7B\u5DE5\u5177\uFF08\`get_node_dsl\`/\`get_screenshot\`/\`export_image\`\uFF09\u5E26 15 \u5206\u949F\u7ED3\u679C\u7F13\u5B58\uFF08\u63D2\u4EF6\u4FA7 TTL\uFF09\uFF1B
|
|
437
525
|
\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\u4F46\u7ED3\u679C\u5BF9\u4E0D\u4E0A\u65F6\uFF0C\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\u3002
|
|
438
|
-
- \u591A\u6587\u4EF6\u573A\u666F\
|
|
526
|
+
- \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
|
|
439
527
|
`;
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
528
|
+
GUIDES = [
|
|
529
|
+
{
|
|
530
|
+
name: "intro",
|
|
531
|
+
uri: "batter://guides/intro",
|
|
532
|
+
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",
|
|
533
|
+
text: INTRO
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: "design-to-code",
|
|
537
|
+
uri: "batter://guides/design-to-code",
|
|
538
|
+
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",
|
|
539
|
+
text: DESIGN_TO_CODE
|
|
540
|
+
}
|
|
541
|
+
];
|
|
452
542
|
}
|
|
453
|
-
|
|
543
|
+
});
|
|
454
544
|
|
|
455
545
|
// src/agent/prompts.ts
|
|
456
|
-
var DESIGN_TO_CODE2
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
{
|
|
461
|
-
name: "
|
|
462
|
-
description: "
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
return [
|
|
469
|
-
{
|
|
470
|
-
role: "user",
|
|
471
|
-
content: {
|
|
472
|
-
type: "text",
|
|
473
|
-
text: [
|
|
474
|
-
`\u628A Pixso \u8BBE\u8BA1\u7A3F\u8FD8\u539F\u4E3A\u524D\u7AEF\u4EE3\u7801\u3002`,
|
|
475
|
-
``,
|
|
476
|
-
`## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011`,
|
|
477
|
-
``,
|
|
478
|
-
`**\u5199\u4EE3\u7801\u524D\u5FC5\u987B\u5148\u8BFB**\u8D44\u6E90 \`batter://guides/design-to-code\` \u5E76\u9075\u5B88\u5176\u5168\u90E8\u786C\u6027\u89C4\u5219\uFF08\u8D44\u6E90\u53D6\u7528/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D/\u7981\u6B62\u622A\u56FE\u786C\u7F16\u7801\uFF09\uFF1B\u603B\u89C8\u89C1 \`batter://guides/intro\`\u3002`,
|
|
479
|
-
``,
|
|
480
|
-
`## \u6267\u884C\u6D41\u7A0B`,
|
|
481
|
-
``,
|
|
482
|
-
`1. \u786E\u8BA4\u76EE\u6807\u8282\u70B9\uFF1A${targetLine}`,
|
|
483
|
-
`2. \u53D6\u6570\u636E\uFF1A\u63D2\u4EF6\u7C7B\u5DE5\u5177\u8FD4\u56DE\u7EDF\u4E00\u4E3A \`{sessionId, data}\` \u4FE1\u5C01\uFF0C\u53D6 \`data\` \u5B57\u6BB5\u3002\u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F\uFF1A\u5148 \`get_metadata(guid)\` \u62FF\u5168\u6811\u9AA8\u67B6\u5B9A\u4F4D\u76EE\u6807 + \`get_screenshot(guid)\` \u62FF\u89C6\u89C9\u5168\u8C8C\uFF1B\u968F\u540E \`get_node_dsl(guid)\` \u62FF\u5168\u91CF DSL\uFF08\u7ED3\u6784\u5F88\u5927\u65F6\u7528 \`maxDepth\` \u5206\u6BB5\u4E0B\u63A2\u2014\u2014\u622A\u65AD\u5904\u8282\u70B9\u5E26 \`more\` \u6807\u8BB0\u4E0E \`kidsSummary\` \u6D45\u9884\u89C8\uFF0C\u62FF\u5176 \`id\` \u56DE\u4F20 \`guid\` \u4E0B\u94BB\uFF09\u3002DSL \u4E2D\u5DF2\u5E26 \`img\` \u7684\u8282\u70B9\uFF08ICON/IMAGE \u53F6\u5B50\uFF09\u5DF2\u5B8C\u6210\u5BFC\u51FA\uFF0C\u76F4\u63A5\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E0D\u8981\u518D\u8C03 \`export_image\`\u3002`,
|
|
484
|
-
`3. \u751F\u6210\u4EE3\u7801\uFF1A\u6309\u6307\u5357\u4E2D\u7684 DSL \u5B57\u6BB5\u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002`,
|
|
485
|
-
`4. \u81EA\u68C0\uFF1A\u518D\u6B21\u8C03 \`get_screenshot(guid)\`\uFF0C\u4E0E\u4EE3\u7801\u6E32\u67D3\u7ED3\u679C\u5BF9\u6BD4\uFF0C\u6709\u504F\u5DEE\u5C31\u4FEE\u6B63\u540E\u518D\u6BD4\u4E00\u8F6E\uFF08\u5BFC\u51FA\u7C7B\u5DE5\u5177\u5E26 15 \u5206\u949F\u7F13\u5B58\uFF1B\u7ED3\u679C\u7591\u4F3C\u8FC7\u671F\u65F6\u52A0 \`cache: "refresh"\` \u5F3A\u5236\u5237\u65B0\uFF09\u3002`,
|
|
486
|
-
``,
|
|
487
|
-
`**\u6700\u540E\u91CD\u7533**\uFF1A\u4EA4\u4ED8\u4EE3\u7801\u4E2D\u4E0D\u5141\u8BB8\u51FA\u73B0\u4EA7\u7269\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5168\u90E8\u56FE\u7247\u5FC5\u987B\u662F\u5DF2\u590D\u5236\u5230\u9879\u76EE\u7684\u672C\u5730\u76F8\u5BF9\u8DEF\u5F84\uFF08\u7EC6\u5219\u89C1\u6307\u5357\u786C\u6027\u89C4\u5219\u7B2C 1 \u6761\uFF09\u3002`
|
|
488
|
-
].join("\n")
|
|
546
|
+
var DESIGN_TO_CODE2, PROMPTS;
|
|
547
|
+
var init_prompts = __esm({
|
|
548
|
+
"src/agent/prompts.ts"() {
|
|
549
|
+
"use strict";
|
|
550
|
+
DESIGN_TO_CODE2 = {
|
|
551
|
+
name: "design-to-code",
|
|
552
|
+
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",
|
|
553
|
+
arguments: [
|
|
554
|
+
{
|
|
555
|
+
name: "url",
|
|
556
|
+
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",
|
|
557
|
+
required: false
|
|
489
558
|
}
|
|
559
|
+
],
|
|
560
|
+
render: (args) => {
|
|
561
|
+
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`;
|
|
562
|
+
return [
|
|
563
|
+
{
|
|
564
|
+
role: "user",
|
|
565
|
+
content: {
|
|
566
|
+
type: "text",
|
|
567
|
+
text: [
|
|
568
|
+
`\u628A Pixso \u8BBE\u8BA1\u7A3F\u8FD8\u539F\u4E3A\u524D\u7AEF\u4EE3\u7801\u3002`,
|
|
569
|
+
``,
|
|
570
|
+
`## \u3010\u786C\u6027\u89C4\u5219 \xB7 \u8FDD\u53CD\u5373\u8FD4\u5DE5\u3011`,
|
|
571
|
+
``,
|
|
572
|
+
`**\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`,
|
|
573
|
+
``,
|
|
574
|
+
`## \u6267\u884C\u6D41\u7A0B`,
|
|
575
|
+
``,
|
|
576
|
+
`1. \u786E\u8BA4\u76EE\u6807\u8282\u70B9\uFF1A${targetLine}`,
|
|
577
|
+
`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`,
|
|
578
|
+
`3. \u751F\u6210\u4EE3\u7801\uFF1A\u6309\u6307\u5357\u4E2D\u7684 DSL \u5B57\u6BB5\u8BED\u4E49\u6620\u5C04\u5230\u76EE\u6807\u6846\u67B6\u3002`,
|
|
579
|
+
`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`,
|
|
580
|
+
``,
|
|
581
|
+
`**\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`
|
|
582
|
+
].join("\n")
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
];
|
|
490
586
|
}
|
|
491
|
-
|
|
587
|
+
};
|
|
588
|
+
PROMPTS = [DESIGN_TO_CODE2];
|
|
492
589
|
}
|
|
493
|
-
};
|
|
494
|
-
var PROMPTS = [DESIGN_TO_CODE2];
|
|
590
|
+
});
|
|
495
591
|
|
|
496
592
|
// src/plugin/command-bus.ts
|
|
497
|
-
var CommandBus
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
593
|
+
var CommandBus, commandBus;
|
|
594
|
+
var init_command_bus = __esm({
|
|
595
|
+
"src/plugin/command-bus.ts"() {
|
|
596
|
+
"use strict";
|
|
597
|
+
init_config();
|
|
598
|
+
CommandBus = class {
|
|
599
|
+
sseListeners = /* @__PURE__ */ new Map();
|
|
600
|
+
pendingById = /* @__PURE__ */ new Map();
|
|
601
|
+
seq = 0;
|
|
602
|
+
/** AI 侧:执行命令 */
|
|
603
|
+
execute(type, args, opts) {
|
|
604
|
+
const timeoutMs = opts.timeoutMs ?? COMMAND_TIMEOUT_MS;
|
|
605
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
606
|
+
const command = { id: `cmd-${++this.seq}`, type, args };
|
|
607
|
+
const entry = {
|
|
608
|
+
command,
|
|
609
|
+
sessionId: opts.sessionId,
|
|
610
|
+
resolve,
|
|
611
|
+
reject,
|
|
612
|
+
dispatched: false,
|
|
613
|
+
timer: setTimeout(() => {
|
|
614
|
+
this.drop(entry);
|
|
615
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u8D85\u65F6\uFF1A\u63D2\u4EF6 ${timeoutMs / 1e3}s \u5185\u672A\u8FD4\u56DE\u7ED3\u679C`));
|
|
616
|
+
}, timeoutMs)
|
|
617
|
+
};
|
|
618
|
+
opts.signal?.addEventListener("abort", () => {
|
|
619
|
+
if (!this.pendingById.has(command.id)) return;
|
|
620
|
+
this.drop(entry);
|
|
621
|
+
reject(new Error(`\u547D\u4EE4 ${command.id} \u5DF2\u88AB\u8C03\u7528\u65B9\u53D6\u6D88`));
|
|
622
|
+
});
|
|
623
|
+
this.pendingById.set(command.id, entry);
|
|
624
|
+
this.dispatch(opts.sessionId);
|
|
625
|
+
return promise;
|
|
626
|
+
}
|
|
627
|
+
/** 插件侧:订阅即时推送(SSE 通道),返回退订函数;补发订阅前已入队的命令 */
|
|
628
|
+
subscribe(sessionId, listener) {
|
|
629
|
+
let subs = this.sseListeners.get(sessionId);
|
|
630
|
+
if (!subs) {
|
|
631
|
+
subs = /* @__PURE__ */ new Set();
|
|
632
|
+
this.sseListeners.set(sessionId, subs);
|
|
633
|
+
}
|
|
634
|
+
subs.add(listener);
|
|
635
|
+
this.dispatch(sessionId);
|
|
636
|
+
return () => {
|
|
637
|
+
subs.delete(listener);
|
|
638
|
+
if (subs.size === 0) this.sseListeners.delete(sessionId);
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
/** 插件侧:回传结果 */
|
|
642
|
+
complete(id, ok, resultOrError) {
|
|
643
|
+
const entry = this.pendingById.get(id);
|
|
644
|
+
if (!entry) return false;
|
|
513
645
|
this.drop(entry);
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
if (subs.size === 0) this.sseListeners.delete(sessionId);
|
|
646
|
+
if (ok) {
|
|
647
|
+
entry.resolve(resultOrError);
|
|
648
|
+
} else {
|
|
649
|
+
entry.reject(resultOrError instanceof Error ? resultOrError : new Error(String(resultOrError)));
|
|
650
|
+
}
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
/** 排空该会话全部未派发命令(无订阅者则留待 subscribe 补发) */
|
|
654
|
+
dispatch(sessionId) {
|
|
655
|
+
const subs = this.sseListeners.get(sessionId);
|
|
656
|
+
if (!subs || subs.size === 0) return;
|
|
657
|
+
for (const entry of this.pendingById.values()) {
|
|
658
|
+
if (entry.sessionId !== sessionId || entry.dispatched) continue;
|
|
659
|
+
entry.dispatched = true;
|
|
660
|
+
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)}`);
|
|
661
|
+
for (const fn of subs) fn(entry.command);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/** 从 pending 表移除(完成/超时/取消共用) */
|
|
665
|
+
drop(entry) {
|
|
666
|
+
clearTimeout(entry.timer);
|
|
667
|
+
this.pendingById.delete(entry.command.id);
|
|
668
|
+
}
|
|
538
669
|
};
|
|
670
|
+
commandBus = new CommandBus();
|
|
539
671
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
} else {
|
|
548
|
-
entry.reject(resultOrError instanceof Error ? resultOrError : new Error(String(resultOrError)));
|
|
549
|
-
}
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
552
|
-
/** 排空该会话全部未派发命令(无订阅者则留待 subscribe 补发) */
|
|
553
|
-
dispatch(sessionId) {
|
|
554
|
-
const subs = this.sseListeners.get(sessionId);
|
|
555
|
-
if (!subs || subs.size === 0) return;
|
|
556
|
-
for (const entry of this.pendingById.values()) {
|
|
557
|
-
if (entry.sessionId !== sessionId || entry.dispatched) continue;
|
|
558
|
-
entry.dispatched = true;
|
|
559
|
-
log(`dispatch command ${entry.command.id}: ${entry.command.type} -> session ${sessionId.slice(0, 8)}`);
|
|
560
|
-
for (const fn of subs) fn(entry.command);
|
|
561
|
-
}
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// src/plugin/sessions.ts
|
|
675
|
+
import { randomUUID } from "node:crypto";
|
|
676
|
+
function unknownRouteError(route) {
|
|
677
|
+
if (route.fileKey) {
|
|
678
|
+
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`);
|
|
562
679
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
680
|
+
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`);
|
|
681
|
+
}
|
|
682
|
+
var SessionRegistry, sessions;
|
|
683
|
+
var init_sessions = __esm({
|
|
684
|
+
"src/plugin/sessions.ts"() {
|
|
685
|
+
"use strict";
|
|
686
|
+
init_config();
|
|
687
|
+
SessionRegistry = class {
|
|
688
|
+
sessions = /* @__PURE__ */ new Map();
|
|
689
|
+
lastActiveId = null;
|
|
690
|
+
/** 会话亲和:fileKey → 上一次路由使用的 sessionId(仍在线时优先,避免同文件多实例间漂移) */
|
|
691
|
+
lastUsedByFileKey = /* @__PURE__ */ new Map();
|
|
692
|
+
isOnline(s) {
|
|
693
|
+
return s.connected || Date.now() - s.lastSeen < SESSION_ONLINE_WINDOW_MS;
|
|
694
|
+
}
|
|
695
|
+
/** fileKey 寻址候选:该文件的在线会话(谁先 pong 由 ping 竞速决定,此处不排序) */
|
|
696
|
+
candidatesForFileKey(fileKey) {
|
|
697
|
+
return [...this.sessions.values()].filter((s) => s.meta.fileKey === fileKey && this.isOnline(s));
|
|
698
|
+
}
|
|
699
|
+
/** 亲和查询:上一次为该 fileKey 服务的 sessionId */
|
|
700
|
+
affinityIdOf(fileKey) {
|
|
701
|
+
return this.lastUsedByFileKey.get(fileKey);
|
|
702
|
+
}
|
|
703
|
+
/** 亲和钉住:fileKey 寻址探活成功后记录(下次优先) */
|
|
704
|
+
pinAffinity(fileKey, sessionId) {
|
|
705
|
+
this.lastUsedByFileKey.set(fileKey, sessionId);
|
|
706
|
+
}
|
|
707
|
+
/** 剔除会话(幽灵剔除/ping 判死共用):清理亲和引用;存活但繁忙被误剔的插件会在 SSE 断开或上报 404 后自重注册;
|
|
708
|
+
* 失效 sessionId 的 failover 不靠墓碑——工具结果信封带 fileKey,agent 手里的稳定句柄一直是 fileKey */
|
|
709
|
+
evict(id) {
|
|
710
|
+
if (this.sessions.delete(id)) {
|
|
711
|
+
for (const [k, v] of this.lastUsedByFileKey) {
|
|
712
|
+
if (v === id) this.lastUsedByFileKey.delete(k);
|
|
713
|
+
}
|
|
714
|
+
log(`session evicted: ${id.slice(0, 8)}`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
register(meta = {}) {
|
|
718
|
+
if (meta.fileKey) {
|
|
719
|
+
for (const s of [...this.sessions.values()]) {
|
|
720
|
+
if (s.meta.fileKey === meta.fileKey && !this.isOnline(s)) {
|
|
721
|
+
this.evict(s.id);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const session = { id: randomUUID(), meta, lastSeen: Date.now(), connected: false };
|
|
726
|
+
this.sessions.set(session.id, session);
|
|
727
|
+
this.lastActiveId = session.id;
|
|
728
|
+
log(`session registered: ${session.id} (${meta.fileName ?? "unknown file"})`);
|
|
729
|
+
return this.toInfo(session);
|
|
730
|
+
}
|
|
731
|
+
/** 心跳/连接状态用:只刷 lastSeen,不影响 lastActiveId("最近活跃"= 用户行为,心跳不算) */
|
|
732
|
+
touchSeen(id) {
|
|
733
|
+
const s = this.sessions.get(id);
|
|
734
|
+
if (!s) return null;
|
|
735
|
+
s.lastSeen = Date.now();
|
|
736
|
+
return s;
|
|
737
|
+
}
|
|
738
|
+
/** 用户行为(选区上报/命令服务/注册):刷 lastSeen 并标记最近活跃 */
|
|
739
|
+
touch(id) {
|
|
740
|
+
const s = this.touchSeen(id);
|
|
741
|
+
if (!s) return null;
|
|
742
|
+
this.lastActiveId = id;
|
|
743
|
+
return s;
|
|
744
|
+
}
|
|
745
|
+
setConnected(id, connected) {
|
|
746
|
+
const s = this.touchSeen(id);
|
|
747
|
+
if (!s) return false;
|
|
748
|
+
s.connected = connected;
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
updateContext(id, meta) {
|
|
752
|
+
const s = this.touch(id);
|
|
753
|
+
if (!s) return false;
|
|
754
|
+
s.meta = { ...s.meta, ...meta };
|
|
755
|
+
log(
|
|
756
|
+
`context updated [${id.slice(0, 8)}]: ${s.meta.fileName ?? "?"} / ${s.meta.pageName ?? "?"} / ${s.meta.selection?.length ?? 0} selected`
|
|
757
|
+
);
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
list() {
|
|
761
|
+
return [...this.sessions.values()].map((s) => this.toInfo(s));
|
|
762
|
+
}
|
|
763
|
+
/** 纯查找(不判活):工具层寻址用——离线会话也可取到 fileKey 用于候选扩展与故障转移 */
|
|
764
|
+
get(id) {
|
|
765
|
+
return this.sessions.get(id);
|
|
766
|
+
}
|
|
767
|
+
/** 缺省路由:最近活跃在线会话 > 任意在线会话(显式 fileKey/sessionId 由工具层 get/candidatesForFileKey + ping 完成) */
|
|
768
|
+
resolve() {
|
|
769
|
+
if (this.lastActiveId) {
|
|
770
|
+
const s = this.sessions.get(this.lastActiveId);
|
|
771
|
+
if (s && this.isOnline(s)) return s;
|
|
772
|
+
}
|
|
773
|
+
const fallback = [...this.sessions.values()].find((s) => this.isOnline(s));
|
|
774
|
+
if (fallback) return fallback;
|
|
775
|
+
throw new Error("Pixso \u63D2\u4EF6\u672A\u8FDE\u63A5\uFF1A\u8BF7\u5728 Pixso \u5BA2\u6237\u7AEF\u4E2D\u8FD0\u884C Batter Pixso Bridge \u63D2\u4EF6");
|
|
776
|
+
}
|
|
777
|
+
toInfo(s) {
|
|
778
|
+
return {
|
|
779
|
+
sessionId: s.id,
|
|
780
|
+
fileKey: s.meta.fileKey,
|
|
781
|
+
fileName: s.meta.fileName,
|
|
782
|
+
pageName: s.meta.pageName,
|
|
783
|
+
selectionCount: s.meta.selection?.length,
|
|
784
|
+
online: this.isOnline(s),
|
|
785
|
+
lastSeen: s.lastSeen
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
sessions = new SessionRegistry();
|
|
567
790
|
}
|
|
568
|
-
};
|
|
569
|
-
var commandBus = new CommandBus();
|
|
791
|
+
});
|
|
570
792
|
|
|
571
793
|
// src/agent/tools/shared.ts
|
|
572
|
-
var PING_TIMEOUT_MS = 800;
|
|
573
794
|
async function run(type, args, signal) {
|
|
574
|
-
const {
|
|
795
|
+
const { fileKey, sessionId, ...rest } = args;
|
|
575
796
|
try {
|
|
576
|
-
const session = await resolveAndPing(
|
|
797
|
+
const session = await resolveAndPing(
|
|
798
|
+
{ fileKey, sessionId },
|
|
799
|
+
signal
|
|
800
|
+
);
|
|
577
801
|
const data = await commandBus.execute(type, rest, { sessionId: session.id, signal });
|
|
578
802
|
sessions.touch(session.id);
|
|
579
|
-
return jsonResult({ sessionId: session.id, data });
|
|
803
|
+
return jsonResult({ sessionId: session.id, fileKey: session.meta.fileKey, data });
|
|
580
804
|
} catch (err) {
|
|
581
805
|
return errorResult(err);
|
|
582
806
|
}
|
|
@@ -605,39 +829,25 @@ async function pingCandidates(candidates, affinityId, signal) {
|
|
|
605
829
|
return null;
|
|
606
830
|
}
|
|
607
831
|
}
|
|
608
|
-
async function resolveAndPing(
|
|
609
|
-
const s =
|
|
610
|
-
|
|
832
|
+
async function resolveAndPing(route, signal) {
|
|
833
|
+
const s = route.sessionId ? sessions.get(route.sessionId) : route.fileKey ? void 0 : sessions.resolve();
|
|
834
|
+
if (route.sessionId && !s) throw unknownRouteError(route);
|
|
835
|
+
const fileKey = s ? s.meta.fileKey : route.fileKey;
|
|
611
836
|
const candidates = s && !fileKey ? [s] : sessions.candidatesForFileKey(fileKey);
|
|
612
837
|
if (candidates.length === 0) {
|
|
613
838
|
if (s) throw new Error(`\u76EE\u6807\u63D2\u4EF6\u5DF2\u79BB\u7EBF\uFF1A${s.meta.fileName ?? s.id}\uFF08\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u5E76\u8FD0\u884C\u63D2\u4EF6\u540E\u91CD\u8BD5\uFF09`);
|
|
614
|
-
throw
|
|
839
|
+
throw unknownRouteError(route);
|
|
615
840
|
}
|
|
616
841
|
const winner = await pingCandidates(candidates, fileKey ? sessions.affinityIdOf(fileKey) : void 0, signal);
|
|
617
842
|
if (!winner) {
|
|
618
843
|
for (const c of candidates) sessions.evict(c.id);
|
|
619
844
|
throw new Error(
|
|
620
|
-
`\u63D2\u4EF6\u65E0\u54CD\u5E94\uFF08${PING_TIMEOUT_MS}ms \u672A\u56DE pong\uFF09\uFF1A${fileKey ??
|
|
845
|
+
`\u63D2\u4EF6\u65E0\u54CD\u5E94\uFF08${PING_TIMEOUT_MS}ms \u672A\u56DE pong\uFF09\uFF1A${fileKey ?? route.sessionId}\u2014\u2014\u63D2\u4EF6\u672A\u8FD0\u884C\uFF0C\u6216\u6B63\u5728\u6267\u884C\u957F\u547D\u4EE4\uFF08\u7A0D\u540E\u91CD\u8BD5\uFF09`
|
|
621
846
|
);
|
|
622
847
|
}
|
|
623
848
|
if (fileKey) sessions.pinAffinity(fileKey, winner.id);
|
|
624
849
|
return winner;
|
|
625
850
|
}
|
|
626
|
-
var ENVELOPE_NOTE = "\u8FD4\u56DE\u5C01\u88C5\u4E3A {sessionId, data}\uFF08\u9519\u8BEF\u4E3A isError \u6587\u672C\uFF0C\u4E0D\u542B\u4FE1\u5C01\uFF09\uFF1AsessionId \u662F ping/pong \u786E\u8BA4\u7684\u5B9E\u9645\u670D\u52A1\u4F1A\u8BDD\uFF1Btarget \u7528 fileKey \u6700\u7A33\u2014\u2014\u6587\u4EF6\u91CD\u5F00\u540E\u81EA\u52A8\u89E3\u6790\u5230\u5B58\u6D3B\u5B9E\u4F8B\uFF08\u591A\u5B9E\u4F8B\u4F18\u5148\u4E0A\u4E00\u6B21\u4F7F\u7528\u7684\uFF09\u3002";
|
|
627
|
-
var GUIDES_PTR = "\u5B8C\u6574\u89C4\u5219\uFF08\u8D44\u6E90\u53D6\u7528/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D/\u7981\u6B62\u622A\u56FE\u786C\u7F16\u7801\uFF09\u89C1\u8D44\u6E90 batter://guides/design-to-code\u3002";
|
|
628
|
-
var TARGET_PROP = {
|
|
629
|
-
type: "string",
|
|
630
|
-
description: "\u76EE\u6807\u63D2\u4EF6\u4F1A\u8BDD\uFF08sessionId \u6216 fileKey\uFF09\u3002\u591A\u4E2A Pixso \u6587\u4EF6\u540C\u65F6\u5F00\u63D2\u4EF6\u65F6\u624D\u9700\u8981\uFF1B\u7F3A\u7701\u8DEF\u7531\u5230\u6700\u8FD1\u6D3B\u8DC3\u6587\u4EF6\u3002\u53EF\u7528 list_sessions \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u3002"
|
|
631
|
-
};
|
|
632
|
-
var CACHE_PROP = {
|
|
633
|
-
type: "string",
|
|
634
|
-
enum: ["first", "refresh"],
|
|
635
|
-
description: "\u7F13\u5B58\u7B56\u7565\uFF1Afirst=\u7F13\u5B58\u4F18\u5148\uFF08\u9ED8\u8BA4\uFF0C15 \u5206\u949F\u5185\u76F8\u540C\u8C03\u7528\u76F4\u63A5\u8FD4\u56DE\uFF09\uFF1Brefresh=\u5F3A\u5236\u91CD\u65B0\u6784\u5EFA/\u5BFC\u51FA\uFF08\u4ECD\u5199\u5165\u7F13\u5B58\uFF09\u3002\u8BBE\u8BA1\u7A3F\u521A\u6539\u8FC7\u4E14\u7ED3\u679C\u5BF9\u4E0D\u4E0A\u65F6\u7528 refresh\u3002"
|
|
636
|
-
};
|
|
637
|
-
var GUID_PROP = {
|
|
638
|
-
type: "string",
|
|
639
|
-
description: '\u8282\u70B9 GUID\uFF08\u5982 "297:133032"\uFF09\u3002\u94FE\u63A5\u8F93\u5165\uFF1A\u4ECE item-id \u63D0\u53D6\u5E76 URL \u89E3\u7801\u540E\u4F20\u5165\uFF1B\u7F3A\u7701\u56DE\u9000\u5F53\u524D\u9009\u4E2D\u56FE\u5C42\uFF08\u7981\u6B62\u9690\u5F0F\u4F7F\u7528\u2014\u2014\u9009\u4E2D\u786E\u8BA4\u89C4\u5219\u89C1 get_selected\uFF09\u3002'
|
|
640
|
-
};
|
|
641
851
|
function jsonResult(data) {
|
|
642
852
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
643
853
|
}
|
|
@@ -647,151 +857,277 @@ function errorResult(err) {
|
|
|
647
857
|
isError: true
|
|
648
858
|
};
|
|
649
859
|
}
|
|
860
|
+
var ENVELOPE_NOTE, GUIDES_PTR, FILE_KEY_PROP, SESSION_ID_PROP, CACHE_PROP, GUID_PROP;
|
|
861
|
+
var init_shared = __esm({
|
|
862
|
+
"src/agent/tools/shared.ts"() {
|
|
863
|
+
"use strict";
|
|
864
|
+
init_command_bus();
|
|
865
|
+
init_sessions();
|
|
866
|
+
init_config();
|
|
867
|
+
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";
|
|
868
|
+
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";
|
|
869
|
+
FILE_KEY_PROP = {
|
|
870
|
+
type: "string",
|
|
871
|
+
minLength: 1,
|
|
872
|
+
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"
|
|
873
|
+
};
|
|
874
|
+
SESSION_ID_PROP = {
|
|
875
|
+
type: "string",
|
|
876
|
+
minLength: 1,
|
|
877
|
+
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"
|
|
878
|
+
};
|
|
879
|
+
CACHE_PROP = {
|
|
880
|
+
type: "string",
|
|
881
|
+
enum: ["first", "refresh"],
|
|
882
|
+
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"
|
|
883
|
+
};
|
|
884
|
+
GUID_PROP = {
|
|
885
|
+
type: "string",
|
|
886
|
+
minLength: 1,
|
|
887
|
+
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'
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
});
|
|
650
891
|
|
|
651
892
|
// src/agent/tools/export_image.ts
|
|
652
|
-
var exportImage
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
guids: {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
893
|
+
var exportImage;
|
|
894
|
+
var init_export_image = __esm({
|
|
895
|
+
"src/agent/tools/export_image.ts"() {
|
|
896
|
+
"use strict";
|
|
897
|
+
init_shared();
|
|
898
|
+
exportImage = {
|
|
899
|
+
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,
|
|
900
|
+
// 两组互斥各取其一:guid|guids(单节点|批量)× fileKey|sessionId(路由)
|
|
901
|
+
requires: [
|
|
902
|
+
["guid", "guids"],
|
|
903
|
+
["fileKey", "sessionId"]
|
|
904
|
+
],
|
|
905
|
+
properties: {
|
|
906
|
+
guid: GUID_PROP,
|
|
907
|
+
guids: {
|
|
908
|
+
type: "array",
|
|
909
|
+
items: { type: "string" },
|
|
910
|
+
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"
|
|
911
|
+
},
|
|
912
|
+
format: {
|
|
913
|
+
type: "string",
|
|
914
|
+
enum: ["PNG", "SVG", "JPG"],
|
|
915
|
+
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"
|
|
916
|
+
},
|
|
917
|
+
scale: {
|
|
918
|
+
type: "number",
|
|
919
|
+
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"
|
|
920
|
+
},
|
|
921
|
+
cache: CACHE_PROP,
|
|
922
|
+
fileKey: FILE_KEY_PROP,
|
|
923
|
+
sessionId: SESSION_ID_PROP
|
|
924
|
+
}
|
|
925
|
+
};
|
|
675
926
|
}
|
|
676
|
-
};
|
|
927
|
+
});
|
|
677
928
|
|
|
678
929
|
// src/agent/tools/get_metadata.ts
|
|
679
|
-
var getMetadata
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
930
|
+
var getMetadata;
|
|
931
|
+
var init_get_metadata = __esm({
|
|
932
|
+
"src/agent/tools/get_metadata.ts"() {
|
|
933
|
+
"use strict";
|
|
934
|
+
init_shared();
|
|
935
|
+
getMetadata = {
|
|
936
|
+
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,
|
|
937
|
+
requires: ["guid", ["fileKey", "sessionId"]],
|
|
938
|
+
properties: {
|
|
939
|
+
guid: GUID_PROP,
|
|
940
|
+
cache: CACHE_PROP,
|
|
941
|
+
fileKey: FILE_KEY_PROP,
|
|
942
|
+
sessionId: SESSION_ID_PROP
|
|
943
|
+
}
|
|
944
|
+
};
|
|
688
945
|
}
|
|
689
|
-
};
|
|
946
|
+
});
|
|
690
947
|
|
|
691
948
|
// src/agent/tools/get_node_dsl.ts
|
|
692
|
-
var URL_RULE
|
|
693
|
-
var
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
949
|
+
var URL_RULE, DSL_LEGEND, getNodeDsl;
|
|
950
|
+
var init_get_node_dsl = __esm({
|
|
951
|
+
"src/agent/tools/get_node_dsl.ts"() {
|
|
952
|
+
"use strict";
|
|
953
|
+
init_shared();
|
|
954
|
+
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';
|
|
955
|
+
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';
|
|
956
|
+
getNodeDsl = {
|
|
957
|
+
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,
|
|
958
|
+
requires: ["guid", ["fileKey", "sessionId"]],
|
|
959
|
+
properties: {
|
|
960
|
+
guid: GUID_PROP,
|
|
961
|
+
maxDepth: {
|
|
962
|
+
type: "number",
|
|
963
|
+
description: "\u4E0B\u63A2\u5B50\u8282\u70B9\u7684\u6700\u5927\u6DF1\u5EA6\uFF08\u6839\u8282\u70B9\u4E3A\u7B2C 0 \u5C42\uFF09\uFF0C\u7F3A\u7701\u4E0D\u9650\u3002\u622A\u65AD\u8282\u70B9\u5E26 more\uFF08\u5269\u4F59\u5C42\u7EA7\u6570\uFF09+ kidsSummary\uFF08\u5B50\u8282\u70B9\u6D45\u9884\u89C8\uFF09\uFF0C\u62FF\u5176 id \u56DE\u4F20 guid \u7EE7\u7EED\u4E0B\u94BB\uFF1B\u4F20 0 \u53EA\u8FD4\u56DE\u6839\u8282\u70B9\uFF08\u914D\u5408 depth \u5B57\u6BB5\u9884\u5224\u89C4\u6A21\uFF09\u3002"
|
|
964
|
+
},
|
|
965
|
+
cache: CACHE_PROP,
|
|
966
|
+
fileKey: FILE_KEY_PROP,
|
|
967
|
+
sessionId: SESSION_ID_PROP
|
|
968
|
+
}
|
|
969
|
+
};
|
|
707
970
|
}
|
|
708
|
-
};
|
|
971
|
+
});
|
|
709
972
|
|
|
710
973
|
// src/agent/tools/get_screenshot.ts
|
|
711
|
-
var getScreenshot
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
974
|
+
var getScreenshot;
|
|
975
|
+
var init_get_screenshot = __esm({
|
|
976
|
+
"src/agent/tools/get_screenshot.ts"() {
|
|
977
|
+
"use strict";
|
|
978
|
+
init_shared();
|
|
979
|
+
getScreenshot = {
|
|
980
|
+
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,
|
|
981
|
+
requires: ["guid", ["fileKey", "sessionId"]],
|
|
982
|
+
properties: {
|
|
983
|
+
guid: GUID_PROP,
|
|
984
|
+
format: {
|
|
985
|
+
type: "string",
|
|
986
|
+
enum: ["PNG", "SVG"],
|
|
987
|
+
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"
|
|
988
|
+
},
|
|
989
|
+
scale: {
|
|
990
|
+
type: "number",
|
|
991
|
+
description: "\u5BFC\u51FA\u500D\u7387\uFF0C\u9ED8\u8BA4 2\u3002"
|
|
992
|
+
},
|
|
993
|
+
cache: CACHE_PROP,
|
|
994
|
+
fileKey: FILE_KEY_PROP,
|
|
995
|
+
sessionId: SESSION_ID_PROP
|
|
996
|
+
}
|
|
997
|
+
};
|
|
729
998
|
}
|
|
730
|
-
};
|
|
999
|
+
});
|
|
731
1000
|
|
|
732
1001
|
// src/agent/tools/get_selected.ts
|
|
733
|
-
var getSelected
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1002
|
+
var getSelected;
|
|
1003
|
+
var init_get_selected = __esm({
|
|
1004
|
+
"src/agent/tools/get_selected.ts"() {
|
|
1005
|
+
"use strict";
|
|
1006
|
+
init_shared();
|
|
1007
|
+
getSelected = {
|
|
1008
|
+
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,
|
|
1009
|
+
properties: { fileKey: FILE_KEY_PROP, sessionId: SESSION_ID_PROP }
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
737
1013
|
|
|
738
1014
|
// src/agent/tools/list_sessions.ts
|
|
739
|
-
var listSessions
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1015
|
+
var listSessions;
|
|
1016
|
+
var init_list_sessions = __esm({
|
|
1017
|
+
"src/agent/tools/list_sessions.ts"() {
|
|
1018
|
+
"use strict";
|
|
1019
|
+
init_sessions();
|
|
1020
|
+
init_shared();
|
|
1021
|
+
listSessions = {
|
|
1022
|
+
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",
|
|
1023
|
+
properties: {},
|
|
1024
|
+
handler: async () => {
|
|
1025
|
+
const list = sessions.list();
|
|
1026
|
+
if (!list.length) {
|
|
1027
|
+
return {
|
|
1028
|
+
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" }]
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
const checks = await Promise.all(
|
|
1032
|
+
list.map(async (info) => info.online && await pingSession(info.sessionId) ? info : null)
|
|
1033
|
+
);
|
|
1034
|
+
const alive = checks.filter((c) => c !== null);
|
|
1035
|
+
for (const info of list) {
|
|
1036
|
+
if (!alive.includes(info)) sessions.evict(info.sessionId);
|
|
1037
|
+
}
|
|
1038
|
+
if (!alive.length) {
|
|
1039
|
+
return {
|
|
1040
|
+
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" }]
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
return jsonResult(
|
|
1044
|
+
alive.map(({ sessionId, fileKey, fileName, pageName, selectionCount }) => ({
|
|
1045
|
+
sessionId,
|
|
1046
|
+
fileKey,
|
|
1047
|
+
fileName,
|
|
1048
|
+
pageName,
|
|
1049
|
+
selectionCount
|
|
1050
|
+
}))
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
// src/agent/tools/index.ts
|
|
1058
|
+
function buildInputSchema(requires, properties) {
|
|
1059
|
+
const singles = requires.filter((r) => typeof r === "string");
|
|
1060
|
+
const xorGroups = requires.filter((r) => Array.isArray(r));
|
|
1061
|
+
const groupSchemas = xorGroups.map((g) => ({ oneOf: g.map((p) => ({ required: [p] })) }));
|
|
1062
|
+
return {
|
|
1063
|
+
type: "object",
|
|
1064
|
+
...singles.length ? { required: singles } : {},
|
|
1065
|
+
...groupSchemas.length === 1 ? groupSchemas[0] : {},
|
|
1066
|
+
...groupSchemas.length > 1 ? { allOf: groupSchemas } : {},
|
|
1067
|
+
properties
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
function validateArgs(requires, args) {
|
|
1071
|
+
const present = (k) => args[k] !== void 0 && args[k] !== null && args[k] !== "";
|
|
1072
|
+
for (const req of requires) {
|
|
1073
|
+
if (typeof req === "string") {
|
|
1074
|
+
if (!present(req)) return `\u7F3A\u5C11\u5FC5\u4F20\u53C2\u6570\uFF1A${req}`;
|
|
1075
|
+
continue;
|
|
760
1076
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
1077
|
+
const given = req.filter(present);
|
|
1078
|
+
if (given.length === 0) return `\u7F3A\u5C11\u53C2\u6570\uFF1A${req.join(" / ")} \u9700\u4F20\u4E00\u4E2A`;
|
|
1079
|
+
if (given.length > 1) return `\u53C2\u6570\u4E92\u65A5\uFF1A${req.join(" / ")} \u53EA\u80FD\u4F20\u4E00\u4E2A`;
|
|
1080
|
+
}
|
|
1081
|
+
return null;
|
|
1082
|
+
}
|
|
1083
|
+
var TOOL_REGISTRY, TOOLS;
|
|
1084
|
+
var init_tools = __esm({
|
|
1085
|
+
"src/agent/tools/index.ts"() {
|
|
1086
|
+
"use strict";
|
|
1087
|
+
init_export_image();
|
|
1088
|
+
init_get_metadata();
|
|
1089
|
+
init_get_node_dsl();
|
|
1090
|
+
init_get_screenshot();
|
|
1091
|
+
init_get_selected();
|
|
1092
|
+
init_list_sessions();
|
|
1093
|
+
init_shared();
|
|
1094
|
+
TOOL_REGISTRY = {
|
|
1095
|
+
get_selected: getSelected,
|
|
1096
|
+
get_node_dsl: getNodeDsl,
|
|
1097
|
+
get_metadata: getMetadata,
|
|
1098
|
+
export_image: exportImage,
|
|
1099
|
+
get_screenshot: getScreenshot,
|
|
1100
|
+
list_sessions: listSessions
|
|
1101
|
+
};
|
|
1102
|
+
TOOLS = Object.fromEntries(
|
|
1103
|
+
Object.entries(TOOL_REGISTRY).map(([name, def]) => {
|
|
1104
|
+
const requires = def.requires ?? [];
|
|
1105
|
+
const inputSchema = buildInputSchema(requires, def.properties);
|
|
1106
|
+
const base = def.handler ?? ((args, ctx) => run(name, args, ctx?.signal));
|
|
1107
|
+
const handler = (args, ctx) => {
|
|
1108
|
+
const err = requires.length ? validateArgs(requires, args) : null;
|
|
1109
|
+
if (err) return Promise.resolve(errorResult(err));
|
|
1110
|
+
return base(args, ctx);
|
|
1111
|
+
};
|
|
1112
|
+
const resolvedDef = { description: def.description, inputSchema, handler };
|
|
1113
|
+
return [name, { def: resolvedDef, tool: { name, description: def.description, inputSchema } }];
|
|
1114
|
+
})
|
|
769
1115
|
);
|
|
770
1116
|
}
|
|
771
|
-
};
|
|
772
|
-
|
|
773
|
-
// src/agent/tools/index.ts
|
|
774
|
-
var TOOL_REGISTRY = {
|
|
775
|
-
get_selected: getSelected,
|
|
776
|
-
get_node_dsl: getNodeDsl,
|
|
777
|
-
get_metadata: getMetadata,
|
|
778
|
-
export_image: exportImage,
|
|
779
|
-
get_screenshot: getScreenshot,
|
|
780
|
-
list_sessions: listSessions
|
|
781
|
-
};
|
|
782
|
-
var TOOLS = Object.fromEntries(
|
|
783
|
-
Object.entries(TOOL_REGISTRY).map(([name, def]) => {
|
|
784
|
-
const handler = def.handler ?? ((args, ctx) => run(name, args, ctx?.signal));
|
|
785
|
-
const resolvedDef = { description: def.description, inputSchema: def.inputSchema, handler };
|
|
786
|
-
return [name, { def: resolvedDef, tool: { name, description: def.description, inputSchema: def.inputSchema } }];
|
|
787
|
-
})
|
|
788
|
-
);
|
|
1117
|
+
});
|
|
789
1118
|
|
|
790
1119
|
// src/agent/mcp.ts
|
|
791
|
-
|
|
1120
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1121
|
+
import {
|
|
1122
|
+
CallToolRequestSchema,
|
|
1123
|
+
GetPromptRequestSchema,
|
|
1124
|
+
ListPromptsRequestSchema,
|
|
1125
|
+
ListResourceTemplatesRequestSchema,
|
|
1126
|
+
ListToolsRequestSchema
|
|
1127
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
792
1128
|
function createMcpServer() {
|
|
793
1129
|
const server = new McpServer(
|
|
794
|
-
{ name:
|
|
1130
|
+
{ name: SERVICE_NAME, version: VERSION },
|
|
795
1131
|
{ capabilities: { tools: {}, resources: {}, prompts: {} }, instructions: INSTRUCTIONS }
|
|
796
1132
|
);
|
|
797
1133
|
for (const guide of GUIDES) {
|
|
@@ -840,21 +1176,55 @@ function createMcpServer() {
|
|
|
840
1176
|
});
|
|
841
1177
|
return server;
|
|
842
1178
|
}
|
|
1179
|
+
var INSTRUCTIONS;
|
|
1180
|
+
var init_mcp = __esm({
|
|
1181
|
+
"src/agent/mcp.ts"() {
|
|
1182
|
+
"use strict";
|
|
1183
|
+
init_config();
|
|
1184
|
+
init_guides();
|
|
1185
|
+
init_prompts();
|
|
1186
|
+
init_tools();
|
|
1187
|
+
INSTRUCTIONS = `Pixso \u8BBE\u8BA1\u7A3F\u8F6C\u4EE3\u7801\u670D\u52A1\u3002\u6309\u7528\u6237\u8F93\u5165\u8DEF\u7531\uFF1A1. \u7ED9\u51FA Pixso \u94FE\u63A5\uFF08\u4EFB\u610F\u57DF\u540D\uFF09\u2192 \u4ECE item-id \u63D0\u53D6 guid\uFF08URL \u89E3\u7801\uFF09\uFF0C\u76F4\u63A5\u8C03 get_node_dsl / get_screenshot / export_image\uFF1B2. \u8BF4"\u6211\u9009\u4E2D\u7684/\u5F53\u524D\u9009\u4E2D\u7684"\u2192 get_selected \u62FF guid\uFF1B\u5176\u4ED6\u4F9D\u8D56\u9009\u533A\u7684\u573A\u666F\u5148 get_selected \u518D\u5411\u7528\u6237\u62A5\u540D\u79F0\u786E\u8BA4\uFF1B3. "\u6837\u5F0F\u8FD8\u539F/\u8BBE\u8BA1\u8FD8\u539F/UI \u8FD8\u539F/\u8BBE\u8BA1\u8F6C\u4EE3\u7801"\u2192 get_node_dsl \u53D6\u7ED3\u6784 + get_screenshot \u53D6\u89C6\u89C9\u53C2\u7167\uFF1B4. "\u5207\u56FE/\u5BFC\u51FA\u56FE\u6807\u6216\u56FE\u7247\u7D20\u6750"\u2192 export_image\uFF08DSL \u91CC\u5DF2\u5E26 img \u7684\u8282\u70B9\u76F4\u63A5\u7528\u5176\u6587\u4EF6\u540D\uFF0C\u4E0D\u8981\u91CD\u590D\u5BFC\u51FA\uFF09\uFF1B5. \u9996\u6B21\u63A5\u89E6\u8BBE\u8BA1\u7A3F/\u53EA\u8981\u7ED3\u6784\u6982\u89C8 \u2192 get_metadata \u62FF\u5168\u6811\u9AA8\u67B6\uFF08\u65E0\u6837\u5F0F\u65E0\u8D44\u6E90\uFF0C\u4F53\u79EF\u6781\u5C0F\uFF09\u5B9A\u4F4D\u76EE\u6807\u8282\u70B9\uFF1B\u7ED3\u6784\u590D\u6742 \u2192 get_node_dsl \u7528 maxDepth \u5206\u6BB5\u4E0B\u63A2\uFF08\u622A\u65AD\u5904 more \u6807\u8BB0 + kidsSummary \u6D45\u9884\u89C8\uFF0C\u6309 id \u4E0B\u94BB\uFF09\uFF1B6. \u751F\u6210\u540E\u5BF9\u6BD4\u8FD8\u539F\u5EA6 \u2192 get_screenshot \u6821\u9A8C\u5FAA\u73AF\uFF1B7. \u591A\u4E2A Pixso \u6587\u4EF6\u540C\u65F6\u6253\u5F00 \u2192 list_sessions + 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/\u7EC4\u4EF6\u6620\u5C04/\u9519\u8BEF\u6062\u590D/\u7981\u6B62\u622A\u56FE\u786C\u7F16\u7801\uFF09\u89C1\u8D44\u6E90 batter://guides/design-to-code\u2014\u2014\u5199\u4EE3\u7801\u524D\u5FC5\u8BFB\u3002`;
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
843
1190
|
|
|
844
1191
|
// src/agent/sessions.ts
|
|
845
|
-
var transports = /* @__PURE__ */ new Map();
|
|
846
1192
|
function getMcpTransport(sessionId) {
|
|
847
|
-
|
|
1193
|
+
const entry = transports.get(sessionId);
|
|
1194
|
+
if (entry) entry.lastSeen = Date.now();
|
|
1195
|
+
return entry?.transport;
|
|
848
1196
|
}
|
|
849
1197
|
function registerMcpTransport(sessionId, transport) {
|
|
850
|
-
transports.set(sessionId, transport);
|
|
1198
|
+
transports.set(sessionId, { transport, lastSeen: Date.now() });
|
|
851
1199
|
log(`MCP HTTP session initialized: ${sessionId.slice(0, 8)} (total ${transports.size})`);
|
|
852
1200
|
transport.onclose = () => {
|
|
853
1201
|
if (transport.sessionId) transports.delete(transport.sessionId);
|
|
854
1202
|
};
|
|
855
1203
|
}
|
|
1204
|
+
var transports, sweeper;
|
|
1205
|
+
var init_sessions2 = __esm({
|
|
1206
|
+
"src/agent/sessions.ts"() {
|
|
1207
|
+
"use strict";
|
|
1208
|
+
init_config();
|
|
1209
|
+
transports = /* @__PURE__ */ new Map();
|
|
1210
|
+
sweeper = setInterval(() => {
|
|
1211
|
+
const now = Date.now();
|
|
1212
|
+
for (const [id, entry] of transports) {
|
|
1213
|
+
if (now - entry.lastSeen <= config.mcpSessionTtlMs) continue;
|
|
1214
|
+
transports.delete(id);
|
|
1215
|
+
void entry.transport.close().catch(() => {
|
|
1216
|
+
});
|
|
1217
|
+
log(`MCP HTTP session expired: ${id.slice(0, 8)} (total ${transports.size})`);
|
|
1218
|
+
}
|
|
1219
|
+
}, MCP_SESSION_SWEEP_MS);
|
|
1220
|
+
sweeper.unref();
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
856
1223
|
|
|
857
1224
|
// src/agent/http.ts
|
|
1225
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1226
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
1227
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
858
1228
|
function handleMcp(req, res, url) {
|
|
859
1229
|
return dispatchRoute(MCP_ROUTES, req, res, url);
|
|
860
1230
|
}
|
|
@@ -867,95 +1237,54 @@ async function sessionRequestHandler({ req, res }) {
|
|
|
867
1237
|
}
|
|
868
1238
|
await transport.handleRequest(req, res);
|
|
869
1239
|
}
|
|
870
|
-
var MCP_ROUTES
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
transport
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1240
|
+
var MCP_ROUTES;
|
|
1241
|
+
var init_http = __esm({
|
|
1242
|
+
"src/agent/http.ts"() {
|
|
1243
|
+
"use strict";
|
|
1244
|
+
init_http_body();
|
|
1245
|
+
init_mcp();
|
|
1246
|
+
init_sessions2();
|
|
1247
|
+
MCP_ROUTES = [
|
|
1248
|
+
{
|
|
1249
|
+
method: "POST",
|
|
1250
|
+
path: "/mcp",
|
|
1251
|
+
handler: async ({ req, res }) => {
|
|
1252
|
+
let body;
|
|
1253
|
+
try {
|
|
1254
|
+
body = await readJsonBody(req);
|
|
1255
|
+
} catch {
|
|
1256
|
+
sendJson(res, 400, { jsonrpc: "2.0", error: { code: -32700, message: "Parse error" }, id: null });
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
1260
|
+
let transport = sessionId ? getMcpTransport(sessionId) : void 0;
|
|
1261
|
+
if (!transport) {
|
|
1262
|
+
if (!sessionId && isInitializeRequest(body)) {
|
|
1263
|
+
const t = new StreamableHTTPServerTransport({
|
|
1264
|
+
sessionIdGenerator: () => randomUUID2(),
|
|
1265
|
+
onsessioninitialized: (id) => registerMcpTransport(id, t)
|
|
1266
|
+
});
|
|
1267
|
+
await createMcpServer().connect(t);
|
|
1268
|
+
transport = t;
|
|
1269
|
+
} else {
|
|
1270
|
+
sendJson(res, 400, {
|
|
1271
|
+
jsonrpc: "2.0",
|
|
1272
|
+
error: { code: -32e3, message: "Bad Request: missing or unknown MCP session" },
|
|
1273
|
+
id: null
|
|
1274
|
+
});
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
await transport.handleRequest(req, res, body);
|
|
899
1279
|
}
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
{ method: "GET", path: "/mcp", handler: sessionRequestHandler },
|
|
905
|
-
{ method: "DELETE", path: "/mcp", handler: sessionRequestHandler }
|
|
906
|
-
];
|
|
907
|
-
|
|
908
|
-
// src/install/dist-resolver.ts
|
|
909
|
-
import { existsSync } from "node:fs";
|
|
910
|
-
import { readFile } from "node:fs/promises";
|
|
911
|
-
import { join as join3 } from "node:path";
|
|
912
|
-
import { fileURLToPath } from "node:url";
|
|
913
|
-
function resolvePluginDir() {
|
|
914
|
-
const candidates = [
|
|
915
|
-
fileURLToPath(new URL("./plugin/", import.meta.url)),
|
|
916
|
-
// bundle: dist/index.js → dist/plugin/
|
|
917
|
-
fileURLToPath(new URL("../../plugin/dist/", import.meta.url)),
|
|
918
|
-
// bundle 开发: dist/index.js → packages/plugin/dist/
|
|
919
|
-
fileURLToPath(new URL("../../../plugin/dist/", import.meta.url))
|
|
920
|
-
// tsx 开发: src/install/ → packages/plugin/dist/
|
|
921
|
-
];
|
|
922
|
-
return candidates.find((p) => existsSync(join3(p, "manifest.json"))) ?? null;
|
|
923
|
-
}
|
|
924
|
-
var resolved = null;
|
|
925
|
-
function pluginDir() {
|
|
926
|
-
resolved ??= resolvePluginDir();
|
|
927
|
-
return resolved;
|
|
928
|
-
}
|
|
929
|
-
function hasPluginDist() {
|
|
930
|
-
return pluginDir() !== null;
|
|
931
|
-
}
|
|
932
|
-
async function pluginManifest() {
|
|
933
|
-
const dir = pluginDir();
|
|
934
|
-
if (!dir) return null;
|
|
935
|
-
const raw = JSON.parse(await readFile(join3(dir, "manifest.json"), "utf8"));
|
|
936
|
-
raw.networkAccess = {
|
|
937
|
-
allowedDomains: [`http://localhost:${config.httpPort}`, `http://127.0.0.1:${config.httpPort}`]
|
|
938
|
-
};
|
|
939
|
-
return Buffer.from(JSON.stringify(raw, null, 2));
|
|
940
|
-
}
|
|
941
|
-
async function readPluginFile(name) {
|
|
942
|
-
const dir = pluginDir();
|
|
943
|
-
if (!dir) return null;
|
|
944
|
-
try {
|
|
945
|
-
return await readFile(join3(dir, name));
|
|
946
|
-
} catch {
|
|
947
|
-
return null;
|
|
1280
|
+
},
|
|
1281
|
+
{ method: "GET", path: "/mcp", handler: sessionRequestHandler },
|
|
1282
|
+
{ method: "DELETE", path: "/mcp", handler: sessionRequestHandler }
|
|
1283
|
+
];
|
|
948
1284
|
}
|
|
949
|
-
}
|
|
1285
|
+
});
|
|
950
1286
|
|
|
951
1287
|
// src/install/template.ts
|
|
952
|
-
var HTML_ESCAPES = {
|
|
953
|
-
"&": "&",
|
|
954
|
-
"<": "<",
|
|
955
|
-
">": ">",
|
|
956
|
-
'"': """,
|
|
957
|
-
"'": "'"
|
|
958
|
-
};
|
|
959
1288
|
function escapeHtml(s) {
|
|
960
1289
|
return s.replace(/[&<>"']/g, (c) => HTML_ESCAPES[c]);
|
|
961
1290
|
}
|
|
@@ -970,12 +1299,33 @@ function renderTemplate(tpl, vars, escape = escapeHtml) {
|
|
|
970
1299
|
};
|
|
971
1300
|
return tpl.replace(/\{\{\{\s*(\w+)\s*\}\}\}/g, (_, key) => lookup(key)).replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => escape(lookup(key)));
|
|
972
1301
|
}
|
|
1302
|
+
var HTML_ESCAPES;
|
|
1303
|
+
var init_template = __esm({
|
|
1304
|
+
"src/install/template.ts"() {
|
|
1305
|
+
"use strict";
|
|
1306
|
+
HTML_ESCAPES = {
|
|
1307
|
+
"&": "&",
|
|
1308
|
+
"<": "<",
|
|
1309
|
+
">": ">",
|
|
1310
|
+
'"': """,
|
|
1311
|
+
"'": "'"
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
973
1315
|
|
|
974
1316
|
// src/install/templates/bookmarklet.txt
|
|
975
|
-
var bookmarklet_default
|
|
1317
|
+
var bookmarklet_default;
|
|
1318
|
+
var init_bookmarklet = __esm({
|
|
1319
|
+
"src/install/templates/bookmarklet.txt"() {
|
|
1320
|
+
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';
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
976
1323
|
|
|
977
1324
|
// src/install/templates/install-page.html
|
|
978
|
-
var install_page_default
|
|
1325
|
+
var install_page_default;
|
|
1326
|
+
var init_install_page = __esm({
|
|
1327
|
+
"src/install/templates/install-page.html"() {
|
|
1328
|
+
install_page_default = `<!DOCTYPE html>
|
|
979
1329
|
<html lang="zh-CN">
|
|
980
1330
|
<head>
|
|
981
1331
|
<meta charset="utf-8">
|
|
@@ -1308,15 +1658,17 @@ var install_page_default = `<!DOCTYPE html>
|
|
|
1308
1658
|
<pre id="cfg">{
|
|
1309
1659
|
"mcpServers": {
|
|
1310
1660
|
"batter-pixso": {
|
|
1311
|
-
"
|
|
1312
|
-
"args": ["-y", "batter-pixso-mcp"],
|
|
1313
|
-
"env": { "HTTP_PORT": "{{ httpPort }}" }
|
|
1661
|
+
"url": "{{ mcpUrl }}"
|
|
1314
1662
|
}
|
|
1315
1663
|
}
|
|
1316
1664
|
}</pre>
|
|
1317
1665
|
<button class="copy" id="copy" type="button">\u590D\u5236</button>
|
|
1318
1666
|
</div>
|
|
1319
|
-
<p class="note"
|
|
1667
|
+
<p class="note">
|
|
1668
|
+
\u7C98\u8D34\u5230 Cursor / Cherry Studio \u7B49\u7684 MCP \u914D\u7F6E\u6587\u4EF6\uFF1BClaude Code \u6267\u884C
|
|
1669
|
+
<code>{{ claudeAddCommand }}</code>\u3002
|
|
1670
|
+
\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
|
|
1671
|
+
</p>
|
|
1320
1672
|
</div>
|
|
1321
1673
|
</div>
|
|
1322
1674
|
|
|
@@ -1368,6 +1720,7 @@ copyBtn.addEventListener("click", function () {
|
|
|
1368
1720
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1369
1721
|
navigator.clipboard.writeText(text).then(done, done);
|
|
1370
1722
|
} else {
|
|
1723
|
+
// \u515C\u5E95\uFF1A\u65E0 clipboard API \u7684\u73AF\u5883\uFF08\u53CC\u6838\u6D4F\u89C8\u5668\u517C\u5BB9\u6A21\u5F0F\u7B49\uFF09
|
|
1371
1724
|
var ta = document.createElement("textarea");
|
|
1372
1725
|
ta.value = text;
|
|
1373
1726
|
document.body.appendChild(ta);
|
|
@@ -1381,6 +1734,8 @@ copyBtn.addEventListener("click", function () {
|
|
|
1381
1734
|
</body>
|
|
1382
1735
|
</html>
|
|
1383
1736
|
`;
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1384
1739
|
|
|
1385
1740
|
// src/install/page.ts
|
|
1386
1741
|
function bookmarkletHref(base) {
|
|
@@ -1388,26 +1743,27 @@ function bookmarkletHref(base) {
|
|
|
1388
1743
|
return `javascript:${encodeURIComponent(source)}`;
|
|
1389
1744
|
}
|
|
1390
1745
|
function renderInstallPage() {
|
|
1391
|
-
const base = `${
|
|
1746
|
+
const base = `${serviceBaseUrl()}/`;
|
|
1392
1747
|
return renderTemplate(install_page_default, {
|
|
1393
1748
|
version: VERSION,
|
|
1394
1749
|
href: bookmarkletHref(base),
|
|
1395
1750
|
base,
|
|
1396
|
-
|
|
1751
|
+
mcpUrl: mcpUrl(),
|
|
1752
|
+
claudeAddCommand: claudeAddCommand()
|
|
1397
1753
|
});
|
|
1398
1754
|
}
|
|
1755
|
+
var init_page = __esm({
|
|
1756
|
+
"src/install/page.ts"() {
|
|
1757
|
+
"use strict";
|
|
1758
|
+
init_config();
|
|
1759
|
+
init_template();
|
|
1760
|
+
init_bookmarklet();
|
|
1761
|
+
init_install_page();
|
|
1762
|
+
}
|
|
1763
|
+
});
|
|
1399
1764
|
|
|
1400
1765
|
// src/install/zip.ts
|
|
1401
1766
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
1402
|
-
var CRC_TABLE = (() => {
|
|
1403
|
-
const table = new Uint32Array(256);
|
|
1404
|
-
for (let n = 0; n < 256; n++) {
|
|
1405
|
-
let c = n;
|
|
1406
|
-
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1407
|
-
table[n] = c >>> 0;
|
|
1408
|
-
}
|
|
1409
|
-
return table;
|
|
1410
|
-
})();
|
|
1411
1767
|
function crc32(buf) {
|
|
1412
1768
|
let crc = 4294967295;
|
|
1413
1769
|
for (const byte of buf) crc = CRC_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
@@ -1463,9 +1819,23 @@ function buildZip(entries) {
|
|
|
1463
1819
|
eocd.writeUInt32LE(offset, 16);
|
|
1464
1820
|
return Buffer2.concat([...chunks, centralBuf, eocd]);
|
|
1465
1821
|
}
|
|
1822
|
+
var CRC_TABLE;
|
|
1823
|
+
var init_zip = __esm({
|
|
1824
|
+
"src/install/zip.ts"() {
|
|
1825
|
+
"use strict";
|
|
1826
|
+
CRC_TABLE = (() => {
|
|
1827
|
+
const table = new Uint32Array(256);
|
|
1828
|
+
for (let n = 0; n < 256; n++) {
|
|
1829
|
+
let c = n;
|
|
1830
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1831
|
+
table[n] = c >>> 0;
|
|
1832
|
+
}
|
|
1833
|
+
return table;
|
|
1834
|
+
})();
|
|
1835
|
+
}
|
|
1836
|
+
});
|
|
1466
1837
|
|
|
1467
1838
|
// src/install/api.ts
|
|
1468
|
-
var NO_DIST = "plugin dist not found\uFF08\u8FD0\u884C npm run build \u751F\u6210\uFF09";
|
|
1469
1839
|
function handleInstall(req, res, url) {
|
|
1470
1840
|
return dispatchRoute(INSTALL_ROUTES, req, res, url);
|
|
1471
1841
|
}
|
|
@@ -1473,168 +1843,271 @@ function sendBinary(res, buf, contentType) {
|
|
|
1473
1843
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "no-store" });
|
|
1474
1844
|
res.end(buf);
|
|
1475
1845
|
}
|
|
1476
|
-
var PLUGIN_FILE_ROUTES
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
}
|
|
1499
|
-
},
|
|
1500
|
-
{
|
|
1501
|
-
method: "GET",
|
|
1502
|
-
path: "/manifest.json",
|
|
1503
|
-
handler: async ({ res }) => {
|
|
1504
|
-
const buf = await pluginManifest();
|
|
1505
|
-
if (!buf) {
|
|
1506
|
-
sendJson(res, 404, { error: NO_DIST });
|
|
1507
|
-
return;
|
|
1846
|
+
var NO_DIST, PLUGIN_FILE_ROUTES, INSTALL_ROUTES;
|
|
1847
|
+
var init_api = __esm({
|
|
1848
|
+
"src/install/api.ts"() {
|
|
1849
|
+
"use strict";
|
|
1850
|
+
init_http_body();
|
|
1851
|
+
init_dist_resolver();
|
|
1852
|
+
init_page();
|
|
1853
|
+
init_zip();
|
|
1854
|
+
NO_DIST = "plugin dist not found\uFF08\u8FD0\u884C npm run build \u751F\u6210\uFF09";
|
|
1855
|
+
PLUGIN_FILE_ROUTES = [
|
|
1856
|
+
{ path: "/code.js", file: "code.js", type: "text/javascript; charset=utf-8" },
|
|
1857
|
+
{ path: "/ui.html", file: "ui.html", type: "text/html; charset=utf-8" }
|
|
1858
|
+
].map(({ path, file, type }) => ({
|
|
1859
|
+
method: "GET",
|
|
1860
|
+
path,
|
|
1861
|
+
handler: async ({ res }) => {
|
|
1862
|
+
const buf = await readPluginFile(file);
|
|
1863
|
+
if (!buf) {
|
|
1864
|
+
sendJson(res, 404, { error: hasPluginDist() ? "not found" : NO_DIST });
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
sendBinary(res, buf, type);
|
|
1508
1868
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1869
|
+
}));
|
|
1870
|
+
INSTALL_ROUTES = [
|
|
1871
|
+
{
|
|
1872
|
+
method: "GET",
|
|
1873
|
+
path: "/",
|
|
1874
|
+
handler: ({ res }) => {
|
|
1875
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1876
|
+
res.end(renderInstallPage());
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
method: "GET",
|
|
1881
|
+
path: "/manifest.json",
|
|
1882
|
+
handler: async ({ res }) => {
|
|
1883
|
+
const buf = await pluginManifest();
|
|
1884
|
+
if (!buf) {
|
|
1885
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
sendBinary(res, buf, "application/json; charset=utf-8");
|
|
1889
|
+
}
|
|
1890
|
+
},
|
|
1891
|
+
...PLUGIN_FILE_ROUTES,
|
|
1892
|
+
{
|
|
1893
|
+
method: "GET",
|
|
1894
|
+
path: "/plugin.zip",
|
|
1895
|
+
handler: async ({ res }) => {
|
|
1896
|
+
if (!hasPluginDist()) {
|
|
1897
|
+
sendJson(res, 404, { error: NO_DIST });
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
try {
|
|
1901
|
+
const [manifest, code, ui] = await Promise.all([
|
|
1902
|
+
pluginManifest(),
|
|
1903
|
+
readPluginFile("code.js"),
|
|
1904
|
+
readPluginFile("ui.html")
|
|
1905
|
+
]);
|
|
1906
|
+
const zip = buildZip([
|
|
1907
|
+
{ name: "manifest.json", data: manifest },
|
|
1908
|
+
{ name: "code.js", data: code },
|
|
1909
|
+
{ name: "ui.html", data: ui }
|
|
1910
|
+
]);
|
|
1911
|
+
res.writeHead(200, {
|
|
1912
|
+
"Content-Type": "application/zip",
|
|
1913
|
+
"Content-Disposition": 'attachment; filename="batter-pixso-bridge.zip"'
|
|
1914
|
+
});
|
|
1915
|
+
res.end(zip);
|
|
1916
|
+
} catch {
|
|
1917
|
+
sendJson(res, 500, { error: "zip build failed" });
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
];
|
|
1922
|
+
}
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1925
|
+
// src/plugin/assets.ts
|
|
1926
|
+
import { createHash } from "node:crypto";
|
|
1927
|
+
import { mkdir, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
1928
|
+
import { join as join4 } from "node:path";
|
|
1929
|
+
async function saveAsset(bytes, format) {
|
|
1930
|
+
dirReady ??= mkdir(ASSETS_DIR, { recursive: true });
|
|
1931
|
+
await dirReady;
|
|
1932
|
+
const ext = EXT_BY_FORMAT[format.toUpperCase()] ?? ".bin";
|
|
1933
|
+
const name = `${createHash("sha1").update(bytes).digest("hex").slice(0, 16)}${ext}`;
|
|
1934
|
+
await writeFile(join4(ASSETS_DIR, name), bytes);
|
|
1935
|
+
log(`asset saved: ${name} (${bytes.length} bytes)`);
|
|
1936
|
+
return name;
|
|
1937
|
+
}
|
|
1938
|
+
function substituteAssets(result, assets) {
|
|
1939
|
+
if (!assets.length) return result;
|
|
1940
|
+
const nameByKey = new Map(assets.map((a) => [a.key, a.name]));
|
|
1941
|
+
return JSON.parse(
|
|
1942
|
+
JSON.stringify(
|
|
1943
|
+
result,
|
|
1944
|
+
(_k, v) => typeof v === "string" ? nameByKey.get(v) ?? v : v
|
|
1945
|
+
)
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
async function cleanOldAssets(ttlMs) {
|
|
1949
|
+
let removed = 0;
|
|
1950
|
+
const cutoff = Date.now() - ttlMs;
|
|
1951
|
+
let names;
|
|
1952
|
+
try {
|
|
1953
|
+
names = await readdir(ASSETS_DIR);
|
|
1954
|
+
} catch {
|
|
1955
|
+
return 0;
|
|
1956
|
+
}
|
|
1957
|
+
const expired = await Promise.all(
|
|
1958
|
+
names.map(async (name) => {
|
|
1959
|
+
try {
|
|
1960
|
+
const p = join4(ASSETS_DIR, name);
|
|
1961
|
+
const s = await stat(p);
|
|
1962
|
+
return s.isFile() && s.mtimeMs < cutoff ? p : null;
|
|
1963
|
+
} catch {
|
|
1964
|
+
return null;
|
|
1520
1965
|
}
|
|
1966
|
+
})
|
|
1967
|
+
);
|
|
1968
|
+
await Promise.all(
|
|
1969
|
+
expired.filter((p) => p !== null).map(async (p) => {
|
|
1521
1970
|
try {
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
readPluginFile("code.js"),
|
|
1525
|
-
readPluginFile("ui.html")
|
|
1526
|
-
]);
|
|
1527
|
-
const zip = buildZip([
|
|
1528
|
-
{ name: "manifest.json", data: manifest },
|
|
1529
|
-
{ name: "code.js", data: code },
|
|
1530
|
-
{ name: "ui.html", data: ui }
|
|
1531
|
-
]);
|
|
1532
|
-
res.writeHead(200, {
|
|
1533
|
-
"Content-Type": "application/zip",
|
|
1534
|
-
"Content-Disposition": 'attachment; filename="batter-pixso-bridge.zip"'
|
|
1535
|
-
});
|
|
1536
|
-
res.end(zip);
|
|
1971
|
+
await unlink(p);
|
|
1972
|
+
removed++;
|
|
1537
1973
|
} catch {
|
|
1538
|
-
sendJson(res, 500, { error: "zip build failed" });
|
|
1539
1974
|
}
|
|
1540
|
-
}
|
|
1975
|
+
})
|
|
1976
|
+
);
|
|
1977
|
+
if (removed > 0) log(`assets cleanup: removed ${removed} expired file(s)`);
|
|
1978
|
+
return removed;
|
|
1979
|
+
}
|
|
1980
|
+
function scheduleAssetsSweep(ttlMs) {
|
|
1981
|
+
void cleanOldAssets(ttlMs);
|
|
1982
|
+
const sweeper2 = setInterval(() => void cleanOldAssets(ttlMs), ASSET_SWEEP_INTERVAL_MS);
|
|
1983
|
+
sweeper2.unref();
|
|
1984
|
+
}
|
|
1985
|
+
var EXT_BY_FORMAT, dirReady;
|
|
1986
|
+
var init_assets = __esm({
|
|
1987
|
+
"src/plugin/assets.ts"() {
|
|
1988
|
+
"use strict";
|
|
1989
|
+
init_config();
|
|
1990
|
+
EXT_BY_FORMAT = {
|
|
1991
|
+
PNG: ".png",
|
|
1992
|
+
SVG: ".svg",
|
|
1993
|
+
JPG: ".jpg",
|
|
1994
|
+
JPEG: ".jpg",
|
|
1995
|
+
WEBP: ".webp",
|
|
1996
|
+
PDF: ".pdf"
|
|
1997
|
+
// export_image 尊重设计师导出标记,标记可能指定 PDF
|
|
1998
|
+
};
|
|
1999
|
+
dirReady = null;
|
|
1541
2000
|
}
|
|
1542
|
-
|
|
2001
|
+
});
|
|
1543
2002
|
|
|
1544
2003
|
// src/plugin/api.ts
|
|
1545
2004
|
function handlePluginApi(req, res, url) {
|
|
1546
2005
|
return dispatchRoute(PLUGIN_ROUTES, req, res, url);
|
|
1547
2006
|
}
|
|
1548
|
-
var PLUGIN_ROUTES
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
2007
|
+
var PLUGIN_ROUTES;
|
|
2008
|
+
var init_api2 = __esm({
|
|
2009
|
+
"src/plugin/api.ts"() {
|
|
2010
|
+
"use strict";
|
|
2011
|
+
init_config();
|
|
2012
|
+
init_http_body();
|
|
2013
|
+
init_assets();
|
|
2014
|
+
init_command_bus();
|
|
2015
|
+
init_sessions();
|
|
2016
|
+
PLUGIN_ROUTES = [
|
|
2017
|
+
{
|
|
2018
|
+
method: "POST",
|
|
2019
|
+
path: "/sessions/register",
|
|
2020
|
+
handler: async ({ req, res }) => {
|
|
2021
|
+
sendJson(res, 201, sessions.register(await readJsonBody(req)));
|
|
2022
|
+
}
|
|
2023
|
+
},
|
|
2024
|
+
{
|
|
2025
|
+
method: "POST",
|
|
2026
|
+
path: "/context",
|
|
2027
|
+
handler: async ({ req, res, url }) => {
|
|
2028
|
+
const session = url.searchParams.get("session");
|
|
2029
|
+
if (!session) {
|
|
2030
|
+
sendJson(res, 400, { error: "missing session" });
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
const meta = await readJsonBody(req);
|
|
2034
|
+
if (!sessions.updateContext(session, meta)) {
|
|
2035
|
+
sendJson(res, 404, { error: "unknown_session" });
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
sendJson(res, 200, { ok: true });
|
|
2039
|
+
}
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
method: "GET",
|
|
2043
|
+
path: "/commands/stream",
|
|
2044
|
+
handler: ({ req, res, url }) => {
|
|
2045
|
+
const session = url.searchParams.get("session");
|
|
2046
|
+
if (!session || !sessions.setConnected(session, true)) {
|
|
2047
|
+
sendJson(res, 404, { error: "unknown_session" });
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
res.writeHead(200, {
|
|
2051
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
2052
|
+
"Cache-Control": "no-store",
|
|
2053
|
+
Connection: "keep-alive"
|
|
2054
|
+
});
|
|
2055
|
+
res.write(": connected\n\n");
|
|
2056
|
+
const unsubscribe = commandBus.subscribe(session, (command) => {
|
|
2057
|
+
res.write(`data: ${JSON.stringify({ command })}
|
|
1590
2058
|
|
|
1591
2059
|
`);
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
}
|
|
1623
|
-
|
|
2060
|
+
});
|
|
2061
|
+
const teardown = () => {
|
|
2062
|
+
clearInterval(heartbeat);
|
|
2063
|
+
unsubscribe();
|
|
2064
|
+
sessions.setConnected(session, false);
|
|
2065
|
+
};
|
|
2066
|
+
const heartbeat = setInterval(() => {
|
|
2067
|
+
if (!res.write(": ping\n\n")) {
|
|
2068
|
+
teardown();
|
|
2069
|
+
res.end();
|
|
2070
|
+
log(`SSE ghost reaped: session ${session.slice(0, 8)}`);
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
sessions.touchSeen(session);
|
|
2074
|
+
}, SSE_HEARTBEAT_MS);
|
|
2075
|
+
req.on("close", () => {
|
|
2076
|
+
teardown();
|
|
2077
|
+
log(`SSE channel closed: session ${session.slice(0, 8)}`);
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
},
|
|
2081
|
+
{
|
|
2082
|
+
method: "POST",
|
|
2083
|
+
path: "/commands/:id/result",
|
|
2084
|
+
handler: async ({ req, res, params }) => {
|
|
2085
|
+
const payload = await readJsonBody(req);
|
|
2086
|
+
const result = payload.ok && payload.assets?.length ? substituteAssets(payload.result, payload.assets) : payload.result;
|
|
2087
|
+
const found = commandBus.complete(params["id"], payload.ok, payload.ok ? result : payload.error);
|
|
2088
|
+
sendJson(res, found ? 200 : 404, found ? { ok: true } : { error: "unknown command" });
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
method: "POST",
|
|
2093
|
+
path: "/assets",
|
|
2094
|
+
handler: async ({ req, res, url }) => {
|
|
2095
|
+
try {
|
|
2096
|
+
const name = await saveAsset(await readBody(req), url.searchParams.get("format") ?? "PNG");
|
|
2097
|
+
sendJson(res, 201, { name });
|
|
2098
|
+
} catch {
|
|
2099
|
+
sendJson(res, 500, { error: "asset save failed" });
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
1624
2102
|
}
|
|
1625
|
-
|
|
2103
|
+
];
|
|
1626
2104
|
}
|
|
1627
|
-
|
|
2105
|
+
});
|
|
1628
2106
|
|
|
1629
2107
|
// src/http.ts
|
|
1630
|
-
|
|
1631
|
-
function getLastActivity() {
|
|
1632
|
-
return lastActivity;
|
|
1633
|
-
}
|
|
1634
|
-
var ROUTES = [handleInstall, handleMcp, handlePluginApi];
|
|
2108
|
+
import { createServer } from "node:http";
|
|
1635
2109
|
function startHttpServer() {
|
|
1636
2110
|
const server = createServer((req, res) => {
|
|
1637
|
-
lastActivity = Date.now();
|
|
1638
2111
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1639
2112
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
1640
2113
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id");
|
|
@@ -1649,7 +2122,7 @@ function startHttpServer() {
|
|
|
1649
2122
|
const all = sessions.list();
|
|
1650
2123
|
sendJson(res, 200, {
|
|
1651
2124
|
ok: true,
|
|
1652
|
-
name:
|
|
2125
|
+
name: SERVICE_NAME,
|
|
1653
2126
|
pid: process.pid,
|
|
1654
2127
|
version: VERSION,
|
|
1655
2128
|
pluginsOnline: all.filter((s) => s.online).length
|
|
@@ -1673,161 +2146,141 @@ function startHttpServer() {
|
|
|
1673
2146
|
});
|
|
1674
2147
|
server.listen(config.httpPort, "127.0.0.1", () => {
|
|
1675
2148
|
log(`plugin HTTP + MCP server on http://127.0.0.1:${config.httpPort} (MCP endpoint: /mcp, pid ${process.pid})`);
|
|
2149
|
+
scheduleAssetsSweep(config.assetTtlMs);
|
|
1676
2150
|
resolve(server);
|
|
1677
2151
|
});
|
|
1678
2152
|
});
|
|
1679
2153
|
}
|
|
2154
|
+
var ROUTES;
|
|
2155
|
+
var init_http2 = __esm({
|
|
2156
|
+
"src/http.ts"() {
|
|
2157
|
+
"use strict";
|
|
2158
|
+
init_http();
|
|
2159
|
+
init_config();
|
|
2160
|
+
init_http_body();
|
|
2161
|
+
init_api();
|
|
2162
|
+
init_api2();
|
|
2163
|
+
init_assets();
|
|
2164
|
+
init_sessions();
|
|
2165
|
+
ROUTES = [handleInstall, handleMcp, handlePluginApi];
|
|
2166
|
+
}
|
|
2167
|
+
});
|
|
1680
2168
|
|
|
1681
|
-
// src/
|
|
1682
|
-
|
|
2169
|
+
// src/cli/serve.ts
|
|
2170
|
+
var serve_exports = {};
|
|
2171
|
+
__export(serve_exports, {
|
|
2172
|
+
serve: () => serve
|
|
2173
|
+
});
|
|
2174
|
+
async function serve() {
|
|
2175
|
+
const { state, health } = await probeService();
|
|
2176
|
+
if (state === "alive" && health?.version === VERSION) {
|
|
2177
|
+
printUsage();
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
if (state === "alive") {
|
|
2181
|
+
throw new Error(
|
|
2182
|
+
`\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`
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
if (state === "hung" || state === "foreign") {
|
|
2186
|
+
throw serviceStateError(state);
|
|
2187
|
+
}
|
|
1683
2188
|
const server = await startHttpServer();
|
|
1684
2189
|
if (!server) {
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
}
|
|
1688
|
-
log(`hub ready (idle TTL ${config.hubIdleMs / 6e4}min)`);
|
|
1689
|
-
void cleanOldAssets(config.assetTtlMs);
|
|
1690
|
-
let tick = 0;
|
|
1691
|
-
const watchdog = setInterval(() => {
|
|
1692
|
-
const idle = Date.now() - getLastActivity();
|
|
1693
|
-
const anyPluginOnline = sessions.list().some((s) => s.online);
|
|
1694
|
-
if (idle > config.hubIdleMs && !anyPluginOnline) {
|
|
1695
|
-
log(`\u7A7A\u95F2 ${Math.round(idle / 6e4)} \u5206\u949F\u4E14\u65E0\u5728\u7EBF\u63D2\u4EF6\uFF0Chub \u81EA\u52A8\u9000\u51FA`);
|
|
1696
|
-
process.exit(0);
|
|
1697
|
-
}
|
|
1698
|
-
if (++tick % 60 === 0) void cleanOldAssets(config.assetTtlMs);
|
|
1699
|
-
}, 6e4);
|
|
1700
|
-
watchdog.unref();
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
// src/process/stdio-proxy.ts
|
|
1704
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1705
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
1706
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1707
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1708
|
-
import { ResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1709
|
-
|
|
1710
|
-
// src/process/hub-process.ts
|
|
1711
|
-
import { spawn } from "node:child_process";
|
|
1712
|
-
import { closeSync, openSync } from "node:fs";
|
|
1713
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
1714
|
-
import { join as join4 } from "node:path";
|
|
1715
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1716
|
-
async function probeHub() {
|
|
1717
|
-
try {
|
|
1718
|
-
const res = await fetch(`${hubBaseUrl()}/health`, { signal: AbortSignal.timeout(1500) });
|
|
1719
|
-
if (!res.ok) return "foreign";
|
|
1720
|
-
const body = await res.json();
|
|
1721
|
-
if (body.ok === true && body.name === HUB_NAME) return "alive";
|
|
1722
|
-
if (body.ok === true) return "legacy";
|
|
1723
|
-
return "foreign";
|
|
1724
|
-
} catch {
|
|
1725
|
-
return "down";
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
var PORT_FIX_HINT = `\u7528 netstat -ano | findstr :${config.httpPort} \u67E5\u5230 PID \u540E taskkill /PID <pid> /F \u7ED3\u675F\uFF0C\u518D\u91CD\u8BD5`;
|
|
1729
|
-
function hubStateError(state) {
|
|
1730
|
-
if (state === "legacy") {
|
|
1731
|
-
return new Error(
|
|
1732
|
-
`\u7AEF\u53E3 ${config.httpPort} \u4E0A\u662F\u65E7\u7248\u672C batter-pixso-mcp \u5B9E\u4F8B\uFF08\u4E0D\u652F\u6301\u591A agent \u590D\u7528\uFF09\u3002${PORT_FIX_HINT}`
|
|
2190
|
+
throw new Error(
|
|
2191
|
+
`\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`
|
|
1733
2192
|
);
|
|
1734
2193
|
}
|
|
1735
|
-
|
|
2194
|
+
printUsage();
|
|
2195
|
+
log("\u524D\u53F0\u8FD0\u884C\u4E2D\uFF1A\u6B64\u7A97\u53E3/\u8FDB\u7A0B\u9700\u4FDD\u6301\uFF08Ctrl+C \u505C\u6B62\u670D\u52A1\uFF09\uFF1B\u514D\u7A97\u53E3\u7528 --setup \u6CE8\u518C\u5F00\u673A\u81EA\u542F");
|
|
1736
2196
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
2197
|
+
function printUsage() {
|
|
2198
|
+
const url = mcpUrl();
|
|
2199
|
+
console.log(
|
|
2200
|
+
[
|
|
2201
|
+
`batter-pixso-mcp v${VERSION} \u2014 \u670D\u52A1\u8FD0\u884C\u4E2D\uFF1A${serviceBaseUrl()}`,
|
|
2202
|
+
``,
|
|
2203
|
+
`MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\uFF08URL \u76F4\u8FDE\uFF0C\u4E00\u4E2A\u5730\u5740\u5168\u5BA2\u6237\u7AEF\u901A\u7528\uFF09\uFF1A`,
|
|
2204
|
+
``,
|
|
2205
|
+
` Claude Code\uFF08\u4E00\u884C\u547D\u4EE4\uFF09\uFF1A`,
|
|
2206
|
+
` ${claudeAddCommand()}`,
|
|
2207
|
+
``,
|
|
2208
|
+
` \u901A\u7528 JSON\uFF08Cursor / Cherry Studio \u7B49\uFF09\uFF1A`,
|
|
2209
|
+
` { "mcpServers": { "batter-pixso": { "url": "${url}" } } }`,
|
|
2210
|
+
``,
|
|
2211
|
+
`\u63D2\u4EF6\u5B89\u88C5\uFF1A\u6D4F\u89C8\u5668\u6253\u5F00 ${serviceBaseUrl()}/ \u6309\u5F15\u5BFC\u5B89\u88C5`,
|
|
2212
|
+
`\u5F00\u673A\u81EA\u542F\uFF1Anpx batter-pixso-mcp --setup\uFF08Windows\uFF09`,
|
|
2213
|
+
`\u505C\u6B62\u670D\u52A1\uFF1Anpx batter-pixso-mcp --stop`
|
|
2214
|
+
].join("\n")
|
|
1744
2215
|
);
|
|
1745
|
-
const child = spawn(process.execPath, [...execArgv, entry, "--hub"], {
|
|
1746
|
-
detached: true,
|
|
1747
|
-
stdio: ["ignore", out, out],
|
|
1748
|
-
windowsHide: true,
|
|
1749
|
-
env: { ...process.env }
|
|
1750
|
-
});
|
|
1751
|
-
child.unref();
|
|
1752
|
-
closeSync(out);
|
|
1753
|
-
log(`hub spawned (pid ${child.pid}), log: ${HUB_LOG}`);
|
|
1754
2216
|
}
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
if (Date.now() > deadline) throw new Error(`\u7B49\u5F85 hub \u5C31\u7EEA\u8D85\u65F6\uFF08${timeoutMs / 1e3}s\uFF09`);
|
|
1762
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
2217
|
+
var init_serve = __esm({
|
|
2218
|
+
"src/cli/serve.ts"() {
|
|
2219
|
+
"use strict";
|
|
2220
|
+
init_config();
|
|
2221
|
+
init_http2();
|
|
2222
|
+
init_service();
|
|
1763
2223
|
}
|
|
1764
|
-
}
|
|
2224
|
+
});
|
|
1765
2225
|
|
|
1766
|
-
// src/
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
await
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
const alive = await client.ping().then(
|
|
1794
|
-
() => true,
|
|
1795
|
-
() => false
|
|
1796
|
-
);
|
|
1797
|
-
if (alive) throw err;
|
|
1798
|
-
log("hub \u4F1A\u8BDD\u5931\u8054\uFF0C\u91CD\u5EFA\u540E\u91CD\u8BD5\u2026");
|
|
1799
|
-
upstream = null;
|
|
1800
|
-
const rebuilt = await ensureUpstream();
|
|
1801
|
-
return await fn(rebuilt);
|
|
2226
|
+
// src/cli/index.ts
|
|
2227
|
+
init_config();
|
|
2228
|
+
|
|
2229
|
+
// src/cli/commands.ts
|
|
2230
|
+
init_config();
|
|
2231
|
+
var COMMANDS = [
|
|
2232
|
+
{
|
|
2233
|
+
flags: ["--stop"],
|
|
2234
|
+
summary: "\u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1",
|
|
2235
|
+
run: async () => (await Promise.resolve().then(() => (init_service(), service_exports))).stopService()
|
|
2236
|
+
},
|
|
2237
|
+
{
|
|
2238
|
+
flags: ["--version", "-v"],
|
|
2239
|
+
summary: "\u6253\u5370\u7248\u672C\u53F7",
|
|
2240
|
+
run: async () => log(VERSION)
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
flags: ["--uninstall"],
|
|
2244
|
+
summary: "\u79FB\u9664\u767B\u5F55\u81EA\u542F + \u505C\u6B62\u670D\u52A1 + \u5220\u9664\u7269\u5316\u76EE\u5F55",
|
|
2245
|
+
run: async () => (await Promise.resolve().then(() => (init_setup(), setup_exports))).uninstallAutostart()
|
|
2246
|
+
},
|
|
2247
|
+
{
|
|
2248
|
+
flags: ["--setup"],
|
|
2249
|
+
summary: "\uFF08Windows\uFF09\u7269\u5316\u81EA\u5305\u542B\u526F\u672C\u5230 %LOCALAPPDATA% \u5E76\u6CE8\u518C\u767B\u5F55\u81EA\u542F\uFF0C\u968F\u540E\u524D\u53F0\u542F\u52A8\u670D\u52A1",
|
|
2250
|
+
run: async () => {
|
|
2251
|
+
await (await Promise.resolve().then(() => (init_setup(), setup_exports))).setupAutostart();
|
|
2252
|
+
await (await Promise.resolve().then(() => (init_serve(), serve_exports))).serve();
|
|
1802
2253
|
}
|
|
2254
|
+
},
|
|
2255
|
+
{
|
|
2256
|
+
flags: ["--help", "-h"],
|
|
2257
|
+
summary: "\u6253\u5370\u672C\u5E2E\u52A9",
|
|
2258
|
+
run: async () => printHelp()
|
|
1803
2259
|
}
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
2260
|
+
];
|
|
2261
|
+
function printHelp() {
|
|
2262
|
+
console.log(
|
|
2263
|
+
[
|
|
2264
|
+
`batter-pixso-mcp v${VERSION}`,
|
|
2265
|
+
``,
|
|
2266
|
+
`\u7528\u6CD5\uFF1Abatter-pixso-mcp [\u547D\u4EE4]\uFF08\u4E0D\u5E26\u547D\u4EE4 = \u524D\u53F0\u542F\u52A8\u670D\u52A1\uFF09`,
|
|
2267
|
+
``,
|
|
2268
|
+
...COMMANDS.map((c) => ` ${c.flags.join(", ").padEnd(18)}${c.summary}`)
|
|
2269
|
+
].join("\n")
|
|
1810
2270
|
);
|
|
1811
|
-
server.fallbackRequestHandler = (req, extra) => callWithRecovery((c) => c.request(req, ResultSchema, { signal: extra.signal }));
|
|
1812
|
-
server.onclose = () => {
|
|
1813
|
-
log("agent disconnected, proxy exiting");
|
|
1814
|
-
process.exit(0);
|
|
1815
|
-
};
|
|
1816
|
-
await server.connect(new StdioServerTransport());
|
|
1817
|
-
log("proxy ready on stdio");
|
|
1818
2271
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
await runHub();
|
|
2272
|
+
async function dispatch(argv) {
|
|
2273
|
+
const hit = COMMANDS.find((c) => c.flags.some((f) => argv.includes(f)));
|
|
2274
|
+
if (hit) {
|
|
2275
|
+
await hit.run();
|
|
1824
2276
|
return;
|
|
1825
2277
|
}
|
|
1826
|
-
await
|
|
2278
|
+
const { serve: serve2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
|
|
2279
|
+
await serve2();
|
|
1827
2280
|
}
|
|
1828
2281
|
|
|
1829
|
-
// src/
|
|
1830
|
-
|
|
1831
|
-
|
|
2282
|
+
// src/cli/index.ts
|
|
2283
|
+
dispatch(process.argv).catch((err) => {
|
|
2284
|
+
log("fatal:", err);
|
|
1832
2285
|
process.exit(1);
|
|
1833
2286
|
});
|