@ai.weget.jp/bot 0.1.8 → 0.1.10

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.
Files changed (34) hide show
  1. package/README.md +41 -16
  2. package/dist/src/config/systemConfig.js +1 -0
  3. package/dist/src/gmoCoinRuntime.js +1 -0
  4. package/dist/src/gmoFxRuntime.js +1 -0
  5. package/dist/src/index.js +1 -0
  6. package/dist/src/ipc/registerHandlers.js +1 -1
  7. package/dist/src/main.js +1 -1
  8. package/dist/src/platformApi.js +1 -0
  9. package/dist/src/preload.cjs +37 -8
  10. package/dist/src/renderer/app.js +1 -1
  11. package/dist/src/renderer/index.html +415 -20
  12. package/dist/src/renderer/styles.css +775 -3
  13. package/dist/src/services/authApiService.js +1 -1
  14. package/dist/src/services/codexService.js +1 -0
  15. package/dist/src/services/windowManagerService.js +1 -1
  16. package/dist/src/skillHostState.js +1 -0
  17. package/dist/src/skills.js +1 -0
  18. package/dist/src/wsClient.js +1 -1
  19. package/package.json +6 -19
  20. package/scripts/build-ts.cjs +8 -2
  21. package/dist/src/renderer/chat.css +0 -128
  22. package/dist/src/renderer/chat.html +0 -27
  23. package/dist/src/renderer/chat.js +0 -1
  24. package/dist/src/services/aiMemoryService.js +0 -1
  25. package/dist/src/services/commandOrchestratorService.js +0 -1
  26. package/dist/src/services/debugLogService.js +0 -1
  27. package/dist/src/services/taskExecutorService.js +0 -1
  28. package/dist/src/services/taskParsingService.js +0 -1
  29. package/dist/src/services/textService.js +0 -1
  30. package/dist/src/services/workflowService.js +0 -1
  31. package/scripts/backtest-loop.cjs +0 -137
  32. package/scripts/prepare-playwright-browsers.cjs +0 -181
  33. package/scripts/run-workflow.cjs +0 -381
  34. package/scripts/workflows/kaitori-login.json +0 -17
package/README.md CHANGED
@@ -1,30 +1,55 @@
1
1
  # @ai.weget.jp/bot
2
2
 
3
- WeGet BotElectron + CLI)。
3
+ WeGet Bot host package (Electron + CLI).
4
+
5
+ ## Install
4
6
 
5
- ## 安装
6
7
  ```bash
7
8
  npm i -g @ai.weget.jp/bot
8
9
  ```
9
- 安装时会自动准备 Playwright Chromium(用于自动化步骤)。
10
10
 
11
- ## 快速使用
12
- 打开 UI:
11
+ ## Quick Start
12
+
13
+ 1. Login to Codex on the local machine:
14
+
13
15
  ```bash
14
- weget-bot ui
16
+ codex login --device-auth
15
17
  ```
16
18
 
17
- 最简演示建议:
18
- 1. 执行 `npm i -g @ai.weget.jp/bot`
19
- 2. 执行 `weget-bot ui`
19
+ You can also open the local `Host Control` page and use `Start Login`.
20
20
 
21
- 默认内置:
22
- - `BOT_WS_URL=wss://ws.weget.jp`
23
- - `BOT_LOGIN_API_URL=https://api.weget.jp/login`
21
+ 2. Open the local UI:
24
22
 
25
- 如需覆盖,再额外提供 `.env`。
26
-
27
- 查看命令帮助:
28
23
  ```bash
29
- weget-bot help
24
+ weget-bot ui
30
25
  ```
26
+
27
+ 3. Sign in the bot to the private control plane from the local login screen.
28
+
29
+ ## What The Host Owns
30
+
31
+ - local Codex auth state
32
+ - Bot WebSocket connection
33
+ - local skill pages for installed packages
34
+ - local GMO credentials and default model
35
+
36
+ The host does not store or receive OpenAI keys from Platform.
37
+
38
+ AI interaction is routed through local Codex only.
39
+
40
+ Default built-in endpoints:
41
+
42
+ - `loginApiUrl=https://api.weget.jp/login`
43
+ - `botUploadApiUrl=https://api.weget.jp/bot/upload-url`
44
+ - `gmoCryptoPrivateApiBaseUrl=https://api.coin.z.com/private`
45
+ - `gmoFxPrivateApiBaseUrl=https://forex-api.coin.z.com/private`
46
+ - `aiModel=gpt-4.1-mini`
47
+
48
+ ## Local UI Structure
49
+
50
+ - `Host Control`: local runtime, Codex auth, default model, installed skill state, local credentials
51
+ - `Codex Console`: local chat with Codex
52
+ - `Installed Skills`: per-skill pages such as `skill-gmo-coin`, `skill-gmo-fx`, `skill-browser`
53
+
54
+ The package is the public runtime host. Private control plane behavior remains
55
+ in `platform/` and `infra/`.
@@ -0,0 +1 @@
1
+ export const SYSTEM_CONFIG={loginApiUrl:"https://api.weget.jp/login",botUploadApiUrl:"https://api.weget.jp/bot/upload-url",gmoCryptoPrivateApiBaseUrl:"https://api.coin.z.com/private",gmoCryptoPublicApiBaseUrl:"https://api.coin.z.com/public",gmoFxPrivateApiBaseUrl:"https://forex-api.coin.z.com/private",gmoFxPublicApiBaseUrl:"https://forex-api.coin.z.com/public",gmoAssetsPath:"/v1/account/assets",gmoFxAssetsPath:"/v1/account/assets",gmoCoinMarginPath:"/v1/account/margin",gmoPositionsPath:"/v1/openPositions",gmoPositionSummaryPath:"/v1/positionSummary",gmoFxOrderPath:"/v1/order",gmoCoinCloseOrderPath:"/v1/closeOrder",gmoFxCloseOrderPath:"/v1/closeOrder"};
@@ -0,0 +1 @@
1
+ import{createGmoCoinExecutionWsService as e,createGmoGateway as t,createGmoMarketDataService as i,createTradeConfigService as r,createTradeStatusService as o,defaultGmoRuntimeConfig as s}from"@ai.weget.jp/skill-gmo-core";const n=e=>e&&"object"==typeof e?e:{},a=e=>{const t=Number(String(e??"").trim().replace(/,/g,""));return Number.isFinite(t)&&t>0?t:null},c=e=>"SELL"===String(e||"").trim().toUpperCase()?"SELL":"BUY",l=e=>{const t=n(e);return{market:"coin",symbol:String(t.symbol||"").trim().toUpperCase(),side:c(t.side),settleType:String(t.settleType||"").toUpperCase()||"OPEN",executionPrice:t.executionPrice??t.price??"",executionSize:t.executionSize??t.size??"",executionId:t.executionId??null,orderId:t.orderId??null,executionTimestamp:String(t.executionTimestamp||t.timestamp||"")}};export const createGmoCoinRuntime=({configPath:p,emitLog:m,emitTradeExecution:g,emitTradeHistory:u,getRuntimeConfig:d=s})=>{const P=r({configPath:p,emitLog:m}),x=t({cryptoApiBaseUrl:d().cryptoApiBaseUrl,fxApiBaseUrl:d().fxApiBaseUrl,assetsPath:d().assetsPath,fxAssetsPath:d().fxAssetsPath,coinMarginPath:d().coinMarginPath,positionsPath:d().positionsPath,positionSummaryPath:d().positionSummaryPath,fxOrderPath:d().fxOrderPath,coinCloseOrderPath:d().coinCloseOrderPath,fxCloseOrderPath:d().fxCloseOrderPath});x.setRuntimeConfigProvider(d),x.setCredentialsProvider(P.getCredentials),x.setLogEmitter(m);const y=i({getCoinPublicBaseUrl:()=>d().coinPublicBaseUrl,getFxPublicBaseUrl:()=>d().fxPublicBaseUrl}),C=o({gateway:x,env:{getRiskDailyLossLimitJpy:()=>P.get().riskDailyLossLimitJpy},emitLog:m}),S=e({getCoinPrivateApiBaseUrl:()=>d().cryptoApiBaseUrl,getCoinCredentials:()=>P.getCredentials("crypto"),emitLog:m,onExecutionEvent:async e=>{const t=n(e),i=(e=>{const t=n(e);return{category:"trade_history",market:"coin",kind:"CLOSE"===String(t.settleType||"").toUpperCase()?"close":"new",symbol:String(t.symbol||"").trim().toUpperCase(),side:c(t.side),size:String(t.executionSize??t.orderSize??"").trim(),price:a(t.executionPrice??t.price),price_source:null!==a(t.executionPrice??t.price)?"execution_event":"none",result:t,ts:(new Date).toISOString()}})(t);u?.(i),m("[trade] coin execution received",l(t)),g?.(l(t))}});return{tradeConfig:P,gateway:x,marketData:y,status:C,loadConfig:()=>P.load(),getConfig:()=>P.get(),saveConfig:async e=>P.save(e),startExecutionStreams:()=>S.start(),stopExecutionStreams:()=>S.stop(),restartExecutionStreams:()=>S.restart()}};
@@ -0,0 +1 @@
1
+ import{createGmoFxExecutionWsService as e,createGmoGateway as t,createGmoMarketDataService as i,createTradeConfigService as r,createTradeStatusService as s,defaultGmoRuntimeConfig as o}from"@ai.weget.jp/skill-gmo-core";const a=e=>e&&"object"==typeof e?e:{},n=e=>{const t=Number(String(e??"").trim().replace(/,/g,""));return Number.isFinite(t)&&t>0?t:null},c=e=>"SELL"===String(e||"").trim().toUpperCase()?"SELL":"BUY",l=e=>{const t=a(e);return{market:"fx",symbol:String(t.symbol||"").trim().toUpperCase(),side:c(t.side),settleType:String(t.settleType||"").toUpperCase()||"OPEN",executionPrice:t.executionPrice??t.price??"",executionSize:t.executionSize??t.size??"",executionId:t.executionId??null,orderId:t.orderId??null,executionTimestamp:String(t.executionTimestamp||t.timestamp||"")}};export const createGmoFxRuntime=({configPath:m,emitLog:p,emitTradeExecution:g,emitTradeHistory:u,getRuntimeConfig:x=o})=>{const d=r({configPath:m,emitLog:p}),P=t({cryptoApiBaseUrl:x().cryptoApiBaseUrl,fxApiBaseUrl:x().fxApiBaseUrl,assetsPath:x().assetsPath,fxAssetsPath:x().fxAssetsPath,coinMarginPath:x().coinMarginPath,positionsPath:x().positionsPath,positionSummaryPath:x().positionSummaryPath,fxOrderPath:x().fxOrderPath,coinCloseOrderPath:x().coinCloseOrderPath,fxCloseOrderPath:x().fxCloseOrderPath});P.setRuntimeConfigProvider(x),P.setCredentialsProvider(d.getCredentials),P.setLogEmitter(p);const f=i({getCoinPublicBaseUrl:()=>x().coinPublicBaseUrl,getFxPublicBaseUrl:()=>x().fxPublicBaseUrl}),y=s({gateway:P,env:{getRiskDailyLossLimitJpy:()=>d.get().riskDailyLossLimitJpy},emitLog:p}),S=e({getFxPrivateApiBaseUrl:()=>x().fxApiBaseUrl,getFxCredentials:()=>d.getCredentials("fx"),emitLog:p,onExecutionEvent:async e=>{const t=a(e),i=(e=>{const t=a(e);return{category:"trade_history",market:"fx",kind:"CLOSE"===String(t.settleType||"").toUpperCase()?"close":"new",symbol:String(t.symbol||"").trim().toUpperCase(),side:c(t.side),size:String(t.executionSize??t.orderSize??"").trim(),price:n(t.executionPrice??t.price),price_source:null!==n(t.executionPrice??t.price)?"execution_event":"none",result:t,ts:(new Date).toISOString()}})(t);u?.(i),p("[trade] fx execution received",l(t)),g?.(l(t))}});return{tradeConfig:d,gateway:P,marketData:f,status:y,loadConfig:()=>d.load(),getConfig:()=>d.get(),saveConfig:async e=>d.save(e),startExecutionStreams:()=>S.start(),stopExecutionStreams:()=>S.stop(),restartExecutionStreams:()=>S.restart()}};
@@ -0,0 +1 @@
1
+ export*from"./skills.js";export*from"./skillHostState.js";export*from"./platformApi.js";export*from"./gmoFxRuntime.js";export*from"./gmoCoinRuntime.js";export const createBotHostManifest=()=>({name:"@ai.weget.jp/bot",version:"0.1.10",skills:[]});
@@ -1 +1 @@
1
- const e=e=>e&&"object"==typeof e?e:{};export const registerIpcHandlers=({ipcMain:r,env:t,loadedEnvFiles:i,wsClient:o,signInWithLoginApi:s,saveLoginProfile:a,clearLineSessions:n,closeTaskBrowser:l,setCurrentSession:d,getCurrentSession:g,emitLog:c,getWorkflowCount:u,getPlaywrightHeadless:m,setPlaywrightHeadless:h,readSavedLoginProfile:w,sendChatMessage:p,closeChatWindow:b})=>{r.handle("auth:login",async(r,l)=>{const g=e(l);if(!t.wsUrl){return{ok:!1,error:`Missing BOT_WS_URL. ${i.length?`loaded: ${i.join(", ")}`:"no .env loaded"}`}}const u=String(g.botId||"").trim();if(!u)return{ok:!1,error:"Bot ID is required"};try{c("[auth] login start");const e=await s(String(g.email||""),String(g.password||""));await a({botId:u,email:String(g.email||"").trim(),password:String(g.password||""),remember:Boolean(g?.remember)}),n(),o.stop();const r={...e,botId:u};return o.start(r),d(r),c("[auth] login success",{userId:e.userId,email:e.email,botId:u,chatModel:e.chatModel||"gpt-4.1-mini"}),{ok:!0,session:{userId:e.userId,email:e.email,botId:u}}}catch(e){const r=e instanceof Error?e.message:String(e);return c("[auth] login failed",{error:r}),{ok:!1,error:r}}}),r.handle("auth:logout",async()=>(o.stop(),d(null),n(),await l(),b(),{ok:!0})),r.handle("bot:getRuntimeInfo",async()=>{const r=g(),i=e(r),s=r?{userId:String(i.userId||""),email:String(i.email||""),botId:String(i.botId||""),chatModel:String(i.chatModel||"gpt-4.1-mini")}:null,a=await u();return{ok:!0,status:1===o?.ws?.readyState?"connected":"disconnected",session:s,runtime:{wsUrl:t.wsUrl||"",loginApiUrl:t.loginApiUrl||"",workflowListApiUrl:t.botWorkflowListApiUrl||"",runtimeConfigApiUrl:t.botRuntimeConfigApiUrl||"",playwrightHeadless:Boolean(m()),capabilities:Array.isArray(t.capabilities)?t.capabilities:[],workflowCount:a}}}),r.handle("bot:setPlaywrightVisible",async(r,t)=>{const i=e(t),o=Boolean(i?.visible);return h(!o),await l(),c("[playwright] visual mode updated",{visible:o,headless:m()}),{ok:!0,visible:o,headless:m()}}),r.handle("auth:getSavedProfile",async()=>{try{return{ok:!0,profile:await w()}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}),r.handle("auth:saveProfile",async(r,t)=>{const i=e(t);try{return await a({botId:String(i.botId||"").trim(),email:String(i.email||"").trim(),password:String(i.password||""),remember:Boolean(i?.remember)}),{ok:!0}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}),r.handle("chat:send",async(r,t)=>{const i=e(t);try{const e=String(i.message||"").trim();if(!e)return{ok:!1,error:"message is required"};return{ok:!0,answer:(await p(e)).text}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}})};
1
+ const r=r=>r&&"object"==typeof r?r:{};export const registerIpcHandlers=({ipcMain:e,env:t,signInWithLoginApi:o,saveLoginProfile:i,readSavedLoginProfile:n,getFxApiConfig:s,saveFxApiConfig:a,getCoinApiConfig:c,saveCoinApiConfig:g,getAiConfig:m,saveAiConfig:u,setCurrentSession:d,getCurrentSession:l,getActiveSkills:S,getCodexAuthStatus:k,startCodexLogin:y,getManagedSkills:f,emitLog:p,openGmoKlineWindow:h,getGmoMarketQuotes:b,getGmoSymbolRules:C,getGmoBuyingPower:w,getGmoAccountMetrics:L,getGmoPositionSummary:x,getGmoOpenPositions:E,placeGmoFxOrder:I,placeGmoCoinOrder:A,placeGmoFxCloseOrder:v,placeGmoCoinCloseOrder:U,getGmoApiState:G,testGmoApi:q,buildTradeStatusSnapshot:F,closeTradeWindows:P,startWsClient:z,stopWsClient:B,handleBotChatMessage:N,writeErrorLog:M,writeRuntimeLog:O})=>{e.handle("auth:login",async(e,t)=>{const n=r(t),s=String(n.botId||"").trim();if(!s)return{ok:!1,error:"Bot ID is required"};try{const r=await o(String(n.email||""),String(n.password||""));await i({botId:s,email:String(n.email||"").trim(),password:String(n.password||""),remember:Boolean(n.remember)});const e={...r,botId:s};return d(e),z({botId:s,userId:String(r.userId||""),accessToken:String(r.accessToken||"")}),p("[auth] login success",{userId:r.userId||"",email:r.email||"",botId:s}),{ok:!0,session:{userId:String(r.userId||""),email:String(r.email||""),botId:s}}}catch(r){const e=r instanceof Error?r.message:String(r);return p("[auth] login failed",{error:e}),{ok:!1,error:e}}}),e.handle("auth:logout",async()=>(B(),d(null),P(),p("[auth] logout"),{ok:!0})),e.handle("auth:getSavedProfile",async()=>{try{return{ok:!0,profile:await n()}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getFxApiConfig",async()=>({ok:!0,config:s()})),e.handle("trade:saveFxApiConfig",async(r,e)=>{try{return{ok:!0,config:await a(e)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getCoinApiConfig",async()=>({ok:!0,config:c()})),e.handle("trade:saveCoinApiConfig",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("ai:getConfig",async()=>({ok:!0,config:m()})),e.handle("ai:saveConfig",async(r,e)=>{try{return{ok:!0,config:await u(e)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("bot:getRuntimeInfo",async()=>{const e=l(),o=r(e),i=await k();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:G("fx"),gmoCoinApiState:G("coin"),aiModel:m().aiModel,activeSkills:S(),codexAuthStatus:i.status,codexAuthDetail:i.detail}}}),e.handle("bot:getSkills",async()=>({ok:!0,skills:f()})),e.handle("codex:startLogin",async()=>y()),e.handle("trade:openGmoKlineWindow",async(e,t)=>{const o=r(t),i=String(o.symbol||"BTC_JPY").trim()||"BTC_JPY",n=String(o.interval||"1m").trim()||"1m",s="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await h({symbol:i,interval:n,market:s})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getMarketQuotes",async(e,t)=>{const o=r(t),i="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",n=Array.isArray(o.symbols)?o.symbols.map(r=>String(r||"").trim().toUpperCase()).filter(Boolean):void 0;try{return{ok:!0,...await b({market:i,symbols:n})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getSymbolRules",async(e,t)=>{const o=r(t),i="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await C({market:i})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getBuyingPower",async(e,t)=>{const o=r(t),i="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await w({market:i})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getAccountMetrics",async(e,t)=>{const o=r(t),i="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin";try{return{ok:!0,...await L({market:i})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:getPositionSummary",async(e,t)=>{const o=r(t),i="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",n=String(o.symbol||"").trim().toUpperCase();if("coin"===i&&!n)return{ok:!1,error:"symbol is required for coin positionSummary"};try{return{ok:!0,...await x({market:i,symbol:n||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),i="fx"===String(o.market||"").trim().toLowerCase()?"fx":"coin",n=String(o.symbol||"").trim().toUpperCase();if("coin"===i&&!n)return{ok:!1,error:"symbol is required for coin openPositions"};const s=Number(o.page),a=Number(o.prevId),c=Number(o.count);try{return{ok:!0,...await E({market:i,symbol:n||void 0,page:Number.isFinite(s)?s:void 0,prevId:Number.isFinite(a)?a: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),i=String(o.symbol||"").trim().toUpperCase(),n="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",s=String(o.size||"").trim();if(!i)return{ok:!1,error:"symbol is required"};if(!s)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await I({symbol:i,side:n,size:s})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:placeCoinOrder",async(e,t)=>{const o=r(t),i=String(o.symbol||"").trim().toUpperCase(),n="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",s=String(o.size||"").trim();if(!i)return{ok:!1,error:"symbol is required"};if(!s)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await A({symbol:i,side:n,size:s})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:closeFxPosition",async(e,t)=>{const o=r(t),i=String(o.symbol||"").trim().toUpperCase(),n="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",s=Number(o.positionId),a=String(o.size||"").trim();if(!i)return{ok:!1,error:"symbol is required"};if(!Number.isFinite(s)||s<=0)return{ok:!1,error:"positionId is required"};if(!a)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await v({symbol:i,side:n,positionId:s,size:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:closeCoinPosition",async(e,t)=>{const o=r(t),i=String(o.symbol||"").trim().toUpperCase(),n="SELL"===String(o.side||"").trim().toUpperCase()?"SELL":"BUY",s=Number(o.positionId),a=String(o.size||"").trim();if(!i)return{ok:!1,error:"symbol is required"};if(!Number.isFinite(s)||s<=0)return{ok:!1,error:"positionId is required"};if(!a)return{ok:!1,error:"size is required"};try{return{ok:!0,result:await U({symbol:i,side:n,positionId:s,size:a})}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("trade:testGmoApi",async(e,t)=>{const o=r(t),i="coin"===String(o.market||"").trim().toLowerCase()?"coin":"fx";return q(i)}),e.handle("trade:getStatusSnapshot",async(e,t)=>{const o=r(t),i="coin"===String(o.market||"").trim().toLowerCase()?"coin":"fx";try{return{ok:!0,snapshot:await F(i)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}),e.handle("chat:send",async(e,t)=>{const o=r(t),i=String(o.message||"").trim();if(!i)return{ok:!1,error:"message is required"};try{return{ok:!0,answer:(await N({text:i})).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),i=String(o.source||"").trim()||"ui",n=String(o.message||"").trim()||"unknown error";try{return await M({source:i,message:n,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),i=String(o.level||"").trim().toLowerCase(),n="error"===i?"error":"debug"===i?"debug":"info",s=String(o.source||"").trim()||"ui",a=String(o.message||"").trim()||"log";try{return await O({level:n,source:s,message:a,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 e from"dotenv";import r from"node:path";import t from"node:os";import o from"node:crypto";import{existsSync as s}from"node:fs";import n from"node:fs/promises";import{fileURLToPath as a}from"node:url";import{createRequire as i}from"node:module";import{BotWsClient as l}from"./wsClient.js";import{detectLanguage as c,normalizeIncomingText as p,isLikelyMojibake as m,buildLanguageInstruction as d,buildEmptyReply as u,buildActionFailedReply as w,buildMojibakeReply as g,buildConfirmPrompt as f,buildCancelReply as v,buildStepStartReply as _,buildStepFallbackSummary as h,isNumericChoice as S,isConfirmText as T,isCancelText as y,isLikelyNewTask as b,matchWorkflowByCommand as A}from"./services/textService.js";import{parseJsonLoose as O,normalizeActionUrl as L,resolveHostFromUrl as I,isValidOpenUrl as B,extractFirstUrl as k,extractBasicCredentials as E,isBasicAuthStep as C}from"./services/taskParsingService.js";import{normalizeWorkflow as P}from"./services/workflowService.js";import{createDebugLogger as R}from"./services/debugLogService.js";import{createAiMemoryService as N}from"./services/aiMemoryService.js";import{registerIpcHandlers as U}from"./ipc/registerHandlers.js";import{createTaskExecutorService as x}from"./services/taskExecutorService.js";import{createAuthApiService as M}from"./services/authApiService.js";import{createWindowManager as W}from"./services/windowManagerService.js";import{createCommandOrchestratorService as j}from"./services/commandOrchestratorService.js";const F=i(import.meta.url),{app:D,BrowserWindow:H,ipcMain:G,dialog:J,shell:$,nativeImage:q}=F("electron"),z=a(import.meta.url),Y=r.dirname(z),K=e=>{const r=e&&"object"==typeof e?e:null;return r&&"string"==typeof r.code?r.code:""},X=(()=>{const t=[...[r.resolve(process.cwd(),".env"),r.resolve(Y,"..",".env"),r.resolve(r.dirname(process.execPath),".env"),r.resolve(process.resourcesPath||"",".env")].filter(Boolean),...[r.resolve(process.cwd(),".env.example"),r.resolve(Y,"..",".env.example"),r.resolve(r.dirname(process.execPath),".env.example"),r.resolve(process.resourcesPath||"",".env.example")].filter(Boolean)],o=[];let n=!1,a=!1;for(const r of t)s(r)&&(e.config({path:r,override:!1}),o.push(r),r.endsWith(".env")&&(n=!0),r.endsWith(".env.example")&&(a=!0));return{loadedFiles:o,loadedFromEnv:n,loadedFromExample:a}})(),V=(()=>{const e=String(process.env.PLAYWRIGHT_BROWSERS_PATH||"").trim();if(e)return e;const o=["win32"===process.platform?r.join(process.env.LOCALAPPDATA||r.join(t.homedir(),"AppData","Local"),"weget-bot","pw-browsers"):"darwin"===process.platform?r.join(t.homedir(),"Library","Caches","weget-bot","pw-browsers"):r.join(t.homedir(),".cache","weget-bot","pw-browsers"),r.resolve(process.cwd(),"pw-browsers"),r.resolve(r.dirname(process.execPath),"pw-browsers"),r.resolve(process.resourcesPath||"","pw-browsers")];for(const e of o)if(s(e))return e;return""})();V&&(process.env.PLAYWRIGHT_BROWSERS_PATH=V);const Z="https://api.weget.jp",Q={wsUrl:process.env.BOT_WS_URL||"wss://ws.weget.jp",loginApiUrl:process.env.BOT_LOGIN_API_URL||`${Z}/login`,botUploadApiUrl:process.env.BOT_UPLOAD_API_URL||`${Z}/bot/upload-url`,botWorkflowListApiUrl:process.env.BOT_WORKFLOW_LIST_API_URL||`${Z}/bot/workflow/list`,botRuntimeConfigApiUrl:process.env.BOT_RUNTIME_CONFIG_API_URL||`${Z}/bot/runtime-config`,version:process.env.BOT_VERSION||"0.1.0",heartbeatSec:Number(process.env.BOT_HEARTBEAT_SEC||30),reconnectMs:Number(process.env.BOT_RECONNECT_MS||3e3),reconnectMaxAttempts:Number(process.env.BOT_RECONNECT_MAX_ATTEMPTS||10),maxOutputTokens:Number(process.env.BOT_MAX_OUTPUT_TOKENS||120),parseMaxOutputTokens:Number(process.env.BOT_TASK_PARSE_MAX_OUTPUT_TOKENS||220),debugLogPath:String(process.env.BOT_DEBUG_LOG_PATH||"").trim(),debugLogLevel:String(process.env.BOT_DEBUG_LOG_LEVEL||"info").trim().toLowerCase(),debugLogMaxMb:Number(process.env.BOT_DEBUG_LOG_MAX_MB||50),debugLogRedact:"true"===String(process.env.BOT_DEBUG_LOG_REDACT||"true").toLowerCase(),playwrightHeadless:"true"===String(process.env.BOT_PLAYWRIGHT_HEADLESS||"true").toLowerCase(),playwrightNavigationTimeoutMs:Number(process.env.BOT_PLAYWRIGHT_NAV_TIMEOUT_MS||3e4),playwrightJsSettleMaxMs:Number(process.env.BOT_PLAYWRIGHT_JS_SETTLE_MAX_MS||3e3),playwrightChannel:String(process.env.BOT_PLAYWRIGHT_CHANNEL||"").trim(),plannerConfidenceThreshold:Number(process.env.BOT_PLANNER_CONFIDENCE_THRESHOLD||.55),plannerInstructions:process.env.BOT_PLANNER_INSTRUCTIONS||["You are an execution planner for a desktop bot.","Default behavior: infer intent, auto-complete missing details with reasonable assumptions, then return executable actions.","Return strict JSON only.","JSON keys: mode, confidence, actions, assumptions, response.","mode enum: action, chat.","confidence: number between 0 and 1.","actions must be an array when mode=action.","Allowed action.type: open_url, wait, screenshot, read_page_text, http_get, write_file, set_http_credentials, auto_login_form.","For wait use params.ms.","For screenshot use params.filename(optional).","For read_page_text use params.max_chars(optional).","For http_get use params.url.","For write_file use params.filename and params.content.","For set_http_credentials use params.username and params.password, optionally params.url or params.host.","For auto_login_form use params.username and params.password.","Prefer mode=action unless request is pure conversational chat.","No tutorial, no user guide, no explanatory prose.","No markdown, no extra keys."].join(" "),capabilities:(process.env.BOT_CAPABILITIES||"playwright,ai").split(",").map(e=>e.trim()).filter(Boolean)},ee=process.argv.includes("--script");0===X.loadedFiles.length&&console.log(`${(new Date).toISOString()} [env] no .env or .env.example file loaded from known paths`);let re=null;const te=()=>r.join(D.getPath("userData"),"login-profile.json"),oe=()=>r.join(D.getPath("userData"),"memory","action-memory.json"),se=(e,r)=>{let t=0;const o=Math.min(e.length,r.length);for(let s=0;s<o;s+=1)t+=Number(e[s]||0)*Number(r[s]||0);return t},ne=e=>Math.sqrt(se(e,e)),ae=W({BrowserWindow:H,preloadPath:r.join(Y,"preload.cjs"),rendererDir:r.join(Y,"renderer"),windowIconPath:r.join(Y,"renderer","logo.png")}),ie=r.join(Y,"renderer","logo.png");let le=Q.playwrightHeadless;const{emitDebugEvent:ce}=R({env:Q,getRuntimeContext:()=>({botId:re?.botId||"",userId:re?.userId||""})}),pe=(e,r)=>{ae.emitBotLog({message:e,data:r||null,ts:(new Date).toISOString()})},me=(e,r="")=>{const t=`--${e}`,o=process.argv.findIndex(e=>e===t);if(o>=0&&o+1<process.argv.length)return String(process.argv[o+1]||"");const s=process.argv.find(e=>e.startsWith(`${t}=`));return s?String(s.slice(t.length+1)):r},de=async()=>{const e=me("email",process.env.BOT_SCRIPT_EMAIL||"").trim(),t=me("password",process.env.BOT_SCRIPT_PASSWORD||""),s=me("bot-id",process.env.BOT_SCRIPT_BOT_ID||"").trim(),a=me("workflow-file",process.env.BOT_SCRIPT_WORKFLOW_FILE||""),i=me("workflow-json",process.env.BOT_SCRIPT_WORKFLOW_JSON||""),l=me("line-text",process.env.BOT_SCRIPT_LINE_TEXT||"").trim(),c=me("lang",process.env.BOT_SCRIPT_LANG||"zh").trim()||"zh",p=me("run-id",o.randomUUID()).trim()||o.randomUUID();if(!e||!t||!s)throw new Error("missing required args: --email --password --bot-id");const m=await(async(e,t)=>{const o=String(t||"").trim();if(o)return JSON.parse(o);const s=String(e||"").trim();if(!s)throw new Error("missing workflow source: --workflow-file or --workflow-json");const a=await n.readFile(r.resolve(s),"utf8");return JSON.parse(a)})(a,i),d=((e,r="script_workflow")=>Array.isArray(e)?P({workflow_id:`script-${o.randomUUID()}`,name:r,trigger_command:"script",description:"script mode",bot_id:null,is_active:!0,steps_json:e}):P(e||{}))(m,"script_workflow");if(!d?.workflowId||!Array.isArray(d.steps)||0===d.steps.length)throw new Error("invalid workflow input: steps required");const u=await fe(e,t);re={...u,botId:s};try{await Le()}catch{}const w=ue.getLineSession("__script__");w.preferredLanguage=c,w.pendingWorkflow=null;const g=await ue.executeWorkflowBySteps({workflow:d,lineText:l||d.triggerCommand||d.name||"script run",lineSession:w,lineUserId:"",runId:p}),f={ok:!0,run_id:p,workflow_id:d.workflowId,workflow_name:d.name,answer:g.answer||"",image_url:g.imageUrl||"",preview_image_url:g.previewImageUrl||""};process.stdout.write(`${JSON.stringify(f,null,2)}\n`)};let ue;const we=new l({wsUrl:Q.wsUrl,version:Q.version,heartbeatSec:Q.heartbeatSec,reconnectMs:Q.reconnectMs,reconnectMaxAttempts:Q.reconnectMaxAttempts,capabilities:Q.capabilities},{onLog:(e,r)=>{pe(e,r)},onStatus:e=>{ae.emitBotStatus({status:e})},onLineMessage:async e=>ue.handleLineMessage(e),onServerConfigUpdate:async e=>ue.handleServerConfigUpdate(e),onWorkflowExecute:async e=>ue.handleWorkflowExecute(e),onRunTask:async e=>{await ue.handleRunTask(e)}}),ge=M({env:Q,emitLog:pe,getCurrentSession:()=>re}),{signInWithLoginApi:fe,callBotApi:ve,requestBotImageUploadUrl:_e,uploadImageToSignedUrl:he,getBotWorkflowListApiUrl:Se,getBotRuntimeConfigApiUrl:Te}=ge,ye=N({env:Q,emitLog:pe,getCurrentSession:()=>re,setCurrentSession:e=>{re=e},callBotApi:ve,getBotMemoryUploadApiUrl:()=>"",getBotMemoryArchivesApiUrl:()=>"",getBotMemoryDownloadApiUrl:()=>"",getBotRuntimeConfigApiUrl:Te,loadLocalMemories:async()=>{try{const e=await n.readFile(oe(),"utf8"),r=JSON.parse(e);return r&&"object"==typeof r&&Array.isArray(r.items)?r.items:[]}catch(e){if("ENOENT"===K(e))return[];throw e}},saveLocalMemories:async e=>{const t=oe();await(async e=>{await n.mkdir(r.dirname(e),{recursive:!0})})(t),await n.writeFile(t,JSON.stringify({version:1,updatedAt:(new Date).toISOString(),items:Array.isArray(e)?e:[]},null,2),"utf8")},cosineSimilarity:(e,r)=>{if(!Array.isArray(e)||!Array.isArray(r)||0===e.length||0===r.length)return 0;const t=ne(e),o=ne(r);return t&&o?se(e,r)/(t*o):0},parseJsonLoose:O,buildLanguageInstruction:d}),{parseTaskPlan:be,sendChatMessage:Ae,summarizeExecutionResult:Oe,refreshRuntimeConfigFromServer:Le}=ye,Ie=(e,r="artifact.txt")=>String(e||r).replace(/[^a-zA-Z0-9._-]/g,"_")||r,Be=x({env:Q,getHeadless:()=>le,emitLog:pe,emitDebugEvent:ce,notifyBrowserUnavailable:async({channel:e,reason:r,message:t})=>{const o=String(e||"").trim()||("win32"===process.platform?"msedge":"chrome"),s="msedge"===o?"https://www.microsoft.com/edge/download":"https://www.google.com/chrome/",n=["未检测到可用浏览器,自动化暂时无法执行。",`尝试系统通道: ${o}`,r?`错误: ${r}`:"","请先安装浏览器后重启 Bot。"].filter(Boolean);pe("[playwright] browser unavailable, user action required",{channel:o,reason:r||"",message:t||"",download:s});const a=ae.getMainWindow();if(!a||a.isDestroyed())return;const{response:i}=await J.showMessageBox(a,{type:"warning",buttons:["打开下载页","稍后处理"],defaultId:0,cancelId:1,title:"浏览器不可用",message:"未找到可用浏览器",detail:n.join("\n"),noLink:!0});0===i&&await $.openExternal(s).catch(()=>{})},normalizeActionUrl:L,resolveHostFromUrl:I,isValidOpenUrl:B,captureScreenshotAndUpload:async(e,t)=>{const o=await e.screenshot({fullPage:!0,type:"png"}),s=await Pe(),a=Ie(t,`shot-${Date.now()}.png`),i=r.join(s,a.endsWith(".png")?a:`${a}.png`);await n.writeFile(i,o);const l=await _e({filename:r.basename(i),contentType:"image/png"});return await he({uploadUrl:l.upload_url,contentType:"image/png",bytes:o}),{localPath:i,fileUrl:String(l.file_url||l.preview_url||"").trim()}},writeArtifactFile:async({filename:e,content:t})=>{const o=await Pe(),s=r.join(o,Ie(e,`note-${Date.now()}.txt`));return await n.writeFile(s,String(t||""),"utf8"),{localPath:s}}}),ke=Be.executeActionPlan,Ee=Be.closeTaskBrowser,Ce=Be.resolveTaskFallbackUrl;ue=j({getCurrentSession:()=>re,setBotBusyState:(e,r="")=>{we.sendBotState(e?"busy":"online",r)},emitChatEvent:e=>{ae.emitChatEvent(e)},emitLog:pe,emitTaskEvent:(e,r,t)=>we.sendTaskEvent(e,r,t),wsSendLineReply:e=>we.sendJson(e),normalizeIncomingText:p,isNumericChoice:S,detectLanguage:c,isLikelyMojibake:m,buildMojibakeReply:g,buildActionFailedReply:w,buildEmptyReply:u,buildCancelReply:v,buildConfirmPrompt:f,buildLanguageInstruction:d,isConfirmText:T,isCancelText:y,matchWorkflowByCommand:A,isLikelyNewTask:b,buildStepStartReply:_,buildStepFallbackSummary:h,extractFirstUrl:k,extractBasicCredentials:E,isBasicAuthStep:C,parseTaskPlan:be,executeActionPlan:ke,summarizeExecutionResult:Oe,sendChatMessage:Ae,refreshRuntimeConfigFromServer:Le,loadWorkflowsFromServer:async()=>{if(!re?.userId||!re?.botId)return[];const e=await ve(Se(),{bot_id:re.botId});return(Array.isArray(e?.workflows)?e.workflows:[]).map(P).filter(e=>e.workflowId&&e.triggerCommand&&e.steps.length>0&&e.isActive)},normalizeWorkflow:P,resolveTaskFallbackUrl:Ce,taskExecutorResetWorkflowState:()=>Be.resetWorkflowState(),emitDebugEvent:ce});const Pe=async()=>{const e=r.join(D.getPath("userData"),"artifacts");return await n.mkdir(e,{recursive:!0}),e};U({ipcMain:G,env:Q,loadedEnvFiles:X.loadedFiles,wsClient:we,signInWithLoginApi:fe,saveLoginProfile:async({botId:e,email:r,password:t,remember:o})=>{if(o)await n.writeFile(te(),JSON.stringify({botId:e,email:r,password:t,remember:!0,updatedAt:(new Date).toISOString()},null,2),"utf8");else try{await n.unlink(te())}catch(e){if("ENOENT"!==K(e))throw e}},clearLineSessions:()=>ue.clearLineSessions(),closeTaskBrowser:Ee,setCurrentSession:e=>{re=e},getCurrentSession:()=>re,emitLog:pe,getWorkflowCount:async()=>ue.getWorkflowCount(),getPlaywrightHeadless:()=>le,setPlaywrightHeadless:e=>{le=Boolean(e)},readSavedLoginProfile:async()=>{try{const e=await n.readFile(te(),"utf8"),r=JSON.parse(e);return{botId:r?.botId||"",email:r?.email||"",password:r?.password||"",remember:Boolean(r?.remember)}}catch(e){if("ENOENT"===K(e))return{botId:"",email:"",password:"",remember:!1};throw e}},sendChatMessage:Ae,closeChatWindow:()=>ae.closeChatWindow()}),D.whenReady().then(()=>{(()=>{if("darwin"!==process.platform)return;if(!s(ie))return;const e=q.createFromPath(ie);e.isEmpty()||D.dock.setIcon(e)})(),ee?(async()=>{try{await de(),await Ee(),D.quit()}catch(e){const r={ok:!1,error:e instanceof Error?e.message:String(e)};process.stderr.write(`${JSON.stringify(r,null,2)}\n`),await Ee(),D.exit(1)}})():(ae.createMainWindow(),D.on("activate",()=>{0===H.getAllWindows().length&&ae.createMainWindow()}))}),D.on("window-all-closed",()=>{we.stop(),Ee(),"darwin"!==process.platform&&D.quit()});
1
+ import e from"node:path";import t from"node:fs/promises";import{existsSync as i}from"node:fs";import{fileURLToPath as o}from"node:url";import{createRequire as a}from"node:module";import{SYSTEM_CONFIG as s}from"./config/systemConfig.js";import{BotWsClient as r}from"./wsClient.js";import{registerIpcHandlers as n}from"./ipc/registerHandlers.js";import{createAuthApiService as l}from"./services/authApiService.js";import{createCodexService as c}from"./services/codexService.js";import{createWindowManager as g}from"./services/windowManagerService.js";import{createGmoCoinRuntime as d}from"./gmoCoinRuntime.js";import{createGmoFxRuntime as m}from"./gmoFxRuntime.js";import{createSkillHostState as p}from"./skillHostState.js";const u=a(import.meta.url),{app:f,BrowserWindow:y,ipcMain:S,nativeImage:w}=u("electron"),h=o(import.meta.url),k=e.dirname(h),x=["trade","ai"],b=()=>e.join(f.getPath("userData"),"login-profile.json"),A=()=>e.join(f.getPath("userData"),"gmo-fx-config.json"),v=()=>e.join(f.getPath("userData"),"gmo-coin-config.json"),_=()=>e.join(f.getPath("userData"),"skill-states.json");let C=null,P=null;const D=new Map;let L=()=>e.join(f.getPath("documents"),"weget-bot-logs");const E=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"},O=async({level:i,source:o,message:a,details:s,ts:r})=>{const n=L();await t.mkdir(n,{recursive:!0});const l=new Date,c=l.getFullYear(),g=String(l.getMonth()+1).padStart(2,"0"),d=String(l.getDate()).padStart(2,"0"),m=e.join(n,`${i}-${c}${g}${d}.log`),p=JSON.stringify({ts:r||l.toISOString(),level:i,source:o,message:a,details:s??null},null,0);await t.appendFile(m,`${p}\n`,"utf8")},j=g({BrowserWindow:y,preloadPath:e.join(k,"preload.cjs"),rendererDir:e.join(k,"renderer"),windowIconPath:e.join(k,"renderer","logo.png")}),I=(e,t)=>{const i=(new Date).toISOString();j.emitBotLog({message:e,data:t||null,ts:i}),O({level:E(e),source:"runtime",message:e,details:t,ts:i}).catch(()=>{})},M=e=>{j.emitChatEvent(e)},B=e=>e&&"object"==typeof e?e:{},T=m({configPath:A(),emitLog:I,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}),emitTradeExecution:e=>{j.emitTradeExecution(e)},emitTradeHistory:e=>{const t=`trade-${Date.now()}`;P?.sendTaskEvent(t,"trade_history",e),I("[trade] history event sent (execution)",e)}}),U=d({configPath:v(),emitLog:I,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}),emitTradeExecution:e=>{j.emitTradeExecution(e)},emitTradeHistory:e=>{const t=`trade-${Date.now()}`;P?.sendTaskEvent(t,"trade_history",e),I("[trade] history event sent (execution)",e)}}),F=p({gmoFxConfigPath:A(),gmoCoinConfigPath:v(),log:I});T.loadConfig(),U.loadConfig(),async function(){try{const e=await t.readFile(_(),"utf8"),i=JSON.parse(e);return(Array.isArray(i?.items)?i.items:[]).filter(e=>Boolean(e&&"object"==typeof e)).map(e=>({user_id:String(e.user_id||""),bot_id:String(e.bot_id||""),skill_id:String(e.skill_id||""),package_name:String(e.package_name||""),enabled:Boolean(e.enabled),install_status:String(e.install_status||""),version:e.version?String(e.version):null,config_json:B(e.config_json),created_at:e.created_at?String(e.created_at):void 0,updated_at:e.updated_at?String(e.updated_at):void 0}))}catch(e){if("ENOENT"===e?.code)return[];throw e}}().then(e=>{e.length>0&&(F.applySnapshots(e),I("[skills] restored local snapshot",{count:e.length}))}).catch(e=>{I("[skills] failed to restore local snapshot",{error:e instanceof Error?e.message:String(e)})}),L=()=>String(T.getConfig().errorLogDir||U.getConfig().errorLogDir||"").trim()||e.join(f.getPath("documents"),"weget-bot-logs");const G=c({getModel:()=>T.getConfig().aiModel||U.getConfig().aiModel,getWorkingDir:()=>f.getPath("userData"),emitLog:I}),J=l({getEnv:()=>({loginApiUrl:s.loginApiUrl,botUploadApiUrl:s.botUploadApiUrl}),emitLog:I,getCurrentSession:()=>C});const K=()=>{const e=F.isSkillEnabled("@ai.weget.jp/skill-gmo-fx"),t=F.isSkillEnabled("@ai.weget.jp/skill-gmo-coin");"connected"===P?.getStatus()&&e?T.startExecutionStreams():T.stopExecutionStreams(),"connected"===P?.getStatus()&&t?U.startExecutionStreams():U.stopExecutionStreams(),I("[skills] applied managed states",{active_skills:F.getActiveSkills().map(e=>e.name)})},R=async()=>{const e=J.getBotSkillsListApiUrl(),i=String(C?.botId||"").trim();if(!e||!i)return;const o=await J.callBotApi(e,{bot_id:i}),a=Array.isArray(o.states)?o.states.filter(e=>Boolean(e&&"object"==typeof e)).map(e=>({user_id:String(e.user_id||""),bot_id:String(e.bot_id||""),skill_id:String(e.skill_id||""),package_name:String(e.package_name||""),enabled:Boolean(e.enabled),install_status:String(e.install_status||""),version:e.version?String(e.version):null,config_json:B(e.config_json),created_at:e.created_at?String(e.created_at):void 0,updated_at:e.updated_at?String(e.updated_at):void 0})):[];F.applySnapshots(a),await(async e=>{await t.writeFile(_(),JSON.stringify({updatedAt:(new Date).toISOString(),items:e},null,2),"utf8")})(a),K(),I("[config] skill states synced",{count:a.length,bot_id:i,enabled:a.filter(e=>e.enabled).length})},N=e=>{if("fx"===e&&!F.isSkillEnabled("@ai.weget.jp/skill-gmo-fx"))throw new Error("GMO FX skill is disabled");if("coin"===e&&!F.isSkillEnabled("@ai.weget.jp/skill-gmo-coin"))throw new Error("GMO Coin skill is disabled")};P=new r({wsUrl:"wss://ws.weget.jp",version:"0.2.0",capabilities:x,heartbeatSec:30,reconnectMs:3e3,reconnectMaxAttempts:10,wsPingIntervalSec:25,wsPongTimeoutSec:12},{onLog:(e,t)=>I(e,t),onStatus:e=>j.emitBotStatus({status:e}),onLineMessage:async e=>{const t=String(e.line_user_id||""),i=String(e.text||"").trim();if(M({type:"line_user",lineUserId:t,text:i}),!i)return{answer:""};try{const e=await G.runPrompt({prompt:i,contextLabel:"line_message"});return M({type:"line_assistant",lineUserId:t,text:e}),{answer:e}}catch(e){const i=e instanceof Error?e.message:String(e);I("[ai] line reply failed",{error:i});const o=`AI error: ${i}`;return M({type:"line_assistant",lineUserId:t,text:o}),{answer:o}}},onRunTask:async e=>{const t=String(e.task_id||"").trim(),i=String(e.prompt||"").trim();if(t){if(D.set(t,{cancelled:!1}),P?.sendTaskEvent(t,"task_received",{prompt:i,conversation_id:String(e.conversation_id||""),continue_from_task_id:String(e.continue_from_task_id||""),ts:(new Date).toISOString()}),!i)return P?.sendTaskEvent(t,"task_failed",{error:"prompt is required",ts:(new Date).toISOString()}),void D.delete(t);try{P?.sendTaskEvent(t,"step_started",{step:"codex_chat",ts:(new Date).toISOString()});const e=await G.runPrompt({prompt:i,contextLabel:`task:${t}`}),o=D.get(t);if(o?.cancelled)return P?.sendTaskEvent(t,"task_cancelled",{reason:"server_cancelled",ts:(new Date).toISOString()}),void D.delete(t);P?.sendTaskEvent(t,"ai_output",{summary:e,ts:(new Date).toISOString()}),P?.sendTaskEvent(t,"step_finished",{step:"codex_chat",ts:(new Date).toISOString()}),P?.sendTaskEvent(t,"task_completed",{summary:e.slice(0,500),ts:(new Date).toISOString()})}catch(e){P?.sendTaskEvent(t,"task_failed",{error:e instanceof Error?e.message:String(e),ts:(new Date).toISOString()})}finally{D.delete(t)}}else I("[task] run_task missing task_id")},onTaskControl:async e=>{const t=String(e.task_id||"").trim(),i=String(e.action||"").trim();if(t&&i&&"cancel_task"===i){const e=D.get(t);if(e)return e.cancelled=!0,void I("[task] cancel requested",{task_id:t});P?.sendTaskEvent(t,"task_cancelled",{reason:"server_cancelled",ts:(new Date).toISOString()})}},onServerConfigUpdate:async e=>{const t=Array.isArray(e.scopes)?e.scopes.map(e=>String(e||"").trim()):[],i=t.length>0?t:["runtime","skills"];i.includes("runtime")&&await(async()=>{const e=J.getBotRuntimeConfigApiUrl(),t=String(C?.botId||"").trim();if(!e||!t)return;const i=await J.callBotApi(e,{bot_id:t}),o=String(i.chat_model||"").trim(),a={...o?{aiModel:o}:{}};0!==Object.keys(a).length&&(await T.saveConfig({...T.getConfig(),...a}),await U.saveConfig({...U.getConfig(),...a}),I("[config] runtime config synced",{chat_model:o||""}))})(),i.includes("skills")&&await R()}}),n({ipcMain:S,env:{capabilities:x,loginApiUrl:s.loginApiUrl},signInWithLoginApi:J.signInWithLoginApi,saveLoginProfile:async({botId:e,email:i,password:o,remember:a})=>{if(a)await t.writeFile(b(),JSON.stringify({botId:e,email:i,password:o,remember:!0,updatedAt:(new Date).toISOString()},null,2),"utf8");else try{await t.unlink(b())}catch(e){if("ENOENT"!==e?.code)throw e}},readSavedLoginProfile:async()=>{try{const e=await t.readFile(b(),"utf8"),i=JSON.parse(e);return{botId:String(i?.botId||""),email:String(i?.email||""),password:String(i?.password||""),remember:Boolean(i?.remember)}}catch(e){if("ENOENT"===e?.code)return{botId:"",email:"",password:"",remember:!1};throw e}},getFxApiConfig:()=>{const e=T.getConfig();return{riskDailyLossLimitJpy:e.riskDailyLossLimitJpy,fxApiKey:e.fxApiKey,fxApiSecret:e.fxApiSecret,errorLogDir:e.errorLogDir}},saveFxApiConfig:async e=>{const t=await T.saveConfig({...T.getConfig(),...e&&"object"==typeof e?e:{}});return"connected"===P?.getStatus()&&K(),{riskDailyLossLimitJpy:t.riskDailyLossLimitJpy,fxApiKey:t.fxApiKey,fxApiSecret:t.fxApiSecret,errorLogDir:t.errorLogDir}},getCoinApiConfig:()=>{const e=U.getConfig();return{riskDailyLossLimitJpy:e.riskDailyLossLimitJpy,cryptoApiKey:e.cryptoApiKey,cryptoApiSecret:e.cryptoApiSecret,errorLogDir:e.errorLogDir}},saveCoinApiConfig:async e=>{const t=await U.saveConfig({...U.getConfig(),...e&&"object"==typeof e?e:{}});return"connected"===P?.getStatus()&&K(),{riskDailyLossLimitJpy:t.riskDailyLossLimitJpy,cryptoApiKey:t.cryptoApiKey,cryptoApiSecret:t.cryptoApiSecret,errorLogDir:t.errorLogDir}},getAiConfig:()=>{const e=T.getConfig(),t=U.getConfig();return{aiModel:e.aiModel||t.aiModel}},saveAiConfig:async e=>{const t=e&&"object"==typeof e?e:{},i=await T.saveConfig({...T.getConfig(),...t});return await U.saveConfig({...U.getConfig(),...t}),{aiModel:i.aiModel}},setCurrentSession:e=>{C=e||null;const t=C?"connected":"disconnected";j.emitBotStatus({status:t})},getCurrentSession:()=>C,getActiveSkills:()=>F.getActiveSkills().map(e=>({name:e.name,displayName:e.displayName,version:e.version})),getCodexAuthStatus:()=>G.getAuthStatus(),startCodexLogin:()=>G.startLogin(),getManagedSkills:()=>F.getManagedSkills(),emitLog:I,openGmoKlineWindow:async({symbol:e,interval:t,market:i})=>(()=>{const o="fx"===i?"fx":"coin";return N(o),("fx"===o?T.marketData:U.marketData).fetchKline({symbol:e,interval:t,market:o})})(),getGmoMarketQuotes:async({market:e,symbols:t})=>(()=>{const i="fx"===e?"fx":"coin";return N(i),("fx"===i?T.marketData:U.marketData).fetchQuotes({market:i,symbols:t})})(),getGmoSymbolRules:async({market:e})=>(()=>{const t="fx"===e?"fx":"coin";return N(t),("fx"===t?T.marketData:U.marketData).fetchSymbolRules({market:t})})(),getGmoBuyingPower:async({market:e})=>({market:e,availableJpy:await(N(e),("fx"===e?T.gateway:U.gateway).getBuyingPower(e))}),getGmoAccountMetrics:async({market:e})=>(N(e),("fx"===e?T.gateway:U.gateway).getAccountMetrics(e)),getGmoPositionSummary:async({market:e,symbol:t})=>({market:e,symbol:t,items:await(N(e),("fx"===e?T.gateway:U.gateway).getPositionSummary({market:e,symbol:t}))}),getGmoOpenPositions:async({market:e,symbol:t,page:i,prevId:o,count:a})=>({market:e,symbol:t||"",items:await(N(e),("fx"===e?T.gateway:U.gateway).getOpenPositions({market:"fx"===e?"fx":"crypto",symbol:t||void 0,page:i,prevId:o,count:a}))}),placeGmoFxOrder:async({symbol:e,side:t,size:i})=>{N("fx");return await T.gateway.placeFxOrder({symbol:e,side:t,size:i,executionType:"MARKET"})},placeGmoCoinOrder:async({symbol:e,side:t,size:i})=>{N("coin");return await U.gateway.placeCoinOrder({symbol:e,side:t,size:i,executionType:"MARKET"})},placeGmoFxCloseOrder:async({symbol:e,side:t,positionId:i,size:o})=>{N("fx");return await T.gateway.placeFxCloseOrder({symbol:e,side:t,positionId:i,size:o,executionType:"MARKET"})},placeGmoCoinCloseOrder:async({symbol:e,side:t,positionId:i,size:o})=>{N("coin");return await U.gateway.placeCoinCloseOrder({symbol:e,side:t,positionId:i,size:o,executionType:"MARKET"})},getGmoApiState:e=>"fx"===e?T.status.getApiState():U.status.getApiState(),testGmoApi:e=>(N(e),"fx"===e?T.status.testApi():U.status.testApi()),buildTradeStatusSnapshot:e=>(N(e),"fx"===e?T.status.buildSnapshot():U.status.buildSnapshot()),closeTradeWindows:()=>{},startWsClient:e=>{P?.start(e),K()},stopWsClient:()=>{T.stopExecutionStreams(),U.stopExecutionStreams(),P?.stop()},handleBotChatMessage:async({text:e})=>{M({type:"user",text:String(e||"")});const t=await G.runPrompt({prompt:e,contextLabel:"desktop_chat"});return M({type:"assistant",text:t}),{answer:t}},writeErrorLog:async({source:e,message:t,details:i})=>{await(async({source:e,message:t,details:i})=>{await O({level:"error",source:e,message:t,details:i})})({source:e,message:t,details:i})},writeRuntimeLog:async({level:e,source:t,message:i,details:o})=>{await O({level:e,source:t,message:i,details:o})}});const W=e.join(k,"renderer","logo.png");f.whenReady().then(()=>{(()=>{if("darwin"!==process.platform)return;if(!i(W))return;const e=w.createFromPath(W);e.isEmpty()||f.dock.setIcon(e)})(),j.createMainWindow(),f.on("activate",()=>{0===y.getAllWindows().length&&j.createMainWindow()})}),f.on("window-all-closed",()=>{T.stopExecutionStreams(),U.stopExecutionStreams(),P?.stop(),"darwin"!==process.platform&&f.quit()});
@@ -0,0 +1 @@
1
+ const t=(r,e="")=>{if(null==r)return r;const o=String(e||"");if("string"==typeof r)return/(password|token|authorization|api_?key|secret|cookie|jwt)/i.test(o)?r.length<=8?"****":`${r.slice(0,3)}****${r.slice(-3)}`:r.length>1e3?`${r.slice(0,1e3)}...(truncated)`:r;if(Array.isArray(r))return r.slice(0,30).map(r=>t(r,o));if("object"==typeof r){const e={};for(const[o,s]of Object.entries(r))e[o]=t(s,o);return e}return r};export const createPlatformApiClient=({getBaseUrl:r,getSession:e,log:o})=>({request:async s=>{const i=e(),n=String(i?.accessToken||"").trim();if(!n)throw new Error("platform api access token missing");const a=s.method||"POST",c=((t,r)=>{const e=String(t||"").trim().replace(/\/$/,""),o=String(r.path||"").trim();if(!e)throw new Error("platform api base url is missing");if(!o)throw new Error("platform api path is missing");const s=new URL(o.startsWith("http")?o:`${e}${o.startsWith("/")?o:`/${o}`}`),i=r.query||{};for(const[t,r]of Object.entries(i))null!=r&&""!==String(r)&&s.searchParams.set(t,String(r));return s.toString()})(r(),s);o?.("[platform-api] request",{method:a,url:c,body:t(s.body)});const p=await fetch(c,{method:a,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:"GET"===a||"DELETE"===a?void 0:JSON.stringify(s.body||{})}),l=await(async t=>{const r=await t.text();if(!r)return{};try{const t=JSON.parse(r);return t&&"object"==typeof t?t:{value:t}}catch{return{raw:r}}})(p);if(o?.("[platform-api] response",{method:a,url:c,status:p.status,ok:p.ok,body:t(l)}),!p.ok)throw new Error(String(l.error||l.message||`platform api failed: ${p.status}`));return l}});
@@ -4,16 +4,35 @@ contextBridge.exposeInMainWorld('botApi', {
4
4
  login: (email, password, botId, remember) => ipcRenderer.invoke('auth:login', { email, password, botId, remember }),
5
5
  logout: () => ipcRenderer.invoke('auth:logout'),
6
6
  getSavedProfile: () => ipcRenderer.invoke('auth:getSavedProfile'),
7
- saveProfile: ({ email, password, botId, remember }) =>
8
- ipcRenderer.invoke('auth:saveProfile', { email, password, botId, remember }),
7
+ getFxApiConfig: () => ipcRenderer.invoke('trade:getFxApiConfig'),
8
+ saveFxApiConfig: (config) => ipcRenderer.invoke('trade:saveFxApiConfig', config),
9
+ getCoinApiConfig: () => ipcRenderer.invoke('trade:getCoinApiConfig'),
10
+ saveCoinApiConfig: (config) => ipcRenderer.invoke('trade:saveCoinApiConfig', config),
11
+ getAiConfig: () => ipcRenderer.invoke('ai:getConfig'),
12
+ saveAiConfig: (config) => ipcRenderer.invoke('ai:saveConfig', config),
9
13
  getRuntimeInfo: () => ipcRenderer.invoke('bot:getRuntimeInfo'),
10
- setPlaywrightVisible: (visible) => ipcRenderer.invoke('bot:setPlaywrightVisible', { visible }),
14
+ getSkills: () => ipcRenderer.invoke('bot:getSkills'),
15
+ startCodexLogin: () => ipcRenderer.invoke('codex:startLogin'),
16
+ openGmoKlineWindow: ({ symbol, interval, market }) =>
17
+ ipcRenderer.invoke('trade:openGmoKlineWindow', { symbol, interval, market }),
18
+ getMarketQuotes: ({ market, symbols }) => ipcRenderer.invoke('trade:getMarketQuotes', { market, symbols }),
19
+ getSymbolRules: ({ market }) => ipcRenderer.invoke('trade:getSymbolRules', { market }),
20
+ getBuyingPower: ({ market }) => ipcRenderer.invoke('trade:getBuyingPower', { market }),
21
+ getAccountMetrics: ({ market }) => ipcRenderer.invoke('trade:getAccountMetrics', { market }),
22
+ getPositionSummary: ({ market, symbol }) => ipcRenderer.invoke('trade:getPositionSummary', { market, symbol }),
23
+ getOpenPositions: ({ market, symbol, page, prevId, count }) =>
24
+ ipcRenderer.invoke('trade:getOpenPositions', { market, symbol, page, prevId, count }),
25
+ placeCoinOrder: ({ symbol, side, size }) => ipcRenderer.invoke('trade:placeCoinOrder', { symbol, side, size }),
26
+ placeFxOrder: ({ symbol, side, size }) => ipcRenderer.invoke('trade:placeFxOrder', { symbol, side, size }),
27
+ closeCoinPosition: ({ symbol, side, positionId, size }) =>
28
+ ipcRenderer.invoke('trade:closeCoinPosition', { symbol, side, positionId, size }),
29
+ closeFxPosition: ({ symbol, side, positionId, size }) =>
30
+ ipcRenderer.invoke('trade:closeFxPosition', { symbol, side, positionId, size }),
31
+ testGmoApi: ({ market } = {}) => ipcRenderer.invoke('trade:testGmoApi', { market }),
32
+ getStatusSnapshot: ({ market } = {}) => ipcRenderer.invoke('trade:getStatusSnapshot', { market }),
11
33
  sendChat: (message) => ipcRenderer.invoke('chat:send', { message }),
12
- onChatEvent: (handler) => {
13
- const listener = (_event, payload) => handler(payload);
14
- ipcRenderer.on('chat:event', listener);
15
- return () => ipcRenderer.removeListener('chat:event', listener);
16
- },
34
+ writeErrorLog: ({ source, message, details }) => ipcRenderer.invoke('log:writeError', { source, message, details }),
35
+ writeLog: ({ level, source, message, details }) => ipcRenderer.invoke('log:write', { level, source, message, details }),
17
36
  onLog: (handler) => {
18
37
  const listener = (_event, payload) => handler(payload);
19
38
  ipcRenderer.on('bot:log', listener);
@@ -24,4 +43,14 @@ contextBridge.exposeInMainWorld('botApi', {
24
43
  ipcRenderer.on('bot:status', listener);
25
44
  return () => ipcRenderer.removeListener('bot:status', listener);
26
45
  },
46
+ onChatEvent: (handler) => {
47
+ const listener = (_event, payload) => handler(payload);
48
+ ipcRenderer.on('chat:event', listener);
49
+ return () => ipcRenderer.removeListener('chat:event', listener);
50
+ },
51
+ onTradeExecution: (handler) => {
52
+ const listener = (_event, payload) => handler(payload);
53
+ ipcRenderer.on('trade:execution', listener);
54
+ return () => ipcRenderer.removeListener('trade:execution', listener);
55
+ },
27
56
  });
@@ -1 +1 @@
1
- const e=document.getElementById("login-form"),t=document.getElementById("login-screen"),n=document.getElementById("app-shell"),o=document.getElementById("bot-id"),i=document.getElementById("email"),s=document.getElementById("password"),l=document.getElementById("remember-me"),a=document.getElementById("status"),r=document.getElementById("session"),d=document.getElementById("runtime-info"),c=document.getElementById("logs"),m=document.getElementById("logout-btn"),u=document.getElementById("login-btn"),g=document.getElementById("chat-form"),p=document.getElementById("chat-input"),f=document.getElementById("messages"),w=document.getElementById("send-btn"),b=Array.from(document.querySelectorAll(".tab-btn")),y=Array.from(document.querySelectorAll(".tab-panel"));let E=!1;const v=e=>e&&"object"==typeof e?e:{},h=e=>{for(const t of b)t.classList.toggle("is-active",t.dataset.tab===e);for(const t of y)t.classList.toggle("is-active",t.id===`tab-${e}`)},C=e=>{E=Boolean(e),t.classList.toggle("hidden",E),n.classList.toggle("hidden",!E)};for(const e of b)e.addEventListener("click",()=>h(e.dataset.tab||"config"));const I=(e,t)=>{const n=document.createElement("div"),o=""===e?"":`${e}: `;if(t&&"object"==typeof t){const e=document.createElement("details");e.className="json-node";const i=document.createElement("summary");i.textContent=`${o}${(e=>Array.isArray(e)?`Array(${e.length})`:e&&"object"==typeof e?`Object(${Object.keys(e).length})`:String(e))(t)}`,e.appendChild(i);const s=document.createElement("div");return s.className="json-children",Array.isArray(t)?t.forEach((e,t)=>{s.appendChild(I(String(t),e))}):Object.entries(t).forEach(([e,t])=>{s.appendChild(I(e,t))}),e.appendChild(s),n.appendChild(e),n}const i=document.createElement("div");if(i.className="json-leaf",""!==e){const t=document.createElement("span");t.className="json-key",t.textContent=`${e}: `,i.appendChild(t)}return i.appendChild((e=>{const t=document.createElement("span");if(null===e)return t.className="json-value json-value-null",t.textContent="null",t;const n=typeof e;return"string"===n?(t.className="json-value json-value-string",t.textContent=`"${e}"`,t):"number"===n?(t.className="json-value json-value-number",t.textContent=String(e),t):"boolean"===n?(t.className="json-value json-value-bool",t.textContent=String(e),t):(t.className="json-value",t.textContent=String(e),t)})(t)),n.appendChild(i),n},S=(e,t=null,n=null)=>{const o=document.createElement("div");o.className="log-entry";const i=document.createElement("div");i.className="log-title",i.textContent=((e,t)=>{const n=v(t),o=String(n.workflowName||"").trim(),i=String(n.workflowId||"").trim(),s=String(n.stepTitle||"").trim(),l=Number(n.workflowStepIndex||0),a=Number(n.workflowStepTotal||0),r=o||(i?`workflow:${i}`:"");let d="";return s?d=s:l>0&&a>0?d=`Step ${l}/${a}`:l>0&&(d=`Step ${l}`),r&&d?`${r} / ${d}`:r||d||String(e||"log")})(e,t),o.appendChild(i);const s=document.createElement("div");s.className="log-line";const l=document.createElement("span");if(l.className="log-ts",l.textContent=n||(new Date).toISOString(),s.appendChild(l),s.append(document.createTextNode(e||"")),o.appendChild(s),null!=t&&""!==t)if("object"==typeof t){const e=document.createElement("details");e.className="log-json-root";const n=document.createElement("summary");n.textContent="JSON",e.appendChild(n);const i=document.createElement("div");i.className="json-children",i.appendChild(I("",t)),e.appendChild(i),o.appendChild(e)}else{const e=document.createElement("div");e.className="json-leaf",e.textContent=String(t),o.appendChild(e)}for(c.appendChild(o);c.children.length>300;)c.removeChild(c.firstChild);c.scrollTop=c.scrollHeight},A=(e,t,n="")=>{const o=document.createElement("div");o.className=`msg ${e}`;let i=n||("user"===e?"You":"Assistant");"line-user"===e&&(i=n||"LINE User"),"line-assistant"===e&&(i=n||"AI"),o.textContent=`${i}: ${t}`,f.appendChild(o),f.scrollTop=f.scrollHeight},$=e=>{const t=String(e||"disconnected").trim().toLowerCase();a.textContent=t,a.classList.remove("status-connected","status-disconnected","status-connecting"),"connected"!==t?"reconnecting"!==t&&"connecting"!==t?a.classList.add("status-disconnected"):a.classList.add("status-connecting"):a.classList.add("status-connected")},N=e=>{u.disabled=e,u.classList.toggle("is-loading",e),u.textContent=e?"Loading...":"Login"},x=async()=>{if(window.botApi?.getRuntimeInfo)try{const e=await window.botApi.getRuntimeInfo();if(!e?.ok)return;$(e.status||"disconnected"),e.session?(C(!0),r.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`):(C(!1),r.textContent="not logged in"),(e=>{d.innerHTML="";const t=v(e),n=v(t.runtime),o=t.session?v(t.session):null,i=[["Bot ID",String(o?.botId||"-")],["User ID",String(o?.userId||"-")],["Email",String(o?.email||"-")],["AI Model",String(o?.chatModel||"-")],["Workflow Count",String(n.workflowCount||0)],["Capabilities",Array.isArray(n.capabilities)?n.capabilities.map(e=>String(e)).join(", "):"-"],["Playwright Visual",n.playwrightHeadless?"off (headless)":"on (visible)"],["WS URL",String(n.wsUrl||"-")],["Login API",String(n.loginApiUrl||"-")],["Workflow API",String(n.workflowListApiUrl||"-")],["Runtime API",String(n.runtimeConfigApiUrl||"-")]];for(const[e,t]of i){const n=document.createElement("div");n.className="runtime-key",n.textContent=e;const o=document.createElement("div");o.className="runtime-value",o.textContent=String(t),d.appendChild(n),d.appendChild(o)}})(e)}catch(e){S(`[ui] getRuntimeInfo failed: ${e instanceof Error?e.message:String(e)}`)}};e.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi)return void S("[ui] login blocked: botApi bridge not available");const t=i.value.trim(),n=o.value.trim(),a=s.value;if(n&&t&&a){N(!0),S("[ui] login request");try{const e=await window.botApi.login(t,a,n,l.checked);if(!e.ok)return void S(`[ui] login failed: ${e.error}`);if(!e.session)return void S("[ui] login failed: missing session");C(!0),h("chat"),r.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`,S("[ui] login success"),await x()}catch(e){S(`[ui] login exception: ${e instanceof Error?e.message:String(e)}`)}finally{N(!1)}}}),m.addEventListener("click",async()=>{window.botApi&&(await window.botApi.logout(),C(!1),$("disconnected"),r.textContent="not logged in",S("[ui] logged out"),await x())}),g.addEventListener("submit",async e=>{if(e.preventDefault(),!E)return;const t=p.value.trim();if(!t||!window.botApi)return;A("user",t),p.value="",w.disabled=!0;const n=await window.botApi.sendChat(t);n.ok?A("assistant",String(n.answer||"")):A("assistant",`Error: ${n.error}`),w.disabled=!1}),window.botApi||S("[ui] preload bridge missing: window.botApi is undefined"),window.botApi&&(window.botApi.onLog(e=>{S(String(e?.message||""),e?.data??null,e?.ts||null)}),window.botApi.onStatus(e=>{$(String(e.status||"")),x()}),window.botApi.onChatEvent(e=>{if(!e)return;const t=v(e);if("line_user"===t.type){const e=t.lineUserId?`LINE(${String(t.lineUserId)})`:"LINE";return void A("line-user",String(t.text||""),e)}if("line_assistant"===t.type){const e=t.lineUserId?`AI->LINE(${String(t.lineUserId)})`:"AI->LINE";A("line-assistant",String(t.text||""),e)}}),(async()=>{if(window.botApi?.getSavedProfile)try{const e=await window.botApi.getSavedProfile();if(!e?.ok||!e.profile)return;o.value=e.profile.botId||"",i.value=e.profile.email||"",s.value=e.profile.password||"",l.checked=Boolean(e.profile.remember),e.profile.remember&&S("[ui] loaded saved login profile")}catch(e){S(`[ui] load saved profile failed: ${e instanceof Error?e.message:String(e)}`)}})(),x());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("user-notice"),r=document.getElementById("bot-id"),a=document.getElementById("email"),s=document.getElementById("password"),c=document.getElementById("remember-me"),l=document.getElementById("status"),d=document.getElementById("session"),m=document.getElementById("runtime-info"),u=document.getElementById("skill-state-list"),g=document.getElementById("tab-btn-skill-gmo-coin"),f=document.getElementById("tab-btn-skill-gmo-fx"),p=document.getElementById("tab-btn-skill-browser"),y=document.getElementById("coin-skill-title"),b=document.getElementById("fx-skill-title"),w=document.getElementById("browser-skill-title"),x=document.getElementById("coin-skill-package"),k=document.getElementById("fx-skill-package"),h=document.getElementById("browser-skill-package"),S=document.getElementById("coin-skill-enabled"),v=document.getElementById("fx-skill-enabled"),E=document.getElementById("browser-skill-enabled"),C=document.getElementById("coin-skill-version"),I=document.getElementById("fx-skill-version"),A=document.getElementById("browser-skill-version"),B=document.getElementById("browser-skill-tools"),L=document.getElementById("logs"),N=document.getElementById("messages"),$=document.getElementById("chat-form"),U=document.getElementById("chat-input"),M=document.getElementById("send-btn"),P=document.getElementById("error-log-dir"),T=document.getElementById("risk-daily-loss-limit-jpy"),F=document.getElementById("crypto-api-key"),D=document.getElementById("crypto-api-secret"),z=document.getElementById("fx-api-key"),O=document.getElementById("fx-api-secret"),j=document.getElementById("toggle-crypto-secret-btn"),q=document.getElementById("toggle-fx-secret-btn"),J=document.getElementById("save-api-config-btn"),Y=document.getElementById("ai-model"),R=document.getElementById("save-ai-config-btn"),H=document.getElementById("codex-login-btn"),_=document.getElementById("codex-copy-login-btn"),K=document.getElementById("codex-refresh-auth-btn"),W=document.getElementById("codex-login-command"),G=document.getElementById("logout-btn"),V=document.getElementById("login-btn"),Q=document.getElementById("coin-symbol-select"),X=document.getElementById("fx-symbol-select"),Z=document.getElementById("coin-kline-intervals"),ee=document.getElementById("fx-kline-intervals"),te=document.getElementById("coin-kline-canvas"),ne=document.getElementById("fx-kline-canvas"),ie=document.getElementById("coin-market-icon"),oe=document.getElementById("fx-market-icon"),re=document.getElementById("coin-order-bid"),ae=document.getElementById("coin-order-ask"),se=document.getElementById("coin-order-spread"),ce=document.getElementById("fx-order-bid"),le=document.getElementById("fx-order-ask"),de=document.getElementById("fx-order-spread"),me=document.getElementById("coin-order-qty"),ue=document.getElementById("fx-order-qty"),ge=document.getElementById("coin-buy-btn"),fe=document.getElementById("coin-sell-btn"),pe=document.getElementById("fx-buy-btn"),ye=document.getElementById("fx-sell-btn"),be=document.getElementById("coin-required-amount"),we=document.getElementById("fx-required-amount"),xe=document.getElementById("coin-account-info-refresh-btn"),ke=document.getElementById("fx-account-info-refresh-btn"),he=document.getElementById("coin-account-pnl"),Se=document.getElementById("coin-account-margin"),ve=document.getElementById("coin-account-available"),Ee=document.getElementById("coin-account-margin-ratio"),Ce=document.getElementById("fx-account-pnl"),Ie=document.getElementById("fx-account-margin"),Ae=document.getElementById("fx-account-available"),Be=document.getElementById("fx-account-margin-ratio"),Le=document.getElementById("coin-position-summary-body"),Ne=document.getElementById("coin-position-list-body"),$e=document.getElementById("fx-position-summary-body"),Ue=document.getElementById("fx-position-list-body"),Me=document.getElementById("coin-position-summary-refresh-btn"),Pe=document.getElementById("coin-position-list-refresh-btn"),Te=document.getElementById("fx-position-summary-refresh-btn"),Fe=document.getElementById("fx-position-list-refresh-btn"),De=Array.from(document.querySelectorAll(".tab-btn")),ze=Array.from(document.querySelectorAll(".tab-panel"));let Oe="15m",je="15m",qe=String(Q?.value||"BTC_JPY").trim().toUpperCase(),Je=String(X?.value||"USD_JPY").trim().toUpperCase(),Ye=null,Re=null,He=null,_e=null,Ke=!1,We=!1,Ge=!1,Ve=!1,Qe=!1,Xe="",Ze="",et=0,tt=0,nt=null,it=null;const ot=new Map,rt={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1},at={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1};let st=[],ct=[];const lt=e=>e&&"object"==typeof e?e:{},dt=e=>{for(const t of De)t.classList.toggle("is-active",t.dataset.tab===e);for(const t of ze)t.classList.toggle("is-active",t.id===`tab-${e}`)};for(const e of De)e.addEventListener("click",()=>dt(e.dataset.tab||"coin"));const mt=e=>{const n=Ke!==e;Ke=e,t.classList.toggle("hidden",e),i.classList.toggle("hidden",!e),e&&Wt(),e?n&&on():(Mt(),We=!1)},ut=e=>{const t=String(e||"disconnected").trim().toLowerCase();l.textContent=t,l.classList.remove("status-connected","status-disconnected","status-connecting"),"connected"!==t?"reconnecting"!==t&&"connecting"!==t?l.classList.add("status-disconnected"):l.classList.add("status-connecting"):l.classList.add("status-connected")},gt=(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})},ft=e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,pt=e=>`${e>0?"+":""}${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,yt=e=>!Number.isFinite(e)||e<=0||e>1e5?"- %":(e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:2})} %`)(e),bt=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(/\.$/,"")},wt=e=>{const t=String(e||"").trim(),n=t.indexOf(".");return n>=0?t.length-n-1:0},xt=(e,t)=>{const n=String(e||"").trim(),i=n.startsWith("-"),o=i?n.slice(1):n,[r,a=""]=o.split("."),s=(a+"0".repeat(t)).slice(0,t),c=BigInt((r||"0")+s);return i?-c:c},kt=({symbol:e,quote:t,orderBidNode:n,orderAskNode:i,orderSpreadNode:o})=>{if(!t)return n.textContent="-",i.textContent="-",void(o.textContent="-");const r=gt(t.bid),a=gt(t.ask);n.textContent=r,i.textContent=a,o.textContent=gt(t.spread,8)},ht=e=>{if("coin"===e){const e=Number(String(me.value||"").trim()),t=nt;return!Number.isFinite(e)||e<=0||null===t||!Number.isFinite(t)?void(be.textContent="- 円"):void(be.textContent=ft(e*t/2))}const t=Number(String(ue.value||"").trim()),n=it;!Number.isFinite(t)||t<=0||null===n||!Number.isFinite(n)?we.textContent="- 円":we.textContent=ft(t*n/20)},St=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?(kt({symbol:n,quote:o,orderBidNode:re,orderAskNode:ae,orderSpreadNode:se}),nt=o?.ask??null,ht("coin")):(kt({symbol:n,quote:o,orderBidNode:ce,orderAskNode:le,orderSpreadNode:de}),it=o?.ask??null,ht("fx")),o},vt=(e,t)=>{const n=String(t||"").trim().toUpperCase();if(!n)return;if("coin"===e){const e=n.replace("_","-").toLowerCase();return void(ie.src=`https://coin.z.com/jp/member/imgs/icon-${e}.svg`)}const[i,o]=n.split("_"),r="JPY"===o?i:`${i}_${o}`;oe.src=`https://coin.z.com/jp/member/imgs/fx/icon_${r}.svg`},Et=e=>"5m"===e?3e5:"15m"===e?9e5:"30m"===e?18e5:"1h"===e?36e5:6e4,Ct=e=>"coin"===e?rt:at,It=e=>"coin"===e?te:ne,At=e=>"coin"===e?qe:Je,Bt=e=>{const t=At(e),n=("coin"===e?st:ct).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},Lt=e=>{const t=Ct(e);t.candles.length&&Vt(It(e),t.candles,Bt(e))},Nt=(e,t)=>{if(!t)return;const n=Ct(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),Vt(It(e),n.candles,Bt(e)))},$t=async e=>{const t=Ct(e);if(!t.interval||!t.symbol||t.fetching)return;if(t.symbol!==At(e))return;if(!(Math.floor(Date.now()/Et(t.interval))<=t.lastFetchBucket)){t.fetching=!0;try{"coin"===e?await en():await tn()}finally{t.fetching=!1}}},Ut=async e=>{if("coin"!==e){if(!Qe){Qe=!0;try{const e=await St("fx",Je);Nt("fx",e),await $t("fx")}catch(e){const t=Yt(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Ze||n-tt>15e3)&&(qt("[fx] quote poll failed",{error:t}),Ze=t,tt=n)}finally{Qe=!1}}}else{if(Ve)return;Ve=!0;try{const e=await St("coin",qe);Nt("coin",e),await $t("coin")}catch(e){const t=Yt(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Xe||n-et>15e3)&&(qt("[coin] quote poll failed",{error:t}),Xe=t,et=n)}finally{Ve=!1}}},Mt=()=>{He&&(clearInterval(He),He=null),_e&&(clearInterval(_e),_e=null)},Pt=async e=>{if(window.botApi?.getAccountMetrics){if(!e||"coin"===e){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);he.textContent=n>0?pt(i):ft(0),Se.textContent=ft(n),ve.textContent=ft(t),Ee.textContent=yt(Number(e.marginRatio||0))}else qt("[coin] account metrics fetch failed",{error:e?.error||"unknown error"})}if(!e||"fx"===e){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);Ce.textContent=n>0?pt(i):ft(0),Ie.textContent=ft(n),Ae.textContent=ft(t),Be.textContent=yt(Number(e.marginRatio||0))}else qt("[fx] account metrics fetch failed",{error:e?.error||"unknown error"})}}},Tt=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").toUpperCase(),o=n.replace("_","/"),r=String(i.side||"-"),a=String(i.size||"").trim(),s=Number(a||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&&a?`data-market="${e}" data-symbol="${n}" data-side="${String(r||"").toUpperCase()}" data-position-id="${u}" data-size="${a}"`:"disabled",f=document.createElement("tr");f.innerHTML=`\n <td><button type="button" class="close-btn" ${g}>決済</button></td>\n <td>${o}<br />${r}</td>\n <td>${s.toLocaleString("ja-JP")}</td>\n <td>${c.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${pt(l)}<br />${pt(d)}</td>\n <td>${m.replace("T"," ").slice(0,19)}</td>\n `,t.appendChild(f)}}else t.innerHTML='<tr><td colspan="6" class="empty-cell">対象のお取引はございません。</td></tr>'},Ft=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").replace("_","/"),o=String(i.side||"-"),r=Number("fx"===e?i.sumPositionSize||0:i.sumPositionQuantity||0),a=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>${r.toLocaleString("ja-JP")}</td>\n <td>${a.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${pt(s)}<br />${pt(c)}</td>\n `,t.appendChild(l)}}else t.innerHTML='<tr><td colspan="5" class="empty-cell">対象のお取引はございません。</td></tr>'},Dt=async e=>{if(window.botApi?.getPositionSummary&&window.botApi?.getOpenPositions){if(!e||"coin"===e){const e=await window.botApi.getPositionSummary({market:"coin",symbol:qe});e?.ok?(st=Array.isArray(e.items)?e.items:[],Ft("coin",Le,st),Lt("coin")):(qt("[coin] position summary fetch failed",{error:e?.error||"unknown error"}),st=[],Ft("coin",Le,[]),Lt("coin"))}if(!e||"fx"===e){const e=await window.botApi.getPositionSummary({market:"fx"});e?.ok?(ct=Array.isArray(e.items)?e.items:[],Ft("fx",$e,ct),Lt("fx")):(qt("[fx] position summary fetch failed",{error:e?.error||"unknown error"}),ct=[],Ft("fx",$e,[]),Lt("fx"))}if(!e||"coin"===e){const e=await window.botApi.getOpenPositions({market:"coin",symbol:qe,page:1,count:100});e?.ok?Tt("coin",Ne,Array.isArray(e.items)?e.items:[]):(qt("[coin] open positions fetch failed",{error:e?.error||"unknown error"}),Tt("coin",Ne,[]))}if(!e||"fx"===e){const e=await window.botApi.getOpenPositions({market:"fx",count:100});e?.ok?Tt("fx",Ue,Array.isArray(e.items)?e.items:[]):(qt("[fx] open positions fetch failed",{error:e?.error||"unknown error"}),Tt("fx",Ue,[]))}}},zt=async(e,t)=>{const n=window.botApi;if(!n?.placeFxOrder)return;const i=String(ue.value||"").trim();i?await Gt(t,async()=>{const t=await n.placeFxOrder({symbol:Je,side:e,size:i});if(!t?.ok){const n=Yt(t?.error||"fx order failed");return qt("[fx] order failed",{symbol:Je,side:e,size:i,error:n}),Rt("error",n),void await Ht("fx.order",n,t)}qt("[fx] order placed",{symbol:Je,side:e,size:i,result:t.result||null}),Rt("success",`${e} 注文を送信しました。`),await Pt("fx"),await Dt("fx")}):Rt("error","数量を入力してください。")},Ot=async(e,t)=>{const n=window.botApi;if(!n?.placeCoinOrder)return;const i=bt(String(me.value||""));if(!i)return void Rt("error","数量の形式が不正です。");const o=((e,t)=>{const n=ot.get(e);if(!n)return{ok:!0};const i=bt(n.minOrderSize),o=bt(n.sizeStep),r=bt(n.maxOrderSize);if(!i||!o)return{ok:!0};const a=Math.max(wt(t),wt(i),wt(o),wt(r)),s=xt(t,a),c=xt(i,a),l=xt(o,a);if(s<c)return{ok:!1,reason:`${e} の最小数量は ${i} です。`};if(l>0n&&s%l!==0n)return{ok:!1,reason:`${e} の数量刻みは ${o} です。`};if(r&&s>xt(r,a))return{ok:!1,reason:`${e} の最大数量は ${r} です。`};return{ok:!0}})(qe,i);o.ok?await Gt(t,async()=>{const t=await n.placeCoinOrder({symbol:qe,side:e,size:i});if(!t?.ok){const n=Yt(t?.error||"coin order failed");return qt("[coin] order failed",{symbol:qe,side:e,size:i,error:n}),Rt("error",n),void await Ht("coin.order",n,t)}qt("[coin] order placed",{symbol:qe,side:e,size:i,result:t.result||null}),Rt("success",`${e} 注文を送信しました。`),await Pt("coin"),await Dt("coin")}):Rt("error",o.reason||"数量が取引ルールに一致しません。")},jt=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"},qt=(e,t=null,n=null)=>{if(window.botApi?.writeLog&&window.botApi.writeLog({level:jt(e),source:"ui.log",message:e,details:{data:t,ts:n||(new Date).toISOString()}}).catch(()=>{}),!L)return;const i=document.createElement("div");i.className="log-entry";const o=document.createElement("div");o.className="log-line";const r=document.createElement("span");if(r.className="log-ts",r.textContent=n||(new Date).toISOString(),o.appendChild(r),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(L.appendChild(i);L.children.length>300;)L.removeChild(L.firstChild);L.scrollTop=L.scrollHeight},Jt=(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}`,N.appendChild(i),N.scrollTop=N.scrollHeight},Yt=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:"处理失败,请稍后重试。"},Rt=(e,t)=>{Ye&&(clearTimeout(Ye),Ye=null),o.classList.remove("hidden","notice-error","notice-success"),o.classList.add("error"===e?"notice-error":"notice-success"),o.textContent=t,Ye=setTimeout(()=>{_t()},"success"===e?2200:4200)},Ht=async(e,t,n=null)=>{window.botApi?.writeErrorLog&&await window.botApi.writeErrorLog({source:e,message:String(t||""),details:n})},_t=()=>{Ye&&(clearTimeout(Ye),Ye=null),o.classList.add("hidden"),o.classList.remove("notice-error","notice-success"),o.textContent=""},Kt=(e,t)=>{Re&&(clearTimeout(Re),Re=null),n.classList.remove("hidden","notice-error","notice-success"),n.classList.add("error"===e?"notice-error":"notice-success"),n.textContent=t,Re=setTimeout(()=>{Wt()},"success"===e?2200:4200)},Wt=()=>{Re&&(clearTimeout(Re),Re=null),n.classList.add("hidden"),n.classList.remove("notice-error","notice-success"),n.textContent=""},Gt=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=Yt(e instanceof Error?e.message:String(e));qt("[ui] action failed",{error:t}),Rt("error",t)}finally{e.classList.remove("is-loading"),e.textContent=n,e.disabled=!1}},Vt=(e,t,n=[])=>{const i=e.getContext("2d");if(!i)return;const o=e.width,r=e.height;if(i.clearRect(0,0,o,r),!t.length)return;const a=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(a,...c):a,d=c.length?Math.min(s,...c):s,m=Math.max(1e-8,l-d),u=10,g=10,f=o-10-62-8,p=r-10-24-8,y=Math.max(2,f/t.length),b=e=>g+(l-e)/m*p,w=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,r-1),i.strokeStyle="#e3eaf5",i.lineWidth=1;for(let e=0;e<=4;e+=1){const t=g+p/4*e;i.beginPath(),i.moveTo(u,t),i.lineTo(u+f,t),i.stroke()}t.forEach((e,t)=>{const n=u+t*y+.5*y,o=b(e.high),r=b(e.low),a=b(e.open),s=b(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,r),i.stroke();const l=n-.3*y,d=Math.max(1,.6*y),m=Math.min(a,s),g=Math.max(1,Math.abs(s-a));i.fillRect(l,m,d,g)});for(const e of n){const t=b(e.price);i.save(),i.setLineDash([4,4]),i.strokeStyle=e.color,i.lineWidth=1,i.beginPath(),i.moveTo(u,t),i.lineTo(u+f,t),i.stroke(),i.restore(),i.font="12px Segoe UI";const n=`${e.label} ${w(e.price)}`,o=Math.ceil(i.measureText(n).width)+10,r=14,a=Math.max(12,Math.min(g+p-18,t-9));i.fillStyle=e.color,i.fillRect(r,a,o,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(n,r+5,a+9)}const x=t[t.length-1],k=x?.close;if(Number.isFinite(k)){const e=b(Number(k));i.save(),i.setLineDash([5,4]),i.strokeStyle="#2b79ff",i.lineWidth=1,i.beginPath(),i.moveTo(u,e),i.lineTo(u+f,e),i.stroke(),i.restore();const t=w(Number(k));i.font="12px Segoe UI";const n=Math.ceil(i.measureText(t).width)+10,o=u+f+6,r=Math.max(12,Math.min(g+p-18,e-9));i.fillStyle="#2b79ff",i.fillRect(o,r,n,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(t,o+5,r+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+p/4*e;i.fillText(w(t),u+f+8,n)}const h=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+f*e/4;i.fillText(h(t[n]?.time||0),o,g+p+6)}},Qt=(e,t)=>{const{tabBtn:n,titleNode:i,packageNode:o,enabledNode:r,versionNode:a,fallbackTitle:s,fallbackPackage:c}=t,l=e?.displayName||s,d=e?.name||c,m=!e||Boolean(e.enabled),u=String(e?.installStatus||"builtin").trim()||"builtin",g=String(e?.configuredVersion||e?.version||"builtin").trim()||"builtin";n&&(n.textContent=d.replace("@ai.weget.jp/",""),n.classList.toggle("is-disabled",!m)),i&&(i.textContent=l),o&&(o.textContent=d),r&&(r.textContent=m?"enabled":"disabled",r.classList.toggle("is-disabled",!m)),a&&(a.textContent=`${u} · ${g}`)},Xt=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;ut(e.status||"disconnected"),e.session?(mt(!0),d.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`):(mt(!1),d.textContent="local mode (not logged in)"),(e=>{m.innerHTML="";const t=lt(e),n=lt(t.runtime),i=t.session?lt(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=lt(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,m.appendChild(n),m.appendChild(i)}})(e);const n=t?.ok&&t.skills||[];(e=>{if(!u)return;u.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 u.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 r=document.createElement("code");if(r.textContent=e.name,i.appendChild(o),i.appendChild(r),e.description){const t=document.createElement("div");t.className="muted",t.textContent=e.description,i.appendChild(t)}const a=document.createElement("div");a.className="skill-badges";const s=document.createElement("span");s.className="skill-badge "+(e.enabled?"enabled":"disabled"),s.textContent=e.enabled?"enabled":"disabled",a.appendChild(s);const c=document.createElement("span");c.className="skill-badge status",c.textContent=e.installStatus||"unknown",a.appendChild(c),n.appendChild(i),n.appendChild(a),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 g=l("Tools"),f=document.createElement("div");f.className="skill-tags";const p=Array.isArray(e.tools)?e.tools:[];if(p.length>0)for(const e of p){const t=document.createElement("span");t.className="skill-tag",t.textContent=e,f.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No tools declared",f.appendChild(e)}g.appendChild(f),t.appendChild(g);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 b=l("State"),w=document.createElement("div");w.className="skill-meta";const x=[["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 x){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),w.appendChild(n)}b.appendChild(w),t.appendChild(b),u.appendChild(t)}})(n),Qt(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{tabBtn:g,titleNode:y,packageNode:x,enabledNode:S,versionNode:C,fallbackTitle:"GMO Coin Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-coin"}),Qt(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{tabBtn:f,titleNode:b,packageNode:k,enabledNode:v,versionNode:I,fallbackTitle:"GMO FX Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-fx"});const i=n.find(e=>"@ai.weget.jp/skill-browser"===e.name);Qt(i,{tabBtn:p,titleNode:w,packageNode:h,enabledNode:E,versionNode:A,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)}}})(B,Array.isArray(i?.tools)?i.tools:[],"No browser tools declared.")};J.addEventListener("click",async()=>{await Gt(J,async()=>{if(!window.botApi?.saveFxApiConfig||!window.botApi.saveCoinApiConfig)return;const e={riskDailyLossLimitJpy:Number(T.value||"0"),errorLogDir:String(P.value||"").trim()},t={...e,fxApiKey:String(z.value||"").trim(),fxApiSecret:String(O.value||"").trim()},n={...e,cryptoApiKey:String(F.value||"").trim(),cryptoApiSecret:String(D.value||"").trim()},[i,o]=await Promise.all([window.botApi.saveFxApiConfig(t),window.botApi.saveCoinApiConfig(n)]);return i.ok?o.ok?(qt("[trade-config] saved"),Rt("success","配置已保存到本机。"),void await Xt()):(qt(`[trade-config] coin save failed: ${o.error||"unknown"}`),Rt("error",Yt(o.error)),void await Ht("trade-config.coin.save",Yt(o.error),o)):(qt(`[trade-config] fx save failed: ${i.error||"unknown"}`),Rt("error",Yt(i.error)),void await Ht("trade-config.fx.save",Yt(i.error),i))})}),R.addEventListener("click",async()=>{await Gt(R,async()=>{if(!window.botApi?.saveAiConfig)return;const e={aiModel:String(Y.value||"gpt-4.1-mini").trim()||"gpt-4.1-mini"},t=await window.botApi.saveAiConfig(e);if(!t.ok)return qt(`[ai-config] save failed: ${t.error||"unknown"}`),Rt("error",Yt(t.error)),void await Ht("ai-config.save",Yt(t.error),t);qt("[ai-config] saved"),Rt("success","AI 设置已保存。"),await Xt()})}),H.addEventListener("click",async()=>{await Gt(H,async()=>{if(!window.botApi?.startCodexLogin)return;const e=await window.botApi.startCodexLogin();if(!e.ok)return qt(`[codex] login launch failed: ${e.detail||"unknown"}`),Rt("error",Yt(e.detail)),void await Ht("codex.login.launch",Yt(e.detail),e);qt("[codex] login launched",{detail:e.detail}),Rt("success",e.detail||"Codex login started."),setTimeout(()=>{Xt()},1500)})}),_.addEventListener("click",async()=>{const e=String(W.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 Rt("error","无法复制命令,请手动执行。");qt("[codex] login command copied"),Rt("success","Codex 登录命令已复制。")}catch(e){const t=Yt(e instanceof Error?e.message:String(e));qt("[codex] copy login command failed",{error:t}),Rt("error",t)}}),K.addEventListener("click",async()=>{await Gt(K,async()=>{await Xt(),qt("[codex] auth status refreshed"),Rt("success","Codex 状态已刷新。")})}),j.addEventListener("click",()=>{const e="password"===D.type?"text":"password";D.type=e,j.textContent="password"===e?"显示":"隐藏"}),q.addEventListener("click",()=>{const e="password"===O.type?"text":"password";O.type=e,q.textContent="password"===e?"显示":"隐藏"}),e.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi)return;Wt();const t=a.value.trim(),n=r.value.trim(),i=s.value;if(t&&n&&i){V.disabled=!0,V.textContent="Loading...";try{const e=await window.botApi.login(t,i,n,c.checked);if(!e.ok)return qt(`[ui] login failed: ${e.error||"unknown"}`),void Kt("error",Yt(e.error));qt("[ui] login success"),Kt("success","登录成功。"),Rt("success","登录成功。"),await Xt()}finally{V.disabled=!1,V.textContent="Login"}}}),G.addEventListener("click",async()=>{await Gt(G,async()=>{if(!window.botApi)return;const e=await window.botApi.logout();if(!e.ok)return qt(`[ui] logout failed: ${e.error||"unknown"}`),Rt("error",Yt(e.error)),void await Ht("auth.logout",Yt(e.error),e);qt("[ui] logout"),Rt("success","已登出。"),mt(!1),d.textContent="local mode (not logged in)",await Xt()})});const Zt=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 qt(`[trade] open ${e} kline failed: ${o.error||"unknown"}`),Rt("error",Yt(o.error)),void await Ht(`${e}.kline`,Yt(o.error),o);const r=Array.isArray(o.candles)?o.candles:[],a=Ct(e);a.symbol=String(o.symbol||t||"").toUpperCase(),a.interval=n,a.candles=r.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)})),a.lastFetchBucket=Math.floor(Date.now()/Et(n)),Vt(i,a.candles,Bt(e)),qt("[trade] kline rendered",{market:e,symbol:o.symbol||t,interval:o.interval||n,candles:r.length}),_t()},en=async()=>{await Zt({market:"coin",symbol:qe,interval:Oe,canvas:te})},tn=async()=>{await Zt({market:"fx",symbol:Je,interval:je,canvas:ne})},nn=({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 r=String(e.dataset.interval||"").trim();if(r&&r!==t()){n(r);for(const t of o)t.classList.toggle("is-active",t===e);await i()}})};Q.addEventListener("change",async()=>{qe=String(Q.value||"BTC_JPY").trim().toUpperCase(),vt("coin",qe);try{await St("coin",qe),await en(),await Dt("coin")}catch(e){const t=Yt(e instanceof Error?e.message:String(e));Rt("error",t)}}),X.addEventListener("change",async()=>{Je=String(X.value||"USD_JPY").trim().toUpperCase(),vt("fx",Je);try{await St("fx",Je),await tn(),await Dt("fx")}catch(e){const t=Yt(e instanceof Error?e.message:String(e));Rt("error",t)}}),nn({container:Z,getCurrent:()=>Oe,setCurrent:e=>{Oe=e},onChange:en}),nn({container:ee,getCurrent:()=>je,setCurrent:e=>{je=e},onChange:tn}),xe.addEventListener("click",async()=>{await Gt(xe,async()=>{await Pt("coin")})}),ke.addEventListener("click",async()=>{await Gt(ke,async()=>{await Pt("fx")})}),me.addEventListener("input",()=>{ht("coin")}),ue.addEventListener("input",()=>{ht("fx")}),Ne.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(),r="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",a="BUY"===r?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=bt(String(n.dataset.size||""));o&&Number.isFinite(s)&&!(s<=0)&&c?await Gt(n,async()=>{const e=await i.closeCoinPosition({symbol:o,side:a,positionId:s,size:c});if(!e?.ok){const t=Yt(e?.error||"coin close order failed");return qt("[coin] close order failed",{symbol:o,positionSide:r,closeSide:a,positionId:s,size:c,error:t}),Rt("error",t),void await Ht("coin.closeOrder",t,e)}qt("[coin] close order placed",{symbol:o,positionSide:r,closeSide:a,positionId:s,size:c,result:e.result||null}),Rt("success","決済注文を送信しました。"),await Pt("coin"),await Dt("coin")}):Rt("error","決済対象データが不正です。")}),Ue.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(),r="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",a="BUY"===r?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=String(n.dataset.size||"").trim();o&&Number.isFinite(s)&&!(s<=0)&&c?await Gt(n,async()=>{const e=await i.closeFxPosition({symbol:o,side:a,positionId:s,size:c});if(!e?.ok){const t=Yt(e?.error||"fx close order failed");return qt("[fx] close order failed",{symbol:o,positionSide:r,closeSide:a,positionId:s,size:c,error:t}),Rt("error",t),void await Ht("fx.closeOrder",t,e)}qt("[fx] close order placed",{symbol:o,positionSide:r,closeSide:a,positionId:s,size:c,result:e.result||null}),Rt("success","決済注文を送信しました。"),await Pt("fx"),await Dt("fx")}):Rt("error","決済対象データが不正です。")}),ge.addEventListener("click",async()=>{await Ot("BUY",ge)}),fe.addEventListener("click",async()=>{await Ot("SELL",fe)}),pe.addEventListener("click",async()=>{await zt("BUY",pe)}),ye.addEventListener("click",async()=>{await zt("SELL",ye)}),Me.addEventListener("click",async()=>{await Gt(Me,async()=>{await Dt("coin")})}),Pe.addEventListener("click",async()=>{await Gt(Pe,async()=>{await Dt("coin")})}),Te.addEventListener("click",async()=>{await Gt(Te,async()=>{await Dt("fx")})}),Fe.addEventListener("click",async()=>{await Gt(Fe,async()=>{await Dt("fx")})}),$.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi?.sendChat)return;const t=String(U.value||"").trim();if(t){Jt("user",t),U.value="",M.disabled=!0;try{const e=await window.botApi.sendChat(t);if(!e.ok)return Jt("assistant",`Error: ${e.error||"unknown"}`),Rt("error",Yt(e.error)),void await Ht("ai.chat",Yt(e.error),e);Jt("assistant",String(e.answer||"")),_t()}catch(e){const t=Yt(e instanceof Error?e.message:String(e));Jt("assistant",`Error: ${t}`),Rt("error",t),await Ht("ai.chat.exception",t,e)}finally{M.disabled=!1}}}),window.addEventListener("error",e=>{const t=Yt(e.error?.message||e.message||"Unknown UI error");qt("[ui] uncaught error",{error:t}),Rt("error",t),Ht("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=Yt(t);qt("[ui] unhandled rejection",{error:n}),Rt("error",n),Ht("ui.unhandledrejection",n,e.reason)}),window.botApi&&(window.botApi.onStatus(e=>{ut(String(e.status||""))}),window.botApi.onChatEvent(e=>{if(!e)return;const t=lt(e);if("line_user"===t.type){const e=t.lineUserId?`LINE(${String(t.lineUserId)})`:"LINE";return void Jt("line-user",String(t.text||""),e)}if("line_assistant"===t.type){const e=t.lineUserId?`AI->LINE(${String(t.lineUserId)})`:"AI->LINE";Jt("line-assistant",String(t.text||""),e)}}),window.botApi.onTradeExecution(e=>{if(!e)return;const t=lt(e),n="coin"===String(t.market||"").trim().toLowerCase()?"coin":"fx",i=String(t.symbol||"").trim().toUpperCase(),o=String(t.side||"").trim().toUpperCase()||"-",r=String(t.settleType||"").trim().toUpperCase()||"OPEN",a=String(t.executionSize??"").trim(),s=String(t.executionPrice??"").trim(),c="CLOSE"===r?"決済":"新規",l=`${i} ${o} ${a}${s?` @ ${s}`:""}`;qt(`[${n}] execution filled`,{symbol:i,side:o,settleType:r,size:a,price:s,raw:t}),Rt("success",`約定成功 (${c}) ${l}`.trim()),Pt(n).catch(()=>{}),Dt(n).catch(()=>{})}));const on=async()=>{if(Ke&&!We&&!Ge){Ge=!0;try{await async function(){vt("coin",qe),vt("fx",Je);try{await St("coin",qe)}catch(e){const t=Yt(e instanceof Error?e.message:String(e));qt("[coin] quote fetch failed",{error:t}),Rt("error",t)}try{await St("fx",Je)}catch(e){const t=Yt(e instanceof Error?e.message:String(e));qt("[fx] quote fetch failed",{error:t}),Rt("error",t)}if(await en(),await tn(),window.botApi?.getSymbolRules)try{const e=await window.botApi.getSymbolRules({market:"coin"});if(e?.ok&&Array.isArray(e.rules)){ot.clear();for(const t of e.rules){const e=String(t.symbol||"").trim().toUpperCase();e&&ot.set(e,{minOrderSize:String(t.minOrderSize||""),maxOrderSize:String(t.maxOrderSize||""),sizeStep:String(t.sizeStep||"")})}}}catch(e){qt("[coin] symbol rules fetch failed",{error:e instanceof Error?e.message:String(e)})}}(),await Pt(),await Dt(),He||(He=setInterval(()=>{Ut("coin")},1e3)),_e||(_e=setInterval(()=>{Ut("fx")},1e3)),We=!0}finally{Ge=!1}}};mt(!1),(async()=>{if(!window.botApi?.getSavedProfile)return;const e=await window.botApi.getSavedProfile();e?.ok&&e.profile&&(r.value=e.profile.botId||"",a.value=e.profile.email||"",s.value=e.profile.password||"",c.checked=Boolean(e.profile.remember))})(),(async()=>{if(!window.botApi?.getFxApiConfig||!window.botApi.getCoinApiConfig||!window.botApi.getAiConfig)return;const[e,t,n]=await Promise.all([window.botApi.getFxApiConfig(),window.botApi.getCoinApiConfig(),window.botApi.getAiConfig()]);e?.ok&&e.config&&(P.value=e.config.errorLogDir||"",T.value=String(e.config.riskDailyLossLimitJpy||5e4),z.value=e.config.fxApiKey||"",O.value=e.config.fxApiSecret||""),t?.ok&&t.config&&(P.value||(P.value=t.config.errorLogDir||""),T.value||(T.value=String(t.config.riskDailyLossLimitJpy||5e4)),F.value=t.config.cryptoApiKey||"",D.value=t.config.cryptoApiSecret||""),n?.ok&&n.config&&(Y.value=n.config.aiModel||"gpt-4.1-mini")})(),Xt();export{};