six-degrees 0.4.10 → 0.5.0

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 (414) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +9 -9
  5. package/.next/standalone/.next/routes-manifest.json +6 -0
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  12. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.rsc +13 -13
  15. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  16. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  17. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  18. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  19. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
  21. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
  23. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  25. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/company-scores/route.js +1 -1
  29. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  31. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
  38. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
  40. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/network/route.js +1 -1
  42. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
  44. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/api/outreach/route.js +1 -1
  46. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
  48. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/api/scraper/route.js +3 -2
  50. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/settings/route.js +1 -1
  52. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js +1 -1
  54. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js +1 -1
  56. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
  58. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/social/crm/route/app-paths-manifest.json +3 -0
  60. package/.next/standalone/.next/server/app/api/social/crm/route/build-manifest.json +12 -0
  61. package/.next/standalone/.next/server/app/api/social/crm/route/server-reference-manifest.json +4 -0
  62. package/.next/standalone/.next/server/app/api/social/crm/route.js +10 -0
  63. package/.next/standalone/.next/server/app/api/social/crm/route.js.map +5 -0
  64. package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -0
  65. package/.next/standalone/.next/server/app/api/social/crm/route_client-reference-manifest.js +3 -0
  66. package/.next/standalone/.next/server/app/api/social/messages/route.js +4 -3
  67. package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/api/social/route.js +4 -3
  69. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
  71. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  74. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  76. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/import.html +1 -1
  81. package/.next/standalone/.next/server/app/import.rsc +15 -15
  82. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +15 -15
  83. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  84. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +14 -14
  85. package/.next/standalone/.next/server/app/index.html +1 -1
  86. package/.next/standalone/.next/server/app/index.rsc +15 -15
  87. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  88. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  89. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  90. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  92. package/.next/standalone/.next/server/app/launch.html +2 -2
  93. package/.next/standalone/.next/server/app/launch.rsc +15 -15
  94. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +15 -15
  95. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  96. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +14 -14
  97. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  99. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  100. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  101. package/.next/standalone/.next/server/app/paths.html +1 -1
  102. package/.next/standalone/.next/server/app/paths.rsc +15 -15
  103. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +15 -15
  104. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  105. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +14 -14
  106. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  108. package/.next/standalone/.next/server/app/profile.html +1 -1
  109. package/.next/standalone/.next/server/app/profile.rsc +15 -15
  110. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +15 -15
  111. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  112. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +14 -14
  113. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  115. package/.next/standalone/.next/server/app/queue.html +1 -1
  116. package/.next/standalone/.next/server/app/queue.rsc +15 -15
  117. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +15 -15
  118. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  119. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +14 -14
  120. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  121. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  122. package/.next/standalone/.next/server/app/scores.html +1 -1
  123. package/.next/standalone/.next/server/app/scores.rsc +15 -15
  124. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +15 -15
  125. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
  126. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +14 -14
  127. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  128. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  129. package/.next/standalone/.next/server/app/settings.html +1 -1
  130. package/.next/standalone/.next/server/app/settings.rsc +15 -15
  131. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +15 -15
  132. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  133. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +14 -14
  134. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  135. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  136. package/.next/standalone/.next/server/app/setup.html +1 -1
  137. package/.next/standalone/.next/server/app/setup.rsc +15 -15
  138. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +15 -15
  139. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  140. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +14 -14
  141. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  143. package/.next/standalone/.next/server/app/social.html +1 -1
  144. package/.next/standalone/.next/server/app/social.rsc +15 -15
  145. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +15 -15
  146. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
  147. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +14 -14
  148. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  149. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0bat8nx._.js → [root-of-the-server]__05_3-22._.js} +2 -2
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
  151. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__06mi9k2._.js → [root-of-the-server]__13dx4sd._.js} +7 -2
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15wc9e-._.js +3 -0
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tir0vc._.js +8 -0
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +8 -0
  156. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  157. package/.next/standalone/.next/server/chunks/_0ea9qwy._.js +1 -1
  158. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
  159. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  160. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  161. package/.next/standalone/.next/server/chunks/_1oqfuh8._.js +3 -0
  162. package/.next/standalone/.next/server/chunks/_1xh07bp._.js +1 -1
  163. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_social_crm_route_actions_0d1aflv.js +3 -0
  164. package/.next/standalone/.next/server/chunks/lib_0851sk0._.js +8 -0
  165. package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +8 -0
  166. package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +8 -0
  167. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +8 -8
  168. package/.next/standalone/.next/server/chunks/lib_linkedin-export_12imqxg.js +3 -0
  169. package/.next/standalone/.next/server/chunks/lib_rpc_04s8mzr.js +3 -0
  170. package/.next/standalone/.next/server/chunks/lib_rpc_053z7as.js +3 -0
  171. package/.next/standalone/.next/server/chunks/lib_rpc_0b_-2te.js +3 -0
  172. package/.next/standalone/.next/server/chunks/lib_rpc_0dhbxdm.js +3 -0
  173. package/.next/standalone/.next/server/chunks/lib_rpc_0dxlk31.js +3 -0
  174. package/.next/standalone/.next/server/chunks/lib_rpc_0f8b-qc.js +3 -0
  175. package/.next/standalone/.next/server/chunks/lib_rpc_0pzie7_.js +3 -0
  176. package/.next/standalone/.next/server/chunks/lib_rpc_0r76fq_.js +3 -0
  177. package/.next/standalone/.next/server/chunks/lib_rpc_0ut7pjp.js +3 -0
  178. package/.next/standalone/.next/server/chunks/lib_rpc_15zyqaz.js +3 -0
  179. package/.next/standalone/.next/server/chunks/lib_rpc_18zpjtq.js +3 -0
  180. package/.next/standalone/.next/server/chunks/lib_rpc_1a8xkiz.js +3 -0
  181. package/.next/standalone/.next/server/chunks/lib_rpc_1e_55xo.js +3 -0
  182. package/.next/standalone/.next/server/chunks/lib_rpc_1oxfd1_.js +3 -0
  183. package/.next/standalone/.next/server/chunks/lib_rpc_1s0lod0.js +3 -0
  184. package/.next/standalone/.next/server/chunks/lib_scoring_1-e4ur0.js +1 -1
  185. package/.next/standalone/.next/server/chunks/ssr/_01kacen._.js +3 -0
  186. package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +3 -0
  187. package/.next/standalone/.next/server/chunks/ssr/{_1ap1tu_._.js → _0gtv9jb._.js} +2 -2
  188. package/.next/standalone/.next/server/chunks/ssr/_0jdkqgi._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/_0xi-fbs._.js +3 -0
  190. package/.next/standalone/.next/server/chunks/ssr/_0xmwmfv._.js +3 -0
  191. package/.next/standalone/.next/server/chunks/ssr/_1-b9hj7._.js +9 -0
  192. package/.next/standalone/.next/server/chunks/ssr/_112ewem._.js +3 -0
  193. package/.next/standalone/.next/server/chunks/ssr/_16ab63q._.js +3 -0
  194. package/.next/standalone/.next/server/chunks/ssr/_1fxm0-s._.js +3 -0
  195. package/.next/standalone/.next/server/chunks/ssr/_1ra1d3r._.js +9 -0
  196. package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +3 -0
  197. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +22 -10
  198. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
  199. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  200. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +6 -5
  201. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  202. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +19 -3
  203. package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -1
  204. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
  205. package/.next/standalone/.next/server/chunks/ssr/lib_13rapwj._.js +3 -0
  206. package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +3 -0
  207. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  208. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  209. package/.next/standalone/.next/server/pages/404.html +1 -1
  210. package/.next/standalone/.next/server/pages/500.html +1 -1
  211. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  212. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  213. package/.next/standalone/.next/static/chunks/03bziijw5srol.js +1 -0
  214. package/.next/standalone/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  215. package/.next/standalone/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  216. package/.next/standalone/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  217. package/.next/standalone/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  218. package/.next/standalone/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  219. package/.next/standalone/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  220. package/.next/standalone/.next/static/chunks/1i51yylxdzht1.js +1 -0
  221. package/.next/{static/chunks/1xptlvtcz6654.css → standalone/.next/static/chunks/1x_ysly42-teq.css} +1 -1
  222. package/.next/standalone/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  223. package/.next/standalone/.next/static/chunks/2rqtpccruavf2.js +1 -0
  224. package/.next/standalone/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  225. package/.next/standalone/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  226. package/.next/standalone/.next/static/chunks/355ryio1b7sm1.js +1 -0
  227. package/.next/standalone/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  228. package/.next/standalone/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  229. package/.next/standalone/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  230. package/.next/standalone/CHANGELOG.md +235 -0
  231. package/.next/standalone/app/api/data/export/route.js +2 -1
  232. package/.next/standalone/app/api/ingest/route.js +40 -1
  233. package/.next/standalone/app/api/scraper/route.js +12 -2
  234. package/.next/standalone/app/api/settings/sector-preview/route.js +2 -1
  235. package/.next/standalone/app/api/social/crm/route.js +37 -0
  236. package/.next/standalone/app/api/social/messages/route.js +44 -8
  237. package/.next/standalone/app/api/social/route.js +80 -22
  238. package/.next/standalone/app/components/AppTabs.js +59 -0
  239. package/.next/standalone/app/components/ChainView.js +303 -89
  240. package/.next/standalone/app/components/CirclePanel.js +162 -0
  241. package/.next/standalone/app/components/CompanyScores.js +2 -2
  242. package/.next/standalone/app/components/FieldStep.js +4 -4
  243. package/.next/standalone/app/components/FilterPanel.js +121 -183
  244. package/.next/standalone/app/components/ForceGraph.js +41 -3
  245. package/.next/standalone/app/components/GalaxyLab.js +26 -31
  246. package/.next/standalone/app/components/LegacyScoresCard.js +2 -2
  247. package/.next/standalone/app/components/MapControls.js +154 -0
  248. package/.next/standalone/app/components/NoteDetail.js +118 -0
  249. package/.next/standalone/app/components/PathsAnalyzer.js +147 -110
  250. package/.next/standalone/app/components/PeopleMap.js +308 -0
  251. package/.next/standalone/app/components/ScanRadar.js +129 -0
  252. package/.next/standalone/app/components/ScanStatusBar.js +72 -27
  253. package/.next/standalone/app/components/SeparationView.js +626 -118
  254. package/.next/standalone/app/components/Sidebar.js +28 -2
  255. package/.next/standalone/app/components/settings/DataSection.js +12 -3
  256. package/.next/standalone/app/components/settings/NetworkHealthSection.js +1 -1
  257. package/.next/standalone/app/components/settings/SectorSection.js +2 -2
  258. package/.next/standalone/app/components/settings/TierSection.js +1 -1
  259. package/.next/standalone/app/components/settings/TitleSection.js +149 -0
  260. package/.next/standalone/app/components/views.js +15 -14
  261. package/.next/standalone/app/globals.css +11 -0
  262. package/.next/standalone/app/page.js +89 -156
  263. package/.next/standalone/app/paths/page.js +39 -27
  264. package/.next/standalone/app/profile/page.js +33 -5
  265. package/.next/standalone/app/queue/page.js +56 -34
  266. package/.next/standalone/app/scores/page.js +16 -66
  267. package/.next/standalone/app/settings/page.js +27 -13
  268. package/.next/standalone/app/setup/page.js +298 -205
  269. package/.next/standalone/app/social/Crm.js +683 -0
  270. package/.next/standalone/app/social/SocialHub.js +374 -0
  271. package/.next/standalone/app/social/Thread.js +90 -0
  272. package/.next/standalone/app/social/page.js +10 -351
  273. package/.next/standalone/lib/chain-layout.js +105 -35
  274. package/.next/standalone/lib/data-export.js +10 -4
  275. package/.next/standalone/lib/data-folder.js +9 -0
  276. package/.next/standalone/lib/data-import.js +13 -3
  277. package/.next/standalone/lib/degrees.js +0 -6
  278. package/.next/standalone/lib/dot-rings.js +25 -0
  279. package/.next/standalone/lib/galaxy-lab.js +65 -6
  280. package/.next/standalone/lib/ingest.js +2 -0
  281. package/.next/standalone/lib/island.js +40 -0
  282. package/.next/standalone/lib/linkedin-export.js +267 -46
  283. package/.next/standalone/lib/linkedin-limits.js +6 -2
  284. package/.next/standalone/lib/map-heat.js +20 -0
  285. package/.next/standalone/lib/network-health.js +1 -1
  286. package/.next/standalone/lib/notifications.js +121 -0
  287. package/.next/standalone/lib/people-map.js +82 -0
  288. package/.next/standalone/lib/rarity.js +45 -3
  289. package/.next/standalone/lib/reach.js +7 -6
  290. package/.next/standalone/lib/rpc.js +15 -4
  291. package/.next/standalone/lib/scan-pace.js +46 -0
  292. package/.next/standalone/lib/scoring.js +39 -8
  293. package/.next/standalone/lib/scraper-client.js +19 -6
  294. package/.next/standalone/lib/sector-focus.js +4 -4
  295. package/.next/standalone/lib/separation.js +137 -10
  296. package/.next/standalone/lib/settings-effects.js +20 -11
  297. package/.next/standalone/lib/settings.js +3 -0
  298. package/.next/standalone/lib/social-crm.js +415 -0
  299. package/.next/standalone/lib/social-store.js +13 -6
  300. package/.next/standalone/lib/tier-grid.js +99 -0
  301. package/.next/standalone/lib/title-ranking.js +83 -0
  302. package/.next/standalone/package-lock.json +2 -2
  303. package/.next/standalone/package.json +1 -1
  304. package/.next/standalone/scripts/scrape.py +177 -35
  305. package/.next/static/chunks/03bziijw5srol.js +1 -0
  306. package/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  307. package/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  308. package/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  309. package/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  310. package/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  311. package/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  312. package/.next/static/chunks/1i51yylxdzht1.js +1 -0
  313. package/.next/{standalone/.next/static/chunks/1xptlvtcz6654.css → static/chunks/1x_ysly42-teq.css} +1 -1
  314. package/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  315. package/.next/static/chunks/2rqtpccruavf2.js +1 -0
  316. package/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  317. package/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  318. package/.next/static/chunks/355ryio1b7sm1.js +1 -0
  319. package/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  320. package/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  321. package/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  322. package/CHANGELOG.md +235 -0
  323. package/lib/chain-layout.js +105 -35
  324. package/lib/data-export.js +10 -4
  325. package/lib/data-folder.js +9 -0
  326. package/lib/data-import.js +13 -3
  327. package/lib/degrees.js +0 -6
  328. package/lib/dot-rings.js +25 -0
  329. package/lib/galaxy-lab.js +65 -6
  330. package/lib/ingest.js +2 -0
  331. package/lib/island.js +40 -0
  332. package/lib/linkedin-export.js +267 -46
  333. package/lib/linkedin-limits.js +6 -2
  334. package/lib/map-heat.js +20 -0
  335. package/lib/network-health.js +1 -1
  336. package/lib/notifications.js +121 -0
  337. package/lib/people-map.js +82 -0
  338. package/lib/rarity.js +45 -3
  339. package/lib/reach.js +7 -6
  340. package/lib/rpc.js +15 -4
  341. package/lib/scan-pace.js +46 -0
  342. package/lib/scoring.js +39 -8
  343. package/lib/scraper-client.js +19 -6
  344. package/lib/sector-focus.js +4 -4
  345. package/lib/separation.js +137 -10
  346. package/lib/settings-effects.js +20 -11
  347. package/lib/settings.js +3 -0
  348. package/lib/social-crm.js +415 -0
  349. package/lib/social-store.js +13 -6
  350. package/lib/tier-grid.js +99 -0
  351. package/lib/title-ranking.js +83 -0
  352. package/package.json +1 -1
  353. package/scripts/scrape.py +177 -35
  354. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08f5awg._.js +0 -8
  355. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17hjtzl._.js +0 -8
  356. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20tmem7._.js +0 -3
  357. package/.next/standalone/.next/server/chunks/lib_18cr1gi._.js +0 -8
  358. package/.next/standalone/.next/server/chunks/lib_rpc_03i8xef.js +0 -3
  359. package/.next/standalone/.next/server/chunks/lib_rpc_08fc3rp.js +0 -3
  360. package/.next/standalone/.next/server/chunks/lib_rpc_08r2t_q.js +0 -3
  361. package/.next/standalone/.next/server/chunks/lib_rpc_0btpv1t.js +0 -3
  362. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +0 -8
  363. package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +0 -3
  364. package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +0 -3
  365. package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +0 -3
  366. package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +0 -3
  367. package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +0 -3
  368. package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +0 -3
  369. package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +0 -3
  370. package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +0 -3
  371. package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +0 -3
  372. package/.next/standalone/.next/server/chunks/ssr/_0c_y09g._.js +0 -3
  373. package/.next/standalone/.next/server/chunks/ssr/_0ezj4e0._.js +0 -3
  374. package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +0 -3
  375. package/.next/standalone/.next/server/chunks/ssr/_0l8cxmo._.js +0 -3
  376. package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +0 -3
  377. package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +0 -3
  378. package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +0 -3
  379. package/.next/standalone/.next/server/chunks/ssr/_1errbuf._.js +0 -3
  380. package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +0 -3
  381. package/.next/standalone/.next/server/chunks/ssr/app_components_19c2d9c._.js +0 -3
  382. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +0 -3
  383. package/.next/standalone/.next/static/chunks/07dyt47mp4s9-.js +0 -1
  384. package/.next/standalone/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  385. package/.next/standalone/.next/static/chunks/160wjb73ddwqd.js +0 -6
  386. package/.next/standalone/.next/static/chunks/1c_n0973t07l0.js +0 -2
  387. package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  388. package/.next/standalone/.next/static/chunks/206rm6j42gl_a.js +0 -1
  389. package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +0 -1
  390. package/.next/standalone/.next/static/chunks/2coxzjm_k6ilr.js +0 -1
  391. package/.next/standalone/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  392. package/.next/standalone/.next/static/chunks/2pw6_osz1n_te.js +0 -1
  393. package/.next/standalone/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  394. package/.next/standalone/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  395. package/.next/standalone/app/components/OrbitGraph.js +0 -737
  396. package/.next/standalone/app/social/Conversations.js +0 -246
  397. package/.next/static/chunks/07dyt47mp4s9-.js +0 -1
  398. package/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  399. package/.next/static/chunks/160wjb73ddwqd.js +0 -6
  400. package/.next/static/chunks/1c_n0973t07l0.js +0 -2
  401. package/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  402. package/.next/static/chunks/206rm6j42gl_a.js +0 -1
  403. package/.next/static/chunks/236f0jtp68j_h.js +0 -1
  404. package/.next/static/chunks/2coxzjm_k6ilr.js +0 -1
  405. package/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  406. package/.next/static/chunks/2pw6_osz1n_te.js +0 -1
  407. package/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  408. package/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  409. /package/.next/standalone/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  410. /package/.next/standalone/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  411. /package/.next/standalone/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
  412. /package/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  413. /package/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  414. /package/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
@@ -4,13 +4,18 @@
4
4
  // messaged, when, who wrote last, the list of
5
5
  // conversations. Never any words.
6
6
  // social-messages-<profile>.json the messages themselves, only while Keep my
7
- // messages is on (app/api/social/messages).
7
+ // messages is on (app/api/social/messages),
8
+ // and the notes sent with requests, likewise.
9
+ // crm-<profile>.json your own CRM: each person's stage, tags,
10
+ // notes and next follow-up (app/api/social/crm,
11
+ // lib/social-crm.js). Always kept: you wrote
12
+ // it. Forget it offers to delete it, asking
13
+ // separately, so notes aren't lost by accident.
8
14
  //
9
15
  // Two files so that turning Keep my messages off, or Forget it, deletes the
10
16
  // words by deleting one file, and so the everyday file the Galaxy and the
11
- // Social tab read stays small however many messages there are. Neither is in
12
- // "Save a copy of my network": that carries an allow-list of files
13
- // (lib/data-folder.js TRAVELLING_FILES) and these aren't on it.
17
+ // Social tab read stays small however many messages there are. "Save a copy of my network" carries
18
+ // them when its Social box is ticked (lib/data-folder.js SOCIAL_FILE).
14
19
 
15
20
  import { readFileSync, writeFileSync, renameSync, rmSync, statSync } from 'node:fs';
16
21
  import path from 'node:path';
@@ -30,6 +35,7 @@ export function socialFiles() {
30
35
  profileId: me.id,
31
36
  social: path.join(dir, `social-${me.id}.json`),
32
37
  messages,
38
+ crm: path.join(dir, `crm-${me.id}.json`),
33
39
  // An import's messages arrive in parts (each request stays under the 10 MB
34
40
  // middleware reads); they gather here and replace `messages` at the end.
35
41
  incoming: `${messages}.incoming`,
@@ -48,17 +54,18 @@ export function writeJsonAtomic(file, value) {
48
54
  if (cache.file === file) cache.file = null;
49
55
  }
50
56
 
51
- /** The kept messages and their live-sync previews, or empty ones. */
57
+ /** The kept messages, their live-sync previews and the requests' notes, or empty ones. */
52
58
  export function readMessages(file) {
53
59
  // Kept in memory while the file is unchanged: a long history is read once,
54
60
  // not on every page of a thread or every letter typed in the search box.
55
61
  let st;
56
- try { st = statSync(file); } catch { return { threads: {}, previews: {} }; }
62
+ try { st = statSync(file); } catch { return { threads: {}, previews: {}, invites: {} }; }
57
63
  if (cache.file === file && cache.mtimeMs === st.mtimeMs && cache.size === st.size) return cache.data;
58
64
  const raw = readJson(file, {});
59
65
  const data = {
60
66
  threads: raw?.threads && typeof raw.threads === 'object' ? raw.threads : {},
61
67
  previews: raw?.previews && typeof raw.previews === 'object' ? raw.previews : {},
68
+ invites: raw?.invites && typeof raw.invites === 'object' ? raw.invites : {},
62
69
  };
63
70
  Object.assign(cache, { file, mtimeMs: st.mtimeMs, size: st.size, data });
64
71
  return data;
@@ -0,0 +1,99 @@
1
+ // The Filters panel's grid: five tiers down, six degrees across, the same in
2
+ // Network Circle and in Degrees.
3
+ //
4
+ // Blake, 2026-10-02: "keeping the tiers as is but enlarging them and having 6
5
+ // dots to let the user tap on to toggle or not if they want specific degrees
6
+ // showing for whatever tier … it will be universal for both".
7
+ //
8
+ // A grid is { hidden: [tiers switched off], degrees: { S: [1, 2], … } }: a tier
9
+ // row switches on and off as it always did, and remembers which degrees it
10
+ // shows while it's off. Counts are { S: { 1: n, 2: n, … }, … }: how many people
11
+ // there are at each tier and degree, so a dot with nobody behind it can't be
12
+ // tapped, and the last people on screen can't be switched off.
13
+
14
+ export const TIERS = ['S', 'A', 'B', 'C', 'D'];
15
+ export const GRID_DEGREES = [1, 2, 3, 4, 5, 6];
16
+
17
+ const tierOf = (tier) => (TIERS.includes(tier) ? tier : 'D'); // no tier: with D, as the views draw it
18
+
19
+ export const makeGrid = (degrees = GRID_DEGREES) => ({
20
+ hidden: [],
21
+ degrees: Object.fromEntries(TIERS.map((t) => [t, degrees.slice()])),
22
+ });
23
+
24
+ /** Network Circle starts with your own connections; you pick the rest. */
25
+ export const NETWORK_GRID = Object.freeze(makeGrid([1]));
26
+ /** Degrees starts with everything it has. */
27
+ export const DEGREES_GRID = Object.freeze(makeGrid());
28
+
29
+ /** Is someone of this tier, at this degree, on screen? */
30
+ export function shows(grid, tier, degree) {
31
+ const t = tierOf(tier);
32
+ return !grid.hidden.includes(t) && grid.degrees[t].includes(degree);
33
+ }
34
+
35
+ const countAt = (counts, tier, degree) => counts?.[tier]?.[degree] || 0;
36
+
37
+ /** How many people one tier is showing. */
38
+ export function showingIn(grid, counts, tier) {
39
+ if (grid.hidden.includes(tier)) return 0;
40
+ return grid.degrees[tier].reduce((n, d) => n + countAt(counts, tier, d), 0);
41
+ }
42
+
43
+ /** How many people the whole grid is showing. */
44
+ export const showing = (grid, counts) => TIERS.reduce((n, t) => n + showingIn(grid, counts, t), 0);
45
+
46
+ // A change that would leave nobody on screen isn't made.
47
+ const keep = (grid, next, counts) => (counts && showing(next, counts) === 0 ? grid : next);
48
+
49
+ /** One dot: that degree, for that tier. Tapping a dot in a tier that's off switches the tier on with it. */
50
+ export function toggleCell(grid, tier, degree, counts) {
51
+ const has = grid.degrees[tier].includes(degree);
52
+ const off = grid.hidden.includes(tier);
53
+ const degrees = off || !has
54
+ ? [...new Set([...grid.degrees[tier], degree])].sort((a, b) => a - b)
55
+ : grid.degrees[tier].filter((d) => d !== degree);
56
+ return keep(grid, {
57
+ hidden: off ? grid.hidden.filter((t) => t !== tier) : grid.hidden,
58
+ degrees: { ...grid.degrees, [tier]: degrees },
59
+ }, counts);
60
+ }
61
+
62
+ /** A whole tier, off or back on with the degrees it had. */
63
+ export function toggleTier(grid, tier, counts) {
64
+ if (!grid.hidden.includes(tier)) return keep(grid, { ...grid, hidden: [...grid.hidden, tier] }, counts);
65
+ // Back on with nothing to show would look broken: give it what the others show.
66
+ const own = grid.degrees[tier];
67
+ const others = [...new Set(TIERS.flatMap((t) => (t === tier ? [] : grid.degrees[t])))].sort((a, b) => a - b);
68
+ return {
69
+ hidden: grid.hidden.filter((t) => t !== tier),
70
+ degrees: own.length ? grid.degrees : { ...grid.degrees, [tier]: others.length ? others : [1] },
71
+ };
72
+ }
73
+
74
+ /** A whole degree: off if every tier with people there is showing it, otherwise on for all of them. */
75
+ export function toggleDegree(grid, degree, counts) {
76
+ const tiers = TIERS.filter((t) => !grid.hidden.includes(t) && (!counts || countAt(counts, t, degree) > 0));
77
+ if (!tiers.length) return grid;
78
+ const allOn = tiers.every((t) => grid.degrees[t].includes(degree));
79
+ const degrees = { ...grid.degrees };
80
+ for (const t of tiers) {
81
+ degrees[t] = allOn ? degrees[t].filter((d) => d !== degree) : [...new Set([...degrees[t], degree])].sort((a, b) => a - b);
82
+ }
83
+ return keep(grid, { ...grid, degrees }, counts);
84
+ }
85
+
86
+ /** Every tier back on, each with the degrees it had. */
87
+ export const showAllTiers = (grid) => (grid.hidden.length ? { ...grid, hidden: [] } : grid);
88
+
89
+ /** People by tier and degree, for the dots: `byDegree` is { 1: rows, 2: rows, … }. */
90
+ export function gridCounts(byDegree = {}) {
91
+ const counts = Object.fromEntries(TIERS.map((t) => [t, {}]));
92
+ for (const [degree, rows] of Object.entries(byDegree)) {
93
+ for (const row of rows || []) {
94
+ const t = tierOf(row?.tier);
95
+ counts[t][degree] = (counts[t][degree] || 0) + 1;
96
+ }
97
+ }
98
+ return counts;
99
+ }
@@ -0,0 +1,83 @@
1
+ // Settings → Scores → Titles: how much each kind of title counts, your way
2
+ // (Blake, 2026-10-02: "let the user change the rankings of titles as well …
3
+ // incase they value certain people or is using this for outreaching new
4
+ // founders", and people who "find more in that role" for outbound or hiring).
5
+ //
6
+ // Two parts, both in title points (the 0–10 a title is worth before the
7
+ // company weighs it, lib/scoring.js LEVELS):
8
+ // points a level's points, yours: Owner / Entrepreneur at 10, say, to put
9
+ // founders of small companies level with CEOs
10
+ // roles words in a title, and what anyone whose title has them counts:
11
+ // "account executive" at 10 puts every AE you can reach in S
12
+ // Nothing set scores exactly as before. The model applies it (scorePerson);
13
+ // this file is the setting and its presets.
14
+
15
+ import { LEVELS } from './scoring.js';
16
+
17
+ /** The ladder shown in Settings, top to bottom (the main kinds of title). */
18
+ export const RANKED_LEVELS = ['csuite', 'owner', 'vp', 'director', 'manager', 'senior', 'ic', 'unknown', 'intern', 'student'];
19
+ export const NO_RANKING = Object.freeze({ points: Object.freeze({}), roles: Object.freeze([]) });
20
+ export const MAX_ROLES = 20;
21
+ const MAX_ROLE_TEXT = 60;
22
+
23
+ const clampPoints = (v) => {
24
+ const n = Number(v);
25
+ if (!Number.isFinite(n) || n < 0 || n > 10) throw new Error('Title points go from 0 to 10.');
26
+ return Math.round(n * 2) / 2;
27
+ };
28
+
29
+ export const TITLE_RANKING_SETTING = {
30
+ default: NO_RANKING,
31
+ parse(value) {
32
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('A title ranking is { points, roles }.');
33
+ const points = {};
34
+ for (const [key, v] of Object.entries(value.points || {})) {
35
+ if (!Object.hasOwn(LEVELS, key)) throw new Error(`There is no kind of title called '${key}'.`);
36
+ const p = clampPoints(v);
37
+ if (p !== LEVELS[key].points) points[key] = p; // the default needn't be stored
38
+ }
39
+ const roles = [];
40
+ const seen = new Set();
41
+ for (const r of Array.isArray(value.roles) ? value.roles : []) {
42
+ const text = String(r?.text ?? '').trim().toLowerCase().replace(/\s+/g, ' ');
43
+ if (text.length < 2) throw new Error('A role needs at least two letters.');
44
+ if (text.length > MAX_ROLE_TEXT) throw new Error(`Keep a role under ${MAX_ROLE_TEXT} letters.`);
45
+ if (seen.has(text)) continue;
46
+ seen.add(text);
47
+ roles.push({ text, points: clampPoints(r.points) });
48
+ }
49
+ if (roles.length > MAX_ROLES) throw new Error(`Up to ${MAX_ROLES} roles.`);
50
+ return { points, roles };
51
+ },
52
+ };
53
+
54
+ /** The same ranking always gives the same text, so the stored scores can say which they were made with. */
55
+ export function rankingFingerprint(r = NO_RANKING) {
56
+ const points = Object.keys(r.points || {}).sort().map((k) => `${k}=${r.points[k]}`).join(',');
57
+ const roles = (r.roles || []).map((x) => `${x.text}=${x.points}`).sort().join(',');
58
+ return points || roles ? `${points}|${roles}` : 'none';
59
+ }
60
+
61
+ export const isDefaultRanking = (r) => rankingFingerprint(r) === 'none';
62
+
63
+ /** Starting points for the common reasons to change it. Each replaces the whole ranking; you can adjust from there. */
64
+ export const RANKING_PRESETS = [
65
+ { key: 'default', label: 'As the app ranks them', ranking: NO_RANKING, note: 'C-suite and founders first, then VPs, directors, managers.' },
66
+ {
67
+ key: 'founders', label: 'Founders first',
68
+ note: 'Owners and entrepreneurs count like CEOs, and anyone who says founder counts as much: for meeting founders, or selling to them.',
69
+ ranking: { points: { owner: 10 }, roles: [{ text: 'founder', points: 10 }, { text: 'co-founder', points: 10 }] },
70
+ },
71
+ {
72
+ key: 'investors', label: 'Investors',
73
+ note: 'Anyone who invests for a living rises to the top: for raising money.',
74
+ ranking: { points: {}, roles: ['investor', 'venture', 'angel', 'general partner', 'managing partner', 'principal at', 'vc'].map((text) => ({ text, points: 10 })) },
75
+ },
76
+ {
77
+ key: 'role', label: 'A role I’m looking for',
78
+ note: 'For outbound or hiring: add the role below, and everyone in it rises, whatever their level.',
79
+ ranking: null, // keeps what you have and opens the role box
80
+ },
81
+ ];
82
+
83
+ export { titleHas } from './scoring.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "six-degrees",
3
- "version": "0.4.10",
3
+ "version": "0.5.0",
4
4
  "description": "Map your professional network as a galaxy — leverage tiers, bridges into new circles, and the shortest path to anyone. Runs locally on your own data.",
5
5
  "license": "MIT",
6
6
  "author": "Blake Burford",
package/scripts/scrape.py CHANGED
@@ -59,7 +59,18 @@ _ENV_LOCAL = _load_env_local()
59
59
  # a server they don't control.
60
60
  APP_URL = os.getenv("APP_URL", "http://localhost:3000")
61
61
  MESSAGING_URL = "https://www.linkedin.com/messaging/"
62
+ # The daily sync reads the top of your messages list: this many scrolls.
62
63
  MESSAGE_SCROLLS = 15
64
+ # Full history (--messages --full-history, the Social tab's "Read my whole
65
+ # history"): keep scrolling until the list stops growing, but never past these
66
+ # ceilings, at the same fixed pace as everything else the scanner does.
67
+ MESSAGE_HISTORY_SCROLLS = 60
68
+ MESSAGE_HISTORY_MAX = 1000
69
+ # Scrolls in a row that bring no conversation it hadn't seen: the end of the list.
70
+ MESSAGE_HISTORY_STILL = 3
71
+ # Seconds after each scroll for the list to load. Fixed, on purpose: the
72
+ # scanner is openly slow, never made to look like a person with random waits.
73
+ MESSAGE_WAIT = 4
63
74
 
64
75
  CONNECTIONS_URL = "https://www.linkedin.com/mynetwork/invite-connect/connections/"
65
76
 
@@ -80,7 +91,7 @@ def _assert_local_target():
80
91
  if host in ("localhost", "127.0.0.1", "::1", "0.0.0.0"):
81
92
  return
82
93
  if os.getenv("ALLOW_REMOTE_PUSH") == "1":
83
- print(f"WARNING: pushing scraped data to remote host {host}")
94
+ print(f"WARNING: pushing scanned data to remote host {host}")
84
95
  return
85
96
  raise SystemExit(
86
97
  f"Refusing to push scraped data to '{APP_URL}'.\n"
@@ -359,7 +370,7 @@ def _print_sign_in_banner(say):
359
370
  print(" inside automated browsers, which is why that window comes up")
360
371
  print(" greyed out and does nothing.")
361
372
  print()
362
- print(" Nothing is scraped until you are signed in, and the scrape starts")
373
+ print(" Nothing is scanned until you are signed in, and the scan starts")
363
374
  print(" by itself the moment you are. Take as long as you need.")
364
375
  print(" Close the browser window to cancel.")
365
376
  print(" ==================================================================")
@@ -376,11 +387,11 @@ def _wait_for_sign_in(page, context, timeout_s, say):
376
387
  # not: signing in legitimately opens and closes tabs.
377
388
  try:
378
389
  if not [pg for pg in context.pages if not pg.is_closed()]:
379
- print("\n Browser closed — nothing was scraped.")
390
+ print("\n Browser closed — nothing was scanned.")
380
391
  say("Browser closed before sign-in.")
381
392
  return False
382
393
  except Exception:
383
- print("\n Browser closed — nothing was scraped.")
394
+ print("\n Browser closed — nothing was scanned.")
384
395
  return False
385
396
 
386
397
  # The cookie belongs to the whole browser, so it is found no matter which
@@ -482,7 +493,7 @@ def resolve_active_user():
482
493
  for u in users:
483
494
  if (u.get("name") or "").strip().lower() == wanted:
484
495
  _active_user_id = u["id"]
485
- print(f" Scraping into profile: {u['name']}")
496
+ print(f" Scanning into profile: {u['name']}")
486
497
  return _active_user_id
487
498
  raise SystemExit(f"No profile named '{os.getenv('SIX_DEGREES_USER')}' in the app.")
488
499
 
@@ -499,7 +510,7 @@ def resolve_active_user():
499
510
  raise SystemExit(f"The app at {APP_URL} did not say which profile to use.")
500
511
 
501
512
  _active_user_id = me["id"]
502
- print(f" Scraping into profile: {me.get('name')}")
513
+ print(f" Scanning into profile: {me.get('name')}")
503
514
  return _active_user_id
504
515
 
505
516
 
@@ -721,7 +732,7 @@ def push_connections(connections, degree=1, bridge_id=None, user_id=None, on_sav
721
732
  if p["profile_url"] in d1_urls:
722
733
  # Unlock via API route
723
734
  requests.post(f"{APP_URL}/api/unlock", json={"connectionId": p["id"]}, headers=app_headers())
724
- print(f" *** PATH UNLOCKED: {p['name']}! Run --bridge \"{p['name']}\" to scrape their network ***")
735
+ print(f" *** PATH UNLOCKED: {p['name']}! Run --bridge \"{p['name']}\" to scan their network ***")
725
736
 
726
737
  return inserted
727
738
 
@@ -882,8 +893,8 @@ def scrape_full(headless=False):
882
893
  if searches_left()[0] <= 0:
883
894
  raise BudgetReached(0, searches_left()[1])
884
895
 
885
- print("\n=== Full Account Setup Scrape ===\n")
886
- print("This scrapes ALL connections via the search page (not the connections page).")
896
+ print("\n=== Full account scan ===\n")
897
+ print("This scans ALL connections via the search page (not the connections page).")
887
898
  print("Takes 3-5 minutes for ~500 connections.\n")
888
899
 
889
900
  with sync_playwright() as p:
@@ -1275,6 +1286,18 @@ SAVE_EVERY_PAGES = 10 # save as a long read goes, so a stop loses li
1275
1286
  # randomised: the point is fewer searches an hour, not looking like a person.
1276
1287
  PAGE_PAUSE = 20 # seconds before each next page of results
1277
1288
  CHUNK_COOLDOWN = 60 # extra seconds after every SAVE_EVERY_PAGES pages
1289
+ # The Scan page's speed (Blake, 2026-09-30 and 10-03: "slow, medium and fast").
1290
+ # Fast is exactly the pacing above and nothing is ever faster; Medium and Slow
1291
+ # only add waiting. Speed changes how many searches an hour, never how many a
1292
+ # day: the daily budget stays the volume cap. Read from scan-limits.json
1293
+ # ("pace") at the start of every run (apply_pace). lib/scan-pace.js holds the
1294
+ # same numbers for the page, and tests/scan-pace.test.mjs checks they agree.
1295
+ SCAN_PACES = {
1296
+ "fast": {"page_pause": 20, "chunk_cooldown": 60, "profile_gap": 60},
1297
+ "medium": {"page_pause": 45, "chunk_cooldown": 180, "profile_gap": 90},
1298
+ "slow": {"page_pause": 90, "chunk_cooldown": 300, "profile_gap": 120},
1299
+ }
1300
+ DEFAULT_PACE = "fast"
1278
1301
  LEGACY_PAGES_READ = 10 # how far every read before 0.1.6 went, at most
1279
1302
 
1280
1303
  # Experimental all-day pacing for Auto-Bridge (--experimental; item 44, Graph
@@ -1550,6 +1573,27 @@ def _profile_wait(data, now):
1550
1573
  return 0
1551
1574
 
1552
1575
 
1576
+ def scan_pace():
1577
+ """The speed saved on the Scan page: "fast", "medium" or "slow"."""
1578
+ try:
1579
+ name = json.loads((_home() / "scan-limits.json").read_text()).get("pace")
1580
+ except Exception:
1581
+ name = None
1582
+ return name if name in SCAN_PACES else DEFAULT_PACE
1583
+
1584
+
1585
+ def apply_pace(name=None):
1586
+ """Set this run's waits from the chosen speed. Never below Fast's."""
1587
+ global PAGE_PAUSE, CHUNK_COOLDOWN, PROFILE_GAP
1588
+ name = name if name in SCAN_PACES else scan_pace()
1589
+ pace = SCAN_PACES[name]
1590
+ fast = SCAN_PACES["fast"]
1591
+ PAGE_PAUSE = max(fast["page_pause"], pace["page_pause"])
1592
+ CHUNK_COOLDOWN = max(fast["chunk_cooldown"], pace["chunk_cooldown"])
1593
+ PROFILE_GAP = max(fast["profile_gap"], pace["profile_gap"])
1594
+ return name
1595
+
1596
+
1553
1597
  def search_limits():
1554
1598
  try:
1555
1599
  data = json.loads((_home() / "scan-limits.json").read_text())
@@ -2068,14 +2112,54 @@ def voyager_conversations(payload):
2068
2112
  return out
2069
2113
 
2070
2114
 
2071
- def sync_messages(headless=False):
2115
+ def conversation_id(c):
2116
+ """One conversation, however many of LinkedIn's responses mention it: its link, else who's in it."""
2117
+ return c.get("url") or ",".join(c.get("people") or [])
2118
+
2119
+
2120
+ def tally_conversations(seen, convos):
2121
+ """Add these conversations to `seen` (id -> conversation, the newest activity
2122
+ kept) and say how many it didn't have before. The full-history read stops
2123
+ when scrolling stops bringing new ones."""
2124
+ new = 0
2125
+ for c in convos:
2126
+ k = conversation_id(c)
2127
+ if not k:
2128
+ continue
2129
+ if k not in seen:
2130
+ new += 1
2131
+ seen[k] = c
2132
+ elif c.get("at", 0) >= seen[k].get("at", 0):
2133
+ seen[k] = c
2134
+ return new
2135
+
2136
+
2137
+ def history_done(new_per_scroll, found, max_scrolls=MESSAGE_HISTORY_SCROLLS,
2138
+ max_found=MESSAGE_HISTORY_MAX, still=MESSAGE_HISTORY_STILL):
2139
+ """Why a full-history read should stop now, or None to scroll again.
2140
+ `new_per_scroll` is how many new conversations each scroll so far brought;
2141
+ `found` how many there are in all."""
2142
+ if found >= max_found:
2143
+ return f"reached {max_found:,} conversations, the most one read takes"
2144
+ if len(new_per_scroll) >= max_scrolls:
2145
+ return f"reached {max_scrolls} scrolls, the most one read takes"
2146
+ if len(new_per_scroll) >= still and not any(new_per_scroll[-still:]):
2147
+ return "the list stopped growing: that's the whole history"
2148
+ return None
2149
+
2150
+
2151
+ def sync_messages(headless=False, full_history=False):
2072
2152
  """Experimental: read your LinkedIn messages list once, for the Social tab. Opens
2073
2153
  Messaging in your own Chrome, scrolls the list a few times, and keeps from LinkedIn's
2074
- own data who each 1:1 conversation is with, when it was last active, whether it has
2075
- unread messages, its link and who wrote last. The newest message's words go to the
2076
- app only while Keep my messages is on there. No search budget."""
2154
+ own data who each conversation is with, when it was last active, whether it has
2155
+ unread messages, its link and who wrote last. The newest message's words (1:1 only)
2156
+ go to the app only while Keep my messages is on there. No search budget.
2157
+
2158
+ full_history: keep scrolling until no new conversations load (history_done), for
2159
+ the whole list rather than its top. Same fixed waits; opens no conversation."""
2077
2160
  from playwright.sync_api import sync_playwright
2078
2161
  kept = []
2162
+ seen = {}
2079
2163
  with sync_playwright() as p:
2080
2164
  browser = p.chromium.launch_persistent_context(
2081
2165
  user_data_dir=get_scraper_profile_path(), headless=headless, channel="chrome",
@@ -2086,13 +2170,38 @@ def sync_messages(headless=False):
2086
2170
  browser.close()
2087
2171
  return
2088
2172
  page.on("response", lambda r: kept.append(r) if "/voyager/api/" in r.url and "messag" in r.url.lower() else None)
2089
- print("Opening your messages list...")
2173
+
2174
+ def wait(seconds):
2175
+ # Playwright hands over the responses that came in only while it's
2176
+ # being called, so wait through it, not with time.sleep.
2177
+ try:
2178
+ page.wait_for_timeout(seconds * 1000)
2179
+ except Exception:
2180
+ time.sleep(seconds)
2181
+
2182
+ def read_new():
2183
+ got = kept[:]
2184
+ del kept[:len(got)]
2185
+ convos = []
2186
+ for r in got:
2187
+ try:
2188
+ data = r.json()
2189
+ except Exception:
2190
+ continue
2191
+ _wire_sample(data, prefix="messages")
2192
+ convos.extend(voyager_conversations(data))
2193
+ return tally_conversations(seen, convos)
2194
+
2195
+ print("Opening your messages list" + (", to read its whole history..." if full_history else "..."))
2090
2196
  try:
2091
2197
  page.goto(MESSAGING_URL, wait_until="domcontentloaded")
2092
2198
  except Exception:
2093
2199
  pass
2094
- time.sleep(6)
2095
- for i in range(MESSAGE_SCROLLS):
2200
+ wait(6)
2201
+ read_new()
2202
+ scrolls = MESSAGE_HISTORY_SCROLLS if full_history else MESSAGE_SCROLLS
2203
+ new_per_scroll = []
2204
+ for i in range(scrolls):
2096
2205
  if stop_requested() or _window_closed(page):
2097
2206
  break
2098
2207
  try:
@@ -2105,19 +2214,21 @@ def sync_messages(headless=False):
2105
2214
  if (l) l.scrollTop = l.scrollHeight; }""")
2106
2215
  except Exception:
2107
2216
  pass
2108
- print(f" Reading the list ({i + 1} of {MESSAGE_SCROLLS})...", flush=True)
2109
- time.sleep(4)
2110
- convos = []
2111
- for r in kept:
2112
- try:
2113
- data = r.json()
2114
- except Exception:
2115
- continue
2116
- _wire_sample(data, prefix="messages")
2117
- convos.extend(voyager_conversations(data))
2217
+ wait(MESSAGE_WAIT)
2218
+ new_per_scroll.append(read_new())
2219
+ if full_history:
2220
+ print(f" Reading the list (scroll {i + 1}, {len(seen):,} conversations so far)...", flush=True)
2221
+ why = history_done(new_per_scroll, len(seen))
2222
+ if why:
2223
+ print(f" Stopping: {why}.", flush=True)
2224
+ break
2225
+ else:
2226
+ print(f" Reading the list ({i + 1} of {scrolls})...", flush=True)
2118
2227
  browser.close()
2119
- _me, people = live_people(convos)
2120
- print(f" Found {len(convos)} conversations in LinkedIn's data, {len(people)} with one person.")
2228
+ convos = list(seen.values())
2229
+ me, people = live_people(convos)
2230
+ groups = live_groups(convos, me)
2231
+ print(f" Found {len(convos)} conversations in LinkedIn's data: {len(people)} with one person, {len(groups)} groups.")
2121
2232
  # The newest message's words go to the app only if Keep my messages is on
2122
2233
  # there. The app checks too; this way they don't even leave the scanner.
2123
2234
  keep = False
@@ -2131,14 +2242,37 @@ def sync_messages(headless=False):
2131
2242
  if v.get("preview"):
2132
2243
  v["preview"]["text"] = None
2133
2244
  try:
2134
- r = requests.put(f"{APP_URL}/api/social", json={"live": people}, headers=app_headers(), timeout=30)
2245
+ r = requests.put(f"{APP_URL}/api/social", json={"live": people, "groups": groups}, headers=app_headers(), timeout=60)
2135
2246
  got = r.json() if r.ok else {}
2136
2247
  matched, unmatched = got.get("people", 0), got.get("unmatched", 0)
2137
- print(f" Saved for the Social tab: {matched} matched to your connections, {unmatched} not.")
2248
+ print(f" Saved for the Social tab: {matched} of your connections, {unmatched} people who aren't connections, "
2249
+ f"{len(groups)} groups.")
2138
2250
  except Exception as e:
2139
2251
  print(f" Couldn't save them: {e}")
2140
2252
 
2141
2253
 
2254
+ def live_groups(convos, me):
2255
+ """The messages sync's group conversations: who's in them (everyone but you,
2256
+ with their names for the app to match to your connections; it keeps the names
2257
+ only of people who aren't), when each was last active, unread, its link and
2258
+ whether someone else wrote last. No words: a group's newest message isn't sent."""
2259
+ groups = {}
2260
+ for c in convos:
2261
+ others = [u for u in c["people"] if u != me]
2262
+ if len(others) < 2:
2263
+ continue
2264
+ k = conversation_id(c)
2265
+ if k in groups and c["at"] <= groups[k]["last"]:
2266
+ continue
2267
+ latest = c.get("latest")
2268
+ sender = latest.get("from") if latest else None
2269
+ groups[k] = {"threadUrl": c.get("url"), "people": others,
2270
+ "names": {u: n for u, n in (c.get("names") or {}).items() if u in others},
2271
+ "last": c["at"], "unread": c["unread"],
2272
+ "lastFromThem": (sender != me) if sender in c["people"] else None}
2273
+ return list(groups.values())
2274
+
2275
+
2142
2276
  def live_people(convos):
2143
2277
  """The messages sync's conversations, per person: you are the one in the most
2144
2278
  conversations, and only the 1:1 ones are kept. For each person, their newest
@@ -3155,7 +3289,7 @@ def scrape_bridge(bridge_name, headless=False, max_pages=LINKEDIN_MAX_PAGES, dee
3155
3289
  if not urn and profiles_left() <= 0:
3156
3290
  raise BudgetReached(0, "profiles")
3157
3291
 
3158
- print(f"\n=== {'Carrying on with' if start_page > 1 else 'Scraping'} connections of {bridge_name} ===\n")
3292
+ print(f"\n=== {'Carrying on with' if start_page > 1 else 'Scanning'} connections of {bridge_name} ===\n")
3159
3293
 
3160
3294
  read = []
3161
3295
 
@@ -3267,7 +3401,7 @@ def scrape_company(company_name, headless=False, log_fn=None):
3267
3401
  if log_fn:
3268
3402
  log_fn(msg)
3269
3403
 
3270
- log(f"Scraping employees at: {company_name}")
3404
+ log(f"Scanning employees at: {company_name}")
3271
3405
 
3272
3406
  # A company scan is people searches too: the same cooldown and budget.
3273
3407
  cd = read_cooldown()
@@ -3944,7 +4078,7 @@ def auto_bridge_all(headless=False, log_fn=None, retry_private=False, max_bridge
3944
4078
 
3945
4079
  def rescrape_bridge(bridge_name, headless=False, max_pages=LINKEDIN_MAX_PAGES):
3946
4080
  """Delete all existing cluster data for a bridge and re-scrape from scratch."""
3947
- print(f"\n=== Re-scraping {bridge_name} (delete + fresh scrape) ===\n")
4081
+ print(f"\n=== Rescanning {bridge_name} (delete + fresh scan) ===\n")
3948
4082
 
3949
4083
  # Check before deleting anything: a cooldown or an empty budget would leave
3950
4084
  # their circle deleted and not read again.
@@ -4207,6 +4341,9 @@ Examples:
4207
4341
  parser.add_argument("--company", type=str, help="Scan everyone the app can see at one company")
4208
4342
  parser.add_argument("--messages", action="store_true",
4209
4343
  help="Experimental: read your messages list once for the Social tab (who, when, unread; the newest message's words only if Keep my messages is on)")
4344
+ parser.add_argument("--full-history", action="store_true",
4345
+ help=f"With --messages: keep scrolling until the whole list has loaded (at most {MESSAGE_HISTORY_SCROLLS} scrolls "
4346
+ f"or {MESSAGE_HISTORY_MAX:,} conversations), not just its top")
4210
4347
  parser.add_argument("--experimental", action="store_true",
4211
4348
  help="Auto-Bridge, experimental: all-day pacing (sittings, rests, daytime only, "
4212
4349
  "waits for the daily budget) and reading LinkedIn's own data beside the page text")
@@ -4245,11 +4382,16 @@ Examples:
4245
4382
  EXPERIMENT["on"] = True
4246
4383
  print("Experimental Auto-Bridge: all-day pacing, and LinkedIn's own data read beside the page text.")
4247
4384
  args.max_pages = max(1, min(LINKEDIN_MAX_PAGES, args.max_pages))
4385
+ # The Scan page's speed. Signing in searches nothing, so it isn't said there.
4386
+ _pace = apply_pace()
4387
+ if not args.login:
4388
+ print(f"Speed: {_pace.capitalize()}. {PAGE_PAUSE}s before each page of results, {CHUNK_COOLDOWN}s more after "
4389
+ f"every {SAVE_EVERY_PAGES}, and profiles at least {PROFILE_GAP}s apart.")
4248
4390
 
4249
4391
  # A failed save ends the run with its reason, not a traceback (TRAPS §32).
4250
4392
  def _say_why(exc_type, exc, tb):
4251
4393
  if issubclass(exc_type, SaveFailed):
4252
- print(f"\n The app could not save what was scraped: {exc}\n", file=sys.stderr)
4394
+ print(f"\n The app could not save what was scanned: {exc}\n", file=sys.stderr)
4253
4395
  elif issubclass(exc_type, TryLater):
4254
4396
  print(f"\n The photos weren't saved: {exc}.\n", file=sys.stderr)
4255
4397
  elif issubclass(exc_type, NotSignedIn):
@@ -4305,7 +4447,7 @@ Examples:
4305
4447
  done = sum(1 for r in results if r.get("status") == "done")
4306
4448
  print(f"\nDone. {done}/{len(results)} bridges mapped.")
4307
4449
  elif args.messages:
4308
- sync_messages(headless=args.headless)
4450
+ sync_messages(headless=args.headless, full_history=args.full_history)
4309
4451
  elif args.search:
4310
4452
  scrape_full(headless=args.headless)
4311
4453
  elif args.full:
@@ -1,8 +0,0 @@
1
- module.exports=[70406,(e,r,t)=>{r.exports=e.x("next/dist/compiled/@opentelemetry/api",()=>require("next/dist/compiled/@opentelemetry/api"))},18622,(e,r,t)=>{r.exports=e.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},56704,(e,r,t)=>{r.exports=e.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(e,r,t)=>{r.exports=e.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},59043,(e,r,t)=>{r.exports=e.x("next/dist/server/runtime-reacts.external.js",()=>require("next/dist/server/runtime-reacts.external.js"))},93695,(e,r,t)=>{r.exports=e.x("next/dist/shared/lib/no-fallback-error.external.js",()=>require("next/dist/shared/lib/no-fallback-error.external.js"))},74533,(e,r,t)=>{r.exports=e.x("node:child_process",()=>require("node:child_process"))},66680,(e,r,t)=>{r.exports=e.x("node:crypto",()=>require("node:crypto"))},2157,(e,r,t)=>{r.exports=e.x("node:fs",()=>require("node:fs"))},12714,(e,r,t)=>{r.exports=e.x("node:fs/promises",()=>require("node:fs/promises"))},60526,(e,r,t)=>{r.exports=e.x("node:os",()=>require("node:os"))},50227,(e,r,t)=>{r.exports=e.x("node:path",()=>require("node:path"))},81111,(e,r,t)=>{r.exports=e.x("node:stream",()=>require("node:stream"))},93836,19307,e=>{"use strict";e.i(68835);var r=e.i(1746);let t=e=>String(e||"").split(",")[0].normalize("NFD").replace(/[̀-ͯ]/g,"").toLowerCase().replace(/[^a-z0-9 ]+/g," ").replace(/\s+/g," ").trim(),n=/^https:\/\/www\.linkedin\.com\/messaging\/[\w\-/%=.]*$/;e.s(["conversationList",0,function(e=[],r={}){let t=(Array.isArray(e)?e:[]).map(e=>({...e,unread:null,threadUrl:null})),n=new Map;for(let e of t){if(e.group||e.people?.length!==1)continue;let r=n.get(e.people[0]);(!r||(e.last||0)>(r.last||0))&&n.set(e.people[0],e)}for(let[e,s]of Object.entries(r||{})){if(!s||"object"!=typeof s)continue;let r=n.get(e);r||(r={id:`live:${e}`,people:[e],group:!1,last:null,lastFromThem:null,count:null,unread:null,threadUrl:null},t.push(r),n.set(e,r)),s.last&&(!r.last||s.last>r.last)&&(r.last=s.last,"boolean"==typeof s.lastFromThem&&(r.lastFromThem=s.lastFromThem)),r.unread=Number.isFinite(s.unread)?s.unread:r.unread,r.threadUrl=s.threadUrl||r.threadUrl}return t},"matchLive",0,function(e={},s=[]){let i=new Map,l=new Map;for(let e of s){let n=(0,r.keyFor)(e);i.set(n,n);let s=t(e.name);s&&l.set(s,l.has(s)?null:n)}let o={},a=0;for(let[s,u]of Object.entries(e||{})){if(!u||"object"!=typeof u)continue;let e=i.get((0,r.keyFor)({profile_url:s}))||l.get(t(u.name))||null;if(!e){a+=1;continue}let c=Number(u.last)||null;o[e]&&(o[e].last||0)>=(c||0)||(o[e]={last:c,unread:Number.isFinite(Number(u.unread))&&null!==u.unread?Number(u.unread):null,threadUrl:function(e){let r=String(e||"").trim();return n.test(r)?r:null}(u.threadUrl),lastFromThem:"boolean"==typeof u.lastFromThem?u.lastFromThem:null,preview:function(e){if(!e||"object"!=typeof e)return null;let r=Number(e.t)||null,t="string"==typeof e.text?e.text.slice(0,2e4):null;return r||t?{t:r,fromMe:!0===e.fromMe,text:t}:null}(u.preview)})}return{live:o,unmatched:a}}],93836);var s=e.i(2157),i=e.i(50227),l=e.i(88431),o=e.i(88602);let a={file:null,mtimeMs:0,size:0,data:null};function u(e,r=null){try{return JSON.parse((0,s.readFileSync)(e,"utf8"))??r}catch{return r}}e.s(["deleteMessages",0,function(e){e&&((0,s.rmSync)(e.messages,{force:!0}),(0,s.rmSync)(e.incoming,{force:!0}),a.file=null)},"readJson",0,u,"readMessages",0,function(e){let r;try{r=(0,s.statSync)(e)}catch{return{threads:{},previews:{}}}if(a.file===e&&a.mtimeMs===r.mtimeMs&&a.size===r.size)return a.data;let t=u(e,{}),n={threads:t?.threads&&"object"==typeof t.threads?t.threads:{},previews:t?.previews&&"object"==typeof t.previews?t.previews:{}};return Object.assign(a,{file:e,mtimeMs:r.mtimeMs,size:r.size,data:n}),n},"socialFiles",0,function(){let e=(0,o.resolveProfile)({create:!1});if(!e?.id||!/^[\w-]+$/.test(String(e.id)))return null;let r=(0,l.dataDir)(),t=i.default.join(r,`social-messages-${e.id}.json`);return{profileId:e.id,social:i.default.join(r,`social-${e.id}.json`),messages:t,incoming:`${t}.incoming`}},"writeJsonAtomic",0,function(e,r){let t=`${e}.${process.pid}.tmp`;(0,s.writeFileSync)(t,JSON.stringify(r)),(0,s.renameSync)(t,e),a.file===e&&(a.file=null)}],19307)},88602,e=>{"use strict";var r=e.i(88431);e.s(["networkCounts",0,function(e){let t=Object.fromEntries((0,r.getDb)().prepare("SELECT degree, COUNT(*) AS n FROM linkedin_connections WHERE user_id = ? GROUP BY degree").all(e).map(e=>[e.degree,e.n]));return{first:t[1]||0,second:t[2]||0,third:t[3]||0}},"resolveProfile",0,function({create:e=!0}={}){let[t]=(0,r.getDb)().prepare(`
2
- SELECT u.*,
3
- (SELECT COUNT(*) FROM linkedin_connections c WHERE c.user_id = u.id) AS connections
4
- FROM users u
5
- ORDER BY connections DESC, u.created_at ASC, u.rowid ASC
6
- `).all().map(e=>(0,r.decodeRow)("users",e));if(t)return t;if(!e)return null;let n=(0,r.newId)();return(0,r.getDb)().prepare("INSERT INTO users (id, name) VALUES (?, ?)").run(n,"You"),{...(0,r.decodeRow)("users",(0,r.getDb)().prepare("SELECT * FROM users WHERE id = ?").get(n)),connections:0}}])},12506,1348,e=>{"use strict";e.s(["compareVersions",0,function(e,r){let t=e=>String(e||"").replace(/^v/i,"").split(/[-+]/)[0].split(".").map(e=>Number.parseInt(e,10)||0),n=t(e),s=t(r);for(let e=0;e<Math.max(n.length,s.length);e++){let r=(n[e]||0)-(s[e]||0);if(0!==r)return r>0?1:-1}return 0},"installKind",0,function(e=process.env,r=""){return["mac-app","source","npm"].includes(e.SIX_DEGREES_INSTALL)?e.SIX_DEGREES_INSTALL:/\.app\/Contents\/Resources/.test(r||"")?"mac-app":"npm"},"repoSlug",0,function(e){let r=("string"==typeof e?e:e?.url||"").match(/github\.com[/:]([^/]+)\/([^/.#?]+)/);return r?`${r[1]}/${r[2]}`:null},"updateCommand",0,function(e,r,{dataDir:t}={}){if("mac-app"===e)return`curl -fsSL https://raw.githubusercontent.com/${r}/main/install.sh | bash`;if("source"===e)return"git pull && npm ci && npm run build && npm run start:packaged";let n=t&&`'${String(t).replace(/'/g,"'\\''")}'`;return n?`npx six-degrees@latest --data-dir ${n}`:"npx six-degrees@latest"}],12506);let r=Symbol.for("six-degrees.scan-state");function t(){let e=globalThis[r];return e?.running!==!0?null:"string"==typeof e.action&&e.action?e.action:"scan"}function n(e){switch(e){case"install":return"The scanner's packages are being installed";case"setup":return"The scanner is being set up";case"login":return"The LinkedIn sign-in window is open";case"photos":return"Profile photos are being saved";default:return"A scan is running"}}e.s(["busyRefusal",0,function(e,r=t()){return r?`${n(r)}. ${e}`:null},"registerScanState",0,function(e){return globalThis[r]||(globalThis[r]=e),globalThis[r]},"runningNow",0,n,"scannerJob",0,t],1348)},1746,e=>{"use strict";e.s(["keyFor",0,function(e){return String(e?.profile_url||"").split(/[?#]/)[0].replace(/\/+$/,"")||`id:${e?.id}`}])}];
7
-
8
- //# sourceMappingURL=%5Broot-of-the-server%5D__08f5awg._.js.map