feat: surface node core/ui versions in macOS
This commit is contained in:
38
CHANGELOG.md
38
CHANGELOG.md
@@ -19,6 +19,44 @@ Docs: https://docs.clawd.bot
|
|||||||
- Memory: add native Gemini embeddings provider for memory search. (#1151)
|
- Memory: add native Gemini embeddings provider for memory search. (#1151)
|
||||||
- Agents: add local docs path resolution and include docs/mirror/source/community pointers in the system prompt.
|
- Agents: add local docs path resolution and include docs/mirror/source/community pointers in the system prompt.
|
||||||
- Slack: add HTTP webhook mode via Bolt HTTP receiver for Events API deployments. (#1143) — thanks @jdrhyne.
|
- Slack: add HTTP webhook mode via Bolt HTTP receiver for Events API deployments. (#1143) — thanks @jdrhyne.
|
||||||
|
- Nodes: report core/ui versions in node list + presence; surface both in CLI + macOS UI.
|
||||||
|
<<<<<<< HEAD
|
||||||
|
- Agents: add local docs path resolution and include docs/mirror/source/community pointers in the system prompt.
|
||||||
|
- Slack: add HTTP webhook mode via Bolt HTTP receiver for Events API deployments. (#1143) — thanks @jdrhyne.
|
||||||
|
||||||| parent of 903e9be49 (feat: surface node core/ui versions in macOS)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Auth profiles: keep auto-pinned preference while allowing rotation on failover; user pins stay locked. (#1138) — thanks @cheeeee.
|
||||||
|
- macOS: avoid touching launchd in Remote over SSH so quitting the app no longer disables the remote gateway. (#1105)
|
||||||
|
- Memory: index atomically so failed reindex preserves the previous memory database. (#1151)
|
||||||
|
- Memory: avoid sqlite-vec unique constraint failures when reindexing duplicate chunk ids. (#1151)
|
||||||
|
|
||||||
|
## 2026.1.18-5
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- Dependencies: update core + plugin deps (grammy, vitest, openai, Microsoft agents hosting, etc.).
|
||||||
|
|
||||||
|
## 2026.1.18-3
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
=======
|
||||||
|
- Nodes: report core/ui versions in node list + presence; surface both in CLI + macOS UI.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Auth profiles: keep auto-pinned preference while allowing rotation on failover; user pins stay locked. (#1138) — thanks @cheeeee.
|
||||||
|
- macOS: avoid touching launchd in Remote over SSH so quitting the app no longer disables the remote gateway. (#1105)
|
||||||
|
- Memory: index atomically so failed reindex preserves the previous memory database. (#1151)
|
||||||
|
- Memory: avoid sqlite-vec unique constraint failures when reindexing duplicate chunk ids. (#1151)
|
||||||
|
|
||||||
|
## 2026.1.18-5
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- Dependencies: update core + plugin deps (grammy, vitest, openai, Microsoft agents hosting, etc.).
|
||||||
|
|
||||||
|
## 2026.1.18-3
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
>>>>>>> 903e9be49 (feat: surface node core/ui versions in macOS)
|
||||||
- Exec: add host/security/ask routing for gateway + node exec.
|
- Exec: add host/security/ask routing for gateway + node exec.
|
||||||
- Exec: add `/exec` directive for per-session exec defaults (host/security/ask/node).
|
- Exec: add `/exec` directive for per-session exec defaults (host/security/ask/node).
|
||||||
- macOS: migrate exec approvals to `~/.clawdbot/exec-approvals.json` with per-agent allowlists and skill auto-allow toggle.
|
- macOS: migrate exec approvals to `~/.clawdbot/exec-approvals.json` with per-agent allowlists and skill auto-allow toggle.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ struct BridgeNodeInfo: Sendable {
|
|||||||
var displayName: String?
|
var displayName: String?
|
||||||
var platform: String?
|
var platform: String?
|
||||||
var version: String?
|
var version: String?
|
||||||
|
var coreVersion: String?
|
||||||
|
var uiVersion: String?
|
||||||
var deviceFamily: String?
|
var deviceFamily: String?
|
||||||
var modelIdentifier: String?
|
var modelIdentifier: String?
|
||||||
var remoteAddress: String?
|
var remoteAddress: String?
|
||||||
@@ -147,6 +149,8 @@ actor BridgeConnectionHandler {
|
|||||||
displayName: hello.displayName,
|
displayName: hello.displayName,
|
||||||
platform: hello.platform,
|
platform: hello.platform,
|
||||||
version: hello.version,
|
version: hello.version,
|
||||||
|
coreVersion: hello.coreVersion,
|
||||||
|
uiVersion: hello.uiVersion,
|
||||||
deviceFamily: hello.deviceFamily,
|
deviceFamily: hello.deviceFamily,
|
||||||
modelIdentifier: hello.modelIdentifier,
|
modelIdentifier: hello.modelIdentifier,
|
||||||
remoteAddress: self.remoteAddressString(),
|
remoteAddress: self.remoteAddressString(),
|
||||||
@@ -171,6 +175,8 @@ actor BridgeConnectionHandler {
|
|||||||
displayName: req.displayName,
|
displayName: req.displayName,
|
||||||
platform: req.platform,
|
platform: req.platform,
|
||||||
version: req.version,
|
version: req.version,
|
||||||
|
coreVersion: req.coreVersion,
|
||||||
|
uiVersion: req.uiVersion,
|
||||||
deviceFamily: req.deviceFamily,
|
deviceFamily: req.deviceFamily,
|
||||||
modelIdentifier: req.modelIdentifier,
|
modelIdentifier: req.modelIdentifier,
|
||||||
caps: req.caps,
|
caps: req.caps,
|
||||||
@@ -186,6 +192,8 @@ actor BridgeConnectionHandler {
|
|||||||
displayName: enriched.displayName,
|
displayName: enriched.displayName,
|
||||||
platform: enriched.platform,
|
platform: enriched.platform,
|
||||||
version: enriched.version,
|
version: enriched.version,
|
||||||
|
coreVersion: enriched.coreVersion,
|
||||||
|
uiVersion: enriched.uiVersion,
|
||||||
deviceFamily: enriched.deviceFamily,
|
deviceFamily: enriched.deviceFamily,
|
||||||
modelIdentifier: enriched.modelIdentifier,
|
modelIdentifier: enriched.modelIdentifier,
|
||||||
remoteAddress: enriched.remoteAddress,
|
remoteAddress: enriched.remoteAddress,
|
||||||
|
|||||||
@@ -249,6 +249,13 @@ actor GatewayConnection {
|
|||||||
return trimmed.isEmpty ? nil : trimmed
|
return trimmed.isEmpty ? nil : trimmed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cachedGatewayVersion() -> String? {
|
||||||
|
guard let snapshot = self.lastSnapshot else { return nil }
|
||||||
|
let raw = snapshot.server["version"]?.value as? String
|
||||||
|
let trimmed = raw?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) ?? ""
|
||||||
|
return trimmed.isEmpty ? nil : trimmed
|
||||||
|
}
|
||||||
|
|
||||||
func snapshotPaths() -> (configPath: String?, stateDir: String?) {
|
func snapshotPaths() -> (configPath: String?, stateDir: String?) {
|
||||||
guard let snapshot = self.lastSnapshot else { return (nil, nil) }
|
guard let snapshot = self.lastSnapshot else { return (nil, nil) }
|
||||||
let configPath = snapshot.snapshot.configpath?.trimmingCharacters(in: .whitespacesAndNewlines)
|
let configPath = snapshot.snapshot.configpath?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|||||||
@@ -747,8 +747,8 @@ extension MenuSessionsInjector {
|
|||||||
menu.addItem(self.makeNodeCopyItem(label: "Platform", value: platform))
|
menu.addItem(self.makeNodeCopyItem(label: "Platform", value: platform))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let version = entry.version?.nonEmpty {
|
if let version = NodeMenuEntryFormatter.detailRightVersion(entry)?.nonEmpty {
|
||||||
menu.addItem(self.makeNodeCopyItem(label: "Version", value: self.formatVersionLabel(version)))
|
menu.addItem(self.makeNodeCopyItem(label: "Version", value: version))
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.addItem(self.makeNodeDetailItem(label: "Connected", value: entry.isConnected ? "Yes" : "No"))
|
menu.addItem(self.makeNodeDetailItem(label: "Connected", value: entry.isConnected ? "Yes" : "No"))
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ actor MacNodeBridgePairingClient {
|
|||||||
displayName: hello.displayName,
|
displayName: hello.displayName,
|
||||||
platform: hello.platform,
|
platform: hello.platform,
|
||||||
version: hello.version,
|
version: hello.version,
|
||||||
|
coreVersion: hello.coreVersion,
|
||||||
|
uiVersion: hello.uiVersion,
|
||||||
deviceFamily: hello.deviceFamily,
|
deviceFamily: hello.deviceFamily,
|
||||||
modelIdentifier: hello.modelIdentifier,
|
modelIdentifier: hello.modelIdentifier,
|
||||||
caps: hello.caps,
|
caps: hello.caps,
|
||||||
|
|||||||
@@ -114,12 +114,19 @@ final class MacNodeModeCoordinator {
|
|||||||
let caps = self.currentCaps()
|
let caps = self.currentCaps()
|
||||||
let commands = self.currentCommands(caps: caps)
|
let commands = self.currentCommands(caps: caps)
|
||||||
let permissions = await self.currentPermissions()
|
let permissions = await self.currentPermissions()
|
||||||
|
let uiVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
||||||
|
let liveGatewayVersion = await GatewayConnection.shared.cachedGatewayVersion()
|
||||||
|
let fallbackGatewayVersion = GatewayProcessManager.shared.environmentStatus.gatewayVersion
|
||||||
|
let coreVersion = (liveGatewayVersion ?? fallbackGatewayVersion)?
|
||||||
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
return BridgeHello(
|
return BridgeHello(
|
||||||
nodeId: Self.nodeId(),
|
nodeId: Self.nodeId(),
|
||||||
displayName: InstanceIdentity.displayName,
|
displayName: InstanceIdentity.displayName,
|
||||||
token: token,
|
token: token,
|
||||||
platform: "macos",
|
platform: "macos",
|
||||||
version: Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String,
|
version: uiVersion,
|
||||||
|
coreVersion: coreVersion?.isEmpty == false ? coreVersion : nil,
|
||||||
|
uiVersion: uiVersion,
|
||||||
deviceFamily: "Mac",
|
deviceFamily: "Mac",
|
||||||
modelIdentifier: InstanceIdentity.modelIdentifier,
|
modelIdentifier: InstanceIdentity.modelIdentifier,
|
||||||
caps: caps,
|
caps: caps,
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ struct NodeMenuEntryFormatter {
|
|||||||
if let platform = self.platformText(entry) {
|
if let platform = self.platformText(entry) {
|
||||||
parts.append("platform \(platform)")
|
parts.append("platform \(platform)")
|
||||||
}
|
}
|
||||||
if let version = entry.version?.nonEmpty {
|
let versionLabels = self.versionLabels(entry)
|
||||||
parts.append("app \(self.compactVersion(version))")
|
if !versionLabels.isEmpty {
|
||||||
|
parts.append(versionLabels.joined(separator: " · "))
|
||||||
}
|
}
|
||||||
parts.append("status \(self.roleText(entry))")
|
parts.append("status \(self.roleText(entry))")
|
||||||
return parts.joined(separator: " · ")
|
return parts.joined(separator: " · ")
|
||||||
@@ -60,8 +61,9 @@ struct NodeMenuEntryFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func detailRightVersion(_ entry: NodeInfo) -> String? {
|
static func detailRightVersion(_ entry: NodeInfo) -> String? {
|
||||||
guard let version = entry.version?.nonEmpty else { return nil }
|
let labels = self.versionLabels(entry, compact: false)
|
||||||
return self.shortVersionLabel(version)
|
if labels.isEmpty { return nil }
|
||||||
|
return labels.joined(separator: " · ")
|
||||||
}
|
}
|
||||||
|
|
||||||
static func platformText(_ entry: NodeInfo) -> String? {
|
static func platformText(_ entry: NodeInfo) -> String? {
|
||||||
@@ -127,6 +129,39 @@ struct NodeMenuEntryFormatter {
|
|||||||
return compact
|
return compact
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func versionLabels(_ entry: NodeInfo, compact: Bool = true) -> [String] {
|
||||||
|
let (core, ui) = self.resolveVersions(entry)
|
||||||
|
var labels: [String] = []
|
||||||
|
if let core {
|
||||||
|
let label = compact ? self.compactVersion(core) : self.shortVersionLabel(core)
|
||||||
|
labels.append("core \(label)")
|
||||||
|
}
|
||||||
|
if let ui {
|
||||||
|
let label = compact ? self.compactVersion(ui) : self.shortVersionLabel(ui)
|
||||||
|
labels.append("ui \(label)")
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func resolveVersions(_ entry: NodeInfo) -> (core: String?, ui: String?) {
|
||||||
|
let core = entry.coreVersion?.nonEmpty
|
||||||
|
let ui = entry.uiVersion?.nonEmpty
|
||||||
|
if core != nil || ui != nil {
|
||||||
|
return (core, ui)
|
||||||
|
}
|
||||||
|
guard let legacy = entry.version?.nonEmpty else { return (nil, nil) }
|
||||||
|
if self.isHeadlessPlatform(entry) {
|
||||||
|
return (legacy, nil)
|
||||||
|
}
|
||||||
|
return (nil, legacy)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func isHeadlessPlatform(_ entry: NodeInfo) -> Bool {
|
||||||
|
let raw = entry.platform?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() ?? ""
|
||||||
|
if raw == "darwin" || raw == "linux" || raw == "win32" || raw == "windows" { return true }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
static func leadingSymbol(_ entry: NodeInfo) -> String {
|
static func leadingSymbol(_ entry: NodeInfo) -> String {
|
||||||
if self.isGateway(entry) {
|
if self.isGateway(entry) {
|
||||||
return self.safeSystemSymbol(
|
return self.safeSystemSymbol(
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ struct NodeInfo: Identifiable, Codable {
|
|||||||
let displayName: String?
|
let displayName: String?
|
||||||
let platform: String?
|
let platform: String?
|
||||||
let version: String?
|
let version: String?
|
||||||
|
let coreVersion: String?
|
||||||
|
let uiVersion: String?
|
||||||
let deviceFamily: String?
|
let deviceFamily: String?
|
||||||
let modelIdentifier: String?
|
let modelIdentifier: String?
|
||||||
let remoteIp: String?
|
let remoteIp: String?
|
||||||
|
|||||||
@@ -530,6 +530,8 @@ public struct NodePairRequestParams: Codable, Sendable {
|
|||||||
public let displayname: String?
|
public let displayname: String?
|
||||||
public let platform: String?
|
public let platform: String?
|
||||||
public let version: String?
|
public let version: String?
|
||||||
|
public let coreversion: String?
|
||||||
|
public let uiversion: String?
|
||||||
public let devicefamily: String?
|
public let devicefamily: String?
|
||||||
public let modelidentifier: String?
|
public let modelidentifier: String?
|
||||||
public let caps: [String]?
|
public let caps: [String]?
|
||||||
@@ -542,6 +544,8 @@ public struct NodePairRequestParams: Codable, Sendable {
|
|||||||
displayname: String?,
|
displayname: String?,
|
||||||
platform: String?,
|
platform: String?,
|
||||||
version: String?,
|
version: String?,
|
||||||
|
coreversion: String?,
|
||||||
|
uiversion: String?,
|
||||||
devicefamily: String?,
|
devicefamily: String?,
|
||||||
modelidentifier: String?,
|
modelidentifier: String?,
|
||||||
caps: [String]?,
|
caps: [String]?,
|
||||||
@@ -553,6 +557,8 @@ public struct NodePairRequestParams: Codable, Sendable {
|
|||||||
self.displayname = displayname
|
self.displayname = displayname
|
||||||
self.platform = platform
|
self.platform = platform
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.coreversion = coreversion
|
||||||
|
self.uiversion = uiversion
|
||||||
self.devicefamily = devicefamily
|
self.devicefamily = devicefamily
|
||||||
self.modelidentifier = modelidentifier
|
self.modelidentifier = modelidentifier
|
||||||
self.caps = caps
|
self.caps = caps
|
||||||
@@ -565,6 +571,8 @@ public struct NodePairRequestParams: Codable, Sendable {
|
|||||||
case displayname = "displayName"
|
case displayname = "displayName"
|
||||||
case platform
|
case platform
|
||||||
case version
|
case version
|
||||||
|
case coreversion = "coreVersion"
|
||||||
|
case uiversion = "uiVersion"
|
||||||
case devicefamily = "deviceFamily"
|
case devicefamily = "deviceFamily"
|
||||||
case modelidentifier = "modelIdentifier"
|
case modelidentifier = "modelIdentifier"
|
||||||
case caps
|
case caps
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ public struct BridgeHello: Codable, Sendable {
|
|||||||
public let token: String?
|
public let token: String?
|
||||||
public let platform: String?
|
public let platform: String?
|
||||||
public let version: String?
|
public let version: String?
|
||||||
|
public let coreVersion: String?
|
||||||
|
public let uiVersion: String?
|
||||||
public let deviceFamily: String?
|
public let deviceFamily: String?
|
||||||
public let modelIdentifier: String?
|
public let modelIdentifier: String?
|
||||||
public let caps: [String]?
|
public let caps: [String]?
|
||||||
@@ -76,6 +78,8 @@ public struct BridgeHello: Codable, Sendable {
|
|||||||
token: String?,
|
token: String?,
|
||||||
platform: String?,
|
platform: String?,
|
||||||
version: String?,
|
version: String?,
|
||||||
|
coreVersion: String? = nil,
|
||||||
|
uiVersion: String? = nil,
|
||||||
deviceFamily: String? = nil,
|
deviceFamily: String? = nil,
|
||||||
modelIdentifier: String? = nil,
|
modelIdentifier: String? = nil,
|
||||||
caps: [String]? = nil,
|
caps: [String]? = nil,
|
||||||
@@ -88,6 +92,8 @@ public struct BridgeHello: Codable, Sendable {
|
|||||||
self.token = token
|
self.token = token
|
||||||
self.platform = platform
|
self.platform = platform
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.coreVersion = coreVersion
|
||||||
|
self.uiVersion = uiVersion
|
||||||
self.deviceFamily = deviceFamily
|
self.deviceFamily = deviceFamily
|
||||||
self.modelIdentifier = modelIdentifier
|
self.modelIdentifier = modelIdentifier
|
||||||
self.caps = caps
|
self.caps = caps
|
||||||
@@ -121,6 +127,8 @@ public struct BridgePairRequest: Codable, Sendable {
|
|||||||
public let displayName: String?
|
public let displayName: String?
|
||||||
public let platform: String?
|
public let platform: String?
|
||||||
public let version: String?
|
public let version: String?
|
||||||
|
public let coreVersion: String?
|
||||||
|
public let uiVersion: String?
|
||||||
public let deviceFamily: String?
|
public let deviceFamily: String?
|
||||||
public let modelIdentifier: String?
|
public let modelIdentifier: String?
|
||||||
public let caps: [String]?
|
public let caps: [String]?
|
||||||
@@ -135,6 +143,8 @@ public struct BridgePairRequest: Codable, Sendable {
|
|||||||
displayName: String?,
|
displayName: String?,
|
||||||
platform: String?,
|
platform: String?,
|
||||||
version: String?,
|
version: String?,
|
||||||
|
coreVersion: String? = nil,
|
||||||
|
uiVersion: String? = nil,
|
||||||
deviceFamily: String? = nil,
|
deviceFamily: String? = nil,
|
||||||
modelIdentifier: String? = nil,
|
modelIdentifier: String? = nil,
|
||||||
caps: [String]? = nil,
|
caps: [String]? = nil,
|
||||||
@@ -148,6 +158,8 @@ public struct BridgePairRequest: Codable, Sendable {
|
|||||||
self.displayName = displayName
|
self.displayName = displayName
|
||||||
self.platform = platform
|
self.platform = platform
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.coreVersion = coreVersion
|
||||||
|
self.uiVersion = uiVersion
|
||||||
self.deviceFamily = deviceFamily
|
self.deviceFamily = deviceFamily
|
||||||
self.modelIdentifier = modelIdentifier
|
self.modelIdentifier = modelIdentifier
|
||||||
self.caps = caps
|
self.caps = caps
|
||||||
|
|||||||
Reference in New Issue
Block a user