relay-codex 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/commands.md CHANGED
@@ -140,7 +140,9 @@ Each session menu can switch to another enabled account after work and approvals
140
140
  finish. The worker checks allowance and session capacity before switching, then
141
141
  checks again that the session is idle before changing its login. Rejected actions
142
142
  show their reason. Pending menu actions cannot be submitted twice, and results
143
- remain visible until dismissed or another action starts. Stopped workers offer
143
+ disappear 30 seconds after first being viewed in the menu. Unseen results wait
144
+ until the menu is opened; action progress remains until the action finishes.
145
+ Starting another action replaces its previous result. Stopped workers offer
144
146
  Restart; restarting retains any review hold.
145
147
 
146
148
  Account menus can disable or enable future routing. Disabling an account does not
@@ -8,8 +8,8 @@ Friends can run `npx relay-codex` or install it with `npm install -g relay-codex
8
8
  The GitHub repository is enough for friends to install or try Relay:
9
9
 
10
10
  ```sh
11
- npm install -g github:blackspider-ops/relay#v0.7.1
12
- npx --yes github:blackspider-ops/relay#v0.7.1 --version
11
+ npm install -g github:blackspider-ops/relay#v0.7.2
12
+ npx --yes github:blackspider-ops/relay#v0.7.2 --version
13
13
  ```
14
14
 
15
15
  `npx` runs packages; it is not a separate publishing service. Publishing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relay-codex",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Keep your Codex sessions moving between your own accounts.",
5
5
  "license": "MIT",
6
6
  "author": "blackspider-ops",
package/python/Menu.swift CHANGED
@@ -6,7 +6,11 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
6
6
  var buffer = Data()
7
7
  var current: [String: Any] = [:]
8
8
  var message: String?
9
- var actionNotice: String?
9
+ var actionNotice: String? {
10
+ didSet { noticeVersion += 1; noticeExpiry?.cancel(); noticeExpiry = nil }
11
+ }
12
+ var noticeVersion = 0
13
+ var noticeExpiry: DispatchWorkItem?
10
14
  var actionFailed = false
11
15
  var actionRunning = false
12
16
  var reconnect: DispatchWorkItem?
@@ -194,6 +198,7 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
194
198
  if let visible = item.menu { MenuController.synchronise(visible, with: menu) }
195
199
  else { item.menu = menu }
196
200
  if menuOpen { scheduleResetExpiry() }
201
+ scheduleNoticeExpiry()
197
202
  }
198
203
  func makeMenu() -> NSMenu {
199
204
  let sessions = current["sessions"] as? [[String: Any]] ?? []
@@ -205,7 +210,6 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
205
210
  if let message = message { text(message, into: menu) }
206
211
  if let notice = actionNotice {
207
212
  text(notice, into: menu)
208
- if !actionRunning { action("Dismiss message", args: ["dismiss"], into: menu) }
209
213
  }
210
214
  if accounts.isEmpty { text("Add an account with relay setup in Terminal.", into: menu) }
211
215
  if liveSessions.isEmpty && !accounts.isEmpty { text("No Relay sessions running. Start with relay run.", into: menu) }
@@ -335,6 +339,21 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
335
339
  }
336
340
  }
337
341
  func menuDidClose(_ menu: NSMenu) { menuOpen = false; resetExpiry?.cancel(); resetExpiry = nil }
342
+ func scheduleNoticeExpiry() {
343
+ guard menuOpen, !actionRunning, actionNotice != nil, noticeExpiry == nil else { return }
344
+ let version = noticeVersion
345
+ let expiry = DispatchWorkItem { [weak self] in
346
+ guard let self = self, self.expireNotice(version: version) else { return }
347
+ self.render()
348
+ }
349
+ noticeExpiry = expiry
350
+ DispatchQueue.main.asyncAfter(deadline: .now() + 30, execute: expiry)
351
+ }
352
+ func expireNotice(version: Int) -> Bool {
353
+ guard !stopping, !actionRunning, actionNotice != nil, noticeVersion == version else { return false }
354
+ actionNotice = nil; actionFailed = false
355
+ return true
356
+ }
338
357
  func resetAllowed(_ id: String) -> Bool {
339
358
  let accounts = current["accounts"] as? [[String: Any]] ?? []
340
359
  guard let account = accounts.first(where: { $0["id"] as? String == id }) else {
@@ -346,7 +365,6 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
346
365
  @objc func runAction(_ sender: NSMenuItem) {
347
366
  guard var args = sender.representedObject as? [String], !args.isEmpty else { return }
348
367
  if args[0] == "copy" { NSPasteboard.general.clearContents(); NSPasteboard.general.setString(args[1], forType: .string); return }
349
- if args[0] == "dismiss" { actionNotice = nil; actionFailed = false; render(); return }
350
368
  guard !actionRunning else { return }
351
369
  if args[0] == "confirm-retry" {
352
370
  let alert = NSAlert(); alert.messageText = "Continue this task?"; alert.informativeText = "Review any interrupted command or external action in the conversation before continuing."
@@ -404,7 +422,7 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
404
422
  runAction(sender)
405
423
  }
406
424
  @objc func quitMenu() { stopping = true; reconnect?.cancel(); stream?.terminate(); NSApp.terminate(nil) }
407
- func applicationWillTerminate(_ notification: Notification) { stopping = true; stream?.terminate() }
425
+ func applicationWillTerminate(_ notification: Notification) { stopping = true; noticeExpiry?.cancel(); resetExpiry?.cancel(); stream?.terminate() }
408
426
  }
409
427
  if CommandLine.arguments.contains("--self-check") {
410
428
  let normal = NSImage(systemSymbolName: "arrow.triangle.branch", accessibilityDescription: "Relay")
@@ -473,12 +491,26 @@ if CommandLine.arguments.contains("--self-check") {
473
491
  controller.actionRunning = false; controller.actionNotice = "Task is busy."
474
492
  controller.current["at"] = 2000000001.0
475
493
  let retainedResult = controller.makeMenu().items.contains { $0.title == "Task is busy." }
494
+ let noDismissButton = findAction(["dismiss"], in: controller.makeMenu()) == nil
495
+ controller.menuOpen = false; controller.actionFailed = true; controller.actionNotice = "Unseen result"
496
+ controller.scheduleNoticeExpiry()
497
+ let unseenRetained = controller.noticeExpiry == nil && controller.actionNotice == "Unseen result"
498
+ controller.menuOpen = true; controller.scheduleNoticeExpiry()
499
+ let startsOnView = controller.noticeExpiry != nil
500
+ let previousNotice = controller.noticeVersion
501
+ controller.actionNotice = "Newer result"
502
+ let newerProtected = !controller.expireNotice(version: previousNotice) && controller.actionNotice == "Newer result"
503
+ controller.actionRunning = true; controller.scheduleNoticeExpiry()
504
+ let progressRetained = controller.noticeExpiry == nil && !controller.expireNotice(version: controller.noticeVersion)
505
+ controller.actionRunning = false
506
+ let expiresAfterView = controller.expireNotice(version: controller.noticeVersion) && controller.actionNotice == nil && !controller.actionFailed
476
507
  print("Account choices and stopped controls correct: \(correctChoices && stoppedControls)")
477
508
  print("Pending action protected and result retained: \(correctPending && retainedResult)")
478
509
  print("In-use accounts and current choice marked: \(correctMarker && correctMultiple && correctEmpty)")
479
510
  print("Available and pending reset actions protected: \(correctResets && resetLocked)")
480
511
  print("Weekly zero and more-than-48-hour guard enforced: \(correctPolicy && pendingLocked)")
481
- exit(normal != nil && attention != nil && liveUpdate && safeIdentity && correctTiming && correctChoices && stoppedControls && correctPending && retainedResult && correctMarker && correctMultiple && correctEmpty && correctResets && resetLocked && correctPolicy && pendingLocked ? 0 : 1)
512
+ print("Viewed notices expire without hiding unseen or newer results: \(noDismissButton && unseenRetained && startsOnView && newerProtected && progressRetained && expiresAfterView)")
513
+ exit(normal != nil && attention != nil && liveUpdate && safeIdentity && correctTiming && correctChoices && stoppedControls && correctPending && retainedResult && correctMarker && correctMultiple && correctEmpty && correctResets && resetLocked && correctPolicy && pendingLocked && noDismissButton && unseenRetained && startsOnView && newerProtected && progressRetained && expiresAfterView ? 0 : 1)
482
514
  } else {
483
515
  let app = NSApplication.shared
484
516
  let delegate = MenuController(); app.delegate = delegate; app.run()
package/python/router.py CHANGED
@@ -24,7 +24,7 @@ from pool import Pool, blocked, reset_time, failure_kind, file_lock
24
24
  from launch import translate, RESUME_FIELDS
25
25
  import settings
26
26
 
27
- VERSION = "0.7.1"
27
+ VERSION = "0.7.2"
28
28
  _support = Path.home() / "Library/Application Support"
29
29
  _legacy = _support / "Codex Accounts"
30
30
  _default_state = _legacy if (_legacy / "accounts").exists() else _support / "Relay"