
Codex 사용량이 Plus 사용자에겐 좀 모자랄 때가 있습니다. Claude Code는 사용량이 리셋되면 알아서 재개를 해주는데 Codex는 아직 그런 기능이 없습니다.
Codex CLI에서 간단한 bash 프로그램으로 시간에 맞춰서 작업을 계속하게 할 수 있습니다. codex 실행과 함께 프롬프트를 주입하는 인터페이스가 있기 때문이죠.
실행하면 아래처럼 됩니다. (하이라이트한 부분이 사용자가 입력하는 부분입니다.)
$ codex-wakeup.sh
Codex 세션 ID: 01axxxxx-xxxx-xxxx-9786-c1e16cf601d6
재개 시각 (HH:MM, 예: 04:13): 01:50
프롬프트 입력 (빈 줄로 완료, 아무것도 입력하지 않으면 '계속해'):
계속 진행하자
세션: 01axxxxx-xxxx-xxxx-9786-c1e16cf601d6
재개: 2026-10-06 01:50:38
프롬프트:
계속 진행하자
대기: 80분
Codex 자동 재개를 기다립니다. Ctrl+C로 취소할 수 있습니다.
“계속 진행하자” 하고 프롬프트를 입력한 다음에 두 줄이 띄어져 있는데, 빈 줄에서 엔터를 한번 쳐 줘야 프롬프트 입력이 완료된다는 점에 주의하세요. 여러줄 프롬프트를 입력하기 위한 방법입니다.
제 경우 1시 40분에 리셋되는 건데, 1시 50분에 작업을 재개하는 프롬프트를 입력하도록 하고 자려고 하는 거죠.
리셋 시간 1분 후로 예약하니까 사용량 리셋이 안 되는 경우가 있더라고요. 시간 간격을 10분 정도 넉넉하게 띄워주시는 게 좋습니다.
맥과 리눅스, 윈도우 버전 스크립트를 각각 첨부했습니다.
컴퓨터가 잠자기 모드로 들어가지 않도록 명령 안에 장치가 돼 있습니다.
코드는 ChatGPT 시켜서 짰습니다. 맥, WSL, 윈도우에서 테스트해서 성공했습니다.
윈도우용은 파일이 두 개입니다. codex-wakeup.cmd를 실행하시면 됩니다. codex-wakeup.ps1도 같은 폴더에 있어야 합니다. 이게 본체고, codex-wakeup.cmd는 권한을 우회하게 도와주는 껍데기 파일입니다.
처음에는 예약 시간이 되면 codex resume SESSION_ID "계속해"처럼 resume 명령에 프롬프트를 바로 넘기도록 만들었습니다. 그런데 Codex가 사용량 한도에 도달했다는 메시지를 마지막으로 남긴 세션에서는, 한도가 리셋된 뒤 resume하면서 프롬프트를 함께 넘겨도 그 프롬프트가 실행되지 않는 문제가 있었습니다. 정상적으로 끝난 세션에서는 잘 작동해서 사용량 제한 상태에서 세션을 복구할 때 생기는 버그로 보입니다. 그래서 지금은 예약 시간이 되면 먼저 비대화형 명령인 codex exec resume SESSION_ID "계속해"로 실제 작업을 재개하게 하고, 그 작업이 끝난 다음 codex resume SESSION_ID를 실행해 평소의 TUI 화면을 띄우도록 바꿨습니다. 이렇게 하면 터미널 화면에 키 입력을 억지로 주입하는 식의 불안정한 우회 없이도 사용량 제한 이후 작업을 자동으로 이어갈 수 있습니다.
아래는 코드입니다.
맥용
#!/bin/bash
# Codex 세션 자동 재개
# macOS용
read -r -p "Codex 세션 ID: " SESSION_ID
if [[ -z "$SESSION_ID" ]]; then
echo "세션 ID가 없습니다."
exit 1
fi
read -r -p "재개 시각 (HH:MM, 예: 04:13): " RESUME_TIME
if [[ ! "$RESUME_TIME" =~ ^([01][0-9]|2[0-3]):[0-5][0-9]$ ]]; then
echo "시각 형식이 잘못됐습니다. HH:MM 형식으로 입력하세요."
exit 1
fi
echo "프롬프트 입력 (빈 줄로 완료, 아무것도 입력하지 않으면 '계속해'):"
PROMPT=""
while IFS= read -r LINE; do
[[ -z "$LINE" ]] && break
if [[ -z "$PROMPT" ]]; then
PROMPT="$LINE"
else
PROMPT="${PROMPT}"$'\n'"${LINE}"
fi
done
PROMPT="${PROMPT:-계속해}"
TODAY=$(date "+%Y-%m-%d")
NOW=$(date "+%s")
TARGET=$(date -j -f "%Y-%m-%d %H:%M" \
"$TODAY $RESUME_TIME" "+%s")
# 입력한 시간이 이미 지났으면 다음 날로 설정
if (( TARGET <= NOW )); then
TOMORROW=$(date -j -v+1d "+%Y-%m-%d")
TARGET=$(date -j -f "%Y-%m-%d %H:%M" \
"$TOMORROW $RESUME_TIME" "+%s")
fi
WAIT=$(( TARGET - NOW ))
echo
echo "세션: $SESSION_ID"
echo "재개: $(date -r "$TARGET" "+%Y-%m-%d %H:%M:%S")"
echo "프롬프트:"
printf '%s\n' "$PROMPT"
echo "대기: $((WAIT / 60))분"
echo
echo "Codex 자동 재개를 기다립니다. Ctrl+C로 취소할 수 있습니다."
# 대기 중 macOS 시스템 sleep 방지
caffeinate -i sleep "$WAIT"
echo
echo "Codex를 비대화형으로 재개합니다."
codex exec resume "$SESSION_ID" "$PROMPT"
EXIT_CODE=$?
if (( EXIT_CODE != 0 )); then
echo
echo "codex exec resume 실패 (exit $EXIT_CODE)"
exit "$EXIT_CODE"
fi
echo
echo "Codex TUI를 엽니다."
exec codex resume "$SESSION_ID"
리눅스용
#!/usr/bin/env bash
# Codex 세션 자동 재개
# Linux / WSL2 통합용
# Codex 세션은 현재 사용자의 HOME/인증/설정을 사용해야 하므로 root 실행 금지
if [[ $EUID -eq 0 ]]; then
echo "이 스크립트는 sudo/root로 실행하면 안 됩니다."
echo "현재 사용자로 다시 실행하세요."
exit 1
fi
# 기다린 뒤에야 실패하지 않도록 Codex를 미리 확인
CODEX_BIN=$(command -v codex 2>/dev/null)
if [[ -z "$CODEX_BIN" ]]; then
echo "codex 명령을 찾을 수 없습니다."
echo "현재 사용자 환경에서 codex가 실행되는지 확인하세요."
exit 1
fi
# WSL 감지
IS_WSL=false
if grep -qi microsoft /proc/sys/kernel/osrelease 2>/dev/null ||
grep -qi microsoft /proc/version 2>/dev/null; then
IS_WSL=true
fi
read -r -p "Codex 세션 ID: " SESSION_ID
if [[ -z "$SESSION_ID" ]]; then
echo "세션 ID가 없습니다."
exit 1
fi
read -r -p "재개 시각 (HH:MM, 예: 04:13): " RESUME_TIME
if [[ ! "$RESUME_TIME" =~ ^([01][0-9]|2[0-3]):[0-5][0-9]$ ]]; then
echo "시각 형식이 잘못됐습니다. HH:MM 형식으로 입력하세요."
exit 1
fi
echo "프롬프트 입력 (빈 줄로 완료, 아무것도 입력하지 않으면 '계속해'):"
PROMPT=""
while IFS= read -r LINE; do
[[ -z "$LINE" ]] && break
if [[ -z "$PROMPT" ]]; then
PROMPT="$LINE"
else
PROMPT+=$'\n'"$LINE"
fi
done
PROMPT="${PROMPT:-계속해}"
NOW=$(date +%s)
TARGET=$(date -d "$(date +%Y-%m-%d) $RESUME_TIME" +%s 2>/dev/null) || {
echo "재개 시각을 계산하지 못했습니다."
exit 1
}
# 입력한 시간이 이미 지났으면 다음 날
if (( TARGET <= NOW )); then
TARGET=$(date -d "tomorrow $RESUME_TIME" +%s) || {
echo "다음 날 재개 시각을 계산하지 못했습니다."
exit 1
}
fi
WAIT=$((TARGET - NOW))
echo
echo "세션: $SESSION_ID"
echo "재개: $(date -d "@$TARGET" "+%Y-%m-%d %H:%M:%S")"
echo "프롬프트:"
printf '%s\n' "$PROMPT"
echo "대기: $((WAIT / 60))분"
echo "Codex: $CODEX_BIN"
if $IS_WSL; then
echo "환경: WSL — systemd 절전 방지는 생략합니다."
else
echo "환경: Linux"
fi
echo
echo "Codex 자동 재개를 기다립니다. Ctrl+C로 취소할 수 있습니다."
run_codex() {
sleep "$WAIT"
echo
echo "Codex를 재개합니다."
exec "$CODEX_BIN" resume "$SESSION_ID" "$PROMPT"
}
# WSL에서는 Windows가 전원 관리를 담당하므로 systemd-inhibit를 사용하지 않는다.
if $IS_WSL; then
run_codex
fi
# 일반 Linux에서는 inhibitor를 사용할 수 있으면 절전 방지.
# 먼저 짧은 명령으로 실제 권한까지 확인하고, 실패하면 본 작업은 계속한다.
if command -v systemd-inhibit >/dev/null 2>&1; then
if systemd-inhibit --what=sleep --mode=block --why="Codex wakeup test" true >/dev/null 2>&1; then
export WAIT SESSION_ID PROMPT CODEX_BIN
exec systemd-inhibit \
--what=sleep \
--mode=block \
--why="Waiting to resume Codex session" \
bash -c '
sleep "$WAIT"
echo
echo "Codex를 재개합니다."
exec "$CODEX_BIN" resume "$SESSION_ID" "$PROMPT"
'
else
echo
echo "경고: systemd-inhibit 권한이 없어 절전 방지 없이 계속합니다."
fi
else
echo
echo "경고: systemd-inhibit가 없어 절전 방지 없이 계속합니다."
fi
run_codex
윈도우용
codex-wakeup.cmd:
@echo off
setlocal
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0codex-wakeup.ps1"
set "EXITCODE=%ERRORLEVEL%"
endlocal & exit /b %EXITCODE%
codex-wakeup.ps1:
# Codex 세션 자동 재개
# Windows PowerShell용
$SessionId = Read-Host "Codex 세션 ID"
if ([string]::IsNullOrWhiteSpace($SessionId)) { Write-Host "세션 ID가 없습니다."; exit 1 }
$ResumeTime = Read-Host "재개 시각 (HH:mm, 예: 04:13)"
if ($ResumeTime -notmatch '^([01]\d|2[0-3]):[0-5]\d$') {
Write-Host "시각 형식이 잘못됐습니다. HH:mm 형식으로 입력하세요."; exit 1
}
Write-Host "프롬프트 입력 (빈 줄로 완료, 아무것도 입력하지 않으면 '계속해'):"
$Lines = @()
while ($true) {
$Line = Read-Host
if ([string]::IsNullOrEmpty($Line)) { break }
$Lines += $Line
}
$Prompt = if ($Lines.Count -eq 0) { "계속해" } else { $Lines -join "`n" }
$Hour, $Minute = $ResumeTime.Split(':')
$Now = Get-Date
$Target = Get-Date -Hour ([int]$Hour) -Minute ([int]$Minute) -Second 0
if ($Target -le $Now) { $Target = $Target.AddDays(1) }
$WaitSeconds = [math]::Ceiling(($Target - $Now).TotalSeconds)
Write-Host ""
Write-Host "세션: $SessionId"
Write-Host ("재개: {0:yyyy-MM-dd HH:mm:ss}" -f $Target)
Write-Host "프롬프트:"
Write-Host $Prompt
Write-Host ("대기: {0}분" -f [math]::Floor($WaitSeconds / 60))
Write-Host ""
Write-Host "Codex 자동 재개를 기다립니다. Ctrl+C로 취소할 수 있습니다."
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class SleepControl {
[DllImport("kernel32.dll")]
public static extern uint SetThreadExecutionState(uint esFlags);
}
"@
[uint32]$ES_CONTINUOUS = 2147483648
[uint32]$ES_SYSTEM_REQUIRED = 1
[uint32]$KEEP_AWAKE = $ES_CONTINUOUS -bor $ES_SYSTEM_REQUIRED
try {
$Result = [SleepControl]::SetThreadExecutionState($KEEP_AWAKE)
if ($Result -eq 0) { Write-Warning "자동 절전 방지 설정에 실패했습니다." }
Start-Sleep -Seconds $WaitSeconds
Write-Host ""
Write-Host "Codex를 재개합니다."
& codex resume $SessionId $Prompt
}
finally {
[void][SleepControl]::SetThreadExecutionState($ES_CONTINUOUS)
}










댓글 남기기