ptn 0.2.5__py3-none-any.whl → 0.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. porterminal/__init__.py +63 -11
  2. porterminal/_version.py +2 -2
  3. porterminal/app.py +25 -1
  4. porterminal/application/ports/__init__.py +2 -0
  5. porterminal/application/ports/connection_registry_port.py +46 -0
  6. porterminal/application/services/management_service.py +30 -55
  7. porterminal/application/services/session_service.py +3 -11
  8. porterminal/application/services/terminal_service.py +97 -56
  9. porterminal/cli/args.py +91 -30
  10. porterminal/cli/display.py +18 -16
  11. porterminal/cli/script_discovery.py +112 -0
  12. porterminal/composition.py +8 -7
  13. porterminal/config.py +12 -2
  14. porterminal/container.py +4 -0
  15. porterminal/domain/__init__.py +0 -9
  16. porterminal/domain/entities/output_buffer.py +56 -1
  17. porterminal/domain/entities/tab.py +11 -10
  18. porterminal/domain/services/__init__.py +0 -2
  19. porterminal/domain/values/__init__.py +0 -4
  20. porterminal/domain/values/environment_rules.py +3 -0
  21. porterminal/infrastructure/auth.py +131 -0
  22. porterminal/infrastructure/cloudflared.py +18 -12
  23. porterminal/infrastructure/config/shell_detector.py +407 -1
  24. porterminal/infrastructure/repositories/in_memory_session.py +1 -4
  25. porterminal/infrastructure/repositories/in_memory_tab.py +2 -10
  26. porterminal/infrastructure/server.py +28 -3
  27. porterminal/pty/env.py +16 -78
  28. porterminal/pty/manager.py +6 -4
  29. porterminal/static/assets/app-DlWNJWFE.js +87 -0
  30. porterminal/static/assets/app-xPAM7YhQ.css +1 -0
  31. porterminal/static/index.html +14 -2
  32. porterminal/updater.py +13 -5
  33. {ptn-0.2.5.dist-info → ptn-0.4.2.dist-info}/METADATA +84 -23
  34. {ptn-0.2.5.dist-info → ptn-0.4.2.dist-info}/RECORD +37 -34
  35. porterminal/static/assets/app-By4EXMHC.js +0 -72
  36. porterminal/static/assets/app-DQePboVd.css +0 -32
  37. {ptn-0.2.5.dist-info → ptn-0.4.2.dist-info}/WHEEL +0 -0
  38. {ptn-0.2.5.dist-info → ptn-0.4.2.dist-info}/entry_points.txt +0 -0
  39. {ptn-0.2.5.dist-info → ptn-0.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1 @@
1
+ .xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;inset:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;inset:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;-webkit-user-select:text;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}:root{--row-height: 30px;--content-padding: 12px;--scrollbar-width: 8px;--transition-fast: .15s ease;--radius-sm: 4px;--radius-md: 6px;--radius-lg: 8px;--radius-xl: 12px;--bg-primary: #1e1e1e;--bg-elevated: #252525;--bg-surface: #2d2d2d;--bg-hover: #3a3a3a;--bg-gradient-top: #232323;--bg-gradient-bottom: #1a1a1a;--text-primary: #cccccc;--text-high: rgba(255, 255, 255, .9);--text-white: #fff;--text-secondary: rgba(255, 255, 255, .7);--text-muted: rgba(255, 255, 255, .5);--text-disabled: rgba(255, 255, 255, .4);--border-subtle: rgba(255, 255, 255, .05);--border-light: rgba(255, 255, 255, .1);--border-medium: rgba(255, 255, 255, .15);--border-active: rgba(255, 255, 255, .2);--color-success: rgba(100, 220, 100, .8);--color-success-text: rgba(130, 230, 150, 1);--color-success-bg: rgba(100, 200, 120, .15);--color-success-border: rgba(100, 200, 120, .3);--bg-success-gradient-top: #2a3a2a;--bg-success-gradient-bottom: #1f2f1f;--color-danger: rgba(255, 100, 100, .8);--color-danger-muted: rgba(255, 120, 120, .7);--color-danger-text: rgba(255, 150, 150, 1);--color-danger-bg: rgba(255, 80, 80, .15);--color-danger-border: rgba(255, 120, 120, .3);--bg-danger-gradient-top: #3a2a2a;--bg-danger-gradient-bottom: #2f1f1f;--color-accent: rgba(80, 160, 255, .8);--color-accent-text: rgba(150, 200, 255, 1);--color-accent-bg: rgba(80, 160, 255, .3);--color-accent-strong: rgba(80, 160, 255, .5);--color-locked: rgba(255, 160, 60, .8);--color-locked-bg: rgba(255, 160, 60, .5);--glow-locked: 0 0 8px rgba(255, 160, 60, .5);--color-tmux: rgba(0, 180, 0, .8);--color-tmux-text: rgba(100, 220, 100, 1);--color-tmux-bg: rgba(0, 180, 0, .15);--hover-overlay: rgba(255, 255, 255, .05);--active-overlay: rgba(255, 255, 255, .08);--scrollbar-thumb: rgba(255, 255, 255, .15);--scrollbar-hover: rgba(255, 255, 255, .25);--cursor-color: #aeafad;--selection-bg: rgba(38, 79, 120, .5);--shadow-elevated: 0 4px 16px rgba(0, 0, 0, .5);--glow-success: 0 0 6px rgba(100, 220, 100, .4);--glow-danger: 0 0 6px rgba(255, 100, 100, .4);--glow-accent: 0 0 8px rgba(80, 160, 255, .4);--overlay-bg: rgba(0, 0, 0, .9)}.tool-btn,.tab-btn{-webkit-touch-callout:none}html,body{margin:0;padding:0;height:100%;overflow:hidden;background:var(--bg-primary);color:var(--text-primary);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;position:fixed;width:100%;touch-action:none;overscroll-behavior:none}#app{display:flex;flex-direction:column;height:100%;height:100dvh;height:100svh;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px);overflow:hidden;overscroll-behavior:none}#tab-bar{display:flex;align-items:center;height:var(--row-height);min-height:var(--row-height);background:linear-gradient(to bottom,var(--bg-gradient-top),var(--bg-gradient-bottom));border-bottom:1px solid var(--border-subtle);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}#tab-bar::-webkit-scrollbar{display:none}.tab-btn{display:flex;align-items:center;gap:2px;height:var(--row-height);padding:0 8px;background:transparent;border:none;border-right:1px solid var(--border-subtle);color:var(--text-muted);font-size:12px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:all var(--transition-fast)}.tab-btn:active{background:var(--hover-overlay)}.tab-btn.active{background:var(--active-overlay);color:var(--text-high)}.tab-btn.tab-add{color:var(--text-disabled);font-size:18px;padding:0 10px;border-right:none}.tab-btn.tab-add:active{color:var(--text-high)}.tab-label{max-width:100px;overflow:hidden;text-overflow:ellipsis}.tab-close{display:flex;align-items:center;justify-content:center;width:16px;height:16px;margin-left:2px;border-radius:var(--radius-sm);font-size:14px;color:var(--text-disabled);transition:all var(--transition-fast);position:relative;overflow:hidden}.tab-close:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;background:var(--color-danger-bg);transform:scaleX(0);transform-origin:left;transition:transform .4s ease-out;z-index:-1}.tab-close.holding:before{transform:scaleX(1)}.tab-close.holding{color:var(--color-danger-text)}.tab-close.ready{background:var(--color-danger-bg);color:var(--color-danger-text);animation:pulseReady .3s ease}@keyframes pulseReady{0%,to{transform:scale(1)}50%{transform:scale(1.1)}}#shell-selector{margin-left:auto;display:flex;align-items:center;padding:0 var(--content-padding);height:var(--row-height);border-left:1px solid var(--border-subtle)}#shell-select{background:transparent;border:none;color:var(--text-secondary);font-size:11px;padding:4px 8px;cursor:pointer;-webkit-appearance:none;appearance:none}#connection-dot{width:6px;height:6px;border-radius:50%;background:var(--color-danger);margin-left:var(--content-padding);margin-right:8px;box-shadow:var(--glow-danger);flex-shrink:0}#connection-dot.connected{background:var(--color-success);box-shadow:var(--glow-success)}#btn-info,#btn-textview,#btn-shutdown{background:transparent;border:none;color:var(--text-disabled);font-size:14px;padding:4px;margin-left:4px;cursor:pointer;transition:color var(--transition-fast)}#btn-textview{padding:4px 0;margin-left:2px;margin-right:4px;font-size:11px;font-weight:500}#btn-info:active,#btn-textview:active{color:var(--color-accent)}#btn-shutdown{color:var(--color-danger-muted)}#btn-shutdown:active{color:var(--color-danger-text)}#terminal-container{flex:1;overflow:hidden;background:var(--bg-primary);padding:0 0 0 var(--content-padding);-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:none;overscroll-behavior:contain;contain:strict;isolation:isolate}#terminal{height:100%!important;contain:layout paint}.terminal-instance{height:100%;width:100%;contain:layout paint}.xterm-screen canvas,.xterm canvas{transform:translateZ(0);backface-visibility:hidden}#terminal .xterm-viewport{background:var(--bg-primary)!important;overflow-y:overlay!important;touch-action:none;overscroll-behavior:contain;contain:paint}#terminal .xterm-viewport::-webkit-scrollbar{width:var(--scrollbar-width)}#terminal .xterm-viewport::-webkit-scrollbar-track{background:transparent}#terminal .xterm-viewport::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:var(--radius-sm)}#terminal .xterm-viewport::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-hover)}#terminal .xterm-screen{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}#terminal .xterm-helper-textarea,.xterm-helper-textarea{-webkit-text-security:none!important;font-size:16px!important;-webkit-user-select:text;user-select:text}.xterm-helper-textarea::-webkit-contacts-auto-fill-button,.xterm-helper-textarea::-webkit-credentials-auto-fill-button{visibility:hidden;display:none!important;pointer-events:none;position:absolute;right:0;width:0;height:0}#toolbar{display:flex;flex-direction:column;background:linear-gradient(to bottom,var(--bg-gradient-top),var(--bg-gradient-bottom));border-top:1px solid var(--border-subtle);padding-bottom:env(safe-area-inset-bottom,0px)}.toolbar-row{display:flex;align-items:center;justify-content:center;height:var(--row-height);min-height:var(--row-height);gap:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.toolbar-row:first-child{border-bottom:1px solid var(--border-subtle)}.toolbar-row::-webkit-scrollbar{display:none}.tool-btn{display:flex;align-items:center;justify-content:center;height:var(--row-height);padding:0 10px;background:transparent;border:none;border-right:1px solid var(--border-subtle);border-radius:0;color:var(--text-muted);font-size:12px;cursor:pointer;transition:all var(--transition-fast);-webkit-touch-callout:none;flex-shrink:0}.tool-btn:active{background:var(--hover-overlay);color:var(--text-high)}.tool-btn.arrow{padding:0 10px}.tool-btn.danger{color:var(--color-danger-muted)}.tool-btn.danger:active{background:var(--color-danger-bg);color:var(--color-danger-text)}.tool-btn.tmux{color:var(--color-tmux)}.tool-btn.tmux:active{background:var(--color-tmux-bg);color:var(--color-tmux-text)}.tool-btn.modifier{padding:0 10px}.tool-btn.icon{font-size:18px}.tool-btn.enter{color:var(--color-success)}.tool-btn.enter:active{background:var(--color-success-bg);color:var(--color-success-text)}.tool-btn.sticky{background:var(--color-accent-bg);color:var(--color-accent-text)}.tool-btn.locked{background:var(--color-locked-bg);color:var(--text-white);box-shadow:var(--glow-locked)}#disconnect-overlay{position:fixed;inset:0;background:var(--overlay-bg);z-index:1000;display:flex;align-items:center;justify-content:center}#disconnect-content{text-align:center}#disconnect-icon{font-size:48px;color:var(--color-danger);margin-bottom:16px}#disconnect-text{color:var(--text-secondary);font-size:18px;margin-bottom:24px}#disconnect-retry{padding:var(--content-padding) 32px;background:var(--border-light);border:1px solid var(--border-medium);border-radius:var(--radius-lg);color:var(--text-high);font-size:14px;cursor:pointer;transition:all var(--transition-fast)}#disconnect-retry:active{background:var(--border-active);color:var(--text-white)}#auth-overlay{position:fixed;inset:0;background:var(--overlay-bg);z-index:1001;display:flex;align-items:center;justify-content:center;padding:20px}#auth-content{background:var(--bg-elevated);border:1px solid var(--border-light);border-radius:0;max-width:320px;width:100%;box-shadow:var(--shadow-elevated);animation:helpAppear var(--transition-fast)}#auth-header{display:flex;align-items:center;justify-content:center;padding:var(--content-padding) 16px;border-bottom:1px solid var(--border-subtle);color:var(--text-high);font-size:14px;font-weight:500}#auth-body{padding:var(--content-padding) 16px;display:flex;flex-direction:column;gap:12px}#auth-error{padding:8px 12px;background:var(--color-danger-bg);border:1px solid var(--color-danger-border);border-radius:0;color:var(--color-danger-text);font-size:12px;text-align:center}#auth-password{padding:10px 12px;background:var(--bg-surface);border:1px solid var(--border-medium);border-radius:0;color:var(--text-high);font-size:14px;outline:none;transition:border-color var(--transition-fast)}#auth-password:focus{border-color:var(--color-accent)}#auth-password::placeholder{color:var(--text-disabled)}#auth-submit{padding:var(--content-padding) 16px;background:var(--color-accent-bg);border:1px solid var(--color-accent);border-radius:0;color:var(--color-accent-text);font-size:14px;cursor:pointer;transition:all var(--transition-fast)}#auth-submit:active{background:var(--color-accent-strong);color:var(--text-white)}#auth-submit:disabled{opacity:.5;cursor:not-allowed}#copy-button{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);padding:8px 20px;background:linear-gradient(to bottom,var(--bg-surface),var(--bg-elevated));border:1px solid var(--border-light);border-radius:var(--radius-md);color:var(--text-high);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;font-weight:500;cursor:pointer;z-index:10000;box-shadow:var(--shadow-elevated);display:none;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;touch-action:manipulation;transition:all var(--transition-fast)}#copy-button:active{background:linear-gradient(to bottom,var(--bg-hover),var(--bg-surface));transform:translate(-50%,-50%) scale(.97)}#copy-button.visible{display:block;animation:copyButtonAppear .1s ease-out}#copy-button.success{background:linear-gradient(to bottom,var(--bg-success-gradient-top),var(--bg-success-gradient-bottom));border-color:var(--color-success-border);color:var(--color-success-text)}#copy-button.error{background:linear-gradient(to bottom,var(--bg-danger-gradient-top),var(--bg-danger-gradient-bottom));border-color:var(--color-danger-border);color:var(--color-danger-text)}@keyframes copyButtonAppear{0%{opacity:0;transform:translate(-50%,-50%) scale(.9)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}#help-overlay{position:fixed;inset:0;background:var(--overlay-bg);z-index:1000;display:flex;align-items:center;justify-content:center;padding:20px}#help-content{background:var(--bg-elevated);border:1px solid var(--border-light);border-radius:var(--radius-xl);max-width:320px;width:100%;box-shadow:var(--shadow-elevated);animation:helpAppear var(--transition-fast)}@keyframes helpAppear{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}#help-header{display:flex;align-items:center;justify-content:space-between;padding:var(--content-padding) 16px;border-bottom:1px solid var(--border-subtle);color:var(--text-high);font-size:14px;font-weight:500}#help-close{background:transparent;border:none;color:var(--text-muted);font-size:20px;cursor:pointer;padding:4px 8px;line-height:1}#help-close:active{color:var(--text-high)}#help-body{padding:var(--content-padding) 16px}.help-section{margin-bottom:var(--content-padding)}.help-section:last-child{margin-bottom:0}.help-title{color:var(--text-muted);font-size:10px;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}.help-item{display:flex;align-items:center;gap:10px;padding:4px 0;color:var(--text-secondary);font-size:12px}.help-key{display:inline-block;min-width:80px;padding:2px 6px;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);color:var(--text-high);font-size:11px;text-align:center}#textview-overlay{position:fixed;inset:0;background:var(--bg-primary);z-index:1000;display:flex;flex-direction:column;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}#textview-content{display:flex;flex-direction:column;width:100%;height:100%;background:var(--bg-primary)}#textview-header{display:flex;align-items:center;height:var(--row-height);min-height:var(--row-height);background:linear-gradient(to bottom,var(--bg-gradient-top),var(--bg-gradient-bottom));border-bottom:1px solid var(--border-subtle);flex-shrink:0}#textview-title{padding:0 var(--content-padding);color:var(--text-muted);font-size:12px;border-right:1px solid var(--border-subtle)}.textview-zoom-btn{display:flex;align-items:center;justify-content:center;height:var(--row-height);padding:0 var(--content-padding);background:transparent;border:none;border-right:1px solid var(--border-subtle);color:var(--text-muted);font-size:14px;cursor:pointer;transition:all var(--transition-fast)}.textview-zoom-btn:active{background:var(--hover-overlay);color:var(--text-high)}#textview-close{margin-left:auto;display:flex;align-items:center;justify-content:center;height:var(--row-height);padding:0 var(--content-padding);background:transparent;border:none;border-left:1px solid var(--border-subtle);color:var(--text-muted);font-size:16px;cursor:pointer;transition:all var(--transition-fast)}#textview-close:active{background:var(--hover-overlay);color:var(--text-high)}#textview-body{flex:1;min-height:0;margin:0;padding:8px var(--content-padding);background:var(--bg-primary);border:none;color:var(--text-primary);font-family:Menlo,Monaco,Consolas,monospace;font-size:9px;line-height:1.3;overflow-y:auto;white-space:pre-wrap;word-wrap:break-word;user-select:text;-webkit-user-select:text}.hidden{display:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}}
@@ -16,8 +16,8 @@
16
16
 
17
17
  <!-- Icons (PWA disabled - not useful for tunnel-based access) -->
18
18
  <link rel="icon" type="image/svg+xml" href="/static/icon.svg">
19
- <script type="module" crossorigin src="/static/assets/app-By4EXMHC.js"></script>
20
- <link rel="stylesheet" crossorigin href="/static/assets/app-DQePboVd.css">
19
+ <script type="module" crossorigin src="/static/assets/app-DlWNJWFE.js"></script>
20
+ <link rel="stylesheet" crossorigin href="/static/assets/app-xPAM7YhQ.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="app">
@@ -126,5 +126,17 @@
126
126
  </div>
127
127
  </div>
128
128
 
129
+ <!-- Auth Overlay -->
130
+ <div id="auth-overlay" class="hidden">
131
+ <div id="auth-content">
132
+ <div id="auth-header">Authentication Required</div>
133
+ <div id="auth-body">
134
+ <div id="auth-error" class="hidden"></div>
135
+ <input type="password" id="auth-password" placeholder="Enter password" autocomplete="current-password">
136
+ <button id="auth-submit">Connect</button>
137
+ </div>
138
+ </div>
139
+ </div>
140
+
129
141
  </body>
130
142
  </html>
porterminal/updater.py CHANGED
@@ -34,6 +34,12 @@ def _detect_install_method() -> str:
34
34
  return "pip"
35
35
 
36
36
 
37
+ def _parse_version(v: str) -> tuple[int, ...]:
38
+ """Parse version string to comparable tuple."""
39
+ v = v.lstrip("v").split("+")[0].split(".dev")[0]
40
+ return tuple(int(p) for p in v.split(".")[:3] if p.isdigit())
41
+
42
+
37
43
  def _is_newer(latest: str, current: str) -> bool:
38
44
  """Return True if latest > current."""
39
45
  try:
@@ -42,12 +48,8 @@ def _is_newer(latest: str, current: str) -> bool:
42
48
  return Version(latest) > Version(current)
43
49
  except Exception:
44
50
  # Fallback: tuple comparison (handles 0.9 vs 0.10 correctly)
45
- def to_tuple(v: str) -> tuple[int, ...]:
46
- v = v.lstrip("v").split("+")[0].split(".dev")[0]
47
- return tuple(int(p) for p in v.split(".")[:3] if p.isdigit())
48
-
49
51
  try:
50
- return to_tuple(latest) > to_tuple(current)
52
+ return _parse_version(latest) > _parse_version(current)
51
53
  except ValueError:
52
54
  return False
53
55
 
@@ -154,6 +156,12 @@ def update_package() -> bool:
154
156
 
155
157
  print(f"Updating {PACKAGE_NAME} {__version__} -> {latest}")
156
158
 
159
+ # Windows: can't upgrade while running (exe is locked)
160
+ if sys.platform == "win32":
161
+ print("On Windows, close ptn first then run from another terminal:")
162
+ print(f" {get_upgrade_command()}")
163
+ return False
164
+
157
165
  method = _detect_install_method()
158
166
 
159
167
  # Build command
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ptn
3
- Version: 0.2.5
3
+ Version: 0.4.2
4
4
  Summary: Web-based terminal accessible from phone via Cloudflare Tunnel
5
5
  Project-URL: Homepage, https://github.com/lyehe/porterminal
6
6
  Project-URL: Repository, https://github.com/lyehe/porterminal
@@ -26,6 +26,7 @@ Classifier: Topic :: Internet :: WWW/HTTP
26
26
  Classifier: Topic :: System :: Shells
27
27
  Classifier: Topic :: Terminals :: Terminal Emulators/X Terminals
28
28
  Requires-Python: >=3.12
29
+ Requires-Dist: bcrypt>=4.0.0
29
30
  Requires-Dist: fastapi>=0.104.0
30
31
  Requires-Dist: pydantic>=2.0
31
32
  Requires-Dist: pywinpty>=2.0.0; sys_platform == 'win32'
@@ -47,7 +48,7 @@ Description-Content-Type: text/markdown
47
48
  <a href="https://pypi.org/project/ptn/"><img src="https://img.shields.io/pypi/v/ptn?style=flat-square&logo=pypi&logoColor=white&label=PyPI" alt="PyPI"></a>
48
49
  <a href="https://pypi.org/project/ptn/"><img src="https://img.shields.io/pypi/pyversions/ptn?style=flat-square&logo=python&logoColor=white" alt="Python"></a>
49
50
  <a href="https://pypi.org/project/ptn/"><img src="https://img.shields.io/pypi/dm/ptn?style=flat-square&label=Downloads" alt="Downloads"></a>
50
- <a href="https://github.com/lyehe/porterminal/blob/main/LICENSE"><img src="https://img.shields.io/github/license/lyehe/porterminal?style=flat-square" alt="License"></a>
51
+ <a href="https://github.com/lyehe/porterminal/blob/master/LICENSE"><img src="https://img.shields.io/github/license/lyehe/porterminal?style=flat-square" alt="License"></a>
51
52
  <a href="https://github.com/lyehe/porterminal/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/lyehe/porterminal/ci.yml?branch=master&style=flat-square&logo=github&label=CI" alt="CI"></a>
52
53
  </p>
53
54
 
@@ -60,22 +61,24 @@ Description-Content-Type: text/markdown
60
61
  </p>
61
62
 
62
63
  <p align="center">
63
- <video src="assets/demo.mp4" width="600" controls></video>
64
+ <img src="assets/demo.gif" alt="Porterminal demo" width="320">
64
65
  </p>
65
66
 
66
- ---
67
-
68
67
  ## Why
69
68
 
70
- I wanted to vibe code from bed. ngrok requires registration and the free tier sucks. Cloudflare Quick Tunnel works great but is hard to use directly on the phone. Termius works after complicated setup: port forwarding, firewall rules, key management... I just wanted something simpler: **run a command, scan a QR, start typing.**
69
+ I wanted to vibe code from bed.
70
+
71
+ **ngrok** requires registration and the free tier sucks. **Cloudflare Quick Tunnel** works great but is hard to use directly on the phone. **Termius** requires complicated setup: port forwarding, firewall rules, key management... Tried **Claude Code web**, but it can't access my local hardware and environment. Also tried **Happy**, but it's too bulky and updates lag behind.
72
+
73
+ So I built something simpler: **run a command, scan a QR, start typing.**
71
74
 
72
75
  ## Features
73
76
 
74
77
  - **One command, instant access** - No SSH, no port forwarding, no config files. Cloudflare tunnel + QR code.
75
- - **Actually usable on mobile** - Essential buttons and gestures for everyday terminal use.
76
- - **Multi-tab shared sessions** - Run builds in one tab, tail logs in another. Sessions persist across reconnects.
77
- - **Cross-platform** - Windows (PowerShell, CMD, WSL), Linux/macOS (Bash, Zsh, Fish). Auto-detects your shells.
78
- - **Update notifications** - Checks PyPI daily, notifies on startup if update available. Run `ptn -U` to update.
78
+ - **Actually usable on mobile** - Touch-optimized with momentum scrolling, pinch-to-zoom, swipe gestures, and modifier keys (Ctrl, Alt).
79
+ - **Full terminal apps** - vim, htop, less, tmux all work correctly with proper alt-screen buffer handling.
80
+ - **Persistent multi-tab sessions** - Sessions survive disconnects. Close the browser, switch networks, reconnect from another device—your shell and running processes are still there. Multiple devices can view the same session simultaneously.
81
+ - **Cross-platform** - Windows (PowerShell, CMD, WSL), Linux/macOS (Bash, Zsh, Fish, Nushell, and any shell via `$SHELL`). Auto-detects your shells.
79
82
 
80
83
  ## Install
81
84
 
@@ -86,6 +89,13 @@ I wanted to vibe code from bed. ngrok requires registration and the free tier su
86
89
  | **pipx** | `pipx install ptn` | `pipx upgrade ptn` |
87
90
  | **pip** | `pip install ptn` | `pip install -U ptn` |
88
91
 
92
+ **One-line install (uv + ptn):**
93
+
94
+ | OS | Command |
95
+ |----|---------|
96
+ | **Windows** | `powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lyehe/porterminal/master/install.ps1 \| iex"` |
97
+ | **macOS/Linux** | `curl -LsSf https://raw.githubusercontent.com/lyehe/porterminal/master/install.sh \| sh` |
98
+
89
99
  Requires Python 3.12+ and [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) (auto-installed if missing).
90
100
 
91
101
  ## Usage
@@ -93,44 +103,95 @@ Requires Python 3.12+ and [cloudflared](https://developers.cloudflare.com/cloudf
93
103
  ```bash
94
104
  ptn # Start in current directory
95
105
  ptn ~/projects/myapp # Start in specific folder
96
- ptn --no-tunnel # Local network only
97
- ptn -b # Run in background
98
- ptn --init # Create .ptn/ptn.yaml config
99
- ptn -U # Update to latest version
100
- ptn --check-update # Check if update available
101
106
  ```
102
107
 
108
+ | Flag | Description |
109
+ |------|-------------|
110
+ | `-n, --no-tunnel` | Local network only (no Cloudflare tunnel) |
111
+ | `-b, --background` | Run in background and return immediately |
112
+ | `-p, --password` | Prompt for password to protect this session |
113
+ | `-dp, --default-password` | Toggle password requirement in config (on/off) |
114
+ | `-v, --verbose` | Show detailed startup logs |
115
+ | `-i, --init` | Create `.ptn/ptn.yaml` config with auto-discovered project scripts |
116
+ | `-u, --update` | Update to the latest version |
117
+ | `-c, --check-update` | Check if a newer version is available |
118
+ | `-V, --version` | Show version |
119
+
120
+ ## Mobile Gestures
121
+
122
+ | Gesture | Action |
123
+ |---------|--------|
124
+ | **Tap** | Focus terminal, clear selection |
125
+ | **Long-press** | Start text selection |
126
+ | **Double-tap** | Select word |
127
+ | **Swipe left/right** | Arrow keys (← →) |
128
+ | **Scroll** | Momentum scrolling with physics |
129
+ | **Pinch** | Zoom text (10-24px) |
130
+
131
+ **Modifier keys** (Ctrl, Alt, Shift): Tap once for sticky (one keystroke), double-tap for lock.
132
+
103
133
  ## Configuration
104
134
 
105
- Run `ptn --init` to create a starter config, or create `ptn.yaml` manually:
135
+ Run `ptn --init` to create a starter config. It auto-discovers project scripts from `package.json`, `pyproject.toml`, or `Makefile` and adds them as buttons:
136
+
137
+ ```bash
138
+ ptn -i
139
+ # Created: .ptn/ptn.yaml
140
+ # Discovered 3 project script(s): build, dev, test
141
+ ```
142
+
143
+ Or create `ptn.yaml` manually:
106
144
 
107
145
  ```yaml
146
+ # Terminal settings
147
+ terminal:
148
+ default_shell: nu # Default shell ID
149
+ shells: # Custom shell definitions
150
+ - id: nu
151
+ name: Nushell
152
+ command: nu
153
+ args: []
154
+
108
155
  # Custom buttons (appear in toolbar)
156
+ # row: 1 = default row, 2+ = additional rows
109
157
  buttons:
110
158
  - label: "claude"
111
159
  send:
112
160
  - "claude"
113
161
  - 100 # delay in ms
114
162
  - "\r"
115
- - label: "tmux"
116
- send: "tmux\r"
163
+ - label: "build"
164
+ send: "npm run build\r"
165
+ row: 2 # second button row
117
166
 
118
167
  # Update checker settings
119
168
  update:
120
169
  notify_on_startup: true # Show update notification
121
170
  check_interval: 86400 # Seconds between checks (default: 24h)
171
+
172
+ # Security settings
173
+ security:
174
+ require_password: true # Always prompt for password at startup
175
+ max_auth_attempts: 5 # Max failed attempts before disconnect
122
176
  ```
123
177
 
124
178
  Config is searched in order: `$PORTERMINAL_CONFIG_PATH`, `./ptn.yaml`, `./.ptn/ptn.yaml`, `~/.ptn/ptn.yaml`.
125
179
 
126
180
  ## Security
127
181
 
128
- > **Warning:** The URL is the only authentication. Anyone with the link has full terminal access.
182
+ Use password if your screen can be exposed to others:
183
+ ```bash
184
+ ptn -p # Prompt for password this session
185
+ ptn -dp # Enable password by default (toggle)
186
+ ```
187
+
188
+ Password is per-session (never saved to disk). See [docs/security.md](docs/security.md) for details.
189
+
190
+ ## Troubleshooting
191
+
192
+ **Connection fails?** Cloudflare tunnel sometimes blocks connections. Restart the server (`Ctrl+C`, then `ptn`) to get a fresh tunnel URL.
129
193
 
130
- - Don't share the URL
131
- - Stop the server when not in use (`Ctrl+C`)
132
- - Use `--no-tunnel` for local network only
133
- - Environment variables are sanitized (API keys, tokens stripped)
194
+ **Shell not detected?** Set your `$SHELL` environment variable or configure shells in `ptn.yaml`.
134
195
 
135
196
  ## Contributing
136
197
 
@@ -1,40 +1,42 @@
1
- porterminal/__init__.py,sha256=fqY1ac5vc_zXQBWBMgziJbRJFEnpfFOezf-feWGCvEQ,10677
1
+ porterminal/__init__.py,sha256=aftLcHuDFp0nI5MF-eNyahFyx3nyGxf8SERK59-G_0Y,12604
2
2
  porterminal/__main__.py,sha256=XLo21rqmISrIZFiaHC58Trgq8E0gxH4Wb3driD4JA7c,137
3
- porterminal/_version.py,sha256=9wrJ_4Dlc0arUzKiaIqvTY85rMJma3eb1nNlF3uHAxU,704
4
- porterminal/app.py,sha256=kuZfiV23wsMLi6382R2mhGjzGibFgutxms8PXAesAoE,14101
3
+ porterminal/_version.py,sha256=A45grTqzrHuDn1CT9K5GVUbY4_Q3OSTcXAl3zdHzcEI,704
4
+ porterminal/app.py,sha256=Q3w-61i6h2Atz4wirAgrT--xTH_qCp4Ig2zgnAIyJ9U,15220
5
5
  porterminal/asgi.py,sha256=P76H7k03T3GYBAmjWqLaCZXV-YIou6NMhfRySO8He1A,1276
6
- porterminal/composition.py,sha256=UfcULLg6_HzKubmAU93jUI7ABwawhMd-XzcgVjBV2ag,6698
7
- porterminal/config.py,sha256=rABTms9AammCSbRFjYvGGdybWx5WZ7Z8PQAi1Zb8v30,5253
8
- porterminal/container.py,sha256=upetU-eK-fo-lKStO_DCY9odD_f-SoBkdgjZlFG4gWA,1450
6
+ porterminal/composition.py,sha256=H1QJszJqGX4BXZE4cFk3I46yxybZPtNG35srqRi35vo,6799
7
+ porterminal/config.py,sha256=zt-Yy59U0o5DbFMcgBpTCw4rOEtywMD1mQpXDni9u5g,5687
8
+ porterminal/container.py,sha256=wDtppL1Xf4A6IQcP5FP-V9-hjKEDciQc4VMz5ezyv2o,1536
9
9
  porterminal/logging_setup.py,sha256=DhoN_FdJFbRawcsYy8mLRU32TCT1ZfZ2G4Q_AOKwZuA,1513
10
- porterminal/updater.py,sha256=iIUQzo4AW8DWoQdJnCQ9DRiSgTZRiN6XXbTYn4puPaw,6222
10
+ porterminal/updater.py,sha256=VqUL8NFOIL36dSeOFUs3TUAJP0u5D4toDFr1k1yi3Jg,6503
11
11
  porterminal/application/__init__.py,sha256=Q5gQia_11ubfMcDbzjvI6RoUU2jFJkuQrsbSuIfQCRA,55
12
- porterminal/application/ports/__init__.py,sha256=zKcSDFmPPOIP0Oxuf8O5AEMmURV7AaSVA2D8iahwU6c,149
12
+ porterminal/application/ports/__init__.py,sha256=bGZlGKTjKWWxiwNhjz47nA2K_wvFSp9dExNmo9Pikxg,240
13
13
  porterminal/application/ports/connection_port.py,sha256=kXIl-fzx3SbpU8kV1rpmgNeI22XSJMbzXGt1YcwZO8Y,926
14
+ porterminal/application/ports/connection_registry_port.py,sha256=OAZMvcETGW4IkeJ2OPQ29wTEJ7HKBLj_pVlsnGY0znk,1386
14
15
  porterminal/application/services/__init__.py,sha256=YO-iPdE_-EEV9tVnu0NcB5bwSCCSVy9CWOGBFCJyoRc,335
15
- porterminal/application/services/management_service.py,sha256=WllcHDCQuPzAKkEEyhV8afDWJ2aUw8rsBsFhbZ3DWPU,8661
16
- porterminal/application/services/session_service.py,sha256=f1Mk-a9ug895jSsskOKY1uW3N5IcMTEutpM_d0RAmaM,8234
16
+ porterminal/application/services/management_service.py,sha256=nHNAlcgu2IoUZsNDmzvgEsh1j0sBMI8yDwg6BJk7r1Q,7933
17
+ porterminal/application/services/session_service.py,sha256=ng1B4GQOVBCjI6gqkb_cO_WDfmN3Lm_RUiyBYYkBzDk,7938
17
18
  porterminal/application/services/tab_service.py,sha256=0_S978dYhBW2-eaZN2wOlcZOY4V4iPt2MMNOyoZQxPs,8307
18
- porterminal/application/services/terminal_service.py,sha256=za8Rr-Hb7C2xzCF0HmSG7VpCk1_gvTsnChyyPyOzVPs,19818
19
+ porterminal/application/services/terminal_service.py,sha256=0zaQWgZ8Uz8JmDfTqww4p2rJfH6AIyG8dVmSH4JIp2I,22054
19
20
  porterminal/cli/__init__.py,sha256=A3y-QgKrT-vdAYV-xsZjeyMkiPymZaZzYUGQ-_3cXmQ,305
20
- porterminal/cli/args.py,sha256=VEkN-nUHgPipFIzK95hmHblRdgin7FLl1spre3Cvf5o,3672
21
- porterminal/cli/display.py,sha256=wErWVPz3PmCvX5vxxaCdPb4YSeByrEj1dj0BSSmrZE0,5530
22
- porterminal/domain/__init__.py,sha256=OIkaxSb2BThs1COAvsuyOWC-l7lWd7Dfvl1kwNVlgPg,1646
21
+ porterminal/cli/args.py,sha256=ixXT4W3D6MjS3IemZNeEjyC0-Vfsf8kQGwHotpjJhXM,5970
22
+ porterminal/cli/display.py,sha256=PgLnRj9odMKYERMCLyrEhTrpYeYPNYC-a1nWcz_fFE0,5433
23
+ porterminal/cli/script_discovery.py,sha256=jdDRBZ-0LbtC2F1oV_oQtrd00duYWH2QXFs1FakKxtc,3869
24
+ porterminal/domain/__init__.py,sha256=16WEj1SAKhIqkrQHqaan66pV3zsUn1aEz7oymN54xFQ,1428
23
25
  porterminal/domain/entities/__init__.py,sha256=dIQp6T0M-Bl_DJpSOGQGBD29Rl_FDR0XfVrDL08ck50,463
24
- porterminal/domain/entities/output_buffer.py,sha256=d5ILcaxBsTLtZQEaGAgXFByQVacdsXRO9ioEAIgWqlA,2133
26
+ porterminal/domain/entities/output_buffer.py,sha256=TqqgGk3NEM_htNbAhZQCJM_dek4ajhwViPaVE3XXCeI,4220
25
27
  porterminal/domain/entities/session.py,sha256=SzAEqbqr49QhcSW_2BqYVJst1_6yo8qs21Efh_uBJDA,2680
26
- porterminal/domain/entities/tab.py,sha256=OQPS_CtTx5MD8bjH23NQw0Z4RbXh8Mqew3P0_KYHYl4,2124
28
+ porterminal/domain/entities/tab.py,sha256=YRh_4fWcRZFrFxtX-2awYfnglEOcWGBLc0baQc1Rffo,1996
27
29
  porterminal/domain/ports/__init__.py,sha256=ryAYtrvSanQecroEz7tPHzWN_U9icx51Fn_snDBNDiQ,264
28
30
  porterminal/domain/ports/pty_port.py,sha256=HGk7kyE1j5vG5aebB2XFfrhjNX6a2g37eUUPpgPJxu0,1179
29
31
  porterminal/domain/ports/session_repository.py,sha256=Gh5c2cneboXYEuNw8Gxw06IZwpMqhN0HA7KINFFi2LQ,1611
30
32
  porterminal/domain/ports/tab_repository.py,sha256=_QMuTqjc9A095sDInD4fk7sjCu_v-aXXM4HzCQsOyO0,1826
31
- porterminal/domain/services/__init__.py,sha256=YiEokgIXvSbfieNlBfdWSPJlbBYDFrzIYc5aVpkjSeI,561
33
+ porterminal/domain/services/__init__.py,sha256=eJYyrp1siHoxivk2eO9UGHucmnyFZbvQH01BjBAFv1I,477
32
34
  porterminal/domain/services/environment_sanitizer.py,sha256=RslZDn8jso8bX1hG9p9NQyv1ey19u1F5xATPlq6pKVw,2014
33
35
  porterminal/domain/services/rate_limiter.py,sha256=I0cybe1PX7uYWlr7l92N3jM89yFQDRKqD5p9-lT8Mas,1724
34
36
  porterminal/domain/services/session_limits.py,sha256=EqW_tqr0Br3a2gzgl5Dg80dx1umcU6VMr4-ELRtGx_w,3181
35
37
  porterminal/domain/services/tab_limits.py,sha256=nokX8gP9_41MNIdQ7Fri2JLAs4_Ch-fy3CgpKHTZnT8,1434
36
- porterminal/domain/values/__init__.py,sha256=_hVr6uiuKOMvlsE1j41b8NxSoOBuK9CkLK2d9mjsFzg,682
37
- porterminal/domain/values/environment_rules.py,sha256=ewBv6Z_iXbWOFNcYmvczgKL6Kb_zzlGH1mh46n2_5jU,3888
38
+ porterminal/domain/values/__init__.py,sha256=SuUiW8EINXgbpaCuqmdTGnVrmP0x2FuoJz3HsUzB7M0,516
39
+ porterminal/domain/values/environment_rules.py,sha256=ii3D3CP4v8V-4H13VadzE76Z8EyZdbx-h7CYnEvXmpw,3946
38
40
  porterminal/domain/values/rate_limit_config.py,sha256=BZb-Jbxav8kJ6ijADIrW_-JhDt8WuPIe8OIqLnQykXA,548
39
41
  porterminal/domain/values/session_id.py,sha256=dYBSZdG6WWjp06mUbTZwjufb30A6lSuK-NKwqxAAavY,461
40
42
  porterminal/domain/values/shell_command.py,sha256=9U723nsdDCsW_M17qBp__cu3r9uuBVuur0kifaoiEF0,1102
@@ -42,30 +44,31 @@ porterminal/domain/values/tab_id.py,sha256=zKZZ1lBQUmXZ3PQlH0jejUQ_AA_SVTW-Z7jCI
42
44
  porterminal/domain/values/terminal_dimensions.py,sha256=mWtMQi40EDtwnKpQadTR5eD0VmInO-2kgbUUw2VVCZY,1334
43
45
  porterminal/domain/values/user_id.py,sha256=eskGRVL-qj4czXljF0e5imDh19pAq16faXJY0lZ73kM,581
44
46
  porterminal/infrastructure/__init__.py,sha256=rIaiEG4gyvfSixSEry0kA2uMhStQQIWv9PRlfs17qts,649
45
- porterminal/infrastructure/cloudflared.py,sha256=6vGUdFmGrxLTbopGgEdYRKJ5n0M3Ov00cuM-hEYAjSk,11506
47
+ porterminal/infrastructure/auth.py,sha256=vBXC9TJrydbQDadvYqfznhQ2YRZ2BGBseabJ-IAmUeA,3931
48
+ porterminal/infrastructure/cloudflared.py,sha256=E7VtcZv4U5HeO1t4JYAO-97JavlD811-lR5A8bwPyPI,11606
46
49
  porterminal/infrastructure/network.py,sha256=XnYbEXKQA8BnrLFl9b4OTTixFG1pVFxfeVuhN-XPkfU,1275
47
- porterminal/infrastructure/server.py,sha256=r0tr4I28ocL1JskGmZcTTmBd6gDW446QjV2paZWvLqY,4850
50
+ porterminal/infrastructure/server.py,sha256=n6lSZjd6m380QzvdAZAu7CbbRoTvTAL8Dp-mcMaNFmY,5803
48
51
  porterminal/infrastructure/config/__init__.py,sha256=kcoM8mQYa83rkxn8TUfh0nETKynrsLmVwD9ySIfvdPQ,139
49
- porterminal/infrastructure/config/shell_detector.py,sha256=pCbtD1HHmH99Ih1i8PypRhoS3ZxKKLvr7jmhD3SB15k,2702
52
+ porterminal/infrastructure/config/shell_detector.py,sha256=CPL7b7EgUcFNmjDzwzA-HxYCQNmyOzVw2xNGfmiQv_Q,16569
50
53
  porterminal/infrastructure/registry/__init__.py,sha256=reNbIYRr1alNps7zm_oENuS6QWNKiiO-WG09LT5MLmM,157
51
54
  porterminal/infrastructure/registry/user_connection_registry.py,sha256=u9KOSijHiRITnwnOBKwXuYOKbFguh2NbxmNMxIoo8vc,3398
52
55
  porterminal/infrastructure/repositories/__init__.py,sha256=UyF9lpobXgKb9Ti6ucC6fDcC9CkMAGDThggEG-Rf-Aw,250
53
- porterminal/infrastructure/repositories/in_memory_session.py,sha256=yNkCuxrR8S5yTC_I_OFOYg1DlgEkxcI2DEW6aPq9ByQ,2490
54
- porterminal/infrastructure/repositories/in_memory_tab.py,sha256=txHwO3xdRZjBzmdxCQy02conzjkq8lRWGQoKUd4J__E,4230
56
+ porterminal/infrastructure/repositories/in_memory_session.py,sha256=PpnW16Hje5pvY_NHMxeWpVxmwjlSx0T4dxAsw5GcAcI,2411
57
+ porterminal/infrastructure/repositories/in_memory_tab.py,sha256=tXeZQkIHLcdR_p-zm5mR4g8AAhB6TQyrQJmc8k4Df4k,4025
55
58
  porterminal/infrastructure/web/__init__.py,sha256=QZqOtxOqhuxh4AD5xzPl9CBfOscdXOoGQv4zT4rpy7Q,147
56
59
  porterminal/infrastructure/web/websocket_adapter.py,sha256=vVjIhcfWikPw-PKfSyqm3Y7PbTxNj1mstfrHIpcg6ng,2424
57
60
  porterminal/pty/__init__.py,sha256=WNniN5uYKthLMOT7nzH63pkfFCJltToika50NZGR2cA,1054
58
- porterminal/pty/env.py,sha256=-TmrdZfRzbVmyt8CJB7AoeZupbvkLBHumJOOtZLWgvI,2268
59
- porterminal/pty/manager.py,sha256=Z7uvxauCC43b3z25VyJD0tI__08EpgcogT-Fe-VV17w,4483
61
+ porterminal/pty/env.py,sha256=fr5H1wEFXPCZnHW5KzeI4B5rWB4ixeXcXdrlR_GzBsU,1048
62
+ porterminal/pty/manager.py,sha256=W-uaPWA4SmNl00OA4-FS05A8b0LoaYo0ANsxll0ypwU,4605
60
63
  porterminal/pty/protocol.py,sha256=-OPHIfLxL6R_JTrOQNB2t-sRH7LqtFgJE3QkBcHBqNA,1982
61
64
  porterminal/pty/unix.py,sha256=hDwHuD2XD4RN6t_Hk_q-XvgQ4dNF9zUSwJhe0-Y9JBU,5094
62
65
  porterminal/pty/windows.py,sha256=7WrtxgUbqDKSPMK9_pN9vNPzMalV1p-UyyZlBZJn8EY,3933
63
66
  porterminal/static/icon.svg,sha256=y7-MIl7F_wVQMLHWmmC7MrZHZK5ikLg1BR_0jbqnTkc,1949
64
- porterminal/static/index.html,sha256=JzhGkRmuEZsCPU-kvJcCWj5yUXrjFxqRpBwxME3MSNU,5920
65
- porterminal/static/assets/app-By4EXMHC.js,sha256=OrKBI-ysghknqzSOLRXb1oeggDdaq4SyYjUwu_nuagU,433902
66
- porterminal/static/assets/app-DQePboVd.css,sha256=um-bPz_s6bxB6ZofHC6DHbApYpy5Ds8OEgvmireZfHg,18043
67
- ptn-0.2.5.dist-info/METADATA,sha256=tGG6euOUP8PWeewJJutBN0_Y-sZrVrmmZGx9PRC8m2w,5676
68
- ptn-0.2.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
69
- ptn-0.2.5.dist-info/entry_points.txt,sha256=Ftj1zSu_7G0yD5mAtGN3RoewyIuoBOfP_noSISe73tU,41
70
- ptn-0.2.5.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
71
- ptn-0.2.5.dist-info/RECORD,,
67
+ porterminal/static/index.html,sha256=xLfeiS_g9ujuJooN0QZWgt38q2ub1UcHs_anNS4ONgU,6401
68
+ porterminal/static/assets/app-DlWNJWFE.js,sha256=HxwLXBSp5CrSOMhaI6O0zHxBY3J8dQlW5IuC_L8LnoQ,486711
69
+ porterminal/static/assets/app-xPAM7YhQ.css,sha256=dYk-DAJSmvxP8fn5ABN5Jy_djhlpREDFjcIGKpb2gjw,18949
70
+ ptn-0.4.2.dist-info/METADATA,sha256=1Y7txZBGmjLeOs4MI_UR3FgWM0X_d_DBUj5Ln10mJtg,8124
71
+ ptn-0.4.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
72
+ ptn-0.4.2.dist-info/entry_points.txt,sha256=Ftj1zSu_7G0yD5mAtGN3RoewyIuoBOfP_noSISe73tU,41
73
+ ptn-0.4.2.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
74
+ ptn-0.4.2.dist-info/RECORD,,