six-degrees 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +11 -11
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/import.html +1 -1
- package/.next/standalone/.next/server/app/import.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/launch.html +2 -2
- package/.next/standalone/.next/server/app/launch.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths.html +1 -1
- package/.next/standalone/.next/server/app/paths.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/profile.html +1 -1
- package/.next/standalone/.next/server/app/profile.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/queue.html +1 -1
- package/.next/standalone/.next/server/app/queue.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup.html +1 -1
- package/.next/standalone/.next/server/app/setup.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jrmmj-._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qx548g._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1an3leo._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1m8zedk._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +2 -2
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h1_f_2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bf3x4q._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1djpk8t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_import_page_1fmu6_l.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/0jk64vo17avsb.js +1 -0
- package/.next/standalone/.next/static/chunks/0s65dsrthylo6.js +2 -0
- package/.next/standalone/.next/static/chunks/0uyfdhi8dek85.js +16 -0
- package/.next/standalone/.next/static/chunks/{1zmpcmngg5c7-.js → 229haorf0f19b.js} +1 -1
- package/.next/{static/chunks/3ts-_4i6bfkh2.js → standalone/.next/static/chunks/2g54uafnwuakc.js} +1 -1
- package/.next/standalone/.next/static/chunks/2ot61qsv6_0eq.js +1 -0
- package/.next/{static/chunks/3pfyuezyqd9ob.js → standalone/.next/static/chunks/2spwqs6b6dx13.js} +1 -1
- package/.next/{static/chunks/1odow92u6wxz0.js → standalone/.next/static/chunks/38dlepuubiox0.js} +1 -1
- package/.next/standalone/AGENTS.md +10 -0
- package/.next/standalone/CHANGELOG.md +41 -0
- package/.next/standalone/CONTRIBUTING.md +9 -0
- package/.next/standalone/README.md +49 -29
- package/.next/standalone/SECURITY.md +12 -9
- package/.next/standalone/app/api/scraper/route.js +4 -3
- package/.next/standalone/app/api/update/route.js +10 -2
- package/.next/standalone/app/components/ForceGraph.js +1 -1
- package/.next/standalone/app/components/Sidebar.js +8 -8
- package/.next/standalone/app/components/UpdatePanel.js +39 -8
- package/.next/standalone/app/import/page.js +3 -3
- package/.next/standalone/app/layout.js +2 -2
- package/.next/standalone/app/page.js +2 -2
- package/.next/standalone/app/paths/page.js +1 -1
- package/.next/standalone/app/profile/page.js +0 -178
- package/.next/standalone/app/setup/page.js +4 -4
- package/.next/standalone/bin/six-degrees.mjs +7 -2
- package/.next/standalone/install.sh +1 -1
- package/.next/standalone/lib/csv.js +53 -27
- package/.next/standalone/lib/release.js +5 -2
- package/.next/standalone/lib/scraper-client.js +3 -3
- package/.next/standalone/package-lock.json +103 -9
- package/.next/standalone/package.json +12 -10
- package/.next/standalone/public/demo-data.json +1 -1
- package/.next/standalone/scripts/gen-synthetic.mjs +16 -1
- package/.next/standalone/scripts/readme_buttons.py +89 -0
- package/.next/standalone/scripts/screenshots.py +2 -0
- package/.next/standalone/site/app.js +84 -0
- package/.next/standalone/site/index.html +221 -0
- package/.next/standalone/site/style.css +155 -0
- package/.next/standalone/site/video/install-mac-poster.jpg +0 -0
- package/.next/standalone/site/video/install-mac.mp4 +0 -0
- package/.next/standalone/site/video/linux-poster.jpg +0 -0
- package/.next/standalone/site/video/linux.mp4 +0 -0
- package/.next/standalone/site/video/scan-poster.jpg +0 -0
- package/.next/standalone/site/video/scan.mp4 +0 -0
- package/.next/static/chunks/0jk64vo17avsb.js +1 -0
- package/.next/static/chunks/0s65dsrthylo6.js +2 -0
- package/.next/static/chunks/0uyfdhi8dek85.js +16 -0
- package/.next/static/chunks/{1zmpcmngg5c7-.js → 229haorf0f19b.js} +1 -1
- package/.next/{standalone/.next/static/chunks/3ts-_4i6bfkh2.js → static/chunks/2g54uafnwuakc.js} +1 -1
- package/.next/static/chunks/2ot61qsv6_0eq.js +1 -0
- package/.next/{standalone/.next/static/chunks/3pfyuezyqd9ob.js → static/chunks/2spwqs6b6dx13.js} +1 -1
- package/.next/{standalone/.next/static/chunks/1odow92u6wxz0.js → static/chunks/38dlepuubiox0.js} +1 -1
- package/CHANGELOG.md +41 -0
- package/README.md +49 -29
- package/SECURITY.md +12 -9
- package/bin/six-degrees.mjs +7 -2
- package/lib/csv.js +53 -27
- package/lib/release.js +5 -2
- package/lib/scraper-client.js +3 -3
- package/package.json +12 -10
- package/public/demo-data.json +1 -1
- package/.next/standalone/.next/static/chunks/1wb_is4wsdm-u.js +0 -1
- package/.next/standalone/.next/static/chunks/2rrf5kzx2fhin.js +0 -2
- package/.next/standalone/.next/static/chunks/2s_k9x4d0gi9h.js +0 -16
- package/.next/standalone/.next/static/chunks/3hsi90cejq2dg.js +0 -1
- package/.next/static/chunks/1wb_is4wsdm-u.js +0 -1
- package/.next/static/chunks/2rrf5kzx2fhin.js +0 -2
- package/.next/static/chunks/2s_k9x4d0gi9h.js +0 -16
- package/.next/static/chunks/3hsi90cejq2dg.js +0 -1
- /package/.next/standalone/.next/static/{N9wokeIiC9rUIGmzBhj6T → ZGTiDrZ5NHGETgGdlhssu}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{N9wokeIiC9rUIGmzBhj6T → ZGTiDrZ5NHGETgGdlhssu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{N9wokeIiC9rUIGmzBhj6T → ZGTiDrZ5NHGETgGdlhssu}/_ssgManifest.js +0 -0
- /package/.next/static/{N9wokeIiC9rUIGmzBhj6T → ZGTiDrZ5NHGETgGdlhssu}/_buildManifest.js +0 -0
- /package/.next/static/{N9wokeIiC9rUIGmzBhj6T → ZGTiDrZ5NHGETgGdlhssu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{N9wokeIiC9rUIGmzBhj6T → ZGTiDrZ5NHGETgGdlhssu}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
-
import { runScrape, scraperStatus, notReadyMessage } from '../../lib/scraper-client';
|
|
5
4
|
import { loadNetwork } from '../../lib/network';
|
|
6
5
|
import MappingProgress from '../components/MappingProgress';
|
|
7
6
|
import { IS_DEMO } from '../../lib/demo';
|
|
@@ -33,10 +32,6 @@ function ProfileInner() {
|
|
|
33
32
|
const [notifications, setNotifications] = useState([]);
|
|
34
33
|
const [queueStats, setQueueStats] = useState({ total: 0, sent: 0, accepted: 0 });
|
|
35
34
|
const [mapping, setMapping] = useState({ degree1: [], degree2: [], skips: [] });
|
|
36
|
-
const [setupStatus, setSetupStatus] = useState('idle');
|
|
37
|
-
const [setupLog, setSetupLog] = useState([]);
|
|
38
|
-
const [bridgeStatus, setBridgeStatus] = useState('idle');
|
|
39
|
-
const [bridgeLog, setBridgeLog] = useState([]);
|
|
40
35
|
|
|
41
36
|
useEffect(() => {
|
|
42
37
|
if (IS_DEMO) return;
|
|
@@ -207,104 +202,6 @@ function ProfileInner() {
|
|
|
207
202
|
</div>
|
|
208
203
|
</div>
|
|
209
204
|
|
|
210
|
-
{/* === SET UP ACCOUNT === */}
|
|
211
|
-
<SetupScrapeCard status={setupStatus} log={setupLog} onStart={async () => {
|
|
212
|
-
setSetupStatus('running');
|
|
213
|
-
setSetupLog(['Checking the scraper...']);
|
|
214
|
-
const blocked = notReadyMessage(await scraperStatus().catch(() => null));
|
|
215
|
-
if (blocked) {
|
|
216
|
-
setSetupStatus('offline');
|
|
217
|
-
setSetupLog([blocked]);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
try {
|
|
221
|
-
const final = await runScrape('full', { onLog: setSetupLog });
|
|
222
|
-
setSetupStatus(final.exitCode === 0 ? 'done' : 'error');
|
|
223
|
-
} catch { setSetupStatus('error'); }
|
|
224
|
-
}} onRetry={() => { setSetupStatus('idle'); setSetupLog([]); }} />
|
|
225
|
-
|
|
226
|
-
{/* === AUTO-BRIDGE ALL === */}
|
|
227
|
-
<div style={{
|
|
228
|
-
background: 'rgba(155,89,182,0.06)', borderRadius: 12, padding: 20, marginBottom: 24,
|
|
229
|
-
border: '1px solid rgba(155,89,182,0.2)',
|
|
230
|
-
}}>
|
|
231
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
|
232
|
-
<h3 style={{ fontSize: 14, fontWeight: 700, margin: 0, color: '#9B59B6' }}>Auto-Bridge All</h3>
|
|
233
|
-
{bridgeStatus === 'done' && <span style={{ fontSize: 10, color: '#00ff88', fontWeight: 600 }}>Complete</span>}
|
|
234
|
-
</div>
|
|
235
|
-
<p style={{ fontSize: 11, color: '#888', margin: '0 0 12px' }}>
|
|
236
|
-
Scan every connection's network. S-tier first → D-tier last. 2 min cooldown between each.
|
|
237
|
-
</p>
|
|
238
|
-
|
|
239
|
-
{bridgeStatus === 'idle' && (
|
|
240
|
-
<button onClick={async () => {
|
|
241
|
-
setBridgeStatus('running');
|
|
242
|
-
setBridgeLog(['Checking the scraper...']);
|
|
243
|
-
const blocked = notReadyMessage(await scraperStatus().catch(() => null));
|
|
244
|
-
if (blocked) {
|
|
245
|
-
setBridgeStatus('offline');
|
|
246
|
-
setBridgeLog([blocked]);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
try {
|
|
250
|
-
const final = await runScrape('auto-bridge', { onLog: setBridgeLog });
|
|
251
|
-
setBridgeStatus(final.exitCode === 0 ? 'done' : 'error');
|
|
252
|
-
} catch { setBridgeStatus('error'); }
|
|
253
|
-
}} style={{
|
|
254
|
-
width: '100%', padding: '12px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
255
|
-
background: 'linear-gradient(135deg, #9B59B6, #FF6B35)',
|
|
256
|
-
color: '#fff', fontWeight: 700, fontSize: 14,
|
|
257
|
-
}}>
|
|
258
|
-
Auto-Bridge All Connections
|
|
259
|
-
</button>
|
|
260
|
-
)}
|
|
261
|
-
|
|
262
|
-
{bridgeStatus === 'offline' && (
|
|
263
|
-
<div>
|
|
264
|
-
<div style={{ padding: '10px', borderRadius: 8, background: 'rgba(255,80,80,0.1)', border: '1px solid rgba(255,80,80,0.3)', color: '#ff5050', fontSize: 12, marginBottom: 8, textAlign: 'center' }}>
|
|
265
|
-
Scraper offline — start it first
|
|
266
|
-
</div>
|
|
267
|
-
<button onClick={() => setBridgeStatus('idle')} style={{
|
|
268
|
-
width: '100%', padding: '10px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
269
|
-
background: 'rgba(255,255,255,0.1)', color: '#aaa', fontWeight: 600, fontSize: 12,
|
|
270
|
-
}}>Retry</button>
|
|
271
|
-
</div>
|
|
272
|
-
)}
|
|
273
|
-
|
|
274
|
-
{bridgeStatus === 'running' && (
|
|
275
|
-
<div style={{
|
|
276
|
-
padding: '10px', borderRadius: 8, background: 'rgba(155,89,182,0.1)',
|
|
277
|
-
border: '1px solid rgba(155,89,182,0.3)',
|
|
278
|
-
}}>
|
|
279
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
|
|
280
|
-
<div style={{ width: 8, height: 8, borderRadius: '50%', background: '#9B59B6', animation: 'pulse 1s infinite' }} />
|
|
281
|
-
<span style={{ fontSize: 11, fontWeight: 600, color: '#9B59B6' }}>Bridging...</span>
|
|
282
|
-
</div>
|
|
283
|
-
<div style={{ fontFamily: 'monospace', fontSize: 9, color: '#777', maxHeight: 150, overflow: 'auto' }}>
|
|
284
|
-
{bridgeLog.map((l, i) => <div key={i}>{l}</div>)}
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
)}
|
|
288
|
-
|
|
289
|
-
{bridgeStatus === 'done' && (
|
|
290
|
-
<div style={{ padding: '10px', borderRadius: 8, background: 'rgba(0,255,136,0.1)', border: '1px solid rgba(0,255,136,0.3)', color: '#00ff88', fontSize: 12, fontWeight: 600, textAlign: 'center' }}>
|
|
291
|
-
All bridges mapped! Refresh the app.
|
|
292
|
-
</div>
|
|
293
|
-
)}
|
|
294
|
-
|
|
295
|
-
{bridgeStatus === 'error' && (
|
|
296
|
-
<div>
|
|
297
|
-
<div style={{ padding: '10px', borderRadius: 8, background: 'rgba(255,80,80,0.1)', border: '1px solid rgba(255,80,80,0.3)', color: '#ff5050', fontSize: 12, textAlign: 'center', marginBottom: 8 }}>
|
|
298
|
-
Something went wrong
|
|
299
|
-
</div>
|
|
300
|
-
<button onClick={() => { setBridgeStatus('idle'); setBridgeLog([]); }} style={{
|
|
301
|
-
width: '100%', padding: '10px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
302
|
-
background: 'rgba(255,255,255,0.1)', color: '#aaa', fontWeight: 600, fontSize: 12,
|
|
303
|
-
}}>Try Again</button>
|
|
304
|
-
</div>
|
|
305
|
-
)}
|
|
306
|
-
</div>
|
|
307
|
-
|
|
308
205
|
{/* === 2ND-DEGREE MAPPING PROGRESS === */}
|
|
309
206
|
<MappingProgress
|
|
310
207
|
degree1={mapping.degree1}
|
|
@@ -495,78 +392,3 @@ function StatCard({ label, value, color }) {
|
|
|
495
392
|
</div>
|
|
496
393
|
);
|
|
497
394
|
}
|
|
498
|
-
|
|
499
|
-
function SetupScrapeCard({ status, log, onStart, onRetry }) {
|
|
500
|
-
return (
|
|
501
|
-
<div style={{
|
|
502
|
-
background: 'rgba(52,152,219,0.06)', borderRadius: 12, padding: 20, marginBottom: 24,
|
|
503
|
-
border: '1px solid rgba(52,152,219,0.2)',
|
|
504
|
-
}}>
|
|
505
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
|
506
|
-
<h3 style={{ fontSize: 14, fontWeight: 700, margin: 0, color: '#3498DB' }}>Account Setup</h3>
|
|
507
|
-
{status === 'done' && <span style={{ fontSize: 10, color: '#00ff88', fontWeight: 600 }}>Complete</span>}
|
|
508
|
-
</div>
|
|
509
|
-
<p style={{ fontSize: 11, color: '#888', margin: '0 0 12px' }}>
|
|
510
|
-
Full scrape of all your LinkedIn connections with profile photos. Takes 3-5 minutes.
|
|
511
|
-
</p>
|
|
512
|
-
|
|
513
|
-
{status === 'idle' && (
|
|
514
|
-
<button onClick={onStart} style={{
|
|
515
|
-
width: '100%', padding: '12px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
516
|
-
background: 'linear-gradient(135deg, #3498DB, #9B59B6)',
|
|
517
|
-
color: '#fff', fontWeight: 700, fontSize: 14,
|
|
518
|
-
}}>
|
|
519
|
-
Set Up Account — Full Scrape
|
|
520
|
-
</button>
|
|
521
|
-
)}
|
|
522
|
-
|
|
523
|
-
{status === 'offline' && (
|
|
524
|
-
<div>
|
|
525
|
-
<div style={{ padding: '10px', borderRadius: 8, background: 'rgba(255,80,80,0.1)', border: '1px solid rgba(255,80,80,0.3)', color: '#ff5050', fontSize: 12, marginBottom: 8, textAlign: 'center' }}>
|
|
526
|
-
Scraper server offline
|
|
527
|
-
</div>
|
|
528
|
-
<p style={{ fontSize: 10, color: '#888', textAlign: 'center', marginBottom: 8 }}>
|
|
529
|
-
Open the <strong>Scan</strong> page to finish setting the scraper up
|
|
530
|
-
</p>
|
|
531
|
-
<button onClick={onRetry} style={{
|
|
532
|
-
width: '100%', padding: '10px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
533
|
-
background: 'rgba(255,255,255,0.1)', color: '#aaa', fontWeight: 600, fontSize: 12,
|
|
534
|
-
}}>Retry</button>
|
|
535
|
-
</div>
|
|
536
|
-
)}
|
|
537
|
-
|
|
538
|
-
{status === 'running' && (
|
|
539
|
-
<div style={{
|
|
540
|
-
padding: '10px', borderRadius: 8, background: 'rgba(52,152,219,0.1)',
|
|
541
|
-
border: '1px solid rgba(52,152,219,0.3)',
|
|
542
|
-
}}>
|
|
543
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
|
|
544
|
-
<div style={{ width: 8, height: 8, borderRadius: '50%', background: '#3498DB', animation: 'pulse 1s infinite' }} />
|
|
545
|
-
<span style={{ fontSize: 11, fontWeight: 600, color: '#3498DB' }}>Scanning all connections...</span>
|
|
546
|
-
</div>
|
|
547
|
-
<div style={{ fontFamily: 'monospace', fontSize: 10, color: '#888', maxHeight: 120, overflow: 'auto' }}>
|
|
548
|
-
{log.map((l, i) => <div key={i}>{l}</div>)}
|
|
549
|
-
</div>
|
|
550
|
-
</div>
|
|
551
|
-
)}
|
|
552
|
-
|
|
553
|
-
{status === 'done' && (
|
|
554
|
-
<div style={{ padding: '10px', borderRadius: 8, background: 'rgba(0,255,136,0.1)', border: '1px solid rgba(0,255,136,0.3)', color: '#00ff88', fontSize: 12, fontWeight: 600, textAlign: 'center' }}>
|
|
555
|
-
All connections scraped with images! Refresh the app to see your network.
|
|
556
|
-
</div>
|
|
557
|
-
)}
|
|
558
|
-
|
|
559
|
-
{status === 'error' && (
|
|
560
|
-
<div>
|
|
561
|
-
<div style={{ padding: '10px', borderRadius: 8, background: 'rgba(255,80,80,0.1)', border: '1px solid rgba(255,80,80,0.3)', color: '#ff5050', fontSize: 12, textAlign: 'center', marginBottom: 8 }}>
|
|
562
|
-
Something went wrong
|
|
563
|
-
</div>
|
|
564
|
-
<button onClick={onRetry} style={{
|
|
565
|
-
width: '100%', padding: '10px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
566
|
-
background: 'rgba(255,255,255,0.1)', color: '#aaa', fontWeight: 600, fontSize: 12,
|
|
567
|
-
}}>Try Again</button>
|
|
568
|
-
</div>
|
|
569
|
-
)}
|
|
570
|
-
</div>
|
|
571
|
-
);
|
|
572
|
-
}
|
|
@@ -15,7 +15,7 @@ const BG = '#0a0a1a';
|
|
|
15
15
|
const LINE = '1px solid rgba(255,255,255,0.1)';
|
|
16
16
|
|
|
17
17
|
const ACTION_LABELS = {
|
|
18
|
-
install: 'Installing the
|
|
18
|
+
install: 'Installing the scanner',
|
|
19
19
|
login: 'Waiting for you to sign in',
|
|
20
20
|
full: 'Scanning your whole network',
|
|
21
21
|
refresh: 'Checking for new connections',
|
|
@@ -183,7 +183,7 @@ function SetupInner() {
|
|
|
183
183
|
|
|
184
184
|
{notFound && (
|
|
185
185
|
<Box tone="bad">
|
|
186
|
-
<b>Can’t find the
|
|
186
|
+
<b>Can’t find the scanner files.</b><br />
|
|
187
187
|
<span style={{ color: '#9aa' }}>
|
|
188
188
|
Expected <code style={code}>scripts/scrape.py</code> next to the app. If you
|
|
189
189
|
downloaded a zip, run the app from inside the project folder.
|
|
@@ -195,7 +195,7 @@ function SetupInner() {
|
|
|
195
195
|
<Box tone="bad">
|
|
196
196
|
<b>Google Chrome isn’t installed.</b><br />
|
|
197
197
|
<span style={{ color: '#9aa' }}>
|
|
198
|
-
The
|
|
198
|
+
The scanner drives your real Chrome. Install it from{' '}
|
|
199
199
|
<a href="https://www.google.com/chrome/" target="_blank" rel="noreferrer"
|
|
200
200
|
style={{ color: '#3498DB' }}>google.com/chrome</a>, then reload this page.
|
|
201
201
|
</span>
|
|
@@ -206,7 +206,7 @@ function SetupInner() {
|
|
|
206
206
|
<Step
|
|
207
207
|
n={1}
|
|
208
208
|
done={!!c.dependencies}
|
|
209
|
-
title="Install what the
|
|
209
|
+
title="Install what the scanner needs"
|
|
210
210
|
body={
|
|
211
211
|
c.dependencies
|
|
212
212
|
? 'Installed.'
|
|
@@ -84,10 +84,15 @@ function freePort(start, attempts = 20) {
|
|
|
84
84
|
function openBrowser(url) {
|
|
85
85
|
const cmd = platform() === 'darwin' ? 'open' : platform() === 'win32' ? 'start' : 'xdg-open';
|
|
86
86
|
const args = platform() === 'win32' ? ['', url] : [url];
|
|
87
|
+
// A machine with no desktop (no xdg-open) can't open one; say where to go.
|
|
88
|
+
// spawn reports a missing command as an 'error' event, not a throw.
|
|
89
|
+
const instead = () => console.log(` Open ${url} in your browser.`);
|
|
87
90
|
try {
|
|
88
|
-
spawn(cmd, args, { stdio: 'ignore', detached: true, shell: platform() === 'win32' })
|
|
91
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true, shell: platform() === 'win32' });
|
|
92
|
+
child.on('error', instead);
|
|
93
|
+
child.unref();
|
|
89
94
|
} catch {
|
|
90
|
-
|
|
95
|
+
instead();
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
|
|
@@ -110,7 +110,7 @@ stop_running_copy() {
|
|
|
110
110
|
main() {
|
|
111
111
|
printf '\n 6 Degrees — installer\n\n'
|
|
112
112
|
|
|
113
|
-
[ "$(uname -s)" = "Darwin" ] || fail "This installer is for macOS. On Linux, run
|
|
113
|
+
[ "$(uname -s)" = "Darwin" ] || fail "This installer is for macOS. On Linux, run: npx six-degrees (needs Node 22.13 or later). Windows isn't supported yet."
|
|
114
114
|
|
|
115
115
|
local arch
|
|
116
116
|
# A Terminal running under Rosetta reports x86_64 even on Apple Silicon, and
|
|
@@ -56,6 +56,34 @@ function hashId(input) {
|
|
|
56
56
|
return `csv-${Math.abs(h).toString(36)}`;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/** One connection, built from what the export itself says about the person. */
|
|
60
|
+
function connectionFrom({ id, name, role, company, profileUrl, connected_date }) {
|
|
61
|
+
// /paths and the sidebar read seniority off `headline`, not `role`, so
|
|
62
|
+
// rebuild the "Role at Company" shape the scanner would have produced.
|
|
63
|
+
const headline = role && company ? `${role} at ${company}` : role || company || name;
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
degree: 1,
|
|
67
|
+
name,
|
|
68
|
+
headline,
|
|
69
|
+
role,
|
|
70
|
+
company,
|
|
71
|
+
profile_url: profileUrl || null,
|
|
72
|
+
profile_image_url: null, // the official export carries no photos
|
|
73
|
+
connected_date,
|
|
74
|
+
source_connection_id: null,
|
|
75
|
+
is_catalyst: false,
|
|
76
|
+
catalyst_score: null,
|
|
77
|
+
circle_power: null,
|
|
78
|
+
circle_s_count: null,
|
|
79
|
+
circle_a_count: null,
|
|
80
|
+
circle_elite_pct: null,
|
|
81
|
+
outreach_status: null,
|
|
82
|
+
unlock_status: null,
|
|
83
|
+
...scoreRecord(headline, company),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
59
87
|
// ── The importer ───────────────────────────────────────────────────────────
|
|
60
88
|
export function parseConnectionsCsv(text) {
|
|
61
89
|
const rows = parseCsv(text);
|
|
@@ -90,10 +118,6 @@ export function parseConnectionsCsv(text) {
|
|
|
90
118
|
const role = cell(iPosition);
|
|
91
119
|
const profileUrl = cell(iUrl);
|
|
92
120
|
|
|
93
|
-
// /paths and the sidebar read seniority off `headline`, not `role`, so
|
|
94
|
-
// rebuild the "Role at Company" shape the scraper would have produced.
|
|
95
|
-
const headline = role && company ? `${role} at ${company}` : role || company || name;
|
|
96
|
-
|
|
97
121
|
const id = profileUrl ? hashId(profileUrl) : `csv-row-${connections.length}`;
|
|
98
122
|
if (seen.has(id)) { skipped++; continue; }
|
|
99
123
|
seen.add(id);
|
|
@@ -105,27 +129,7 @@ export function parseConnectionsCsv(text) {
|
|
|
105
129
|
if (!isNaN(d.getTime())) connected_date = d.toISOString().split('T')[0];
|
|
106
130
|
}
|
|
107
131
|
|
|
108
|
-
connections.push({
|
|
109
|
-
id,
|
|
110
|
-
degree: 1,
|
|
111
|
-
name,
|
|
112
|
-
headline,
|
|
113
|
-
role,
|
|
114
|
-
company,
|
|
115
|
-
profile_url: profileUrl || null,
|
|
116
|
-
profile_image_url: null, // the official export carries no photos
|
|
117
|
-
connected_date,
|
|
118
|
-
source_connection_id: null,
|
|
119
|
-
is_catalyst: false,
|
|
120
|
-
catalyst_score: null,
|
|
121
|
-
circle_power: null,
|
|
122
|
-
circle_s_count: null,
|
|
123
|
-
circle_a_count: null,
|
|
124
|
-
circle_elite_pct: null,
|
|
125
|
-
outreach_status: null,
|
|
126
|
-
unlock_status: null,
|
|
127
|
-
...scoreRecord(headline, company),
|
|
128
|
-
});
|
|
132
|
+
connections.push(connectionFrom({ id, name, role, company, profileUrl, connected_date }));
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
if (!connections.length) {
|
|
@@ -148,8 +152,29 @@ export function saveSessionNetwork({ degree1 = [], degree2 = [], source = 'csv'
|
|
|
148
152
|
}
|
|
149
153
|
}
|
|
150
154
|
|
|
155
|
+
// A scored row is ~540 characters and the tab holds ~5 million, which capped an
|
|
156
|
+
// import near 9,000 connections. So the tab keeps only what the export says about
|
|
157
|
+
// each person (~130 characters) and scores it again on load: an export at
|
|
158
|
+
// LinkedIn's own maximum of 30,000 connections fits.
|
|
159
|
+
export function packConnections(connections) {
|
|
160
|
+
return connections.map((c) => [c.id, c.name, c.role, c.company, c.profile_url, c.connected_date]);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function unpackConnections(rows = []) {
|
|
164
|
+
return rows
|
|
165
|
+
.map(([id, name, role, company, profileUrl, connected_date]) => connectionFrom({ id, name, role, company, profileUrl, connected_date }))
|
|
166
|
+
.sort((a, b) => b.power_score - a.power_score);
|
|
167
|
+
}
|
|
168
|
+
|
|
151
169
|
export function saveCsvNetwork(connections) {
|
|
152
|
-
|
|
170
|
+
try {
|
|
171
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
172
|
+
packed: packConnections(connections), degree2: [], source: 'csv', importedAt: Date.now(),
|
|
173
|
+
}));
|
|
174
|
+
return true;
|
|
175
|
+
} catch {
|
|
176
|
+
return false; // quota
|
|
177
|
+
}
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
export function hasCsvNetwork() {
|
|
@@ -161,7 +186,8 @@ export function loadCsvNetwork() {
|
|
|
161
186
|
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
162
187
|
if (!raw) return null;
|
|
163
188
|
const parsed = JSON.parse(raw);
|
|
164
|
-
|
|
189
|
+
const degree1 = parsed.packed ? unpackConnections(parsed.packed) : parsed.degree1 || [];
|
|
190
|
+
return { degree1, degree2: parsed.degree2 || [], source: parsed.source || 'csv' };
|
|
165
191
|
} catch { return null; }
|
|
166
192
|
}
|
|
167
193
|
|
|
@@ -39,12 +39,15 @@ export function installKind(env = process.env, root = '') {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/** What to run to update, for each way of installing. */
|
|
42
|
-
export function updateCommand(kind, slug) {
|
|
42
|
+
export function updateCommand(kind, slug, { dataDir } = {}) {
|
|
43
43
|
if (kind === 'mac-app') {
|
|
44
44
|
return `curl -fsSL https://raw.githubusercontent.com/${slug}/main/install.sh | bash`;
|
|
45
45
|
}
|
|
46
46
|
if (kind === 'source') {
|
|
47
47
|
return 'git pull && npm ci && npm run build && npm run start:packaged';
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
// A copy started with --data-dir must be restarted with it, or the new
|
|
50
|
+
// version opens an empty network in the default folder.
|
|
51
|
+
const quoted = dataDir && `'${String(dataDir).replace(/'/g, `'\\''`)}'`;
|
|
52
|
+
return quoted ? `npx six-degrees@latest --data-dir ${quoted}` : 'npx six-degrees@latest';
|
|
50
53
|
}
|
|
@@ -20,7 +20,7 @@ export async function startScrape(action, name, extra = {}) {
|
|
|
20
20
|
body: JSON.stringify({ action, ...(name ? { name } : {}), ...extra }),
|
|
21
21
|
});
|
|
22
22
|
const d = await r.json().catch(() => ({}));
|
|
23
|
-
if (!r.ok) throw new Error(d.error || 'Could not start the
|
|
23
|
+
if (!r.ok) throw new Error(d.error || 'Could not start the scanner.');
|
|
24
24
|
return d;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -48,9 +48,9 @@ export function runScrape(action, { name, onLog, ...extra } = {}) {
|
|
|
48
48
|
/** What to tell someone when the scraper is not usable yet. */
|
|
49
49
|
export function notReadyMessage(status) {
|
|
50
50
|
if (!status) return 'Could not reach the app.';
|
|
51
|
-
if (!status.checks?.scriptsFound) return 'The
|
|
51
|
+
if (!status.checks?.scriptsFound) return 'The scanner files are missing from this install.';
|
|
52
52
|
if (!status.checks?.python) return 'Python 3 is not installed on this machine.';
|
|
53
|
-
if (!status.checks?.dependencies) return 'The
|
|
53
|
+
if (!status.checks?.dependencies) return 'The scanner is not installed yet — open Scan to set it up.';
|
|
54
54
|
if (!status.checks?.chrome) return 'Google Chrome is not installed.';
|
|
55
55
|
return null;
|
|
56
56
|
}
|