@gamaze/hicortex 0.23.0 → 0.23.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.
- package/assets/dashboard.html +430 -140
- package/dist/llm.d.ts +12 -1
- package/dist/llm.js +14 -3
- package/dist/mcp-stdio.d.ts +61 -3
- package/dist/mcp-stdio.js +272 -51
- package/package.json +1 -1
- package/server.json +2 -2
package/assets/dashboard.html
CHANGED
|
@@ -345,9 +345,8 @@
|
|
|
345
345
|
}
|
|
346
346
|
.acct-btn:hover{ border-color:var(--green-mid); color:var(--green-mid); }
|
|
347
347
|
.acct-btn:disabled{ opacity:0.5; cursor:default; }
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
.acct-menu summary:hover{ color:var(--green-mid); }
|
|
348
|
+
/* the acct-* snippet classes are shared by the account menu's token row
|
|
349
|
+
and the connect overlay's snippet blocks (#506 — one idiom, two hosts) */
|
|
351
350
|
.acct-snippet{ margin-bottom:8px; }
|
|
352
351
|
.acct-snippet pre{
|
|
353
352
|
background:var(--panel); border:1px solid var(--line2); border-radius:7px;
|
|
@@ -363,12 +362,40 @@
|
|
|
363
362
|
}
|
|
364
363
|
.acct-link:hover{ background:var(--panel); }
|
|
365
364
|
.acct-link.acct-danger:hover{ color:var(--red); }
|
|
366
|
-
/* the
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
.acct-link.acct-
|
|
365
|
+
/* the Subscription entry's upgrade pill (#371 round 3, owner: the menu's
|
|
366
|
+
two billing entries merge into ONE "Subscription" item — "it can have
|
|
367
|
+
an upgrade pill/label after it before any subscription is active").
|
|
368
|
+
The ENTRY always renders; the PILL is what the plan-unset eligibility
|
|
369
|
+
gates. Accent badge in the trigger's chip idiom. */
|
|
370
|
+
.acct-link .acct-pill{
|
|
371
|
+
margin-left:8px; font-size:9px; font-weight:640; letter-spacing:.08em; text-transform:uppercase;
|
|
372
|
+
color:var(--green-mid); border:1px solid color-mix(in srgb, var(--green) 35%, transparent);
|
|
373
|
+
background:color-mix(in srgb, var(--green) 8%, transparent);
|
|
374
|
+
border-radius:999px; padding:1px 7px; vertical-align:1px;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* ============ billing-return banner (#371) — the one-line acknowledgment
|
|
378
|
+
for ?billing=success|cancelled (the URLs the router stamps on every
|
|
379
|
+
checkout). A page-level element under the header, deliberately NOT a
|
|
380
|
+
toast: the returning buyer must still find it when they look back at
|
|
381
|
+
the page. Shown once — the param is stripped on show, so neither
|
|
382
|
+
dismissal nor a plain reload can re-show it. Theme tokens only. */
|
|
383
|
+
#billing-banner{
|
|
384
|
+
display:flex; align-items:center; justify-content:center; gap:10px;
|
|
385
|
+
margin:8px 16px 0; padding:7px 12px; border-radius:10px;
|
|
386
|
+
border:1px solid var(--line2); background:var(--panel);
|
|
387
|
+
font-size:12px; color:var(--dim); flex:none;
|
|
388
|
+
}
|
|
389
|
+
/* author display beats the UA's [hidden]{display:none} — without the
|
|
390
|
+
override the "hidden" banner rendered an EMPTY strip under the header
|
|
391
|
+
on every plain load (found live in preview). The #model-modal idiom. */
|
|
392
|
+
#billing-banner[hidden]{ display:none; }
|
|
393
|
+
#billing-banner[data-kind="success"]{
|
|
394
|
+
color:var(--green-mid);
|
|
395
|
+
border-color:color-mix(in srgb, var(--green) 30%, var(--line2));
|
|
396
|
+
}
|
|
397
|
+
#billing-banner button{ color:var(--faint); font-size:11px; padding:2px 4px; }
|
|
398
|
+
#billing-banner button:hover{ color:var(--text); }
|
|
372
399
|
|
|
373
400
|
/* ============ the stats band — a WRAPPING CARD GRID ============
|
|
374
401
|
Cards come in TWO widths only — the row is a positional grid
|
|
@@ -503,8 +530,9 @@
|
|
|
503
530
|
|
|
504
531
|
.cap-of{ font-size:10.5px; color:var(--faint); font-variant-numeric:tabular-nums; }
|
|
505
532
|
.bseg.warn .csub{ color:var(--amber); }
|
|
506
|
-
/* Agents card: a faded amber Connect pill, bottom-right — opens the
|
|
507
|
-
connect
|
|
533
|
+
/* Agents card: a faded amber Connect pill, bottom-right — opens the
|
|
534
|
+
full-cover connect overlay (#506; the phase-1 rail drawer stand-in for
|
|
535
|
+
the seats CTA is retired: no cap exists) */
|
|
508
536
|
#lim-ag .limit-cta{
|
|
509
537
|
display:inline-block; align-self:flex-end;
|
|
510
538
|
color:var(--amber); border-color:color-mix(in srgb, var(--amber) 45%, transparent);
|
|
@@ -766,24 +794,6 @@
|
|
|
766
794
|
.ctx-detail .ctx-head{ display:flex; align-items:center; gap:8px; margin-bottom:4px; min-width:0; }
|
|
767
795
|
.ctx-detail .ctx-head b{ font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
768
796
|
.ctx-detail .ctx-head small{ color:var(--faint); font-family:var(--mono); font-size:9px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
769
|
-
#connect{ flex:none; border-top:1px solid var(--line); padding:8px; }
|
|
770
|
-
#connect-btn{
|
|
771
|
-
display:flex; align-items:center; gap:7px; width:100%; justify-content:center;
|
|
772
|
-
color:var(--green-mid); border:1px dashed color-mix(in srgb, var(--green) 35%, transparent); border-radius:10px;
|
|
773
|
-
padding:7px 10px; font-size:11.5px; transition:box-shadow .15s, background .15s;
|
|
774
|
-
}
|
|
775
|
-
html:not([data-theme="light"]) #connect-btn:hover{ box-shadow:0 0 12px rgba(61,255,122,.18); }
|
|
776
|
-
html[data-theme="light"] #connect-btn:hover{ background:color-mix(in srgb, var(--green) 5%, transparent); }
|
|
777
|
-
#connect-btn .plus{ font-size:14px; line-height:1; }
|
|
778
|
-
#connect-x{ display:none; margin-top:8px; background:var(--elev); border:1px solid var(--line2); border-radius:10px; padding:10px 11px; }
|
|
779
|
-
#connect-x.open{ display:block; }
|
|
780
|
-
#connect-x p{ font-size:10.5px; color:var(--dim); line-height:1.5; margin-bottom:7px; }
|
|
781
|
-
#connect-x .snip{
|
|
782
|
-
font-family:var(--mono); font-size:10px; color:var(--green-mid); background:var(--panel);
|
|
783
|
-
border:1px solid var(--line2); border-radius:7px; padding:7px 9px;
|
|
784
|
-
white-space:nowrap; overflow-x:auto; margin-bottom:7px;
|
|
785
|
-
}
|
|
786
|
-
#connect-x .row{ display:flex; justify-content:flex-end; gap:6px; }
|
|
787
797
|
|
|
788
798
|
/* ---- shared kv rows (band detail groups + memory detail internals) ---- */
|
|
789
799
|
.kv{ display:flex; justify-content:space-between; gap:12px; font-size:11.5px; padding:2.5px 0; min-width:0; }
|
|
@@ -975,9 +985,9 @@
|
|
|
975
985
|
/* toasts + tooltip */
|
|
976
986
|
#toasts{ position:fixed; left:50%; bottom:20px; transform:translateX(-50%); z-index:95; display:flex; flex-direction:column; gap:8px; align-items:center; pointer-events:none; }
|
|
977
987
|
|
|
978
|
-
/* model-settings modal (#422) — the #panel
|
|
979
|
-
|
|
980
|
-
|
|
988
|
+
/* model-settings modal (#422) — the #panel visual language as a centered
|
|
989
|
+
overlay; every color rides the theme vars (zero theme-specific rules,
|
|
990
|
+
both themes work) */
|
|
981
991
|
#model-modal{ position:fixed; inset:0; z-index:90; display:grid; place-items:center; }
|
|
982
992
|
#model-modal[hidden]{ display:none; }
|
|
983
993
|
#model-modal .mm-backdrop{ position:absolute; inset:0; background:rgba(0,0,0,.55); }
|
|
@@ -994,6 +1004,61 @@
|
|
|
994
1004
|
.mm-check small{ color:var(--faint); }
|
|
995
1005
|
#model-modal .mm-note{ padding:4px 14px 8px; font-size:10px; color:var(--faint); font-style:italic; }
|
|
996
1006
|
#model-modal .mm-foot{ display:flex; justify-content:flex-end; gap:7px; padding:10px 14px; border-top:1px solid var(--line); }
|
|
1007
|
+
|
|
1008
|
+
/* plan & billing overlay (#371, owner amendment 2 2026-09-23: the under-
|
|
1009
|
+
graph section was rejected — the upgrade surface must COVER the dash).
|
|
1010
|
+
The #model-modal idiom, widened for two side-by-side option cards; the
|
|
1011
|
+
cover is OPAQUE (var(--bg), not a scrim) — the dash is fully hidden
|
|
1012
|
+
behind it, not dimmed-partial. The [hidden] override is mandatory
|
|
1013
|
+
(author display beats the UA rule — the banner lesson, pinned by tests). */
|
|
1014
|
+
#plan-overlay{ position:fixed; inset:0; z-index:92; display:grid; place-items:center; }
|
|
1015
|
+
#plan-overlay[hidden]{ display:none; }
|
|
1016
|
+
#plan-overlay .po-cover{ position:absolute; inset:0; background:var(--bg); }
|
|
1017
|
+
#plan-overlay .po-card{
|
|
1018
|
+
position:relative; width:min(860px, calc(100vw - 32px)); max-height:calc(100vh - 48px);
|
|
1019
|
+
overflow-y:auto; background:var(--elev); border:1px solid var(--line2); border-radius:14px;
|
|
1020
|
+
box-shadow:var(--shadow-pop);
|
|
1021
|
+
}
|
|
1022
|
+
#plan-overlay .po-head{ display:flex; align-items:center; justify-content:space-between; padding:14px 16px 10px; border-bottom:1px solid var(--line); }
|
|
1023
|
+
#plan-overlay .po-head b{ font-family:var(--serif); font-weight:500; font-size:17px; letter-spacing:.06em; text-transform:uppercase; }
|
|
1024
|
+
#plan-overlay .po-body{ padding:14px 16px 16px; display:flex; flex-direction:column; gap:10px; }
|
|
1025
|
+
#plan-overlay .po-current{ font-size:12.5px; color:var(--dim); }
|
|
1026
|
+
.po-opts{ display:grid; grid-template-columns:1fr 1fr; gap:12px; }
|
|
1027
|
+
#acct-options[hidden]{ display:none; } /* author grid beats [hidden] (renderPlanOverlay hides it when a plan is set) */
|
|
1028
|
+
.po-opt{ background:var(--panel); border:1px solid var(--line2); border-radius:12px; padding:14px 16px 13px; display:flex; flex-direction:column; gap:5px; min-width:0; }
|
|
1029
|
+
.po-opt .po-name{ font-size:10px; color:var(--faint); text-transform:uppercase; letter-spacing:.13em; }
|
|
1030
|
+
.po-opt .po-price{ font-size:26px; font-weight:640; font-variant-numeric:tabular-nums; letter-spacing:.01em; color:var(--green); }
|
|
1031
|
+
html:not([data-theme="light"]) .po-opt .po-price{ text-shadow:0 0 12px rgba(61,255,122,.35); }
|
|
1032
|
+
.po-opt .po-note{ font-size:11px; color:var(--dim); }
|
|
1033
|
+
/* what's included (round 3, owner: "NO info on what is included in the
|
|
1034
|
+
plan") — the marketing site's Cloud feature lines, verbatim, per card. */
|
|
1035
|
+
.po-opt .po-feat{ list-style:none; margin-top:7px; display:flex; flex-direction:column; gap:3px; }
|
|
1036
|
+
.po-opt .po-feat li{ font-size:11px; color:var(--dim); display:flex; gap:7px; align-items:baseline; }
|
|
1037
|
+
.po-opt .po-feat li::before{ content:"✓"; color:var(--green-mid); font-size:10px; }
|
|
1038
|
+
.po-choose{
|
|
1039
|
+
align-self:flex-start; margin-top:8px;
|
|
1040
|
+
color:var(--green-mid); border:1px solid color-mix(in srgb, var(--green) 35%, transparent);
|
|
1041
|
+
border-radius:999px; padding:4px 15px; font-size:11.5px; font-weight:600; white-space:nowrap;
|
|
1042
|
+
}
|
|
1043
|
+
.po-choose:hover{ border-color:var(--green-mid); }
|
|
1044
|
+
.po-choose:disabled{ opacity:.5; cursor:default; }
|
|
1045
|
+
@media (max-width:720px){ .po-opts{ grid-template-columns:1fr; } }
|
|
1046
|
+
|
|
1047
|
+
/* connect overlay (#506) — ONE full-cover surface for every "Connect an
|
|
1048
|
+
agent" entry (the #plan-overlay idiom, owner 2026-09-24: the fleet
|
|
1049
|
+
rail's inline drawer is REMOVED, not kept; the account-menu entry and
|
|
1050
|
+
the Agents-detected pill both open THIS). The cover is OPAQUE
|
|
1051
|
+
(var(--bg), not a scrim) and the [hidden] override is MANDATORY
|
|
1052
|
+
(author display beats the UA rule — the banner lesson, pinned by
|
|
1053
|
+
tests). The snippet blocks reuse the acct-* classes. */
|
|
1054
|
+
#connect-overlay{ position:fixed; inset:0; z-index:92; display:grid; place-items:center; }
|
|
1055
|
+
#connect-overlay[hidden]{ display:none; }
|
|
1056
|
+
#connect-overlay .co-cover{ position:absolute; inset:0; background:var(--bg); }
|
|
1057
|
+
#connect-overlay .co-card{ position:relative; width:min(640px, calc(100vw - 32px)); max-height:calc(100vh - 48px); overflow-y:auto; background:var(--elev); border:1px solid var(--line2); border-radius:14px; box-shadow:var(--shadow-pop); }
|
|
1058
|
+
#connect-overlay .co-head{ display:flex; align-items:center; justify-content:space-between; padding:14px 16px 10px; border-bottom:1px solid var(--line); }
|
|
1059
|
+
#connect-overlay .co-head b{ font-family:var(--serif); font-weight:500; font-size:17px; letter-spacing:.06em; text-transform:uppercase; }
|
|
1060
|
+
#connect-overlay .co-body{ padding:14px 16px 16px; display:flex; flex-direction:column; gap:10px; }
|
|
1061
|
+
#connect-overlay .co-intro{ font-size:12px; color:var(--dim); line-height:1.5; }
|
|
997
1062
|
.toast{
|
|
998
1063
|
background:var(--elev); border:1px solid var(--line2); border-radius:999px; padding:8px 17px;
|
|
999
1064
|
font-size:12px; color:var(--text); box-shadow:var(--shadow-pop);
|
|
@@ -1146,6 +1211,11 @@
|
|
|
1146
1211
|
</div>
|
|
1147
1212
|
</header>
|
|
1148
1213
|
|
|
1214
|
+
<!-- billing-return banner (#371) — filled by wireBillingBanner() when the
|
|
1215
|
+
URL carries ?billing=success|cancelled (the checkout return URLs the
|
|
1216
|
+
router stamps). Dismiss removes it; the param is stripped on show. -->
|
|
1217
|
+
<div id="billing-banner" role="status" aria-live="polite" hidden></div>
|
|
1218
|
+
|
|
1149
1219
|
<!-- ============ the stats band — card grid (two rows of four) ============ -->
|
|
1150
1220
|
<section id="measures" aria-label="Vitals, operator data, and account limits">
|
|
1151
1221
|
<div class="band" id="strip">
|
|
@@ -1164,8 +1234,10 @@
|
|
|
1164
1234
|
<div class="csub" id="ag-sub-band">found on their own</div>
|
|
1165
1235
|
<button class="limit-cta" id="ag-cta">Connect Agent</button>
|
|
1166
1236
|
</div>
|
|
1167
|
-
<!-- #plan-billing: the
|
|
1168
|
-
|
|
1237
|
+
<!-- #plan-billing: the token/cap usage card — this page's
|
|
1238
|
+
billing-adjacent surface. The account-menu item that scrolled
|
|
1239
|
+
here was consolidated into the Subscription entry (#371 round 3);
|
|
1240
|
+
the anchor id stays (diagnostics + tests pin it). -->
|
|
1169
1241
|
<div class="bseg c-red" id="plan-billing">
|
|
1170
1242
|
<div class="gh"><span class="t">Token usage</span></div>
|
|
1171
1243
|
<div class="big"><span class="n" id="tok-n">—</span><span class="l">used</span><span class="cap-of" id="tok-of" hidden></span></div>
|
|
@@ -1284,17 +1356,9 @@
|
|
|
1284
1356
|
<aside id="agentscol" aria-label="Connected agents">
|
|
1285
1357
|
<div class="col-head"><span class="t">Fleet</span><small id="ag-sub">0 · auto-detected</small></div>
|
|
1286
1358
|
<div id="ag-list"></div>
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
<p>Run this on the machine that hosts the agent. It appears here the moment it first talks to the server.</p>
|
|
1291
|
-
<div class="snip" id="connect-snip"></div>
|
|
1292
|
-
<div class="row">
|
|
1293
|
-
<button class="btn sm" id="connect-copy">Copy command</button>
|
|
1294
|
-
<button class="btn sm save" id="connect-close">Done</button>
|
|
1295
|
-
</div>
|
|
1296
|
-
</div>
|
|
1297
|
-
</div>
|
|
1359
|
+
<!-- #506: the fleet rail's + connect-an-agent drawer lived here — REMOVED
|
|
1360
|
+
(owner 2026-09-24). Both surviving connect entries (the account-menu
|
|
1361
|
+
entry + the Agents-detected pill) open the #connect-overlay. -->
|
|
1298
1362
|
</aside>
|
|
1299
1363
|
</main>
|
|
1300
1364
|
|
|
@@ -1338,6 +1402,64 @@
|
|
|
1338
1402
|
</div>
|
|
1339
1403
|
</div>
|
|
1340
1404
|
</div>
|
|
1405
|
+
|
|
1406
|
+
<!-- ============ the PLAN & BILLING overlay (#371, owner amendment 2
|
|
1407
|
+
2026-09-23: the under-graph section was rejected after the live look —
|
|
1408
|
+
"the plan and billing needs to open up as overlay, not under the
|
|
1409
|
+
graph, it could not even find it first. needs to cover the normal dash
|
|
1410
|
+
or be a separate tab or view"; the owner picked the full overlay).
|
|
1411
|
+
Opens from the account menu's Subscription entry (round 3: the two
|
|
1412
|
+
billing entries merged into one); the opaque .po-cover fully hides
|
|
1413
|
+
the dash (not dimmed-partial). × / cover-click / ESC close and return
|
|
1414
|
+
where the user was. The option cards are GENERATED from the
|
|
1415
|
+
PLAN_OPTIONS data array (round 3: N-driven — a future plan is a data
|
|
1416
|
+
entry + a router price id, not a page rework); prices and the
|
|
1417
|
+
included-features bullets are display constants aligned with the
|
|
1418
|
+
marketing site (lockstep-pinned by tests). The router deliberately
|
|
1419
|
+
knows only Stripe price ids, so there is no server-side amount to
|
|
1420
|
+
read; the Stripe Checkout page shows the authoritative amount before
|
|
1421
|
+
payment. ============ -->
|
|
1422
|
+
<div id="plan-overlay" hidden>
|
|
1423
|
+
<div class="po-cover" id="po-cover"></div>
|
|
1424
|
+
<div class="po-card" role="dialog" aria-modal="true" aria-labelledby="po-title">
|
|
1425
|
+
<div class="po-head"><b id="po-title">Plan & billing</b><button class="pm-close" id="po-close" aria-label="Close">✕</button></div>
|
|
1426
|
+
<div class="po-body">
|
|
1427
|
+
<div class="po-current" id="po-current"></div>
|
|
1428
|
+
<div class="po-opts" id="acct-options"></div>
|
|
1429
|
+
</div>
|
|
1430
|
+
</div>
|
|
1431
|
+
</div>
|
|
1432
|
+
|
|
1433
|
+
<!-- ============ the CONNECT overlay (#506) — ONE full-cover surface for
|
|
1434
|
+
every "Connect an agent" entry. Before this there were two shapes: the
|
|
1435
|
+
account menu's inline expandable snippets and the fleet rail's +
|
|
1436
|
+
drawer. Owner 2026-09-24: the rail entry is REMOVED, not kept; the
|
|
1437
|
+
account-menu entry and the Agents-detected pill both open THIS
|
|
1438
|
+
(#plan-overlay idiom: opaque cover, × / cover-click / ESC). The
|
|
1439
|
+
snippet <pre>s and copy buttons keep the acct-* ids so
|
|
1440
|
+
renderAcctToken keeps driving them (token-gated: disabled until
|
|
1441
|
+
/account/token answers); the wiring is STATIC so the overlay works
|
|
1442
|
+
even when /account failed and the menu never rendered. The overlay
|
|
1443
|
+
itself lazily fetches the token on open (idempotent). ============ -->
|
|
1444
|
+
<div id="connect-overlay" hidden>
|
|
1445
|
+
<div class="co-cover" id="co-cover"></div>
|
|
1446
|
+
<div class="co-card" role="dialog" aria-modal="true" aria-labelledby="co-title">
|
|
1447
|
+
<div class="co-head"><b id="co-title">Connect an agent</b><button class="pm-close" id="co-close" aria-label="Close">✕</button></div>
|
|
1448
|
+
<div class="co-body">
|
|
1449
|
+
<p class="co-intro">Run this on the machine that hosts the agent. It appears here the moment it first talks to the server.</p>
|
|
1450
|
+
<div class="acct-label">CLI</div>
|
|
1451
|
+
<div class="acct-snippet">
|
|
1452
|
+
<pre id="acct-cli"></pre>
|
|
1453
|
+
<button id="acct-copy-cli" type="button" class="acct-btn" disabled>Copy</button>
|
|
1454
|
+
</div>
|
|
1455
|
+
<div class="acct-label">MCP — ~/.claude.json</div>
|
|
1456
|
+
<div class="acct-snippet">
|
|
1457
|
+
<pre id="acct-mcp"></pre>
|
|
1458
|
+
<button id="acct-copy-mcp" type="button" class="acct-btn" disabled>Copy</button>
|
|
1459
|
+
</div>
|
|
1460
|
+
</div>
|
|
1461
|
+
</div>
|
|
1462
|
+
</div>
|
|
1341
1463
|
<script>
|
|
1342
1464
|
'use strict';
|
|
1343
1465
|
/* =====================================================================
|
|
@@ -2966,9 +3088,12 @@ document.addEventListener('keydown',e=>{
|
|
|
2966
3088
|
else if(e.key==='ArrowRight'){ manualTake(); seek(T+1); }
|
|
2967
3089
|
else if(e.key==='t'||e.key==='T'){ setTheme(document.documentElement.dataset.theme==='dark'?'light':'dark'); }
|
|
2968
3090
|
else if(e.key==='Escape'){
|
|
2969
|
-
|
|
3091
|
+
const po=el('plan-overlay');
|
|
3092
|
+
const co=el('connect-overlay');
|
|
3093
|
+
if(po && !po.hidden) closePlanOverlay(); /* the full-cover overlay is topmost — it closes first (#371) */
|
|
3094
|
+
else if(co && !co.hidden) closeConnectOverlay(); /* #506 — same tier: the connect overlay */
|
|
3095
|
+
else if(document.getElementById('acct-menu') && document.getElementById('acct-menu').classList.contains('open')) closeAcctMenu();
|
|
2970
3096
|
else if(panel.classList.contains('open')) closePanel();
|
|
2971
|
-
else if(el('connect-x').classList.contains('open')) el('connect-x').classList.remove('open');
|
|
2972
3097
|
else if(focusDom!=null) setFocus(null);
|
|
2973
3098
|
}
|
|
2974
3099
|
wake();
|
|
@@ -3784,7 +3909,9 @@ function lastNightByAgent(){
|
|
|
3784
3909
|
function buildAgents(){
|
|
3785
3910
|
const listEl=el('ag-list'); listEl.innerHTML='';
|
|
3786
3911
|
const gs=contextGroups();
|
|
3787
|
-
|
|
3912
|
+
/* #506: the "below" target (the rail drawer) is gone — the empty state
|
|
3913
|
+
points at the one remaining connect surface, the account menu */
|
|
3914
|
+
if(!gs.length){ listEl.innerHTML='<div class="pm-empty" style="padding:10px 6px;">no contexts yet — connect an agent from the account menu (top right)</div>'; return; }
|
|
3788
3915
|
const lastNight=lastNightByAgent();
|
|
3789
3916
|
/* #423 phase 3: presence + pauses ride D.fleet — ABSENT on a pre-phase-3
|
|
3790
3917
|
server → no dots, no toggles, no paused state (phase-2 rendering) */
|
|
@@ -4022,8 +4149,6 @@ async function refreshFleet(){
|
|
|
4022
4149
|
renderBand();
|
|
4023
4150
|
}catch(e){ toast('Refresh failed: '+e.message); }
|
|
4024
4151
|
}
|
|
4025
|
-
el('connect-btn').onclick=()=>{ el('connect-x').classList.toggle('open'); wake(); };
|
|
4026
|
-
|
|
4027
4152
|
/* model-settings modal wiring (#422) */
|
|
4028
4153
|
el('model-cfg').onclick=()=>{ openModelModal(); wake(); };
|
|
4029
4154
|
el('mm-close').onclick=closeModelModal;
|
|
@@ -4031,14 +4156,10 @@ el('mm-cancel').onclick=closeModelModal;
|
|
|
4031
4156
|
el('mm-backdrop').onclick=closeModelModal;
|
|
4032
4157
|
el('mm-save').onclick=()=>{ saveModelSettings(); wake(); };
|
|
4033
4158
|
el('mm-think').addEventListener('change',()=>{ mmThinkTouched=true; });
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
el('
|
|
4038
|
-
el('connect-copy').onclick=()=>{
|
|
4039
|
-
copyToClipboard('npx @gamaze/hicortex init --server '+location.origin, el('connect-copy'));
|
|
4040
|
-
wake();
|
|
4041
|
-
};
|
|
4159
|
+
/* #506: the Agents-detected pill opens the full-cover connect overlay —
|
|
4160
|
+
the rail drawer (+ button, open/scroll mechanics) it used to target is
|
|
4161
|
+
REMOVED (owner 2026-09-24) */
|
|
4162
|
+
el('ag-cta').onclick=()=>{ openConnectOverlay(); wake(); };
|
|
4042
4163
|
|
|
4043
4164
|
/* ---------- the memory browser — dropdown under the search pill ---------- */
|
|
4044
4165
|
const panel=el('panel');
|
|
@@ -4416,8 +4537,9 @@ async function refreshAfterWrite(){
|
|
|
4416
4537
|
|
|
4417
4538
|
/* =====================================================================
|
|
4418
4539
|
ACCOUNT MENU — harvested from the 0.20.4 console (#365): authoritative
|
|
4419
|
-
token via lazy GET /account/token, connect
|
|
4420
|
-
|
|
4540
|
+
token via lazy GET /account/token, connect overlay entry (#506 — the
|
|
4541
|
+
snippets moved to the full-cover overlay), plan & billing, sign-out
|
|
4542
|
+
(client-side only — dropping the stored tokens IS the sign-out).
|
|
4421
4543
|
===================================================================== */
|
|
4422
4544
|
const $ = el; // the harvested block below uses the $ shorthand
|
|
4423
4545
|
/* Pure decision — what the corner renders for an /account payload (#409 fix
|
|
@@ -4441,6 +4563,58 @@ function acctMenuPlan(a) {
|
|
|
4441
4563
|
avatar: ((typeof a.name === "string" && a.name) ? a.name : "?").slice(0, 2).toUpperCase(),
|
|
4442
4564
|
};
|
|
4443
4565
|
}
|
|
4566
|
+
/* Pure decision (#371) — the purchasable periods. Decided pricing (owner
|
|
4567
|
+
2026-08-19): ONE price, TWO periods — €12/mo and €108/yr. ANNUAL FIRST:
|
|
4568
|
+
annual is the stated product default; its note is the numeric savings
|
|
4569
|
+
sub-line (round-4 addendum 2026-09-24: it replaces the retired
|
|
4570
|
+
words-only annual tagline — same fact, numbers not words; effective
|
|
4571
|
+
monthly 108/12 = €9, discount (144−108)/144 = 25%, derived-pinned by
|
|
4572
|
+
tests so a price change forces the sub-line update). Prices are
|
|
4573
|
+
display constants — the
|
|
4574
|
+
router deliberately knows only Stripe price ids (no server-side amount
|
|
4575
|
+
exists to read), and the Stripe Checkout page shows the authoritative
|
|
4576
|
+
amount before payment. Round 3: a PURE DATA ARRAY and the single source
|
|
4577
|
+
of truth — the overlay cards are GENERATED from it (renderPlanCards)
|
|
4578
|
+
and the wiring loops it (wireSubscription); no two-plan hardcoding
|
|
4579
|
+
anywhere. The bullets are the marketing site's Cloud feature lines,
|
|
4580
|
+
verbatim (lockstep-pinned against web/index.html) — the seven
|
|
4581
|
+
owner-decided lines (amendment 4, 2026-09-24: copy-first ruling; the
|
|
4582
|
+
agent-cap enforcement rides its own issue); both periods are the same
|
|
4583
|
+
Cloud product, so both carry the same seven. */
|
|
4584
|
+
const PLAN_OPTIONS = [
|
|
4585
|
+
{ id: "annual", posts: "annual", name: "Annual", price: "€108/year", note: "€9/mo · 25% off", cta: "Choose annual", bullets: ["Fully managed — no API keys needed", "Claude Code, Hermes, OpenClaw, Pi, OpenCode", "10,000 memories included", "Up to 10 agents", "Learnings & nightly reflection", "Semantic search + knowledge graph", "Multi-machine support"] },
|
|
4586
|
+
{ id: "monthly", posts: "monthly", name: "Monthly", price: "€12/mo", note: "", cta: "Choose monthly", bullets: ["Fully managed — no API keys needed", "Claude Code, Hermes, OpenClaw, Pi, OpenCode", "10,000 memories included", "Up to 10 agents", "Learnings & nightly reflection", "Semantic search + knowledge graph", "Multi-machine support"] },
|
|
4587
|
+
];
|
|
4588
|
+
/* Pure decision (#371) — the Stripe return parameter (?billing=…, the
|
|
4589
|
+
success_url/cancel_url the router stamps on every checkout) → the
|
|
4590
|
+
acknowledgment banner spec, or null when there is nothing to acknowledge
|
|
4591
|
+
(unknown or absent → the page shows nothing, never a guess). */
|
|
4592
|
+
function billingBanner(value) {
|
|
4593
|
+
if (value === "success")
|
|
4594
|
+
return { kind: "success", text: "payment received — activation completes within a minute" };
|
|
4595
|
+
if (value === "cancelled")
|
|
4596
|
+
return { kind: "cancelled", text: "checkout cancelled — nothing was charged" };
|
|
4597
|
+
return null;
|
|
4598
|
+
}
|
|
4599
|
+
/* Pure decision (#371, amendment 2) — the overlay's current-plan line.
|
|
4600
|
+
HONEST by construction: /account provides name/org/plan only, so the
|
|
4601
|
+
line shows the plan label verbatim when set, and when unset the
|
|
4602
|
+
documented meaning of a null label (the same eligibility the Upgrade
|
|
4603
|
+
entry uses) — never invented tiers or numbers. */
|
|
4604
|
+
function acctPlanLine(p) {
|
|
4605
|
+
return p.plan
|
|
4606
|
+
? "Current plan — " + p.plan
|
|
4607
|
+
: "Current plan — none yet (hosted trial, or self-hosted install)";
|
|
4608
|
+
}
|
|
4609
|
+
/* Pure decision (#371 round 3) — the Subscription entry's Upgrade pill.
|
|
4610
|
+
The menu's two billing entries (Upgrade + Plan & billing) merged into
|
|
4611
|
+
ONE always-rendered "Subscription" item; the PILL is what the old
|
|
4612
|
+
plan-unset eligibility gates (owner: "it can have an upgrade pill/label
|
|
4613
|
+
after it before any subscription is active"). Returns the pill markup
|
|
4614
|
+
or "" — interpolated into the entry's label. */
|
|
4615
|
+
function acctPill(p) {
|
|
4616
|
+
return p.upgrade ? '<span class="acct-pill">Upgrade</span>' : "";
|
|
4617
|
+
}
|
|
4444
4618
|
function renderAccount(a) {
|
|
4445
4619
|
const host = $("nav-account");
|
|
4446
4620
|
const p = acctMenuPlan(a);
|
|
@@ -4449,9 +4623,7 @@ function renderAccount(a) {
|
|
|
4449
4623
|
const plan = p.plan
|
|
4450
4624
|
? `<span class="mini" style="font-size:10.5px;border:1px solid var(--line2);border-radius:999px;padding:2px 9px;color:var(--dim)">${esc(p.plan)}</span>`
|
|
4451
4625
|
: "";
|
|
4452
|
-
const
|
|
4453
|
-
? '<button id="acct-upgrade" type="button" class="acct-link acct-upgrade">Upgrade</button>'
|
|
4454
|
-
: "";
|
|
4626
|
+
const pill = acctPill(p); // #371 round 3: the entry always renders; the PILL is eligibility-gated
|
|
4455
4627
|
host.innerHTML = `
|
|
4456
4628
|
<button id="acct-trigger" type="button" class="acct-trigger" aria-haspopup="menu" aria-expanded="false">
|
|
4457
4629
|
<span class="avatar">${esc(p.avatar)}</span>${who?'<span class="who">'+who+'</span> ':''}${plan}<span class="acct-caret" aria-hidden="true">▼</span>
|
|
@@ -4463,55 +4635,182 @@ function renderAccount(a) {
|
|
|
4463
4635
|
<button id="acct-reveal" type="button" class="acct-btn" disabled>Reveal</button>
|
|
4464
4636
|
<button id="acct-copy-token" type="button" class="acct-btn" disabled>Copy</button>
|
|
4465
4637
|
</div>
|
|
4466
|
-
<
|
|
4467
|
-
|
|
4468
|
-
<div class="acct-label">CLI</div>
|
|
4469
|
-
<div class="acct-snippet">
|
|
4470
|
-
<pre id="acct-cli"></pre>
|
|
4471
|
-
<button id="acct-copy-cli" type="button" class="acct-btn" disabled>Copy</button>
|
|
4472
|
-
</div>
|
|
4473
|
-
<div class="acct-label">MCP — ~/.claude.json</div>
|
|
4474
|
-
<div class="acct-snippet">
|
|
4475
|
-
<pre id="acct-mcp"></pre>
|
|
4476
|
-
<button id="acct-copy-mcp" type="button" class="acct-btn" disabled>Copy</button>
|
|
4477
|
-
</div>
|
|
4478
|
-
</details>
|
|
4479
|
-
${upgrade}
|
|
4480
|
-
<button id="acct-plan" type="button" class="acct-link">Plan & billing</button>
|
|
4638
|
+
<button id="acct-connect-entry" type="button" class="acct-link">Connect an agent</button>
|
|
4639
|
+
<button id="acct-subscription" type="button" class="acct-link">Subscription${pill}</button>
|
|
4481
4640
|
<button id="acct-signout" type="button" class="acct-link acct-danger">Sign out</button>
|
|
4482
4641
|
</div>`;
|
|
4483
4642
|
wireAcctMenu();
|
|
4484
|
-
|
|
4643
|
+
renderPlanOverlay(p);
|
|
4644
|
+
wireSubscription();
|
|
4645
|
+
wireConnectEntry();
|
|
4646
|
+
}
|
|
4647
|
+
/* The overlay's content (#371, amendment 2): the honest current-plan line
|
|
4648
|
+
from the pure decision, and the purchase options under the SAME
|
|
4649
|
+
plan-label-unset eligibility as the menu entry (a second checkout for an
|
|
4650
|
+
active subscriber would mint a second Stripe subscription — checkout
|
|
4651
|
+
passes customer_email, not a subscription update; switching is Phase 3).
|
|
4652
|
+
/account failed (p null) → nothing fills (fail-soft, like the empty
|
|
4653
|
+
corner); the overlay itself opens only from the menu entry anyway. */
|
|
4654
|
+
function renderPlanOverlay(p) {
|
|
4655
|
+
if (!p) return;
|
|
4656
|
+
const cur = $("po-current");
|
|
4657
|
+
if (cur) cur.textContent = acctPlanLine(p);
|
|
4658
|
+
const opts = $("acct-options");
|
|
4659
|
+
if (opts) opts.hidden = !p.upgrade;
|
|
4660
|
+
}
|
|
4661
|
+
function openPlanOverlay() {
|
|
4662
|
+
const ov = $("plan-overlay");
|
|
4663
|
+
if (!ov || !ov.hidden) return;
|
|
4664
|
+
ov.hidden = false;
|
|
4665
|
+
const close = $("po-close");
|
|
4666
|
+
if (close) close.focus(); // keyboard users land on the primary exit (the modal focus idiom)
|
|
4667
|
+
}
|
|
4668
|
+
function closePlanOverlay() {
|
|
4669
|
+
const ov = $("plan-overlay");
|
|
4670
|
+
if (ov) ov.hidden = true;
|
|
4671
|
+
}
|
|
4672
|
+
/* #506 — the connect overlay: ONE full-cover surface for every "Connect an
|
|
4673
|
+
agent" entry (the #plan-overlay idiom). Opening lazily fetches the token
|
|
4674
|
+
(idempotent) because the overlay is reachable WITHOUT the account menu
|
|
4675
|
+
(the Agents-detected pill) — the menu path would otherwise be the only
|
|
4676
|
+
thing seeding the snippets. */
|
|
4677
|
+
function openConnectOverlay() {
|
|
4678
|
+
const ov = $("connect-overlay");
|
|
4679
|
+
if (!ov || !ov.hidden) return;
|
|
4680
|
+
ov.hidden = false;
|
|
4681
|
+
fetchAcctToken(); // lazy + idempotent — one attempt per page load
|
|
4682
|
+
const close = $("co-close");
|
|
4683
|
+
if (close) close.focus(); // keyboard users land on the primary exit (the modal focus idiom)
|
|
4485
4684
|
}
|
|
4486
|
-
function
|
|
4487
|
-
const
|
|
4685
|
+
function closeConnectOverlay() {
|
|
4686
|
+
const ov = $("connect-overlay");
|
|
4687
|
+
if (ov) ov.hidden = true;
|
|
4688
|
+
}
|
|
4689
|
+
/* N-driven card render (#371 round 3): the option cards are GENERATED from
|
|
4690
|
+
PLAN_OPTIONS — N entries, N cards, no per-plan literals anywhere in the
|
|
4691
|
+
markup or this template (a future plan is a data entry + a router price
|
|
4692
|
+
id, not a page rework). Runs once at script init; account data only
|
|
4693
|
+
toggles visibility (renderPlanOverlay). */
|
|
4694
|
+
function renderPlanCards() {
|
|
4695
|
+
const host = $("acct-options");
|
|
4696
|
+
if (!host) return;
|
|
4697
|
+
host.innerHTML = PLAN_OPTIONS.map((o) => `
|
|
4698
|
+
<div class="po-opt" id="acct-opt-${o.id}">
|
|
4699
|
+
<div class="po-name">${esc(o.name)}</div>
|
|
4700
|
+
<div class="po-price">${esc(o.price)}</div>
|
|
4701
|
+
${o.note ? `<div class="po-note">${esc(o.note)}</div>` : ""}
|
|
4702
|
+
<ul class="po-feat">${o.bullets.map((b) => `<li>${esc(b)}</li>`).join("")}</ul>
|
|
4703
|
+
<button id="acct-buy-${o.id}" type="button" class="po-choose">${esc(o.cta)}</button>
|
|
4704
|
+
</div>`).join("");
|
|
4705
|
+
}
|
|
4706
|
+
renderPlanCards();
|
|
4707
|
+
function wireSubscription() {
|
|
4708
|
+
const btn = $("acct-subscription");
|
|
4488
4709
|
if (!btn) return;
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4710
|
+
// #371 round 3: the ONE Subscription entry (Upgrade + Plan & billing
|
|
4711
|
+
// merged, owner) opens the FULL-COVER plan & billing overlay; the entry
|
|
4712
|
+
// always renders — the Upgrade pill alone marks the plan-unset state.
|
|
4713
|
+
// Menu closes first; × / cover-click / ESC return.
|
|
4714
|
+
btn.addEventListener("click", () => {
|
|
4715
|
+
closeAcctMenu();
|
|
4716
|
+
openPlanOverlay();
|
|
4717
|
+
});
|
|
4718
|
+
for (const o of PLAN_OPTIONS) {
|
|
4719
|
+
const buy = $("acct-buy-" + o.id);
|
|
4720
|
+
if (!buy) continue;
|
|
4721
|
+
buy.addEventListener("click", () => { startCheckout(o.posts, buy); });
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4724
|
+
/* #506: the menu's Connect-an-agent entry opens the full-cover connect
|
|
4725
|
+
overlay — the same launch idiom as wireSubscription (#371): the menu
|
|
4726
|
+
closes first; × / cover-click / ESC close and return where the user
|
|
4727
|
+
was. The inline expandable snippets this entry used to reveal are gone;
|
|
4728
|
+
the overlay owns connecting. */
|
|
4729
|
+
function wireConnectEntry() {
|
|
4730
|
+
const btn = $("acct-connect-entry");
|
|
4731
|
+
if (!btn) return;
|
|
4732
|
+
btn.addEventListener("click", () => {
|
|
4733
|
+
closeAcctMenu();
|
|
4734
|
+
openConnectOverlay();
|
|
4735
|
+
});
|
|
4736
|
+
}
|
|
4737
|
+
/** POST /stripe/checkout for ONE plan, then navigate to the returned Stripe
|
|
4738
|
+
* URL (#371 — each period's action posts its own plan; the hardcoded
|
|
4739
|
+
* annual-only body was the defect). The failure seams are the shipped
|
|
4740
|
+
* checkout-handler contract, kept verbatim for EVERY action: 401 → Google
|
|
4741
|
+
* re-auth where the probe says OAuth exists, else the expired-session
|
|
4742
|
+
* alert; non-ok → the unavailable alert; network error → the unreachable
|
|
4743
|
+
* alert. Each button is disabled independently while its own request is in
|
|
4744
|
+
* flight. */
|
|
4745
|
+
async function startCheckout(plan, btn) {
|
|
4746
|
+
btn.disabled = true;
|
|
4747
|
+
try {
|
|
4748
|
+
const resp = await fetch("/stripe/checkout", {
|
|
4749
|
+
method: "POST",
|
|
4750
|
+
headers: { "content-type": "application/json" },
|
|
4751
|
+
body: JSON.stringify({ plan }),
|
|
4752
|
+
});
|
|
4753
|
+
if (resp.status === 401) {
|
|
4754
|
+
// Never hard-redirect to Google on a daemon (dead end / loop) — only
|
|
4755
|
+
// where the probe says OAuth exists; else surface the expired session.
|
|
4756
|
+
if (await probeGoogleAuth()) { location.href = "/auth/google/start"; return; }
|
|
4757
|
+
alert("Session expired — reload the page to sign in again.");
|
|
4511
4758
|
btn.disabled = false;
|
|
4759
|
+
return;
|
|
4512
4760
|
}
|
|
4513
|
-
|
|
4761
|
+
const body = await resp.json().catch(() => ({}));
|
|
4762
|
+
if (resp.ok && body.url) { location.href = body.url; return; }
|
|
4763
|
+
alert("Checkout unavailable (" + resp.status + ") — try again later.");
|
|
4764
|
+
btn.disabled = false;
|
|
4765
|
+
} catch {
|
|
4766
|
+
alert("Checkout unreachable — try again later.");
|
|
4767
|
+
btn.disabled = false;
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
/* The return acknowledgment (#371): read ?billing= once on load and show
|
|
4771
|
+
the one-line banner. The param is STRIPPED via history.replaceState the
|
|
4772
|
+
moment the banner shows — neither dismissal nor a plain reload can
|
|
4773
|
+
re-show it (a reload re-reads a URL that no longer carries the param).
|
|
4774
|
+
Runs at script init, before boot: the acknowledgment must not depend on
|
|
4775
|
+
the data fetches succeeding. */
|
|
4776
|
+
function wireBillingBanner() {
|
|
4777
|
+
const host = $("billing-banner");
|
|
4778
|
+
if (!host) return;
|
|
4779
|
+
const params = new URLSearchParams(location.search);
|
|
4780
|
+
const spec = billingBanner(params.get("billing"));
|
|
4781
|
+
if (!spec) return;
|
|
4782
|
+
params.delete("billing");
|
|
4783
|
+
const qs = params.toString();
|
|
4784
|
+
history.replaceState(null, "", location.pathname + (qs ? "?" + qs : "") + location.hash);
|
|
4785
|
+
host.dataset.kind = spec.kind;
|
|
4786
|
+
host.innerHTML =
|
|
4787
|
+
"<span>" + esc(spec.text) + "</span>" +
|
|
4788
|
+
'<button id="billing-banner-dismiss" type="button" aria-label="Dismiss">✕</button>';
|
|
4789
|
+
host.hidden = false;
|
|
4790
|
+
$("billing-banner-dismiss").addEventListener("click", () => { host.remove(); });
|
|
4514
4791
|
}
|
|
4792
|
+
wireBillingBanner();
|
|
4793
|
+
/* the plan & billing overlay's close affordances (the #model-modal wiring
|
|
4794
|
+
idiom): the × button and a click on the opaque cover both return to the
|
|
4795
|
+
dash; ESC lives in the shared keyboard handler below. */
|
|
4796
|
+
$("po-close").addEventListener("click", closePlanOverlay);
|
|
4797
|
+
$("po-cover").addEventListener("click", closePlanOverlay);
|
|
4798
|
+
/* the connect overlay's close + copy affordances (#506) — the same static
|
|
4799
|
+
wiring idiom. The copy buttons are STATIC overlay elements, wired at
|
|
4800
|
+
script init so they work even when /account failed and the menu never
|
|
4801
|
+
rendered; the payloads are the same byte-for-byte commands the old
|
|
4802
|
+
handlers copied. Token-gated exactly like the menu's buttons: a copy
|
|
4803
|
+
without the token would paste a command that cannot authenticate. */
|
|
4804
|
+
$("co-close").addEventListener("click", closeConnectOverlay);
|
|
4805
|
+
$("co-cover").addEventListener("click", closeConnectOverlay);
|
|
4806
|
+
const copyCliBtn = $("acct-copy-cli");
|
|
4807
|
+
copyCliBtn.addEventListener("click", () => {
|
|
4808
|
+
if (acctTokenState === "ok") copyToClipboard("npx @gamaze/hicortex init --server " + location.origin, copyCliBtn);
|
|
4809
|
+
});
|
|
4810
|
+
const copyMcpBtn = $("acct-copy-mcp");
|
|
4811
|
+
copyMcpBtn.addEventListener("click", () => {
|
|
4812
|
+
if (acctTokenState === "ok") copyToClipboard(acctMcpText(), copyMcpBtn);
|
|
4813
|
+
});
|
|
4515
4814
|
let acctToken = null; // token from /account/token (server truth)
|
|
4516
4815
|
let acctTokenState = "unfetched"; // "unfetched" | "ok" | "unavailable"
|
|
4517
4816
|
let acctTokenFetched = false; // one lazy fetch attempt per page load
|
|
@@ -4539,30 +4838,33 @@ function acctMcpText() {
|
|
|
4539
4838
|
}, null, 2);
|
|
4540
4839
|
}
|
|
4541
4840
|
function renderAcctToken() {
|
|
4841
|
+
/* #506: per-element guards instead of the old bail-early `if (!val)
|
|
4842
|
+
return` — the snippet <pre>s + copy buttons now live in the STATIC
|
|
4843
|
+
connect overlay, so token state must render even when /account failed
|
|
4844
|
+
and the menu (val/reveal/copy-token) never rendered. Gating semantics
|
|
4845
|
+
are unchanged: everything disabled unless ok, "token unavailable"
|
|
4846
|
+
text otherwise, "…" while unfetched, revealed text only in the ok
|
|
4847
|
+
path. */
|
|
4848
|
+
const ok = acctTokenState === "ok";
|
|
4542
4849
|
const val = $("acct-token-val");
|
|
4543
|
-
if (!val) return; // menu not rendered (/account failed — the corner stays empty)
|
|
4544
4850
|
const revealBtn = $("acct-reveal");
|
|
4545
4851
|
const copyToken = $("acct-copy-token");
|
|
4546
4852
|
const copyCli = $("acct-copy-cli");
|
|
4547
4853
|
const copyMcp = $("acct-copy-mcp");
|
|
4548
|
-
if (
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
val.textContent = acctRevealed ? acctToken : maskToken(acctToken);
|
|
4563
|
-
revealBtn.textContent = acctRevealed ? "Hide" : "Reveal";
|
|
4564
|
-
$("acct-cli").textContent = acctCliText();
|
|
4565
|
-
$("acct-mcp").textContent = acctMcpText();
|
|
4854
|
+
if (revealBtn) revealBtn.disabled = !ok;
|
|
4855
|
+
if (copyToken) copyToken.disabled = !ok;
|
|
4856
|
+
if (copyCli) copyCli.disabled = !ok;
|
|
4857
|
+
if (copyMcp) copyMcp.disabled = !ok;
|
|
4858
|
+
if (val) {
|
|
4859
|
+
if (!ok) val.textContent = acctTokenState === "unfetched" ? "…" : "token unavailable";
|
|
4860
|
+
else val.textContent = acctRevealed ? acctToken : maskToken(acctToken);
|
|
4861
|
+
}
|
|
4862
|
+
if (revealBtn && ok) revealBtn.textContent = acctRevealed ? "Hide" : "Reveal";
|
|
4863
|
+
const cli = $("acct-cli"); // the connect overlay's snippets (#506) — render regardless of menu presence
|
|
4864
|
+
const mcp = $("acct-mcp");
|
|
4865
|
+
const degraded = acctTokenState === "unfetched" ? "" : "token unavailable";
|
|
4866
|
+
if (cli) cli.textContent = ok ? acctCliText() : degraded;
|
|
4867
|
+
if (mcp) mcp.textContent = ok ? acctMcpText() : degraded;
|
|
4566
4868
|
}
|
|
4567
4869
|
async function fetchAcctToken() {
|
|
4568
4870
|
if (acctTokenFetched) return;
|
|
@@ -4588,8 +4890,6 @@ function closeAcctMenu() {
|
|
|
4588
4890
|
const trigger = document.getElementById("acct-trigger");
|
|
4589
4891
|
if (trigger) trigger.setAttribute("aria-expanded", "false");
|
|
4590
4892
|
acctRevealed = false;
|
|
4591
|
-
const details = document.getElementById("acct-connect");
|
|
4592
|
-
if (details) details.removeAttribute("open");
|
|
4593
4893
|
renderAcctToken();
|
|
4594
4894
|
}
|
|
4595
4895
|
function openAcctMenu() {
|
|
@@ -4652,21 +4952,11 @@ function wireAcctMenu() {
|
|
|
4652
4952
|
copyToken.addEventListener("click", () => {
|
|
4653
4953
|
if (acctTokenState === "ok") copyToClipboard(acctConnectLine(), copyToken);
|
|
4654
4954
|
});
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
});
|
|
4661
|
-
const copyMcp = $("acct-copy-mcp");
|
|
4662
|
-
copyMcp.addEventListener("click", () => {
|
|
4663
|
-
if (acctTokenState === "ok") copyToClipboard(acctMcpText(), copyMcp);
|
|
4664
|
-
});
|
|
4665
|
-
$("acct-plan").addEventListener("click", () => {
|
|
4666
|
-
closeAcctMenu();
|
|
4667
|
-
const t = document.getElementById("plan-billing");
|
|
4668
|
-
if (t) t.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
4669
|
-
});
|
|
4955
|
+
// #506: the copy-cli/copy-mcp wiring moved to the STATIC init block by
|
|
4956
|
+
// the connect overlay — those buttons live in the overlay now, not here.
|
|
4957
|
+
// #371 round 3: the old "Plan & billing" scroll item is gone — its
|
|
4958
|
+
// target tile (#plan-billing) stays in the measures band as the
|
|
4959
|
+
// billing-adjacent surface; the Subscription entry owns the route now.
|
|
4670
4960
|
$("acct-signout").addEventListener("click", signOut);
|
|
4671
4961
|
renderAcctToken();
|
|
4672
4962
|
}
|