cicy-desktop 2.1.65 → 2.1.67

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.
@@ -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}
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}
@@ -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-C0vFqx44.js"></script>
8
- <link rel="stylesheet" crossorigin href="./assets/index-BV0z4aaf.css">
7
+ <script type="module" crossorigin src="./assets/index-B8gGhz8B.js"></script>
8
+ <link rel="stylesheet" crossorigin href="./assets/index-BniEbx_j.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -116,18 +116,41 @@ function register({ sidecarLogPath } = {}) {
116
116
  // Update: stop + spawn cicy-code@latest (or reload the Docker image on
117
117
  // win32). The npx re-resolve / image pull can take a while on a cold cache,
118
118
  // so allow a longer window for :8008 to come back.
119
- ipcMain.handle("sidecar:update", async () => {
119
+ ipcMain.handle("sidecar:update", async (e) => {
120
+ // Stream phase/progress events to the homepage so the user SEES the
121
+ // update working (download %, swap, restart) instead of a frozen label.
122
+ const emit = (ev) => { try { e.sender.send("sidecar:op-progress", { op: "update", ...ev }); } catch {} };
120
123
  try {
121
- const child = await sidecar.update({ logPath: sidecarLogPath });
124
+ const child = await sidecar.update({ logPath: sidecarLogPath, port: PORT, emit });
122
125
  for (let i = 0; i < 240; i++) {
123
126
  if (await sidecar.probeExisting(PORT)) return { ok: true, pid: child?.pid || null };
124
127
  await new Promise((r) => setTimeout(r, 250));
125
128
  }
126
129
  return { ok: true, pid: child?.pid || null, warning: "updated but did not bind :8008 within 60s" };
127
- } catch (e) {
128
- return { ok: false, error: e.message };
130
+ } catch (err) {
131
+ emit({ phase: "done", status: "error", message: `更新失败:${err.message}` });
132
+ return { ok: false, error: err.message };
129
133
  }
130
134
  });
135
+
136
+ // MITM CA elevation fallback: when POST /api/mitm/consent returns
137
+ // need_elevation (cicy-code not running elevated), the homepage card calls
138
+ // this to exec `<runtime cicy-code> mitm install-ca|uninstall-ca`, which
139
+ // self-elevates (Win UAC / mac osascript / linux pkexec) — the OS prompt is
140
+ // the compliance second-consent. Returns { ok, code, stderr }.
141
+ ipcMain.handle("mitm:ca-exec", async (_e, action) => {
142
+ const verb = action === "uninstall" ? "uninstall-ca" : "install-ca";
143
+ let exe = null;
144
+ try { exe = require("../sidecar/runtime").binPath("cicy-code"); } catch {}
145
+ if (!exe) return { ok: false, error: "cicy-code runtime binary not found" };
146
+ return await new Promise((resolve) => {
147
+ const { execFile } = require("child_process");
148
+ execFile(exe, ["mitm", verb], { windowsHide: false, timeout: 120000 }, (err, _stdout, stderr) => {
149
+ if (err) resolve({ ok: false, code: err.code ?? 1, stderr: String(stderr || err.message).slice(0, 400) });
150
+ else resolve({ ok: true, code: 0 });
151
+ });
152
+ });
153
+ });
131
154
  }
132
155
 
133
156
  module.exports = { register };
@@ -82,5 +82,40 @@
82
82
  "cli": {
83
83
  "controllerTitle": "cicy - CiCy Desktop Controller",
84
84
  "usage": "Usage: cicy [command] [options]"
85
+ },
86
+ "mitmConsent": {
87
+ "cardTitle": "Local HTTPS Traffic Auditing",
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.",
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
+ "localNote": "Decrypted data is stored only on your machine and is never uploaded to any server.",
93
+ "enable": "Enable",
94
+ "stateGrantedTitle": "Enabled",
95
+ "grantedDesc": "HTTPS auditing is on; you can revoke and uninstall the certificate anytime.",
96
+ "revoke": "Revoke",
97
+ "revokeConfirm": "Revoking will uninstall the certificate, stop decryption, and clear the consent flag. Continue?",
98
+ "stateProcessingTitle": "Working…",
99
+ "processingEnable": "Installing certificate…",
100
+ "processingRevoke": "Uninstalling certificate…",
101
+ "errorTitle": "Operation failed",
102
+ "errorAdminDenied": "Administrator authorization was not granted; cancelled.",
103
+ "retry": "Retry"
104
+ },
105
+ "firstRunTerms": {
106
+ "title": "Terms of Use & Authorization Notice",
107
+ "subtitle": "Please read and accept the following before using CiCy Desktop",
108
+ "summaryTitle": "At a glance",
109
+ "summary1": "CiCy Desktop is an AI agent orchestration tool that runs on your own computer, helping you coordinate third-party AI assistants (Claude Code, Codex, Gemini, etc.) to get work done.",
110
+ "summary2": "Third-party AI and accounts are between you and the respective provider: which AI you use, whose account you log in with, and the usage and fees are your decision and responsibility, not CiCy's.",
111
+ "summary3": "To do the work, this software needs a range of authorizations on your computer (running programs, executing scripts, controlling the browser, accessing files, etc.). You are fully responsible for the operations initiated through this software.",
112
+ "summary4": "Your data stays on your machine by default; CiCy does not upload your work content for operational purposes.",
113
+ "summary5": "Two high-risk capabilities require your separate, explicit authorization and are off by default: (a) local HTTPS traffic auditing (installs a certificate, requires administrator); (b) local command/script execution. Accepting these Terms does not enable them.",
114
+ "summary6": "You must not use this software to control unauthorized devices, conduct unlawful multi-accounting / traffic faking, circumvent platform risk controls, or scrape in violation of terms.",
115
+ "viewFull": "View full Terms",
116
+ "agree": "Agree and continue",
117
+ "decline": "Decline and exit",
118
+ "mustAgree": "You cannot use this software without accepting the Terms.",
119
+ "scrollHint": "Please read to the end to continue"
85
120
  }
86
- }
121
+ }
@@ -82,5 +82,42 @@
82
82
  "cli": {
83
83
  "controllerTitle": "cicy - CiCy Desktop Controller",
84
84
  "usage": "Usage: cicy [command] [options]"
85
+ },
86
+ "mitmConsent": {
87
+ "_TODO": "professional fr translation pending — using en (do not machine-translate legal text)",
88
+ "cardTitle": "Local HTTPS Traffic Auditing",
89
+ "stateUngrantedTitle": "Not enabled",
90
+ "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.",
91
+ "adminNote": "Requires writing a certificate into the system root trust store; administrator authorization is required.",
92
+ "scopeNote": "Only traffic to the AI provider domains above is decrypted; all other traffic (banking, social, other sites) is never decrypted or read.",
93
+ "localNote": "Decrypted data is stored only on your machine and is never uploaded to any server.",
94
+ "enable": "Enable",
95
+ "stateGrantedTitle": "Enabled",
96
+ "grantedDesc": "HTTPS auditing is on; you can revoke and uninstall the certificate anytime.",
97
+ "revoke": "Revoke",
98
+ "revokeConfirm": "Revoking will uninstall the certificate, stop decryption, and clear the consent flag. Continue?",
99
+ "stateProcessingTitle": "Working…",
100
+ "processingEnable": "Installing certificate…",
101
+ "processingRevoke": "Uninstalling certificate…",
102
+ "errorTitle": "Operation failed",
103
+ "errorAdminDenied": "Administrator authorization was not granted; cancelled.",
104
+ "retry": "Retry"
105
+ },
106
+ "firstRunTerms": {
107
+ "_TODO": "professional fr translation pending — using en (do not machine-translate legal text)",
108
+ "title": "Terms of Use & Authorization Notice",
109
+ "subtitle": "Please read and accept the following before using CiCy Desktop",
110
+ "summaryTitle": "At a glance",
111
+ "summary1": "CiCy Desktop is an AI agent orchestration tool that runs on your own computer, helping you coordinate third-party AI assistants (Claude Code, Codex, Gemini, etc.) to get work done.",
112
+ "summary2": "Third-party AI and accounts are between you and the respective provider: which AI you use, whose account you log in with, and the usage and fees are your decision and responsibility, not CiCy's.",
113
+ "summary3": "To do the work, this software needs a range of authorizations on your computer (running programs, executing scripts, controlling the browser, accessing files, etc.). You are fully responsible for the operations initiated through this software.",
114
+ "summary4": "Your data stays on your machine by default; CiCy does not upload your work content for operational purposes.",
115
+ "summary5": "Two high-risk capabilities require your separate, explicit authorization and are off by default: (a) local HTTPS traffic auditing (installs a certificate, requires administrator); (b) local command/script execution. Accepting these Terms does not enable them.",
116
+ "summary6": "You must not use this software to control unauthorized devices, conduct unlawful multi-accounting / traffic faking, circumvent platform risk controls, or scrape in violation of terms.",
117
+ "viewFull": "View full Terms",
118
+ "agree": "Agree and continue",
119
+ "decline": "Decline and exit",
120
+ "mustAgree": "You cannot use this software without accepting the Terms.",
121
+ "scrollHint": "Please read to the end to continue"
85
122
  }
86
- }
123
+ }
@@ -82,5 +82,42 @@
82
82
  "cli": {
83
83
  "controllerTitle": "cicy - CiCy Desktop Controller",
84
84
  "usage": "Usage: cicy [command] [options]"
85
+ },
86
+ "mitmConsent": {
87
+ "_TODO": "professional ja translation pending — using en (do not machine-translate legal text)",
88
+ "cardTitle": "Local HTTPS Traffic Auditing",
89
+ "stateUngrantedTitle": "Not enabled",
90
+ "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.",
91
+ "adminNote": "Requires writing a certificate into the system root trust store; administrator authorization is required.",
92
+ "scopeNote": "Only traffic to the AI provider domains above is decrypted; all other traffic (banking, social, other sites) is never decrypted or read.",
93
+ "localNote": "Decrypted data is stored only on your machine and is never uploaded to any server.",
94
+ "enable": "Enable",
95
+ "stateGrantedTitle": "Enabled",
96
+ "grantedDesc": "HTTPS auditing is on; you can revoke and uninstall the certificate anytime.",
97
+ "revoke": "Revoke",
98
+ "revokeConfirm": "Revoking will uninstall the certificate, stop decryption, and clear the consent flag. Continue?",
99
+ "stateProcessingTitle": "Working…",
100
+ "processingEnable": "Installing certificate…",
101
+ "processingRevoke": "Uninstalling certificate…",
102
+ "errorTitle": "Operation failed",
103
+ "errorAdminDenied": "Administrator authorization was not granted; cancelled.",
104
+ "retry": "Retry"
105
+ },
106
+ "firstRunTerms": {
107
+ "_TODO": "professional ja translation pending — using en (do not machine-translate legal text)",
108
+ "title": "Terms of Use & Authorization Notice",
109
+ "subtitle": "Please read and accept the following before using CiCy Desktop",
110
+ "summaryTitle": "At a glance",
111
+ "summary1": "CiCy Desktop is an AI agent orchestration tool that runs on your own computer, helping you coordinate third-party AI assistants (Claude Code, Codex, Gemini, etc.) to get work done.",
112
+ "summary2": "Third-party AI and accounts are between you and the respective provider: which AI you use, whose account you log in with, and the usage and fees are your decision and responsibility, not CiCy's.",
113
+ "summary3": "To do the work, this software needs a range of authorizations on your computer (running programs, executing scripts, controlling the browser, accessing files, etc.). You are fully responsible for the operations initiated through this software.",
114
+ "summary4": "Your data stays on your machine by default; CiCy does not upload your work content for operational purposes.",
115
+ "summary5": "Two high-risk capabilities require your separate, explicit authorization and are off by default: (a) local HTTPS traffic auditing (installs a certificate, requires administrator); (b) local command/script execution. Accepting these Terms does not enable them.",
116
+ "summary6": "You must not use this software to control unauthorized devices, conduct unlawful multi-accounting / traffic faking, circumvent platform risk controls, or scrape in violation of terms.",
117
+ "viewFull": "View full Terms",
118
+ "agree": "Agree and continue",
119
+ "decline": "Decline and exit",
120
+ "mustAgree": "You cannot use this software without accepting the Terms.",
121
+ "scrollHint": "Please read to the end to continue"
85
122
  }
86
- }
123
+ }
@@ -82,5 +82,40 @@
82
82
  "cli": {
83
83
  "controllerTitle": "cicy - CiCy Desktop 控制器",
84
84
  "usage": "用法: cicy [命令] [选项]"
85
+ },
86
+ "mitmConsent": {
87
+ "cardTitle": "HTTPS 流量本地审计",
88
+ "stateUngrantedTitle": "尚未启用",
89
+ "body": "启用后,本机到 AI 厂商(Claude / OpenAI / DeepSeek / Gemini)的 HTTPS 将被本地审计解密,数据留本地,可随时关闭。",
90
+ "adminNote": "需写入系统根证书信任库,需要管理员授权。",
91
+ "scopeNote": "仅解密上述 AI 厂商域名,其余一切流量(网银、社交、其他网站)不被解密、不被读取。",
92
+ "localNote": "解密后的数据仅保存在你本机,不上传到任何服务器。",
93
+ "enable": "启用",
94
+ "stateGrantedTitle": "已启用",
95
+ "grantedDesc": "HTTPS 审计已开启;可随时撤销并卸载证书。",
96
+ "revoke": "撤销",
97
+ "revokeConfirm": "撤销后将卸载证书、停止解密,并清除同意标记。确定?",
98
+ "stateProcessingTitle": "处理中…",
99
+ "processingEnable": "正在安装证书…",
100
+ "processingRevoke": "正在卸载证书…",
101
+ "errorTitle": "操作失败",
102
+ "errorAdminDenied": "未获得管理员授权,已取消。",
103
+ "retry": "重试"
104
+ },
105
+ "firstRunTerms": {
106
+ "title": "用户协议与授权说明",
107
+ "subtitle": "使用 CiCy Desktop 前,请阅读并同意以下条款",
108
+ "summaryTitle": "一眼看懂",
109
+ "summary1": "CiCy Desktop 是在你自己电脑上运行的 AI Agent 编排工具,帮你调度第三方 AI 助手(Claude Code、Codex、Gemini 等)完成工作。",
110
+ "summary2": "第三方 AI 与账号是你和对应厂商之间的事:用哪个 AI、用谁的账号、用量与费用,由你决定并承担,与 CiCy 无关。",
111
+ "summary3": "为完成工作,本软件需要一系列对你电脑的授权(运行程序、执行脚本、控制浏览器、访问文件等)。你对经本软件发起的操作负全部责任。",
112
+ "summary4": "你的数据默认留在本机,CiCy 不以运营为目的上传你的工作内容。",
113
+ "summary5": "两项高风险能力需要你单独额外授权、默认关闭:(a) HTTPS 流量本地审计(需装证书、需管理员);(b) 本机命令/脚本执行。整体同意本协议不等于开启它们。",
114
+ "summary6": "严禁用本软件控制未授权设备、违法多账号/刷量、绕过平台风控、违规抓取等。",
115
+ "viewFull": "查看完整条款",
116
+ "agree": "同意并继续",
117
+ "decline": "不同意并退出",
118
+ "mustAgree": "未同意则无法使用本软件。",
119
+ "scrollHint": "请阅读至底部以继续"
85
120
  }
86
- }
121
+ }
package/src/main.js CHANGED
@@ -792,6 +792,33 @@ electronApp.whenReady().then(async () => {
792
792
  } catch (e) { log.warn(`[auth] logout clear failed: ${e.message}`); }
793
793
  return { ok: true };
794
794
  });
795
+
796
+ // First-run terms gate (合规第一道整体同意). Persist acceptance in
797
+ // global.json keyed by terms VERSION — a future version bump re-gates.
798
+ // This is DISTINCT from the MITM CA opt-in (the compliance second consent);
799
+ // accepting the terms NEVER enables HTTPS audit.
800
+ __ipcMainAuth.handle("terms:status", (_e, version) => {
801
+ try {
802
+ const c = readGlobalConfig(GLOBAL_JSON);
803
+ const a = c && c.termsAccepted;
804
+ return { accepted: !!(a && a.version === version), version: a?.version || null };
805
+ } catch { return { accepted: false, version: null }; }
806
+ });
807
+ __ipcMainAuth.handle("terms:agree", (_e, version) => {
808
+ try {
809
+ updateGlobalConfig(GLOBAL_JSON, (c) => {
810
+ c.termsAccepted = { version, accepted_at: new Date().toISOString() };
811
+ return c;
812
+ });
813
+ log.info(`[terms] accepted v${version}`);
814
+ return { ok: true };
815
+ } catch (e) { return { ok: false, error: e.message }; }
816
+ });
817
+ __ipcMainAuth.handle("terms:decline", () => {
818
+ log.info("[terms] declined — quitting");
819
+ setTimeout(() => electronApp.quit(), 50);
820
+ return { ok: true };
821
+ });
795
822
  }
796
823
 
797
824
  // Local-team discovery — reads ~/cicy-ai/global.json's cicyDesktopNodes
@@ -37,6 +37,47 @@ function probeExisting(port = DEFAULT_PORT, timeoutMs = 500) {
37
37
 
38
38
  let child = null;
39
39
 
40
+ // Runtime Bundle v1 (主人指令): prefer the versioned runtime store on EVERY
41
+ // platform — first run seeds it from the bundled optionalDependency (zero
42
+ // network, zero npx), upgrades come through runtime.upgrade(). Returns the
43
+ // spawn child or null when the store has no usable binary (legacy fallbacks
44
+ // below take over).
45
+ function startFromRuntime({ logPath, port }) {
46
+ let runtime;
47
+ try { runtime = require("./runtime"); } catch { return null; }
48
+ let exe = null;
49
+ try { exe = runtime.binPath("cicy-code") || runtime.ensureFromBundle("cicy-code"); } catch (e) {
50
+ console.warn(`[cicy-code-sidecar] runtime store unusable: ${e.message}`);
51
+ return null;
52
+ }
53
+ if (!exe) return null;
54
+
55
+ let stdio = ["ignore", "ignore", "ignore"];
56
+ if (logPath) {
57
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
58
+ const fd = fs.openSync(logPath, "a");
59
+ stdio = ["ignore", fd, fd];
60
+ }
61
+ const env = {
62
+ ...process.env,
63
+ CICY_CODE_PORT: String(port),
64
+ PORT: String(port),
65
+ };
66
+ if (process.platform === "win32") {
67
+ try {
68
+ const msys = runtime.binPath("msys2") || runtime.ensureFromBundle("msys2");
69
+ if (msys) env.CICY_MSYS_ROOT = msys; // w-10084 exe 探测约定
70
+ } catch {}
71
+ }
72
+ const c = spawn(exe, [], { stdio, detached: false, windowsHide: true, env });
73
+ console.log(`[cicy-code-sidecar] spawned runtime ${exe} (v${runtime.currentVersion("cicy-code")}) pid=${c.pid} port=${port}`);
74
+ c.on("exit", (code, signal) => {
75
+ console.log(`[cicy-code-sidecar] exited code=${code} signal=${signal}`);
76
+ child = null;
77
+ });
78
+ return c;
79
+ }
80
+
40
81
  async function start({ logPath, port = DEFAULT_PORT, force = false, version = null } = {}) {
41
82
  if (child && !force) return child;
42
83
 
@@ -45,6 +86,10 @@ async function start({ logPath, port = DEFAULT_PORT, force = false, version = nu
45
86
  return null;
46
87
  }
47
88
 
89
+ // Runtime store first — uniform across platforms.
90
+ const rt = startFromRuntime({ logPath, port });
91
+ if (rt) { child = rt; return child; }
92
+
48
93
  if (process.platform === "win32") {
49
94
  // NATIVE route (2026-06 方向): cicy-code.exe + bundled slim MSYS2, no
50
95
  // Docker/WSL. Gated behind CICY_WIN_NATIVE=1 while in 联调; the Docker
@@ -219,17 +264,55 @@ async function restart({ logPath, port = DEFAULT_PORT } = {}) {
219
264
  // win32 → reload the Docker image (from R2) and re-run the container.
220
265
  // else → clear the npx cache + spawn `cicy-code@latest` so npx re-resolves
221
266
  // against the registry (npmmirror for CN) and pulls a newer build.
222
- async function update({ logPath, port = DEFAULT_PORT } = {}) {
223
- await stop({ port });
267
+ async function update({ logPath, port = DEFAULT_PORT, emit } = {}) {
268
+ const e = emit || (() => {});
269
+
270
+ // Runtime store managed → versioned upgrade with health-verify + rollback.
271
+ try {
272
+ const runtime = require("./runtime");
273
+ if (runtime.currentVersion("cicy-code")) {
274
+ return await runtime.upgrade("cicy-code", {
275
+ emit,
276
+ stop: () => stop({ port }),
277
+ start: async () => {
278
+ child = startFromRuntime({ logPath, port });
279
+ if (!child) throw new Error("runtime spawn failed");
280
+ },
281
+ verify: async () => {
282
+ for (let i = 0; i < 120; i++) {
283
+ if (await probeExisting(port)) return true;
284
+ await new Promise((r) => setTimeout(r, 500));
285
+ }
286
+ return false;
287
+ },
288
+ });
289
+ }
290
+ } catch (err) {
291
+ console.warn(`[cicy-code-sidecar] runtime upgrade failed: ${err.message}`);
292
+ e({ phase: "done", status: "error", message: `更新失败:${err.message}` });
293
+ return null;
294
+ }
295
+
224
296
  if (process.platform === "win32") {
225
- try { await require("./docker").loadImage(); } catch (e) {
226
- console.warn(`[cicy-code-sidecar] docker image reload failed: ${e.message}`);
297
+ // NATIVE route: safe-swap update with real download progress.
298
+ if (process.env.CICY_WIN_NATIVE === "1") {
299
+ return require("./native").update({ port, logPath, emit });
300
+ }
301
+ // Transitional Docker route. loadImage streams 下载镜像 % via emit.
302
+ await stop({ port });
303
+ try { await require("./docker").loadImage({ emit }); } catch (err) {
304
+ console.warn(`[cicy-code-sidecar] docker image reload failed: ${err.message}`);
305
+ e({ phase: "download", status: "error", message: `镜像更新失败:${err.message}` });
227
306
  }
228
307
  await new Promise(r => setTimeout(r, 300));
308
+ e({ phase: "swap", status: "running", message: "重启容器…" });
229
309
  return start({ logPath, port, force: true });
230
310
  }
311
+ e({ phase: "download", status: "running", message: "获取最新版 cicy-code…" });
312
+ await stop({ port });
231
313
  clearNpxCache();
232
314
  await new Promise(r => setTimeout(r, 300));
315
+ e({ phase: "swap", status: "running", message: "启动新版本…" });
233
316
  return start({ logPath, port, force: true, version: "latest" });
234
317
  }
235
318
 
@@ -167,12 +167,33 @@ async function restart({ port = 8008, logPath = null } = {}) {
167
167
  return start({ port, logPath });
168
168
  }
169
169
 
170
- // Update: npm route re-resolves @latest; dev route unlinks the cached exe to
171
- // defeat ensureDownloaded's size-match skip. Then restart on the new build.
170
+ // Update SAFE swap order: acquire the new build FIRST (progress streamed),
171
+ // only then stop the running instance and switch. A dead network therefore
172
+ // fails the update loudly but never kills a working install. Dev route
173
+ // downloads to .new and renames; npm route re-resolves @latest in place
174
+ // (npm's own staging is already atomic).
172
175
  async function update({ port = 8008, logPath = null, emit } = {}) {
173
- await stop({ port });
174
- if (devExeUrl()) { try { fs.unlinkSync(DEV_EXE_PATH); } catch {} }
175
- return start({ port, logPath, emit, version: devExeUrl() ? null : "latest" });
176
+ const e = emit || (() => {});
177
+ if (devExeUrl()) {
178
+ const tmp = DEV_EXE_PATH + ".new";
179
+ try { fs.unlinkSync(tmp); } catch {}
180
+ e({ phase: "download", status: "running", message: "下载新版 cicy-code.exe…", progress: 0 });
181
+ await docker.ensureDownloaded(devExeUrl(), tmp, null, { emit, phase: "download", label: "下载新版" });
182
+ if (!fs.existsSync(tmp)) throw new Error("下载未完成,保留当前版本");
183
+ e({ phase: "swap", status: "running", message: "停止旧版本…" });
184
+ await stop({ port });
185
+ try { fs.unlinkSync(DEV_EXE_PATH + ".bak"); } catch {}
186
+ try { fs.renameSync(DEV_EXE_PATH, DEV_EXE_PATH + ".bak"); } catch {}
187
+ fs.renameSync(tmp, DEV_EXE_PATH);
188
+ } else {
189
+ e({ phase: "download", status: "running", message: "npm 获取最新版…" });
190
+ await ensureExe({ emit, version: "latest" }); // service untouched until this succeeds
191
+ e({ phase: "swap", status: "running", message: "重启服务…" });
192
+ await stop({ port });
193
+ }
194
+ const r = await start({ port, logPath, emit });
195
+ e({ phase: "done", status: r ? "done" : "error", message: r ? "已更新并重启" : "更新后启动失败" });
196
+ return r;
176
197
  }
177
198
 
178
199
  async function checkStatus({ port = 8008 } = {}) {