Skip to content

Session command line: land #99 - #146

Open
krishhgg wants to merge 14 commits into
integration/menu-controlsfrom
integration/session-command
Open

krishhgg wants to merge 14 commits into
integration/menu-controlsfrom
integration/session-command

Conversation

@krishhgg

@krishhgg krishhgg commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Scripts and coding agents can start, extend and end the menu bar app's session with Insomnia --session start <seconds> | extend <seconds> | end | status. The session they start is the same one the menu bar counts down, and the command line never runs pmset itself.

This lands #99 by @0xRafie at e738536. The commit is merged here unchanged, so GitHub marks #99 merged when this one merges and its author keeps the credit. It sits on #145 (menu controls), because both add an object to the app's launch code. Review and merge #145 first; this pull request's base then moves to main.

To @0xRafie: please don't push to your branch now. A new commit on it isn't in this pull request, so #99 would stay open instead of showing as merged.

Conflicts resolved

Maintainer fixes

  • 0ec2765, an extend the cap leaves in place was reported as done. tryExtend returned true whenever it saved the session. At the 30 day cap, or after a lower maxDuration in config.json, Insomnia --session extend 3600 printed success for an extension that never happened. It now reports success only when the end moved later, and the command prints its "not extended" reason otherwise. The menu's extend ignores the result and behaves as before.
  • 7f6654a, the same at the cap with a moving clock. 0ec2765 assumed the end stays put at the cap. It doesn't. The cap is now + 30 days, so each extend at the cap moves the end out by the time since the one before. The macOS 27 VM run at 37f87d5 caught it: a second extend 2147483647 right after the first moved the end about 0.1 s and exited 0. 0ec2765's test used a frozen clock, so it missed this. An extend now counts only when the end moves out by at least a second, the smallest request the command takes. A new test advances the clock 0.1 s at the cap (not extended), then 60 s (extended), and fails with the old comparison. Another keeps a one second extend below the cap a success.

Not changed: after a hand-lowered maxDuration, an extend still moves the end in to the new cap, from the menu or the command line. That is main's existing rule ("never past now + maxDuration"); only the command's answer changes.

Tests

Full suite at 7f6654a on macOS 26.2 (Xcode 26.4.1): 1,281 tests, 0 failures. Hosted CI on macOS 26 and 27 passes at 7f6654a. UIStatusTests, UIStartupTests and KeychainStoreTests are skipped on the maintainer's Mac; hosted CI runs them.

Codex review

Clean at 0ec2765, at 37f87d5 and at 7f6654a (gpt-6.1-sol, xhigh, read-only): the whole of #99 combined with #145, the merge resolution and both extend fixes. A read-only audit of #99 alone at e738536 found only the extend result, fixed in 0ec2765. None of these reviews caught the moving cap; the VM run did. #145's pill fixes, merged in at 37f87d5, were reviewed on #145 (clean at 85576e5).

Evidence

  • macOS 27.0.1 VM run at 7f6654a: every case passes. It covers start, status, extend and end; an end sent just before a start, and a start just before an end; two command lines at once; an extend at the cap; an end refused while another process holds the recovery lock, then the app's retry; a start while the pills are open; and a request while the app isn't running.
  • The owner's report from their own Mac on macOS 26 and 27: still to come.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no blocking issue was found.

Summary

Adds Insomnia --session start, extend, end, and status for scripts driving the running menu bar app.

  • Scripts can manage the menu bar session from the command line.

No actionable issues were found in the changes since the previous review or the full-PR rule check. Tests were inspected, not run.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  CLI["Insomnia --session"] --> Action{"Command"}
  Action -->|status| Session["Read session.json"]
  Action -->|start, extend, end| Request["Write request file"]
  Request --> Watcher["Running app claims request"]
  Watcher --> Manager["SessionManager runs action"]
  Manager --> Reply["Write reply file"]
  Reply --> CLI
Loading

Reviews (5) · Last reviewed commit: "Session command: an extend that only cre..." · Reviewed by Greptile

0xRafie and others added 10 commits October 12, 2026 20:16
Insomnia --session start <seconds> | extend <seconds> | end | status lets a
script or an agent start, extend and end the session the menu bar counts
down, with the same battery and heat rules. The command line changes
nothing itself: it links one request into session-command in the support
directory, the app's watcher claims it, runs it like the matching menu
action and answers in session-command.reply.<id>. A request nothing claims
within 2 s is taken back, and one older than 5 s is dropped, so a request
left behind never starts a session later.
A start queued behind another, an end whose restore failed, and an end that
arrived while a start was in flight could all answer done. The manager's
tryStart and tryExtend now say whether this call took effect, an end maps
every EndOutcome other than restored to a reason, and an end or extend with
no session yet waits for queued work first. The watcher starts after the
launch reconcile, a request a killed command line left no longer blocks the
next one, a command line waits on and takes back only its own file (by
inode), old answers are removed, and an app still starting is told apart
from an older build.
…nd every time

Greptile on #99: an end that found no session answered done without ending,
which left a dirty journal or an unadopted session.json behind, and the
take-back of a shared request path could still drop another caller's
request when three command lines raced. An end now always runs end(reason:
.user), which also abandons a start in flight. Each command line writes its
own session-command.<id>.request, so publishing, the app's claim and a
take-back are each one rename of that caller's own file, and the app serves
the requests it finds oldest first.
…st runs first

`end` then `start` each run in their own task. While the end waited on the
lifecycle queue or the recovery lock, the early `session != nil` check in
`run(.start)` still saw the old session and refused the start as already
running; the end then ran and left no session. The check is gone, so the
start queues behind the end and `performStart` decides under the lock. The
check after `tryStart` still gives the refusal message.

The new test holds an extend at the backstop arm, queues `end` and then
`start`, and checks that the start makes a new session.
`tryStart` refuses a start while an end is pending, but only when the
request arrives. An end already in the queue can leave itself to its retry
(`scheduleEndRetry` in `performEnd`), and a start queued behind it went on
to start a session that the retry stopped 30 s later. `performStart` now
checks `pendingEnd` as well, with the same refusal.

Removing the early check in `run(.start)` made this reachable from `end`
then `start`; a start sent with no session running could already reach it.
The new test fails the end's arm, lets the start's succeed, and checks the
start is refused and nothing runs.
Lands #99 at e738536, by 0xRafie.

Conflict with #137 and the maintainer's time change fix, resolved here:
each added an app-lifetime object to InsomniaApp, the display sleep and
time change observers and #99's session command watcher. All three are
kept. The observers start first; the watcher starts after the launch
reconcile, as in #99.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
tryExtend reported success whenever it saved the session. At the 30 day
cap the new end equals the old one, and after a lower maxDuration in
config.json it is earlier, so `Insomnia --session extend` printed a
success for an extension that never happened. It now reports success
only when the end moved later, and the command line prints its "not
extended" reason otherwise. The menu's extend ignores the result, so it
behaves as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 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-10-24T01:17:15.168092Z 0ec2765 PR opened
🔒 Security Review ✅ Completed 2026-10-24T01:18:55.263207Z 0ec2765 PR opened
ℹ️ 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.

krishhgg and others added 4 commits October 23, 2026 20:13
Brings in 54f3c7e, which keeps the pills open when a pill is clicked
on macOS 27.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Brings in 732de03: one refocus watch per press, and the pills return
focus to the app the user was in.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Brings in 85576e5, a test seam for the focus-loss test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…uccess

0ec2765 counted any later end as an extension, on the belief that at the
30 day cap the new end equals the old one. It doesn't: the cap is now +
maxDuration, so each extend at the cap moves the end out by the time
since the last one. In a macOS 27 VM, a second `extend 2147483647` right
after the first moved the end about 0.1 s and exited 0. The test used a
frozen clock, so it never saw this.

An extend now counts only when the end moves out by at least a second,
the smallest request the command line takes. A new test advances the
clock 0.1 s at the cap (not extended), then 60 s (extended, by the
minute the cap moved), and fails with the old comparison. Another pins
a one second extend below the cap as a success.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@krishhgg

Copy link
Copy Markdown
Owner Author

VM run on macOS 27.0.1 at 7f6654a: the session command line works

Every case below passes at the head, 7f6654a, in a macOS 27.0.1 VM. The same run at 37f87d5 found one bug: a second extend at the 30 day cap exited 0. 7f6654a fixes it. This is supporting evidence from a VM. It does not replace the owner's report from their own Mac.

Setup. macOS 27.0.1 (26A434) with Xcode 27.0, in a Tart VM on the maintainer's Mac mini. The commit was installed from a clean checkout with scripts/install.sh, after config.json was removed, so the app started from its defaults. The script runs the installed app's binary the way the README shows, Insomnia.app/Contents/MacOS/Insomnia --session …, and times each call. After every step it checks session.json, pmset -g SleepDisabled, and that no request or reply file is left in the support folder. None was left after any step.

Step Result at 7f6654a
status with no session Exit 0: "no session is running"
start 120 Exit 0 in 5.3 s: "running until … (2m left)". SleepDisabled 1, and the menu bar counts down
start 60 while it runs Exit 1: "a session is already running; use extend"
extend 60 Exit 0: 3m left
end Exit 0. session.json is gone, SleepDisabled 0, and the status item is back to the eye alone (34 pt)
end, then extend, with no session end exits 0. extend exits 1: "no session is running"
start 0, start abc, no arguments Exit 64 with the usage line
end, then start 0.3 s later Both exit 0, in that order, and the session runs
start, then end 0.3 s later The start exits 1: "the session did not start; see the log". The end exits 0. No session is left, and SleepDisabled is 0
Two start 120 at once One exits 0. The other exits 1: "a session is already running; use extend"
Two extend 600 at once Both exit 0, and the end moves 20 minutes
extend 2147483647 Exit 0: 30d left
The same again, right after Exit 1: "the session was not extended; it may already be as long as a session can be". At 37f87d5 this exited 0
end while another process holds the recovery lock Exit 1 after 10.1 s: "the recovery lock is held by another process, so nothing changed; Insomnia retries the end". The session keeps running
The lock released 1 s later The app ends the session at its own retry, about 30 s after the refused end. SleepDisabled 0
start 120 while the pills are open Exit 0. The pills stay open and the eye opens. Escape closes them and the countdown shows
start 120 with the app quit Exit 1 in 2.2 s: "Insomnia is not running, so nothing took the command; open the app and try again". No file is left, and the next launch starts nothing

The bug at 37f87d5. The cap is now + 30 days, and now keeps moving. So each extend at the cap moved the end out by the time since the one before, about 0.1 s here. 0ec2765 counted any later end as an extension, so the command exited 0. 7f6654a counts an extend only when the end moves out by at least a second, the smallest request the command takes. New unit tests advance the clock 0.1 s at the cap (not extended), then 60 s (extended), and fail with the old comparison.

Menu bar after start 120 from the command line: the eye and the countdown

Menu bar after end from the command line: the eye alone

The pills open with no session

The pills still open after a start from the command line, with the eye open

Evidence: t146-7f6654a.log (the head), t146-37f87d5.log (before the fix), the images and the README, the script.

Limits

  • macOS 26 is not covered here. The lab has no macOS 26 VM now. Hosted CI runs the unit tests on macOS 26 and 27.
  • Each case ran once.
  • The VM has no lid, battery or heat sensor, so a command meeting those ends was not tried.
  • A quit while a command is in flight was not tried.

🤖 Generated with Claude Code

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