forkit-connect 0.1.17 → 0.1.19

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 +386 -79
  2. package/package.json +1 -1
package/dist/launcher.js CHANGED
@@ -695,14 +695,14 @@ function buildDiscovery(service) {
695
695
  }
696
696
  else if (draftPending) {
697
697
  statusLabel = 'Registration in progress';
698
- statusMeta = 'Forkit already started this privately. Continue it in Passports instead of starting over.';
698
+ statusMeta = 'Continue the existing private review before starting anything new.';
699
699
  statusTone = 'warn';
700
700
  actionLabel = 'Continue';
701
701
  actionTone = 'accent';
702
702
  }
703
703
  else if (registrationState === 'private_draft_required') {
704
704
  statusLabel = 'Finish privately first';
705
- statusMeta = registrationGuidance ?? 'Forkit needs one private review step before this passport can finish publishing.';
705
+ statusMeta = registrationGuidance ?? 'Complete one private review step before publishing.';
706
706
  statusTone = 'warn';
707
707
  actionLabel = 'Continue';
708
708
  actionTone = 'accent';
@@ -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,23 @@ 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);
1369
+ --launcher-inbox-row-columns: 28px minmax(220px, 2.35fr) minmax(92px, 0.72fr) minmax(116px, 0.84fr) minmax(0, 1.08fr) 154px;
1370
+ --launcher-inbox-row-columns-compact: 28px minmax(0, 1.85fr) minmax(84px, 0.68fr) minmax(108px, 0.82fr) minmax(0, 0.98fr) 146px;
1371
+ --launcher-inbox-action-width: 154px;
1339
1372
  --traffic-intensity: 32;
1340
1373
  --traffic-speed: 12s;
1341
1374
  }
@@ -1876,17 +1909,17 @@ function renderLauncherHtml(launcherToken) {
1876
1909
 
1877
1910
  .quick-review-card {
1878
1911
  display: grid;
1879
- gap: 14px;
1912
+ gap: 10px;
1880
1913
  padding: 18px;
1881
1914
  border-radius: 26px;
1882
- border: 1px solid rgba(241, 235, 223, 0.16);
1915
+ border: 1px solid var(--launcher-border-strong);
1883
1916
  background:
1884
1917
  radial-gradient(circle at 100% 0%, rgba(106, 167, 171, 0.18), transparent 38%),
1885
1918
  linear-gradient(135deg, rgba(24, 22, 38, 0.98) 0%, rgba(17, 19, 30, 0.985) 100%);
1886
1919
  box-shadow:
1887
- 0 28px 56px rgba(7, 10, 24, 0.42),
1920
+ var(--launcher-shadow-strong),
1888
1921
  inset 0 1px 0 rgba(255,255,255,0.07);
1889
- color: #f7efe4;
1922
+ color: var(--launcher-text-primary);
1890
1923
  }
1891
1924
 
1892
1925
  .review-summary-grid {
@@ -1895,13 +1928,17 @@ function renderLauncherHtml(launcherToken) {
1895
1928
  gap: 10px;
1896
1929
  }
1897
1930
 
1931
+ .quick-review-summary-grid {
1932
+ display: none;
1933
+ }
1934
+
1898
1935
  .review-summary-pill {
1899
1936
  display: grid;
1900
- gap: 4px;
1901
- padding: 11px 12px;
1937
+ gap: 3px;
1938
+ padding: 10px 12px;
1902
1939
  border-radius: 16px;
1903
- border: 1px solid rgba(241, 235, 223, 0.1);
1904
- background: rgba(255,255,255,0.045);
1940
+ border: 1px solid var(--launcher-border-soft);
1941
+ background: var(--launcher-surface-soft);
1905
1942
  }
1906
1943
 
1907
1944
  .review-summary-label {
@@ -1909,13 +1946,13 @@ function renderLauncherHtml(launcherToken) {
1909
1946
  text-transform: uppercase;
1910
1947
  letter-spacing: 0.12em;
1911
1948
  font-weight: 700;
1912
- color: rgba(241, 235, 223, 0.55);
1949
+ color: var(--launcher-text-faint);
1913
1950
  }
1914
1951
 
1915
1952
  .review-summary-value {
1916
1953
  font-size: 0.86rem;
1917
1954
  line-height: 1.35;
1918
- color: #fff7ea;
1955
+ color: var(--launcher-text-strong);
1919
1956
  font-weight: 600;
1920
1957
  }
1921
1958
 
@@ -1924,7 +1961,7 @@ function renderLauncherHtml(launcherToken) {
1924
1961
  gap: 6px;
1925
1962
  padding: 14px 15px;
1926
1963
  border-radius: 18px;
1927
- border: 1px solid rgba(157, 238, 245, 0.14);
1964
+ border: 1px solid var(--launcher-border-accent);
1928
1965
  background:
1929
1966
  linear-gradient(180deg, rgba(39, 51, 69, 0.52), rgba(22, 18, 47, 0.72)),
1930
1967
  radial-gradient(circle at top right, rgba(244, 147, 85, 0.12), transparent 52%);
@@ -1937,7 +1974,7 @@ function renderLauncherHtml(launcherToken) {
1937
1974
  text-transform: uppercase;
1938
1975
  letter-spacing: 0.13em;
1939
1976
  font-weight: 700;
1940
- color: rgba(157, 238, 245, 0.82);
1977
+ color: var(--launcher-accent-text);
1941
1978
  }
1942
1979
 
1943
1980
  .review-recommendation-title {
@@ -1945,7 +1982,7 @@ function renderLauncherHtml(launcherToken) {
1945
1982
  font-family: "Sora", sans-serif;
1946
1983
  font-size: 1rem;
1947
1984
  line-height: 1.2;
1948
- color: #fff8ef;
1985
+ color: var(--launcher-text-strong);
1949
1986
  font-weight: 700;
1950
1987
  }
1951
1988
 
@@ -1953,7 +1990,7 @@ function renderLauncherHtml(launcherToken) {
1953
1990
  margin: 0;
1954
1991
  font-size: 0.86rem;
1955
1992
  line-height: 1.5;
1956
- color: rgba(241, 235, 223, 0.74);
1993
+ color: var(--launcher-text-secondary);
1957
1994
  }
1958
1995
 
1959
1996
  .quick-review-topline {
@@ -1969,7 +2006,7 @@ function renderLauncherHtml(launcherToken) {
1969
2006
  text-transform: uppercase;
1970
2007
  letter-spacing: 0.12em;
1971
2008
  font-weight: 700;
1972
- color: rgba(157, 238, 245, 0.84);
2009
+ color: var(--launcher-accent-text);
1973
2010
  }
1974
2011
 
1975
2012
  .quick-review-title {
@@ -1978,7 +2015,7 @@ function renderLauncherHtml(launcherToken) {
1978
2015
  font-size: 1.22rem;
1979
2016
  line-height: 1.12;
1980
2017
  letter-spacing: -0.02em;
1981
- color: #fff8ef;
2018
+ color: var(--launcher-text-strong);
1982
2019
  }
1983
2020
 
1984
2021
  .quick-review-badge {
@@ -1991,7 +2028,7 @@ function renderLauncherHtml(launcherToken) {
1991
2028
  border-radius: 999px;
1992
2029
  border: 1px solid rgba(157, 238, 245, 0.26);
1993
2030
  background: rgba(55, 68, 86, 0.42);
1994
- color: rgba(157, 238, 245, 0.96);
2031
+ color: var(--launcher-accent-text);
1995
2032
  font-family: "Sora", sans-serif;
1996
2033
  font-size: 0.88rem;
1997
2034
  font-weight: 700;
@@ -1999,22 +2036,35 @@ function renderLauncherHtml(launcherToken) {
1999
2036
  text-transform: uppercase;
2000
2037
  }
2001
2038
 
2002
- .quick-review-meta,
2039
+ .quick-review-meta {
2040
+ margin: 0;
2041
+ font-size: 0.84rem;
2042
+ line-height: 1.4;
2043
+ color: var(--launcher-text-primary);
2044
+ letter-spacing: 0.01em;
2045
+ font-weight: 500;
2046
+ }
2047
+
2003
2048
  .quick-review-detail {
2004
2049
  margin: 0;
2005
- font-size: 0.92rem;
2006
- line-height: 1.5;
2007
- color: rgba(241, 235, 223, 0.78);
2050
+ font-size: 0.8rem;
2051
+ line-height: 1.4;
2052
+ color: var(--launcher-text-muted);
2053
+ }
2054
+
2055
+ .quick-review-detail[hidden] {
2056
+ display: none;
2008
2057
  }
2009
2058
 
2010
2059
  .quick-review-status {
2011
2060
  border-radius: 16px;
2012
- padding: 12px 14px;
2061
+ padding: 9px 12px;
2013
2062
  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;
2017
- line-height: 1.45;
2063
+ background: var(--launcher-surface-muted);
2064
+ color: var(--launcher-text-primary);
2065
+ font-size: 0.8rem;
2066
+ line-height: 1.4;
2067
+ font-weight: 600;
2018
2068
  }
2019
2069
 
2020
2070
  .quick-review-status.ok {
@@ -2144,7 +2194,7 @@ function renderLauncherHtml(launcherToken) {
2144
2194
  padding: 12px 13px;
2145
2195
  border-radius: 16px;
2146
2196
  border: 1px solid rgba(244, 147, 85, 0.16);
2147
- background: rgba(255,255,255,0.038);
2197
+ background: var(--launcher-surface-muted);
2148
2198
  }
2149
2199
 
2150
2200
  .review-resolution-shell[hidden] {
@@ -2157,14 +2207,14 @@ function renderLauncherHtml(launcherToken) {
2157
2207
  text-transform: uppercase;
2158
2208
  letter-spacing: 0.12em;
2159
2209
  font-weight: 700;
2160
- color: rgba(255, 211, 178, 0.82);
2210
+ color: var(--launcher-warning-text);
2161
2211
  }
2162
2212
 
2163
2213
  .review-resolution-copy {
2164
2214
  margin: 0;
2165
2215
  font-size: 0.84rem;
2166
2216
  line-height: 1.45;
2167
- color: rgba(241, 235, 223, 0.72);
2217
+ color: var(--launcher-text-secondary);
2168
2218
  }
2169
2219
 
2170
2220
  .review-resolution-actions[hidden] {
@@ -2554,7 +2604,7 @@ function renderLauncherHtml(launcherToken) {
2554
2604
  font-family: "Sora", sans-serif;
2555
2605
  font-size: 0.96rem;
2556
2606
  line-height: 1.2;
2557
- color: #fff8ef;
2607
+ color: var(--launcher-text-strong);
2558
2608
  overflow: hidden;
2559
2609
  text-overflow: ellipsis;
2560
2610
  white-space: nowrap;
@@ -2564,19 +2614,50 @@ function renderLauncherHtml(launcherToken) {
2564
2614
  display: block;
2565
2615
  margin-top: 3px;
2566
2616
  font-size: 0.78rem;
2567
- color: rgba(241,235,223,0.66);
2617
+ color: var(--launcher-text-muted);
2568
2618
  overflow: hidden;
2569
2619
  text-overflow: ellipsis;
2570
2620
  white-space: nowrap;
2571
2621
  }
2572
2622
 
2623
+ .launch-account-menu-summary {
2624
+ display: grid;
2625
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2626
+ gap: 8px;
2627
+ padding: 0 2px 6px;
2628
+ }
2629
+
2630
+ .launch-account-menu-metric {
2631
+ display: grid;
2632
+ gap: 4px;
2633
+ padding: 10px 11px;
2634
+ border-radius: 14px;
2635
+ border: 1px solid var(--launcher-border-soft);
2636
+ background: var(--launcher-surface-soft);
2637
+ }
2638
+
2639
+ .launch-account-menu-metric-label {
2640
+ font-size: 0.66rem;
2641
+ text-transform: uppercase;
2642
+ letter-spacing: 0.12em;
2643
+ font-weight: 700;
2644
+ color: var(--launcher-text-faint);
2645
+ }
2646
+
2647
+ .launch-account-menu-metric-value {
2648
+ font-size: 0.84rem;
2649
+ line-height: 1.35;
2650
+ color: var(--launcher-text-strong);
2651
+ font-weight: 600;
2652
+ }
2653
+
2573
2654
  .launch-account-action {
2574
2655
  appearance: none;
2575
2656
  width: 100%;
2576
2657
  border: 1px solid transparent;
2577
2658
  border-radius: 12px;
2578
- background: rgba(255,255,255,0.035);
2579
- color: #f1ebdf;
2659
+ background: var(--launcher-surface-muted);
2660
+ color: var(--launcher-text-primary);
2580
2661
  text-align: left;
2581
2662
  padding: 11px 12px;
2582
2663
  font-size: 0.9rem;
@@ -3994,6 +4075,23 @@ function renderLauncherHtml(launcherToken) {
3994
4075
  .chart svg { width: 100%; height: 100%; display: block; }
3995
4076
 
3996
4077
  .status-line { margin: 6px 0; font-size: 0.92rem; color: rgba(42, 31, 85, 0.9); }
4078
+
4079
+ .discovery-side-meta {
4080
+ display: inline-flex;
4081
+ align-items: center;
4082
+ gap: 8px;
4083
+ width: fit-content;
4084
+ min-height: 34px;
4085
+ padding: 0 12px;
4086
+ border-radius: 999px;
4087
+ border: 1px solid var(--launcher-border-soft);
4088
+ background: rgba(18, 15, 40, 0.72);
4089
+ color: var(--launcher-text-primary);
4090
+ font-size: 0.8rem;
4091
+ font-weight: 600;
4092
+ letter-spacing: 0.01em;
4093
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
4094
+ }
3997
4095
  .ok { color: #0b7f46; }
3998
4096
  .warn { color: #a85b00; }
3999
4097
  .error { color: #a11522; }
@@ -4327,7 +4425,7 @@ function renderLauncherHtml(launcherToken) {
4327
4425
  .discovery-rows-head {
4328
4426
  margin-top: 14px;
4329
4427
  display: grid;
4330
- grid-template-columns: 28px minmax(260px, 2.5fr) minmax(110px, 0.78fr) minmax(150px, 0.96fr) minmax(190px, 1.18fr) auto;
4428
+ grid-template-columns: var(--launcher-inbox-row-columns);
4331
4429
  gap: 16px;
4332
4430
  padding: 0 14px;
4333
4431
  font-size: 0.86rem;
@@ -4350,7 +4448,7 @@ function renderLauncherHtml(launcherToken) {
4350
4448
 
4351
4449
  .discovery-item-row {
4352
4450
  display: grid;
4353
- grid-template-columns: 28px minmax(260px, 2.5fr) minmax(110px, 0.78fr) minmax(150px, 0.96fr) minmax(190px, 1.18fr) auto;
4451
+ grid-template-columns: var(--launcher-inbox-row-columns);
4354
4452
  gap: 16px;
4355
4453
  align-items: center;
4356
4454
  min-width: 0;
@@ -4512,6 +4610,8 @@ function renderLauncherHtml(launcherToken) {
4512
4610
  margin-top: 4px;
4513
4611
  font-size: 0.84rem;
4514
4612
  color: rgba(241, 235, 223, 0.62);
4613
+ line-height: 1.4;
4614
+ overflow-wrap: anywhere;
4515
4615
  }
4516
4616
 
4517
4617
  .discovery-type-pill {
@@ -4574,6 +4674,13 @@ function renderLauncherHtml(launcherToken) {
4574
4674
  white-space: nowrap;
4575
4675
  }
4576
4676
 
4677
+ .discovery-status-copy small {
4678
+ display: -webkit-box;
4679
+ -webkit-line-clamp: 2;
4680
+ -webkit-box-orient: vertical;
4681
+ overflow: hidden;
4682
+ }
4683
+
4577
4684
  .discovery-status-dot.ok { background: #3fd08f; box-shadow: 0 0 0 5px rgba(63, 208, 143, 0.12); }
4578
4685
  .discovery-status-dot.warn { background: #f59b52; box-shadow: 0 0 0 5px rgba(245, 155, 82, 0.12); }
4579
4686
  .discovery-status-dot.error { background: #4fa9ff; box-shadow: 0 0 0 5px rgba(79, 169, 255, 0.12); }
@@ -4584,6 +4691,9 @@ function renderLauncherHtml(launcherToken) {
4584
4691
  align-items: center;
4585
4692
  justify-content: flex-end;
4586
4693
  gap: 10px;
4694
+ width: var(--launcher-inbox-action-width);
4695
+ justify-self: end;
4696
+ flex: 0 0 auto;
4587
4697
  }
4588
4698
 
4589
4699
  .discovery-action-btn,
@@ -6268,7 +6378,8 @@ function renderLauncherHtml(launcherToken) {
6268
6378
  .discovery-filter-wrap,
6269
6379
  .discovery-search-wrap { flex: 1 1 0; }
6270
6380
  .discovery-rows-head,
6271
- .discovery-item-row { grid-template-columns: 28px minmax(0, 1.9fr) minmax(90px, 0.7fr) minmax(120px, 0.9fr) minmax(136px, 1fr) auto; }
6381
+ .discovery-item-row { grid-template-columns: var(--launcher-inbox-row-columns-compact); }
6382
+ .discovery-action-group { width: 146px; }
6272
6383
  .passport-overview-grid { grid-template-columns: 1fr; }
6273
6384
  .passport-stats { grid-template-columns: 1fr 1fr; }
6274
6385
  .runtime-layout { grid-template-columns: 1fr; }
@@ -6552,18 +6663,8 @@ function renderLauncherHtml(launcherToken) {
6552
6663
  </div>
6553
6664
  <span class="quick-review-badge" id="quick-review-badge">Review</span>
6554
6665
  </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>
6557
- <div class="review-summary-grid">
6558
- <div class="review-summary-pill">
6559
- <span class="review-summary-label">Action</span>
6560
- <span class="review-summary-value" id="quick-review-action-summary">Register on this account now</span>
6561
- </div>
6562
- <div class="review-summary-pill">
6563
- <span class="review-summary-label">Scope</span>
6564
- <span class="review-summary-value" id="quick-review-scope-summary">Account scope</span>
6565
- </div>
6566
- </div>
6666
+ <p class="quick-review-meta" id="quick-review-meta">Model · Local</p>
6667
+ <p class="quick-review-detail" id="quick-review-detail" hidden>Nothing is registered automatically.</p>
6567
6668
  <div class="review-recommendation-card">
6568
6669
  <p class="review-recommendation-kicker">Recommended next step</p>
6569
6670
  <h4 class="review-recommendation-title" id="quick-review-recommendation-title">Register solo</h4>
@@ -6632,9 +6733,27 @@ function renderLauncherHtml(launcherToken) {
6632
6733
  <span class="launch-account-menu-subtitle" id="account-menu-subtitle">Account scope</span>
6633
6734
  </span>
6634
6735
  </div>
6736
+ <div class="launch-account-menu-summary">
6737
+ <div class="launch-account-menu-metric">
6738
+ <span class="launch-account-menu-metric-label">Plan</span>
6739
+ <span class="launch-account-menu-metric-value" id="account-menu-tier">Solo</span>
6740
+ </div>
6741
+ <div class="launch-account-menu-metric">
6742
+ <span class="launch-account-menu-metric-label">Sync</span>
6743
+ <span class="launch-account-menu-metric-value" id="account-menu-sync">Not signed in</span>
6744
+ </div>
6745
+ </div>
6746
+ <button class="launch-account-action" id="account-open-profile" type="button">
6747
+ <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>
6748
+ Open Profile
6749
+ </button>
6750
+ <button class="launch-account-action" id="account-open-security" type="button">
6751
+ <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>
6752
+ Account Security
6753
+ </button>
6635
6754
  <button class="launch-account-action" id="account-open-settings" type="button">
6636
6755
  <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
6756
+ Launcher Settings
6638
6757
  </button>
6639
6758
  <button class="launch-account-action" id="account-switch-account" type="button">
6640
6759
  <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 +7076,7 @@ function renderLauncherHtml(launcherToken) {
6957
7076
  </svg>
6958
7077
  </div>
6959
7078
 
6960
- <div class="status-line" id="discovery-last-scan">Last scan: -</div>
7079
+ <div class="discovery-side-meta" id="discovery-last-scan">Last scan · unavailable</div>
6961
7080
  <h3 class="discovery-side-title">Inbox review</h3>
6962
7081
  <p class="discovery-side-copy">Select an inbox item and continue the exact next step Forkit Connect recommends.</p>
6963
7082
 
@@ -7824,6 +7943,64 @@ function renderLauncherHtml(launcherToken) {
7824
7943
  return item.detailSummary || item.statusMeta || 'Review local metadata, then decide what happens next.';
7825
7944
  }
7826
7945
 
7946
+ function getQuickReviewDetailText(item) {
7947
+ if (!item) return '';
7948
+ if (item.kind === 'runtime') {
7949
+ return item.statusTone === 'error'
7950
+ ? 'Resolve runtime attention before linked registrations continue.'
7951
+ : 'Open runtime review to confirm health and connected models.';
7952
+ }
7953
+ if (item.statusLabel === 'Registration in progress') {
7954
+ return '';
7955
+ }
7956
+ if (item.statusLabel === 'Finish privately first') {
7957
+ return '';
7958
+ }
7959
+ if (item.matchedPassportGaid && !item.passportGaid) {
7960
+ return 'A matching passport already exists. Reuse it first.';
7961
+ }
7962
+ if (item.passportGaid) {
7963
+ return 'This item is already linked. Open the passport to inspect it.';
7964
+ }
7965
+ if (item.actionLabel === 'Retry') {
7966
+ return 'Registration failed earlier. Retry when the scope is ready.';
7967
+ }
7968
+ return '';
7969
+ }
7970
+
7971
+ function getQuickReviewStatusText(item) {
7972
+ if (!item) return 'Waiting for review.';
7973
+ const meta = typeof item.statusMeta === 'string' ? item.statusMeta.trim() : '';
7974
+ if (item.kind === 'runtime') {
7975
+ return meta || item.statusLabel;
7976
+ }
7977
+ if (item.statusLabel === 'Registration in progress') {
7978
+ return 'Continue the existing private review.';
7979
+ }
7980
+ if (item.statusLabel === 'Finish privately first') {
7981
+ return 'Private review is required before publishing.';
7982
+ }
7983
+ if (item.matchedPassportGaid && !item.passportGaid) {
7984
+ return 'A matching passport is ready to reuse.';
7985
+ }
7986
+ if (item.passportGaid) {
7987
+ return 'Passport already linked on Forkit.dev.';
7988
+ }
7989
+ if (item.actionLabel === 'Retry') {
7990
+ return 'Retry registration after the required fix is complete.';
7991
+ }
7992
+ return meta || item.statusLabel;
7993
+ }
7994
+
7995
+ function getQuickReviewChipMeta(item, total) {
7996
+ if (!item) return 'Everything looks clear';
7997
+ const precise = getQuickReviewStatusText(item);
7998
+ if (total > 1) {
7999
+ return precise + ' · ' + String(total) + ' items in inbox';
8000
+ }
8001
+ return precise;
8002
+ }
8003
+
7827
8004
  function formatLauncherActionFeedback(result, fallbackMessage) {
7828
8005
  const base = result && typeof result.message === 'string' && result.message.trim()
7829
8006
  ? result.message.trim()
@@ -8092,16 +8269,14 @@ function renderLauncherHtml(launcherToken) {
8092
8269
  setText('quick-review-chip-title', item.name);
8093
8270
  setText(
8094
8271
  'quick-review-chip-meta',
8095
- total > 1
8096
- ? (getReviewPrimaryLabel(item, 'solo') + ' · ' + String(total) + ' items need review')
8097
- : getReviewActionSummary(item, 'solo'),
8272
+ getQuickReviewChipMeta(item, total),
8098
8273
  );
8099
8274
  setText('quick-review-kicker', item.typeLabel || item.kind);
8100
8275
  setText('quick-review-title', item.name);
8101
8276
  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);
8277
+ setText('quick-review-meta', [item.typeLabel || item.kind, item.source].filter(Boolean).join(' · '));
8278
+ setOptionalDetail('quick-review-detail', getQuickReviewDetailText(item));
8279
+ setQuickReviewStatus(getQuickReviewStatusText(item), item.statusTone === 'muted' ? '' : item.statusTone);
8105
8280
  resetReviewResolutionActions('quick-review');
8106
8281
 
8107
8282
  const primaryLabel = getReviewPrimaryLabel(item, 'solo');
@@ -8565,16 +8740,44 @@ function renderLauncherHtml(launcherToken) {
8565
8740
  const normalizedUrl = String(avatarUrl || '').trim();
8566
8741
  if (initialsEl) initialsEl.textContent = initials || 'FC';
8567
8742
  if (!root || !image) return;
8568
- if (normalizedUrl) {
8569
- image.setAttribute('src', normalizedUrl);
8570
- image.hidden = false;
8571
- if (initialsEl) initialsEl.hidden = true;
8572
- root.classList.add('has-image');
8573
- } else {
8574
- image.removeAttribute('src');
8743
+ image.alt = initials ? (initials + ' profile photo') : 'Forkit Connect profile photo';
8744
+
8745
+ const showInitials = () => {
8575
8746
  image.hidden = true;
8747
+ image.removeAttribute('src');
8748
+ image.removeAttribute('data-avatar-url');
8576
8749
  if (initialsEl) initialsEl.hidden = false;
8577
8750
  root.classList.remove('has-image');
8751
+ };
8752
+
8753
+ const showPhoto = () => {
8754
+ image.hidden = false;
8755
+ if (initialsEl) initialsEl.hidden = true;
8756
+ root.classList.add('has-image');
8757
+ };
8758
+
8759
+ if (!normalizedUrl) {
8760
+ image.onload = null;
8761
+ image.onerror = null;
8762
+ showInitials();
8763
+ return;
8764
+ }
8765
+
8766
+ image.onload = () => {
8767
+ if (image.getAttribute('data-avatar-url') !== normalizedUrl) return;
8768
+ showPhoto();
8769
+ };
8770
+ image.onerror = () => {
8771
+ if (image.getAttribute('data-avatar-url') !== normalizedUrl) return;
8772
+ showInitials();
8773
+ };
8774
+ image.setAttribute('data-avatar-url', normalizedUrl);
8775
+ image.hidden = true;
8776
+ if (initialsEl) initialsEl.hidden = false;
8777
+ root.classList.remove('has-image');
8778
+ image.setAttribute('src', normalizedUrl);
8779
+ if (image.complete && image.naturalWidth > 0) {
8780
+ showPhoto();
8578
8781
  }
8579
8782
  }
8580
8783
 
@@ -8645,6 +8848,42 @@ function renderLauncherHtml(launcherToken) {
8645
8848
  return normalized.slice(0, 2).toUpperCase();
8646
8849
  }
8647
8850
 
8851
+ function formatTierLabel(value) {
8852
+ const normalized = String(value || '').trim();
8853
+ if (!normalized) return 'Solo';
8854
+ return normalized.split(/[_-]+/).filter(Boolean).map((chunk) => chunk.charAt(0).toUpperCase() + chunk.slice(1)).join(' ');
8855
+ }
8856
+
8857
+ function summarizeAccountSync(summary) {
8858
+ if (!summary || !summary.authenticated) {
8859
+ return 'Sign in required';
8860
+ }
8861
+ if (summary.workspaceId && summary.projectId) {
8862
+ return 'Governed scope ready';
8863
+ }
8864
+ if (summary.workspaceId) {
8865
+ return 'Workspace selected';
8866
+ }
8867
+ return 'Solo ready';
8868
+ }
8869
+
8870
+ function formatSettingsAccountLabel(profile) {
8871
+ if (!profile || !profile.authenticated) {
8872
+ return 'Not signed in';
8873
+ }
8874
+ const primary = String(profile.displayName || profile.email || 'Connected account').trim();
8875
+ const extras = [profile.email, formatTierLabel(profile.tier)].filter((value, index, array) => value && value !== primary && array.indexOf(value) === index);
8876
+ return [primary, ...extras].filter(Boolean).join(' · ');
8877
+ }
8878
+
8879
+ function setOptionalDetail(id, message) {
8880
+ const element = document.getElementById(id);
8881
+ if (!element) return;
8882
+ const normalized = typeof message === 'string' ? message.trim() : '';
8883
+ element.textContent = normalized;
8884
+ element.hidden = normalized.length === 0;
8885
+ }
8886
+
8648
8887
  function getGreetingLabel() {
8649
8888
  const hour = new Date().getHours();
8650
8889
  if (hour < 12) return 'Good morning';
@@ -9088,6 +9327,8 @@ function renderLauncherHtml(launcherToken) {
9088
9327
  setText('header-account-meta', accountMeta);
9089
9328
  setText('account-menu-title', accountTitle);
9090
9329
  setText('account-menu-subtitle', accountMeta);
9330
+ setText('account-menu-tier', formatTierLabel(summary.tier));
9331
+ setText('account-menu-sync', summarizeAccountSync(summary));
9091
9332
 
9092
9333
  const bindingBits = [
9093
9334
  'State: ' + (summary.bindingState || 'none'),
@@ -9240,6 +9481,38 @@ function renderLauncherHtml(launcherToken) {
9240
9481
  await refreshAll();
9241
9482
  }
9242
9483
 
9484
+ async function handleAccountSessionExit(startLogin) {
9485
+ if (currentPollTimer) {
9486
+ clearInterval(currentPollTimer);
9487
+ currentPollTimer = null;
9488
+ }
9489
+ currentDeviceCode = null;
9490
+ const accountDropdownBtn = document.getElementById('user-chip-btn');
9491
+ const accountDropdownMenu = document.getElementById('account-dropdown-menu');
9492
+ if (accountDropdownMenu) {
9493
+ accountDropdownMenu.hidden = true;
9494
+ }
9495
+ if (accountDropdownBtn) {
9496
+ accountDropdownBtn.setAttribute('aria-expanded', 'false');
9497
+ }
9498
+ const result = await postAction('/api/session/logout', startLogin ? 'Switching account…' : 'Signing out…');
9499
+ if (!result.ok) {
9500
+ setActivityMessage(result.message || 'Session update failed.', 'warn');
9501
+ return;
9502
+ }
9503
+ if (startLogin) {
9504
+ setView('login');
9505
+ setLoginStatus(result.message || 'Signed out. Continue with another account.', 'ok');
9506
+ await refreshAll();
9507
+ handleLogin();
9508
+ return;
9509
+ }
9510
+ setActivityMessage(result.message || 'Signed out.', 'ok');
9511
+ setView('signed-out');
9512
+ setLoginStatus('Sign in required to continue.', 'warn');
9513
+ await refreshAll();
9514
+ }
9515
+
9243
9516
  function setScopeStatus(message, tone) {
9244
9517
  const status = document.getElementById('scope-modal-status');
9245
9518
  if (!status) return;
@@ -9781,7 +10054,12 @@ function renderLauncherHtml(launcherToken) {
9781
10054
  setText('count-agents', String(discovery.counts.agents));
9782
10055
  setText('count-runtimes', String(discovery.counts.runtimes));
9783
10056
  setText('discovery-page-count', String(discovery.counts.all));
9784
- setText('discovery-last-scan', discovery.lastScanAt ? ('Last scan: ' + discovery.lastScanAt) : 'Last scan: not available');
10057
+ setText(
10058
+ 'discovery-last-scan',
10059
+ discovery.lastScanAt
10060
+ ? ('Last scan · ' + formatRelativeTime(discovery.lastScanAt) + ' · ' + formatTimestamp(discovery.lastScanAt))
10061
+ : 'Last scan · not available',
10062
+ );
9785
10063
  setText('disc-check-local', discovery.checks.localDetection, discovery.checks.localDetection === 'active' ? 'ok' : 'warn');
9786
10064
  setText('disc-check-meta', discovery.checks.metadataExtraction, discovery.checks.metadataExtraction === 'active' ? 'ok' : 'warn');
9787
10065
  setText('disc-check-dup', discovery.checks.duplicateMatching, discovery.checks.duplicateMatching === 'active' ? 'ok' : 'warn');
@@ -10171,18 +10449,23 @@ function renderLauncherHtml(launcherToken) {
10171
10449
  if (!data) return;
10172
10450
 
10173
10451
  // Account & Sync section
10174
- const email = data.profile.authenticated ? (data.system.runtimeId || 'Authenticated') : 'Not signed in';
10175
- setText('settings-signed-in-email', email);
10452
+ setText('settings-signed-in-email', formatSettingsAccountLabel(data.profile));
10176
10453
  const syncBadge = document.getElementById('settings-sync-badge');
10177
10454
  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';
10455
+ syncBadge.textContent = !data.profile.authenticated
10456
+ ? 'Sign in required'
10457
+ : data.profile.workspaceBound
10458
+ ? 'Governed ready'
10459
+ : 'Solo ready';
10460
+ syncBadge.className = 'settings-sync-badge' + (data.profile.authenticated ? '' : ' warn');
10461
+ }
10462
+ const lastHeartbeatLine = data.preferences.updatedAt ? 'Last sync: ' + formatTimestamp(data.preferences.updatedAt) : 'Not yet synced';
10182
10463
  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.';
10464
+ const syncDesc = !data.profile.authenticated
10465
+ ? 'Sign in to sync registrations, review items, and runtime health across devices.'
10466
+ : data.profile.workspaceBound
10467
+ ? 'Account, workspace, and project are synced and ready for governed registration.'
10468
+ : 'Your account is synced. Solo registration can continue now, and governed sync starts when you choose a workspace/project.';
10186
10469
  setText('settings-sync-desc', syncDesc);
10187
10470
 
10188
10471
  // Notifications toggles — reflect real preferences
@@ -10623,6 +10906,20 @@ function renderLauncherHtml(launcherToken) {
10623
10906
  }
10624
10907
 
10625
10908
  const accountOpenSettings = document.getElementById('account-open-settings');
10909
+ const accountOpenProfile = document.getElementById('account-open-profile');
10910
+ const accountOpenSecurity = document.getElementById('account-open-security');
10911
+ if (accountOpenProfile) {
10912
+ accountOpenProfile.addEventListener('click', () => {
10913
+ window.open('${FORKIT_DEV_URL}/profile?tab=profile', '_blank', 'noopener,noreferrer');
10914
+ if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10915
+ });
10916
+ }
10917
+ if (accountOpenSecurity) {
10918
+ accountOpenSecurity.addEventListener('click', () => {
10919
+ window.open('${FORKIT_DEV_URL}/profile?tab=security', '_blank', 'noopener,noreferrer');
10920
+ if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10921
+ });
10922
+ }
10626
10923
  if (accountOpenSettings) {
10627
10924
  accountOpenSettings.addEventListener('click', () => {
10628
10925
  setView('settings');
@@ -10633,18 +10930,16 @@ function renderLauncherHtml(launcherToken) {
10633
10930
  const accountSwitch = document.getElementById('account-switch-account');
10634
10931
  if (accountSwitch) {
10635
10932
  accountSwitch.addEventListener('click', () => {
10636
- setView('login');
10637
- handleLogin();
10638
10933
  if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10934
+ void handleAccountSessionExit(true);
10639
10935
  });
10640
10936
  }
10641
10937
 
10642
10938
  const accountSignOut = document.getElementById('account-sign-out');
10643
10939
  if (accountSignOut) {
10644
10940
  accountSignOut.addEventListener('click', () => {
10645
- setView('signed-out');
10646
- setLoginStatus('Sign in required to continue.', 'warn');
10647
10941
  if (accountDropdownMenu) accountDropdownMenu.hidden = true;
10942
+ void handleAccountSessionExit(false);
10648
10943
  });
10649
10944
  }
10650
10945
 
@@ -11474,6 +11769,18 @@ function createLauncherApp(options) {
11474
11769
  sendInternalError(response, error, 'launcher_settings_failed');
11475
11770
  }
11476
11771
  });
11772
+ app.post('/api/session/logout', (_request, response) => {
11773
+ try {
11774
+ options.service.setSessionRef(null);
11775
+ response.json({
11776
+ ok: true,
11777
+ message: 'Signed out. Local discovery stays available on this device.',
11778
+ });
11779
+ }
11780
+ catch (error) {
11781
+ sendInternalError(response, error, 'launcher_logout_failed');
11782
+ }
11783
+ });
11477
11784
  app.post('/api/scan', async (_request, response) => {
11478
11785
  try {
11479
11786
  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.19",
4
4
  "description": "Forkit Connect Local Engine - The Global AI Governance Fabric",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",