@surenkid/acli-hub 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +6 -0
- package/README.md +6 -1
- package/package.json +1 -1
- package/server/index.js +3 -2
- package/server/public/assets/{PreviewDialog-BkLLhap2.js → PreviewDialog-Dc7jb9yA.js} +1 -1
- package/server/public/assets/index-DlR3k_jH.js +17 -0
- package/server/public/assets/{index-B-BHzxof.css → index-xnjgJSWe.css} +1 -1
- package/server/public/index.html +2 -2
- package/server/routes/auth.js +9 -2
- package/server/routes/auth.test.js +71 -0
- package/server/services/user-config.js +23 -0
- package/server/services/user-config.test.js +87 -0
- package/server/public/assets/index-CGMIO8Dh.js +0 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--bg-primary: #0f172a;--bg-secondary: #1e293b;--bg-tertiary: #334155;--bg-card: #1e293b;--bg-card-hover: #334155;--border-color: #334155;--border-color-hover: #475569;--text-primary: #f8fafc;--text-secondary: #94a3b8;--text-tertiary: #64748b;--accent-primary: #3b82f6;--accent-hover: #2563eb;--accent-dim: rgba(59, 130, 246, .15);--success: #10b981;--warning: #f59e0b;--danger: #ef4444;--sidebar-width: 260px;--sidebar-collapsed-width: 0px;--toolbar-height: 56px;--radius-sm: 4px;--radius-md: 6px;--radius-lg: 8px;--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / .05);--shadow-md: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1)}*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol;background:var(--bg-primary);color:var(--text-primary);overflow:hidden;height:100vh;font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}#app{height:100vh;display:flex;flex-direction:column}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-tertiary);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text-tertiary)}.glass{background:#1e293bb3;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--border-color)}.btn{display:inline-flex;align-items:center;justify-content:center;padding:8px 16px;border-radius:var(--radius-md);font-weight:500;cursor:pointer;transition:all .2s;border:1px solid transparent;outline:none}.btn-primary{background:var(--accent-primary);color:#fff}.btn-primary:hover{background:var(--accent-hover)}input{background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);padding:8px 12px;border-radius:var(--radius-md);outline:none;transition:border-color .2s}input:focus{border-color:var(--accent-primary)}.session-card[data-v-0e38e5ff]{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:var(--radius-md);cursor:pointer;transition:all .2s;border:1px solid transparent;color:var(--text-secondary)}.session-card[data-v-0e38e5ff]:hover{background:var(--bg-card-hover);color:var(--text-primary)}.session-card.active[data-v-0e38e5ff]{background:var(--bg-tertiary);color:var(--text-primary);border-color:var(--border-color)}.status-indicator[data-v-0e38e5ff]{width:8px;height:8px;border-radius:50%;background:var(--text-tertiary);flex-shrink:0}.status-indicator.running[data-v-0e38e5ff]{background:var(--success);box-shadow:0 0 8px #10b98166}.status-indicator.running.output-active[data-v-0e38e5ff]{background:var(--warning);box-shadow:0 0 8px #f59e0b66}.card-content[data-v-0e38e5ff]{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.card-top[data-v-0e38e5ff]{display:flex;align-items:center;gap:4px}.session-name[data-v-0e38e5ff]{font-size:14px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;display:flex;align-items:baseline;gap:6px}.alias-text[data-v-0e38e5ff]{overflow:hidden;text-overflow:ellipsis}.real-name[data-v-0e38e5ff]{flex-shrink:1;max-width:45%;overflow:hidden;text-overflow:ellipsis;font-size:11px;font-weight:400;color:var(--text-tertiary)}.alias-input[data-v-0e38e5ff]{flex:1;min-width:0;padding:2px 6px;font-size:13px;color:var(--text-primary);background:var(--bg-primary);border:1px solid var(--border-color);border-radius:4px;outline:none}.alias-input[data-v-0e38e5ff]:focus{border-color:var(--accent-primary, #3b82f6)}.close-btn[data-v-0e38e5ff]{display:none;align-items:center;justify-content:center;background:none;border:none;color:var(--text-tertiary);cursor:pointer;padding:2px;border-radius:4px;flex-shrink:0}.session-card:hover .close-btn[data-v-0e38e5ff]{display:flex}.close-btn[data-v-0e38e5ff]:hover{background:var(--bg-tertiary);color:var(--danger)}.session-info[data-v-0e38e5ff]{display:flex;align-items:baseline;gap:8px;font-size:11px;color:var(--text-tertiary)}.pane-title[data-v-0e38e5ff]{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.confirm-bar[data-v-0e38e5ff]{display:flex;align-items:center;gap:6px;font-size:12px}.confirm-text[data-v-0e38e5ff]{color:var(--danger);font-weight:500}.confirm-yes[data-v-0e38e5ff],.confirm-no[data-v-0e38e5ff]{background:none;border:1px solid var(--border-color);border-radius:4px;padding:1px 8px;font-size:11px;cursor:pointer;color:var(--text-secondary)}.confirm-yes[data-v-0e38e5ff]:hover{background:var(--danger);border-color:var(--danger);color:#fff}.confirm-no[data-v-0e38e5ff]:hover{background:var(--bg-tertiary);color:var(--text-primary)}.session-card.collapsed[data-v-0e38e5ff]{justify-content:center;padding:12px}.session-card.collapsed .status-indicator[data-v-0e38e5ff]{width:10px;height:10px}li[data-v-5c068d88]{list-style:none}.tree-row[data-v-5c068d88]{display:flex;align-items:center;gap:5px;min-height:31px;padding:2px 6px;cursor:pointer;border-radius:3px;outline-offset:-2px}.tree-row[data-v-5c068d88]:hover,.tree-row[data-v-5c068d88]:focus-visible{background:var(--bg-tertiary)}.chevron[data-v-5c068d88]{flex:0 0 10px;color:var(--text-secondary)}.file-icon[data-v-5c068d88]{color:var(--text-secondary);font-size:18px;width:15px}.filename[data-v-5c068d88]{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;flex:1;font-size:13px}.dotfile[data-v-5c068d88]{opacity:.55}.deleted[data-v-5c068d88]{text-decoration:line-through;opacity:.65}.row-end[data-v-5c068d88]{position:relative;flex:0 0 20px;height:24px}.git-badge[data-v-5c068d88],.git-dot[data-v-5c068d88]{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.git-badge[data-v-5c068d88]{font:600 11px/1.5 ui-monospace,monospace}.git-M[data-v-5c068d88],.git-dot[data-v-5c068d88]{color:#e5b45c}.git-A[data-v-5c068d88],.git-U[data-v-5c068d88]{color:#71c993}.git-D[data-v-5c068d88],.git-C[data-v-5c068d88]{color:#f08080}.git-R[data-v-5c068d88]{color:#79b8ff}.git-dot[data-v-5c068d88]{font-size:7px}.more[data-v-5c068d88]{position:absolute;inset:0;width:100%;background:var(--bg-tertiary);color:var(--text-secondary);border:none;border-radius:3px;cursor:pointer;padding:0;font-size:18px;opacity:0;pointer-events:none}.tree-row:hover .more[data-v-5c068d88],.tree-row:focus-within .more[data-v-5c068d88]{opacity:1;pointer-events:auto}.tree-row:hover .has-menu .git-badge[data-v-5c068d88],.tree-row:hover .has-menu .git-dot[data-v-5c068d88],.tree-row:focus-within .has-menu .git-badge[data-v-5c068d88],.tree-row:focus-within .has-menu .git-dot[data-v-5c068d88]{opacity:0}.tree-children[data-v-5c068d88]{margin-left:14px;padding-left:5px;border-left:1px solid var(--border-color)}.tree-status[data-v-5c068d88]{padding:5px 10px;color:var(--text-secondary);font-size:12px;overflow-wrap:anywhere}.tree-status button[data-v-5c068d88]{background:none;color:var(--text-primary);border:none;cursor:pointer;text-decoration:underline}.error[data-v-5c068d88]{color:var(--danger)}@media(hover:none){.more[data-v-5c068d88]{pointer-events:auto}.row-end:not(.has-status) .more[data-v-5c068d88]{opacity:1;background:transparent}}.file-dialog[data-v-5ceee09a]{margin:auto;width:min(480px,calc(100vw - 32px));max-height:calc(100dvh - 40px);overflow:auto;padding:24px;background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius-lg)}.file-dialog[data-v-5ceee09a]::backdrop{background:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}p[data-v-5ceee09a]{margin:12px 0;color:var(--text-secondary)}.operation-path[data-v-5ceee09a]{overflow-wrap:anywhere;font:12px/1.6 monospace}label[data-v-5ceee09a]{display:flex;flex-direction:column;gap:8px}.dialog-actions[data-v-5ceee09a]{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.btn[data-v-5ceee09a]{background:var(--bg-tertiary);color:var(--text-primary)}.btn-primary[data-v-5ceee09a]{background:var(--accent-primary)}.delete-btn[data-v-5ceee09a]{background:var(--danger);color:#fff}.error[data-v-5ceee09a]{color:var(--danger)}button[data-v-5ceee09a]:disabled{opacity:.5;cursor:default}.clipboard-overlay[data-v-317cb3e2]{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;background:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.clipboard-dialog[data-v-317cb3e2]{width:min(520px,calc(100vw - 32px));padding:20px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-lg);box-shadow:var(--shadow-md)}h3[data-v-317cb3e2]{font-size:16px}p[data-v-317cb3e2]{margin:12px 0;color:var(--text-secondary)}textarea[data-v-317cb3e2]{width:100%;height:180px;resize:vertical;padding:12px;background:var(--bg-primary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius-md);font:13px/1.5 monospace}textarea[data-v-317cb3e2]:focus{outline:2px solid var(--accent-primary);outline-offset:2px}.clipboard-actions[data-v-317cb3e2]{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}button[type=button][data-v-317cb3e2]{color:var(--text-primary);background:var(--bg-tertiary)}button[data-v-317cb3e2]:disabled{opacity:.5;cursor:default}.file-panel[data-v-771bf310]{display:flex;flex-direction:column;min-height:0;flex:1}.file-toolbar[data-v-771bf310]{display:flex;align-items:center;gap:4px;padding:10px 12px 4px}.project-name[data-v-771bf310]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:600}.file-toolbar button[data-v-771bf310],.panel-hint button[data-v-771bf310]{border:none;background:transparent;color:var(--text-secondary);padding:3px 5px;cursor:pointer;font-size:17px}.file-toolbar button[data-v-771bf310]:hover{background:var(--bg-tertiary);border-radius:4px}button[data-v-771bf310]:disabled{opacity:.4;cursor:default}.root-path[data-v-771bf310]{color:var(--text-secondary);font:11px/1.6 monospace;overflow-wrap:anywhere;padding:0 12px 10px;border-bottom:1px solid var(--border-color)}.file-tree[data-v-771bf310]{overflow:auto;padding:6px;flex:1;min-height:0}.panel-hint[data-v-771bf310]{padding:12px;color:var(--text-secondary);font-size:12px;list-style:none;overflow-wrap:anywhere}.error[data-v-771bf310]{color:var(--danger)}.git-warning[data-v-771bf310]{color:#e5b45c}.file-menu[data-v-771bf310]{position:fixed;z-index:110;width:min(230px,calc(100vw - 16px));padding:6px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-md);box-shadow:var(--shadow-md)}.file-menu button[data-v-771bf310]{display:flex;justify-content:space-between;width:100%;text-align:left;padding:8px 10px;background:none;color:var(--text-primary);border:none;border-radius:4px;cursor:pointer}.file-menu button[data-v-771bf310]:hover:not(:disabled),.file-menu button[data-v-771bf310]:focus-visible{background:var(--bg-tertiary)}.file-menu span[data-v-771bf310]{color:var(--text-secondary)}.file-menu .danger[data-v-771bf310]{color:var(--danger)}.sidebar[data-v-b8faf56e]{width:var(--sidebar-width);border-right:1px solid var(--border-color);background:var(--bg-secondary);display:flex;flex-direction:column;flex-shrink:0;transition:all .3s cubic-bezier(.4,0,.2,1);overflow:hidden;height:100%;z-index:20}.sidebar.collapsed[data-v-b8faf56e]{width:72px}.sidebar-tabs[data-v-b8faf56e]{display:flex;flex-shrink:0;border-bottom:1px solid var(--border-color)}.sidebar-tabs button[data-v-b8faf56e]{flex:1;padding:12px 4px;border:none;border-bottom:2px solid transparent;background:none;color:var(--text-secondary);cursor:pointer;font:inherit;font-size:12px}.sidebar-tabs button[aria-selected=true][data-v-b8faf56e]{border-bottom-color:var(--accent-primary);color:var(--text-primary);background:var(--bg-primary)}.sessions-panel[data-v-b8faf56e],.files-panel[data-v-b8faf56e]{display:flex;flex-direction:column;min-height:0;flex:1}.sidebar-header[data-v-b8faf56e]{padding:16px;border-bottom:1px solid var(--border-color);display:flex;flex-direction:column;gap:12px}.sidebar-title[data-v-b8faf56e]{font-size:12px;text-transform:uppercase;letter-spacing:.1em;color:var(--text-tertiary);font-weight:600}.new-session-btn[data-v-b8faf56e]{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;background:var(--accent-primary);color:#fff;border:none;border-radius:var(--radius-md);padding:10px;cursor:pointer;font-weight:500;transition:all .2s}.new-session-btn[data-v-b8faf56e]:hover{background:var(--accent-hover)}.sidebar.collapsed .new-session-btn[data-v-b8faf56e]{padding:10px 0}.session-list[data-v-b8faf56e]{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:4px}.empty-hint[data-v-b8faf56e]{text-align:center;padding:40px 10px;color:var(--text-tertiary)}.empty-hint p[data-v-b8faf56e]{font-size:14px;margin-bottom:4px}.empty-hint .sub-hint[data-v-b8faf56e]{font-size:12px;opacity:.7}@media(max-width:768px){.sidebar[data-v-b8faf56e]{position:fixed;top:0;left:0;bottom:0;width:var(--sidebar-width);transform:translate(0);box-shadow:var(--shadow-md);z-index:50}.sidebar.collapsed[data-v-b8faf56e]{transform:translate(-100%);width:var(--sidebar-width)}}.brand-mark[data-v-f54af403]{display:block;flex-shrink:0;object-fit:contain}.terminal-area[data-v-95819edf]{flex:1;display:flex;flex-direction:column;overflow:hidden;background:#000;position:relative}.terminal-frame[data-v-95819edf]{flex:1;width:100%;min-height:0;border:none;background:#000}.terminal-tools[data-v-95819edf]{display:flex;align-items:center;flex-shrink:0;gap:8px;padding:6px 12px;background:var(--bg-primary);border-bottom:1px solid var(--border-color);flex-wrap:wrap}.upload-progress[data-v-95819edf]{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text-secondary)}.upload-progress progress[data-v-95819edf]{width:64px;height:4px;accent-color:var(--accent-primary)}.clipboard-btn[data-v-95819edf]{padding:4px 10px;background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius-sm);cursor:pointer}.clipboard-btn[data-v-95819edf]:hover:not(:disabled){background:var(--bg-tertiary)}.clipboard-btn[data-v-95819edf]:disabled,.clipboard-menu button[data-v-95819edf]:disabled{opacity:.45;cursor:default}.selection-hint[data-v-95819edf]{color:var(--text-secondary);font-size:12px}.clipboard-menu[data-v-95819edf]{position:fixed;z-index:100;width:min(280px,calc(100vw - 16px));padding:6px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-md);box-shadow:var(--shadow-md)}.clipboard-menu button[data-v-95819edf]{display:flex;justify-content:space-between;width:100%;padding:8px 10px;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--text-primary);text-align:left;cursor:pointer}.clipboard-menu button[data-v-95819edf]:hover:not(:disabled),.clipboard-menu button[data-v-95819edf]:focus-visible{background:var(--bg-tertiary)}.clipboard-menu span[data-v-95819edf],.clipboard-menu p[data-v-95819edf]{color:var(--text-secondary);font-size:12px}.clipboard-menu p[data-v-95819edf]{padding:8px 10px 4px;border-top:1px solid var(--border-color)}@media(max-width:600px){.selection-hint[data-v-95819edf]{display:none}}.welcome__container[data-v-95819edf]{flex:1;display:flex;align-items:center;justify-content:center;background:var(--bg-primary);background-image:radial-gradient(circle at center,var(--bg-secondary) 0%,var(--bg-primary) 100%)}.welcome__content[data-v-95819edf]{text-align:center;max-width:400px;padding:40px}.welcome-brand[data-v-95819edf]{display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:24px}.logo-text[data-v-95819edf]{font-size:32px;font-weight:700;color:var(--text-primary);letter-spacing:-1px}.welcome__text[data-v-95819edf]{color:var(--text-secondary);font-size:16px;margin-bottom:24px}.highlight[data-v-95819edf]{color:var(--text-primary);font-weight:600}.sub-text[data-v-95819edf]{color:var(--text-tertiary);font-size:14px}.modal-overlay[data-v-f9ae44fb]{position:fixed;inset:0;background:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:1000;animation:fadeIn-f9ae44fb .2s ease}.modal-content[data-v-f9ae44fb]{width:100%;max-width:400px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-lg);box-shadow:var(--shadow-md);margin:16px;overflow:hidden;animation:slideUp-f9ae44fb .3s cubic-bezier(.16,1,.3,1)}.modal-header[data-v-f9ae44fb]{padding:16px 20px;border-bottom:1px solid var(--border-color);display:flex;justify-content:space-between;align-items:center}.modal-header h3[data-v-f9ae44fb]{font-size:16px;font-weight:600;color:var(--text-primary)}.close-btn[data-v-f9ae44fb]{background:none;border:none;color:var(--text-tertiary);cursor:pointer;font-size:18px;padding:4px;border-radius:4px}.close-btn[data-v-f9ae44fb]:hover{background:var(--bg-tertiary);color:var(--text-primary)}.modal-body[data-v-f9ae44fb]{padding:20px;display:flex;flex-direction:column;gap:16px}.form-group[data-v-f9ae44fb]{display:flex;flex-direction:column;gap:8px}.label-text[data-v-f9ae44fb]{font-size:13px;font-weight:500;color:var(--text-secondary)}select[data-v-f9ae44fb],input[data-v-f9ae44fb]{width:100%;background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);padding:10px;border-radius:var(--radius-md);outline:none;font-size:14px;appearance:none}select[data-v-f9ae44fb]:focus,input[data-v-f9ae44fb]:focus{border-color:var(--accent-primary);box-shadow:0 0 0 2px var(--accent-dim)}.modal-footer[data-v-f9ae44fb]{padding:16px 20px;background:var(--bg-primary);border-top:1px solid var(--border-color);display:flex;justify-content:flex-end;gap:12px}@keyframes fadeIn-f9ae44fb{0%{opacity:0}to{opacity:1}}@keyframes slideUp-f9ae44fb{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.login-screen[data-v-86b6b672]{height:100vh;display:flex;align-items:center;justify-content:center;background:var(--bg-primary)}.login-card[data-v-86b6b672]{width:min(360px,calc(100vw - 48px));padding:32px;display:flex;flex-direction:column;align-items:center;gap:12px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-lg);box-shadow:var(--shadow-md)}.login-logo[data-v-86b6b672]{display:flex;align-items:center;justify-content:center;width:72px;height:72px}h1[data-v-86b6b672]{font-size:22px;font-weight:600;color:var(--text-primary);letter-spacing:-.5px}.login-hint[data-v-86b6b672]{margin:-4px 0 8px;font-size:13px;color:var(--text-secondary)}input[data-v-86b6b672]{width:100%;padding:10px 12px;font-size:14px;color:var(--text-primary);background:var(--bg-primary);border:1px solid var(--border-color);border-radius:var(--radius-md);outline:none}input[data-v-86b6b672]:focus{border-color:var(--accent-primary, #3b82f6)}.login-error[data-v-86b6b672]{width:100%;margin:0;font-size:13px;color:var(--danger)}.login-btn[data-v-86b6b672]{width:100%;margin-top:4px;padding:10px 12px;font-size:14px;font-weight:500;color:#fff;background:var(--accent-primary, #3b82f6);border:none;border-radius:var(--radius-md);cursor:pointer}.login-btn[data-v-86b6b672]:disabled{opacity:.5;cursor:default}.login-btn[data-v-86b6b672]:not(:disabled):hover{filter:brightness(1.1)}.toast-container[data-v-6bea03ca]{position:fixed;top:60px;right:16px;z-index:200;display:flex;flex-direction:column;gap:8px;max-width:min(520px,calc(100vw - 32px));max-height:calc(100vh - 80px);overflow:auto}.toast[data-v-6bea03ca]{padding:10px 16px;border-radius:8px;font-size:13px;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--border-color);min-width:200px;background:var(--bg-secondary);display:flex;align-items:flex-start;gap:12px}.toast-message[data-v-6bea03ca]{white-space:pre-wrap;overflow-wrap:anywhere;max-height:35vh;overflow:auto;flex:1}.toast-actions[data-v-6bea03ca]{display:flex;gap:8px;flex-shrink:0}.toast-actions button[data-v-6bea03ca]{border:none;background:transparent;color:inherit;cursor:pointer;font:inherit}.toast.info[data-v-6bea03ca]{background:var(--bg-secondary);color:var(--text-primary)}.toast.error[data-v-6bea03ca]{background:var(--bg-secondary);color:var(--danger)}.toast.success[data-v-6bea03ca]{background:#4ade8026;color:#4ade80}.toast-enter-active[data-v-6bea03ca]{animation:slideIn-6bea03ca .3s ease}.toast-leave-active[data-v-6bea03ca]{animation:slideOut-6bea03ca .3s ease}@keyframes slideIn-6bea03ca{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideOut-6bea03ca{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}.app-layout[data-v-1baffbd3]{height:100vh;display:flex;flex-direction:column}.toolbar[data-v-1baffbd3]{height:var(--toolbar-height);display:flex;align-items:center;justify-content:space-between;padding:0 16px;border-bottom:1px solid var(--border-color);flex-shrink:0;z-index:30;background:var(--bg-primary)}.toolbar-left[data-v-1baffbd3]{display:flex;align-items:center;gap:12px}.toggle-btn[data-v-1baffbd3]{background:transparent;border:none;color:var(--text-secondary);cursor:pointer;padding:4px;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center}.toggle-btn[data-v-1baffbd3]:hover{color:var(--text-primary);background:var(--bg-tertiary)}.logo-wrapper[data-v-1baffbd3]{display:flex;align-items:center;justify-content:center;flex-shrink:0}.toolbar-title[data-v-1baffbd3]{font-size:16px;font-weight:600;color:var(--text-primary);letter-spacing:-.5px}.main-area[data-v-1baffbd3]{flex:1;display:flex;overflow:hidden;position:relative}.sidebar-overlay[data-v-1baffbd3]{display:none;position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);z-index:15;opacity:0;transition:opacity .3s ease;pointer-events:none}@media(max-width:768px){.sidebar-overlay[data-v-1baffbd3]{display:block}.sidebar-overlay.show[data-v-1baffbd3]{opacity:1;pointer-events:auto}}
|
|
1
|
+
:root{--bg-primary: #0f172a;--bg-secondary: #1e293b;--bg-tertiary: #334155;--bg-card: #1e293b;--bg-card-hover: #334155;--border-color: #334155;--border-color-hover: #475569;--text-primary: #f8fafc;--text-secondary: #94a3b8;--text-tertiary: #64748b;--accent-primary: #3b82f6;--accent-hover: #2563eb;--accent-dim: rgba(59, 130, 246, .15);--success: #10b981;--warning: #f59e0b;--danger: #ef4444;--sidebar-width: 260px;--sidebar-collapsed-width: 0px;--toolbar-height: 56px;--radius-sm: 4px;--radius-md: 6px;--radius-lg: 8px;--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / .05);--shadow-md: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1)}*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol;background:var(--bg-primary);color:var(--text-primary);overflow:hidden;height:100vh;font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}#app{height:100vh;display:flex;flex-direction:column}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-tertiary);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text-tertiary)}.glass{background:#1e293bb3;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--border-color)}.btn{display:inline-flex;align-items:center;justify-content:center;padding:8px 16px;border-radius:var(--radius-md);font-weight:500;cursor:pointer;transition:all .2s;border:1px solid transparent;outline:none}.btn-primary{background:var(--accent-primary);color:#fff}.btn-primary:hover{background:var(--accent-hover)}input{background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);padding:8px 12px;border-radius:var(--radius-md);outline:none;transition:border-color .2s}input:focus{border-color:var(--accent-primary)}.session-card[data-v-0e38e5ff]{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:var(--radius-md);cursor:pointer;transition:all .2s;border:1px solid transparent;color:var(--text-secondary)}.session-card[data-v-0e38e5ff]:hover{background:var(--bg-card-hover);color:var(--text-primary)}.session-card.active[data-v-0e38e5ff]{background:var(--bg-tertiary);color:var(--text-primary);border-color:var(--border-color)}.status-indicator[data-v-0e38e5ff]{width:8px;height:8px;border-radius:50%;background:var(--text-tertiary);flex-shrink:0}.status-indicator.running[data-v-0e38e5ff]{background:var(--success);box-shadow:0 0 8px #10b98166}.status-indicator.running.output-active[data-v-0e38e5ff]{background:var(--warning);box-shadow:0 0 8px #f59e0b66}.card-content[data-v-0e38e5ff]{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.card-top[data-v-0e38e5ff]{display:flex;align-items:center;gap:4px}.session-name[data-v-0e38e5ff]{font-size:14px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;display:flex;align-items:baseline;gap:6px}.alias-text[data-v-0e38e5ff]{overflow:hidden;text-overflow:ellipsis}.real-name[data-v-0e38e5ff]{flex-shrink:1;max-width:45%;overflow:hidden;text-overflow:ellipsis;font-size:11px;font-weight:400;color:var(--text-tertiary)}.alias-input[data-v-0e38e5ff]{flex:1;min-width:0;padding:2px 6px;font-size:13px;color:var(--text-primary);background:var(--bg-primary);border:1px solid var(--border-color);border-radius:4px;outline:none}.alias-input[data-v-0e38e5ff]:focus{border-color:var(--accent-primary, #3b82f6)}.close-btn[data-v-0e38e5ff]{display:none;align-items:center;justify-content:center;background:none;border:none;color:var(--text-tertiary);cursor:pointer;padding:2px;border-radius:4px;flex-shrink:0}.session-card:hover .close-btn[data-v-0e38e5ff]{display:flex}.close-btn[data-v-0e38e5ff]:hover{background:var(--bg-tertiary);color:var(--danger)}.session-info[data-v-0e38e5ff]{display:flex;align-items:baseline;gap:8px;font-size:11px;color:var(--text-tertiary)}.pane-title[data-v-0e38e5ff]{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.confirm-bar[data-v-0e38e5ff]{display:flex;align-items:center;gap:6px;font-size:12px}.confirm-text[data-v-0e38e5ff]{color:var(--danger);font-weight:500}.confirm-yes[data-v-0e38e5ff],.confirm-no[data-v-0e38e5ff]{background:none;border:1px solid var(--border-color);border-radius:4px;padding:1px 8px;font-size:11px;cursor:pointer;color:var(--text-secondary)}.confirm-yes[data-v-0e38e5ff]:hover{background:var(--danger);border-color:var(--danger);color:#fff}.confirm-no[data-v-0e38e5ff]:hover{background:var(--bg-tertiary);color:var(--text-primary)}.session-card.collapsed[data-v-0e38e5ff]{justify-content:center;padding:12px}.session-card.collapsed .status-indicator[data-v-0e38e5ff]{width:10px;height:10px}li[data-v-5c068d88]{list-style:none}.tree-row[data-v-5c068d88]{display:flex;align-items:center;gap:5px;min-height:31px;padding:2px 6px;cursor:pointer;border-radius:3px;outline-offset:-2px}.tree-row[data-v-5c068d88]:hover,.tree-row[data-v-5c068d88]:focus-visible{background:var(--bg-tertiary)}.chevron[data-v-5c068d88]{flex:0 0 10px;color:var(--text-secondary)}.file-icon[data-v-5c068d88]{color:var(--text-secondary);font-size:18px;width:15px}.filename[data-v-5c068d88]{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;flex:1;font-size:13px}.dotfile[data-v-5c068d88]{opacity:.55}.deleted[data-v-5c068d88]{text-decoration:line-through;opacity:.65}.row-end[data-v-5c068d88]{position:relative;flex:0 0 20px;height:24px}.git-badge[data-v-5c068d88],.git-dot[data-v-5c068d88]{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.git-badge[data-v-5c068d88]{font:600 11px/1.5 ui-monospace,monospace}.git-M[data-v-5c068d88],.git-dot[data-v-5c068d88]{color:#e5b45c}.git-A[data-v-5c068d88],.git-U[data-v-5c068d88]{color:#71c993}.git-D[data-v-5c068d88],.git-C[data-v-5c068d88]{color:#f08080}.git-R[data-v-5c068d88]{color:#79b8ff}.git-dot[data-v-5c068d88]{font-size:7px}.more[data-v-5c068d88]{position:absolute;inset:0;width:100%;background:var(--bg-tertiary);color:var(--text-secondary);border:none;border-radius:3px;cursor:pointer;padding:0;font-size:18px;opacity:0;pointer-events:none}.tree-row:hover .more[data-v-5c068d88],.tree-row:focus-within .more[data-v-5c068d88]{opacity:1;pointer-events:auto}.tree-row:hover .has-menu .git-badge[data-v-5c068d88],.tree-row:hover .has-menu .git-dot[data-v-5c068d88],.tree-row:focus-within .has-menu .git-badge[data-v-5c068d88],.tree-row:focus-within .has-menu .git-dot[data-v-5c068d88]{opacity:0}.tree-children[data-v-5c068d88]{margin-left:14px;padding-left:5px;border-left:1px solid var(--border-color)}.tree-status[data-v-5c068d88]{padding:5px 10px;color:var(--text-secondary);font-size:12px;overflow-wrap:anywhere}.tree-status button[data-v-5c068d88]{background:none;color:var(--text-primary);border:none;cursor:pointer;text-decoration:underline}.error[data-v-5c068d88]{color:var(--danger)}@media(hover:none){.more[data-v-5c068d88]{pointer-events:auto}.row-end:not(.has-status) .more[data-v-5c068d88]{opacity:1;background:transparent}}.file-dialog[data-v-5ceee09a]{margin:auto;width:min(480px,calc(100vw - 32px));max-height:calc(100dvh - 40px);overflow:auto;padding:24px;background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius-lg)}.file-dialog[data-v-5ceee09a]::backdrop{background:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}p[data-v-5ceee09a]{margin:12px 0;color:var(--text-secondary)}.operation-path[data-v-5ceee09a]{overflow-wrap:anywhere;font:12px/1.6 monospace}label[data-v-5ceee09a]{display:flex;flex-direction:column;gap:8px}.dialog-actions[data-v-5ceee09a]{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.btn[data-v-5ceee09a]{background:var(--bg-tertiary);color:var(--text-primary)}.btn-primary[data-v-5ceee09a]{background:var(--accent-primary)}.delete-btn[data-v-5ceee09a]{background:var(--danger);color:#fff}.error[data-v-5ceee09a]{color:var(--danger)}button[data-v-5ceee09a]:disabled{opacity:.5;cursor:default}.clipboard-overlay[data-v-317cb3e2]{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;background:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.clipboard-dialog[data-v-317cb3e2]{width:min(520px,calc(100vw - 32px));padding:20px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-lg);box-shadow:var(--shadow-md)}h3[data-v-317cb3e2]{font-size:16px}p[data-v-317cb3e2]{margin:12px 0;color:var(--text-secondary)}textarea[data-v-317cb3e2]{width:100%;height:180px;resize:vertical;padding:12px;background:var(--bg-primary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius-md);font:13px/1.5 monospace}textarea[data-v-317cb3e2]:focus{outline:2px solid var(--accent-primary);outline-offset:2px}.clipboard-actions[data-v-317cb3e2]{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}button[type=button][data-v-317cb3e2]{color:var(--text-primary);background:var(--bg-tertiary)}button[data-v-317cb3e2]:disabled{opacity:.5;cursor:default}.file-panel[data-v-771bf310]{display:flex;flex-direction:column;min-height:0;flex:1}.file-toolbar[data-v-771bf310]{display:flex;align-items:center;gap:4px;padding:10px 12px 4px}.project-name[data-v-771bf310]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:600}.file-toolbar button[data-v-771bf310],.panel-hint button[data-v-771bf310]{border:none;background:transparent;color:var(--text-secondary);padding:3px 5px;cursor:pointer;font-size:17px}.file-toolbar button[data-v-771bf310]:hover{background:var(--bg-tertiary);border-radius:4px}button[data-v-771bf310]:disabled{opacity:.4;cursor:default}.root-path[data-v-771bf310]{color:var(--text-secondary);font:11px/1.6 monospace;overflow-wrap:anywhere;padding:0 12px 10px;border-bottom:1px solid var(--border-color)}.file-tree[data-v-771bf310]{overflow:auto;padding:6px;flex:1;min-height:0}.panel-hint[data-v-771bf310]{padding:12px;color:var(--text-secondary);font-size:12px;list-style:none;overflow-wrap:anywhere}.error[data-v-771bf310]{color:var(--danger)}.git-warning[data-v-771bf310]{color:#e5b45c}.file-menu[data-v-771bf310]{position:fixed;z-index:110;width:min(230px,calc(100vw - 16px));padding:6px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-md);box-shadow:var(--shadow-md)}.file-menu button[data-v-771bf310]{display:flex;justify-content:space-between;width:100%;text-align:left;padding:8px 10px;background:none;color:var(--text-primary);border:none;border-radius:4px;cursor:pointer}.file-menu button[data-v-771bf310]:hover:not(:disabled),.file-menu button[data-v-771bf310]:focus-visible{background:var(--bg-tertiary)}.file-menu span[data-v-771bf310]{color:var(--text-secondary)}.file-menu .danger[data-v-771bf310]{color:var(--danger)}.sidebar[data-v-b8faf56e]{width:var(--sidebar-width);border-right:1px solid var(--border-color);background:var(--bg-secondary);display:flex;flex-direction:column;flex-shrink:0;transition:all .3s cubic-bezier(.4,0,.2,1);overflow:hidden;height:100%;z-index:20}.sidebar.collapsed[data-v-b8faf56e]{width:72px}.sidebar-tabs[data-v-b8faf56e]{display:flex;flex-shrink:0;border-bottom:1px solid var(--border-color)}.sidebar-tabs button[data-v-b8faf56e]{flex:1;padding:12px 4px;border:none;border-bottom:2px solid transparent;background:none;color:var(--text-secondary);cursor:pointer;font:inherit;font-size:12px}.sidebar-tabs button[aria-selected=true][data-v-b8faf56e]{border-bottom-color:var(--accent-primary);color:var(--text-primary);background:var(--bg-primary)}.sessions-panel[data-v-b8faf56e],.files-panel[data-v-b8faf56e]{display:flex;flex-direction:column;min-height:0;flex:1}.sidebar-header[data-v-b8faf56e]{padding:16px;border-bottom:1px solid var(--border-color);display:flex;flex-direction:column;gap:12px}.sidebar-title[data-v-b8faf56e]{font-size:12px;text-transform:uppercase;letter-spacing:.1em;color:var(--text-tertiary);font-weight:600}.new-session-btn[data-v-b8faf56e]{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;background:var(--accent-primary);color:#fff;border:none;border-radius:var(--radius-md);padding:10px;cursor:pointer;font-weight:500;transition:all .2s}.new-session-btn[data-v-b8faf56e]:hover{background:var(--accent-hover)}.sidebar.collapsed .new-session-btn[data-v-b8faf56e]{padding:10px 0}.session-list[data-v-b8faf56e]{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:4px}.empty-hint[data-v-b8faf56e]{text-align:center;padding:40px 10px;color:var(--text-tertiary)}.empty-hint p[data-v-b8faf56e]{font-size:14px;margin-bottom:4px}.empty-hint .sub-hint[data-v-b8faf56e]{font-size:12px;opacity:.7}@media(max-width:768px){.sidebar[data-v-b8faf56e]{position:fixed;top:0;left:0;bottom:0;width:var(--sidebar-width);transform:translate(0);box-shadow:var(--shadow-md);z-index:50}.sidebar.collapsed[data-v-b8faf56e]{transform:translate(-100%);width:var(--sidebar-width)}}.brand-mark[data-v-f54af403]{display:block;flex-shrink:0;object-fit:contain}.terminal-area[data-v-95819edf]{flex:1;display:flex;flex-direction:column;overflow:hidden;background:#000;position:relative}.terminal-frame[data-v-95819edf]{flex:1;width:100%;min-height:0;border:none;background:#000}.terminal-tools[data-v-95819edf]{display:flex;align-items:center;flex-shrink:0;gap:8px;padding:6px 12px;background:var(--bg-primary);border-bottom:1px solid var(--border-color);flex-wrap:wrap}.upload-progress[data-v-95819edf]{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text-secondary)}.upload-progress progress[data-v-95819edf]{width:64px;height:4px;accent-color:var(--accent-primary)}.clipboard-btn[data-v-95819edf]{padding:4px 10px;background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius-sm);cursor:pointer}.clipboard-btn[data-v-95819edf]:hover:not(:disabled){background:var(--bg-tertiary)}.clipboard-btn[data-v-95819edf]:disabled,.clipboard-menu button[data-v-95819edf]:disabled{opacity:.45;cursor:default}.selection-hint[data-v-95819edf]{color:var(--text-secondary);font-size:12px}.clipboard-menu[data-v-95819edf]{position:fixed;z-index:100;width:min(280px,calc(100vw - 16px));padding:6px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-md);box-shadow:var(--shadow-md)}.clipboard-menu button[data-v-95819edf]{display:flex;justify-content:space-between;width:100%;padding:8px 10px;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--text-primary);text-align:left;cursor:pointer}.clipboard-menu button[data-v-95819edf]:hover:not(:disabled),.clipboard-menu button[data-v-95819edf]:focus-visible{background:var(--bg-tertiary)}.clipboard-menu span[data-v-95819edf],.clipboard-menu p[data-v-95819edf]{color:var(--text-secondary);font-size:12px}.clipboard-menu p[data-v-95819edf]{padding:8px 10px 4px;border-top:1px solid var(--border-color)}@media(max-width:600px){.selection-hint[data-v-95819edf]{display:none}}.welcome__container[data-v-95819edf]{flex:1;display:flex;align-items:center;justify-content:center;background:var(--bg-primary);background-image:radial-gradient(circle at center,var(--bg-secondary) 0%,var(--bg-primary) 100%)}.welcome__content[data-v-95819edf]{text-align:center;max-width:400px;padding:40px}.welcome-brand[data-v-95819edf]{display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:24px}.logo-text[data-v-95819edf]{font-size:32px;font-weight:700;color:var(--text-primary);letter-spacing:-1px}.welcome__text[data-v-95819edf]{color:var(--text-secondary);font-size:16px;margin-bottom:24px}.highlight[data-v-95819edf]{color:var(--text-primary);font-weight:600}.sub-text[data-v-95819edf]{color:var(--text-tertiary);font-size:14px}.modal-overlay[data-v-ef0ce732]{position:fixed;inset:0;background:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:1000;animation:fadeIn-ef0ce732 .2s ease}.modal-content[data-v-ef0ce732]{width:100%;max-width:400px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-lg);box-shadow:var(--shadow-md);margin:16px;overflow:hidden;animation:slideUp-ef0ce732 .3s cubic-bezier(.16,1,.3,1)}.modal-header[data-v-ef0ce732]{padding:16px 20px;border-bottom:1px solid var(--border-color);display:flex;justify-content:space-between;align-items:center}.modal-header h3[data-v-ef0ce732]{font-size:16px;font-weight:600;color:var(--text-primary)}.close-btn[data-v-ef0ce732]{background:none;border:none;color:var(--text-tertiary);cursor:pointer;font-size:18px;padding:4px;border-radius:4px}.close-btn[data-v-ef0ce732]:hover{background:var(--bg-tertiary);color:var(--text-primary)}.modal-body[data-v-ef0ce732]{padding:20px;display:flex;flex-direction:column;gap:16px}.form-group[data-v-ef0ce732]{display:flex;flex-direction:column;gap:8px}.label-text[data-v-ef0ce732]{font-size:13px;font-weight:500;color:var(--text-secondary)}select[data-v-ef0ce732]{width:100%;background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);padding:10px;border-radius:var(--radius-md);outline:none;font-size:14px;appearance:none}select[data-v-ef0ce732]:focus{border-color:var(--accent-primary);box-shadow:0 0 0 2px var(--accent-dim)}.modal-footer[data-v-ef0ce732]{padding:16px 20px;background:var(--bg-primary);border-top:1px solid var(--border-color);display:flex;justify-content:flex-end;gap:12px}@keyframes fadeIn-ef0ce732{0%{opacity:0}to{opacity:1}}@keyframes slideUp-ef0ce732{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.login-screen[data-v-86b6b672]{height:100vh;display:flex;align-items:center;justify-content:center;background:var(--bg-primary)}.login-card[data-v-86b6b672]{width:min(360px,calc(100vw - 48px));padding:32px;display:flex;flex-direction:column;align-items:center;gap:12px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:var(--radius-lg);box-shadow:var(--shadow-md)}.login-logo[data-v-86b6b672]{display:flex;align-items:center;justify-content:center;width:72px;height:72px}h1[data-v-86b6b672]{font-size:22px;font-weight:600;color:var(--text-primary);letter-spacing:-.5px}.login-hint[data-v-86b6b672]{margin:-4px 0 8px;font-size:13px;color:var(--text-secondary)}input[data-v-86b6b672]{width:100%;padding:10px 12px;font-size:14px;color:var(--text-primary);background:var(--bg-primary);border:1px solid var(--border-color);border-radius:var(--radius-md);outline:none}input[data-v-86b6b672]:focus{border-color:var(--accent-primary, #3b82f6)}.login-error[data-v-86b6b672]{width:100%;margin:0;font-size:13px;color:var(--danger)}.login-btn[data-v-86b6b672]{width:100%;margin-top:4px;padding:10px 12px;font-size:14px;font-weight:500;color:#fff;background:var(--accent-primary, #3b82f6);border:none;border-radius:var(--radius-md);cursor:pointer}.login-btn[data-v-86b6b672]:disabled{opacity:.5;cursor:default}.login-btn[data-v-86b6b672]:not(:disabled):hover{filter:brightness(1.1)}.toast-container[data-v-6bea03ca]{position:fixed;top:60px;right:16px;z-index:200;display:flex;flex-direction:column;gap:8px;max-width:min(520px,calc(100vw - 32px));max-height:calc(100vh - 80px);overflow:auto}.toast[data-v-6bea03ca]{padding:10px 16px;border-radius:8px;font-size:13px;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--border-color);min-width:200px;background:var(--bg-secondary);display:flex;align-items:flex-start;gap:12px}.toast-message[data-v-6bea03ca]{white-space:pre-wrap;overflow-wrap:anywhere;max-height:35vh;overflow:auto;flex:1}.toast-actions[data-v-6bea03ca]{display:flex;gap:8px;flex-shrink:0}.toast-actions button[data-v-6bea03ca]{border:none;background:transparent;color:inherit;cursor:pointer;font:inherit}.toast.info[data-v-6bea03ca]{background:var(--bg-secondary);color:var(--text-primary)}.toast.error[data-v-6bea03ca]{background:var(--bg-secondary);color:var(--danger)}.toast.success[data-v-6bea03ca]{background:#4ade8026;color:#4ade80}.toast-enter-active[data-v-6bea03ca]{animation:slideIn-6bea03ca .3s ease}.toast-leave-active[data-v-6bea03ca]{animation:slideOut-6bea03ca .3s ease}@keyframes slideIn-6bea03ca{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideOut-6bea03ca{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}.app-layout[data-v-cfab805f]{height:100vh;display:flex;flex-direction:column}.toolbar[data-v-cfab805f]{height:var(--toolbar-height);display:flex;align-items:center;justify-content:space-between;padding:0 16px;border-bottom:1px solid var(--border-color);flex-shrink:0;z-index:30;background:var(--bg-primary)}.toolbar-left[data-v-cfab805f]{display:flex;align-items:center;gap:12px}.toggle-btn[data-v-cfab805f]{background:transparent;border:none;color:var(--text-secondary);cursor:pointer;padding:4px;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center}.toggle-btn[data-v-cfab805f]:hover{color:var(--text-primary);background:var(--bg-tertiary)}.logo-wrapper[data-v-cfab805f]{display:flex;align-items:center;justify-content:center;flex-shrink:0}.toolbar-title[data-v-cfab805f]{font-size:16px;font-weight:600;color:var(--text-primary);letter-spacing:-.5px}.main-area[data-v-cfab805f]{flex:1;display:flex;overflow:hidden;position:relative}.sidebar-overlay[data-v-cfab805f]{display:none;position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);z-index:15;opacity:0;transition:opacity .3s ease;pointer-events:none}@media(max-width:768px){.sidebar-overlay[data-v-cfab805f]{display:block}.sidebar-overlay.show[data-v-cfab805f]{opacity:1;pointer-events:auto}}
|
package/server/public/index.html
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180" />
|
|
11
11
|
<link rel="manifest" href="/site.webmanifest" />
|
|
12
12
|
<title>ACLI Hub</title>
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-DlR3k_jH.js"></script>
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-xnjgJSWe.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="app"></div>
|
package/server/routes/auth.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
|
+
const os = require('os');
|
|
2
3
|
const { sessionCookie, clearCookie, sessionTokenFromRequest } = require('../services/auth');
|
|
3
4
|
|
|
4
|
-
module.exports = function (auth) {
|
|
5
|
+
module.exports = function (auth, { name = process.env.ACLI_HUB_NAME, hostname = os.hostname() } = {}) {
|
|
5
6
|
const router = express.Router();
|
|
7
|
+
const normalizeName = value => (typeof value === 'string' ? value : '')
|
|
8
|
+
.replace(/[\s\u0000-\u001f\u007f]+/g, ' ').trim().slice(0, 100);
|
|
9
|
+
const instanceName = normalizeName(name) || normalizeName(hostname) || 'localhost';
|
|
6
10
|
|
|
7
11
|
router.get('/status', (req, res) => {
|
|
8
12
|
const authRequired = auth.isEnabled();
|
|
9
13
|
const authenticated = !authRequired || auth.validateSession(sessionTokenFromRequest(req));
|
|
10
|
-
|
|
14
|
+
// The login page needs the same tab identity as an authenticated page.
|
|
15
|
+
// Return only the chosen display name, not the hostname behind an alias.
|
|
16
|
+
res.set('Cache-Control', 'no-store');
|
|
17
|
+
res.json({ authRequired, authenticated, instance: { name: instanceName } });
|
|
11
18
|
});
|
|
12
19
|
|
|
13
20
|
router.post('/login', (req, res) => {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const http = require('node:http');
|
|
4
|
+
const express = require('express');
|
|
5
|
+
const { AuthService } = require('../services/auth');
|
|
6
|
+
const authRoute = require('./auth');
|
|
7
|
+
|
|
8
|
+
async function serve(t, auth, identity) {
|
|
9
|
+
const app = express();
|
|
10
|
+
app.use(express.json());
|
|
11
|
+
app.use('/api/auth', authRoute(auth, identity));
|
|
12
|
+
const server = http.createServer(app);
|
|
13
|
+
await new Promise((resolve, reject) => {
|
|
14
|
+
server.once('error', reject);
|
|
15
|
+
server.listen(0, '127.0.0.1', resolve);
|
|
16
|
+
});
|
|
17
|
+
t.after(async () => {
|
|
18
|
+
server.closeAllConnections();
|
|
19
|
+
await new Promise(resolve => server.close(resolve));
|
|
20
|
+
});
|
|
21
|
+
const base = `http://127.0.0.1:${server.address().port}/api/auth`;
|
|
22
|
+
return (path, options) => fetch(`${base}${path}`, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test('hostname identity is available without authentication and status is never cached', async t => {
|
|
26
|
+
const request = await serve(t, new AuthService(), { name: '', hostname: 'dev-server' });
|
|
27
|
+
const response = await request('/status');
|
|
28
|
+
assert.equal(response.status, 200);
|
|
29
|
+
assert.equal(response.headers.get('cache-control'), 'no-store');
|
|
30
|
+
assert.deepEqual(await response.json(), {
|
|
31
|
+
authRequired: false, authenticated: true, instance: { name: 'dev-server' },
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('custom names are public before login, hide the hostname and survive login/logout unchanged', async t => {
|
|
36
|
+
const identity = { name: ' 东京\t开发机\n🚀 ', hostname: 'private.internal.example' };
|
|
37
|
+
const request = await serve(t, new AuthService('fixture-password'), identity);
|
|
38
|
+
identity.name = 'Not the startup name';
|
|
39
|
+
let response = await request('/status');
|
|
40
|
+
let data = await response.json();
|
|
41
|
+
assert.deepEqual(data, {
|
|
42
|
+
authRequired: true, authenticated: false, instance: { name: '东京 开发机 🚀' },
|
|
43
|
+
});
|
|
44
|
+
assert.ok(!JSON.stringify(data).includes(identity.hostname));
|
|
45
|
+
|
|
46
|
+
response = await request('/login', {
|
|
47
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
body: JSON.stringify({ password: 'fixture-password' }),
|
|
49
|
+
});
|
|
50
|
+
assert.equal(response.status, 200);
|
|
51
|
+
const cookie = response.headers.get('set-cookie').split(';')[0];
|
|
52
|
+
data = await (await request('/status', { headers: { Cookie: cookie } })).json();
|
|
53
|
+
assert.equal(data.authenticated, true);
|
|
54
|
+
assert.deepEqual(data.instance, { name: '东京 开发机 🚀' });
|
|
55
|
+
|
|
56
|
+
await request('/logout', { method: 'POST', headers: { Cookie: cookie } });
|
|
57
|
+
data = await (await request('/status', { headers: { Cookie: cookie } })).json();
|
|
58
|
+
assert.equal(data.authenticated, false);
|
|
59
|
+
assert.deepEqual(data.instance, { name: '东京 开发机 🚀' });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('empty/invalid names fall back safely, control characters are removed and names are bounded', async t => {
|
|
63
|
+
for (const name of ['', ' \t\n\0 ', null, 123]) {
|
|
64
|
+
const request = await serve(t, new AuthService(), { name, hostname: 'host-fallback' });
|
|
65
|
+
assert.deepEqual((await (await request('/status')).json()).instance, { name: 'host-fallback' });
|
|
66
|
+
}
|
|
67
|
+
const long = await serve(t, new AuthService(), { name: 'a'.repeat(200), hostname: 'host' });
|
|
68
|
+
assert.equal((await (await long('/status')).json()).instance.name, 'a'.repeat(100));
|
|
69
|
+
const empty = await serve(t, new AuthService(), { name: '', hostname: '' });
|
|
70
|
+
assert.equal((await (await empty('/status')).json()).instance.name, 'localhost');
|
|
71
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function ensureUserConfig({ env = process.env, home = os.homedir(), warn = console.warn } = {}) {
|
|
6
|
+
const directory = path.join(env.XDG_CONFIG_HOME || path.join(home, '.config'), 'acli-hub');
|
|
7
|
+
const example = path.join(directory, '.env.example');
|
|
8
|
+
try {
|
|
9
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
10
|
+
try {
|
|
11
|
+
fs.copyFileSync(path.join(__dirname, '..', '..', '.env.example'), example, fs.constants.COPYFILE_EXCL);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
// Preserve existing templates, including symlinks, without following them.
|
|
14
|
+
if (err.code !== 'EEXIST') throw err;
|
|
15
|
+
}
|
|
16
|
+
} catch (err) {
|
|
17
|
+
// A missing template must not prevent environment-only/read-only deployments.
|
|
18
|
+
warn(`WARNING: Could not create config template at ${example}: ${err.message}`);
|
|
19
|
+
}
|
|
20
|
+
return directory;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { ensureUserConfig };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { ensureUserConfig } = require('./user-config');
|
|
7
|
+
|
|
8
|
+
function fixture(t) {
|
|
9
|
+
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'hub-user-config-'));
|
|
10
|
+
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
11
|
+
const warnings = [];
|
|
12
|
+
return { directory, options: { env: {}, home: directory, warn: message => warnings.push(message) }, warnings };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const template = () => fs.readFileSync(path.join(__dirname, '..', '..', '.env.example'), 'utf8');
|
|
16
|
+
|
|
17
|
+
test('first startup creates the user config directory and packaged example, not an active .env', t => {
|
|
18
|
+
const { directory, options, warnings } = fixture(t);
|
|
19
|
+
const config = ensureUserConfig(options);
|
|
20
|
+
assert.equal(config, path.join(directory, '.config', 'acli-hub'));
|
|
21
|
+
assert.equal(fs.readFileSync(path.join(config, '.env.example'), 'utf8'), template());
|
|
22
|
+
assert.equal(fs.existsSync(path.join(config, '.env')), false);
|
|
23
|
+
if (process.platform !== 'win32') assert.equal(fs.statSync(config).mode & 0o777, 0o700);
|
|
24
|
+
assert.deepEqual(warnings, []);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('XDG_CONFIG_HOME redirects initialization without creating the default config directory', t => {
|
|
28
|
+
const { directory, options, warnings } = fixture(t);
|
|
29
|
+
options.env.XDG_CONFIG_HOME = path.join(directory, 'custom config', '配置');
|
|
30
|
+
const config = ensureUserConfig(options);
|
|
31
|
+
assert.equal(config, path.join(options.env.XDG_CONFIG_HOME, 'acli-hub'));
|
|
32
|
+
assert.equal(fs.readFileSync(path.join(config, '.env.example'), 'utf8'), template());
|
|
33
|
+
assert.equal(fs.existsSync(path.join(directory, '.config')), false);
|
|
34
|
+
assert.deepEqual(warnings, []);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('repeated startups preserve an existing template and active configuration', t => {
|
|
38
|
+
const { options, warnings } = fixture(t);
|
|
39
|
+
const config = ensureUserConfig(options);
|
|
40
|
+
const example = path.join(config, '.env.example');
|
|
41
|
+
const active = path.join(config, '.env');
|
|
42
|
+
fs.writeFileSync(example, '# user-maintained example\n');
|
|
43
|
+
fs.writeFileSync(active, 'ACLI_HUB_PORT=4321\n');
|
|
44
|
+
const before = fs.statSync(example);
|
|
45
|
+
ensureUserConfig(options);
|
|
46
|
+
ensureUserConfig(options);
|
|
47
|
+
assert.equal(fs.readFileSync(example, 'utf8'), '# user-maintained example\n');
|
|
48
|
+
assert.equal(fs.readFileSync(active, 'utf8'), 'ACLI_HUB_PORT=4321\n');
|
|
49
|
+
assert.equal(fs.statSync(example).mtimeMs, before.mtimeMs);
|
|
50
|
+
assert.deepEqual(warnings, []);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('an existing directory receives a missing example without modifying its .env', t => {
|
|
54
|
+
const { directory, options, warnings } = fixture(t);
|
|
55
|
+
const config = path.join(directory, '.config', 'acli-hub');
|
|
56
|
+
fs.mkdirSync(config, { recursive: true });
|
|
57
|
+
fs.writeFileSync(path.join(config, '.env'), 'ACLI_HUB_NAME=东京开发机\n');
|
|
58
|
+
ensureUserConfig(options);
|
|
59
|
+
assert.equal(fs.readFileSync(path.join(config, '.env.example'), 'utf8'), template());
|
|
60
|
+
assert.equal(fs.readFileSync(path.join(config, '.env'), 'utf8'), 'ACLI_HUB_NAME=东京开发机\n');
|
|
61
|
+
assert.deepEqual(warnings, []);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('an existing example symlink is preserved and its target is never overwritten', t => {
|
|
65
|
+
const { directory, options, warnings } = fixture(t);
|
|
66
|
+
const config = path.join(directory, '.config', 'acli-hub');
|
|
67
|
+
fs.mkdirSync(config, { recursive: true });
|
|
68
|
+
const target = path.join(directory, 'external-example');
|
|
69
|
+
fs.writeFileSync(target, 'keep this file');
|
|
70
|
+
const example = path.join(config, '.env.example');
|
|
71
|
+
fs.symlinkSync(target, example);
|
|
72
|
+
ensureUserConfig(options);
|
|
73
|
+
assert.equal(fs.readlinkSync(example), target);
|
|
74
|
+
assert.equal(fs.readFileSync(target, 'utf8'), 'keep this file');
|
|
75
|
+
assert.deepEqual(warnings, []);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('unwritable config locations warn instead of blocking startup', t => {
|
|
79
|
+
const { directory, options, warnings } = fixture(t);
|
|
80
|
+
const blocked = path.join(directory, 'not-a-directory');
|
|
81
|
+
fs.writeFileSync(blocked, 'keep this file');
|
|
82
|
+
options.env.XDG_CONFIG_HOME = blocked;
|
|
83
|
+
assert.equal(ensureUserConfig(options), path.join(blocked, 'acli-hub'));
|
|
84
|
+
assert.equal(fs.readFileSync(blocked, 'utf8'), 'keep this file');
|
|
85
|
+
assert.equal(warnings.length, 1);
|
|
86
|
+
assert.match(warnings[0], /WARNING: Could not create config template/);
|
|
87
|
+
});
|