Skip to content

Commit 5154fe0

Browse files
feat: show Codex sessions across Gateway and paired nodes (#102586)
* feat(codex): add federated session catalog * fix(codex): align session catalog checks * fix(ui): translate Codex session catalog --------- Co-authored-by: Peter Steinberger <[email protected]>
1 parent 201acd4 commit 5154fe0

93 files changed

Lines changed: 8017 additions & 125 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/macos/Sources/OpenClaw/NodeMode/MacNodeCodexThreadCatalog.swift

Lines changed: 726 additions & 0 deletions
Large diffs are not rendered by default.

apps/macos/Sources/OpenClaw/NodeMode/MacNodeModeCoordinator.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,17 @@ final class MacNodeModeCoordinator: NSObject {
168168

169169
let cameraEnabled = defaults.object(forKey: cameraEnabledKey) as? Bool ?? false
170170
let browserControlEnabled = OpenClawConfigFile.browserControlEnabled()
171+
let codexThreadCatalogEnabled = OpenClawConfigFile.explicitlyEnabledPlugin(
172+
MacNodeCodexThreadCatalogContract.pluginId)
171173

172174
var attemptedURL: URL?
173175
do {
174176
let config = try await GatewayEndpointStore.shared.requireConfig()
175177
attemptedURL = config.url
176178
let caps = self.currentCaps(
177179
browserControlEnabled: browserControlEnabled,
178-
cameraEnabled: cameraEnabled)
180+
cameraEnabled: cameraEnabled,
181+
codexThreadCatalogEnabled: codexThreadCatalogEnabled)
179182
let commands = self.currentCommands(caps: caps)
180183
let permissions = await self.currentPermissions()
181184
let connectOptions = GatewayConnectOptions(
@@ -268,7 +271,8 @@ final class MacNodeModeCoordinator: NSObject {
268271
browserControlEnabled: Bool,
269272
cameraEnabled: Bool,
270273
locationMode: OpenClawLocationMode,
271-
connectionMode: AppState.ConnectionMode) -> [String]
274+
connectionMode: AppState.ConnectionMode,
275+
codexThreadCatalogEnabled: Bool = false) -> [String]
272276
{
273277
var caps: [String] = [
274278
OpenClawCapability.canvas.rawValue,
@@ -283,16 +287,24 @@ final class MacNodeModeCoordinator: NSObject {
283287
if locationMode != .off {
284288
caps.append(OpenClawCapability.location.rawValue)
285289
}
290+
if codexThreadCatalogEnabled {
291+
caps.append(MacNodeCodexThreadCatalogContract.capability)
292+
}
286293
return caps
287294
}
288295

289-
private func currentCaps(browserControlEnabled: Bool, cameraEnabled: Bool) -> [String] {
296+
private func currentCaps(
297+
browserControlEnabled: Bool,
298+
cameraEnabled: Bool,
299+
codexThreadCatalogEnabled: Bool) -> [String]
300+
{
290301
let rawLocationMode = UserDefaults.standard.string(forKey: locationModeKey) ?? "off"
291302
return Self.resolvedCaps(
292303
browserControlEnabled: browserControlEnabled,
293304
cameraEnabled: cameraEnabled,
294305
locationMode: OpenClawLocationMode(rawValue: rawLocationMode) ?? .off,
295-
connectionMode: AppStateStore.shared.connectionMode)
306+
connectionMode: AppStateStore.shared.connectionMode,
307+
codexThreadCatalogEnabled: codexThreadCatalogEnabled)
296308
}
297309

298310
private func currentPermissions() async -> [String: Bool] {
@@ -331,6 +343,9 @@ final class MacNodeModeCoordinator: NSObject {
331343
if capsSet.contains(OpenClawCapability.location.rawValue) {
332344
commands.append(OpenClawLocationCommand.get.rawValue)
333345
}
346+
if capsSet.contains(MacNodeCodexThreadCatalogContract.capability) {
347+
commands.append(MacNodeCodexThreadCatalogContract.listCommand)
348+
}
334349

335350
return commands
336351
}

apps/macos/Sources/OpenClaw/NodeMode/MacNodeRuntime.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ actor MacNodeRuntime {
1414
private let browserControlEnabled: @Sendable () -> Bool
1515
private let canvasSurfaceUrl: @Sendable () async -> String?
1616
private let refreshCanvasSurfaceUrl: @Sendable () async -> String?
17+
private let codexThreadCatalogEnabled: @Sendable () -> Bool
18+
private let codexThreadListRequest: @Sendable (String?) async throws -> String
1719
private var cachedMainActorServices: (any MacNodeRuntimeMainActorServices)?
1820
private var mainSessionKey: String = "main"
1921
private var eventSender: (@Sendable (String, String?) async -> Void)?
@@ -31,13 +33,22 @@ actor MacNodeRuntime {
3133
canvasSurfaceUrl: @escaping @Sendable () async -> String? = {
3234
await GatewayConnection.shared.canvasPluginSurfaceUrl()
3335
},
34-
refreshCanvasSurfaceUrl: @escaping @Sendable () async -> String? = { nil })
36+
refreshCanvasSurfaceUrl: @escaping @Sendable () async -> String? = { nil },
37+
codexThreadCatalogEnabled: @escaping @Sendable () -> Bool = {
38+
OpenClawConfigFile.explicitlyEnabledPlugin(
39+
MacNodeCodexThreadCatalogContract.pluginId)
40+
},
41+
codexThreadListRequest: @escaping @Sendable (String?) async throws -> String = { paramsJSON in
42+
try await MacNodeCodexThreadCatalog.list(paramsJSON: paramsJSON)
43+
})
3544
{
3645
self.makeMainActorServices = makeMainActorServices
3746
self.browserProxyRequest = browserProxyRequest
3847
self.browserControlEnabled = browserControlEnabled
3948
self.canvasSurfaceUrl = canvasSurfaceUrl
4049
self.refreshCanvasSurfaceUrl = refreshCanvasSurfaceUrl
50+
self.codexThreadCatalogEnabled = codexThreadCatalogEnabled
51+
self.codexThreadListRequest = codexThreadListRequest
4152
}
4253

4354
func updateMainSessionKey(_ sessionKey: String) {
@@ -94,9 +105,23 @@ actor MacNodeRuntime {
94105
return try await self.handleSystemExecApprovalsGet(req)
95106
case OpenClawSystemCommand.execApprovalsSet.rawValue:
96107
return try await self.handleSystemExecApprovalsSet(req)
108+
case MacNodeCodexThreadCatalogContract.listCommand:
109+
guard self.codexThreadCatalogEnabled() else {
110+
return Self.errorResponse(
111+
req,
112+
code: .unavailable,
113+
message: "UNAVAILABLE: Codex session catalog is disabled")
114+
}
115+
let payload = try await self.codexThreadListRequest(req.paramsJSON)
116+
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
97117
default:
98118
return Self.errorResponse(req, code: .invalidRequest, message: "INVALID_REQUEST: unknown command")
99119
}
120+
} catch let error as MacNodeCodexThreadCatalog.CatalogError {
121+
return Self.errorResponse(
122+
req,
123+
code: error.isInvalidRequest ? .invalidRequest : .unavailable,
124+
message: error.localizedDescription)
100125
} catch {
101126
return Self.errorResponse(req, code: .unavailable, message: error.localizedDescription)
102127
}

apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,38 @@ enum OpenClawConfigFile {
207207
return browser?["enabled"] as? Bool ?? defaultValue
208208
}
209209

210+
private static func normalizedPluginConfigId(_ value: Any?) -> String? {
211+
guard let value = value as? String else { return nil }
212+
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
213+
return trimmed.isEmpty ? nil : trimmed
214+
}
215+
216+
static func pluginEntry(_ pluginId: String, root: [String: Any]? = nil) -> [String: Any]? {
217+
let root = root ?? self.loadDict()
218+
guard let plugins = root["plugins"] as? [String: Any],
219+
let entries = plugins["entries"] as? [String: Any]
220+
else { return nil }
221+
return entries.first(where: { key, _ in
222+
self.normalizedPluginConfigId(key) == pluginId
223+
})?.value as? [String: Any]
224+
}
225+
226+
static func explicitlyEnabledPlugin(_ pluginId: String, root: [String: Any]? = nil) -> Bool {
227+
let root = root ?? self.loadDict()
228+
guard let plugins = root["plugins"] as? [String: Any],
229+
plugins["enabled"] as? Bool != false,
230+
let entry = self.pluginEntry(pluginId, root: root),
231+
entry["enabled"] as? Bool == true
232+
else { return false }
233+
234+
let deny = (plugins["deny"] as? [Any] ?? []).compactMap(self.normalizedPluginConfigId)
235+
if deny.contains(pluginId) { return false }
236+
237+
let allow = (plugins["allow"] as? [Any] ?? []).compactMap(self.normalizedPluginConfigId)
238+
if !allow.isEmpty, !allow.contains(pluginId) { return false }
239+
return true
240+
}
241+
210242
static func setBrowserControlEnabled(_ enabled: Bool) {
211243
var root = self.loadDict()
212244
var browser = root["browser"] as? [String: Any] ?? [:]

0 commit comments

Comments
 (0)