somratpro Claude Haiku 4.5 commited on
Commit
0502cce
·
1 Parent(s): 28c31b5

Fix admin invite URL extraction and add setup banner to dashboard

Browse files

- grep for 'Invite URL:' line specifically instead of any 'http' match,
which was capturing corepack's npm registry download URL
- Strip ANSI escape codes before extracting URL
- Save invite URL to /tmp/invite-url.txt for dashboard to read
- Add prominent orange setup banner on dashboard when invite URL exists
- Expose setupUrl in /health JSON response

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

Files changed (2) hide show
  1. health-server.js +82 -0
  2. start.sh +14 -7
health-server.js CHANGED
@@ -29,11 +29,14 @@ app.get("/health", async (req, res) => {
29
  // Try to check if Paperclip is responding
30
  const paperclipStatus = await checkPaperclipHealth();
31
 
 
 
32
  res.status(200).json({
33
  status: "healthy",
34
  timestamp: new Date().toISOString(),
35
  uptime: Math.floor(uptime),
36
  startTime: new Date(Date.now() - uptime * 1000).toISOString(),
 
37
  services: {
38
  healthServer: {
39
  status: "running",
@@ -227,6 +230,17 @@ function readSyncStatus() {
227
  };
228
  }
229
 
 
 
 
 
 
 
 
 
 
 
 
230
  function checkPaperclipHealth() {
231
  return new Promise((resolve) => {
232
  const healthUrl = `http://${PAPERCLIP_HOST}:${PAPERCLIP_PORT}/health`;
@@ -516,6 +530,56 @@ function getDashboardHTML() {
516
  0%, 100% { opacity: 1; }
517
  50% { opacity: 0.3; }
518
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  </style>
520
  </head>
521
  <body>
@@ -525,6 +589,14 @@ function getDashboardHTML() {
525
  <p>Paperclip AI Agent Orchestration on Hugging Face Spaces</p>
526
  </div>
527
 
 
 
 
 
 
 
 
 
528
  <div class="grid">
529
  <!-- Paperclip Status Card -->
530
  <div class="card">
@@ -683,6 +755,16 @@ function getDashboardHTML() {
683
  const startTime = new Date(data.startTime).toLocaleString();
684
  startTimeEl.textContent = startTime;
685
 
 
 
 
 
 
 
 
 
 
 
686
  // Update footer time
687
  document.getElementById('footer-time').textContent = new Date().toLocaleString();
688
  } catch (error) {
 
29
  // Try to check if Paperclip is responding
30
  const paperclipStatus = await checkPaperclipHealth();
31
 
32
+ const inviteUrl = readInviteUrl();
33
+
34
  res.status(200).json({
35
  status: "healthy",
36
  timestamp: new Date().toISOString(),
37
  uptime: Math.floor(uptime),
38
  startTime: new Date(Date.now() - uptime * 1000).toISOString(),
39
+ setupUrl: inviteUrl || null,
40
  services: {
41
  healthServer: {
42
  status: "running",
 
230
  };
231
  }
232
 
233
+ function readInviteUrl() {
234
+ try {
235
+ if (fs.existsSync("/tmp/invite-url.txt")) {
236
+ return fs.readFileSync("/tmp/invite-url.txt", "utf8").trim();
237
+ }
238
+ } catch (error) {
239
+ // ignore
240
+ }
241
+ return null;
242
+ }
243
+
244
  function checkPaperclipHealth() {
245
  return new Promise((resolve) => {
246
  const healthUrl = `http://${PAPERCLIP_HOST}:${PAPERCLIP_PORT}/health`;
 
530
  0%, 100% { opacity: 1; }
531
  50% { opacity: 0.3; }
532
  }
533
+
534
+ .setup-banner {
535
+ background: linear-gradient(135deg, #f59e0b, #d97706);
536
+ color: white;
537
+ border-radius: 12px;
538
+ padding: 24px 28px;
539
+ margin-bottom: 24px;
540
+ box-shadow: 0 10px 30px rgba(245, 158, 11, 0.4);
541
+ animation: fadeIn 0.5s ease-out;
542
+ }
543
+
544
+ .setup-banner h2 {
545
+ font-size: 1.3em;
546
+ margin-bottom: 10px;
547
+ }
548
+
549
+ .setup-banner p {
550
+ opacity: 0.9;
551
+ margin-bottom: 16px;
552
+ font-size: 0.95em;
553
+ }
554
+
555
+ .setup-banner .invite-link {
556
+ background: rgba(255,255,255,0.2);
557
+ border: 1px solid rgba(255,255,255,0.4);
558
+ border-radius: 8px;
559
+ padding: 12px 16px;
560
+ font-family: 'Monaco', 'Courier New', monospace;
561
+ font-size: 0.85em;
562
+ word-break: break-all;
563
+ margin-bottom: 16px;
564
+ display: block;
565
+ }
566
+
567
+ .setup-banner .btn-setup {
568
+ display: inline-block;
569
+ background: white;
570
+ color: #d97706;
571
+ font-weight: 700;
572
+ padding: 10px 24px;
573
+ border-radius: 6px;
574
+ text-decoration: none;
575
+ font-size: 0.95em;
576
+ transition: all 0.2s;
577
+ }
578
+
579
+ .setup-banner .btn-setup:hover {
580
+ transform: translateY(-2px);
581
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
582
+ }
583
  </style>
584
  </head>
585
  <body>
 
589
  <p>Paperclip AI Agent Orchestration on Hugging Face Spaces</p>
590
  </div>
591
 
592
+ <!-- First-time Setup Banner (hidden by default, shown if invite URL exists) -->
593
+ <div class="setup-banner" id="setup-banner" style="display:none;">
594
+ <h2>🔑 Admin Setup Required</h2>
595
+ <p>No admin account exists yet. Open the link below to create your first admin account, then complete onboarding to start using Paperclip.</p>
596
+ <span class="invite-link" id="invite-url-text">Loading...</span>
597
+ <a href="#" class="btn-setup" id="invite-url-link" target="_blank">Open Setup Page &rarr;</a>
598
+ </div>
599
+
600
  <div class="grid">
601
  <!-- Paperclip Status Card -->
602
  <div class="card">
 
755
  const startTime = new Date(data.startTime).toLocaleString();
756
  startTimeEl.textContent = startTime;
757
 
758
+ // Show setup banner if invite URL exists
759
+ const setupBanner = document.getElementById('setup-banner');
760
+ if (data.setupUrl) {
761
+ document.getElementById('invite-url-text').textContent = data.setupUrl;
762
+ document.getElementById('invite-url-link').href = data.setupUrl;
763
+ setupBanner.style.display = 'block';
764
+ } else {
765
+ setupBanner.style.display = 'none';
766
+ }
767
+
768
  // Update footer time
769
  document.getElementById('footer-time').textContent = new Date().toLocaleString();
770
  } catch (error) {
start.sh CHANGED
@@ -277,14 +277,21 @@ done
277
  if [ "$PAPERCLIP_READY" = true ]; then
278
  echo -e "${BLUE}Bootstrapping admin account...${NC}"
279
  BOOTSTRAP_OUTPUT=$(pnpm paperclipai auth bootstrap-ceo 2>&1 || true)
280
- if echo "$BOOTSTRAP_OUTPUT" | grep -q "http"; then
281
- INVITE_URL=$(echo "$BOOTSTRAP_OUTPUT" | grep -o 'https\?://[^ ]*' | head -1)
282
- echo -e "${GREEN}╔══════════════════════════════════════════════╗${NC}"
283
- echo -e "${GREEN}║ ADMIN SETUP URL (open in browser): ║${NC}"
284
- echo -e "${GREEN}║ ${INVITE_URL} ║${NC}"
285
- echo -e "${GREEN}══════════════════════════════════════════════${NC}"
 
 
 
 
 
286
  else
287
- echo -e "${GREEN}✓ Admin account ready${NC}"
 
 
288
  fi
289
  else
290
  echo -e "${YELLOW}Warning: Paperclip did not become ready in 90s${NC}"
 
277
  if [ "$PAPERCLIP_READY" = true ]; then
278
  echo -e "${BLUE}Bootstrapping admin account...${NC}"
279
  BOOTSTRAP_OUTPUT=$(pnpm paperclipai auth bootstrap-ceo 2>&1 || true)
280
+ # Extract URL from the specific "Invite URL: ..." line, stripping ANSI color codes
281
+ INVITE_URL=$(echo "$BOOTSTRAP_OUTPUT" | grep "Invite URL:" | sed 's/\x1B\[[0-9;]*[a-zA-Z]//g' | grep -o 'https\?://[^ ]*' | head -1)
282
+ if [ -n "$INVITE_URL" ]; then
283
+ # Save invite URL for health dashboard to display
284
+ echo "$INVITE_URL" > /tmp/invite-url.txt
285
+ echo -e "${GREEN}══════════════════════════════════════════════════════════╗${NC}"
286
+ echo -e "${GREEN}║ ADMIN SETUP — open this URL in your browser: ║${NC}"
287
+ echo -e "${GREEN}║ ║${NC}"
288
+ echo -e " ${INVITE_URL}"
289
+ echo -e "${GREEN}║ ║${NC}"
290
+ echo -e "${GREEN}╚══════════════════════════════════════════════════════════╝${NC}"
291
  else
292
+ # Clear any stale invite URL file
293
+ rm -f /tmp/invite-url.txt
294
+ echo -e "${GREEN}✓ Admin account already configured${NC}"
295
  fi
296
  else
297
  echo -e "${YELLOW}Warning: Paperclip did not become ready in 90s${NC}"