@ai.weget.jp/bot 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/renderer/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=document.getElementById("login-form"),t=document.getElementById("login-screen"),n=document.getElementById("login-notice"),i=document.getElementById("app-shell"),o=document.getElementById("sidebar-nav"),a=document.getElementById("sidebar-toggle-btn"),r=document.getElementById("user-notice"),s=document.getElementById("bot-id"),c=document.getElementById("email"),l=document.getElementById("password"),d=document.getElementById("remember-me"),m=document.getElementById("status"),u=document.getElementById("session"),g=document.getElementById("runtime-info"),p=document.getElementById("skill-state-list"),f=document.getElementById("tab-btn-skill-gmo-coin"),y=document.getElementById("tab-btn-skill-gmo-fx"),w=document.getElementById("tab-btn-skill-browser"),b=document.getElementById("tab-btn-skill-macro-economy"),k=document.getElementById("coin-skill-title"),h=document.getElementById("fx-skill-title"),x=document.getElementById("browser-skill-title"),v=document.getElementById("macro-skill-title"),S=document.getElementById("coin-skill-package"),E=document.getElementById("fx-skill-package"),C=document.getElementById("browser-skill-package"),I=document.getElementById("macro-skill-package"),A=document.getElementById("coin-skill-enabled"),N=document.getElementById("fx-skill-enabled"),L=document.getElementById("browser-skill-enabled"),B=document.getElementById("macro-skill-enabled"),$=document.getElementById("coin-skill-version"),M=document.getElementById("fx-skill-version"),T=document.getElementById("browser-skill-version"),P=document.getElementById("macro-skill-version"),D=document.getElementById("browser-skill-cards"),U=document.getElementById("browser-skill-tools"),F=document.getElementById("macro-snapshot-as-of"),j=document.getElementById("macro-fear-greed-score"),_=document.getElementById("macro-snapshot-hint"),z=document.getElementById("macro-coin-cards"),O=document.getElementById("macro-fx-cards"),H=document.getElementById("macro-coin-heatmap-treemap"),J=document.getElementById("macro-fx-heatmap-treemap"),q=document.getElementById("macro-calendar-body"),G=document.getElementById("macro-news-body"),K=document.getElementById("macro-date-key"),Y=document.getElementById("macro-refresh-snapshot-btn"),R=document.getElementById("macro-refresh-day-btn"),W=document.getElementById("browser-chromium-status"),X=document.getElementById("browser-chromium-detail"),V=document.getElementById("browser-prereq-hint"),Q=document.getElementById("browser-refresh-playwright-mcp-btn"),Z=document.getElementById("logs"),ee=document.getElementById("gateway-codex-auth"),te=document.getElementById("gateway-mcp-status"),ne=document.getElementById("gateway-browser-status"),ie=document.getElementById("gateway-context-file"),oe=document.getElementById("gateway-detail"),ae=document.getElementById("gateway-install-btn"),re=document.getElementById("gateway-refresh-btn"),se=document.getElementById("gateway-skill-grid"),ce=document.getElementById("messages"),le=document.getElementById("active-tasks-list"),de=document.getElementById("chat-form"),me=document.getElementById("chat-input"),ue=document.getElementById("send-btn"),ge=document.getElementById("fx-risk-daily-loss-limit-jpy"),pe=document.getElementById("coin-risk-daily-loss-limit-jpy"),fe=document.getElementById("crypto-api-key"),ye=document.getElementById("crypto-api-secret"),we=document.getElementById("fx-api-key"),be=document.getElementById("fx-api-secret"),ke=document.getElementById("toggle-crypto-key-btn"),he=document.getElementById("toggle-crypto-secret-btn"),xe=document.getElementById("toggle-fx-key-btn"),ve=document.getElementById("toggle-fx-secret-btn"),Se=document.getElementById("save-fx-config-btn"),Ee=document.getElementById("save-coin-config-btn"),Ce=document.getElementById("fx-config-title"),Ie=document.getElementById("coin-config-title"),Ae=document.getElementById("fx-config-label-riskDailyLossLimitJpy"),Ne=document.getElementById("fx-config-label-fxApiKey"),Le=document.getElementById("fx-config-label-fxApiSecret"),Be=document.getElementById("coin-config-label-riskDailyLossLimitJpy"),$e=document.getElementById("coin-config-label-cryptoApiKey"),Me=document.getElementById("coin-config-label-cryptoApiSecret"),Te=document.getElementById("ai-model"),Pe=document.getElementById("host-log-output-dir"),De=document.getElementById("save-ai-config-btn"),Ue=document.getElementById("codex-login-btn"),Fe=document.getElementById("codex-copy-login-btn"),je=document.getElementById("codex-refresh-auth-btn"),_e=document.getElementById("codex-login-command"),ze=document.getElementById("logout-btn"),Oe=document.getElementById("login-btn"),He=document.getElementById("coin-symbol-select"),Je=document.getElementById("fx-symbol-select"),qe=document.getElementById("coin-kline-intervals"),Ge=document.getElementById("fx-kline-intervals"),Ke=document.getElementById("coin-kline-canvas"),Ye=document.getElementById("fx-kline-canvas"),Re=document.getElementById("coin-market-icon"),We=document.getElementById("fx-market-icon"),Xe=document.getElementById("coin-order-bid"),Ve=document.getElementById("coin-order-ask"),Qe=document.getElementById("coin-order-spread"),Ze=document.getElementById("fx-order-bid"),et=document.getElementById("fx-order-ask"),tt=document.getElementById("fx-order-spread"),nt=document.getElementById("coin-order-qty"),it=document.getElementById("fx-order-qty"),ot=document.getElementById("coin-buy-btn"),at=document.getElementById("coin-sell-btn"),rt=document.getElementById("fx-buy-btn"),st=document.getElementById("fx-sell-btn"),ct=document.getElementById("coin-required-amount"),lt=document.getElementById("fx-required-amount"),dt=document.getElementById("coin-account-info-refresh-btn"),mt=document.getElementById("fx-account-info-refresh-btn"),ut=document.getElementById("coin-account-pnl"),gt=document.getElementById("coin-account-margin"),pt=document.getElementById("coin-account-available"),ft=document.getElementById("coin-account-margin-ratio"),yt=document.getElementById("fx-account-pnl"),wt=document.getElementById("fx-account-margin"),bt=document.getElementById("fx-account-available"),kt=document.getElementById("fx-account-margin-ratio"),ht=document.getElementById("coin-position-summary-body"),xt=document.getElementById("coin-position-list-body"),vt=document.getElementById("fx-position-summary-body"),St=document.getElementById("fx-position-list-body"),Et=document.getElementById("coin-position-summary-refresh-btn"),Ct=document.getElementById("coin-position-list-refresh-btn"),It=document.getElementById("fx-position-summary-refresh-btn"),At=document.getElementById("fx-position-list-refresh-btn"),Nt=Array.from(document.querySelectorAll(".tab-btn")),Lt=Array.from(document.querySelectorAll(".tab-panel"));let Bt="15m",$t="15m",Mt=String(He?.value||"BTC_JPY").trim().toUpperCase(),Tt=String(Je?.value||"USD_JPY").trim().toUpperCase(),Pt=null,Dt=null,Ut=null,Ft=null,jt=!1,_t=!1,zt=!1,Ot=!1,Ht=!1,Jt="",qt="",Gt=0,Kt=0,Yt=null,Rt=null,Wt=[],Xt=!1,Vt=null,Qt={gatewayMcpStatus:"unknown",chromiumStatus:"unknown"};const Zt=new Map,en={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1},tn={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1};let nn=[],on=[];const an=new Map;let rn=null;const sn=e=>e&&"object"==typeof e?e:{},cn="weget.bot.sidebar.collapsed",ln=e=>{i.classList.toggle("sidebar-collapsed",e),a&&(a.textContent=e?"▶":"◀",a.setAttribute("aria-label",e?"Expand menu":"Collapse menu")),o&&o.setAttribute("data-collapsed",e?"true":"false")},dn=e=>{for(const t of Nt)t.classList.toggle("is-active",t.dataset.tab===e);for(const t of Lt)t.classList.toggle("is-active",t.id===`tab-${e}`)};for(const e of Nt)e.addEventListener("click",()=>dn(e.dataset.tab||"coin"));a?.addEventListener("click",()=>{const e=!i.classList.contains("sidebar-collapsed");ln(e);try{window.localStorage.setItem(cn,String(e))}catch{}});const mn=e=>{const n=jt!==e;jt=e,t.classList.toggle("hidden",e),i.classList.toggle("hidden",!e),e&&di(),e?n&&Mi():(Rn(),_t=!1)},un=e=>{const t=String(e||"disconnected").trim().toLowerCase();m.textContent=t,m.classList.remove("status-connected","status-disconnected","status-connecting"),"connected"!==t?"reconnecting"!==t&&"connecting"!==t?m.classList.add("status-disconnected"):m.classList.add("status-connecting"):m.classList.add("status-connected")},gn=(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})},pn=e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,fn=e=>`${e>0?"+":""}${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,yn=e=>!Number.isFinite(e)||e<=0||e>1e5?"- %":(e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:2})} %`)(e),wn=e=>{const t=e instanceof Date?e:new Date(e);if(Number.isNaN(t.getTime()))return"";return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`},bn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t);return Number.isNaN(n.getTime())?t:n.toLocaleString("ja-JP",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})},kn=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),hn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t).getTime();if(!Number.isFinite(n))return t;const i=Math.max(0,Math.floor((Date.now()-n)/1e3));if(i<5)return"just now";if(i<60)return`${i}s ago`;const o=Math.floor(i/60);if(o<60)return`${o}m ago`;const a=Math.floor(o/60);return a<24?`${a}h ago`:`${Math.floor(a/24)}d ago`},xn=()=>{if(!le)return;const e=Array.from(an.values()).sort((e,t)=>{const n=new Date(String(e.lastProgressAt||e.startedAt||0)).getTime();return new Date(String(t.lastProgressAt||t.startedAt||0)).getTime()-n});e.length?le.innerHTML=e.map(e=>{const t=kn(e.title||e.prompt||e.taskId),n=kn(e.status||"Running"),i=kn(e.source||"-"),o=kn(e.channel||"-"),a=kn(hn(e.startedAt)),r=kn(hn(e.lastProgressAt)),s=kn(e.conversationId||"-"),c=kn(e.lineUserId||"-");return`\n <section class="active-task-card" data-task-id="${kn(e.taskId)}">\n <div class="active-task-meta">\n <span class="active-task-badge">${o}</span>\n <span class="active-task-badge">${i}</span>\n </div>\n <div class="active-task-title">${t}</div>\n <div class="active-task-status">${n}</div>\n <div class="active-task-subtext">Started ${a} · Last update ${r}</div>\n <div class="active-task-subtext">Task ${kn(e.taskId)} · Conv ${s}</div>\n ${e.lineUserId?`<div class="active-task-subtext">LINE user ${c}</div>`:""}\n <div class="active-task-actions">\n <button type="button" class="secondary active-task-cancel-btn" data-task-id="${kn(e.taskId)}" ${!1===e.canCancel?"disabled":""}>Cancel</button>\n </div>\n </section>\n `}).join(""):le.innerHTML='<div class="active-task-empty">No active tasks.</div>'},vn=e=>{if(e)if("snapshot"!==e.type){if("upsert"===e.type&&e.task?.taskId)return an.set(e.task.taskId,e.task),void xn();"remove"===e.type&&e.taskId&&(an.delete(e.taskId),xn())}else{an.clear();for(const t of Array.isArray(e.tasks)?e.tasks:[])t?.taskId&&an.set(t.taskId,t);xn()}},Sn=e=>{const t=String(e||"").trim();if(!t)return{cleaned:"",impact:"",direction:""};const n=t.indexOf("\n"),i=n>=0?t.slice(0,n).trim():t,o=n>=0?t.slice(n+1).trim():"";if(i.startsWith("{")&&i.endsWith("}"))try{const e=JSON.parse(i);return{cleaned:o,impact:String(e.impact||"").trim().toLowerCase(),direction:String(e.usd_jpy_direction||"").trim().toLowerCase()}}catch{}return{cleaned:t,impact:"",direction:""}},En=e=>{const t=String(e||"").trim().toLowerCase();if(!t)return"-";const n="high"===t?"High":"medium"===t?"Medium":"low"===t?"Low":t;return`<span class="macro-impact-badge ${kn(t)}">${kn(n)}</span>`},Cn=e=>{const t=Math.max(0,Math.min(3,Math.round(Number(e||0))));return t<=0?"-":`<span class="macro-importance-stars">${"★".repeat(t)}</span>`},In=e=>{const t=String(e||"").trim().toLowerCase();return t?"up"===t?'<span class="macro-direction up">↑↑</span>':"down"===t?'<span class="macro-direction down">↓↓</span>':"sideways"===t?'<span class="macro-direction flat">→</span>':kn(t):"-"},An=e=>{const t=String(e||"").split(/\r?\n/).map(e=>e.trim()).filter(Boolean);return t.length?t.map(e=>`<div class="macro-analyzer-line">${kn(e)}</div>`).join(""):"-"},Nn=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(/\.$/,"")},Ln=e=>{const t=String(e||"").trim(),n=t.indexOf(".");return n>=0?t.length-n-1:0},Bn=(e,t)=>{const n=String(e||"").trim(),i=n.startsWith("-"),o=i?n.slice(1):n,[a,r=""]=o.split("."),s=(r+"0".repeat(t)).slice(0,t),c=BigInt((a||"0")+s);return i?-c:c},$n=e=>{const t=Pn("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),n=sn(t?.configJson);return"fx"===e?Boolean(String(n.fxApiKey||we.value||"").trim()&&String(n.fxApiSecret||be.value||"").trim()):Boolean(String(n.cryptoApiKey||fe.value||"").trim()&&String(n.cryptoApiSecret||ye.value||"").trim())},Mn=({symbol:e,quote:t,orderBidNode:n,orderAskNode:i,orderSpreadNode:o})=>{if(!t)return n.textContent="-",i.textContent="-",void(o.textContent="-");const a=gn(t.bid),r=gn(t.ask);n.textContent=a,i.textContent=r,o.textContent=gn(t.spread,8)},Tn=e=>{if("coin"===e){const e=Number(String(nt.value||"").trim()),t=Yt;return!Number.isFinite(e)||e<=0||null===t||!Number.isFinite(t)?void(ct.textContent="- 円"):void(ct.textContent=pn(e*t/2))}const t=Number(String(it.value||"").trim()),n=Rt;!Number.isFinite(t)||t<=0||null===n||!Number.isFinite(n)?lt.textContent="- 円":lt.textContent=pn(t*n/20)},Pn=e=>Wt.find(t=>t.name===e),Dn=e=>{const t=Pn(e);if(!t)return!1;const n=String(t.installStatus||"").trim().toLowerCase();return t.enabled&&"active"===n},Un=e=>Dn("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),Fn=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?(Mn({symbol:n,quote:o,orderBidNode:Xe,orderAskNode:Ve,orderSpreadNode:Qe}),Yt=o?.ask??null,Tn("coin")):(Mn({symbol:n,quote:o,orderBidNode:Ze,orderAskNode:et,orderSpreadNode:tt}),Rt=o?.ask??null,Tn("fx")),o},jn=(e,t)=>{const n=String(t||"").trim().toUpperCase();if(!n)return;if("coin"===e){const e=n.replace("_","-").toLowerCase();return void(Re.src=`https://coin.z.com/jp/member/imgs/icon-${e}.svg`)}const[i,o]=n.split("_"),a="JPY"===o?i:`${i}_${o}`;We.src=`https://coin.z.com/jp/member/imgs/fx/icon_${a}.svg`},_n=e=>"5m"===e?3e5:"15m"===e?9e5:"30m"===e?18e5:"1h"===e?36e5:6e4,zn=e=>"coin"===e?en:tn,On=e=>"coin"===e?Ke:Ye,Hn=e=>"coin"===e?Mt:Tt,Jn=e=>{const t=Hn(e),n=("coin"===e?nn:on).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},qn=e=>{const t=zn(e);t.candles.length&&ui(On(e),t.candles,Jn(e))},Gn=(e,t)=>{if(!t)return;const n=zn(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),ui(On(e),n.candles,Jn(e)))},Kn=async e=>{const t=zn(e);if(!t.interval||!t.symbol||t.fetching)return;if(t.symbol!==Hn(e))return;if(!(Math.floor(Date.now()/_n(t.interval))<=t.lastFetchBucket)){t.fetching=!0;try{"coin"===e?await Li():await Bi()}finally{t.fetching=!1}}},Yn=async e=>{if(Un(e))if("coin"!==e){if(!Ht){Ht=!0;try{const e=await Fn("fx",Tt);Gn("fx",e),await Kn("fx")}catch(e){const t=ai(e instanceof Error?e.message:String(e)),n=Date.now();(t!==qt||n-Kt>15e3)&&(ni("[fx] quote poll failed",{error:t}),qt=t,Kt=n)}finally{Ht=!1}}}else{if(Ot)return;Ot=!0;try{const e=await Fn("coin",Mt);Gn("coin",e),await Kn("coin")}catch(e){const t=ai(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Jt||n-Gt>15e3)&&(ni("[coin] quote poll failed",{error:t}),Jt=t,Gt=n)}finally{Ot=!1}}},Rn=()=>{Ut&&(clearInterval(Ut),Ut=null),Ft&&(clearInterval(Ft),Ft=null)},Wn=async e=>{if(window.botApi?.getAccountMetrics){if((!e||"coin"===e)&&Un("coin"))if($n("coin")){const e=await window.botApi.getAccountMetrics({market:"coin"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);ut.textContent=n>0?fn(i):pn(0),gt.textContent=pn(n),pt.textContent=pn(t),ft.textContent=yn(Number(e.marginRatio||0))}else ni("[coin] account metrics fetch failed",{error:e?.error||"unknown error"})}else ut.textContent=pn(0),gt.textContent="- 円",pt.textContent="- 円",ft.textContent="- %";if((!e||"fx"===e)&&Un("fx"))if($n("fx")){const e=await window.botApi.getAccountMetrics({market:"fx"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);yt.textContent=n>0?fn(i):pn(0),wt.textContent=pn(n),bt.textContent=pn(t),kt.textContent=yn(Number(e.marginRatio||0))}else ni("[fx] account metrics fetch failed",{error:e?.error||"unknown error"})}else yt.textContent=pn(0),wt.textContent="- 円",bt.textContent="- 円",kt.textContent="- %"}},Xn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").toUpperCase(),o=n.replace("_","/"),a=String(i.side||"-"),r=String(i.size||"").trim(),s=Number(r||0),c=Number(i.price||0),l=Number(i.lossGain||0),d=Number(i.totalSwap||0),m=String(i.timestamp||"-"),u=Number(i.positionId||0),g=Number.isFinite(u)&&u>0&&r?`data-market="${e}" data-symbol="${n}" data-side="${String(a||"").toUpperCase()}" data-position-id="${u}" data-size="${r}"`:"disabled",p=document.createElement("tr");p.innerHTML=`\n <td><button type="button" class="close-btn" ${g}>決済</button></td>\n <td>${o}<br />${a}</td>\n <td>${s.toLocaleString("ja-JP")}</td>\n <td>${c.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${fn(l)}<br />${fn(d)}</td>\n <td>${m.replace("T"," ").slice(0,19)}</td>\n `,t.appendChild(p)}}else t.innerHTML='<tr><td colspan="6" class="empty-cell">対象のお取引はございません。</td></tr>'},Vn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").replace("_","/"),o=String(i.side||"-"),a=Number("fx"===e?i.sumPositionSize||0:i.sumPositionQuantity||0),r=Number(i.averagePositionRate||0),s=Number(i.positionLossGain||0),c=Number("fx"===e&&i.sumTotalSwap||0),l=document.createElement("tr");l.innerHTML=`\n <td><button type="button" class="close-btn">決済</button></td>\n <td>${n}<br />${o}</td>\n <td>${a.toLocaleString("ja-JP")}</td>\n <td>${r.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${fn(s)}<br />${fn(c)}</td>\n `,t.appendChild(l)}}else t.innerHTML='<tr><td colspan="5" class="empty-cell">対象のお取引はございません。</td></tr>'},Qn=async e=>{if(window.botApi?.getPositionSummary&&window.botApi?.getOpenPositions){if((!e||"coin"===e)&&Un("coin"))if($n("coin")){const e=await window.botApi.getPositionSummary({market:"coin",symbol:Mt});e?.ok?(nn=Array.isArray(e.items)?e.items:[],Vn("coin",ht,nn),qn("coin")):(ni("[coin] position summary fetch failed",{error:e?.error||"unknown error"}),nn=[],Vn("coin",ht,[]),qn("coin"))}else nn=[],Vn("coin",ht,[]),qn("coin");if((!e||"fx"===e)&&Un("fx"))if($n("fx")){const e=await window.botApi.getPositionSummary({market:"fx"});e?.ok?(on=Array.isArray(e.items)?e.items:[],Vn("fx",vt,on),qn("fx")):(ni("[fx] position summary fetch failed",{error:e?.error||"unknown error"}),on=[],Vn("fx",vt,[]),qn("fx"))}else on=[],Vn("fx",vt,[]),qn("fx");if((!e||"coin"===e)&&Un("coin"))if($n("coin")){const e=await window.botApi.getOpenPositions({market:"coin",symbol:Mt,page:1,count:100});e?.ok?Xn("coin",xt,Array.isArray(e.items)?e.items:[]):(ni("[coin] open positions fetch failed",{error:e?.error||"unknown error"}),Xn("coin",xt,[]))}else Xn("coin",xt,[]);if((!e||"fx"===e)&&Un("fx"))if($n("fx")){const e=await window.botApi.getOpenPositions({market:"fx",count:100});e?.ok?Xn("fx",St,Array.isArray(e.items)?e.items:[]):(ni("[fx] open positions fetch failed",{error:e?.error||"unknown error"}),Xn("fx",St,[]))}else Xn("fx",St,[])}},Zn=async(e,t)=>{const n=window.botApi;if(!n?.placeFxOrder)return;const i=String(it.value||"").trim();i?await mi(t,async()=>{const t=await n.placeFxOrder({symbol:Tt,side:e,size:i});if(!t?.ok){const n=ai(t?.error||"fx order failed");return ni("[fx] order failed",{symbol:Tt,side:e,size:i,error:n}),ri("error",n),void await si("fx.order",n,t)}ni("[fx] order placed",{symbol:Tt,side:e,size:i,result:t.result||null}),ri("success",`${e} 注文を送信しました。`),await Wn("fx"),await Qn("fx")}):ri("error","数量を入力してください。")},ei=async(e,t)=>{const n=window.botApi;if(!n?.placeCoinOrder)return;const i=Nn(String(nt.value||""));if(!i)return void ri("error","数量の形式が不正です。");const o=((e,t)=>{const n=Zt.get(e);if(!n)return{ok:!0};const i=Nn(n.minOrderSize),o=Nn(n.sizeStep),a=Nn(n.maxOrderSize);if(!i||!o)return{ok:!0};const r=Math.max(Ln(t),Ln(i),Ln(o),Ln(a)),s=Bn(t,r),c=Bn(i,r),l=Bn(o,r);if(s<c)return{ok:!1,reason:`${e} の最小数量は ${i} です。`};if(l>0n&&s%l!==0n)return{ok:!1,reason:`${e} の数量刻みは ${o} です。`};if(a&&s>Bn(a,r))return{ok:!1,reason:`${e} の最大数量は ${a} です。`};return{ok:!0}})(Mt,i);o.ok?await mi(t,async()=>{const t=await n.placeCoinOrder({symbol:Mt,side:e,size:i});if(!t?.ok){const n=ai(t?.error||"coin order failed");return ni("[coin] order failed",{symbol:Mt,side:e,size:i,error:n}),ri("error",n),void await si("coin.order",n,t)}ni("[coin] order placed",{symbol:Mt,side:e,size:i,result:t.result||null}),ri("success",`${e} 注文を送信しました。`),await Wn("coin"),await Qn("coin")}):ri("error",o.reason||"数量が取引ルールに一致しません。")},ti=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"},ni=(e,t=null,n=null)=>{if(window.botApi?.writeLog&&window.botApi.writeLog({level:ti(e),source:"ui.log",message:e,details:{data:t,ts:n||(new Date).toISOString()}}).catch(()=>{}),!Z)return;const i=document.createElement("div");i.className="log-entry";const o=document.createElement("div");o.className="log-line";const a=document.createElement("span");if(a.className="log-ts",a.textContent=n||(new Date).toISOString(),o.appendChild(a),o.append(document.createTextNode(e||"")),i.appendChild(o),null!=t&&""!==t){const e=document.createElement("pre");e.className="status-output",e.textContent="string"==typeof t?t:JSON.stringify(t,null,2),i.appendChild(e)}for(Z.appendChild(i);Z.children.length>300;)Z.removeChild(Z.firstChild);Z.scrollTop=Z.scrollHeight},ii=(e,t,n="")=>{const i=document.createElement("div");i.className=`msg ${e}`;let o=n||("user"===e?"You":"Assistant");"line-user"===e&&(o=n||"LINE User"),"line-assistant"===e&&(o=n||"AI"),i.textContent=`${o}: ${t}`,ce.appendChild(i),ce.scrollTop=ce.scrollHeight},oi=()=>{const e=ce.querySelector(".msg.assistant-thinking");e&&e.remove()},ai=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:"处理失败,请稍后重试。"},ri=(e,t)=>{Pt&&(clearTimeout(Pt),Pt=null),r.classList.remove("hidden","notice-error","notice-success"),r.classList.add("error"===e?"notice-error":"notice-success"),r.textContent=t,Pt=setTimeout(()=>{ci()},"success"===e?2200:4200)},si=async(e,t,n=null)=>{window.botApi?.writeErrorLog&&await window.botApi.writeErrorLog({source:e,message:String(t||""),details:n})},ci=()=>{Pt&&(clearTimeout(Pt),Pt=null),r.classList.add("hidden"),r.classList.remove("notice-error","notice-success"),r.textContent=""},li=(e,t)=>{Dt&&(clearTimeout(Dt),Dt=null),n.classList.remove("hidden","notice-error","notice-success"),n.classList.add("error"===e?"notice-error":"notice-success"),n.textContent=t,Dt=setTimeout(()=>{di()},"success"===e?2200:4200)},di=()=>{Dt&&(clearTimeout(Dt),Dt=null),n.classList.add("hidden"),n.classList.remove("notice-error","notice-success"),n.textContent=""},mi=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=ai(e instanceof Error?e.message:String(e));ni("[ui] action failed",{error:t}),ri("error",t)}finally{e.classList.remove("is-loading"),e.textContent=n,e.disabled=!1}},ui=(e,t,n=[])=>{const i=e.getContext("2d");if(!i)return;const o=e.width,a=e.height;if(i.clearRect(0,0,o,a),!t.length)return;const r=Math.max(...t.map(e=>e.high)),s=Math.min(...t.map(e=>e.low)),c=n.map(e=>e.price).filter(e=>Number.isFinite(e)),l=c.length?Math.max(r,...c):r,d=c.length?Math.min(s,...c):s,m=Math.max(1e-8,l-d),u=10,g=10,p=o-10-62-8,f=a-10-24-8,y=Math.max(2,p/t.length),w=e=>g+(l-e)/m*f,b=e=>e>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:0}):e>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:3}):e.toLocaleString("en-US",{minimumFractionDigits:5,maximumFractionDigits:5});i.strokeStyle="#c9d4ea",i.strokeRect(.5,.5,o-1,a-1),i.strokeStyle="#e3eaf5",i.lineWidth=1;for(let e=0;e<=4;e+=1){const t=g+f/4*e;i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke()}t.forEach((e,t)=>{const n=u+t*y+.5*y,o=w(e.high),a=w(e.low),r=w(e.open),s=w(e.close),c=e.close>=e.open;i.strokeStyle=c?"#1f9d55":"#d64545",i.fillStyle=c?"#1f9d55":"#d64545",i.lineWidth=1,i.beginPath(),i.moveTo(n,o),i.lineTo(n,a),i.stroke();const l=n-.3*y,d=Math.max(1,.6*y),m=Math.min(r,s),g=Math.max(1,Math.abs(s-r));i.fillRect(l,m,d,g)});for(const e of n){const t=w(e.price);i.save(),i.setLineDash([4,4]),i.strokeStyle=e.color,i.lineWidth=1,i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke(),i.restore(),i.font="12px Segoe UI";const n=`${e.label} ${b(e.price)}`,o=Math.ceil(i.measureText(n).width)+10,a=14,r=Math.max(12,Math.min(g+f-18,t-9));i.fillStyle=e.color,i.fillRect(a,r,o,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(n,a+5,r+9)}const k=t[t.length-1],h=k?.close;if(Number.isFinite(h)){const e=w(Number(h));i.save(),i.setLineDash([5,4]),i.strokeStyle="#2b79ff",i.lineWidth=1,i.beginPath(),i.moveTo(u,e),i.lineTo(u+p,e),i.stroke(),i.restore();const t=b(Number(h));i.font="12px Segoe UI";const n=Math.ceil(i.measureText(t).width)+10,o=u+p+6,a=Math.max(12,Math.min(g+f-18,e-9));i.fillStyle="#2b79ff",i.fillRect(o,a,n,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(t,o+5,a+9)}i.fillStyle="#66758d",i.font="12px Segoe UI",i.textBaseline="middle",i.textAlign="left";for(let e=0;e<=4;e+=1){const t=l-m/4*e,n=g+f/4*e;i.fillText(b(t),u+p+8,n)}const x=e=>{const t=new Date(e>1e12?e:1e3*e);if(Number.isNaN(t.getTime()))return"-";const n=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${n}:${i}`};i.textAlign="center",i.textBaseline="top";for(let e=0;e<=4;e+=1){const n=Math.min(t.length-1,Math.floor((t.length-1)*(e/4))),o=u+p*e/4;i.fillText(x(t[n]?.time||0),o,g+f+6)}},gi=e=>{g.innerHTML="";const t=sn(e),n=sn(t.runtime),i=t.session?sn(t.session):null,o=[{key:"Codex Auth",value:String(n.codexAuthStatus||"unknown")},{key:"Gateway MCP",value:Qt.gatewayMcpStatus},{key:"Default Model",value:String(n.aiModel||"-")},{key:"Bot ID",value:String(i?.botId||"-")},{key:"User ID",value:String(i?.userId||"-")},{key:"Email",value:String(i?.email||"-")},{key:"Capabilities",value:Array.isArray(n.capabilities)?n.capabilities.map(e=>String(e)).join(", "):"-"},{key:"Active Skills",value:Array.isArray(n.activeSkills)&&n.activeSkills.map(e=>{const t=sn(e);return String(t.displayName||t.name||"").trim()}).filter(Boolean).join(", ")||"-"},{key:"Login API",value:String(n.loginApiUrl||"-")},{key:"Chromium",value:Qt.chromiumStatus},{key:"GMO FX API State",value:String(n.gmoFxApiState||"unknown")},{key:"GMO Coin API State",value:String(n.gmoCoinApiState||"unknown")}],a=(e,t)=>{const n=String(t||"").trim().toLowerCase();if("Default Model"!==e&&n&&"-"!==n)return"unknown"===n?"warn":["logged_in","configured","installed","ready","ok","connected","enabled"].includes(n)?"ok":["missing","error","failed","disconnected","disabled","not_installed","invalid"].includes(n)?"error":void 0};for(const e of o)e.tone=a(e.key,e.value);for(const{key:e,value:t,tone:n}of o){const i=document.createElement("div");i.className="runtime-key",i.textContent=e;const o=document.createElement("div");if(o.className="runtime-value",n){const e=document.createElement("span");e.className=`runtime-status runtime-status-${n}`,e.textContent=t,o.appendChild(e)}else o.textContent=t;g.appendChild(i),g.appendChild(o)}},pi=(e,t)=>{const{tabBtn:n,titleNode:i,packageNode:o,enabledNode:a,versionNode:r,fallbackTitle:s,fallbackPackage:c}=t,l=String(e?.ui?.surfaceTitle||e?.displayName||s).trim()||s,d=e?.name||c,m=String(e?.ui?.tabLabel||d.replace("@ai.weget.jp/","")).trim()||d.replace("@ai.weget.jp/",""),u=!!e&&Boolean(e.enabled),g=String(e?.installStatus||"uninstalled").trim()||"uninstalled",p=String(e?.configuredVersion||e?.version||"-").trim()||"-";if(n){const e=n.querySelector(".tab-btn-label");e&&(e.textContent=m),n.classList.toggle("is-disabled",!u)}i&&(i.textContent=l),o&&(o.textContent=d),a&&(a.textContent=u?"enabled":"disabled",a.classList.toggle("is-disabled",!u)),r&&(r.textContent=`${g} · ${p}`)},fi=(e,t)=>{t.titleNode&&(t.titleNode.textContent=String(e?.ui?.configTitle||t.fallbackTitle).trim()||t.fallbackTitle);const n=Array.isArray(e?.ui?.configFields)&&e.ui?.configFields||[];for(const[e,i]of Object.entries(t.fields)){const t=n.find(t=>t.key===e);t?.label&&i.labelNode&&(i.labelNode.textContent=t.label),void 0!==t?.placeholder&&(i.inputNode.placeholder=t.placeholder||"")}},yi=(e,t)=>{if(e){if(e.innerHTML="",!t.length){const t=document.createElement("span");return t.className="muted",t.textContent="No macro snapshot cards available.",void e.appendChild(t)}for(const n of t){const t=document.createElement("div");t.className="macro-chip"+("down"===n.trend?" is-negative":"");const i=document.createElement("div");i.className="macro-chip-code",i.textContent=n.code||"-";const o=document.createElement("div");o.className="macro-chip-value",o.textContent=n.value||"-";const a=document.createElement("div");a.className="macro-chip-delta"+("down"===n.trend?" is-negative":""),a.textContent=n.delta||"-",t.append(i,o,a),e.appendChild(t)}}},wi=(e,t,n)=>{if(!e)return;if(e.innerHTML="",!t.length){const t=document.createElement("div");return t.className="macro-treemap-empty",t.textContent=n,void e.appendChild(t)}const i=[...t].map(e=>({symbol:String(e.symbol||"").trim(),change:Number(e.change||0),weight:Math.max(1,Math.abs(Number(e.change||0)))})).filter(e=>e.symbol).sort((e,t)=>Math.abs(t.change)-Math.abs(e.change)).slice(0,12),o=[],a=i.reduce((e,t)=>e+t.weight,0)||1,r=(e,t,n,i,a,s)=>{if(!e.length)return;if(1===e.length)return void o.push({item:e[0],x:t,y:n,width:i,height:a});const c=e.reduce((e,t)=>e+t.weight,0);let l=1,d=e[0].weight;for(;l<e.length-1&&d<c/2;)l+=1,d+=e[l-1].weight;const m=e.slice(0,l),u=e.slice(l),g=d/c;if(s){const e=i*g;return r(m,t,n,e,a,!s),void r(u,t+e,n,i-e,a,!s)}const p=a*g;r(m,t,n,i,p,!s),r(u,t,n+p,i,a-p,!s)};r(i,0,0,100,100,!0);for(const{item:t,x:n,y:i,width:r,height:s}of o){const o=document.createElement("div"),c=Math.min(.88,.22+t.weight/Math.max(.18*a,1)),l=Math.max(8,Math.min(r,s)),d=Math.max(80,r*s),m=Math.max(12,Math.min(30,Math.round(.42*l+.16*Math.sqrt(d)))),u=Math.max(11,Math.min(22,Math.round(.72*m))),g=Math.max(2,Math.min(6,Math.round(.16*m)));o.className="macro-treemap-tile "+(t.change>=0?"up":"down"),o.style.setProperty("--tile-intensity",String(c)),o.style.setProperty("--tile-symbol-font-size",`${m}px`),o.style.setProperty("--tile-change-font-size",`${u}px`),o.style.setProperty("--tile-gap",`${g}px`),o.style.left=`${n}%`,o.style.top=`${i}%`,o.style.width=`${r}%`,o.style.height=`${s}%`,o.innerHTML=`\n <div class="macro-treemap-symbol">${kn(t.symbol.replace(/_JPY$/i,"").replace(/_USD$/i," $"))}</div>\n <div class="macro-treemap-change">${t.change>=0?"+":""}${t.change.toFixed(1)}%</div>\n `,e.appendChild(o)}},bi=e=>{if(q)if(q.innerHTML="",e.length)for(const t of e){const e=Sn(t.ai_analyzer||""),n=document.createElement("tr");n.className="macro-data-row",n.innerHTML=`\n <td>${kn(bn(t.date))}</td>\n <td>${kn(t.country||"-")}</td>\n <td class="macro-title-cell"><strong>${kn(t.title||"-")}</strong></td>\n <td>${Cn(Number(t.importance||0))}</td>\n <td>${kn(t.actual||"-")}</td>\n <td>${kn(t.forecast||"-")}</td>\n <td>${kn(t.previous||"-")}</td>\n <td>${In(e.direction)}</td>\n `;const i=document.createElement("tr");i.className="macro-ai-row",i.innerHTML=`\n <td colspan="8" class="macro-analyzer">${An(e.cleaned||"-")}</td>\n `,q.append(n,i)}else q.innerHTML='<tr><td colspan="8" class="empty-cell">No calendar rows for the selected date.</td></tr>'},ki=e=>{if(G)if(G.innerHTML="",e.length)for(const t of e){const e=Sn(t.ai_analyzer||""),n=String(t.url||"").trim(),i=document.createElement("tr");i.className="macro-data-row",i.innerHTML=`\n <td>${kn(bn(t.time))}</td>\n <td class="macro-title-cell"><strong>${kn(t.content||"-")}</strong></td>\n <td>${En(e.impact)}</td>\n <td>${In(e.direction)}</td>\n <td>${n?`<a class="macro-link" href="${kn(n)}" target="_blank" rel="noreferrer noopener">open</a>`:"-"}</td>\n `;const o=document.createElement("tr");o.className="macro-ai-row",o.innerHTML=`\n <td colspan="5" class="macro-analyzer">${An(e.cleaned||"-")}</td>\n `,G.append(i,o)}else G.innerHTML='<tr><td colspan="4" class="empty-cell">No news rows for the selected date.</td></tr>'},hi=e=>{F&&(F.textContent="-"),j&&(j.textContent="-"),_&&(_.textContent=e),yi(z,[]),yi(O,[]),wi(H,[],"No coin heatmap data."),wi(J,[],"No FX heatmap data."),bi([]),ki([])},xi=async({forceSnapshot:e=!1}={})=>{if(Xt)return;if(!window.botApi?.getMacroSnapshot||!window.botApi?.getMacroCalendar||!window.botApi?.getMacroNews)return void hi("Macro IPC bridge is not available in this host build.");const t=Pn("@ai.weget.jp/skill-macro-economy");if(!t||!t.enabled||"active"!==String(t.installStatus||"").trim().toLowerCase())return void hi("Macro Economy skill is disabled or not active.");if(!jt)return void hi("Login is required to load macro snapshot, calendar, and news.");Xt=!0;const n=String(K?.value||"").trim()||wn(new Date);K&&!K.value&&(K.value=n);try{const[t,i,o]=await Promise.all([window.botApi.getMacroSnapshot({force:e}),window.botApi.getMacroCalendar({dateKey:n}),window.botApi.getMacroNews({dateKey:n})]);if(!t.ok)throw new Error(t.error||"macro snapshot failed");if(!i.ok)throw new Error(i.error||"macro calendar failed");if(!o.ok)throw new Error(o.error||"macro news failed");const a=t.snapshot;F&&(F.textContent=bn(a?.as_of)),j&&(j.textContent=null==a?.fear_greed_score?"-":`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`),_&&(_.textContent=`Loaded macro data for ${n} from the local macro economy skill runtime.`);const r=Array.isArray(a?.coin_cards)?[...a.coin_cards]:[];null!=a?.fear_greed_score&&r.unshift({code:"FGI",value:`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`,delta:"Fear & Greed",trend:Number(a.fear_greed_score)>=50?"up":"down",points:[],usePill:!0}),yi(z,r),yi(O,Array.isArray(a?.fx_cards)?a?.fx_cards:[]),wi(H,Array.isArray(a?.coin_heatmap)?a?.coin_heatmap:[],"No coin heatmap data."),wi(J,Array.isArray(a?.fx_heatmap)?a?.fx_heatmap:[],"No FX heatmap data."),bi(Array.isArray(i.rows)?i.rows:[]),ki(Array.isArray(o.rows)?o.rows:[])}catch(e){hi(ai(e instanceof Error?e.message:String(e)))}finally{Xt=!1}},vi=(e,t)=>{W&&(W.textContent=e,W.classList.toggle("is-disabled","installed"!==e),W.classList.toggle("is-muted","unknown"===e)),X&&(X.textContent=t||"No Playwright browser detail available.")},Si=({browserStatus:e})=>{V&&(V.innerHTML="installed"!==e?"Run <code>npx playwright install chromium</code> on this machine.":"Chromium is ready for Playwright-backed browser tasks.")},Ei=async()=>{if(!window.botApi?.getPlaywrightBrowserStatus)return vi("unknown","Playwright browser check is not available in this host build."),void Si({browserStatus:"unknown"});const e=await window.botApi.getPlaywrightBrowserStatus(),t=e.ok&&e.status||"unknown";e.ok?vi(t,String(e.detail||"").trim()):vi("unknown",ai(e.error)),Si({browserStatus:t})},Ci=(e,t,n,i=!1)=>{e&&(e.textContent=t||"-",e.classList.toggle("is-disabled",!n&&!i),e.classList.toggle("is-muted",i))},Ii=async()=>{if(!window.botApi?.getGatewayStatus)return;const e=await window.botApi.getGatewayStatus();if(!e?.ok)return Qt={gatewayMcpStatus:"error",chromiumStatus:"error"},Ci(ee,"error",!1),Ci(te,"error",!1),Ci(ne,"error",!1),oe&&(oe.textContent=ai(e?.error||"gateway status failed")),void(Vt&&gi(Vt));Qt={gatewayMcpStatus:String(e.gatewayStatus?.status||"unknown"),chromiumStatus:String(e.browserStatus?.status||"unknown")},Ci(ee,String(e.codexAuth?.status||"unknown"),"logged_in"===String(e.codexAuth?.status||""),"unknown"===String(e.codexAuth?.status||"")),Ci(te,String(e.gatewayStatus?.status||"unknown"),"configured"===String(e.gatewayStatus?.status||""),"unknown"===String(e.gatewayStatus?.status||"")),Ci(ne,String(e.browserStatus?.status||"unknown"),"installed"===String(e.browserStatus?.status||""),"unknown"===String(e.browserStatus?.status||"")),ie&&(ie.textContent=`Context file: ${String(e.contextFilePath||"-")}`),oe&&(oe.textContent=JSON.stringify({codexAuth:e.codexAuth||null,gatewayStatus:e.gatewayStatus||null,browserStatus:e.browserStatus||null},null,2)),Vt&&gi(Vt),(e=>{if(!se)return;se.innerHTML="";const t=[{target:"gateway",title:"Gateway Core"},{target:"codex_macro",title:"Codex Macro Chain",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"browser",title:"Browser Skill",packageName:"@ai.weget.jp/skill-browser"},{target:"macro",title:"Macro Economy Skill",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"coin",title:"GMO Coin Skill",packageName:"@ai.weget.jp/skill-gmo-coin"},{target:"fx",title:"GMO FX Skill",packageName:"@ai.weget.jp/skill-gmo-fx"}];for(const n of t){const t=n.packageName?e.find(e=>e.name===n.packageName):null,i=document.createElement("section");i.className="browser-skill-card gateway-test-card",i.innerHTML=`\n <div class="gateway-test-head">\n <h3>${n.title}</h3>\n <span class="skill-surface-badge is-muted">${t?t.enabled?"enabled":"disabled":"system"}</span>\n </div>\n <div class="muted">${t?`${t.name} · ${t.installStatus}`:"Gateway MCP self-test"}</div>\n <div class="skill-tags">${t&&Array.isArray(t.tools)&&t.tools.length?t.tools.map(e=>`<span class="skill-tag">${kn(e)}</span>`).join(""):'<span class="muted">No declared tools</span>'}</div>\n <div class="row config-actions">\n <button type="button" class="secondary gateway-test-btn" data-target="${n.target}">Run Test</button>\n </div>\n <div class="gateway-test-result">\n <span class="skill-surface-badge is-muted gateway-test-badge" data-target="${n.target}">not run</span>\n <div class="muted gateway-test-summary" data-target="${n.target}">-</div>\n </div>\n `,se.appendChild(i)}})(Array.isArray(e.skills)?e.skills:[])},Ai=async()=>{if(!window.botApi?.getRuntimeInfo)return;const[e,t]=await Promise.all([window.botApi.getRuntimeInfo(),window.botApi.getSkills?window.botApi.getSkills():(async()=>({ok:!1,skills:[]}))()]);if(!e?.ok)return;const n=t?.ok&&t.skills||[];Wt=n,un(e.status||"disconnected"),e.session?(mn(!0),u.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`):(mn(!1),u.textContent="local mode (not logged in)"),Vt=e,gi(e),(e=>{if(!p)return;p.innerHTML="";const t=Array.isArray(e)?e:[];if(0===t.length){const e=document.createElement("div");return e.className="skill-empty",e.textContent="No managed skills are available in this bot host.",void p.appendChild(e)}for(const e of t){const t=document.createElement("section");t.className="skill-card"+(e.enabled?"":" is-disabled");const n=document.createElement("div");n.className="skill-card-head";const i=document.createElement("div");i.className="skill-card-title";const o=document.createElement("strong");o.textContent=e.displayName||e.name;const a=document.createElement("code");if(a.textContent=e.name,i.appendChild(o),i.appendChild(a),e.description){const t=document.createElement("div");t.className="muted",t.textContent=e.description,i.appendChild(t)}const r=document.createElement("div");r.className="skill-badges";const s=document.createElement("span");s.className="skill-badge "+(e.enabled?"enabled":"disabled"),s.textContent=e.enabled?"enabled":"disabled",r.appendChild(s);const c=document.createElement("span");c.className="skill-badge status",c.textContent=e.installStatus||"unknown",r.appendChild(c),n.appendChild(i),n.appendChild(r),t.appendChild(n);const l=e=>{const t=document.createElement("div");t.className="skill-card-section";const n=document.createElement("div");return n.className="skill-section-label",n.textContent=e,t.appendChild(n),t},d=l("Package"),m=document.createElement("code");m.className="skill-package-code",m.textContent=e.name,d.appendChild(m),t.appendChild(d);const u=l("Tools"),g=document.createElement("div");g.className="skill-tags";const f=Array.isArray(e.tools)?e.tools:[];if(f.length>0)for(const e of f){const t=document.createElement("span");t.className="skill-tag",t.textContent=e,g.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No tools declared",g.appendChild(e)}u.appendChild(g),t.appendChild(u);const y=l("Permissions");if(Array.isArray(e.permissions)&&e.permissions.length>0){const t=document.createElement("div");t.className="skill-tags";for(const n of e.permissions){const e=document.createElement("span");e.className="skill-tag permissions",e.textContent=n,t.appendChild(e)}y.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No permissions declared",y.appendChild(e)}t.appendChild(y);const w=l("State"),b=document.createElement("div");b.className="skill-meta";const k=[["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 k){const n=document.createElement("div");n.className="skill-meta-row";const i=document.createElement("span");i.textContent=e;const o=document.createElement("strong");o.textContent=t,n.appendChild(i),n.appendChild(o),b.appendChild(n)}w.appendChild(b),t.appendChild(w),p.appendChild(t)}})(n),pi(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{tabBtn:f,titleNode:k,packageNode:S,enabledNode:A,versionNode:$,fallbackTitle:"GMO Coin Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-coin"}),pi(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{tabBtn:y,titleNode:h,packageNode:E,enabledNode:N,versionNode:M,fallbackTitle:"GMO FX Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-fx"});const i=n.find(e=>"@ai.weget.jp/skill-browser"===e.name);pi(i,{tabBtn:w,titleNode:x,packageNode:C,enabledNode:L,versionNode:T,fallbackTitle:"Browser Skill",fallbackPackage:"@ai.weget.jp/skill-browser"}),((e,t,n)=>{if(e){if(e.innerHTML="",0===t.length){const t=document.createElement("span");return t.className="muted",t.textContent=n,void e.appendChild(t)}for(const n of t){const t=document.createElement("span");t.className="skill-tag",t.textContent=n,e.appendChild(t)}}})(U,Array.isArray(i?.tools)?i.tools:[],"No browser tools declared."),D&&(D.innerHTML="");const o=n.find(e=>"@ai.weget.jp/skill-macro-economy"===e.name);pi(o,{tabBtn:b,titleNode:v,packageNode:I,enabledNode:B,versionNode:P,fallbackTitle:"Macro Economy Skill",fallbackPackage:"@ai.weget.jp/skill-macro-economy"}),await Ii(),await Ei(),fi(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{titleNode:Ce,fallbackTitle:"FX Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Ae,inputNode:ge},fxApiKey:{labelNode:Ne,inputNode:we},fxApiSecret:{labelNode:Le,inputNode:be}}}),fi(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{titleNode:Ie,fallbackTitle:"Coin Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Be,inputNode:pe},cryptoApiKey:{labelNode:$e,inputNode:fe},cryptoApiSecret:{labelNode:Me,inputNode:ye}}}),e.session?(xi(),Mi()):hi("Login is required to load macro snapshot, calendar, and news.")};Se.addEventListener("click",async()=>{await mi(Se,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(ge.value||"0"),fxApiKey:String(we.value||"").trim(),fxApiSecret:String(be.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-fx",e);if(!t.ok)return ni(`[trade-config] fx save failed: ${t.error||"unknown"}`),ri("error",ai(t.error)),void await si("trade-config.fx.save",ai(t.error),t);ni("[trade-config] fx saved"),ri("success","FX skill 配置已保存到本机。"),await Ai()})}),Ee.addEventListener("click",async()=>{await mi(Ee,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(pe.value||"0"),cryptoApiKey:String(fe.value||"").trim(),cryptoApiSecret:String(ye.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-coin",e);if(!t.ok)return ni(`[trade-config] coin save failed: ${t.error||"unknown"}`),ri("error",ai(t.error)),void await si("trade-config.coin.save",ai(t.error),t);ni("[trade-config] coin saved"),ri("success","Coin skill 配置已保存到本机。"),await Ai()})}),De.addEventListener("click",async()=>{await mi(De,async()=>{if(!window.botApi?.saveAiConfig)return;const e={aiModel:String(Te.value||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(Pe.value||"").trim()},t=await window.botApi.saveAiConfig(e);if(!t.ok)return ni(`[ai-config] save failed: ${t.error||"unknown"}`),ri("error",ai(t.error)),void await si("ai-config.save",ai(t.error),t);ni("[ai-config] saved"),ri("success","AI 设置已保存。"),await Ai()})}),Ue.addEventListener("click",async()=>{await mi(Ue,async()=>{if(!window.botApi?.startCodexLogin)return;const e=await window.botApi.startCodexLogin();if(!e.ok)return ni(`[codex] login launch failed: ${e.detail||"unknown"}`),ri("error",ai(e.detail)),void await si("codex.login.launch",ai(e.detail),e);ni("[codex] login launched",{detail:e.detail}),ri("success",e.detail||"Codex login started."),setTimeout(()=>{Ai()},1500)})}),Fe.addEventListener("click",async()=>{const e=String(_e.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 ri("error","无法复制命令,请手动执行。");ni("[codex] login command copied"),ri("success","Codex 登录命令已复制。")}catch(e){const t=ai(e instanceof Error?e.message:String(e));ni("[codex] copy login command failed",{error:t}),ri("error",t)}}),je.addEventListener("click",async()=>{await mi(je,async()=>{await Ai(),ni("[codex] auth status refreshed"),ri("success","Codex 状态已刷新。")})}),Q?.addEventListener("click",async()=>{await mi(Q,async()=>{await Ei(),ni("[browser] chromium status refreshed"),ri("success","Browser status 已刷新。")})}),ae?.addEventListener("click",async()=>{await mi(ae,async()=>{if(!window.botApi?.installPlaywrightMcp)return;const e=await window.botApi.installPlaywrightMcp();if(!e.ok)return ni("[gateway] mcp install failed",{error:e.detail||e.error||"unknown"}),ri("error",ai(e.detail||e.error)),await si("gateway.install",ai(e.detail||e.error),e),void await Ii();ni("[gateway] mcp configured",{detail:e.detail}),ri("success",e.detail||"WeGet Gateway MCP configured."),await Ii()})}),re?.addEventListener("click",async()=>{await mi(re,async()=>{await Ii(),ri("success","Gateway status 已刷新。")})}),se?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".gateway-test-btn");if(!n)return;const i=String(n.dataset.target||"").trim().toLowerCase();"gateway"!==i&&"codex_macro"!==i&&"browser"!==i&&"macro"!==i&&"coin"!==i&&"fx"!==i||await(async(e,t)=>{const n=window.botApi;n?.runGatewaySelfTest&&await mi(t,async()=>{const t=await n.runGatewaySelfTest({target:e}),i=se?.querySelector(`.gateway-test-badge[data-target="${e}"]`),o=se?.querySelector(`.gateway-test-summary[data-target="${e}"]`);if(i){const e=Boolean(t?.ok);i.textContent=e?"OK":"NG",i.classList.toggle("is-muted",!1),i.classList.toggle("is-disabled",!e)}if(o){const e=String(t?.summary||t?.error||t?.detail||"-").trim()||"-",n=String(t?.logPath||"").trim();o.textContent=n?`${e} [log] ${n}`:e}if(!t?.ok){const n=ai(t?.summary||t?.error||t?.detail||`${e} test failed`);return ni("[gateway] self-test failed",{target:e,error:n,logPath:t?.logPath||""}),void ri("error",n)}ni("[gateway] self-test ok",{target:e,summary:t?.summary||"",logPath:t?.logPath||""}),ri("success",`${e} test completed.`)})})(i,n)}),Y?.addEventListener("click",async()=>{await mi(Y,async()=>{await xi({forceSnapshot:!0}),ni("[macro] snapshot refreshed"),ri("success","Macro snapshot 已刷新。")})}),R?.addEventListener("click",async()=>{await mi(R,async()=>{await xi(),ni("[macro] calendar and news refreshed",{dateKey:String(K?.value||"").trim()}),ri("success","Macro calendar / news 已刷新。")})}),K?.addEventListener("change",()=>{xi()}),he.addEventListener("click",()=>{const e="password"===ye.type?"text":"password";ye.type=e,he.textContent="password"===e?"显示":"隐藏"}),ke.addEventListener("click",()=>{const e="password"===fe.type?"text":"password";fe.type=e,ke.textContent="password"===e?"显示":"隐藏"}),ve.addEventListener("click",()=>{const e="password"===be.type?"text":"password";be.type=e,ve.textContent="password"===e?"显示":"隐藏"}),xe.addEventListener("click",()=>{const e="password"===we.type?"text":"password";we.type=e,xe.textContent="password"===e?"显示":"隐藏"}),e.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi)return;di();const t=c.value.trim(),n=s.value.trim(),i=l.value;if(t&&n&&i){Oe.disabled=!0,Oe.textContent="Loading...";try{const e=await window.botApi.login(t,i,n,d.checked);if(!e.ok)return ni(`[ui] login failed: ${e.error||"unknown"}`),void li("error",ai(e.error));ni("[ui] login success"),li("success","登录成功。"),ri("success","登录成功。"),await Ai()}finally{Oe.disabled=!1,Oe.textContent="Login"}}}),ze.addEventListener("click",async()=>{await mi(ze,async()=>{if(!window.botApi)return;const e=await window.botApi.logout();if(!e.ok)return ni(`[ui] logout failed: ${e.error||"unknown"}`),ri("error",ai(e.error)),void await si("auth.logout",ai(e.error),e);ni("[ui] logout"),ri("success","已登出。"),mn(!1),u.textContent="local mode (not logged in)",await Ai()})});const Ni=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 ni(`[trade] open ${e} kline failed: ${o.error||"unknown"}`),ri("error",ai(o.error)),void await si(`${e}.kline`,ai(o.error),o);const a=Array.isArray(o.candles)?o.candles:[],r=zn(e);r.symbol=String(o.symbol||t||"").toUpperCase(),r.interval=n,r.candles=a.map(e=>({time:Number(e.time||0),open:Number(e.open||0),high:Number(e.high||0),low:Number(e.low||0),close:Number(e.close||0)})),r.lastFetchBucket=Math.floor(Date.now()/_n(n)),ui(i,r.candles,Jn(e)),ni("[trade] kline rendered",{market:e,symbol:o.symbol||t,interval:o.interval||n,candles:a.length}),ci()},Li=async()=>{await Ni({market:"coin",symbol:Mt,interval:Bt,canvas:Ke})},Bi=async()=>{await Ni({market:"fx",symbol:Tt,interval:$t,canvas:Ye})},$i=({container:e,getCurrent:t,setCurrent:n,onChange:i})=>{const o=Array.from(e.querySelectorAll(".interval-btn"));for(const e of o)e.addEventListener("click",async()=>{const a=String(e.dataset.interval||"").trim();if(a&&a!==t()){n(a);for(const t of o)t.classList.toggle("is-active",t===e);await i()}})};He.addEventListener("change",async()=>{Mt=String(He.value||"BTC_JPY").trim().toUpperCase(),jn("coin",Mt);try{await Fn("coin",Mt),await Li(),await Qn("coin")}catch(e){const t=ai(e instanceof Error?e.message:String(e));ri("error",t)}}),Je.addEventListener("change",async()=>{Tt=String(Je.value||"USD_JPY").trim().toUpperCase(),jn("fx",Tt);try{await Fn("fx",Tt),await Bi(),await Qn("fx")}catch(e){const t=ai(e instanceof Error?e.message:String(e));ri("error",t)}}),$i({container:qe,getCurrent:()=>Bt,setCurrent:e=>{Bt=e},onChange:Li}),$i({container:Ge,getCurrent:()=>$t,setCurrent:e=>{$t=e},onChange:Bi}),dt.addEventListener("click",async()=>{await mi(dt,async()=>{await Wn("coin")})}),mt.addEventListener("click",async()=>{await mi(mt,async()=>{await Wn("fx")})}),nt.addEventListener("input",()=>{Tn("coin")}),it.addEventListener("input",()=>{Tn("fx")}),xt.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeCoinPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=Nn(String(n.dataset.size||""));o&&Number.isFinite(s)&&!(s<=0)&&c?await mi(n,async()=>{const e=await i.closeCoinPosition({symbol:o,side:r,positionId:s,size:c});if(!e?.ok){const t=ai(e?.error||"coin close order failed");return ni("[coin] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,error:t}),ri("error",t),void await si("coin.closeOrder",t,e)}ni("[coin] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,result:e.result||null}),ri("success","決済注文を送信しました。"),await Wn("coin"),await Qn("coin")}):ri("error","決済対象データが不正です。")}),St.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeFxPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=String(n.dataset.size||"").trim();o&&Number.isFinite(s)&&!(s<=0)&&c?await mi(n,async()=>{const e=await i.closeFxPosition({symbol:o,side:r,positionId:s,size:c});if(!e?.ok){const t=ai(e?.error||"fx close order failed");return ni("[fx] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,error:t}),ri("error",t),void await si("fx.closeOrder",t,e)}ni("[fx] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,result:e.result||null}),ri("success","決済注文を送信しました。"),await Wn("fx"),await Qn("fx")}):ri("error","決済対象データが不正です。")}),ot.addEventListener("click",async()=>{await ei("BUY",ot)}),at.addEventListener("click",async()=>{await ei("SELL",at)}),rt.addEventListener("click",async()=>{await Zn("BUY",rt)}),st.addEventListener("click",async()=>{await Zn("SELL",st)}),Et.addEventListener("click",async()=>{await mi(Et,async()=>{await Qn("coin")})}),Ct.addEventListener("click",async()=>{await mi(Ct,async()=>{await Qn("coin")})}),It.addEventListener("click",async()=>{await mi(It,async()=>{await Qn("fx")})}),At.addEventListener("click",async()=>{await mi(At,async()=>{await Qn("fx")})}),de.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi?.sendChat)return;const t=String(me.value||"").trim();if(t){ii("user",t),me.value="",ue.disabled=!0,((e="AI")=>{oi();const t=document.createElement("div");t.className="msg assistant assistant-thinking";const n=document.createElement("div");n.className="msg-title",n.textContent=`${e}:`;const i=document.createElement("div");i.className="thinking-body";const o=document.createElement("span");o.className="thinking-text",o.textContent="Thinking";const a=document.createElement("span");a.className="thinking-dots",a.innerHTML="<span></span><span></span><span></span>",i.append(o,a),t.append(n,i),ce.appendChild(t),ce.scrollTop=ce.scrollHeight})("AI");try{const e=await window.botApi.sendChat(t);if(!e.ok)return oi(),ii("assistant",`Error: ${e.error||"unknown"}`),ri("error",ai(e.error)),void await si("ai.chat",ai(e.error),e);ci()}catch(e){oi();const t=ai(e instanceof Error?e.message:String(e));ii("assistant",`Error: ${t}`),ri("error",t),await si("ai.chat.exception",t,e)}finally{ue.disabled=!1}}}),window.addEventListener("error",e=>{const t=ai(e.error?.message||e.message||"Unknown UI error");ni("[ui] uncaught error",{error:t}),ri("error",t),si("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=ai(t);ni("[ui] unhandled rejection",{error:n}),ri("error",n),si("ui.unhandledrejection",n,e.reason)}),le?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".active-task-cancel-btn");if(!n)return;const i=String(n.dataset.taskId||"").trim();if(!i||!window.botApi?.cancelActiveTask)return;n.disabled=!0;const o=await window.botApi.cancelActiveTask(i);if(!o?.ok)return n.disabled=!1,void ri("error",ai(o?.error||"Failed to cancel task"));ri("success",`Cancel requested for ${i}`)}),window.botApi&&(window.botApi.getActiveTasks?.().then(e=>{e?.ok&&vn({type:"snapshot",tasks:Array.isArray(e.tasks)?e.tasks:[]})}),window.botApi.onStatus(e=>{un(String(e.status||""))}),window.botApi.onTaskRuntime(e=>{vn(e)}),window.botApi.onChatEvent(e=>{if(!e)return;const t=sn(e);if("line_user"===t.type){const e=t.lineUserId?`LINE(${String(t.lineUserId)})`:"LINE";return void ii("line-user",String(t.text||""),e)}if("line_assistant"===t.type){oi();const e=t.lineUserId?`AI->LINE(${String(t.lineUserId)})`:"AI->LINE";return void ii("line-assistant",String(t.text||""),e)}"assistant_status"!==t.type?"assistant"===t.type&&(oi(),ii("assistant",String(t.text||""))):(e=>{const t=String(e||"").trim().replace(/\s+/g," ");if(!t)return;if(/^[{}[\],:]+$/.test(t))return;if(/^["']?[a-zA-Z0-9_-]+["']?\s*:\s*$/.test(t))return;const n=ce.querySelector(".msg.assistant-thinking .thinking-text");n&&(n.textContent=t)})(String(t.status||"Thinking"))}),window.botApi.onTradeExecution(e=>{if(!e)return;const t=sn(e),n="coin"===String(t.market||"").trim().toLowerCase()?"coin":"fx",i=String(t.symbol||"").trim().toUpperCase(),o=String(t.side||"").trim().toUpperCase()||"-",a=String(t.settleType||"").trim().toUpperCase()||"OPEN",r=String(t.executionSize??"").trim(),s=String(t.executionPrice??"").trim(),c="CLOSE"===a?"決済":"新規",l=`${i} ${o} ${r}${s?` @ ${s}`:""}`;ni(`[${n}] execution filled`,{symbol:i,side:o,settleType:a,size:r,price:s,raw:t}),ri("success",`約定成功 (${c}) ${l}`.trim()),Wn(n).catch(()=>{}),Qn(n).catch(()=>{})})),rn=setInterval(()=>{xn()},15e3);const Mi=async()=>{if(jt&&!_t&&!zt){zt=!0;try{if(!await async function(){let e=0;if(jn("coin",Mt),jn("fx",Tt),Un("coin")){e+=1;try{await Fn("coin",Mt)}catch(e){const t=ai(e instanceof Error?e.message:String(e));ni("[coin] quote fetch failed",{error:t}),ri("error",t)}await Li()}if(Un("fx")){e+=1;try{await Fn("fx",Tt)}catch(e){const t=ai(e instanceof Error?e.message:String(e));ni("[fx] quote fetch failed",{error:t}),ri("error",t)}await Bi()}if(window.botApi?.getSymbolRules&&Un("coin"))try{const e=await window.botApi.getSymbolRules({market:"coin"});if(e?.ok&&Array.isArray(e.rules)){Zt.clear();for(const t of e.rules){const e=String(t.symbol||"").trim().toUpperCase();e&&Zt.set(e,{minOrderSize:String(t.minOrderSize||""),maxOrderSize:String(t.maxOrderSize||""),sizeStep:String(t.sizeStep||"")})}}}catch(e){ni("[coin] symbol rules fetch failed",{error:e instanceof Error?e.message:String(e)})}return e>0}())return;await Wn(),await Qn(),Un("coin")&&!Ut&&(Ut=setInterval(()=>{Yn("coin")},1e3)),Un("fx")&&!Ft&&(Ft=setInterval(()=>{Yn("fx")},1e3)),_t=!0}finally{zt=!1}}};mn(!1),(()=>{try{ln("true"===window.localStorage.getItem(cn))}catch{ln(!1)}})(),K&&!K.value&&(K.value=wn(new Date)),(async()=>{if(!window.botApi?.getSavedProfile)return;const e=await window.botApi.getSavedProfile();e?.ok&&e.profile&&(s.value=e.profile.botId||"",c.value=e.profile.email||"",l.value=e.profile.password||"",d.checked=Boolean(e.profile.remember))})(),(async()=>{if(!window.botApi?.getSkillConfig||!window.botApi.getAiConfig)return;const[e,t,n]=await Promise.all([window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-fx"),window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-coin"),window.botApi.getAiConfig()]);e?.ok&&e.config&&(ge.value=String(e.config.riskDailyLossLimitJpy||5e4),we.value=String(e.config.fxApiKey||""),be.value=String(e.config.fxApiSecret||"")),t?.ok&&t.config&&(pe.value=String(t.config.riskDailyLossLimitJpy||5e4),fe.value=String(t.config.cryptoApiKey||""),ye.value=String(t.config.cryptoApiSecret||"")),n?.ok&&n.config&&(Te.value=n.config.aiModel||"gpt-5.4",Pe.value=n.config.logOutputDir||"")})(),Ai();export{};
|
|
1
|
+
const e=document.getElementById("login-form"),t=document.getElementById("login-screen"),n=document.getElementById("login-notice"),i=document.getElementById("app-shell"),o=document.getElementById("sidebar-nav"),a=document.getElementById("sidebar-toggle-btn"),r=document.getElementById("user-notice"),s=document.getElementById("bot-id"),l=document.getElementById("email"),c=document.getElementById("password"),d=document.getElementById("remember-me"),m=document.getElementById("status"),u=document.getElementById("session"),g=document.getElementById("runtime-info"),p=document.getElementById("skill-state-list"),f=document.getElementById("tab-btn-skill-gmo-coin"),y=document.getElementById("tab-btn-skill-gmo-fx"),w=document.getElementById("tab-btn-skill-browser"),b=document.getElementById("tab-btn-skill-macro-economy"),h=document.getElementById("coin-skill-title"),k=document.getElementById("fx-skill-title"),x=document.getElementById("browser-skill-title"),v=document.getElementById("macro-skill-title"),S=document.getElementById("coin-skill-package"),E=document.getElementById("fx-skill-package"),C=document.getElementById("browser-skill-package"),I=document.getElementById("macro-skill-package"),A=document.getElementById("coin-skill-enabled"),N=document.getElementById("fx-skill-enabled"),L=document.getElementById("browser-skill-enabled"),B=document.getElementById("macro-skill-enabled"),$=document.getElementById("coin-skill-version"),M=document.getElementById("fx-skill-version"),T=document.getElementById("browser-skill-version"),P=document.getElementById("macro-skill-version"),D=document.getElementById("browser-skill-cards"),U=document.getElementById("browser-skill-tools"),F=document.getElementById("macro-snapshot-as-of"),j=document.getElementById("macro-fear-greed-score"),_=document.getElementById("macro-snapshot-hint"),z=document.getElementById("macro-coin-cards"),O=document.getElementById("macro-fx-cards"),H=document.getElementById("macro-coin-heatmap-treemap"),J=document.getElementById("macro-fx-heatmap-treemap"),q=document.getElementById("macro-calendar-body"),G=document.getElementById("macro-news-body"),K=document.getElementById("macro-date-key"),Y=document.getElementById("macro-refresh-snapshot-btn"),R=document.getElementById("macro-refresh-day-btn"),W=document.getElementById("browser-chromium-status"),X=document.getElementById("browser-chromium-detail"),V=document.getElementById("browser-prereq-hint"),Q=document.getElementById("browser-refresh-playwright-mcp-btn"),Z=document.getElementById("logs"),ee=document.getElementById("gateway-codex-auth"),te=document.getElementById("gateway-mcp-status"),ne=document.getElementById("gateway-browser-status"),ie=document.getElementById("gateway-context-file"),oe=document.getElementById("gateway-detail"),ae=document.getElementById("gateway-install-btn"),re=document.getElementById("gateway-refresh-btn"),se=document.getElementById("gateway-skill-grid"),le=document.getElementById("messages"),ce=document.getElementById("active-tasks-list"),de=document.getElementById("chat-form"),me=document.getElementById("chat-input"),ue=document.getElementById("send-btn"),ge=document.getElementById("fx-risk-daily-loss-limit-jpy"),pe=document.getElementById("coin-risk-daily-loss-limit-jpy"),fe=document.getElementById("crypto-api-key"),ye=document.getElementById("crypto-api-secret"),we=document.getElementById("fx-api-key"),be=document.getElementById("fx-api-secret"),he=document.getElementById("toggle-crypto-key-btn"),ke=document.getElementById("toggle-crypto-secret-btn"),xe=document.getElementById("toggle-fx-key-btn"),ve=document.getElementById("toggle-fx-secret-btn"),Se=document.getElementById("save-fx-config-btn"),Ee=document.getElementById("save-coin-config-btn"),Ce=document.getElementById("fx-config-title"),Ie=document.getElementById("coin-config-title"),Ae=document.getElementById("fx-config-label-riskDailyLossLimitJpy"),Ne=document.getElementById("fx-config-label-fxApiKey"),Le=document.getElementById("fx-config-label-fxApiSecret"),Be=document.getElementById("coin-config-label-riskDailyLossLimitJpy"),$e=document.getElementById("coin-config-label-cryptoApiKey"),Me=document.getElementById("coin-config-label-cryptoApiSecret"),Te=document.getElementById("ai-model"),Pe=document.getElementById("host-log-output-dir"),De=document.getElementById("save-ai-config-btn"),Ue=document.getElementById("codex-login-btn"),Fe=document.getElementById("codex-copy-login-btn"),je=document.getElementById("codex-refresh-auth-btn"),_e=document.getElementById("codex-login-command"),ze=document.getElementById("logout-btn"),Oe=document.getElementById("login-btn"),He=document.getElementById("coin-symbol-select"),Je=document.getElementById("fx-symbol-select"),qe=document.getElementById("coin-kline-intervals"),Ge=document.getElementById("fx-kline-intervals"),Ke=document.getElementById("coin-kline-canvas"),Ye=document.getElementById("fx-kline-canvas"),Re=document.getElementById("coin-market-icon"),We=document.getElementById("fx-market-icon"),Xe=document.getElementById("coin-order-bid"),Ve=document.getElementById("coin-order-ask"),Qe=document.getElementById("coin-order-spread"),Ze=document.getElementById("fx-order-bid"),et=document.getElementById("fx-order-ask"),tt=document.getElementById("fx-order-spread"),nt=document.getElementById("coin-order-qty"),it=document.getElementById("fx-order-qty"),ot=document.getElementById("coin-buy-btn"),at=document.getElementById("coin-sell-btn"),rt=document.getElementById("fx-buy-btn"),st=document.getElementById("fx-sell-btn"),lt=document.getElementById("coin-required-amount"),ct=document.getElementById("fx-required-amount"),dt=document.getElementById("coin-account-info-refresh-btn"),mt=document.getElementById("fx-account-info-refresh-btn"),ut=document.getElementById("coin-account-pnl"),gt=document.getElementById("coin-account-margin"),pt=document.getElementById("coin-account-available"),ft=document.getElementById("coin-account-margin-ratio"),yt=document.getElementById("fx-account-pnl"),wt=document.getElementById("fx-account-margin"),bt=document.getElementById("fx-account-available"),ht=document.getElementById("fx-account-margin-ratio"),kt=document.getElementById("coin-position-summary-body"),xt=document.getElementById("coin-position-list-body"),vt=document.getElementById("fx-position-summary-body"),St=document.getElementById("fx-position-list-body"),Et=document.getElementById("coin-position-summary-refresh-btn"),Ct=document.getElementById("coin-position-list-refresh-btn"),It=document.getElementById("fx-position-summary-refresh-btn"),At=document.getElementById("fx-position-list-refresh-btn"),Nt=Array.from(document.querySelectorAll(".tab-btn")),Lt=Array.from(document.querySelectorAll(".tab-panel"));let Bt="15m",$t="15m",Mt=String(He?.value||"BTC_JPY").trim().toUpperCase(),Tt=String(Je?.value||"USD_JPY").trim().toUpperCase(),Pt=null,Dt=null,Ut=null,Ft=null,jt=!1,_t=!1,zt=!1,Ot=!1,Ht=!1,Jt="",qt="",Gt=0,Kt=0,Yt=null,Rt=null,Wt=[],Xt=!1,Vt=null,Qt={gatewayMcpStatus:"unknown",chromiumStatus:"unknown"};const Zt=new Map,en={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1},tn={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1};let nn=[],on=[];const an=new Map;let rn=null;const sn=e=>e&&"object"==typeof e?e:{},ln="weget.bot.sidebar.collapsed",cn=e=>{i.classList.toggle("sidebar-collapsed",e),a&&(a.textContent=e?"▶":"◀",a.setAttribute("aria-label",e?"Expand menu":"Collapse menu")),o&&o.setAttribute("data-collapsed",e?"true":"false")},dn=e=>{for(const t of Nt)t.classList.toggle("is-active",t.dataset.tab===e);for(const t of Lt)t.classList.toggle("is-active",t.id===`tab-${e}`)};for(const e of Nt)e.addEventListener("click",()=>dn(e.dataset.tab||"coin"));a?.addEventListener("click",()=>{const e=!i.classList.contains("sidebar-collapsed");cn(e);try{window.localStorage.setItem(ln,String(e))}catch{}});const mn=e=>{const n=jt!==e;jt=e,t.classList.toggle("hidden",e),i.classList.toggle("hidden",!e),e&&di(),e?n&&Mi():(Rn(),_t=!1)},un=e=>{const t=String(e||"disconnected").trim().toLowerCase();m.textContent=t,m.classList.remove("status-connected","status-disconnected","status-connecting"),"connected"!==t?"reconnecting"!==t&&"connecting"!==t?m.classList.add("status-disconnected"):m.classList.add("status-connecting"):m.classList.add("status-connected")},gn=(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})},pn=e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,fn=e=>`${e>0?"+":""}${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,yn=e=>!Number.isFinite(e)||e<=0||e>1e5?"- %":(e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:2})} %`)(e),wn=e=>{const t=e instanceof Date?e:new Date(e);if(Number.isNaN(t.getTime()))return"";return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`},bn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t);return Number.isNaN(n.getTime())?t:n.toLocaleString("ja-JP",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})},hn=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),kn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t).getTime();if(!Number.isFinite(n))return t;const i=Math.max(0,Math.floor((Date.now()-n)/1e3));if(i<5)return"just now";if(i<60)return`${i}s ago`;const o=Math.floor(i/60);if(o<60)return`${o}m ago`;const a=Math.floor(o/60);return a<24?`${a}h ago`:`${Math.floor(a/24)}d ago`},xn=()=>{if(!ce)return;const e=Array.from(an.values()).sort((e,t)=>{const n=new Date(String(e.lastProgressAt||e.startedAt||0)).getTime();return new Date(String(t.lastProgressAt||t.startedAt||0)).getTime()-n});e.length?ce.innerHTML=e.map(e=>{const t=hn(e.title||e.prompt||e.taskId),n=hn(e.status||"Running"),i=hn(e.source||"-"),o=hn(e.channel||"-"),a=hn(kn(e.startedAt)),r=hn(kn(e.lastProgressAt)),s=hn(e.conversationId||"-"),l=hn(e.lineUserId||"-");return`\n <section class="active-task-card" data-task-id="${hn(e.taskId)}">\n <div class="active-task-meta">\n <span class="active-task-badge">${o}</span>\n <span class="active-task-badge">${i}</span>\n </div>\n <div class="active-task-title">${t}</div>\n <div class="active-task-status">${n}</div>\n <div class="active-task-subtext">Started ${a} · Last update ${r}</div>\n <div class="active-task-subtext">Task ${hn(e.taskId)} · Conv ${s}</div>\n ${e.lineUserId?`<div class="active-task-subtext">LINE user ${l}</div>`:""}\n <div class="active-task-actions">\n <button type="button" class="secondary active-task-cancel-btn" data-task-id="${hn(e.taskId)}" ${!1===e.canCancel?"disabled":""}>Cancel</button>\n </div>\n </section>\n `}).join(""):ce.innerHTML='<div class="active-task-empty">No active tasks.</div>'},vn=e=>{if(e)if("snapshot"!==e.type){if("upsert"===e.type&&e.task?.taskId)return an.set(e.task.taskId,e.task),void xn();"remove"===e.type&&e.taskId&&(an.delete(e.taskId),xn())}else{an.clear();for(const t of Array.isArray(e.tasks)?e.tasks:[])t?.taskId&&an.set(t.taskId,t);xn()}},Sn=e=>{const t=String(e||"").trim();if(!t)return{cleaned:"",impact:"",direction:""};const n=t.indexOf("\n"),i=n>=0?t.slice(0,n).trim():t,o=n>=0?t.slice(n+1).trim():"";if(i.startsWith("{")&&i.endsWith("}"))try{const e=JSON.parse(i);return{cleaned:o,impact:String(e.impact||"").trim().toLowerCase(),direction:String(e.usd_jpy_direction||"").trim().toLowerCase()}}catch{}return{cleaned:t,impact:"",direction:""}},En=e=>{const t=String(e||"").trim().toLowerCase();if(!t)return"-";const n="high"===t?"High":"medium"===t?"Medium":"low"===t?"Low":t;return`<span class="macro-impact-badge ${hn(t)}">${hn(n)}</span>`},Cn=e=>{const t=Math.max(0,Math.min(3,Math.round(Number(e||0))));return t<=0?"-":`<span class="macro-importance-stars">${"★".repeat(t)}</span>`},In=e=>{const t=String(e||"").trim().toLowerCase();return t?"up"===t?'<span class="macro-direction up">↑↑</span>':"down"===t?'<span class="macro-direction down">↓↓</span>':"sideways"===t?'<span class="macro-direction flat">→</span>':hn(t):"-"},An=e=>{const t=String(e||"").split(/\r?\n/).map(e=>e.trim()).filter(Boolean);return t.length?t.map(e=>`<div class="macro-analyzer-line">${hn(e)}</div>`).join(""):"-"},Nn=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(/\.$/,"")},Ln=e=>{const t=String(e||"").trim(),n=t.indexOf(".");return n>=0?t.length-n-1:0},Bn=(e,t)=>{const n=String(e||"").trim(),i=n.startsWith("-"),o=i?n.slice(1):n,[a,r=""]=o.split("."),s=(r+"0".repeat(t)).slice(0,t),l=BigInt((a||"0")+s);return i?-l:l},$n=e=>{const t=Pn("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),n=sn(t?.configJson);return"fx"===e?Boolean(String(n.fxApiKey||we.value||"").trim()&&String(n.fxApiSecret||be.value||"").trim()):Boolean(String(n.cryptoApiKey||fe.value||"").trim()&&String(n.cryptoApiSecret||ye.value||"").trim())},Mn=({symbol:e,quote:t,orderBidNode:n,orderAskNode:i,orderSpreadNode:o})=>{if(!t)return n.textContent="-",i.textContent="-",void(o.textContent="-");const a=gn(t.bid),r=gn(t.ask);n.textContent=a,i.textContent=r,o.textContent=gn(t.spread,8)},Tn=e=>{if("coin"===e){const e=Number(String(nt.value||"").trim()),t=Yt;return!Number.isFinite(e)||e<=0||null===t||!Number.isFinite(t)?void(lt.textContent="- 円"):void(lt.textContent=pn(e*t/2))}const t=Number(String(it.value||"").trim()),n=Rt;!Number.isFinite(t)||t<=0||null===n||!Number.isFinite(n)?ct.textContent="- 円":ct.textContent=pn(t*n/20)},Pn=e=>Wt.find(t=>t.name===e),Dn=e=>{const t=Pn(e);if(!t)return!1;const n=String(t.installStatus||"").trim().toLowerCase();return t.enabled&&"active"===n},Un=e=>Dn("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),Fn=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?(Mn({symbol:n,quote:o,orderBidNode:Xe,orderAskNode:Ve,orderSpreadNode:Qe}),Yt=o?.ask??null,Tn("coin")):(Mn({symbol:n,quote:o,orderBidNode:Ze,orderAskNode:et,orderSpreadNode:tt}),Rt=o?.ask??null,Tn("fx")),o},jn=(e,t)=>{const n=String(t||"").trim().toUpperCase();if(!n)return;if("coin"===e){const e=n.replace("_","-").toLowerCase();return void(Re.src=`https://coin.z.com/jp/member/imgs/icon-${e}.svg`)}const[i,o]=n.split("_"),a="JPY"===o?i:`${i}_${o}`;We.src=`https://coin.z.com/jp/member/imgs/fx/icon_${a}.svg`},_n=e=>"5m"===e?3e5:"15m"===e?9e5:"30m"===e?18e5:"1h"===e?36e5:6e4,zn=e=>"coin"===e?en:tn,On=e=>"coin"===e?Ke:Ye,Hn=e=>"coin"===e?Mt:Tt,Jn=e=>{const t=Hn(e),n=("coin"===e?nn:on).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},qn=e=>{const t=zn(e);t.candles.length&&ui(On(e),t.candles,Jn(e))},Gn=(e,t)=>{if(!t)return;const n=zn(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),ui(On(e),n.candles,Jn(e)))},Kn=async e=>{const t=zn(e);if(!t.interval||!t.symbol||t.fetching)return;if(t.symbol!==Hn(e))return;if(!(Math.floor(Date.now()/_n(t.interval))<=t.lastFetchBucket)){t.fetching=!0;try{"coin"===e?await Li():await Bi()}finally{t.fetching=!1}}},Yn=async e=>{if(Un(e))if("coin"!==e){if(!Ht){Ht=!0;try{const e=await Fn("fx",Tt);Gn("fx",e),await Kn("fx")}catch(e){const t=ai(e instanceof Error?e.message:String(e)),n=Date.now();(t!==qt||n-Kt>15e3)&&(ni("[fx] quote poll failed",{error:t}),qt=t,Kt=n)}finally{Ht=!1}}}else{if(Ot)return;Ot=!0;try{const e=await Fn("coin",Mt);Gn("coin",e),await Kn("coin")}catch(e){const t=ai(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Jt||n-Gt>15e3)&&(ni("[coin] quote poll failed",{error:t}),Jt=t,Gt=n)}finally{Ot=!1}}},Rn=()=>{Ut&&(clearInterval(Ut),Ut=null),Ft&&(clearInterval(Ft),Ft=null)},Wn=async e=>{if(window.botApi?.getAccountMetrics){if((!e||"coin"===e)&&Un("coin"))if($n("coin")){const e=await window.botApi.getAccountMetrics({market:"coin"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);ut.textContent=n>0?fn(i):pn(0),gt.textContent=pn(n),pt.textContent=pn(t),ft.textContent=yn(Number(e.marginRatio||0))}else ni("[coin] account metrics fetch failed",{error:e?.error||"unknown error"})}else ut.textContent=pn(0),gt.textContent="- 円",pt.textContent="- 円",ft.textContent="- %";if((!e||"fx"===e)&&Un("fx"))if($n("fx")){const e=await window.botApi.getAccountMetrics({market:"fx"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);yt.textContent=n>0?fn(i):pn(0),wt.textContent=pn(n),bt.textContent=pn(t),ht.textContent=yn(Number(e.marginRatio||0))}else ni("[fx] account metrics fetch failed",{error:e?.error||"unknown error"})}else yt.textContent=pn(0),wt.textContent="- 円",bt.textContent="- 円",ht.textContent="- %"}},Xn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").toUpperCase(),o=n.replace("_","/"),a=String(i.side||"-"),r=String(i.size||"").trim(),s=Number(r||0),l=Number(i.price||0),c=Number(i.lossGain||0),d=Number(i.totalSwap||0),m=String(i.timestamp||"-"),u=Number(i.positionId||0),g=Number.isFinite(u)&&u>0&&r?`data-market="${e}" data-symbol="${n}" data-side="${String(a||"").toUpperCase()}" data-position-id="${u}" data-size="${r}"`:"disabled",p=document.createElement("tr");p.innerHTML=`\n <td><button type="button" class="close-btn" ${g}>決済</button></td>\n <td>${o}<br />${a}</td>\n <td>${s.toLocaleString("ja-JP")}</td>\n <td>${l.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${fn(c)}<br />${fn(d)}</td>\n <td>${m.replace("T"," ").slice(0,19)}</td>\n `,t.appendChild(p)}}else t.innerHTML='<tr><td colspan="6" class="empty-cell">対象のお取引はございません。</td></tr>'},Vn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").replace("_","/"),o=String(i.side||"-"),a=Number("fx"===e?i.sumPositionSize||0:i.sumPositionQuantity||0),r=Number(i.averagePositionRate||0),s=Number(i.positionLossGain||0),l=Number("fx"===e&&i.sumTotalSwap||0),c=document.createElement("tr");c.innerHTML=`\n <td><button type="button" class="close-btn">決済</button></td>\n <td>${n}<br />${o}</td>\n <td>${a.toLocaleString("ja-JP")}</td>\n <td>${r.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${fn(s)}<br />${fn(l)}</td>\n `,t.appendChild(c)}}else t.innerHTML='<tr><td colspan="5" class="empty-cell">対象のお取引はございません。</td></tr>'},Qn=async e=>{if(window.botApi?.getPositionSummary&&window.botApi?.getOpenPositions){if((!e||"coin"===e)&&Un("coin"))if($n("coin")){const e=await window.botApi.getPositionSummary({market:"coin",symbol:Mt});e?.ok?(nn=Array.isArray(e.items)?e.items:[],Vn("coin",kt,nn),qn("coin")):(ni("[coin] position summary fetch failed",{error:e?.error||"unknown error"}),nn=[],Vn("coin",kt,[]),qn("coin"))}else nn=[],Vn("coin",kt,[]),qn("coin");if((!e||"fx"===e)&&Un("fx"))if($n("fx")){const e=await window.botApi.getPositionSummary({market:"fx"});e?.ok?(on=Array.isArray(e.items)?e.items:[],Vn("fx",vt,on),qn("fx")):(ni("[fx] position summary fetch failed",{error:e?.error||"unknown error"}),on=[],Vn("fx",vt,[]),qn("fx"))}else on=[],Vn("fx",vt,[]),qn("fx");if((!e||"coin"===e)&&Un("coin"))if($n("coin")){const e=await window.botApi.getOpenPositions({market:"coin",symbol:Mt,page:1,count:100});e?.ok?Xn("coin",xt,Array.isArray(e.items)?e.items:[]):(ni("[coin] open positions fetch failed",{error:e?.error||"unknown error"}),Xn("coin",xt,[]))}else Xn("coin",xt,[]);if((!e||"fx"===e)&&Un("fx"))if($n("fx")){const e=await window.botApi.getOpenPositions({market:"fx",count:100});e?.ok?Xn("fx",St,Array.isArray(e.items)?e.items:[]):(ni("[fx] open positions fetch failed",{error:e?.error||"unknown error"}),Xn("fx",St,[]))}else Xn("fx",St,[])}},Zn=async(e,t)=>{const n=window.botApi;if(!n?.placeFxOrder)return;const i=String(it.value||"").trim();i?await mi(t,async()=>{const t=await n.placeFxOrder({symbol:Tt,side:e,size:i});if(!t?.ok){const n=ai(t?.error||"fx order failed");return ni("[fx] order failed",{symbol:Tt,side:e,size:i,error:n}),ri("error",n),void await si("fx.order",n,t)}ni("[fx] order placed",{symbol:Tt,side:e,size:i,result:t.result||null}),ri("success",`${e} 注文を送信しました。`),await Wn("fx"),await Qn("fx")}):ri("error","数量を入力してください。")},ei=async(e,t)=>{const n=window.botApi;if(!n?.placeCoinOrder)return;const i=Nn(String(nt.value||""));if(!i)return void ri("error","数量の形式が不正です。");const o=((e,t)=>{const n=Zt.get(e);if(!n)return{ok:!0};const i=Nn(n.minOrderSize),o=Nn(n.sizeStep),a=Nn(n.maxOrderSize);if(!i||!o)return{ok:!0};const r=Math.max(Ln(t),Ln(i),Ln(o),Ln(a)),s=Bn(t,r),l=Bn(i,r),c=Bn(o,r);if(s<l)return{ok:!1,reason:`${e} の最小数量は ${i} です。`};if(c>0n&&s%c!==0n)return{ok:!1,reason:`${e} の数量刻みは ${o} です。`};if(a&&s>Bn(a,r))return{ok:!1,reason:`${e} の最大数量は ${a} です。`};return{ok:!0}})(Mt,i);o.ok?await mi(t,async()=>{const t=await n.placeCoinOrder({symbol:Mt,side:e,size:i});if(!t?.ok){const n=ai(t?.error||"coin order failed");return ni("[coin] order failed",{symbol:Mt,side:e,size:i,error:n}),ri("error",n),void await si("coin.order",n,t)}ni("[coin] order placed",{symbol:Mt,side:e,size:i,result:t.result||null}),ri("success",`${e} 注文を送信しました。`),await Wn("coin"),await Qn("coin")}):ri("error",o.reason||"数量が取引ルールに一致しません。")},ti=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"},ni=(e,t=null,n=null)=>{if(window.botApi?.writeLog&&window.botApi.writeLog({level:ti(e),source:"ui.log",message:e,details:{data:t,ts:n||(new Date).toISOString()}}).catch(()=>{}),!Z)return;const i=document.createElement("div");i.className="log-entry";const o=document.createElement("div");o.className="log-line";const a=document.createElement("span");if(a.className="log-ts",a.textContent=n||(new Date).toISOString(),o.appendChild(a),o.append(document.createTextNode(e||"")),i.appendChild(o),null!=t&&""!==t){const e=document.createElement("pre");e.className="status-output",e.textContent="string"==typeof t?t:JSON.stringify(t,null,2),i.appendChild(e)}for(Z.appendChild(i);Z.children.length>300;)Z.removeChild(Z.firstChild);Z.scrollTop=Z.scrollHeight},ii=(e,t,n="")=>{const i=document.createElement("div");i.className=`msg ${e}`;let o=n||("user"===e?"You":"Assistant");"line-user"===e&&(o=n||"LINE User"),"line-assistant"===e&&(o=n||"AI"),i.textContent=`${o}: ${t}`,le.appendChild(i),le.scrollTop=le.scrollHeight},oi=()=>{const e=le.querySelector(".msg.assistant-thinking");e&&e.remove()},ai=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:"处理失败,请稍后重试。"},ri=(e,t)=>{Pt&&(clearTimeout(Pt),Pt=null),r.classList.remove("hidden","notice-error","notice-success"),r.classList.add("error"===e?"notice-error":"notice-success"),r.textContent=t,Pt=setTimeout(()=>{li()},"success"===e?2200:4200)},si=async(e,t,n=null)=>{window.botApi?.writeErrorLog&&await window.botApi.writeErrorLog({source:e,message:String(t||""),details:n})},li=()=>{Pt&&(clearTimeout(Pt),Pt=null),r.classList.add("hidden"),r.classList.remove("notice-error","notice-success"),r.textContent=""},ci=(e,t)=>{Dt&&(clearTimeout(Dt),Dt=null),n.classList.remove("hidden","notice-error","notice-success"),n.classList.add("error"===e?"notice-error":"notice-success"),n.textContent=t,Dt=setTimeout(()=>{di()},"success"===e?2200:4200)},di=()=>{Dt&&(clearTimeout(Dt),Dt=null),n.classList.add("hidden"),n.classList.remove("notice-error","notice-success"),n.textContent=""},mi=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=ai(e instanceof Error?e.message:String(e));ni("[ui] action failed",{error:t}),ri("error",t)}finally{e.classList.remove("is-loading"),e.textContent=n,e.disabled=!1}},ui=(e,t,n=[])=>{const i=e.getContext("2d");if(!i)return;const o=e.width,a=e.height;if(i.clearRect(0,0,o,a),!t.length)return;const r=Math.max(...t.map(e=>e.high)),s=Math.min(...t.map(e=>e.low)),l=n.map(e=>e.price).filter(e=>Number.isFinite(e)),c=l.length?Math.max(r,...l):r,d=l.length?Math.min(s,...l):s,m=Math.max(1e-8,c-d),u=10,g=10,p=o-10-62-8,f=a-10-24-8,y=Math.max(2,p/t.length),w=e=>g+(c-e)/m*f,b=e=>e>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:0}):e>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:3}):e.toLocaleString("en-US",{minimumFractionDigits:5,maximumFractionDigits:5});i.strokeStyle="#c9d4ea",i.strokeRect(.5,.5,o-1,a-1),i.strokeStyle="#e3eaf5",i.lineWidth=1;for(let e=0;e<=4;e+=1){const t=g+f/4*e;i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke()}t.forEach((e,t)=>{const n=u+t*y+.5*y,o=w(e.high),a=w(e.low),r=w(e.open),s=w(e.close),l=e.close>=e.open;i.strokeStyle=l?"#1f9d55":"#d64545",i.fillStyle=l?"#1f9d55":"#d64545",i.lineWidth=1,i.beginPath(),i.moveTo(n,o),i.lineTo(n,a),i.stroke();const c=n-.3*y,d=Math.max(1,.6*y),m=Math.min(r,s),g=Math.max(1,Math.abs(s-r));i.fillRect(c,m,d,g)});for(const e of n){const t=w(e.price);i.save(),i.setLineDash([4,4]),i.strokeStyle=e.color,i.lineWidth=1,i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke(),i.restore(),i.font="12px Segoe UI";const n=`${e.label} ${b(e.price)}`,o=Math.ceil(i.measureText(n).width)+10,a=14,r=Math.max(12,Math.min(g+f-18,t-9));i.fillStyle=e.color,i.fillRect(a,r,o,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(n,a+5,r+9)}const h=t[t.length-1],k=h?.close;if(Number.isFinite(k)){const e=w(Number(k));i.save(),i.setLineDash([5,4]),i.strokeStyle="#2b79ff",i.lineWidth=1,i.beginPath(),i.moveTo(u,e),i.lineTo(u+p,e),i.stroke(),i.restore();const t=b(Number(k));i.font="12px Segoe UI";const n=Math.ceil(i.measureText(t).width)+10,o=u+p+6,a=Math.max(12,Math.min(g+f-18,e-9));i.fillStyle="#2b79ff",i.fillRect(o,a,n,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(t,o+5,a+9)}i.fillStyle="#66758d",i.font="12px Segoe UI",i.textBaseline="middle",i.textAlign="left";for(let e=0;e<=4;e+=1){const t=c-m/4*e,n=g+f/4*e;i.fillText(b(t),u+p+8,n)}const x=e=>{const t=new Date(e>1e12?e:1e3*e);if(Number.isNaN(t.getTime()))return"-";const n=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${n}:${i}`};i.textAlign="center",i.textBaseline="top";for(let e=0;e<=4;e+=1){const n=Math.min(t.length-1,Math.floor((t.length-1)*(e/4))),o=u+p*e/4;i.fillText(x(t[n]?.time||0),o,g+f+6)}},gi=e=>{g.innerHTML="";const t=sn(e),n=sn(t.runtime),i=t.session?sn(t.session):null,o=[{key:"Codex Auth",value:String(n.codexAuthStatus||"unknown")},{key:"Gateway MCP",value:Qt.gatewayMcpStatus},{key:"Default Model",value:String(n.aiModel||"-")},{key:"Bot ID",value:String(i?.botId||"-")},{key:"User ID",value:String(i?.userId||"-")},{key:"Email",value:String(i?.email||"-")},{key:"Capabilities",value:Array.isArray(n.capabilities)?n.capabilities.map(e=>String(e)).join(", "):"-"},{key:"Active Skills",value:Array.isArray(n.activeSkills)&&n.activeSkills.map(e=>{const t=sn(e);return String(t.displayName||t.name||"").trim()}).filter(Boolean).join(", ")||"-"},{key:"Login API",value:String(n.loginApiUrl||"-")},{key:"Chromium",value:Qt.chromiumStatus},{key:"GMO FX API State",value:String(n.gmoFxApiState||"unknown")},{key:"GMO Coin API State",value:String(n.gmoCoinApiState||"unknown")}],a=(e,t)=>{const n=String(t||"").trim().toLowerCase();if("Default Model"!==e&&n&&"-"!==n)return"unknown"===n?"warn":["logged_in","configured","installed","ready","ok","connected","enabled"].includes(n)?"ok":["missing","error","failed","disconnected","disabled","not_installed","invalid"].includes(n)?"error":void 0};for(const e of o)e.tone=a(e.key,e.value);for(const{key:e,value:t,tone:n}of o){const i=document.createElement("div");i.className="runtime-key",i.textContent=e;const o=document.createElement("div");if(o.className="runtime-value",n){const e=document.createElement("span");e.className=`runtime-status runtime-status-${n}`,e.textContent=t,o.appendChild(e)}else o.textContent=t;g.appendChild(i),g.appendChild(o)}},pi=(e,t)=>{const{tabBtn:n,titleNode:i,packageNode:o,enabledNode:a,versionNode:r,fallbackTitle:s,fallbackPackage:l}=t,c=String(e?.ui?.surfaceTitle||e?.displayName||s).trim()||s,d=e?.name||l,m=String(e?.ui?.tabLabel||d.replace("@ai.weget.jp/","")).trim()||d.replace("@ai.weget.jp/",""),u=!!e&&Boolean(e.enabled),g=String(e?.installStatus||"uninstalled").trim()||"uninstalled",p=String(e?.configuredVersion||e?.version||"-").trim()||"-";if(n){const e=n.querySelector(".tab-btn-label");e&&(e.textContent=m),n.classList.toggle("is-disabled",!u)}i&&(i.textContent=c),o&&(o.textContent=d),a&&(a.textContent=u?"enabled":"disabled",a.classList.toggle("is-disabled",!u)),r&&(r.textContent=`${g} · ${p}`)},fi=(e,t)=>{t.titleNode&&(t.titleNode.textContent=String(e?.ui?.configTitle||t.fallbackTitle).trim()||t.fallbackTitle);const n=Array.isArray(e?.ui?.configFields)&&e.ui?.configFields||[];for(const[e,i]of Object.entries(t.fields)){const t=n.find(t=>t.key===e);t?.label&&i.labelNode&&(i.labelNode.textContent=t.label),void 0!==t?.placeholder&&(i.inputNode.placeholder=t.placeholder||"")}},yi=(e,t)=>{if(e){if(e.innerHTML="",!t.length){const t=document.createElement("span");return t.className="muted",t.textContent="No macro snapshot cards available.",void e.appendChild(t)}for(const n of t){const t=document.createElement("div");t.className="macro-chip"+("down"===n.trend?" is-negative":"");const i=document.createElement("div");i.className="macro-chip-code",i.textContent=n.code||"-";const o=document.createElement("div");o.className="macro-chip-value"+(n.highlightValue?" "+("down"===n.trend?"is-negative":"is-positive"):""),o.textContent=n.value||"-";const a=document.createElement("div");a.className=n.usePill?"macro-chip-pill":"macro-chip-delta"+("down"===n.trend?" is-negative":""),a.textContent=n.delta||"-",t.append(i,o,a),e.appendChild(t)}}},wi=(e,t,n)=>{if(!e)return;if(e.innerHTML="",!t.length){const t=document.createElement("div");return t.className="macro-treemap-empty",t.textContent=n,void e.appendChild(t)}const i=[...t].map(e=>({symbol:String(e.symbol||"").trim(),change:Number(e.change||0),weight:Math.max(1,Math.abs(Number(e.change||0)))})).filter(e=>e.symbol).sort((e,t)=>Math.abs(t.change)-Math.abs(e.change)).slice(0,12),o=[],a=i.reduce((e,t)=>e+t.weight,0)||1,r=(e,t,n,i,a,s)=>{if(!e.length)return;if(1===e.length)return void o.push({item:e[0],x:t,y:n,width:i,height:a});const l=e.reduce((e,t)=>e+t.weight,0);let c=1,d=e[0].weight;for(;c<e.length-1&&d<l/2;)c+=1,d+=e[c-1].weight;const m=e.slice(0,c),u=e.slice(c),g=d/l;if(s){const e=i*g;return r(m,t,n,e,a,!s),void r(u,t+e,n,i-e,a,!s)}const p=a*g;r(m,t,n,i,p,!s),r(u,t,n+p,i,a-p,!s)};r(i,0,0,100,100,!0);for(const{item:t,x:n,y:i,width:r,height:s}of o){const o=document.createElement("div"),l=Math.min(.88,.22+t.weight/Math.max(.18*a,1)),c=Math.max(8,Math.min(r,s)),d=Math.max(80,r*s),m=Math.max(12,Math.min(30,Math.round(.42*c+.16*Math.sqrt(d)))),u=Math.max(11,Math.min(22,Math.round(.72*m))),g=Math.max(2,Math.min(6,Math.round(.16*m)));o.className="macro-treemap-tile "+(t.change>=0?"up":"down"),o.style.setProperty("--tile-intensity",String(l)),o.style.setProperty("--tile-symbol-font-size",`${m}px`),o.style.setProperty("--tile-change-font-size",`${u}px`),o.style.setProperty("--tile-gap",`${g}px`),o.style.left=`${n}%`,o.style.top=`${i}%`,o.style.width=`${r}%`,o.style.height=`${s}%`,o.innerHTML=`\n <div class="macro-treemap-symbol">${hn(t.symbol.replace(/_JPY$/i,"").replace(/_USD$/i," $"))}</div>\n <div class="macro-treemap-change">${t.change>=0?"+":""}${t.change.toFixed(1)}%</div>\n `,e.appendChild(o)}},bi=e=>{if(q)if(q.innerHTML="",e.length)for(const t of e){const e=Sn(t.ai_analyzer||""),n=document.createElement("tr");n.className="macro-data-row",n.innerHTML=`\n <td>${hn(bn(t.date))}</td>\n <td>${hn(t.country||"-")}</td>\n <td class="macro-title-cell"><strong>${hn(t.title||"-")}</strong></td>\n <td>${Cn(Number(t.importance||0))}</td>\n <td>${hn(t.actual||"-")}</td>\n <td>${hn(t.forecast||"-")}</td>\n <td>${hn(t.previous||"-")}</td>\n <td>${In(e.direction)}</td>\n `;const i=document.createElement("tr");i.className="macro-ai-row",i.innerHTML=`\n <td colspan="8" class="macro-analyzer">${An(e.cleaned||"-")}</td>\n `,q.append(n,i)}else q.innerHTML='<tr><td colspan="8" class="empty-cell">No calendar rows for the selected date.</td></tr>'},hi=e=>{if(G)if(G.innerHTML="",e.length)for(const t of e){const e=Sn(t.ai_analyzer||""),n=String(t.url||"").trim(),i=document.createElement("tr");i.className="macro-data-row",i.innerHTML=`\n <td>${hn(bn(t.time))}</td>\n <td class="macro-title-cell"><strong>${hn(t.content||"-")}</strong></td>\n <td>${En(e.impact)}</td>\n <td>${In(e.direction)}</td>\n <td>${n?`<a class="macro-link" href="${hn(n)}" target="_blank" rel="noreferrer noopener">open</a>`:"-"}</td>\n `;const o=document.createElement("tr");o.className="macro-ai-row",o.innerHTML=`\n <td colspan="5" class="macro-analyzer">${An(e.cleaned||"-")}</td>\n `,G.append(i,o)}else G.innerHTML='<tr><td colspan="4" class="empty-cell">No news rows for the selected date.</td></tr>'},ki=e=>{F&&(F.textContent="-"),j&&(j.textContent="-"),_&&(_.textContent=e),yi(z,[]),yi(O,[]),wi(H,[],"No coin heatmap data."),wi(J,[],"No FX heatmap data."),bi([]),hi([])},xi=async({forceSnapshot:e=!1}={})=>{if(Xt)return;if(!window.botApi?.getMacroSnapshot||!window.botApi?.getMacroCalendar||!window.botApi?.getMacroNews)return void ki("Macro IPC bridge is not available in this host build.");const t=Pn("@ai.weget.jp/skill-macro-economy");if(!t||!t.enabled||"active"!==String(t.installStatus||"").trim().toLowerCase())return void ki("Macro Economy skill is disabled or not active.");if(!jt)return void ki("Login is required to load macro snapshot, calendar, and news.");Xt=!0;const n=String(K?.value||"").trim()||wn(new Date);K&&!K.value&&(K.value=n);try{const[t,i,o]=await Promise.all([window.botApi.getMacroSnapshot({force:e}),window.botApi.getMacroCalendar({dateKey:n}),window.botApi.getMacroNews({dateKey:n})]);if(!t.ok)throw new Error(t.error||"macro snapshot failed");if(!i.ok)throw new Error(i.error||"macro calendar failed");if(!o.ok)throw new Error(o.error||"macro news failed");const a=t.snapshot;F&&(F.textContent=bn(a?.as_of)),j&&(j.textContent=null==a?.fear_greed_score?"-":`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`),_&&(_.textContent=`Loaded macro data for ${n} from the local macro economy skill runtime.`);const r=Array.isArray(a?.coin_cards)?[...a.coin_cards]:[];null!=a?.fear_greed_score&&r.unshift({code:"FGI",value:`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`,delta:"Fear & Greed",trend:Number(a.fear_greed_score)>=50?"up":"down",points:[],usePill:!0}),yi(z,r),yi(O,Array.isArray(a?.fx_cards)?a?.fx_cards:[]),wi(H,Array.isArray(a?.coin_heatmap)?a?.coin_heatmap:[],"No coin heatmap data."),wi(J,Array.isArray(a?.fx_heatmap)?a?.fx_heatmap:[],"No FX heatmap data."),bi(Array.isArray(i.rows)?i.rows:[]),hi(Array.isArray(o.rows)?o.rows:[])}catch(e){ki(ai(e instanceof Error?e.message:String(e)))}finally{Xt=!1}},vi=(e,t)=>{W&&(W.textContent=e,W.classList.toggle("is-disabled","installed"!==e),W.classList.toggle("is-muted","unknown"===e)),X&&(X.textContent=t||"No Playwright browser detail available.")},Si=({browserStatus:e})=>{V&&(V.innerHTML="installed"!==e?"Run <code>npx playwright install chromium</code> on this machine.":"Chromium is ready for Playwright-backed browser tasks.")},Ei=async()=>{if(!window.botApi?.getPlaywrightBrowserStatus)return vi("unknown","Playwright browser check is not available in this host build."),void Si({browserStatus:"unknown"});const e=await window.botApi.getPlaywrightBrowserStatus(),t=e.ok&&e.status||"unknown";e.ok?vi(t,String(e.detail||"").trim()):vi("unknown",ai(e.error)),Si({browserStatus:t})},Ci=(e,t,n,i=!1)=>{e&&(e.textContent=t||"-",e.classList.toggle("is-disabled",!n&&!i),e.classList.toggle("is-muted",i))},Ii=async()=>{if(!window.botApi?.getGatewayStatus)return;const e=await window.botApi.getGatewayStatus();if(!e?.ok)return Qt={gatewayMcpStatus:"error",chromiumStatus:"error"},Ci(ee,"error",!1),Ci(te,"error",!1),Ci(ne,"error",!1),oe&&(oe.textContent=ai(e?.error||"gateway status failed")),void(Vt&&gi(Vt));Qt={gatewayMcpStatus:String(e.gatewayStatus?.status||"unknown"),chromiumStatus:String(e.browserStatus?.status||"unknown")},Ci(ee,String(e.codexAuth?.status||"unknown"),"logged_in"===String(e.codexAuth?.status||""),"unknown"===String(e.codexAuth?.status||"")),Ci(te,String(e.gatewayStatus?.status||"unknown"),"configured"===String(e.gatewayStatus?.status||""),"unknown"===String(e.gatewayStatus?.status||"")),Ci(ne,String(e.browserStatus?.status||"unknown"),"installed"===String(e.browserStatus?.status||""),"unknown"===String(e.browserStatus?.status||"")),ie&&(ie.textContent=`Context file: ${String(e.contextFilePath||"-")}`),oe&&(oe.textContent=JSON.stringify({codexAuth:e.codexAuth||null,gatewayStatus:e.gatewayStatus||null,browserStatus:e.browserStatus||null},null,2)),Vt&&gi(Vt),(e=>{if(!se)return;se.innerHTML="";const t=[{target:"gateway",title:"Gateway Core"},{target:"codex_macro",title:"Codex Macro Chain",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"browser",title:"Browser Skill",packageName:"@ai.weget.jp/skill-browser"},{target:"macro",title:"Macro Economy Skill",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"coin",title:"GMO Coin Skill",packageName:"@ai.weget.jp/skill-gmo-coin"},{target:"fx",title:"GMO FX Skill",packageName:"@ai.weget.jp/skill-gmo-fx"}];for(const n of t){const t=n.packageName?e.find(e=>e.name===n.packageName):null,i=document.createElement("section");i.className="browser-skill-card gateway-test-card",i.innerHTML=`\n <div class="gateway-test-head">\n <h3>${n.title}</h3>\n <span class="skill-surface-badge is-muted">${t?t.enabled?"enabled":"disabled":"system"}</span>\n </div>\n <div class="muted">${t?`${t.name} · ${t.installStatus}`:"Gateway MCP self-test"}</div>\n <div class="skill-tags">${t&&Array.isArray(t.tools)&&t.tools.length?t.tools.map(e=>`<span class="skill-tag">${hn(e)}</span>`).join(""):'<span class="muted">No declared tools</span>'}</div>\n <div class="row config-actions">\n <button type="button" class="secondary gateway-test-btn" data-target="${n.target}">Run Test</button>\n </div>\n <div class="gateway-test-result">\n <span class="skill-surface-badge is-muted gateway-test-badge" data-target="${n.target}">not run</span>\n <div class="muted gateway-test-summary" data-target="${n.target}">-</div>\n </div>\n `,se.appendChild(i)}})(Array.isArray(e.skills)?e.skills:[])},Ai=async()=>{if(!window.botApi?.getRuntimeInfo)return;const[e,t]=await Promise.all([window.botApi.getRuntimeInfo(),window.botApi.getSkills?window.botApi.getSkills():(async()=>({ok:!1,skills:[]}))()]);if(!e?.ok)return;const n=t?.ok&&t.skills||[];Wt=n,un(e.status||"disconnected"),e.session?(mn(!0),u.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`):(mn(!1),u.textContent="local mode (not logged in)"),Vt=e,gi(e),(e=>{if(!p)return;p.innerHTML="";const t=Array.isArray(e)?e:[];if(0===t.length){const e=document.createElement("div");return e.className="skill-empty",e.textContent="No managed skills are available in this bot host.",void p.appendChild(e)}for(const e of t){const t=document.createElement("section");t.className="skill-card"+(e.enabled?"":" is-disabled");const n=document.createElement("div");n.className="skill-card-head";const i=document.createElement("div");i.className="skill-card-title";const o=document.createElement("strong");o.textContent=e.displayName||e.name;const a=document.createElement("code");if(a.textContent=e.name,i.appendChild(o),i.appendChild(a),e.description){const t=document.createElement("div");t.className="muted",t.textContent=e.description,i.appendChild(t)}const r=document.createElement("div");r.className="skill-badges";const s=document.createElement("span");s.className="skill-badge "+(e.enabled?"enabled":"disabled"),s.textContent=e.enabled?"enabled":"disabled",r.appendChild(s);const l=document.createElement("span");l.className="skill-badge status",l.textContent=e.installStatus||"unknown",r.appendChild(l),n.appendChild(i),n.appendChild(r),t.appendChild(n);const c=e=>{const t=document.createElement("div");t.className="skill-card-section";const n=document.createElement("div");return n.className="skill-section-label",n.textContent=e,t.appendChild(n),t},d=c("Package"),m=document.createElement("code");m.className="skill-package-code",m.textContent=e.name,d.appendChild(m),t.appendChild(d);const u=c("Tools"),g=document.createElement("div");g.className="skill-tags";const f=Array.isArray(e.tools)?e.tools:[];if(f.length>0)for(const e of f){const t=document.createElement("span");t.className="skill-tag",t.textContent=e,g.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No tools declared",g.appendChild(e)}u.appendChild(g),t.appendChild(u);const y=c("Permissions");if(Array.isArray(e.permissions)&&e.permissions.length>0){const t=document.createElement("div");t.className="skill-tags";for(const n of e.permissions){const e=document.createElement("span");e.className="skill-tag permissions",e.textContent=n,t.appendChild(e)}y.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No permissions declared",y.appendChild(e)}t.appendChild(y);const w=c("State"),b=document.createElement("div");b.className="skill-meta";const h=[["Bundled Version",e.version||"-"],["Configured Version",e.configuredVersion||"-"],["UI",e.hasUi?"has ui":"no ui"],["Config Keys",String(Object.keys(e.configJson||{}).length)]];for(const[e,t]of h){const n=document.createElement("div");n.className="skill-meta-row";const i=document.createElement("span");i.textContent=e;const o=document.createElement("strong");o.textContent=t,n.appendChild(i),n.appendChild(o),b.appendChild(n)}w.appendChild(b),t.appendChild(w),p.appendChild(t)}})(n),pi(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{tabBtn:f,titleNode:h,packageNode:S,enabledNode:A,versionNode:$,fallbackTitle:"GMO Coin Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-coin"}),pi(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{tabBtn:y,titleNode:k,packageNode:E,enabledNode:N,versionNode:M,fallbackTitle:"GMO FX Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-fx"});const i=n.find(e=>"@ai.weget.jp/skill-browser"===e.name);pi(i,{tabBtn:w,titleNode:x,packageNode:C,enabledNode:L,versionNode:T,fallbackTitle:"Browser Skill",fallbackPackage:"@ai.weget.jp/skill-browser"}),((e,t,n)=>{if(e){if(e.innerHTML="",0===t.length){const t=document.createElement("span");return t.className="muted",t.textContent=n,void e.appendChild(t)}for(const n of t){const t=document.createElement("span");t.className="skill-tag",t.textContent=n,e.appendChild(t)}}})(U,Array.isArray(i?.tools)?i.tools:[],"No browser tools declared."),D&&(D.innerHTML="");const o=n.find(e=>"@ai.weget.jp/skill-macro-economy"===e.name);pi(o,{tabBtn:b,titleNode:v,packageNode:I,enabledNode:B,versionNode:P,fallbackTitle:"Macro Economy Skill",fallbackPackage:"@ai.weget.jp/skill-macro-economy"}),await Ii(),await Ei(),fi(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{titleNode:Ce,fallbackTitle:"FX Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Ae,inputNode:ge},fxApiKey:{labelNode:Ne,inputNode:we},fxApiSecret:{labelNode:Le,inputNode:be}}}),fi(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{titleNode:Ie,fallbackTitle:"Coin Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Be,inputNode:pe},cryptoApiKey:{labelNode:$e,inputNode:fe},cryptoApiSecret:{labelNode:Me,inputNode:ye}}}),e.session?(xi(),Mi()):ki("Login is required to load macro snapshot, calendar, and news.")};Se.addEventListener("click",async()=>{await mi(Se,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(ge.value||"0"),fxApiKey:String(we.value||"").trim(),fxApiSecret:String(be.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-fx",e);if(!t.ok)return ni(`[trade-config] fx save failed: ${t.error||"unknown"}`),ri("error",ai(t.error)),void await si("trade-config.fx.save",ai(t.error),t);ni("[trade-config] fx saved"),ri("success","FX skill 配置已保存到本机。"),await Ai()})}),Ee.addEventListener("click",async()=>{await mi(Ee,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(pe.value||"0"),cryptoApiKey:String(fe.value||"").trim(),cryptoApiSecret:String(ye.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-coin",e);if(!t.ok)return ni(`[trade-config] coin save failed: ${t.error||"unknown"}`),ri("error",ai(t.error)),void await si("trade-config.coin.save",ai(t.error),t);ni("[trade-config] coin saved"),ri("success","Coin skill 配置已保存到本机。"),await Ai()})}),De.addEventListener("click",async()=>{await mi(De,async()=>{if(!window.botApi?.saveAiConfig)return;const e={aiModel:String(Te.value||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(Pe.value||"").trim()},t=await window.botApi.saveAiConfig(e);if(!t.ok)return ni(`[ai-config] save failed: ${t.error||"unknown"}`),ri("error",ai(t.error)),void await si("ai-config.save",ai(t.error),t);ni("[ai-config] saved"),ri("success","AI 设置已保存。"),await Ai()})}),Ue.addEventListener("click",async()=>{await mi(Ue,async()=>{if(!window.botApi?.startCodexLogin)return;const e=await window.botApi.startCodexLogin();if(!e.ok)return ni(`[codex] login launch failed: ${e.detail||"unknown"}`),ri("error",ai(e.detail)),void await si("codex.login.launch",ai(e.detail),e);ni("[codex] login launched",{detail:e.detail}),ri("success",e.detail||"Codex login started."),setTimeout(()=>{Ai()},1500)})}),Fe.addEventListener("click",async()=>{const e=String(_e.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 ri("error","无法复制命令,请手动执行。");ni("[codex] login command copied"),ri("success","Codex 登录命令已复制。")}catch(e){const t=ai(e instanceof Error?e.message:String(e));ni("[codex] copy login command failed",{error:t}),ri("error",t)}}),je.addEventListener("click",async()=>{await mi(je,async()=>{await Ai(),ni("[codex] auth status refreshed"),ri("success","Codex 状态已刷新。")})}),Q?.addEventListener("click",async()=>{await mi(Q,async()=>{await Ei(),ni("[browser] chromium status refreshed"),ri("success","Browser status 已刷新。")})}),ae?.addEventListener("click",async()=>{await mi(ae,async()=>{if(!window.botApi?.installPlaywrightMcp)return;const e=await window.botApi.installPlaywrightMcp();if(!e.ok)return ni("[gateway] mcp install failed",{error:e.detail||e.error||"unknown"}),ri("error",ai(e.detail||e.error)),await si("gateway.install",ai(e.detail||e.error),e),void await Ii();ni("[gateway] mcp configured",{detail:e.detail}),ri("success",e.detail||"WeGet Gateway MCP configured."),await Ii()})}),re?.addEventListener("click",async()=>{await mi(re,async()=>{await Ii(),ri("success","Gateway status 已刷新。")})}),se?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".gateway-test-btn");if(!n)return;const i=String(n.dataset.target||"").trim().toLowerCase();"gateway"!==i&&"codex_macro"!==i&&"browser"!==i&&"macro"!==i&&"coin"!==i&&"fx"!==i||await(async(e,t)=>{const n=window.botApi;n?.runGatewaySelfTest&&await mi(t,async()=>{const t=await n.runGatewaySelfTest({target:e}),i=se?.querySelector(`.gateway-test-badge[data-target="${e}"]`),o=se?.querySelector(`.gateway-test-summary[data-target="${e}"]`);if(i){const e=Boolean(t?.ok);i.textContent=e?"OK":"NG",i.classList.toggle("is-muted",!1),i.classList.toggle("is-disabled",!e)}if(o){const e=String(t?.summary||t?.error||t?.detail||"-").trim()||"-",n=String(t?.logPath||"").trim();o.textContent=n?`${e} [log] ${n}`:e}if(!t?.ok){const n=ai(t?.summary||t?.error||t?.detail||`${e} test failed`);return ni("[gateway] self-test failed",{target:e,error:n,logPath:t?.logPath||""}),void ri("error",n)}ni("[gateway] self-test ok",{target:e,summary:t?.summary||"",logPath:t?.logPath||""}),ri("success",`${e} test completed.`)})})(i,n)}),Y?.addEventListener("click",async()=>{await mi(Y,async()=>{await xi({forceSnapshot:!0}),ni("[macro] snapshot refreshed"),ri("success","Macro snapshot 已刷新。")})}),R?.addEventListener("click",async()=>{await mi(R,async()=>{await xi(),ni("[macro] calendar and news refreshed",{dateKey:String(K?.value||"").trim()}),ri("success","Macro calendar / news 已刷新。")})}),K?.addEventListener("change",()=>{xi()}),ke.addEventListener("click",()=>{const e="password"===ye.type?"text":"password";ye.type=e,ke.textContent="password"===e?"显示":"隐藏"}),he.addEventListener("click",()=>{const e="password"===fe.type?"text":"password";fe.type=e,he.textContent="password"===e?"显示":"隐藏"}),ve.addEventListener("click",()=>{const e="password"===be.type?"text":"password";be.type=e,ve.textContent="password"===e?"显示":"隐藏"}),xe.addEventListener("click",()=>{const e="password"===we.type?"text":"password";we.type=e,xe.textContent="password"===e?"显示":"隐藏"}),e.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi)return;di();const t=l.value.trim(),n=s.value.trim(),i=c.value;if(t&&n&&i){Oe.disabled=!0,Oe.textContent="Loading...";try{const e=await window.botApi.login(t,i,n,d.checked);if(!e.ok)return ni(`[ui] login failed: ${e.error||"unknown"}`),void ci("error",ai(e.error));ni("[ui] login success"),ci("success","登录成功。"),ri("success","登录成功。"),await Ai()}finally{Oe.disabled=!1,Oe.textContent="Login"}}}),ze.addEventListener("click",async()=>{await mi(ze,async()=>{if(!window.botApi)return;const e=await window.botApi.logout();if(!e.ok)return ni(`[ui] logout failed: ${e.error||"unknown"}`),ri("error",ai(e.error)),void await si("auth.logout",ai(e.error),e);ni("[ui] logout"),ri("success","已登出。"),mn(!1),u.textContent="local mode (not logged in)",await Ai()})});const Ni=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 ni(`[trade] open ${e} kline failed: ${o.error||"unknown"}`),ri("error",ai(o.error)),void await si(`${e}.kline`,ai(o.error),o);const a=Array.isArray(o.candles)?o.candles:[],r=zn(e);r.symbol=String(o.symbol||t||"").toUpperCase(),r.interval=n,r.candles=a.map(e=>({time:Number(e.time||0),open:Number(e.open||0),high:Number(e.high||0),low:Number(e.low||0),close:Number(e.close||0)})),r.lastFetchBucket=Math.floor(Date.now()/_n(n)),ui(i,r.candles,Jn(e)),ni("[trade] kline rendered",{market:e,symbol:o.symbol||t,interval:o.interval||n,candles:a.length}),li()},Li=async()=>{await Ni({market:"coin",symbol:Mt,interval:Bt,canvas:Ke})},Bi=async()=>{await Ni({market:"fx",symbol:Tt,interval:$t,canvas:Ye})},$i=({container:e,getCurrent:t,setCurrent:n,onChange:i})=>{const o=Array.from(e.querySelectorAll(".interval-btn"));for(const e of o)e.addEventListener("click",async()=>{const a=String(e.dataset.interval||"").trim();if(a&&a!==t()){n(a);for(const t of o)t.classList.toggle("is-active",t===e);await i()}})};He.addEventListener("change",async()=>{Mt=String(He.value||"BTC_JPY").trim().toUpperCase(),jn("coin",Mt);try{await Fn("coin",Mt),await Li(),await Qn("coin")}catch(e){const t=ai(e instanceof Error?e.message:String(e));ri("error",t)}}),Je.addEventListener("change",async()=>{Tt=String(Je.value||"USD_JPY").trim().toUpperCase(),jn("fx",Tt);try{await Fn("fx",Tt),await Bi(),await Qn("fx")}catch(e){const t=ai(e instanceof Error?e.message:String(e));ri("error",t)}}),$i({container:qe,getCurrent:()=>Bt,setCurrent:e=>{Bt=e},onChange:Li}),$i({container:Ge,getCurrent:()=>$t,setCurrent:e=>{$t=e},onChange:Bi}),dt.addEventListener("click",async()=>{await mi(dt,async()=>{await Wn("coin")})}),mt.addEventListener("click",async()=>{await mi(mt,async()=>{await Wn("fx")})}),nt.addEventListener("input",()=>{Tn("coin")}),it.addEventListener("input",()=>{Tn("fx")}),xt.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeCoinPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),l=Nn(String(n.dataset.size||""));o&&Number.isFinite(s)&&!(s<=0)&&l?await mi(n,async()=>{const e=await i.closeCoinPosition({symbol:o,side:r,positionId:s,size:l});if(!e?.ok){const t=ai(e?.error||"coin close order failed");return ni("[coin] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,error:t}),ri("error",t),void await si("coin.closeOrder",t,e)}ni("[coin] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,result:e.result||null}),ri("success","決済注文を送信しました。"),await Wn("coin"),await Qn("coin")}):ri("error","決済対象データが不正です。")}),St.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeFxPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),l=String(n.dataset.size||"").trim();o&&Number.isFinite(s)&&!(s<=0)&&l?await mi(n,async()=>{const e=await i.closeFxPosition({symbol:o,side:r,positionId:s,size:l});if(!e?.ok){const t=ai(e?.error||"fx close order failed");return ni("[fx] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,error:t}),ri("error",t),void await si("fx.closeOrder",t,e)}ni("[fx] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:l,result:e.result||null}),ri("success","決済注文を送信しました。"),await Wn("fx"),await Qn("fx")}):ri("error","決済対象データが不正です。")}),ot.addEventListener("click",async()=>{await ei("BUY",ot)}),at.addEventListener("click",async()=>{await ei("SELL",at)}),rt.addEventListener("click",async()=>{await Zn("BUY",rt)}),st.addEventListener("click",async()=>{await Zn("SELL",st)}),Et.addEventListener("click",async()=>{await mi(Et,async()=>{await Qn("coin")})}),Ct.addEventListener("click",async()=>{await mi(Ct,async()=>{await Qn("coin")})}),It.addEventListener("click",async()=>{await mi(It,async()=>{await Qn("fx")})}),At.addEventListener("click",async()=>{await mi(At,async()=>{await Qn("fx")})}),de.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi?.sendChat)return;const t=String(me.value||"").trim();if(t){ii("user",t),me.value="",ue.disabled=!0,((e="AI")=>{oi();const t=document.createElement("div");t.className="msg assistant assistant-thinking";const n=document.createElement("div");n.className="msg-title",n.textContent=`${e}:`;const i=document.createElement("div");i.className="thinking-body";const o=document.createElement("span");o.className="thinking-text",o.textContent="Thinking";const a=document.createElement("span");a.className="thinking-dots",a.innerHTML="<span></span><span></span><span></span>",i.append(o,a),t.append(n,i),le.appendChild(t),le.scrollTop=le.scrollHeight})("AI");try{const e=await window.botApi.sendChat(t);if(!e.ok)return oi(),ii("assistant",`Error: ${e.error||"unknown"}`),ri("error",ai(e.error)),void await si("ai.chat",ai(e.error),e);li()}catch(e){oi();const t=ai(e instanceof Error?e.message:String(e));ii("assistant",`Error: ${t}`),ri("error",t),await si("ai.chat.exception",t,e)}finally{ue.disabled=!1}}}),window.addEventListener("error",e=>{const t=ai(e.error?.message||e.message||"Unknown UI error");ni("[ui] uncaught error",{error:t}),ri("error",t),si("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=ai(t);ni("[ui] unhandled rejection",{error:n}),ri("error",n),si("ui.unhandledrejection",n,e.reason)}),ce?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".active-task-cancel-btn");if(!n)return;const i=String(n.dataset.taskId||"").trim();if(!i||!window.botApi?.cancelActiveTask)return;n.disabled=!0;const o=await window.botApi.cancelActiveTask(i);if(!o?.ok)return n.disabled=!1,void ri("error",ai(o?.error||"Failed to cancel task"));ri("success",`Cancel requested for ${i}`)}),window.botApi&&(window.botApi.getActiveTasks?.().then(e=>{e?.ok&&vn({type:"snapshot",tasks:Array.isArray(e.tasks)?e.tasks:[]})}),window.botApi.onStatus(e=>{un(String(e.status||""))}),window.botApi.onTaskRuntime(e=>{vn(e)}),window.botApi.onChatEvent(e=>{if(!e)return;const t=sn(e);if("line_user"===t.type){const e=t.lineUserId?`LINE(${String(t.lineUserId)})`:"LINE";return void ii("line-user",String(t.text||""),e)}if("line_assistant"===t.type){oi();const e=t.lineUserId?`AI->LINE(${String(t.lineUserId)})`:"AI->LINE";return void ii("line-assistant",String(t.text||""),e)}"assistant_status"!==t.type?"assistant"===t.type&&(oi(),ii("assistant",String(t.text||""))):(e=>{const t=String(e||"").trim().replace(/\s+/g," ");if(!t)return;if(/^[{}[\],:]+$/.test(t))return;if(/^["']?[a-zA-Z0-9_-]+["']?\s*:\s*$/.test(t))return;const n=le.querySelector(".msg.assistant-thinking .thinking-text");n&&(n.textContent=t)})(String(t.status||"Thinking"))}),window.botApi.onTradeExecution(e=>{if(!e)return;const t=sn(e),n="coin"===String(t.market||"").trim().toLowerCase()?"coin":"fx",i=String(t.symbol||"").trim().toUpperCase(),o=String(t.side||"").trim().toUpperCase()||"-",a=String(t.settleType||"").trim().toUpperCase()||"OPEN",r=String(t.executionSize??"").trim(),s=String(t.executionPrice??"").trim(),l="CLOSE"===a?"決済":"新規",c=`${i} ${o} ${r}${s?` @ ${s}`:""}`;ni(`[${n}] execution filled`,{symbol:i,side:o,settleType:a,size:r,price:s,raw:t}),ri("success",`約定成功 (${l}) ${c}`.trim()),Wn(n).catch(()=>{}),Qn(n).catch(()=>{})})),rn=setInterval(()=>{xn()},15e3);const Mi=async()=>{if(jt&&!_t&&!zt){zt=!0;try{if(!await async function(){let e=0;if(jn("coin",Mt),jn("fx",Tt),Un("coin")){e+=1;try{await Fn("coin",Mt)}catch(e){const t=ai(e instanceof Error?e.message:String(e));ni("[coin] quote fetch failed",{error:t}),ri("error",t)}await Li()}if(Un("fx")){e+=1;try{await Fn("fx",Tt)}catch(e){const t=ai(e instanceof Error?e.message:String(e));ni("[fx] quote fetch failed",{error:t}),ri("error",t)}await Bi()}if(window.botApi?.getSymbolRules&&Un("coin"))try{const e=await window.botApi.getSymbolRules({market:"coin"});if(e?.ok&&Array.isArray(e.rules)){Zt.clear();for(const t of e.rules){const e=String(t.symbol||"").trim().toUpperCase();e&&Zt.set(e,{minOrderSize:String(t.minOrderSize||""),maxOrderSize:String(t.maxOrderSize||""),sizeStep:String(t.sizeStep||"")})}}}catch(e){ni("[coin] symbol rules fetch failed",{error:e instanceof Error?e.message:String(e)})}return e>0}())return;await Wn(),await Qn(),Un("coin")&&!Ut&&(Ut=setInterval(()=>{Yn("coin")},1e3)),Un("fx")&&!Ft&&(Ft=setInterval(()=>{Yn("fx")},1e3)),_t=!0}finally{zt=!1}}};mn(!1),(()=>{try{cn("true"===window.localStorage.getItem(ln))}catch{cn(!1)}})(),K&&!K.value&&(K.value=wn(new Date)),(async()=>{if(!window.botApi?.getSavedProfile)return;const e=await window.botApi.getSavedProfile();e?.ok&&e.profile&&(s.value=e.profile.botId||"",l.value=e.profile.email||"",c.value=e.profile.password||"",d.checked=Boolean(e.profile.remember))})(),(async()=>{if(!window.botApi?.getSkillConfig||!window.botApi.getAiConfig)return;const[e,t,n]=await Promise.all([window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-fx"),window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-coin"),window.botApi.getAiConfig()]);e?.ok&&e.config&&(ge.value=String(e.config.riskDailyLossLimitJpy||5e4),we.value=String(e.config.fxApiKey||""),be.value=String(e.config.fxApiSecret||"")),t?.ok&&t.config&&(pe.value=String(t.config.riskDailyLossLimitJpy||5e4),fe.value=String(t.config.cryptoApiKey||""),ye.value=String(t.config.cryptoApiSecret||"")),n?.ok&&n.config&&(Te.value=n.config.aiModel||"gpt-5.4",Pe.value=n.config.logOutputDir||"")})(),Ai();export{};
|
|
@@ -484,10 +484,6 @@
|
|
|
484
484
|
<span class="muted">As Of</span>
|
|
485
485
|
<strong id="macro-snapshot-as-of">-</strong>
|
|
486
486
|
</div>
|
|
487
|
-
<div class="macro-stat-row">
|
|
488
|
-
<span class="muted">Fear & Greed</span>
|
|
489
|
-
<strong id="macro-fear-greed-score">-</strong>
|
|
490
|
-
</div>
|
|
491
487
|
</div>
|
|
492
488
|
<div class="macro-toolbar-actions">
|
|
493
489
|
<button id="macro-refresh-snapshot-btn" type="button">Refresh Snapshot</button>
|
|
@@ -1262,6 +1262,14 @@ button.is-loading::before {
|
|
|
1262
1262
|
font-weight: 700;
|
|
1263
1263
|
}
|
|
1264
1264
|
|
|
1265
|
+
.macro-chip-value.is-positive {
|
|
1266
|
+
color: #1f9d55;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.macro-chip-value.is-negative {
|
|
1270
|
+
color: #d64545;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1265
1273
|
.macro-chip-delta {
|
|
1266
1274
|
color: #1f9d55;
|
|
1267
1275
|
font-size: 13px;
|
|
@@ -1272,6 +1280,22 @@ button.is-loading::before {
|
|
|
1272
1280
|
color: #d64545;
|
|
1273
1281
|
}
|
|
1274
1282
|
|
|
1283
|
+
.macro-chip-pill {
|
|
1284
|
+
display: inline-flex;
|
|
1285
|
+
align-items: center;
|
|
1286
|
+
justify-content: center;
|
|
1287
|
+
width: fit-content;
|
|
1288
|
+
min-width: 68px;
|
|
1289
|
+
height: 28px;
|
|
1290
|
+
border: 3px solid #2fb344;
|
|
1291
|
+
border-right-color: #c8d2e1;
|
|
1292
|
+
border-bottom-color: #c8d2e1;
|
|
1293
|
+
border-radius: 999px;
|
|
1294
|
+
font-size: 12px;
|
|
1295
|
+
font-weight: 700;
|
|
1296
|
+
color: #1f2a44;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1275
1299
|
.macro-treemap {
|
|
1276
1300
|
position: relative;
|
|
1277
1301
|
height: 240px;
|