Skip to content

fix(serial): auto-login on serial consoles with saved host credentials - #3418

Open
netcatty-bot wants to merge 10 commits into
mainfrom
ai/issue-3417-35101260182
Open

netcatty-bot wants to merge 10 commits into
mainfrom
ai/issue-3417-35101260182

Conversation

@netcatty-bot

Copy link
Copy Markdown
Collaborator

Summary

  • Serial host settings now include Username and Password fields (with a show/hide toggle), saved on the existing host record so the password is encrypted and synced like every other saved credential.
  • The serial session bridge now reuses the proven Telnet login-assist detector: when the connected device prints a Login: / Password: style prompt, Netcatty automatically sends the saved username and password over the port.
  • Any interactive keystroke takes over and cancels the auto-login, so manual logins still work; automated bridge writes never cancel it.
  • A pending startup command is deferred until auto-login finishes (or the auto-login window expires) so it is not typed at a login prompt. Serial hosts without saved credentials behave exactly as before.

Why

SSH sessions can auto-fill saved credentials, but serial consoles (very common in embedded debugging) could not — users had to keep snippets/scripts around to type credentials at a login prompt (#3417). The Telnet bridge already has a robust login-prompt detector (telnetAutoLogin), so serial now reuses it instead of adding a second implementation.

Changes

  • components/SerialHostDetailsPanel.tsx: username/password inputs (eye toggle) on the serial host editor, stored in the host's existing username/password fields.
  • electron/bridges/terminalBridge.cjs: startSerialSession wires telnetAutoLogin to the decoded serial data stream when credentials were saved; serial sessions now also cancel auto-login on interactive user input (same as Telnet). Completion/cancel events are emitted on the existing session-scoped auto-login channels.
  • components/terminal/runtime/createTerminalSessionStarters.ts: startSerial passes saved credentials to the bridge and, when both credentials and a startup command exist, schedules the startup command after auto-login completes (with a fallback timer for devices that never prompt).
  • types/global/netcatty-bridge-session.d.ts: serial start options accept username/password.
  • i18n: new serial credential labels/hint for en, es, ru, zh-CN, zh-TW.

Testing

  • node --test --import tsx components/terminal/runtime/createTerminalSessionStarters.serial.test.ts (new: credential pass-through, placeholder skip, startup-command deferral until auto-login completes, unchanged behavior without credentials)
  • node --test electron/bridges/terminalBridge.serialAutoLogin.test.cjs plus all terminalBridge* suites (173 pass), telnet auto-login suites, and the full components/terminal/runtime dir (974 pass)
  • npx tsc --noEmit error count unchanged vs. baseline (850 before/after); eslint clean on changed files
  • Manual check recommended: connect to a saved serial host with credentials on a device that prompts login:/Password:

Fixes #3417

🤖 Generated with Claude Code

Automation

  • Automated implement pass
  • Review gate: @codex review (own/bot PRs only)
  • Draft until Codex reports clean findings

@netcatty-bot netcatty-bot added automation:bot-pr PR created or owned by Cursor automation automation:codex-loop Own/bot PR waiting on Codex review↔fix loop triage Touched by Cursor automation labels Sep 16, 2026
@binaricat

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T19:21:20.183274Z 58fdb45 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 97a8018821

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1997 to +2000
const serialUsername = (ctx.host.username ?? "").trim();
const serialPassword = sanitizeCredentialValue(ctx.host.password);
const hasSerialAutoLoginCredentials = Boolean(
serialUsername || serialPassword !== undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Limit auto-login to explicit serial credentials

When a serial host belongs to a group with default SSH credentials, ctx.host already contains those inherited values: TerminalLayer.tsx:1025-1032 resolves the effective host, while domain/groupConfig.ts:146-152,210-226 applies username and password without excluding serial hosts. Consequently, even though the serial editor's credential fields are blank, connecting to that host now transmits the group's SSH password to any device that prints a login prompt. Read credentials from the raw host or introduce serial-specific credentials so group SSH secrets are not sent implicitly.

Useful? React with 👍 / 👎.

Comment on lines +2036 to +2041
autoLoginFallbackTimer = setTimeout(() => {
autoLoginFallbackTimer = undefined;
if (!disposeAutoLoginComplete) return;
if ((ctx.bootEpochRef?.current ?? 0) !== bootEpoch) return;
scheduleStartupAfterAutoLogin();
}, SERIAL_AUTO_LOGIN_FALLBACK_MS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Start the fallback timer after the serial port opens

The 65-second fallback begins before awaiting startSerialSession, but the main-process 60-second auto-login window does not begin until the port's open callback creates the detector. If opening a busy or slow serial device takes a substantial part of that interval, the startup command can run while auto-login is still active; if opening takes longer than the fallback plus the startup delay, scheduleStartupCommand sees no attached sessionRef, drops the command, and marks it as already run. Arm this timer only after the session has opened and attached.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f861952f3f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread electron/bridges/terminalBridge.cjs Outdated
Comment on lines 1537 to 1538
if ((session.type === 'telnet-native' || session.type === 'serial') && !payload.automated) {
session.autoLogin?.handleUserInput();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude terminal replies from auto-login cancellation

When a serial device emits an ANSI query before its login prompt, xterm sends the corresponding report through this path without automated: true. Although the surrounding input pipeline explicitly recognizes these values with isTerminalReportSequence, this new serial branch treats the report as interactive input, disables auto-login, and emits the cancellation event that also drops a pending startup command. Apply the same terminal-report exclusion here so only genuine user input takes over.

Useful? React with 👍 / 👎.

Comment on lines +1997 to +2001
const serialUsername = (ctx.host.username ?? "").trim();
const serialPassword = sanitizeCredentialValue(ctx.host.password);
const hasSerialAutoLoginCredentials = Boolean(
serialUsername || serialPassword !== undefined,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unreadable serial passwords before connecting

When a synced password cannot be decrypted, sanitizeCredentialValue() returns undefined, but a saved username still makes hasSerialAutoLoginCredentials true. The detector then submits the username without its password, and a configured startup command waits for 65 seconds before potentially being sent into the password prompt. Detect the encrypted placeholder as the Telnet path does and ask the user to re-enter the credential instead of starting a partial auto-login.

Useful? React with 👍 / 👎.

Comment thread components/SerialHostDetailsPanel.tsx Outdated
hostname: selectedPort,
port: baudRate,
username: username.trim() || undefined,
password: password || undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve intentionally blank serial passwords

For serial accounts whose valid password is empty, saving this form converts "" to undefined. The reused auto-login detector deliberately distinguishes an absent password from a present empty string and can submit the required blank line, so after this conversion it stops responding at the password prompt. Preserve the empty string or provide an explicit control that distinguishes disabling password handling from saving a blank password.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 91a69e2e08

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2034 to +2035
clearAutoLoginFallbackTimer();
scheduleStartupAfterAutoLogin();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defer completed-login startup until the session attaches

When a serial device completes its login exchange before the startSerialSession IPC promise resolves, this callback schedules the startup command while ctx.sessionRef.current is still unset. With a configured startup delay of zero or another sufficiently short value, scheduleStartupCommand marks the command as run, then its timer fails the current-session check and drops it permanently. Record the completion here and schedule only after tryAttachSessionToTerminal succeeds.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1f05be440

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1537 to 1542
if (
(session.type === 'telnet-native' || session.type === 'serial')
&& !payload.automated
&& !isTerminalReportSequence(data)
) {
session.autoLogin?.handleUserInput();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cancel auto-login when line-mode input is buffered

When serialConfig.lineMode is enabled, createXTermRuntime.ts:1325-1341 buffers printable keystrokes locally and does not call writeToSession until Enter, so this main-process hook never sees the user's initial interactive input. A user can begin typing to take control while the detector remains active and sends saved credentials—or later runs the pending startup command—before the buffered line is submitted. Signal cancellation when line-mode buffering receives its first user input rather than only when data reaches this write path.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 283d15c4d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1340 to +1341
if (!isSessionWrite) {
ctx.terminalBackend.notifyUserInput?.(id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude terminal reports from line-mode cancellation

When serial line mode is enabled and the device sends an ANSI query such as DA1 or CPR before its login prompt, xterm emits the automatic reply through onData, and this branch calls notifyUserInput because the reply is not CR, LF, or Ctrl+C. That disables auto-login and cancels the pending startup command even though the user never typed. Fresh evidence in this revision is that the dedicated line-mode notification bypasses the isTerminalReportSequence filter added to the normal write path; apply equivalent report classification before notifying.

Useful? React with 👍 / 👎.

Comment thread components/SerialHostDetailsPanel.tsx Outdated
// distinguishes an absent password from a present empty string and
// submits the required blank line at a Password prompt. Only clear the
// credential when it was previously set; preserve a saved blank password.
password: password || (initialData.password === '' ? '' : undefined),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Let users select an empty serial password

For a device whose valid password is an empty line, a user cannot configure that credential from scratch or change a non-empty password to blank: this expression stores undefined unless initialData.password was already '', so the detector will not answer the password prompt. The new conditional preserves imported/pre-existing blank passwords but does not resolve the editable form ambiguity; add an explicit enabled/blank state or otherwise distinguish clearing auto-login from saving an empty password.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 07fa516738

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread components/SerialHostDetailsPanel.tsx Outdated
// submits the required blank line at a Password prompt. An explicit edit
// saves exactly what the user typed (including a blank line); an
// untouched field preserves the stored credential as-is.
password: passwordChanged ? password : (initialData.password ?? undefined),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow users to remove a saved serial password

After a serial host has a saved password, clearing this field sets passwordChanged and persists "", which the auto-login detector deliberately treats as a present blank password (typeof options.password === "string"). Even if the username is also cleared, the host therefore continues answering password prompts automatically, with no control in this form that can restore password to undefined; add an explicit auto-login/password-enabled state or another way to remove the credential.

Useful? React with 👍 / 👎.

Comment on lines +2115 to +2119
autoLoginFallbackTimer = setTimeout(() => {
autoLoginFallbackTimer = undefined;
if (!disposeAutoLoginComplete) return;
if ((ctx.bootEpochRef?.current ?? 0) !== bootEpoch) return;
scheduleStartupAfterAutoLogin();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not run the fallback after a partial login exchange

When only a username is configured and the device proceeds from Login: to Password:, the detector submits the username but cannot answer the password prompt. This unconditional fallback then sends the configured startup command after 65 seconds, causing that command to be consumed as the password. Restrict the quiet-device fallback to cases where no login interaction occurred, or receive an explicit expired/incomplete result from the detector instead of scheduling blindly.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d8abc3801d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2131 to +2135
autoLoginFallbackTimer = setTimeout(() => {
autoLoginFallbackTimer = undefined;
if (!disposeAutoLoginComplete) return;
if ((ctx.bootEpochRef?.current ?? 0) !== bootEpoch) return;
scheduleStartupAfterAutoLogin();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Suppress fallback after a rejected login

For a serial device that rejects the saved credentials, reprints Login: or Password: before the 60-second window expires, and then becomes idle, this timer still schedules the startup command at 65 seconds, so that command is consumed as the next credential. createTelnetAutoLogin reports an incomplete exchange only when handleText is called after expiry; it does not cancel on the earlier repeated prompt, and no later bytes means no cancellation event. Fresh evidence beyond the previously reported partial-credential case is the full-credential rejection path in telnetAutoLogin.test.cjs, which explicitly remains silent until another post-timeout chunk arrives; have the fallback consult whether any login exchange occurred or make the detector own an actual expiry timer.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 908e6260e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread electron/bridges/terminalBridge.cjs Outdated
Comment on lines +1350 to +1354
liveSession?.webContentsId ?? session.webContentsId,
);
contents?.send(channel, {
sessionId,
bootEpoch: liveSession?.bootEpoch ?? options.bootEpoch,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject auto-login events from displaced serial sessions

When a serial reconnect replaces the same sessionId before the previous detector's 60-second timer fires, the displaced detector remains alive and can still invoke onIncomplete. This callback then reads the replacement session and stamps the stale event with its new bootEpoch, so the renderer accepts the cancellation and drops the new session's pending startup command. Capture the original session/epoch and suppress events once sessions.get(sessionId) !== session, while also cancelling the displaced detector's timer.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac4b43cf25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1548 to 1553
if (
(session.type === 'telnet-native' || session.type === 'serial')
&& !payload.automated
&& !isTerminalReportSequence(data)
) {
session.autoLogin?.handleUserInput();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cancel auto-login before awaiting input interceptors

When a serial session has a plugin input interceptor, interactive input reaches this cancellation only after terminalDataPipeline.interceptInput() resolves in writeToSessionWithInterception. A slow interceptor therefore leaves auto-login armed after the user has typed, allowing a prompt arriving during that wait to trigger saved-credential transmission. Cancel the detector at user-input ingress, before asynchronous interception, while retaining the terminal-report exclusion.

Useful? React with 👍 / 👎.

Comment on lines +2131 to +2135
autoLoginFallbackTimer = setTimeout(() => {
autoLoginFallbackTimer = undefined;
if (!disposeAutoLoginComplete) return;
if ((ctx.bootEpochRef?.current ?? 0) !== bootEpoch) return;
scheduleStartupAfterAutoLogin();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cancel the fallback for login prompts arriving after expiry

On a slow-booting serial device whose first login prompt appears after the detector's 60-second window but before this 65-second timer, the expired detector ignores the prompt and emits no cancellation because it never sent credentials. This fallback then sends the startup command into that login prompt. Keep observing late output for login/password prompts, or require confirmation that the device remained prompt-free before scheduling the quiet-device fallback.

Useful? React with 👍 / 👎.

@binaricat

Copy link
Copy Markdown
Owner

@codex review

Repository owner deleted a comment from binaricat Sep 16, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 58fdb45a1d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@netcatty-bot
netcatty-bot marked this pull request as ready for review September 16, 2026 19:21
@netcatty-bot netcatty-bot added automation:codex-clean Last Codex review reported clean and removed automation:codex-loop Own/bot PR waiting on Codex review↔fix loop labels Sep 16, 2026
@netcatty-bot

Copy link
Copy Markdown
Collaborator Author

Codex reported no major issues. This PR is marked ready for human review/merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation:bot-pr PR created or owned by Cursor automation automation:codex-clean Last Codex review reported clean triage Touched by Cursor automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 串口Serial保存用户名密码并实现自动登录

2 participants