@ai.weget.jp/bot 0.1.19 → 0.1.21
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/src/ipc/registerHandlers.js +1 -1
- package/dist/src/main.js +1 -1
- package/dist/src/preload.cjs +7 -0
- package/dist/src/renderer/app.js +1 -1
- package/dist/src/renderer/index.html +20 -5
- package/dist/src/renderer/styles.css +104 -2
- package/dist/src/services/codexService.js +1 -1
- package/dist/src/services/taskDirective.js +1 -0
- package/dist/src/services/taskDirective.test.js +1 -0
- package/dist/src/services/windowManagerService.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const r=r=>r&&"object"==typeof r?r:{};export const registerIpcHandlers=({ipcMain:e,env:t,signInWithLoginApi:o,saveLoginProfile:n,readSavedLoginProfile:i,getSkillConfig:a,saveSkillConfig:s,getAiConfig:c,saveAiConfig:g,setCurrentSession:l,getCurrentSession:m,getActiveSkills:u,getCodexAuthStatus:d,getCodexMcpStatus:S,getPlaywrightBrowserStatus:y,startCodexLogin:k,installPlaywrightMcp:h,getGatewayContextFilePath:f,runGatewaySelfTest:w,getManagedSkills:p,getMacroSnapshot:b,getMacroCalendar:C,getMacroNews:E,emitLog:x,openGmoKlineWindow:L,getGmoMarketQuotes:I,getGmoSymbolRules:v,getGmoBuyingPower:P,getGmoAccountMetrics:A,getGmoPositionSummary:G,getGmoOpenPositions:U,placeGmoFxOrder:q,placeGmoCoinOrder:M,placeGmoFxCloseOrder:B,placeGmoCoinCloseOrder:N,getGmoApiState:z,testGmoApi:F,buildTradeStatusSnapshot:K,closeTradeWindows:O,startWsClient:T,stopWsClient:W,handleBotChatMessage:Y,writeErrorLog:R,writeRuntimeLog:_})=>{e.handle("auth:login",async(e,t)=>{const i=r(t),a=String(i.botId||"").trim();if(!a)return{ok:!1,error:"Bot ID is required"};try{const r=await o(String(i.email||""),String(i.password||""));await n({botId:a,email:String(i.email||"").trim(),password:String(i.password||""),remember:Boolean(i.remember)});const e={...r,botId:a};return l(e),T({botId:a,userId:String(r.userId||""),accessToken:String(r.accessToken||"")}),x("[auth] login success",{userId:r.userId||"",email:r.email||"",botId:a}),{ok:!0,session:{userId:String(r.userId||""),email:String(r.email||""),botId:a}}}catch(r){const e=r instanceof Error?r.message:String(r);return x("[auth] login failed",{error:e}),{ok:!1,error:e}}}),e.handle("auth:logout",async()=>(W(),l(null),O(),x("[auth] logout"),{ok:!0})),e.handle("auth:getSavedProfile",async()=>{try{return{ok:!0,profile:await i()}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("skill:getConfig",async(r,e)=>{try{const r=String(e||"").trim();return{ok:!0,...a(r)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("skill:saveConfig",async(e,t)=>{try{const e=r(t),o=String(e.skillName||"").trim();return{ok:!0,...await s(o,e.config)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("ai:getConfig",async()=>({ok:!0,config:c()})),e.handle("ai:saveConfig",async(r,e)=>{try{return{ok:!0,config:await g(e)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("bot:getRuntimeInfo",async()=>{const e=m(),o=r(e),n=await d();return{ok:!0,status:e?"connected":"disconnected",session:e?{userId:String(o.userId||""),email:String(o.email||""),botId:String(o.botId||"")}:null,runtime:{loginApiUrl:t.loginApiUrl,capabilities:t.capabilities,gmoFxApiState:z("fx"),gmoCoinApiState:z("coin"),aiModel:c().aiModel,activeSkills:u(),codexAuthStatus:n.status,codexAuthDetail:n.detail}}}),e.handle("bot:getSkills",async()=>({ok:!0,skills:p()})),e.handle("codex:startLogin",async()=>k()),e.handle("browser:getPlaywrightMcpStatus",async()=>{try{return{ok:!0,...await S("weget-gateway")}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("browser:getPlaywrightBrowserStatus",async()=>{try{return{ok:!0,...await y()}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("browser:installPlaywrightMcp",async()=>h()),e.handle("gateway:getStatus",async()=>{try{const[r,e,t]=await Promise.all([d(),S("weget-gateway"),y()]);return{ok:!0,codexAuth:r,gatewayStatus:e,browserStatus:t,contextFilePath:f(),skills:p().map(r=>({name:r.name,displayName:r.displayName,enabled:r.enabled,installStatus:r.installStatus,tools:r.tools}))}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("gateway:runSelfTest",async(e,t)=>{const o=r(t),n=String(o.target||"").trim().toLowerCase(),i="browser"===n||"macro"===n||"coin"===n||"fx"===n||"codex_macro"===n?n:"gateway";try{return await w(i)}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("macro:getSnapshot",async(e,t)=>{const o=r(t);try{return{ok:!0,snapshot:await b({force:Boolean(o.force)})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("macro:getCalendar",async(e,t)=>{const o=r(t),n=String(o.dateKey||"").trim();if(!n)return{ok:!1,error:"dateKey is required"};try{return{ok:!0,rows:await C({dateKey:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("macro:getNews",async(e,t)=>{const o=r(t),n=String(o.dateKey||"").trim();if(!n)return{ok:!1,error:"dateKey is required"};try{return{ok:!0,rows:await E({dateKey:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:openGmoKlineWindow",async(e,t)=>{const o=r(t),n=String(o.symbol||"BTC_JPY").trim()||"BTC_JPY",i=String(o.interval||"1m").trim()||"1m",a="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await L({symbol:n,interval:i,market:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getMarketQuotes",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",i=Array.isArray(o.symbols)?o.symbols.map(r=>String(r||"").trim().toUpperCase()).filter(Boolean):void 0;try{return{ok:!0,...await I({market:n,symbols:i})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getSymbolRules",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await v({market:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getBuyingPower",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await P({market:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getAccountMetrics",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await A({market:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getPositionSummary",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",i=String(o.symbol||"").trim().toUpperCase();if("coin"===n&&!i)return{ok:!1,error:"symbol is required for coin positionSummary"};try{return{ok:!0,...await G({market:n,symbol:i||void 0})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getOpenPositions",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",i=String(o.symbol||"").trim().toUpperCase();if("coin"===n&&!i)return{ok:!1,error:"symbol is required for coin openPositions"};const a=Number(o.page),s=Number(o.prevId),c=Number(o.count);try{return{ok:!0,...await U({market:n,symbol:i||void 0,page:Number.isFinite(a)?a:void 0,prevId:Number.isFinite(s)?s:void 0,count:Number.isFinite(c)?c:void 0})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:placeFxOrder",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!a)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await q({symbol:n,side:i,size:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:placeCoinOrder",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!a)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await M({symbol:n,side:i,size:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:closeFxPosition",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=Number(o.positionId),s=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!Number.isFinite(a)||a<=0)return{ok:!1,error:"positionId is required"};if(!s)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await B({symbol:n,side:i,positionId:a,size:s})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:closeCoinPosition",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=Number(o.positionId),s=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!Number.isFinite(a)||a<=0)return{ok:!1,error:"positionId is required"};if(!s)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await N({symbol:n,side:i,positionId:a,size:s})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:testGmoApi",async(e,t)=>{const o=r(t),n="coin"===String(o.market||"").trim().toLowerCase()?"coin":"fx";return F(n)}),e.handle("trade:getStatusSnapshot",async(e,t)=>{const o=r(t),n="coin"===String(o.market||"").trim().toLowerCase()?"coin":"fx";try{return{ok:!0,snapshot:await K(n)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("chat:send",async(e,t)=>{const o=r(t),n=String(o.message||"").trim();if(!n)return{ok:!1,error:"message is required"};try{return{ok:!0,answer:(await Y({text:n})).answer}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("log:writeError",async(e,t)=>{const o=r(t),n=String(o.source||"").trim()||"ui",i=String(o.message||"").trim()||"unknown error";try{return await R({source:n,message:i,details:o.details}),{ok:!0}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("log:write",async(e,t)=>{const o=r(t),n=String(o.level||"").trim().toLowerCase(),i="error"===n?"error":"debug"===n?"debug":"info",a=String(o.source||"").trim()||"ui",s=String(o.message||"").trim()||"log";try{return await _({level:i,source:a,message:s,details:o.details}),{ok:!0}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}})};
|
|
1
|
+
const r=r=>r&&"object"==typeof r?r:{};export const registerIpcHandlers=({ipcMain:e,env:t,signInWithLoginApi:o,saveLoginProfile:n,readSavedLoginProfile:i,getSkillConfig:a,saveSkillConfig:s,getAiConfig:c,saveAiConfig:g,setCurrentSession:l,getCurrentSession:m,getActiveSkills:u,getCodexAuthStatus:d,getCodexMcpStatus:S,getPlaywrightBrowserStatus:y,startCodexLogin:k,installPlaywrightMcp:h,getGatewayContextFilePath:f,runGatewaySelfTest:w,getManagedSkills:p,getMacroSnapshot:b,getMacroCalendar:C,getMacroNews:E,emitLog:x,openGmoKlineWindow:L,getGmoMarketQuotes:I,getGmoSymbolRules:v,getGmoBuyingPower:A,getGmoAccountMetrics:P,getGmoPositionSummary:q,getGmoOpenPositions:G,placeGmoFxOrder:U,placeGmoCoinOrder:M,placeGmoFxCloseOrder:B,placeGmoCoinCloseOrder:N,getGmoApiState:z,testGmoApi:F,buildTradeStatusSnapshot:T,closeTradeWindows:K,startWsClient:O,stopWsClient:R,handleBotChatMessage:W,getActiveTaskRuntimes:Y,cancelActiveTaskRuntime:_,writeErrorLog:D,writeRuntimeLog:J})=>{e.handle("auth:login",async(e,t)=>{const i=r(t),a=String(i.botId||"").trim();if(!a)return{ok:!1,error:"Bot ID is required"};try{const r=await o(String(i.email||""),String(i.password||""));await n({botId:a,email:String(i.email||"").trim(),password:String(i.password||""),remember:Boolean(i.remember)});const e={...r,botId:a};return l(e),O({botId:a,userId:String(r.userId||""),accessToken:String(r.accessToken||"")}),x("[auth] login success",{userId:r.userId||"",email:r.email||"",botId:a}),{ok:!0,session:{userId:String(r.userId||""),email:String(r.email||""),botId:a}}}catch(r){const e=r instanceof Error?r.message:String(r);return x("[auth] login failed",{error:e}),{ok:!1,error:e}}}),e.handle("auth:logout",async()=>(R(),l(null),K(),x("[auth] logout"),{ok:!0})),e.handle("auth:getSavedProfile",async()=>{try{return{ok:!0,profile:await i()}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("skill:getConfig",async(r,e)=>{try{const r=String(e||"").trim();return{ok:!0,...a(r)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("skill:saveConfig",async(e,t)=>{try{const e=r(t),o=String(e.skillName||"").trim();return{ok:!0,...await s(o,e.config)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("ai:getConfig",async()=>({ok:!0,config:c()})),e.handle("ai:saveConfig",async(r,e)=>{try{return{ok:!0,config:await g(e)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("bot:getRuntimeInfo",async()=>{const e=m(),o=r(e),n=await d();return{ok:!0,status:e?"connected":"disconnected",session:e?{userId:String(o.userId||""),email:String(o.email||""),botId:String(o.botId||"")}:null,runtime:{loginApiUrl:t.loginApiUrl,capabilities:t.capabilities,gmoFxApiState:z("fx"),gmoCoinApiState:z("coin"),aiModel:c().aiModel,activeSkills:u(),codexAuthStatus:n.status,codexAuthDetail:n.detail}}}),e.handle("bot:getSkills",async()=>({ok:!0,skills:p()})),e.handle("task:getActiveRuntimes",async()=>({ok:!0,tasks:Y()})),e.handle("task:cancelActiveRuntime",async(e,t)=>{const o=r(t),n=String(o.taskId||"").trim();return n?_(n):{ok:!1,error:"taskId is required"}}),e.handle("codex:startLogin",async()=>k()),e.handle("browser:getPlaywrightMcpStatus",async()=>{try{return{ok:!0,...await S("weget-gateway")}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("browser:getPlaywrightBrowserStatus",async()=>{try{return{ok:!0,...await y()}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("browser:installPlaywrightMcp",async()=>h()),e.handle("gateway:getStatus",async()=>{try{const[r,e,t]=await Promise.all([d(),S("weget-gateway"),y()]);return{ok:!0,codexAuth:r,gatewayStatus:e,browserStatus:t,contextFilePath:f(),skills:p().map(r=>({name:r.name,displayName:r.displayName,enabled:r.enabled,installStatus:r.installStatus,tools:r.tools}))}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("gateway:runSelfTest",async(e,t)=>{const o=r(t),n=String(o.target||"").trim().toLowerCase(),i="browser"===n||"macro"===n||"coin"===n||"fx"===n||"codex_macro"===n?n:"gateway";try{return await w(i)}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("macro:getSnapshot",async(e,t)=>{const o=r(t);try{return{ok:!0,snapshot:await b({force:Boolean(o.force)})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("macro:getCalendar",async(e,t)=>{const o=r(t),n=String(o.dateKey||"").trim();if(!n)return{ok:!1,error:"dateKey is required"};try{return{ok:!0,rows:await C({dateKey:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("macro:getNews",async(e,t)=>{const o=r(t),n=String(o.dateKey||"").trim();if(!n)return{ok:!1,error:"dateKey is required"};try{return{ok:!0,rows:await E({dateKey:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:openGmoKlineWindow",async(e,t)=>{const o=r(t),n=String(o.symbol||"BTC_JPY").trim()||"BTC_JPY",i=String(o.interval||"1m").trim()||"1m",a="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await L({symbol:n,interval:i,market:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getMarketQuotes",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",i=Array.isArray(o.symbols)?o.symbols.map(r=>String(r||"").trim().toUpperCase()).filter(Boolean):void 0;try{return{ok:!0,...await I({market:n,symbols:i})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getSymbolRules",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await v({market:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getBuyingPower",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await A({market:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getAccountMetrics",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await P({market:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getPositionSummary",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",i=String(o.symbol||"").trim().toUpperCase();if("coin"===n&&!i)return{ok:!1,error:"symbol is required for coin positionSummary"};try{return{ok:!0,...await q({market:n,symbol:i||void 0})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getOpenPositions",async(e,t)=>{const o=r(t),n="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",i=String(o.symbol||"").trim().toUpperCase();if("coin"===n&&!i)return{ok:!1,error:"symbol is required for coin openPositions"};const a=Number(o.page),s=Number(o.prevId),c=Number(o.count);try{return{ok:!0,...await G({market:n,symbol:i||void 0,page:Number.isFinite(a)?a:void 0,prevId:Number.isFinite(s)?s:void 0,count:Number.isFinite(c)?c:void 0})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:placeFxOrder",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!a)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await U({symbol:n,side:i,size:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:placeCoinOrder",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!a)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await M({symbol:n,side:i,size:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:closeFxPosition",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=Number(o.positionId),s=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!Number.isFinite(a)||a<=0)return{ok:!1,error:"positionId is required"};if(!s)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await B({symbol:n,side:i,positionId:a,size:s})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:closeCoinPosition",async(e,t)=>{const o=r(t),n=String(o.symbol||"").trim().toUpperCase(),i="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",a=Number(o.positionId),s=String(o.size||"").trim();if(!n)return{ok:!1,error:"symbol is required"};if(!Number.isFinite(a)||a<=0)return{ok:!1,error:"positionId is required"};if(!s)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await N({symbol:n,side:i,positionId:a,size:s})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:testGmoApi",async(e,t)=>{const o=r(t),n="coin"===String(o.market||"").trim().toLowerCase()?"coin":"fx";return F(n)}),e.handle("trade:getStatusSnapshot",async(e,t)=>{const o=r(t),n="coin"===String(o.market||"").trim().toLowerCase()?"coin":"fx";try{return{ok:!0,snapshot:await T(n)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("chat:send",async(e,t)=>{const o=r(t),n=String(o.message||"").trim();if(!n)return{ok:!1,error:"message is required"};try{return{ok:!0,answer:(await W({text:n})).answer}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("log:writeError",async(e,t)=>{const o=r(t),n=String(o.source||"").trim()||"ui",i=String(o.message||"").trim()||"unknown error";try{return await D({source:n,message:i,details:o.details}),{ok:!0}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("log:write",async(e,t)=>{const o=r(t),n=String(o.level||"").trim().toLowerCase(),i="error"===n?"error":"debug"===n?"debug":"info",a=String(o.source||"").trim()||"ui",s=String(o.message||"").trim()||"log";try{return await J({level:i,source:a,message:s,details:o.details}),{ok:!0}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}})};
|
package/dist/src/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node:path";import e from"node:fs/promises";import{existsSync as i}from"node:fs";import{fileURLToPath as a}from"node:url";import{createRequire as r}from"node:module";import{SYSTEM_CONFIG as s}from"./config/systemConfig.js";import{BotWsClient as n}from"./wsClient.js";import{registerIpcHandlers as o}from"./ipc/registerHandlers.js";import{createAuthApiService as l}from"./services/authApiService.js";import{createCodexService as g}from"./services/codexService.js";import{createGatewayFileBridgeService as c}from"./services/gatewayFileBridgeService.js";import{createPlatformApiClient as d}from"./platformApi.js";import{createSkillRuntimeManager as p}from"./skillRuntimeManager.js";import{createWindowManager as m}from"./services/windowManagerService.js";import{createSkillHostState as u}from"./skillHostState.js";const S=r(import.meta.url),{app:y,BrowserWindow:f,ipcMain:w,nativeImage:k}=S("electron"),_=a(import.meta.url),b=t.dirname(_),h=["trade","ai"],v=()=>t.join(y.getPath("userData"),"login-profile.json"),x=()=>t.join(y.getPath("userData"),"host-config.json"),C=()=>t.join(y.getPath("userData"),"skill-states.json");let A=null,O=null,M={aiModel:"gpt-5.4",logOutputDir:""};const j=new Map;let E=()=>t.join(y.getPath("documents"),"weget-bot-logs");const P=t=>{const e=String(t||"").toLowerCase();return e.includes("debug")||e.includes("[debug]")?"debug":e.includes("error")||e.includes("failed")||e.includes("exception")?"error":"info"},I=async({level:i,source:a,message:r,details:s,ts:n})=>{const o=E();await e.mkdir(o,{recursive:!0});const l=new Date,g=l.getFullYear(),c=String(l.getMonth()+1).padStart(2,"0"),d=String(l.getDate()).padStart(2,"0"),p=t.join(o,`${i}-${g}${c}${d}.log`),m=JSON.stringify({ts:n||l.toISOString(),level:i,source:a,message:r,details:s??null},null,0);await e.appendFile(p,`${m}\n`,"utf8")},D=m({BrowserWindow:f,preloadPath:t.join(b,"preload.cjs"),rendererDir:t.join(b,"renderer"),windowIconPath:t.join(b,"renderer","logo.png")}),T=(t,e)=>{const i=(new Date).toISOString();D.emitBotLog({message:t,data:e||null,ts:i}),I({level:P(t),source:"runtime",message:t,details:e,ts:i}).catch(()=>{})},B=t=>{D.emitChatEvent(t)},L=t=>t&&"object"==typeof t?t:{},U=u({log:T}),N=l({getEnv:()=>({loginApiUrl:s.loginApiUrl,botUploadApiUrl:s.botUploadApiUrl}),emitLog:T,getCurrentSession:()=>A}),F=d({getBaseUrl:()=>String(s.loginApiUrl||"").trim().replace(/\/login\/?$/i,""),getSession:()=>A,log:T}),G=async t=>{const e=(()=>{const t=String(s.loginApiUrl||"").trim();return t?t.replace(/\/login\/?$/i,"/bot/runtime/line-reply"):""})();if(!e)throw new Error("runtime line reply api url is missing");return N.callBotApi(e,t)},q=p({installRoot:t.join(y.getPath("userData"),"skills"),botId:()=>String(A?.botId||"").trim()||void 0,log:T,api:F,getRuntimeConfig:()=>({cryptoApiBaseUrl:s.gmoCryptoPrivateApiBaseUrl,fxApiBaseUrl:s.gmoFxPrivateApiBaseUrl,coinPublicBaseUrl:s.gmoCryptoPublicApiBaseUrl,fxPublicBaseUrl:s.gmoFxPublicApiBaseUrl,assetsPath:s.gmoAssetsPath,fxAssetsPath:s.gmoFxAssetsPath,coinMarginPath:s.gmoCoinMarginPath,positionsPath:s.gmoPositionsPath,positionSummaryPath:s.gmoPositionSummaryPath,fxOrderPath:s.gmoFxOrderPath,coinCloseOrderPath:s.gmoCoinCloseOrderPath,fxCloseOrderPath:s.gmoFxCloseOrderPath}),emitSkillEvent:(t,e)=>{if("trade_execution"!==e.type){if("trade_history"===e.type){const i=`trade-${Date.now()}`;O?.sendTaskEvent(i,"trade_history",{package_name:t,...e.payload&&"object"==typeof e.payload?e.payload:{}}),T("[trade] history event sent (execution)",{package_name:t,payload:e.payload})}}else D.emitTradeExecution(e.payload)}}),R=()=>{const t=q.getLoadedSkill("@ai.weget.jp/skill-gmo-fx")?.runtime;if(!t)throw new Error("GMO FX skill runtime is not installed");return t},K=()=>{const t=q.getLoadedSkill("@ai.weget.jp/skill-gmo-coin")?.runtime;if(!t)throw new Error("GMO Coin skill runtime is not installed");return t},$=()=>q.getLoadedSkill("@ai.weget.jp/skill-gmo-fx")?.runtime,J=()=>q.getLoadedSkill("@ai.weget.jp/skill-gmo-coin")?.runtime,W=()=>{const t=q.getLoadedSkill("@ai.weget.jp/skill-macro-economy")?.runtime;if(!t)throw new Error("Macro Economy skill runtime is not installed");return t},z=()=>q.getLoadedSkill("@ai.weget.jp/skill-macro-economy")?.runtime,H=t=>String(t||"").replace(/```weget_result\s*[\s\S]*?```/gi,"").replace(/```weget_pending_request\s*[\s\S]*?```/gi,"").trim(),Q=(t,e)=>{const i=t.gateway[e];if("function"!=typeof i)throw new Error(`skill runtime does not implement gateway.${e}`);return i},X=t=>{const e=L(t);return{riskDailyLossLimitJpy:Number(e.riskDailyLossLimitJpy||0),fxApiKey:String(e.fxApiKey||"").trim(),fxApiSecret:String(e.fxApiSecret||"").trim(),aiModel:String(e.aiModel||"").trim()||"gpt-5.4"}},Y=t=>{const e=L(t);return{riskDailyLossLimitJpy:Number(e.riskDailyLossLimitJpy||0),cryptoApiKey:String(e.cryptoApiKey||"").trim(),cryptoApiSecret:String(e.cryptoApiSecret||"").trim(),aiModel:String(e.aiModel||"").trim()||"gpt-5.4"}};E=()=>String(M.logOutputDir||"").trim()||t.join(y.getPath("documents"),"weget-bot-logs");const V=g({getModel:()=>{const t=$(),e=J();return M.aiModel||t?.getConfig?.().aiModel||e?.getConfig?.().aiModel||"gpt-5.4"},getWorkingDir:()=>y.getPath("userData"),getLogDir:()=>E(),emitLog:T,getGatewayContext:()=>({activeSkills:U.getManagedSkills().filter(t=>t.enabled).map(t=>({name:t.name,displayName:t.displayName,version:t.version,tools:t.tools})),hostMetadata:{aiModel:M.aiModel||"gpt-5.4",capabilities:h,userDataPath:y.getPath("userData"),platform:process.platform,gmoCoinPublicBaseUrl:s.gmoCryptoPublicApiBaseUrl,gmoFxPublicBaseUrl:s.gmoFxPublicApiBaseUrl},botId:String(A?.botId||"").trim(),platformApiBaseUrl:String(s.loginApiUrl||"").trim().replace(/\/login\/?$/i,""),accessToken:String(A?.accessToken||"").trim(),fileBridgeDir:String(ot.getContext()?.dir||"").trim(),fileBridgeToken:String(ot.getContext()?.token||"").trim()})}),Z=async t=>{const i=L(t);return M={aiModel:String(i.aiModel||M.aiModel||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(i.logOutputDir||"").trim()},await e.writeFile(x(),JSON.stringify({...M,updatedAt:(new Date).toISOString()},null,2),"utf8"),M};const tt=async()=>{const t=await q.refreshLoadedSkills();return U.setAvailableSkills(t.map(t=>t.manifest)),t},et=async t=>{const e=await tt();T("[skills] reconciled bundled skills",{desired:t.map(t=>({package_name:t.package_name,install_status:t.install_status,enabled:t.enabled,version:t.version})),bundled:e.map(t=>({package_name:t.packageName,version:t.version}))})},it=()=>{const t=U.isSkillEnabled("@ai.weget.jp/skill-gmo-fx"),e=U.isSkillEnabled("@ai.weget.jp/skill-gmo-coin"),i=$(),a=J();i&&("connected"===O?.getStatus()&&t?i.startExecutionStreams():i.stopExecutionStreams()),a&&("connected"===O?.getStatus()&&e?a.startExecutionStreams():a.stopExecutionStreams()),T("[skills] applied managed states",{active_skills:U.getActiveSkills().map(t=>t.name)})},at=async()=>{const t=N.getBotRuntimeConfigApiUrl(),e=String(A?.botId||"").trim();if(!t||!e)return;const i=await N.callBotApi(t,{bot_id:e}),a=String(i.chat_model||"").trim(),r={...a?{aiModel:a}:{}};if(0===Object.keys(r).length)return;await Z({...M,...r});const s=$(),n=J();s&&await s.saveConfig({...s.getConfig(),...r}),n&&await n.saveConfig({...n.getConfig(),...r}),T("[config] runtime config synced",{chat_model:a||""})},rt=async()=>{const t=N.getBotSkillsListApiUrl(),i=String(A?.botId||"").trim();if(!t||!i)return;const a=await N.callBotApi(t,{bot_id:i}),r=Array.isArray(a.states)?a.states.filter(t=>Boolean(t&&"object"==typeof t)).map(t=>({user_id:String(t.user_id||""),bot_id:String(t.bot_id||""),skill_id:String(t.skill_id||""),package_name:String(t.package_name||""),enabled:Boolean(t.enabled),install_status:String(t.install_status||""),version:t.version?String(t.version):null,config_json:L(t.config_json),created_at:t.created_at?String(t.created_at):void 0,updated_at:t.updated_at?String(t.updated_at):void 0})):[];U.applySnapshots(r),await(async t=>{await e.writeFile(C(),JSON.stringify({updatedAt:(new Date).toISOString(),items:t},null,2),"utf8")})(r),await et(r),it(),T("[config] skill states synced",{count:r.length,bot_id:i,enabled:r.filter(t=>t.enabled).length})},st=t=>{if("fx"===t&&!U.isSkillEnabled("@ai.weget.jp/skill-gmo-fx"))throw new Error("GMO FX skill is disabled");if("coin"===t&&!U.isSkillEnabled("@ai.weget.jp/skill-gmo-coin"))throw new Error("GMO Coin skill is disabled")},nt=()=>{if(!U.isSkillEnabled("@ai.weget.jp/skill-macro-economy"))throw new Error("Macro Economy skill is disabled")},ot=c({getMacroSnapshot:async({force:t}={})=>(nt(),W().getSnapshot({force:t})),getMacroCalendar:async({dateKey:t})=>(nt(),W().listCalendar({dateKey:t})),getMacroNews:async({dateKey:t})=>(nt(),W().listNews({dateKey:t})),emitLog:T,getWorkingDir:()=>y.getPath("userData")}),lt={"@ai.weget.jp/skill-gmo-fx":{getConfig:()=>X(R().getConfig()),saveConfig:async t=>{const e=R(),i=await e.saveConfig({...e.getConfig(),...X(t)});return"connected"===O?.getStatus()&&it(),X(i)}},"@ai.weget.jp/skill-gmo-coin":{getConfig:()=>Y(K().getConfig()),saveConfig:async t=>{const e=K(),i=await e.saveConfig({...e.getConfig(),...Y(t)});return"connected"===O?.getStatus()&&it(),Y(i)}}},gt=t=>{const e=String(t||"").trim(),i=lt[e];if(!i)throw new Error(`skill config is not managed by bot host: ${t}`);return i},ct=async()=>{await q.ensureInstallRoot(),await tt();const t=await async function(){try{const t=await e.readFile(C(),"utf8"),i=JSON.parse(t);return(Array.isArray(i?.items)?i.items:[]).filter(t=>Boolean(t&&"object"==typeof t)).map(t=>({user_id:String(t.user_id||""),bot_id:String(t.bot_id||""),skill_id:String(t.skill_id||""),package_name:String(t.package_name||""),enabled:Boolean(t.enabled),install_status:String(t.install_status||""),version:t.version?String(t.version):null,config_json:L(t.config_json),created_at:t.created_at?String(t.created_at):void 0,updated_at:t.updated_at?String(t.updated_at):void 0}))}catch(t){if("ENOENT"===t?.code)return[];throw t}}();t.length>0&&(U.applySnapshots(t),T("[skills] restored local snapshot",{count:t.length}),await et(t)),it()};O=new n({wsUrl:"wss://ws.weget.jp",taskEventApiUrl:String(s.loginApiUrl||"").trim().replace(/\/login\/?$/i,"/bot/runtime/task-event"),version:"0.2.0",capabilities:h,heartbeatSec:30,reconnectMs:3e3,reconnectMaxAttempts:10,wsPingIntervalSec:25,wsPongTimeoutSec:12},{onLog:(t,e)=>T(t,e),onStatus:t=>{D.emitBotStatus({status:t}),it()},onLineMessage:async t=>{const e=String(t.line_user_id||""),i=String(t.text||"").trim();if(B({type:"line_user",lineUserId:e,text:i}),!i)return{answer:""};try{const t=await V.runPrompt({prompt:i,contextLabel:"line_message"});return B({type:"line_assistant",lineUserId:e,text:t}),{answer:t}}catch(t){const i=t instanceof Error?t.message:String(t);T("[ai] line reply failed",{error:i});const a=`AI error: ${i}`;return B({type:"line_assistant",lineUserId:e,text:a}),{answer:a}}},onRunTask:async t=>{const e=String(t.task_id||"").trim(),i=String(t.prompt||"").trim(),a=L(t.metadata_json),r=L(t.prompt_bundle),s=L(t.runtime_config),n=String(a.source||r.source||"").trim(),o=String(a.channel||r.channel||"").trim(),l=String(t.skill_id||a.skill_id||r.skill_id||"").trim(),g=String(a.line_user_id||"").trim(),c=String(a.line_reply_token||"").trim(),d="line"===n||"line"===o||Boolean(g&&c),p="bot_host"===n,m=String(t.model||s.chat_model||"").trim(),u=String(s.system_prompt||r.system_prompt||"").trim();if(e){if(j.set(e,{cancelled:!1}),d&&g&&i&&B({type:"line_user",lineUserId:g,text:i}),O?.sendTaskEvent(e,"task_received",{prompt:i,conversation_id:String(t.conversation_id||""),continue_from_task_id:String(t.continue_from_task_id||""),ts:(new Date).toISOString()}),!i)return O?.sendTaskResult(e,"failed",{error:"prompt is required",data_json:{ts:(new Date).toISOString()}}),void j.delete(e);try{p&&B({type:"assistant_status",status:"Task received"}),l&&O?.sendSkillStatus(e,{skill_id:l,status:"running",summary:"task execution started",data_json:{ts:(new Date).toISOString()}}),O?.sendTaskEvent(e,"step_started",{step:"codex_chat",ts:(new Date).toISOString()});const s=await V.runPrompt({prompt:i,model:m||void 0,systemPrompt:u||void 0,contextLabel:`task:${e}`,promptBundle:r,metadata:a,onStatus:p?t=>B({type:"assistant_status",status:t}):void 0}),n=(t=>{const e=String(t||""),i=e.match(/```weget_result\s*([\s\S]*?)```/i);if(i?.[1])try{const t=JSON.parse(i[1].trim()),a=String(t.status||"").trim().toLowerCase(),r=t.pending_request&&"object"==typeof t.pending_request?t.pending_request:{};if("pending_request"!==a)return null;const s=String(r.type||"").trim().toLowerCase(),n=String(r.prompt||"").trim(),o=String(r.subtype||"").trim();return"input"!==s&&"approval"!==s||!n?null:{visibleText:H(e),requestType:s,prompt:n,subtype:o||void 0}}catch{return null}const a=e.match(/```weget_pending_request\s*([\s\S]*?)```/i);if(!a?.[1])return null;try{const t=JSON.parse(a[1].trim()),i=String(t.type||"").trim().toLowerCase(),r=String(t.prompt||"").trim(),s=String(t.subtype||"").trim();return"input"!==i&&"approval"!==i||!r?null:{visibleText:H(e),requestType:i,prompt:r,subtype:s||void 0}}catch{return null}})(s),o=n?.visibleText||H(s),S=j.get(e);if(S?.cancelled)return O?.sendTaskResult(e,"cancelled",{summary:"server_cancelled",data_json:{reason:"server_cancelled",ts:(new Date).toISOString()}}),void j.delete(e);if(O?.sendTaskEvent(e,"ai_output",{summary:o,ts:(new Date).toISOString()}),p&&B({type:"assistant",text:o}),d&&g&&c){B({type:"line_assistant",lineUserId:g,text:o});const i={bot_id:String(t.bot_id||A?.botId||""),line_user_id:g,line_reply_token:c,text:o};try{await G(i)}catch(t){T("[line] callback line reply failed",{error:t instanceof Error?t.message:String(t)})}O?.sendTaskEvent(e,"line_message_ai_output",{line_user_id:g,answer:o,ts:(new Date).toISOString()})}if(O?.sendTaskEvent(e,"step_finished",{step:"codex_chat",ts:(new Date).toISOString()}),n)return"approval"===n.requestType?O?.requestTaskApproval(e,{prompt:n.prompt,approval_type:n.subtype||"generic",data_json:{prompt:n.prompt,approval_type:n.subtype||"generic",ts:(new Date).toISOString()}}):O?.requestTaskInput(e,{prompt:n.prompt,input_type:n.subtype||"text",data_json:{prompt:n.prompt,input_type:n.subtype||"text",ts:(new Date).toISOString()}}),l&&O?.sendSkillStatus(e,{skill_id:l,status:"approval"===n.requestType?"waiting_approval":"waiting_input",summary:n.prompt.slice(0,200),data_json:{ts:(new Date).toISOString(),request_type:n.requestType}}),void j.delete(e);O?.sendTaskResult(e,"succeeded",{summary:o.slice(0,500),data_json:{ts:(new Date).toISOString()}}),l&&O?.sendSkillStatus(e,{skill_id:l,status:"succeeded",summary:o.slice(0,200),data_json:{ts:(new Date).toISOString()}})}catch(i){if(p){const t=`Error: ${i instanceof Error?i.message:String(i)}`;B({type:"assistant",text:t})}if(d&&g&&c){const e=`AI error: ${i instanceof Error?i.message:String(i)}`;B({type:"line_assistant",lineUserId:g,text:e});const a={bot_id:String(t.bot_id||A?.botId||""),line_user_id:g,line_reply_token:c,text:e};try{await G(a)}catch(t){T("[line] callback line reply failed",{error:t instanceof Error?t.message:String(t)})}}O?.sendTaskResult(e,"failed",{error:i instanceof Error?i.message:String(i),data_json:{ts:(new Date).toISOString()}}),l&&O?.sendSkillStatus(e,{skill_id:l,status:"failed",summary:i instanceof Error?i.message:String(i),data_json:{ts:(new Date).toISOString()}})}finally{j.delete(e)}}else T("[task] run_task missing task_id")},onTaskControl:async t=>{const e=String(t.task_id||"").trim(),i=String(t.action||"").trim();if(e&&i&&"cancel_task"===i){const i=j.get(e);if(i){i.cancelled=!0;const a=String(t.skill_id||"").trim();return a&&O?.sendSkillStatus(e,{skill_id:a,status:"cancelled",summary:"server_cancelled",data_json:{ts:(new Date).toISOString()}}),void T("[task] cancel requested",{task_id:e})}O?.sendTaskResult(e,"cancelled",{summary:"server_cancelled",data_json:{reason:"server_cancelled",ts:(new Date).toISOString()}})}},onServerConfigUpdate:async t=>{const e=Array.isArray(t.scopes)?t.scopes.map(t=>String(t||"").trim()):[],i=e.length>0?e:["runtime","skills"];i.includes("runtime")&&await at(),i.includes("skills")&&await rt()}}),o({ipcMain:w,env:{capabilities:h,loginApiUrl:s.loginApiUrl},signInWithLoginApi:N.signInWithLoginApi,saveLoginProfile:async({botId:t,email:i,password:a,remember:r})=>{if(r)await e.writeFile(v(),JSON.stringify({botId:t,email:i,password:a,remember:!0,updatedAt:(new Date).toISOString()},null,2),"utf8");else try{await e.unlink(v())}catch(t){if("ENOENT"!==t?.code)throw t}},readSavedLoginProfile:async()=>{try{const t=await e.readFile(v(),"utf8"),i=JSON.parse(t);return{botId:String(i?.botId||""),email:String(i?.email||""),password:String(i?.password||""),remember:Boolean(i?.remember)}}catch(t){if("ENOENT"===t?.code)return{botId:"",email:"",password:"",remember:!1};throw t}},getSkillConfig:t=>({config:gt(t).getConfig()}),saveSkillConfig:async(t,e)=>({config:await gt(t).saveConfig(e)}),getAiConfig:()=>({aiModel:String(M.aiModel||"gpt-5.4"),logOutputDir:String(M.logOutputDir||"")}),saveAiConfig:async t=>{const e=t&&"object"==typeof t?t:{},i=await Z({...M,...e}),a=$(),r=J(),s=a?await a.saveConfig({...a.getConfig(),...e}):null;return r&&await r.saveConfig({...r.getConfig(),...e}),{aiModel:String(i.aiModel||s?.aiModel||e.aiModel||"gpt-5.4"),logOutputDir:String(i.logOutputDir||"")}},setCurrentSession:t=>{A=t||null;const e=A?"connected":"disconnected";D.emitBotStatus({status:e})},getCurrentSession:()=>A,getActiveSkills:()=>U.getActiveSkills().map(t=>({name:t.name,displayName:t.displayName,version:t.version})),getCodexAuthStatus:()=>V.getAuthStatus(),getCodexMcpStatus:t=>V.getMcpServerStatus(t),getPlaywrightBrowserStatus:()=>V.getPlaywrightBrowserStatus(),startCodexLogin:()=>V.startLogin(),installPlaywrightMcp:()=>V.installWegetGatewayMcp(),getGatewayContextFilePath:()=>V.getGatewayContextFilePath(),runGatewaySelfTest:t=>V.runGatewaySelfTest(t),getManagedSkills:()=>U.getManagedSkills().map(t=>({...t,ui:q.getLoadedSkill(t.name)?.ui||void 0})),getMacroSnapshot:async({force:t}={})=>(nt(),W().getSnapshot({force:t})),getMacroCalendar:async({dateKey:t})=>(nt(),W().listCalendar({dateKey:t})),getMacroNews:async({dateKey:t})=>(nt(),W().listNews({dateKey:t})),emitLog:T,openGmoKlineWindow:async({symbol:t,interval:e,market:i})=>(()=>{const a="fx"===i?"fx":"coin";return st(a),("fx"===a?R().marketData:K().marketData).fetchKline({symbol:t,interval:e,market:a})})(),getGmoMarketQuotes:async({market:t,symbols:e})=>(()=>{const i="fx"===t?"fx":"coin";return st(i),("fx"===i?R().marketData:K().marketData).fetchQuotes({market:i,symbols:e})})(),getGmoSymbolRules:async({market:t})=>(()=>{const e="fx"===t?"fx":"coin";return st(e),("fx"===e?R().marketData:K().marketData).fetchSymbolRules({market:e})})(),getGmoBuyingPower:async({market:t})=>({market:t,availableJpy:await(st(t),("fx"===t?R().gateway:K().gateway).getBuyingPower(t))}),getGmoAccountMetrics:async({market:t})=>(st(t),("fx"===t?R().gateway:K().gateway).getAccountMetrics(t)),getGmoPositionSummary:async({market:t,symbol:e})=>({market:t,symbol:e,items:await(st(t),("fx"===t?R().gateway:K().gateway).getPositionSummary({market:t,symbol:e}))}),getGmoOpenPositions:async({market:t,symbol:e,page:i,prevId:a,count:r})=>({market:t,symbol:e||"",items:await(st(t),("fx"===t?R().gateway:K().gateway).getOpenPositions({market:"fx"===t?"fx":"crypto",symbol:e||void 0,page:i,prevId:a,count:r}))}),placeGmoFxOrder:async({symbol:t,side:e,size:i})=>{st("fx");return await Q(R(),"placeFxOrder")({symbol:t,side:e,size:i,executionType:"MARKET"})},placeGmoCoinOrder:async({symbol:t,side:e,size:i})=>{st("coin");return await Q(K(),"placeCoinOrder")({symbol:t,side:e,size:i,executionType:"MARKET"})},placeGmoFxCloseOrder:async({symbol:t,side:e,positionId:i,size:a})=>{st("fx");return await Q(R(),"placeFxCloseOrder")({symbol:t,side:e,positionId:i,size:a,executionType:"MARKET"})},placeGmoCoinCloseOrder:async({symbol:t,side:e,positionId:i,size:a})=>{st("coin");return await Q(K(),"placeCoinCloseOrder")({symbol:t,side:e,positionId:i,size:a,executionType:"MARKET"})},getGmoApiState:t=>{const e="fx"===t?$():J();return e?.status.getApiState()||"unknown"},testGmoApi:t=>(st(t),"fx"===t?R().status.testApi():K().status.testApi()),buildTradeStatusSnapshot:t=>(st(t),"fx"===t?R().status.buildSnapshot():K().status.buildSnapshot()),closeTradeWindows:()=>{},startWsClient:t=>{O?.start(t),at().catch(t=>{T("[config] runtime sync failed after login",{error:t instanceof Error?t.message:String(t)})}),rt().catch(t=>{T("[config] skill sync failed after login",{error:t instanceof Error?t.message:String(t)})}),it()},stopWsClient:()=>{$()?.stopExecutionStreams(),J()?.stopExecutionStreams(),z()?.stop?.(),O?.stop()},handleBotChatMessage:async({text:t})=>{const e=String(t||"").trim(),i=String(A?.botId||"").trim();if(!e)throw new Error("message is required");if(!i)throw new Error("bot session is not ready");B({type:"assistant_status",status:"Submitting task"});const a=await F.request({method:"POST",path:"/bot/tasks",body:{source:"bot_host",bot_id:i,prompt:e,title:e.slice(0,80),channel:"desktop",metadata_json:{source:"bot_host",channel:"desktop"}}}),r=L(a.dispatch_result);if(!1===r.delivered)throw new Error(`task queued but bot is offline (${String(r.status||"offline")})`);return{answer:"",taskId:String(L(a.task).task_id||"")}},writeErrorLog:async({source:t,message:e,details:i})=>{await(async({source:t,message:e,details:i})=>{await I({level:"error",source:t,message:e,details:i})})({source:t,message:e,details:i})},writeRuntimeLog:async({level:t,source:e,message:i,details:a})=>{await I({level:t,source:e,message:i,details:a})}});const dt=t.join(b,"renderer","logo.png");y.whenReady().then(async()=>{(()=>{if("darwin"!==process.platform)return;if(!i(dt))return;const t=k.createFromPath(dt);t.isEmpty()||y.dock.setIcon(t)})(),await(async()=>{try{const t=await e.readFile(x(),"utf8"),i=JSON.parse(t);M={aiModel:String(i?.aiModel||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(i?.logOutputDir||"").trim()}}catch(t){if("ENOENT"!==t?.code)throw t;M={aiModel:"gpt-5.4",logOutputDir:""}}return M})();try{await ot.ensureStarted()}catch(t){T("[gateway-file-bridge] startup failed",{error:t instanceof Error?t.message:String(t)})}try{await ct()}catch(t){T("[skills] local initialization failed",{error:t instanceof Error?t.message:String(t)})}D.createMainWindow(),y.on("activate",()=>{0===f.getAllWindows().length&&D.createMainWindow()})}),y.on("window-all-closed",()=>{$()?.stopExecutionStreams(),J()?.stopExecutionStreams(),z()?.stop?.(),ot.stop().catch(()=>{}),O?.stop(),"darwin"!==process.platform&&y.quit()});
|
|
1
|
+
import t from"node:path";import e from"node:fs/promises";import{existsSync as a}from"node:fs";import{fileURLToPath as i}from"node:url";import{createRequire as r}from"node:module";import{SYSTEM_CONFIG as s}from"./config/systemConfig.js";import{BotWsClient as n}from"./wsClient.js";import{registerIpcHandlers as o}from"./ipc/registerHandlers.js";import{createAuthApiService as l}from"./services/authApiService.js";import{createCodexService as c}from"./services/codexService.js";import{createGatewayFileBridgeService as d}from"./services/gatewayFileBridgeService.js";import{createPlatformApiClient as g}from"./platformApi.js";import{createSkillRuntimeManager as m}from"./skillRuntimeManager.js";import{parseTaskDirective as u,stripWegetControlBlocks as p}from"./services/taskDirective.js";import{createWindowManager as k}from"./services/windowManagerService.js";import{createSkillHostState as y}from"./skillHostState.js";const S=r(import.meta.url),{app:f,BrowserWindow:_,ipcMain:w,nativeImage:b}=S("electron"),v=i(import.meta.url),h=t.dirname(v),I=["trade","ai"],x=()=>t.join(f.getPath("userData"),"login-profile.json"),A=()=>t.join(f.getPath("userData"),"host-config.json"),C=()=>t.join(f.getPath("userData"),"skill-states.json");let O=null,E=null,P={aiModel:"gpt-5.4",logOutputDir:""};const M=new Map;let j=()=>t.join(f.getPath("documents"),"weget-bot-logs");const T=t=>{const e=String(t||"").toLowerCase();return e.includes("debug")||e.includes("[debug]")?"debug":e.includes("error")||e.includes("failed")||e.includes("exception")?"error":"info"},D=async({level:a,source:i,message:r,details:s,ts:n})=>{const o=j();await e.mkdir(o,{recursive:!0});const l=new Date,c=l.getFullYear(),d=String(l.getMonth()+1).padStart(2,"0"),g=String(l.getDate()).padStart(2,"0"),m=t.join(o,`${a}-${c}${d}${g}.log`),u=JSON.stringify({ts:n||l.toISOString(),level:a,source:i,message:r,details:s??null},null,0);await e.appendFile(m,`${u}\n`,"utf8")},B=k({BrowserWindow:_,preloadPath:t.join(h,"preload.cjs"),rendererDir:t.join(h,"renderer"),windowIconPath:t.join(h,"renderer","logo.png")}),q=(t,e)=>{const a=(new Date).toISOString();B.emitBotLog({message:t,data:e||null,ts:a}),D({level:T(t),source:"runtime",message:t,details:e,ts:a}).catch(()=>{})},L=t=>{B.emitChatEvent(t)},U=t=>({taskId:t.taskId,title:t.title,prompt:t.prompt,status:t.status,source:t.source,channel:t.channel,lineUserId:t.lineUserId,conversationId:t.conversationId,startedAt:t.startedAt,lastProgressAt:t.lastProgressAt,canCancel:!t.cancelled}),R=()=>{B.emitTaskRuntime({type:"snapshot",tasks:Array.from(M.values()).map(U)})},N=(t,e)=>{const a=M.get(t);if(!a)return;const i={...a,...e,taskId:t,abortController:e.abortController||a.abortController,lastProgressAt:e.lastProgressAt||(new Date).toISOString()};M.set(t,i),B.emitTaskRuntime({type:"upsert",task:U(i)})},F=t=>{M.has(t)&&(M.delete(t),B.emitTaskRuntime({type:"remove",taskId:t}))},G=(t,e)=>({summary:t?.cancelReason||e,data_json:{reason:t?.cancelReason||e,requested_by_task_id:t?.cancelRequestedByTaskId||null,requested_by_action:t?.cancelRequestedByAction||null,ts:(new Date).toISOString()}}),K=async({taskId:t,directive:e,promptBundle:a})=>{const i=$(a.arbitration_context),r=$(i.active_task),s=String(e.targetTaskId||r.task_id||"").trim();if("cancel_running_task"===e.action){const a=await(async({taskId:t,reason:e,requestedByTaskId:a,requestedByAction:i})=>{const r=String(t||"").trim();if(!r)return{ok:!1,mode:"invalid",error:"task_id is required"};const s=M.get(r);if(s)return s.cancelled=!0,s.cancelReason=e,s.cancelRequestedByTaskId=a,s.cancelRequestedByAction=i,s.abortController.abort(),N(r,{status:"Cancelling..."}),q("[task] cancellation requested",{task_id:r,mode:"local",reason:e,requested_by_task_id:a||"",requested_by_action:i||""}),{ok:!0,mode:"local"};try{return await z.request({method:"POST",path:"/bot/tasks/cancel",body:{task_id:r}}),q("[task] cancellation requested",{task_id:r,mode:"platform",reason:e,requested_by_task_id:a||"",requested_by_action:i||""}),{ok:!0,mode:"platform"}}catch(t){return{ok:!1,mode:"platform",error:t instanceof Error?t.message:String(t)}}})({taskId:s,reason:e.reason||"arbitration_cancelled",requestedByTaskId:t,requestedByAction:e.action});return E?.sendTaskEvent(t,"task_control_action",{action:e.action,target_task_id:s||null,ok:a.ok,mode:a.mode,error:"error"in a?a.error:null,ts:(new Date).toISOString()}),a}return E?.sendTaskEvent(t,"task_control_action",{action:e.action,target_task_id:s||null,ok:!0,mode:"advisory",ts:(new Date).toISOString()}),{ok:!0,mode:"advisory"}},$=t=>t&&"object"==typeof t?t:{},W=y({log:q}),J=l({getEnv:()=>({loginApiUrl:s.loginApiUrl,botUploadApiUrl:s.botUploadApiUrl}),emitLog:q,getCurrentSession:()=>O}),z=g({getBaseUrl:()=>String(s.loginApiUrl||"").trim().replace(/\/login\/?$/i,""),getSession:()=>O,log:q}),H=async t=>{const e=(()=>{const t=String(s.loginApiUrl||"").trim();return t?t.replace(/\/login\/?$/i,"/bot/runtime/line-reply"):""})();if(!e)throw new Error("runtime line reply api url is missing");return J.callBotApi(e,t)},Q=m({installRoot:t.join(f.getPath("userData"),"skills"),botId:()=>String(O?.botId||"").trim()||void 0,log:q,api:z,getRuntimeConfig:()=>({cryptoApiBaseUrl:s.gmoCryptoPrivateApiBaseUrl,fxApiBaseUrl:s.gmoFxPrivateApiBaseUrl,coinPublicBaseUrl:s.gmoCryptoPublicApiBaseUrl,fxPublicBaseUrl:s.gmoFxPublicApiBaseUrl,assetsPath:s.gmoAssetsPath,fxAssetsPath:s.gmoFxAssetsPath,coinMarginPath:s.gmoCoinMarginPath,positionsPath:s.gmoPositionsPath,positionSummaryPath:s.gmoPositionSummaryPath,fxOrderPath:s.gmoFxOrderPath,coinCloseOrderPath:s.gmoCoinCloseOrderPath,fxCloseOrderPath:s.gmoFxCloseOrderPath}),emitSkillEvent:(t,e)=>{if("trade_execution"!==e.type){if("trade_history"===e.type){const a=`trade-${Date.now()}`;E?.sendTaskEvent(a,"trade_history",{package_name:t,...e.payload&&"object"==typeof e.payload?e.payload:{}}),q("[trade] history event sent (execution)",{package_name:t,payload:e.payload})}}else B.emitTradeExecution(e.payload)}}),X=()=>{const t=Q.getLoadedSkill("@ai.weget.jp/skill-gmo-fx")?.runtime;if(!t)throw new Error("GMO FX skill runtime is not installed");return t},Y=()=>{const t=Q.getLoadedSkill("@ai.weget.jp/skill-gmo-coin")?.runtime;if(!t)throw new Error("GMO Coin skill runtime is not installed");return t},V=()=>Q.getLoadedSkill("@ai.weget.jp/skill-gmo-fx")?.runtime,Z=()=>Q.getLoadedSkill("@ai.weget.jp/skill-gmo-coin")?.runtime,tt=()=>{const t=Q.getLoadedSkill("@ai.weget.jp/skill-macro-economy")?.runtime;if(!t)throw new Error("Macro Economy skill runtime is not installed");return t},et=()=>Q.getLoadedSkill("@ai.weget.jp/skill-macro-economy")?.runtime,at=({isLineTask:t,status:e})=>{const a=String(e||"").trim();return a?t?`LINE task: ${a}`:a:""},it=(t,e)=>{const a=t.gateway[e];if("function"!=typeof a)throw new Error(`skill runtime does not implement gateway.${e}`);return a},rt=t=>{const e=$(t);return{riskDailyLossLimitJpy:Number(e.riskDailyLossLimitJpy||0),fxApiKey:String(e.fxApiKey||"").trim(),fxApiSecret:String(e.fxApiSecret||"").trim(),aiModel:String(e.aiModel||"").trim()||"gpt-5.4"}},st=t=>{const e=$(t);return{riskDailyLossLimitJpy:Number(e.riskDailyLossLimitJpy||0),cryptoApiKey:String(e.cryptoApiKey||"").trim(),cryptoApiSecret:String(e.cryptoApiSecret||"").trim(),aiModel:String(e.aiModel||"").trim()||"gpt-5.4"}};j=()=>String(P.logOutputDir||"").trim()||t.join(f.getPath("documents"),"weget-bot-logs");const nt=c({getModel:()=>{const t=V(),e=Z();return P.aiModel||t?.getConfig?.().aiModel||e?.getConfig?.().aiModel||"gpt-5.4"},getWorkingDir:()=>f.getPath("userData"),getLogDir:()=>j(),emitLog:q,getGatewayContext:()=>({activeSkills:W.getManagedSkills().filter(t=>t.enabled).map(t=>({name:t.name,displayName:t.displayName,version:t.version,tools:t.tools})),hostMetadata:{aiModel:P.aiModel||"gpt-5.4",capabilities:I,userDataPath:f.getPath("userData"),platform:process.platform,gmoCoinPublicBaseUrl:s.gmoCryptoPublicApiBaseUrl,gmoFxPublicBaseUrl:s.gmoFxPublicApiBaseUrl},botId:String(O?.botId||"").trim(),platformApiBaseUrl:String(s.loginApiUrl||"").trim().replace(/\/login\/?$/i,""),accessToken:String(O?.accessToken||"").trim(),fileBridgeDir:String(kt.getContext()?.dir||"").trim(),fileBridgeToken:String(kt.getContext()?.token||"").trim()})}),ot=async t=>{const a=$(t);return P={aiModel:String(a.aiModel||P.aiModel||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(a.logOutputDir||"").trim()},await e.writeFile(A(),JSON.stringify({...P,updatedAt:(new Date).toISOString()},null,2),"utf8"),P};const lt=async()=>{const t=await Q.refreshLoadedSkills();return W.setAvailableSkills(t.map(t=>t.manifest)),t},ct=async t=>{const e=await lt();q("[skills] reconciled bundled skills",{desired:t.map(t=>({package_name:t.package_name,install_status:t.install_status,enabled:t.enabled,version:t.version})),bundled:e.map(t=>({package_name:t.packageName,version:t.version}))})},dt=()=>{const t=W.isSkillEnabled("@ai.weget.jp/skill-gmo-fx"),e=W.isSkillEnabled("@ai.weget.jp/skill-gmo-coin"),a=V(),i=Z();a&&("connected"===E?.getStatus()&&t?a.startExecutionStreams():a.stopExecutionStreams()),i&&("connected"===E?.getStatus()&&e?i.startExecutionStreams():i.stopExecutionStreams()),q("[skills] applied managed states",{active_skills:W.getActiveSkills().map(t=>t.name)})},gt=async()=>{const t=J.getBotRuntimeConfigApiUrl(),e=String(O?.botId||"").trim();if(!t||!e)return;const a=await J.callBotApi(t,{bot_id:e}),i=String(a.chat_model||"").trim(),r={...i?{aiModel:i}:{}};if(0===Object.keys(r).length)return;await ot({...P,...r});const s=V(),n=Z();s&&await s.saveConfig({...s.getConfig(),...r}),n&&await n.saveConfig({...n.getConfig(),...r}),q("[config] runtime config synced",{chat_model:i||""})},mt=async()=>{const t=J.getBotSkillsListApiUrl(),a=String(O?.botId||"").trim();if(!t||!a)return;const i=await J.callBotApi(t,{bot_id:a}),r=Array.isArray(i.states)?i.states.filter(t=>Boolean(t&&"object"==typeof t)).map(t=>({user_id:String(t.user_id||""),bot_id:String(t.bot_id||""),skill_id:String(t.skill_id||""),package_name:String(t.package_name||""),enabled:Boolean(t.enabled),install_status:String(t.install_status||""),version:t.version?String(t.version):null,config_json:$(t.config_json),created_at:t.created_at?String(t.created_at):void 0,updated_at:t.updated_at?String(t.updated_at):void 0})):[];W.applySnapshots(r),await(async t=>{await e.writeFile(C(),JSON.stringify({updatedAt:(new Date).toISOString(),items:t},null,2),"utf8")})(r),await ct(r),dt(),q("[config] skill states synced",{count:r.length,bot_id:a,enabled:r.filter(t=>t.enabled).length})},ut=t=>{if("fx"===t&&!W.isSkillEnabled("@ai.weget.jp/skill-gmo-fx"))throw new Error("GMO FX skill is disabled");if("coin"===t&&!W.isSkillEnabled("@ai.weget.jp/skill-gmo-coin"))throw new Error("GMO Coin skill is disabled")},pt=()=>{if(!W.isSkillEnabled("@ai.weget.jp/skill-macro-economy"))throw new Error("Macro Economy skill is disabled")},kt=d({getMacroSnapshot:async({force:t}={})=>(pt(),tt().getSnapshot({force:t})),getMacroCalendar:async({dateKey:t})=>(pt(),tt().listCalendar({dateKey:t})),getMacroNews:async({dateKey:t})=>(pt(),tt().listNews({dateKey:t})),emitLog:q,getWorkingDir:()=>f.getPath("userData")}),yt={"@ai.weget.jp/skill-gmo-fx":{getConfig:()=>rt(X().getConfig()),saveConfig:async t=>{const e=X(),a=await e.saveConfig({...e.getConfig(),...rt(t)});return"connected"===E?.getStatus()&&dt(),rt(a)}},"@ai.weget.jp/skill-gmo-coin":{getConfig:()=>st(Y().getConfig()),saveConfig:async t=>{const e=Y(),a=await e.saveConfig({...e.getConfig(),...st(t)});return"connected"===E?.getStatus()&&dt(),st(a)}}},St=t=>{const e=String(t||"").trim(),a=yt[e];if(!a)throw new Error(`skill config is not managed by bot host: ${t}`);return a},ft=async()=>{await Q.ensureInstallRoot(),await lt();const t=await async function(){try{const t=await e.readFile(C(),"utf8"),a=JSON.parse(t);return(Array.isArray(a?.items)?a.items:[]).filter(t=>Boolean(t&&"object"==typeof t)).map(t=>({user_id:String(t.user_id||""),bot_id:String(t.bot_id||""),skill_id:String(t.skill_id||""),package_name:String(t.package_name||""),enabled:Boolean(t.enabled),install_status:String(t.install_status||""),version:t.version?String(t.version):null,config_json:$(t.config_json),created_at:t.created_at?String(t.created_at):void 0,updated_at:t.updated_at?String(t.updated_at):void 0}))}catch(t){if("ENOENT"===t?.code)return[];throw t}}();t.length>0&&(W.applySnapshots(t),q("[skills] restored local snapshot",{count:t.length}),await ct(t)),dt()};E=new n({wsUrl:"wss://ws.weget.jp",taskEventApiUrl:String(s.loginApiUrl||"").trim().replace(/\/login\/?$/i,"/bot/runtime/task-event"),version:"0.2.0",capabilities:I,heartbeatSec:30,reconnectMs:3e3,reconnectMaxAttempts:10,wsPingIntervalSec:25,wsPongTimeoutSec:12},{onLog:(t,e)=>q(t,e),onStatus:t=>{B.emitBotStatus({status:t}),dt()},onLineMessage:async t=>{const e=String(t.line_user_id||""),a=String(t.text||"").trim();if(L({type:"line_user",lineUserId:e,text:a}),!a)return{answer:""};try{const t=await nt.runPrompt({prompt:a,contextLabel:"line_message"});return L({type:"line_assistant",lineUserId:e,text:t}),{answer:t}}catch(t){const a=t instanceof Error?t.message:String(t);q("[ai] line reply failed",{error:a});const i=`AI error: ${a}`;return L({type:"line_assistant",lineUserId:e,text:i}),{answer:i}}},onRunTask:async t=>{const e=String(t.task_id||"").trim(),a=String(t.prompt||"").trim(),i=$(t.metadata_json),r=$(t.prompt_bundle),s=$(t.runtime_config),n=String(i.source||r.source||"").trim(),o=String(i.channel||r.channel||"").trim(),l=String(t.skill_id||i.skill_id||r.skill_id||"").trim(),c=String(t.bot_id||O?.botId||"").trim(),d=String(i.line_user_id||"").trim(),g=String(i.line_reply_token||"").trim(),m="line"===n||"line"===o||Boolean(d&&g),k="bot_host"===n,y=String(t.model||s.chat_model||"").trim(),S=String(s.system_prompt||r.system_prompt||"").trim();if(!e)return void q("[task] run_task missing task_id");const f=(new Date).toISOString();if(M.set(e,{cancelled:!1,abortController:new AbortController,taskId:e,title:String(t.title||a.slice(0,120)||e).trim(),prompt:a,status:"Task received",source:n||"unknown",channel:o||"",lineUserId:d||void 0,conversationId:String(t.conversation_id||"").trim()||void 0,startedAt:f,lastProgressAt:f}),R(),m&&d&&a&&L({type:"line_user",lineUserId:d,text:a}),E?.sendTaskEvent(e,"task_received",{prompt:a,conversation_id:String(t.conversation_id||""),continue_from_task_id:String(t.continue_from_task_id||""),ts:(new Date).toISOString()}),!a)return E?.sendTaskResult(e,"failed",{error:"prompt is required",data_json:{ts:(new Date).toISOString()}}),void M.delete(e);try{if(k||m){const t=at({isLineTask:m,status:"Task received"});L({type:"assistant_status",status:t}),N(e,{status:t})}l&&E?.sendSkillStatus(e,{skill_id:l,status:"running",summary:"task execution started",data_json:{ts:(new Date).toISOString()}}),E?.sendTaskEvent(e,"step_started",{step:"codex_chat",ts:(new Date).toISOString()});const f=await nt.runPrompt({prompt:a,model:y||void 0,systemPrompt:S||void 0,contextLabel:`task:${e}`,promptBundle:r,metadata:i,onStatus:k||m?t=>{const a=at({isLineTask:m,status:t});L({type:"assistant_status",status:a}),N(e,{status:a})}:void 0,abortSignal:M.get(e)?.abortController.signal}),_=u(f),w=_.pendingRequest,b=_.arbitrationAction,v=_.visibleText||p(f),h=M.get(e);if(h?.cancelled)return E?.sendTaskResult(e,"cancelled",G(h,"server_cancelled")),void F(e);let I=null;if(b){if(I=await K({taskId:e,directive:b,promptBundle:r}),!I.ok&&"cancel_running_task"===b.action)throw new Error("error"in I&&I.error||"failed to cancel running task");if("start_new_task"===b.action){const t=await(async({taskId:t,prompt:e,visibleAnswer:a,source:i,channel:r,botId:s,skillId:n,runtimeConfig:o,promptBundle:l,metadata:c})=>{const d=String(l.conversation_id||"").trim();if(!d)return{ok:!1,error:"conversation_id is required for start_new_task"};const g={...c,arbitration_parent_task_id:t,arbitration_decision:"start_new_task",arbitration_visible_answer:a},m=await z.request({method:"POST",path:"/bot/tasks",body:{source:i||null,channel:r||null,bot_id:s,skill_id:n||void 0,prompt:e,title:e.slice(0,80),conversation_id:d,continue_from_task_id:t,model:String(o.chat_model||"").trim()||void 0,sandbox_mode:String(o.sandbox_mode||"").trim()||void 0,approval_policy:String(o.approval_policy||"").trim()||void 0,metadata_json:g}});return{ok:!0,taskId:String($(m.task).task_id||"").trim()||null,dispatch:$(m.dispatch_result)}})({taskId:e,prompt:a,visibleAnswer:v,source:n,channel:o,botId:c,skillId:l||void 0,runtimeConfig:s,promptBundle:r,metadata:i});if(!t.ok)throw new Error(t.error||"failed to queue arbitrated new task");I={ok:!0,mode:"local"===I.mode||"platform"===I.mode||"advisory"===I.mode?I.mode:"advisory",queuedTaskId:t.taskId,dispatch:t.dispatch}}}if(E?.sendTaskEvent(e,"ai_output",{summary:v,action:b?.action||null,ts:(new Date).toISOString()}),k&&L({type:"assistant",text:v}),m&&d&&g){L({type:"line_assistant",lineUserId:d,text:v});const a={bot_id:String(t.bot_id||O?.botId||""),line_user_id:d,line_reply_token:g,text:v};try{await H(a)}catch(t){q("[line] callback line reply failed",{error:t instanceof Error?t.message:String(t)})}E?.sendTaskEvent(e,"line_message_ai_output",{line_user_id:d,answer:v,ts:(new Date).toISOString()})}if(E?.sendTaskEvent(e,"step_finished",{step:"codex_chat",action:b?.action||null,ts:(new Date).toISOString()}),w)return"approval"===w.requestType?E?.requestTaskApproval(e,{prompt:w.prompt,approval_type:w.subtype||"generic",data_json:{prompt:w.prompt,approval_type:w.subtype||"generic",ts:(new Date).toISOString()}}):E?.requestTaskInput(e,{prompt:w.prompt,input_type:w.subtype||"text",data_json:{prompt:w.prompt,input_type:w.subtype||"text",ts:(new Date).toISOString()}}),l&&E?.sendSkillStatus(e,{skill_id:l,status:"approval"===w.requestType?"waiting_approval":"waiting_input",summary:w.prompt.slice(0,200),data_json:{ts:(new Date).toISOString(),request_type:w.requestType}}),void F(e);E?.sendTaskResult(e,"succeeded",{summary:v.slice(0,500),data_json:{ts:(new Date).toISOString(),action:b?.action||null,target_task_id:b?.targetTaskId||null,action_reason:b?.reason||null,action_mode:I?.mode||null,queued_task_id:I?.queuedTaskId||null}}),l&&E?.sendSkillStatus(e,{skill_id:l,status:"succeeded",summary:v.slice(0,200),data_json:{ts:(new Date).toISOString()}})}catch(a){if(k){const t=`Error: ${a instanceof Error?a.message:String(a)}`;L({type:"assistant",text:t})}const i=M.get(e);if(i?.cancelled)return E?.sendTaskResult(e,"cancelled",G(i,"server_cancelled")),void F(e);if(m&&d&&g){const e=`AI error: ${a instanceof Error?a.message:String(a)}`;L({type:"line_assistant",lineUserId:d,text:e});const i={bot_id:String(t.bot_id||O?.botId||""),line_user_id:d,line_reply_token:g,text:e};try{await H(i)}catch(t){q("[line] callback line reply failed",{error:t instanceof Error?t.message:String(t)})}}E?.sendTaskResult(e,"failed",{error:a instanceof Error?a.message:String(a),data_json:{ts:(new Date).toISOString()}}),l&&E?.sendSkillStatus(e,{skill_id:l,status:"failed",summary:a instanceof Error?a.message:String(a),data_json:{ts:(new Date).toISOString()}})}finally{F(e)}},onTaskControl:async t=>{const e=String(t.task_id||"").trim(),a=String(t.action||"").trim();if(e&&a&&"cancel_task"===a){const a=M.get(e);if(a){a.cancelled=!0,a.cancelReason="server_cancelled",a.cancelRequestedByAction="cancel_task",a.abortController.abort();const i=String(t.skill_id||"").trim();return i&&E?.sendSkillStatus(e,{skill_id:i,status:"cancelled",summary:"server_cancelled",data_json:{ts:(new Date).toISOString()}}),void q("[task] cancel requested",{task_id:e})}E?.sendTaskResult(e,"cancelled",G(void 0,"server_cancelled"))}},onServerConfigUpdate:async t=>{const e=Array.isArray(t.scopes)?t.scopes.map(t=>String(t||"").trim()):[],a=e.length>0?e:["runtime","skills"];a.includes("runtime")&&await gt(),a.includes("skills")&&await mt()}}),o({ipcMain:w,env:{capabilities:I,loginApiUrl:s.loginApiUrl},signInWithLoginApi:J.signInWithLoginApi,saveLoginProfile:async({botId:t,email:a,password:i,remember:r})=>{if(r)await e.writeFile(x(),JSON.stringify({botId:t,email:a,password:i,remember:!0,updatedAt:(new Date).toISOString()},null,2),"utf8");else try{await e.unlink(x())}catch(t){if("ENOENT"!==t?.code)throw t}},readSavedLoginProfile:async()=>{try{const t=await e.readFile(x(),"utf8"),a=JSON.parse(t);return{botId:String(a?.botId||""),email:String(a?.email||""),password:String(a?.password||""),remember:Boolean(a?.remember)}}catch(t){if("ENOENT"===t?.code)return{botId:"",email:"",password:"",remember:!1};throw t}},getSkillConfig:t=>({config:St(t).getConfig()}),saveSkillConfig:async(t,e)=>({config:await St(t).saveConfig(e)}),getAiConfig:()=>({aiModel:String(P.aiModel||"gpt-5.4"),logOutputDir:String(P.logOutputDir||"")}),saveAiConfig:async t=>{const e=t&&"object"==typeof t?t:{},a=await ot({...P,...e}),i=V(),r=Z(),s=i?await i.saveConfig({...i.getConfig(),...e}):null;return r&&await r.saveConfig({...r.getConfig(),...e}),{aiModel:String(a.aiModel||s?.aiModel||e.aiModel||"gpt-5.4"),logOutputDir:String(a.logOutputDir||"")}},setCurrentSession:t=>{O=t||null;const e=O?"connected":"disconnected";B.emitBotStatus({status:e})},getCurrentSession:()=>O,getActiveSkills:()=>W.getActiveSkills().map(t=>({name:t.name,displayName:t.displayName,version:t.version})),getCodexAuthStatus:()=>nt.getAuthStatus(),getCodexMcpStatus:t=>nt.getMcpServerStatus(t),getPlaywrightBrowserStatus:()=>nt.getPlaywrightBrowserStatus(),startCodexLogin:()=>nt.startLogin(),installPlaywrightMcp:()=>nt.installWegetGatewayMcp(),getGatewayContextFilePath:()=>nt.getGatewayContextFilePath(),runGatewaySelfTest:t=>nt.runGatewaySelfTest(t),getManagedSkills:()=>W.getManagedSkills().map(t=>({...t,ui:Q.getLoadedSkill(t.name)?.ui||void 0})),getMacroSnapshot:async({force:t}={})=>(pt(),tt().getSnapshot({force:t})),getMacroCalendar:async({dateKey:t})=>(pt(),tt().listCalendar({dateKey:t})),getMacroNews:async({dateKey:t})=>(pt(),tt().listNews({dateKey:t})),emitLog:q,openGmoKlineWindow:async({symbol:t,interval:e,market:a})=>(()=>{const i="fx"===a?"fx":"coin";return ut(i),("fx"===i?X().marketData:Y().marketData).fetchKline({symbol:t,interval:e,market:i})})(),getGmoMarketQuotes:async({market:t,symbols:e})=>(()=>{const a="fx"===t?"fx":"coin";return ut(a),("fx"===a?X().marketData:Y().marketData).fetchQuotes({market:a,symbols:e})})(),getGmoSymbolRules:async({market:t})=>(()=>{const e="fx"===t?"fx":"coin";return ut(e),("fx"===e?X().marketData:Y().marketData).fetchSymbolRules({market:e})})(),getGmoBuyingPower:async({market:t})=>({market:t,availableJpy:await(ut(t),("fx"===t?X().gateway:Y().gateway).getBuyingPower(t))}),getGmoAccountMetrics:async({market:t})=>(ut(t),("fx"===t?X().gateway:Y().gateway).getAccountMetrics(t)),getGmoPositionSummary:async({market:t,symbol:e})=>({market:t,symbol:e,items:await(ut(t),("fx"===t?X().gateway:Y().gateway).getPositionSummary({market:t,symbol:e}))}),getGmoOpenPositions:async({market:t,symbol:e,page:a,prevId:i,count:r})=>({market:t,symbol:e||"",items:await(ut(t),("fx"===t?X().gateway:Y().gateway).getOpenPositions({market:"fx"===t?"fx":"crypto",symbol:e||void 0,page:a,prevId:i,count:r}))}),placeGmoFxOrder:async({symbol:t,side:e,size:a})=>{ut("fx");return await it(X(),"placeFxOrder")({symbol:t,side:e,size:a,executionType:"MARKET"})},placeGmoCoinOrder:async({symbol:t,side:e,size:a})=>{ut("coin");return await it(Y(),"placeCoinOrder")({symbol:t,side:e,size:a,executionType:"MARKET"})},placeGmoFxCloseOrder:async({symbol:t,side:e,positionId:a,size:i})=>{ut("fx");return await it(X(),"placeFxCloseOrder")({symbol:t,side:e,positionId:a,size:i,executionType:"MARKET"})},placeGmoCoinCloseOrder:async({symbol:t,side:e,positionId:a,size:i})=>{ut("coin");return await it(Y(),"placeCoinCloseOrder")({symbol:t,side:e,positionId:a,size:i,executionType:"MARKET"})},getGmoApiState:t=>{const e="fx"===t?V():Z();return e?.status.getApiState()||"unknown"},testGmoApi:t=>(ut(t),"fx"===t?X().status.testApi():Y().status.testApi()),buildTradeStatusSnapshot:t=>(ut(t),"fx"===t?X().status.buildSnapshot():Y().status.buildSnapshot()),closeTradeWindows:()=>{},startWsClient:t=>{E?.start(t),gt().catch(t=>{q("[config] runtime sync failed after login",{error:t instanceof Error?t.message:String(t)})}),mt().catch(t=>{q("[config] skill sync failed after login",{error:t instanceof Error?t.message:String(t)})}),dt()},stopWsClient:()=>{V()?.stopExecutionStreams(),Z()?.stopExecutionStreams(),et()?.stop?.(),E?.stop(),R()},handleBotChatMessage:async({text:t})=>{const e=String(t||"").trim(),a=String(O?.botId||"").trim();if(!e)throw new Error("message is required");if(!a)throw new Error("bot session is not ready");L({type:"assistant_status",status:"Submitting task"});const i=await z.request({method:"POST",path:"/bot/tasks",body:{source:"bot_host",bot_id:a,prompt:e,title:e.slice(0,80),channel:"desktop",metadata_json:{source:"bot_host",channel:"desktop"}}}),r=$(i.dispatch_result);if(!1===r.delivered)throw new Error(`task queued but bot is offline (${String(r.status||"offline")})`);return{answer:"",taskId:String($(i.task).task_id||"")}},getActiveTaskRuntimes:()=>Array.from(M.values()).map(U),cancelActiveTaskRuntime:async t=>{const e=M.get(String(t||"").trim());return e?(e.cancelled=!0,e.cancelReason="local_cancelled",e.cancelRequestedByAction="cancel_task",e.abortController.abort(),N(e.taskId,{status:"Cancelling..."}),q("[task] local cancel requested",{task_id:e.taskId}),{ok:!0}):{ok:!1,error:"task not found"}},writeErrorLog:async({source:t,message:e,details:a})=>{await(async({source:t,message:e,details:a})=>{await D({level:"error",source:t,message:e,details:a})})({source:t,message:e,details:a})},writeRuntimeLog:async({level:t,source:e,message:a,details:i})=>{await D({level:t,source:e,message:a,details:i})}});const _t=t.join(h,"renderer","logo.png");f.whenReady().then(async()=>{(()=>{if("darwin"!==process.platform)return;if(!a(_t))return;const t=b.createFromPath(_t);t.isEmpty()||f.dock.setIcon(t)})(),await(async()=>{try{const t=await e.readFile(A(),"utf8"),a=JSON.parse(t);P={aiModel:String(a?.aiModel||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(a?.logOutputDir||"").trim()}}catch(t){if("ENOENT"!==t?.code)throw t;P={aiModel:"gpt-5.4",logOutputDir:""}}return P})();try{await kt.ensureStarted()}catch(t){q("[gateway-file-bridge] startup failed",{error:t instanceof Error?t.message:String(t)})}try{await ft()}catch(t){q("[skills] local initialization failed",{error:t instanceof Error?t.message:String(t)})}B.createMainWindow(),R(),f.on("activate",()=>{0===_.getAllWindows().length&&(B.createMainWindow(),R())})}),f.on("window-all-closed",()=>{V()?.stopExecutionStreams(),Z()?.stopExecutionStreams(),et()?.stop?.(),kt.stop().catch(()=>{}),E?.stop(),"darwin"!==process.platform&&f.quit()});
|
package/dist/src/preload.cjs
CHANGED
|
@@ -37,6 +37,8 @@ contextBridge.exposeInMainWorld('botApi', {
|
|
|
37
37
|
testGmoApi: ({ market } = {}) => ipcRenderer.invoke('trade:testGmoApi', { market }),
|
|
38
38
|
getStatusSnapshot: ({ market } = {}) => ipcRenderer.invoke('trade:getStatusSnapshot', { market }),
|
|
39
39
|
sendChat: (message) => ipcRenderer.invoke('chat:send', { message }),
|
|
40
|
+
getActiveTasks: () => ipcRenderer.invoke('task:getActiveRuntimes'),
|
|
41
|
+
cancelActiveTask: (taskId) => ipcRenderer.invoke('task:cancelActiveRuntime', { taskId }),
|
|
40
42
|
writeErrorLog: ({ source, message, details }) => ipcRenderer.invoke('log:writeError', { source, message, details }),
|
|
41
43
|
writeLog: ({ level, source, message, details }) => ipcRenderer.invoke('log:write', { level, source, message, details }),
|
|
42
44
|
onLog: (handler) => {
|
|
@@ -59,4 +61,9 @@ contextBridge.exposeInMainWorld('botApi', {
|
|
|
59
61
|
ipcRenderer.on('trade:execution', listener);
|
|
60
62
|
return () => ipcRenderer.removeListener('trade:execution', listener);
|
|
61
63
|
},
|
|
64
|
+
onTaskRuntime: (handler) => {
|
|
65
|
+
const listener = (_event, payload) => handler(payload);
|
|
66
|
+
ipcRenderer.on('task:runtime', listener);
|
|
67
|
+
return () => ipcRenderer.removeListener('task:runtime', listener);
|
|
68
|
+
},
|
|
62
69
|
});
|
package/dist/src/renderer/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=document.getElementById("login-form"),t=document.getElementById("login-screen"),n=document.getElementById("login-notice"),i=document.getElementById("app-shell"),o=document.getElementById("sidebar-nav"),a=document.getElementById("sidebar-toggle-btn"),r=document.getElementById("user-notice"),s=document.getElementById("bot-id"),l=document.getElementById("email"),c=document.getElementById("password"),d=document.getElementById("remember-me"),m=document.getElementById("status"),u=document.getElementById("session"),g=document.getElementById("runtime-info"),p=document.getElementById("skill-state-list"),f=document.getElementById("tab-btn-skill-gmo-coin"),y=document.getElementById("tab-btn-skill-gmo-fx"),w=document.getElementById("tab-btn-skill-browser"),b=document.getElementById("tab-btn-skill-macro-economy"),h=document.getElementById("coin-skill-title"),k=document.getElementById("fx-skill-title"),x=document.getElementById("browser-skill-title"),S=document.getElementById("macro-skill-title"),E=document.getElementById("coin-skill-package"),v=document.getElementById("fx-skill-package"),C=document.getElementById("browser-skill-package"),N=document.getElementById("macro-skill-package"),I=document.getElementById("coin-skill-enabled"),A=document.getElementById("fx-skill-enabled"),L=document.getElementById("browser-skill-enabled"),B=document.getElementById("macro-skill-enabled"),$=document.getElementById("coin-skill-version"),M=document.getElementById("fx-skill-version"),T=document.getElementById("browser-skill-version"),P=document.getElementById("macro-skill-version"),U=document.getElementById("browser-skill-cards"),D=document.getElementById("browser-skill-tools"),F=document.getElementById("macro-snapshot-as-of"),j=document.getElementById("macro-fear-greed-score"),_=document.getElementById("macro-snapshot-hint"),z=document.getElementById("macro-coin-cards"),O=document.getElementById("macro-fx-cards"),H=document.getElementById("macro-coin-heatmap-treemap"),J=document.getElementById("macro-fx-heatmap-treemap"),q=document.getElementById("macro-calendar-body"),G=document.getElementById("macro-news-body"),K=document.getElementById("macro-date-key"),Y=document.getElementById("macro-refresh-snapshot-btn"),R=document.getElementById("macro-refresh-day-btn"),W=document.getElementById("browser-chromium-status"),X=document.getElementById("browser-chromium-detail"),V=document.getElementById("browser-prereq-hint"),Q=document.getElementById("browser-refresh-playwright-mcp-btn"),Z=document.getElementById("logs"),ee=document.getElementById("gateway-codex-auth"),te=document.getElementById("gateway-mcp-status"),ne=document.getElementById("gateway-browser-status"),ie=document.getElementById("gateway-context-file"),oe=document.getElementById("gateway-detail"),ae=document.getElementById("gateway-install-btn"),re=document.getElementById("gateway-refresh-btn"),se=document.getElementById("gateway-skill-grid"),le=document.getElementById("messages"),ce=document.getElementById("chat-form"),de=document.getElementById("chat-input"),me=document.getElementById("send-btn"),ue=document.getElementById("fx-risk-daily-loss-limit-jpy"),ge=document.getElementById("coin-risk-daily-loss-limit-jpy"),pe=document.getElementById("crypto-api-key"),fe=document.getElementById("crypto-api-secret"),ye=document.getElementById("fx-api-key"),we=document.getElementById("fx-api-secret"),be=document.getElementById("toggle-crypto-secret-btn"),he=document.getElementById("toggle-fx-secret-btn"),ke=document.getElementById("save-fx-config-btn"),xe=document.getElementById("save-coin-config-btn"),Se=document.getElementById("fx-config-title"),Ee=document.getElementById("coin-config-title"),ve=document.getElementById("fx-config-label-riskDailyLossLimitJpy"),Ce=document.getElementById("fx-config-label-fxApiKey"),Ne=document.getElementById("fx-config-label-fxApiSecret"),Ie=document.getElementById("coin-config-label-riskDailyLossLimitJpy"),Ae=document.getElementById("coin-config-label-cryptoApiKey"),Le=document.getElementById("coin-config-label-cryptoApiSecret"),Be=document.getElementById("ai-model"),$e=document.getElementById("host-log-output-dir"),Me=document.getElementById("save-ai-config-btn"),Te=document.getElementById("codex-login-btn"),Pe=document.getElementById("codex-copy-login-btn"),Ue=document.getElementById("codex-refresh-auth-btn"),De=document.getElementById("codex-login-command"),Fe=document.getElementById("logout-btn"),je=document.getElementById("login-btn"),_e=document.getElementById("coin-symbol-select"),ze=document.getElementById("fx-symbol-select"),Oe=document.getElementById("coin-kline-intervals"),He=document.getElementById("fx-kline-intervals"),Je=document.getElementById("coin-kline-canvas"),qe=document.getElementById("fx-kline-canvas"),Ge=document.getElementById("coin-market-icon"),Ke=document.getElementById("fx-market-icon"),Ye=document.getElementById("coin-order-bid"),Re=document.getElementById("coin-order-ask"),We=document.getElementById("coin-order-spread"),Xe=document.getElementById("fx-order-bid"),Ve=document.getElementById("fx-order-ask"),Qe=document.getElementById("fx-order-spread"),Ze=document.getElementById("coin-order-qty"),et=document.getElementById("fx-order-qty"),tt=document.getElementById("coin-buy-btn"),nt=document.getElementById("coin-sell-btn"),it=document.getElementById("fx-buy-btn"),ot=document.getElementById("fx-sell-btn"),at=document.getElementById("coin-required-amount"),rt=document.getElementById("fx-required-amount"),st=document.getElementById("coin-account-info-refresh-btn"),lt=document.getElementById("fx-account-info-refresh-btn"),ct=document.getElementById("coin-account-pnl"),dt=document.getElementById("coin-account-margin"),mt=document.getElementById("coin-account-available"),ut=document.getElementById("coin-account-margin-ratio"),gt=document.getElementById("fx-account-pnl"),pt=document.getElementById("fx-account-margin"),ft=document.getElementById("fx-account-available"),yt=document.getElementById("fx-account-margin-ratio"),wt=document.getElementById("coin-position-summary-body"),bt=document.getElementById("coin-position-list-body"),ht=document.getElementById("fx-position-summary-body"),kt=document.getElementById("fx-position-list-body"),xt=document.getElementById("coin-position-summary-refresh-btn"),St=document.getElementById("coin-position-list-refresh-btn"),Et=document.getElementById("fx-position-summary-refresh-btn"),vt=document.getElementById("fx-position-list-refresh-btn"),Ct=Array.from(document.querySelectorAll(".tab-btn")),Nt=Array.from(document.querySelectorAll(".tab-panel"));let It="15m",At="15m",Lt=String(_e?.value||"BTC_JPY").trim().toUpperCase(),Bt=String(ze?.value||"USD_JPY").trim().toUpperCase(),$t=null,Mt=null,Tt=null,Pt=null,Ut=!1,Dt=!1,Ft=!1,jt=!1,_t=!1,zt="",Ot="",Ht=0,Jt=0,qt=null,Gt=null,Kt=[],Yt=!1;const Rt=new Map,Wt={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1},Xt={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1};let Vt=[],Qt=[];const Zt=e=>e&&"object"==typeof e?e:{},en="weget.bot.sidebar.collapsed",tn=e=>{i.classList.toggle("sidebar-collapsed",e),a&&(a.textContent=e?"▶":"◀",a.setAttribute("aria-label",e?"Expand menu":"Collapse menu")),o&&o.setAttribute("data-collapsed",e?"true":"false")},nn=e=>{for(const t of Ct)t.classList.toggle("is-active",t.dataset.tab===e);for(const t of Nt)t.classList.toggle("is-active",t.id===`tab-${e}`)};for(const e of Ct)e.addEventListener("click",()=>nn(e.dataset.tab||"coin"));a?.addEventListener("click",()=>{const e=!i.classList.contains("sidebar-collapsed");tn(e);try{window.localStorage.setItem(en,String(e))}catch{}});const on=e=>{const n=Ut!==e;Ut=e,t.classList.toggle("hidden",e),i.classList.toggle("hidden",!e),e&&Zn(),e?n&&ki():(Fn(),Dt=!1)},an=e=>{const t=String(e||"disconnected").trim().toLowerCase();m.textContent=t,m.classList.remove("status-connected","status-disconnected","status-connecting"),"connected"!==t?"reconnecting"!==t&&"connecting"!==t?m.classList.add("status-disconnected"):m.classList.add("status-connecting"):m.classList.add("status-connected")},rn=(e,t=6)=>{if(null===e||!Number.isFinite(e))return"-";const n=Math.abs(e);return n>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:3}):n>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:Math.min(t,5)}):e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:t})},sn=e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,ln=e=>`${e>0?"+":""}${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,cn=e=>!Number.isFinite(e)||e<=0||e>1e5?"- %":(e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:2})} %`)(e),dn=e=>{const t=e instanceof Date?e:new Date(e);if(Number.isNaN(t.getTime()))return"";return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`},mn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t);return Number.isNaN(n.getTime())?t:n.toLocaleString("ja-JP",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})},un=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),gn=e=>{const t=String(e||"").trim();if(!t)return{cleaned:"",impact:"",direction:""};const n=t.indexOf("\n"),i=n>=0?t.slice(0,n).trim():t,o=n>=0?t.slice(n+1).trim():"";if(i.startsWith("{")&&i.endsWith("}"))try{const e=JSON.parse(i);return{cleaned:o,impact:String(e.impact||"").trim().toLowerCase(),direction:String(e.usd_jpy_direction||"").trim().toLowerCase()}}catch{}return{cleaned:t,impact:"",direction:""}},pn=e=>{const t=String(e||"").trim().toLowerCase();if(!t)return"-";const n="high"===t?"High":"medium"===t?"Medium":"low"===t?"Low":t;return`<span class="macro-impact-badge ${un(t)}">${un(n)}</span>`},fn=e=>{const t=Math.max(0,Math.min(3,Math.round(Number(e||0))));return t<=0?"-":`<span class="macro-importance-stars">${"★".repeat(t)}</span>`},yn=e=>{const t=String(e||"").trim().toLowerCase();return t?"up"===t?'<span class="macro-direction up">↑↑</span>':"down"===t?'<span class="macro-direction down">↓↓</span>':"sideways"===t?'<span class="macro-direction flat">→</span>':un(t):"-"},wn=e=>{const t=String(e||"").trim().replace(/,/g,"");if(!t)return"";if(!/^\d+(\.\d+)?$/.test(t))return"";if(!t.includes("."))return t;return t.replace(/(\.\d*?[1-9])0+$/,"$1").replace(/\.0+$/,"").replace(/\.$/,"")},bn=e=>{const t=String(e||"").trim(),n=t.indexOf(".");return n>=0?t.length-n-1:0},hn=(e,t)=>{const n=String(e||"").trim(),i=n.startsWith("-"),o=i?n.slice(1):n,[a,r=""]=o.split("."),s=(r+"0".repeat(t)).slice(0,t),l=BigInt((a||"0")+s);return i?-l:l},kn=e=>{const t=En("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),n=Zt(t?.configJson);return"fx"===e?Boolean(String(n.fxApiKey||ye.value||"").trim()&&String(n.fxApiSecret||we.value||"").trim()):Boolean(String(n.cryptoApiKey||pe.value||"").trim()&&String(n.cryptoApiSecret||fe.value||"").trim())},xn=({symbol:e,quote:t,orderBidNode:n,orderAskNode:i,orderSpreadNode:o})=>{if(!t)return n.textContent="-",i.textContent="-",void(o.textContent="-");const a=rn(t.bid),r=rn(t.ask);n.textContent=a,i.textContent=r,o.textContent=rn(t.spread,8)},Sn=e=>{if("coin"===e){const e=Number(String(Ze.value||"").trim()),t=qt;return!Number.isFinite(e)||e<=0||null===t||!Number.isFinite(t)?void(at.textContent="- 円"):void(at.textContent=sn(e*t/2))}const t=Number(String(et.value||"").trim()),n=Gt;!Number.isFinite(t)||t<=0||null===n||!Number.isFinite(n)?rt.textContent="- 円":rt.textContent=sn(t*n/20)},En=e=>Kt.find(t=>t.name===e),vn=e=>{const t=En(e);if(!t)return!1;const n=String(t.installStatus||"").trim().toLowerCase();return t.enabled&&"active"===n},Cn=e=>vn("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),Nn=async(e,t)=>{if(!window.botApi?.getMarketQuotes)return null;const n=String(t||"").trim().toUpperCase(),i=await window.botApi.getMarketQuotes({market:e,symbols:[n]});if(!i?.ok)throw new Error(String(i?.error||"quote api failed"));const o=(i.quotes||[]).find(e=>String(e.symbol||"").toUpperCase()===n)||null;return"coin"===e?(xn({symbol:n,quote:o,orderBidNode:Ye,orderAskNode:Re,orderSpreadNode:We}),qt=o?.ask??null,Sn("coin")):(xn({symbol:n,quote:o,orderBidNode:Xe,orderAskNode:Ve,orderSpreadNode:Qe}),Gt=o?.ask??null,Sn("fx")),o},In=(e,t)=>{const n=String(t||"").trim().toUpperCase();if(!n)return;if("coin"===e){const e=n.replace("_","-").toLowerCase();return void(Ge.src=`https://coin.z.com/jp/member/imgs/icon-${e}.svg`)}const[i,o]=n.split("_"),a="JPY"===o?i:`${i}_${o}`;Ke.src=`https://coin.z.com/jp/member/imgs/fx/icon_${a}.svg`},An=e=>"5m"===e?3e5:"15m"===e?9e5:"30m"===e?18e5:"1h"===e?36e5:6e4,Ln=e=>"coin"===e?Wt:Xt,Bn=e=>"coin"===e?Je:qe,$n=e=>"coin"===e?Lt:Bt,Mn=e=>{const t=$n(e),n=("coin"===e?Vt:Qt).filter(e=>String(e.symbol||"").trim().toUpperCase()===t),i=[];for(const e of n){const t=String(e.side||"").trim().toUpperCase(),n=Number(e.averagePositionRate||0);if(!Number.isFinite(n)||n<=0)continue;const o="BUY"===t;i.push({price:n,label:o?"BUY Avg":"SELL Avg",color:o?"#1f9d55":"#e03131"})}return i},Tn=e=>{const t=Ln(e);t.candles.length&&ti(Bn(e),t.candles,Mn(e))},Pn=(e,t)=>{if(!t)return;const n=Ln(e);if(!n.candles.length)return;if(n.symbol!==t.symbol)return;const i=n.candles[n.candles.length-1],o=null!==t.bid&&null!==t.ask?(t.bid+t.ask)/2:null!==t.ask?t.ask:t.bid;null!==o&&Number.isFinite(o)&&(i.close=o,i.high=Math.max(i.high,o),i.low=Math.min(i.low,o),ti(Bn(e),n.candles,Mn(e)))},Un=async e=>{const t=Ln(e);if(!t.interval||!t.symbol||t.fetching)return;if(t.symbol!==$n(e))return;if(!(Math.floor(Date.now()/An(t.interval))<=t.lastFetchBucket)){t.fetching=!0;try{"coin"===e?await wi():await bi()}finally{t.fetching=!1}}},Dn=async e=>{if(Cn(e))if("coin"!==e){if(!_t){_t=!0;try{const e=await Nn("fx",Bt);Pn("fx",e),await Un("fx")}catch(e){const t=Rn(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Ot||n-Jt>15e3)&&(Gn("[fx] quote poll failed",{error:t}),Ot=t,Jt=n)}finally{_t=!1}}}else{if(jt)return;jt=!0;try{const e=await Nn("coin",Lt);Pn("coin",e),await Un("coin")}catch(e){const t=Rn(e instanceof Error?e.message:String(e)),n=Date.now();(t!==zt||n-Ht>15e3)&&(Gn("[coin] quote poll failed",{error:t}),zt=t,Ht=n)}finally{jt=!1}}},Fn=()=>{Tt&&(clearInterval(Tt),Tt=null),Pt&&(clearInterval(Pt),Pt=null)},jn=async e=>{if(window.botApi?.getAccountMetrics){if((!e||"coin"===e)&&Cn("coin"))if(kn("coin")){const e=await window.botApi.getAccountMetrics({market:"coin"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);ct.textContent=n>0?ln(i):sn(0),dt.textContent=sn(n),mt.textContent=sn(t),ut.textContent=cn(Number(e.marginRatio||0))}else Gn("[coin] account metrics fetch failed",{error:e?.error||"unknown error"})}else ct.textContent=sn(0),dt.textContent="- 円",mt.textContent="- 円",ut.textContent="- %";if((!e||"fx"===e)&&Cn("fx"))if(kn("fx")){const e=await window.botApi.getAccountMetrics({market:"fx"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);gt.textContent=n>0?ln(i):sn(0),pt.textContent=sn(n),ft.textContent=sn(t),yt.textContent=cn(Number(e.marginRatio||0))}else Gn("[fx] account metrics fetch failed",{error:e?.error||"unknown error"})}else gt.textContent=sn(0),pt.textContent="- 円",ft.textContent="- 円",yt.textContent="- %"}},_n=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").toUpperCase(),o=n.replace("_","/"),a=String(i.side||"-"),r=String(i.size||"").trim(),s=Number(r||0),l=Number(i.price||0),c=Number(i.lossGain||0),d=Number(i.totalSwap||0),m=String(i.timestamp||"-"),u=Number(i.positionId||0),g=Number.isFinite(u)&&u>0&&r?`data-market="${e}" data-symbol="${n}" data-side="${String(a||"").toUpperCase()}" data-position-id="${u}" data-size="${r}"`:"disabled",p=document.createElement("tr");p.innerHTML=`\n <td><button type="button" class="close-btn" ${g}>決済</button></td>\n <td>${o}<br />${a}</td>\n <td>${s.toLocaleString("ja-JP")}</td>\n <td>${l.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${ln(c)}<br />${ln(d)}</td>\n <td>${m.replace("T"," ").slice(0,19)}</td>\n `,t.appendChild(p)}}else t.innerHTML='<tr><td colspan="6" class="empty-cell">対象のお取引はございません。</td></tr>'},zn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").replace("_","/"),o=String(i.side||"-"),a=Number("fx"===e?i.sumPositionSize||0:i.sumPositionQuantity||0),r=Number(i.averagePositionRate||0),s=Number(i.positionLossGain||0),l=Number("fx"===e&&i.sumTotalSwap||0),c=document.createElement("tr");c.innerHTML=`\n <td><button type="button" class="close-btn">決済</button></td>\n <td>${n}<br />${o}</td>\n <td>${a.toLocaleString("ja-JP")}</td>\n <td>${r.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${ln(s)}<br />${ln(l)}</td>\n `,t.appendChild(c)}}else t.innerHTML='<tr><td colspan="5" class="empty-cell">対象のお取引はございません。</td></tr>'},On=async e=>{if(window.botApi?.getPositionSummary&&window.botApi?.getOpenPositions){if((!e||"coin"===e)&&Cn("coin"))if(kn("coin")){const e=await window.botApi.getPositionSummary({market:"coin",symbol:Lt});e?.ok?(Vt=Array.isArray(e.items)?e.items:[],zn("coin",wt,Vt),Tn("coin")):(Gn("[coin] position summary fetch failed",{error:e?.error||"unknown error"}),Vt=[],zn("coin",wt,[]),Tn("coin"))}else Vt=[],zn("coin",wt,[]),Tn("coin");if((!e||"fx"===e)&&Cn("fx"))if(kn("fx")){const e=await window.botApi.getPositionSummary({market:"fx"});e?.ok?(Qt=Array.isArray(e.items)?e.items:[],zn("fx",ht,Qt),Tn("fx")):(Gn("[fx] position summary fetch failed",{error:e?.error||"unknown error"}),Qt=[],zn("fx",ht,[]),Tn("fx"))}else Qt=[],zn("fx",ht,[]),Tn("fx");if((!e||"coin"===e)&&Cn("coin"))if(kn("coin")){const e=await window.botApi.getOpenPositions({market:"coin",symbol:Lt,page:1,count:100});e?.ok?_n("coin",bt,Array.isArray(e.items)?e.items:[]):(Gn("[coin] open positions fetch failed",{error:e?.error||"unknown error"}),_n("coin",bt,[]))}else _n("coin",bt,[]);if((!e||"fx"===e)&&Cn("fx"))if(kn("fx")){const e=await window.botApi.getOpenPositions({market:"fx",count:100});e?.ok?_n("fx",kt,Array.isArray(e.items)?e.items:[]):(Gn("[fx] open positions fetch failed",{error:e?.error||"unknown error"}),_n("fx",kt,[]))}else _n("fx",kt,[])}},Hn=async(e,t)=>{const n=window.botApi;if(!n?.placeFxOrder)return;const i=String(et.value||"").trim();i?await ei(t,async()=>{const t=await n.placeFxOrder({symbol:Bt,side:e,size:i});if(!t?.ok){const n=Rn(t?.error||"fx order failed");return Gn("[fx] order failed",{symbol:Bt,side:e,size:i,error:n}),Wn("error",n),void await Xn("fx.order",n,t)}Gn("[fx] order placed",{symbol:Bt,side:e,size:i,result:t.result||null}),Wn("success",`${e} 注文を送信しました。`),await jn("fx"),await On("fx")}):Wn("error","数量を入力してください。")},Jn=async(e,t)=>{const n=window.botApi;if(!n?.placeCoinOrder)return;const i=wn(String(Ze.value||""));if(!i)return void Wn("error","数量の形式が不正です。");const o=((e,t)=>{const n=Rt.get(e);if(!n)return{ok:!0};const i=wn(n.minOrderSize),o=wn(n.sizeStep),a=wn(n.maxOrderSize);if(!i||!o)return{ok:!0};const r=Math.max(bn(t),bn(i),bn(o),bn(a)),s=hn(t,r),l=hn(i,r),c=hn(o,r);if(s<l)return{ok:!1,reason:`${e} の最小数量は ${i} です。`};if(c>0n&&s%c!==0n)return{ok:!1,reason:`${e} の数量刻みは ${o} です。`};if(a&&s>hn(a,r))return{ok:!1,reason:`${e} の最大数量は ${a} です。`};return{ok:!0}})(Lt,i);o.ok?await ei(t,async()=>{const t=await n.placeCoinOrder({symbol:Lt,side:e,size:i});if(!t?.ok){const n=Rn(t?.error||"coin order failed");return Gn("[coin] order failed",{symbol:Lt,side:e,size:i,error:n}),Wn("error",n),void await Xn("coin.order",n,t)}Gn("[coin] order placed",{symbol:Lt,side:e,size:i,result:t.result||null}),Wn("success",`${e} 注文を送信しました。`),await jn("coin"),await On("coin")}):Wn("error",o.reason||"数量が取引ルールに一致しません。")},qn=e=>{const t=String(e||"").toLowerCase();return t.includes("debug")||t.includes("[debug]")?"debug":t.includes("error")||t.includes("failed")||t.includes("exception")?"error":"info"},Gn=(e,t=null,n=null)=>{if(window.botApi?.writeLog&&window.botApi.writeLog({level:qn(e),source:"ui.log",message:e,details:{data:t,ts:n||(new Date).toISOString()}}).catch(()=>{}),!Z)return;const i=document.createElement("div");i.className="log-entry";const o=document.createElement("div");o.className="log-line";const a=document.createElement("span");if(a.className="log-ts",a.textContent=n||(new Date).toISOString(),o.appendChild(a),o.append(document.createTextNode(e||"")),i.appendChild(o),null!=t&&""!==t){const e=document.createElement("pre");e.className="status-output",e.textContent="string"==typeof t?t:JSON.stringify(t,null,2),i.appendChild(e)}for(Z.appendChild(i);Z.children.length>300;)Z.removeChild(Z.firstChild);Z.scrollTop=Z.scrollHeight},Kn=(e,t,n="")=>{const i=document.createElement("div");i.className=`msg ${e}`;let o=n||("user"===e?"You":"Assistant");"line-user"===e&&(o=n||"LINE User"),"line-assistant"===e&&(o=n||"AI"),i.textContent=`${o}: ${t}`,le.appendChild(i),le.scrollTop=le.scrollHeight},Yn=()=>{const e=le.querySelector(".msg.assistant-thinking");e&&e.remove()},Rn=e=>{const t=String(e||"").trim(),n=t.toLowerCase();return t?n.includes("api key")&&n.includes("missing")?"API Key 未设置,请先在 Config 页面保存。":n.includes("auth")||n.includes("401")||n.includes("403")?"认证失败,请检查 API Key 和 Secret。":n.includes("network")||n.includes("fetch failed")||n.includes("timeout")?"网络连接异常,请检查网络后重试。":n.includes("message is required")?"请输入内容后再发送。":t:"处理失败,请稍后重试。"},Wn=(e,t)=>{$t&&(clearTimeout($t),$t=null),r.classList.remove("hidden","notice-error","notice-success"),r.classList.add("error"===e?"notice-error":"notice-success"),r.textContent=t,$t=setTimeout(()=>{Vn()},"success"===e?2200:4200)},Xn=async(e,t,n=null)=>{window.botApi?.writeErrorLog&&await window.botApi.writeErrorLog({source:e,message:String(t||""),details:n})},Vn=()=>{$t&&(clearTimeout($t),$t=null),r.classList.add("hidden"),r.classList.remove("notice-error","notice-success"),r.textContent=""},Qn=(e,t)=>{Mt&&(clearTimeout(Mt),Mt=null),n.classList.remove("hidden","notice-error","notice-success"),n.classList.add("error"===e?"notice-error":"notice-success"),n.textContent=t,Mt=setTimeout(()=>{Zn()},"success"===e?2200:4200)},Zn=()=>{Mt&&(clearTimeout(Mt),Mt=null),n.classList.add("hidden"),n.classList.remove("notice-error","notice-success"),n.textContent=""},ei=async(e,t)=>{if(e.disabled)return;e.disabled=!0,e.classList.add("is-loading");const n=e.textContent||"";try{await t()}catch(e){const t=Rn(e instanceof Error?e.message:String(e));Gn("[ui] action failed",{error:t}),Wn("error",t)}finally{e.classList.remove("is-loading"),e.textContent=n,e.disabled=!1}},ti=(e,t,n=[])=>{const i=e.getContext("2d");if(!i)return;const o=e.width,a=e.height;if(i.clearRect(0,0,o,a),!t.length)return;const r=Math.max(...t.map(e=>e.high)),s=Math.min(...t.map(e=>e.low)),l=n.map(e=>e.price).filter(e=>Number.isFinite(e)),c=l.length?Math.max(r,...l):r,d=l.length?Math.min(s,...l):s,m=Math.max(1e-8,c-d),u=10,g=10,p=o-10-62-8,f=a-10-24-8,y=Math.max(2,p/t.length),w=e=>g+(c-e)/m*f,b=e=>e>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:0}):e>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:3}):e.toLocaleString("en-US",{minimumFractionDigits:5,maximumFractionDigits:5});i.strokeStyle="#c9d4ea",i.strokeRect(.5,.5,o-1,a-1),i.strokeStyle="#e3eaf5",i.lineWidth=1;for(let e=0;e<=4;e+=1){const t=g+f/4*e;i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke()}t.forEach((e,t)=>{const n=u+t*y+.5*y,o=w(e.high),a=w(e.low),r=w(e.open),s=w(e.close),l=e.close>=e.open;i.strokeStyle=l?"#1f9d55":"#d64545",i.fillStyle=l?"#1f9d55":"#d64545",i.lineWidth=1,i.beginPath(),i.moveTo(n,o),i.lineTo(n,a),i.stroke();const c=n-.3*y,d=Math.max(1,.6*y),m=Math.min(r,s),g=Math.max(1,Math.abs(s-r));i.fillRect(c,m,d,g)});for(const e of n){const t=w(e.price);i.save(),i.setLineDash([4,4]),i.strokeStyle=e.color,i.lineWidth=1,i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke(),i.restore(),i.font="12px Segoe UI";const n=`${e.label} ${b(e.price)}`,o=Math.ceil(i.measureText(n).width)+10,a=14,r=Math.max(12,Math.min(g+f-18,t-9));i.fillStyle=e.color,i.fillRect(a,r,o,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(n,a+5,r+9)}const h=t[t.length-1],k=h?.close;if(Number.isFinite(k)){const e=w(Number(k));i.save(),i.setLineDash([5,4]),i.strokeStyle="#2b79ff",i.lineWidth=1,i.beginPath(),i.moveTo(u,e),i.lineTo(u+p,e),i.stroke(),i.restore();const t=b(Number(k));i.font="12px Segoe UI";const n=Math.ceil(i.measureText(t).width)+10,o=u+p+6,a=Math.max(12,Math.min(g+f-18,e-9));i.fillStyle="#2b79ff",i.fillRect(o,a,n,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(t,o+5,a+9)}i.fillStyle="#66758d",i.font="12px Segoe UI",i.textBaseline="middle",i.textAlign="left";for(let e=0;e<=4;e+=1){const t=c-m/4*e,n=g+f/4*e;i.fillText(b(t),u+p+8,n)}const x=e=>{const t=new Date(e>1e12?e:1e3*e);if(Number.isNaN(t.getTime()))return"-";const n=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${n}:${i}`};i.textAlign="center",i.textBaseline="top";for(let e=0;e<=4;e+=1){const n=Math.min(t.length-1,Math.floor((t.length-1)*(e/4))),o=u+p*e/4;i.fillText(x(t[n]?.time||0),o,g+f+6)}},ni=(e,t)=>{const{tabBtn:n,titleNode:i,packageNode:o,enabledNode:a,versionNode:r,fallbackTitle:s,fallbackPackage:l}=t,c=String(e?.ui?.surfaceTitle||e?.displayName||s).trim()||s,d=e?.name||l,m=String(e?.ui?.tabLabel||d.replace("@ai.weget.jp/","")).trim()||d.replace("@ai.weget.jp/",""),u=!!e&&Boolean(e.enabled),g=String(e?.installStatus||"uninstalled").trim()||"uninstalled",p=String(e?.configuredVersion||e?.version||"-").trim()||"-";if(n){const e=n.querySelector(".tab-btn-label");e&&(e.textContent=m),n.classList.toggle("is-disabled",!u)}i&&(i.textContent=c),o&&(o.textContent=d),a&&(a.textContent=u?"enabled":"disabled",a.classList.toggle("is-disabled",!u)),r&&(r.textContent=`${g} · ${p}`)},ii=(e,t)=>{t.titleNode&&(t.titleNode.textContent=String(e?.ui?.configTitle||t.fallbackTitle).trim()||t.fallbackTitle);const n=Array.isArray(e?.ui?.configFields)&&e.ui?.configFields||[];for(const[e,i]of Object.entries(t.fields)){const t=n.find(t=>t.key===e);t?.label&&i.labelNode&&(i.labelNode.textContent=t.label),void 0!==t?.placeholder&&(i.inputNode.placeholder=t.placeholder||"")}},oi=(e,t)=>{if(e){if(e.innerHTML="",!t.length){const t=document.createElement("span");return t.className="muted",t.textContent="No macro snapshot cards available.",void e.appendChild(t)}for(const n of t){const t=document.createElement("div");t.className="macro-chip"+("down"===n.trend?" is-negative":"");const i=document.createElement("div");i.className="macro-chip-code",i.textContent=n.code||"-";const o=document.createElement("div");o.className="macro-chip-value",o.textContent=n.value||"-";const a=document.createElement("div");a.className="macro-chip-delta"+("down"===n.trend?" is-negative":""),a.textContent=n.delta||"-",t.append(i,o,a),e.appendChild(t)}}},ai=(e,t,n)=>{if(!e)return;if(e.innerHTML="",!t.length){const t=document.createElement("div");return t.className="macro-treemap-empty",t.textContent=n,void e.appendChild(t)}const i=[...t].map(e=>({symbol:String(e.symbol||"").trim(),change:Number(e.change||0),weight:Math.max(1,Math.abs(Number(e.change||0)))})).filter(e=>e.symbol).sort((e,t)=>Math.abs(t.change)-Math.abs(e.change)).slice(0,12),o=[],a=i.reduce((e,t)=>e+t.weight,0)||1,r=(e,t,n,i,a,s)=>{if(!e.length)return;if(1===e.length)return void o.push({item:e[0],x:t,y:n,width:i,height:a});const l=e.reduce((e,t)=>e+t.weight,0);let c=1,d=e[0].weight;for(;c<e.length-1&&d<l/2;)c+=1,d+=e[c-1].weight;const m=e.slice(0,c),u=e.slice(c),g=d/l;if(s){const e=i*g;return r(m,t,n,e,a,!s),void r(u,t+e,n,i-e,a,!s)}const p=a*g;r(m,t,n,i,p,!s),r(u,t,n+p,i,a-p,!s)};r(i,0,0,100,100,!0);for(const{item:t,x:n,y:i,width:r,height:s}of o){const o=document.createElement("div"),l=Math.min(.88,.22+t.weight/Math.max(.18*a,1));o.className="macro-treemap-tile "+(t.change>=0?"up":"down"),o.style.setProperty("--tile-intensity",String(l)),o.style.left=`${n}%`,o.style.top=`${i}%`,o.style.width=`${r}%`,o.style.height=`${s}%`,o.innerHTML=`\n <div class="macro-treemap-symbol">${un(t.symbol.replace(/_JPY$/i,"").replace(/_USD$/i," $"))}</div>\n <div class="macro-treemap-change">${t.change>=0?"+":""}${t.change.toFixed(1)}%</div>\n `,e.appendChild(o)}},ri=e=>{if(q)if(q.innerHTML="",e.length)for(const t of e){const e=gn(t.ai_analyzer||""),n=document.createElement("tr");n.className="macro-data-row",n.innerHTML=`\n <td>${un(mn(t.date))}</td>\n <td>${un(t.country||"-")}</td>\n <td class="macro-title-cell"><strong>${un(t.title||"-")}</strong></td>\n <td>${fn(Number(t.importance||0))}</td>\n <td>${un(t.actual||"-")}</td>\n <td>${un(t.forecast||"-")}</td>\n <td>${un(t.previous||"-")}</td>\n <td>${yn(e.direction)}</td>\n `;const i=document.createElement("tr");i.className="macro-ai-row",i.innerHTML=`\n <td colspan="8" class="macro-analyzer">${un(e.cleaned||"-")}</td>\n `,q.append(n,i)}else q.innerHTML='<tr><td colspan="8" class="empty-cell">No calendar rows for the selected date.</td></tr>'},si=e=>{if(G)if(G.innerHTML="",e.length)for(const t of e){const e=gn(t.ai_analyzer||""),n=String(t.url||"").trim(),i=document.createElement("tr");i.className="macro-data-row",i.innerHTML=`\n <td>${un(mn(t.time))}</td>\n <td class="macro-title-cell"><strong>${un(t.content||"-")}</strong></td>\n <td>${pn(e.impact)}</td>\n <td>${yn(e.direction)}</td>\n <td>${n?`<a class="macro-link" href="${un(n)}" target="_blank" rel="noreferrer noopener">open</a>`:"-"}</td>\n `;const o=document.createElement("tr");o.className="macro-ai-row",o.innerHTML=`\n <td colspan="5" class="macro-analyzer">${un(e.cleaned||"-")}</td>\n `,G.append(i,o)}else G.innerHTML='<tr><td colspan="4" class="empty-cell">No news rows for the selected date.</td></tr>'},li=e=>{F&&(F.textContent="-"),j&&(j.textContent="-"),_&&(_.textContent=e),oi(z,[]),oi(O,[]),ai(H,[],"No coin heatmap data."),ai(J,[],"No FX heatmap data."),ri([]),si([])},ci=async({forceSnapshot:e=!1}={})=>{if(Yt)return;if(!window.botApi?.getMacroSnapshot||!window.botApi?.getMacroCalendar||!window.botApi?.getMacroNews)return void li("Macro IPC bridge is not available in this host build.");const t=En("@ai.weget.jp/skill-macro-economy");if(!t||!t.enabled||"active"!==String(t.installStatus||"").trim().toLowerCase())return void li("Macro Economy skill is disabled or not active.");if(!Ut)return void li("Login is required to load macro snapshot, calendar, and news.");Yt=!0;const n=String(K?.value||"").trim()||dn(new Date);K&&!K.value&&(K.value=n);try{const[t,i,o]=await Promise.all([window.botApi.getMacroSnapshot({force:e}),window.botApi.getMacroCalendar({dateKey:n}),window.botApi.getMacroNews({dateKey:n})]);if(!t.ok)throw new Error(t.error||"macro snapshot failed");if(!i.ok)throw new Error(i.error||"macro calendar failed");if(!o.ok)throw new Error(o.error||"macro news failed");const a=t.snapshot;F&&(F.textContent=mn(a?.as_of)),j&&(j.textContent=null==a?.fear_greed_score?"-":`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`),_&&(_.textContent=`Loaded macro data for ${n} from the local macro economy skill runtime.`);const r=Array.isArray(a?.coin_cards)?[...a.coin_cards]:[];null!=a?.fear_greed_score&&r.unshift({code:"FGI",value:`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`,delta:"Fear & Greed",trend:Number(a.fear_greed_score)>=50?"up":"down",points:[],usePill:!0}),oi(z,r),oi(O,Array.isArray(a?.fx_cards)?a?.fx_cards:[]),ai(H,Array.isArray(a?.coin_heatmap)?a?.coin_heatmap:[],"No coin heatmap data."),ai(J,Array.isArray(a?.fx_heatmap)?a?.fx_heatmap:[],"No FX heatmap data."),ri(Array.isArray(i.rows)?i.rows:[]),si(Array.isArray(o.rows)?o.rows:[])}catch(e){li(Rn(e instanceof Error?e.message:String(e)))}finally{Yt=!1}},di=(e,t)=>{W&&(W.textContent=e,W.classList.toggle("is-disabled","installed"!==e),W.classList.toggle("is-muted","unknown"===e)),X&&(X.textContent=t||"No Playwright browser detail available.")},mi=({browserStatus:e})=>{V&&(V.innerHTML="installed"!==e?"Run <code>npx playwright install chromium</code> on this machine.":"Chromium is ready for Playwright-backed browser tasks.")},ui=async()=>{if(!window.botApi?.getPlaywrightBrowserStatus)return di("unknown","Playwright browser check is not available in this host build."),void mi({browserStatus:"unknown"});const e=await window.botApi.getPlaywrightBrowserStatus(),t=e.ok&&e.status||"unknown";e.ok?di(t,String(e.detail||"").trim()):di("unknown",Rn(e.error)),mi({browserStatus:t})},gi=(e,t,n,i=!1)=>{e&&(e.textContent=t||"-",e.classList.toggle("is-disabled",!n&&!i),e.classList.toggle("is-muted",i))},pi=async()=>{if(!window.botApi?.getGatewayStatus)return;const e=await window.botApi.getGatewayStatus();if(!e?.ok)return gi(ee,"error",!1),gi(te,"error",!1),gi(ne,"error",!1),void(oe&&(oe.textContent=Rn(e?.error||"gateway status failed")));gi(ee,String(e.codexAuth?.status||"unknown"),"logged_in"===String(e.codexAuth?.status||""),"unknown"===String(e.codexAuth?.status||"")),gi(te,String(e.gatewayStatus?.status||"unknown"),"configured"===String(e.gatewayStatus?.status||""),"unknown"===String(e.gatewayStatus?.status||"")),gi(ne,String(e.browserStatus?.status||"unknown"),"installed"===String(e.browserStatus?.status||""),"unknown"===String(e.browserStatus?.status||"")),ie&&(ie.textContent=`Context file: ${String(e.contextFilePath||"-")}`),oe&&(oe.textContent=JSON.stringify({codexAuth:e.codexAuth||null,gatewayStatus:e.gatewayStatus||null,browserStatus:e.browserStatus||null},null,2)),(e=>{if(!se)return;se.innerHTML="";const t=[{target:"gateway",title:"Gateway Core"},{target:"codex_macro",title:"Codex Macro Chain",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"browser",title:"Browser Skill",packageName:"@ai.weget.jp/skill-browser"},{target:"macro",title:"Macro Economy Skill",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"coin",title:"GMO Coin Skill",packageName:"@ai.weget.jp/skill-gmo-coin"},{target:"fx",title:"GMO FX Skill",packageName:"@ai.weget.jp/skill-gmo-fx"}];for(const n of t){const t=n.packageName?e.find(e=>e.name===n.packageName):null,i=document.createElement("section");i.className="browser-skill-card gateway-test-card",i.innerHTML=`\n <div class="gateway-test-head">\n <h3>${n.title}</h3>\n <span class="skill-surface-badge is-muted">${t?t.enabled?"enabled":"disabled":"system"}</span>\n </div>\n <div class="muted">${t?`${t.name} · ${t.installStatus}`:"Gateway MCP self-test"}</div>\n <div class="skill-tags">${t&&Array.isArray(t.tools)&&t.tools.length?t.tools.map(e=>`<span class="skill-tag">${un(e)}</span>`).join(""):'<span class="muted">No declared tools</span>'}</div>\n <div class="row config-actions">\n <button type="button" class="secondary gateway-test-btn" data-target="${n.target}">Run Test</button>\n </div>\n <div class="gateway-test-result">\n <span class="skill-surface-badge is-muted gateway-test-badge" data-target="${n.target}">not run</span>\n <div class="muted gateway-test-summary" data-target="${n.target}">-</div>\n </div>\n `,se.appendChild(i)}})(Array.isArray(e.skills)?e.skills:[])},fi=async()=>{if(!window.botApi?.getRuntimeInfo)return;const[e,t]=await Promise.all([window.botApi.getRuntimeInfo(),window.botApi.getSkills?window.botApi.getSkills():(async()=>({ok:!1,skills:[]}))()]);if(!e?.ok)return;const n=t?.ok&&t.skills||[];Kt=n,an(e.status||"disconnected"),e.session?(on(!0),u.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`):(on(!1),u.textContent="local mode (not logged in)"),(e=>{g.innerHTML="";const t=Zt(e),n=Zt(t.runtime),i=t.session?Zt(t.session):null,o=[["Bot ID",String(i?.botId||"-")],["User ID",String(i?.userId||"-")],["Email",String(i?.email||"-")],["Capabilities",Array.isArray(n.capabilities)?n.capabilities.map(e=>String(e)).join(", "):"-"],["Active Skills",Array.isArray(n.activeSkills)&&n.activeSkills.map(e=>{const t=Zt(e);return String(t.displayName||t.name||"").trim()}).filter(Boolean).join(", ")||"-"],["GMO FX API State",String(n.gmoFxApiState||"unknown")],["GMO Coin API State",String(n.gmoCoinApiState||"unknown")],["Codex Auth",String(n.codexAuthStatus||"unknown")],["Codex Auth Detail",String(n.codexAuthDetail||"-")],["Default Model",String(n.aiModel||"-")],["Login API",String(n.loginApiUrl||"-")]];for(const[e,t]of o){const n=document.createElement("div");n.className="runtime-key",n.textContent=e;const i=document.createElement("div");i.className="runtime-value",i.textContent=t,g.appendChild(n),g.appendChild(i)}})(e),(e=>{if(!p)return;p.innerHTML="";const t=Array.isArray(e)?e:[];if(0===t.length){const e=document.createElement("div");return e.className="skill-empty",e.textContent="No managed skills are available in this bot host.",void p.appendChild(e)}for(const e of t){const t=document.createElement("section");t.className="skill-card"+(e.enabled?"":" is-disabled");const n=document.createElement("div");n.className="skill-card-head";const i=document.createElement("div");i.className="skill-card-title";const o=document.createElement("strong");o.textContent=e.displayName||e.name;const a=document.createElement("code");if(a.textContent=e.name,i.appendChild(o),i.appendChild(a),e.description){const t=document.createElement("div");t.className="muted",t.textContent=e.description,i.appendChild(t)}const r=document.createElement("div");r.className="skill-badges";const s=document.createElement("span");s.className="skill-badge "+(e.enabled?"enabled":"disabled"),s.textContent=e.enabled?"enabled":"disabled",r.appendChild(s);const l=document.createElement("span");l.className="skill-badge status",l.textContent=e.installStatus||"unknown",r.appendChild(l),n.appendChild(i),n.appendChild(r),t.appendChild(n);const c=e=>{const t=document.createElement("div");t.className="skill-card-section";const n=document.createElement("div");return n.className="skill-section-label",n.textContent=e,t.appendChild(n),t},d=c("Package"),m=document.createElement("code");m.className="skill-package-code",m.textContent=e.name,d.appendChild(m),t.appendChild(d);const u=c("Tools"),g=document.createElement("div");g.className="skill-tags";const f=Array.isArray(e.tools)?e.tools:[];if(f.length>0)for(const e of f){const t=document.createElement("span");t.className="skill-tag",t.textContent=e,g.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No tools declared",g.appendChild(e)}u.appendChild(g),t.appendChild(u);const y=c("Permissions");if(Array.isArray(e.permissions)&&e.permissions.length>0){const t=document.createElement("div");t.className="skill-tags";for(const n of e.permissions){const e=document.createElement("span");e.className="skill-tag permissions",e.textContent=n,t.appendChild(e)}y.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No permissions declared",y.appendChild(e)}t.appendChild(y);const w=c("State"),b=document.createElement("div");b.className="skill-meta";const h=[["Bundled Version",e.version||"-"],["Configured Version",e.configuredVersion||"-"],["UI",e.hasUi?"has ui":"no ui"],["Config Keys",String(Object.keys(e.configJson||{}).length)]];for(const[e,t]of h){const n=document.createElement("div");n.className="skill-meta-row";const i=document.createElement("span");i.textContent=e;const o=document.createElement("strong");o.textContent=t,n.appendChild(i),n.appendChild(o),b.appendChild(n)}w.appendChild(b),t.appendChild(w),p.appendChild(t)}})(n),ni(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{tabBtn:f,titleNode:h,packageNode:E,enabledNode:I,versionNode:$,fallbackTitle:"GMO Coin Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-coin"}),ni(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{tabBtn:y,titleNode:k,packageNode:v,enabledNode:A,versionNode:M,fallbackTitle:"GMO FX Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-fx"});const i=n.find(e=>"@ai.weget.jp/skill-browser"===e.name);ni(i,{tabBtn:w,titleNode:x,packageNode:C,enabledNode:L,versionNode:T,fallbackTitle:"Browser Skill",fallbackPackage:"@ai.weget.jp/skill-browser"}),((e,t,n)=>{if(e){if(e.innerHTML="",0===t.length){const t=document.createElement("span");return t.className="muted",t.textContent=n,void e.appendChild(t)}for(const n of t){const t=document.createElement("span");t.className="skill-tag",t.textContent=n,e.appendChild(t)}}})(D,Array.isArray(i?.tools)?i.tools:[],"No browser tools declared."),U&&(U.innerHTML="");const o=n.find(e=>"@ai.weget.jp/skill-macro-economy"===e.name);ni(o,{tabBtn:b,titleNode:S,packageNode:N,enabledNode:B,versionNode:P,fallbackTitle:"Macro Economy Skill",fallbackPackage:"@ai.weget.jp/skill-macro-economy"}),await pi(),await ui(),ii(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{titleNode:Se,fallbackTitle:"FX Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:ve,inputNode:ue},fxApiKey:{labelNode:Ce,inputNode:ye},fxApiSecret:{labelNode:Ne,inputNode:we}}}),ii(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{titleNode:Ee,fallbackTitle:"Coin Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Ie,inputNode:ge},cryptoApiKey:{labelNode:Ae,inputNode:pe},cryptoApiSecret:{labelNode:Le,inputNode:fe}}}),e.session?(ci(),ki()):li("Login is required to load macro snapshot, calendar, and news.")};ke.addEventListener("click",async()=>{await ei(ke,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(ue.value||"0"),fxApiKey:String(ye.value||"").trim(),fxApiSecret:String(we.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-fx",e);if(!t.ok)return Gn(`[trade-config] fx save failed: ${t.error||"unknown"}`),Wn("error",Rn(t.error)),void await Xn("trade-config.fx.save",Rn(t.error),t);Gn("[trade-config] fx saved"),Wn("success","FX skill 配置已保存到本机。"),await fi()})}),xe.addEventListener("click",async()=>{await ei(xe,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(ge.value||"0"),cryptoApiKey:String(pe.value||"").trim(),cryptoApiSecret:String(fe.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-coin",e);if(!t.ok)return Gn(`[trade-config] coin save failed: ${t.error||"unknown"}`),Wn("error",Rn(t.error)),void await Xn("trade-config.coin.save",Rn(t.error),t);Gn("[trade-config] coin saved"),Wn("success","Coin skill 配置已保存到本机。"),await fi()})}),Me.addEventListener("click",async()=>{await ei(Me,async()=>{if(!window.botApi?.saveAiConfig)return;const e={aiModel:String(Be.value||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String($e.value||"").trim()},t=await window.botApi.saveAiConfig(e);if(!t.ok)return Gn(`[ai-config] save failed: ${t.error||"unknown"}`),Wn("error",Rn(t.error)),void await Xn("ai-config.save",Rn(t.error),t);Gn("[ai-config] saved"),Wn("success","AI 设置已保存。"),await fi()})}),Te.addEventListener("click",async()=>{await ei(Te,async()=>{if(!window.botApi?.startCodexLogin)return;const e=await window.botApi.startCodexLogin();if(!e.ok)return Gn(`[codex] login launch failed: ${e.detail||"unknown"}`),Wn("error",Rn(e.detail)),void await Xn("codex.login.launch",Rn(e.detail),e);Gn("[codex] login launched",{detail:e.detail}),Wn("success",e.detail||"Codex login started."),setTimeout(()=>{fi()},1500)})}),Pe.addEventListener("click",async()=>{const e=String(De.textContent||"codex login --device-auth").trim();try{if(!await(async e=>{if(navigator.clipboard?.writeText)return await navigator.clipboard.writeText(e),!0;const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly","true"),t.style.position="absolute",t.style.left="-9999px",document.body.appendChild(t),t.select();const n=document.execCommand("copy");return document.body.removeChild(t),n})(e))return void Wn("error","无法复制命令,请手动执行。");Gn("[codex] login command copied"),Wn("success","Codex 登录命令已复制。")}catch(e){const t=Rn(e instanceof Error?e.message:String(e));Gn("[codex] copy login command failed",{error:t}),Wn("error",t)}}),Ue.addEventListener("click",async()=>{await ei(Ue,async()=>{await fi(),Gn("[codex] auth status refreshed"),Wn("success","Codex 状态已刷新。")})}),Q?.addEventListener("click",async()=>{await ei(Q,async()=>{await ui(),Gn("[browser] chromium status refreshed"),Wn("success","Browser status 已刷新。")})}),ae?.addEventListener("click",async()=>{await ei(ae,async()=>{if(!window.botApi?.installPlaywrightMcp)return;const e=await window.botApi.installPlaywrightMcp();if(!e.ok)return Gn("[gateway] mcp install failed",{error:e.detail||e.error||"unknown"}),Wn("error",Rn(e.detail||e.error)),await Xn("gateway.install",Rn(e.detail||e.error),e),void await pi();Gn("[gateway] mcp configured",{detail:e.detail}),Wn("success",e.detail||"WeGet Gateway MCP configured."),await pi()})}),re?.addEventListener("click",async()=>{await ei(re,async()=>{await pi(),Wn("success","Gateway status 已刷新。")})}),se?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".gateway-test-btn");if(!n)return;const i=String(n.dataset.target||"").trim().toLowerCase();"gateway"!==i&&"codex_macro"!==i&&"browser"!==i&&"macro"!==i&&"coin"!==i&&"fx"!==i||await(async(e,t)=>{const n=window.botApi;n?.runGatewaySelfTest&&await ei(t,async()=>{const t=await n.runGatewaySelfTest({target:e}),i=se?.querySelector(`.gateway-test-badge[data-target="${e}"]`),o=se?.querySelector(`.gateway-test-summary[data-target="${e}"]`);if(i){const e=Boolean(t?.ok);i.textContent=e?"OK":"NG",i.classList.toggle("is-muted",!1),i.classList.toggle("is-disabled",!e)}if(o){const e=String(t?.summary||t?.error||t?.detail||"-").trim()||"-",n=String(t?.logPath||"").trim();o.textContent=n?`${e} [log] ${n}`:e}if(!t?.ok){const n=Rn(t?.summary||t?.error||t?.detail||`${e} test failed`);return Gn("[gateway] self-test failed",{target:e,error:n,logPath:t?.logPath||""}),void Wn("error",n)}Gn("[gateway] self-test ok",{target:e,summary:t?.summary||"",logPath:t?.logPath||""}),Wn("success",`${e} test completed.`)})})(i,n)}),Y?.addEventListener("click",async()=>{await ei(Y,async()=>{await ci({forceSnapshot:!0}),Gn("[macro] snapshot refreshed"),Wn("success","Macro snapshot 已刷新。")})}),R?.addEventListener("click",async()=>{await ei(R,async()=>{await ci(),Gn("[macro] calendar and news refreshed",{dateKey:String(K?.value||"").trim()}),Wn("success","Macro calendar / news 已刷新。")})}),K?.addEventListener("change",()=>{ci()}),be.addEventListener("click",()=>{const e="password"===fe.type?"text":"password";fe.type=e,be.textContent="password"===e?"显示":"隐藏"}),he.addEventListener("click",()=>{const e="password"===we.type?"text":"password";we.type=e,he.textContent="password"===e?"显示":"隐藏"}),e.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi)return;Zn();const t=l.value.trim(),n=s.value.trim(),i=c.value;if(t&&n&&i){je.disabled=!0,je.textContent="Loading...";try{const e=await window.botApi.login(t,i,n,d.checked);if(!e.ok)return Gn(`[ui] login failed: ${e.error||"unknown"}`),void Qn("error",Rn(e.error));Gn("[ui] login success"),Qn("success","登录成功。"),Wn("success","登录成功。"),await fi()}finally{je.disabled=!1,je.textContent="Login"}}}),Fe.addEventListener("click",async()=>{await ei(Fe,async()=>{if(!window.botApi)return;const e=await window.botApi.logout();if(!e.ok)return Gn(`[ui] logout failed: ${e.error||"unknown"}`),Wn("error",Rn(e.error)),void await Xn("auth.logout",Rn(e.error),e);Gn("[ui] logout"),Wn("success","已登出。"),on(!1),u.textContent="local mode (not logged in)",await fi()})});const yi=async({market:e,symbol:t,interval:n,canvas:i})=>{if(!window.botApi)return;const o=await window.botApi.openGmoKlineWindow({symbol:t,interval:n,market:e});if(!o.ok)return Gn(`[trade] open ${e} kline failed: ${o.error||"unknown"}`),Wn("error",Rn(o.error)),void await Xn(`${e}.kline`,Rn(o.error),o);const a=Array.isArray(o.candles)?o.candles:[],r=Ln(e);r.symbol=String(o.symbol||t||"").toUpperCase(),r.interval=n,r.candles=a.map(e=>({time:Number(e.time||0),open:Number(e.open||0),high:Number(e.high||0),low:Number(e.low||0),close:Number(e.close||0)})),r.lastFetchBucket=Math.floor(Date.now()/An(n)),ti(i,r.candles,Mn(e)),Gn("[trade] kline rendered",{market:e,symbol:o.symbol||t,interval:o.interval||n,candles:a.length}),Vn()},wi=async()=>{await yi({market:"coin",symbol:Lt,interval:It,canvas:Je})},bi=async()=>{await yi({market:"fx",symbol:Bt,interval:At,canvas:qe})},hi=({container:e,getCurrent:t,setCurrent:n,onChange:i})=>{const o=Array.from(e.querySelectorAll(".interval-btn"));for(const e of o)e.addEventListener("click",async()=>{const a=String(e.dataset.interval||"").trim();if(a&&a!==t()){n(a);for(const t of o)t.classList.toggle("is-active",t===e);await i()}})};_e.addEventListener("change",async()=>{Lt=String(_e.value||"BTC_JPY").trim().toUpperCase(),In("coin",Lt);try{await Nn("coin",Lt),await wi(),await On("coin")}catch(e){const t=Rn(e instanceof Error?e.message:String(e));Wn("error",t)}}),ze.addEventListener("change",async()=>{Bt=String(ze.value||"USD_JPY").trim().toUpperCase(),In("fx",Bt);try{await Nn("fx",Bt),await bi(),await On("fx")}catch(e){const t=Rn(e instanceof Error?e.message:String(e));Wn("error",t)}}),hi({container:Oe,getCurrent:()=>It,setCurrent:e=>{It=e},onChange:wi}),hi({container:He,getCurrent:()=>At,setCurrent:e=>{At=e},onChange:bi}),st.addEventListener("click",async()=>{await ei(st,async()=>{await jn("coin")})}),lt.addEventListener("click",async()=>{await ei(lt,async()=>{await jn("fx")})}),Ze.addEventListener("input",()=>{Sn("coin")}),et.addEventListener("input",()=>{Sn("fx")}),bt.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeCoinPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),l=wn(String(n.dataset.size||""));o&&Number.isFinite(s)&&!(s<=0)&&l?await ei(n,async()=>{const e=await i.closeCoinPosition({symbol:o,side:r,positionId:s,size:l});if(!e?.ok){const t=Rn(e?.error||"coin close order failed");return Gn("[coin] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,error:t}),Wn("error",t),void await Xn("coin.closeOrder",t,e)}Gn("[coin] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,result:e.result||null}),Wn("success","決済注文を送信しました。"),await jn("coin"),await On("coin")}):Wn("error","決済対象データが不正です。")}),kt.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeFxPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),l=String(n.dataset.size||"").trim();o&&Number.isFinite(s)&&!(s<=0)&&l?await ei(n,async()=>{const e=await i.closeFxPosition({symbol:o,side:r,positionId:s,size:l});if(!e?.ok){const t=Rn(e?.error||"fx close order failed");return Gn("[fx] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,error:t}),Wn("error",t),void await Xn("fx.closeOrder",t,e)}Gn("[fx] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,result:e.result||null}),Wn("success","決済注文を送信しました。"),await jn("fx"),await On("fx")}):Wn("error","決済対象データが不正です。")}),tt.addEventListener("click",async()=>{await Jn("BUY",tt)}),nt.addEventListener("click",async()=>{await Jn("SELL",nt)}),it.addEventListener("click",async()=>{await Hn("BUY",it)}),ot.addEventListener("click",async()=>{await Hn("SELL",ot)}),xt.addEventListener("click",async()=>{await ei(xt,async()=>{await On("coin")})}),St.addEventListener("click",async()=>{await ei(St,async()=>{await On("coin")})}),Et.addEventListener("click",async()=>{await ei(Et,async()=>{await On("fx")})}),vt.addEventListener("click",async()=>{await ei(vt,async()=>{await On("fx")})}),ce.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi?.sendChat)return;const t=String(de.value||"").trim();if(t){Kn("user",t),de.value="",me.disabled=!0,((e="AI")=>{Yn();const t=document.createElement("div");t.className="msg assistant assistant-thinking";const n=document.createElement("div");n.className="msg-title",n.textContent=`${e}:`;const i=document.createElement("div");i.className="thinking-body";const o=document.createElement("span");o.className="thinking-text",o.textContent="Thinking";const a=document.createElement("span");a.className="thinking-dots",a.innerHTML="<span></span><span></span><span></span>",i.append(o,a),t.append(n,i),le.appendChild(t),le.scrollTop=le.scrollHeight})("AI");try{const e=await window.botApi.sendChat(t);if(!e.ok)return Yn(),Kn("assistant",`Error: ${e.error||"unknown"}`),Wn("error",Rn(e.error)),void await Xn("ai.chat",Rn(e.error),e);Vn()}catch(e){Yn();const t=Rn(e instanceof Error?e.message:String(e));Kn("assistant",`Error: ${t}`),Wn("error",t),await Xn("ai.chat.exception",t,e)}finally{me.disabled=!1}}}),window.addEventListener("error",e=>{const t=Rn(e.error?.message||e.message||"Unknown UI error");Gn("[ui] uncaught error",{error:t}),Wn("error",t),Xn("ui.error",t,{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno})}),window.addEventListener("unhandledrejection",e=>{const t=e.reason instanceof Error?e.reason.message:String(e.reason||"Unhandled promise rejection"),n=Rn(t);Gn("[ui] unhandled rejection",{error:n}),Wn("error",n),Xn("ui.unhandledrejection",n,e.reason)}),window.botApi&&(window.botApi.onStatus(e=>{an(String(e.status||""))}),window.botApi.onChatEvent(e=>{if(!e)return;const t=Zt(e);if("line_user"===t.type){const e=t.lineUserId?`LINE(${String(t.lineUserId)})`:"LINE";return void Kn("line-user",String(t.text||""),e)}if("line_assistant"===t.type){const e=t.lineUserId?`AI->LINE(${String(t.lineUserId)})`:"AI->LINE";return void Kn("line-assistant",String(t.text||""),e)}"assistant_status"!==t.type?"assistant"===t.type&&(Yn(),Kn("assistant",String(t.text||""))):(e=>{const t=String(e||"").trim().replace(/\s+/g," ");if(!t)return;if(/^[{}[\],:]+$/.test(t))return;if(/^["']?[a-zA-Z0-9_-]+["']?\s*:\s*$/.test(t))return;const n=le.querySelector(".msg.assistant-thinking .thinking-text");n&&(n.textContent=t)})(String(t.status||"Thinking"))}),window.botApi.onTradeExecution(e=>{if(!e)return;const t=Zt(e),n="coin"===String(t.market||"").trim().toLowerCase()?"coin":"fx",i=String(t.symbol||"").trim().toUpperCase(),o=String(t.side||"").trim().toUpperCase()||"-",a=String(t.settleType||"").trim().toUpperCase()||"OPEN",r=String(t.executionSize??"").trim(),s=String(t.executionPrice??"").trim(),l="CLOSE"===a?"決済":"新規",c=`${i} ${o} ${r}${s?` @ ${s}`:""}`;Gn(`[${n}] execution filled`,{symbol:i,side:o,settleType:a,size:r,price:s,raw:t}),Wn("success",`約定成功 (${l}) ${c}`.trim()),jn(n).catch(()=>{}),On(n).catch(()=>{})}));const ki=async()=>{if(Ut&&!Dt&&!Ft){Ft=!0;try{if(!await async function(){let e=0;if(In("coin",Lt),In("fx",Bt),Cn("coin")){e+=1;try{await Nn("coin",Lt)}catch(e){const t=Rn(e instanceof Error?e.message:String(e));Gn("[coin] quote fetch failed",{error:t}),Wn("error",t)}await wi()}if(Cn("fx")){e+=1;try{await Nn("fx",Bt)}catch(e){const t=Rn(e instanceof Error?e.message:String(e));Gn("[fx] quote fetch failed",{error:t}),Wn("error",t)}await bi()}if(window.botApi?.getSymbolRules&&Cn("coin"))try{const e=await window.botApi.getSymbolRules({market:"coin"});if(e?.ok&&Array.isArray(e.rules)){Rt.clear();for(const t of e.rules){const e=String(t.symbol||"").trim().toUpperCase();e&&Rt.set(e,{minOrderSize:String(t.minOrderSize||""),maxOrderSize:String(t.maxOrderSize||""),sizeStep:String(t.sizeStep||"")})}}}catch(e){Gn("[coin] symbol rules fetch failed",{error:e instanceof Error?e.message:String(e)})}return e>0}())return;await jn(),await On(),Cn("coin")&&!Tt&&(Tt=setInterval(()=>{Dn("coin")},1e3)),Cn("fx")&&!Pt&&(Pt=setInterval(()=>{Dn("fx")},1e3)),Dt=!0}finally{Ft=!1}}};on(!1),(()=>{try{tn("true"===window.localStorage.getItem(en))}catch{tn(!1)}})(),K&&!K.value&&(K.value=dn(new Date)),(async()=>{if(!window.botApi?.getSavedProfile)return;const e=await window.botApi.getSavedProfile();e?.ok&&e.profile&&(s.value=e.profile.botId||"",l.value=e.profile.email||"",c.value=e.profile.password||"",d.checked=Boolean(e.profile.remember))})(),(async()=>{if(!window.botApi?.getSkillConfig||!window.botApi.getAiConfig)return;const[e,t,n]=await Promise.all([window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-fx"),window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-coin"),window.botApi.getAiConfig()]);e?.ok&&e.config&&(ue.value=String(e.config.riskDailyLossLimitJpy||5e4),ye.value=String(e.config.fxApiKey||""),we.value=String(e.config.fxApiSecret||"")),t?.ok&&t.config&&(ge.value=String(t.config.riskDailyLossLimitJpy||5e4),pe.value=String(t.config.cryptoApiKey||""),fe.value=String(t.config.cryptoApiSecret||"")),n?.ok&&n.config&&(Be.value=n.config.aiModel||"gpt-5.4",$e.value=n.config.logOutputDir||"")})(),fi();export{};
|
|
1
|
+
const e=document.getElementById("login-form"),t=document.getElementById("login-screen"),n=document.getElementById("login-notice"),i=document.getElementById("app-shell"),o=document.getElementById("sidebar-nav"),a=document.getElementById("sidebar-toggle-btn"),r=document.getElementById("user-notice"),s=document.getElementById("bot-id"),c=document.getElementById("email"),l=document.getElementById("password"),d=document.getElementById("remember-me"),m=document.getElementById("status"),u=document.getElementById("session"),g=document.getElementById("runtime-info"),p=document.getElementById("skill-state-list"),f=document.getElementById("tab-btn-skill-gmo-coin"),y=document.getElementById("tab-btn-skill-gmo-fx"),w=document.getElementById("tab-btn-skill-browser"),b=document.getElementById("tab-btn-skill-macro-economy"),h=document.getElementById("coin-skill-title"),k=document.getElementById("fx-skill-title"),x=document.getElementById("browser-skill-title"),S=document.getElementById("macro-skill-title"),v=document.getElementById("coin-skill-package"),E=document.getElementById("fx-skill-package"),C=document.getElementById("browser-skill-package"),I=document.getElementById("macro-skill-package"),A=document.getElementById("coin-skill-enabled"),N=document.getElementById("fx-skill-enabled"),L=document.getElementById("browser-skill-enabled"),B=document.getElementById("macro-skill-enabled"),$=document.getElementById("coin-skill-version"),M=document.getElementById("fx-skill-version"),T=document.getElementById("browser-skill-version"),P=document.getElementById("macro-skill-version"),D=document.getElementById("browser-skill-cards"),U=document.getElementById("browser-skill-tools"),F=document.getElementById("macro-snapshot-as-of"),j=document.getElementById("macro-fear-greed-score"),_=document.getElementById("macro-snapshot-hint"),z=document.getElementById("macro-coin-cards"),O=document.getElementById("macro-fx-cards"),H=document.getElementById("macro-coin-heatmap-treemap"),J=document.getElementById("macro-fx-heatmap-treemap"),q=document.getElementById("macro-calendar-body"),G=document.getElementById("macro-news-body"),K=document.getElementById("macro-date-key"),Y=document.getElementById("macro-refresh-snapshot-btn"),R=document.getElementById("macro-refresh-day-btn"),W=document.getElementById("browser-chromium-status"),X=document.getElementById("browser-chromium-detail"),V=document.getElementById("browser-prereq-hint"),Q=document.getElementById("browser-refresh-playwright-mcp-btn"),Z=document.getElementById("logs"),ee=document.getElementById("gateway-codex-auth"),te=document.getElementById("gateway-mcp-status"),ne=document.getElementById("gateway-browser-status"),ie=document.getElementById("gateway-context-file"),oe=document.getElementById("gateway-detail"),ae=document.getElementById("gateway-install-btn"),re=document.getElementById("gateway-refresh-btn"),se=document.getElementById("gateway-skill-grid"),ce=document.getElementById("messages"),le=document.getElementById("active-tasks-list"),de=document.getElementById("chat-form"),me=document.getElementById("chat-input"),ue=document.getElementById("send-btn"),ge=document.getElementById("fx-risk-daily-loss-limit-jpy"),pe=document.getElementById("coin-risk-daily-loss-limit-jpy"),fe=document.getElementById("crypto-api-key"),ye=document.getElementById("crypto-api-secret"),we=document.getElementById("fx-api-key"),be=document.getElementById("fx-api-secret"),he=document.getElementById("toggle-crypto-secret-btn"),ke=document.getElementById("toggle-fx-secret-btn"),xe=document.getElementById("save-fx-config-btn"),Se=document.getElementById("save-coin-config-btn"),ve=document.getElementById("fx-config-title"),Ee=document.getElementById("coin-config-title"),Ce=document.getElementById("fx-config-label-riskDailyLossLimitJpy"),Ie=document.getElementById("fx-config-label-fxApiKey"),Ae=document.getElementById("fx-config-label-fxApiSecret"),Ne=document.getElementById("coin-config-label-riskDailyLossLimitJpy"),Le=document.getElementById("coin-config-label-cryptoApiKey"),Be=document.getElementById("coin-config-label-cryptoApiSecret"),$e=document.getElementById("ai-model"),Me=document.getElementById("host-log-output-dir"),Te=document.getElementById("save-ai-config-btn"),Pe=document.getElementById("codex-login-btn"),De=document.getElementById("codex-copy-login-btn"),Ue=document.getElementById("codex-refresh-auth-btn"),Fe=document.getElementById("codex-login-command"),je=document.getElementById("logout-btn"),_e=document.getElementById("login-btn"),ze=document.getElementById("coin-symbol-select"),Oe=document.getElementById("fx-symbol-select"),He=document.getElementById("coin-kline-intervals"),Je=document.getElementById("fx-kline-intervals"),qe=document.getElementById("coin-kline-canvas"),Ge=document.getElementById("fx-kline-canvas"),Ke=document.getElementById("coin-market-icon"),Ye=document.getElementById("fx-market-icon"),Re=document.getElementById("coin-order-bid"),We=document.getElementById("coin-order-ask"),Xe=document.getElementById("coin-order-spread"),Ve=document.getElementById("fx-order-bid"),Qe=document.getElementById("fx-order-ask"),Ze=document.getElementById("fx-order-spread"),et=document.getElementById("coin-order-qty"),tt=document.getElementById("fx-order-qty"),nt=document.getElementById("coin-buy-btn"),it=document.getElementById("coin-sell-btn"),ot=document.getElementById("fx-buy-btn"),at=document.getElementById("fx-sell-btn"),rt=document.getElementById("coin-required-amount"),st=document.getElementById("fx-required-amount"),ct=document.getElementById("coin-account-info-refresh-btn"),lt=document.getElementById("fx-account-info-refresh-btn"),dt=document.getElementById("coin-account-pnl"),mt=document.getElementById("coin-account-margin"),ut=document.getElementById("coin-account-available"),gt=document.getElementById("coin-account-margin-ratio"),pt=document.getElementById("fx-account-pnl"),ft=document.getElementById("fx-account-margin"),yt=document.getElementById("fx-account-available"),wt=document.getElementById("fx-account-margin-ratio"),bt=document.getElementById("coin-position-summary-body"),ht=document.getElementById("coin-position-list-body"),kt=document.getElementById("fx-position-summary-body"),xt=document.getElementById("fx-position-list-body"),St=document.getElementById("coin-position-summary-refresh-btn"),vt=document.getElementById("coin-position-list-refresh-btn"),Et=document.getElementById("fx-position-summary-refresh-btn"),Ct=document.getElementById("fx-position-list-refresh-btn"),It=Array.from(document.querySelectorAll(".tab-btn")),At=Array.from(document.querySelectorAll(".tab-panel"));let Nt="15m",Lt="15m",Bt=String(ze?.value||"BTC_JPY").trim().toUpperCase(),$t=String(Oe?.value||"USD_JPY").trim().toUpperCase(),Mt=null,Tt=null,Pt=null,Dt=null,Ut=!1,Ft=!1,jt=!1,_t=!1,zt=!1,Ot="",Ht="",Jt=0,qt=0,Gt=null,Kt=null,Yt=[],Rt=!1;const Wt=new Map,Xt={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1},Vt={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1};let Qt=[],Zt=[];const en=new Map;let tn=null;const nn=e=>e&&"object"==typeof e?e:{},on="weget.bot.sidebar.collapsed",an=e=>{i.classList.toggle("sidebar-collapsed",e),a&&(a.textContent=e?"▶":"◀",a.setAttribute("aria-label",e?"Expand menu":"Collapse menu")),o&&o.setAttribute("data-collapsed",e?"true":"false")},rn=e=>{for(const t of It)t.classList.toggle("is-active",t.dataset.tab===e);for(const t of At)t.classList.toggle("is-active",t.id===`tab-${e}`)};for(const e of It)e.addEventListener("click",()=>rn(e.dataset.tab||"coin"));a?.addEventListener("click",()=>{const e=!i.classList.contains("sidebar-collapsed");an(e);try{window.localStorage.setItem(on,String(e))}catch{}});const sn=e=>{const n=Ut!==e;Ut=e,t.classList.toggle("hidden",e),i.classList.toggle("hidden",!e),e&&ai(),e?n&&Ii():(Jn(),Ft=!1)},cn=e=>{const t=String(e||"disconnected").trim().toLowerCase();m.textContent=t,m.classList.remove("status-connected","status-disconnected","status-connecting"),"connected"!==t?"reconnecting"!==t&&"connecting"!==t?m.classList.add("status-disconnected"):m.classList.add("status-connecting"):m.classList.add("status-connected")},ln=(e,t=6)=>{if(null===e||!Number.isFinite(e))return"-";const n=Math.abs(e);return n>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:3}):n>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:Math.min(t,5)}):e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:t})},dn=e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,mn=e=>`${e>0?"+":""}${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,un=e=>!Number.isFinite(e)||e<=0||e>1e5?"- %":(e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:2})} %`)(e),gn=e=>{const t=e instanceof Date?e:new Date(e);if(Number.isNaN(t.getTime()))return"";return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`},pn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t);return Number.isNaN(n.getTime())?t:n.toLocaleString("ja-JP",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})},fn=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),yn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t).getTime();if(!Number.isFinite(n))return t;const i=Math.max(0,Math.floor((Date.now()-n)/1e3));if(i<5)return"just now";if(i<60)return`${i}s ago`;const o=Math.floor(i/60);if(o<60)return`${o}m ago`;const a=Math.floor(o/60);return a<24?`${a}h ago`:`${Math.floor(a/24)}d ago`},wn=()=>{if(!le)return;const e=Array.from(en.values()).sort((e,t)=>{const n=new Date(String(e.lastProgressAt||e.startedAt||0)).getTime();return new Date(String(t.lastProgressAt||t.startedAt||0)).getTime()-n});e.length?le.innerHTML=e.map(e=>{const t=fn(e.title||e.prompt||e.taskId),n=fn(e.status||"Running"),i=fn(e.source||"-"),o=fn(e.channel||"-"),a=fn(yn(e.startedAt)),r=fn(yn(e.lastProgressAt)),s=fn(e.conversationId||"-"),c=fn(e.lineUserId||"-");return`\n <section class="active-task-card" data-task-id="${fn(e.taskId)}">\n <div class="active-task-meta">\n <span class="active-task-badge">${o}</span>\n <span class="active-task-badge">${i}</span>\n </div>\n <div class="active-task-title">${t}</div>\n <div class="active-task-status">${n}</div>\n <div class="active-task-subtext">Started ${a} · Last update ${r}</div>\n <div class="active-task-subtext">Task ${fn(e.taskId)} · Conv ${s}</div>\n ${e.lineUserId?`<div class="active-task-subtext">LINE user ${c}</div>`:""}\n <div class="active-task-actions">\n <button type="button" class="secondary active-task-cancel-btn" data-task-id="${fn(e.taskId)}" ${!1===e.canCancel?"disabled":""}>Cancel</button>\n </div>\n </section>\n `}).join(""):le.innerHTML='<div class="active-task-empty">No active tasks.</div>'},bn=e=>{if(e)if("snapshot"!==e.type){if("upsert"===e.type&&e.task?.taskId)return en.set(e.task.taskId,e.task),void wn();"remove"===e.type&&e.taskId&&(en.delete(e.taskId),wn())}else{en.clear();for(const t of Array.isArray(e.tasks)?e.tasks:[])t?.taskId&&en.set(t.taskId,t);wn()}},hn=e=>{const t=String(e||"").trim();if(!t)return{cleaned:"",impact:"",direction:""};const n=t.indexOf("\n"),i=n>=0?t.slice(0,n).trim():t,o=n>=0?t.slice(n+1).trim():"";if(i.startsWith("{")&&i.endsWith("}"))try{const e=JSON.parse(i);return{cleaned:o,impact:String(e.impact||"").trim().toLowerCase(),direction:String(e.usd_jpy_direction||"").trim().toLowerCase()}}catch{}return{cleaned:t,impact:"",direction:""}},kn=e=>{const t=String(e||"").trim().toLowerCase();if(!t)return"-";const n="high"===t?"High":"medium"===t?"Medium":"low"===t?"Low":t;return`<span class="macro-impact-badge ${fn(t)}">${fn(n)}</span>`},xn=e=>{const t=Math.max(0,Math.min(3,Math.round(Number(e||0))));return t<=0?"-":`<span class="macro-importance-stars">${"★".repeat(t)}</span>`},Sn=e=>{const t=String(e||"").trim().toLowerCase();return t?"up"===t?'<span class="macro-direction up">↑↑</span>':"down"===t?'<span class="macro-direction down">↓↓</span>':"sideways"===t?'<span class="macro-direction flat">→</span>':fn(t):"-"},vn=e=>{const t=String(e||"").trim().replace(/,/g,"");if(!t)return"";if(!/^\d+(\.\d+)?$/.test(t))return"";if(!t.includes("."))return t;return t.replace(/(\.\d*?[1-9])0+$/,"$1").replace(/\.0+$/,"").replace(/\.$/,"")},En=e=>{const t=String(e||"").trim(),n=t.indexOf(".");return n>=0?t.length-n-1:0},Cn=(e,t)=>{const n=String(e||"").trim(),i=n.startsWith("-"),o=i?n.slice(1):n,[a,r=""]=o.split("."),s=(r+"0".repeat(t)).slice(0,t),c=BigInt((a||"0")+s);return i?-c:c},In=e=>{const t=Ln("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),n=nn(t?.configJson);return"fx"===e?Boolean(String(n.fxApiKey||we.value||"").trim()&&String(n.fxApiSecret||be.value||"").trim()):Boolean(String(n.cryptoApiKey||fe.value||"").trim()&&String(n.cryptoApiSecret||ye.value||"").trim())},An=({symbol:e,quote:t,orderBidNode:n,orderAskNode:i,orderSpreadNode:o})=>{if(!t)return n.textContent="-",i.textContent="-",void(o.textContent="-");const a=ln(t.bid),r=ln(t.ask);n.textContent=a,i.textContent=r,o.textContent=ln(t.spread,8)},Nn=e=>{if("coin"===e){const e=Number(String(et.value||"").trim()),t=Gt;return!Number.isFinite(e)||e<=0||null===t||!Number.isFinite(t)?void(rt.textContent="- 円"):void(rt.textContent=dn(e*t/2))}const t=Number(String(tt.value||"").trim()),n=Kt;!Number.isFinite(t)||t<=0||null===n||!Number.isFinite(n)?st.textContent="- 円":st.textContent=dn(t*n/20)},Ln=e=>Yt.find(t=>t.name===e),Bn=e=>{const t=Ln(e);if(!t)return!1;const n=String(t.installStatus||"").trim().toLowerCase();return t.enabled&&"active"===n},$n=e=>Bn("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),Mn=async(e,t)=>{if(!window.botApi?.getMarketQuotes)return null;const n=String(t||"").trim().toUpperCase(),i=await window.botApi.getMarketQuotes({market:e,symbols:[n]});if(!i?.ok)throw new Error(String(i?.error||"quote api failed"));const o=(i.quotes||[]).find(e=>String(e.symbol||"").toUpperCase()===n)||null;return"coin"===e?(An({symbol:n,quote:o,orderBidNode:Re,orderAskNode:We,orderSpreadNode:Xe}),Gt=o?.ask??null,Nn("coin")):(An({symbol:n,quote:o,orderBidNode:Ve,orderAskNode:Qe,orderSpreadNode:Ze}),Kt=o?.ask??null,Nn("fx")),o},Tn=(e,t)=>{const n=String(t||"").trim().toUpperCase();if(!n)return;if("coin"===e){const e=n.replace("_","-").toLowerCase();return void(Ke.src=`https://coin.z.com/jp/member/imgs/icon-${e}.svg`)}const[i,o]=n.split("_"),a="JPY"===o?i:`${i}_${o}`;Ye.src=`https://coin.z.com/jp/member/imgs/fx/icon_${a}.svg`},Pn=e=>"5m"===e?3e5:"15m"===e?9e5:"30m"===e?18e5:"1h"===e?36e5:6e4,Dn=e=>"coin"===e?Xt:Vt,Un=e=>"coin"===e?qe:Ge,Fn=e=>"coin"===e?Bt:$t,jn=e=>{const t=Fn(e),n=("coin"===e?Qt:Zt).filter(e=>String(e.symbol||"").trim().toUpperCase()===t),i=[];for(const e of n){const t=String(e.side||"").trim().toUpperCase(),n=Number(e.averagePositionRate||0);if(!Number.isFinite(n)||n<=0)continue;const o="BUY"===t;i.push({price:n,label:o?"BUY Avg":"SELL Avg",color:o?"#1f9d55":"#e03131"})}return i},_n=e=>{const t=Dn(e);t.candles.length&&si(Un(e),t.candles,jn(e))},zn=(e,t)=>{if(!t)return;const n=Dn(e);if(!n.candles.length)return;if(n.symbol!==t.symbol)return;const i=n.candles[n.candles.length-1],o=null!==t.bid&&null!==t.ask?(t.bid+t.ask)/2:null!==t.ask?t.ask:t.bid;null!==o&&Number.isFinite(o)&&(i.close=o,i.high=Math.max(i.high,o),i.low=Math.min(i.low,o),si(Un(e),n.candles,jn(e)))},On=async e=>{const t=Dn(e);if(!t.interval||!t.symbol||t.fetching)return;if(t.symbol!==Fn(e))return;if(!(Math.floor(Date.now()/Pn(t.interval))<=t.lastFetchBucket)){t.fetching=!0;try{"coin"===e?await vi():await Ei()}finally{t.fetching=!1}}},Hn=async e=>{if($n(e))if("coin"!==e){if(!zt){zt=!0;try{const e=await Mn("fx",$t);zn("fx",e),await On("fx")}catch(e){const t=ei(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Ht||n-qt>15e3)&&(Vn("[fx] quote poll failed",{error:t}),Ht=t,qt=n)}finally{zt=!1}}}else{if(_t)return;_t=!0;try{const e=await Mn("coin",Bt);zn("coin",e),await On("coin")}catch(e){const t=ei(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Ot||n-Jt>15e3)&&(Vn("[coin] quote poll failed",{error:t}),Ot=t,Jt=n)}finally{_t=!1}}},Jn=()=>{Pt&&(clearInterval(Pt),Pt=null),Dt&&(clearInterval(Dt),Dt=null)},qn=async e=>{if(window.botApi?.getAccountMetrics){if((!e||"coin"===e)&&$n("coin"))if(In("coin")){const e=await window.botApi.getAccountMetrics({market:"coin"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);dt.textContent=n>0?mn(i):dn(0),mt.textContent=dn(n),ut.textContent=dn(t),gt.textContent=un(Number(e.marginRatio||0))}else Vn("[coin] account metrics fetch failed",{error:e?.error||"unknown error"})}else dt.textContent=dn(0),mt.textContent="- 円",ut.textContent="- 円",gt.textContent="- %";if((!e||"fx"===e)&&$n("fx"))if(In("fx")){const e=await window.botApi.getAccountMetrics({market:"fx"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);pt.textContent=n>0?mn(i):dn(0),ft.textContent=dn(n),yt.textContent=dn(t),wt.textContent=un(Number(e.marginRatio||0))}else Vn("[fx] account metrics fetch failed",{error:e?.error||"unknown error"})}else pt.textContent=dn(0),ft.textContent="- 円",yt.textContent="- 円",wt.textContent="- %"}},Gn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").toUpperCase(),o=n.replace("_","/"),a=String(i.side||"-"),r=String(i.size||"").trim(),s=Number(r||0),c=Number(i.price||0),l=Number(i.lossGain||0),d=Number(i.totalSwap||0),m=String(i.timestamp||"-"),u=Number(i.positionId||0),g=Number.isFinite(u)&&u>0&&r?`data-market="${e}" data-symbol="${n}" data-side="${String(a||"").toUpperCase()}" data-position-id="${u}" data-size="${r}"`:"disabled",p=document.createElement("tr");p.innerHTML=`\n <td><button type="button" class="close-btn" ${g}>決済</button></td>\n <td>${o}<br />${a}</td>\n <td>${s.toLocaleString("ja-JP")}</td>\n <td>${c.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${mn(l)}<br />${mn(d)}</td>\n <td>${m.replace("T"," ").slice(0,19)}</td>\n `,t.appendChild(p)}}else t.innerHTML='<tr><td colspan="6" class="empty-cell">対象のお取引はございません。</td></tr>'},Kn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").replace("_","/"),o=String(i.side||"-"),a=Number("fx"===e?i.sumPositionSize||0:i.sumPositionQuantity||0),r=Number(i.averagePositionRate||0),s=Number(i.positionLossGain||0),c=Number("fx"===e&&i.sumTotalSwap||0),l=document.createElement("tr");l.innerHTML=`\n <td><button type="button" class="close-btn">決済</button></td>\n <td>${n}<br />${o}</td>\n <td>${a.toLocaleString("ja-JP")}</td>\n <td>${r.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${mn(s)}<br />${mn(c)}</td>\n `,t.appendChild(l)}}else t.innerHTML='<tr><td colspan="5" class="empty-cell">対象のお取引はございません。</td></tr>'},Yn=async e=>{if(window.botApi?.getPositionSummary&&window.botApi?.getOpenPositions){if((!e||"coin"===e)&&$n("coin"))if(In("coin")){const e=await window.botApi.getPositionSummary({market:"coin",symbol:Bt});e?.ok?(Qt=Array.isArray(e.items)?e.items:[],Kn("coin",bt,Qt),_n("coin")):(Vn("[coin] position summary fetch failed",{error:e?.error||"unknown error"}),Qt=[],Kn("coin",bt,[]),_n("coin"))}else Qt=[],Kn("coin",bt,[]),_n("coin");if((!e||"fx"===e)&&$n("fx"))if(In("fx")){const e=await window.botApi.getPositionSummary({market:"fx"});e?.ok?(Zt=Array.isArray(e.items)?e.items:[],Kn("fx",kt,Zt),_n("fx")):(Vn("[fx] position summary fetch failed",{error:e?.error||"unknown error"}),Zt=[],Kn("fx",kt,[]),_n("fx"))}else Zt=[],Kn("fx",kt,[]),_n("fx");if((!e||"coin"===e)&&$n("coin"))if(In("coin")){const e=await window.botApi.getOpenPositions({market:"coin",symbol:Bt,page:1,count:100});e?.ok?Gn("coin",ht,Array.isArray(e.items)?e.items:[]):(Vn("[coin] open positions fetch failed",{error:e?.error||"unknown error"}),Gn("coin",ht,[]))}else Gn("coin",ht,[]);if((!e||"fx"===e)&&$n("fx"))if(In("fx")){const e=await window.botApi.getOpenPositions({market:"fx",count:100});e?.ok?Gn("fx",xt,Array.isArray(e.items)?e.items:[]):(Vn("[fx] open positions fetch failed",{error:e?.error||"unknown error"}),Gn("fx",xt,[]))}else Gn("fx",xt,[])}},Rn=async(e,t)=>{const n=window.botApi;if(!n?.placeFxOrder)return;const i=String(tt.value||"").trim();i?await ri(t,async()=>{const t=await n.placeFxOrder({symbol:$t,side:e,size:i});if(!t?.ok){const n=ei(t?.error||"fx order failed");return Vn("[fx] order failed",{symbol:$t,side:e,size:i,error:n}),ti("error",n),void await ni("fx.order",n,t)}Vn("[fx] order placed",{symbol:$t,side:e,size:i,result:t.result||null}),ti("success",`${e} 注文を送信しました。`),await qn("fx"),await Yn("fx")}):ti("error","数量を入力してください。")},Wn=async(e,t)=>{const n=window.botApi;if(!n?.placeCoinOrder)return;const i=vn(String(et.value||""));if(!i)return void ti("error","数量の形式が不正です。");const o=((e,t)=>{const n=Wt.get(e);if(!n)return{ok:!0};const i=vn(n.minOrderSize),o=vn(n.sizeStep),a=vn(n.maxOrderSize);if(!i||!o)return{ok:!0};const r=Math.max(En(t),En(i),En(o),En(a)),s=Cn(t,r),c=Cn(i,r),l=Cn(o,r);if(s<c)return{ok:!1,reason:`${e} の最小数量は ${i} です。`};if(l>0n&&s%l!==0n)return{ok:!1,reason:`${e} の数量刻みは ${o} です。`};if(a&&s>Cn(a,r))return{ok:!1,reason:`${e} の最大数量は ${a} です。`};return{ok:!0}})(Bt,i);o.ok?await ri(t,async()=>{const t=await n.placeCoinOrder({symbol:Bt,side:e,size:i});if(!t?.ok){const n=ei(t?.error||"coin order failed");return Vn("[coin] order failed",{symbol:Bt,side:e,size:i,error:n}),ti("error",n),void await ni("coin.order",n,t)}Vn("[coin] order placed",{symbol:Bt,side:e,size:i,result:t.result||null}),ti("success",`${e} 注文を送信しました。`),await qn("coin"),await Yn("coin")}):ti("error",o.reason||"数量が取引ルールに一致しません。")},Xn=e=>{const t=String(e||"").toLowerCase();return t.includes("debug")||t.includes("[debug]")?"debug":t.includes("error")||t.includes("failed")||t.includes("exception")?"error":"info"},Vn=(e,t=null,n=null)=>{if(window.botApi?.writeLog&&window.botApi.writeLog({level:Xn(e),source:"ui.log",message:e,details:{data:t,ts:n||(new Date).toISOString()}}).catch(()=>{}),!Z)return;const i=document.createElement("div");i.className="log-entry";const o=document.createElement("div");o.className="log-line";const a=document.createElement("span");if(a.className="log-ts",a.textContent=n||(new Date).toISOString(),o.appendChild(a),o.append(document.createTextNode(e||"")),i.appendChild(o),null!=t&&""!==t){const e=document.createElement("pre");e.className="status-output",e.textContent="string"==typeof t?t:JSON.stringify(t,null,2),i.appendChild(e)}for(Z.appendChild(i);Z.children.length>300;)Z.removeChild(Z.firstChild);Z.scrollTop=Z.scrollHeight},Qn=(e,t,n="")=>{const i=document.createElement("div");i.className=`msg ${e}`;let o=n||("user"===e?"You":"Assistant");"line-user"===e&&(o=n||"LINE User"),"line-assistant"===e&&(o=n||"AI"),i.textContent=`${o}: ${t}`,ce.appendChild(i),ce.scrollTop=ce.scrollHeight},Zn=()=>{const e=ce.querySelector(".msg.assistant-thinking");e&&e.remove()},ei=e=>{const t=String(e||"").trim(),n=t.toLowerCase();return t?n.includes("api key")&&n.includes("missing")?"API Key 未设置,请先在 Config 页面保存。":n.includes("auth")||n.includes("401")||n.includes("403")?"认证失败,请检查 API Key 和 Secret。":n.includes("network")||n.includes("fetch failed")||n.includes("timeout")?"网络连接异常,请检查网络后重试。":n.includes("message is required")?"请输入内容后再发送。":t:"处理失败,请稍后重试。"},ti=(e,t)=>{Mt&&(clearTimeout(Mt),Mt=null),r.classList.remove("hidden","notice-error","notice-success"),r.classList.add("error"===e?"notice-error":"notice-success"),r.textContent=t,Mt=setTimeout(()=>{ii()},"success"===e?2200:4200)},ni=async(e,t,n=null)=>{window.botApi?.writeErrorLog&&await window.botApi.writeErrorLog({source:e,message:String(t||""),details:n})},ii=()=>{Mt&&(clearTimeout(Mt),Mt=null),r.classList.add("hidden"),r.classList.remove("notice-error","notice-success"),r.textContent=""},oi=(e,t)=>{Tt&&(clearTimeout(Tt),Tt=null),n.classList.remove("hidden","notice-error","notice-success"),n.classList.add("error"===e?"notice-error":"notice-success"),n.textContent=t,Tt=setTimeout(()=>{ai()},"success"===e?2200:4200)},ai=()=>{Tt&&(clearTimeout(Tt),Tt=null),n.classList.add("hidden"),n.classList.remove("notice-error","notice-success"),n.textContent=""},ri=async(e,t)=>{if(e.disabled)return;e.disabled=!0,e.classList.add("is-loading");const n=e.textContent||"";try{await t()}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[ui] action failed",{error:t}),ti("error",t)}finally{e.classList.remove("is-loading"),e.textContent=n,e.disabled=!1}},si=(e,t,n=[])=>{const i=e.getContext("2d");if(!i)return;const o=e.width,a=e.height;if(i.clearRect(0,0,o,a),!t.length)return;const r=Math.max(...t.map(e=>e.high)),s=Math.min(...t.map(e=>e.low)),c=n.map(e=>e.price).filter(e=>Number.isFinite(e)),l=c.length?Math.max(r,...c):r,d=c.length?Math.min(s,...c):s,m=Math.max(1e-8,l-d),u=10,g=10,p=o-10-62-8,f=a-10-24-8,y=Math.max(2,p/t.length),w=e=>g+(l-e)/m*f,b=e=>e>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:0}):e>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:3}):e.toLocaleString("en-US",{minimumFractionDigits:5,maximumFractionDigits:5});i.strokeStyle="#c9d4ea",i.strokeRect(.5,.5,o-1,a-1),i.strokeStyle="#e3eaf5",i.lineWidth=1;for(let e=0;e<=4;e+=1){const t=g+f/4*e;i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke()}t.forEach((e,t)=>{const n=u+t*y+.5*y,o=w(e.high),a=w(e.low),r=w(e.open),s=w(e.close),c=e.close>=e.open;i.strokeStyle=c?"#1f9d55":"#d64545",i.fillStyle=c?"#1f9d55":"#d64545",i.lineWidth=1,i.beginPath(),i.moveTo(n,o),i.lineTo(n,a),i.stroke();const l=n-.3*y,d=Math.max(1,.6*y),m=Math.min(r,s),g=Math.max(1,Math.abs(s-r));i.fillRect(l,m,d,g)});for(const e of n){const t=w(e.price);i.save(),i.setLineDash([4,4]),i.strokeStyle=e.color,i.lineWidth=1,i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke(),i.restore(),i.font="12px Segoe UI";const n=`${e.label} ${b(e.price)}`,o=Math.ceil(i.measureText(n).width)+10,a=14,r=Math.max(12,Math.min(g+f-18,t-9));i.fillStyle=e.color,i.fillRect(a,r,o,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(n,a+5,r+9)}const h=t[t.length-1],k=h?.close;if(Number.isFinite(k)){const e=w(Number(k));i.save(),i.setLineDash([5,4]),i.strokeStyle="#2b79ff",i.lineWidth=1,i.beginPath(),i.moveTo(u,e),i.lineTo(u+p,e),i.stroke(),i.restore();const t=b(Number(k));i.font="12px Segoe UI";const n=Math.ceil(i.measureText(t).width)+10,o=u+p+6,a=Math.max(12,Math.min(g+f-18,e-9));i.fillStyle="#2b79ff",i.fillRect(o,a,n,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(t,o+5,a+9)}i.fillStyle="#66758d",i.font="12px Segoe UI",i.textBaseline="middle",i.textAlign="left";for(let e=0;e<=4;e+=1){const t=l-m/4*e,n=g+f/4*e;i.fillText(b(t),u+p+8,n)}const x=e=>{const t=new Date(e>1e12?e:1e3*e);if(Number.isNaN(t.getTime()))return"-";const n=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${n}:${i}`};i.textAlign="center",i.textBaseline="top";for(let e=0;e<=4;e+=1){const n=Math.min(t.length-1,Math.floor((t.length-1)*(e/4))),o=u+p*e/4;i.fillText(x(t[n]?.time||0),o,g+f+6)}},ci=(e,t)=>{const{tabBtn:n,titleNode:i,packageNode:o,enabledNode:a,versionNode:r,fallbackTitle:s,fallbackPackage:c}=t,l=String(e?.ui?.surfaceTitle||e?.displayName||s).trim()||s,d=e?.name||c,m=String(e?.ui?.tabLabel||d.replace("@ai.weget.jp/","")).trim()||d.replace("@ai.weget.jp/",""),u=!!e&&Boolean(e.enabled),g=String(e?.installStatus||"uninstalled").trim()||"uninstalled",p=String(e?.configuredVersion||e?.version||"-").trim()||"-";if(n){const e=n.querySelector(".tab-btn-label");e&&(e.textContent=m),n.classList.toggle("is-disabled",!u)}i&&(i.textContent=l),o&&(o.textContent=d),a&&(a.textContent=u?"enabled":"disabled",a.classList.toggle("is-disabled",!u)),r&&(r.textContent=`${g} · ${p}`)},li=(e,t)=>{t.titleNode&&(t.titleNode.textContent=String(e?.ui?.configTitle||t.fallbackTitle).trim()||t.fallbackTitle);const n=Array.isArray(e?.ui?.configFields)&&e.ui?.configFields||[];for(const[e,i]of Object.entries(t.fields)){const t=n.find(t=>t.key===e);t?.label&&i.labelNode&&(i.labelNode.textContent=t.label),void 0!==t?.placeholder&&(i.inputNode.placeholder=t.placeholder||"")}},di=(e,t)=>{if(e){if(e.innerHTML="",!t.length){const t=document.createElement("span");return t.className="muted",t.textContent="No macro snapshot cards available.",void e.appendChild(t)}for(const n of t){const t=document.createElement("div");t.className="macro-chip"+("down"===n.trend?" is-negative":"");const i=document.createElement("div");i.className="macro-chip-code",i.textContent=n.code||"-";const o=document.createElement("div");o.className="macro-chip-value",o.textContent=n.value||"-";const a=document.createElement("div");a.className="macro-chip-delta"+("down"===n.trend?" is-negative":""),a.textContent=n.delta||"-",t.append(i,o,a),e.appendChild(t)}}},mi=(e,t,n)=>{if(!e)return;if(e.innerHTML="",!t.length){const t=document.createElement("div");return t.className="macro-treemap-empty",t.textContent=n,void e.appendChild(t)}const i=[...t].map(e=>({symbol:String(e.symbol||"").trim(),change:Number(e.change||0),weight:Math.max(1,Math.abs(Number(e.change||0)))})).filter(e=>e.symbol).sort((e,t)=>Math.abs(t.change)-Math.abs(e.change)).slice(0,12),o=[],a=i.reduce((e,t)=>e+t.weight,0)||1,r=(e,t,n,i,a,s)=>{if(!e.length)return;if(1===e.length)return void o.push({item:e[0],x:t,y:n,width:i,height:a});const c=e.reduce((e,t)=>e+t.weight,0);let l=1,d=e[0].weight;for(;l<e.length-1&&d<c/2;)l+=1,d+=e[l-1].weight;const m=e.slice(0,l),u=e.slice(l),g=d/c;if(s){const e=i*g;return r(m,t,n,e,a,!s),void r(u,t+e,n,i-e,a,!s)}const p=a*g;r(m,t,n,i,p,!s),r(u,t,n+p,i,a-p,!s)};r(i,0,0,100,100,!0);for(const{item:t,x:n,y:i,width:r,height:s}of o){const o=document.createElement("div"),c=Math.min(.88,.22+t.weight/Math.max(.18*a,1));o.className="macro-treemap-tile "+(t.change>=0?"up":"down"),o.style.setProperty("--tile-intensity",String(c)),o.style.left=`${n}%`,o.style.top=`${i}%`,o.style.width=`${r}%`,o.style.height=`${s}%`,o.innerHTML=`\n <div class="macro-treemap-symbol">${fn(t.symbol.replace(/_JPY$/i,"").replace(/_USD$/i," $"))}</div>\n <div class="macro-treemap-change">${t.change>=0?"+":""}${t.change.toFixed(1)}%</div>\n `,e.appendChild(o)}},ui=e=>{if(q)if(q.innerHTML="",e.length)for(const t of e){const e=hn(t.ai_analyzer||""),n=document.createElement("tr");n.className="macro-data-row",n.innerHTML=`\n <td>${fn(pn(t.date))}</td>\n <td>${fn(t.country||"-")}</td>\n <td class="macro-title-cell"><strong>${fn(t.title||"-")}</strong></td>\n <td>${xn(Number(t.importance||0))}</td>\n <td>${fn(t.actual||"-")}</td>\n <td>${fn(t.forecast||"-")}</td>\n <td>${fn(t.previous||"-")}</td>\n <td>${Sn(e.direction)}</td>\n `;const i=document.createElement("tr");i.className="macro-ai-row",i.innerHTML=`\n <td colspan="8" class="macro-analyzer">${fn(e.cleaned||"-")}</td>\n `,q.append(n,i)}else q.innerHTML='<tr><td colspan="8" class="empty-cell">No calendar rows for the selected date.</td></tr>'},gi=e=>{if(G)if(G.innerHTML="",e.length)for(const t of e){const e=hn(t.ai_analyzer||""),n=String(t.url||"").trim(),i=document.createElement("tr");i.className="macro-data-row",i.innerHTML=`\n <td>${fn(pn(t.time))}</td>\n <td class="macro-title-cell"><strong>${fn(t.content||"-")}</strong></td>\n <td>${kn(e.impact)}</td>\n <td>${Sn(e.direction)}</td>\n <td>${n?`<a class="macro-link" href="${fn(n)}" target="_blank" rel="noreferrer noopener">open</a>`:"-"}</td>\n `;const o=document.createElement("tr");o.className="macro-ai-row",o.innerHTML=`\n <td colspan="5" class="macro-analyzer">${fn(e.cleaned||"-")}</td>\n `,G.append(i,o)}else G.innerHTML='<tr><td colspan="4" class="empty-cell">No news rows for the selected date.</td></tr>'},pi=e=>{F&&(F.textContent="-"),j&&(j.textContent="-"),_&&(_.textContent=e),di(z,[]),di(O,[]),mi(H,[],"No coin heatmap data."),mi(J,[],"No FX heatmap data."),ui([]),gi([])},fi=async({forceSnapshot:e=!1}={})=>{if(Rt)return;if(!window.botApi?.getMacroSnapshot||!window.botApi?.getMacroCalendar||!window.botApi?.getMacroNews)return void pi("Macro IPC bridge is not available in this host build.");const t=Ln("@ai.weget.jp/skill-macro-economy");if(!t||!t.enabled||"active"!==String(t.installStatus||"").trim().toLowerCase())return void pi("Macro Economy skill is disabled or not active.");if(!Ut)return void pi("Login is required to load macro snapshot, calendar, and news.");Rt=!0;const n=String(K?.value||"").trim()||gn(new Date);K&&!K.value&&(K.value=n);try{const[t,i,o]=await Promise.all([window.botApi.getMacroSnapshot({force:e}),window.botApi.getMacroCalendar({dateKey:n}),window.botApi.getMacroNews({dateKey:n})]);if(!t.ok)throw new Error(t.error||"macro snapshot failed");if(!i.ok)throw new Error(i.error||"macro calendar failed");if(!o.ok)throw new Error(o.error||"macro news failed");const a=t.snapshot;F&&(F.textContent=pn(a?.as_of)),j&&(j.textContent=null==a?.fear_greed_score?"-":`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`),_&&(_.textContent=`Loaded macro data for ${n} from the local macro economy skill runtime.`);const r=Array.isArray(a?.coin_cards)?[...a.coin_cards]:[];null!=a?.fear_greed_score&&r.unshift({code:"FGI",value:`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`,delta:"Fear & Greed",trend:Number(a.fear_greed_score)>=50?"up":"down",points:[],usePill:!0}),di(z,r),di(O,Array.isArray(a?.fx_cards)?a?.fx_cards:[]),mi(H,Array.isArray(a?.coin_heatmap)?a?.coin_heatmap:[],"No coin heatmap data."),mi(J,Array.isArray(a?.fx_heatmap)?a?.fx_heatmap:[],"No FX heatmap data."),ui(Array.isArray(i.rows)?i.rows:[]),gi(Array.isArray(o.rows)?o.rows:[])}catch(e){pi(ei(e instanceof Error?e.message:String(e)))}finally{Rt=!1}},yi=(e,t)=>{W&&(W.textContent=e,W.classList.toggle("is-disabled","installed"!==e),W.classList.toggle("is-muted","unknown"===e)),X&&(X.textContent=t||"No Playwright browser detail available.")},wi=({browserStatus:e})=>{V&&(V.innerHTML="installed"!==e?"Run <code>npx playwright install chromium</code> on this machine.":"Chromium is ready for Playwright-backed browser tasks.")},bi=async()=>{if(!window.botApi?.getPlaywrightBrowserStatus)return yi("unknown","Playwright browser check is not available in this host build."),void wi({browserStatus:"unknown"});const e=await window.botApi.getPlaywrightBrowserStatus(),t=e.ok&&e.status||"unknown";e.ok?yi(t,String(e.detail||"").trim()):yi("unknown",ei(e.error)),wi({browserStatus:t})},hi=(e,t,n,i=!1)=>{e&&(e.textContent=t||"-",e.classList.toggle("is-disabled",!n&&!i),e.classList.toggle("is-muted",i))},ki=async()=>{if(!window.botApi?.getGatewayStatus)return;const e=await window.botApi.getGatewayStatus();if(!e?.ok)return hi(ee,"error",!1),hi(te,"error",!1),hi(ne,"error",!1),void(oe&&(oe.textContent=ei(e?.error||"gateway status failed")));hi(ee,String(e.codexAuth?.status||"unknown"),"logged_in"===String(e.codexAuth?.status||""),"unknown"===String(e.codexAuth?.status||"")),hi(te,String(e.gatewayStatus?.status||"unknown"),"configured"===String(e.gatewayStatus?.status||""),"unknown"===String(e.gatewayStatus?.status||"")),hi(ne,String(e.browserStatus?.status||"unknown"),"installed"===String(e.browserStatus?.status||""),"unknown"===String(e.browserStatus?.status||"")),ie&&(ie.textContent=`Context file: ${String(e.contextFilePath||"-")}`),oe&&(oe.textContent=JSON.stringify({codexAuth:e.codexAuth||null,gatewayStatus:e.gatewayStatus||null,browserStatus:e.browserStatus||null},null,2)),(e=>{if(!se)return;se.innerHTML="";const t=[{target:"gateway",title:"Gateway Core"},{target:"codex_macro",title:"Codex Macro Chain",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"browser",title:"Browser Skill",packageName:"@ai.weget.jp/skill-browser"},{target:"macro",title:"Macro Economy Skill",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"coin",title:"GMO Coin Skill",packageName:"@ai.weget.jp/skill-gmo-coin"},{target:"fx",title:"GMO FX Skill",packageName:"@ai.weget.jp/skill-gmo-fx"}];for(const n of t){const t=n.packageName?e.find(e=>e.name===n.packageName):null,i=document.createElement("section");i.className="browser-skill-card gateway-test-card",i.innerHTML=`\n <div class="gateway-test-head">\n <h3>${n.title}</h3>\n <span class="skill-surface-badge is-muted">${t?t.enabled?"enabled":"disabled":"system"}</span>\n </div>\n <div class="muted">${t?`${t.name} · ${t.installStatus}`:"Gateway MCP self-test"}</div>\n <div class="skill-tags">${t&&Array.isArray(t.tools)&&t.tools.length?t.tools.map(e=>`<span class="skill-tag">${fn(e)}</span>`).join(""):'<span class="muted">No declared tools</span>'}</div>\n <div class="row config-actions">\n <button type="button" class="secondary gateway-test-btn" data-target="${n.target}">Run Test</button>\n </div>\n <div class="gateway-test-result">\n <span class="skill-surface-badge is-muted gateway-test-badge" data-target="${n.target}">not run</span>\n <div class="muted gateway-test-summary" data-target="${n.target}">-</div>\n </div>\n `,se.appendChild(i)}})(Array.isArray(e.skills)?e.skills:[])},xi=async()=>{if(!window.botApi?.getRuntimeInfo)return;const[e,t]=await Promise.all([window.botApi.getRuntimeInfo(),window.botApi.getSkills?window.botApi.getSkills():(async()=>({ok:!1,skills:[]}))()]);if(!e?.ok)return;const n=t?.ok&&t.skills||[];Yt=n,cn(e.status||"disconnected"),e.session?(sn(!0),u.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`):(sn(!1),u.textContent="local mode (not logged in)"),(e=>{g.innerHTML="";const t=nn(e),n=nn(t.runtime),i=t.session?nn(t.session):null,o=[["Bot ID",String(i?.botId||"-")],["User ID",String(i?.userId||"-")],["Email",String(i?.email||"-")],["Capabilities",Array.isArray(n.capabilities)?n.capabilities.map(e=>String(e)).join(", "):"-"],["Active Skills",Array.isArray(n.activeSkills)&&n.activeSkills.map(e=>{const t=nn(e);return String(t.displayName||t.name||"").trim()}).filter(Boolean).join(", ")||"-"],["GMO FX API State",String(n.gmoFxApiState||"unknown")],["GMO Coin API State",String(n.gmoCoinApiState||"unknown")],["Codex Auth",String(n.codexAuthStatus||"unknown")],["Codex Auth Detail",String(n.codexAuthDetail||"-")],["Default Model",String(n.aiModel||"-")],["Login API",String(n.loginApiUrl||"-")]];for(const[e,t]of o){const n=document.createElement("div");n.className="runtime-key",n.textContent=e;const i=document.createElement("div");i.className="runtime-value",i.textContent=t,g.appendChild(n),g.appendChild(i)}})(e),(e=>{if(!p)return;p.innerHTML="";const t=Array.isArray(e)?e:[];if(0===t.length){const e=document.createElement("div");return e.className="skill-empty",e.textContent="No managed skills are available in this bot host.",void p.appendChild(e)}for(const e of t){const t=document.createElement("section");t.className="skill-card"+(e.enabled?"":" is-disabled");const n=document.createElement("div");n.className="skill-card-head";const i=document.createElement("div");i.className="skill-card-title";const o=document.createElement("strong");o.textContent=e.displayName||e.name;const a=document.createElement("code");if(a.textContent=e.name,i.appendChild(o),i.appendChild(a),e.description){const t=document.createElement("div");t.className="muted",t.textContent=e.description,i.appendChild(t)}const r=document.createElement("div");r.className="skill-badges";const s=document.createElement("span");s.className="skill-badge "+(e.enabled?"enabled":"disabled"),s.textContent=e.enabled?"enabled":"disabled",r.appendChild(s);const c=document.createElement("span");c.className="skill-badge status",c.textContent=e.installStatus||"unknown",r.appendChild(c),n.appendChild(i),n.appendChild(r),t.appendChild(n);const l=e=>{const t=document.createElement("div");t.className="skill-card-section";const n=document.createElement("div");return n.className="skill-section-label",n.textContent=e,t.appendChild(n),t},d=l("Package"),m=document.createElement("code");m.className="skill-package-code",m.textContent=e.name,d.appendChild(m),t.appendChild(d);const u=l("Tools"),g=document.createElement("div");g.className="skill-tags";const f=Array.isArray(e.tools)?e.tools:[];if(f.length>0)for(const e of f){const t=document.createElement("span");t.className="skill-tag",t.textContent=e,g.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No tools declared",g.appendChild(e)}u.appendChild(g),t.appendChild(u);const y=l("Permissions");if(Array.isArray(e.permissions)&&e.permissions.length>0){const t=document.createElement("div");t.className="skill-tags";for(const n of e.permissions){const e=document.createElement("span");e.className="skill-tag permissions",e.textContent=n,t.appendChild(e)}y.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No permissions declared",y.appendChild(e)}t.appendChild(y);const w=l("State"),b=document.createElement("div");b.className="skill-meta";const h=[["Bundled Version",e.version||"-"],["Configured Version",e.configuredVersion||"-"],["UI",e.hasUi?"has ui":"no ui"],["Config Keys",String(Object.keys(e.configJson||{}).length)]];for(const[e,t]of h){const n=document.createElement("div");n.className="skill-meta-row";const i=document.createElement("span");i.textContent=e;const o=document.createElement("strong");o.textContent=t,n.appendChild(i),n.appendChild(o),b.appendChild(n)}w.appendChild(b),t.appendChild(w),p.appendChild(t)}})(n),ci(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{tabBtn:f,titleNode:h,packageNode:v,enabledNode:A,versionNode:$,fallbackTitle:"GMO Coin Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-coin"}),ci(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{tabBtn:y,titleNode:k,packageNode:E,enabledNode:N,versionNode:M,fallbackTitle:"GMO FX Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-fx"});const i=n.find(e=>"@ai.weget.jp/skill-browser"===e.name);ci(i,{tabBtn:w,titleNode:x,packageNode:C,enabledNode:L,versionNode:T,fallbackTitle:"Browser Skill",fallbackPackage:"@ai.weget.jp/skill-browser"}),((e,t,n)=>{if(e){if(e.innerHTML="",0===t.length){const t=document.createElement("span");return t.className="muted",t.textContent=n,void e.appendChild(t)}for(const n of t){const t=document.createElement("span");t.className="skill-tag",t.textContent=n,e.appendChild(t)}}})(U,Array.isArray(i?.tools)?i.tools:[],"No browser tools declared."),D&&(D.innerHTML="");const o=n.find(e=>"@ai.weget.jp/skill-macro-economy"===e.name);ci(o,{tabBtn:b,titleNode:S,packageNode:I,enabledNode:B,versionNode:P,fallbackTitle:"Macro Economy Skill",fallbackPackage:"@ai.weget.jp/skill-macro-economy"}),await ki(),await bi(),li(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{titleNode:ve,fallbackTitle:"FX Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Ce,inputNode:ge},fxApiKey:{labelNode:Ie,inputNode:we},fxApiSecret:{labelNode:Ae,inputNode:be}}}),li(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{titleNode:Ee,fallbackTitle:"Coin Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Ne,inputNode:pe},cryptoApiKey:{labelNode:Le,inputNode:fe},cryptoApiSecret:{labelNode:Be,inputNode:ye}}}),e.session?(fi(),Ii()):pi("Login is required to load macro snapshot, calendar, and news.")};xe.addEventListener("click",async()=>{await ri(xe,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(ge.value||"0"),fxApiKey:String(we.value||"").trim(),fxApiSecret:String(be.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-fx",e);if(!t.ok)return Vn(`[trade-config] fx save failed: ${t.error||"unknown"}`),ti("error",ei(t.error)),void await ni("trade-config.fx.save",ei(t.error),t);Vn("[trade-config] fx saved"),ti("success","FX skill 配置已保存到本机。"),await xi()})}),Se.addEventListener("click",async()=>{await ri(Se,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(pe.value||"0"),cryptoApiKey:String(fe.value||"").trim(),cryptoApiSecret:String(ye.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-coin",e);if(!t.ok)return Vn(`[trade-config] coin save failed: ${t.error||"unknown"}`),ti("error",ei(t.error)),void await ni("trade-config.coin.save",ei(t.error),t);Vn("[trade-config] coin saved"),ti("success","Coin skill 配置已保存到本机。"),await xi()})}),Te.addEventListener("click",async()=>{await ri(Te,async()=>{if(!window.botApi?.saveAiConfig)return;const e={aiModel:String($e.value||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(Me.value||"").trim()},t=await window.botApi.saveAiConfig(e);if(!t.ok)return Vn(`[ai-config] save failed: ${t.error||"unknown"}`),ti("error",ei(t.error)),void await ni("ai-config.save",ei(t.error),t);Vn("[ai-config] saved"),ti("success","AI 设置已保存。"),await xi()})}),Pe.addEventListener("click",async()=>{await ri(Pe,async()=>{if(!window.botApi?.startCodexLogin)return;const e=await window.botApi.startCodexLogin();if(!e.ok)return Vn(`[codex] login launch failed: ${e.detail||"unknown"}`),ti("error",ei(e.detail)),void await ni("codex.login.launch",ei(e.detail),e);Vn("[codex] login launched",{detail:e.detail}),ti("success",e.detail||"Codex login started."),setTimeout(()=>{xi()},1500)})}),De.addEventListener("click",async()=>{const e=String(Fe.textContent||"codex login --device-auth").trim();try{if(!await(async e=>{if(navigator.clipboard?.writeText)return await navigator.clipboard.writeText(e),!0;const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly","true"),t.style.position="absolute",t.style.left="-9999px",document.body.appendChild(t),t.select();const n=document.execCommand("copy");return document.body.removeChild(t),n})(e))return void ti("error","无法复制命令,请手动执行。");Vn("[codex] login command copied"),ti("success","Codex 登录命令已复制。")}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[codex] copy login command failed",{error:t}),ti("error",t)}}),Ue.addEventListener("click",async()=>{await ri(Ue,async()=>{await xi(),Vn("[codex] auth status refreshed"),ti("success","Codex 状态已刷新。")})}),Q?.addEventListener("click",async()=>{await ri(Q,async()=>{await bi(),Vn("[browser] chromium status refreshed"),ti("success","Browser status 已刷新。")})}),ae?.addEventListener("click",async()=>{await ri(ae,async()=>{if(!window.botApi?.installPlaywrightMcp)return;const e=await window.botApi.installPlaywrightMcp();if(!e.ok)return Vn("[gateway] mcp install failed",{error:e.detail||e.error||"unknown"}),ti("error",ei(e.detail||e.error)),await ni("gateway.install",ei(e.detail||e.error),e),void await ki();Vn("[gateway] mcp configured",{detail:e.detail}),ti("success",e.detail||"WeGet Gateway MCP configured."),await ki()})}),re?.addEventListener("click",async()=>{await ri(re,async()=>{await ki(),ti("success","Gateway status 已刷新。")})}),se?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".gateway-test-btn");if(!n)return;const i=String(n.dataset.target||"").trim().toLowerCase();"gateway"!==i&&"codex_macro"!==i&&"browser"!==i&&"macro"!==i&&"coin"!==i&&"fx"!==i||await(async(e,t)=>{const n=window.botApi;n?.runGatewaySelfTest&&await ri(t,async()=>{const t=await n.runGatewaySelfTest({target:e}),i=se?.querySelector(`.gateway-test-badge[data-target="${e}"]`),o=se?.querySelector(`.gateway-test-summary[data-target="${e}"]`);if(i){const e=Boolean(t?.ok);i.textContent=e?"OK":"NG",i.classList.toggle("is-muted",!1),i.classList.toggle("is-disabled",!e)}if(o){const e=String(t?.summary||t?.error||t?.detail||"-").trim()||"-",n=String(t?.logPath||"").trim();o.textContent=n?`${e} [log] ${n}`:e}if(!t?.ok){const n=ei(t?.summary||t?.error||t?.detail||`${e} test failed`);return Vn("[gateway] self-test failed",{target:e,error:n,logPath:t?.logPath||""}),void ti("error",n)}Vn("[gateway] self-test ok",{target:e,summary:t?.summary||"",logPath:t?.logPath||""}),ti("success",`${e} test completed.`)})})(i,n)}),Y?.addEventListener("click",async()=>{await ri(Y,async()=>{await fi({forceSnapshot:!0}),Vn("[macro] snapshot refreshed"),ti("success","Macro snapshot 已刷新。")})}),R?.addEventListener("click",async()=>{await ri(R,async()=>{await fi(),Vn("[macro] calendar and news refreshed",{dateKey:String(K?.value||"").trim()}),ti("success","Macro calendar / news 已刷新。")})}),K?.addEventListener("change",()=>{fi()}),he.addEventListener("click",()=>{const e="password"===ye.type?"text":"password";ye.type=e,he.textContent="password"===e?"显示":"隐藏"}),ke.addEventListener("click",()=>{const e="password"===be.type?"text":"password";be.type=e,ke.textContent="password"===e?"显示":"隐藏"}),e.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi)return;ai();const t=c.value.trim(),n=s.value.trim(),i=l.value;if(t&&n&&i){_e.disabled=!0,_e.textContent="Loading...";try{const e=await window.botApi.login(t,i,n,d.checked);if(!e.ok)return Vn(`[ui] login failed: ${e.error||"unknown"}`),void oi("error",ei(e.error));Vn("[ui] login success"),oi("success","登录成功。"),ti("success","登录成功。"),await xi()}finally{_e.disabled=!1,_e.textContent="Login"}}}),je.addEventListener("click",async()=>{await ri(je,async()=>{if(!window.botApi)return;const e=await window.botApi.logout();if(!e.ok)return Vn(`[ui] logout failed: ${e.error||"unknown"}`),ti("error",ei(e.error)),void await ni("auth.logout",ei(e.error),e);Vn("[ui] logout"),ti("success","已登出。"),sn(!1),u.textContent="local mode (not logged in)",await xi()})});const Si=async({market:e,symbol:t,interval:n,canvas:i})=>{if(!window.botApi)return;const o=await window.botApi.openGmoKlineWindow({symbol:t,interval:n,market:e});if(!o.ok)return Vn(`[trade] open ${e} kline failed: ${o.error||"unknown"}`),ti("error",ei(o.error)),void await ni(`${e}.kline`,ei(o.error),o);const a=Array.isArray(o.candles)?o.candles:[],r=Dn(e);r.symbol=String(o.symbol||t||"").toUpperCase(),r.interval=n,r.candles=a.map(e=>({time:Number(e.time||0),open:Number(e.open||0),high:Number(e.high||0),low:Number(e.low||0),close:Number(e.close||0)})),r.lastFetchBucket=Math.floor(Date.now()/Pn(n)),si(i,r.candles,jn(e)),Vn("[trade] kline rendered",{market:e,symbol:o.symbol||t,interval:o.interval||n,candles:a.length}),ii()},vi=async()=>{await Si({market:"coin",symbol:Bt,interval:Nt,canvas:qe})},Ei=async()=>{await Si({market:"fx",symbol:$t,interval:Lt,canvas:Ge})},Ci=({container:e,getCurrent:t,setCurrent:n,onChange:i})=>{const o=Array.from(e.querySelectorAll(".interval-btn"));for(const e of o)e.addEventListener("click",async()=>{const a=String(e.dataset.interval||"").trim();if(a&&a!==t()){n(a);for(const t of o)t.classList.toggle("is-active",t===e);await i()}})};ze.addEventListener("change",async()=>{Bt=String(ze.value||"BTC_JPY").trim().toUpperCase(),Tn("coin",Bt);try{await Mn("coin",Bt),await vi(),await Yn("coin")}catch(e){const t=ei(e instanceof Error?e.message:String(e));ti("error",t)}}),Oe.addEventListener("change",async()=>{$t=String(Oe.value||"USD_JPY").trim().toUpperCase(),Tn("fx",$t);try{await Mn("fx",$t),await Ei(),await Yn("fx")}catch(e){const t=ei(e instanceof Error?e.message:String(e));ti("error",t)}}),Ci({container:He,getCurrent:()=>Nt,setCurrent:e=>{Nt=e},onChange:vi}),Ci({container:Je,getCurrent:()=>Lt,setCurrent:e=>{Lt=e},onChange:Ei}),ct.addEventListener("click",async()=>{await ri(ct,async()=>{await qn("coin")})}),lt.addEventListener("click",async()=>{await ri(lt,async()=>{await qn("fx")})}),et.addEventListener("input",()=>{Nn("coin")}),tt.addEventListener("input",()=>{Nn("fx")}),ht.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeCoinPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=vn(String(n.dataset.size||""));o&&Number.isFinite(s)&&!(s<=0)&&c?await ri(n,async()=>{const e=await i.closeCoinPosition({symbol:o,side:r,positionId:s,size:c});if(!e?.ok){const t=ei(e?.error||"coin close order failed");return Vn("[coin] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,error:t}),ti("error",t),void await ni("coin.closeOrder",t,e)}Vn("[coin] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,result:e.result||null}),ti("success","決済注文を送信しました。"),await qn("coin"),await Yn("coin")}):ti("error","決済対象データが不正です。")}),xt.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeFxPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=String(n.dataset.size||"").trim();o&&Number.isFinite(s)&&!(s<=0)&&c?await ri(n,async()=>{const e=await i.closeFxPosition({symbol:o,side:r,positionId:s,size:c});if(!e?.ok){const t=ei(e?.error||"fx close order failed");return Vn("[fx] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,error:t}),ti("error",t),void await ni("fx.closeOrder",t,e)}Vn("[fx] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,result:e.result||null}),ti("success","決済注文を送信しました。"),await qn("fx"),await Yn("fx")}):ti("error","決済対象データが不正です。")}),nt.addEventListener("click",async()=>{await Wn("BUY",nt)}),it.addEventListener("click",async()=>{await Wn("SELL",it)}),ot.addEventListener("click",async()=>{await Rn("BUY",ot)}),at.addEventListener("click",async()=>{await Rn("SELL",at)}),St.addEventListener("click",async()=>{await ri(St,async()=>{await Yn("coin")})}),vt.addEventListener("click",async()=>{await ri(vt,async()=>{await Yn("coin")})}),Et.addEventListener("click",async()=>{await ri(Et,async()=>{await Yn("fx")})}),Ct.addEventListener("click",async()=>{await ri(Ct,async()=>{await Yn("fx")})}),de.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi?.sendChat)return;const t=String(me.value||"").trim();if(t){Qn("user",t),me.value="",ue.disabled=!0,((e="AI")=>{Zn();const t=document.createElement("div");t.className="msg assistant assistant-thinking";const n=document.createElement("div");n.className="msg-title",n.textContent=`${e}:`;const i=document.createElement("div");i.className="thinking-body";const o=document.createElement("span");o.className="thinking-text",o.textContent="Thinking";const a=document.createElement("span");a.className="thinking-dots",a.innerHTML="<span></span><span></span><span></span>",i.append(o,a),t.append(n,i),ce.appendChild(t),ce.scrollTop=ce.scrollHeight})("AI");try{const e=await window.botApi.sendChat(t);if(!e.ok)return Zn(),Qn("assistant",`Error: ${e.error||"unknown"}`),ti("error",ei(e.error)),void await ni("ai.chat",ei(e.error),e);ii()}catch(e){Zn();const t=ei(e instanceof Error?e.message:String(e));Qn("assistant",`Error: ${t}`),ti("error",t),await ni("ai.chat.exception",t,e)}finally{ue.disabled=!1}}}),window.addEventListener("error",e=>{const t=ei(e.error?.message||e.message||"Unknown UI error");Vn("[ui] uncaught error",{error:t}),ti("error",t),ni("ui.error",t,{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno})}),window.addEventListener("unhandledrejection",e=>{const t=e.reason instanceof Error?e.reason.message:String(e.reason||"Unhandled promise rejection"),n=ei(t);Vn("[ui] unhandled rejection",{error:n}),ti("error",n),ni("ui.unhandledrejection",n,e.reason)}),le?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".active-task-cancel-btn");if(!n)return;const i=String(n.dataset.taskId||"").trim();if(!i||!window.botApi?.cancelActiveTask)return;n.disabled=!0;const o=await window.botApi.cancelActiveTask(i);if(!o?.ok)return n.disabled=!1,void ti("error",ei(o?.error||"Failed to cancel task"));ti("success",`Cancel requested for ${i}`)}),window.botApi&&(window.botApi.getActiveTasks?.().then(e=>{e?.ok&&bn({type:"snapshot",tasks:Array.isArray(e.tasks)?e.tasks:[]})}),window.botApi.onStatus(e=>{cn(String(e.status||""))}),window.botApi.onTaskRuntime(e=>{bn(e)}),window.botApi.onChatEvent(e=>{if(!e)return;const t=nn(e);if("line_user"===t.type){const e=t.lineUserId?`LINE(${String(t.lineUserId)})`:"LINE";return void Qn("line-user",String(t.text||""),e)}if("line_assistant"===t.type){Zn();const e=t.lineUserId?`AI->LINE(${String(t.lineUserId)})`:"AI->LINE";return void Qn("line-assistant",String(t.text||""),e)}"assistant_status"!==t.type?"assistant"===t.type&&(Zn(),Qn("assistant",String(t.text||""))):(e=>{const t=String(e||"").trim().replace(/\s+/g," ");if(!t)return;if(/^[{}[\],:]+$/.test(t))return;if(/^["']?[a-zA-Z0-9_-]+["']?\s*:\s*$/.test(t))return;const n=ce.querySelector(".msg.assistant-thinking .thinking-text");n&&(n.textContent=t)})(String(t.status||"Thinking"))}),window.botApi.onTradeExecution(e=>{if(!e)return;const t=nn(e),n="coin"===String(t.market||"").trim().toLowerCase()?"coin":"fx",i=String(t.symbol||"").trim().toUpperCase(),o=String(t.side||"").trim().toUpperCase()||"-",a=String(t.settleType||"").trim().toUpperCase()||"OPEN",r=String(t.executionSize??"").trim(),s=String(t.executionPrice??"").trim(),c="CLOSE"===a?"決済":"新規",l=`${i} ${o} ${r}${s?` @ ${s}`:""}`;Vn(`[${n}] execution filled`,{symbol:i,side:o,settleType:a,size:r,price:s,raw:t}),ti("success",`約定成功 (${c}) ${l}`.trim()),qn(n).catch(()=>{}),Yn(n).catch(()=>{})})),tn=setInterval(()=>{wn()},15e3);const Ii=async()=>{if(Ut&&!Ft&&!jt){jt=!0;try{if(!await async function(){let e=0;if(Tn("coin",Bt),Tn("fx",$t),$n("coin")){e+=1;try{await Mn("coin",Bt)}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[coin] quote fetch failed",{error:t}),ti("error",t)}await vi()}if($n("fx")){e+=1;try{await Mn("fx",$t)}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[fx] quote fetch failed",{error:t}),ti("error",t)}await Ei()}if(window.botApi?.getSymbolRules&&$n("coin"))try{const e=await window.botApi.getSymbolRules({market:"coin"});if(e?.ok&&Array.isArray(e.rules)){Wt.clear();for(const t of e.rules){const e=String(t.symbol||"").trim().toUpperCase();e&&Wt.set(e,{minOrderSize:String(t.minOrderSize||""),maxOrderSize:String(t.maxOrderSize||""),sizeStep:String(t.sizeStep||"")})}}}catch(e){Vn("[coin] symbol rules fetch failed",{error:e instanceof Error?e.message:String(e)})}return e>0}())return;await qn(),await Yn(),$n("coin")&&!Pt&&(Pt=setInterval(()=>{Hn("coin")},1e3)),$n("fx")&&!Dt&&(Dt=setInterval(()=>{Hn("fx")},1e3)),Ft=!0}finally{jt=!1}}};sn(!1),(()=>{try{an("true"===window.localStorage.getItem(on))}catch{an(!1)}})(),K&&!K.value&&(K.value=gn(new Date)),(async()=>{if(!window.botApi?.getSavedProfile)return;const e=await window.botApi.getSavedProfile();e?.ok&&e.profile&&(s.value=e.profile.botId||"",c.value=e.profile.email||"",l.value=e.profile.password||"",d.checked=Boolean(e.profile.remember))})(),(async()=>{if(!window.botApi?.getSkillConfig||!window.botApi.getAiConfig)return;const[e,t,n]=await Promise.all([window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-fx"),window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-coin"),window.botApi.getAiConfig()]);e?.ok&&e.config&&(ge.value=String(e.config.riskDailyLossLimitJpy||5e4),we.value=String(e.config.fxApiKey||""),be.value=String(e.config.fxApiSecret||"")),t?.ok&&t.config&&(pe.value=String(t.config.riskDailyLossLimitJpy||5e4),fe.value=String(t.config.cryptoApiKey||""),ye.value=String(t.config.cryptoApiSecret||"")),n?.ok&&n.config&&($e.value=n.config.aiModel||"gpt-5.4",Me.value=n.config.logOutputDir||"")})(),xi();export{};
|
|
@@ -610,11 +610,26 @@
|
|
|
610
610
|
<h2>Codex Console</h2>
|
|
611
611
|
<p class="muted">本地桌面聊天同样走 Codex 中枢,不单独保存任何模型 key。</p>
|
|
612
612
|
</div>
|
|
613
|
-
<div
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
613
|
+
<div class="ai-console-layout">
|
|
614
|
+
<aside class="active-tasks-panel">
|
|
615
|
+
<div class="active-tasks-head">
|
|
616
|
+
<div>
|
|
617
|
+
<h3>Active Tasks</h3>
|
|
618
|
+
<p class="muted">当前正在处理的任务、最近状态与取消入口。</p>
|
|
619
|
+
</div>
|
|
620
|
+
</div>
|
|
621
|
+
<div id="active-tasks-list" class="active-tasks-list">
|
|
622
|
+
<div class="active-task-empty">No active tasks.</div>
|
|
623
|
+
</div>
|
|
624
|
+
</aside>
|
|
625
|
+
<div class="ai-chat-shell">
|
|
626
|
+
<div id="messages" class="messages"></div>
|
|
627
|
+
<form id="chat-form" class="composer">
|
|
628
|
+
<textarea id="chat-input" rows="3" placeholder="Ask Codex to inspect code, reason about tasks, or operate through enabled skills..." required></textarea>
|
|
629
|
+
<button id="send-btn" type="submit">Send</button>
|
|
630
|
+
</form>
|
|
631
|
+
</div>
|
|
632
|
+
</div>
|
|
618
633
|
</section>
|
|
619
634
|
</section>
|
|
620
635
|
|
|
@@ -1516,12 +1516,110 @@ button.is-loading::before {
|
|
|
1516
1516
|
}
|
|
1517
1517
|
|
|
1518
1518
|
#tab-ai .panel {
|
|
1519
|
-
display: flex;
|
|
1520
|
-
flex-direction: column;
|
|
1521
1519
|
height: calc(100vh - 180px);
|
|
1522
1520
|
min-height: 520px;
|
|
1523
1521
|
}
|
|
1524
1522
|
|
|
1523
|
+
.ai-console-layout {
|
|
1524
|
+
display: grid;
|
|
1525
|
+
grid-template-columns: minmax(280px, 320px) minmax(0, 1fr);
|
|
1526
|
+
gap: 14px;
|
|
1527
|
+
height: 100%;
|
|
1528
|
+
min-height: 0;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
.active-tasks-panel {
|
|
1532
|
+
border: 1px solid #d8e2f1;
|
|
1533
|
+
border-radius: 12px;
|
|
1534
|
+
background: linear-gradient(180deg, #f8fbff 0%, #eef4fb 100%);
|
|
1535
|
+
padding: 14px;
|
|
1536
|
+
display: flex;
|
|
1537
|
+
flex-direction: column;
|
|
1538
|
+
min-height: 0;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
.active-tasks-head h3 {
|
|
1542
|
+
margin: 0 0 4px;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.active-tasks-list {
|
|
1546
|
+
display: grid;
|
|
1547
|
+
gap: 10px;
|
|
1548
|
+
overflow: auto;
|
|
1549
|
+
margin-top: 10px;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
.active-task-empty {
|
|
1553
|
+
border: 1px dashed #c7d5ea;
|
|
1554
|
+
border-radius: 10px;
|
|
1555
|
+
padding: 14px;
|
|
1556
|
+
color: #60708a;
|
|
1557
|
+
background: rgba(255, 255, 255, 0.66);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
.active-task-card {
|
|
1561
|
+
border: 1px solid #cfdcf0;
|
|
1562
|
+
border-radius: 12px;
|
|
1563
|
+
background: #ffffff;
|
|
1564
|
+
padding: 12px;
|
|
1565
|
+
display: grid;
|
|
1566
|
+
gap: 10px;
|
|
1567
|
+
box-shadow: 0 8px 20px rgba(34, 58, 96, 0.06);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
.active-task-meta {
|
|
1571
|
+
display: flex;
|
|
1572
|
+
flex-wrap: wrap;
|
|
1573
|
+
gap: 6px;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
.active-task-badge {
|
|
1577
|
+
display: inline-flex;
|
|
1578
|
+
align-items: center;
|
|
1579
|
+
border-radius: 999px;
|
|
1580
|
+
padding: 3px 10px;
|
|
1581
|
+
background: #eaf2ff;
|
|
1582
|
+
color: #234171;
|
|
1583
|
+
font-size: 12px;
|
|
1584
|
+
font-weight: 700;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
.active-task-title {
|
|
1588
|
+
color: #132744;
|
|
1589
|
+
font-size: 14px;
|
|
1590
|
+
font-weight: 700;
|
|
1591
|
+
line-height: 1.4;
|
|
1592
|
+
word-break: break-word;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.active-task-status {
|
|
1596
|
+
color: #204770;
|
|
1597
|
+
font-size: 13px;
|
|
1598
|
+
font-weight: 600;
|
|
1599
|
+
line-height: 1.5;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
.active-task-subtext {
|
|
1603
|
+
color: #667b99;
|
|
1604
|
+
font-size: 12px;
|
|
1605
|
+
line-height: 1.5;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
.active-task-actions {
|
|
1609
|
+
display: flex;
|
|
1610
|
+
justify-content: flex-end;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
.active-task-actions button {
|
|
1614
|
+
min-width: 92px;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
.ai-chat-shell {
|
|
1618
|
+
min-height: 0;
|
|
1619
|
+
display: flex;
|
|
1620
|
+
flex-direction: column;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1525
1623
|
#tab-ai .messages {
|
|
1526
1624
|
flex: 1 1 auto;
|
|
1527
1625
|
margin-bottom: 8px;
|
|
@@ -1633,6 +1731,10 @@ button.is-loading::before {
|
|
|
1633
1731
|
grid-template-columns: 1fr;
|
|
1634
1732
|
}
|
|
1635
1733
|
|
|
1734
|
+
.ai-console-layout {
|
|
1735
|
+
grid-template-columns: 1fr;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1636
1738
|
.skill-meta {
|
|
1637
1739
|
grid-template-columns: 1fr;
|
|
1638
1740
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node:fs/promises";import{existsSync as e}from"node:fs";import{createRequire as r}from"node:module";import s from"node:os";import n from"node:path";import{spawn as i,spawnSync as o}from"node:child_process";const a="gpt-5.4",c=r(import.meta.url);let d=null,l=null,u=null,g=null,m=null,p=null;const w=async t=>{if(!t)return"";const e=[];for await(const r of t)e.push(Buffer.isBuffer(r)?r:Buffer.from(String(r)));return Buffer.concat(e).toString("utf8")},f=t=>String(t||"").replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,""),y=t=>{let e="";return{push:r=>{if(!t)return;e+=f(Buffer.isBuffer(r)?r.toString("utf8"):String(r));const s=e.split(/\r?\n/);e=s.pop()||"";for(const e of s){const r=String(e||"").trim();r&&t(r)}},flush:()=>{if(!t)return;const r=String(e||"").trim();r&&t(r),e=""}}},h=t=>{const e=f(String(t||"")).trim();return e?/^[{}[\],:]+$/.test(e)||/^["']?[a-zA-Z0-9_-]+["']?\s*:\s*$/.test(e)||/^`{3,}|^#{1,6}\s*$/.test(e)||/^Assistant:?\s*$/i.test(e)||/^Warning: no last agent message/i.test(e)?"":/^ERROR:\s*unexpected status\s+\d+/i.test(e)?"Tool call failed":e:""},x=(t,e)=>{const r=f(String(t||"")).replace(/\s+/g," ").trim();return r?r.length>180?`${r.slice(0,177)}...`:r:e},S=t=>t&&"object"==typeof t?t:{},_=t=>String(t||"").trim(),C=t=>{const e=S(t);if(0===Object.keys(e).length)return"";const r=S(e.waiting),s=[`- source task: ${_(e.task_id)||"-"}`,`- status: ${_(e.status)||"-"}`,`- title: ${_(e.title)||"-"}`,`- user prompt: ${_(e.user_prompt)||"-"}`,`- summary: ${_(e.summary)||"-"}`,`- latest output: ${_(e.latest_output)||"-"}`,`- last event: ${_(e.last_event_type)||"-"}`];return Object.keys(r).length>0&&(s.push(`- waiting type: ${_(r.type)||"-"}`),s.push(`- waiting subtype: ${_(r.subtype)||"-"}`),s.push(`- waiting prompt: ${_(r.prompt)||"-"}`)),s.join("\n")},v=e=>{const r=(new Date).toISOString(),s=`exec-${Date.now()}`;let i="",o="",a=!1,c=Promise.resolve();const d=[],l=async()=>{if(a)return o;const r=n.join(e,"codex-sessions");await t.mkdir(r,{recursive:!0});const c=String(i||s||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_");return o=n.join(r,`${c}.log`),a=!0,d.length>0&&(await t.appendFile(o,d.join(""),"utf8"),d.length=0),o},u=(e,r={})=>{(e=>{c=c.then(async()=>{if(!a&&!i)return void d.push(e);const r=await l();await t.appendFile(r,e,"utf8")}).catch(()=>{})})(`${JSON.stringify({ts:(new Date).toISOString(),event:e,...r},null,0)}\n`)};return{get startedAt(){return r},get sessionId(){return i},setSessionId:t=>{const e=String(t||"").trim();e&&i!==e&&(i=e,u("session_identified",{sessionId:e}))},write:u,flush:async()=>(a||await l(),await c,o)}},$=()=>{if(l)return l;const t=[n.join(String(process.env.ProgramFiles||"C:\\Program Files"),"nodejs","npx.cmd"),n.join(String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)"),"nodejs","npx.cmd"),n.join(String(process.env.APPDATA||""),"npm","npx.cmd")].filter(Boolean);for(const r of t)if(e(r))return l=r,r;const r=o("where.exe",["npx.cmd"],{windowsHide:!0,encoding:"utf8"}),s=String(r.stdout||"").split(/\r?\n/).map(t=>t.trim()).find(Boolean);return s?(l=s,s):"npx.cmd"},P=()=>{if(u)return u;const t=[n.join(String(process.env.ProgramFiles||"C:\\Program Files"),"nodejs","node.exe"),n.join(String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)"),"nodejs","node.exe")];for(const r of t)if(e(r))return u=r,r;return u="node",u},k=()=>{if(g)return g;try{const t=c.resolve("@ai.weget.jp/weget-gateway-mcp/dist/index.js");return g=t,t}catch{}const t=[n.join(String(process.env.APPDATA||""),"npm","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js"),n.join(String(process.env.ProgramFiles||"C:\\Program Files"),"nodejs","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js"),n.join(String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)"),"nodejs","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js")].filter(Boolean);for(const r of t)if(e(r))return g=r,r;return g=n.join(String(process.env.APPDATA||""),"npm","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js"),g},O=t=>{const e=n.join(t,"weget-gateway-context.json");return"win32"===process.platform?{command:P(),args:[k(),"--context-file",e]}:{command:"npx",args:["-y","@ai.weget.jp/weget-gateway-mcp@latest","--context-file",e]}},j=t=>String(t||"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),E=(t,e)=>{const r=String(t||""),s=O(e);if("win32"===process.platform){const t=new RegExp(`command:\\s+${j(s.command)}`,"i"),e=new RegExp(j(String(s.args[0]||"")),"i"),n=new RegExp(j(String(s.args[2]||"")),"i");return t.test(r)&&e.test(r)&&n.test(r)}return new RegExp(`command:\\s+${j(s.command)}`,"i").test(r)&&r.includes(String(s.args[0]||""))},b=async({args:t,stdinText:r,cwd:s,extraEnv:a,onStdoutLine:c,onStderrLine:l})=>new Promise((u,g)=>{const m=t=>{const e="object"==typeof t&&t&&"code"in t?String(t.code||""):"";return"EPIPE"===e||"ERR_STREAM_DESTROYED"===e},p=(()=>{if(d)return d;if("win32"===process.platform){const t=[String(process.env.ProgramFiles||"C:\\Program Files"),String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)")];for(const r of t){const t=n.join(r,"nodejs","node.exe"),s=n.join(r,"nodejs","node_modules","@openai","codex","bin","codex.js");if(e(t)&&e(s))return d={command:t,baseArgs:[s]},d}const r=["codex.cmd","codex"];for(const t of r){const e=o("where.exe",[t],{windowsHide:!0,encoding:"utf8"}),r=String(e.stdout||"").split(/\r?\n/).map(t=>t.trim()).find(Boolean);if(r)return d={command:r,baseArgs:[]},d}}return d={command:"codex",baseArgs:[]},d})(),f={...process.env,...a||{},NO_COLOR:"1",FORCE_COLOR:"0",CLICOLOR:"0"},h=i(p.command,[...p.baseArgs,...t],{cwd:s,stdio:["pipe","pipe","pipe"],windowsHide:!0,env:f});let x=!1;const S=t=>{x||(x=!0,t())},_=w(h.stdout),C=w(h.stderr),v=y(c),$=y(l);h.stdout?.on("data",t=>v.push(t)),h.stderr?.on("data",t=>$.push(t)),h.on("error",t=>{S(()=>g(t))}),h.stdin?.on("error",t=>{m(t)||S(()=>g(t))}),h.on("close",async t=>{v.flush(),$.flush();const[e,r]=await Promise.all([_,C]);S(()=>u({stdout:e,stderr:r,exitCode:Number.isFinite(t)?Number(t):1}))}),"string"==typeof r&&h.stdin?.writable?h.stdin.write(r,t=>{!t||m(t)?h.stdin?.end():S(()=>g(t))}):h.stdin?.end()}),A=async({command:t,args:e,cwd:r})=>new Promise((s,n)=>{const o=i(t,e,{cwd:r,stdio:["pipe","pipe","pipe"],windowsHide:!0,env:{...process.env,NO_COLOR:"1",FORCE_COLOR:"0",CLICOLOR:"0"}});let a=!1;const c=t=>{a||(a=!0,t())},d=w(o.stdout),l=w(o.stderr);o.on("error",t=>c(()=>n(t))),o.on("close",async t=>{const[e,r]=await Promise.all([d,l]);c(()=>s({stdout:e,stderr:r,exitCode:Number.isFinite(t)?Number(t):1}))})}),F=async t=>null!==m?m:(p||(p=(async(t,e)=>{const r=String(t||"").trim();if(!r)return!1;try{const t=await b({cwd:e,args:["features","list"]});return 0===t.exitCode&&f(`${t.stdout||""}\n${t.stderr||""}`).split(/\r?\n/).map(t=>t.trim()).some(t=>t.startsWith(`${r} `)||t===r)}catch{return!1}})("rmcp_client",t).then(t=>(m=t,t)).finally(()=>{p=null})),p),I=async({cwd:t,model:e,outputPath:r})=>{const s=["exec"];return await F(t)&&s.push("--enable","rmcp_client"),s.push("--dangerously-bypass-approvals-and-sandbox","-C",t,"--skip-git-repo-check","-m",e,"-o",r,"-"),s};export const buildCodexResumeArgs=({cwd:t,sessionId:e,outputPath:r})=>["exec","--dangerously-bypass-approvals-and-sandbox","-C",t,"--skip-git-repo-check","-o",r,"resume",e,"-"];export const createCodexService=({getModel:e,getWorkingDir:r,getLogDir:o,emitLog:c,getGatewayContext:d})=>{const l=()=>n.join(o(),"codex-session-bindings.json"),u=async()=>{try{const e=await t.readFile(l(),"utf8"),r=JSON.parse(e);return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[String(t||"").trim(),String(e||"").trim()]).filter(([t,e])=>t&&e))}catch{return{}}},g=async e=>{await t.mkdir(o(),{recursive:!0}),await t.writeFile(l(),`${JSON.stringify(e,null,2)}\n`,"utf8")},m=()=>{const t=d?.()||{},e={WEGET_ACTIVE_SKILLS:JSON.stringify(Array.isArray(t.activeSkills)?t.activeSkills:[]),WEGET_HOST_METADATA:JSON.stringify(t.hostMetadata&&"object"==typeof t.hostMetadata?t.hostMetadata:{})},r=String(t.botId||"").trim(),s=String(t.platformApiBaseUrl||"").trim(),n=String(t.accessToken||"").trim(),i=String(t.fileBridgeDir||"").trim(),o=String(t.fileBridgeToken||"").trim();return r&&(e.WEGET_BOT_ID=r),s&&(e.WEGET_PLATFORM_API_BASE_URL=s),n&&(e.WEGET_PLATFORM_API_ACCESS_TOKEN=n),i&&(e.WEGET_FILE_BRIDGE_DIR=i),o&&(e.WEGET_FILE_BRIDGE_TOKEN=o),e},p=()=>n.join(r(),"weget-gateway-context.json"),w=async()=>{const e=d?.()||{};await t.mkdir(r(),{recursive:!0}),await t.writeFile(p(),JSON.stringify({activeSkills:Array.isArray(e.activeSkills)?e.activeSkills:[],hostMetadata:e.hostMetadata&&"object"==typeof e.hostMetadata?e.hostMetadata:{},botId:String(e.botId||"").trim(),platformApiBaseUrl:String(e.platformApiBaseUrl||"").trim(),accessToken:String(e.accessToken||"").trim(),fileBridgeDir:String(e.fileBridgeDir||"").trim(),fileBridgeToken:String(e.fileBridgeToken||"").trim(),logDir:o(),updatedAt:(new Date).toISOString()},null,2),"utf8")},f=async({target:e,payload:r})=>{const s=n.join(o(),"gateway-tests");await t.mkdir(s,{recursive:!0});const i=n.join(s,`${e}-${Date.now()}.log`);return await t.writeFile(i,`${JSON.stringify(r,null,2)}\n`,"utf8"),i},y=async({userPrompt:t,systemPrompt:r,contextLabel:s,promptBundle:n,metadata:i})=>{const o=d?.()||{},c=Array.isArray(o.activeSkills)?o.activeSkills:[],l=o.hostMetadata&&"object"==typeof o.hostMetadata?o.hostMetadata:{},[u,g]=await Promise.all([P("weget-gateway"),k()]),m=c.length?c.map(t=>{const e=Array.isArray(t.tools)&&t.tools.length?` tools=${t.tools.join(", ")}`:"",r=String(t.version||"").trim()?` v${String(t.version||"").trim()}`:"";return`- ${String(t.name||"").trim()}${r}${e}`}):["- none"],p=[`- weget-gateway MCP: ${u.status}`,`- browser runtime: ${g.status}`,`- context: ${String(s||"").trim()||"general"}`,`- default model: ${String(l.aiModel||e()||a).trim()||a}`],w=(t=>{const e=String(t||"").trim().toLowerCase();return!!e&&[/macro/,/economic/,/economy/,/calendar/,/news/,/usd\s*\/\s*jpy/,/usd_jpy/,/risk sentiment/,/economic situation/,/macro snapshot/,/经济/,/宏观/,/日历/,/新闻/,/形势/,/経済/,/マクロ/,/ニュース/,/カレンダー/,/相場/].some(t=>t.test(e))})(t)?["","Macro tool routing hint:","- This request matches macro intent.","- Before answering, use the WeGet macro tools to fetch snapshot, calendar, and news if available.","- Prefer gateway tool calls such as weget_macro_get_snapshot, weget_macro_list_calendar, and weget_macro_list_news.","- Base the summary on fetched tool results rather than unsupported memory."]:[],f=String(r||"").trim(),y=S(n),h=S(i),x=C(y.latest_continuation_state),v=(t=>{const e=Array.isArray(t)?t:[];return e.length?e.slice(-6).map((t,e)=>{const r=S(t),s=C(r.continuation_state);return[`History ${e+1}:`,`- task_id: ${_(r.task_id)||"-"}`,`- title: ${_(r.title)||"-"}`,`- status: ${_(r.status)||"-"}`,`- prompt: ${_(r.prompt)||"-"}`,`- summary: ${_(r.summary)||"-"}`,...s?["- continuation state:",...s.split("\n").map(t=>` ${t}`)]:[]].join("\n")}).join("\n\n"):""})(y.conversation_context);return["You are running inside WeGet Bot Host.","","Active skills:",...m,"","Runtime status:",...p,"","Tool policy:","- Prefer direct reasoning for simple text-only requests.","- When the user asks about macro conditions, economic situation, calendar, or news, prefer the WeGet macro tools before summarizing.","- Use WeGet browser tools only for website access, page extraction, navigation, or screenshots.","- If browser runtime or gateway MCP is unavailable, state that limitation explicitly instead of pretending the task was executed.","- For trading or any action with side effects, do not claim execution unless a concrete tool call actually performed it.","- After the user-visible answer, always append a fenced control block using ```weget_result with compact JSON.",'- The JSON must be {"status":"completed"} for a finished task.','- If you must pause and wait for a user reply before continuing, output {"status":"pending_request","pending_request":{"type":"input","prompt":"...","subtype":"text"}}.','- For approvals, use {"status":"pending_request","pending_request":{"type":"approval","prompt":"...","subtype":"generic"}}.',"- Only use status=pending_request when the task truly cannot continue without the user.","- Keep the final answer plain text only.",...w,...f?["","System prompt:",f]:[],"","Task context:",...[`- conversation id: ${_(y.conversation_id)||"-"}`,`- continue from task id: ${_(y.continue_from_task_id)||"-"}`,`- source: ${_(y.source)||_(h.source)||"-"}`,`- channel: ${_(y.channel)||_(h.channel)||"-"}`],...x?["","Latest continuation state:",x]:[],...v?["","Recent conversation context:",v]:[],..._(h.pending_request_id)||_(h.pending_request_type)?["","Pending request context:",`- request id: ${_(h.pending_request_id)||"-"}`,`- request type: ${_(h.pending_request_type)||"-"}`,`- response via: ${_(h.pending_response_via)||"-"}`,`- response text: ${_(h.pending_response_text)||"-"}`]:[],"","User request:",String(t||"").trim(),"","Respond as plain text only."].join("\n")},P=async t=>{const e=String(t||"").trim();if(!e)return{status:"unknown",detail:"MCP server name is required"};try{"weget-gateway"===e&&await w().catch(()=>{});const t=await b({cwd:r(),args:["mcp","get",e],extraEnv:m()}),s=String(t.stdout||t.stderr||"").trim();return 0!==t.exitCode?/No MCP server named/i.test(s)?{status:"missing",detail:s}:{status:"unknown",detail:s||"codex mcp list failed"}:"playwright"!==e||(t=>{const e=String(t||"");return"win32"===process.platform?/command:\s+.*node(?:\.exe)?/i.test(e)&&/@playwright[\\\/]mcp[\\\/]cli\.js/i.test(e)&&/args:\s+.*--headless/i.test(e):/command:\s+npx/i.test(e)&&/@playwright\/mcp@latest/i.test(e)&&/args:\s+.*--headless/i.test(e)})(s)?"weget-gateway"!==e||E(s,r())?{status:"configured",detail:s||`${e} is configured`}:{status:"missing",detail:"WeGet Gateway MCP exists but points to an outdated launch path. Reinstall it from the host UI."}:{status:"missing",detail:"Playwright MCP exists but uses an unsupported Windows wrapper. Click Install Playwright MCP to repair it."}}catch(t){return{status:"unknown",detail:t instanceof Error?t.message:String(t)}}},k=async()=>{try{const t="win32"===process.platform?"powershell.exe":"npx",e="win32"===process.platform?["-NoProfile","-Command",`& '${$().replace(/'/g,"''")}' playwright install --list`]:["playwright","install","--list"],s=await A({command:t,args:e,cwd:r()}),n=String(s.stdout||s.stderr||"").trim();return 0!==s.exitCode?{status:"unknown",detail:n||"playwright install --list failed"}:/chromium-\d+/i.test(n)||/\\ms-playwright\\chromium-/i.test(n)?{status:"installed",detail:n}:{status:"missing",detail:n||"Chromium is not installed for Playwright."}}catch(t){return{status:"unknown",detail:t instanceof Error?t.message:String(t)}}};return{runPrompt:async({prompt:i,model:d,systemPrompt:l,contextLabel:f,promptBundle:x,metadata:_,onStatus:C})=>{const $=String(d||e()||a).trim()||a,P=r(),k=String(S(x).conversation_id||"").trim();await w().catch(()=>{});const O=v(o()),j=await t.mkdtemp(n.join(s.tmpdir(),"weget-codex-")),E=n.join(j,"last-message.txt"),A=await y({userPrompt:String(i||"").trim(),systemPrompt:String(l||"").trim(),contextLabel:f,promptBundle:x,metadata:_});O.write("exec_start",{model:$,cwd:P,context:String(f||"").trim(),prompt:String(i||"").trim(),instruction:A,outputPath:E,tempDir:j,gatewayContextFile:p(),traceStartedAt:O.startedAt}),C?.("Preparing request"),c("[codex] exec start",{model:$,cwd:P,context:String(f||"").trim(),conversationId:k||void 0});try{const e=async(t,e)=>b({cwd:P,extraEnv:m(),args:t,stdinText:e,onStdoutLine:t=>{O.write("stdout_line",{line:t});const e=h(t);if(e)return/^re-connecting/i.test(e)||/checking tools|tool use|using tool|calling tool/i.test(e)?(O.write("status",{source:"stdout",status:e}),void C?.(e)):void(/reading data|fetching|loading|snapshot|calendar|news/i.test(e)&&(O.write("status",{source:"stdout",status:e}),C?.(e)))},onStderrLine:t=>{O.write("stderr_line",{line:t});const e=String(t||"").match(/session id:\s*([a-zA-Z0-9-]+)/i);e?.[1]&&O.setSessionId(e[1]);const r=h(t);if(r&&!/^--------$/.test(r)&&!/^user$/i.test(r))return/^OpenAI Codex/i.test(r)?(O.write("status",{source:"stderr",status:"Starting Codex"}),void C?.("Starting Codex")):void(/workdir:|model:|provider:|approval:|sandbox:|reasoning effort:|reasoning summaries:|session id:/i.test(r)||(O.write("status",{source:"stderr",status:r}),C?.(r)))}}),r=k?await(async t=>{const e=String(t||"").trim();if(!e)return"";const r=await u();return String(r[e]||"").trim()})(k):"";let s=!1,n=null;if(r&&(s=!0,O.write("resume_attempt",{conversationId:k,sessionId:r}),C?.("Resuming previous Codex session"),n=await e(buildCodexResumeArgs({cwd:P,sessionId:r,outputPath:E}),A),0!==n.exitCode&&(O.write("resume_failed",{conversationId:k,sessionId:r,stdout:n.stdout,stderr:n.stderr}),await(async t=>{const e=String(t||"").trim();if(!e)return;const r=await u();r[e]&&(delete r[e],await g(r))})(k).catch(()=>{}),s=!1)),!s){const t=await I({cwd:P,model:$,outputPath:E});n=await e(t,A)}if(!n)throw new Error("codex execution result is missing");O.write("status",{source:"host",status:"Finalizing reply"}),C?.("Finalizing reply");const i=await t.readFile(E,"utf8").catch(()=>"");if(0!==n.exitCode)throw O.write("exec_error",{exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr,lastMessage:i}),new Error((n.stderr||n.stdout||`codex exec failed with code ${n.exitCode}`).trim());const o=String(i||n.stdout||"").trim();if(!o)throw O.write("exec_error",{exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr,lastMessage:i,error:"codex returned empty output"}),new Error("codex returned empty output");return O.write("exec_completed",{exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr,lastMessage:i,finalAnswer:o}),c("[codex] exec completed",{model:$,context:String(f||"").trim(),resumed:s,conversationId:k||void 0}),k&&O.sessionId&&await(async({conversationId:t,sessionId:e})=>{const r=String(t||"").trim(),s=String(e||"").trim();if(!r||!s)return;const n=await u();n[r]=s,await g(n)})({conversationId:k,sessionId:O.sessionId}).catch(()=>{}),o}catch(t){throw O.write("exec_exception",{error:t instanceof Error?t.message:String(t)}),t}finally{const e=await O.flush().catch(()=>"");e&&c("[codex] session trace written",{context:String(f||"").trim(),tracePath:e}),await t.rm(j,{recursive:!0,force:!0}).catch(()=>{})}},getAuthStatus:async()=>{try{const t=await b({cwd:r(),args:["login","status"]}),e=String(t.stdout||t.stderr||"").trim();return 0!==t.exitCode?{status:"unknown",detail:e||"codex login status failed"}:/logged in/i.test(e)?{status:"logged_in",detail:e}:/not logged in|logged out/i.test(e)?{status:"logged_out",detail:e}:{status:"unknown",detail:e||"unknown codex auth state"}}catch(t){return{status:"unknown",detail:t instanceof Error?t.message:String(t)}}},startLogin:async()=>{const e=r();try{if("win32"===process.platform){const r=String(e||"").replace(/'/g,"''"),o=n.join(s.tmpdir(),`weget-codex-login-${Date.now()}.ps1`),a=["$OutputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)","chcp 65001 > $null",'$env:NO_COLOR = "1"','$env:FORCE_COLOR = "0"','$env:CLICOLOR = "0"','$env:LANG = "en_US.UTF-8"','$env:LC_ALL = "en_US.UTF-8"',`Set-Location -LiteralPath '${r}'`,"$stripAnsi = {"," param([string]$line)",' if ($null -eq $line) { return "" }',' return ($line -replace "`e\\[[0-9;?]*[ -/]*[@-~]", "")',"}","codex login --device-auth 2>&1 | ForEach-Object { & $stripAnsi ([string]$_) }"].join(`${s.EOL}`);await t.writeFile(o,a,"utf8");const c=["$script = @()",'$script += "-NoExit"','$script += "-ExecutionPolicy"','$script += "Bypass"','$script += "-File"',`$script += '${o.replace(/'/g,"''")}'`,`Start-Process -FilePath 'powershell.exe' -WorkingDirectory '${r}' -ArgumentList $script`].join("; ");return i("powershell.exe",["-NoProfile","-ExecutionPolicy","Bypass","-Command",c],{cwd:e,stdio:"ignore",windowsHide:!0}).unref(),{ok:!0,detail:"Started `codex login --device-auth` in a new PowerShell window with UTF-8 output."}}if("darwin"===process.platform){const t=`cd ${JSON.stringify(e)} && codex login --device-auth`;return i("open",["-a","Terminal",t],{cwd:e,detached:!0,stdio:"ignore"}).unref(),{ok:!0,detail:"Started `codex login --device-auth` in Terminal."}}const r=`cd ${JSON.stringify(e)} && codex login --device-auth; exec $SHELL`,o=[["x-terminal-emulator",["-e","bash","-lc",r]],["gnome-terminal",["--","bash","-lc",r]],["konsole",["-e","bash","-lc",r]]];for(const[t,r]of o)try{return i(t,r,{cwd:e,detached:!0,stdio:"ignore"}).unref(),{ok:!0,detail:`Started \`codex login --device-auth\` using ${t}.`}}catch{}return{ok:!1,detail:"No supported terminal launcher was found. Run `codex login --device-auth` manually."}}catch(t){const e=t instanceof Error?t.message:String(t);return c("[codex] login launch failed",{error:e}),{ok:!1,detail:e}}},getMcpServerStatus:P,installWegetGatewayMcp:async()=>{try{await w();const t=await P("weget-gateway");if("configured"===t.status&&E(t.detail,r()))return{ok:!0,detail:t.detail||"WeGet Gateway MCP is already configured."};const e=await b({cwd:r(),args:["mcp","remove","weget-gateway"],extraEnv:m()});if(0!==e.exitCode){const t=String(e.stdout||e.stderr||"").trim().toLowerCase();if(!(t.includes("no mcp server named")||t.includes("not found")||t.includes("no server named")))return{ok:!1,detail:String(e.stdout||e.stderr||"").trim()||"Failed to remove existing WeGet Gateway MCP entry."}}const s=O(r()),n=await b({cwd:r(),args:["mcp","add","weget-gateway",s.command,...s.args],extraEnv:m()}),i=String(n.stdout||n.stderr||"").trim();if(0!==n.exitCode)return{ok:!1,detail:i||"Failed to add WeGet Gateway MCP to Codex."};const o=await P("weget-gateway");return"configured"===o.status?{ok:!0,detail:o.detail||"WeGet Gateway MCP configured."}:{ok:!0,detail:i||"WeGet Gateway MCP add command completed, but verification returned no explicit entry."}}catch(t){const e=t instanceof Error?t.message:String(t);return c("[codex] weget gateway mcp install failed",{error:e}),{ok:!1,detail:e}}},getPlaywrightBrowserStatus:k,getGatewayContextFilePath:p,runGatewaySelfTest:async e=>{if("codex_macro"===e){const i=(new Date).toISOString().slice(0,10);await w().catch(()=>{});const c=v(o()),d=await t.mkdtemp(n.join(s.tmpdir(),"weget-codex-test-")),l=n.join(d,"last-message.txt"),u=["You are running a WeGet gateway chain self-test.","Use WeGet gateway MCP tools only.",`Call weget_macro_get_snapshot, weget_macro_list_calendar with dateKey=${i}, and weget_macro_list_news with dateKey=${i}.`,"If all three tool calls succeed, reply with exactly: OK macro chain","If any tool call fails, reply with exactly: NG macro chain: <short reason>","Respond as plain text only."].join("\n");c.write("exec_start",{model:a,cwd:r(),context:"gateway_self_test:codex_macro",prompt:"codex macro chain self-test",instruction:u,outputPath:l,tempDir:d,gatewayContextFile:p(),traceStartedAt:c.startedAt});try{const s=await I({cwd:r(),model:a,outputPath:l}),n=await b({cwd:r(),extraEnv:m(),args:s,stdinText:u,onStdoutLine:t=>{c.write("stdout_line",{line:t})},onStderrLine:t=>{c.write("stderr_line",{line:t});const e=String(t||"").match(/session id:\s*([a-zA-Z0-9-]+)/i);e?.[1]&&c.setSessionId(e[1])}}),o=await t.readFile(l,"utf8").catch(()=>""),g=String(o||n.stdout||"").trim(),w=String(n.stderr||""),y=String(n.stdout||""),h=/tool weget-gateway\.weget_macro_get_snapshot\(\{\}\)/i.test(w),S=new RegExp(`tool weget-gateway\\.weget_macro_list_calendar\\(\\{"dateKey":"${j(i)}"\\}\\)`,"i").test(w),_=new RegExp(`tool weget-gateway\\.weget_macro_list_news\\(\\{"dateKey":"${j(i)}"\\}\\)`,"i").test(w),C=/weget-gateway\.weget_macro_get_snapshot\(\{\}\) success/i.test(w),v=/weget-gateway\.weget_macro_list_calendar\(\{\"dateKey\":\"\d{4}-\d{2}-\d{2}\"\}\) success/i.test(w),$=/weget-gateway\.weget_macro_list_news\(\{\"dateKey\":\"\d{4}-\d{2}-\d{2}\"\}\) success/i.test(w),P=0===n.exitCode&&/^OK macro chain$/i.test(g)&&h&&S&&_&&C&&v&&$;c.write(P?"exec_completed":"exec_error",{exitCode:n.exitCode,stdout:y,stderr:w,lastMessage:o,finalAnswer:g,invokedSnapshot:h,invokedCalendar:S,invokedNews:_,succeededSnapshot:C,succeededCalendar:v,succeededNews:$});const k=await c.flush().catch(()=>""),O={target:e,ok:P,summary:P?"Codex gateway macro chain OK":"Codex gateway macro chain failed",contextFilePath:p(),tracePath:k,codex:{exitCode:n.exitCode,stdout:y,stderr:w,finalAnswer:g},assertions:{invokedSnapshot:h,invokedCalendar:S,invokedNews:_,succeededSnapshot:C,succeededCalendar:v,succeededNews:$},ts:(new Date).toISOString()},E=await f({target:e,payload:O});return await t.rm(d,{recursive:!0,force:!0}).catch(()=>{}),{ok:P,summary:P?"Codex gateway macro chain OK":x(g||w,"Codex gateway macro chain failed"),detail:g||w||y,logPath:E,result:O}}catch(r){c.write("exec_exception",{error:r instanceof Error?r.message:String(r)});const s=await c.flush().catch(()=>""),n=r instanceof Error?r.message:String(r),i=await f({target:e,payload:{target:e,ok:!1,summary:"Codex gateway macro chain failed",contextFilePath:p(),tracePath:s,error:n,ts:(new Date).toISOString()}});return await t.rm(d,{recursive:!0,force:!0}).catch(()=>{}),{ok:!1,summary:x(n,"Codex gateway macro chain failed"),detail:n,logPath:i}}}await w();const c=O(r());let d,l=null;try{d=await A({command:c.command,args:[...c.args,"--self-test","--test-target",e],cwd:r()}),"gateway"===e&&(l=await(async({command:t,args:e,cwd:r,timeoutMs:s=2500})=>new Promise((n,o)=>{const a=i(t,e,{cwd:r,stdio:["ignore","pipe","pipe"],windowsHide:!0,env:{...process.env,NO_COLOR:"1",FORCE_COLOR:"0",CLICOLOR:"0"}}),c=[],d=[];let l=!1;const u=t=>{l||(l=!0,n(t))};a.stdout.on("data",t=>c.push(Buffer.from(t))),a.stderr.on("data",t=>d.push(Buffer.from(t))),a.on("error",t=>{l||(l=!0,o(t))}),a.on("close",t=>{u({ok:!1,exitCode:Number.isFinite(t)?Number(t):1,stdout:Buffer.concat(c).toString("utf8"),stderr:Buffer.concat(d).toString("utf8"),responseText:""})}),setTimeout(()=>{const t=a.stdin;t?.write(`${JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"weget-gateway-probe",version:"0.1.0"}}})}\n`)},150),setTimeout(()=>{if(l)return;const t=Buffer.concat(c).toString("utf8"),e=Buffer.concat(d).toString("utf8"),r=t.trim(),s=/"result"\s*:/.test(r)&&/"protocolVersion"\s*:/.test(r);a.kill(),u({ok:s,exitCode:null,stdout:t,stderr:e,responseText:r})},Math.max(250,s))}))({command:c.command,args:c.args,cwd:r()}))}catch(t){const r=t instanceof Error?t.message:String(t),s=await f({target:e,payload:{target:e,launch:c,contextFilePath:p(),failure:"spawn_exception",error:r,ts:(new Date).toISOString()}});return{ok:!1,summary:x(r,`${e} test failed`),detail:r,logPath:s}}const u=String(d.stdout||d.stderr||"").trim();let g;if(u)try{g=JSON.parse(u)}catch{}let y=0===d.exitCode&&Boolean(!g||!1!==g.ok),h=x(u,`${e} self-test completed`);"gateway"===e&&l&&!l.ok?(y=!1,h=x(l.stderr||l.stdout,"Gateway handshake probe failed.")):"gateway"===e&&l?.ok&&(h="Gateway MCP handshake OK");const S={target:e,ok:y,summary:h,launch:c,contextFilePath:p(),selfTest:{exitCode:d.exitCode,stdout:d.stdout,stderr:d.stderr,result:g??null},handshakeProbe:l?{ok:l.ok,exitCode:l.exitCode,stdout:l.stdout,stderr:l.stderr,responseText:l.responseText}:null,ts:(new Date).toISOString()};return{ok:y,summary:h,detail:u||h,logPath:await f({target:e,payload:S}),result:g}}}};
|
|
1
|
+
import t from"node:fs/promises";import{existsSync as e}from"node:fs";import{createRequire as r}from"node:module";import s from"node:os";import n from"node:path";import{spawn as i,spawnSync as o}from"node:child_process";const a="gpt-5.4",c=r(import.meta.url);let d=null,l=null,u=null,g=null,m=null,p=null;const w=async t=>{if(!t)return"";const e=[];for await(const r of t)e.push(Buffer.isBuffer(r)?r:Buffer.from(String(r)));return Buffer.concat(e).toString("utf8")},f=t=>String(t||"").replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,""),y=t=>{let e="";return{push:r=>{if(!t)return;e+=f(Buffer.isBuffer(r)?r.toString("utf8"):String(r));const s=e.split(/\r?\n/);e=s.pop()||"";for(const e of s){const r=String(e||"").trim();r&&t(r)}},flush:()=>{if(!t)return;const r=String(e||"").trim();r&&t(r),e=""}}},h=t=>{const e=f(String(t||"")).trim();return e?/^[{}[\],:]+$/.test(e)||/^["']?[a-zA-Z0-9_-]+["']?\s*:\s*$/.test(e)||/^`{3,}|^#{1,6}\s*$/.test(e)||/^Assistant:?\s*$/i.test(e)||/^Warning: no last agent message/i.test(e)?"":/^ERROR:\s*unexpected status\s+\d+/i.test(e)?"Tool call failed":e:""},x=(t,e)=>{const r=f(String(t||"")).replace(/\s+/g," ").trim();return r?r.length>180?`${r.slice(0,177)}...`:r:e},S=t=>t&&"object"==typeof t?t:{},_=t=>String(t||"").trim(),v=t=>{const e=S(t);if(0===Object.keys(e).length)return"";const r=S(e.waiting),s=[`- source task: ${_(e.task_id)||"-"}`,`- status: ${_(e.status)||"-"}`,`- title: ${_(e.title)||"-"}`,`- user prompt: ${_(e.user_prompt)||"-"}`,`- summary: ${_(e.summary)||"-"}`,`- latest output: ${_(e.latest_output)||"-"}`,`- last event: ${_(e.last_event_type)||"-"}`];return Object.keys(r).length>0&&(s.push(`- waiting type: ${_(r.type)||"-"}`),s.push(`- waiting subtype: ${_(r.subtype)||"-"}`),s.push(`- waiting prompt: ${_(r.prompt)||"-"}`)),s.join("\n")},$=e=>{const r=(new Date).toISOString(),s=`exec-${Date.now()}`;let i="",o="",a=!1,c=Promise.resolve();const d=[],l=async()=>{if(a)return o;const r=n.join(e,"codex-sessions");await t.mkdir(r,{recursive:!0});const c=String(i||s||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_");return o=n.join(r,`${c}.log`),a=!0,d.length>0&&(await t.appendFile(o,d.join(""),"utf8"),d.length=0),o},u=(e,r={})=>{(e=>{c=c.then(async()=>{if(!a&&!i)return void d.push(e);const r=await l();await t.appendFile(r,e,"utf8")}).catch(()=>{})})(`${JSON.stringify({ts:(new Date).toISOString(),event:e,...r},null,0)}\n`)};return{get startedAt(){return r},get sessionId(){return i},setSessionId:t=>{const e=String(t||"").trim();e&&i!==e&&(i=e,u("session_identified",{sessionId:e}))},write:u,flush:async()=>(a||await l(),await c,o)}},C=()=>{if(l)return l;const t=[n.join(String(process.env.ProgramFiles||"C:\\Program Files"),"nodejs","npx.cmd"),n.join(String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)"),"nodejs","npx.cmd"),n.join(String(process.env.APPDATA||""),"npm","npx.cmd")].filter(Boolean);for(const r of t)if(e(r))return l=r,r;const r=o("where.exe",["npx.cmd"],{windowsHide:!0,encoding:"utf8"}),s=String(r.stdout||"").split(/\r?\n/).map(t=>t.trim()).find(Boolean);return s?(l=s,s):"npx.cmd"},k=()=>{if(u)return u;const t=[n.join(String(process.env.ProgramFiles||"C:\\Program Files"),"nodejs","node.exe"),n.join(String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)"),"nodejs","node.exe")];for(const r of t)if(e(r))return u=r,r;return u="node",u},P=()=>{if(g)return g;try{const t=c.resolve("@ai.weget.jp/weget-gateway-mcp/dist/index.js");return g=t,t}catch{}const t=[n.join(String(process.env.APPDATA||""),"npm","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js"),n.join(String(process.env.ProgramFiles||"C:\\Program Files"),"nodejs","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js"),n.join(String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)"),"nodejs","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js")].filter(Boolean);for(const r of t)if(e(r))return g=r,r;return g=n.join(String(process.env.APPDATA||""),"npm","node_modules","@ai.weget.jp","weget-gateway-mcp","dist","index.js"),g},b=t=>{const e=n.join(t,"weget-gateway-context.json");return"win32"===process.platform?{command:k(),args:[P(),"--context-file",e]}:{command:"npx",args:["-y","@ai.weget.jp/weget-gateway-mcp@latest","--context-file",e]}},O=t=>String(t||"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),E=(t,e)=>{const r=String(t||""),s=b(e);if("win32"===process.platform){const t=new RegExp(`command:\\s+${O(s.command)}`,"i"),e=new RegExp(O(String(s.args[0]||"")),"i"),n=new RegExp(O(String(s.args[2]||"")),"i");return t.test(r)&&e.test(r)&&n.test(r)}return new RegExp(`command:\\s+${O(s.command)}`,"i").test(r)&&r.includes(String(s.args[0]||""))},j=async({args:t,stdinText:r,cwd:s,extraEnv:a,abortSignal:c,onStdoutLine:l,onStderrLine:u})=>new Promise((g,m)=>{const p=t=>{const e="object"==typeof t&&t&&"code"in t?String(t.code||""):"";return"EPIPE"===e||"ERR_STREAM_DESTROYED"===e},f=(()=>{if(d)return d;if("win32"===process.platform){const t=[String(process.env.ProgramFiles||"C:\\Program Files"),String(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)")];for(const r of t){const t=n.join(r,"nodejs","node.exe"),s=n.join(r,"nodejs","node_modules","@openai","codex","bin","codex.js");if(e(t)&&e(s))return d={command:t,baseArgs:[s]},d}const r=["codex.cmd","codex"];for(const t of r){const e=o("where.exe",[t],{windowsHide:!0,encoding:"utf8"}),r=String(e.stdout||"").split(/\r?\n/).map(t=>t.trim()).find(Boolean);if(r)return d={command:r,baseArgs:[]},d}}return d={command:"codex",baseArgs:[]},d})(),h={...process.env,...a||{},NO_COLOR:"1",FORCE_COLOR:"0",CLICOLOR:"0"},x=i(f.command,[...f.baseArgs,...t],{cwd:s,stdio:["pipe","pipe","pipe"],windowsHide:!0,env:h});let S=!1;const _=t=>{S||(S=!0,t())},v=w(x.stdout),$=w(x.stderr),C=y(l),k=y(u);if(x.stdout?.on("data",t=>C.push(t)),x.stderr?.on("data",t=>k.push(t)),x.on("error",t=>{_(()=>m(t))}),x.stdin?.on("error",t=>{p(t)||_(()=>m(t))}),x.on("close",async t=>{C.flush(),k.flush();const[e,r]=await Promise.all([v,$]);_(()=>g({stdout:e,stderr:r,exitCode:Number.isFinite(t)?Number(t):1}))}),c){const t=()=>{try{x.kill()}catch{}_(()=>m(new Error("codex execution aborted")))};if(c.aborted)return void t();c.addEventListener("abort",t,{once:!0}),x.on("close",()=>{c.removeEventListener("abort",t)})}"string"==typeof r&&x.stdin?.writable?x.stdin.write(r,t=>{!t||p(t)?x.stdin?.end():_(()=>m(t))}):x.stdin?.end()}),A=async({command:t,args:e,cwd:r})=>new Promise((s,n)=>{const o=i(t,e,{cwd:r,stdio:["pipe","pipe","pipe"],windowsHide:!0,env:{...process.env,NO_COLOR:"1",FORCE_COLOR:"0",CLICOLOR:"0"}});let a=!1;const c=t=>{a||(a=!0,t())},d=w(o.stdout),l=w(o.stderr);o.on("error",t=>c(()=>n(t))),o.on("close",async t=>{const[e,r]=await Promise.all([d,l]);c(()=>s({stdout:e,stderr:r,exitCode:Number.isFinite(t)?Number(t):1}))})}),F=async t=>null!==m?m:(p||(p=(async(t,e)=>{const r=String(t||"").trim();if(!r)return!1;try{const t=await j({cwd:e,args:["features","list"]});return 0===t.exitCode&&f(`${t.stdout||""}\n${t.stderr||""}`).split(/\r?\n/).map(t=>t.trim()).some(t=>t.startsWith(`${r} `)||t===r)}catch{return!1}})("rmcp_client",t).then(t=>(m=t,t)).finally(()=>{p=null})),p),I=async({cwd:t,model:e,outputPath:r})=>{const s=["exec"];return await F(t)&&s.push("--enable","rmcp_client"),s.push("--dangerously-bypass-approvals-and-sandbox","-C",t,"--skip-git-repo-check","-m",e,"-o",r,"-"),s};export const buildCodexResumeArgs=({cwd:t,sessionId:e,outputPath:r})=>["exec","--dangerously-bypass-approvals-and-sandbox","-C",t,"--skip-git-repo-check","-o",r,"resume",e,"-"];export const createCodexService=({getModel:e,getWorkingDir:r,getLogDir:o,emitLog:c,getGatewayContext:d})=>{const l=()=>n.join(o(),"codex-session-bindings.json"),u=async()=>{try{const e=await t.readFile(l(),"utf8"),r=JSON.parse(e);return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[String(t||"").trim(),String(e||"").trim()]).filter(([t,e])=>t&&e))}catch{return{}}},g=async e=>{await t.mkdir(o(),{recursive:!0}),await t.writeFile(l(),`${JSON.stringify(e,null,2)}\n`,"utf8")},m=()=>{const t=d?.()||{},e={WEGET_ACTIVE_SKILLS:JSON.stringify(Array.isArray(t.activeSkills)?t.activeSkills:[]),WEGET_HOST_METADATA:JSON.stringify(t.hostMetadata&&"object"==typeof t.hostMetadata?t.hostMetadata:{})},r=String(t.botId||"").trim(),s=String(t.platformApiBaseUrl||"").trim(),n=String(t.accessToken||"").trim(),i=String(t.fileBridgeDir||"").trim(),o=String(t.fileBridgeToken||"").trim();return r&&(e.WEGET_BOT_ID=r),s&&(e.WEGET_PLATFORM_API_BASE_URL=s),n&&(e.WEGET_PLATFORM_API_ACCESS_TOKEN=n),i&&(e.WEGET_FILE_BRIDGE_DIR=i),o&&(e.WEGET_FILE_BRIDGE_TOKEN=o),e},p=()=>n.join(r(),"weget-gateway-context.json"),w=async()=>{const e=d?.()||{};await t.mkdir(r(),{recursive:!0}),await t.writeFile(p(),JSON.stringify({activeSkills:Array.isArray(e.activeSkills)?e.activeSkills:[],hostMetadata:e.hostMetadata&&"object"==typeof e.hostMetadata?e.hostMetadata:{},botId:String(e.botId||"").trim(),platformApiBaseUrl:String(e.platformApiBaseUrl||"").trim(),accessToken:String(e.accessToken||"").trim(),fileBridgeDir:String(e.fileBridgeDir||"").trim(),fileBridgeToken:String(e.fileBridgeToken||"").trim(),logDir:o(),updatedAt:(new Date).toISOString()},null,2),"utf8")},f=async({target:e,payload:r})=>{const s=n.join(o(),"gateway-tests");await t.mkdir(s,{recursive:!0});const i=n.join(s,`${e}-${Date.now()}.log`);return await t.writeFile(i,`${JSON.stringify(r,null,2)}\n`,"utf8"),i},y=async({userPrompt:t,systemPrompt:r,contextLabel:s,promptBundle:n,metadata:i})=>{const o=d?.()||{},c=Array.isArray(o.activeSkills)?o.activeSkills:[],l=o.hostMetadata&&"object"==typeof o.hostMetadata?o.hostMetadata:{},[u,g]=await Promise.all([k("weget-gateway"),P()]),m=c.length?c.map(t=>{const e=Array.isArray(t.tools)&&t.tools.length?` tools=${t.tools.join(", ")}`:"",r=String(t.version||"").trim()?` v${String(t.version||"").trim()}`:"";return`- ${String(t.name||"").trim()}${r}${e}`}):["- none"],p=[`- weget-gateway MCP: ${u.status}`,`- browser runtime: ${g.status}`,`- context: ${String(s||"").trim()||"general"}`,`- default model: ${String(l.aiModel||e()||a).trim()||a}`],w=(t=>{const e=String(t||"").trim().toLowerCase();return!!e&&[/macro/,/economic/,/economy/,/calendar/,/news/,/usd\s*\/\s*jpy/,/usd_jpy/,/risk sentiment/,/economic situation/,/macro snapshot/,/经济/,/宏观/,/日历/,/新闻/,/形势/,/経済/,/マクロ/,/ニュース/,/カレンダー/,/相場/].some(t=>t.test(e))})(t)?["","Macro tool routing hint:","- This request matches macro intent.","- Before answering, use the WeGet macro tools to fetch snapshot, calendar, and news if available.","- Prefer gateway tool calls such as weget_macro_get_snapshot, weget_macro_list_calendar, and weget_macro_list_news.","- Base the summary on fetched tool results rather than unsupported memory."]:[],f=String(r||"").trim(),y=S(n),h=S(i),x=v(y.latest_continuation_state),$=(t=>{const e=Array.isArray(t)?t:[];return e.length?e.slice(-6).map((t,e)=>{const r=S(t),s=v(r.continuation_state);return[`History ${e+1}:`,`- task_id: ${_(r.task_id)||"-"}`,`- title: ${_(r.title)||"-"}`,`- status: ${_(r.status)||"-"}`,`- prompt: ${_(r.prompt)||"-"}`,`- summary: ${_(r.summary)||"-"}`,...s?["- continuation state:",...s.split("\n").map(t=>` ${t}`)]:[]].join("\n")}).join("\n\n"):""})(y.conversation_context),C=(t=>{const e=S(t);if(0===Object.keys(e).length)return"";const r=S(e.active_task),s=S(e.new_message);return[`- mode: ${_(e.mode)||"-"}`,`- requested at: ${_(e.requested_at)||"-"}`,`- active task id: ${_(r.task_id)||"-"}`,`- active status: ${_(r.status)||"-"}`,`- active title: ${_(r.title)||"-"}`,`- active prompt: ${_(r.prompt)||"-"}`,`- active summary: ${_(r.summary)||"-"}`,`- active started at: ${_(r.started_at)||"-"}`,`- active updated at: ${_(r.updated_at)||"-"}`,`- incoming source: ${_(s.source)||"-"}`,`- incoming channel: ${_(s.channel)||"-"}`,`- incoming prompt: ${_(s.prompt)||"-"}`].join("\n")})(y.arbitration_context||h.arbitration_context);return["You are running inside WeGet Bot Host.","","Active skills:",...m,"","Runtime status:",...p,"","Tool policy:","- Prefer direct reasoning for simple text-only requests.","- When the user asks about macro conditions, economic situation, calendar, or news, prefer the WeGet macro tools before summarizing.","- Use WeGet browser tools only for website access, page extraction, navigation, or screenshots.","- If browser runtime or gateway MCP is unavailable, state that limitation explicitly instead of pretending the task was executed.","- For trading or any action with side effects, do not claim execution unless a concrete tool call actually performed it.","- After the user-visible answer, always append a fenced control block using ```weget_result with compact JSON.",'- The JSON must be {"status":"completed"} for a finished task.','- If you must pause and wait for a user reply before continuing, output {"status":"pending_request","pending_request":{"type":"input","prompt":"...","subtype":"text"}}.','- For approvals, use {"status":"pending_request","pending_request":{"type":"approval","prompt":"...","subtype":"generic"}}.',"- If running task arbitration context is present, you may also output actions with status=completed.",'- Valid arbitration actions are {"status":"completed","action":"report_progress"}, {"status":"completed","action":"continue_current_task"}, {"status":"completed","action":"start_new_task"}, and {"status":"completed","action":"cancel_running_task","target_task_id":"...","reason":"..."}',"- Only use status=pending_request when the task truly cannot continue without the user.","- Keep the final answer plain text only.",...w,...f?["","System prompt:",f]:[],"","Task context:",...[`- conversation id: ${_(y.conversation_id)||"-"}`,`- continue from task id: ${_(y.continue_from_task_id)||"-"}`,`- source: ${_(y.source)||_(h.source)||"-"}`,`- channel: ${_(y.channel)||_(h.channel)||"-"}`],...x?["","Latest continuation state:",x]:[],...$?["","Recent conversation context:",$]:[],...C?["","Running task arbitration context:",C]:[],..._(h.pending_request_id)||_(h.pending_request_type)?["","Pending request context:",`- request id: ${_(h.pending_request_id)||"-"}`,`- request type: ${_(h.pending_request_type)||"-"}`,`- response via: ${_(h.pending_response_via)||"-"}`,`- response text: ${_(h.pending_response_text)||"-"}`]:[],"","User request:",String(t||"").trim(),"","Respond as plain text only."].join("\n")},k=async t=>{const e=String(t||"").trim();if(!e)return{status:"unknown",detail:"MCP server name is required"};try{"weget-gateway"===e&&await w().catch(()=>{});const t=await j({cwd:r(),args:["mcp","get",e],extraEnv:m()}),s=String(t.stdout||t.stderr||"").trim();return 0!==t.exitCode?/No MCP server named/i.test(s)?{status:"missing",detail:s}:{status:"unknown",detail:s||"codex mcp list failed"}:"playwright"!==e||(t=>{const e=String(t||"");return"win32"===process.platform?/command:\s+.*node(?:\.exe)?/i.test(e)&&/@playwright[\\\/]mcp[\\\/]cli\.js/i.test(e)&&/args:\s+.*--headless/i.test(e):/command:\s+npx/i.test(e)&&/@playwright\/mcp@latest/i.test(e)&&/args:\s+.*--headless/i.test(e)})(s)?"weget-gateway"!==e||E(s,r())?{status:"configured",detail:s||`${e} is configured`}:{status:"missing",detail:"WeGet Gateway MCP exists but points to an outdated launch path. Reinstall it from the host UI."}:{status:"missing",detail:"Playwright MCP exists but uses an unsupported Windows wrapper. Click Install Playwright MCP to repair it."}}catch(t){return{status:"unknown",detail:t instanceof Error?t.message:String(t)}}},P=async()=>{try{const t="win32"===process.platform?"powershell.exe":"npx",e="win32"===process.platform?["-NoProfile","-Command",`& '${C().replace(/'/g,"''")}' playwright install --list`]:["playwright","install","--list"],s=await A({command:t,args:e,cwd:r()}),n=String(s.stdout||s.stderr||"").trim();return 0!==s.exitCode?{status:"unknown",detail:n||"playwright install --list failed"}:/chromium-\d+/i.test(n)||/\\ms-playwright\\chromium-/i.test(n)?{status:"installed",detail:n}:{status:"missing",detail:n||"Chromium is not installed for Playwright."}}catch(t){return{status:"unknown",detail:t instanceof Error?t.message:String(t)}}};return{runPrompt:async({prompt:i,model:d,systemPrompt:l,contextLabel:f,promptBundle:x,metadata:_,onStatus:v,abortSignal:C})=>{const k=String(d||e()||a).trim()||a,P=r(),b=String(S(x).conversation_id||"").trim();await w().catch(()=>{});const O=$(o()),E=await t.mkdtemp(n.join(s.tmpdir(),"weget-codex-")),A=n.join(E,"last-message.txt"),F=await y({userPrompt:String(i||"").trim(),systemPrompt:String(l||"").trim(),contextLabel:f,promptBundle:x,metadata:_});O.write("exec_start",{model:k,cwd:P,context:String(f||"").trim(),prompt:String(i||"").trim(),instruction:F,outputPath:A,tempDir:E,gatewayContextFile:p(),traceStartedAt:O.startedAt}),v?.("Preparing request"),c("[codex] exec start",{model:k,cwd:P,context:String(f||"").trim(),conversationId:b||void 0});try{const e=async(t,e)=>j({cwd:P,extraEnv:m(),args:t,stdinText:e,abortSignal:C,onStdoutLine:t=>{O.write("stdout_line",{line:t});const e=h(t);if(e)return/^re-connecting/i.test(e)||/checking tools|tool use|using tool|calling tool/i.test(e)?(O.write("status",{source:"stdout",status:e}),void v?.(e)):void(/reading data|fetching|loading|snapshot|calendar|news/i.test(e)&&(O.write("status",{source:"stdout",status:e}),v?.(e)))},onStderrLine:t=>{O.write("stderr_line",{line:t});const e=String(t||"").match(/session id:\s*([a-zA-Z0-9-]+)/i);e?.[1]&&O.setSessionId(e[1]);const r=h(t);if(r&&!/^--------$/.test(r)&&!/^user$/i.test(r))return/^OpenAI Codex/i.test(r)?(O.write("status",{source:"stderr",status:"Starting Codex"}),void v?.("Starting Codex")):void(/workdir:|model:|provider:|approval:|sandbox:|reasoning effort:|reasoning summaries:|session id:/i.test(r)||(O.write("status",{source:"stderr",status:r}),v?.(r)))}}),r=b?await(async t=>{const e=String(t||"").trim();if(!e)return"";const r=await u();return String(r[e]||"").trim()})(b):"";let s=!1,n=null;if(r&&(s=!0,O.write("resume_attempt",{conversationId:b,sessionId:r}),v?.("Resuming previous Codex session"),n=await e(buildCodexResumeArgs({cwd:P,sessionId:r,outputPath:A}),F),0!==n.exitCode&&(O.write("resume_failed",{conversationId:b,sessionId:r,stdout:n.stdout,stderr:n.stderr}),await(async t=>{const e=String(t||"").trim();if(!e)return;const r=await u();r[e]&&(delete r[e],await g(r))})(b).catch(()=>{}),s=!1)),!s){const t=await I({cwd:P,model:k,outputPath:A});n=await e(t,F)}if(!n)throw new Error("codex execution result is missing");O.write("status",{source:"host",status:"Finalizing reply"}),v?.("Finalizing reply");const i=await t.readFile(A,"utf8").catch(()=>"");if(0!==n.exitCode)throw O.write("exec_error",{exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr,lastMessage:i}),new Error((n.stderr||n.stdout||`codex exec failed with code ${n.exitCode}`).trim());const o=String(i||n.stdout||"").trim();if(!o)throw O.write("exec_error",{exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr,lastMessage:i,error:"codex returned empty output"}),new Error("codex returned empty output");return O.write("exec_completed",{exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr,lastMessage:i,finalAnswer:o}),c("[codex] exec completed",{model:k,context:String(f||"").trim(),resumed:s,conversationId:b||void 0}),b&&O.sessionId&&await(async({conversationId:t,sessionId:e})=>{const r=String(t||"").trim(),s=String(e||"").trim();if(!r||!s)return;const n=await u();n[r]=s,await g(n)})({conversationId:b,sessionId:O.sessionId}).catch(()=>{}),o}catch(t){throw O.write("exec_exception",{error:t instanceof Error?t.message:String(t)}),t}finally{const e=await O.flush().catch(()=>"");e&&c("[codex] session trace written",{context:String(f||"").trim(),tracePath:e}),await t.rm(E,{recursive:!0,force:!0}).catch(()=>{})}},getAuthStatus:async()=>{try{const t=await j({cwd:r(),args:["login","status"]}),e=String(t.stdout||t.stderr||"").trim();return 0!==t.exitCode?{status:"unknown",detail:e||"codex login status failed"}:/logged in/i.test(e)?{status:"logged_in",detail:e}:/not logged in|logged out/i.test(e)?{status:"logged_out",detail:e}:{status:"unknown",detail:e||"unknown codex auth state"}}catch(t){return{status:"unknown",detail:t instanceof Error?t.message:String(t)}}},startLogin:async()=>{const e=r();try{if("win32"===process.platform){const r=String(e||"").replace(/'/g,"''"),o=n.join(s.tmpdir(),`weget-codex-login-${Date.now()}.ps1`),a=["$OutputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)","chcp 65001 > $null",'$env:NO_COLOR = "1"','$env:FORCE_COLOR = "0"','$env:CLICOLOR = "0"','$env:LANG = "en_US.UTF-8"','$env:LC_ALL = "en_US.UTF-8"',`Set-Location -LiteralPath '${r}'`,"$stripAnsi = {"," param([string]$line)",' if ($null -eq $line) { return "" }',' return ($line -replace "`e\\[[0-9;?]*[ -/]*[@-~]", "")',"}","codex login --device-auth 2>&1 | ForEach-Object { & $stripAnsi ([string]$_) }"].join(`${s.EOL}`);await t.writeFile(o,a,"utf8");const c=["$script = @()",'$script += "-NoExit"','$script += "-ExecutionPolicy"','$script += "Bypass"','$script += "-File"',`$script += '${o.replace(/'/g,"''")}'`,`Start-Process -FilePath 'powershell.exe' -WorkingDirectory '${r}' -ArgumentList $script`].join("; ");return i("powershell.exe",["-NoProfile","-ExecutionPolicy","Bypass","-Command",c],{cwd:e,stdio:"ignore",windowsHide:!0}).unref(),{ok:!0,detail:"Started `codex login --device-auth` in a new PowerShell window with UTF-8 output."}}if("darwin"===process.platform){const t=`cd ${JSON.stringify(e)} && codex login --device-auth`;return i("open",["-a","Terminal",t],{cwd:e,detached:!0,stdio:"ignore"}).unref(),{ok:!0,detail:"Started `codex login --device-auth` in Terminal."}}const r=`cd ${JSON.stringify(e)} && codex login --device-auth; exec $SHELL`,o=[["x-terminal-emulator",["-e","bash","-lc",r]],["gnome-terminal",["--","bash","-lc",r]],["konsole",["-e","bash","-lc",r]]];for(const[t,r]of o)try{return i(t,r,{cwd:e,detached:!0,stdio:"ignore"}).unref(),{ok:!0,detail:`Started \`codex login --device-auth\` using ${t}.`}}catch{}return{ok:!1,detail:"No supported terminal launcher was found. Run `codex login --device-auth` manually."}}catch(t){const e=t instanceof Error?t.message:String(t);return c("[codex] login launch failed",{error:e}),{ok:!1,detail:e}}},getMcpServerStatus:k,installWegetGatewayMcp:async()=>{try{await w();const t=await k("weget-gateway");if("configured"===t.status&&E(t.detail,r()))return{ok:!0,detail:t.detail||"WeGet Gateway MCP is already configured."};const e=await j({cwd:r(),args:["mcp","remove","weget-gateway"],extraEnv:m()});if(0!==e.exitCode){const t=String(e.stdout||e.stderr||"").trim().toLowerCase();if(!(t.includes("no mcp server named")||t.includes("not found")||t.includes("no server named")))return{ok:!1,detail:String(e.stdout||e.stderr||"").trim()||"Failed to remove existing WeGet Gateway MCP entry."}}const s=b(r()),n=await j({cwd:r(),args:["mcp","add","weget-gateway",s.command,...s.args],extraEnv:m()}),i=String(n.stdout||n.stderr||"").trim();if(0!==n.exitCode)return{ok:!1,detail:i||"Failed to add WeGet Gateway MCP to Codex."};const o=await k("weget-gateway");return"configured"===o.status?{ok:!0,detail:o.detail||"WeGet Gateway MCP configured."}:{ok:!0,detail:i||"WeGet Gateway MCP add command completed, but verification returned no explicit entry."}}catch(t){const e=t instanceof Error?t.message:String(t);return c("[codex] weget gateway mcp install failed",{error:e}),{ok:!1,detail:e}}},getPlaywrightBrowserStatus:P,getGatewayContextFilePath:p,runGatewaySelfTest:async e=>{if("codex_macro"===e){const i=(new Date).toISOString().slice(0,10);await w().catch(()=>{});const c=$(o()),d=await t.mkdtemp(n.join(s.tmpdir(),"weget-codex-test-")),l=n.join(d,"last-message.txt"),u=["You are running a WeGet gateway chain self-test.","Use WeGet gateway MCP tools only.",`Call weget_macro_get_snapshot, weget_macro_list_calendar with dateKey=${i}, and weget_macro_list_news with dateKey=${i}.`,"If all three tool calls succeed, reply with exactly: OK macro chain","If any tool call fails, reply with exactly: NG macro chain: <short reason>","Respond as plain text only."].join("\n");c.write("exec_start",{model:a,cwd:r(),context:"gateway_self_test:codex_macro",prompt:"codex macro chain self-test",instruction:u,outputPath:l,tempDir:d,gatewayContextFile:p(),traceStartedAt:c.startedAt});try{const s=await I({cwd:r(),model:a,outputPath:l}),n=await j({cwd:r(),extraEnv:m(),args:s,stdinText:u,onStdoutLine:t=>{c.write("stdout_line",{line:t})},onStderrLine:t=>{c.write("stderr_line",{line:t});const e=String(t||"").match(/session id:\s*([a-zA-Z0-9-]+)/i);e?.[1]&&c.setSessionId(e[1])}}),o=await t.readFile(l,"utf8").catch(()=>""),g=String(o||n.stdout||"").trim(),w=String(n.stderr||""),y=String(n.stdout||""),h=/tool weget-gateway\.weget_macro_get_snapshot\(\{\}\)/i.test(w),S=new RegExp(`tool weget-gateway\\.weget_macro_list_calendar\\(\\{"dateKey":"${O(i)}"\\}\\)`,"i").test(w),_=new RegExp(`tool weget-gateway\\.weget_macro_list_news\\(\\{"dateKey":"${O(i)}"\\}\\)`,"i").test(w),v=/weget-gateway\.weget_macro_get_snapshot\(\{\}\) success/i.test(w),$=/weget-gateway\.weget_macro_list_calendar\(\{\"dateKey\":\"\d{4}-\d{2}-\d{2}\"\}\) success/i.test(w),C=/weget-gateway\.weget_macro_list_news\(\{\"dateKey\":\"\d{4}-\d{2}-\d{2}\"\}\) success/i.test(w),k=0===n.exitCode&&/^OK macro chain$/i.test(g)&&h&&S&&_&&v&&$&&C;c.write(k?"exec_completed":"exec_error",{exitCode:n.exitCode,stdout:y,stderr:w,lastMessage:o,finalAnswer:g,invokedSnapshot:h,invokedCalendar:S,invokedNews:_,succeededSnapshot:v,succeededCalendar:$,succeededNews:C});const P=await c.flush().catch(()=>""),b={target:e,ok:k,summary:k?"Codex gateway macro chain OK":"Codex gateway macro chain failed",contextFilePath:p(),tracePath:P,codex:{exitCode:n.exitCode,stdout:y,stderr:w,finalAnswer:g},assertions:{invokedSnapshot:h,invokedCalendar:S,invokedNews:_,succeededSnapshot:v,succeededCalendar:$,succeededNews:C},ts:(new Date).toISOString()},E=await f({target:e,payload:b});return await t.rm(d,{recursive:!0,force:!0}).catch(()=>{}),{ok:k,summary:k?"Codex gateway macro chain OK":x(g||w,"Codex gateway macro chain failed"),detail:g||w||y,logPath:E,result:b}}catch(r){c.write("exec_exception",{error:r instanceof Error?r.message:String(r)});const s=await c.flush().catch(()=>""),n=r instanceof Error?r.message:String(r),i=await f({target:e,payload:{target:e,ok:!1,summary:"Codex gateway macro chain failed",contextFilePath:p(),tracePath:s,error:n,ts:(new Date).toISOString()}});return await t.rm(d,{recursive:!0,force:!0}).catch(()=>{}),{ok:!1,summary:x(n,"Codex gateway macro chain failed"),detail:n,logPath:i}}}await w();const c=b(r());let d,l=null;try{d=await A({command:c.command,args:[...c.args,"--self-test","--test-target",e],cwd:r()}),"gateway"===e&&(l=await(async({command:t,args:e,cwd:r,timeoutMs:s=2500})=>new Promise((n,o)=>{const a=i(t,e,{cwd:r,stdio:["ignore","pipe","pipe"],windowsHide:!0,env:{...process.env,NO_COLOR:"1",FORCE_COLOR:"0",CLICOLOR:"0"}}),c=[],d=[];let l=!1;const u=t=>{l||(l=!0,n(t))};a.stdout.on("data",t=>c.push(Buffer.from(t))),a.stderr.on("data",t=>d.push(Buffer.from(t))),a.on("error",t=>{l||(l=!0,o(t))}),a.on("close",t=>{u({ok:!1,exitCode:Number.isFinite(t)?Number(t):1,stdout:Buffer.concat(c).toString("utf8"),stderr:Buffer.concat(d).toString("utf8"),responseText:""})}),setTimeout(()=>{const t=a.stdin;t?.write(`${JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"weget-gateway-probe",version:"0.1.0"}}})}\n`)},150),setTimeout(()=>{if(l)return;const t=Buffer.concat(c).toString("utf8"),e=Buffer.concat(d).toString("utf8"),r=t.trim(),s=/"result"\s*:/.test(r)&&/"protocolVersion"\s*:/.test(r);a.kill(),u({ok:s,exitCode:null,stdout:t,stderr:e,responseText:r})},Math.max(250,s))}))({command:c.command,args:c.args,cwd:r()}))}catch(t){const r=t instanceof Error?t.message:String(t),s=await f({target:e,payload:{target:e,launch:c,contextFilePath:p(),failure:"spawn_exception",error:r,ts:(new Date).toISOString()}});return{ok:!1,summary:x(r,`${e} test failed`),detail:r,logPath:s}}const u=String(d.stdout||d.stderr||"").trim();let g;if(u)try{g=JSON.parse(u)}catch{}let y=0===d.exitCode&&Boolean(!g||!1!==g.ok),h=x(u,`${e} self-test completed`);"gateway"===e&&l&&!l.ok?(y=!1,h=x(l.stderr||l.stdout,"Gateway handshake probe failed.")):"gateway"===e&&l?.ok&&(h="Gateway MCP handshake OK");const S={target:e,ok:y,summary:h,launch:c,contextFilePath:p(),selfTest:{exitCode:d.exitCode,stdout:d.stdout,stderr:d.stderr,result:g??null},handshakeProbe:l?{ok:l.ok,exitCode:l.exitCode,stdout:l.stdout,stderr:l.stderr,responseText:l.responseText}:null,ts:(new Date).toISOString()};return{ok:y,summary:h,detail:u||h,logPath:await f({target:e,payload:S}),result:g}}}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=t=>String(t||"").trim();export const stripWegetControlBlocks=t=>String(t||"").replace(/```weget_result\s*[\s\S]*?```/gi,"").replace(/```weget_pending_request\s*[\s\S]*?```/gi,"").trim();export const parseTaskDirective=e=>{const n=String(e||""),i=stripWegetControlBlocks(n),r=n.match(/```weget_result\s*([\s\S]*?)```/i);if(r?.[1])try{const e=JSON.parse(r[1].trim()),n=t(e.status).toLowerCase(),s=e.pending_request&&"object"==typeof e.pending_request?e.pending_request:{};if("pending_request"===n){const n=t(s.type).toLowerCase(),r=t(s.prompt),o=t(s.subtype);if(("input"===n||"approval"===n)&&r)return{visibleText:i,payload:e,pendingRequest:{visibleText:i,requestType:n,prompt:r,subtype:o||void 0},arbitrationAction:null}}const o=(e=>{const n=t(e).toLowerCase();return"report_progress"===n||"cancel_running_task"===n||"continue_current_task"===n||"start_new_task"===n?n:null})(e.action);return o?{visibleText:i,payload:e,arbitrationAction:{visibleText:i,action:o,targetTaskId:t(e.target_task_id)||void 0,reason:t(e.reason)||void 0},pendingRequest:null}:{visibleText:i,payload:e,pendingRequest:null,arbitrationAction:null}}catch{return{visibleText:i,payload:null,pendingRequest:null,arbitrationAction:null}}const s=n.match(/```weget_pending_request\s*([\s\S]*?)```/i);if(!s?.[1])return{visibleText:i,payload:null,pendingRequest:null,arbitrationAction:null};try{const e=JSON.parse(s[1].trim()),n=t(e.type).toLowerCase(),r=t(e.prompt),o=t(e.subtype);if(("input"===n||"approval"===n)&&r)return{visibleText:i,payload:e,pendingRequest:{visibleText:i,requestType:n,prompt:r,subtype:o||void 0},arbitrationAction:null}}catch{}return{visibleText:i,payload:null,pendingRequest:null,arbitrationAction:null}};export const shouldQueueArbitratedNewTask=e=>"start_new_task"===e.arbitrationAction?.action&&Boolean(t(e.visibleText));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"node:test";import e from"node:assert/strict";import{parseTaskDirective as i,shouldQueueArbitratedNewTask as a,stripWegetControlBlocks as s}from"./taskDirective.js";t("taskDirective strips control blocks from visible text",()=>{const t=["Working on it.","","```weget_result",'{"status":"completed"}',"```"].join("\n");e.equal(s(t),"Working on it.")}),t("taskDirective parses pending request directive",()=>{const t=["Need your confirmation.","","```weget_result",'{"status":"pending_request","pending_request":{"type":"approval","prompt":"Proceed?","subtype":"generic"}}',"```"].join("\n"),a=i(t);e.equal(a.visibleText,"Need your confirmation."),e.equal(a.pendingRequest?.requestType,"approval"),e.equal(a.pendingRequest?.prompt,"Proceed?"),e.equal(a.pendingRequest?.subtype,"generic"),e.equal(a.arbitrationAction,null)}),t("taskDirective parses arbitration cancel action",()=>{const t=["I will stop the current task and summarize the completed work.","","```weget_result",'{"status":"completed","action":"cancel_running_task","target_task_id":"t-active","reason":"user_requested_cancel"}',"```"].join("\n"),a=i(t);e.equal(a.pendingRequest,null),e.equal(a.arbitrationAction?.action,"cancel_running_task"),e.equal(a.arbitrationAction?.targetTaskId,"t-active"),e.equal(a.arbitrationAction?.reason,"user_requested_cancel")}),t("taskDirective parses arbitration progress action",()=>{const t=["The active task is still collecting logs.","","```weget_result",'{"status":"completed","action":"report_progress"}',"```"].join("\n"),a=i(t);e.equal(a.arbitrationAction?.action,"report_progress"),e.equal(a.visibleText,"The active task is still collecting logs.")}),t("taskDirective parses continue current task action",()=>{const t=["I will keep the current task running with your added instruction in mind.","","```weget_result",'{"status":"completed","action":"continue_current_task","target_task_id":"t-active"}',"```"].join("\n"),a=i(t);e.equal(a.arbitrationAction?.action,"continue_current_task"),e.equal(a.arbitrationAction?.targetTaskId,"t-active")}),t("taskDirective parses start new task action",()=>{const t=["This is a separate request, so I will treat it as a new task.","","```weget_result",'{"status":"completed","action":"start_new_task"}',"```"].join("\n"),s=i(t);e.equal(s.arbitrationAction?.action,"start_new_task"),e.equal(s.visibleText,"This is a separate request, so I will treat it as a new task."),e.equal(a(s),!0)});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"node:path";export const createWindowManager=({BrowserWindow:t,preloadPath:n,rendererDir:o,windowIconPath:i=""})=>{let
|
|
1
|
+
import e from"node:path";export const createWindowManager=({BrowserWindow:t,preloadPath:n,rendererDir:o,windowIconPath:i=""})=>{let s=null;return{getMainWindow:()=>s,createMainWindow:()=>(s=new t({width:1024,height:860,minWidth:1e3,minHeight:820,icon:i||void 0,autoHideMenuBar:!0,webPreferences:{preload:n,contextIsolation:!0,nodeIntegration:!1}}),s.loadFile(e.join(o,"index.html")),s.setMenuBarVisibility(!1),s),emitBotLog:e=>{s&&!s.isDestroyed()&&s.webContents.send("bot:log",e)},emitBotStatus:e=>{s&&!s.isDestroyed()&&s.webContents.send("bot:status",e)},emitChatEvent:e=>{s&&!s.isDestroyed()&&s.webContents.send("chat:event",e)},emitTaskRuntime:e=>{s&&!s.isDestroyed()&&s.webContents.send("task:runtime",e)},emitTradeExecution:e=>{s&&!s.isDestroyed()&&s.webContents.send("trade:execution",e)}}};
|