anurag008w commited on
Commit
d5203bf
Β·
1 Parent(s): 7807f9d

Enhance Space Privacy Detection, Proxy Defaults, and Environment Builder UX

Browse files
Files changed (9) hide show
  1. Dockerfile +1 -0
  2. cloudflare-proxy-setup.py +7 -3
  3. cloudflare-proxy.js +9 -2
  4. env-builder.html +141 -13
  5. env-builder.js +595 -409
  6. health-server.js +214 -57
  7. login.html +1 -1
  8. start.sh +28 -4
  9. wa-guardian.js +1 -1
Dockerfile CHANGED
@@ -105,6 +105,7 @@ RUN chmod +x /home/node/app/start.sh \
105
  /home/node/app/cloudflare-proxy-setup.py \
106
  /home/node/app/cloudflare-keepalive-setup.py \
107
  /home/node/app/openclaw-sync.py \
 
108
  /home/node/app/multi-provider-key-rotator.cjs
109
 
110
  USER node
 
105
  /home/node/app/cloudflare-proxy-setup.py \
106
  /home/node/app/cloudflare-keepalive-setup.py \
107
  /home/node/app/openclaw-sync.py \
108
+ /home/node/app/jupyter-devdata-sync.py \
109
  /home/node/app/multi-provider-key-rotator.cjs
110
 
111
  USER node
cloudflare-proxy-setup.py CHANGED
@@ -12,7 +12,8 @@ from pathlib import Path
12
  API_BASE = "https://api.cloudflare.com/client/v4"
13
  ENV_FILE = Path("/tmp/huggingclaw-cloudflare-proxy.env")
14
  DEFAULT_ALLOWED = [
15
- # Messaging
 
16
  "api.telegram.org",
17
  "discord.com",
18
  "discordapp.com",
@@ -32,8 +33,6 @@ DEFAULT_ALLOWED = [
32
  # Video
33
  "youtube.com",
34
  "www.youtube.com",
35
- # AI APIs
36
- "api.openai.com",
37
  # Email HTTP APIs (SMTP ports are blocked; use these instead)
38
  "api.resend.com",
39
  "api.sendgrid.com",
@@ -43,6 +42,11 @@ DEFAULT_ALLOWED = [
43
  "google.com",
44
  "googleusercontent.com",
45
  "gstatic.com",
 
 
 
 
 
46
  ]
47
 
48
 
 
12
  API_BASE = "https://api.cloudflare.com/client/v4"
13
  ENV_FILE = Path("/tmp/huggingclaw-cloudflare-proxy.env")
14
  DEFAULT_ALLOWED = [
15
+ # Messaging & social platforms β€” primary use-case for the Cloudflare proxy
16
+ # on HF Spaces (geo-restrictions on Telegram, Discord, WhatsApp, etc.).
17
  "api.telegram.org",
18
  "discord.com",
19
  "discordapp.com",
 
33
  # Video
34
  "youtube.com",
35
  "www.youtube.com",
 
 
36
  # Email HTTP APIs (SMTP ports are blocked; use these instead)
37
  "api.resend.com",
38
  "api.sendgrid.com",
 
42
  "google.com",
43
  "googleusercontent.com",
44
  "gstatic.com",
45
+ # NOTE: AI-provider domains (api.openai.com, api.anthropic.com, etc.) are
46
+ # intentionally NOT included here. Proxying AI calls routes API keys through
47
+ # the Cloudflare Worker without an explicit opt-in. Users who need AI API
48
+ # calls proxied (e.g. geo-restricted regions) can add specific domains via
49
+ # the CLOUDFLARE_PROXY_DOMAINS environment variable.
50
  ]
51
 
52
 
cloudflare-proxy.js CHANGED
@@ -24,6 +24,8 @@ if (
24
  const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true";
25
  const PROXY_SHARED_SECRET = (process.env.CLOUDFLARE_PROXY_SECRET || "").trim();
26
  const DEFAULT_PROXY_DOMAINS = [
 
 
27
  "api.telegram.org", "discord.com", "discordapp.com",
28
  "gateway.discord.gg", "status.discord.com", "web.whatsapp.com",
29
  "graph.facebook.com", "graph.instagram.com",
@@ -31,10 +33,15 @@ const DEFAULT_PROXY_DOMAINS = [
31
  "api.linkedin.com", "www.linkedin.com",
32
  "open.tiktokapis.com", "oauth.reddit.com",
33
  "youtube.com", "www.youtube.com",
34
- "api.openai.com",
35
- "integrate.api.nvidia.com", "api.nvidia.com",
36
  "api.resend.com", "api.sendgrid.com", "api.mailgun.net",
 
37
  "googleapis.com", "google.com", "googleusercontent.com", "gstatic.com",
 
 
 
 
 
38
  ];
39
  const PROXY_DOMAINS_RAW = (process.env.CLOUDFLARE_PROXY_DOMAINS || "").trim();
40
  const PROXY_ALL = PROXY_DOMAINS_RAW === "*";
 
24
  const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true";
25
  const PROXY_SHARED_SECRET = (process.env.CLOUDFLARE_PROXY_SECRET || "").trim();
26
  const DEFAULT_PROXY_DOMAINS = [
27
+ // Messaging & social platforms β€” these are the primary use-case for the
28
+ // Cloudflare proxy on HF Spaces (geo-restrictions on Telegram, Discord, WA).
29
  "api.telegram.org", "discord.com", "discordapp.com",
30
  "gateway.discord.gg", "status.discord.com", "web.whatsapp.com",
31
  "graph.facebook.com", "graph.instagram.com",
 
33
  "api.linkedin.com", "www.linkedin.com",
34
  "open.tiktokapis.com", "oauth.reddit.com",
35
  "youtube.com", "www.youtube.com",
36
+ // Email delivery
 
37
  "api.resend.com", "api.sendgrid.com", "api.mailgun.net",
38
+ // Google services
39
  "googleapis.com", "google.com", "googleusercontent.com", "gstatic.com",
40
+ // NOTE: AI-provider domains (api.openai.com, api.anthropic.com, etc.) are
41
+ // intentionally NOT included here. Proxying AI calls routes your API keys
42
+ // through the Cloudflare Worker without an explicit opt-in. Users who need
43
+ // AI API calls proxied (e.g. geo-restricted regions) can add specific
44
+ // domains via the CLOUDFLARE_PROXY_DOMAINS environment variable.
45
  ];
46
  const PROXY_DOMAINS_RAW = (process.env.CLOUDFLARE_PROXY_DOMAINS || "").trim();
47
  const PROXY_ALL = PROXY_DOMAINS_RAW === "*";
env-builder.html CHANGED
@@ -37,16 +37,16 @@
37
  --panel-w: 340px;
38
  }
39
 
40
- html { scroll-behavior: smooth; }
41
 
42
  body {
43
  font-family: var(--sans);
44
  background: var(--bg);
45
  color: var(--text);
46
- min-height: 100vh;
 
47
  display: flex;
48
  flex-direction: column;
49
- overflow-x: hidden;
50
  }
51
 
52
  /* ── Topbar ── */
@@ -330,6 +330,15 @@ body {
330
  letter-spacing: 1.2px;
331
  color: var(--text3);
332
  }
 
 
 
 
 
 
 
 
 
333
 
334
  .sec-line {
335
  flex: 1;
@@ -339,8 +348,8 @@ body {
339
 
340
  .cards {
341
  display: grid;
342
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
343
- gap: 10px;
344
  }
345
 
346
  /* ── ENV Card ── */
@@ -360,6 +369,22 @@ body {
360
  background: linear-gradient(135deg, var(--bg2) 80%, rgba(245,166,35,.04));
361
  }
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  .card-top {
364
  display: flex;
365
  align-items: flex-start;
@@ -407,16 +432,46 @@ body {
407
  border-radius: 20px;
408
  }
409
 
410
- .badge-s {
411
- background: rgba(240,95,95,.12);
412
- color: var(--red);
413
- border: 1px solid rgba(240,95,95,.25);
 
414
  }
415
 
416
- .badge-f {
417
- background: rgba(61,214,140,.1);
418
- color: var(--green);
419
- border: 1px solid rgba(61,214,140,.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  }
421
 
422
  /* ── Card inputs ── */
@@ -692,6 +747,56 @@ body {
692
  .sidebar-wrap { display: none; }
693
  .topbar-divider, .topbar-title { display: none; }
694
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
695
  </style>
696
  </head>
697
 
@@ -746,6 +851,29 @@ body {
746
 
747
  <!-- sections -->
748
  <div class="sections-scroll">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  <div id="sections"></div>
750
 
751
  <!-- Custom Env section -->
 
37
  --panel-w: 340px;
38
  }
39
 
40
+ html { scroll-behavior: smooth; height: 100%; }
41
 
42
  body {
43
  font-family: var(--sans);
44
  background: var(--bg);
45
  color: var(--text);
46
+ height: 100vh;
47
+ overflow: hidden;
48
  display: flex;
49
  flex-direction: column;
 
50
  }
51
 
52
  /* ── Topbar ── */
 
330
  letter-spacing: 1.2px;
331
  color: var(--text3);
332
  }
333
+ .sec-count {
334
+ font-family: var(--mono);
335
+ font-size: 10px;
336
+ color: var(--text3);
337
+ background: var(--bg3);
338
+ border: 1px solid var(--border);
339
+ border-radius: 10px;
340
+ padding: 1px 7px;
341
+ }
342
 
343
  .sec-line {
344
  flex: 1;
 
348
 
349
  .cards {
350
  display: grid;
351
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
352
+ gap: 9px;
353
  }
354
 
355
  /* ── ENV Card ── */
 
369
  background: linear-gradient(135deg, var(--bg2) 80%, rgba(245,166,35,.04));
370
  }
371
 
372
+ /* Critical cards get a red left-border accent */
373
+ .env-card:has(.badge-critical) {
374
+ border-left: 3px solid rgba(240,80,80,.4);
375
+ }
376
+ .env-card:has(.badge-critical):hover {
377
+ border-left-color: rgba(240,80,80,.7);
378
+ }
379
+ .env-card:has(.badge-critical).selected {
380
+ border-left-color: #f05f5f;
381
+ }
382
+
383
+ /* Credential cards get an amber left-border accent */
384
+ .env-card:has(.badge-credential) {
385
+ border-left: 3px solid rgba(220,140,60,.3);
386
+ }
387
+
388
  .card-top {
389
  display: flex;
390
  align-items: flex-start;
 
432
  border-radius: 20px;
433
  }
434
 
435
+ /* critical β€” space breaks without this */
436
+ .badge-critical {
437
+ background: rgba(240,80,80,.14);
438
+ color: #f05f5f;
439
+ border: 1px solid rgba(240,80,80,.3);
440
  }
441
 
442
+ /* credential β€” sensitive key / token */
443
+ .badge-credential {
444
+ background: rgba(220,140,60,.13);
445
+ color: #e09040;
446
+ border: 1px solid rgba(220,140,60,.28);
447
+ }
448
+
449
+ /* feature β€” enables an optional feature */
450
+ .badge-feature {
451
+ background: rgba(70,140,250,.12);
452
+ color: #5a9eff;
453
+ border: 1px solid rgba(70,140,250,.25);
454
+ }
455
+
456
+ /* optional β€” safe to change freely */
457
+ .badge-optional {
458
+ background: rgba(61,214,140,.10);
459
+ color: #3dd68c;
460
+ border: 1px solid rgba(61,214,140,.22);
461
+ }
462
+
463
+ /* advanced β€” power-user, risky if wrong */
464
+ .badge-advanced {
465
+ background: rgba(160,100,230,.12);
466
+ color: #b07ae0;
467
+ border: 1px solid rgba(160,100,230,.25);
468
+ }
469
+
470
+ /* build-time β€” needs HF Space rebuild */
471
+ .badge-build {
472
+ background: rgba(240,185,60,.12);
473
+ color: #e0b030;
474
+ border: 1px solid rgba(240,185,60,.28);
475
  }
476
 
477
  /* ── Card inputs ── */
 
747
  .sidebar-wrap { display: none; }
748
  .topbar-divider, .topbar-title { display: none; }
749
  }
750
+
751
+ /* ── Tag Legend (collapsible) ── */
752
+ .tag-legend {
753
+ margin-bottom: 14px;
754
+ background: var(--bg2);
755
+ border: 1px solid var(--border);
756
+ border-radius: var(--r);
757
+ overflow: hidden;
758
+ }
759
+ .legend-summary {
760
+ display: flex;
761
+ align-items: center;
762
+ gap: 10px;
763
+ padding: 7px 12px;
764
+ cursor: pointer;
765
+ list-style: none;
766
+ user-select: none;
767
+ outline: none;
768
+ }
769
+ .legend-summary::-webkit-details-marker { display: none; }
770
+ .legend-chips { display: flex; gap: 5px; flex-wrap: wrap; flex: 1; }
771
+ .legend-hint {
772
+ font-size: 10px;
773
+ color: var(--text3);
774
+ white-space: nowrap;
775
+ flex-shrink: 0;
776
+ }
777
+ .tag-legend[open] .legend-hint { opacity: 0; }
778
+ .legend-body {
779
+ padding: 8px 12px 10px;
780
+ border-top: 1px solid var(--border);
781
+ display: flex;
782
+ flex-direction: column;
783
+ gap: 6px;
784
+ }
785
+ .legend-row {
786
+ display: flex;
787
+ align-items: center;
788
+ gap: 10px;
789
+ font-size: 11px;
790
+ color: var(--text2);
791
+ }
792
+ .legend-row .badge { flex-shrink: 0; width: 74px; text-align: center; }
793
+ .legend-tip {
794
+ font-size: 9.5px;
795
+ color: var(--text3);
796
+ margin-top: 4px;
797
+ padding-top: 6px;
798
+ border-top: 1px solid var(--border);
799
+ }
800
  </style>
801
  </head>
802
 
 
851
 
852
  <!-- sections -->
853
  <div class="sections-scroll">
854
+ <!-- Tag Legend (compact collapsible) -->
855
+ <details class="tag-legend" id="tagLegend">
856
+ <summary class="legend-summary">
857
+ <span class="legend-chips">
858
+ <span class="badge badge-critical">critical</span>
859
+ <span class="badge badge-credential">credential</span>
860
+ <span class="badge badge-feature">feature</span>
861
+ <span class="badge badge-optional">optional</span>
862
+ <span class="badge badge-advanced">advanced</span>
863
+ <span class="badge badge-build">build-time</span>
864
+ </span>
865
+ <span class="legend-hint">what do these mean?</span>
866
+ </summary>
867
+ <div class="legend-body">
868
+ <div class="legend-row"><span class="badge badge-critical">critical</span><span>Space won't work without this</span></div>
869
+ <div class="legend-row"><span class="badge badge-credential">credential</span><span>Sensitive key or token β€” never share</span></div>
870
+ <div class="legend-row"><span class="badge badge-feature">feature</span><span>Enables a specific optional feature</span></div>
871
+ <div class="legend-row"><span class="badge badge-optional">optional</span><span>Safe to change β€” nothing breaks</span></div>
872
+ <div class="legend-row"><span class="badge badge-advanced">advanced</span><span>Power-user setting β€” wrong value causes issues</span></div>
873
+ <div class="legend-row"><span class="badge badge-build">build-time</span><span>Needs HF Space rebuild to fully take effect</span></div>
874
+ <div class="legend-tip">πŸ’‘ Type a tag name in search to filter by it</div>
875
+ </div>
876
+ </details>
877
  <div id="sections"></div>
878
 
879
  <!-- Custom Env section -->
env-builder.js CHANGED
@@ -416,7 +416,7 @@ const MODEL_CATALOGS = {
416
  };
417
 
418
  const FIELDS = [
419
- {
420
  "g": "Core",
421
  "icon": "⚑",
422
  "k": "LLM_MODEL",
@@ -424,248 +424,258 @@ const FIELDS = [
424
  "type": "model",
425
  "options_key": "LLM_MODEL",
426
  "ph": "choose a provider model",
427
- "common": 1
 
428
  },
429
- {
430
  "g": "Core",
431
  "icon": "⚑",
432
  "k": "LLM_API_KEY",
433
  "lbl": "Primary provider API key",
434
  "type": "password",
435
- "secret": 1,
436
  "ph": "sk-...",
437
- "common": 1
 
438
  },
439
- {
440
  "g": "Core",
441
  "icon": "⚑",
442
  "k": "GATEWAY_TOKEN",
443
  "lbl": "Control UI gateway token",
444
  "type": "password",
445
- "secret": 1,
446
- "common": 1
447
  },
448
- {
449
  "g": "Core",
450
  "icon": "⚑",
451
  "k": "OPENCLAW_PASSWORD",
452
  "lbl": "Optional password auth",
453
  "type": "password",
454
- "secret": 1
455
  },
456
- {
457
  "g": "Core",
458
  "icon": "⚑",
459
  "k": "OPENCLAW_VERSION",
460
  "lbl": "Pin OpenClaw version",
461
  "type": "text",
462
- "ph": "latest"
 
463
  },
464
- {
465
- "g": "Core",
466
  "icon": "⚑",
467
  "k": "LLM_API_KEY_FALLBACK_ENABLED",
468
  "lbl": "Allow global LLM_API_KEY fallback",
469
  "type": "toggle",
470
- "ph": "true"
 
471
  },
472
- {
473
- "g": "Core",
474
  "icon": "⚑",
475
  "k": "DEV_MODE",
476
  "lbl": "Enable dev mode",
477
  "type": "toggle",
478
  "ph": "false",
479
- "common": 1
 
480
  },
481
- {
482
- "g": "Core",
483
  "icon": "⚑",
484
  "k": "HUGGINGCLAW_JUPYTER_ENABLED",
485
  "lbl": "Enable Jupyter terminal",
486
  "type": "toggle",
487
  "ph": "false",
488
- "common": 1
 
489
  },
490
- {
491
- "g": "Core",
492
  "icon": "⚑",
493
  "k": "DEVDATA",
494
  "lbl": "DevData switch",
495
  "type": "toggle",
496
  "ph": "on",
497
- "common": 1
 
498
  },
499
- {
500
- "g": "Core",
501
  "icon": "⚑",
502
  "k": "DEVDATA_DATASET_NAME",
503
  "lbl": "DevData dataset name",
504
  "type": "text",
505
  "ph": "huggingclaw-devdata",
506
- "common": 1
 
507
  },
508
- {
509
- "g": "Core",
510
  "icon": "⚑",
511
  "k": "DEVDATA_SYNC_INTERVAL",
512
  "lbl": "DevData sync interval (seconds)",
513
  "type": "number",
514
- "ph": "180"
 
515
  },
516
- {
517
- "g": "Core",
518
  "icon": "⚑",
519
  "k": "WHATSAPP_ENABLED",
520
  "lbl": "Enable WhatsApp pairing",
521
  "type": "toggle",
522
  "ph": "false",
523
- "common": 1
 
524
  },
525
- {
526
- "g": "Core",
527
  "icon": "⚑",
528
  "k": "HUGGINGCLAW_CAPTURE_DISABLE",
529
  "lbl": "Disable capture wrapper",
530
  "type": "toggle",
531
- "ph": "false"
 
532
  },
533
- {
534
- "g": "Core",
535
  "icon": "⚑",
536
  "k": "HUGGINGCLAW_STARTUP_STRICT",
537
  "lbl": "Stop on startup failure",
538
  "type": "toggle",
539
- "ph": "false"
 
540
  },
541
- {
542
- "g": "Core",
543
  "icon": "⚑",
544
  "k": "HUGGINGCLAW_RUN",
545
  "lbl": "Startup command (one-liner)",
546
- "type": "textarea"
 
547
  },
548
- {
549
- "g": "Core",
550
  "icon": "⚑",
551
  "k": "HUGGINGCLAW_STARTUP_COMMANDS",
552
  "lbl": "Multiline startup commands",
553
- "type": "textarea"
 
554
  },
555
- {
556
- "g": "Core",
557
  "icon": "⚑",
558
  "k": "HUGGINGCLAW_STARTUP_SCRIPT",
559
  "lbl": "Startup shell script",
560
- "type": "textarea"
 
561
  },
562
- {
563
- "g": "Core",
564
  "icon": "⚑",
565
  "k": "HUGGINGCLAW_STARTUP_SCRIPT_B64",
566
  "lbl": "Startup script (base64)",
567
- "type": "textarea"
 
568
  },
569
- {
570
- "g": "Core",
571
  "icon": "⚑",
572
  "k": "HUGGINGCLAW_APT_PACKAGES",
573
  "lbl": "APT packages to install",
574
- "type": "textarea"
 
575
  },
576
- {
577
- "g": "Core",
578
  "icon": "⚑",
579
  "k": "HUGGINGCLAW_PIP_PACKAGES",
580
  "lbl": "Pip packages to install",
581
- "type": "textarea"
 
582
  },
583
- {
584
- "g": "Core",
585
  "icon": "⚑",
586
  "k": "HUGGINGCLAW_NPM_PACKAGES",
587
  "lbl": "NPM packages to install",
588
- "type": "textarea"
 
589
  },
590
- {
591
- "g": "Core",
592
  "icon": "⚑",
593
  "k": "HUGGINGCLAW_OPENCLAW_PLUGINS",
594
  "lbl": "OpenClaw plugins to load",
595
- "type": "textarea"
 
596
  },
597
- {
598
- "g": "Core",
599
  "icon": "⚑",
600
  "k": "ALLOWED_ORIGINS",
601
  "lbl": "Allowed CORS origins",
602
- "type": "textarea"
 
603
  },
604
- {
605
- "g": "Core",
606
  "icon": "⚑",
607
  "k": "TRUSTED_PROXIES",
608
  "lbl": "Trusted proxy CIDRs",
609
- "type": "textarea"
 
610
  },
611
- {
612
- "g": "Core",
613
  "icon": "⚑",
614
  "k": "WEBHOOK_URL",
615
  "lbl": "Webhook URL",
616
  "type": "text",
617
- "ph": "https://..."
618
- },
619
- {
620
- "g": "Core",
621
- "icon": "⚑",
622
- "k": "WS_MIN_PROTOCOL",
623
- "lbl": "Min WebSocket protocol",
624
- "type": "number",
625
- "ph": "1"
626
- },
627
- {
628
- "g": "Core",
629
- "icon": "⚑",
630
- "k": "WS_MAX_PROTOCOL",
631
- "lbl": "Max WebSocket protocol",
632
- "type": "number",
633
- "ph": "5"
634
  },
635
- {
636
- "g": "Core",
637
  "icon": "⚑",
638
  "k": "GATEWAY_MAX_RESTARTS",
639
  "lbl": "Gateway max restarts",
640
  "type": "number",
641
- "ph": "10"
 
642
  },
643
- {
644
- "g": "Core",
645
  "icon": "⚑",
646
  "k": "GATEWAY_READY_TIMEOUT",
647
  "lbl": "Gateway ready timeout",
648
  "type": "number",
649
- "ph": "90"
 
650
  },
651
- {
652
- "g": "Core",
653
  "icon": "⚑",
654
  "k": "GATEWAY_RESTART_DELAY",
655
  "lbl": "Gateway restart delay",
656
  "type": "number",
657
- "ph": "5"
 
658
  },
659
- {
660
- "g": "Core",
661
  "icon": "⚑",
662
  "k": "GATEWAY_VERBOSE",
663
  "lbl": "Verbose gateway logs",
664
  "type": "toggle",
665
- "ph": "false"
 
666
  },
667
- {
668
- "g": "Core",
669
  "icon": "⚑",
670
  "k": "OPENCLAW_CONSOLE_LOG_LEVEL",
671
  "lbl": "Console log level",
@@ -676,10 +686,11 @@ const FIELDS = [
676
  "warn",
677
  "error"
678
  ],
679
- "ph": "info"
 
680
  },
681
- {
682
- "g": "Core",
683
  "icon": "⚑",
684
  "k": "OPENCLAW_FILE_LOG_LEVEL",
685
  "lbl": "File log level",
@@ -690,10 +701,11 @@ const FIELDS = [
690
  "warn",
691
  "error"
692
  ],
693
- "ph": "info"
 
694
  },
695
- {
696
- "g": "Core",
697
  "icon": "⚑",
698
  "k": "OPENCLAW_CONSOLE_LOG_STYLE",
699
  "lbl": "Console log style",
@@ -703,10 +715,11 @@ const FIELDS = [
703
  "json",
704
  "compact"
705
  ],
706
- "ph": "pretty"
 
707
  },
708
- {
709
- "g": "Core",
710
  "icon": "⚑",
711
  "k": "BROWSER_PLUGIN_MODE",
712
  "lbl": "Browser plugin mode",
@@ -716,10 +729,11 @@ const FIELDS = [
716
  "enabled",
717
  "disabled"
718
  ],
719
- "ph": "auto"
 
720
  },
721
- {
722
- "g": "Core",
723
  "icon": "⚑",
724
  "k": "ACP_PLUGIN_MODE",
725
  "lbl": "ACP plugin mode",
@@ -729,912 +743,985 @@ const FIELDS = [
729
  "enabled",
730
  "disabled"
731
  ],
732
- "ph": "auto"
 
733
  },
734
- {
735
- "g": "Core",
736
  "icon": "⚑",
737
  "k": "CLOUDFLARE_PROXY_DEBUG",
738
  "lbl": "Cloudflare proxy debug",
739
  "type": "toggle",
740
- "ph": "false"
 
741
  },
742
- {
743
- "g": "Core",
744
  "icon": "⚑",
745
  "k": "CLOUDFLARE_KEEPALIVE_ENABLED",
746
  "lbl": "Enable keep-awake worker",
747
  "type": "toggle",
748
- "ph": "true"
 
749
  },
750
- {
751
- "g": "Core",
752
  "icon": "⚑",
753
  "k": "CLOUDFLARE_PROXY_URL",
754
  "lbl": "Proxy worker URL",
755
  "type": "text",
756
  "ph": "https://your-proxy.workers.dev",
757
- "common": 1
 
758
  },
759
- {
760
- "g": "Core",
761
  "icon": "⚑",
762
  "k": "CLOUDFLARE_PROXY_SECRET",
763
  "lbl": "Proxy shared secret",
764
  "type": "password",
765
- "secret": 1
766
  },
767
- {
768
- "g": "Core",
769
  "icon": "⚑",
770
  "k": "CLOUDFLARE_PROXY_DOMAINS",
771
  "lbl": "Extra domains to proxy",
772
  "type": "textarea",
773
- "ph": "api.sendgrid.com,slack.com"
 
774
  },
775
- {
776
- "g": "Core",
777
  "icon": "⚑",
778
  "k": "CLOUDFLARE_WORKERS_TOKEN",
779
  "lbl": "Workers API token",
780
  "type": "password",
781
- "secret": 1,
782
- "common": 1
783
  },
784
- {
785
  "g": "Core",
786
  "icon": "⚑",
787
  "k": "HF_USERNAME",
788
  "lbl": "Hugging Face username",
789
  "type": "text",
790
- "common": 1
 
791
  },
792
- {
793
  "g": "Core",
794
  "icon": "⚑",
795
  "k": "HF_TOKEN",
796
  "lbl": "HF write token",
797
  "type": "password",
798
- "secret": 1,
799
- "common": 1
800
  },
801
- {
802
  "g": "Core",
803
  "icon": "⚑",
804
  "k": "BACKUP_DATASET_NAME",
805
  "lbl": "Backup dataset name",
806
  "type": "text",
807
  "ph": "huggingclaw-backup",
808
- "common": 1
 
809
  },
810
- {
811
  "g": "Core",
812
  "icon": "⚑",
813
  "k": "SYNC_INTERVAL",
814
  "lbl": "Sync interval (seconds)",
815
  "type": "number",
816
  "ph": "180",
817
- "common": 1
 
818
  },
819
- {
820
  "g": "Core",
821
  "icon": "⚑",
822
  "k": "JUPYTER_TOKEN",
823
  "lbl": "Jupyter access token",
824
  "type": "password",
825
- "secret": 1,
826
  "ph": "huggingface",
827
- "common": 1
 
828
  },
829
- {
830
  "g": "Core",
831
  "icon": "⚑",
832
  "k": "KEEP_ALIVE_INTERVAL",
833
  "lbl": "Keep-alive ping interval (seconds)",
834
  "type": "number",
835
  "ph": "300",
836
- "common": 1
 
837
  },
838
- {
839
  "g": "Core",
840
  "icon": "⚑",
841
  "k": "OPENCLAW_DISABLE_BONJOUR",
842
  "lbl": "Disable Bonjour/mDNS discovery",
843
  "type": "toggle",
844
- "ph": "false"
 
845
  },
846
- {
847
  "g": "Core",
848
  "icon": "⚑",
849
  "k": "OPENCLAW_RUNTIME_VERSION",
850
  "lbl": "Pin runtime version",
851
  "type": "text",
852
- "ph": "latest"
 
853
  },
854
- {
855
  "g": "Core",
856
  "icon": "⚑",
857
  "k": "OPENCLAW_DISPLAY_VERSION",
858
  "lbl": "Display version label",
859
  "type": "text",
860
- "ph": ""
 
861
  },
862
- {
863
  "g": "Integrations",
864
  "icon": "πŸ”Œ",
865
  "k": "CLOUDFLARE_ACCOUNT_ID",
866
  "lbl": "Cloudflare account ID",
867
  "type": "text",
868
- "ph": "account-id"
 
869
  },
870
- {
871
  "g": "Integrations",
872
  "icon": "πŸ”Œ",
873
  "k": "CLOUDFLARE_WORKER_NAME",
874
  "lbl": "Outbound proxy worker name",
875
  "type": "text",
876
- "ph": "huggingclaw-proxy"
 
877
  },
878
- {
879
  "g": "Integrations",
880
  "icon": "πŸ”Œ",
881
  "k": "CLOUDFLARE_KEEPALIVE_URL",
882
  "lbl": "Keepalive worker URL",
883
  "type": "text",
884
- "ph": "https://your-worker.workers.dev"
 
885
  },
886
- {
887
  "g": "Integrations",
888
  "icon": "πŸ”Œ",
889
  "k": "CLOUDFLARE_KEEPALIVE_WORKER_NAME",
890
  "lbl": "Keepalive worker name",
891
  "type": "text",
892
- "ph": "huggingclaw-keepalive"
 
893
  },
894
- {
895
  "g": "Integrations",
896
  "icon": "πŸ”Œ",
897
  "k": "CLOUDFLARE_KEEPALIVE_CRON",
898
  "lbl": "Keepalive cron schedule",
899
  "type": "text",
900
- "ph": "*/5 * * * *"
 
901
  },
902
- {
903
  "g": "Integrations",
904
  "icon": "πŸ”Œ",
905
  "k": "TELEGRAM_API_ROOT",
906
  "lbl": "Telegram API root override",
907
  "type": "text",
908
- "ph": "https://api.telegram.org"
 
909
  },
910
- {
911
  "g": "Runtime",
912
  "icon": "βš™οΈ",
913
  "k": "OPENCLAW_CONFIG_WATCH_INTERVAL",
914
  "lbl": "Config watch interval (seconds)",
915
  "type": "number",
916
- "ph": "1"
 
917
  },
918
- {
919
  "g": "Runtime",
920
  "icon": "βš™οΈ",
921
  "k": "OPENCLAW_CONFIG_SETTLE_SECONDS",
922
  "lbl": "Config settle window (seconds)",
923
  "type": "number",
924
- "ph": "3"
 
925
  },
926
- {
927
  "g": "Runtime",
928
  "icon": "βš™οΈ",
929
  "k": "JUPYTER_ROOT_DIR",
930
  "lbl": "Jupyter root directory",
931
  "type": "text",
932
- "ph": "/home/node"
 
933
  },
934
- {
935
  "g": "Backup",
936
  "icon": "πŸ’Ύ",
937
  "k": "WORKSPACE_GIT_USER",
938
  "lbl": "Workspace git author email",
939
  "type": "text",
940
- "ph": "openclaw@example.com"
 
941
  },
942
- {
943
  "g": "Backup",
944
  "icon": "πŸ’Ύ",
945
  "k": "WORKSPACE_GIT_NAME",
946
  "lbl": "Workspace git author name",
947
  "type": "text",
948
- "ph": "OpenClaw Bot"
 
949
  },
950
- {
951
  "g": "Provider Keys",
952
  "icon": "πŸ”‘",
953
  "k": "ANTHROPIC_API_KEY",
954
  "lbl": "Anthropic (Claude)",
955
  "type": "password",
956
- "secret": 1,
957
- "common": 0
958
  },
959
- {
960
  "g": "Provider Keys",
961
  "icon": "πŸ”‘",
962
  "k": "OPENAI_API_KEY",
963
  "lbl": "OpenAI (GPT)",
964
  "type": "password",
965
- "secret": 1,
966
- "common": 0
967
  },
968
- {
969
  "g": "Provider Keys",
970
  "icon": "πŸ”‘",
971
  "k": "GOOGLE_API_KEY",
972
  "lbl": "Google AI Studio",
973
  "type": "password",
974
- "secret": 1,
975
- "common": 0
976
  },
977
- {
978
  "g": "Provider Keys",
979
  "icon": "πŸ”‘",
980
  "k": "GEMINI_API_KEY",
981
  "lbl": "Google Gemini",
982
  "type": "password",
983
- "secret": 1,
984
- "common": 0
985
  },
986
- {
987
  "g": "Provider Keys",
988
  "icon": "πŸ”‘",
989
  "k": "DEEPSEEK_API_KEY",
990
  "lbl": "DeepSeek",
991
  "type": "password",
992
- "secret": 1,
993
- "common": 0
994
  },
995
- {
996
  "g": "Provider Keys",
997
  "icon": "πŸ”‘",
998
  "k": "OPENROUTER_API_KEY",
999
  "lbl": "OpenRouter",
1000
  "type": "password",
1001
- "secret": 1,
1002
- "common": 1
1003
  },
1004
- {
1005
  "g": "Provider Keys",
1006
  "icon": "πŸ”‘",
1007
  "k": "OPENCODE_API_KEY",
1008
  "lbl": "OpenCode",
1009
  "type": "password",
1010
- "secret": 1,
1011
- "common": 0
1012
  },
1013
- {
1014
  "g": "Provider Keys",
1015
  "icon": "πŸ”‘",
1016
  "k": "KILOCODE_API_KEY",
1017
  "lbl": "KiloCode",
1018
  "type": "password",
1019
- "secret": 1,
1020
- "common": 0
1021
  },
1022
- {
1023
  "g": "Provider Keys",
1024
  "icon": "πŸ”‘",
1025
  "k": "ZAI_API_KEY",
1026
  "lbl": "Z.ai / GLM",
1027
  "type": "password",
1028
- "secret": 1,
1029
- "common": 0
1030
  },
1031
- {
1032
  "g": "Provider Keys",
1033
  "icon": "πŸ”‘",
1034
  "k": "MOONSHOT_API_KEY",
1035
  "lbl": "Moonshot / Kimi",
1036
  "type": "password",
1037
- "secret": 1,
1038
- "common": 0
1039
  },
1040
- {
1041
  "g": "Provider Keys",
1042
  "icon": "πŸ”‘",
1043
  "k": "MINIMAX_API_KEY",
1044
  "lbl": "MiniMax",
1045
  "type": "password",
1046
- "secret": 1,
1047
- "common": 0
1048
  },
1049
- {
1050
  "g": "Provider Keys",
1051
  "icon": "πŸ”‘",
1052
  "k": "XIAOMI_API_KEY",
1053
  "lbl": "Xiaomi / MiMo",
1054
  "type": "password",
1055
- "secret": 1,
1056
- "common": 0
1057
  },
1058
- {
1059
  "g": "Provider Keys",
1060
  "icon": "πŸ”‘",
1061
  "k": "VOLCANO_ENGINE_API_KEY",
1062
  "lbl": "Volcengine / Doubao",
1063
  "type": "password",
1064
- "secret": 1,
1065
- "common": 0
1066
  },
1067
- {
1068
  "g": "Provider Keys",
1069
  "icon": "πŸ”‘",
1070
  "k": "BYTEPLUS_API_KEY",
1071
  "lbl": "BytePlus",
1072
  "type": "password",
1073
- "secret": 1,
1074
- "common": 0
1075
  },
1076
- {
1077
  "g": "Provider Keys",
1078
  "icon": "πŸ”‘",
1079
  "k": "MISTRAL_API_KEY",
1080
  "lbl": "Mistral",
1081
  "type": "password",
1082
- "secret": 1,
1083
- "common": 0
1084
  },
1085
- {
1086
  "g": "Provider Keys",
1087
  "icon": "πŸ”‘",
1088
  "k": "XAI_API_KEY",
1089
  "lbl": "xAI (Grok)",
1090
  "type": "password",
1091
- "secret": 1,
1092
- "common": 0
1093
  },
1094
- {
1095
  "g": "Provider Keys",
1096
  "icon": "πŸ”‘",
1097
  "k": "NVIDIA_API_KEY",
1098
  "lbl": "NVIDIA",
1099
  "type": "password",
1100
- "secret": 1,
1101
- "common": 0
1102
  },
1103
- {
1104
  "g": "Provider Keys",
1105
  "icon": "πŸ”‘",
1106
  "k": "GROQ_API_KEY",
1107
  "lbl": "Groq",
1108
  "type": "password",
1109
- "secret": 1,
1110
- "common": 0
1111
  },
1112
- {
1113
  "g": "Provider Keys",
1114
  "icon": "πŸ”‘",
1115
  "k": "COHERE_API_KEY",
1116
  "lbl": "Cohere",
1117
  "type": "password",
1118
- "secret": 1,
1119
- "common": 0
1120
  },
1121
- {
1122
  "g": "Provider Keys",
1123
  "icon": "πŸ”‘",
1124
  "k": "TOGETHER_API_KEY",
1125
  "lbl": "Together AI",
1126
  "type": "password",
1127
- "secret": 1,
1128
- "common": 0
1129
  },
1130
- {
1131
  "g": "Provider Keys",
1132
  "icon": "πŸ”‘",
1133
  "k": "CEREBRAS_API_KEY",
1134
  "lbl": "Cerebras",
1135
  "type": "password",
1136
- "secret": 1,
1137
- "common": 0
1138
  },
1139
- {
1140
  "g": "Provider Keys",
1141
  "icon": "πŸ”‘",
1142
  "k": "QIANFAN_API_KEY",
1143
  "lbl": "Qianfan",
1144
  "type": "password",
1145
- "secret": 1,
1146
- "common": 0
1147
  },
1148
- {
1149
  "g": "Provider Keys",
1150
  "icon": "πŸ”‘",
1151
  "k": "MODELSTUDIO_API_KEY",
1152
  "lbl": "ModelStudio",
1153
  "type": "password",
1154
- "secret": 1,
1155
- "common": 0
1156
  },
1157
- {
1158
  "g": "Provider Keys",
1159
  "icon": "πŸ”‘",
1160
  "k": "KIMI_API_KEY",
1161
  "lbl": "Kimi",
1162
  "type": "password",
1163
- "secret": 1,
1164
- "common": 0
1165
  },
1166
- {
1167
  "g": "Provider Keys",
1168
  "icon": "πŸ”‘",
1169
  "k": "HUGGINGFACE_HUB_TOKEN",
1170
  "lbl": "Hugging Face token",
1171
  "type": "password",
1172
- "secret": 1,
1173
- "common": 0
1174
  },
1175
- {
1176
  "g": "Provider Keys",
1177
  "icon": "πŸ”‘",
1178
  "k": "COPILOT_GITHUB_TOKEN",
1179
  "lbl": "GitHub Copilot",
1180
  "type": "password",
1181
- "secret": 1,
1182
- "common": 0
1183
  },
1184
- {
1185
  "g": "Provider Keys",
1186
  "icon": "πŸ”‘",
1187
  "k": "VENICE_API_KEY",
1188
  "lbl": "Venice",
1189
  "type": "password",
1190
- "secret": 1,
1191
- "common": 0
1192
  },
1193
- {
1194
  "g": "Provider Keys",
1195
  "icon": "πŸ”‘",
1196
  "k": "SYNTHETIC_API_KEY",
1197
  "lbl": "Synthetic",
1198
  "type": "password",
1199
- "secret": 1,
1200
- "common": 0
1201
  },
1202
- {
1203
  "g": "Provider Keys",
1204
  "icon": "πŸ”‘",
1205
  "k": "AI_GATEWAY_API_KEY",
1206
  "lbl": "AI Gateway",
1207
  "type": "password",
1208
- "secret": 1,
1209
- "common": 0
1210
  },
1211
- {
1212
  "g": "Provider Keys",
1213
  "icon": "πŸ”‘",
1214
  "k": "CLOUDFLARE_API_TOKEN",
1215
  "lbl": "Cloudflare API token",
1216
  "type": "password",
1217
- "secret": 1,
1218
- "common": 0
1219
  },
1220
- {
1221
  "g": "Rotation Pools",
1222
  "icon": "πŸ”„",
1223
  "k": "ANTHROPIC_API_KEYS",
1224
  "lbl": "Anthropic pool (comma-sep)",
1225
- "type": "text"
 
1226
  },
1227
- {
1228
  "g": "Rotation Pools",
1229
  "icon": "πŸ”„",
1230
  "k": "OPENAI_API_KEYS",
1231
  "lbl": "OpenAI pool",
1232
- "type": "text"
 
1233
  },
1234
- {
1235
  "g": "Rotation Pools",
1236
  "icon": "πŸ”„",
1237
  "k": "GEMINI_API_KEYS",
1238
  "lbl": "Gemini pool",
1239
- "type": "text"
 
1240
  },
1241
- {
1242
  "g": "Rotation Pools",
1243
  "icon": "πŸ”„",
1244
  "k": "GOOGLE_API_KEYS",
1245
  "lbl": "Google pool",
1246
- "type": "text"
 
1247
  },
1248
- {
1249
  "g": "Rotation Pools",
1250
  "icon": "πŸ”„",
1251
  "k": "DEEPSEEK_API_KEYS",
1252
  "lbl": "DeepSeek pool",
1253
- "type": "text"
 
1254
  },
1255
- {
1256
  "g": "Rotation Pools",
1257
  "icon": "πŸ”„",
1258
  "k": "OPENROUTER_API_KEYS",
1259
  "lbl": "OpenRouter pool",
1260
- "type": "text"
 
1261
  },
1262
- {
1263
  "g": "Rotation Pools",
1264
  "icon": "πŸ”„",
1265
  "k": "OPENCODE_API_KEYS",
1266
  "lbl": "OpenCode pool",
1267
- "type": "text"
 
1268
  },
1269
- {
1270
  "g": "Rotation Pools",
1271
  "icon": "πŸ”„",
1272
  "k": "KILOCODE_API_KEYS",
1273
  "lbl": "KiloCode pool",
1274
- "type": "text"
 
1275
  },
1276
- {
1277
  "g": "Rotation Pools",
1278
  "icon": "πŸ”„",
1279
  "k": "ZAI_API_KEYS",
1280
  "lbl": "Z.ai / GLM pool",
1281
- "type": "text"
 
1282
  },
1283
- {
1284
  "g": "Rotation Pools",
1285
  "icon": "πŸ”„",
1286
  "k": "MOONSHOT_API_KEYS",
1287
  "lbl": "Moonshot / Kimi pool",
1288
- "type": "text"
 
1289
  },
1290
- {
1291
  "g": "Rotation Pools",
1292
  "icon": "πŸ”„",
1293
  "k": "MINIMAX_API_KEYS",
1294
  "lbl": "MiniMax pool",
1295
- "type": "text"
 
1296
  },
1297
- {
1298
  "g": "Rotation Pools",
1299
  "icon": "πŸ”„",
1300
  "k": "XIAOMI_API_KEYS",
1301
  "lbl": "Xiaomi pool",
1302
- "type": "text"
 
1303
  },
1304
- {
1305
  "g": "Rotation Pools",
1306
  "icon": "πŸ”„",
1307
  "k": "VOLCANO_ENGINE_API_KEYS",
1308
  "lbl": "Volcano Engine pool",
1309
- "type": "text"
 
1310
  },
1311
- {
1312
  "g": "Rotation Pools",
1313
  "icon": "πŸ”„",
1314
  "k": "BYTEPLUS_API_KEYS",
1315
  "lbl": "BytePlus pool",
1316
- "type": "text"
 
1317
  },
1318
- {
1319
  "g": "Rotation Pools",
1320
  "icon": "πŸ”„",
1321
  "k": "MISTRAL_API_KEYS",
1322
  "lbl": "Mistral pool",
1323
- "type": "text"
 
1324
  },
1325
- {
1326
  "g": "Rotation Pools",
1327
  "icon": "πŸ”„",
1328
  "k": "XAI_API_KEYS",
1329
  "lbl": "xAI pool",
1330
- "type": "text"
 
1331
  },
1332
- {
1333
  "g": "Rotation Pools",
1334
  "icon": "πŸ”„",
1335
  "k": "NVIDIA_API_KEYS",
1336
  "lbl": "NVIDIA pool",
1337
- "type": "text"
 
1338
  },
1339
- {
1340
  "g": "Rotation Pools",
1341
  "icon": "πŸ”„",
1342
  "k": "GROQ_API_KEYS",
1343
  "lbl": "Groq pool",
1344
- "type": "text"
 
1345
  },
1346
- {
1347
  "g": "Rotation Pools",
1348
  "icon": "πŸ”„",
1349
  "k": "COHERE_API_KEYS",
1350
  "lbl": "Cohere pool",
1351
- "type": "text"
 
1352
  },
1353
- {
1354
  "g": "Rotation Pools",
1355
  "icon": "πŸ”„",
1356
  "k": "TOGETHER_API_KEYS",
1357
  "lbl": "Together pool",
1358
- "type": "text"
 
1359
  },
1360
- {
1361
  "g": "Rotation Pools",
1362
  "icon": "πŸ”„",
1363
  "k": "CEREBRAS_API_KEYS",
1364
  "lbl": "Cerebras pool",
1365
- "type": "text"
 
1366
  },
1367
- {
1368
  "g": "Rotation Pools",
1369
  "icon": "πŸ”„",
1370
  "k": "HUGGINGFACE_HUB_TOKENS",
1371
  "lbl": "HF token pool",
1372
  "type": "text"
1373
  },
1374
- {
1375
  "g": "Model Lists",
1376
  "icon": "πŸ“‹",
1377
  "k": "OPENAI_MODELS",
1378
  "lbl": "Visible OpenAI models",
1379
  "type": "model_list",
1380
  "options_key": "OPENAI_MODELS",
1381
- "ph": "Select models to build a comma list"
 
1382
  },
1383
- {
1384
  "g": "Model Lists",
1385
  "icon": "πŸ“‹",
1386
  "k": "ANTHROPIC_MODELS",
1387
  "lbl": "Visible Anthropic models",
1388
  "type": "model_list",
1389
  "options_key": "ANTHROPIC_MODELS",
1390
- "ph": "Select models to build a comma list"
 
1391
  },
1392
- {
1393
  "g": "Model Lists",
1394
  "icon": "πŸ“‹",
1395
  "k": "GEMINI_MODELS",
1396
  "lbl": "Visible Gemini models",
1397
  "type": "model_list",
1398
  "options_key": "GEMINI_MODELS",
1399
- "ph": "Select models to build a comma list"
 
1400
  },
1401
- {
1402
  "g": "Model Lists",
1403
  "icon": "πŸ“‹",
1404
  "k": "DEEPSEEK_MODELS",
1405
  "lbl": "Visible DeepSeek models",
1406
  "type": "model_list",
1407
  "options_key": "DEEPSEEK_MODELS",
1408
- "ph": "Select models to build a comma list"
 
1409
  },
1410
- {
1411
  "g": "Model Lists",
1412
  "icon": "πŸ“‹",
1413
  "k": "OPENROUTER_MODELS",
1414
  "lbl": "Visible OpenRouter models",
1415
  "type": "model_list",
1416
  "options_key": "OPENROUTER_MODELS",
1417
- "ph": "Select models to build a comma list"
 
1418
  },
1419
- {
1420
  "g": "Model Lists",
1421
  "icon": "πŸ“‹",
1422
  "k": "GROQ_MODELS",
1423
  "lbl": "Visible Groq models",
1424
  "type": "model_list",
1425
  "options_key": "GROQ_MODELS",
1426
- "ph": "Select models to build a comma list"
 
1427
  },
1428
- {
1429
  "g": "Model Lists",
1430
  "icon": "πŸ“‹",
1431
  "k": "MISTRAL_MODELS",
1432
  "lbl": "Visible Mistral models",
1433
  "type": "model_list",
1434
  "options_key": "MISTRAL_MODELS",
1435
- "ph": "Select models to build a comma list"
 
1436
  },
1437
- {
1438
  "g": "Model Lists",
1439
  "icon": "πŸ“‹",
1440
  "k": "XAI_MODELS",
1441
  "lbl": "Visible xAI models",
1442
  "type": "model_list",
1443
  "options_key": "XAI_MODELS",
1444
- "ph": "Select models to build a comma list"
 
1445
  },
1446
- {
1447
  "g": "Model Lists",
1448
  "icon": "πŸ“‹",
1449
  "k": "COHERE_MODELS",
1450
  "lbl": "Visible Cohere models",
1451
  "type": "model_list",
1452
  "options_key": "COHERE_MODELS",
1453
- "ph": "Select models to build a comma list"
 
1454
  },
1455
- {
1456
  "g": "Model Lists",
1457
  "icon": "πŸ“‹",
1458
  "k": "TOGETHER_MODELS",
1459
  "lbl": "Visible Together models",
1460
  "type": "model_list",
1461
  "options_key": "TOGETHER_MODELS",
1462
- "ph": "Select models to build a comma list"
 
1463
  },
1464
- {
1465
  "g": "Model Lists",
1466
  "icon": "πŸ“‹",
1467
  "k": "CEREBRAS_MODELS",
1468
  "lbl": "Visible Cerebras models",
1469
  "type": "model_list",
1470
  "options_key": "CEREBRAS_MODELS",
1471
- "ph": "Select models to build a comma list"
 
1472
  },
1473
- {
1474
  "g": "Model Lists",
1475
  "icon": "πŸ“‹",
1476
  "k": "NVIDIA_MODELS",
1477
  "lbl": "Visible NVIDIA models",
1478
  "type": "model_list",
1479
  "options_key": "NVIDIA_MODELS",
1480
- "ph": "Select models to build a comma list"
 
1481
  },
1482
- {
1483
  "g": "Model Lists",
1484
  "icon": "πŸ“‹",
1485
  "k": "KILOCODE_MODELS",
1486
  "lbl": "Visible KiloCode models",
1487
  "type": "model_list",
1488
  "options_key": "KILOCODE_MODELS",
1489
- "ph": "Select models to build a comma list"
 
1490
  },
1491
- {
1492
  "g": "Model Lists",
1493
  "icon": "πŸ“‹",
1494
  "k": "OPENCODE_MODELS",
1495
  "lbl": "Visible OpenCode models",
1496
  "type": "model_list",
1497
  "options_key": "OPENCODE_MODELS",
1498
- "ph": "Select models to build a comma list"
 
1499
  },
1500
- {
1501
  "g": "Model Lists",
1502
  "icon": "πŸ“‹",
1503
  "k": "ZAI_MODELS",
1504
  "lbl": "Visible Z.ai / GLM models",
1505
  "type": "model_list",
1506
  "options_key": "ZAI_MODELS",
1507
- "ph": "Select models to build a comma list"
 
1508
  },
1509
- {
1510
  "g": "Model Lists",
1511
  "icon": "πŸ“‹",
1512
  "k": "MOONSHOT_MODELS",
1513
  "lbl": "Visible Moonshot / Kimi models",
1514
  "type": "model_list",
1515
  "options_key": "MOONSHOT_MODELS",
1516
- "ph": "Select models to build a comma list"
 
1517
  },
1518
- {
1519
  "g": "Model Lists",
1520
  "icon": "πŸ“‹",
1521
  "k": "MINIMAX_MODELS",
1522
  "lbl": "Visible MiniMax models",
1523
  "type": "model_list",
1524
  "options_key": "MINIMAX_MODELS",
1525
- "ph": "Select models to build a comma list"
 
1526
  },
1527
- {
1528
  "g": "Model Lists",
1529
  "icon": "πŸ“‹",
1530
  "k": "XIAOMI_MODELS",
1531
  "lbl": "Visible Xiaomi models",
1532
  "type": "model_list",
1533
  "options_key": "XIAOMI_MODELS",
1534
- "ph": "Select models to build a comma list"
 
1535
  },
1536
- {
1537
  "g": "Model Lists",
1538
  "icon": "πŸ“‹",
1539
  "k": "VOLCANO_ENGINE_MODELS",
1540
  "lbl": "Visible Volcano Engine models",
1541
  "type": "model_list",
1542
  "options_key": "VOLCANO_ENGINE_MODELS",
1543
- "ph": "Select models to build a comma list"
 
1544
  },
1545
- {
1546
  "g": "Model Lists",
1547
  "icon": "πŸ“‹",
1548
  "k": "BYTEPLUS_MODELS",
1549
  "lbl": "Visible BytePlus models",
1550
  "type": "model_list",
1551
  "options_key": "BYTEPLUS_MODELS",
1552
- "ph": "Select models to build a comma list"
 
1553
  },
1554
- {
1555
  "g": "Model Lists",
1556
  "icon": "πŸ“‹",
1557
  "k": "QIANFAN_MODELS",
1558
  "lbl": "Visible Qianfan models",
1559
  "type": "model_list",
1560
  "options_key": "QIANFAN_MODELS",
1561
- "ph": "Select models to build a comma list"
 
1562
  },
1563
- {
1564
  "g": "Model Lists",
1565
  "icon": "πŸ“‹",
1566
  "k": "MODELSTUDIO_MODELS",
1567
  "lbl": "Visible ModelStudio models",
1568
  "type": "model_list",
1569
  "options_key": "MODELSTUDIO_MODELS",
1570
- "ph": "Select models to build a comma list"
 
1571
  },
1572
- {
1573
  "g": "Model Lists",
1574
  "icon": "πŸ“‹",
1575
  "k": "KIMI_MODELS",
1576
  "lbl": "Visible Kimi models",
1577
  "type": "model_list",
1578
  "options_key": "KIMI_MODELS",
1579
- "ph": "Select models to build a comma list"
 
1580
  },
1581
- {
1582
  "g": "Model Lists",
1583
  "icon": "πŸ“‹",
1584
  "k": "HUGGINGFACE_MODELS",
1585
  "lbl": "Visible Hugging Face models",
1586
  "type": "model_list",
1587
  "options_key": "HUGGINGFACE_MODELS",
1588
- "ph": "Select models to build a comma list"
 
1589
  },
1590
- {
1591
  "g": "Model Lists",
1592
  "icon": "πŸ“‹",
1593
  "k": "GITHUB_COPILOT_MODELS",
1594
  "lbl": "Visible GitHub Copilot models",
1595
  "type": "model_list",
1596
  "options_key": "GITHUB_COPILOT_MODELS",
1597
- "ph": "Select models to build a comma list"
 
1598
  },
1599
- {
1600
  "g": "Custom Provider",
1601
  "icon": "πŸ”Œ",
1602
  "k": "CUSTOM_PROVIDER_NAME",
1603
  "lbl": "Provider display name",
1604
- "type": "text"
 
1605
  },
1606
- {
1607
  "g": "Custom Provider",
1608
  "icon": "πŸ”Œ",
1609
  "k": "CUSTOM_BASE_URL",
1610
  "lbl": "OpenAI-compatible base URL",
1611
- "type": "text"
 
1612
  },
1613
- {
1614
  "g": "Custom Provider",
1615
  "icon": "πŸ”Œ",
1616
  "k": "CUSTOM_MODEL_ID",
1617
  "lbl": "Model ID",
1618
  "type": "model",
1619
  "options_key": "LLM_MODEL",
1620
- "ph": "custom model id"
 
1621
  },
1622
- {
1623
  "g": "Custom Provider",
1624
  "icon": "πŸ”Œ",
1625
  "k": "CUSTOM_MODEL_NAME",
1626
  "lbl": "Friendly model name",
1627
- "type": "text"
 
1628
  },
1629
- {
1630
  "g": "Custom Provider",
1631
  "icon": "πŸ”Œ",
1632
  "k": "CUSTOM_API_KEY",
1633
  "lbl": "Provider API key",
1634
  "type": "password",
1635
- "secret": 1
1636
  },
1637
- {
1638
  "g": "Custom Provider",
1639
  "icon": "πŸ”Œ",
1640
  "k": "CUSTOM_API_TYPE",
@@ -1647,126 +1734,217 @@ const FIELDS = [
1647
  "gemini",
1648
  "openrouter"
1649
  ],
1650
- "ph": "openai-completions"
 
1651
  },
1652
- {
1653
  "g": "Custom Provider",
1654
  "icon": "πŸ”Œ",
1655
  "k": "CUSTOM_CONTEXT_WINDOW",
1656
  "lbl": "Context window",
1657
  "type": "number",
1658
- "ph": "128000"
 
1659
  },
1660
- {
1661
  "g": "Custom Provider",
1662
  "icon": "πŸ”Œ",
1663
  "k": "CUSTOM_MAX_TOKENS",
1664
  "lbl": "Max output tokens",
1665
  "type": "number",
1666
- "ph": "500"
 
1667
  },
1668
- {
1669
  "g": "Telegram",
1670
  "icon": "✈️",
1671
  "k": "TELEGRAM_BOT_TOKEN",
1672
  "lbl": "Bot token from BotFather",
1673
  "type": "password",
1674
- "secret": 1,
1675
- "common": 1
1676
  },
1677
- {
1678
  "g": "Telegram",
1679
  "icon": "✈️",
1680
  "k": "TELEGRAM_ALLOWED_USERS",
1681
  "lbl": "Allowed user IDs (comma)",
1682
  "type": "text",
1683
  "ph": "123456789,987654321",
1684
- "common": 1
 
1685
  },
1686
- {
1687
  "g": "Telegram",
1688
  "icon": "✈️",
1689
  "k": "TELEGRAM_USER_ID",
1690
  "lbl": "Single Telegram user ID",
1691
- "type": "text"
 
1692
  },
1693
- {
1694
  "g": "Telegram",
1695
  "icon": "✈️",
1696
  "k": "TELEGRAM_USER_IDS",
1697
  "lbl": "Telegram user IDs (comma)",
1698
- "type": "text"
 
1699
  },
1700
- {
1701
  "g": "Deployment",
1702
  "icon": "🧭",
1703
  "k": "APP_BASE",
1704
  "lbl": "Public app base path",
1705
  "type": "text",
1706
- "ph": "/app"
 
1707
  },
1708
- {
1709
  "g": "Deployment",
1710
  "icon": "🧭",
1711
  "k": "BACKUP_DATASET",
1712
  "lbl": "Backup dataset alias",
1713
  "type": "text",
1714
- "ph": "huggingclaw-backup"
 
1715
  },
1716
- {
1717
  "g": "Deployment",
1718
  "icon": "🧭",
1719
  "k": "SPACE_AUTHOR_NAME",
1720
  "lbl": "HF Space author name",
1721
- "type": "text"
 
1722
  },
1723
- {
1724
  "g": "Deployment",
1725
  "icon": "🧭",
1726
  "k": "SPACE_HOST",
1727
  "lbl": "HF Space host domain",
1728
- "type": "text"
 
1729
  },
1730
- {
1731
  "g": "Deployment",
1732
  "icon": "🧭",
1733
  "k": "PORT",
1734
  "lbl": "Public dashboard port",
1735
  "type": "number",
1736
- "ph": "7861"
 
1737
  },
1738
- {
1739
  "g": "Deployment",
1740
  "icon": "🧭",
1741
  "k": "GATEWAY_PORT",
1742
  "lbl": "OpenClaw internal port",
1743
  "type": "number",
1744
- "ph": "7860"
 
1745
  },
1746
- {
1747
  "g": "Deployment",
1748
  "icon": "🧭",
1749
  "k": "JUPYTER_PORT",
1750
  "lbl": "Jupyter internal port",
1751
  "type": "number",
1752
- "ph": "8888"
 
1753
  },
1754
- {
1755
  "g": "Deployment",
1756
  "icon": "🧭",
1757
  "k": "JUPYTER_BASE",
1758
  "lbl": "Jupyter public base path",
1759
  "type": "text",
1760
- "ph": "/terminal"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1761
  }
1762
- ];
1763
 
1764
  const ICONS = {
1765
- All:'🏠', Core:'⚑', Deployment:'🧭', 'Provider Keys':'πŸ”‘', 'Rotation Pools':'πŸ”„',
1766
- 'Model Lists':'πŸ“‹', 'Custom Provider':'πŸ”Œ', Telegram:'✈️', WhatsApp:'πŸ’¬',
1767
- Cloudflare:'☁️', Backup:'πŸ’Ύ', DevData:'πŸ§ͺ', Runtime:'βš™οΈ', 'Custom Env':'πŸ”§'
 
 
1768
  };
1769
-
1770
  const $ = id => document.getElementById(id);
1771
  const esc = s => String(s ?? '').replace(/[&<>"']/g, c => ({
1772
  '&': '&amp;',
@@ -1946,11 +2124,18 @@ function valueControlHTML(field) {
1946
  }
1947
 
1948
  function cardHTML(f) {
1949
- const badge = f.secret
1950
- ? '<span class="badge badge-s">secret</span>'
1951
- : '<span class="badge badge-f">safe</span>';
 
 
 
 
 
 
 
1952
 
1953
- return `<div class="env-card" data-row data-group="${esc(f.g)}" data-search="${esc((f.g + ' ' + f.k + ' ' + (f.lbl || '')).toLowerCase())}">
1954
  <div class="card-top">
1955
  <input type="checkbox" class="card-check" data-check="${esc(f.k)}" ${f.common ? 'data-common="1"' : ''}>
1956
  <div class="card-info">
@@ -2241,6 +2426,7 @@ function renderSections() {
2241
  <div class="sec-header">
2242
  <span class="sec-icon">${ICONS[grp] || 'πŸ“'}</span>
2243
  <span class="sec-title">${esc(grp)}</span>
 
2244
  <div class="sec-line"></div>
2245
  </div>
2246
  <div class="cards">${items.map(cardHTML).join('')}</div>`;
 
416
  };
417
 
418
  const FIELDS = [
419
+ {
420
  "g": "Core",
421
  "icon": "⚑",
422
  "k": "LLM_MODEL",
 
424
  "type": "model",
425
  "options_key": "LLM_MODEL",
426
  "ph": "choose a provider model",
427
+ "common": 1,
428
+ "tag": "critical"
429
  },
430
+ {
431
  "g": "Core",
432
  "icon": "⚑",
433
  "k": "LLM_API_KEY",
434
  "lbl": "Primary provider API key",
435
  "type": "password",
 
436
  "ph": "sk-...",
437
+ "common": 1,
438
+ "tag": "credential"
439
  },
440
+ {
441
  "g": "Core",
442
  "icon": "⚑",
443
  "k": "GATEWAY_TOKEN",
444
  "lbl": "Control UI gateway token",
445
  "type": "password",
446
+ "common": 1,
447
+ "tag": "critical"
448
  },
449
+ {
450
  "g": "Core",
451
  "icon": "⚑",
452
  "k": "OPENCLAW_PASSWORD",
453
  "lbl": "Optional password auth",
454
  "type": "password",
455
+ "tag": "credential"
456
  },
457
+ {
458
  "g": "Core",
459
  "icon": "⚑",
460
  "k": "OPENCLAW_VERSION",
461
  "lbl": "Pin OpenClaw version",
462
  "type": "text",
463
+ "ph": "latest",
464
+ "tag": "optional"
465
  },
466
+ {
467
+ "g": "Plugins",
468
  "icon": "⚑",
469
  "k": "LLM_API_KEY_FALLBACK_ENABLED",
470
  "lbl": "Allow global LLM_API_KEY fallback",
471
  "type": "toggle",
472
+ "ph": "true",
473
+ "tag": "advanced"
474
  },
475
+ {
476
+ "g": "Startup",
477
  "icon": "⚑",
478
  "k": "DEV_MODE",
479
  "lbl": "Enable dev mode",
480
  "type": "toggle",
481
  "ph": "false",
482
+ "common": 1,
483
+ "tag": "build"
484
  },
485
+ {
486
+ "g": "Startup",
487
  "icon": "⚑",
488
  "k": "HUGGINGCLAW_JUPYTER_ENABLED",
489
  "lbl": "Enable Jupyter terminal",
490
  "type": "toggle",
491
  "ph": "false",
492
+ "common": 1,
493
+ "tag": "feature"
494
  },
495
+ {
496
+ "g": "DevData",
497
  "icon": "⚑",
498
  "k": "DEVDATA",
499
  "lbl": "DevData switch",
500
  "type": "toggle",
501
  "ph": "on",
502
+ "common": 1,
503
+ "tag": "feature"
504
  },
505
+ {
506
+ "g": "DevData",
507
  "icon": "⚑",
508
  "k": "DEVDATA_DATASET_NAME",
509
  "lbl": "DevData dataset name",
510
  "type": "text",
511
  "ph": "huggingclaw-devdata",
512
+ "common": 1,
513
+ "tag": "feature"
514
  },
515
+ {
516
+ "g": "DevData",
517
  "icon": "⚑",
518
  "k": "DEVDATA_SYNC_INTERVAL",
519
  "lbl": "DevData sync interval (seconds)",
520
  "type": "number",
521
+ "ph": "180",
522
+ "tag": "advanced"
523
  },
524
+ {
525
+ "g": "WhatsApp",
526
  "icon": "⚑",
527
  "k": "WHATSAPP_ENABLED",
528
  "lbl": "Enable WhatsApp pairing",
529
  "type": "toggle",
530
  "ph": "false",
531
+ "common": 1,
532
+ "tag": "feature"
533
  },
534
+ {
535
+ "g": "Startup",
536
  "icon": "⚑",
537
  "k": "HUGGINGCLAW_CAPTURE_DISABLE",
538
  "lbl": "Disable capture wrapper",
539
  "type": "toggle",
540
+ "ph": "false",
541
+ "tag": "advanced"
542
  },
543
+ {
544
+ "g": "Startup",
545
  "icon": "⚑",
546
  "k": "HUGGINGCLAW_STARTUP_STRICT",
547
  "lbl": "Stop on startup failure",
548
  "type": "toggle",
549
+ "ph": "false",
550
+ "tag": "advanced"
551
  },
552
+ {
553
+ "g": "Startup",
554
  "icon": "⚑",
555
  "k": "HUGGINGCLAW_RUN",
556
  "lbl": "Startup command (one-liner)",
557
+ "type": "textarea",
558
+ "tag": "optional"
559
  },
560
+ {
561
+ "g": "Startup",
562
  "icon": "⚑",
563
  "k": "HUGGINGCLAW_STARTUP_COMMANDS",
564
  "lbl": "Multiline startup commands",
565
+ "type": "textarea",
566
+ "tag": "optional"
567
  },
568
+ {
569
+ "g": "Startup",
570
  "icon": "⚑",
571
  "k": "HUGGINGCLAW_STARTUP_SCRIPT",
572
  "lbl": "Startup shell script",
573
+ "type": "textarea",
574
+ "tag": "optional"
575
  },
576
+ {
577
+ "g": "Startup",
578
  "icon": "⚑",
579
  "k": "HUGGINGCLAW_STARTUP_SCRIPT_B64",
580
  "lbl": "Startup script (base64)",
581
+ "type": "textarea",
582
+ "tag": "optional"
583
  },
584
+ {
585
+ "g": "Startup",
586
  "icon": "⚑",
587
  "k": "HUGGINGCLAW_APT_PACKAGES",
588
  "lbl": "APT packages to install",
589
+ "type": "textarea",
590
+ "tag": "optional"
591
  },
592
+ {
593
+ "g": "Startup",
594
  "icon": "⚑",
595
  "k": "HUGGINGCLAW_PIP_PACKAGES",
596
  "lbl": "Pip packages to install",
597
+ "type": "textarea",
598
+ "tag": "optional"
599
  },
600
+ {
601
+ "g": "Startup",
602
  "icon": "⚑",
603
  "k": "HUGGINGCLAW_NPM_PACKAGES",
604
  "lbl": "NPM packages to install",
605
+ "type": "textarea",
606
+ "tag": "optional"
607
  },
608
+ {
609
+ "g": "Startup",
610
  "icon": "⚑",
611
  "k": "HUGGINGCLAW_OPENCLAW_PLUGINS",
612
  "lbl": "OpenClaw plugins to load",
613
+ "type": "textarea",
614
+ "tag": "optional"
615
  },
616
+ {
617
+ "g": "Network",
618
  "icon": "⚑",
619
  "k": "ALLOWED_ORIGINS",
620
  "lbl": "Allowed CORS origins",
621
+ "type": "textarea",
622
+ "tag": "advanced"
623
  },
624
+ {
625
+ "g": "Network",
626
  "icon": "⚑",
627
  "k": "TRUSTED_PROXIES",
628
  "lbl": "Trusted proxy CIDRs",
629
+ "type": "textarea",
630
+ "tag": "advanced"
631
  },
632
+ {
633
+ "g": "Network",
634
  "icon": "⚑",
635
  "k": "WEBHOOK_URL",
636
  "lbl": "Webhook URL",
637
  "type": "text",
638
+ "ph": "https://...",
639
+ "tag": "feature"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
640
  },
641
+ {
642
+ "g": "Gateway",
643
  "icon": "⚑",
644
  "k": "GATEWAY_MAX_RESTARTS",
645
  "lbl": "Gateway max restarts",
646
  "type": "number",
647
+ "ph": "10",
648
+ "tag": "advanced"
649
  },
650
+ {
651
+ "g": "Gateway",
652
  "icon": "⚑",
653
  "k": "GATEWAY_READY_TIMEOUT",
654
  "lbl": "Gateway ready timeout",
655
  "type": "number",
656
+ "ph": "90",
657
+ "tag": "advanced"
658
  },
659
+ {
660
+ "g": "Gateway",
661
  "icon": "⚑",
662
  "k": "GATEWAY_RESTART_DELAY",
663
  "lbl": "Gateway restart delay",
664
  "type": "number",
665
+ "ph": "5",
666
+ "tag": "advanced"
667
  },
668
+ {
669
+ "g": "Gateway",
670
  "icon": "⚑",
671
  "k": "GATEWAY_VERBOSE",
672
  "lbl": "Verbose gateway logs",
673
  "type": "toggle",
674
+ "ph": "false",
675
+ "tag": "advanced"
676
  },
677
+ {
678
+ "g": "Logging",
679
  "icon": "⚑",
680
  "k": "OPENCLAW_CONSOLE_LOG_LEVEL",
681
  "lbl": "Console log level",
 
686
  "warn",
687
  "error"
688
  ],
689
+ "ph": "info",
690
+ "tag": "optional"
691
  },
692
+ {
693
+ "g": "Logging",
694
  "icon": "⚑",
695
  "k": "OPENCLAW_FILE_LOG_LEVEL",
696
  "lbl": "File log level",
 
701
  "warn",
702
  "error"
703
  ],
704
+ "ph": "info",
705
+ "tag": "optional"
706
  },
707
+ {
708
+ "g": "Logging",
709
  "icon": "⚑",
710
  "k": "OPENCLAW_CONSOLE_LOG_STYLE",
711
  "lbl": "Console log style",
 
715
  "json",
716
  "compact"
717
  ],
718
+ "ph": "pretty",
719
+ "tag": "optional"
720
  },
721
+ {
722
+ "g": "Plugins",
723
  "icon": "⚑",
724
  "k": "BROWSER_PLUGIN_MODE",
725
  "lbl": "Browser plugin mode",
 
729
  "enabled",
730
  "disabled"
731
  ],
732
+ "ph": "auto",
733
+ "tag": "feature"
734
  },
735
+ {
736
+ "g": "Plugins",
737
  "icon": "⚑",
738
  "k": "ACP_PLUGIN_MODE",
739
  "lbl": "ACP plugin mode",
 
743
  "enabled",
744
  "disabled"
745
  ],
746
+ "ph": "auto",
747
+ "tag": "feature"
748
  },
749
+ {
750
+ "g": "Cloudflare",
751
  "icon": "⚑",
752
  "k": "CLOUDFLARE_PROXY_DEBUG",
753
  "lbl": "Cloudflare proxy debug",
754
  "type": "toggle",
755
+ "ph": "false",
756
+ "tag": "advanced"
757
  },
758
+ {
759
+ "g": "Cloudflare",
760
  "icon": "⚑",
761
  "k": "CLOUDFLARE_KEEPALIVE_ENABLED",
762
  "lbl": "Enable keep-awake worker",
763
  "type": "toggle",
764
+ "ph": "true",
765
+ "tag": "feature"
766
  },
767
+ {
768
+ "g": "Cloudflare",
769
  "icon": "⚑",
770
  "k": "CLOUDFLARE_PROXY_URL",
771
  "lbl": "Proxy worker URL",
772
  "type": "text",
773
  "ph": "https://your-proxy.workers.dev",
774
+ "common": 1,
775
+ "tag": "feature"
776
  },
777
+ {
778
+ "g": "Cloudflare",
779
  "icon": "⚑",
780
  "k": "CLOUDFLARE_PROXY_SECRET",
781
  "lbl": "Proxy shared secret",
782
  "type": "password",
783
+ "tag": "credential"
784
  },
785
+ {
786
+ "g": "Cloudflare",
787
  "icon": "⚑",
788
  "k": "CLOUDFLARE_PROXY_DOMAINS",
789
  "lbl": "Extra domains to proxy",
790
  "type": "textarea",
791
+ "ph": "api.sendgrid.com,slack.com",
792
+ "tag": "advanced"
793
  },
794
+ {
795
+ "g": "Cloudflare",
796
  "icon": "⚑",
797
  "k": "CLOUDFLARE_WORKERS_TOKEN",
798
  "lbl": "Workers API token",
799
  "type": "password",
800
+ "common": 1,
801
+ "tag": "credential"
802
  },
803
+ {
804
  "g": "Core",
805
  "icon": "⚑",
806
  "k": "HF_USERNAME",
807
  "lbl": "Hugging Face username",
808
  "type": "text",
809
+ "common": 1,
810
+ "tag": "optional"
811
  },
812
+ {
813
  "g": "Core",
814
  "icon": "⚑",
815
  "k": "HF_TOKEN",
816
  "lbl": "HF write token",
817
  "type": "password",
818
+ "common": 1,
819
+ "tag": "credential"
820
  },
821
+ {
822
  "g": "Core",
823
  "icon": "⚑",
824
  "k": "BACKUP_DATASET_NAME",
825
  "lbl": "Backup dataset name",
826
  "type": "text",
827
  "ph": "huggingclaw-backup",
828
+ "common": 1,
829
+ "tag": "optional"
830
  },
831
+ {
832
  "g": "Core",
833
  "icon": "⚑",
834
  "k": "SYNC_INTERVAL",
835
  "lbl": "Sync interval (seconds)",
836
  "type": "number",
837
  "ph": "180",
838
+ "common": 1,
839
+ "tag": "advanced"
840
  },
841
+ {
842
  "g": "Core",
843
  "icon": "⚑",
844
  "k": "JUPYTER_TOKEN",
845
  "lbl": "Jupyter access token",
846
  "type": "password",
 
847
  "ph": "huggingface",
848
+ "common": 1,
849
+ "tag": "credential"
850
  },
851
+ {
852
  "g": "Core",
853
  "icon": "⚑",
854
  "k": "KEEP_ALIVE_INTERVAL",
855
  "lbl": "Keep-alive ping interval (seconds)",
856
  "type": "number",
857
  "ph": "300",
858
+ "common": 1,
859
+ "tag": "advanced"
860
  },
861
+ {
862
  "g": "Core",
863
  "icon": "⚑",
864
  "k": "OPENCLAW_DISABLE_BONJOUR",
865
  "lbl": "Disable Bonjour/mDNS discovery",
866
  "type": "toggle",
867
+ "ph": "false",
868
+ "tag": "advanced"
869
  },
870
+ {
871
  "g": "Core",
872
  "icon": "⚑",
873
  "k": "OPENCLAW_RUNTIME_VERSION",
874
  "lbl": "Pin runtime version",
875
  "type": "text",
876
+ "ph": "latest",
877
+ "tag": "advanced"
878
  },
879
+ {
880
  "g": "Core",
881
  "icon": "⚑",
882
  "k": "OPENCLAW_DISPLAY_VERSION",
883
  "lbl": "Display version label",
884
  "type": "text",
885
+ "ph": "",
886
+ "tag": "optional"
887
  },
888
+ {
889
  "g": "Integrations",
890
  "icon": "πŸ”Œ",
891
  "k": "CLOUDFLARE_ACCOUNT_ID",
892
  "lbl": "Cloudflare account ID",
893
  "type": "text",
894
+ "ph": "account-id",
895
+ "tag": "feature"
896
  },
897
+ {
898
  "g": "Integrations",
899
  "icon": "πŸ”Œ",
900
  "k": "CLOUDFLARE_WORKER_NAME",
901
  "lbl": "Outbound proxy worker name",
902
  "type": "text",
903
+ "ph": "huggingclaw-proxy",
904
+ "tag": "feature"
905
  },
906
+ {
907
  "g": "Integrations",
908
  "icon": "πŸ”Œ",
909
  "k": "CLOUDFLARE_KEEPALIVE_URL",
910
  "lbl": "Keepalive worker URL",
911
  "type": "text",
912
+ "ph": "https://your-worker.workers.dev",
913
+ "tag": "feature"
914
  },
915
+ {
916
  "g": "Integrations",
917
  "icon": "πŸ”Œ",
918
  "k": "CLOUDFLARE_KEEPALIVE_WORKER_NAME",
919
  "lbl": "Keepalive worker name",
920
  "type": "text",
921
+ "ph": "huggingclaw-keepalive",
922
+ "tag": "feature"
923
  },
924
+ {
925
  "g": "Integrations",
926
  "icon": "πŸ”Œ",
927
  "k": "CLOUDFLARE_KEEPALIVE_CRON",
928
  "lbl": "Keepalive cron schedule",
929
  "type": "text",
930
+ "ph": "*/5 * * * *",
931
+ "tag": "advanced"
932
  },
933
+ {
934
  "g": "Integrations",
935
  "icon": "πŸ”Œ",
936
  "k": "TELEGRAM_API_ROOT",
937
  "lbl": "Telegram API root override",
938
  "type": "text",
939
+ "ph": "https://api.telegram.org",
940
+ "tag": "advanced"
941
  },
942
+ {
943
  "g": "Runtime",
944
  "icon": "βš™οΈ",
945
  "k": "OPENCLAW_CONFIG_WATCH_INTERVAL",
946
  "lbl": "Config watch interval (seconds)",
947
  "type": "number",
948
+ "ph": "1",
949
+ "tag": "advanced"
950
  },
951
+ {
952
  "g": "Runtime",
953
  "icon": "βš™οΈ",
954
  "k": "OPENCLAW_CONFIG_SETTLE_SECONDS",
955
  "lbl": "Config settle window (seconds)",
956
  "type": "number",
957
+ "ph": "3",
958
+ "tag": "advanced"
959
  },
960
+ {
961
  "g": "Runtime",
962
  "icon": "βš™οΈ",
963
  "k": "JUPYTER_ROOT_DIR",
964
  "lbl": "Jupyter root directory",
965
  "type": "text",
966
+ "ph": "/home/node",
967
+ "tag": "advanced"
968
  },
969
+ {
970
  "g": "Backup",
971
  "icon": "πŸ’Ύ",
972
  "k": "WORKSPACE_GIT_USER",
973
  "lbl": "Workspace git author email",
974
  "type": "text",
975
+ "ph": "openclaw@example.com",
976
+ "tag": "optional"
977
  },
978
+ {
979
  "g": "Backup",
980
  "icon": "πŸ’Ύ",
981
  "k": "WORKSPACE_GIT_NAME",
982
  "lbl": "Workspace git author name",
983
  "type": "text",
984
+ "ph": "OpenClaw Bot",
985
+ "tag": "optional"
986
  },
987
+ {
988
  "g": "Provider Keys",
989
  "icon": "πŸ”‘",
990
  "k": "ANTHROPIC_API_KEY",
991
  "lbl": "Anthropic (Claude)",
992
  "type": "password",
993
+ "common": 0,
994
+ "tag": "credential"
995
  },
996
+ {
997
  "g": "Provider Keys",
998
  "icon": "πŸ”‘",
999
  "k": "OPENAI_API_KEY",
1000
  "lbl": "OpenAI (GPT)",
1001
  "type": "password",
1002
+ "common": 0,
1003
+ "tag": "credential"
1004
  },
1005
+ {
1006
  "g": "Provider Keys",
1007
  "icon": "πŸ”‘",
1008
  "k": "GOOGLE_API_KEY",
1009
  "lbl": "Google AI Studio",
1010
  "type": "password",
1011
+ "common": 0,
1012
+ "tag": "credential"
1013
  },
1014
+ {
1015
  "g": "Provider Keys",
1016
  "icon": "πŸ”‘",
1017
  "k": "GEMINI_API_KEY",
1018
  "lbl": "Google Gemini",
1019
  "type": "password",
1020
+ "common": 0,
1021
+ "tag": "credential"
1022
  },
1023
+ {
1024
  "g": "Provider Keys",
1025
  "icon": "πŸ”‘",
1026
  "k": "DEEPSEEK_API_KEY",
1027
  "lbl": "DeepSeek",
1028
  "type": "password",
1029
+ "common": 0,
1030
+ "tag": "credential"
1031
  },
1032
+ {
1033
  "g": "Provider Keys",
1034
  "icon": "πŸ”‘",
1035
  "k": "OPENROUTER_API_KEY",
1036
  "lbl": "OpenRouter",
1037
  "type": "password",
1038
+ "common": 1,
1039
+ "tag": "credential"
1040
  },
1041
+ {
1042
  "g": "Provider Keys",
1043
  "icon": "πŸ”‘",
1044
  "k": "OPENCODE_API_KEY",
1045
  "lbl": "OpenCode",
1046
  "type": "password",
1047
+ "common": 0,
1048
+ "tag": "credential"
1049
  },
1050
+ {
1051
  "g": "Provider Keys",
1052
  "icon": "πŸ”‘",
1053
  "k": "KILOCODE_API_KEY",
1054
  "lbl": "KiloCode",
1055
  "type": "password",
1056
+ "common": 0,
1057
+ "tag": "credential"
1058
  },
1059
+ {
1060
  "g": "Provider Keys",
1061
  "icon": "πŸ”‘",
1062
  "k": "ZAI_API_KEY",
1063
  "lbl": "Z.ai / GLM",
1064
  "type": "password",
1065
+ "common": 0,
1066
+ "tag": "credential"
1067
  },
1068
+ {
1069
  "g": "Provider Keys",
1070
  "icon": "πŸ”‘",
1071
  "k": "MOONSHOT_API_KEY",
1072
  "lbl": "Moonshot / Kimi",
1073
  "type": "password",
1074
+ "common": 0,
1075
+ "tag": "credential"
1076
  },
1077
+ {
1078
  "g": "Provider Keys",
1079
  "icon": "πŸ”‘",
1080
  "k": "MINIMAX_API_KEY",
1081
  "lbl": "MiniMax",
1082
  "type": "password",
1083
+ "common": 0,
1084
+ "tag": "credential"
1085
  },
1086
+ {
1087
  "g": "Provider Keys",
1088
  "icon": "πŸ”‘",
1089
  "k": "XIAOMI_API_KEY",
1090
  "lbl": "Xiaomi / MiMo",
1091
  "type": "password",
1092
+ "common": 0,
1093
+ "tag": "credential"
1094
  },
1095
+ {
1096
  "g": "Provider Keys",
1097
  "icon": "πŸ”‘",
1098
  "k": "VOLCANO_ENGINE_API_KEY",
1099
  "lbl": "Volcengine / Doubao",
1100
  "type": "password",
1101
+ "common": 0,
1102
+ "tag": "credential"
1103
  },
1104
+ {
1105
  "g": "Provider Keys",
1106
  "icon": "πŸ”‘",
1107
  "k": "BYTEPLUS_API_KEY",
1108
  "lbl": "BytePlus",
1109
  "type": "password",
1110
+ "common": 0,
1111
+ "tag": "credential"
1112
  },
1113
+ {
1114
  "g": "Provider Keys",
1115
  "icon": "πŸ”‘",
1116
  "k": "MISTRAL_API_KEY",
1117
  "lbl": "Mistral",
1118
  "type": "password",
1119
+ "common": 0,
1120
+ "tag": "credential"
1121
  },
1122
+ {
1123
  "g": "Provider Keys",
1124
  "icon": "πŸ”‘",
1125
  "k": "XAI_API_KEY",
1126
  "lbl": "xAI (Grok)",
1127
  "type": "password",
1128
+ "common": 0,
1129
+ "tag": "credential"
1130
  },
1131
+ {
1132
  "g": "Provider Keys",
1133
  "icon": "πŸ”‘",
1134
  "k": "NVIDIA_API_KEY",
1135
  "lbl": "NVIDIA",
1136
  "type": "password",
1137
+ "common": 0,
1138
+ "tag": "credential"
1139
  },
1140
+ {
1141
  "g": "Provider Keys",
1142
  "icon": "πŸ”‘",
1143
  "k": "GROQ_API_KEY",
1144
  "lbl": "Groq",
1145
  "type": "password",
1146
+ "common": 0,
1147
+ "tag": "credential"
1148
  },
1149
+ {
1150
  "g": "Provider Keys",
1151
  "icon": "πŸ”‘",
1152
  "k": "COHERE_API_KEY",
1153
  "lbl": "Cohere",
1154
  "type": "password",
1155
+ "common": 0,
1156
+ "tag": "credential"
1157
  },
1158
+ {
1159
  "g": "Provider Keys",
1160
  "icon": "πŸ”‘",
1161
  "k": "TOGETHER_API_KEY",
1162
  "lbl": "Together AI",
1163
  "type": "password",
1164
+ "common": 0,
1165
+ "tag": "credential"
1166
  },
1167
+ {
1168
  "g": "Provider Keys",
1169
  "icon": "πŸ”‘",
1170
  "k": "CEREBRAS_API_KEY",
1171
  "lbl": "Cerebras",
1172
  "type": "password",
1173
+ "common": 0,
1174
+ "tag": "credential"
1175
  },
1176
+ {
1177
  "g": "Provider Keys",
1178
  "icon": "πŸ”‘",
1179
  "k": "QIANFAN_API_KEY",
1180
  "lbl": "Qianfan",
1181
  "type": "password",
1182
+ "common": 0,
1183
+ "tag": "credential"
1184
  },
1185
+ {
1186
  "g": "Provider Keys",
1187
  "icon": "πŸ”‘",
1188
  "k": "MODELSTUDIO_API_KEY",
1189
  "lbl": "ModelStudio",
1190
  "type": "password",
1191
+ "common": 0,
1192
+ "tag": "credential"
1193
  },
1194
+ {
1195
  "g": "Provider Keys",
1196
  "icon": "πŸ”‘",
1197
  "k": "KIMI_API_KEY",
1198
  "lbl": "Kimi",
1199
  "type": "password",
1200
+ "common": 0,
1201
+ "tag": "credential"
1202
  },
1203
+ {
1204
  "g": "Provider Keys",
1205
  "icon": "πŸ”‘",
1206
  "k": "HUGGINGFACE_HUB_TOKEN",
1207
  "lbl": "Hugging Face token",
1208
  "type": "password",
1209
+ "common": 0,
1210
+ "tag": "credential"
1211
  },
1212
+ {
1213
  "g": "Provider Keys",
1214
  "icon": "πŸ”‘",
1215
  "k": "COPILOT_GITHUB_TOKEN",
1216
  "lbl": "GitHub Copilot",
1217
  "type": "password",
1218
+ "common": 0,
1219
+ "tag": "credential"
1220
  },
1221
+ {
1222
  "g": "Provider Keys",
1223
  "icon": "πŸ”‘",
1224
  "k": "VENICE_API_KEY",
1225
  "lbl": "Venice",
1226
  "type": "password",
1227
+ "common": 0,
1228
+ "tag": "credential"
1229
  },
1230
+ {
1231
  "g": "Provider Keys",
1232
  "icon": "πŸ”‘",
1233
  "k": "SYNTHETIC_API_KEY",
1234
  "lbl": "Synthetic",
1235
  "type": "password",
1236
+ "common": 0,
1237
+ "tag": "credential"
1238
  },
1239
+ {
1240
  "g": "Provider Keys",
1241
  "icon": "πŸ”‘",
1242
  "k": "AI_GATEWAY_API_KEY",
1243
  "lbl": "AI Gateway",
1244
  "type": "password",
1245
+ "common": 0,
1246
+ "tag": "credential"
1247
  },
1248
+ {
1249
  "g": "Provider Keys",
1250
  "icon": "πŸ”‘",
1251
  "k": "CLOUDFLARE_API_TOKEN",
1252
  "lbl": "Cloudflare API token",
1253
  "type": "password",
1254
+ "common": 0,
1255
+ "tag": "credential"
1256
  },
1257
+ {
1258
  "g": "Rotation Pools",
1259
  "icon": "πŸ”„",
1260
  "k": "ANTHROPIC_API_KEYS",
1261
  "lbl": "Anthropic pool (comma-sep)",
1262
+ "type": "text",
1263
+ "tag": "advanced"
1264
  },
1265
+ {
1266
  "g": "Rotation Pools",
1267
  "icon": "πŸ”„",
1268
  "k": "OPENAI_API_KEYS",
1269
  "lbl": "OpenAI pool",
1270
+ "type": "text",
1271
+ "tag": "advanced"
1272
  },
1273
+ {
1274
  "g": "Rotation Pools",
1275
  "icon": "πŸ”„",
1276
  "k": "GEMINI_API_KEYS",
1277
  "lbl": "Gemini pool",
1278
+ "type": "text",
1279
+ "tag": "advanced"
1280
  },
1281
+ {
1282
  "g": "Rotation Pools",
1283
  "icon": "πŸ”„",
1284
  "k": "GOOGLE_API_KEYS",
1285
  "lbl": "Google pool",
1286
+ "type": "text",
1287
+ "tag": "advanced"
1288
  },
1289
+ {
1290
  "g": "Rotation Pools",
1291
  "icon": "πŸ”„",
1292
  "k": "DEEPSEEK_API_KEYS",
1293
  "lbl": "DeepSeek pool",
1294
+ "type": "text",
1295
+ "tag": "advanced"
1296
  },
1297
+ {
1298
  "g": "Rotation Pools",
1299
  "icon": "πŸ”„",
1300
  "k": "OPENROUTER_API_KEYS",
1301
  "lbl": "OpenRouter pool",
1302
+ "type": "text",
1303
+ "tag": "advanced"
1304
  },
1305
+ {
1306
  "g": "Rotation Pools",
1307
  "icon": "πŸ”„",
1308
  "k": "OPENCODE_API_KEYS",
1309
  "lbl": "OpenCode pool",
1310
+ "type": "text",
1311
+ "tag": "advanced"
1312
  },
1313
+ {
1314
  "g": "Rotation Pools",
1315
  "icon": "πŸ”„",
1316
  "k": "KILOCODE_API_KEYS",
1317
  "lbl": "KiloCode pool",
1318
+ "type": "text",
1319
+ "tag": "advanced"
1320
  },
1321
+ {
1322
  "g": "Rotation Pools",
1323
  "icon": "πŸ”„",
1324
  "k": "ZAI_API_KEYS",
1325
  "lbl": "Z.ai / GLM pool",
1326
+ "type": "text",
1327
+ "tag": "advanced"
1328
  },
1329
+ {
1330
  "g": "Rotation Pools",
1331
  "icon": "πŸ”„",
1332
  "k": "MOONSHOT_API_KEYS",
1333
  "lbl": "Moonshot / Kimi pool",
1334
+ "type": "text",
1335
+ "tag": "advanced"
1336
  },
1337
+ {
1338
  "g": "Rotation Pools",
1339
  "icon": "πŸ”„",
1340
  "k": "MINIMAX_API_KEYS",
1341
  "lbl": "MiniMax pool",
1342
+ "type": "text",
1343
+ "tag": "advanced"
1344
  },
1345
+ {
1346
  "g": "Rotation Pools",
1347
  "icon": "πŸ”„",
1348
  "k": "XIAOMI_API_KEYS",
1349
  "lbl": "Xiaomi pool",
1350
+ "type": "text",
1351
+ "tag": "advanced"
1352
  },
1353
+ {
1354
  "g": "Rotation Pools",
1355
  "icon": "πŸ”„",
1356
  "k": "VOLCANO_ENGINE_API_KEYS",
1357
  "lbl": "Volcano Engine pool",
1358
+ "type": "text",
1359
+ "tag": "advanced"
1360
  },
1361
+ {
1362
  "g": "Rotation Pools",
1363
  "icon": "πŸ”„",
1364
  "k": "BYTEPLUS_API_KEYS",
1365
  "lbl": "BytePlus pool",
1366
+ "type": "text",
1367
+ "tag": "advanced"
1368
  },
1369
+ {
1370
  "g": "Rotation Pools",
1371
  "icon": "πŸ”„",
1372
  "k": "MISTRAL_API_KEYS",
1373
  "lbl": "Mistral pool",
1374
+ "type": "text",
1375
+ "tag": "advanced"
1376
  },
1377
+ {
1378
  "g": "Rotation Pools",
1379
  "icon": "πŸ”„",
1380
  "k": "XAI_API_KEYS",
1381
  "lbl": "xAI pool",
1382
+ "type": "text",
1383
+ "tag": "advanced"
1384
  },
1385
+ {
1386
  "g": "Rotation Pools",
1387
  "icon": "πŸ”„",
1388
  "k": "NVIDIA_API_KEYS",
1389
  "lbl": "NVIDIA pool",
1390
+ "type": "text",
1391
+ "tag": "advanced"
1392
  },
1393
+ {
1394
  "g": "Rotation Pools",
1395
  "icon": "πŸ”„",
1396
  "k": "GROQ_API_KEYS",
1397
  "lbl": "Groq pool",
1398
+ "type": "text",
1399
+ "tag": "advanced"
1400
  },
1401
+ {
1402
  "g": "Rotation Pools",
1403
  "icon": "πŸ”„",
1404
  "k": "COHERE_API_KEYS",
1405
  "lbl": "Cohere pool",
1406
+ "type": "text",
1407
+ "tag": "advanced"
1408
  },
1409
+ {
1410
  "g": "Rotation Pools",
1411
  "icon": "πŸ”„",
1412
  "k": "TOGETHER_API_KEYS",
1413
  "lbl": "Together pool",
1414
+ "type": "text",
1415
+ "tag": "advanced"
1416
  },
1417
+ {
1418
  "g": "Rotation Pools",
1419
  "icon": "πŸ”„",
1420
  "k": "CEREBRAS_API_KEYS",
1421
  "lbl": "Cerebras pool",
1422
+ "type": "text",
1423
+ "tag": "advanced"
1424
  },
1425
+ {
1426
  "g": "Rotation Pools",
1427
  "icon": "πŸ”„",
1428
  "k": "HUGGINGFACE_HUB_TOKENS",
1429
  "lbl": "HF token pool",
1430
  "type": "text"
1431
  },
1432
+ {
1433
  "g": "Model Lists",
1434
  "icon": "πŸ“‹",
1435
  "k": "OPENAI_MODELS",
1436
  "lbl": "Visible OpenAI models",
1437
  "type": "model_list",
1438
  "options_key": "OPENAI_MODELS",
1439
+ "ph": "Select models to build a comma list",
1440
+ "tag": "optional"
1441
  },
1442
+ {
1443
  "g": "Model Lists",
1444
  "icon": "πŸ“‹",
1445
  "k": "ANTHROPIC_MODELS",
1446
  "lbl": "Visible Anthropic models",
1447
  "type": "model_list",
1448
  "options_key": "ANTHROPIC_MODELS",
1449
+ "ph": "Select models to build a comma list",
1450
+ "tag": "optional"
1451
  },
1452
+ {
1453
  "g": "Model Lists",
1454
  "icon": "πŸ“‹",
1455
  "k": "GEMINI_MODELS",
1456
  "lbl": "Visible Gemini models",
1457
  "type": "model_list",
1458
  "options_key": "GEMINI_MODELS",
1459
+ "ph": "Select models to build a comma list",
1460
+ "tag": "optional"
1461
  },
1462
+ {
1463
  "g": "Model Lists",
1464
  "icon": "πŸ“‹",
1465
  "k": "DEEPSEEK_MODELS",
1466
  "lbl": "Visible DeepSeek models",
1467
  "type": "model_list",
1468
  "options_key": "DEEPSEEK_MODELS",
1469
+ "ph": "Select models to build a comma list",
1470
+ "tag": "optional"
1471
  },
1472
+ {
1473
  "g": "Model Lists",
1474
  "icon": "πŸ“‹",
1475
  "k": "OPENROUTER_MODELS",
1476
  "lbl": "Visible OpenRouter models",
1477
  "type": "model_list",
1478
  "options_key": "OPENROUTER_MODELS",
1479
+ "ph": "Select models to build a comma list",
1480
+ "tag": "optional"
1481
  },
1482
+ {
1483
  "g": "Model Lists",
1484
  "icon": "πŸ“‹",
1485
  "k": "GROQ_MODELS",
1486
  "lbl": "Visible Groq models",
1487
  "type": "model_list",
1488
  "options_key": "GROQ_MODELS",
1489
+ "ph": "Select models to build a comma list",
1490
+ "tag": "optional"
1491
  },
1492
+ {
1493
  "g": "Model Lists",
1494
  "icon": "πŸ“‹",
1495
  "k": "MISTRAL_MODELS",
1496
  "lbl": "Visible Mistral models",
1497
  "type": "model_list",
1498
  "options_key": "MISTRAL_MODELS",
1499
+ "ph": "Select models to build a comma list",
1500
+ "tag": "optional"
1501
  },
1502
+ {
1503
  "g": "Model Lists",
1504
  "icon": "πŸ“‹",
1505
  "k": "XAI_MODELS",
1506
  "lbl": "Visible xAI models",
1507
  "type": "model_list",
1508
  "options_key": "XAI_MODELS",
1509
+ "ph": "Select models to build a comma list",
1510
+ "tag": "optional"
1511
  },
1512
+ {
1513
  "g": "Model Lists",
1514
  "icon": "πŸ“‹",
1515
  "k": "COHERE_MODELS",
1516
  "lbl": "Visible Cohere models",
1517
  "type": "model_list",
1518
  "options_key": "COHERE_MODELS",
1519
+ "ph": "Select models to build a comma list",
1520
+ "tag": "optional"
1521
  },
1522
+ {
1523
  "g": "Model Lists",
1524
  "icon": "πŸ“‹",
1525
  "k": "TOGETHER_MODELS",
1526
  "lbl": "Visible Together models",
1527
  "type": "model_list",
1528
  "options_key": "TOGETHER_MODELS",
1529
+ "ph": "Select models to build a comma list",
1530
+ "tag": "optional"
1531
  },
1532
+ {
1533
  "g": "Model Lists",
1534
  "icon": "πŸ“‹",
1535
  "k": "CEREBRAS_MODELS",
1536
  "lbl": "Visible Cerebras models",
1537
  "type": "model_list",
1538
  "options_key": "CEREBRAS_MODELS",
1539
+ "ph": "Select models to build a comma list",
1540
+ "tag": "optional"
1541
  },
1542
+ {
1543
  "g": "Model Lists",
1544
  "icon": "πŸ“‹",
1545
  "k": "NVIDIA_MODELS",
1546
  "lbl": "Visible NVIDIA models",
1547
  "type": "model_list",
1548
  "options_key": "NVIDIA_MODELS",
1549
+ "ph": "Select models to build a comma list",
1550
+ "tag": "optional"
1551
  },
1552
+ {
1553
  "g": "Model Lists",
1554
  "icon": "πŸ“‹",
1555
  "k": "KILOCODE_MODELS",
1556
  "lbl": "Visible KiloCode models",
1557
  "type": "model_list",
1558
  "options_key": "KILOCODE_MODELS",
1559
+ "ph": "Select models to build a comma list",
1560
+ "tag": "optional"
1561
  },
1562
+ {
1563
  "g": "Model Lists",
1564
  "icon": "πŸ“‹",
1565
  "k": "OPENCODE_MODELS",
1566
  "lbl": "Visible OpenCode models",
1567
  "type": "model_list",
1568
  "options_key": "OPENCODE_MODELS",
1569
+ "ph": "Select models to build a comma list",
1570
+ "tag": "optional"
1571
  },
1572
+ {
1573
  "g": "Model Lists",
1574
  "icon": "πŸ“‹",
1575
  "k": "ZAI_MODELS",
1576
  "lbl": "Visible Z.ai / GLM models",
1577
  "type": "model_list",
1578
  "options_key": "ZAI_MODELS",
1579
+ "ph": "Select models to build a comma list",
1580
+ "tag": "optional"
1581
  },
1582
+ {
1583
  "g": "Model Lists",
1584
  "icon": "πŸ“‹",
1585
  "k": "MOONSHOT_MODELS",
1586
  "lbl": "Visible Moonshot / Kimi models",
1587
  "type": "model_list",
1588
  "options_key": "MOONSHOT_MODELS",
1589
+ "ph": "Select models to build a comma list",
1590
+ "tag": "optional"
1591
  },
1592
+ {
1593
  "g": "Model Lists",
1594
  "icon": "πŸ“‹",
1595
  "k": "MINIMAX_MODELS",
1596
  "lbl": "Visible MiniMax models",
1597
  "type": "model_list",
1598
  "options_key": "MINIMAX_MODELS",
1599
+ "ph": "Select models to build a comma list",
1600
+ "tag": "optional"
1601
  },
1602
+ {
1603
  "g": "Model Lists",
1604
  "icon": "πŸ“‹",
1605
  "k": "XIAOMI_MODELS",
1606
  "lbl": "Visible Xiaomi models",
1607
  "type": "model_list",
1608
  "options_key": "XIAOMI_MODELS",
1609
+ "ph": "Select models to build a comma list",
1610
+ "tag": "optional"
1611
  },
1612
+ {
1613
  "g": "Model Lists",
1614
  "icon": "πŸ“‹",
1615
  "k": "VOLCANO_ENGINE_MODELS",
1616
  "lbl": "Visible Volcano Engine models",
1617
  "type": "model_list",
1618
  "options_key": "VOLCANO_ENGINE_MODELS",
1619
+ "ph": "Select models to build a comma list",
1620
+ "tag": "optional"
1621
  },
1622
+ {
1623
  "g": "Model Lists",
1624
  "icon": "πŸ“‹",
1625
  "k": "BYTEPLUS_MODELS",
1626
  "lbl": "Visible BytePlus models",
1627
  "type": "model_list",
1628
  "options_key": "BYTEPLUS_MODELS",
1629
+ "ph": "Select models to build a comma list",
1630
+ "tag": "optional"
1631
  },
1632
+ {
1633
  "g": "Model Lists",
1634
  "icon": "πŸ“‹",
1635
  "k": "QIANFAN_MODELS",
1636
  "lbl": "Visible Qianfan models",
1637
  "type": "model_list",
1638
  "options_key": "QIANFAN_MODELS",
1639
+ "ph": "Select models to build a comma list",
1640
+ "tag": "optional"
1641
  },
1642
+ {
1643
  "g": "Model Lists",
1644
  "icon": "πŸ“‹",
1645
  "k": "MODELSTUDIO_MODELS",
1646
  "lbl": "Visible ModelStudio models",
1647
  "type": "model_list",
1648
  "options_key": "MODELSTUDIO_MODELS",
1649
+ "ph": "Select models to build a comma list",
1650
+ "tag": "optional"
1651
  },
1652
+ {
1653
  "g": "Model Lists",
1654
  "icon": "πŸ“‹",
1655
  "k": "KIMI_MODELS",
1656
  "lbl": "Visible Kimi models",
1657
  "type": "model_list",
1658
  "options_key": "KIMI_MODELS",
1659
+ "ph": "Select models to build a comma list",
1660
+ "tag": "optional"
1661
  },
1662
+ {
1663
  "g": "Model Lists",
1664
  "icon": "πŸ“‹",
1665
  "k": "HUGGINGFACE_MODELS",
1666
  "lbl": "Visible Hugging Face models",
1667
  "type": "model_list",
1668
  "options_key": "HUGGINGFACE_MODELS",
1669
+ "ph": "Select models to build a comma list",
1670
+ "tag": "optional"
1671
  },
1672
+ {
1673
  "g": "Model Lists",
1674
  "icon": "πŸ“‹",
1675
  "k": "GITHUB_COPILOT_MODELS",
1676
  "lbl": "Visible GitHub Copilot models",
1677
  "type": "model_list",
1678
  "options_key": "GITHUB_COPILOT_MODELS",
1679
+ "ph": "Select models to build a comma list",
1680
+ "tag": "optional"
1681
  },
1682
+ {
1683
  "g": "Custom Provider",
1684
  "icon": "πŸ”Œ",
1685
  "k": "CUSTOM_PROVIDER_NAME",
1686
  "lbl": "Provider display name",
1687
+ "type": "text",
1688
+ "tag": "feature"
1689
  },
1690
+ {
1691
  "g": "Custom Provider",
1692
  "icon": "πŸ”Œ",
1693
  "k": "CUSTOM_BASE_URL",
1694
  "lbl": "OpenAI-compatible base URL",
1695
+ "type": "text",
1696
+ "tag": "feature"
1697
  },
1698
+ {
1699
  "g": "Custom Provider",
1700
  "icon": "πŸ”Œ",
1701
  "k": "CUSTOM_MODEL_ID",
1702
  "lbl": "Model ID",
1703
  "type": "model",
1704
  "options_key": "LLM_MODEL",
1705
+ "ph": "custom model id",
1706
+ "tag": "feature"
1707
  },
1708
+ {
1709
  "g": "Custom Provider",
1710
  "icon": "πŸ”Œ",
1711
  "k": "CUSTOM_MODEL_NAME",
1712
  "lbl": "Friendly model name",
1713
+ "type": "text",
1714
+ "tag": "feature"
1715
  },
1716
+ {
1717
  "g": "Custom Provider",
1718
  "icon": "πŸ”Œ",
1719
  "k": "CUSTOM_API_KEY",
1720
  "lbl": "Provider API key",
1721
  "type": "password",
1722
+ "tag": "credential"
1723
  },
1724
+ {
1725
  "g": "Custom Provider",
1726
  "icon": "πŸ”Œ",
1727
  "k": "CUSTOM_API_TYPE",
 
1734
  "gemini",
1735
  "openrouter"
1736
  ],
1737
+ "ph": "openai-completions",
1738
+ "tag": "feature"
1739
  },
1740
+ {
1741
  "g": "Custom Provider",
1742
  "icon": "πŸ”Œ",
1743
  "k": "CUSTOM_CONTEXT_WINDOW",
1744
  "lbl": "Context window",
1745
  "type": "number",
1746
+ "ph": "128000",
1747
+ "tag": "advanced"
1748
  },
1749
+ {
1750
  "g": "Custom Provider",
1751
  "icon": "πŸ”Œ",
1752
  "k": "CUSTOM_MAX_TOKENS",
1753
  "lbl": "Max output tokens",
1754
  "type": "number",
1755
+ "ph": "500",
1756
+ "tag": "advanced"
1757
  },
1758
+ {
1759
  "g": "Telegram",
1760
  "icon": "✈️",
1761
  "k": "TELEGRAM_BOT_TOKEN",
1762
  "lbl": "Bot token from BotFather",
1763
  "type": "password",
1764
+ "common": 1,
1765
+ "tag": "credential"
1766
  },
1767
+ {
1768
  "g": "Telegram",
1769
  "icon": "✈️",
1770
  "k": "TELEGRAM_ALLOWED_USERS",
1771
  "lbl": "Allowed user IDs (comma)",
1772
  "type": "text",
1773
  "ph": "123456789,987654321",
1774
+ "common": 1,
1775
+ "tag": "critical"
1776
  },
1777
+ {
1778
  "g": "Telegram",
1779
  "icon": "✈️",
1780
  "k": "TELEGRAM_USER_ID",
1781
  "lbl": "Single Telegram user ID",
1782
+ "type": "text",
1783
+ "tag": "optional"
1784
  },
1785
+ {
1786
  "g": "Telegram",
1787
  "icon": "✈️",
1788
  "k": "TELEGRAM_USER_IDS",
1789
  "lbl": "Telegram user IDs (comma)",
1790
+ "type": "text",
1791
+ "tag": "optional"
1792
  },
1793
+ {
1794
  "g": "Deployment",
1795
  "icon": "🧭",
1796
  "k": "APP_BASE",
1797
  "lbl": "Public app base path",
1798
  "type": "text",
1799
+ "ph": "/app",
1800
+ "tag": "advanced"
1801
  },
1802
+ {
1803
  "g": "Deployment",
1804
  "icon": "🧭",
1805
  "k": "BACKUP_DATASET",
1806
  "lbl": "Backup dataset alias",
1807
  "type": "text",
1808
+ "ph": "huggingclaw-backup",
1809
+ "tag": "optional"
1810
  },
1811
+ {
1812
  "g": "Deployment",
1813
  "icon": "🧭",
1814
  "k": "SPACE_AUTHOR_NAME",
1815
  "lbl": "HF Space author name",
1816
+ "type": "text",
1817
+ "tag": "optional"
1818
  },
1819
+ {
1820
  "g": "Deployment",
1821
  "icon": "🧭",
1822
  "k": "SPACE_HOST",
1823
  "lbl": "HF Space host domain",
1824
+ "type": "text",
1825
+ "tag": "optional"
1826
  },
1827
+ {
1828
  "g": "Deployment",
1829
  "icon": "🧭",
1830
  "k": "PORT",
1831
  "lbl": "Public dashboard port",
1832
  "type": "number",
1833
+ "ph": "7861",
1834
+ "tag": "advanced"
1835
  },
1836
+ {
1837
  "g": "Deployment",
1838
  "icon": "🧭",
1839
  "k": "GATEWAY_PORT",
1840
  "lbl": "OpenClaw internal port",
1841
  "type": "number",
1842
+ "ph": "7860",
1843
+ "tag": "advanced"
1844
  },
1845
+ {
1846
  "g": "Deployment",
1847
  "icon": "🧭",
1848
  "k": "JUPYTER_PORT",
1849
  "lbl": "Jupyter internal port",
1850
  "type": "number",
1851
+ "ph": "8888",
1852
+ "tag": "advanced"
1853
  },
1854
+ {
1855
  "g": "Deployment",
1856
  "icon": "🧭",
1857
  "k": "JUPYTER_BASE",
1858
  "lbl": "Jupyter public base path",
1859
  "type": "text",
1860
+ "ph": "/terminal",
1861
+ "tag": "advanced"
1862
+ },
1863
+ {
1864
+ "g": "Rotation Pools",
1865
+ "icon": "πŸ”„",
1866
+ "k": "AI_GATEWAY_API_KEYS",
1867
+ "lbl": "AI Gateway pool (comma-sep)",
1868
+ "type": "text",
1869
+ "tag": "advanced"
1870
+ },
1871
+ {
1872
+ "g": "Rotation Pools",
1873
+ "icon": "πŸ”„",
1874
+ "k": "COPILOT_GITHUB_TOKENS",
1875
+ "lbl": "GitHub Copilot token pool",
1876
+ "type": "text",
1877
+ "tag": "advanced"
1878
+ },
1879
+ {
1880
+ "g": "Rotation Pools",
1881
+ "icon": "πŸ”„",
1882
+ "k": "KIMI_API_KEYS",
1883
+ "lbl": "Kimi pool",
1884
+ "type": "text",
1885
+ "tag": "advanced"
1886
+ },
1887
+ {
1888
+ "g": "Rotation Pools",
1889
+ "icon": "πŸ”„",
1890
+ "k": "MODELSTUDIO_API_KEYS",
1891
+ "lbl": "ModelStudio pool",
1892
+ "type": "text",
1893
+ "tag": "advanced"
1894
+ },
1895
+ {
1896
+ "g": "Rotation Pools",
1897
+ "icon": "πŸ”„",
1898
+ "k": "QIANFAN_API_KEYS",
1899
+ "lbl": "Qianfan pool",
1900
+ "type": "text",
1901
+ "tag": "advanced"
1902
+ },
1903
+ {
1904
+ "g": "Rotation Pools",
1905
+ "icon": "πŸ”„",
1906
+ "k": "SYNTHETIC_API_KEYS",
1907
+ "lbl": "Synthetic pool",
1908
+ "type": "text",
1909
+ "tag": "advanced"
1910
+ },
1911
+ {
1912
+ "g": "Rotation Pools",
1913
+ "icon": "πŸ”„",
1914
+ "k": "VENICE_API_KEYS",
1915
+ "lbl": "Venice pool",
1916
+ "type": "text",
1917
+ "tag": "advanced"
1918
+ },
1919
+ {
1920
+ "g": "Model Lists",
1921
+ "icon": "πŸ“‹",
1922
+ "k": "VENICE_MODELS",
1923
+ "lbl": "Visible Venice models",
1924
+ "type": "model_list",
1925
+ "options_key": "VENICE_MODELS",
1926
+ "ph": "Select models to build a comma list",
1927
+ "tag": "optional"
1928
+ },
1929
+ {
1930
+ "g": "Model Lists",
1931
+ "icon": "πŸ“‹",
1932
+ "k": "SYNTHETIC_MODELS",
1933
+ "lbl": "Visible Synthetic models",
1934
+ "type": "model_list",
1935
+ "options_key": "SYNTHETIC_MODELS",
1936
+ "ph": "Select models to build a comma list",
1937
+ "tag": "optional"
1938
  }
1939
+ ]
1940
 
1941
  const ICONS = {
1942
+ All:'🏠', Core:'⚑', Startup:'πŸš€', DevData:'πŸ§ͺ', WhatsApp:'πŸ’¬',
1943
+ Cloudflare:'☁️', Gateway:'πŸ”€', Logging:'πŸ“', Network:'🌐', Plugins:'πŸ”Œ',
1944
+ Deployment:'🧭', 'Provider Keys':'πŸ”‘', 'Rotation Pools':'πŸ”„',
1945
+ 'Model Lists':'πŸ“‹', 'Custom Provider':'🧩', Telegram:'✈️',
1946
+ Backup:'πŸ’Ύ', Runtime:'βš™οΈ', Integrations:'πŸ”—', 'Custom Env':'πŸ”§'
1947
  };
 
1948
  const $ = id => document.getElementById(id);
1949
  const esc = s => String(s ?? '').replace(/[&<>"']/g, c => ({
1950
  '&': '&amp;',
 
2124
  }
2125
 
2126
  function cardHTML(f) {
2127
+ const TAG_META = {
2128
+ critical: { cls: 'badge-critical', lbl: 'critical' },
2129
+ credential: { cls: 'badge-credential', lbl: 'credential' },
2130
+ feature: { cls: 'badge-feature', lbl: 'feature' },
2131
+ optional: { cls: 'badge-optional', lbl: 'optional' },
2132
+ advanced: { cls: 'badge-advanced', lbl: 'advanced' },
2133
+ build: { cls: 'badge-build', lbl: 'build-time' },
2134
+ };
2135
+ const tm = TAG_META[f.tag] || TAG_META.optional;
2136
+ const badge = `<span class="badge ${tm.cls}">${tm.lbl}</span>`;
2137
 
2138
+ return `<div class="env-card" data-row data-group="${esc(f.g)}" data-search="${esc((f.g + ' ' + f.k + ' ' + (f.lbl || '') + ' ' + (f.tag || '')).toLowerCase())}">
2139
  <div class="card-top">
2140
  <input type="checkbox" class="card-check" data-check="${esc(f.k)}" ${f.common ? 'data-common="1"' : ''}>
2141
  <div class="card-info">
 
2426
  <div class="sec-header">
2427
  <span class="sec-icon">${ICONS[grp] || 'πŸ“'}</span>
2428
  <span class="sec-title">${esc(grp)}</span>
2429
+ <span class="sec-count">${items.length}</span>
2430
  <div class="sec-line"></div>
2431
  </div>
2432
  <div class="cards">${items.map(cardHTML).join('')}</div>`;
health-server.js CHANGED
@@ -54,49 +54,130 @@ function deriveHfSpaceUrl() {
54
  }
55
  const HF_SPACE_URL = deriveHfSpaceUrl();
56
 
57
- // Auto-detect space privacy via HF API at startup.
58
- // Caches result so every request doesn't hit the API.
59
- let SPACE_IS_PRIVATE = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  async function detectSpacePrivacy() {
61
- if (!SPACE_ID) return;
62
- try {
63
- const token = (process.env.HF_TOKEN || "").trim();
64
- const reqOptions = {
65
- hostname: "huggingface.co",
66
- path: `/api/spaces/${SPACE_ID}`,
67
- method: "GET",
68
- headers: Object.assign(
69
- { "User-Agent": "HuggingClaw/health-server" },
70
- token ? { Authorization: `Bearer ${token}` } : {}
71
- ),
72
- };
73
- await new Promise((resolve) => {
74
- const r = https.request(reqOptions, (res) => {
75
- let body = "";
76
- res.on("data", (chunk) => { body += chunk; });
77
- res.on("end", () => {
78
- try {
79
- if (res.statusCode === 200) {
80
- const data = JSON.parse(body);
81
- SPACE_IS_PRIVATE = data.private === true;
82
- } else if (res.statusCode === 404 && !token) {
83
- // 404 with no token usually means private space
84
- SPACE_IS_PRIVATE = true;
85
- }
86
- } catch {}
87
- resolve();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  });
 
 
 
89
  });
90
- r.on("error", resolve);
91
- r.setTimeout(5000, () => { r.destroy(); resolve(); });
92
- r.end();
93
- });
94
- console.log(`[health-server] Space privacy detected: ${SPACE_IS_PRIVATE ? "private" : "public"}`);
95
- } catch {
96
- // Network error β€” default to false (safe)
 
 
 
 
 
97
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
- detectSpacePrivacy();
100
  const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
101
  "/tmp/huggingclaw-cloudflare-keepalive-status.json";
102
 
@@ -240,15 +321,63 @@ function renderDashboard(data) {
240
  <a class="hero-action env" data-space-link="env-builder" href="/env-builder">βš™οΈ Env Builder β†’</a>
241
  </div>
242
  <section class="overview">${tilesHtml}</section>
243
- <footer>Built by <a href="https://github.com/somratpro" target="_blank" rel="noopener noreferrer" style="color:inherit;text-decoration:none">@somratpro</a>${JUPYTER_ENABLED ? " Β· Terminal by JupyterLab" : ""}<br><span>Public Spaces open via <code>.hf.space</code> directly. Private Spaces require the <a href="${HF_SPACE_URL || "#"}" target="_blank" rel="noopener noreferrer" style="color:inherit">Hugging Face App tab</a> for the authenticated session${HF_SPACE_URL ? ` β€” or share <code>huggingface.co/spaces/${SPACE_ID}</code>` : ""}.</span></footer>
244
  </main>
245
  <script>
246
  document.querySelectorAll('.local-time').forEach(el=>{const d=new Date(el.getAttribute('data-iso'));if(!isNaN(d))el.textContent='At '+d.toLocaleTimeString()});
247
  const inEmbeddedApp = (() => { try { return window.top !== window.self; } catch { return true; } })();
248
  const isDirectHfSpaceHost = /\.hf\.space$/i.test(window.location.hostname);
249
  const HF_SPACE_URL = ${JSON.stringify(HF_SPACE_URL)};
250
- const SPACE_IS_PRIVATE = ${JSON.stringify(SPACE_IS_PRIVATE)};
251
- // ── Private Space Guard ──
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  // Direct .hf.space access outside the HF App iframe has no valid session cookie
253
  // for private spaces β€” HF CDN returns 404 before the request reaches the container.
254
  // Redirect users to huggingface.co/spaces/... which authenticates them properly.
@@ -259,18 +388,6 @@ function renderDashboard(data) {
259
  document.body.appendChild(notice);
260
  setTimeout(() => { window.location.replace(HF_SPACE_URL); }, 300);
261
  }
262
- // If inside the HF App iframe, force new-tab navigation so users can break out
263
- // to the standalone Space host. Also keep direct .hf.space behavior opening new tabs.
264
- const openInNewTab = inEmbeddedApp || isDirectHfSpaceHost;
265
- document.querySelectorAll('a[data-space-link]').forEach((a) => {
266
- if (openInNewTab) {
267
- a.setAttribute('target', '_blank');
268
- a.setAttribute('rel', 'noopener noreferrer');
269
- } else {
270
- a.removeAttribute('target');
271
- a.removeAttribute('rel');
272
- }
273
- });
274
  </script>
275
  </body></html>`;
276
  }
@@ -279,7 +396,6 @@ function renderPrivateRedirect(targetUrl) {
279
  const safeUrl = escapeHtml(targetUrl);
280
  return `<!doctype html><html lang="en"><head>
281
  <meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>
282
- <meta http-equiv="refresh" content="3;url=${safeUrl}"/>
283
  <title>HuggingClaw β€” Private Space</title>
284
  <style>
285
  :root{color-scheme:dark}
@@ -302,8 +418,13 @@ function renderPrivateRedirect(targetUrl) {
302
  <div class="sub">Redirecting in 3 seconds&hellip;</div>
303
  </div>
304
  <script>
305
- // Immediate redirect if JS available β€” don't wait for meta refresh
306
- setTimeout(() => { window.location.replace(${JSON.stringify(targetUrl)}); }, 100);
 
 
 
 
 
307
  </script>
308
  </body></html>`;
309
  }
@@ -403,6 +524,15 @@ function proxyHTTP(req, res, targetHost, targetPort, options = {}) {
403
  const server = http.createServer(async (req, res) => {
404
  const { pathname } = parseRequestUrl(req.url);
405
 
 
 
 
 
 
 
 
 
 
406
  if (pathname === "/health") {
407
  const gatewayReady = await probePort(GATEWAY_HOST, GATEWAY_PORT, "/health");
408
  res.writeHead(gatewayReady ? 200 : 503, { "Content-Type": "application/json" });
@@ -435,11 +565,37 @@ const server = http.createServer(async (req, res) => {
435
  // and WebSocket upgrades pass through untouched.
436
  // /health and /status are always exempt so uptime monitors keep working.
437
  const isHtmlRequest = (req.headers.accept || "").includes("text/html");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  const isDirectHfSpaceRequest = SPACE_IS_PRIVATE &&
439
  HF_SPACE_URL &&
440
  isHtmlRequest &&
441
  typeof req.headers.host === "string" &&
442
- req.headers.host.endsWith(".hf.space");
 
 
443
 
444
  if (pathname === "/env-builder" || pathname === "/env-builder/") {
445
  if (isDirectHfSpaceRequest) {
@@ -462,6 +618,7 @@ const server = http.createServer(async (req, res) => {
462
  }
463
 
464
  if (pathname === "/" || pathname === "/dashboard") {
 
465
  if (isDirectHfSpaceRequest) {
466
  res.writeHead(200, { "Content-Type": "text/html" });
467
  return res.end(renderPrivateRedirect(HF_SPACE_URL));
 
54
  }
55
  const HF_SPACE_URL = deriveHfSpaceUrl();
56
 
57
+ // ── Privacy Detection ──
58
+ // Priority order:
59
+ // 1. SPACE_PRIVACY env var ("public" / "private") β€” explicit user override, most reliable
60
+ // 2. HF API call to huggingface.co β€” auto-detect
61
+ // 3. Fail-secure default: treat as private if SPACE_ID is set
62
+
63
+ // 1. Check explicit env var override first
64
+ const _spacPrivacyEnv = (process.env.SPACE_PRIVACY || "").trim().toLowerCase();
65
+ let SPACE_IS_PRIVATE;
66
+ let _privacyDetectionDone = false;
67
+ let _privacyDetectionResolve;
68
+ const privacyDetectionReady = new Promise((res) => { _privacyDetectionResolve = res; });
69
+
70
+ if (_spacPrivacyEnv === "public") {
71
+ // User explicitly set SPACE_PRIVACY=public β€” skip API call entirely
72
+ SPACE_IS_PRIVATE = false;
73
+ _privacyDetectionDone = true;
74
+ console.log("[health-server] Space privacy: public (SPACE_PRIVACY env var override)");
75
+ privacyDetectionReady.then ? void 0 : null;
76
+ _privacyDetectionResolve && _privacyDetectionResolve();
77
+ } else if (_spacPrivacyEnv === "private") {
78
+ // User explicitly set SPACE_PRIVACY=private β€” skip API call entirely
79
+ SPACE_IS_PRIVATE = true;
80
+ _privacyDetectionDone = true;
81
+ console.log("[health-server] Space privacy: private (SPACE_PRIVACY env var override)");
82
+ _privacyDetectionResolve && _privacyDetectionResolve();
83
+ } else {
84
+ // 2. Auto-detect via HF API (with fail-secure default)
85
+ // Default to private if SPACE_ID is set β€” gets corrected by API call below.
86
+ SPACE_IS_PRIVATE = !!SPACE_ID;
87
+ }
88
+
89
  async function detectSpacePrivacy() {
90
+ // Skip if already resolved via env var
91
+ if (_spacPrivacyEnv === "public" || _spacPrivacyEnv === "private") return;
92
+ // Skip if not running on HF Spaces
93
+ if (!SPACE_ID) {
94
+ SPACE_IS_PRIVATE = false;
95
+ _privacyDetectionDone = true;
96
+ _privacyDetectionResolve();
97
+ return;
98
+ }
99
+
100
+ const token = (process.env.HF_TOKEN || "").trim();
101
+ const reqOptions = {
102
+ hostname: "huggingface.co",
103
+ path: `/api/spaces/${SPACE_ID}`,
104
+ method: "GET",
105
+ headers: Object.assign(
106
+ { "User-Agent": "HuggingClaw/health-server" },
107
+ token ? { Authorization: `Bearer ${token}` } : {}
108
+ ),
109
+ };
110
+
111
+ // Retry up to 5 times with increasing delay β€” covers transient failures at boot
112
+ const MAX_ATTEMPTS = 5;
113
+ let detected = false;
114
+
115
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
116
+ try {
117
+ const result = await new Promise((resolve) => {
118
+ const r = https.request(reqOptions, (apiRes) => {
119
+ let body = "";
120
+ apiRes.on("data", (chunk) => { body += chunk; });
121
+ apiRes.on("end", () => {
122
+ try {
123
+ if (apiRes.statusCode === 200) {
124
+ const data = JSON.parse(body);
125
+ // API confirmed privacy status
126
+ SPACE_IS_PRIVATE = data.private === true;
127
+ resolve({ ok: true, status: apiRes.statusCode });
128
+ } else if (apiRes.statusCode === 401 || apiRes.statusCode === 403) {
129
+ // 401/403 on /api/spaces means the space IS private and our token
130
+ // is missing or wrong. Mark as private.
131
+ SPACE_IS_PRIVATE = true;
132
+ resolve({ ok: true, status: apiRes.statusCode, forcedPrivate: true });
133
+ } else if (apiRes.statusCode === 404) {
134
+ // Space not found β€” shouldn't happen but treat as non-blocking; default stays.
135
+ resolve({ ok: false, status: apiRes.statusCode });
136
+ } else {
137
+ // Other non-200 β€” transient; retry
138
+ resolve({ ok: false, status: apiRes.statusCode });
139
+ }
140
+ } catch { resolve({ ok: false, status: apiRes.statusCode }); }
141
+ });
142
  });
143
+ r.on("error", (err) => resolve({ ok: false, error: err.message }));
144
+ r.setTimeout(8000, () => { r.destroy(); resolve({ ok: false, error: "timeout" }); });
145
+ r.end();
146
  });
147
+
148
+ console.log(`[health-server] Privacy detection attempt ${attempt}/${MAX_ATTEMPTS}: status=${result.status || "network-error"} ok=${result.ok}`);
149
+
150
+ if (result.ok) { detected = true; break; }
151
+ } catch (err) {
152
+ console.warn(`[health-server] Privacy detection attempt ${attempt} threw: ${err.message}`);
153
+ }
154
+
155
+ const delay = Math.min(2000 * attempt, 10000); // 2s, 4s, 6s, 8s, 10s
156
+ if (attempt < MAX_ATTEMPTS) {
157
+ await new Promise((r) => setTimeout(r, delay));
158
+ }
159
  }
160
+
161
+ if (!detected) {
162
+ console.warn(
163
+ `[health-server] Privacy detection failed after ${MAX_ATTEMPTS} attempts β€” ` +
164
+ `defaulting to ${SPACE_IS_PRIVATE ? "private" : "public"}. ` +
165
+ `TIP: Set SPACE_PRIVACY=public (or private) in your Space secrets to skip API detection.`
166
+ );
167
+ } else {
168
+ console.log(`[health-server] Space privacy detected via HF API: ${SPACE_IS_PRIVATE ? "private" : "public"}`);
169
+ }
170
+
171
+ _privacyDetectionDone = true;
172
+ _privacyDetectionResolve();
173
+ }
174
+
175
+ // Only run API detection if env var override not used
176
+ if (_spacPrivacyEnv !== "public" && _spacPrivacyEnv !== "private") {
177
+ detectSpacePrivacy();
178
+ // Re-check every 5 minutes so runtime public↔private changes are picked up
179
+ setInterval(detectSpacePrivacy, 5 * 60 * 1000);
180
  }
 
181
  const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
182
  "/tmp/huggingclaw-cloudflare-keepalive-status.json";
183
 
 
321
  <a class="hero-action env" data-space-link="env-builder" href="/env-builder">βš™οΈ Env Builder β†’</a>
322
  </div>
323
  <section class="overview">${tilesHtml}</section>
324
+ <footer>Built by <a href="https://github.com/somratpro" target="_blank" rel="noopener noreferrer" style="color:inherit;text-decoration:none">@somratpro</a>${JUPYTER_ENABLED ? " Β· Terminal by JupyterLab" : ""}<br>Env Builder &amp; JupyterLab integration by<br><a href="https://github.com/anurag008w" target="_blank" rel="noopener noreferrer" style="color:inherit;text-decoration:none">@anurag</a></footer>
325
  </main>
326
  <script>
327
  document.querySelectorAll('.local-time').forEach(el=>{const d=new Date(el.getAttribute('data-iso'));if(!isNaN(d))el.textContent='At '+d.toLocaleTimeString()});
328
  const inEmbeddedApp = (() => { try { return window.top !== window.self; } catch { return true; } })();
329
  const isDirectHfSpaceHost = /\.hf\.space$/i.test(window.location.hostname);
330
  const HF_SPACE_URL = ${JSON.stringify(HF_SPACE_URL)};
331
+ // Server-side detected value (may be stale if page was cached β€” see /api/is-private)
332
+ let SPACE_IS_PRIVATE = ${JSON.stringify(SPACE_IS_PRIVATE)};
333
+
334
+ function applyLinkTargets() {
335
+ // Keep hero buttons in-frame for private spaces; open new tab for public spaces
336
+ // accessed via the HF iframe or directly at .hf.space.
337
+ const openInNewTab = !SPACE_IS_PRIVATE && (inEmbeddedApp || isDirectHfSpaceHost);
338
+ document.querySelectorAll('a[data-space-link]').forEach((a) => {
339
+ if (openInNewTab) {
340
+ a.setAttribute('target', '_blank');
341
+ a.setAttribute('rel', 'noopener noreferrer');
342
+ } else {
343
+ a.removeAttribute('target');
344
+ a.removeAttribute('rel');
345
+ }
346
+ });
347
+ }
348
+
349
+ applyLinkTargets();
350
+
351
+ // Always re-fetch the live privacy status from the server to handle:
352
+ // 1. Startup race condition where server rendered before API detection finished
353
+ // 2. Any mismatch between client-rendered value and actual server-side state
354
+ // 3. Public spaces where the fail-secure default (private) needs correcting
355
+ // Also retries after 4s in case the first fetch raced with a server-side retry.
356
+ function syncPrivacy() {
357
+ return fetch('/api/is-private', { cache: 'no-store' })
358
+ .then(r => r.json())
359
+ .then(d => {
360
+ if (d.isPrivate !== SPACE_IS_PRIVATE) {
361
+ SPACE_IS_PRIVATE = d.isPrivate;
362
+ applyLinkTargets(); // re-run: adds or removes target="_blank" on buttons
363
+ }
364
+ return d.isPrivate;
365
+ })
366
+ .catch(() => SPACE_IS_PRIVATE);
367
+ }
368
+
369
+ if (isDirectHfSpaceHost) {
370
+ // Immediate check on page load
371
+ syncPrivacy().then(isPrivate => {
372
+ // If space appears private after first check, re-verify after server retries
373
+ // complete (server retries up to 3Γ—5s = ~15s). This catches the edge case
374
+ // where a PUBLIC space returned private due to a transient API failure.
375
+ if (isPrivate) {
376
+ setTimeout(syncPrivacy, 8000);
377
+ setTimeout(syncPrivacy, 16000);
378
+ }
379
+ });
380
+ }
381
  // Direct .hf.space access outside the HF App iframe has no valid session cookie
382
  // for private spaces β€” HF CDN returns 404 before the request reaches the container.
383
  // Redirect users to huggingface.co/spaces/... which authenticates them properly.
 
388
  document.body.appendChild(notice);
389
  setTimeout(() => { window.location.replace(HF_SPACE_URL); }, 300);
390
  }
 
 
 
 
 
 
 
 
 
 
 
 
391
  </script>
392
  </body></html>`;
393
  }
 
396
  const safeUrl = escapeHtml(targetUrl);
397
  return `<!doctype html><html lang="en"><head>
398
  <meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>
 
399
  <title>HuggingClaw β€” Private Space</title>
400
  <style>
401
  :root{color-scheme:dark}
 
418
  <div class="sub">Redirecting in 3 seconds&hellip;</div>
419
  </div>
420
  <script>
421
+ // Only auto-redirect when NOT inside an iframe (e.g. HF App tab embeds this
422
+ // page in an iframe; navigating that iframe to huggingface.co is blocked by
423
+ // X-Frame-Options and causes "refused to connect" in the browser).
424
+ const _inFrame = (() => { try { return window.top !== window.self; } catch { return true; } })();
425
+ if (!_inFrame) {
426
+ setTimeout(() => { window.location.replace(${JSON.stringify(targetUrl)}); }, 100);
427
+ }
428
  </script>
429
  </body></html>`;
430
  }
 
524
  const server = http.createServer(async (req, res) => {
525
  const { pathname } = parseRequestUrl(req.url);
526
 
527
+ // Lightweight endpoint for client-side fallback detection.
528
+ // Called by the dashboard JS if it suspects the server-rendered SPACE_IS_PRIVATE
529
+ // value was stale (race condition at startup). No auth required β€” it's not sensitive.
530
+ if (pathname === "/api/is-private") {
531
+ if (!_privacyDetectionDone) await privacyDetectionReady;
532
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
533
+ return res.end(JSON.stringify({ isPrivate: SPACE_IS_PRIVATE }));
534
+ }
535
+
536
  if (pathname === "/health") {
537
  const gatewayReady = await probePort(GATEWAY_HOST, GATEWAY_PORT, "/health");
538
  res.writeHead(gatewayReady ? 200 : 503, { "Content-Type": "application/json" });
 
565
  // and WebSocket upgrades pass through untouched.
566
  // /health and /status are always exempt so uptime monitors keep working.
567
  const isHtmlRequest = (req.headers.accept || "").includes("text/html");
568
+
569
+ // RACE CONDITION FIX: Wait for privacy detection to finish BEFORE computing
570
+ // isDirectHfSpaceRequest. Previously this const was computed immediately with
571
+ // the fail-secure default (SPACE_IS_PRIVATE=true), causing private redirects
572
+ // even when the space is actually public or the owner is accessing via HF App.
573
+ // After the very first HTML request, _privacyDetectionDone=true so no delay.
574
+ if (isHtmlRequest && !_privacyDetectionDone) await privacyDetectionReady;
575
+
576
+ // In-app navigation (clicking links within the HF iframe) sends a Referer
577
+ // from the same .hf.space origin β€” don't redirect those, only redirect
578
+ // fresh direct browser access that has no same-origin referer.
579
+ const referer = req.headers.referer || req.headers.referrer || "";
580
+ const isSameOriginNav = !!(referer && typeof req.headers.host === "string" &&
581
+ referer.startsWith(`https://${req.headers.host}`));
582
+ // When HF App embeds the space in an iframe, the initial request has
583
+ // Referer: https://huggingface.co/spaces/... (NOT .hf.space).
584
+ // HF handles authentication itself β€” if the user is not logged in, HF
585
+ // redirects them before the iframe ever loads. So a huggingface.co referer
586
+ // means the user is already authenticated; skip the private redirect.
587
+ const isFromHFApp = !!(referer && (
588
+ referer.startsWith("https://huggingface.co") ||
589
+ referer.startsWith("https://hf.co")
590
+ ));
591
+ // NOTE: computed AFTER detection is awaited above β€” always uses real value.
592
  const isDirectHfSpaceRequest = SPACE_IS_PRIVATE &&
593
  HF_SPACE_URL &&
594
  isHtmlRequest &&
595
  typeof req.headers.host === "string" &&
596
+ req.headers.host.endsWith(".hf.space") &&
597
+ !isSameOriginNav &&
598
+ !isFromHFApp;
599
 
600
  if (pathname === "/env-builder" || pathname === "/env-builder/") {
601
  if (isDirectHfSpaceRequest) {
 
618
  }
619
 
620
  if (pathname === "/" || pathname === "/dashboard") {
621
+ // Detection already awaited above (in the isHtmlRequest guard) β€” no extra wait needed.
622
  if (isDirectHfSpaceRequest) {
623
  res.writeHead(200, { "Content-Type": "text/html" });
624
  return res.end(renderPrivateRedirect(HF_SPACE_URL));
login.html CHANGED
@@ -8,7 +8,7 @@
8
  <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face Logo" style="max-width: 120px; margin-bottom: 24px;">
9
  <h3>HuggingClaw Terminal</h3>
10
  <h4>Welcome to JupyterLab</h4>
11
- <h5>The default token is <span style="color:orange;">huggingface</span></h5>
12
  <p style="color:#666;">This terminal is mounted at <code>/terminal/</code> inside the same Hugging Face Space as the OpenClaw UI.</p>
13
 
14
  {% if login_available %}
 
8
  <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face Logo" style="max-width: 120px; margin-bottom: 24px;">
9
  <h3>HuggingClaw Terminal</h3>
10
  <h4>Welcome to JupyterLab</h4>
11
+ <p style="color:#666;">Enter the <strong>JUPYTER_TOKEN</strong> you set in your Space secrets to access the terminal.</p>
12
  <p style="color:#666;">This terminal is mounted at <code>/terminal/</code> inside the same Hugging Face Space as the OpenClaw UI.</p>
13
 
14
  {% if login_available %}
start.sh CHANGED
@@ -458,8 +458,10 @@ inject_provider_models_from_env() {
458
  CONFIG_JSON=$(jq \
459
  --arg provider "$provider" \
460
  --argjson models "$models_json" \
461
- '.models.mode = "merge"
462
- | .models.providers[$provider] = ((.models.providers[$provider] // {}) + {models: $models})' <<<"$CONFIG_JSON")
 
 
463
  }
464
 
465
  # Built-in provider model envs (optional)
@@ -838,6 +840,21 @@ warmup_browser() {
838
 
839
  # ── Start background services ──
840
  export LLM_MODEL="$LLM_MODEL"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
841
  # 10. Start Health Server & Dashboard
842
  node /home/node/app/health-server.js &
843
  HEALTH_PID=$!
@@ -1420,6 +1437,9 @@ if [ -n "${HUGGINGCLAW_OPENCLAW_PLUGINS:-}" ]; then
1420
  fi
1421
  fi
1422
 
 
 
 
1423
  # ── Arbitrary startup commands from HF Variables/Secrets ──
1424
  # Recommended: use one variable, HUGGINGCLAW_RUN, as a full bash script. If the
1425
  # value starts with base64: or b64:, the rest is decoded and run as the script.
@@ -1561,6 +1581,7 @@ while true; do
1561
  fi
1562
  fi
1563
 
 
1564
  echo "Launching OpenClaw gateway on port 7860..."
1565
 
1566
  GATEWAY_ARGS=(gateway run --port 7860 --bind lan)
@@ -1597,7 +1618,9 @@ while true; do
1597
  echo "Gateway failed to start. Last 30 lines of log:"
1598
  echo "────────────────────────────────────────────"
1599
  tail -30 /home/node/.openclaw/gateway.log
1600
- exit 1
 
 
1601
  fi
1602
 
1603
  # 11. Start WhatsApp Guardian after the gateway is accepting connections
@@ -1622,7 +1645,8 @@ while true; do
1622
  GATEWAY_RESTART_COUNT=$((GATEWAY_RESTART_COUNT + 1))
1623
  if [ "$GATEWAY_MAX_RESTARTS" != "0" ] && [ "$GATEWAY_RESTART_COUNT" -ge "$GATEWAY_MAX_RESTARTS" ]; then
1624
  echo "Gateway exited with code ${GATEWAY_EXIT_CODE}; restart limit (${GATEWAY_MAX_RESTARTS}) reached."
1625
- exit "$GATEWAY_EXIT_CODE"
 
1626
  fi
1627
 
1628
  echo "Gateway exited with code ${GATEWAY_EXIT_CODE}; restarting in ${GATEWAY_RESTART_DELAY}s..."
 
458
  CONFIG_JSON=$(jq \
459
  --arg provider "$provider" \
460
  --argjson models "$models_json" \
461
+ 'if .models.providers[$provider] then
462
+ .models.mode = "merge"
463
+ | .models.providers[$provider].models = $models
464
+ else . end' <<<"$CONFIG_JSON")
465
  }
466
 
467
  # Built-in provider model envs (optional)
 
840
 
841
  # ── Start background services ──
842
  export LLM_MODEL="$LLM_MODEL"
843
+
844
+ # ── Ensure key-rotator uses the correct HF token for huggingface.co calls ──
845
+ # NODE_OPTIONS preloads multi-provider-key-rotator.cjs into health-server.js.
846
+ # The rotator patches https.request and injects HUGGINGFACE_HUB_TOKEN (or
847
+ # falls back to LLM_API_KEY) for any call to huggingface.co β€” including the
848
+ # privacy-detection API call in detectSpacePrivacy(). If HUGGINGFACE_HUB_TOKEN
849
+ # is not set (user's LLM provider is not HuggingFace), the rotator falls back
850
+ # to LLM_API_KEY, which is the AI-provider key, NOT the HF owner token.
851
+ # This causes a 401 on /api/spaces/${SPACE_ID} β†’ privacy detection always
852
+ # fails β†’ SPACE_IS_PRIVATE stays true β†’ public-space links never open in a
853
+ # new tab.
854
+ # Fix: seed HUGGINGFACE_HUB_TOKEN from HF_TOKEN when not already set.
855
+ # HF Spaces auto-injects HF_TOKEN as the space owner's token, so this is safe.
856
+ export HUGGINGFACE_HUB_TOKEN="${HUGGINGFACE_HUB_TOKEN:-${HF_TOKEN:-}}"
857
+
858
  # 10. Start Health Server & Dashboard
859
  node /home/node/app/health-server.js &
860
  HEALTH_PID=$!
 
1437
  fi
1438
  fi
1439
 
1440
+ # ── Fix config before running startup commands ──
1441
+ openclaw doctor --fix || true
1442
+
1443
  # ── Arbitrary startup commands from HF Variables/Secrets ──
1444
  # Recommended: use one variable, HUGGINGCLAW_RUN, as a full bash script. If the
1445
  # value starts with base64: or b64:, the rest is decoded and run as the script.
 
1581
  fi
1582
  fi
1583
 
1584
+ openclaw doctor --fix || true
1585
  echo "Launching OpenClaw gateway on port 7860..."
1586
 
1587
  GATEWAY_ARGS=(gateway run --port 7860 --bind lan)
 
1618
  echo "Gateway failed to start. Last 30 lines of log:"
1619
  echo "────────────────────────────────────────────"
1620
  tail -30 /home/node/.openclaw/gateway.log
1621
+ echo "Gateway failed β€” JupyterLab and env-builder still running. Retrying in 10s..."
1622
+ sleep 10
1623
+ continue
1624
  fi
1625
 
1626
  # 11. Start WhatsApp Guardian after the gateway is accepting connections
 
1645
  GATEWAY_RESTART_COUNT=$((GATEWAY_RESTART_COUNT + 1))
1646
  if [ "$GATEWAY_MAX_RESTARTS" != "0" ] && [ "$GATEWAY_RESTART_COUNT" -ge "$GATEWAY_MAX_RESTARTS" ]; then
1647
  echo "Gateway exited with code ${GATEWAY_EXIT_CODE}; restart limit (${GATEWAY_MAX_RESTARTS}) reached."
1648
+ echo "Gateway stopped β€” JupyterLab and env-builder still running."
1649
+ break
1650
  fi
1651
 
1652
  echo "Gateway exited with code ${GATEWAY_EXIT_CODE}; restarting in ${GATEWAY_RESTART_DELAY}s..."
wa-guardian.js CHANGED
@@ -91,7 +91,7 @@ async function createConnection() {
91
  method: "connect",
92
  params: {
93
  minProtocol: 3,
94
- maxProtocol: 3,
95
  client: {
96
  id: "gateway-client",
97
  version: "1.0.0",
 
91
  method: "connect",
92
  params: {
93
  minProtocol: 3,
94
+ maxProtocol: 4,
95
  client: {
96
  id: "gateway-client",
97
  version: "1.0.0",