forkit-connect 0.1.17 → 0.1.18

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 (2) hide show
  1. package/dist/launcher.js +299 -53
  2. package/package.json +1 -1
package/dist/launcher.js CHANGED
@@ -1250,6 +1250,18 @@ async function buildSettings(service, options) {
1250
1250
  const config = service.getConfig();
1251
1251
  const entitlements = service.getServiceEntitlements();
1252
1252
  const updateCheck = options?.updateChecker ? await options.updateChecker() : await (0, update_1.checkForUpdates)();
1253
+ const effectiveBinding = service.getEffectiveBinding();
1254
+ const approvedUser = effectiveBinding?.approved_user ?? null;
1255
+ const approvedUserRecord = approvedUser;
1256
+ const avatarUrl = safeStringRecordValue(approvedUserRecord, [
1257
+ 'avatarUrl',
1258
+ 'avatar_url',
1259
+ 'picture',
1260
+ 'pictureUrl',
1261
+ 'picture_url',
1262
+ 'photoUrl',
1263
+ 'photo_url',
1264
+ ]);
1253
1265
  const capabilities = [
1254
1266
  { key: 'secure_device_pairing', enabled: entitlements.secure_device_pairing },
1255
1267
  { key: 'signed_events', enabled: entitlements.signed_events },
@@ -1268,6 +1280,10 @@ async function buildSettings(service, options) {
1268
1280
  tier: entitlements.tier,
1269
1281
  workspaceBound: entitlements.workspace_bound,
1270
1282
  projectBound: entitlements.project_bound,
1283
+ displayName: approvedUser?.name ?? null,
1284
+ email: approvedUser?.email ?? null,
1285
+ goaid: approvedUser?.goaid ?? null,
1286
+ avatarUrl,
1271
1287
  },
1272
1288
  preferences: {
1273
1289
  discoveryMode: config.discovery_mode,
@@ -1336,6 +1352,20 @@ function renderLauncherHtml(launcherToken) {
1336
1352
  --surface-strong: rgba(34, 26, 68, 0.88);
1337
1353
  --border: rgba(241, 235, 223, 0.18);
1338
1354
  --glass-shadow: 0 24px 70px rgba(0, 0, 0, 0.34), inset 0 1px 0 rgba(255, 255, 255, 0.08);
1355
+ --launcher-text-strong: #fff8ef;
1356
+ --launcher-text-primary: rgba(241, 235, 223, 0.92);
1357
+ --launcher-text-secondary: rgba(241, 235, 223, 0.8);
1358
+ --launcher-text-muted: rgba(241, 235, 223, 0.68);
1359
+ --launcher-text-faint: rgba(241, 235, 223, 0.55);
1360
+ --launcher-surface-soft: rgba(255, 255, 255, 0.045);
1361
+ --launcher-surface-muted: rgba(255, 255, 255, 0.038);
1362
+ --launcher-surface-strong: rgba(17, 19, 30, 0.985);
1363
+ --launcher-border-soft: rgba(241, 235, 223, 0.1);
1364
+ --launcher-border-strong: rgba(241, 235, 223, 0.16);
1365
+ --launcher-border-accent: rgba(157, 238, 245, 0.18);
1366
+ --launcher-accent-text: rgba(157, 238, 245, 0.92);
1367
+ --launcher-warning-text: rgba(255, 211, 178, 0.9);
1368
+ --launcher-shadow-strong: 0 28px 56px rgba(7, 10, 24, 0.42);
1339
1369
  --traffic-intensity: 32;
1340
1370
  --traffic-speed: 12s;
1341
1371
  }
@@ -1876,17 +1906,17 @@ function renderLauncherHtml(launcherToken) {
1876
1906
 
1877
1907
  .quick-review-card {
1878
1908
  display: grid;
1879
- gap: 14px;
1909
+ gap: 12px;
1880
1910
  padding: 18px;
1881
1911
  border-radius: 26px;
1882
- border: 1px solid rgba(241, 235, 223, 0.16);
1912
+ border: 1px solid var(--launcher-border-strong);
1883
1913
  background:
1884
1914
  radial-gradient(circle at 100% 0%, rgba(106, 167, 171, 0.18), transparent 38%),
1885
1915
  linear-gradient(135deg, rgba(24, 22, 38, 0.98) 0%, rgba(17, 19, 30, 0.985) 100%);
1886
1916
  box-shadow:
1887
- 0 28px 56px rgba(7, 10, 24, 0.42),
1917
+ var(--launcher-shadow-strong),
1888
1918
  inset 0 1px 0 rgba(255,255,255,0.07);
1889
- color: #f7efe4;
1919
+ color: var(--launcher-text-primary);
1890
1920
  }
1891
1921
 
1892
1922
  .review-summary-grid {
@@ -1897,11 +1927,11 @@ function renderLauncherHtml(launcherToken) {
1897
1927
 
1898
1928
  .review-summary-pill {
1899
1929
  display: grid;
1900
- gap: 4px;
1901
- padding: 11px 12px;
1930
+ gap: 3px;
1931
+ padding: 10px 12px;
1902
1932
  border-radius: 16px;
1903
- border: 1px solid rgba(241, 235, 223, 0.1);
1904
- background: rgba(255,255,255,0.045);
1933
+ border: 1px solid var(--launcher-border-soft);
1934
+ background: var(--launcher-surface-soft);
1905
1935
  }
1906
1936
 
1907
1937
  .review-summary-label {
@@ -1909,13 +1939,13 @@ function renderLauncherHtml(launcherToken) {
1909
1939
  text-transform: uppercase;
1910
1940
  letter-spacing: 0.12em;
1911
1941
  font-weight: 700;
1912
- color: rgba(241, 235, 223, 0.55);
1942
+ color: var(--launcher-text-faint);
1913
1943
  }
1914
1944
 
1915
1945
  .review-summary-value {
1916
1946
  font-size: 0.86rem;
1917
1947
  line-height: 1.35;
1918
- color: #fff7ea;
1948
+ color: var(--launcher-text-strong);
1919
1949
  font-weight: 600;
1920
1950
  }
1921
1951
 
@@ -1924,7 +1954,7 @@ function renderLauncherHtml(launcherToken) {
1924
1954
  gap: 6px;
1925
1955
  padding: 14px 15px;
1926
1956
  border-radius: 18px;
1927
- border: 1px solid rgba(157, 238, 245, 0.14);
1957
+ border: 1px solid var(--launcher-border-accent);
1928
1958
  background:
1929
1959
  linear-gradient(180deg, rgba(39, 51, 69, 0.52), rgba(22, 18, 47, 0.72)),
1930
1960
  radial-gradient(circle at top right, rgba(244, 147, 85, 0.12), transparent 52%);
@@ -1937,7 +1967,7 @@ function renderLauncherHtml(launcherToken) {
1937
1967
  text-transform: uppercase;
1938
1968
  letter-spacing: 0.13em;
1939
1969
  font-weight: 700;
1940
- color: rgba(157, 238, 245, 0.82);
1970
+ color: var(--launcher-accent-text);
1941
1971
  }
1942
1972
 
1943
1973
  .review-recommendation-title {
@@ -1945,7 +1975,7 @@ function renderLauncherHtml(launcherToken) {
1945
1975
  font-family: "Sora", sans-serif;
1946
1976
  font-size: 1rem;
1947
1977
  line-height: 1.2;
1948
- color: #fff8ef;
1978
+ color: var(--launcher-text-strong);
1949
1979
  font-weight: 700;
1950
1980
  }
1951
1981
 
@@ -1953,7 +1983,7 @@ function renderLauncherHtml(launcherToken) {
1953
1983
  margin: 0;
1954
1984
  font-size: 0.86rem;
1955
1985
  line-height: 1.5;
1956
- color: rgba(241, 235, 223, 0.74);
1986
+ color: var(--launcher-text-secondary);
1957
1987
  }
1958
1988
 
1959
1989
  .quick-review-topline {
@@ -1969,7 +1999,7 @@ function renderLauncherHtml(launcherToken) {
1969
1999
  text-transform: uppercase;
1970
2000
  letter-spacing: 0.12em;
1971
2001
  font-weight: 700;
1972
- color: rgba(157, 238, 245, 0.84);
2002
+ color: var(--launcher-accent-text);
1973
2003
  }
1974
2004
 
1975
2005
  .quick-review-title {
@@ -1978,7 +2008,7 @@ function renderLauncherHtml(launcherToken) {
1978
2008
  font-size: 1.22rem;
1979
2009
  line-height: 1.12;
1980
2010
  letter-spacing: -0.02em;
1981
- color: #fff8ef;
2011
+ color: var(--launcher-text-strong);
1982
2012
  }
1983
2013
 
1984
2014
  .quick-review-badge {
@@ -1991,7 +2021,7 @@ function renderLauncherHtml(launcherToken) {
1991
2021
  border-radius: 999px;
1992
2022
  border: 1px solid rgba(157, 238, 245, 0.26);
1993
2023
  background: rgba(55, 68, 86, 0.42);
1994
- color: rgba(157, 238, 245, 0.96);
2024
+ color: var(--launcher-accent-text);
1995
2025
  font-family: "Sora", sans-serif;
1996
2026
  font-size: 0.88rem;
1997
2027
  font-weight: 700;
@@ -1999,21 +2029,32 @@ function renderLauncherHtml(launcherToken) {
1999
2029
  text-transform: uppercase;
2000
2030
  }
2001
2031
 
2002
- .quick-review-meta,
2032
+ .quick-review-meta {
2033
+ margin: 0;
2034
+ font-size: 0.8rem;
2035
+ line-height: 1.35;
2036
+ color: var(--launcher-text-muted);
2037
+ letter-spacing: 0.01em;
2038
+ }
2039
+
2003
2040
  .quick-review-detail {
2004
2041
  margin: 0;
2005
- font-size: 0.92rem;
2006
- line-height: 1.5;
2007
- color: rgba(241, 235, 223, 0.78);
2042
+ font-size: 0.86rem;
2043
+ line-height: 1.45;
2044
+ color: var(--launcher-text-secondary);
2045
+ }
2046
+
2047
+ .quick-review-detail[hidden] {
2048
+ display: none;
2008
2049
  }
2009
2050
 
2010
2051
  .quick-review-status {
2011
2052
  border-radius: 16px;
2012
- padding: 12px 14px;
2053
+ padding: 10px 13px;
2013
2054
  border: 1px solid rgba(202, 188, 255, 0.12);
2014
- background: rgba(255,255,255,0.04);
2015
- color: rgba(241, 235, 223, 0.8);
2016
- font-size: 0.88rem;
2055
+ background: var(--launcher-surface-muted);
2056
+ color: var(--launcher-text-secondary);
2057
+ font-size: 0.84rem;
2017
2058
  line-height: 1.45;
2018
2059
  }
2019
2060
 
@@ -2144,7 +2185,7 @@ function renderLauncherHtml(launcherToken) {
2144
2185
  padding: 12px 13px;
2145
2186
  border-radius: 16px;
2146
2187
  border: 1px solid rgba(244, 147, 85, 0.16);
2147
- background: rgba(255,255,255,0.038);
2188
+ background: var(--launcher-surface-muted);
2148
2189
  }
2149
2190
 
2150
2191
  .review-resolution-shell[hidden] {
@@ -2157,14 +2198,14 @@ function renderLauncherHtml(launcherToken) {
2157
2198
  text-transform: uppercase;
2158
2199
  letter-spacing: 0.12em;
2159
2200
  font-weight: 700;
2160
- color: rgba(255, 211, 178, 0.82);
2201
+ color: var(--launcher-warning-text);
2161
2202
  }
2162
2203
 
2163
2204
  .review-resolution-copy {
2164
2205
  margin: 0;
2165
2206
  font-size: 0.84rem;
2166
2207
  line-height: 1.45;
2167
- color: rgba(241, 235, 223, 0.72);
2208
+ color: var(--launcher-text-secondary);
2168
2209
  }
2169
2210
 
2170
2211
  .review-resolution-actions[hidden] {
@@ -2554,7 +2595,7 @@ function renderLauncherHtml(launcherToken) {
2554
2595
  font-family: "Sora", sans-serif;
2555
2596
  font-size: 0.96rem;
2556
2597
  line-height: 1.2;
2557
- color: #fff8ef;
2598
+ color: var(--launcher-text-strong);
2558
2599
  overflow: hidden;
2559
2600
  text-overflow: ellipsis;
2560
2601
  white-space: nowrap;
@@ -2564,19 +2605,50 @@ function renderLauncherHtml(launcherToken) {
2564
2605
  display: block;
2565
2606
  margin-top: 3px;
2566
2607
  font-size: 0.78rem;
2567
- color: rgba(241,235,223,0.66);
2608
+ color: var(--launcher-text-muted);
2568
2609
  overflow: hidden;
2569
2610
  text-overflow: ellipsis;
2570
2611
  white-space: nowrap;
2571
2612
  }
2572
2613
 
2614
+ .launch-account-menu-summary {
2615
+ display: grid;
2616
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2617
+ gap: 8px;
2618
+ padding: 0 2px 6px;
2619
+ }
2620
+
2621
+ .launch-account-menu-metric {
2622
+ display: grid;
2623
+ gap: 4px;
2624
+ padding: 10px 11px;
2625
+ border-radius: 14px;
2626
+ border: 1px solid var(--launcher-border-soft);
2627
+ background: var(--launcher-surface-soft);
2628
+ }
2629
+
2630
+ .launch-account-menu-metric-label {
2631
+ font-size: 0.66rem;
2632
+ text-transform: uppercase;
2633
+ letter-spacing: 0.12em;
2634
+ font-weight: 700;
2635
+ color: var(--launcher-text-faint);
2636
+ }
2637
+
2638
+ .launch-account-menu-metric-value {
2639
+ font-size: 0.84rem;
2640
+ line-height: 1.35;
2641
+ color: var(--launcher-text-strong);
2642
+ font-weight: 600;
2643
+ }
2644
+
2573
2645
  .launch-account-action {
2574
2646
  appearance: none;
2575
2647
  width: 100%;
2576
2648
  border: 1px solid transparent;
2577
2649
  border-radius: 12px;
2578
- background: rgba(255,255,255,0.035);
2579
- color: #f1ebdf;
2650
+ background: var(--launcher-surface-muted);
2651
+ color: var(--launcher-text-primary);
2580
2652
  text-align: left;
2581
2653
  padding: 11px 12px;
2582
2654
  font-size: 0.9rem;
@@ -3994,6 +4066,23 @@ function renderLauncherHtml(launcherToken) {
3994
4066
  .chart svg { width: 100%; height: 100%; display: block; }
3995
4067
 
3996
4068
  .status-line { margin: 6px 0; font-size: 0.92rem; color: rgba(42, 31, 85, 0.9); }
4069
+
4070
+ .discovery-side-meta {
4071
+ display: inline-flex;
4072
+ align-items: center;
4073
+ gap: 8px;
4074
+ width: fit-content;
4075
+ min-height: 34px;
4076
+ padding: 0 12px;
4077
+ border-radius: 999px;
4078
+ border: 1px solid var(--launcher-border-soft);
4079
+ background: rgba(18, 15, 40, 0.72);
4080
+ color: var(--launcher-text-primary);
4081
+ font-size: 0.8rem;
4082
+ font-weight: 600;
4083
+ letter-spacing: 0.01em;
4084
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
4085
+ }
3997
4086
  .ok { color: #0b7f46; }
3998
4087
  .warn { color: #a85b00; }
3999
4088
  .error { color: #a11522; }
@@ -6552,8 +6641,8 @@ function renderLauncherHtml(launcherToken) {
6552
6641
  </div>
6553
6642
  <span class="quick-review-badge" id="quick-review-badge">Review</span>
6554
6643
  </div>
6555
- <p class="quick-review-meta" id="quick-review-meta">Open Connect to approve or defer this item.</p>
6556
- <p class="quick-review-detail" id="quick-review-detail">Nothing is registered automatically.</p>
6644
+ <p class="quick-review-meta" id="quick-review-meta">Model · Local</p>
6645
+ <p class="quick-review-detail" id="quick-review-detail" hidden>Nothing is registered automatically.</p>
6557
6646
  <div class="review-summary-grid">
6558
6647
  <div class="review-summary-pill">
6559
6648
  <span class="review-summary-label">Action</span>
@@ -6632,9 +6721,27 @@ function renderLauncherHtml(launcherToken) {
6632
6721
  <span class="launch-account-menu-subtitle" id="account-menu-subtitle">Account scope</span>
6633
6722
  </span>
6634
6723
  </div>
6724
+ <div class="launch-account-menu-summary">
6725
+ <div class="launch-account-menu-metric">
6726
+ <span class="launch-account-menu-metric-label">Plan</span>
6727
+ <span class="launch-account-menu-metric-value" id="account-menu-tier">Solo</span>
6728
+ </div>
6729
+ <div class="launch-account-menu-metric">
6730
+ <span class="launch-account-menu-metric-label">Sync</span>
6731
+ <span class="launch-account-menu-metric-value" id="account-menu-sync">Not signed in</span>
6732
+ </div>
6733
+ </div>
6734
+ <button class="launch-account-action" id="account-open-profile" type="button">
6735
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><circle cx="12" cy="8" r="3.5" stroke="currentColor" stroke-width="1.7"/><path d="M5 20c0-4 3.2-6.5 7-6.5s7 2.5 7 6.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>
6736
+ Open Profile
6737
+ </button>
6738
+ <button class="launch-account-action" id="account-open-security" type="button">
6739
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M12 3l7 3v5c0 4-3.5 7-7 8-3.5-1-7-4-7-8V6l7-3Z" stroke="currentColor" stroke-width="1.7"/><path d="M9.5 11.5 11 13l3.5-3.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
6740
+ Account Security
6741
+ </button>
6635
6742
  <button class="launch-account-action" id="account-open-settings" type="button">
6636
6743
  <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7Z" stroke="currentColor" stroke-width="1.7"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.05.05a2 2 0 1 1-2.83 2.83l-.05-.05A1.7 1.7 0 0 0 15 19.4a1.7 1.7 0 0 0-1 .6l-.03.03a2 2 0 0 1-3.42-1.42v-.07A1.7 1.7 0 0 0 9.5 17a1.7 1.7 0 0 0-1.88.34l-.05.05a2 2 0 1 1-2.83-2.83l.05-.05A1.7 1.7 0 0 0 5.1 12a1.7 1.7 0 0 0-.6-1l-.03-.03a2 2 0 0 1 1.42-3.42h.07A1.7 1.7 0 0 0 7 6.5a1.7 1.7 0 0 0-.34-1.88l-.05-.05a2 2 0 1 1 2.83-2.83l.05.05A1.7 1.7 0 0 0 12 4.9a1.7 1.7 0 0 0 1-.6l.03-.03a2 2 0 0 1 3.42 1.42v.07A1.7 1.7 0 0 0 17 7a1.7 1.7 0 0 0 1.88-.34l.05-.05a2 2 0 1 1 2.83 2.83l-.05.05A1.7 1.7 0 0 0 19.4 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
6637
- Open Settings
6744
+ Launcher Settings
6638
6745
  </button>
6639
6746
  <button class="launch-account-action" id="account-switch-account" type="button">
6640
6747
  <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M16 3h3a2 2 0 0 1 2 2v3M21 16v3a2 2 0 0 1-2 2h-3M8 21H5a2 2 0 0 1-2-2v-3M3 8V5a2 2 0 0 1 2-2h3" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/><path d="M8 12h8M13 9l3 3-3 3" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
@@ -6957,7 +7064,7 @@ function renderLauncherHtml(launcherToken) {
6957
7064
  </svg>
6958
7065
  </div>
6959
7066
 
6960
- <div class="status-line" id="discovery-last-scan">Last scan: -</div>
7067
+ <div class="discovery-side-meta" id="discovery-last-scan">Last scan · unavailable</div>
6961
7068
  <h3 class="discovery-side-title">Inbox review</h3>
6962
7069
  <p class="discovery-side-copy">Select an inbox item and continue the exact next step Forkit Connect recommends.</p>
6963
7070
 
@@ -7824,6 +7931,41 @@ function renderLauncherHtml(launcherToken) {
7824
7931
  return item.detailSummary || item.statusMeta || 'Review local metadata, then decide what happens next.';
7825
7932
  }
7826
7933
 
7934
+ function getQuickReviewDetailText(item) {
7935
+ if (!item) return '';
7936
+ if (item.kind === 'runtime') {
7937
+ return item.statusTone === 'error'
7938
+ ? 'Resolve runtime attention before linked registrations continue.'
7939
+ : 'Open runtime review to confirm health and connected models.';
7940
+ }
7941
+ if (item.statusLabel === 'Registration in progress') {
7942
+ return 'Continue the existing private review instead of creating a second record.';
7943
+ }
7944
+ if (item.statusLabel === 'Finish privately first') {
7945
+ return 'One private review step is still required before publishing.';
7946
+ }
7947
+ if (item.matchedPassportGaid && !item.passportGaid) {
7948
+ return 'A matching passport already exists. Reuse it first.';
7949
+ }
7950
+ if (item.passportGaid) {
7951
+ return 'This item is already linked. Open the passport to inspect it.';
7952
+ }
7953
+ if (item.actionLabel === 'Retry') {
7954
+ return 'Registration failed earlier. Retry when the scope is ready.';
7955
+ }
7956
+ return '';
7957
+ }
7958
+
7959
+ function getQuickReviewStatusText(item) {
7960
+ if (!item) return 'Waiting for review.';
7961
+ const meta = typeof item.statusMeta === 'string' ? item.statusMeta.trim() : '';
7962
+ if (!meta) return item.statusLabel;
7963
+ if (item.kind === 'runtime' || item.statusTone === 'error' || item.statusLabel === 'Registration in progress' || item.statusLabel === 'Finish privately first') {
7964
+ return item.statusLabel + ' · ' + meta;
7965
+ }
7966
+ return item.statusLabel;
7967
+ }
7968
+
7827
7969
  function formatLauncherActionFeedback(result, fallbackMessage) {
7828
7970
  const base = result && typeof result.message === 'string' && result.message.trim()
7829
7971
  ? result.message.trim()
@@ -8099,9 +8241,9 @@ function renderLauncherHtml(launcherToken) {
8099
8241
  setText('quick-review-kicker', item.typeLabel || item.kind);
8100
8242
  setText('quick-review-title', item.name);
8101
8243
  setText('quick-review-badge', item.kind === 'runtime' ? 'Runtime' : item.kind === 'agent' ? 'Agent' : 'Model');
8102
- setText('quick-review-meta', item.subtitle + ' · ' + item.source);
8103
- setText('quick-review-detail', getReviewDetailText(item));
8104
- setQuickReviewStatus(item.statusLabel + (item.statusMeta ? ' · ' + item.statusMeta : ''), item.statusTone === 'muted' ? '' : item.statusTone);
8244
+ setText('quick-review-meta', [item.typeLabel || item.kind, item.source].filter(Boolean).join(' · '));
8245
+ setOptionalDetail('quick-review-detail', getQuickReviewDetailText(item));
8246
+ setQuickReviewStatus(getQuickReviewStatusText(item), item.statusTone === 'muted' ? '' : item.statusTone);
8105
8247
  resetReviewResolutionActions('quick-review');
8106
8248
 
8107
8249
  const primaryLabel = getReviewPrimaryLabel(item, 'solo');
@@ -8645,6 +8787,42 @@ function renderLauncherHtml(launcherToken) {
8645
8787
  return normalized.slice(0, 2).toUpperCase();
8646
8788
  }
8647
8789
 
8790
+ function formatTierLabel(value) {
8791
+ const normalized = String(value || '').trim();
8792
+ if (!normalized) return 'Solo';
8793
+ return normalized.split(/[_-]+/).filter(Boolean).map((chunk) => chunk.charAt(0).toUpperCase() + chunk.slice(1)).join(' ');
8794
+ }
8795
+
8796
+ function summarizeAccountSync(summary) {
8797
+ if (!summary || !summary.authenticated) {
8798
+ return 'Sign in required';
8799
+ }
8800
+ if (summary.workspaceId && summary.projectId) {
8801
+ return 'Governed scope ready';
8802
+ }
8803
+ if (summary.workspaceId) {
8804
+ return 'Workspace selected';
8805
+ }
8806
+ return 'Solo ready';
8807
+ }
8808
+
8809
+ function formatSettingsAccountLabel(profile) {
8810
+ if (!profile || !profile.authenticated) {
8811
+ return 'Not signed in';
8812
+ }
8813
+ const primary = String(profile.displayName || profile.email || 'Connected account').trim();
8814
+ const extras = [profile.email, formatTierLabel(profile.tier)].filter((value, index, array) => value && value !== primary && array.indexOf(value) === index);
8815
+ return [primary, ...extras].filter(Boolean).join(' · ');
8816
+ }
8817
+
8818
+ function setOptionalDetail(id, message) {
8819
+ const element = document.getElementById(id);
8820
+ if (!element) return;
8821
+ const normalized = typeof message === 'string' ? message.trim() : '';
8822
+ element.textContent = normalized;
8823
+ element.hidden = normalized.length === 0;
8824
+ }
8825
+
8648
8826
  function getGreetingLabel() {
8649
8827
  const hour = new Date().getHours();
8650
8828
  if (hour < 12) return 'Good morning';
@@ -9088,6 +9266,8 @@ function renderLauncherHtml(launcherToken) {
9088
9266
  setText('header-account-meta', accountMeta);
9089
9267
  setText('account-menu-title', accountTitle);
9090
9268
  setText('account-menu-subtitle', accountMeta);
9269
+ setText('account-menu-tier', formatTierLabel(summary.tier));
9270
+ setText('account-menu-sync', summarizeAccountSync(summary));
9091
9271
 
9092
9272
  const bindingBits = [
9093
9273
  'State: ' + (summary.bindingState || 'none'),
@@ -9240,6 +9420,38 @@ function renderLauncherHtml(launcherToken) {
9240
9420
  await refreshAll();
9241
9421
  }
9242
9422
 
9423
+ async function handleAccountSessionExit(startLogin) {
9424
+ if (currentPollTimer) {
9425
+ clearInterval(currentPollTimer);
9426
+ currentPollTimer = null;
9427
+ }
9428
+ currentDeviceCode = null;
9429
+ const accountDropdownBtn = document.getElementById('user-chip-btn');
9430
+ const accountDropdownMenu = document.getElementById('account-dropdown-menu');
9431
+ if (accountDropdownMenu) {
9432
+ accountDropdownMenu.hidden = true;
9433
+ }
9434
+ if (accountDropdownBtn) {
9435
+ accountDropdownBtn.setAttribute('aria-expanded', 'false');
9436
+ }
9437
+ const result = await postAction('/api/session/logout', startLogin ? 'Switching account…' : 'Signing out…');
9438
+ if (!result.ok) {
9439
+ setActivityMessage(result.message || 'Session update failed.', 'warn');
9440
+ return;
9441
+ }
9442
+ if (startLogin) {
9443
+ setView('login');
9444
+ setLoginStatus(result.message || 'Signed out. Continue with another account.', 'ok');
9445
+ await refreshAll();
9446
+ handleLogin();
9447
+ return;
9448
+ }
9449
+ setActivityMessage(result.message || 'Signed out.', 'ok');
9450
+ setView('signed-out');
9451
+ setLoginStatus('Sign in required to continue.', 'warn');
9452
+ await refreshAll();
9453
+ }
9454
+
9243
9455
  function setScopeStatus(message, tone) {
9244
9456
  const status = document.getElementById('scope-modal-status');
9245
9457
  if (!status) return;
@@ -9781,7 +9993,12 @@ function renderLauncherHtml(launcherToken) {
9781
9993
  setText('count-agents', String(discovery.counts.agents));
9782
9994
  setText('count-runtimes', String(discovery.counts.runtimes));
9783
9995
  setText('discovery-page-count', String(discovery.counts.all));
9784
- setText('discovery-last-scan', discovery.lastScanAt ? ('Last scan: ' + discovery.lastScanAt) : 'Last scan: not available');
9996
+ setText(
9997
+ 'discovery-last-scan',
9998
+ discovery.lastScanAt
9999
+ ? ('Last scan · ' + formatRelativeTime(discovery.lastScanAt) + ' · ' + formatTimestamp(discovery.lastScanAt))
10000
+ : 'Last scan · not available',
10001
+ );
9785
10002
  setText('disc-check-local', discovery.checks.localDetection, discovery.checks.localDetection === 'active' ? 'ok' : 'warn');
9786
10003
  setText('disc-check-meta', discovery.checks.metadataExtraction, discovery.checks.metadataExtraction === 'active' ? 'ok' : 'warn');
9787
10004
  setText('disc-check-dup', discovery.checks.duplicateMatching, discovery.checks.duplicateMatching === 'active' ? 'ok' : 'warn');
@@ -10171,18 +10388,23 @@ function renderLauncherHtml(launcherToken) {
10171
10388
  if (!data) return;
10172
10389
 
10173
10390
  // Account & Sync section
10174
- const email = data.profile.authenticated ? (data.system.runtimeId || 'Authenticated') : 'Not signed in';
10175
- setText('settings-signed-in-email', email);
10391
+ setText('settings-signed-in-email', formatSettingsAccountLabel(data.profile));
10176
10392
  const syncBadge = document.getElementById('settings-sync-badge');
10177
10393
  if (syncBadge) {
10178
- syncBadge.textContent = data.profile.workspaceBound ? 'Synced' : 'Not synced';
10179
- syncBadge.className = 'settings-sync-badge' + (data.profile.workspaceBound ? '' : ' warn');
10180
- }
10181
- const lastHeartbeatLine = data.preferences.updatedAt ? 'Last sync: ' + data.preferences.updatedAt : 'Not yet synced';
10394
+ syncBadge.textContent = !data.profile.authenticated
10395
+ ? 'Sign in required'
10396
+ : data.profile.workspaceBound
10397
+ ? 'Governed ready'
10398
+ : 'Solo ready';
10399
+ syncBadge.className = 'settings-sync-badge' + (data.profile.authenticated ? '' : ' warn');
10400
+ }
10401
+ const lastHeartbeatLine = data.preferences.updatedAt ? 'Last sync: ' + formatTimestamp(data.preferences.updatedAt) : 'Not yet synced';
10182
10402
  setText('settings-last-sync', lastHeartbeatLine);
10183
- const syncDesc = data.profile.workspaceBound
10184
- ? 'All registrations are up to date.'
10185
- : 'Workspace not yet bound. Sign in and select the target scope to continue sync.';
10403
+ const syncDesc = !data.profile.authenticated
10404
+ ? 'Sign in to sync registrations, review items, and runtime health across devices.'
10405
+ : data.profile.workspaceBound
10406
+ ? 'Account, workspace, and project are synced and ready for governed registration.'
10407
+ : 'Your account is synced. Solo registration can continue now, and governed sync starts when you choose a workspace/project.';
10186
10408
  setText('settings-sync-desc', syncDesc);
10187
10409
 
10188
10410
  // Notifications toggles — reflect real preferences
@@ -10623,6 +10845,20 @@ function renderLauncherHtml(launcherToken) {
10623
10845
  }
10624
10846
 
10625
10847
  const accountOpenSettings = document.getElementById('account-open-settings');
10848
+ const accountOpenProfile = document.getElementById('account-open-profile');
10849
+ const accountOpenSecurity = document.getElementById('account-open-security');
10850
+ if (accountOpenProfile) {
10851
+ accountOpenProfile.addEventListener('click', () => {
10852
+ window.open('${FORKIT_DEV_URL}/profile?tab=profile', '_blank', 'noopener,noreferrer');
10853
+ if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10854
+ });
10855
+ }
10856
+ if (accountOpenSecurity) {
10857
+ accountOpenSecurity.addEventListener('click', () => {
10858
+ window.open('${FORKIT_DEV_URL}/profile?tab=security', '_blank', 'noopener,noreferrer');
10859
+ if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10860
+ });
10861
+ }
10626
10862
  if (accountOpenSettings) {
10627
10863
  accountOpenSettings.addEventListener('click', () => {
10628
10864
  setView('settings');
@@ -10633,18 +10869,16 @@ function renderLauncherHtml(launcherToken) {
10633
10869
  const accountSwitch = document.getElementById('account-switch-account');
10634
10870
  if (accountSwitch) {
10635
10871
  accountSwitch.addEventListener('click', () => {
10636
- setView('login');
10637
- handleLogin();
10638
10872
  if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10873
+ void handleAccountSessionExit(true);
10639
10874
  });
10640
10875
  }
10641
10876
 
10642
10877
  const accountSignOut = document.getElementById('account-sign-out');
10643
10878
  if (accountSignOut) {
10644
10879
  accountSignOut.addEventListener('click', () => {
10645
- setView('signed-out');
10646
- setLoginStatus('Sign in required to continue.', 'warn');
10647
10880
  if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10881
+ void handleAccountSessionExit(false);
10648
10882
  });
10649
10883
  }
10650
10884
 
@@ -11474,6 +11708,18 @@ function createLauncherApp(options) {
11474
11708
  sendInternalError(response, error, 'launcher_settings_failed');
11475
11709
  }
11476
11710
  });
11711
+ app.post('/api/session/logout', (_request, response) => {
11712
+ try {
11713
+ options.service.setSessionRef(null);
11714
+ response.json({
11715
+ ok: true,
11716
+ message: 'Signed out. Local discovery stays available on this device.',
11717
+ });
11718
+ }
11719
+ catch (error) {
11720
+ sendInternalError(response, error, 'launcher_logout_failed');
11721
+ }
11722
+ });
11477
11723
  app.post('/api/scan', async (_request, response) => {
11478
11724
  try {
11479
11725
  const result = await options.service.scanRuntime();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forkit-connect",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Forkit Connect Local Engine - The Global AI Governance Fabric",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",