relay-codex 0.6.0 → 0.6.1

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
@@ -109,6 +109,10 @@ allowance and does not fetch account allowance. `refresh` explicitly checks it.
109
109
  The menu uses one local status stream, a template icon, and native session actions.
110
110
  Opening it reads a fresh local snapshot, and visible rows update in place.
111
111
  Short-window and weekly allowance are labeled separately.
112
+ Accounts assigned to live Relay sessions have a checkmark and an “In use” label;
113
+ shared accounts also show their session count. To switch, open the Relay icon,
114
+ then your session, then **Switch account**, and choose the account email. The
115
+ current choice is marked **Current**. The menu tracks tasks started through Relay.
112
116
  Each session menu can switch to another enabled account after work and approvals
113
117
  finish. The worker checks allowance and session capacity before switching, then
114
118
  checks again that the session is idle before changing its login. Rejected actions
@@ -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.6.0
12
- npx --yes github:blackspider-ops/relay#v0.6.0 --version
11
+ npm install -g github:blackspider-ops/relay#v0.6.1
12
+ npx --yes github:blackspider-ops/relay#v0.6.1 --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.6.0",
3
+ "version": "0.6.1",
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
@@ -186,6 +186,7 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
186
186
  func makeMenu() -> NSMenu {
187
187
  let sessions = current["sessions"] as? [[String: Any]] ?? []
188
188
  let accounts = current["accounts"] as? [[String: Any]] ?? []
189
+ let liveSessions = sessions.filter { $0["running"] as? Bool ?? true }
189
190
  let menu = NSMenu(); menu.delegate = self
190
191
  menu.autoenablesItems = false
191
192
  text("Relay · \(sessions.count) sessions", into: menu)
@@ -195,8 +196,12 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
195
196
  if !actionRunning { action("Dismiss message", args: ["dismiss"], into: menu) }
196
197
  }
197
198
  if accounts.isEmpty { text("Add an account with relay setup in Terminal.", into: menu) }
199
+ if liveSessions.isEmpty && !accounts.isEmpty { text("No Relay sessions running. Start with relay run.", into: menu) }
198
200
  for account in accounts {
199
201
  let name = account["name"] as? String ?? "Account"
202
+ let accountID = account["id"] as? String ?? ""
203
+ let assigned = liveSessions.filter { $0["account"] as? String == accountID }
204
+ let inUse = assigned.isEmpty ? "" : assigned.count == 1 ? " · In use" : " · In use by \(assigned.count) sessions"
200
205
  let primary = account["primary"] as? [String: Any]
201
206
  let short = account["short_remaining"] as? String ?? (primary?["usedPercent"] as? NSNumber).map { "\(max(0, 100 - $0.intValue))%" } ?? "?"
202
207
  let remaining = short == "?" ? "allowance unknown" : short + " left"
@@ -206,6 +211,13 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
206
211
  let submenu = NSMenu()
207
212
  submenu.autoenablesItems = false
208
213
  if let email = account["email"] as? String { text(email, into: submenu) }
214
+ if !assigned.isEmpty {
215
+ for session in assigned {
216
+ let sessionName = session["name"] as? String ?? String((session["id"] as? String ?? "").prefix(10))
217
+ text("In use by: \(sessionName)", into: submenu)
218
+ }
219
+ text("To switch: open that session → Switch account.", into: submenu)
220
+ }
209
221
  text("Short window: \(remaining)", into: submenu)
210
222
  if weekly != "?" { text("Weekly window: \(weekly) left", into: submenu) }
211
223
  for row in MenuController.accountTimingRows(account) {
@@ -220,7 +232,8 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
220
232
  action(enabled ? "Disable for future routing" : "Enable for routing", args: ["account", enabled ? "disable" : "enable", id], into: submenu)
221
233
  text("Current tasks keep running.", into: submenu)
222
234
  }
223
- let entry = NSMenuItem(title: "\(name) · \(label)", action: nil, keyEquivalent: "")
235
+ let entry = NSMenuItem(title: "\(name)\(inUse) · \(label)", action: nil, keyEquivalent: "")
236
+ entry.state = assigned.isEmpty ? .off : .on
224
237
  entry.identifier = NSUserInterfaceItemIdentifier("account:" + (account["id"] as? String ?? name))
225
238
  entry.submenu = submenu; menu.addItem(entry)
226
239
  }
@@ -248,7 +261,7 @@ final class MenuController: NSObject, NSApplicationDelegate, NSMenuDelegate {
248
261
  let modelAllowed = models.isEmpty || model == nil || models.contains(model!)
249
262
  let available = (account["enabled"] as? Bool ?? true) && !(account["maintenance"] as? Bool ?? false) && modelAllowed
250
263
  let name = account["name"] as? String ?? accountID
251
- let choice = action(name + (available ? "" : " · unavailable"), args: ["switch", id, "--account", accountID, "--wait"], into: choices)
264
+ let choice = action(name + (selected ? " · Current" : available ? "" : " · unavailable"), args: ["switch", id, "--account", accountID, "--wait"], into: choices)
252
265
  choice.state = selected ? .on : .off
253
266
  choice.isEnabled = !selected && available && !actionRunning
254
267
  }
@@ -363,6 +376,20 @@ if CommandLine.arguments.contains("--self-check") {
363
376
  let switchArgs = ["switch", "live", "--account", "two", "--wait"]
364
377
  let correctChoices = findAction(switchArgs, in: controls)?.isEnabled == true && findAction(["switch", "live", "--account", "one", "--wait"], in: controls)?.state == .on && findAction(["switch", "live", "--account", "off", "--wait"], in: controls)?.isEnabled == false
365
378
  let stoppedControls = findAction(["restart", "stopped", "--if-stopped"], in: controls) != nil && findAction(["pause", "stopped", "--wait"], in: controls) == nil
379
+ let activeMarker = controls.items.first { $0.identifier?.rawValue == "account:one" }
380
+ let currentChoice = findAction(["switch", "live", "--account", "one", "--wait"], in: controls)
381
+ let correctMarker = activeMarker?.state == .on && activeMarker?.title.contains("In use") == true && currentChoice?.title.contains("Current") == true
382
+ let originalSessions = controller.current["sessions"]
383
+ controller.current["sessions"] = [["id": "a", "account": "one", "running": true], ["id": "b", "account": "one", "running": true], ["id": "c", "account": "two", "running": true], ["id": "d", "account": "off", "running": false]]
384
+ let multiple = controller.makeMenu()
385
+ let one = multiple.items.first { $0.identifier?.rawValue == "account:one" }
386
+ let two = multiple.items.first { $0.identifier?.rawValue == "account:two" }
387
+ let off = multiple.items.first { $0.identifier?.rawValue == "account:off" }
388
+ let correctMultiple = one?.title.contains("In use by 2 sessions") == true && two?.state == .on && off?.state == .off
389
+ controller.current["sessions"] = [["id": "stopped", "account": "one", "running": false]]
390
+ let empty = controller.makeMenu()
391
+ let correctEmpty = empty.items.contains { $0.title.contains("No Relay sessions running") } && empty.items.first { $0.identifier?.rawValue == "account:one" }?.state == .off
392
+ controller.current["sessions"] = originalSessions
366
393
  controller.actionRunning = true; controller.actionNotice = "Applying change…"
367
394
  let pending = controller.makeMenu()
368
395
  let correctPending = findAction(switchArgs, in: pending)?.isEnabled == false && findAction(["refresh"], in: pending)?.isEnabled == false && findAction(["copy", "relay attach live"], in: pending)?.isEnabled == true
@@ -371,7 +398,8 @@ if CommandLine.arguments.contains("--self-check") {
371
398
  let retainedResult = controller.makeMenu().items.contains { $0.title == "Task is busy." }
372
399
  print("Account choices and stopped controls correct: \(correctChoices && stoppedControls)")
373
400
  print("Pending action protected and result retained: \(correctPending && retainedResult)")
374
- exit(normal != nil && attention != nil && liveUpdate && safeIdentity && correctTiming && correctChoices && stoppedControls && correctPending && retainedResult ? 0 : 1)
401
+ print("In-use accounts and current choice marked: \(correctMarker && correctMultiple && correctEmpty)")
402
+ exit(normal != nil && attention != nil && liveUpdate && safeIdentity && correctTiming && correctChoices && stoppedControls && correctPending && retainedResult && correctMarker && correctMultiple && correctEmpty ? 0 : 1)
375
403
  } else {
376
404
  let app = NSApplication.shared
377
405
  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.6.0"
27
+ VERSION = "0.6.1"
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"