|
|
|
@ -195,249 +195,4 @@ this.viewport.stateManager.update(({ getEntity, putEntity, addEntity }) => { |
|
|
|
system.showInfoDialog(content) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 脚本与 Worker 的映射,用于快速查找
|
|
|
|
private scriptWorkers = new WeakMap<CustomScript, Worker>() |
|
|
|
private toolProxyManager = new ToolProxyManager() |
|
|
|
|
|
|
|
/** |
|
|
|
* 开启另外的线程,运行脚本 |
|
|
|
*/ |
|
|
|
async runScript(script: CustomScript) { |
|
|
|
// 检查脚本是否已在运行
|
|
|
|
if (script.isRunning) { |
|
|
|
console.warn(`脚本 "${script.name}" 已在运行中`) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
// 创建 Blob URL 作为 Worker 执行内容
|
|
|
|
const jsCode = await compileTypeScript(script.content) |
|
|
|
|
|
|
|
const wrappedContent = ` |
|
|
|
// Worker 端工具代理
|
|
|
|
// 创建代理对象
|
|
|
|
const createToolProxy = (port) => { |
|
|
|
return new Proxy({}, { |
|
|
|
get(_, toolName) { |
|
|
|
return new Proxy({}, { |
|
|
|
get(_, methodName) { |
|
|
|
return (...args) => { |
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
const callId = Math.random().toString(36).substr(2, 10); |
|
|
|
|
|
|
|
// 发送调用请求
|
|
|
|
port.postMessage({ |
|
|
|
type: 'call', |
|
|
|
tool: toolName, |
|
|
|
method: methodName, |
|
|
|
callId, |
|
|
|
args |
|
|
|
}); |
|
|
|
|
|
|
|
// 监听响应
|
|
|
|
const responseHandler = (event) => { |
|
|
|
const { data } = event; |
|
|
|
if (data.callId === callId) { |
|
|
|
console.log('[Worker] 收到响应: ' + toolName + '.' + methodName, data.type); |
|
|
|
port.removeEventListener('message', responseHandler); |
|
|
|
|
|
|
|
if (data.type === 'result') { |
|
|
|
resolve(data.result); |
|
|
|
} else if (data.type === 'error') { |
|
|
|
const err = new Error(data.error.message); |
|
|
|
err.name = data.error.name; |
|
|
|
err.stack = data.error.stack; |
|
|
|
reject(err); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
port.addEventListener('message', responseHandler); |
|
|
|
}); |
|
|
|
}; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
// 创建代理对象
|
|
|
|
const tools = new Proxy({}, toolProxyHandler); |
|
|
|
|
|
|
|
self.onmessage = function(__initMessage) { |
|
|
|
if (__initMessage.data.type === 'init') { |
|
|
|
// 设置通信端口
|
|
|
|
self.port = __initMessage.data.port; |
|
|
|
self.port.onmessage = function(event) { |
|
|
|
const { type, callId, result, error } = event.data; |
|
|
|
|
|
|
|
if (!self.pendingCalls || !self.pendingCalls[callId]) return; |
|
|
|
|
|
|
|
const { resolve, reject } = self.pendingCalls[callId]; |
|
|
|
delete self.pendingCalls[callId]; |
|
|
|
|
|
|
|
if (type === 'result') { |
|
|
|
resolve(result); |
|
|
|
} else if (type === 'error') { |
|
|
|
const err = new Error(error.message); |
|
|
|
err.name = error.name; |
|
|
|
err.stack = error.stack; |
|
|
|
reject(err); |
|
|
|
} |
|
|
|
}; |
|
|
|
self.port.start(); |
|
|
|
|
|
|
|
// 解构代理工具
|
|
|
|
const { LCC, RCS } = tools; |
|
|
|
|
|
|
|
try { |
|
|
|
(async function() { |
|
|
|
// ---------------------custom script start---------------------[
|
|
|
|
${jsCode} |
|
|
|
// ]---------------------custom script end---------------------
|
|
|
|
})().then(() => { |
|
|
|
self.postMessage({ type: 'completed' }) |
|
|
|
}).catch(error => { |
|
|
|
// 捕获异步错误
|
|
|
|
self.postMessage({ |
|
|
|
type: 'error', |
|
|
|
error: { |
|
|
|
name: error.name, |
|
|
|
message: error.message, |
|
|
|
stack: error.stack |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
} catch (error) { |
|
|
|
// 捕获脚本错误
|
|
|
|
self.postMessage({ |
|
|
|
type: 'error', |
|
|
|
error: { |
|
|
|
name: error.name, |
|
|
|
message: error.message, |
|
|
|
stack: error.stack |
|
|
|
} |
|
|
|
}) |
|
|
|
} finally { |
|
|
|
// 确保关闭 worker
|
|
|
|
self.close() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
` |
|
|
|
// console.log(wrappedContent)
|
|
|
|
|
|
|
|
const blob = new Blob([wrappedContent], { type: 'application/javascript' }) |
|
|
|
const blobUrl = URL.createObjectURL(blob) |
|
|
|
const worker = new Worker(blobUrl) |
|
|
|
|
|
|
|
// 创建代理并初始化 Worker
|
|
|
|
const port = this.toolProxyManager.createToolProxyForWorker(worker) |
|
|
|
|
|
|
|
// 存储 Worker 引用并更新状态
|
|
|
|
this.scriptWorkers.set(script, worker) |
|
|
|
script.worker = markRaw(worker) |
|
|
|
script.isRunning = true |
|
|
|
URL.revokeObjectURL(blobUrl) // 清理不再需要的 Blob URL
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 Worker 事件监听器
|
|
|
|
worker.onmessage = (e) => this.handleWorkerMessage(script, e) |
|
|
|
worker.onerror = (e) => this.handleWorkerError(script, e) |
|
|
|
worker.onmessageerror = (e) => this.handleMessageError(script, e) |
|
|
|
|
|
|
|
console.log(`[ScriptRunner] 创建 Worker 并启动脚本 "${script.name}"`) |
|
|
|
|
|
|
|
worker.postMessage({ |
|
|
|
type: 'init', |
|
|
|
port: port |
|
|
|
}, [port]) |
|
|
|
|
|
|
|
console.log(`[ScriptRunner] 初始化消息已发送给 Worker`) |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error(`启动脚本 "${script.name}" 失败:`, error) |
|
|
|
script.isRunning = false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 停止脚本 |
|
|
|
*/ |
|
|
|
stopScript(script: CustomScript) { |
|
|
|
this.cleanupScript(script, '手动停止') |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 处理来自 Worker 的消息 |
|
|
|
*/ |
|
|
|
private handleWorkerMessage(script: CustomScript, event: MessageEvent): void { |
|
|
|
const { data } = event |
|
|
|
|
|
|
|
if (data?.type === 'call') { |
|
|
|
// 处理来自 Worker 的工具调用请求
|
|
|
|
this.toolProxyManager.handleWorkerCall(script.worker, event) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if (data?.type === 'completed') { |
|
|
|
// 脚本自然完成
|
|
|
|
console.log(`脚本 "${script.name}" 执行完成`) |
|
|
|
this.cleanupScript(script, '执行完成') |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if (data?.type === 'error') { |
|
|
|
// 脚本内部错误
|
|
|
|
const error = new Error(data.error.message) |
|
|
|
error.name = data.error.name |
|
|
|
error.stack = data.error.stack |
|
|
|
|
|
|
|
console.error(`[${script.name}] 执行错误:`, error) |
|
|
|
this.cleanupScript(script, '执行错误') |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 常规消息处理
|
|
|
|
console.log(`[${script.name}] 收到消息:`, data) |
|
|
|
debugger |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 处理 Worker 错误 |
|
|
|
*/ |
|
|
|
private handleWorkerError(script: CustomScript, event: ErrorEvent): void { |
|
|
|
console.error(`[${script.name}] 发生错误:`, event.message, `@${event.filename}:${event.lineno}`) |
|
|
|
this.cleanupScript(script, '运行时错误') |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 处理消息错误 |
|
|
|
*/ |
|
|
|
private handleMessageError(script: CustomScript, event: MessageEvent): void { |
|
|
|
console.error(`[${script.name}] 消息解析错误:`, event) |
|
|
|
this.cleanupScript(script, '消息错误') |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 清理脚本资源 |
|
|
|
*/ |
|
|
|
private cleanupScript(script: CustomScript, reason: string): void { |
|
|
|
if (!script.isRunning || !script.worker) return |
|
|
|
|
|
|
|
try { |
|
|
|
// 清理代理管理器中的资源
|
|
|
|
this.toolProxyManager.cleanupWorker(script.worker) |
|
|
|
|
|
|
|
// 终止 Worker
|
|
|
|
script.worker.terminate() |
|
|
|
this.scriptWorkers.delete(script) |
|
|
|
script.worker = undefined |
|
|
|
script.isRunning = false |
|
|
|
console.log(`脚本 "${script.name}" 已停止 (原因: ${reason})`) |
|
|
|
} catch (error) { |
|
|
|
console.error(`清理脚本 "${script.name}" 资源失败:`, error) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|