Skip to content

chore: sync upstream main (interactive PTY terminal, dev-server detection, v1.2.28) - #38

Merged
Demonic1594 merged 15 commits into
mainfrom
sync-upstream-oct28
Oct 29, 2026
Merged

Demonic1594 merged 15 commits into
mainfrom
sync-upstream-oct28

Conversation

@Demonic1594

Copy link
Copy Markdown
Owner

Syncs upstream yuga-hashimoto/and-code main (f0c0806, through their v1.2.28 + docs) into the fork.

What comes in

Conflict resolutions

  • TerminalScreen.kt: upstream's full rewrite taken; its FontFamily.Monospace swapped to the fork's CodeFontFamily (the fork's only prior change there).
  • Routes.kt / WorkspaceNavGraph.kt: both sides kept — upstream's terminalRoute(directory) (opens the terminal in that workspace) plus the fork's Git-panel route.
  • Version collision: both trees were 1.2.28 (67). The fork bumps to 1.2.29 (68) (.release-version, build.gradle.kts, ReleaseMetadataTest), upstream's 1.2.28 notes stay in changelogs/67.txt, and the fork's unpublished notes roll forward into changelogs/68.txt with the PTY terminal line added (the fork never shipped 67, so 66→68 upgraders would otherwise miss it).

Note for the re-author of #1: it follows this PR and rebases on top.

detekt + spotlessCheck pass locally; the test suite (including upstream's nine new terminal test files) runs in CI.

Pre-PR review

判定: APPROVE

ブロッカー

  • なし

提案(非ブロッキング)

  • fastlane/metadata/android/{en-US,ja-JP}/changelogs/68.txt — このリリース(68)で実際に ships される最大の変更(upstream の対話型PTYターミナル)への言及が68.txtに無い。fork の前回公開版は67を出していないため、ユーザーは66→68で更新し、67.txt(upstream の 1.2.28 ノート)は二度と表示されない。結果、「ターミナルが本物のシェルになった」ことが変更履歴から読めない。en/ja 両方に1行(例: 「- New: the embedded terminal is now a real interactive shell / 内蔵ターミナルが本物の対話シェルになりました」)を足すことを検討してはどうか。構成自体(67=upstreamノート、68=forkノート繰り越し)は整合しているので、方針のままであればこのままで問題ない。
  • docs/RELEASE.md — fdroiddata スナップショットが upstream の 1.2.28/67 のままで、fork の 1.2.29/68 と食い違う。upstream ドキュメントの鏡像という意図なら問題ないが、fork 利用者が混乱しないよう一行注記があってもよい。

チェック済み項目

  • スコープ: git diff origin/main...HEAD(46ファイル、+4246/−213)の全ハンクを読了。ブランチは origin/main(eaff7716)を起点とし、その上に upstream f0c0806 のマージのみ(worktree rule 準拠)。
  • 競合解決(1) TerminalScreen.kt: 3方向で検証 — fork が前回sync以降に加えた変更は CodeFontFamily スワップ(3利用箇所+import)のみと特定(git diff 414c7806 eaff7716)。HEAD では upstream の PTY リライト全体を取り込み、FontFamily.Monospace は0件(rg で確認)、CodeFontFamily が OutputTextStyle / TerminalKeyButton / TerminalInput の3箇所+import(com.yugahashimoto ブロック内)で復元されている。fork 側の落下物なし。
  • 競合解決(2) Routes.kt: upstream の TERMINAL_ARG_DIRECTORY/TERMINAL_ROUTE_PATTERN/terminalRoute() と fork の ROUTE_GIT(+fork 追加ルート群)が両方存在。encodeRouteArg/decodeRouteArg(Base64 URL)は既存で、RoutesTest が新規に往復をテスト。
  • 競合解決(3) WorkspaceNavGraph.kt: onOpenTerminal = terminalRoute(workspace.path)(upstream)と onOpenSourceControl = ROUTE_GIT(fork、GitScreen 配線ごと)が両方生存。terminal composable は TERMINAL_ROUTE_PATTERN に切り替わり、VM は (app.terminalSessions, app.runtimeWork, directory) で構築 — 後述の AndCodeApplication の terminalSessions 初期化と型一致。AndCodeApplication.kt の LocalTerminalSessionFactory(runtimeDirectory, installer::installedRuntime, accessCoordinator) はすべてスコープ内の既存依存(行304-346で順序も確認)。
  • 競合解決(4) バージョン衝突: .release-version=v1.2.29、versionCode=68/versionName="1.2.29"、ReleaseMetadataTest.assertEquals(68,...) が一致。changelogs は en-US/ja-JP とも67.txt=upstream 1.2.28ノート、68.txt=fork繰り越し+fix(artifacts): open artifacts written outside the workspace #37 artifact修正行で内容が一致。
  • ProcessTree リネーム: killAntigravityProcessTree への参照はコードベ全体で0件。AntigravityAbortTracker/AuthCoordinator/Runtime のKDocリンク・呼び出し3箇所が killProcessTree に更新。
  • i18n: 8ロケールすべてで同一の15キー追加/2キー削除(terminal_banner/terminal_exit_code はコードからも参照なし)。en=1621キー(うち translatable="false" 10)、全ロケール=1611キーで、i18n-check.yml の required キー過不足なし。ハードコードされたユーザー可視文字列なし(Esc/Tab/^C/^D はキーキャップとして意図的かつドキュメント化済み、矢印には contentDescription 付き)。
  • 新規 upstream コードのレビュー: AnsiTerminalEmulator(信頼できない入力へのクランプ: MAX_PARAMETER 9,999 / MAX_CSI 128 / MAX_STYLES 4,096 / スクロールバック10,000行)、ProcessTerminalSession(入力専用スレッド、close時の destroyForcibly 保証、IOException 欠落なし)、LocalTerminalLauncher(cd は絶対パスのみ+shellQuote、失敗時 /root フォールバック→インジェクション不可)、DevServerDetector/Tracker/LoopbackDevServerProbe(ホスト名は再利用せずポートのみ、localhost 固定でブラウザを開く)— セキュリティ・リソース管理とも問題なし。
  • 並行性/ライフサイクル: ターミナル VM は back stack entry スコープ(pop で onCleared→close→killProcessTree+lease解放+probeキャンセル)、runtime lease は shell 生存期間と正確に一致、adopt は close と open の競合を処理、publish は clear と出力のレースを単一ロックで防止。probe の無限ループは probeJob.cancel で終了し viewModelScope 内 — リークなし。
  • DI 整合: TerminalViewModel は Koin に登録されておらず(変更前も同様)、ViewModelFactory 経由の単一構築パスのみ。DI 同期ルールに抵触なし。
  • テスト: upstream の新規テスト9ファイル(TerminalViewModelTest 802行等)は upstream とバイト同一(git diff f0c08063 HEAD で ReleaseMetadataTest の68更新以外のテスト変更なし)。fork 側テストの削除・改変なし。unitTests.isReturnDefaultValues = true を確認済みで Log 呼び出しは JVM テストでも問題なし。
  • その他: コンフリクトマーカー残留なし(全diff走査)、シークレット類の追加なし、detekt/spotlessCheck はローカル通過報告どおり(CI が再検証)。

Post-approval: 提案1(68.txt への対話型ターミナル1行、en/ja)をコミット 5772cbd でそのまま採用した。提案2(RELEASE.md 注記)は不採用 — あの文書は upstream の鏡像であり次回同期で置き換わるため。


Generated by Claude Code

yuga-hashimoto and others added 15 commits October 27, 2026 23:11
The terminal ran each line as its own `sh -lc`, killed it after 30 s and
kept only the last 4,000 characters of output, so a dev server, a log
follower or an interactive prompt could not be used. It also always
started in /root, whatever workspace it was opened from (yuga-hashimoto#329).

It now keeps one shell alive for as long as the screen is open:

- PTY: the sandbox's `script -qefc` allocates the pty (the same trick the
  Claude Code and Antigravity sign-in flows use), so the shell has a real
  controlling terminal - line editing, password prompts and Ctrl+C work.
  `stty` sets the size the screen measured, since `script` otherwise
  reports 0x0 over pipes.
- Streaming: output is read as it is produced and interpreted by
  AnsiTerminalEmulator, a line-oriented VT100/xterm subset (SGR colours,
  \r rewrites, line editing, clear, wrapping, 10,000 lines of
  scrollback). Unknown sequences are dropped instead of printed.
- Input: the typed line is sent with Enter; Esc, Tab, Ctrl+C, Ctrl+D and
  the arrow keys have their own key row.
- Starts in the workspace it was opened from, falling back to /root; the
  workspace folder is bound at /workspace.
- A runtime work lease is held while the shell lives so the device does
  not suspend under a build. The runtime access lock is held only while
  the process is spawned, so a long-lived shell cannot stall an update.
- The shell and anything it started end when the screen is left.

Full-screen programs (vim, htop), resizing an open shell and the
multi-terminal tab placeholder are not part of this change.

Closes yuga-hashimoto#329

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ut parser

Pre-PR review of the interactive terminal found two blockers and several
smaller problems.

Blockers:
- Closing the screen only called destroyForcibly() on proot. A proot guest
  outlives that (the repo's own on-device finding, ProcessTree.kt): the
  `script`/`bash` and any dev server stayed running while the runtime lease
  was released. close() now walks /proc and kills the tree end first
  (killProcessTree, renamed from killAntigravityProcessTree since it is not
  Antigravity specific) on a background thread, then destroys the root even
  if the walk failed.
- A CSI parameter such as ESC[2147483647@ overflowed Int arithmetic in the
  emulator and threw out of the collector, crashing the app from any
  `cat` of a hostile file. Numeric parameters are clamped to 9,999, a
  feed() failure is logged instead of propagated, CAN/SUB abort an escape
  sequence in progress, and the interned style table is bounded.

Also fixed:
- A shell that finished opening after the screen was closed was never
  owned by anyone and kept running; it is now ended on arrival.
- The emulator was sized from the raw measured width while the pty was
  clamped to 20..240 columns; both use the same bounds now.
- Tab and the arrow keys act on the shell's line editor, so text still in
  the input field is sent first.
- Output following stopped when the soft keyboard shrank the viewport (it
  judged "at the end" by canScrollForward at that moment). It now follows
  until the reader drags away and resumes when they drag back to the end.
- State publishes happen under the emulator lock so a late snapshot cannot
  bring back text that `clear` removed.
- Removed the now unused terminal_banner / terminal_exit_code strings from
  all locales; documented the remaining limits (CJK width, OpenCode
  restarts ending the shell, the lease being held while a shell is open).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- close() no longer walks the process tree of a shell that already ended:
  its pid has been reaped and may belong to something else.
- The output safety net now also covers snapshot(), and a CSI sequence
  that carries a negative parameter is ignored whole (ESC[-1m used to be
  read as ESC[0m and reset the colours).
- Typing, pressing a key and clearing bring the view back to the end of
  the output after the reader scrolled away. Scrolling away by hand still
  stops the following; mouse-wheel and TalkBack scrolling do not, which
  is written down in the docs rather than guessed at.
- Documented that Tab and the arrow keys send the pending input first.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ub-issues-review-08495d

feat(terminal): interactive PTY shell in the embedded terminal
Ships the work merged since v1.2.27: the interactive PTY terminal, local
workspace file editing, clipboard image paste, Codex per-turn settings, the
OpenCode V2 server API, and the privacy and stability fixes listed in the
changelog.
…in the browser

A dev server started in the embedded terminal had no status and no way to
reach it: the user had to remember the port and type the address into the
in-app browser by hand.

Read the ports servers announce on screen ("Local: http://localhost:5173/",
"Serving HTTP on 0.0.0.0 port 8000", "listening on :3000"), probe them with a
TCP connect to the loopback addresses, and show the ones that answer as chips
above the output. A chip opens http://localhost:PORT/ in the in-app browser;
a server that stops answering is shown as stopped, and forgotten after about
half a minute. A port is only shown once it has answered, because the echoed
command line ("curl http://localhost:8080") names ports nothing listens on.

Only the port number is taken from the output, never the host, so a program's
output cannot send the in-app browser anywhere but this device. Probing starts
when the output first names a server and ends with the shell, so a plain shell
costs nothing. Only screen lines that are new since the last look are
searched, so an old address still in the scrollback cannot revive a server
that was forgotten.

Refs yuga-hashimoto#402

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…real servers

Apply the pre-PR review's suggestions.

The terminal stores a line wider than the screen as several rows, so an
address crossing the wrap point was whole in none of them: python's
"Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/)" was missed on a
narrow phone, and a cut-off "80" could be read as a port. Rows that fill the
width are now joined to the next one before searching, and the trailing digits
of a line that may still be growing are held back until it ends.

When the slots are full, a new port now pushes out the oldest candidate that
never answered (echoed commands, cut-off fragments) instead of being dropped;
a port that has answered is never pushed out.

Also: a failure while searching for servers no longer takes the output stream
down; the probe loop is created inside the lock that decides to start it, so a
close or an exit that comes in between cancels it before it runs; the chip's
touch target is 48dp and its port number is always left-to-right; and
onOpenServer is a required parameter.

Refs yuga-hashimoto#402

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eir length

The pre-PR re-review noted that a row filling the width is joined to the next
one whether it wrapped or merely ended in the last column. Say so in the
KDoc and in LOCAL_RUNTIME.md, with what it costs. Comments and docs only.

Refs yuga-hashimoto#402

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lable-tasks-review-d887ba

feat(terminal): list the dev servers started in the terminal and open them in the in-app browser
The MR branch now pins v1.2.28 (versionCode 67, commit 4a662c4) and the
NonFreeNet reason also names the Codex binary download and the ChatGPT/OpenAI
sign-in. The snapshot is byte-identical to the recipe that passed the fork
pipeline, including the reproducible-build comparison against the published
-fdroid-release.apk.
…elease-md-recipe-1.2.28

docs(release): sync the fdroiddata recipe snapshot to v1.2.28
# Conflicts:
#	app/src/main/java/com/yugahashimoto/andcode/feature/workspace/TerminalScreen.kt
#	app/src/main/java/com/yugahashimoto/andcode/ui/navigation/Routes.kt
#	app/src/main/java/com/yugahashimoto/andcode/ui/navigation/WorkspaceNavGraph.kt
#	fastlane/metadata/android/en-US/changelogs/67.txt
#	fastlane/metadata/android/ja-JP/changelogs/67.txt
@Demonic1594
Demonic1594 merged commit c412032 into main Oct 29, 2026
6 checks passed
@Demonic1594
Demonic1594 deleted the sync-upstream-oct28 branch October 29, 2026 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants