tollkit-wallet 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +13 -0
  2. package/dist/actions.d.ts +131 -35
  3. package/dist/ai.d.ts +13 -1
  4. package/dist/ai.js +2 -2
  5. package/dist/ai.js.map +1 -1
  6. package/dist/autopilot.d.ts +7 -0
  7. package/dist/autopilot.js +118 -3
  8. package/dist/autopilot.js.map +1 -1
  9. package/dist/bots.d.ts +92 -0
  10. package/dist/bots.js +183 -0
  11. package/dist/bots.js.map +1 -0
  12. package/dist/bridge.js +7 -5
  13. package/dist/bridge.js.map +1 -1
  14. package/dist/dapp.d.ts +11 -0
  15. package/dist/dapp.js +82 -14
  16. package/dist/dapp.js.map +1 -1
  17. package/dist/dashboard.d.ts +12 -0
  18. package/dist/dashboard.js +136 -14
  19. package/dist/dashboard.js.map +1 -1
  20. package/dist/data/token-list.d.ts +12 -0
  21. package/dist/data/token-list.js +13 -0
  22. package/dist/data/token-list.js.map +1 -0
  23. package/dist/evmsend.d.ts +30 -0
  24. package/dist/evmsend.js +47 -0
  25. package/dist/evmsend.js.map +1 -0
  26. package/dist/holdings.d.ts +6 -0
  27. package/dist/holdings.js +41 -8
  28. package/dist/holdings.js.map +1 -1
  29. package/dist/ledger.d.ts +1 -0
  30. package/dist/ledger.js.map +1 -1
  31. package/dist/mcp.d.ts +1 -1
  32. package/dist/mcp.js +1 -1
  33. package/dist/pay.js +1 -1
  34. package/dist/pay.js.map +1 -1
  35. package/dist/policy.d.ts +2 -2
  36. package/dist/rules.d.ts +57 -6
  37. package/dist/rules.js +46 -4
  38. package/dist/rules.js.map +1 -1
  39. package/dist/seller.js +15 -4
  40. package/dist/seller.js.map +1 -1
  41. package/dist/send.js +3 -2
  42. package/dist/send.js.map +1 -1
  43. package/dist/solconfirm.d.ts +13 -0
  44. package/dist/solconfirm.js +21 -0
  45. package/dist/solconfirm.js.map +1 -0
  46. package/dist/stake.js +3 -2
  47. package/dist/stake.js.map +1 -1
  48. package/dist/swap.d.ts +58 -5
  49. package/dist/swap.js +327 -21
  50. package/dist/swap.js.map +1 -1
  51. package/dist/tokenlist.d.ts +70 -0
  52. package/dist/tokenlist.js +259 -0
  53. package/dist/tokenlist.js.map +1 -0
  54. package/dist/trademode.d.ts +3 -0
  55. package/dist/trademode.js +23 -0
  56. package/dist/trademode.js.map +1 -0
  57. package/dist/ui.js +202 -19
  58. package/dist/ui.js.map +1 -1
  59. package/dist/wallet.d.ts +13 -1
  60. package/dist/wallet.js +85 -5
  61. package/dist/wallet.js.map +1 -1
  62. package/extension/background.js +1 -1
  63. package/extension/popup.js +1 -1
  64. package/package.json +2 -2
package/dist/ui.js CHANGED
@@ -12,18 +12,24 @@ export const PAGE = String.raw `<!doctype html>
12
12
  @media (prefers-color-scheme:light){:root:not([data-theme=dark]){--bg:#eceef3;--card:#fff;--surf:#f4f5f8;--surf2:#e9ebf1;--line:#e1e3ea;--tx:#15161b;--mut:#666a7a}}
13
13
  *{box-sizing:border-box}html,body{margin:0;background:var(--bg);color:var(--tx);font:14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif}
14
14
  .wrap{max-width:420px;margin:24px auto;padding:0 16px}
15
- .card{background:var(--card);border:1px solid var(--line);border-radius:20px;overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,.35);display:flex;flex-direction:column;min-height:640px}
15
+ .card{background:var(--card);border:1px solid var(--line);border-radius:20px;overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,.35);display:flex;flex-direction:column;height:calc(100vh - 96px);height:calc(100dvh - 96px);min-height:520px;max-height:900px}
16
16
  header{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line)}
17
17
  .logo{width:30px;height:30px;flex:none;display:block}
18
18
  .brand{font:700 15px ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:-.01em;white-space:nowrap}.brand .dot{color:#4a8eff}.brand .sub{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;font-weight:500;color:var(--mut);margin-left:6px}
19
19
  .cp{margin-left:auto;flex:none;border:0;background:var(--surf2);color:var(--tx);border-radius:9px;padding:6px 10px;font:600 12px inherit;cursor:pointer;display:flex;align-items:center;gap:5px}
20
20
  .cp svg{width:14px;height:14px}.cp.done{color:var(--ok)}header .pill{margin-left:auto;white-space:nowrap;font-size:11px;padding:4px 9px;border-radius:99px;background:var(--surf);color:var(--mut)}
21
- main{flex:1;padding:16px;overflow:auto}
22
- nav{display:grid;grid-template-columns:repeat(6,1fr);border-top:1px solid var(--line);background:var(--card)}
21
+ main{flex:1;min-height:0;padding:16px;overflow:auto;overscroll-behavior:contain}
22
+ nav{flex:none;display:grid;grid-template-columns:repeat(6,1fr);border-top:1px solid var(--line);background:var(--card)}
23
23
  nav button{background:none;border:0;color:var(--mut);padding:10px 0 12px;font:inherit;font-size:11px;cursor:pointer;position:relative}
24
24
  nav button svg{display:block;margin:0 auto 3px;width:20px;height:20px}
25
25
  nav button.on{color:var(--acc)}
26
26
  .badge{position:absolute;top:5px;left:calc(50% + 6px);background:var(--bad);color:#fff;border-radius:99px;font-size:10px;padding:0 5px;min-width:16px}
27
+ .badge.dot{min-width:0;width:8px;height:8px;padding:0;top:7px;left:calc(50% + 8px)}
28
+ nav button.attn{--hi:var(--acc)}nav button.attn-bad{--hi:var(--bad)}nav button.attn-warn{--hi:var(--warn)}nav button.attn-ok{--hi:var(--ok)}
29
+ nav button.attn:not(.on){color:var(--hi)}nav button.attn .badge{background:var(--hi);color:#fff}nav button.attn-warn .badge{color:#1a1206}
30
+ nav button.attn:not(.on)::before{content:"";position:absolute;left:50%;top:6px;width:34px;height:34px;margin-left:-17px;border-radius:50%;background:radial-gradient(circle,color-mix(in srgb,var(--hi) 30%,transparent),transparent 70%);animation:navhi 2.4s ease-in-out infinite;pointer-events:none}
31
+ nav button.attn:not(.on) .emo{opacity:1;filter:none}
32
+ @keyframes navhi{0%,100%{opacity:.35;transform:scale(.85)}50%{opacity:1;transform:scale(1.1)}}
27
33
  .big{font-size:34px;font-weight:700;letter-spacing:-.5px;margin:6px 0 2px}
28
34
  .mut{color:var(--mut)}.sm{font-size:12px}
29
35
  .row{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--surf);border-radius:12px;margin-top:8px}
@@ -170,6 +176,16 @@ input,select{transition:border-color .2s ease,box-shadow .25s ease}input:focus,s
170
176
  .aist{margin:0 0 4px;padding-left:18px;font-size:13px;line-height:1.55}.aist li{margin:5px 0}
171
177
  .aidlg code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;color:var(--acc);overflow-wrap:anywhere}
172
178
  .kbd{font-family:ui-monospace,monospace;font-size:.85em;background:var(--surf2);border:1px solid var(--line);border-radius:6px;padding:1px 6px}
179
+ .tmdlg{max-width:560px}.tmdlg h2{text-align:center}.tmdlg .ailead{text-align:center}
180
+ .tmopts{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:6px 0 12px}@media(max-width:480px){.tmopts{grid-template-columns:1fr}}
181
+ .tmopt{display:flex;flex-direction:column;gap:6px;text-align:left;background:var(--surf);border:1px solid var(--line);border-radius:16px;padding:14px;color:var(--tx);font:inherit;cursor:pointer;animation:tk-rise .5s var(--ease) both;animation-delay:calc(var(--i,0)*90ms + 120ms);transition:border-color .2s ease,transform .25s var(--ease),box-shadow .25s ease}
182
+ .tmopt:hover,.tmopt:focus-visible{border-color:var(--acc);transform:translateY(-2px);box-shadow:0 14px 34px -18px rgba(var(--glow),.55)}.tmopt b{font-size:15.5px}.tmopt small{color:var(--mut);font-size:12.5px;line-height:1.45}.tmopt .botcta{margin-top:auto}
183
+ .tmico{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:12px;flex:none}.tmico svg{width:21px;height:21px}.tmico.sm{width:32px;height:32px;border-radius:10px}.tmico.sm svg{width:18px;height:18px}
184
+ .tmai{background:color-mix(in srgb,#d97757 18%,transparent);color:#d97757}.tmpy{background:color-mix(in srgb,var(--acc) 16%,transparent);color:var(--acc)}
185
+ .pycard{background:var(--surf);border:1px solid color-mix(in srgb,var(--acc) 40%,var(--line));border-radius:14px;padding:12px;margin:12px 0 4px}.pyct{display:flex;align-items:center;gap:10px}.pyct>div{flex:1}
186
+ .pystep{display:flex;align-items:center;gap:8px;margin:16px 0 4px}.pystep b{font-size:14px}.pyn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background:color-mix(in srgb,var(--acc) 18%,transparent);color:var(--acc);font-size:12px;font-weight:700}
187
+ .pymore{margin:2px 0 6px}.pymore summary{cursor:pointer;font-size:12.5px;color:var(--mut)}.pymore summary:hover{color:var(--tx)}
188
+ .tmlater{padding:4px 10px 0}.tmseg{margin:0}.tmseg .bsb{padding:5px 12px}
173
189
  .aicmd{display:flex;gap:8px;align-items:flex-start;background:#08090c;border:1px solid var(--line);border-radius:12px;padding:9px 9px 9px 13px;margin:8px 0}
174
190
  .aicmd pre{flex:1;min-width:0;margin:0;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:#cdd6e4;white-space:pre-wrap;overflow-wrap:anywhere}
175
191
  .aicp{padding:6px 11px;font-size:12.5px;border-radius:9px}
@@ -230,6 +246,24 @@ input,select{transition:border-color .2s ease,box-shadow .25s ease}input:focus,s
230
246
  .tl{width:100%;height:100%;border-radius:50%;background:var(--surf2);display:flex;align-items:center;justify-content:center;font-size:10.5px;font-weight:700;color:var(--mut);letter-spacing:-.02em}
231
247
  .rowc{cursor:pointer}.rowc:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
232
248
  .hfoot{margin-top:8px;font-size:12px;color:var(--mut);text-align:center}
249
+ .botc{background:var(--surf);border:1px solid var(--line);border-radius:14px;padding:12px;margin-top:8px}.botct{display:flex;align-items:flex-start;gap:8px;justify-content:space-between}.botpill{white-space:nowrap}.botpill.ok{background:rgba(60,207,145,.15);color:var(--ok)}.botpill.warn{background:rgba(245,184,74,.15);color:var(--warn)}.botpill.bad{background:rgba(255,107,107,.15);color:var(--bad)}
250
+ .botstats{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-top:10px}.botstats div{background:var(--card);border-radius:10px;padding:6px 8px;min-width:0}.botstats small{display:block;color:var(--mut);font-size:11px}.botstats b{font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
251
+ .botcat{display:grid;grid-template-columns:1fr 1fr;grid-auto-flow:row dense;gap:8px}.botlim{font-style:normal;color:var(--mut);opacity:.75;font-size:11px;margin-left:4px;white-space:nowrap}.botf input.botcap{animation:botcap .5s ease}@keyframes botcap{0%,100%{border-color:var(--line)}30%{border-color:var(--acc);box-shadow:0 0 0 3px rgba(var(--glow),.25)}}.botwrap{grid-column:1/-1;display:grid;grid-template-rows:1fr}.botin{min-height:0}.botin>.botform{margin:0}.botwrap.drop{animation:botopen .42s var(--ease) both}.botwrap.drop .botform{animation:botslide .42s var(--ease) both}.botwrap.closing{animation:botshut .24s ease-in both}.botwrap.drop .botin,.botwrap.closing .botin{overflow:hidden}
252
+ @keyframes botopen{from{grid-template-rows:0fr}to{grid-template-rows:1fr}}@keyframes botslide{from{opacity:0;transform:translateY(-14px) scale(.98)}to{opacity:1;transform:none}}@keyframes botshut{from{grid-template-rows:1fr;opacity:1}to{grid-template-rows:0fr;opacity:0}}
253
+ nav button .emo{display:block;margin:0 auto 3px;width:20px;height:20px;font-size:17px;line-height:20px;text-align:center;opacity:.75;filter:grayscale(.35);transition:transform .45s var(--ease),opacity .3s,filter .3s}nav button.on .emo{opacity:1;filter:none;transform:translateY(-2px) scale(1.1)}.bottpl{text-align:left;background:var(--surf);border:1px solid var(--line);border-radius:12px;padding:10px;color:var(--tx);font:inherit;cursor:pointer;display:flex;flex-direction:column;gap:4px}.bottpl small{color:var(--mut);font-size:12px;line-height:1.35}.bottpl.on,.bottpl:hover{border-color:var(--acc)}
254
+ .tplh{display:flex;align-items:flex-start;justify-content:space-between;gap:6px}.tplh b{min-width:0}
255
+ .botcta{margin-top:auto;align-self:flex-start;display:inline-flex;align-items:center;gap:6px;padding:6px 11px;border-radius:99px;border:0;background:color-mix(in srgb,var(--acc) 15%,transparent);color:var(--acc);font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;transition:background .2s ease}
256
+ .botcta svg{transition:transform .25s var(--ease)}.bottpl:hover .botcta,.botcta:hover{background:color-mix(in srgb,var(--acc) 24%,transparent)}.bottpl:hover .botcta svg,.botcta:hover svg{transform:translateX(3px)}
257
+ .botcta.g{background:transparent;color:var(--mut);padding-left:6px;padding-right:6px}.botcta.g:hover{background:var(--surf2);color:var(--tx)}
258
+ .bottpl>.botcta{margin-top:auto;padding-top:6px}.bottpl>.boardl{margin-bottom:6px}
259
+ .bottpl.has{cursor:default;border-color:color-mix(in srgb,var(--ok) 45%,var(--line))}.bottpl.has.err{border-color:color-mix(in srgb,var(--bad) 55%,var(--line))}.bottpl.has.on{border-color:var(--acc)}
260
+ .tplbtns{display:flex;flex-wrap:wrap;gap:4px;margin-top:auto;padding-top:6px}.tplbtns .botcta{margin-top:0}
261
+ .botc.botflash{animation:botflash 1.6s ease}@keyframes botflash{0%,60%{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 30%,transparent)}100%{border-color:var(--line);box-shadow:none}}
262
+ .botgrid{display:grid;grid-template-columns:1fr 1fr;gap:8px}.botf{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--mut)}.botf input,.botf select{background:var(--card);border:1px solid var(--line);color:var(--tx);border-radius:10px;padding:8px;font:inherit;font-size:13px;min-width:0}
263
+ .botshare{display:flex;gap:8px;align-items:flex-start;font-size:12px;color:var(--mut);margin-top:14px;line-height:1.4}.botshare input{width:auto;flex:0 0 auto;margin:2px 0 0}.addt .crow2 select{flex:0 0 104px;width:104px}.addt .crow2 input{flex:1 1 auto;min-width:0;width:auto}@media(max-width:420px){.botcat,.botgrid{grid-template-columns:1fr}.botstats{grid-template-columns:1fr 1fr}}
264
+ .tlogo{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block}.tbadge{font-size:10px;padding:1px 6px;vertical-align:1px;margin-left:4px}
265
+ .atlist{display:flex;flex-direction:column;gap:4px;margin-top:8px;max-height:320px;overflow-y:auto}.atrow{display:flex;align-items:center;gap:10px;text-align:left;background:var(--surf);border:1px solid var(--line);border-radius:12px;padding:8px 10px;color:var(--tx);font:inherit;cursor:pointer}.atrow:hover{border-color:var(--acc)}.atn{min-width:0;display:flex;flex-direction:column}.atn small{color:var(--mut);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
266
+ .atw{margin-top:10px;padding:10px;border-radius:10px;background:rgba(255,107,107,.08);border:1px solid rgba(255,107,107,.35);color:var(--bad);font-size:13px}.atw ul,.atchk ul{margin:6px 0 0;padding-left:18px}.atw li{margin:3px 0;color:var(--tx)}.atwarn{border-color:rgba(255,107,107,.45)}
233
267
  .crow2{display:flex;gap:6px;margin-top:8px}.crow2 select,.crow2 input{background:var(--card);border:1px solid var(--line);color:var(--tx);border-radius:10px;padding:8px;font:inherit;font-size:13px}.crow2 input{flex:1;min-width:0}.crow2 .b{padding:8px 14px}
234
268
  .chdlg{max-width:560px}
235
269
  .chh{display:flex;align-items:center;gap:10px}.chh h2{margin:0;font-size:18px}
@@ -284,11 +318,12 @@ var COIN=${COIN_JSON};
284
318
  var AIIC=${AI_ICON_JSON},AIO=null,AIPW="",AIMSG={},AIOPEN={},AIBUSY=null;
285
319
  function aiSvg(k){var i=AIIC[k];return '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor"'+(i.rule?' fill-rule="'+i.rule+'"':'')+' d="'+i.d+'"/></svg>'}
286
320
  var AIMETA={claude:["claude","Claude"],"claude-code":["claude","Claude Code"],cursor:["cursor","Cursor"],vscode:["copilot","VS Code"],codex:["terminal","Codex"],gemini:["gemini","Gemini CLI"]};
287
- function aiBtn(){var on=((S.connect||{}).apps||[]).filter(function(a){return a.added});
321
+ function aiBtn(){var on=((S.connect||{}).apps||[]).filter(function(a){return a.added}),rm=(S.connect||{}).remote;
322
+ if(rm&&rm.on)on=[{id:"claude"}];
288
323
  if(!on.length)return '<button class="b aib" onclick="openAI()"><span class="aiic">'+aiSvg("claude")+aiSvg("cursor")+aiSvg("gemini")+'</span>Connect your AI</button>';
289
324
  var icons=[],names=[];on.forEach(function(a){var m=AIMETA[a.id];if(!m)return;names.push(m[1]);if(icons.indexOf(m[0])<0)icons.push(m[0])});
290
325
  var wait=(S.rules||[]).filter(function(r){return r.status==="proposed"}).length;
291
- return '<div class="aiconn"><button class="aich" onclick="openAI()" title="Manage connected AI apps"><span class="aistk">'+icons.slice(0,4).map(function(k){return '<i class="ail-'+k+'">'+aiSvg(k)+'</i>'}).join("")+'</span><span class="aichn"><b>'+esc(names.join(" + "))+'</b><small><span class="ok">●</span> Connected</small></span></button>'+
326
+ return '<div class="aiconn"><button class="aich" onclick="openAI()" title="Manage connected AI apps"><span class="aistk">'+icons.slice(0,4).map(function(k){return '<i class="ail-'+k+'">'+aiSvg(k)+'</i>'}).join("")+'</span><span class="aichn"><b>'+esc(rm&&rm.on?"Your AI apps":names.join(" + "))+'</b><small>'+(rm&&rm.on&&!rm.online?'<span class="warn">●</span> Waiting for the wallet':'<span class="ok">●</span> Connected')+'</small></span></button>'+
292
327
  '<button class="b aiap" onclick="goAuto()"><svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M13 2L4 14h7l-1 8 9-12h-7z" fill="currentColor"/></svg>Autopilot'+(wait?'<span class="aiwt">'+wait+'</span>':'')+'</button></div>'}
293
328
  function goAuto(){go("autopilot");setTimeout(function(){var t=document.getElementById("chin");if(t&&!t.disabled)t.focus()},120)}
294
329
  function aiCfg(){var c=S.connect||{};return c.launch||{command:"npx",args:["-y","tollkit-wallet","mcp"],env:{TOLLKIT_WALLET_PASSWORD_FILE:c.password_file||""}}}
@@ -316,7 +351,34 @@ else if(st.added){line='<span class="warn">Needs an update</span>';btn='<button
316
351
  else{line='Found on this computer';btn='<button class="b aiadd" data-act="add" data-id="'+x.id+'"'+(busy?" disabled":"")+'>'+(busy?"Adding…":"Add")+'</button>'}
317
352
  return '<div class="airow" style="--i:'+i+'"><div class="airt">'+aiSvg(x.icon)+'<div class="ainm"><b>'+esc(x.name)+'</b><small>'+line+'</small></div>'+btn+'</div>'+(m?'<div class="aim '+(m[0]?"ok":"bad")+'">'+esc(m[1])+'</div>':'')+'<button class="aihand" data-hand="'+x.id+'">'+(AIOPEN[x.id]?"Hide steps":"Do it by hand")+'</button>'+(AIOPEN[x.id]?'<div class="aihb">'+x.hand()+'</div>':'')+'</div>'}
318
353
  function svgOk(){return '<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" style="vertical-align:-2px;margin-right:4px"><path d="M5 13l4 4L19 7" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg>'}
354
+ // Adding a connector by its address. Steps checked against each app's docs on 2026-10-14: support.claude.com/en/articles/11175166,
355
+ // developers.openai.com/api/docs/guides/developer-mode, code.claude.com/docs/en/mcp-quickstart, cursor.com/docs/mcp,
356
+ // code.visualstudio.com/docs/copilot/reference/mcp-configuration, developers.openai.com/codex/mcp, geminicli.com/docs/tools/mcp-server.
357
+ var AIREMOTE=[
358
+ {id:"claude",name:"Claude",sub:"Web, desktop and mobile",icon:"claude",hand:function(u){return '<ol class="aist"><li>Open <a href="https://claude.ai/customize/connectors" target="_blank" rel="noopener">claude.ai/customize/connectors</a>, click <b>+</b>, then <b>Add custom connector</b>.</li><li>Paste your address and click <b>Add</b>.</li></ol><p class="aismall">Free plans can add one custom connector. On Team and Enterprise, an Owner adds it (Add → Custom → Web).</p>'}},
359
+ {id:"chatgpt",name:"ChatGPT",sub:"Developer mode",icon:"terminal",hand:function(u){return '<ol class="aist"><li>In ChatGPT on the web: <b>Settings → Apps → Advanced settings</b>, turn on <b>Developer mode</b>.</li><li>Click <b>Create app</b>, paste your address, choose <b>No authentication</b>, and create it.</li></ol><p class="aismall">Developer mode depends on your ChatGPT plan; OpenAI’s help pages list which.</p>'}},
360
+ {id:"claude-code",name:"Claude Code",sub:"Terminal",icon:"claude",hand:function(u){return '<p class="aip">Paste into your terminal:</p>'+aiCode("ar-cc","claude mcp add --transport http --scope user tollkit-wallet "+shq(u))}},
361
+ {id:"cursor",name:"Cursor",sub:"Code editor",icon:"cursor",hand:function(u){return '<p class="aip">Add this to <span class="kbd">~/.cursor/mcp.json</span>:</p>'+aiCode("ar-cu",JSON.stringify({mcpServers:{"tollkit-wallet":{url:u}}},null,2))}},
362
+ {id:"vscode",name:"VS Code",sub:"With GitHub Copilot",icon:"copilot",hand:function(u){return '<p class="aip">Run <b>MCP: Add Server</b>, choose <b>HTTP</b> and paste your address. Or add to <span class="kbd">mcp.json</span>:</p>'+aiCode("ar-vs",JSON.stringify({servers:{"tollkit-wallet":{type:"http",url:u}}},null,2))}},
363
+ {id:"codex",name:"Codex",sub:"OpenAI’s terminal agent",icon:"terminal",hand:function(u){return '<p class="aip">Add to <span class="kbd">~/.codex/config.toml</span>:</p>'+aiCode("ar-cx",'[mcp_servers.tollkit_wallet]\nurl = "'+u+'"')}},
364
+ {id:"gemini",name:"Gemini CLI",sub:"Terminal",icon:"gemini",hand:function(u){return '<p class="aip">Paste into your terminal:</p>'+aiCode("ar-gm","gemini mcp add -s user --transport http tollkit-wallet "+shq(u))}}
365
+ ];
366
+ function aiRemote(r){var h='<h2>Connect your AI</h2><p class="ailead">Let Claude, ChatGPT, Cursor or another AI app use this wallet. It follows your limits, can’t withdraw or see your keys, and any rules it suggests wait in Autopilot for you.</p>',m=AIMSG.remote;
367
+ if(!r.on)return h+'<label class="ailbl" for="aipw">Wallet password</label><input id="aipw" class="aiin" type="password" autocomplete="current-password" placeholder="Needed to turn this on" value="'+esc(AIPW)+'">'+
368
+ '<button class="b aigoap" data-ract="on"'+(AIBUSY?" disabled":"")+'>'+(AIBUSY?"Making it…":"Make my connector address")+'</button>'+(m?'<div class="aim '+(m[0]?"ok":"bad")+'">'+esc(m[1])+'</div>':'')+
369
+ '<p class="aismall">You get a private web address to add to your AI app. Tollkit’s server passes the app’s requests to this wallet while Chrome is open and the wallet is unlocked. Your keys stay here.</p>';
370
+ h+='<label class="ailbl">Your connector address</label>'+aiCode("ai-url",r.url)+
371
+ '<p class="aismall"><span class="'+(r.online?"ok":"warn")+'">●</span> '+(r.online?"The wallet is reachable.":"Waiting for the wallet to check in…")+' Anyone with this address can use the wallet within your limits, so keep it private.</p>'+
372
+ '<div class="ailist">'+AIREMOTE.map(function(x,i){return '<div class="airow" style="--i:'+i+'"><div class="airt">'+aiSvg(x.icon)+'<div class="ainm"><b>'+esc(x.name)+'</b><small>'+esc(x.sub)+'</small></div></div><button class="aihand" data-hand="r-'+x.id+'">'+(AIOPEN["r-"+x.id]?"Hide steps":"How to add it")+'</button>'+(AIOPEN["r-"+x.id]?'<div class="aihb">'+x.hand(r.url)+'</div>':'')+'</div>'}).join("")+'</div>'+
373
+ '<div class="aidone"><b>Check it worked.</b> Ask your AI: <b>“What’s in my Tollkit wallet?”</b></div>'+
374
+ '<label class="ailbl" for="aipw">Wallet password (for a new address)</label><input id="aipw" class="aiin" type="password" autocomplete="current-password" value="'+esc(AIPW)+'">'+
375
+ '<div class="btns" style="margin-top:10px"><button class="b g" data-ract="new"'+(AIBUSY?" disabled":"")+'>New address</button><button class="b no" data-ract="off"'+(AIBUSY?" disabled":"")+'>Turn off</button></div>'+(m?'<div class="aim '+(m[0]?"ok":"bad")+'">'+esc(m[1])+'</div>':'')+
376
+ '<p class="aismall">A new address stops the old one. Turning off stops every app.</p>';
377
+ return h}
378
+ function aiRemoteDo(act){if(act!=="off"&&!AIPW){AIMSG.remote=[0,"Type your wallet password first."];aiRender();document.getElementById("aipw").focus();return}
379
+ AIBUSY="remote";AIMSG.remote=null;aiRender();api("/connect/remote",{password:AIPW,action:act}).then(function(){AIPW="";AIMSG.remote=act==="off"?[1,"Turned off. No app can reach the wallet now."]:act==="new"?[1,"New address made. Add it to your apps again."]:null;return refresh()}).catch(function(e){AIMSG.remote=[0,e.message]}).then(function(){AIBUSY=null;aiRender()})}
319
380
  function aiRender(){if(!AIO)return;var c=S.connect||{},foc=document.activeElement&&document.activeElement.id==="aipw";
381
+ if(c.remote){var sc0=AIO.querySelector(".aidlg").scrollTop;AIO.querySelector(".aibody").innerHTML=aiRemote(c.remote);AIO.querySelector(".aidlg").scrollTop=sc0;var pw0=document.getElementById("aipw");if(pw0){pw0.oninput=function(){AIPW=pw0.value};if(foc)pw0.focus()}return}
320
382
  var h='<h2>Connect your AI</h2><p class="ailead">Let Claude, Cursor or another AI app use this wallet. It follows your limits, can’t withdraw or see your keys, and any rules it suggests wait in Autopilot for you.</p>'+
321
383
  '<label class="ailbl" for="aipw">Wallet password</label><input id="aipw" class="aiin" type="password" autocomplete="current-password" placeholder="Needed to add an app" value="'+esc(AIPW)+'">'+
322
384
  '<p class="aismall">'+(c.ready?'Your AI apps unlock the wallet with a saved copy of this password, readable only by your account on this computer.':'Adding an app saves a copy of this password on this computer, readable only by your account, so the app can unlock the wallet. Anyone who can read it and your wallet folder could spend within your limits.')+'</p>'+
@@ -327,7 +389,7 @@ function openAI(){if(!AIO){AIO=document.createElement("div");AIO.className="aio"
327
389
  function closeAI(){if(AIO){AIO.remove();AIO=null;AIPW="";AIMSG={}}}
328
390
  function aiDo(act,id){if(act!=="remove"&&!AIPW){AIMSG[id]=[0,"Type your wallet password first."];aiRender();document.getElementById("aipw").focus();return}
329
391
  AIBUSY=id;AIMSG[id]=null;aiRender();api("/connect",{password:AIPW,action:act,app:id}).then(function(r){AIMSG[id]=[1,r.message];return refresh()}).catch(function(e){AIMSG[id]=[0,e.message]}).then(function(){AIBUSY=null;aiRender()})}
330
- function aiClick(e){var t=e.target.closest("button,a");if(!t)return;if(t.classList.contains("aix"))closeAI();else if(t.hasAttribute("data-act"))aiDo(t.getAttribute("data-act"),t.getAttribute("data-id"));else if(t.hasAttribute("data-hand")){var k=t.getAttribute("data-hand");AIOPEN[k]=!AIOPEN[k];aiRender()}else if(t.hasAttribute("data-copy")){var el=document.getElementById(t.getAttribute("data-copy")),v=el?el.textContent:"",done=function(){t.textContent="Copied";setTimeout(function(){t.textContent="Copy"},1500)};if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(v).then(done,function(){fallback(v);done()});else{fallback(v);done()}}}
392
+ function aiClick(e){var t=e.target.closest("button,a");if(!t)return;if(t.classList.contains("aix"))closeAI();else if(t.hasAttribute("data-ract"))aiRemoteDo(t.getAttribute("data-ract"));else if(t.hasAttribute("data-act"))aiDo(t.getAttribute("data-act"),t.getAttribute("data-id"));else if(t.hasAttribute("data-hand")){var k=t.getAttribute("data-hand");AIOPEN[k]=!AIOPEN[k];aiRender()}else if(t.hasAttribute("data-copy")){var el=document.getElementById(t.getAttribute("data-copy")),v=el?el.textContent:"",done=function(){t.textContent="Copied";setTimeout(function(){t.textContent="Copy"},1500)};if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(v).then(done,function(){fallback(v);done()});else{fallback(v);done()}}}
331
393
  document.addEventListener("keydown",function(e){if(AIO&&e.key==="Escape")closeAI()});
332
394
  var BUY={o:null,chain:null,asset:"USDC",amt:"50",custom:"",test:false,err:null,busy:false,done:null};
333
395
  var BUYA={base:["USDC","ETH"],ethereum:["USDC","ETH"],solana:["USDC","SOL"]};
@@ -460,11 +522,30 @@ var CHT={o:null,coin:"",sym:"",chain:"",name:"",range:"1D",data:null,loading:fal
460
522
  function coinOf(chain,sym){var c=(S.coins||{})[chain]||{};return c[sym]||c[String(sym).toUpperCase()]||""}
461
523
  function rowAttrs(coin,sym,chain,name){return coin?' rowc" tabindex="0" role="button" aria-label="'+esc((name||sym)+" price chart")+'" data-chart="'+esc(coin)+'" data-sym="'+esc(sym)+'" data-chain="'+esc(chain)+'" data-name="'+esc(name||sym)+'"':'"'}
462
524
  function holdRows(){var H=S.holdings;if(!H)return {rows:"",tot:0,chg:0};var rows="",tot=0,chg=0;(H.tokens||[]).forEach(function(t){var a=Number(t.amount),val=t.usd||0;tot+=val;if(typeof t.change_24h==="number")chg+=val-val/(1+t.change_24h);
463
- rows+='<div class="row'+rowAttrs(t.coin,t.symbol,t.chain,t.name)+'>'+tokIcon(t.symbol,t.chain)+'<div style="min-width:0"><div class="tname">'+esc(t.name||t.symbol)+'</div><div class="sm">'+(typeof t.change_24h==="number"?'<span class="'+(t.change_24h>=0?"ok":"bad")+'">'+(t.change_24h>=0?"+":"")+(t.change_24h*100).toFixed(2)+'%</span><span class="mut"> · '+esc(CHAIN_NAMES[t.chain]||t.chain)+'</span>':'<span class="mut">'+esc(CHAIN_NAMES[t.chain]||t.chain)+'</span>')+'</div></div><div class="r"><div class="usdv">'+fmtUsd(val)+'</div><div class="sm mut">'+esc(fmtAmt(a))+' '+esc(t.symbol)+'</div></div></div>'});
525
+ rows+='<div class="row'+rowAttrs(t.coin,t.symbol,t.chain,t.name)+'>'+tokIcon(t.symbol,t.chain,t.icon)+'<div style="min-width:0"><div class="tname">'+esc(t.name||t.symbol)+(t.trust==="unlisted"?' <span class="chip warn tbadge" title="Not on the wallet’s token list. Check it before you trade it.">Unverified</span>':'')+'</div><div class="sm">'+(typeof t.change_24h==="number"?'<span class="'+(t.change_24h>=0?"ok":"bad")+'">'+(t.change_24h>=0?"+":"")+(t.change_24h*100).toFixed(2)+'%</span><span class="mut"> · '+esc(CHAIN_NAMES[t.chain]||t.chain)+'</span>':'<span class="mut">'+esc(CHAIN_NAMES[t.chain]||t.chain)+'</span>')+'</div></div><div class="r"><div class="usdv">'+fmtUsd(val)+'</div><div class="sm mut">'+esc(fmtAmt(a))+' '+esc(t.symbol)+'</div></div></div>'});
464
526
  return {rows:rows,tot:tot,chg:chg}}
465
- function holdFoot(){var H=S.holdings;if(!H)return "";return '<div class="hfoot">'+(H.hidden?'<span>'+H.hidden+' token'+(H.hidden>1?"s":"")+' without a price hidden</span> · ':'')+'<button class="aihand" style="padding:0" onclick="addTok()">Add a token</button></div>'+(ADDT?'<div class="row addt" style="display:block"><div class="sm mut">Base or Ethereum token not showing? Paste its contract address.</div><div class="crow2"><select id="at_chain"><option value="base">Base</option><option value="ethereum">Ethereum</option></select><input id="at_addr" placeholder="0x…" autocomplete="off" spellcheck="false"><button class="b" onclick="addTokGo()">Add</button></div>'+(ADDM?'<div class="msg '+(ADDM[0]?"okk":"err")+'">'+esc(ADDM[1])+'</div>':'')+'</div>':(ADDM&&ADDM[0]?'<div class="msg okk">'+esc(ADDM[1])+'</div>':''))}
466
- var ADDT=false,ADDM=null;function addTok(){ADDT=!ADDT;ADDM=null;render()}
467
- function addTokGo(){var c=document.getElementById("at_chain").value,a=document.getElementById("at_addr").value.trim();ADDM=[1,"Checking…"];render();var ce=document.getElementById("at_chain"),ae=document.getElementById("at_addr");if(ce)ce.value=c;if(ae)ae.value=a;api("/tokens/add",{chain:c,address:a}).then(function(r){ADDT=false;ADDM=[1,"Added "+r.token.symbol+". It shows here once it has a balance and a price."];refresh()}).catch(function(e){ADDM=[0,e.message];render();var ce2=document.getElementById("at_chain"),ae2=document.getElementById("at_addr");if(ce2)ce2.value=c;if(ae2)ae2.value=a})}
527
+ function holdFoot(){var H=S.holdings;if(!H)return "";return '<div class="hfoot">'+(H.hidden?'<span>'+H.hidden+' token'+(H.hidden>1?"s":"")+' without a price hidden</span> · ':'')+'<button class="aihand" style="padding:0" onclick="addTok()">'+(ADDT?"Close":"Add a token")+'</button></div>'+(ADDT?addTokSheet():(ADDM&&ADDM[0]?'<div class="msg okk">'+esc(ADDM[1])+'</div>':''))}
528
+ var ADDT=false,ADDM=null,AT={chain:"base",q:"",res:null,chk:null,busy:false,timer:null,confirm:false,gen:0};
529
+ function addTok(){ADDT=!ADDT;ADDM=null;AT.res=null;AT.chk=null;AT.q="";AT.confirm=false;render();if(ADDT){setTimeout(function(){var q=document.getElementById("at_q");if(q)q.focus();atSearch()},30)}}
530
+ function addTokSheet(){return '<div class="row addt" style="display:block"><div class="sm mut">Search by name or symbol, or paste a contract or mint address. Every token gets Tollkit’s scam check before it\'s added.</div><div class="crow2"><select id="at_chain" onchange="AT.chain=this.value;AT.chk=null;atSearch()">'+[["base","Base"],["ethereum","Ethereum"],["solana","Solana"]].map(function(o){return '<option value="'+o[0]+'"'+(AT.chain===o[0]?" selected":"")+'>'+o[1]+'</option>'}).join("")+'</select><input id="at_q" placeholder="USDC, Bonk, 0x…" autocomplete="off" spellcheck="false" value="'+esc(AT.q)+'" oninput="AT.q=this.value;clearTimeout(AT.timer);AT.timer=setTimeout(atSearch,300)"></div><div id="at_res">'+atResHtml()+'</div>'+(ADDM?'<div class="msg '+(ADDM[0]?"okk":"err")+'">'+esc(ADDM[1])+'</div>':'')+'</div>'}
531
+ function atBadge(t){return t.trust==="core"?'<span class="chip ok tbadge">Built in</span>':t.trust==="listed"?'<span class="chip tbadge">'+(t.chain==="solana"?"Jupiter verified":"Listed")+'</span>':'<span class="chip warn tbadge">Not verified</span>'}
532
+ function atResHtml(){if(AT.chk)return atCheckHtml(AT.chk);if(AT.busy)return '<div class="sm mut" style="padding:8px 2px">Searching…</div>';if(!AT.res)return "";var q=AT.q.trim(),isAddr=AT.chain==="solana"?/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(q):/^0x[0-9a-fA-F]{40}$/.test(q);
533
+ var h=AT.res.map(function(t,i){return '<button class="atrow" data-ati="'+i+'">'+tokIcon(t.symbol,t.chain,t.logo)+'<span class="atn"><b>'+esc(t.symbol)+'</b> '+atBadge(t)+'<small>'+esc(t.name)+' · '+esc(short(t.address))+'</small></span></button>'}).join("");
534
+ if(isAddr&&!AT.res.some(function(t){return t.address.toLowerCase()===q.toLowerCase()}))h='<button class="atrow" data-atq="1"><div class="ic tok"><span class="tl">?</span></div><span class="atn"><b>Check this address</b><small>'+esc(short(q))+'</small></span></button>'+h;
535
+ if(!h)h='<div class="sm mut" style="padding:8px 2px">No match. Paste the token’s address to check it.</div>';
536
+ return '<div class="atlist">'+h+'</div>'+(AT.chain==="solana"?'<div class="sm mut" style="text-align:right;margin-top:4px">Powered by Jupiter</div>':'<div class="sm mut" style="text-align:right;margin-top:4px">Superchain and Uniswap token lists</div>')}
537
+ function atPaint(){var el=document.getElementById("at_res");if(el)el.innerHTML=atResHtml();else render()}
538
+ function atSearch(){var c=AT.chain,q=AT.q,g=++AT.gen;AT.chk=null;AT.busy=true;atPaint();api("/tokens/search",{chain:c,q:q}).then(function(r){if(g!==AT.gen)return;AT.res=r.tokens||[];AT.busy=false;atPaint()}).catch(function(e){AT.busy=false;AT.res=[];ADDM=[0,e.message];atPaint()})}
539
+ function atCheck(addr){clearTimeout(AT.timer);var g=++AT.gen;AT.busy=false;ADDM=null;AT.confirm=false;AT.chk={loading:true,address:addr};atPaint();api("/tokens/check",{chain:AT.chain,address:addr}).then(function(r){if(g!==AT.gen)return;AT.chk=r.check;atPaint()}).catch(function(e){AT.chk=null;ADDM=[0,e.message];render()})}
540
+ function atCheckHtml(c){if(c.loading)return '<div class="ap" style="margin-top:8px"><div class="sm mut">Running Tollkit’s token check…</div></div>';var w=c.warnings||[],p=c.powers||[],f=c.facts||[];
541
+ return '<div class="ap atchk'+(w.length?" atwarn":"")+'" style="margin-top:8px"><div style="display:flex;align-items:center;gap:10px">'+tokIcon(c.symbol||"?",c.chain,c.logo)+'<div style="min-width:0"><b>'+esc(c.symbol||"Unknown")+'</b> '+atBadge(c)+'<div class="sm mut">'+esc(c.name||"")+' · '+esc(short(c.address))+'</div></div></div>'+
542
+ (w.length?'<div class="atw"><b>'+w.length+' warning'+(w.length>1?"s":"")+'</b><ul>'+w.map(function(x){return '<li>'+esc(x)+'</li>'}).join("")+'</ul></div>':'<div class="sm ok" style="margin-top:8px">No warnings from the check. That doesn’t make a token safe.</div>')+
543
+ (p.length?'<div class="sm" style="margin-top:8px"><b>What the issuer can do</b><ul>'+p.map(function(x){return '<li>'+esc(x)+'</li>'}).join("")+'</ul></div>':'')+
544
+ (f.length?'<div class="sm mut" style="margin-top:6px">'+f.map(esc).join(" ")+'</div>':'')+
545
+ '<div class="btns"><button class="b g" onclick="AT.chk=null;atPaint()">Back</button>'+(w.length?(AT.confirm?'<button class="b no" onclick="atAdd(true)">Yes, add it anyway</button>':'<button class="b no" onclick="AT.confirm=true;atPaint()">Add anyway</button>'):'<button class="b ok" onclick="atAdd(false)">Add</button>')+'</div>'+(w.length&&AT.confirm?'<div class="sm bad" style="margin-top:6px">Only add it if you know where it came from. Many scam tokens copy real names.</div>':'')+'</div>'}
546
+ function atAdd(confirm){var c=AT.chk;api("/tokens/add",{chain:c.chain,address:c.address,confirm:!!confirm}).then(function(r){if(r.needs_confirm){AT.chk=r.check;AT.confirm=true;atPaint();return}ADDT=false;AT.chk=null;ADDM=[1,"Added "+r.token.symbol+"."];refresh().then(function(){render()})}).catch(function(e){ADDM=[0,e.message];render()})}
547
+ document.addEventListener("error",function(e){var t=e.target;if(t&&t.classList&&t.classList.contains("tlogo")){var sp=document.createElement("span");sp.className="tl";sp.textContent=t.getAttribute("data-tl")||"?";t.replaceWith(sp)}},true);
548
+ document.addEventListener("click",function(e){var b=e.target.closest&&e.target.closest("[data-ati],[data-atq]");if(!b)return;if(b.hasAttribute("data-atq"))atCheck(AT.q.trim());else{var t=AT.res[Number(b.getAttribute("data-ati"))];if(t)atCheck(t.address)}});
468
549
  document.addEventListener("click",function(e){var r=e.target.closest&&e.target.closest("[data-chart]");if(r)openChart(r.getAttribute("data-chart"),r.getAttribute("data-sym"),r.getAttribute("data-chain"),r.getAttribute("data-name"))});
469
550
  document.addEventListener("keydown",function(e){if(e.key==="Enter"&&document.activeElement&&document.activeElement.hasAttribute&&document.activeElement.hasAttribute("data-chart")){var r=document.activeElement;openChart(r.getAttribute("data-chart"),r.getAttribute("data-sym"),r.getAttribute("data-chain"),r.getAttribute("data-name"))}if(CHT.o&&e.key==="Escape")closeChart()});
470
551
  function openChart(coin,sym,chain,name){CHT.coin=coin;CHT.sym=sym;CHT.chain=chain;CHT.name=name;CHT.data=null;CHT.err=null;CHT.table=false;CHT.hover=null;if(!CHT.o){CHT.o=document.createElement("div");CHT.o.className="aio";CHT.o.innerHTML='<div class="aidlg chdlg" role="dialog" aria-modal="true" aria-label="Price chart"><button class="aix" aria-label="Close">×</button><div class="aibody"></div></div>';CHT.o.addEventListener("mousedown",function(e){if(e.target===CHT.o)closeChart()});CHT.o.addEventListener("click",function(e){var t=e.target.closest("button");if(!t)return;if(t.classList.contains("aix"))closeChart();else if(t.hasAttribute("data-rg")){CHT.range=t.getAttribute("data-rg");loadChart()}else if(t.hasAttribute("data-tbl")){CHT.table=!CHT.table;drawChart()}});document.body.appendChild(CHT.o)}loadChart()}
@@ -522,7 +603,7 @@ var cards=(m.rules||[]).map(function(id){var r=(S.rules||[]).filter(function(x){
522
603
  return '<div class="tc-cm tc-ai'+nw+(m.error?" tc-err":"")+'"><div class="tc-cav">'+aiSvg("claude")+'</div><div class="tc-cbw">'+(m.checkin?'<div class="tc-ctag">Check-in · '+tm(m.ts)+'</div>':'')+'<div class="tc-cb">'+fmtAi(m.text)+'</div>'+(cards?'<div class="tc-ccards">'+cards+'</div>':'')+'</div></div>'}
523
604
  function svgClock(){return '<svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"/><path d="M12 7v5l3 2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>'}
524
605
  function chatBox(){var A=S.ai;if(!A)return "";var M=A.messages||[],busy=A.busy||CH.sending;
525
- var head='<div class="tc-chd"><div class="tc-chn">'+aiSvg("claude")+'<div><b>'+esc(aiName())+'</b><small>'+(busy?'<span class="ok">Working…</span>':A.provider?'Your Claude, on this computer':'Not set up')+'</small></div></div>'+
606
+ var head='<div class="tc-chd"><div class="tc-chn">'+aiSvg("claude")+'<div><b>'+esc(aiName())+'</b><small>'+(busy?'<span class="ok">Working…</span>':A.provider?esc(A.provider.note||'Your Claude, on this computer'):'Not set up')+'</small></div></div>'+
526
607
  '<div class="tc-chr"><label class="tc-chk" title="Let the AI review your rules on a schedule">'+svgClock()+'<select onchange="setCheckin(this.value)">'+[[0,"Check-ins off"],[1,"Every hour"],[4,"Every 4 hours"],[12,"Every 12 hours"],[24,"Daily"]].map(function(o){return '<option value="'+o[0]+'"'+(A.checkin.every_hours===o[0]?" selected":"")+'>'+o[1]+'</option>'}).join("")+'</select></label>'+(M.length?'<button class="b g tc-chx'+(CH.confirmNew?" tc-warn":"")+'" onclick="newChat()" title="Start over. Your rules stay on.">'+(CH.confirmNew?'Clear chat?':'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h4L19 9l-4-4L4 16z M14 6l4 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/></svg>New chat')+'</button>':'')+'</div></div>';
527
608
  if(!A.provider)return '<div class="tc-chat">'+head+'<div class="tc-cempty"><b>Chat with your AI here</b><p>Install <b>Claude Code</b> on this computer and sign in once (run <code>claude</code> in Terminal). Then reopen this page. It uses your own Claude plan; the wallet never sees your Claude login.</p></div></div>';
528
609
  var body=M.length?M.map(chatMsg).join(""):'<div class="tc-cempty"><b>What do you want your autopilot to do?</b><p>Say it in your own words. '+esc(aiName())+' checks prices and your balances, then suggests rules you can turn on with one tap.</p><div class="tc-cchips">'+CHIPS.map(function(c,i){return '<button class="tc-cchip" onclick="chatSend(CHIPS['+i+'])">'+esc(c)+'</button>'}).join("")+'</div></div>';
@@ -538,7 +619,7 @@ function chatPw(){api("/ai/password",{password:CH.pw}).then(function(){CH.pw="";
538
619
  function setCheckin(h){api("/ai/checkin",{hours:Number(h)}).then(function(){flash=[1,Number(h)?"Check-ins on. "+aiName()+" will review your rules and suggest changes for you to approve.":"Check-ins off."];refresh()}).catch(function(e){flash=[0,e.message];render()})}
539
620
  function newChat(){if(!CH.confirmNew){CH.confirmNew=true;LASTAP=null;render();clearTimeout(CH.cnT);CH.cnT=setTimeout(function(){CH.confirmNew=false;LASTAP=null;render()},4000);return}
540
621
  clearTimeout(CH.cnT);CH.confirmNew=false;api("/ai/new",{}).then(function(){CH.n=-1;CH.top=null;flash=[1,"New chat started. Your rules are unchanged."];return refresh()}).catch(function(e){flash=[0,e.message];render()})}
541
- function tokIcon(sym,chain){var k=String(sym||"").toUpperCase();if(k==="WETH")k="ETH";var svg=COIN.coin[k];return '<div class="ic tok">'+(svg||'<span class="tl">'+esc(k.slice(0,4))+'</span>')+(chain&&COIN.chain[chain]?'<span class="cb">'+COIN.chain[chain]+'</span>':"")+'</div>'}
622
+ function tokIcon(sym,chain,logo){var k=String(sym||"").toUpperCase();if(k==="WETH")k="ETH";var svg=COIN.coin[k];return '<div class="ic tok">'+(svg||(logo&&logo.indexOf("https:")===0?'<img class="tlogo" src="'+esc(logo)+'" alt="" loading="lazy" referrerpolicy="no-referrer" data-tl="'+esc(k.slice(0,4))+'">':'<span class="tl">'+esc(k.slice(0,4))+'</span>'))+(chain&&COIN.chain[chain]?'<span class="cb">'+COIN.chain[chain]+'</span>':"")+'</div>'}
542
623
  function chainIcon(chain,extra){return '<div class="ic tok">'+(COIN.chain[chain]||esc(String(chain||"?")[0].toUpperCase()))+(extra&&COIN.chain[extra]?'<span class="cb">'+COIN.chain[extra]+'</span>':"")+'</div>'}
543
624
  function kindIcon(e){if(e.kind==="stake")return tokIcon("SOL","solana");var m=/^[\d.,]+ ([A-Za-z]+)/.exec(e.detail||"");if(e.kind!=="payment"&&m)return tokIcon(m[1],e.chain);return '<div class="ic tok"><span class="glyph">'+(e.kind==="payment"?"P":e.kind==="trade"?"S":"W")+'</span>'+(COIN.chain[e.chain]?'<span class="cb">'+COIN.chain[e.chain]+'</span>':"")+'</div>'}
544
625
  function amt(x){var n=Number(x);return isFinite(n)?n.toLocaleString(undefined,{maximumFractionDigits:6}):String(x)}
@@ -558,6 +639,7 @@ var prop=R.filter(function(r){return r.status==="proposed"&&!inChat[r.id]}),act=
558
639
  var pv=PV?'<div class="ap" style="margin-top:10px"><div class="sm mut" style="margin-bottom:6px">Preview: nothing runs until you turn it on</div>'+(PV.rules.length?PV.rules.map(function(r){return '<div style="margin:4px 0">• '+esc(r.description)+(r.problem?' <span class="bad sm">('+esc(r.problem)+')</span>':"")+'</div>'}).join(""):'<div class="bad">No rules understood.</div>')+(PV.unparsed.length?'<div class="sm warn" style="margin-top:6px">Not understood: '+esc(PV.unparsed.join(" · "))+'</div>':"")+'<div class="btns"><button class="b g" onclick="PV=null;render()">Discard</button><button class="b ok"'+(PV.rules.length&&!PV.rules.some(function(r){return r.problem})?"":" disabled")+' onclick="addRules()">Turn on</button></div></div>':"";
559
640
  var manualOpen=CH.manual||!S.ai||!(S.ai||{}).provider;
560
641
  return '<div class="sm">'+st+'</div><div class="sm mut" style="margin-top:3px">Every fill follows your limits and approvals, with the same 0.5% fee as any swap.</div>'+msg()+
642
+ (S.trade_mode==="python"?pyCard():"")+botsHtml()+'<h3>Ask Claude</h3>'+
561
643
  chatBox()+
562
644
  (prop.length?'<h3>Waiting for you</h3>'+prop.map(ruleCard).join(""):"")+
563
645
  '<h3>Your rules</h3>'+(act.length?act.map(ruleCard).join(""):'<div class="mut sm">No rules on yet.</div>')+
@@ -565,6 +647,50 @@ chatBox()+
565
647
  '<details class="mrule"'+(manualOpen?" open":"")+' ontoggle="CH.manual=this.open"><summary>Write a rule yourself</summary><textarea id="stxt" rows="3" placeholder="e.g. Buy $25 of SOL every Monday. Sell half my ETH if it rises 10% and all if it falls 8%." style="width:100%;margin-top:8px;background:var(--surf);border:1px solid var(--line);color:var(--tx);border-radius:10px;padding:10px;font:inherit;resize:vertical"></textarea>'+
566
648
  '<div class="toggles" style="margin-top:6px">'+TEMPL.map(function(t,i){return '<button class="chip" style="border:0;cursor:pointer" onclick="document.getElementById(\'stxt\').value=TEMPL['+i+']">'+esc(t)+'</button>'}).join("")+'</div>'+
567
649
  '<div style="margin-top:10px"><button class="b" onclick="preview()">Preview</button></div>'+pv+'</details>'}
650
+ // ---- Bots: rule-based (no AI) bots, their status and results, and Tollkit's public board ----
651
+ var BOT={open:null,tpl:null,anim:false,flash:null,chain:"base",token:"ETH",params:{},pv:null,err:null,busy:false,more:{}};
652
+ function botPill(st){var m={running:["ok","Running"],waiting:["","Waiting for its price"],paused:["warn","Paused"],error:["bad","Stopped by errors"],done:["","Finished"],stopped:["","Stopped"]}[st]||["",st];return '<span class="chip botpill '+m[0]+'">'+m[1]+'</span>'}
653
+ function money(n){return (n<0?"−":"")+fmtUsd(Math.abs(n))}
654
+ function botCard(b){var res=b.result_usd!==null?'<span class="'+(b.result_usd>=0?"ok":"bad")+'">'+(b.result_usd>=0?"+":"")+money(b.result_usd)+(b.result_pct!==null?' ('+(b.result_pct>=0?"+":"")+b.result_pct+'%)':'')+'</span>':'<span class="mut">—</span>';
655
+ var live=b.status==="running"||b.status==="waiting"||b.status==="error",open=BOT.more[b.id];
656
+ return '<div class="botc'+(BOT.flash===b.id?" botflash":"")+'" data-botcard="'+esc(b.id)+'"><div class="botct"><div style="min-width:0"><b>'+esc(b.name)+'</b><div class="sm mut">'+esc(CHAIN_NAMES[b.chain]||b.chain)+(b.last_trade?' · last trade '+ago(b.last_trade):' · no trades yet')+'</div></div>'+botPill(b.status)+'</div>'+
657
+ '<div class="botstats"><div><small>Trades</small><b>'+b.trades_ok+'</b></div><div><small>Failed</small><b class="'+(b.trades_failed?"bad":"")+'">'+b.trades_failed+'</b></div><div><small>Success</small><b>'+(b.success_rate===null?"—":b.success_rate+"%")+'</b></div><div><small>Result</small><b>'+res+'</b></div></div>'+
658
+ (b.round_trips?'<div class="sm mut" style="margin-top:6px">'+b.round_trips+' buy-and-sell round trip'+(b.round_trips>1?"s":"")+' done</div>':'')+
659
+ '<div class="btns" style="margin-top:8px"><button class="b g" data-botmore="'+esc(b.id)+'">'+(open?"Hide":"Details")+'</button>'+(b.status==="paused"?'<button class="b ok" data-botst="active" data-bot="'+esc(b.id)+'">Resume</button>':live?'<button class="b g" data-botst="paused" data-bot="'+esc(b.id)+'">Pause</button>':'')+(b.status!=="done"&&b.status!=="stopped"?'<button class="b no" data-botst="cancelled" data-bot="'+esc(b.id)+'">Stop</button>':'')+'</div>'+
660
+ (open?'<div class="sm" style="margin-top:8px">'+b.rules.map(function(r){return '<div style="margin:4px 0">• '+esc(r.description)+' <span class="mut">('+esc(r.status)+')</span>'+(r.last_error?'<div class="bad">'+esc(r.last_error)+'</div>':'')+'</div>'}).join("")+'<div class="mut" style="margin-top:4px">Spent '+fmtUsd(b.spent_usd)+' · received '+fmtUsd(b.received_usd)+(b.value_now_usd!==null&&b.units_held?' · holding about '+fmtUsd(b.value_now_usd):'')+'</div></div>':'')+'</div>'}
661
+ function boardFor(id){var B=S.bot_board;if(!B||!B.templates)return null;for(var i=0;i<B.templates.length;i++)if(B.templates[i].template===id)return B.templates[i];return null}
662
+ function boardLine(id){var x=boardFor(id);if(!x||x.bots<3)return '<div class="sm mut">Not enough shared results yet.</div>';return '<div class="sm mut">'+x.bots+' bots shared · median result '+(x.median_result_pct>=0?"+":"")+x.median_result_pct+'% · '+x.success_rate+'% of trades went through</div>'}
663
+ function botsHtml(){var L=S.bots||[],T=S.bot_templates||[];
664
+ var h='<h3>Your bots</h3>'+(L.length?L.map(botCard).join(""):'<div class="mut sm">No bots yet. Start one below; it follows your limits like any trade.</div>');
665
+ h+='<h3>Start a bot</h3><div class="sm mut" style="margin-bottom:6px">Plain rules, no AI. You see every step before it starts.</div><div class="botcat">'+T.map(function(t){var on=BOT.tpl===t.id;return tplCard(t,on,L)+(on?'<div class="botwrap'+(BOT.anim?" drop":"")+'"><div class="botin">'+botForm()+'</div></div>':'')}).join("")+'</div>';
666
+ BOT.anim=false;
667
+ h+='<label class="botshare"><input type="checkbox" id="botshare"'+(S.bot_share?" checked":"")+' onchange="botShare(this.checked)"> Share my bots’ results with Tollkit’s public bot board (anonymous: bot type, token, trade counts and result %, no addresses or amounts)</label>';
668
+ return h}
669
+ var ARROW='<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>';
670
+ function tplCard(t,on,L){var mine=L.filter(function(b){return b.template===t.id}),live=mine.filter(function(b){return b.status!=="done"&&b.status!=="stopped"});
671
+ var head='<div class="tplh"><b>'+esc(t.name)+'</b>'+(live.length===1?botPill(live[0].status):live.length>1?'<span class="chip botpill ok">'+live.length+' bots</span>':'')+'</div>';
672
+ if(!live.length){var past=mine.length?'<div class="sm mut">Your last one: '+esc(({done:"finished",stopped:"stopped"})[mine[0].status]||mine[0].status)+'.</div>':'';
673
+ return '<button class="bottpl'+(on?" on":"")+'" data-tpl="'+esc(t.id)+'" aria-expanded="'+on+'">'+head+'<small>'+esc(t.blurb)+'</small>'+boardLine(t.id)+past+'<span class="botcta">'+(on?'Close setup':(mine.length?'Set up again':'Set up '+esc(t.name)+' bot')+ARROW)+'</span></button>'}
674
+ var b=live[0],line;if(live.length===1){var res=b.result_usd!==null?' · <span class="'+(b.result_usd>=0?"ok":"bad")+'">'+(b.result_usd>=0?"+":"")+money(b.result_usd)+'</span>':'';line=esc(b.token)+' on '+esc(CHAIN_NAMES[b.chain]||b.chain)+' · '+b.trades_ok+' trade'+(b.trades_ok===1?"":"s")+(b.trades_failed?' · <span class="bad">'+b.trades_failed+' failed</span>':'')+res+(b.last_trade?' · last '+ago(b.last_trade):'')}
675
+ else{var c={};live.forEach(function(x){c[x.status]=(c[x.status]||0)+1});line=Object.keys(c).map(function(k){return c[k]+' '+({running:"running",waiting:"waiting for a price",paused:"paused",error:"stopped by errors"})[k]}).join(" · ")}
676
+ return '<div class="bottpl has'+(on?" on":"")+(live.some(function(x){return x.status==="error"})?" err":"")+'">'+head+'<div class="sm">'+line+'</div>'+boardLine(t.id)+'<div class="tplbtns"><button class="botcta" data-botview="'+esc(b.id)+'">'+(live.length>1?'View bots':'View bot')+ARROW+'</button><button class="botcta g" data-tpl="'+esc(t.id)+'" aria-expanded="'+on+'">'+(on?'Close setup':'+ Set up another')+'</button></div></div>'}
677
+ function botForm(){var t=(S.bot_templates||[]).filter(function(x){return x.id===BOT.tpl})[0];if(!t)return "";
678
+ var f=t.fields.map(function(x){var v=BOT.params[x.key]!==undefined?BOT.params[x.key]:x.default;if(x.kind==="choice")return '<label class="botf"><span>'+esc(x.label)+'</span><select data-bp="'+esc(x.key)+'">'+x.choices.map(function(c){return '<option value="'+esc(c[0])+'"'+(String(v)===c[0]?" selected":"")+'>'+esc(c[1])+'</option>'}).join("")+'</select></label>';var lim=x.kind==="pct"&&x.max!==undefined?' <em class="botlim">up to '+Number(x.max).toLocaleString()+'%</em>':'';return '<label class="botf"><span>'+esc(x.label)+lim+'</span><input type="number" step="any" inputmode="decimal" data-bp="'+esc(x.key)+'"'+(x.min!==undefined?' min="'+x.min+'"':'')+(x.max!==undefined?' max="'+x.max+'"':'')+' value="'+esc(String(v))+'"></label>'}).join("");
679
+ return '<div class="ap botform" style="white-space:normal"><b>'+esc(t.name)+'</b><div class="sm mut" style="margin:4px 0 8px">Risk: '+esc(t.risk)+'</div><div class="botgrid"><label class="botf"><span>Network</span><select id="bot_chain">'+[["base","Base"],["ethereum","Ethereum"],["solana","Solana"]].map(function(o){return '<option value="'+o[0]+'"'+(BOT.chain===o[0]?" selected":"")+'>'+o[1]+'</option>'}).join("")+'</select></label><label class="botf"><span>Token (symbol or address)</span><input id="bot_token" value="'+esc(BOT.token)+'" autocomplete="off" spellcheck="false"></label>'+f+'</div>'+
680
+ (BOT.err?'<div class="msg err">'+esc(BOT.err)+'</div>':'')+(BOT.pv?'<div class="sm" style="margin-top:8px"><div class="mut">'+(BOT.pv.price?'Today’s price about $'+Number(BOT.pv.price).toLocaleString(undefined,{maximumSignificantDigits:6})+'. ':'')+'It will:</div>'+BOT.pv.rules.map(function(r){return '<div style="margin:4px 0">• '+esc(r.description)+'</div>'}).join("")+'</div>':'')+
681
+ '<div class="btns"><button class="b g" onclick="botClose()">Cancel</button>'+(BOT.pv?'<button class="b ok" onclick="botGo(\'start\')"'+(BOT.busy?" disabled":"")+'>Start bot</button>':'<button class="b" onclick="botGo(\'preview\')"'+(BOT.busy?" disabled":"")+'>Preview</button>')+'</div></div>'}
682
+ function botRead(){var c=document.getElementById("bot_chain"),t=document.getElementById("bot_token");if(c)BOT.chain=c.value;if(t)BOT.token=t.value.trim();document.querySelectorAll("[data-bp]").forEach(function(el){BOT.params[el.getAttribute("data-bp")]=el.tagName==="SELECT"?el.value:Number(el.value)})}
683
+ function botGo(what){botRead();BOT.busy=true;BOT.err=null;render();api("/bots/"+what,{template:BOT.tpl,chain:BOT.chain,token:BOT.token,params:BOT.params}).then(function(r){BOT.busy=false;if(what==="preview"){BOT.pv=r;render()}else{BOT.tpl=null;BOT.pv=null;flash=[1,"Bot started. It shows under Your bots."];refresh().then(function(){render()})}}).catch(function(e){BOT.busy=false;BOT.err=e.message;render()})}
684
+ function botClose(){var w=document.querySelector(".botwrap");BOT.pv=null;BOT.err=null;var done=function(){BOT.tpl=null;render()};if(!w||(window.matchMedia&&matchMedia("(prefers-reduced-motion: reduce)").matches)){done();return}w.classList.remove("drop");w.classList.add("closing");setTimeout(done,240)}
685
+ function botShare(on){api("/bots/share",{on:on}).then(function(){refresh()}).catch(function(e){flash=[0,e.message];render()})}
686
+ document.addEventListener("click",function(e){var t=e.target.closest&&e.target.closest("[data-tpl],[data-botst],[data-botmore],[data-botview]");if(!t)return;
687
+ if(t.hasAttribute("data-botview")){var vid=t.getAttribute("data-botview");BOT.more[vid]=true;BOT.flash=vid;render();var card=document.querySelector('[data-botcard="'+vid+'"]');if(card&&card.scrollIntoView)card.scrollIntoView({behavior:RM?"auto":"smooth",block:"center"});setTimeout(function(){if(BOT.flash===vid){BOT.flash=null;var c2=document.querySelector('[data-botcard="'+vid+'"]');if(c2)c2.classList.remove("botflash")}},1600);return}
688
+ if(t.hasAttribute("data-tpl")){var id=t.getAttribute("data-tpl");if(BOT.tpl===id){botClose();return}BOT.tpl=id;BOT.anim=true;BOT.params={};BOT.pv=null;BOT.err=null;if(BOT.chain==="solana"&&BOT.token==="ETH")BOT.token="SOL";render();setTimeout(function(){var w=document.querySelector(".botwrap");if(!w)return;w.classList.remove("drop");if(w.scrollIntoView)w.scrollIntoView({behavior:"smooth",block:"nearest"})},460);return}
689
+ if(t.hasAttribute("data-botmore")){var k=t.getAttribute("data-botmore");BOT.more[k]=!BOT.more[k];render();return}
690
+ var st=t.getAttribute("data-botst"),bid=t.getAttribute("data-bot");api("/bots/status",{bot:bid,status:st}).then(function(){flash=[1,st==="paused"?"Paused.":st==="active"?"Resumed.":"Stopped."];refresh().then(function(){render()})}).catch(function(e){flash=[0,e.message];render()})});
691
+ document.addEventListener("input",function(e){var t=e.target;if(!t||!t.hasAttribute||!t.hasAttribute("data-bp")||t.tagName!=="INPUT"||t.value==="")return;var mx=t.getAttribute("max");if(mx!==null&&Number(t.value)>Number(mx)){t.value=mx;t.classList.remove("botcap");void t.offsetWidth;t.classList.add("botcap")}});
692
+ document.addEventListener("focusout",function(e){var t=e.target;if(!t||!t.hasAttribute||!t.hasAttribute("data-bp")||t.tagName!=="INPUT")return;var mn=t.getAttribute("min");if(mn!==null&&(t.value===""||Number(t.value)<Number(mn)))t.value=mn});
693
+ document.addEventListener("change",function(e){if(e.target&&e.target.id==="bot_chain"){BOT.chain=e.target.value;var ti=document.getElementById("bot_token");if(ti&&(ti.value==="ETH"||ti.value==="SOL")){ti.value=BOT.chain==="solana"?"SOL":"ETH";BOT.token=ti.value}BOT.pv=null}else if(e.target&&(e.target.id==="bot_token"||e.target.hasAttribute&&e.target.hasAttribute("data-bp")))BOT.pv=null});
568
694
  function preview(){var t=document.getElementById("stxt").value;api("/rules/preview",{text:t}).then(function(r){PV=r;flash=null;render()}).catch(function(e){flash=[0,e.message];render()})}
569
695
  function addRules(){var rules=PV.rules.map(function(r){var o={};for(var k in r)if(k!=="description"&&k!=="problem")o[k]=r[k];return o});api("/rules",{rules:rules}).then(function(){PV=null;flash=[1,"Turned on. The autopilot is watching prices."];refresh()}).catch(function(e){flash=[0,e.message];render()})}
570
696
  function setRule(id,st){api("/rules/status",{id:id,status:st}).then(function(){flash=[1,st==="active"?"Turned on.":st==="paused"?"Paused.":"Cancelled."];refresh()}).catch(function(e){flash=[0,e.message];render()})}
@@ -591,15 +717,34 @@ function rowUsd(e){if(Number(e.usd)>0)return usd(e.usd);var m=(e.kind==="stake"?
591
717
  function usd(n){n=Number(n)||0;return "$"+n.toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:n<1&&n>0?4:2})}
592
718
  function ago(ts){var s=(Date.now()-Date.parse(ts))/1000;if(s<60)return "just now";if(s<3600)return Math.floor(s/60)+"m ago";if(s<86400)return Math.floor(s/3600)+"h ago";return new Date(ts).toLocaleDateString()}
593
719
  var ICONS={autopilot:'<path d="M13 2L4 14h7l-1 8 9-12h-7z"/>',home:'<path d="M3 11l9-7 9 7v9a1 1 0 0 1-1 1h-5v-6H9v6H4a1 1 0 0 1-1-1z"/>',activity:'<path d="M3 12h4l3-8 4 16 3-8h4"/>',approvals:'<path d="M5 13l4 4L19 7"/>',settings:'<path d="M4 6h10M18 6h2M4 12h4M12 12h8M4 18h12"/><circle cx="16" cy="6" r="2"/><circle cx="10" cy="12" r="2"/><circle cx="18" cy="18" r="2"/>',withdraw:'<path d="M12 4v12m0 0l-5-5m5 5l5-5M5 20h14"/>'};
594
- var TABS=["home","activity","autopilot","approvals","settings","withdraw"],lastBadge=-1,lastAuto=-1;
595
- function nav(){var el=document.getElementById("nav");if(!el.querySelector("button")){el.innerHTML='<div class="ind" id="ind"></div>'+TABS.map(function(k){return '<button data-k="'+k+'" onclick="go(\''+k+'\')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">'+ICONS[k]+'</svg>'+k[0].toUpperCase()+k.slice(1)+'</button>'}).join("")}
720
+ var TABS=["home","activity","autopilot","approvals","settings","withdraw"];
721
+ function nav(){var el=document.getElementById("nav");if(!el.querySelector("button")){el.innerHTML='<div class="ind" id="ind"></div>'+TABS.map(function(k){return '<button data-k="'+k+'" onclick="go(\''+k+'\')">'+(k==="autopilot"?'<span class="emo" aria-hidden="true">\u{1F916}</span>':'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">'+ICONS[k]+'</svg>')+(k==="autopilot"?"Bots":k[0].toUpperCase()+k.slice(1))+'</button>'}).join("")}
596
722
  el.querySelectorAll("button").forEach(function(b){b.classList.toggle("on",b.getAttribute("data-k")===tab)});
597
723
  document.getElementById("ind").style.transform="translateX("+(TABS.indexOf(tab)*100)+"%)";
598
- var na=S&&S.rules?S.rules.filter(function(r){return r.status==="proposed"}).length:0;if(na!==lastAuto){lastAuto=na;var aa=el.querySelector('[data-k="autopilot"]'),o2=aa.querySelector(".badge");if(o2)o2.remove();if(na){var s2=document.createElement("span");s2.className="badge";s2.textContent=na;aa.appendChild(s2)}}
599
- var n=S?S.pending.length:0;if(n!==lastBadge){lastBadge=n;var ab=el.querySelector('[data-k="approvals"]'),old=ab.querySelector(".badge");if(old)old.remove();if(n){var sp=document.createElement("span");sp.className="badge";sp.textContent=n;ab.appendChild(sp)}}}
600
- function go(k){if(k===tab&&!flash)return;tab=k;flash=null;render(true)}
724
+ navMark(el)}
725
+ // Why a tab is worth a tap. Activity and Home only count what's new since this page opened (or since you last looked).
726
+ var SEEN=null;
727
+ function navSeen(k){if(!S||!SEEN)return;if(k==="activity")(S.receipts||[]).forEach(function(r){SEEN.rc[r.id]=r.status});if(k==="home"&&S.live)S.live.events.forEach(function(e){SEEN.ev[e.id]=1})}
728
+ function navWhy(){var w={};if(!S)return w;
729
+ if(!SEEN){SEEN={rc:{},ev:{}};navSeen("activity");navSeen("home")}
730
+ if(tab==="activity"||tab==="home")navSeen(tab);
731
+ var np=(S.pending||[]).length;if(np)w.approvals={n:np,lvl:"bad",t:np+(np>1?" requests are":" request is")+" waiting for your OK"};
732
+ var prop=(S.rules||[]).filter(function(r){return r.status==="proposed"}).length,berr=(S.bots||[]).filter(function(b){return b.status==="error"}).length;
733
+ if(prop||berr)w.autopilot={n:prop+berr,lvl:berr?"bad":"acc",t:[prop?prop+" suggested rule"+(prop>1?"s":"")+" to review":"",berr?berr+" bot"+(berr>1?"s":"")+" stopped by errors":""].filter(Boolean).join(" · ")};
734
+ var bk=S.backup;if(bk&&!bk.done)w.settings={lvl:"warn",t:"Your wallet isn't backed up yet"};
735
+ var nr=0,nf=0;(S.receipts||[]).forEach(function(r){var was=SEEN.rc[r.id];if(was===undefined||(r.status==="failed"&&was!=="failed")){nr++;if(r.status==="failed")nf++}});
736
+ if(nr)w.activity={n:nr,lvl:nf?"bad":"acc",t:nr+" new in Activity"+(nf?" ("+nf+" failed)":"")};
737
+ var nd=S.live?S.live.events.filter(function(e){return e.direction==="in"&&!SEEN.ev[e.id]}).length:0;
738
+ if(nd)w.home={n:nd,lvl:"ok",t:nd+" new deposit"+(nd>1?"s":"")};
739
+ return w}
740
+ function navMark(el){var w=navWhy();el.querySelectorAll("button").forEach(function(b){var k=b.getAttribute("data-k"),r=w[k],name=k==="autopilot"?"Bots":k[0].toUpperCase()+k.slice(1);
741
+ var key=r?r.lvl+"|"+(r.n||"")+"|"+r.t:"";if(b.getAttribute("data-why")===key)return;b.setAttribute("data-why",key);
742
+ b.classList.remove("attn","attn-bad","attn-warn","attn-ok","attn-acc");var old=b.querySelector(".badge");if(old)old.remove();
743
+ b.title=r?r.t:"";b.setAttribute("aria-label",r?name+": "+r.t:name);if(!r)return;
744
+ b.classList.add("attn","attn-"+r.lvl);var sp=document.createElement("span");sp.className="badge"+(r.n?"":" dot");sp.textContent=r.n?(r.n>99?"99+":String(r.n)):"";b.appendChild(sp)})}
745
+ function go(k){if(k==="autopilot"&&S&&!S.trade_mode)openTM();if(k===tab&&!flash)return;var changed=k!==tab;tab=k;flash=null;render(true);if(changed){var mm=document.getElementById("main");if(mm)mm.scrollTop=0}}
601
746
  function msg(){return flash?'<div class="msg '+(flash[0]?"okk":"err")+'">'+esc(flash[1])+'</div>':""}
602
- function render(enter){nav();if(!S)return;var a=S.approvals||"none";var ap=document.getElementById("appr");ap.classList.toggle("live",a!=="none"||!!S.live);ap.textContent=(S.live?"Live":"")+(S.live?" · ":"")+(a==="none"?"Approvals off":"Approvals on");ap.title=a==="none"?"Set up Telegram or keep this page open":"Answered via "+a.replace("+"," and ");ap.onclick=function(){go("settings")};ap.style.cursor="pointer";var m=document.getElementById("main");
747
+ function render(enter){nav();if(!S)return;if(PYO)pyRender();var a=S.approvals||"none";var ap=document.getElementById("appr");ap.classList.toggle("live",a!=="none"||!!S.live);ap.textContent=(S.live?"Live":"")+(S.live?" · ":"")+(a==="none"?"Approvals off":"Approvals on");ap.title=a==="none"?"Set up Telegram or keep this page open":"Answered via "+a.replace("+"," and ");ap.onclick=function(){go("settings")};ap.style.cursor="pointer";var m=document.getElementById("main");
603
748
  if(tab!=="autopilot")LASTAP=null;if(tab!=="settings"){LIMD={};LIMPW=""}var prevTab=m.dataset.tab;m.dataset.tab=tab;if(tab==="home")m.innerHTML=home();else if(tab==="activity")m.innerHTML=activity();else if(tab==="approvals")m.innerHTML=approvals();else if(tab==="settings")m.innerHTML=settings();else if(tab==="autopilot"){var h=autopilot();if(!enter&&h===LASTAP&&prevTab==="autopilot"){chatAfter();return}m.innerHTML=h;LASTAP=h;chatAfter()}else{m.innerHTML=withdraw();tokOpts()}
604
749
  after(m,enter)}
605
750
  var shown=null;
@@ -637,6 +782,44 @@ function limT(id,on,text){var c=lv(id,on);return '<label><input type="checkbox"
637
782
  function setRow(icon,title,sub,btn){return '<div class="row">'+icon+'<div style="min-width:0"><div>'+title+'</div><div class="sm mut">'+sub+'</div></div><div class="r">'+btn+'</div></div>'}
638
783
  var SICO={ai:'<path d="M12 3v3M12 18v3M3 12h3M18 12h3"/><rect x="7" y="7" width="10" height="10" rx="2"/>',tg:'<path d="M21 4L3 11l6 2 2 6 3-4 5 4z"/>',bk:'<path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7z"/>',ext:'<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.7 3.8 5.7 3.8 9s-1.3 6.3-3.8 9c-2.5-2.7-3.8-5.7-3.8-9S9.5 5.7 12 3z"/>'};
639
784
  function sIco(k){return '<div class="ic"><svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'+SICO[k]+'</svg></div>'}
785
+ // Opening the Bots tab before picking: AI trading or Python trading. Python bots get the wallet's bot link (the same one AI apps use).
786
+ var TMO=null,PYO=null,PYPW="",PYMSG=null,PYBUSY=false;
787
+ var CODEICO='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 7l-5 5 5 5M16 7l5 5-5 5M13.5 4l-3 16"/></svg>';
788
+ function openTM(){if(TMO)return;TMO=document.createElement("div");TMO.className="aio tmo";
789
+ TMO.innerHTML='<div class="aidlg tmdlg" role="dialog" aria-modal="true" aria-label="How do you want to trade?"><h2>How do you want to trade?</h2><p class="ailead">Pick one to start. You can switch any time in Settings.</p><div class="tmopts">'+
790
+ '<button class="tmopt" data-tm="ai" style="--i:0"><span class="tmico tmai">'+aiSvg("claude")+'</span><b>AI trading</b><small>Tell Claude what you want in plain words. It suggests rules and bots, and nothing runs until you turn it on. You can also connect your own AI app.</small><span class="botcta">Start with AI'+ARROW+'</span></button>'+
791
+ '<button class="tmopt" data-tm="python" style="--i:1"><span class="tmico tmpy">'+CODEICO+'</span><b>Python trading</b><small>Run your own Python bots: four starter strategies, backtests on past prices and paper trading with pretend money.</small><span class="botcta">Start with Python'+ARROW+'</span></button>'+
792
+ '</div><p class="aismall" style="text-align:center;margin-bottom:4px">Either way, every trade stays inside your spending limits and approvals.</p><div style="text-align:center"><button class="aihand tmlater" data-tm="later">Decide later</button></div></div>';
793
+ TMO.addEventListener("click",function(e){var b=e.target.closest("[data-tm]");if(!b)return;var m=b.getAttribute("data-tm");if(m==="later"){closeTM();return}pickTM(m,true)});document.body.appendChild(TMO)}
794
+ function closeTM(){if(TMO){TMO.remove();TMO=null}}
795
+ function pickTM(m,first){api("/trade-mode",{mode:m}).then(function(){closeTM();return refresh()}).then(function(){if(!first){render();return}if(m==="python"){go("autopilot");openPy()}else{go("autopilot");setTimeout(function(){var c=document.querySelector(".tc-chat");if(c&&c.scrollIntoView)c.scrollIntoView({behavior:RM?"auto":"smooth",block:"start"})},120)}}).catch(function(e){flash=[0,e.message];closeTM();render()})}
796
+ function pyLocalEnv(){var l=(S.connect||{}).launch||{},env=l.env||{};return Object.keys(env).filter(function(k){return /^TOLLKIT_WALLET_/.test(k)}).map(function(k){return 'export '+k+'="'+env[k]+'"'}).join("\n")}
797
+ function pyBody(){var c=S.connect||{},r=c.remote,lim=S.policy||{},m=PYMSG,n=0;
798
+ function step(t){n++;return '<div class="pystep"><span class="pyn">'+n+'</span><b>'+t+'</b></div>'}
799
+ var h='<h2>Python bots</h2><p class="ailead">Run your own trading bots in Python. They trade through this wallet, inside your limits, and can’t withdraw, change limits or see your keys.</p>';
800
+ h+=step("Install")+aiCode("py-i","pip install tollkit-wallet")+'<p class="aismall">Python 3.9 or newer. No other packages needed.</p>';
801
+ var ready=false,env="";
802
+ if(r){h+=step("Your bot link");
803
+ if(!r.on){h+='<label class="ailbl" for="pypw">Wallet password</label><input id="pypw" class="aiin" type="password" autocomplete="current-password" placeholder="Needed to make the link" value="'+esc(PYPW)+'"><button class="b aigoap" data-pyact="on"'+(PYBUSY?" disabled":"")+'>'+(PYBUSY?"Making it…":"Make my bot link")+'</button>'+(m?'<div class="aim '+(m[0]?"ok":"bad")+'">'+esc(m[1])+'</div>':'')+'<p class="aismall">Your bot sends its requests to Tollkit’s server, which passes them to this wallet while Chrome is open and the wallet is unlocked. Your keys stay here.</p>'}
804
+ else{ready=true;env='export TOLLKIT_LINK="'+r.url+'"';h+=aiCode("py-link",r.url)+'<p class="aismall"><span class="'+(r.online?"ok":"warn")+'">●</span> '+(r.online?"The wallet is reachable.":"Waiting for the wallet to check in…")+' Keep this link private: anyone with it can trade inside your limits. It’s the same link your AI apps use. Keep Chrome open with the wallet unlocked while a bot runs.</p>'+
805
+ '<details class="pymore"><summary>New link or turn off</summary><label class="ailbl" for="pypw">Wallet password (for a new link)</label><input id="pypw" class="aiin" type="password" autocomplete="current-password" value="'+esc(PYPW)+'"><div class="btns" style="margin-top:8px"><button class="b g" data-pyact="new"'+(PYBUSY?" disabled":"")+'>New link</button><button class="b no" data-pyact="off"'+(PYBUSY?" disabled":"")+'>Turn off</button></div><p class="aismall">A new link stops the old one, for bots and AI apps alike.</p></details>'+(m?'<div class="aim '+(m[0]?"ok":"bad")+'">'+esc(m[1])+'</div>':'')}}
806
+ else{h+=step("Let your scripts unlock the wallet");
807
+ if(c.ready){ready=true;env=pyLocalEnv();h+='<p class="aismall"><span class="ok">●</span> Saved. Scripts on this computer unlock the wallet with a copy of your password that only your account can read.</p>'}
808
+ else h+='<label class="ailbl" for="pypw">Wallet password</label><input id="pypw" class="aiin" type="password" autocomplete="current-password" value="'+esc(PYPW)+'"><button class="b aigoap" data-pyact="pw"'+(PYBUSY?" disabled":"")+'>'+(PYBUSY?"Saving…":"Save it for my scripts")+'</button>'+(m?'<div class="aim '+(m[0]?"ok":"bad")+'">'+esc(m[1])+'</div>':'')+'<p class="aismall">Saves a copy of the password on this computer, readable only by your account. Anyone who can read it and your wallet folder could trade inside your limits.</p>'}
809
+ if(ready){h+=step("Try it")+aiCode("py-try",(env?env+"\n":"")+"tollkit-bot check\ntollkit-bot backtest dip-buyer\ntollkit-bot run dip-buyer")+'<p class="aismall">Paper trading (pretend money, live prices) unless you add <code>--live</code>. Built in: <code>dca</code>, <code>dip-buyer</code>, <code>momentum</code>, <code>rebalance</code>.'+(r?' On Windows PowerShell: <code>$env:TOLLKIT_LINK="…"</code>.':'')+'</p>'+
810
+ step("Write your own")+aiCode("py-code",'from tollkit_wallet import Strategy, buy, hold\n\nclass BuyDips(Strategy):\n token, chain = "ETH", "base"\n def decide(self, m, notes):\n if m.change_pct(24) is not None and m.change_pct(24) <= -5 and m.cash >= 20:\n return buy(20, "down 5% in a day")\n return hold()')+
811
+ '<p class="aismall"><code>tollkit-bot new my_bot.py</code> writes a full example to edit. Trades over '+usd(lim.approval_above_usd||0)+' wait for you in Approvals.</p>'}
812
+ return h}
813
+ function pyRender(){if(!PYO)return;var d=PYO.querySelector(".aidlg"),sc=d.scrollTop,foc=document.activeElement&&document.activeElement.id==="pypw";PYO.querySelector(".aibody").innerHTML=pyBody();d.scrollTop=sc;var pw=document.getElementById("pypw");if(pw){pw.oninput=function(){PYPW=pw.value};if(foc)pw.focus()}}
814
+ function openPy(){if(!PYO){PYO=document.createElement("div");PYO.className="aio";PYO.innerHTML='<div class="aidlg" role="dialog" aria-modal="true" aria-label="Python bots"><button class="aix" aria-label="Close">×</button><div class="aibody"></div></div>';PYO.addEventListener("mousedown",function(e){if(e.target===PYO)closePy()});PYO.addEventListener("click",pyClick);document.body.appendChild(PYO)}pyRender()}
815
+ function closePy(){if(PYO){PYO.remove();PYO=null;PYPW="";PYMSG=null}}
816
+ function pyDo(act){if(act!=="off"&&!PYPW){PYMSG=[0,"Type your wallet password first."];pyRender();var p=document.getElementById("pypw");if(p)p.focus();return}
817
+ PYBUSY=true;PYMSG=null;pyRender();(act==="pw"?api("/ai/password",{password:PYPW}):api("/connect/remote",{password:PYPW,action:act})).then(function(){PYPW="";PYMSG=act==="off"?[1,"Turned off. No bot or app can reach the wallet now."]:act==="new"?[1,"New link made. Update it in your bots and AI apps."]:null;return refresh()}).catch(function(e){PYMSG=[0,e.message]}).then(function(){PYBUSY=false;pyRender()})}
818
+ function pyClick(e){var t=e.target.closest("button");if(!t)return;if(t.classList.contains("aix"))closePy();else if(t.hasAttribute("data-pyact"))pyDo(t.getAttribute("data-pyact"));else if(t.hasAttribute("data-copy")){var el=document.getElementById(t.getAttribute("data-copy")),v=el?el.textContent:"",done=function(){t.textContent="Copied";setTimeout(function(){t.textContent="Copy"},1500)};if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(v).then(done,function(){fallback(v);done()});else{fallback(v);done()}}}
819
+ function pyStatus(){var c=S.connect||{},r=c.remote;if(r)return r.on?(r.online?'<span class="ok">●</span> Bot link on, wallet reachable':'<span class="warn">●</span> Bot link on, waiting for the wallet'):'No bot link yet';return c.ready?'<span class="ok">●</span> Ready for scripts on this computer':'Not set up yet'}
820
+ function pyCard(){return '<div class="pycard"><div class="pyct"><span class="tmico tmpy sm">'+CODEICO+'</span><div style="min-width:0"><b>Python bots</b><div class="sm mut">'+pyStatus()+'</div></div><button class="botcta" onclick="openPy()">'+(((S.connect||{}).remote||{}).on||(!(S.connect||{}).remote&&(S.connect||{}).ready)?"Show setup":"Set up")+ARROW+'</button></div></div>'}
821
+ function tradeRow(){var m=S.trade_mode;return '<div class="row"><div style="min-width:0"><div>Trading with</div><div class="sm mut">'+(m==="python"?"Your own Python bots.":m==="ai"?"Claude and your AI apps.":"Not picked yet.")+' Both use the same limits.</div></div><div class="r"><div class="bseg tmseg"><button class="bsb'+(m==="ai"?" on":"")+'" onclick="pickTM(\'ai\',false)">AI</button><button class="bsb'+(m==="python"?" on":"")+'" onclick="pickTM(\'python\',false)">Python</button></div></div></div>'+
822
+ setRow('<div class="ic">'+CODEICO.replace('aria-hidden','width="17" height="17" aria-hidden')+'</div>',"Python bots",pyStatus(),'<button class="b g sbtn" onclick="openPy()">'+(S.trade_mode==="python"?"Setup":"Set up")+'</button>')}
640
823
  function settings(){var p=S.policy,sp=S.spent_today||{},dirty=Object.keys(LIMD).length>0;
641
824
  var lim='<h3>Spending limits</h3><div class="mut sm">Your AI and autopilot have to stay inside these and can\'t change them. Saving needs your password.</div>'+
642
825
  '<div class="sgrp">Swaps</div><div class="grid2">'+limF("per_trade_usd","Per swap ($)","Largest single swap")+limF("daily_trades_usd","Swaps per day ($)",null,sp.trades_usd||0)+limF("approval_above_usd","Ask me above ($)","Bigger swaps wait for your OK")+limF("max_slippage_bps","Max slippage (bps)","100 bps = 1%")+'</div>'+
@@ -657,7 +840,7 @@ var exr=setRow(sIco("ext"),"Browser extension",ex.paired?'<span class="ok">Paire
657
840
  (EXC?'<div class="row" style="flex-wrap:wrap"><div style="min-width:0"><div class="sm mut">Paste this code into the Tollkit extension. It works once, for 5 minutes.</div><div class="addr" style="font-size:17px;letter-spacing:.06em;margin-top:4px">'+esc(EXC)+'</div></div><div class="r">'+cpBtn(EXC)+'</div></div>':'')+
658
841
  (ex.sites.length?ex.sites.map(function(x){var h=x.origin.replace(/^https?:\/\//,"");return '<div class="row"><div style="min-width:0"><div>'+esc(h)+'</div><div class="sm mut">'+[x.evm?"Base/Ethereum":"",x.solana?"Solana":""].filter(Boolean).join(" + ")+'</div></div><div class="r"><button class="b no sbtn" data-extoff="'+esc(x.origin)+'">Disconnect</button></div></div>'}).join(""):'')+
659
842
  (ex.paired?'<div class="sm" style="margin-top:6px"><a href="#" onclick="extUnpair();return false">Unpair all browsers</a></div>':'');
660
- return lim+'<h3>AI</h3>'+ai+'<h3>Websites</h3>'+exr+'<h3>Approvals</h3>'+tg+'<h3>Recovery</h3>'+bkr}
843
+ return lim+'<h3>How you trade</h3>'+tradeRow()+'<h3>AI</h3>'+ai+'<h3>Websites</h3>'+exr+'<h3>Approvals</h3>'+tg+'<h3>Recovery</h3>'+bkr}
661
844
  var EXC=null,EXCN=0;
662
845
  function extCode(){api("/extmgr/code",{}).then(function(r){EXC=r.code;EXCN=(S.ext||{}).paired||0;render()}).catch(function(e){flash=[0,e.message];render()})}
663
846
  document.addEventListener("click",function(e){var b=e.target.closest&&e.target.closest("[data-extoff]");if(b)extOff(b.getAttribute("data-extoff"))});
package/dist/ui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACtE,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAqRnB,SAAS;WACT,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA0ZC,CAAC"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACtE,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAuTnB,SAAS;WACT,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA+iBC,CAAC"}
package/dist/wallet.d.ts CHANGED
@@ -33,6 +33,13 @@ export interface WalletOptions {
33
33
  dapp?: Partial<ConstructorParameters<typeof DappBridge>[0]>;
34
34
  /** Override for tests / other channels. Default: Telegram if configured. */
35
35
  approve?: ((text: string) => Promise<Decision>) | null;
36
+ /** Override for tests: Tollkit's separately-sent Solana fee (recipient, SOL price, balance and sender). */
37
+ solFee?: {
38
+ recipient?: string;
39
+ solUsd?: number;
40
+ balance?: () => Promise<bigint>;
41
+ send?: (lamports: bigint, to: string) => Promise<string>;
42
+ };
36
43
  }
37
44
  export interface SwapResult {
38
45
  ok: boolean;
@@ -93,7 +100,7 @@ export declare class TollkitWallet {
93
100
  max_slippage_bps: number;
94
101
  chains: ("base" | "ethereum" | "solana")[];
95
102
  block_seller_warnings: boolean;
96
- agent_rules: "propose" | "auto";
103
+ agent_rules: "auto" | "propose";
97
104
  withdraw_addresses: {
98
105
  address: string;
99
106
  chain: "base" | "ethereum" | "solana";
@@ -119,8 +126,13 @@ export declare class TollkitWallet {
119
126
  private taker;
120
127
  /** Dollar value of a quote. Stable side if there is one; Jupiter reports it; otherwise price the sell side against USDC. */
121
128
  valueOf(q: SwapQuote): Promise<number | null>;
129
+ /** Where and how much Tollkit's separately-sent Solana fee is. */
130
+ private separateFee;
131
+ private sendSolFee;
122
132
  /** A priced, checked quote. Nothing is signed. */
123
133
  quote(req: SwapRequest): Promise<SwapResult>;
134
+ /** Core, listed (Superchain list on Base/Ethereum, Jupiter-verified on Solana) or added by the owner. */
135
+ private knownToken;
124
136
  private priced;
125
137
  /** Quote, check, approve if needed, reserve, sign, send, record. */
126
138
  swap(req: SwapRequest): Promise<SwapResult>;