cicy-desktop 2.1.70 → 2.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.cicy-code-ref +1 -1
  2. package/.github/workflows/linux-app-release.yml +3 -0
  3. package/.github/workflows/mac-app-release.yml +3 -0
  4. package/.github/workflows/npm-publish.yml +32 -0
  5. package/.github/workflows/windows-exe-release.yml +3 -0
  6. package/package.json +8 -9
  7. package/scripts/sync-runtime-deps.cjs +54 -0
  8. package/src/backends/homepage-react/assets/index-BpljolQs.js +365 -0
  9. package/src/backends/homepage-react/assets/{index-BniEbx_j.css → index-C9AZlTew.css} +1 -1
  10. package/src/backends/homepage-react/index.html +2 -2
  11. package/src/backends/local-teams.js +42 -4
  12. package/src/backends/sidecar-ipc.js +23 -1
  13. package/src/i18n/locales/en.json +9 -7
  14. package/src/i18n/locales/zh-CN.json +9 -7
  15. package/src/sidecar/cicy-code.js +49 -111
  16. package/src/sidecar/localbin.js +133 -0
  17. package/src/sidecar/native.js +3 -1
  18. package/workers/render/src/App.css +156 -10
  19. package/workers/render/src/App.jsx +254 -39
  20. package/.env.dev +0 -7
  21. package/src/backends/homepage-react/assets/index-B8gGhz8B.js +0 -365
  22. package/workers/render.bak.20260528-2338/DESIGN_v2.md +0 -254
  23. package/workers/render.bak.20260528-2338/index.html +0 -12
  24. package/workers/render.bak.20260528-2338/package-lock.json +0 -827
  25. package/workers/render.bak.20260528-2338/package.json +0 -19
  26. package/workers/render.bak.20260528-2338/public/_headers +0 -5
  27. package/workers/render.bak.20260528-2338/public/manifest.json +0 -6
  28. package/workers/render.bak.20260528-2338/src/App.css +0 -224
  29. package/workers/render.bak.20260528-2338/src/App.jsx +0 -1028
  30. package/workers/render.bak.20260528-2338/src/api.js +0 -285
  31. package/workers/render.bak.20260528-2338/src/cicycode-ops.js +0 -222
  32. package/workers/render.bak.20260528-2338/src/components/BackendCard.css +0 -299
  33. package/workers/render.bak.20260528-2338/src/components/BackendCard.jsx +0 -133
  34. package/workers/render.bak.20260528-2338/src/components/BackendModal.css +0 -161
  35. package/workers/render.bak.20260528-2338/src/components/BackendModal.jsx +0 -199
  36. package/workers/render.bak.20260528-2338/src/components/Button.css +0 -72
  37. package/workers/render.bak.20260528-2338/src/components/Button.jsx +0 -37
  38. package/workers/render.bak.20260528-2338/src/components/Card.css +0 -42
  39. package/workers/render.bak.20260528-2338/src/components/Card.jsx +0 -21
  40. package/workers/render.bak.20260528-2338/src/components/Icon.jsx +0 -30
  41. package/workers/render.bak.20260528-2338/src/components/Menu.css +0 -55
  42. package/workers/render.bak.20260528-2338/src/components/Menu.jsx +0 -91
  43. package/workers/render.bak.20260528-2338/src/components/SidecarBanner.css +0 -79
  44. package/workers/render.bak.20260528-2338/src/components/SidecarBanner.jsx +0 -84
  45. package/workers/render.bak.20260528-2338/src/components/StatusChip.css +0 -19
  46. package/workers/render.bak.20260528-2338/src/components/StatusChip.jsx +0 -31
  47. package/workers/render.bak.20260528-2338/src/components/Toast.css +0 -31
  48. package/workers/render.bak.20260528-2338/src/components/Toast.jsx +0 -23
  49. package/workers/render.bak.20260528-2338/src/components/WslSetupBanner.css +0 -464
  50. package/workers/render.bak.20260528-2338/src/components/WslSetupBanner.jsx +0 -716
  51. package/workers/render.bak.20260528-2338/src/dockerInstaller.js +0 -0
  52. package/workers/render.bak.20260528-2338/src/i18n/en.json +0 -116
  53. package/workers/render.bak.20260528-2338/src/i18n/fr.json +0 -116
  54. package/workers/render.bak.20260528-2338/src/i18n/index.js +0 -69
  55. package/workers/render.bak.20260528-2338/src/i18n/ja.json +0 -116
  56. package/workers/render.bak.20260528-2338/src/i18n/zh-CN.json +0 -121
  57. package/workers/render.bak.20260528-2338/src/main.js +0 -475
  58. package/workers/render.bak.20260528-2338/src/main.jsx +0 -18
  59. package/workers/render.bak.20260528-2338/src/style.css +0 -275
  60. package/workers/render.bak.20260528-2338/src/styles/base.css +0 -98
  61. package/workers/render.bak.20260528-2338/src/styles/tokens.css +0 -90
  62. package/workers/render.bak.20260528-2338/src/tos.js +0 -72
  63. package/workers/render.bak.20260528-2338/src/worker.js +0 -40
  64. package/workers/render.bak.20260528-2338/src/wslInstaller.js +0 -1563
  65. package/workers/render.bak.20260528-2338/vite.config.js +0 -36
  66. package/workers/render.bak.20260528-2338/wrangler.toml +0 -17
@@ -1 +1 @@
1
- *{box-sizing:border-box}html,body,#root{margin:0;height:100%}body{font-family:-apple-system,BlinkMacSystemFont,PingFang SC,Segoe UI,Roboto,Helvetica Neue,sans-serif;background:#07080c;color:#e5e7eb;-webkit-font-smoothing:antialiased}.shell{position:relative;min-height:100vh;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-app-region:drag}.shell--app{align-items:stretch;justify-content:stretch;flex-direction:row;background:linear-gradient(180deg,#0b0d13,#07080c);overflow:hidden;-webkit-app-region:no-drag}.shell__left{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:auto}.helper-aside{flex:0 0 auto;position:relative;background:#0f1115;border-left:1px solid rgba(255,255,255,.06);display:flex;flex-direction:column;min-width:320px;z-index:1}.helper-aside,.helper-aside *,.shell--app .main{-webkit-app-region:no-drag}.shell--app .topbar{-webkit-app-region:drag}.shell--app .topbar .user-chip,.shell--app .topbar .user-chip *,.shell--app .topbar .btn-ghost{-webkit-app-region:no-drag}.glow{position:absolute;top:-40%;right:-40%;bottom:-40%;left:-40%;z-index:0;pointer-events:none;background:radial-gradient(closest-side,rgba(91,141,247,.2),transparent 60%),radial-gradient(closest-side at 30% 70%,rgba(167,139,250,.13),transparent 65%);filter:blur(30px)}.card{-webkit-app-region:no-drag;position:relative;z-index:1;width:380px;padding:36px 36px 28px;background:linear-gradient(180deg,#141820d9,#0d1016d9);border:1px solid rgba(255,255,255,.06);border-radius:16px;box-shadow:0 1px #ffffff0a inset,0 30px 60px #0006;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);display:flex;flex-direction:column;align-items:center;gap:18px}.brand{display:flex;align-items:center;gap:12px;align-self:stretch}.brand-mark{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);box-shadow:0 8px 20px #5b8df759}.brand-mark.sm{width:28px;height:28px;border-radius:8px;box-shadow:none}.brand-mark.sm svg{width:16px;height:16px}.brand-text{line-height:1.2}.brand-name{font-weight:600;font-size:15px}.brand-sub{font-size:12px;color:#9ca3af;margin-top:2px}.tagline{margin:4px 0 0;color:#c7cdd6;font-size:13.5px;text-align:center;line-height:1.55}.btn-primary{-webkit-app-region:no-drag;margin-top:4px;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:42px;display:inline-flex;align-items:center;justify-content:center;gap:8px;background:linear-gradient(180deg,#5b8df7,#4570d8);color:#fff;border:0;border-radius:10px;font-size:14px;font-weight:500;letter-spacing:.2px;cursor:pointer;box-shadow:0 1px #ffffff2e inset,0 -1px #0000002e inset,0 10px 22px #5b8df747;transition:transform 80ms ease,filter .12s ease}.btn-primary:hover{filter:brightness(1.08)}.btn-primary:active{transform:translateY(1px)}.btn-primary:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.btn-ghost{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:6px 14px;font-size:12.5px;cursor:pointer;transition:color .12s ease,border-color .12s ease,background .12s ease}.btn-ghost.sm{padding:4px 10px;font-size:11.5px}.btn-ghost:hover{color:#e5e7eb;background:#ffffff0a;border-color:#ffffff24}.btn-ghost:disabled{opacity:.4;cursor:default}.hint{margin:0;font-size:11.5px;color:#6b7280}.spinner-row{display:inline-flex;align-items:center;gap:8px;color:#c7cdd6;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error{width:100%;font-size:12px;color:#fca5a5;background:#ef444414;border:1px solid rgba(239,68,68,.18);padding:8px 12px;border-radius:8px;text-align:center;line-height:1.5}.topbar{-webkit-app-region:drag;position:sticky;top:0;z-index:10;display:flex;align-items:center;justify-content:space-between;padding:14px 24px 12px;background:#08090e99;border-bottom:1px solid rgba(255,255,255,.05);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}[data-platform=darwin][data-fullscreen="0"] .topbar{padding-left:84px}.brand-mini{display:inline-flex;align-items:center;gap:10px}.brand-mini .brand-name{font-size:14px}.user-chip{-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:10px}.welcome{font-size:12px;color:#34d399;padding:4px 10px;border-radius:999px;background:#10b9811a;border:1px solid rgba(16,185,129,.25);animation:fadein .2s ease}@keyframes fadein{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:none}}.avatar{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);font-size:12px;font-weight:600;color:#fff}.user-name{font-size:13px;color:#c7cdd6}.glow--app{inset:-10% -10% auto -10%;height:50vh;background:radial-gradient(closest-side at 75% 0%,rgba(91,141,247,.18),transparent 65%),radial-gradient(closest-side at 20% 10%,rgba(167,139,250,.1),transparent 60%);filter:blur(40px)}.main{position:relative;z-index:1;padding:22px 32px 48px;width:100%;display:flex;flex-direction:column;gap:16px}.app__tabs{display:inline-flex;align-items:center;gap:4px;padding:4px;background:#1418208c;border:1px solid rgba(255,255,255,.06);border-radius:10px;align-self:flex-start;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.app__tab{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:0;border-radius:7px;padding:6px 14px;font-size:12.5px;font-weight:500;letter-spacing:.15px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:color .12s ease,background .12s ease}.app__tab:hover{color:#e5e7eb;background:#ffffff0a}.app__tab.is-active{color:#fff;background:#5b8df72e;box-shadow:0 0 0 1px #5b8df74d inset}.app__tab-count{font-size:10.5px;color:#9ca3af;background:#ffffff0f;padding:1px 6px;border-radius:999px;min-width:18px;text-align:center}.app__tab.is-active .app__tab-count{background:#5b8df74d;color:#fff}.app__grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.add-card{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:200px;height:200px;background:transparent;border:1.5px dashed rgba(255,255,255,.12);border-radius:14px;color:#9ca3af;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;transition:border-color .16s ease,color .16s ease,background .16s ease}.add-card:hover{border-color:#5b8df780;color:#e5e7eb;background:#5b8df70d}.add-card__plus{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;background:#ffffff0a;font-size:22px;font-weight:300;color:inherit}.add-card__label{font-size:13px;font-weight:500}.bcard--helper{background:linear-gradient(160deg,#5b8df729,#a78bfa24 60%,#1418208c);border-color:#a78bfa40}.bcard--helper .bcard__accent{background:linear-gradient(90deg,#5b8df7,#a78bfa);opacity:1}.bcard--helper:hover{border-color:#a78bfa73}.bcard__pill--helper{background:#a78bfa2e;border-color:#a78bfa4d;color:#c4b5fd;font-size:11px;font-weight:600;letter-spacing:.2px}.bcard__helper-icon{font-size:12px;filter:grayscale(.2)}.bcard__badge--free{background:#34d39926;color:#34d399;border:1px solid rgba(52,211,153,.3)}.bcard__badge--trial{background:#fbbf2426;color:#fcd34d;border:1px solid rgba(251,191,36,.3);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard__badge--local{background:#5b8df72e;color:#a5c4ff;border:1px solid rgba(91,141,247,.35);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard--helper .bcard__name{color:#f3f4f6}.bcard__desc{margin:4px 0 0;font-size:11.5px;line-height:1.5;color:#c7cdd6;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.bcard__fineprint{margin:4px 0 0;font-size:10.5px;color:#9ca3af;opacity:.8}.bcard__cta--helper{background:linear-gradient(90deg,#5b8df7,#a78bfa);box-shadow:0 6px 18px -4px #a78bfa73}.bcard__cta--helper:hover{filter:brightness(1.1)}.helper-aside__top{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 10px;border-bottom:1px solid rgba(255,255,255,.05)}.helper-aside__title{font-size:12.5px;font-weight:600;color:#c7cdd6}.helper-aside__close{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:#6b7280;font-size:20px;line-height:1;width:26px;height:26px;border-radius:6px;cursor:pointer;transition:color .12s ease,background .12s ease}.helper-aside__close:hover{color:#e5e7eb;background:#ffffff0f}.helper-modal__backdrop{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:transparent;z-index:20;animation:fadein .14s ease;padding:16px;pointer-events:auto}.helper-modal{width:min(360px,100%);background:linear-gradient(180deg,#1f2733,#1a2029);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:20px 22px 16px;color:#e5e7eb;box-shadow:0 24px 48px -12px #0009,0 1px #ffffff14 inset;animation:fadein .18s ease}.helper-modal__title{font-size:14px;font-weight:600;color:#f3f4f6;margin-bottom:8px}.helper-modal__desc{font-size:12.5px;line-height:1.6;color:#b6bcc6;margin-bottom:18px}.helper-modal__desc code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11.5px;padding:1px 6px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);border-radius:4px;color:#e7eaef}.helper-modal__actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.helper-modal__btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#ffffff0f;color:#d2d6dd;border:1px solid rgba(255,255,255,.1);border-radius:7px;padding:7px 14px;font-size:12.5px;font-weight:500;cursor:pointer;transition:background .12s ease,color .12s ease,transform 80ms ease}.helper-modal__btn:hover{background:#ffffff1a;color:#fff}.helper-modal__btn:active{transform:translateY(1px)}.helper-modal__btn--ghost{background:transparent;border-color:transparent;color:#ffffff8c;margin-right:auto;padding-left:4px;padding-right:4px}.helper-modal__btn--ghost:hover{background:transparent;color:#ffffffd9}.helper-modal__btn--primary{background:linear-gradient(180deg,#5b8df7,#4570d8);border-color:#ffffff2e;color:#fff}.helper-modal__btn--primary:hover{background:linear-gradient(180deg,#6c9af9,#5a82e0)}.helper-modal__btn:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.helper-placeholder{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 40px;text-align:center;color:#9ca3af}.helper-placeholder__mark{font-size:48px;line-height:1;filter:grayscale(.2);opacity:.85}.helper-placeholder__title{margin:0;font-size:16px;font-weight:600;color:#e5e7eb}.helper-placeholder__sub{margin:0;font-size:12.5px;line-height:1.6;color:#9ca3af;max-width:320px}.helper-placeholder__note{margin-top:8px;font-size:11px;color:#6b7280;padding:4px 10px;background:#ffffff08;border:1px solid rgba(255,255,255,.06);border-radius:999px}.helper-placeholder__note code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#c7cdd6}.section{background:linear-gradient(180deg,#141820b8,#0d1016b8);border:1px solid rgba(255,255,255,.06);border-radius:14px;box-shadow:0 1px #ffffff0a inset,0 14px 30px #00000052;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px 18px 16px}.section-head{display:flex;align-items:center;gap:8px;padding:0 4px 14px;border-bottom:1px solid rgba(255,255,255,.04);margin-bottom:14px}.section-icon{font-size:14px;opacity:.8}.section-head h2{margin:0;font-size:13.5px;font-weight:600;color:#e5e7eb;letter-spacing:.2px}.section-sub{font-size:12px;color:#6b7280}.section-body{display:flex;flex-direction:column;gap:10px}.grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.bcard{position:relative;width:200px;height:200px;background:#1418208c;border:1px solid rgba(255,255,255,.07);border-radius:14px;padding:16px;display:flex;flex-direction:column;overflow:hidden;transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease;--brand: #5b8df7;--accent-cloud: #f59e0b;--accent-custom: #8b5cf6}.bcard:hover{transform:translateY(-2px);border-color:#ffffff24;box-shadow:0 12px 32px -10px #00000073,0 1px #ffffff0a inset}.bcard__accent{position:absolute;top:0;left:0;right:0;height:3px;background:var(--brand);opacity:.9}.bcard--cloud .bcard__accent{background:var(--accent-cloud)}.bcard--custom .bcard__accent{background:var(--accent-custom)}.bcard--online:before{content:"";position:absolute;bottom:-40%;right:-20%;width:140%;height:100%;background:radial-gradient(circle at center,rgba(91,141,247,.22) 0%,transparent 60%);pointer-events:none;opacity:.55}.bcard--cloud.bcard--online:before{background:radial-gradient(circle at center,rgba(245,158,11,.22) 0%,transparent 60%)}.bcard--custom.bcard--online:before{background:radial-gradient(circle at center,rgba(139,92,246,.22) 0%,transparent 60%)}.bcard__top{display:flex;align-items:center;justify-content:space-between;position:relative;z-index:5}.bcard__pill{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;background:#08090eb3;border:1px solid rgba(255,255,255,.07);border-radius:999px;color:#9ca3af}.bcard__pill svg{display:block}.bcard__dot{width:7px;height:7px;border-radius:50%;background:#6b7280;flex-shrink:0}.bcard__dot[data-tone=ok]{background:#34d399;box-shadow:0 0 0 2px #34d39940}.bcard__dot[data-tone=off]{background:#6b7280}.bcard__dot[data-tone=warn]{background:#fbbf24;box-shadow:0 0 0 2px #fbbf2440}.bcard__dot[data-tone=err]{background:#f87171;box-shadow:0 0 0 2px #f8717140}.bcard__badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#fbbf241f;color:#fbbf24;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.bcard__body{flex:1;display:flex;flex-direction:column;gap:4px;padding-top:12px;padding-bottom:12px;position:relative;z-index:1;min-width:0}.bcard__name{margin:0;font-size:17px;font-weight:700;color:#e5e7eb;letter-spacing:-.015em;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__host{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#9ca3af;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__meta{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}.bcard__chip{font-size:10.5px;color:#9ca3af;background:#ffffff08;border:1px solid rgba(255,255,255,.06);padding:2px 7px;border-radius:999px}.bcard__cta{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;gap:8px;width:100%;height:38px;border:0;border-radius:9px;background:var(--brand);color:#fff;font-size:13px;font-weight:600;letter-spacing:.1px;cursor:pointer;transition:transform 80ms ease,background .16s ease,box-shadow .16s ease,filter .16s ease;box-shadow:0 4px 12px -2px #5b8df773}.bcard--cloud .bcard__cta{background:var(--accent-cloud);box-shadow:0 4px 12px -2px #f59e0b73}.bcard--custom .bcard__cta{background:var(--accent-custom);box-shadow:0 4px 12px -2px #8b5cf673}.bcard__cta:hover{filter:brightness(1.08)}.bcard__cta:active{transform:translateY(1px)}.bcard__cta:disabled{background:#ffffff08;color:#6b7280;cursor:not-allowed;box-shadow:none;border:1px solid rgba(255,255,255,.06)}.empty{font-size:12.5px;color:#6b7280;padding:14px 16px;background:#ffffff05;border:1px dashed rgba(255,255,255,.08);border-radius:10px}.bcard__menuwrap{position:relative}.bcard__kebab{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid transparent;border-radius:8px;background:transparent;color:#8b949e;cursor:pointer;transition:.15s}.bcard__kebab:hover:not(:disabled){color:#e6edf3;background:#ffffff0f;border-color:#ffffff1f}.bcard__kebab:disabled{opacity:.5;cursor:default}.bcard__kebab.has-dot{color:#e6edf3}.bcard__kebab.has-dot:after{content:"";position:absolute;top:3px;right:3px;width:7px;height:7px;border-radius:50%;background:#f59e0b;box-shadow:0 0 0 2px #0f1115}.bcard__menu{position:absolute;top:32px;right:0;z-index:20;min-width:150px;padding:5px;background:#1b2027;border:1px solid rgba(255,255,255,.12);border-radius:10px;box-shadow:0 10px 30px #00000073;display:flex;flex-direction:column;gap:2px}.bcard__menu-item{text-align:left;width:100%;border:none;background:transparent;color:#d1d5db;border-radius:7px;padding:7px 10px;font-size:12.5px;cursor:pointer;transition:.12s}.bcard__menu-item:hover{background:#ffffff12;color:#fff}.bcard__menu-item.is-accent{color:#fbbf24;font-weight:600}.bcard__menu-item.is-accent:hover{background:#f59e0b26;color:#fcd34d}.bcard__menu-item.is-danger{color:#f7a3a3}.bcard__menu-item.is-danger:hover{background:#ef444429;color:#fff}.docker-setup{margin-bottom:14px;padding:14px 16px;background:linear-gradient(180deg,#5b8df714,#14182080);border:1px solid rgba(91,141,247,.22);border-radius:12px}.docker-setup__head{display:flex;flex-direction:column;gap:3px;margin-bottom:12px}.docker-setup__title{font-size:13.5px;font-weight:600;color:#e6edf3}.docker-setup__sub{font-size:11.5px;color:#9ca3af;line-height:1.5}.docker-setup__steps{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.docker-step{display:flex;align-items:center;gap:8px;font-size:12px;color:#9ca3af}.docker-step__dot{width:8px;height:8px;border-radius:50%;flex:none;background:#3a4150;transition:.2s}.docker-step__label{color:#c7cdd6;min-width:96px}.docker-step__msg{color:#8b949e;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.docker-step__pct{margin-left:auto;font-variant-numeric:tabular-nums;color:#5b8df7;flex:none}.docker-step.is-running .docker-step__dot{background:#5b8df7;box-shadow:0 0 0 3px #5b8df72e;animation:dpulse 1.2s ease-in-out infinite}.docker-step.is-running .docker-step__label{color:#e6edf3}.docker-step.is-done .docker-step__dot,.docker-step.is-skip .docker-step__dot{background:#10b981}.docker-step.is-skip{opacity:.7}.docker-step.is-retry .docker-step__dot{background:#f59e0b}.docker-step.is-error .docker-step__dot{background:#ef4444;box-shadow:0 0 0 3px #ef444426}.docker-step.is-error .docker-step__msg{color:#f7a3a3}@keyframes dpulse{0%,to{opacity:1}50%{opacity:.45}}.docker-setup__actions{display:flex;gap:8px;align-items:center}.docker-setup__actions .btn-primary{width:auto;padding:7px 16px;font-size:12.5px}.bcard__ver{font-size:11px;color:#8b949e;font-variant-numeric:tabular-nums}.bcard__chip--new{color:#fbbf24;background:#f59e0b1f;border-color:#f59e0b4d}.bcard__opmsg{display:flex;align-items:center;gap:6px;margin-top:9px;font-size:11.5px;color:#9ca3af;word-break:break-word}.bcard__prog{display:flex;flex-direction:column;gap:4px;margin-top:6px;font-size:12px;line-height:1.35;color:var(--text-dim, #9da7b3)}.bcard__prog[data-status=error] .bcard__progmsg{color:#f87171}.bcard__prog[data-status=done] .bcard__progmsg{color:#4ade80}.bcard__progbar{display:block;height:4px;border-radius:2px;background:#7d879640;overflow:hidden}.bcard__progbar>span{display:block;height:100%;border-radius:2px;background:var(--accent, #3b82f6);transition:width .25s ease}.mitm-card{border:1px solid rgba(125,135,150,.22);border-radius:12px;padding:14px 16px;margin-bottom:14px;background:#1e242e66}.mitm-card--on{border-color:#4ade8059;background:#16281e66}.mitm-card__head{display:flex;align-items:center;gap:8px;margin-bottom:6px}.mitm-card__dot{width:8px;height:8px;border-radius:50%;background:#9da7b3;flex:0 0 auto}.mitm-card__dot[data-state=on]{background:#4ade80;box-shadow:0 0 6px #4ade8099}.mitm-card__dot[data-state=warn]{background:#fbbf24}.mitm-card__dot[data-state=off]{background:#60a5fa}.mitm-card__title{font-size:14px;font-weight:600;color:var(--text, #e6edf3)}.mitm-card__desc{font-size:12.5px;line-height:1.5;color:var(--text-dim, #9da7b3);margin:0 0 10px}.mitm-card__note{color:#fbbf24}.mitm-card__error{font-size:12px;color:#f87171;margin-bottom:8px}.mitm-card__actions{display:flex;gap:8px}.mitm-card__btn{font-size:13px;padding:7px 16px;border-radius:8px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:500}.mitm-card__btn:disabled{opacity:.6;cursor:default}.mitm-card__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3)}.mitm-card__sub{color:var(--text-dim, #9da7b3);opacity:.8;font-size:11.5px}.terms-gate{display:flex;align-items:center;justify-content:center;padding:24px}.terms-gate__panel{position:relative;z-index:1;width:min(680px,94vw);max-height:90vh;display:flex;flex-direction:column;background:#141921eb;border:1px solid rgba(125,135,150,.22);border-radius:16px;padding:28px 30px;box-shadow:0 24px 60px #00000080}.terms-gate__title{font-size:20px;font-weight:700;margin:0 0 4px;color:var(--text, #e6edf3)}.terms-gate__subtitle{font-size:13px;color:var(--text-dim, #9da7b3);margin:0 0 16px}.terms-gate__body{overflow-y:auto;flex:1 1 auto;min-height:0;padding-right:8px;border-top:1px solid rgba(125,135,150,.15);border-bottom:1px solid rgba(125,135,150,.15);padding-top:14px;padding-bottom:14px}.terms-gate__h2{font-size:14px;font-weight:600;margin:0 0 10px;color:var(--text, #e6edf3)}.terms-gate__summary{margin:0 0 14px;padding-left:20px}.terms-gate__summary li{font-size:13px;line-height:1.6;color:var(--text-dim, #c2cbd6);margin-bottom:8px}.terms-gate__viewfull{background:none;border:none;color:var(--accent, #3b82f6);cursor:pointer;font-size:13px;padding:4px 0;text-decoration:underline}.terms-gate__fulltext{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.6;color:var(--text-dim, #b3bcc8);background:#0003;border-radius:8px;padding:14px;margin:10px 0 0;font-family:inherit}.terms-gate__scrollhint{font-size:12px;color:#fbbf24;text-align:center;margin:12px 0 0}.terms-gate__actions{display:flex;gap:12px;justify-content:flex-end;margin-top:18px}.terms-gate__btn{font-size:14px;padding:10px 22px;border-radius:9px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:600}.terms-gate__btn:disabled{opacity:.45;cursor:not-allowed}.terms-gate__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3);font-weight:500}
1
+ *{box-sizing:border-box}html,body,#root{margin:0;height:100%}body{font-family:-apple-system,BlinkMacSystemFont,PingFang SC,Segoe UI,Roboto,Helvetica Neue,sans-serif;background:#07080c;color:#e5e7eb;-webkit-font-smoothing:antialiased}.shell{position:relative;min-height:100vh;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-app-region:drag}.shell--app{align-items:stretch;justify-content:stretch;flex-direction:row;background:linear-gradient(180deg,#0b0d13,#07080c);overflow:hidden;-webkit-app-region:no-drag}.shell__left{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:auto}.helper-aside{flex:0 0 auto;position:relative;background:#0f1115;border-left:1px solid rgba(255,255,255,.06);display:flex;flex-direction:column;min-width:320px;z-index:1}.helper-aside,.helper-aside *,.shell--app .main{-webkit-app-region:no-drag}.shell--app .topbar{-webkit-app-region:drag}.shell--app .topbar .user-chip,.shell--app .topbar .user-chip *,.shell--app .topbar .btn-ghost{-webkit-app-region:no-drag}.glow{position:absolute;top:-40%;right:-40%;bottom:-40%;left:-40%;z-index:0;pointer-events:none;background:radial-gradient(closest-side,rgba(91,141,247,.2),transparent 60%),radial-gradient(closest-side at 30% 70%,rgba(167,139,250,.13),transparent 65%);filter:blur(30px)}.card{-webkit-app-region:no-drag;position:relative;z-index:1;width:380px;padding:36px 36px 28px;background:linear-gradient(180deg,#141820d9,#0d1016d9);border:1px solid rgba(255,255,255,.06);border-radius:16px;box-shadow:0 1px #ffffff0a inset,0 30px 60px #0006;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);display:flex;flex-direction:column;align-items:center;gap:18px}.brand{display:flex;align-items:center;gap:12px;align-self:stretch}.brand-mark{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);box-shadow:0 8px 20px #5b8df759}.brand-mark.sm{width:28px;height:28px;border-radius:8px;box-shadow:none}.brand-mark.sm svg{width:16px;height:16px}.brand-text{line-height:1.2}.brand-name{font-weight:600;font-size:15px}.brand-sub{font-size:12px;color:#9ca3af;margin-top:2px}.tagline{margin:4px 0 0;color:#c7cdd6;font-size:13.5px;text-align:center;line-height:1.55}.btn-primary{-webkit-app-region:no-drag;margin-top:4px;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:42px;display:inline-flex;align-items:center;justify-content:center;gap:8px;background:linear-gradient(180deg,#5b8df7,#4570d8);color:#fff;border:0;border-radius:10px;font-size:14px;font-weight:500;letter-spacing:.2px;cursor:pointer;box-shadow:0 1px #ffffff2e inset,0 -1px #0000002e inset,0 10px 22px #5b8df747;transition:transform 80ms ease,filter .12s ease}.btn-primary:hover{filter:brightness(1.08)}.btn-primary:active{transform:translateY(1px)}.btn-primary:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.btn-ghost{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:6px 14px;font-size:12.5px;cursor:pointer;transition:color .12s ease,border-color .12s ease,background .12s ease}.btn-ghost.sm{padding:4px 10px;font-size:11.5px}.btn-ghost:hover{color:#e5e7eb;background:#ffffff0a;border-color:#ffffff24}.btn-ghost:disabled{opacity:.4;cursor:default}.hint{margin:0;font-size:11.5px;color:#6b7280}.spinner-row{display:inline-flex;align-items:center;gap:8px;color:#c7cdd6;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error{width:100%;font-size:12px;color:#fca5a5;background:#ef444414;border:1px solid rgba(239,68,68,.18);padding:8px 12px;border-radius:8px;text-align:center;line-height:1.5}.topbar{-webkit-app-region:drag;position:sticky;top:0;z-index:10;display:flex;align-items:center;justify-content:space-between;padding:14px 24px 12px;background:#08090e99;border-bottom:1px solid rgba(255,255,255,.05);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}[data-platform=darwin][data-fullscreen="0"] .topbar{padding-left:84px}.brand-mini{display:inline-flex;align-items:center;gap:10px}.brand-mini .brand-name{font-size:14px}.user-chip{-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:10px}.welcome{font-size:12px;color:#34d399;padding:4px 10px;border-radius:999px;background:#10b9811a;border:1px solid rgba(16,185,129,.25);animation:fadein .2s ease}@keyframes fadein{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:none}}.avatar{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);font-size:12px;font-weight:600;color:#fff}.user-name{font-size:13px;color:#c7cdd6}.glow--app{inset:-10% -10% auto -10%;height:50vh;background:radial-gradient(closest-side at 75% 0%,rgba(91,141,247,.18),transparent 65%),radial-gradient(closest-side at 20% 10%,rgba(167,139,250,.1),transparent 60%);filter:blur(40px)}.main{position:relative;z-index:1;padding:22px 32px 48px;width:100%;display:flex;flex-direction:column;gap:16px}.app__tabs{display:inline-flex;align-items:center;gap:4px;padding:4px;background:#1418208c;border:1px solid rgba(255,255,255,.06);border-radius:10px;align-self:flex-start;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.app__tab{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:0;border-radius:7px;padding:6px 14px;font-size:12.5px;font-weight:500;letter-spacing:.15px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:color .12s ease,background .12s ease}.app__tab:hover{color:#e5e7eb;background:#ffffff0a}.app__tab.is-active{color:#fff;background:#5b8df72e;box-shadow:0 0 0 1px #5b8df74d inset}.app__tab-count{font-size:10.5px;color:#9ca3af;background:#ffffff0f;padding:1px 6px;border-radius:999px;min-width:18px;text-align:center}.app__tab.is-active .app__tab-count{background:#5b8df74d;color:#fff}.app__grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.add-card{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:200px;height:200px;background:transparent;border:1.5px dashed rgba(255,255,255,.12);border-radius:14px;color:#9ca3af;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;transition:border-color .16s ease,color .16s ease,background .16s ease}.add-card:hover{border-color:#5b8df780;color:#e5e7eb;background:#5b8df70d}.add-card__plus{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;background:#ffffff0a;font-size:22px;font-weight:300;color:inherit}.add-card__label{font-size:13px;font-weight:500}.bcard--helper{background:linear-gradient(160deg,#5b8df729,#a78bfa24 60%,#1418208c);border-color:#a78bfa40}.bcard--helper .bcard__accent{background:linear-gradient(90deg,#5b8df7,#a78bfa);opacity:1}.bcard--helper:hover{border-color:#a78bfa73}.bcard__pill--helper{background:#a78bfa2e;border-color:#a78bfa4d;color:#c4b5fd;font-size:11px;font-weight:600;letter-spacing:.2px}.bcard__helper-icon{font-size:12px;filter:grayscale(.2)}.bcard__badge--free{background:#34d39926;color:#34d399;border:1px solid rgba(52,211,153,.3)}.bcard__badge--trial{background:#fbbf2426;color:#fcd34d;border:1px solid rgba(251,191,36,.3);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard__badge--local{background:#5b8df72e;color:#a5c4ff;border:1px solid rgba(91,141,247,.35);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard--helper .bcard__name{color:#f3f4f6}.bcard__desc{margin:4px 0 0;font-size:11.5px;line-height:1.5;color:#c7cdd6;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.bcard__fineprint{margin:4px 0 0;font-size:10.5px;color:#9ca3af;opacity:.8}.bcard__cta--helper{background:linear-gradient(90deg,#5b8df7,#a78bfa);box-shadow:0 6px 18px -4px #a78bfa73}.bcard__cta--helper:hover{filter:brightness(1.1)}.helper-aside__top{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 10px;border-bottom:1px solid rgba(255,255,255,.05)}.helper-aside__title{font-size:12.5px;font-weight:600;color:#c7cdd6}.helper-aside__close{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:#6b7280;font-size:20px;line-height:1;width:26px;height:26px;border-radius:6px;cursor:pointer;transition:color .12s ease,background .12s ease}.helper-aside__close:hover{color:#e5e7eb;background:#ffffff0f}.helper-modal__backdrop{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:transparent;z-index:20;animation:fadein .14s ease;padding:16px;pointer-events:auto}.helper-modal{width:min(360px,100%);background:linear-gradient(180deg,#1f2733,#1a2029);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:20px 22px 16px;color:#e5e7eb;box-shadow:0 24px 48px -12px #0009,0 1px #ffffff14 inset;animation:fadein .18s ease}.helper-modal__title{font-size:14px;font-weight:600;color:#f3f4f6;margin-bottom:8px}.helper-modal__desc{font-size:12.5px;line-height:1.6;color:#b6bcc6;margin-bottom:18px}.helper-modal__desc code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11.5px;padding:1px 6px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);border-radius:4px;color:#e7eaef}.helper-modal__actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.helper-modal__btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#ffffff0f;color:#d2d6dd;border:1px solid rgba(255,255,255,.1);border-radius:7px;padding:7px 14px;font-size:12.5px;font-weight:500;cursor:pointer;transition:background .12s ease,color .12s ease,transform 80ms ease}.helper-modal__btn:hover{background:#ffffff1a;color:#fff}.helper-modal__btn:active{transform:translateY(1px)}.helper-modal__btn--ghost{background:transparent;border-color:transparent;color:#ffffff8c;margin-right:auto;padding-left:4px;padding-right:4px}.helper-modal__btn--ghost:hover{background:transparent;color:#ffffffd9}.helper-modal__btn--primary{background:linear-gradient(180deg,#5b8df7,#4570d8);border-color:#ffffff2e;color:#fff}.helper-modal__btn--primary:hover{background:linear-gradient(180deg,#6c9af9,#5a82e0)}.helper-modal__btn:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.helper-placeholder{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 40px;text-align:center;color:#9ca3af}.helper-placeholder__mark{font-size:48px;line-height:1;filter:grayscale(.2);opacity:.85}.helper-placeholder__title{margin:0;font-size:16px;font-weight:600;color:#e5e7eb}.helper-placeholder__sub{margin:0;font-size:12.5px;line-height:1.6;color:#9ca3af;max-width:320px}.helper-placeholder__note{margin-top:8px;font-size:11px;color:#6b7280;padding:4px 10px;background:#ffffff08;border:1px solid rgba(255,255,255,.06);border-radius:999px}.helper-placeholder__note code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#c7cdd6}.section{background:linear-gradient(180deg,#141820b8,#0d1016b8);border:1px solid rgba(255,255,255,.06);border-radius:14px;box-shadow:0 1px #ffffff0a inset,0 14px 30px #00000052;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px 18px 16px}.section-head{display:flex;align-items:center;gap:8px;padding:0 4px 14px;border-bottom:1px solid rgba(255,255,255,.04);margin-bottom:14px}.section-icon{font-size:14px;opacity:.8}.section-head h2{margin:0;font-size:13.5px;font-weight:600;color:#e5e7eb;letter-spacing:.2px}.section-sub{font-size:12px;color:#6b7280}.section-body{display:flex;flex-direction:column;gap:10px}.grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.bcard{position:relative;width:200px;height:200px;background:#1418208c;border:1px solid rgba(255,255,255,.07);border-radius:14px;padding:16px;display:flex;flex-direction:column;overflow:hidden;transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease;--brand: #5b8df7;--accent-cloud: #f59e0b;--accent-custom: #8b5cf6}.bcard:hover{transform:translateY(-2px);border-color:#ffffff24;box-shadow:0 12px 32px -10px #00000073,0 1px #ffffff0a inset}.bcard__accent{position:absolute;top:0;left:0;right:0;height:3px;background:var(--brand);opacity:.9}.bcard--cloud .bcard__accent{background:var(--accent-cloud)}.bcard--custom .bcard__accent{background:var(--accent-custom)}.bcard--online:before{content:"";position:absolute;bottom:-40%;right:-20%;width:140%;height:100%;background:radial-gradient(circle at center,rgba(91,141,247,.22) 0%,transparent 60%);pointer-events:none;opacity:.55}.bcard--cloud.bcard--online:before{background:radial-gradient(circle at center,rgba(245,158,11,.22) 0%,transparent 60%)}.bcard--custom.bcard--online:before{background:radial-gradient(circle at center,rgba(139,92,246,.22) 0%,transparent 60%)}.bcard__top{display:flex;align-items:center;justify-content:space-between;position:relative;z-index:5}.bcard__pill{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;background:#08090eb3;border:1px solid rgba(255,255,255,.07);border-radius:999px;color:#9ca3af}.bcard__pill svg{display:block}.bcard__dot{width:7px;height:7px;border-radius:50%;background:#6b7280;flex-shrink:0}.bcard__dot[data-tone=ok]{background:#34d399;box-shadow:0 0 0 2px #34d39940}.bcard__dot[data-tone=off]{background:#6b7280}.bcard__dot[data-tone=warn]{background:#fbbf24;box-shadow:0 0 0 2px #fbbf2440}.bcard__dot[data-tone=err]{background:#f87171;box-shadow:0 0 0 2px #f8717140}.bcard__badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#fbbf241f;color:#fbbf24;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.bcard__body{flex:1;display:flex;flex-direction:column;gap:4px;padding-top:12px;padding-bottom:12px;position:relative;z-index:1;min-width:0}.bcard__name{margin:0;font-size:17px;font-weight:700;color:#e5e7eb;letter-spacing:-.015em;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__host{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#9ca3af;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__meta{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}.bcard__chip{font-size:10.5px;color:#9ca3af;background:#ffffff08;border:1px solid rgba(255,255,255,.06);padding:2px 7px;border-radius:999px}.bcard__cta{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;gap:8px;width:100%;height:38px;border:0;border-radius:9px;background:var(--brand);color:#fff;font-size:13px;font-weight:600;letter-spacing:.1px;cursor:pointer;transition:transform 80ms ease,background .16s ease,box-shadow .16s ease,filter .16s ease;box-shadow:0 4px 12px -2px #5b8df773}.bcard--cloud .bcard__cta{background:var(--accent-cloud);box-shadow:0 4px 12px -2px #f59e0b73}.bcard--custom .bcard__cta{background:var(--accent-custom);box-shadow:0 4px 12px -2px #8b5cf673}.bcard__cta:hover{filter:brightness(1.08)}.bcard__cta:active{transform:translateY(1px)}.bcard__cta:disabled{background:#ffffff08;color:#6b7280;cursor:not-allowed;box-shadow:none;border:1px solid rgba(255,255,255,.06)}.empty{font-size:12.5px;color:#6b7280;padding:14px 16px;background:#ffffff05;border:1px dashed rgba(255,255,255,.08);border-radius:10px}.bcard__menuwrap{position:relative}.bcard__kebab{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid transparent;border-radius:8px;background:transparent;color:#8b949e;cursor:pointer;transition:.15s}.bcard__kebab:hover:not(:disabled){color:#e6edf3;background:#ffffff0f;border-color:#ffffff1f}.bcard__kebab:disabled{opacity:.5;cursor:default}.bcard__kebab.has-dot{color:#e6edf3}.bcard__kebab.has-dot:after{content:"";position:absolute;top:3px;right:3px;width:7px;height:7px;border-radius:50%;background:#f59e0b;box-shadow:0 0 0 2px #0f1115}.bcard__menu{position:absolute;top:32px;right:0;z-index:20;min-width:150px;padding:5px;background:#1b2027;border:1px solid rgba(255,255,255,.12);border-radius:10px;box-shadow:0 10px 30px #00000073;display:flex;flex-direction:column;gap:2px}.bcard__menu-item{text-align:left;width:100%;border:none;background:transparent;color:#d1d5db;border-radius:7px;padding:7px 10px;font-size:12.5px;cursor:pointer;transition:.12s}.bcard__menu-item:hover{background:#ffffff12;color:#fff}.bcard__menu-item.is-accent{color:#fbbf24;font-weight:600}.bcard__menu-item.is-accent:hover{background:#f59e0b26;color:#fcd34d}.bcard__menu-item.is-danger{color:#f7a3a3}.bcard__menu-item.is-danger:hover{background:#ef444429;color:#fff}.docker-setup{margin-bottom:14px;padding:14px 16px;background:linear-gradient(180deg,#5b8df714,#14182080);border:1px solid rgba(91,141,247,.22);border-radius:12px}.docker-setup__head{display:flex;flex-direction:column;gap:3px;margin-bottom:12px}.docker-setup__title{font-size:13.5px;font-weight:600;color:#e6edf3}.docker-setup__sub{font-size:11.5px;color:#9ca3af;line-height:1.5}.docker-setup__steps{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.docker-step{display:flex;align-items:center;gap:8px;font-size:12px;color:#9ca3af}.docker-step__dot{width:8px;height:8px;border-radius:50%;flex:none;background:#3a4150;transition:.2s}.docker-step__label{color:#c7cdd6;min-width:96px}.docker-step__msg{color:#8b949e;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.docker-step__pct{margin-left:auto;font-variant-numeric:tabular-nums;color:#5b8df7;flex:none}.docker-step.is-running .docker-step__dot{background:#5b8df7;box-shadow:0 0 0 3px #5b8df72e;animation:dpulse 1.2s ease-in-out infinite}.docker-step.is-running .docker-step__label{color:#e6edf3}.docker-step.is-done .docker-step__dot,.docker-step.is-skip .docker-step__dot{background:#10b981}.docker-step.is-skip{opacity:.7}.docker-step.is-retry .docker-step__dot{background:#f59e0b}.docker-step.is-error .docker-step__dot{background:#ef4444;box-shadow:0 0 0 3px #ef444426}.docker-step.is-error .docker-step__msg{color:#f7a3a3}@keyframes dpulse{0%,to{opacity:1}50%{opacity:.45}}.docker-setup__actions{display:flex;gap:8px;align-items:center}.docker-setup__actions .btn-primary{width:auto;padding:7px 16px;font-size:12.5px}.bcard__ver{font-size:11px;color:#8b949e;font-variant-numeric:tabular-nums}.bcard__chip--new{color:#fbbf24;background:#f59e0b1f;border-color:#f59e0b4d}.bcard__opmsg{display:flex;align-items:center;gap:6px;margin-top:9px;font-size:11.5px;color:#9ca3af;word-break:break-word}.toast-host{position:fixed;right:16px;bottom:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;max-width:min(360px,calc(100vw - 32px));pointer-events:none}.toast{pointer-events:auto;position:relative;display:flex;flex-direction:column;gap:6px;padding:10px 30px 10px 12px;font-size:12.5px;line-height:1.4;color:var(--text, #e6eaf0);background:#161a21f5;border:1px solid rgba(125,135,150,.22);border-left:3px solid var(--accent, #3b82f6);border-radius:10px;box-shadow:0 8px 28px #0000006b;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);animation:toast-in .18s ease}@keyframes toast-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.toast[data-status=error]{border-left-color:#f87171}.toast[data-status=error] .toast__msg{color:#f87171}.toast[data-status=done]{border-left-color:#4ade80}.toast[data-status=done] .toast__msg{color:#4ade80}.toast__msg{word-break:break-word}.toast__x{position:absolute;top:6px;right:8px;background:none;border:none;cursor:pointer;padding:0;font-size:15px;line-height:1;color:var(--text-dim, #9da7b3)}.toast__x:hover{color:var(--text, #e6eaf0)}.toast__bar{display:block;height:4px;border-radius:2px;background:#7d879640;overflow:hidden}.toast__bar>span{display:block;height:100%;border-radius:2px;background:var(--accent, #3b82f6);transition:width .25s ease}.drawer-scrim{position:fixed;top:0;right:0;bottom:0;left:0;z-index:10000;display:flex;align-items:flex-end;justify-content:center;background:#06080c80;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:drawer-fade .18s ease}@keyframes drawer-fade{0%{opacity:0}to{opacity:1}}.drawer{width:min(560px,calc(100vw - 24px));max-height:76vh;display:flex;flex-direction:column;margin-bottom:12px;background:#14181ffa;border:1px solid rgba(125,135,150,.2);border-radius:16px 16px 12px 12px;box-shadow:0 -10px 44px #00000080;overflow:hidden;animation:drawer-up .24s cubic-bezier(.22,1,.36,1)}@keyframes drawer-up{0%{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}.drawer__head{display:flex;align-items:center;justify-content:space-between;padding:14px 14px 12px 16px;border-bottom:1px solid rgba(125,135,150,.14)}.drawer__title{display:flex;align-items:center;gap:11px}.drawer__spark{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:8px;font-size:14px;font-weight:700;background:#5b8df729;color:var(--brand, #5b8df7)}.drawer__spark--done{background:#4ade8029;color:#4ade80}.drawer__spark--error{background:#f8717129;color:#f87171}.drawer__h{font-size:13.5px;font-weight:650;color:var(--text, #e6eaf0)}.drawer__sub{font-size:11.5px;color:var(--text-dim, #9da7b3);margin-top:1px}.drawer__x{background:none;border:none;cursor:pointer;padding:2px 6px;font-size:19px;line-height:1;color:var(--text-dim, #9da7b3);border-radius:6px}.drawer__x:hover:not(:disabled){color:var(--text, #e6eaf0);background:#7d87961f}.drawer__x:disabled{opacity:.35;cursor:default}.drawer__steps{display:flex;align-items:center;gap:0;padding:14px 18px 4px}.drawer__step{display:flex;align-items:center;gap:7px;color:var(--text-dim, #9da7b3);font-size:12px}.drawer__step-dot{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;font-size:11px;font-weight:700;border:1.5px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3);background:transparent;flex:none}.drawer__step-bar{width:30px;height:1.5px;background:#7d879640;margin:0 8px}.drawer__step.is-active .drawer__step-dot{border-color:var(--brand, #5b8df7);color:var(--brand, #5b8df7);box-shadow:0 0 0 3px #5b8df72e}.drawer__step.is-active .drawer__step-label{color:var(--text, #e6eaf0)}.drawer__step.is-done .drawer__step-dot{border-color:#4ade80;color:#06210f;background:#4ade80}.drawer__step.is-done .drawer__step-bar{background:#4ade8080}.drawer__step.is-error .drawer__step-dot{border-color:#f87171;color:#f87171}.drawer__log{flex:1;min-height:96px;overflow-y:auto;margin:10px 14px;padding:10px 12px;background:#0a0c10b3;border:1px solid rgba(125,135,150,.12);border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;line-height:1.7}.drawer__log-empty{color:var(--text-dim, #9da7b3)}.drawer__line{display:flex;align-items:baseline;gap:8px;padding:1px 0}.drawer__t{color:#6b7686;flex:none;font-variant-numeric:tabular-nums}.drawer__badge{flex:none;padding:0 6px;border-radius:4px;font-size:10px;font-weight:600;background:#5b8df729;color:#8fb0f5}.drawer__badge--swap{background:#f59e0b29;color:#f5b342}.drawer__badge--done{background:#4ade8029;color:#6ee79b}.drawer__linemsg{color:var(--text, #e6eaf0);word-break:break-word}.drawer__line[data-status=error] .drawer__linemsg{color:#f87171}.drawer__line[data-status=done] .drawer__linemsg{color:#6ee79b}.drawer__line[data-status=skip] .drawer__linemsg{color:var(--text-dim, #9da7b3)}.drawer__hint{margin:0 14px 8px;padding:8px 12px;background:#f59e0b1a;border:1px solid rgba(245,158,11,.28);border-radius:8px;color:#f5b342;font-size:11.5px;line-height:1.5}.drawer__foot{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid rgba(125,135,150,.14)}.drawer__foot-status{font-size:12.5px;color:var(--text-dim, #9da7b3);margin-right:auto}.drawer__foot-status.is-error{color:#f87171}.drawer__foot-status.is-done{color:#4ade80}.drawer__btn{padding:7px 16px;border-radius:8px;cursor:pointer;font-size:12.5px;font-weight:550;background:#7d879624;border:1px solid rgba(125,135,150,.2);color:var(--text, #e6eaf0)}.drawer__btn:hover{background:#7d879638}.drawer__btn.is-accent{background:var(--brand, #5b8df7);border-color:var(--brand, #5b8df7);color:#fff}.drawer__btn.is-accent:hover{filter:brightness(1.08)}.mitm-card{border:1px solid rgba(125,135,150,.22);border-radius:12px;padding:14px 16px;margin-bottom:14px;background:#1e242e66}.mitm-card--on{border-color:#4ade8059;background:#16281e66}.mitm-pill{position:fixed;top:64px;right:16px;z-index:9998;-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:8px;padding:4px 6px 4px 11px;border-radius:999px;width:fit-content;max-width:calc(100vw - 32px);background:#141e18d9;border:1px solid rgba(74,222,128,.3);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 4px 14px #0000004d;font-size:12px;line-height:1}.mitm-pill__dot{width:7px;height:7px;border-radius:50%;flex:none;background:#4ade80;box-shadow:0 0 0 3px #4ade8029}.mitm-pill__dot[data-busy="1"]{background:#9da7b3;box-shadow:0 0 0 3px #9da7b329}.mitm-pill__text{color:var(--text-dim, #9da7b3);white-space:nowrap}.mitm-pill__off{background:none;border:none;cursor:pointer;color:#6b7686;font-size:11.5px;padding:3px 7px;border-radius:999px}.mitm-pill__off:hover{color:#f87171;background:#f871711a}.mitm-card__head{display:flex;align-items:center;gap:8px;margin-bottom:6px}.mitm-card__dot{width:8px;height:8px;border-radius:50%;background:#9da7b3;flex:0 0 auto}.mitm-card__dot[data-state=on]{background:#4ade80;box-shadow:0 0 6px #4ade8099}.mitm-card__dot[data-state=warn]{background:#fbbf24}.mitm-card__dot[data-state=off]{background:#60a5fa}.mitm-card__title{font-size:14px;font-weight:600;color:var(--text, #e6edf3)}.mitm-card__desc{font-size:12.5px;line-height:1.5;color:var(--text-dim, #9da7b3);margin:0 0 10px}.mitm-card__note{color:#fbbf24}.mitm-card__error{font-size:12px;color:#f87171;margin-bottom:8px}.mitm-card__actions{display:flex;gap:8px}.mitm-card__btn{font-size:13px;padding:7px 16px;border-radius:8px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:500}.mitm-card__btn:disabled{opacity:.6;cursor:default}.mitm-card__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3)}.mitm-card__sub{color:var(--text-dim, #9da7b3);opacity:.8;font-size:11.5px}.terms-gate{display:flex;align-items:center;justify-content:center;padding:24px}.terms-gate__panel{position:relative;z-index:1;width:min(680px,94vw);max-height:90vh;display:flex;flex-direction:column;background:#141921eb;border:1px solid rgba(125,135,150,.22);border-radius:16px;padding:28px 30px;box-shadow:0 24px 60px #00000080}.terms-gate__title{font-size:20px;font-weight:700;margin:0 0 4px;color:var(--text, #e6edf3)}.terms-gate__subtitle{font-size:13px;color:var(--text-dim, #9da7b3);margin:0 0 16px}.terms-gate__body{overflow-y:auto;flex:1 1 auto;min-height:0;padding-right:8px;border-top:1px solid rgba(125,135,150,.15);border-bottom:1px solid rgba(125,135,150,.15);padding-top:14px;padding-bottom:14px}.terms-gate__h2{font-size:14px;font-weight:600;margin:0 0 10px;color:var(--text, #e6edf3)}.terms-gate__summary{margin:0 0 14px;padding-left:20px}.terms-gate__summary li{font-size:13px;line-height:1.6;color:var(--text-dim, #c2cbd6);margin-bottom:8px}.terms-gate__viewfull{background:none;border:none;color:var(--accent, #3b82f6);cursor:pointer;font-size:13px;padding:4px 0;text-decoration:underline}.terms-gate__fulltext{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.6;color:var(--text-dim, #b3bcc8);background:#0003;border-radius:8px;padding:14px;margin:10px 0 0;font-family:inherit}.terms-gate__scrollhint{font-size:12px;color:#fbbf24;text-align:center;margin:12px 0 0}.terms-gate__actions{display:flex;gap:12px;justify-content:flex-end;margin-top:18px}.terms-gate__btn{font-size:14px;padding:10px 22px;border-radius:9px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:600}.terms-gate__btn:disabled{opacity:.45;cursor:not-allowed}.terms-gate__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3);font-weight:500}
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>CiCy Desktop</title>
7
- <script type="module" crossorigin src="./assets/index-B8gGhz8B.js"></script>
8
- <link rel="stylesheet" crossorigin href="./assets/index-BniEbx_j.css">
7
+ <script type="module" crossorigin src="./assets/index-BpljolQs.js"></script>
8
+ <link rel="stylesheet" crossorigin href="./assets/index-C9AZlTew.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -16,6 +16,7 @@ const path = require("path");
16
16
  const os = require("os");
17
17
  const http = require("http");
18
18
  const https = require("https");
19
+ const net = require("net");
19
20
  const { execFile } = require("child_process");
20
21
  const { spawn } = require("child_process");
21
22
  const { BrowserWindow } = require("electron");
@@ -33,6 +34,7 @@ const log = require("electron-log");
33
34
  const GLOBAL_JSON = path.join(os.homedir(), "cicy-ai", "global.json");
34
35
  const TEAMS_JSON = path.join(os.homedir(), "cicy-ai", "db", "teams.json");
35
36
  const HEALTH_TIMEOUT_MS = 1500;
37
+ const PORT_TIMEOUT_MS = 700; // raw TCP-connect liveness for the LOCAL sidecar
36
38
  const CACHE_MS = 4000; // small dedupe so rapid renderer polls don't fan-out
37
39
 
38
40
  let _cache = null;
@@ -131,6 +133,21 @@ function probeHealth(baseUrl, token) {
131
133
  });
132
134
  }
133
135
 
136
+ // Raw TCP-connect liveness: is ANYTHING listening on host:port? A successful
137
+ // connect means the daemon's socket is up — even if it's mid-boot or busy and
138
+ // /api/health hasn't answered yet. This is the authoritative "is it alive" for
139
+ // the LOCAL sidecar: /api/health is a heavy, blockable signal, so timing it out
140
+ // used to mis-classify a live-but-busy daemon as "stopped" — which made the card
141
+ // offer 启动并打开 and a click would spawn a SECOND cicy-code racing for :8008.
142
+ function probePort(hostname, port, timeoutMs = PORT_TIMEOUT_MS) {
143
+ return new Promise((resolve) => {
144
+ const sock = net.connect({ host: hostname, port }, () => { sock.destroy(); resolve(true); });
145
+ sock.setTimeout(timeoutMs);
146
+ sock.on("timeout", () => { sock.destroy(); resolve(false); });
147
+ sock.on("error", () => resolve(false)); // ECONNREFUSED → nothing listening
148
+ });
149
+ }
150
+
134
151
  function classify(health) {
135
152
  if (health.ok) return "running";
136
153
  if (health.status === 401 || health.status === 403) return "auth_error";
@@ -140,6 +157,27 @@ function classify(health) {
140
157
  return "error";
141
158
  }
142
159
 
160
+ // Liveness for one team. LOCAL sidecar: TCP-port-listening is authoritative
161
+ // (never down-grade a listening daemon to "stopped"), /api/health only enriches
162
+ // version/auth. REMOTE/cloud (https): the port is a CDN that's always "open", so
163
+ // /api/health stays authoritative there.
164
+ async function probeLiveness(baseUrl, token) {
165
+ let parsed;
166
+ try { parsed = new URL(baseUrl); } catch { return { status: "misconfigured", version: null, error: "bad_url" }; }
167
+ const isLocal = parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost" || parsed.hostname === "::1";
168
+ if (isLocal) {
169
+ const port = Number(parsed.port) || (parsed.protocol === "https:" ? 443 : 80);
170
+ const open = await probePort(parsed.hostname, port);
171
+ if (!open) return { status: "stopped", version: null, error: "port_closed" };
172
+ const health = await probeHealth(baseUrl, token); // best-effort enrichment
173
+ if (health.status === 401 || health.status === 403) return { status: "auth_error", version: health.version || null, error: null };
174
+ // Port is listening → the daemon is up. Health timing out just means busy.
175
+ return { status: "running", version: health.version || null, error: health.ok ? null : (health.error || null) };
176
+ }
177
+ const health = await probeHealth(baseUrl, token);
178
+ return { status: classify(health), version: health.version || null, error: health.error || null };
179
+ }
180
+
143
181
  async function list({ refresh = false } = {}) {
144
182
  if (!refresh && _cache && Date.now() < _cacheUntil) return _cache;
145
183
  const nodes = readNodes();
@@ -149,7 +187,7 @@ async function list({ refresh = false } = {}) {
149
187
  const baseUrl = node.base_url || "";
150
188
  let port = null;
151
189
  try { port = parseInt(new URL(baseUrl).port, 10) || null; } catch {}
152
- const health = await probeHealth(baseUrl, node.api_token);
190
+ const live = await probeLiveness(baseUrl, node.api_token);
153
191
  return {
154
192
  id: slug,
155
193
  name: node.name || slug,
@@ -162,9 +200,9 @@ async function list({ refresh = false } = {}) {
162
200
  install_path: node.install_path || null,
163
201
  container_name: node.container_name || null,
164
202
  image: node.image || null,
165
- status: classify(health),
166
- version: health.version || null,
167
- error: health.error || null,
203
+ status: live.status,
204
+ version: live.version || null,
205
+ error: live.error || null,
168
206
  };
169
207
  }));
170
208
  _cache = teams;
@@ -140,9 +140,31 @@ function register({ sidecarLogPath } = {}) {
140
140
  // the compliance second-consent. Returns { ok, code, stderr }.
141
141
  ipcMain.handle("mitm:ca-exec", async (_e, action) => {
142
142
  const verb = action === "uninstall" ? "uninstall-ca" : "install-ca";
143
+ const fs = require("fs"), os = require("os"), path = require("path");
144
+ // Resolve a runnable cicy-code for the self-elevating CA install. Order:
145
+ // runtime store (production schtasks build) → npx-cached platform binary
146
+ // (macOS/global npx — runtime store is EMPTY there, which is why the card's
147
+ // auto-elevate used to fail with "runtime binary not found") → global npm
148
+ // launcher → bare name on PATH.
143
149
  let exe = null;
144
150
  try { exe = require("../sidecar/runtime").binPath("cicy-code"); } catch {}
145
- if (!exe) return { ok: false, error: "cicy-code runtime binary not found" };
151
+ if (!exe) {
152
+ const cands = [];
153
+ try {
154
+ const npxRoot = path.join(os.homedir(), ".npm", "_npx");
155
+ for (const hash of fs.readdirSync(npxRoot)) {
156
+ let pkgs = []; try { pkgs = fs.readdirSync(path.join(npxRoot, hash, "node_modules")); } catch {}
157
+ for (const pkg of pkgs) {
158
+ if (pkg.startsWith("cicy-code-")) {
159
+ const p = path.join(npxRoot, hash, "node_modules", pkg, process.platform === "win32" ? "cicy-code.exe" : "cicy-code");
160
+ if (fs.existsSync(p)) cands.push(p);
161
+ }
162
+ }
163
+ }
164
+ } catch {}
165
+ cands.push("/usr/local/bin/cicy-code", "/opt/homebrew/bin/cicy-code");
166
+ exe = cands.find((c) => { try { return fs.existsSync(c); } catch { return false; } }) || "cicy-code";
167
+ }
146
168
  return await new Promise((resolve) => {
147
169
  const { execFile } = require("child_process");
148
170
  execFile(exe, ["mitm", verb], { windowsHide: false, timeout: 120000 }, (err, _stdout, stderr) => {
@@ -86,21 +86,23 @@
86
86
  "mitmConsent": {
87
87
  "cardTitle": "Local HTTPS Traffic Auditing",
88
88
  "stateUngrantedTitle": "Not enabled",
89
- "body": "When enabled, HTTPS traffic from this machine to AI providers (Claude / OpenAI / DeepSeek / Gemini) is decrypted locally for auditing. Data stays local and you can turn it off anytime.",
90
- "adminNote": "Requires writing a certificate into the system root trust store; administrator authorization is required.",
89
+ "body": "When enabled, HTTPS traffic from AI tools launched by CiCy (claude / codex, etc.) to AI providers (Claude / OpenAI / DeepSeek / Gemini) is decrypted locally for auditing. Data stays local; turn it off anytime.",
90
+ "adminNote": "Applies only to AI tools launched by CiCy (via an environment variable) — your system is not modified and no administrator access is needed.",
91
91
  "scopeNote": "Only traffic to the AI provider domains above is decrypted; all other traffic (banking, social, other sites) is never decrypted or read.",
92
92
  "localNote": "Decrypted data is stored only on your machine and is never uploaded to any server.",
93
93
  "enable": "Enable",
94
94
  "stateGrantedTitle": "Enabled",
95
- "grantedDesc": "HTTPS auditing is on; you can revoke and uninstall the certificate anytime.",
95
+ "grantedDesc": "HTTPS auditing is on for AI tools launched by CiCy (claude / codex, etc.); you can turn it off anytime.",
96
96
  "revoke": "Revoke",
97
- "revokeConfirm": "Revoking will uninstall the certificate, stop decryption, and clear the consent flag. Continue?",
97
+ "revokeConfirm": "Revoking will stop decryption auditing and clear the consent flag. Continue?",
98
98
  "stateProcessingTitle": "Working…",
99
- "processingEnable": "Installing certificate…",
100
- "processingRevoke": "Uninstalling certificate…",
99
+ "processingEnable": "Enabling…",
100
+ "processingRevoke": "Disabling…",
101
101
  "errorTitle": "Operation failed",
102
102
  "errorAdminDenied": "Administrator authorization was not granted; cancelled.",
103
- "retry": "Retry"
103
+ "retry": "Retry",
104
+ "statePillOn": "HTTPS auditing on",
105
+ "turnOff": "Turn off"
104
106
  },
105
107
  "firstRunTerms": {
106
108
  "title": "Terms of Use & Authorization Notice",
@@ -86,21 +86,23 @@
86
86
  "mitmConsent": {
87
87
  "cardTitle": "HTTPS 流量本地审计",
88
88
  "stateUngrantedTitle": "尚未启用",
89
- "body": "启用后,本机到 AI 厂商(Claude / OpenAI / DeepSeek / Gemini)的 HTTPS 将被本地审计解密,数据留本地,可随时关闭。",
90
- "adminNote": "需写入系统根证书信任库,需要管理员授权。",
89
+ "body": "启用后,CiCy 启动的 AI 工具(claude / codex 等)访问 AI 厂商(Claude / OpenAI / DeepSeek / Gemini)的 HTTPS 流量将被本地审计解密,数据留本地,随时可关闭。",
90
+ "adminNote": "通过环境变量对 CiCy 启动的 AI 工具生效,不修改系统、无需管理员授权。",
91
91
  "scopeNote": "仅解密上述 AI 厂商域名,其余一切流量(网银、社交、其他网站)不被解密、不被读取。",
92
92
  "localNote": "解密后的数据仅保存在你本机,不上传到任何服务器。",
93
93
  "enable": "启用",
94
94
  "stateGrantedTitle": "已启用",
95
- "grantedDesc": "HTTPS 审计已开启;可随时撤销并卸载证书。",
95
+ "grantedDesc": "HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具(claude / codex 等)生效;随时可关闭。",
96
96
  "revoke": "撤销",
97
- "revokeConfirm": "撤销后将卸载证书、停止解密,并清除同意标记。确定?",
97
+ "revokeConfirm": "撤销后将停止解密审计并清除同意标记。确定?",
98
98
  "stateProcessingTitle": "处理中…",
99
- "processingEnable": "正在安装证书…",
100
- "processingRevoke": "正在卸载证书…",
99
+ "processingEnable": "正在启用…",
100
+ "processingRevoke": "正在关闭…",
101
101
  "errorTitle": "操作失败",
102
102
  "errorAdminDenied": "未获得管理员授权,已取消。",
103
- "retry": "重试"
103
+ "retry": "重试",
104
+ "statePillOn": "HTTPS 审计已开启",
105
+ "turnOff": "关闭"
104
106
  },
105
107
  "firstRunTerms": {
106
108
  "title": "用户协议与授权说明",
@@ -16,22 +16,24 @@
16
16
  const fs = require("fs");
17
17
  const os = require("os");
18
18
  const http = require("http");
19
+ const net = require("net");
19
20
  const path = require("path");
20
21
  const { spawn, execFileSync } = require("child_process");
21
22
 
22
23
  const DEFAULT_PORT = Number(process.env.CICY_CODE_PORT || 8008);
23
24
 
25
+ // Liveness = "is something LISTENING on :port", via a raw TCP connect — NOT an
26
+ // HTTP GET. /health can block (mid-boot, busy, hung) and time out even while the
27
+ // daemon owns the socket; an HTTP-timeout probe then returns false and start()
28
+ // spawns a SECOND cicy-code that races the first for :8008 (the duplicate-spawn
29
+ // storm). A TCP connect succeeds the instant the socket is bound, so a present
30
+ // daemon is always reused, never double-spawned.
24
31
  function probeExisting(port = DEFAULT_PORT, timeoutMs = 500) {
25
32
  return new Promise(resolve => {
26
- const req = http.get(
27
- { host: "127.0.0.1", port, path: "/health", timeout: timeoutMs },
28
- res => {
29
- res.resume();
30
- resolve(res.statusCode >= 200 && res.statusCode < 500);
31
- }
32
- );
33
- req.on("error", () => resolve(false));
34
- req.on("timeout", () => { req.destroy(); resolve(false); });
33
+ const sock = net.connect({ host: "127.0.0.1", port }, () => { sock.destroy(); resolve(true); });
34
+ sock.setTimeout(timeoutMs);
35
+ sock.on("timeout", () => { sock.destroy(); resolve(false); });
36
+ sock.on("error", () => resolve(false)); // ECONNREFUSED → nothing there
35
37
  });
36
38
  }
37
39
 
@@ -100,46 +102,18 @@ async function start({ logPath, port = DEFAULT_PORT, force = false, version = nu
100
102
  return null;
101
103
  }
102
104
 
103
- // Runtime store first uniform across platforms.
104
- const rt = await startFromRuntime({ logPath, port });
105
- if (rt) { child = rt; return child; }
106
-
107
- if (process.platform === "win32") {
108
- // NATIVE route (2026-06 方向): cicy-code.exe + bundled slim MSYS2, no
109
- // Docker/WSL. Gated behind CICY_WIN_NATIVE=1 while in 联调; the Docker
110
- // container route below remains the transitional default until native
111
- // ships.
112
- if (process.env.CICY_WIN_NATIVE === "1") {
113
- try {
114
- const native = require("./native");
115
- const r = await native.start({ port, logPath });
116
- if (!r) { console.warn("[cicy-code-sidecar] native start failed"); return null; }
117
- child = r; // { native:true, pid|adopted, port }
118
- console.log(`[cicy-code-sidecar] started native exe (${r.adopted ? "adopted" : `pid=${r.pid}`}) on :${port}`);
119
- return child;
120
- } catch (e) {
121
- console.warn(`[cicy-code-sidecar] native start failed: ${e.message}`);
122
- return null;
123
- }
124
- }
125
- // Transitional: Windows runs cicy-code in Docker Desktop (the container's
126
- // entrypoint npx-installs cicy-code). The docker module owns
127
- // image-load-from-R2 + container run; here we just delegate.
128
- try {
129
- const docker = require("./docker");
130
- const r = await docker.start({ port });
131
- if (!r) {
132
- console.warn("[cicy-code-sidecar] Docker not ready — homepage will guide install");
133
- return null;
134
- }
135
- child = r; // { docker:true, container, id }
136
- console.log(`[cicy-code-sidecar] started in Docker container ${r.container} (${r.id})`);
137
- return child;
138
- } catch (e) {
139
- console.warn(`[cicy-code-sidecar] Docker start failed: ${e.message}`);
140
- return null;
141
- }
105
+ // UNIFIED model (主人指令): run our OWN binary at ~/.local/bin/cicy-code on
106
+ // every platform never `npx cicy-code` (which reuses a stale globally-installed
107
+ // copy and shadows updates). localbin seeds from the bundled subpackage on first
108
+ // run (zero network) and uses npm ONLY as a download channel for updates; the run
109
+ // path is always stable.
110
+ const localbin = require("./localbin");
111
+ let exe = localbin.currentLink();
112
+ if (!exe) {
113
+ try { exe = (await localbin.ensure({ version }))?.exe; }
114
+ catch (e) { console.warn(`[cicy-code-sidecar] localbin ensure failed: ${e.message}`); }
142
115
  }
116
+ if (!exe) { console.warn("[cicy-code-sidecar] no cicy-code binary available"); return null; }
143
117
 
144
118
  let stdio = ["ignore", "ignore", "ignore"];
145
119
  if (logPath) {
@@ -147,28 +121,19 @@ async function start({ logPath, port = DEFAULT_PORT, force = false, version = nu
147
121
  const fd = fs.openSync(logPath, "a");
148
122
  stdio = ["ignore", fd, fd];
149
123
  }
150
-
151
- // Run the daemon via `npx cicy-code` — no bundled/downloaded binary. The
152
- // launcher fetches the per-version binary from npm (default npmmirror for
153
- // CN; override with CICY_NPM_REGISTRY) and does its own :8008 port hygiene.
154
- // cicy-code reads PORT; we also set CICY_CODE_PORT and override the parent's
155
- // PORT (the worker sets it to its own listen port, e.g. 8101) so it doesn't
156
- // leak in and clash with the worker's HTTP server.
157
- const registry = process.env.CICY_NPM_REGISTRY || "https://registry.npmmirror.com";
158
124
  const env = {
159
125
  ...process.env,
160
126
  CICY_CODE_PORT: String(port),
161
127
  PORT: String(port),
162
- npm_config_registry: registry,
163
128
  };
164
- const npxBin = process.platform === "win32" ? "npx.cmd" : "npx";
165
- // version arg (e.g. "latest" from update()) wins over the env pin; explicit
166
- // `cicy-code@latest` makes npx re-resolve against the registry so an update
167
- // actually pulls a newer build even when an older one is cached/global.
168
- const pin = version || process.env.CICY_CODE_VERSION;
169
- const spec = pin ? `cicy-code@${pin}` : "cicy-code";
170
- child = spawn(npxBin, ["-y", spec], { stdio, detached: false, env });
171
- console.log(`[cicy-code-sidecar] spawned npx ${spec} pid=${child.pid} port=${port} registry=${registry} log=${logPath || "(none)"}`);
129
+ const args = [];
130
+ if (process.platform === "win32") {
131
+ // Windows runs the single headless 团队助手 (--helper=1) on w-1001 — no tmux
132
+ // panes, so msys2/tmux are NOT bundled or referenced anymore (主人指令 2026-06-08).
133
+ args.push("--helper=1");
134
+ }
135
+ child = spawn(exe, args, { stdio, detached: false, windowsHide: true, env });
136
+ console.log(`[cicy-code-sidecar] spawned ${exe} ${args.join(" ")} pid=${child.pid} port=${port} log=${logPath || "(none)"}`);
172
137
 
173
138
  child.on("exit", (code, signal) => {
174
139
  console.log(`[cicy-code-sidecar] exited code=${code} signal=${signal}`);
@@ -274,60 +239,33 @@ async function restart({ logPath, port = DEFAULT_PORT } = {}) {
274
239
  return start({ logPath, port, force: true });
275
240
  }
276
241
 
277
- // Update: stop, then start the LATEST build.
278
- // win32 → reload the Docker image (from R2) and re-run the container.
279
- // else → clear the npx cache + spawn `cicy-code@latest` so npx re-resolves
280
- // against the registry (npmmirror for CN) and pulls a newer build.
242
+ // Update (UNIFIED, all platforms): npm is ONLY the download channel — `npm pack`
243
+ // the latest per-platform subpackage into ~/.local/bin as a NEW version-named
244
+ // binary, re-point cicy-code at it (re-copy on Windows), then stop + start from
245
+ // that stable path and health-verify.
281
246
  async function update({ logPath, port = DEFAULT_PORT, emit } = {}) {
282
247
  const e = emit || (() => {});
283
-
284
- // Runtime store managed → versioned upgrade with health-verify + rollback.
248
+ const localbin = require("./localbin");
285
249
  try {
286
- const runtime = require("./runtime");
287
- if (runtime.currentVersion("cicy-code")) {
288
- return await runtime.upgrade("cicy-code", {
289
- emit,
290
- stop: () => stop({ port }),
291
- start: async () => {
292
- child = await startFromRuntime({ logPath, port });
293
- if (!child) throw new Error("runtime spawn failed");
294
- },
295
- verify: async () => {
296
- for (let i = 0; i < 120; i++) {
297
- if (await probeExisting(port)) return true;
298
- await new Promise((r) => setTimeout(r, 500));
299
- }
300
- return false;
301
- },
302
- });
250
+ e({ phase: "download", status: "running", message: "检查最新版本…" });
251
+ const latest = await localbin.latestVersion();
252
+ if (!latest) throw new Error("无法获取最新版本号");
253
+ await localbin.fetchToLocalBin(latest, { emit }); // download → ~/.local/bin → re-link
254
+ e({ phase: "swap", status: "running", message: `切换到 ${latest},启动…` });
255
+ await stop({ port });
256
+ await new Promise(r => setTimeout(r, 300));
257
+ const c = await start({ logPath, port, force: true });
258
+ for (let i = 0; i < 120; i++) {
259
+ if (await probeExisting(port)) { e({ phase: "done", status: "done", message: `已更新到 ${latest}` }); return c; }
260
+ await new Promise(r => setTimeout(r, 500));
303
261
  }
262
+ e({ phase: "done", status: "error", message: "新版本未在 60s 内就绪" });
263
+ return c;
304
264
  } catch (err) {
305
- console.warn(`[cicy-code-sidecar] runtime upgrade failed: ${err.message}`);
265
+ console.warn(`[cicy-code-sidecar] update failed: ${err.message}`);
306
266
  e({ phase: "done", status: "error", message: `更新失败:${err.message}` });
307
267
  return null;
308
268
  }
309
-
310
- if (process.platform === "win32") {
311
- // NATIVE route: safe-swap update with real download progress.
312
- if (process.env.CICY_WIN_NATIVE === "1") {
313
- return require("./native").update({ port, logPath, emit });
314
- }
315
- // Transitional Docker route. loadImage streams 下载镜像 % via emit.
316
- await stop({ port });
317
- try { await require("./docker").loadImage({ emit }); } catch (err) {
318
- console.warn(`[cicy-code-sidecar] docker image reload failed: ${err.message}`);
319
- e({ phase: "download", status: "error", message: `镜像更新失败:${err.message}` });
320
- }
321
- await new Promise(r => setTimeout(r, 300));
322
- e({ phase: "swap", status: "running", message: "重启容器…" });
323
- return start({ logPath, port, force: true });
324
- }
325
- e({ phase: "download", status: "running", message: "获取最新版 cicy-code…" });
326
- await stop({ port });
327
- clearNpxCache();
328
- await new Promise(r => setTimeout(r, 300));
329
- e({ phase: "swap", status: "running", message: "启动新版本…" });
330
- return start({ logPath, port, force: true, version: "latest" });
331
269
  }
332
270
 
333
271
  module.exports = { start, stop, restart, update, probeExisting, clearNpxCache };