yaport 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -60,7 +60,7 @@ GUI 能添加机器,但大量机器用 CLI 更快。`add-machine` 是非交互
60
60
  OpenSSH alias,等价于 `ssh erya`:
61
61
 
62
62
  ```bash
63
- yaport add-machine --name Erya --host erya --json
63
+ yaport add-machine --host erya --json
64
64
  ```
65
65
 
66
66
  明确指定用户和端口:
@@ -114,9 +114,9 @@ yaport add-machine \
114
114
 
115
115
  | 参数 | 说明 |
116
116
  | --- | --- |
117
- | `--name <name>` | UI 里展示的机器名称。 |
117
+ | `--name <name>` | UI 里展示的机器名称;留空时默认用 `user@host:port`。 |
118
118
  | `--host <host>` | SSH host、IP 或 OpenSSH alias。 |
119
- | `--user <user>` | SSH 用户。OpenSSH alias 已配置时可以省略。 |
119
+ | `--user <user>` | SSH 用户。OpenSSH alias 已配置时可以省略;`--host` 是 IP 地址时必须填写。 |
120
120
  | `--port <port>` | SSH 端口。OpenSSH alias 已配置时可以省略。 |
121
121
  | `--password-env <env>` | 从环境变量读取目标机器 SSH 密码。 |
122
122
  | `--connect-timeout <seconds>` | SSH 连接超时。可选值:`8`、`15`、`30`、`60`。默认 `15`。 |
@@ -134,8 +134,31 @@ yaport add-machine \
134
134
  - agent 和脚本优先使用 `--json`。
135
135
  - 密码优先使用 `--password-env` 和 `--jumpN-password-env`,不要把密码直接放进命令参数。
136
136
  - 使用 OpenSSH alias 时,`--user` 和 `--port` 可以留空。
137
+ - 使用 IP 地址连接目标机器或跳板机时,必须填写对应用户。
137
138
  - 两层跳板或密码认证握手较慢时,建议使用 `--connect-timeout 30 --command-timeout 90`。
138
139
 
140
+ ## 配置开机自启
141
+
142
+ 丫口可以安装为当前用户的系统服务,不需要 sudo。macOS 使用 LaunchAgent,Linux 使用 systemd user service。服务启动时会带 `--no-open`,不会开机自动弹浏览器。
143
+
144
+ ```bash
145
+ yaport service install --port 5173 --json
146
+ yaport service status
147
+ yaport service uninstall
148
+ ```
149
+
150
+ 常用参数:
151
+
152
+ | 参数 | 说明 |
153
+ | --- | --- |
154
+ | `install` | 写入并启动用户级服务。 |
155
+ | `uninstall` | 停止并移除用户级服务。 |
156
+ | `status` | 调用 `launchctl` 或 `systemctl --user` 查看状态。 |
157
+ | `--host <host>` | 服务监听地址,默认 `127.0.0.1`。 |
158
+ | `--port <port>` | 服务监听端口,默认 `5173`。 |
159
+ | `--data-file <path>` | 服务使用的机器配置文件;不传时仍按 `./.yaport`、`~/.yaport` 查找。 |
160
+ | `--json` | `install` / `uninstall` 输出稳定 JSON。 |
161
+
139
162
  ## 端口信息
140
163
 
141
164
  丫口通过本机 OpenSSH 登录远程机器,并读取:
@@ -173,6 +196,10 @@ yaport add-machine \
173
196
 
174
197
  - 添加机器表单默认折叠。
175
198
  - 添加机器时可以选择“连接超时”和“命令超时”。
199
+ - 连接名称可选,留空时默认用主机信息;Host 是 IP 地址时用户必填,OpenSSH alias 可以留空。
200
+ - 机器卡片右侧可以打开本机 Terminal,并用该机器配置执行 `ssh`。已保存密码不会写入命令行。
201
+ - 左侧机器列表在页面滚动时固定在左侧,也可以折叠成窄栏。
202
+ - 右键机器卡片可以编辑或删除连接配置;编辑时密码框留空表示保留原密码。
176
203
  - TCP / UDP 指标卡带开关,默认开启,用于过滤下方记录。
177
204
  - “监听端口”指标卡可以按端口分组。
178
205
  - “进程”指标卡可以按进程分组。
@@ -273,7 +300,7 @@ The GUI can add machines, but the CLI is faster for repeated setup. `add-machine
273
300
  OpenSSH alias, same as `ssh erya`:
274
301
 
275
302
  ```bash
276
- yaport add-machine --name Erya --host erya --json
303
+ yaport add-machine --host erya --json
277
304
  ```
278
305
 
279
306
  Explicit user and port:
@@ -327,9 +354,9 @@ Common options:
327
354
 
328
355
  | Option | Description |
329
356
  | --- | --- |
330
- | `--name <name>` | Display name in the UI. |
357
+ | `--name <name>` | Display name in the UI. When omitted, Yaport uses `user@host:port`. |
331
358
  | `--host <host>` | SSH host, IP address, or OpenSSH alias. |
332
- | `--user <user>` | SSH user. Omit it when an OpenSSH alias already defines `User`. |
359
+ | `--user <user>` | SSH user. Omit it when an OpenSSH alias already defines `User`; required when `--host` is an IP address. |
333
360
  | `--port <port>` | SSH port. Omit it when an OpenSSH alias already defines `Port`. |
334
361
  | `--password-env <env>` | Read the target machine SSH password from an environment variable. |
335
362
  | `--connect-timeout <seconds>` | SSH connection timeout. Choices: `8`, `15`, `30`, `60`. Default: `15`. |
@@ -347,8 +374,31 @@ Recommendations:
347
374
  - Use `--json` from agents and scripts.
348
375
  - Prefer `--password-env` and `--jumpN-password-env`; avoid putting passwords directly in process arguments.
349
376
  - Omit `--user` and `--port` when `--host` is an OpenSSH alias.
377
+ - IP targets and IP jump hosts must include a user.
350
378
  - For slow two-hop jump paths or password handshakes, use `--connect-timeout 30 --command-timeout 90`.
351
379
 
380
+ ## Autostart Service
381
+
382
+ Yaport can install a user-level service without sudo. macOS uses LaunchAgent, and Linux uses a systemd user service. The installed service starts with `--no-open`, so it does not open a browser automatically after login.
383
+
384
+ ```bash
385
+ yaport service install --port 5173 --json
386
+ yaport service status
387
+ yaport service uninstall
388
+ ```
389
+
390
+ Common options:
391
+
392
+ | Option | Description |
393
+ | --- | --- |
394
+ | `install` | Write and start the user-level service. |
395
+ | `uninstall` | Stop and remove the user-level service. |
396
+ | `status` | Show status through `launchctl` or `systemctl --user`. |
397
+ | `--host <host>` | Service bind host. Default: `127.0.0.1`. |
398
+ | `--port <port>` | Service bind port. Default: `5173`. |
399
+ | `--data-file <path>` | Machine config file used by the service. Without this, Yaport still checks `./.yaport`, then `~/.yaport`. |
400
+ | `--json` | Print deterministic JSON for `install` / `uninstall`. |
401
+
352
402
  ## Port Inventory
353
403
 
354
404
  Yaport logs in through local OpenSSH and reads:
@@ -386,6 +436,10 @@ Rules:
386
436
 
387
437
  - The add-machine form is collapsed by default.
388
438
  - The add-machine form lets you choose connection timeout and command timeout.
439
+ - The connection name is optional. When omitted, Yaport uses the host information. IP hosts require a user; OpenSSH aliases can omit it.
440
+ - The right side of each machine card opens the local Terminal and runs `ssh` with that machine's connection settings. Stored passwords are not written into the command line.
441
+ - The left machine list stays fixed while the page scrolls and can collapse into a narrow rail.
442
+ - Right-click a machine card to edit or delete that connection config. During edits, leaving password fields blank keeps the existing password.
389
443
  - TCP and UDP metric cards include switches, both enabled by default, to filter visible records.
390
444
  - The listening-port metric card can group the table by port.
391
445
  - The process metric card can group the table by process.
@@ -0,0 +1 @@
1
+ :root{--ink:#12211d;--ink-soft:#42514b;--paper:#f7f5ec;--panel:#ebe7d7;--line:#c8cfbf;--rail:#d8e8dc;--teal:#128fa1;--teal-deep:#0b6370;--amber:#c48925;--brick:#a9473b;--white:#fffdf6;--shadow:0 14px 36px #19231e1f;color:var(--ink);background:var(--paper);font-synthesis:none;text-rendering:optimizelegibility;font-family:Aptos,Inter,Segoe UI,system-ui,sans-serif}*{box-sizing:border-box}body{background:linear-gradient(90deg, #12211d08 1px, transparent 1px), linear-gradient(#12211d08 1px, transparent 1px), var(--paper);background-size:24px 24px;min-width:320px;min-height:100vh;margin:0;font-size:14px;line-height:1.35}button,input,select{font:inherit}button{cursor:pointer}button:disabled{cursor:not-allowed}button:focus-visible,input:focus-visible,select:focus-visible{outline-offset:2px;outline:3px solid #128fa159}.app-shell{grid-template-columns:minmax(280px,326px) 1fr;min-height:100vh;display:grid}.app-shell.sidebar-collapsed{grid-template-columns:64px 1fr}.machine-pane{background:var(--ink);height:100vh;min-height:0;color:var(--white);box-shadow:var(--shadow);flex-direction:column;gap:16px;padding:22px;display:flex;position:sticky;top:0;overflow:hidden}.machine-pane.collapsed{align-items:center;padding:16px 12px}.machine-pane-top{justify-content:space-between;align-items:center;gap:10px;display:flex}.machine-pane.collapsed .machine-pane-top{flex-direction:column}.machine-pane-content{flex-direction:column;flex:auto;gap:16px;min-height:0;padding-right:2px;display:flex;overflow:auto}.brand-block{align-items:center;gap:12px;display:flex}.brand-mark{background:#fffdf614;border:1px solid #fffdf642;border-radius:6px;flex:none;place-items:center;width:40px;height:40px;display:grid}.pane-collapse-button{color:#fffdf6c7;background:#fffdf612;border:1px solid #fffdf629;border-radius:6px;flex:none;place-items:center;width:32px;height:32px;display:inline-grid}.pane-collapse-button:hover{border-color:var(--amber);color:var(--white);background:#c4892529}.eyebrow,.section-label,.form-title,.metric-label{color:var(--ink-soft);letter-spacing:0;text-transform:uppercase;margin:0;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px;font-weight:700}.machine-pane .eyebrow,.machine-pane .section-label,.machine-pane .form-title{color:#fffdf69e}h1,h2{letter-spacing:0;margin:0;font-family:Arial Narrow,Aptos Display,Segoe UI,sans-serif;font-weight:800}h1{font-size:31px;line-height:1}h2{color:var(--ink);font-size:38px;line-height:1.02}.machine-form{gap:12px;padding-top:2px;display:grid}.add-machine-toggle{width:100%;min-height:40px;color:var(--white);text-align:left;background:#fffdf612;border:1px solid #fffdf629;border-radius:6px;justify-content:space-between;align-items:center;gap:10px;padding:0 12px;display:flex}.add-machine-toggle>span:first-child{align-items:center;gap:8px;font-weight:800;display:inline-flex}.add-machine-toggle:hover,.add-machine-toggle[aria-expanded=true]{border-color:var(--amber);background:#c4892529}.toggle-hint{color:#fffdf694;letter-spacing:0;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px;font-weight:800}.jump-hosts{gap:8px;padding-top:2px;display:grid}.jump-host-group{background:#fffdf60d;border:1px solid #fffdf624;border-radius:6px;gap:8px;margin:0;padding:10px;display:grid}.jump-host-group legend{color:#fffdf69e;letter-spacing:0;padding:0 5px;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px;font-weight:800}.form-title,.section-label,.rail-header{align-items:center;gap:7px;display:flex}label{color:#fffdf6b8;gap:6px;font-size:12px;font-weight:700;display:grid}input,select{width:100%;min-height:38px;color:var(--white);background:#fffdf617;border:1px solid #fffdf62e;border-radius:6px;padding:0 10px}select{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}input::placeholder{color:#fffdf657}.form-grid{grid-template-columns:1fr 96px;gap:10px;display:grid}.primary-button,.secondary-button{border:0;border-radius:6px;justify-content:center;align-items:center;gap:7px;min-height:38px;font-size:13px;font-weight:800;display:inline-flex}.primary-button{background:var(--amber);color:#21180a}.secondary-button{border:1px solid var(--line);background:var(--white);color:var(--ink);padding:0 12px}.primary-button:disabled,.secondary-button:disabled{opacity:.55}.machine-list{gap:8px;min-height:0;display:grid}.machine-item{width:100%;color:var(--white);background:#fffdf612;border:1px solid #fffdf624;border-radius:6px;grid-template-columns:minmax(0,1fr) 34px;align-items:center;display:grid;overflow:hidden}.machine-item:hover,.machine-item.active{border-color:var(--amber);background:#c489252e}.machine-select-button{min-width:0;color:inherit;text-align:left;background:0 0;border:0;gap:4px;padding:11px 10px 11px 12px;display:grid}.machine-terminal-button{color:#fffdf6b3;background:#fffdf614;border:1px solid #fffdf624;border-radius:6px;place-items:center;width:28px;height:28px;display:inline-grid}.machine-terminal-button:hover{border-color:var(--teal);color:var(--white);background:#128fa138}.machine-name{font-size:14px;font-weight:800}.machine-address,.target-line,.quiet-state{color:#fffdf694;letter-spacing:0;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px}.machine-address,.target-line{overflow-wrap:anywhere}.quiet-state{border:1px dashed #fffdf633;border-radius:6px;padding:12px}.machine-context-menu{z-index:30;border:1px solid var(--line);background:var(--white);border-radius:6px;min-width:132px;padding:4px;position:fixed;box-shadow:0 12px 28px #12211d2e}.machine-context-menu button{width:100%;min-height:30px;color:var(--ink);text-align:left;background:0 0;border:0;border-radius:4px;align-items:center;gap:7px;padding:0 9px;font-size:12px;font-weight:800;display:flex}.machine-context-menu button:hover{background:#12211d14}.machine-context-menu button.danger{color:var(--brick)}.machine-context-menu button.danger:hover{background:#a9473b1c}.inventory-pane{flex-direction:column;gap:14px;min-width:0;padding:22px 30px;display:flex}.inventory-header{justify-content:space-between;align-items:flex-start;gap:14px;display:flex}.inventory-header .target-line{color:var(--ink-soft);margin:6px 0 0}.error-strip{border-left:5px solid var(--brick);color:#5d211b;white-space:pre-wrap;background:#f2d8cf;padding:10px 12px;font-size:13px;font-weight:700}.signal-rail{border:1px solid var(--line);background:var(--rail);gap:10px;padding:12px;display:grid}.rail-header{color:var(--ink);letter-spacing:0;text-transform:uppercase;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px;font-weight:800}.port-grid{grid-template-columns:repeat(auto-fill,minmax(10px,1fr));align-items:stretch;gap:4px;min-height:38px;display:grid}.port-cell{background:var(--teal);border:1px solid #12211d38;min-height:13px;display:block}.port-cell.udp{background:var(--amber)}.empty-matrix{color:var(--ink-soft);grid-column:1/-1;align-self:center;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px}.summary-grid{grid-template-columns:repeat(5,minmax(106px,1fr));gap:10px;display:grid}.metric{border:1px solid var(--line);background:var(--white);gap:6px;min-height:72px;padding:10px 12px;display:grid}.metric-topline{justify-content:space-between;align-items:flex-start;gap:8px;min-width:0;display:flex}.metric.cyan{border-bottom:4px solid var(--teal)}.metric.amber{border-bottom:4px solid var(--amber)}.metric-label{align-items:center;gap:6px;min-width:0;display:inline-flex}.metric strong{align-self:end;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:28px;line-height:1}.metric-switch{border:1px solid var(--line);background:#e7e4d8;border-radius:999px;flex:none;align-items:center;width:36px;height:20px;padding:2px;display:inline-flex}.metric-switch span{background:var(--white);border-radius:999px;width:14px;height:14px;transition:transform .14s;display:block;box-shadow:0 1px 3px #12211d38}.metric-switch[aria-checked=true]{border-color:var(--teal);background:#128fa142}.metric-switch[aria-checked=true] span{background:var(--teal-deep);transform:translate(16px)}.table-wrap{border:1px solid var(--line);background:var(--white);overflow:auto}table{border-collapse:collapse;width:100%;min-width:920px}th,td{border-bottom:1px solid var(--line);text-align:left;padding:10px 12px}th{color:var(--ink-soft);letter-spacing:0;text-transform:uppercase;background:#e4eadf;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px}td{font-size:13px}tbody tr:hover{background:#f1f4eb}.group-row td{border-bottom:1px solid var(--line);background:#edf0e7;padding:7px 12px}.group-row-label{color:var(--ink-soft);justify-content:space-between;align-items:center;gap:12px;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px;font-weight:800;display:flex}.group-row-label span:first-child{overflow-wrap:anywhere;min-width:0}.group-row-label span:last-child{flex:none}.protocol-pill{min-width:44px;color:var(--teal-deep);background:#128fa129;border-radius:999px;justify-content:center;padding:3px 7px;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px;font-weight:800;display:inline-flex}.protocol-pill.udp{color:#734a08;background:#c4892533}.mono,.port-number{font-variant-numeric:tabular-nums;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace}.port-number{font-weight:800}.process-cell{overflow-wrap:anywhere;min-width:180px;max-width:300px;line-height:1.35}.route-list{flex-wrap:wrap;gap:6px;min-width:190px;display:flex}.route-chip{max-width:280px;color:var(--teal-deep);letter-spacing:0;background:linear-gradient(90deg,#128fa124,#d8e8dc70);border:1px solid #128fa147;border-radius:999px;align-items:center;gap:6px;padding:5px 8px;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px;font-weight:800;text-decoration:none;display:inline-flex}.route-source-badge{width:17px;height:17px;color:var(--white);flex:none;justify-content:center;align-items:center;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:10px;font-weight:900;line-height:1;display:inline-flex}.route-source-badge.nginx{clip-path:polygon(25% 4%,75% 4%,100% 50%,75% 96%,25% 96%,0 50%);background:#05924d;box-shadow:inset 0 -1px #12211d3d}.route-chip-url{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.route-chip:hover{border-color:var(--teal);background:#128fa12e}.route-empty{color:#42514b8a;font-family:SFMono-Regular,Cascadia Mono,Roboto Mono,monospace;font-size:11px}.table-empty{height:140px;color:var(--ink-soft);text-align:center;position:sticky;left:0}.spin{animation:.9s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;animation-duration:1ms!important;animation-iteration-count:1!important}}@media (width<=980px){.app-shell,.app-shell.sidebar-collapsed{grid-template-columns:1fr}.machine-pane{height:auto;min-height:auto;padding:20px;position:static}.machine-pane.collapsed{align-items:stretch}.machine-pane.collapsed .machine-pane-top{flex-direction:row}h1{font-size:30px}h2{font-size:34px}.summary-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.metric.wide{grid-column:span 2}}@media (width<=560px){.machine-pane,.inventory-pane{padding:16px}h1{font-size:28px}h2{font-size:30px}.inventory-header{flex-direction:column;align-items:stretch}.form-grid,.summary-grid{grid-template-columns:1fr}.metric.wide{grid-column:auto}.secondary-button{width:100%}}