@ai.weget.jp/bot 0.1.21 → 0.1.23

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.
@@ -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"),h=document.getElementById("coin-skill-title"),k=document.getElementById("fx-skill-title"),x=document.getElementById("browser-skill-title"),S=document.getElementById("macro-skill-title"),v=document.getElementById("coin-skill-package"),E=document.getElementById("fx-skill-package"),C=document.getElementById("browser-skill-package"),I=document.getElementById("macro-skill-package"),A=document.getElementById("coin-skill-enabled"),N=document.getElementById("fx-skill-enabled"),L=document.getElementById("browser-skill-enabled"),B=document.getElementById("macro-skill-enabled"),$=document.getElementById("coin-skill-version"),M=document.getElementById("fx-skill-version"),T=document.getElementById("browser-skill-version"),P=document.getElementById("macro-skill-version"),D=document.getElementById("browser-skill-cards"),U=document.getElementById("browser-skill-tools"),F=document.getElementById("macro-snapshot-as-of"),j=document.getElementById("macro-fear-greed-score"),_=document.getElementById("macro-snapshot-hint"),z=document.getElementById("macro-coin-cards"),O=document.getElementById("macro-fx-cards"),H=document.getElementById("macro-coin-heatmap-treemap"),J=document.getElementById("macro-fx-heatmap-treemap"),q=document.getElementById("macro-calendar-body"),G=document.getElementById("macro-news-body"),K=document.getElementById("macro-date-key"),Y=document.getElementById("macro-refresh-snapshot-btn"),R=document.getElementById("macro-refresh-day-btn"),W=document.getElementById("browser-chromium-status"),X=document.getElementById("browser-chromium-detail"),V=document.getElementById("browser-prereq-hint"),Q=document.getElementById("browser-refresh-playwright-mcp-btn"),Z=document.getElementById("logs"),ee=document.getElementById("gateway-codex-auth"),te=document.getElementById("gateway-mcp-status"),ne=document.getElementById("gateway-browser-status"),ie=document.getElementById("gateway-context-file"),oe=document.getElementById("gateway-detail"),ae=document.getElementById("gateway-install-btn"),re=document.getElementById("gateway-refresh-btn"),se=document.getElementById("gateway-skill-grid"),ce=document.getElementById("messages"),le=document.getElementById("active-tasks-list"),de=document.getElementById("chat-form"),me=document.getElementById("chat-input"),ue=document.getElementById("send-btn"),ge=document.getElementById("fx-risk-daily-loss-limit-jpy"),pe=document.getElementById("coin-risk-daily-loss-limit-jpy"),fe=document.getElementById("crypto-api-key"),ye=document.getElementById("crypto-api-secret"),we=document.getElementById("fx-api-key"),be=document.getElementById("fx-api-secret"),he=document.getElementById("toggle-crypto-secret-btn"),ke=document.getElementById("toggle-fx-secret-btn"),xe=document.getElementById("save-fx-config-btn"),Se=document.getElementById("save-coin-config-btn"),ve=document.getElementById("fx-config-title"),Ee=document.getElementById("coin-config-title"),Ce=document.getElementById("fx-config-label-riskDailyLossLimitJpy"),Ie=document.getElementById("fx-config-label-fxApiKey"),Ae=document.getElementById("fx-config-label-fxApiSecret"),Ne=document.getElementById("coin-config-label-riskDailyLossLimitJpy"),Le=document.getElementById("coin-config-label-cryptoApiKey"),Be=document.getElementById("coin-config-label-cryptoApiSecret"),$e=document.getElementById("ai-model"),Me=document.getElementById("host-log-output-dir"),Te=document.getElementById("save-ai-config-btn"),Pe=document.getElementById("codex-login-btn"),De=document.getElementById("codex-copy-login-btn"),Ue=document.getElementById("codex-refresh-auth-btn"),Fe=document.getElementById("codex-login-command"),je=document.getElementById("logout-btn"),_e=document.getElementById("login-btn"),ze=document.getElementById("coin-symbol-select"),Oe=document.getElementById("fx-symbol-select"),He=document.getElementById("coin-kline-intervals"),Je=document.getElementById("fx-kline-intervals"),qe=document.getElementById("coin-kline-canvas"),Ge=document.getElementById("fx-kline-canvas"),Ke=document.getElementById("coin-market-icon"),Ye=document.getElementById("fx-market-icon"),Re=document.getElementById("coin-order-bid"),We=document.getElementById("coin-order-ask"),Xe=document.getElementById("coin-order-spread"),Ve=document.getElementById("fx-order-bid"),Qe=document.getElementById("fx-order-ask"),Ze=document.getElementById("fx-order-spread"),et=document.getElementById("coin-order-qty"),tt=document.getElementById("fx-order-qty"),nt=document.getElementById("coin-buy-btn"),it=document.getElementById("coin-sell-btn"),ot=document.getElementById("fx-buy-btn"),at=document.getElementById("fx-sell-btn"),rt=document.getElementById("coin-required-amount"),st=document.getElementById("fx-required-amount"),ct=document.getElementById("coin-account-info-refresh-btn"),lt=document.getElementById("fx-account-info-refresh-btn"),dt=document.getElementById("coin-account-pnl"),mt=document.getElementById("coin-account-margin"),ut=document.getElementById("coin-account-available"),gt=document.getElementById("coin-account-margin-ratio"),pt=document.getElementById("fx-account-pnl"),ft=document.getElementById("fx-account-margin"),yt=document.getElementById("fx-account-available"),wt=document.getElementById("fx-account-margin-ratio"),bt=document.getElementById("coin-position-summary-body"),ht=document.getElementById("coin-position-list-body"),kt=document.getElementById("fx-position-summary-body"),xt=document.getElementById("fx-position-list-body"),St=document.getElementById("coin-position-summary-refresh-btn"),vt=document.getElementById("coin-position-list-refresh-btn"),Et=document.getElementById("fx-position-summary-refresh-btn"),Ct=document.getElementById("fx-position-list-refresh-btn"),It=Array.from(document.querySelectorAll(".tab-btn")),At=Array.from(document.querySelectorAll(".tab-panel"));let Nt="15m",Lt="15m",Bt=String(ze?.value||"BTC_JPY").trim().toUpperCase(),$t=String(Oe?.value||"USD_JPY").trim().toUpperCase(),Mt=null,Tt=null,Pt=null,Dt=null,Ut=!1,Ft=!1,jt=!1,_t=!1,zt=!1,Ot="",Ht="",Jt=0,qt=0,Gt=null,Kt=null,Yt=[],Rt=!1;const Wt=new Map,Xt={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1},Vt={symbol:"",interval:"",candles:[],lastFetchBucket:-1,fetching:!1};let Qt=[],Zt=[];const en=new Map;let tn=null;const nn=e=>e&&"object"==typeof e?e:{},on="weget.bot.sidebar.collapsed",an=e=>{i.classList.toggle("sidebar-collapsed",e),a&&(a.textContent=e?"▶":"◀",a.setAttribute("aria-label",e?"Expand menu":"Collapse menu")),o&&o.setAttribute("data-collapsed",e?"true":"false")},rn=e=>{for(const t of It)t.classList.toggle("is-active",t.dataset.tab===e);for(const t of At)t.classList.toggle("is-active",t.id===`tab-${e}`)};for(const e of It)e.addEventListener("click",()=>rn(e.dataset.tab||"coin"));a?.addEventListener("click",()=>{const e=!i.classList.contains("sidebar-collapsed");an(e);try{window.localStorage.setItem(on,String(e))}catch{}});const sn=e=>{const n=Ut!==e;Ut=e,t.classList.toggle("hidden",e),i.classList.toggle("hidden",!e),e&&ai(),e?n&&Ii():(Jn(),Ft=!1)},cn=e=>{const t=String(e||"disconnected").trim().toLowerCase();m.textContent=t,m.classList.remove("status-connected","status-disconnected","status-connecting"),"connected"!==t?"reconnecting"!==t&&"connecting"!==t?m.classList.add("status-disconnected"):m.classList.add("status-connecting"):m.classList.add("status-connected")},ln=(e,t=6)=>{if(null===e||!Number.isFinite(e))return"-";const n=Math.abs(e);return n>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:3}):n>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:Math.min(t,5)}):e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:t})},dn=e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,mn=e=>`${e>0?"+":""}${e.toLocaleString("ja-JP",{maximumFractionDigits:0})} 円`,un=e=>!Number.isFinite(e)||e<=0||e>1e5?"- %":(e=>`${e.toLocaleString("ja-JP",{maximumFractionDigits:2})} %`)(e),gn=e=>{const t=e instanceof Date?e:new Date(e);if(Number.isNaN(t.getTime()))return"";return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`},pn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t);return Number.isNaN(n.getTime())?t:n.toLocaleString("ja-JP",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})},fn=e=>String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),yn=e=>{const t=String(e||"").trim();if(!t)return"-";const n=new Date(t).getTime();if(!Number.isFinite(n))return t;const i=Math.max(0,Math.floor((Date.now()-n)/1e3));if(i<5)return"just now";if(i<60)return`${i}s ago`;const o=Math.floor(i/60);if(o<60)return`${o}m ago`;const a=Math.floor(o/60);return a<24?`${a}h ago`:`${Math.floor(a/24)}d ago`},wn=()=>{if(!le)return;const e=Array.from(en.values()).sort((e,t)=>{const n=new Date(String(e.lastProgressAt||e.startedAt||0)).getTime();return new Date(String(t.lastProgressAt||t.startedAt||0)).getTime()-n});e.length?le.innerHTML=e.map(e=>{const t=fn(e.title||e.prompt||e.taskId),n=fn(e.status||"Running"),i=fn(e.source||"-"),o=fn(e.channel||"-"),a=fn(yn(e.startedAt)),r=fn(yn(e.lastProgressAt)),s=fn(e.conversationId||"-"),c=fn(e.lineUserId||"-");return`\n <section class="active-task-card" data-task-id="${fn(e.taskId)}">\n <div class="active-task-meta">\n <span class="active-task-badge">${o}</span>\n <span class="active-task-badge">${i}</span>\n </div>\n <div class="active-task-title">${t}</div>\n <div class="active-task-status">${n}</div>\n <div class="active-task-subtext">Started ${a} · Last update ${r}</div>\n <div class="active-task-subtext">Task ${fn(e.taskId)} · Conv ${s}</div>\n ${e.lineUserId?`<div class="active-task-subtext">LINE user ${c}</div>`:""}\n <div class="active-task-actions">\n <button type="button" class="secondary active-task-cancel-btn" data-task-id="${fn(e.taskId)}" ${!1===e.canCancel?"disabled":""}>Cancel</button>\n </div>\n </section>\n `}).join(""):le.innerHTML='<div class="active-task-empty">No active tasks.</div>'},bn=e=>{if(e)if("snapshot"!==e.type){if("upsert"===e.type&&e.task?.taskId)return en.set(e.task.taskId,e.task),void wn();"remove"===e.type&&e.taskId&&(en.delete(e.taskId),wn())}else{en.clear();for(const t of Array.isArray(e.tasks)?e.tasks:[])t?.taskId&&en.set(t.taskId,t);wn()}},hn=e=>{const t=String(e||"").trim();if(!t)return{cleaned:"",impact:"",direction:""};const n=t.indexOf("\n"),i=n>=0?t.slice(0,n).trim():t,o=n>=0?t.slice(n+1).trim():"";if(i.startsWith("{")&&i.endsWith("}"))try{const e=JSON.parse(i);return{cleaned:o,impact:String(e.impact||"").trim().toLowerCase(),direction:String(e.usd_jpy_direction||"").trim().toLowerCase()}}catch{}return{cleaned:t,impact:"",direction:""}},kn=e=>{const t=String(e||"").trim().toLowerCase();if(!t)return"-";const n="high"===t?"High":"medium"===t?"Medium":"low"===t?"Low":t;return`<span class="macro-impact-badge ${fn(t)}">${fn(n)}</span>`},xn=e=>{const t=Math.max(0,Math.min(3,Math.round(Number(e||0))));return t<=0?"-":`<span class="macro-importance-stars">${"★".repeat(t)}</span>`},Sn=e=>{const t=String(e||"").trim().toLowerCase();return t?"up"===t?'<span class="macro-direction up">↑↑</span>':"down"===t?'<span class="macro-direction down">↓↓</span>':"sideways"===t?'<span class="macro-direction flat">→</span>':fn(t):"-"},vn=e=>{const t=String(e||"").trim().replace(/,/g,"");if(!t)return"";if(!/^\d+(\.\d+)?$/.test(t))return"";if(!t.includes("."))return t;return t.replace(/(\.\d*?[1-9])0+$/,"$1").replace(/\.0+$/,"").replace(/\.$/,"")},En=e=>{const t=String(e||"").trim(),n=t.indexOf(".");return n>=0?t.length-n-1:0},Cn=(e,t)=>{const n=String(e||"").trim(),i=n.startsWith("-"),o=i?n.slice(1):n,[a,r=""]=o.split("."),s=(r+"0".repeat(t)).slice(0,t),c=BigInt((a||"0")+s);return i?-c:c},In=e=>{const t=Ln("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),n=nn(t?.configJson);return"fx"===e?Boolean(String(n.fxApiKey||we.value||"").trim()&&String(n.fxApiSecret||be.value||"").trim()):Boolean(String(n.cryptoApiKey||fe.value||"").trim()&&String(n.cryptoApiSecret||ye.value||"").trim())},An=({symbol:e,quote:t,orderBidNode:n,orderAskNode:i,orderSpreadNode:o})=>{if(!t)return n.textContent="-",i.textContent="-",void(o.textContent="-");const a=ln(t.bid),r=ln(t.ask);n.textContent=a,i.textContent=r,o.textContent=ln(t.spread,8)},Nn=e=>{if("coin"===e){const e=Number(String(et.value||"").trim()),t=Gt;return!Number.isFinite(e)||e<=0||null===t||!Number.isFinite(t)?void(rt.textContent="- 円"):void(rt.textContent=dn(e*t/2))}const t=Number(String(tt.value||"").trim()),n=Kt;!Number.isFinite(t)||t<=0||null===n||!Number.isFinite(n)?st.textContent="- 円":st.textContent=dn(t*n/20)},Ln=e=>Yt.find(t=>t.name===e),Bn=e=>{const t=Ln(e);if(!t)return!1;const n=String(t.installStatus||"").trim().toLowerCase();return t.enabled&&"active"===n},$n=e=>Bn("fx"===e?"@ai.weget.jp/skill-gmo-fx":"@ai.weget.jp/skill-gmo-coin"),Mn=async(e,t)=>{if(!window.botApi?.getMarketQuotes)return null;const n=String(t||"").trim().toUpperCase(),i=await window.botApi.getMarketQuotes({market:e,symbols:[n]});if(!i?.ok)throw new Error(String(i?.error||"quote api failed"));const o=(i.quotes||[]).find(e=>String(e.symbol||"").toUpperCase()===n)||null;return"coin"===e?(An({symbol:n,quote:o,orderBidNode:Re,orderAskNode:We,orderSpreadNode:Xe}),Gt=o?.ask??null,Nn("coin")):(An({symbol:n,quote:o,orderBidNode:Ve,orderAskNode:Qe,orderSpreadNode:Ze}),Kt=o?.ask??null,Nn("fx")),o},Tn=(e,t)=>{const n=String(t||"").trim().toUpperCase();if(!n)return;if("coin"===e){const e=n.replace("_","-").toLowerCase();return void(Ke.src=`https://coin.z.com/jp/member/imgs/icon-${e}.svg`)}const[i,o]=n.split("_"),a="JPY"===o?i:`${i}_${o}`;Ye.src=`https://coin.z.com/jp/member/imgs/fx/icon_${a}.svg`},Pn=e=>"5m"===e?3e5:"15m"===e?9e5:"30m"===e?18e5:"1h"===e?36e5:6e4,Dn=e=>"coin"===e?Xt:Vt,Un=e=>"coin"===e?qe:Ge,Fn=e=>"coin"===e?Bt:$t,jn=e=>{const t=Fn(e),n=("coin"===e?Qt:Zt).filter(e=>String(e.symbol||"").trim().toUpperCase()===t),i=[];for(const e of n){const t=String(e.side||"").trim().toUpperCase(),n=Number(e.averagePositionRate||0);if(!Number.isFinite(n)||n<=0)continue;const o="BUY"===t;i.push({price:n,label:o?"BUY Avg":"SELL Avg",color:o?"#1f9d55":"#e03131"})}return i},_n=e=>{const t=Dn(e);t.candles.length&&si(Un(e),t.candles,jn(e))},zn=(e,t)=>{if(!t)return;const n=Dn(e);if(!n.candles.length)return;if(n.symbol!==t.symbol)return;const i=n.candles[n.candles.length-1],o=null!==t.bid&&null!==t.ask?(t.bid+t.ask)/2:null!==t.ask?t.ask:t.bid;null!==o&&Number.isFinite(o)&&(i.close=o,i.high=Math.max(i.high,o),i.low=Math.min(i.low,o),si(Un(e),n.candles,jn(e)))},On=async e=>{const t=Dn(e);if(!t.interval||!t.symbol||t.fetching)return;if(t.symbol!==Fn(e))return;if(!(Math.floor(Date.now()/Pn(t.interval))<=t.lastFetchBucket)){t.fetching=!0;try{"coin"===e?await vi():await Ei()}finally{t.fetching=!1}}},Hn=async e=>{if($n(e))if("coin"!==e){if(!zt){zt=!0;try{const e=await Mn("fx",$t);zn("fx",e),await On("fx")}catch(e){const t=ei(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Ht||n-qt>15e3)&&(Vn("[fx] quote poll failed",{error:t}),Ht=t,qt=n)}finally{zt=!1}}}else{if(_t)return;_t=!0;try{const e=await Mn("coin",Bt);zn("coin",e),await On("coin")}catch(e){const t=ei(e instanceof Error?e.message:String(e)),n=Date.now();(t!==Ot||n-Jt>15e3)&&(Vn("[coin] quote poll failed",{error:t}),Ot=t,Jt=n)}finally{_t=!1}}},Jn=()=>{Pt&&(clearInterval(Pt),Pt=null),Dt&&(clearInterval(Dt),Dt=null)},qn=async e=>{if(window.botApi?.getAccountMetrics){if((!e||"coin"===e)&&$n("coin"))if(In("coin")){const e=await window.botApi.getAccountMetrics({market:"coin"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);dt.textContent=n>0?mn(i):dn(0),mt.textContent=dn(n),ut.textContent=dn(t),gt.textContent=un(Number(e.marginRatio||0))}else Vn("[coin] account metrics fetch failed",{error:e?.error||"unknown error"})}else dt.textContent=dn(0),mt.textContent="- 円",ut.textContent="- 円",gt.textContent="- %";if((!e||"fx"===e)&&$n("fx"))if(In("fx")){const e=await window.botApi.getAccountMetrics({market:"fx"});if(e?.ok){const t=Number(e.availableAmount||0),n=Number(e.margin||0),i=Number(e.pnlWithSwap||0);pt.textContent=n>0?mn(i):dn(0),ft.textContent=dn(n),yt.textContent=dn(t),wt.textContent=un(Number(e.marginRatio||0))}else Vn("[fx] account metrics fetch failed",{error:e?.error||"unknown error"})}else pt.textContent=dn(0),ft.textContent="- 円",yt.textContent="- 円",wt.textContent="- %"}},Gn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").toUpperCase(),o=n.replace("_","/"),a=String(i.side||"-"),r=String(i.size||"").trim(),s=Number(r||0),c=Number(i.price||0),l=Number(i.lossGain||0),d=Number(i.totalSwap||0),m=String(i.timestamp||"-"),u=Number(i.positionId||0),g=Number.isFinite(u)&&u>0&&r?`data-market="${e}" data-symbol="${n}" data-side="${String(a||"").toUpperCase()}" data-position-id="${u}" data-size="${r}"`:"disabled",p=document.createElement("tr");p.innerHTML=`\n <td><button type="button" class="close-btn" ${g}>決済</button></td>\n <td>${o}<br />${a}</td>\n <td>${s.toLocaleString("ja-JP")}</td>\n <td>${c.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${mn(l)}<br />${mn(d)}</td>\n <td>${m.replace("T"," ").slice(0,19)}</td>\n `,t.appendChild(p)}}else t.innerHTML='<tr><td colspan="6" class="empty-cell">対象のお取引はございません。</td></tr>'},Kn=(e,t,n)=>{if(n.length){t.innerHTML="";for(const i of n){const n=String(i.symbol||"").replace("_","/"),o=String(i.side||"-"),a=Number("fx"===e?i.sumPositionSize||0:i.sumPositionQuantity||0),r=Number(i.averagePositionRate||0),s=Number(i.positionLossGain||0),c=Number("fx"===e&&i.sumTotalSwap||0),l=document.createElement("tr");l.innerHTML=`\n <td><button type="button" class="close-btn">決済</button></td>\n <td>${n}<br />${o}</td>\n <td>${a.toLocaleString("ja-JP")}</td>\n <td>${r.toLocaleString("ja-JP",{maximumFractionDigits:6})}</td>\n <td>${mn(s)}<br />${mn(c)}</td>\n `,t.appendChild(l)}}else t.innerHTML='<tr><td colspan="5" class="empty-cell">対象のお取引はございません。</td></tr>'},Yn=async e=>{if(window.botApi?.getPositionSummary&&window.botApi?.getOpenPositions){if((!e||"coin"===e)&&$n("coin"))if(In("coin")){const e=await window.botApi.getPositionSummary({market:"coin",symbol:Bt});e?.ok?(Qt=Array.isArray(e.items)?e.items:[],Kn("coin",bt,Qt),_n("coin")):(Vn("[coin] position summary fetch failed",{error:e?.error||"unknown error"}),Qt=[],Kn("coin",bt,[]),_n("coin"))}else Qt=[],Kn("coin",bt,[]),_n("coin");if((!e||"fx"===e)&&$n("fx"))if(In("fx")){const e=await window.botApi.getPositionSummary({market:"fx"});e?.ok?(Zt=Array.isArray(e.items)?e.items:[],Kn("fx",kt,Zt),_n("fx")):(Vn("[fx] position summary fetch failed",{error:e?.error||"unknown error"}),Zt=[],Kn("fx",kt,[]),_n("fx"))}else Zt=[],Kn("fx",kt,[]),_n("fx");if((!e||"coin"===e)&&$n("coin"))if(In("coin")){const e=await window.botApi.getOpenPositions({market:"coin",symbol:Bt,page:1,count:100});e?.ok?Gn("coin",ht,Array.isArray(e.items)?e.items:[]):(Vn("[coin] open positions fetch failed",{error:e?.error||"unknown error"}),Gn("coin",ht,[]))}else Gn("coin",ht,[]);if((!e||"fx"===e)&&$n("fx"))if(In("fx")){const e=await window.botApi.getOpenPositions({market:"fx",count:100});e?.ok?Gn("fx",xt,Array.isArray(e.items)?e.items:[]):(Vn("[fx] open positions fetch failed",{error:e?.error||"unknown error"}),Gn("fx",xt,[]))}else Gn("fx",xt,[])}},Rn=async(e,t)=>{const n=window.botApi;if(!n?.placeFxOrder)return;const i=String(tt.value||"").trim();i?await ri(t,async()=>{const t=await n.placeFxOrder({symbol:$t,side:e,size:i});if(!t?.ok){const n=ei(t?.error||"fx order failed");return Vn("[fx] order failed",{symbol:$t,side:e,size:i,error:n}),ti("error",n),void await ni("fx.order",n,t)}Vn("[fx] order placed",{symbol:$t,side:e,size:i,result:t.result||null}),ti("success",`${e} 注文を送信しました。`),await qn("fx"),await Yn("fx")}):ti("error","数量を入力してください。")},Wn=async(e,t)=>{const n=window.botApi;if(!n?.placeCoinOrder)return;const i=vn(String(et.value||""));if(!i)return void ti("error","数量の形式が不正です。");const o=((e,t)=>{const n=Wt.get(e);if(!n)return{ok:!0};const i=vn(n.minOrderSize),o=vn(n.sizeStep),a=vn(n.maxOrderSize);if(!i||!o)return{ok:!0};const r=Math.max(En(t),En(i),En(o),En(a)),s=Cn(t,r),c=Cn(i,r),l=Cn(o,r);if(s<c)return{ok:!1,reason:`${e} の最小数量は ${i} です。`};if(l>0n&&s%l!==0n)return{ok:!1,reason:`${e} の数量刻みは ${o} です。`};if(a&&s>Cn(a,r))return{ok:!1,reason:`${e} の最大数量は ${a} です。`};return{ok:!0}})(Bt,i);o.ok?await ri(t,async()=>{const t=await n.placeCoinOrder({symbol:Bt,side:e,size:i});if(!t?.ok){const n=ei(t?.error||"coin order failed");return Vn("[coin] order failed",{symbol:Bt,side:e,size:i,error:n}),ti("error",n),void await ni("coin.order",n,t)}Vn("[coin] order placed",{symbol:Bt,side:e,size:i,result:t.result||null}),ti("success",`${e} 注文を送信しました。`),await qn("coin"),await Yn("coin")}):ti("error",o.reason||"数量が取引ルールに一致しません。")},Xn=e=>{const t=String(e||"").toLowerCase();return t.includes("debug")||t.includes("[debug]")?"debug":t.includes("error")||t.includes("failed")||t.includes("exception")?"error":"info"},Vn=(e,t=null,n=null)=>{if(window.botApi?.writeLog&&window.botApi.writeLog({level:Xn(e),source:"ui.log",message:e,details:{data:t,ts:n||(new Date).toISOString()}}).catch(()=>{}),!Z)return;const i=document.createElement("div");i.className="log-entry";const o=document.createElement("div");o.className="log-line";const a=document.createElement("span");if(a.className="log-ts",a.textContent=n||(new Date).toISOString(),o.appendChild(a),o.append(document.createTextNode(e||"")),i.appendChild(o),null!=t&&""!==t){const e=document.createElement("pre");e.className="status-output",e.textContent="string"==typeof t?t:JSON.stringify(t,null,2),i.appendChild(e)}for(Z.appendChild(i);Z.children.length>300;)Z.removeChild(Z.firstChild);Z.scrollTop=Z.scrollHeight},Qn=(e,t,n="")=>{const i=document.createElement("div");i.className=`msg ${e}`;let o=n||("user"===e?"You":"Assistant");"line-user"===e&&(o=n||"LINE User"),"line-assistant"===e&&(o=n||"AI"),i.textContent=`${o}: ${t}`,ce.appendChild(i),ce.scrollTop=ce.scrollHeight},Zn=()=>{const e=ce.querySelector(".msg.assistant-thinking");e&&e.remove()},ei=e=>{const t=String(e||"").trim(),n=t.toLowerCase();return t?n.includes("api key")&&n.includes("missing")?"API Key 未设置,请先在 Config 页面保存。":n.includes("auth")||n.includes("401")||n.includes("403")?"认证失败,请检查 API Key 和 Secret。":n.includes("network")||n.includes("fetch failed")||n.includes("timeout")?"网络连接异常,请检查网络后重试。":n.includes("message is required")?"请输入内容后再发送。":t:"处理失败,请稍后重试。"},ti=(e,t)=>{Mt&&(clearTimeout(Mt),Mt=null),r.classList.remove("hidden","notice-error","notice-success"),r.classList.add("error"===e?"notice-error":"notice-success"),r.textContent=t,Mt=setTimeout(()=>{ii()},"success"===e?2200:4200)},ni=async(e,t,n=null)=>{window.botApi?.writeErrorLog&&await window.botApi.writeErrorLog({source:e,message:String(t||""),details:n})},ii=()=>{Mt&&(clearTimeout(Mt),Mt=null),r.classList.add("hidden"),r.classList.remove("notice-error","notice-success"),r.textContent=""},oi=(e,t)=>{Tt&&(clearTimeout(Tt),Tt=null),n.classList.remove("hidden","notice-error","notice-success"),n.classList.add("error"===e?"notice-error":"notice-success"),n.textContent=t,Tt=setTimeout(()=>{ai()},"success"===e?2200:4200)},ai=()=>{Tt&&(clearTimeout(Tt),Tt=null),n.classList.add("hidden"),n.classList.remove("notice-error","notice-success"),n.textContent=""},ri=async(e,t)=>{if(e.disabled)return;e.disabled=!0,e.classList.add("is-loading");const n=e.textContent||"";try{await t()}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[ui] action failed",{error:t}),ti("error",t)}finally{e.classList.remove("is-loading"),e.textContent=n,e.disabled=!1}},si=(e,t,n=[])=>{const i=e.getContext("2d");if(!i)return;const o=e.width,a=e.height;if(i.clearRect(0,0,o,a),!t.length)return;const r=Math.max(...t.map(e=>e.high)),s=Math.min(...t.map(e=>e.low)),c=n.map(e=>e.price).filter(e=>Number.isFinite(e)),l=c.length?Math.max(r,...c):r,d=c.length?Math.min(s,...c):s,m=Math.max(1e-8,l-d),u=10,g=10,p=o-10-62-8,f=a-10-24-8,y=Math.max(2,p/t.length),w=e=>g+(l-e)/m*f,b=e=>e>=1e3?e.toLocaleString("en-US",{maximumFractionDigits:0}):e>=1?e.toLocaleString("en-US",{minimumFractionDigits:3,maximumFractionDigits:3}):e.toLocaleString("en-US",{minimumFractionDigits:5,maximumFractionDigits:5});i.strokeStyle="#c9d4ea",i.strokeRect(.5,.5,o-1,a-1),i.strokeStyle="#e3eaf5",i.lineWidth=1;for(let e=0;e<=4;e+=1){const t=g+f/4*e;i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke()}t.forEach((e,t)=>{const n=u+t*y+.5*y,o=w(e.high),a=w(e.low),r=w(e.open),s=w(e.close),c=e.close>=e.open;i.strokeStyle=c?"#1f9d55":"#d64545",i.fillStyle=c?"#1f9d55":"#d64545",i.lineWidth=1,i.beginPath(),i.moveTo(n,o),i.lineTo(n,a),i.stroke();const l=n-.3*y,d=Math.max(1,.6*y),m=Math.min(r,s),g=Math.max(1,Math.abs(s-r));i.fillRect(l,m,d,g)});for(const e of n){const t=w(e.price);i.save(),i.setLineDash([4,4]),i.strokeStyle=e.color,i.lineWidth=1,i.beginPath(),i.moveTo(u,t),i.lineTo(u+p,t),i.stroke(),i.restore(),i.font="12px Segoe UI";const n=`${e.label} ${b(e.price)}`,o=Math.ceil(i.measureText(n).width)+10,a=14,r=Math.max(12,Math.min(g+f-18,t-9));i.fillStyle=e.color,i.fillRect(a,r,o,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(n,a+5,r+9)}const h=t[t.length-1],k=h?.close;if(Number.isFinite(k)){const e=w(Number(k));i.save(),i.setLineDash([5,4]),i.strokeStyle="#2b79ff",i.lineWidth=1,i.beginPath(),i.moveTo(u,e),i.lineTo(u+p,e),i.stroke(),i.restore();const t=b(Number(k));i.font="12px Segoe UI";const n=Math.ceil(i.measureText(t).width)+10,o=u+p+6,a=Math.max(12,Math.min(g+f-18,e-9));i.fillStyle="#2b79ff",i.fillRect(o,a,n,18),i.fillStyle="#ffffff",i.textAlign="left",i.textBaseline="middle",i.fillText(t,o+5,a+9)}i.fillStyle="#66758d",i.font="12px Segoe UI",i.textBaseline="middle",i.textAlign="left";for(let e=0;e<=4;e+=1){const t=l-m/4*e,n=g+f/4*e;i.fillText(b(t),u+p+8,n)}const x=e=>{const t=new Date(e>1e12?e:1e3*e);if(Number.isNaN(t.getTime()))return"-";const n=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${n}:${i}`};i.textAlign="center",i.textBaseline="top";for(let e=0;e<=4;e+=1){const n=Math.min(t.length-1,Math.floor((t.length-1)*(e/4))),o=u+p*e/4;i.fillText(x(t[n]?.time||0),o,g+f+6)}},ci=(e,t)=>{const{tabBtn:n,titleNode:i,packageNode:o,enabledNode:a,versionNode:r,fallbackTitle:s,fallbackPackage:c}=t,l=String(e?.ui?.surfaceTitle||e?.displayName||s).trim()||s,d=e?.name||c,m=String(e?.ui?.tabLabel||d.replace("@ai.weget.jp/","")).trim()||d.replace("@ai.weget.jp/",""),u=!!e&&Boolean(e.enabled),g=String(e?.installStatus||"uninstalled").trim()||"uninstalled",p=String(e?.configuredVersion||e?.version||"-").trim()||"-";if(n){const e=n.querySelector(".tab-btn-label");e&&(e.textContent=m),n.classList.toggle("is-disabled",!u)}i&&(i.textContent=l),o&&(o.textContent=d),a&&(a.textContent=u?"enabled":"disabled",a.classList.toggle("is-disabled",!u)),r&&(r.textContent=`${g} · ${p}`)},li=(e,t)=>{t.titleNode&&(t.titleNode.textContent=String(e?.ui?.configTitle||t.fallbackTitle).trim()||t.fallbackTitle);const n=Array.isArray(e?.ui?.configFields)&&e.ui?.configFields||[];for(const[e,i]of Object.entries(t.fields)){const t=n.find(t=>t.key===e);t?.label&&i.labelNode&&(i.labelNode.textContent=t.label),void 0!==t?.placeholder&&(i.inputNode.placeholder=t.placeholder||"")}},di=(e,t)=>{if(e){if(e.innerHTML="",!t.length){const t=document.createElement("span");return t.className="muted",t.textContent="No macro snapshot cards available.",void e.appendChild(t)}for(const n of t){const t=document.createElement("div");t.className="macro-chip"+("down"===n.trend?" is-negative":"");const i=document.createElement("div");i.className="macro-chip-code",i.textContent=n.code||"-";const o=document.createElement("div");o.className="macro-chip-value",o.textContent=n.value||"-";const a=document.createElement("div");a.className="macro-chip-delta"+("down"===n.trend?" is-negative":""),a.textContent=n.delta||"-",t.append(i,o,a),e.appendChild(t)}}},mi=(e,t,n)=>{if(!e)return;if(e.innerHTML="",!t.length){const t=document.createElement("div");return t.className="macro-treemap-empty",t.textContent=n,void e.appendChild(t)}const i=[...t].map(e=>({symbol:String(e.symbol||"").trim(),change:Number(e.change||0),weight:Math.max(1,Math.abs(Number(e.change||0)))})).filter(e=>e.symbol).sort((e,t)=>Math.abs(t.change)-Math.abs(e.change)).slice(0,12),o=[],a=i.reduce((e,t)=>e+t.weight,0)||1,r=(e,t,n,i,a,s)=>{if(!e.length)return;if(1===e.length)return void o.push({item:e[0],x:t,y:n,width:i,height:a});const c=e.reduce((e,t)=>e+t.weight,0);let l=1,d=e[0].weight;for(;l<e.length-1&&d<c/2;)l+=1,d+=e[l-1].weight;const m=e.slice(0,l),u=e.slice(l),g=d/c;if(s){const e=i*g;return r(m,t,n,e,a,!s),void r(u,t+e,n,i-e,a,!s)}const p=a*g;r(m,t,n,i,p,!s),r(u,t,n+p,i,a-p,!s)};r(i,0,0,100,100,!0);for(const{item:t,x:n,y:i,width:r,height:s}of o){const o=document.createElement("div"),c=Math.min(.88,.22+t.weight/Math.max(.18*a,1));o.className="macro-treemap-tile "+(t.change>=0?"up":"down"),o.style.setProperty("--tile-intensity",String(c)),o.style.left=`${n}%`,o.style.top=`${i}%`,o.style.width=`${r}%`,o.style.height=`${s}%`,o.innerHTML=`\n <div class="macro-treemap-symbol">${fn(t.symbol.replace(/_JPY$/i,"").replace(/_USD$/i," $"))}</div>\n <div class="macro-treemap-change">${t.change>=0?"+":""}${t.change.toFixed(1)}%</div>\n `,e.appendChild(o)}},ui=e=>{if(q)if(q.innerHTML="",e.length)for(const t of e){const e=hn(t.ai_analyzer||""),n=document.createElement("tr");n.className="macro-data-row",n.innerHTML=`\n <td>${fn(pn(t.date))}</td>\n <td>${fn(t.country||"-")}</td>\n <td class="macro-title-cell"><strong>${fn(t.title||"-")}</strong></td>\n <td>${xn(Number(t.importance||0))}</td>\n <td>${fn(t.actual||"-")}</td>\n <td>${fn(t.forecast||"-")}</td>\n <td>${fn(t.previous||"-")}</td>\n <td>${Sn(e.direction)}</td>\n `;const i=document.createElement("tr");i.className="macro-ai-row",i.innerHTML=`\n <td colspan="8" class="macro-analyzer">${fn(e.cleaned||"-")}</td>\n `,q.append(n,i)}else q.innerHTML='<tr><td colspan="8" class="empty-cell">No calendar rows for the selected date.</td></tr>'},gi=e=>{if(G)if(G.innerHTML="",e.length)for(const t of e){const e=hn(t.ai_analyzer||""),n=String(t.url||"").trim(),i=document.createElement("tr");i.className="macro-data-row",i.innerHTML=`\n <td>${fn(pn(t.time))}</td>\n <td class="macro-title-cell"><strong>${fn(t.content||"-")}</strong></td>\n <td>${kn(e.impact)}</td>\n <td>${Sn(e.direction)}</td>\n <td>${n?`<a class="macro-link" href="${fn(n)}" target="_blank" rel="noreferrer noopener">open</a>`:"-"}</td>\n `;const o=document.createElement("tr");o.className="macro-ai-row",o.innerHTML=`\n <td colspan="5" class="macro-analyzer">${fn(e.cleaned||"-")}</td>\n `,G.append(i,o)}else G.innerHTML='<tr><td colspan="4" class="empty-cell">No news rows for the selected date.</td></tr>'},pi=e=>{F&&(F.textContent="-"),j&&(j.textContent="-"),_&&(_.textContent=e),di(z,[]),di(O,[]),mi(H,[],"No coin heatmap data."),mi(J,[],"No FX heatmap data."),ui([]),gi([])},fi=async({forceSnapshot:e=!1}={})=>{if(Rt)return;if(!window.botApi?.getMacroSnapshot||!window.botApi?.getMacroCalendar||!window.botApi?.getMacroNews)return void pi("Macro IPC bridge is not available in this host build.");const t=Ln("@ai.weget.jp/skill-macro-economy");if(!t||!t.enabled||"active"!==String(t.installStatus||"").trim().toLowerCase())return void pi("Macro Economy skill is disabled or not active.");if(!Ut)return void pi("Login is required to load macro snapshot, calendar, and news.");Rt=!0;const n=String(K?.value||"").trim()||gn(new Date);K&&!K.value&&(K.value=n);try{const[t,i,o]=await Promise.all([window.botApi.getMacroSnapshot({force:e}),window.botApi.getMacroCalendar({dateKey:n}),window.botApi.getMacroNews({dateKey:n})]);if(!t.ok)throw new Error(t.error||"macro snapshot failed");if(!i.ok)throw new Error(i.error||"macro calendar failed");if(!o.ok)throw new Error(o.error||"macro news failed");const a=t.snapshot;F&&(F.textContent=pn(a?.as_of)),j&&(j.textContent=null==a?.fear_greed_score?"-":`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`),_&&(_.textContent=`Loaded macro data for ${n} from the local macro economy skill runtime.`);const r=Array.isArray(a?.coin_cards)?[...a.coin_cards]:[];null!=a?.fear_greed_score&&r.unshift({code:"FGI",value:`${Number(a.fear_greed_score).toLocaleString("ja-JP",{maximumFractionDigits:0})}`,delta:"Fear & Greed",trend:Number(a.fear_greed_score)>=50?"up":"down",points:[],usePill:!0}),di(z,r),di(O,Array.isArray(a?.fx_cards)?a?.fx_cards:[]),mi(H,Array.isArray(a?.coin_heatmap)?a?.coin_heatmap:[],"No coin heatmap data."),mi(J,Array.isArray(a?.fx_heatmap)?a?.fx_heatmap:[],"No FX heatmap data."),ui(Array.isArray(i.rows)?i.rows:[]),gi(Array.isArray(o.rows)?o.rows:[])}catch(e){pi(ei(e instanceof Error?e.message:String(e)))}finally{Rt=!1}},yi=(e,t)=>{W&&(W.textContent=e,W.classList.toggle("is-disabled","installed"!==e),W.classList.toggle("is-muted","unknown"===e)),X&&(X.textContent=t||"No Playwright browser detail available.")},wi=({browserStatus:e})=>{V&&(V.innerHTML="installed"!==e?"Run <code>npx playwright install chromium</code> on this machine.":"Chromium is ready for Playwright-backed browser tasks.")},bi=async()=>{if(!window.botApi?.getPlaywrightBrowserStatus)return yi("unknown","Playwright browser check is not available in this host build."),void wi({browserStatus:"unknown"});const e=await window.botApi.getPlaywrightBrowserStatus(),t=e.ok&&e.status||"unknown";e.ok?yi(t,String(e.detail||"").trim()):yi("unknown",ei(e.error)),wi({browserStatus:t})},hi=(e,t,n,i=!1)=>{e&&(e.textContent=t||"-",e.classList.toggle("is-disabled",!n&&!i),e.classList.toggle("is-muted",i))},ki=async()=>{if(!window.botApi?.getGatewayStatus)return;const e=await window.botApi.getGatewayStatus();if(!e?.ok)return hi(ee,"error",!1),hi(te,"error",!1),hi(ne,"error",!1),void(oe&&(oe.textContent=ei(e?.error||"gateway status failed")));hi(ee,String(e.codexAuth?.status||"unknown"),"logged_in"===String(e.codexAuth?.status||""),"unknown"===String(e.codexAuth?.status||"")),hi(te,String(e.gatewayStatus?.status||"unknown"),"configured"===String(e.gatewayStatus?.status||""),"unknown"===String(e.gatewayStatus?.status||"")),hi(ne,String(e.browserStatus?.status||"unknown"),"installed"===String(e.browserStatus?.status||""),"unknown"===String(e.browserStatus?.status||"")),ie&&(ie.textContent=`Context file: ${String(e.contextFilePath||"-")}`),oe&&(oe.textContent=JSON.stringify({codexAuth:e.codexAuth||null,gatewayStatus:e.gatewayStatus||null,browserStatus:e.browserStatus||null},null,2)),(e=>{if(!se)return;se.innerHTML="";const t=[{target:"gateway",title:"Gateway Core"},{target:"codex_macro",title:"Codex Macro Chain",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"browser",title:"Browser Skill",packageName:"@ai.weget.jp/skill-browser"},{target:"macro",title:"Macro Economy Skill",packageName:"@ai.weget.jp/skill-macro-economy"},{target:"coin",title:"GMO Coin Skill",packageName:"@ai.weget.jp/skill-gmo-coin"},{target:"fx",title:"GMO FX Skill",packageName:"@ai.weget.jp/skill-gmo-fx"}];for(const n of t){const t=n.packageName?e.find(e=>e.name===n.packageName):null,i=document.createElement("section");i.className="browser-skill-card gateway-test-card",i.innerHTML=`\n <div class="gateway-test-head">\n <h3>${n.title}</h3>\n <span class="skill-surface-badge is-muted">${t?t.enabled?"enabled":"disabled":"system"}</span>\n </div>\n <div class="muted">${t?`${t.name} · ${t.installStatus}`:"Gateway MCP self-test"}</div>\n <div class="skill-tags">${t&&Array.isArray(t.tools)&&t.tools.length?t.tools.map(e=>`<span class="skill-tag">${fn(e)}</span>`).join(""):'<span class="muted">No declared tools</span>'}</div>\n <div class="row config-actions">\n <button type="button" class="secondary gateway-test-btn" data-target="${n.target}">Run Test</button>\n </div>\n <div class="gateway-test-result">\n <span class="skill-surface-badge is-muted gateway-test-badge" data-target="${n.target}">not run</span>\n <div class="muted gateway-test-summary" data-target="${n.target}">-</div>\n </div>\n `,se.appendChild(i)}})(Array.isArray(e.skills)?e.skills:[])},xi=async()=>{if(!window.botApi?.getRuntimeInfo)return;const[e,t]=await Promise.all([window.botApi.getRuntimeInfo(),window.botApi.getSkills?window.botApi.getSkills():(async()=>({ok:!1,skills:[]}))()]);if(!e?.ok)return;const n=t?.ok&&t.skills||[];Yt=n,cn(e.status||"disconnected"),e.session?(sn(!0),u.textContent=`${e.session.email} (${e.session.userId}) [${e.session.botId}]`):(sn(!1),u.textContent="local mode (not logged in)"),(e=>{g.innerHTML="";const t=nn(e),n=nn(t.runtime),i=t.session?nn(t.session):null,o=[["Bot ID",String(i?.botId||"-")],["User ID",String(i?.userId||"-")],["Email",String(i?.email||"-")],["Capabilities",Array.isArray(n.capabilities)?n.capabilities.map(e=>String(e)).join(", "):"-"],["Active Skills",Array.isArray(n.activeSkills)&&n.activeSkills.map(e=>{const t=nn(e);return String(t.displayName||t.name||"").trim()}).filter(Boolean).join(", ")||"-"],["GMO FX API State",String(n.gmoFxApiState||"unknown")],["GMO Coin API State",String(n.gmoCoinApiState||"unknown")],["Codex Auth",String(n.codexAuthStatus||"unknown")],["Codex Auth Detail",String(n.codexAuthDetail||"-")],["Default Model",String(n.aiModel||"-")],["Login API",String(n.loginApiUrl||"-")]];for(const[e,t]of o){const n=document.createElement("div");n.className="runtime-key",n.textContent=e;const i=document.createElement("div");i.className="runtime-value",i.textContent=t,g.appendChild(n),g.appendChild(i)}})(e),(e=>{if(!p)return;p.innerHTML="";const t=Array.isArray(e)?e:[];if(0===t.length){const e=document.createElement("div");return e.className="skill-empty",e.textContent="No managed skills are available in this bot host.",void p.appendChild(e)}for(const e of t){const t=document.createElement("section");t.className="skill-card"+(e.enabled?"":" is-disabled");const n=document.createElement("div");n.className="skill-card-head";const i=document.createElement("div");i.className="skill-card-title";const o=document.createElement("strong");o.textContent=e.displayName||e.name;const a=document.createElement("code");if(a.textContent=e.name,i.appendChild(o),i.appendChild(a),e.description){const t=document.createElement("div");t.className="muted",t.textContent=e.description,i.appendChild(t)}const r=document.createElement("div");r.className="skill-badges";const s=document.createElement("span");s.className="skill-badge "+(e.enabled?"enabled":"disabled"),s.textContent=e.enabled?"enabled":"disabled",r.appendChild(s);const c=document.createElement("span");c.className="skill-badge status",c.textContent=e.installStatus||"unknown",r.appendChild(c),n.appendChild(i),n.appendChild(r),t.appendChild(n);const l=e=>{const t=document.createElement("div");t.className="skill-card-section";const n=document.createElement("div");return n.className="skill-section-label",n.textContent=e,t.appendChild(n),t},d=l("Package"),m=document.createElement("code");m.className="skill-package-code",m.textContent=e.name,d.appendChild(m),t.appendChild(d);const u=l("Tools"),g=document.createElement("div");g.className="skill-tags";const f=Array.isArray(e.tools)?e.tools:[];if(f.length>0)for(const e of f){const t=document.createElement("span");t.className="skill-tag",t.textContent=e,g.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No tools declared",g.appendChild(e)}u.appendChild(g),t.appendChild(u);const y=l("Permissions");if(Array.isArray(e.permissions)&&e.permissions.length>0){const t=document.createElement("div");t.className="skill-tags";for(const n of e.permissions){const e=document.createElement("span");e.className="skill-tag permissions",e.textContent=n,t.appendChild(e)}y.appendChild(t)}else{const e=document.createElement("span");e.className="muted",e.textContent="No permissions declared",y.appendChild(e)}t.appendChild(y);const w=l("State"),b=document.createElement("div");b.className="skill-meta";const h=[["Bundled Version",e.version||"-"],["Configured Version",e.configuredVersion||"-"],["UI",e.hasUi?"has ui":"no ui"],["Config Keys",String(Object.keys(e.configJson||{}).length)]];for(const[e,t]of h){const n=document.createElement("div");n.className="skill-meta-row";const i=document.createElement("span");i.textContent=e;const o=document.createElement("strong");o.textContent=t,n.appendChild(i),n.appendChild(o),b.appendChild(n)}w.appendChild(b),t.appendChild(w),p.appendChild(t)}})(n),ci(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{tabBtn:f,titleNode:h,packageNode:v,enabledNode:A,versionNode:$,fallbackTitle:"GMO Coin Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-coin"}),ci(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{tabBtn:y,titleNode:k,packageNode:E,enabledNode:N,versionNode:M,fallbackTitle:"GMO FX Skill",fallbackPackage:"@ai.weget.jp/skill-gmo-fx"});const i=n.find(e=>"@ai.weget.jp/skill-browser"===e.name);ci(i,{tabBtn:w,titleNode:x,packageNode:C,enabledNode:L,versionNode:T,fallbackTitle:"Browser Skill",fallbackPackage:"@ai.weget.jp/skill-browser"}),((e,t,n)=>{if(e){if(e.innerHTML="",0===t.length){const t=document.createElement("span");return t.className="muted",t.textContent=n,void e.appendChild(t)}for(const n of t){const t=document.createElement("span");t.className="skill-tag",t.textContent=n,e.appendChild(t)}}})(U,Array.isArray(i?.tools)?i.tools:[],"No browser tools declared."),D&&(D.innerHTML="");const o=n.find(e=>"@ai.weget.jp/skill-macro-economy"===e.name);ci(o,{tabBtn:b,titleNode:S,packageNode:I,enabledNode:B,versionNode:P,fallbackTitle:"Macro Economy Skill",fallbackPackage:"@ai.weget.jp/skill-macro-economy"}),await ki(),await bi(),li(n.find(e=>"@ai.weget.jp/skill-gmo-fx"===e.name),{titleNode:ve,fallbackTitle:"FX Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Ce,inputNode:ge},fxApiKey:{labelNode:Ie,inputNode:we},fxApiSecret:{labelNode:Ae,inputNode:be}}}),li(n.find(e=>"@ai.weget.jp/skill-gmo-coin"===e.name),{titleNode:Ee,fallbackTitle:"Coin Skill Config",fields:{riskDailyLossLimitJpy:{labelNode:Ne,inputNode:pe},cryptoApiKey:{labelNode:Le,inputNode:fe},cryptoApiSecret:{labelNode:Be,inputNode:ye}}}),e.session?(fi(),Ii()):pi("Login is required to load macro snapshot, calendar, and news.")};xe.addEventListener("click",async()=>{await ri(xe,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(ge.value||"0"),fxApiKey:String(we.value||"").trim(),fxApiSecret:String(be.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-fx",e);if(!t.ok)return Vn(`[trade-config] fx save failed: ${t.error||"unknown"}`),ti("error",ei(t.error)),void await ni("trade-config.fx.save",ei(t.error),t);Vn("[trade-config] fx saved"),ti("success","FX skill 配置已保存到本机。"),await xi()})}),Se.addEventListener("click",async()=>{await ri(Se,async()=>{if(!window.botApi?.saveSkillConfig)return;const e={riskDailyLossLimitJpy:Number(pe.value||"0"),cryptoApiKey:String(fe.value||"").trim(),cryptoApiSecret:String(ye.value||"").trim()},t=await window.botApi.saveSkillConfig("@ai.weget.jp/skill-gmo-coin",e);if(!t.ok)return Vn(`[trade-config] coin save failed: ${t.error||"unknown"}`),ti("error",ei(t.error)),void await ni("trade-config.coin.save",ei(t.error),t);Vn("[trade-config] coin saved"),ti("success","Coin skill 配置已保存到本机。"),await xi()})}),Te.addEventListener("click",async()=>{await ri(Te,async()=>{if(!window.botApi?.saveAiConfig)return;const e={aiModel:String($e.value||"gpt-5.4").trim()||"gpt-5.4",logOutputDir:String(Me.value||"").trim()},t=await window.botApi.saveAiConfig(e);if(!t.ok)return Vn(`[ai-config] save failed: ${t.error||"unknown"}`),ti("error",ei(t.error)),void await ni("ai-config.save",ei(t.error),t);Vn("[ai-config] saved"),ti("success","AI 设置已保存。"),await xi()})}),Pe.addEventListener("click",async()=>{await ri(Pe,async()=>{if(!window.botApi?.startCodexLogin)return;const e=await window.botApi.startCodexLogin();if(!e.ok)return Vn(`[codex] login launch failed: ${e.detail||"unknown"}`),ti("error",ei(e.detail)),void await ni("codex.login.launch",ei(e.detail),e);Vn("[codex] login launched",{detail:e.detail}),ti("success",e.detail||"Codex login started."),setTimeout(()=>{xi()},1500)})}),De.addEventListener("click",async()=>{const e=String(Fe.textContent||"codex login --device-auth").trim();try{if(!await(async e=>{if(navigator.clipboard?.writeText)return await navigator.clipboard.writeText(e),!0;const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly","true"),t.style.position="absolute",t.style.left="-9999px",document.body.appendChild(t),t.select();const n=document.execCommand("copy");return document.body.removeChild(t),n})(e))return void ti("error","无法复制命令,请手动执行。");Vn("[codex] login command copied"),ti("success","Codex 登录命令已复制。")}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[codex] copy login command failed",{error:t}),ti("error",t)}}),Ue.addEventListener("click",async()=>{await ri(Ue,async()=>{await xi(),Vn("[codex] auth status refreshed"),ti("success","Codex 状态已刷新。")})}),Q?.addEventListener("click",async()=>{await ri(Q,async()=>{await bi(),Vn("[browser] chromium status refreshed"),ti("success","Browser status 已刷新。")})}),ae?.addEventListener("click",async()=>{await ri(ae,async()=>{if(!window.botApi?.installPlaywrightMcp)return;const e=await window.botApi.installPlaywrightMcp();if(!e.ok)return Vn("[gateway] mcp install failed",{error:e.detail||e.error||"unknown"}),ti("error",ei(e.detail||e.error)),await ni("gateway.install",ei(e.detail||e.error),e),void await ki();Vn("[gateway] mcp configured",{detail:e.detail}),ti("success",e.detail||"WeGet Gateway MCP configured."),await ki()})}),re?.addEventListener("click",async()=>{await ri(re,async()=>{await ki(),ti("success","Gateway status 已刷新。")})}),se?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".gateway-test-btn");if(!n)return;const i=String(n.dataset.target||"").trim().toLowerCase();"gateway"!==i&&"codex_macro"!==i&&"browser"!==i&&"macro"!==i&&"coin"!==i&&"fx"!==i||await(async(e,t)=>{const n=window.botApi;n?.runGatewaySelfTest&&await ri(t,async()=>{const t=await n.runGatewaySelfTest({target:e}),i=se?.querySelector(`.gateway-test-badge[data-target="${e}"]`),o=se?.querySelector(`.gateway-test-summary[data-target="${e}"]`);if(i){const e=Boolean(t?.ok);i.textContent=e?"OK":"NG",i.classList.toggle("is-muted",!1),i.classList.toggle("is-disabled",!e)}if(o){const e=String(t?.summary||t?.error||t?.detail||"-").trim()||"-",n=String(t?.logPath||"").trim();o.textContent=n?`${e} [log] ${n}`:e}if(!t?.ok){const n=ei(t?.summary||t?.error||t?.detail||`${e} test failed`);return Vn("[gateway] self-test failed",{target:e,error:n,logPath:t?.logPath||""}),void ti("error",n)}Vn("[gateway] self-test ok",{target:e,summary:t?.summary||"",logPath:t?.logPath||""}),ti("success",`${e} test completed.`)})})(i,n)}),Y?.addEventListener("click",async()=>{await ri(Y,async()=>{await fi({forceSnapshot:!0}),Vn("[macro] snapshot refreshed"),ti("success","Macro snapshot 已刷新。")})}),R?.addEventListener("click",async()=>{await ri(R,async()=>{await fi(),Vn("[macro] calendar and news refreshed",{dateKey:String(K?.value||"").trim()}),ti("success","Macro calendar / news 已刷新。")})}),K?.addEventListener("change",()=>{fi()}),he.addEventListener("click",()=>{const e="password"===ye.type?"text":"password";ye.type=e,he.textContent="password"===e?"显示":"隐藏"}),ke.addEventListener("click",()=>{const e="password"===be.type?"text":"password";be.type=e,ke.textContent="password"===e?"显示":"隐藏"}),e.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi)return;ai();const t=c.value.trim(),n=s.value.trim(),i=l.value;if(t&&n&&i){_e.disabled=!0,_e.textContent="Loading...";try{const e=await window.botApi.login(t,i,n,d.checked);if(!e.ok)return Vn(`[ui] login failed: ${e.error||"unknown"}`),void oi("error",ei(e.error));Vn("[ui] login success"),oi("success","登录成功。"),ti("success","登录成功。"),await xi()}finally{_e.disabled=!1,_e.textContent="Login"}}}),je.addEventListener("click",async()=>{await ri(je,async()=>{if(!window.botApi)return;const e=await window.botApi.logout();if(!e.ok)return Vn(`[ui] logout failed: ${e.error||"unknown"}`),ti("error",ei(e.error)),void await ni("auth.logout",ei(e.error),e);Vn("[ui] logout"),ti("success","已登出。"),sn(!1),u.textContent="local mode (not logged in)",await xi()})});const Si=async({market:e,symbol:t,interval:n,canvas:i})=>{if(!window.botApi)return;const o=await window.botApi.openGmoKlineWindow({symbol:t,interval:n,market:e});if(!o.ok)return Vn(`[trade] open ${e} kline failed: ${o.error||"unknown"}`),ti("error",ei(o.error)),void await ni(`${e}.kline`,ei(o.error),o);const a=Array.isArray(o.candles)?o.candles:[],r=Dn(e);r.symbol=String(o.symbol||t||"").toUpperCase(),r.interval=n,r.candles=a.map(e=>({time:Number(e.time||0),open:Number(e.open||0),high:Number(e.high||0),low:Number(e.low||0),close:Number(e.close||0)})),r.lastFetchBucket=Math.floor(Date.now()/Pn(n)),si(i,r.candles,jn(e)),Vn("[trade] kline rendered",{market:e,symbol:o.symbol||t,interval:o.interval||n,candles:a.length}),ii()},vi=async()=>{await Si({market:"coin",symbol:Bt,interval:Nt,canvas:qe})},Ei=async()=>{await Si({market:"fx",symbol:$t,interval:Lt,canvas:Ge})},Ci=({container:e,getCurrent:t,setCurrent:n,onChange:i})=>{const o=Array.from(e.querySelectorAll(".interval-btn"));for(const e of o)e.addEventListener("click",async()=>{const a=String(e.dataset.interval||"").trim();if(a&&a!==t()){n(a);for(const t of o)t.classList.toggle("is-active",t===e);await i()}})};ze.addEventListener("change",async()=>{Bt=String(ze.value||"BTC_JPY").trim().toUpperCase(),Tn("coin",Bt);try{await Mn("coin",Bt),await vi(),await Yn("coin")}catch(e){const t=ei(e instanceof Error?e.message:String(e));ti("error",t)}}),Oe.addEventListener("change",async()=>{$t=String(Oe.value||"USD_JPY").trim().toUpperCase(),Tn("fx",$t);try{await Mn("fx",$t),await Ei(),await Yn("fx")}catch(e){const t=ei(e instanceof Error?e.message:String(e));ti("error",t)}}),Ci({container:He,getCurrent:()=>Nt,setCurrent:e=>{Nt=e},onChange:vi}),Ci({container:Je,getCurrent:()=>Lt,setCurrent:e=>{Lt=e},onChange:Ei}),ct.addEventListener("click",async()=>{await ri(ct,async()=>{await qn("coin")})}),lt.addEventListener("click",async()=>{await ri(lt,async()=>{await qn("fx")})}),et.addEventListener("input",()=>{Nn("coin")}),tt.addEventListener("input",()=>{Nn("fx")}),ht.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeCoinPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=vn(String(n.dataset.size||""));o&&Number.isFinite(s)&&!(s<=0)&&c?await ri(n,async()=>{const e=await i.closeCoinPosition({symbol:o,side:r,positionId:s,size:c});if(!e?.ok){const t=ei(e?.error||"coin close order failed");return Vn("[coin] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,error:t}),ti("error",t),void await ni("coin.closeOrder",t,e)}Vn("[coin] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,result:e.result||null}),ti("success","決済注文を送信しました。"),await qn("coin"),await Yn("coin")}):ti("error","決済対象データが不正です。")}),xt.addEventListener("click",async e=>{const t=e.target,n=t?.closest("button.close-btn");if(!n||n.disabled)return;const i=window.botApi;if(!i?.closeFxPosition)return;const o=String(n.dataset.symbol||"").trim().toUpperCase(),a="SELL"===String(n.dataset.side||"").trim().toUpperCase()?"SELL":"BUY",r="BUY"===a?"SELL":"BUY",s=Number(n.dataset.positionId||0),c=String(n.dataset.size||"").trim();o&&Number.isFinite(s)&&!(s<=0)&&c?await ri(n,async()=>{const e=await i.closeFxPosition({symbol:o,side:r,positionId:s,size:c});if(!e?.ok){const t=ei(e?.error||"fx close order failed");return Vn("[fx] close order failed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,error:t}),ti("error",t),void await ni("fx.closeOrder",t,e)}Vn("[fx] close order placed",{symbol:o,positionSide:a,closeSide:r,positionId:s,size:c,result:e.result||null}),ti("success","決済注文を送信しました。"),await qn("fx"),await Yn("fx")}):ti("error","決済対象データが不正です。")}),nt.addEventListener("click",async()=>{await Wn("BUY",nt)}),it.addEventListener("click",async()=>{await Wn("SELL",it)}),ot.addEventListener("click",async()=>{await Rn("BUY",ot)}),at.addEventListener("click",async()=>{await Rn("SELL",at)}),St.addEventListener("click",async()=>{await ri(St,async()=>{await Yn("coin")})}),vt.addEventListener("click",async()=>{await ri(vt,async()=>{await Yn("coin")})}),Et.addEventListener("click",async()=>{await ri(Et,async()=>{await Yn("fx")})}),Ct.addEventListener("click",async()=>{await ri(Ct,async()=>{await Yn("fx")})}),de.addEventListener("submit",async e=>{if(e.preventDefault(),!window.botApi?.sendChat)return;const t=String(me.value||"").trim();if(t){Qn("user",t),me.value="",ue.disabled=!0,((e="AI")=>{Zn();const t=document.createElement("div");t.className="msg assistant assistant-thinking";const n=document.createElement("div");n.className="msg-title",n.textContent=`${e}:`;const i=document.createElement("div");i.className="thinking-body";const o=document.createElement("span");o.className="thinking-text",o.textContent="Thinking";const a=document.createElement("span");a.className="thinking-dots",a.innerHTML="<span></span><span></span><span></span>",i.append(o,a),t.append(n,i),ce.appendChild(t),ce.scrollTop=ce.scrollHeight})("AI");try{const e=await window.botApi.sendChat(t);if(!e.ok)return Zn(),Qn("assistant",`Error: ${e.error||"unknown"}`),ti("error",ei(e.error)),void await ni("ai.chat",ei(e.error),e);ii()}catch(e){Zn();const t=ei(e instanceof Error?e.message:String(e));Qn("assistant",`Error: ${t}`),ti("error",t),await ni("ai.chat.exception",t,e)}finally{ue.disabled=!1}}}),window.addEventListener("error",e=>{const t=ei(e.error?.message||e.message||"Unknown UI error");Vn("[ui] uncaught error",{error:t}),ti("error",t),ni("ui.error",t,{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno})}),window.addEventListener("unhandledrejection",e=>{const t=e.reason instanceof Error?e.reason.message:String(e.reason||"Unhandled promise rejection"),n=ei(t);Vn("[ui] unhandled rejection",{error:n}),ti("error",n),ni("ui.unhandledrejection",n,e.reason)}),le?.addEventListener("click",async e=>{const t=e.target,n=t?.closest(".active-task-cancel-btn");if(!n)return;const i=String(n.dataset.taskId||"").trim();if(!i||!window.botApi?.cancelActiveTask)return;n.disabled=!0;const o=await window.botApi.cancelActiveTask(i);if(!o?.ok)return n.disabled=!1,void ti("error",ei(o?.error||"Failed to cancel task"));ti("success",`Cancel requested for ${i}`)}),window.botApi&&(window.botApi.getActiveTasks?.().then(e=>{e?.ok&&bn({type:"snapshot",tasks:Array.isArray(e.tasks)?e.tasks:[]})}),window.botApi.onStatus(e=>{cn(String(e.status||""))}),window.botApi.onTaskRuntime(e=>{bn(e)}),window.botApi.onChatEvent(e=>{if(!e)return;const t=nn(e);if("line_user"===t.type){const e=t.lineUserId?`LINE(${String(t.lineUserId)})`:"LINE";return void Qn("line-user",String(t.text||""),e)}if("line_assistant"===t.type){Zn();const e=t.lineUserId?`AI->LINE(${String(t.lineUserId)})`:"AI->LINE";return void Qn("line-assistant",String(t.text||""),e)}"assistant_status"!==t.type?"assistant"===t.type&&(Zn(),Qn("assistant",String(t.text||""))):(e=>{const t=String(e||"").trim().replace(/\s+/g," ");if(!t)return;if(/^[{}[\],:]+$/.test(t))return;if(/^["']?[a-zA-Z0-9_-]+["']?\s*:\s*$/.test(t))return;const n=ce.querySelector(".msg.assistant-thinking .thinking-text");n&&(n.textContent=t)})(String(t.status||"Thinking"))}),window.botApi.onTradeExecution(e=>{if(!e)return;const t=nn(e),n="coin"===String(t.market||"").trim().toLowerCase()?"coin":"fx",i=String(t.symbol||"").trim().toUpperCase(),o=String(t.side||"").trim().toUpperCase()||"-",a=String(t.settleType||"").trim().toUpperCase()||"OPEN",r=String(t.executionSize??"").trim(),s=String(t.executionPrice??"").trim(),c="CLOSE"===a?"決済":"新規",l=`${i} ${o} ${r}${s?` @ ${s}`:""}`;Vn(`[${n}] execution filled`,{symbol:i,side:o,settleType:a,size:r,price:s,raw:t}),ti("success",`約定成功 (${c}) ${l}`.trim()),qn(n).catch(()=>{}),Yn(n).catch(()=>{})})),tn=setInterval(()=>{wn()},15e3);const Ii=async()=>{if(Ut&&!Ft&&!jt){jt=!0;try{if(!await async function(){let e=0;if(Tn("coin",Bt),Tn("fx",$t),$n("coin")){e+=1;try{await Mn("coin",Bt)}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[coin] quote fetch failed",{error:t}),ti("error",t)}await vi()}if($n("fx")){e+=1;try{await Mn("fx",$t)}catch(e){const t=ei(e instanceof Error?e.message:String(e));Vn("[fx] quote fetch failed",{error:t}),ti("error",t)}await Ei()}if(window.botApi?.getSymbolRules&&$n("coin"))try{const e=await window.botApi.getSymbolRules({market:"coin"});if(e?.ok&&Array.isArray(e.rules)){Wt.clear();for(const t of e.rules){const e=String(t.symbol||"").trim().toUpperCase();e&&Wt.set(e,{minOrderSize:String(t.minOrderSize||""),maxOrderSize:String(t.maxOrderSize||""),sizeStep:String(t.sizeStep||"")})}}}catch(e){Vn("[coin] symbol rules fetch failed",{error:e instanceof Error?e.message:String(e)})}return e>0}())return;await qn(),await Yn(),$n("coin")&&!Pt&&(Pt=setInterval(()=>{Hn("coin")},1e3)),$n("fx")&&!Dt&&(Dt=setInterval(()=>{Hn("fx")},1e3)),Ft=!0}finally{jt=!1}}};sn(!1),(()=>{try{an("true"===window.localStorage.getItem(on))}catch{an(!1)}})(),K&&!K.value&&(K.value=gn(new Date)),(async()=>{if(!window.botApi?.getSavedProfile)return;const e=await window.botApi.getSavedProfile();e?.ok&&e.profile&&(s.value=e.profile.botId||"",c.value=e.profile.email||"",l.value=e.profile.password||"",d.checked=Boolean(e.profile.remember))})(),(async()=>{if(!window.botApi?.getSkillConfig||!window.botApi.getAiConfig)return;const[e,t,n]=await Promise.all([window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-fx"),window.botApi.getSkillConfig("@ai.weget.jp/skill-gmo-coin"),window.botApi.getAiConfig()]);e?.ok&&e.config&&(ge.value=String(e.config.riskDailyLossLimitJpy||5e4),we.value=String(e.config.fxApiKey||""),be.value=String(e.config.fxApiSecret||"")),t?.ok&&t.config&&(pe.value=String(t.config.riskDailyLossLimitJpy||5e4),fe.value=String(t.config.cryptoApiKey||""),ye.value=String(t.config.cryptoApiSecret||"")),n?.ok&&n.config&&($e.value=n.config.aiModel||"gpt-5.4",Me.value=n.config.logOutputDir||"")})(),xi();export{};
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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),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{};
@@ -76,10 +76,6 @@
76
76
  <span class="tab-btn-icon">AI</span>
77
77
  <span class="tab-btn-label">Codex Console</span>
78
78
  </button>
79
- <button type="button" class="tab-btn" data-tab="gateway" data-icon="GW">
80
- <span class="tab-btn-icon">GW</span>
81
- <span class="tab-btn-label">Gateway</span>
82
- </button>
83
79
  </div>
84
80
  </div>
85
81
  <div class="tab-group">
@@ -250,7 +246,10 @@
250
246
  </label>
251
247
  <label>
252
248
  <span id="coin-config-label-cryptoApiKey">Crypto API Key</span>
253
- <input id="crypto-api-key" type="text" />
249
+ <div class="inline-input-row">
250
+ <input id="crypto-api-key" type="password" />
251
+ <button id="toggle-crypto-key-btn" type="button" class="secondary">显示</button>
252
+ </div>
254
253
  </label>
255
254
  <label>
256
255
  <span id="coin-config-label-cryptoApiSecret">Crypto API Secret</span>
@@ -411,7 +410,10 @@
411
410
  </label>
412
411
  <label>
413
412
  <span id="fx-config-label-fxApiKey">FX API Key</span>
414
- <input id="fx-api-key" type="text" />
413
+ <div class="inline-input-row">
414
+ <input id="fx-api-key" type="password" />
415
+ <button id="toggle-fx-key-btn" type="button" class="secondary">显示</button>
416
+ </div>
415
417
  </label>
416
418
  <label>
417
419
  <span id="fx-config-label-fxApiSecret">FX API Secret</span>
@@ -441,6 +443,10 @@
441
443
  </div>
442
444
  </div>
443
445
  <div id="browser-skill-cards" class="browser-skill-shell"></div>
446
+ <div class="browser-skill-card">
447
+ <h3>Install Command</h3>
448
+ <code class="codex-command">npx playwright install chromium</code>
449
+ </div>
444
450
  <div class="browser-skill-card">
445
451
  <div class="browser-mcp-status-row">
446
452
  <span id="browser-chromium-status" class="skill-surface-badge is-muted">unknown</span>
@@ -458,47 +464,6 @@
458
464
  </section>
459
465
  </section>
460
466
 
461
- <section id="tab-gateway" class="tab-panel">
462
- <section class="panel">
463
- <div class="panel-title-block">
464
- <h2>WeGet Gateway MCP</h2>
465
- <p class="muted">Codex integration, gateway MCP registration, and per-skill connectivity tests.</p>
466
- </div>
467
- <div class="macro-status-strip">
468
- <div class="macro-status-main">
469
- <div class="macro-stat-row">
470
- <span class="muted">Codex Auth</span>
471
- <strong id="gateway-codex-auth">-</strong>
472
- </div>
473
- <div class="macro-stat-row">
474
- <span class="muted">Gateway MCP</span>
475
- <strong id="gateway-mcp-status">-</strong>
476
- </div>
477
- <div class="macro-stat-row">
478
- <span class="muted">Chromium</span>
479
- <strong id="gateway-browser-status">-</strong>
480
- </div>
481
- </div>
482
- <div class="macro-toolbar-actions">
483
- <button id="gateway-install-btn" type="button">Install Gateway MCP</button>
484
- <button id="gateway-refresh-btn" type="button" class="secondary">Refresh Status</button>
485
- </div>
486
- </div>
487
- <div class="browser-skill-card">
488
- <h3>Connection Detail</h3>
489
- <div id="gateway-context-file" class="muted">Context file: -</div>
490
- <pre id="gateway-detail" class="status-output">Loading gateway status...</pre>
491
- </div>
492
- </section>
493
- <section class="panel">
494
- <div class="panel-title-block">
495
- <h2>Skill Connectivity</h2>
496
- <p class="muted">Use these tests to verify the gateway can actually reach each skill path.</p>
497
- </div>
498
- <div id="gateway-skill-grid" class="gateway-skill-grid"></div>
499
- </section>
500
- </section>
501
-
502
467
  <section id="tab-macro" class="tab-panel">
503
468
  <section class="panel">
504
469
  <div class="skill-surface-head">
@@ -519,10 +484,6 @@
519
484
  <span class="muted">As Of</span>
520
485
  <strong id="macro-snapshot-as-of">-</strong>
521
486
  </div>
522
- <div class="macro-stat-row">
523
- <span class="muted">Fear & Greed</span>
524
- <strong id="macro-fear-greed-score">-</strong>
525
- </div>
526
487
  </div>
527
488
  <div class="macro-toolbar-actions">
528
489
  <button id="macro-refresh-snapshot-btn" type="button">Refresh Snapshot</button>
@@ -666,8 +627,6 @@
666
627
  <span>模型</span>
667
628
  <select id="ai-model">
668
629
  <option value="gpt-5.4">gpt-5.4</option>
669
- <option value="gpt-5-mini">gpt-5-mini</option>
670
- <option value="gpt-4.1">gpt-4.1</option>
671
630
  </select>
672
631
  </label>
673
632
  <label>
@@ -679,6 +638,37 @@
679
638
  <button id="logout-btn" type="button" class="secondary">Logout</button>
680
639
  </div>
681
640
  </section>
641
+ <section class="host-section">
642
+ <div class="panel-title-block">
643
+ <h3>WeGet Gateway MCP</h3>
644
+ <p class="muted">Codex integration, gateway MCP registration, and browser runtime status.</p>
645
+ </div>
646
+ <div class="macro-status-strip">
647
+ <div class="macro-status-main">
648
+ <div class="macro-stat-row">
649
+ <span class="muted">Codex Auth</span>
650
+ <strong id="gateway-codex-auth">-</strong>
651
+ </div>
652
+ <div class="macro-stat-row">
653
+ <span class="muted">Gateway MCP</span>
654
+ <strong id="gateway-mcp-status">-</strong>
655
+ </div>
656
+ <div class="macro-stat-row">
657
+ <span class="muted">Chromium</span>
658
+ <strong id="gateway-browser-status">-</strong>
659
+ </div>
660
+ </div>
661
+ <div class="macro-toolbar-actions">
662
+ <button id="gateway-install-btn" type="button">Install Gateway MCP</button>
663
+ <button id="gateway-refresh-btn" type="button" class="secondary">Refresh Status</button>
664
+ </div>
665
+ </div>
666
+ <div class="browser-skill-card">
667
+ <h3>Connection Detail</h3>
668
+ <div id="gateway-context-file" class="muted">Context file: -</div>
669
+ <pre id="gateway-detail" class="status-output">Loading gateway status...</pre>
670
+ </div>
671
+ </section>
682
672
  <section class="host-section skill-panel">
683
673
  <div class="skill-panel-head">
684
674
  <h3>Managed Skills</h3>
@@ -688,7 +678,6 @@
688
678
  </section>
689
679
  </section>
690
680
  </section>
691
- </section>
692
681
  </div>
693
682
  </main>
694
683
  <script type="module" src="./app.js"></script>
@@ -921,6 +921,31 @@ button.is-loading::before {
921
921
  word-break: break-all;
922
922
  }
923
923
 
924
+ .runtime-status {
925
+ display: inline-flex;
926
+ align-items: center;
927
+ border-radius: 999px;
928
+ padding: 4px 10px;
929
+ font-size: 12px;
930
+ font-weight: 700;
931
+ line-height: 1.2;
932
+ }
933
+
934
+ .runtime-status-ok {
935
+ background: #dcfce7;
936
+ color: #166534;
937
+ }
938
+
939
+ .runtime-status-warn {
940
+ background: #fef3c7;
941
+ color: #92400e;
942
+ }
943
+
944
+ .runtime-status-error {
945
+ background: #fee2e2;
946
+ color: #991b1b;
947
+ }
948
+
924
949
  .skill-panel {
925
950
  display: grid;
926
951
  gap: 10px;
@@ -1279,8 +1304,9 @@ button.is-loading::before {
1279
1304
  justify-content: center;
1280
1305
  text-align: center;
1281
1306
  padding: 12px 10px;
1282
- gap: 4px;
1307
+ gap: var(--tile-gap, 4px);
1283
1308
  color: #ffffff;
1309
+ overflow: hidden;
1284
1310
  }
1285
1311
 
1286
1312
  .macro-treemap-tile.up {
@@ -1292,13 +1318,13 @@ button.is-loading::before {
1292
1318
  }
1293
1319
 
1294
1320
  .macro-treemap-symbol {
1295
- font-size: clamp(17px, 1.8vw, 30px);
1321
+ font-size: var(--tile-symbol-font-size, clamp(17px, 1.8vw, 30px));
1296
1322
  font-weight: 700;
1297
1323
  line-height: 1;
1298
1324
  }
1299
1325
 
1300
1326
  .macro-treemap-change {
1301
- font-size: clamp(15px, 1.5vw, 22px);
1327
+ font-size: var(--tile-change-font-size, clamp(15px, 1.5vw, 22px));
1302
1328
  font-weight: 600;
1303
1329
  line-height: 1;
1304
1330
  }
@@ -1322,12 +1348,21 @@ button.is-loading::before {
1322
1348
 
1323
1349
  .macro-analyzer {
1324
1350
  max-width: none;
1325
- white-space: pre-wrap;
1351
+ white-space: normal;
1326
1352
  word-break: break-word;
1327
1353
  line-height: 1.55;
1354
+ text-align: left;
1328
1355
  background: #f8fbff;
1329
1356
  }
1330
1357
 
1358
+ .macro-analyzer-line {
1359
+ text-align: left;
1360
+ }
1361
+
1362
+ .macro-analyzer-line + .macro-analyzer-line {
1363
+ margin-top: 6px;
1364
+ }
1365
+
1331
1366
  .macro-ai-label {
1332
1367
  color: #62748f;
1333
1368
  font-weight: 700;
@@ -1507,16 +1542,19 @@ button.is-loading::before {
1507
1542
  }
1508
1543
 
1509
1544
  .composer {
1510
- display: grid;
1511
- gap: 8px;
1545
+ position: relative;
1546
+ display: block;
1547
+ padding-bottom: 54px;
1548
+ margin-top: 2px;
1512
1549
  }
1513
1550
 
1514
1551
  .composer button {
1515
- justify-self: end;
1552
+ position: absolute;
1553
+ right: 0;
1554
+ bottom: 0;
1516
1555
  }
1517
1556
 
1518
1557
  #tab-ai .panel {
1519
- height: calc(100vh - 180px);
1520
1558
  min-height: 520px;
1521
1559
  }
1522
1560
 
@@ -1524,8 +1562,7 @@ button.is-loading::before {
1524
1562
  display: grid;
1525
1563
  grid-template-columns: minmax(280px, 320px) minmax(0, 1fr);
1526
1564
  gap: 14px;
1527
- height: 100%;
1528
- min-height: 0;
1565
+ align-items: start;
1529
1566
  }
1530
1567
 
1531
1568
  .active-tasks-panel {
@@ -1535,7 +1572,7 @@ button.is-loading::before {
1535
1572
  padding: 14px;
1536
1573
  display: flex;
1537
1574
  flex-direction: column;
1538
- min-height: 0;
1575
+ min-height: 540px;
1539
1576
  }
1540
1577
 
1541
1578
  .active-tasks-head h3 {
@@ -1547,6 +1584,8 @@ button.is-loading::before {
1547
1584
  gap: 10px;
1548
1585
  overflow: auto;
1549
1586
  margin-top: 10px;
1587
+ min-height: 240px;
1588
+ max-height: 540px;
1550
1589
  }
1551
1590
 
1552
1591
  .active-task-empty {
@@ -1615,20 +1654,36 @@ button.is-loading::before {
1615
1654
  }
1616
1655
 
1617
1656
  .ai-chat-shell {
1618
- min-height: 0;
1619
1657
  display: flex;
1620
1658
  flex-direction: column;
1621
1659
  }
1622
1660
 
1623
1661
  #tab-ai .messages {
1624
- flex: 1 1 auto;
1625
- margin-bottom: 8px;
1662
+ flex: 0 0 auto;
1663
+ min-height: 400px;
1664
+ max-height: 540px;
1665
+ margin-bottom: 10px;
1626
1666
  }
1627
1667
 
1628
1668
  #tab-ai .composer {
1629
1669
  flex: 0 0 auto;
1630
1670
  }
1631
1671
 
1672
+ #tab-ai .composer textarea {
1673
+ width: 100%;
1674
+ min-height: 78px;
1675
+ max-height: 140px;
1676
+ box-sizing: border-box;
1677
+ resize: vertical;
1678
+ margin-top: 0;
1679
+ }
1680
+
1681
+ #tab-ai #send-btn {
1682
+ min-width: 66px;
1683
+ min-height: 34px;
1684
+ padding: 8px 16px;
1685
+ }
1686
+
1632
1687
  .logs-list {
1633
1688
  background: #10141f;
1634
1689
  color: #ddedff;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai.weget.jp/bot",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "WeGet bot host for Codex-centered skill runtime ",