@@ -17,6 +17,20 @@ step() {
1717 print -- " [$1 ] $2 "
1818}
1919
20+ settle () {
21+ local label=" $1 "
22+ if [[ -t 1 ]]; then
23+ local frame
24+ for frame in ' [> ]' ' [=> ]' ' [==> ]' ' [===>]' ; do
25+ printf ' \r %s %s' " $frame " " $label "
26+ sleep 0.025
27+ done
28+ printf ' \r [ ok ] %s\n' " $label "
29+ else
30+ print -- " [ ok ] $label "
31+ fi
32+ }
33+
2034valid_host () {
2135 [[ -n " $1 " && " $1 " != -* && " $1 " != * [$' \n\r\t ' ]* ]]
2236}
@@ -78,6 +92,23 @@ read_host() {
7892 print -r -- " $value "
7993}
8094
95+ read_chat_name () {
96+ local default_name=" $1 "
97+ local value=" "
98+ read " value?Chat name [$default_name ]: "
99+ value=" ${value:- $default_name } "
100+ if [[ -z " $value " || ${# value} -gt 80 || " $value " == * [$' \n\r\t ' ]* || " $value " == * " '" * ]]; then
101+ fail " Use 1-80 characters. Do not use quotes or line breaks."
102+ return 1
103+ fi
104+ print -r -- " $value "
105+ }
106+
107+ sync_remote_agent () {
108+ local host=" $1 "
109+ ssh " $host " ' umask 077; mkdir -p "$HOME/.local/bin"; cat > "$HOME/.local/bin/noturcode-agent"; chmod 700 "$HOME/.local/bin/noturcode-agent"' < " $remote_agent "
110+ }
111+
81112pair_host () {
82113 local host=" ${1:- } "
83114 if [[ -z " $host " ]]; then
@@ -94,11 +125,12 @@ pair_host() {
94125 start_proxy " $local_socket " pair || return 1
95126
96127 step 1 " Copy the free helper to $host "
97- if ! ssh " $host " ' umask 077; mkdir -p "$HOME/.local/bin"; cat > "$HOME/.local/bin/noturcode-agent"; chmod 700 "$HOME/.local/bin/noturcode-agent" ' < " $remote_agent " ; then
128+ if ! sync_remote_agent " $host " ; then
98129 stop_proxy
99130 fail " Could not copy the helper to $host ."
100131 return 1
101132 fi
133+ settle " Helper copied"
102134
103135 step 2 " Pair with one-time code $code "
104136 if ! ssh -tt \
@@ -112,28 +144,51 @@ pair_host() {
112144 return 1
113145 fi
114146 stop_proxy
147+ settle " VPS paired"
115148
116149 step 3 " Ready. Run nc, then choose Open an SSH workspace."
117150}
118151
119152connect_host () {
120153 local host=" ${1:- } "
154+ local mode=" ${2:- shell} "
155+ local chat_name=" ${3:- } "
121156 if [[ -z " $host " ]]; then
122157 host=$( read_host) || return 1
123158 fi
124159 valid_host " $host " || fail " Invalid SSH host or alias." || return 1
160+ if [[ -z " $chat_name " ]]; then
161+ chat_name=$( read_chat_name " $host " ) || return 1
162+ fi
125163 ensure_app || return 1
126164
165+ step 1 " Sync the remote helper on $host "
166+ if ! sync_remote_agent " $host " ; then
167+ fail " Could not update the helper on $host ."
168+ return 1
169+ fi
170+ settle " Helper ready"
171+
127172 local local_socket terminal_id remote_socket remote_command
128173 local_socket=$( " $bridge " socket-path) || return 1
129174 terminal_id=$( " $bridge " terminal-id) || return 1
130175 remote_socket=" /tmp/noturcode-${USER// [^A-Za-z0-9_-]/ _} -$$ .sock"
131- remote_command=" export NOTURCODE_REMOTE_SOCKET='$remote_socket '; export NOTURCODE_TERMINAL_SESSION_ID='$terminal_id '; export NOTURCODE_REMOTE_HOST='$host '; exec \"\$ {SHELL:-/bin/sh}\" -l"
176+ if [[ " $mode " == " resume" ]]; then
177+ remote_command=" export NOTURCODE_REMOTE_SOCKET='$remote_socket '; export NOTURCODE_TERMINAL_SESSION_ID='$terminal_id '; export NOTURCODE_REMOTE_HOST='$host '; export NOTURCODE_SESSION_NAME='$chat_name '; exec \"\$ {SHELL:-/bin/sh}\" -lc 'codex resume --all'"
178+ else
179+ remote_command=" export NOTURCODE_REMOTE_SOCKET='$remote_socket '; export NOTURCODE_TERMINAL_SESSION_ID='$terminal_id '; export NOTURCODE_REMOTE_HOST='$host '; export NOTURCODE_SESSION_NAME='$chat_name '; exec \"\$ {SHELL:-/bin/sh}\" -l"
180+ fi
132181 start_proxy " $local_socket " session || return 1
133182
134- step 1 " Open the encrypted tunnel"
135- step 2 " Start the interactive shell on $host "
136- step 3 " Run Claude, Codex, or Gemini. Use /nc NAME inside the agent."
183+ step 2 " Open the encrypted tunnel"
184+ settle " Tunnel ready"
185+ if [[ " $mode " == " resume" ]]; then
186+ step 3 " Open the Codex chat picker on $host "
187+ step 4 " Select a chat. Noturcode will show it as $chat_name ."
188+ else
189+ step 3 " Start the interactive shell on $host "
190+ step 4 " Run your coding agent. Noturcode will show it as $chat_name ."
191+ fi
137192 ssh -tt \
138193 -o ExitOnForwardFailure=yes \
139194 -o StreamLocalBindUnlink=yes \
@@ -144,6 +199,15 @@ connect_host() {
144199 return " $status "
145200}
146201
202+ resume_codex () {
203+ local host=" ${1:- } "
204+ local chat_name=" ${2:- } "
205+ if [[ -z " $host " ]]; then
206+ host=$( read_host) || return 1
207+ fi
208+ connect_host " $host " resume " $chat_name "
209+ }
210+
147211doctor () {
148212 ensure_app || return 1
149213 " $bridge " doctor
@@ -157,29 +221,33 @@ menu() {
157221 print -- " Noturcode remote"
158222 print -- " 1. Pair a VPS"
159223 print -- " 2. Open an SSH workspace"
160- print -- " 3. Check setup"
161- print -- " 4. Exit"
224+ print -- " 3. Resume an existing Codex chat"
225+ print -- " 4. Check setup"
226+ print -- " 5. Exit"
162227 local choice=" "
163- read " choice?Choose 1-4 : "
228+ read " choice?Choose 1-5 : "
164229 case " $choice " in
165230 1) pair_host || true ;;
166231 2) connect_host; return $? ;;
167- 3) doctor || true ;;
168- 4) return 0 ;;
169- * ) print -u2 -- " Choose 1, 2, 3, or 4." ;;
232+ 3) resume_codex; return $? ;;
233+ 4) doctor || true ;;
234+ 5) return 0 ;;
235+ * ) print -u2 -- " Choose 1, 2, 3, 4, or 5." ;;
170236 esac
171237 done
172238}
173239
174240case " ${1:- } " in
175241 " " ) menu ;;
176242 pair) shift ; pair_host " ${1:- } " ;;
177- ssh) shift ; connect_host " ${1:- } " ;;
243+ ssh) shift ; connect_host " ${1:- } " shell " ${2:- } " ;;
244+ resume) shift ; resume_codex " ${1:- } " " ${2:- } " ;;
178245 doctor) doctor ;;
179246 help|-h|--help)
180247 print -- " nc Interactive setup"
181248 print -- " nc pair [host] Pair one VPS"
182249 print -- " nc ssh [host] Open one tracked SSH shell"
250+ print -- " nc resume [host] Resume an existing Codex chat"
183251 print -- " nc doctor Check the local setup"
184252 ;;
185253 * ) fail " Unknown command. Run nc for the guided setup." ;;
0 commit comments