Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2ce6323
test: reserve cloud machine row space for resource metrics
austinywang Sep 13, 2026
b88e525
feat: give cloud machines prominent CPU RAM and disk readings
austinywang Sep 13, 2026
334f7cd
fix: keep full resource percentages visible in narrow rows
austinywang Sep 13, 2026
ba47476
test: require live usage from Freestyle machine stats
austinywang Sep 13, 2026
9768d45
feat: collect cloud resource usage from awake guests
austinywang Sep 13, 2026
24d634a
Merge main and preserve starred resource rows
austinywang Sep 13, 2026
f7a183f
fix: use scoped cloud workspace group defaults
austinywang Sep 13, 2026
85321a3
Merge remote-tracking branch 'origin/main' into issue-12437-machine-r…
austinywang Sep 13, 2026
4c2611f
docs: cover the cloud diagnostics socket method
austinywang Sep 13, 2026
d4dfa3d
test: align startup and network guards with current flows
austinywang Sep 13, 2026
cac0a53
test: cover reporter setup failure and concurrent samples
austinywang Sep 13, 2026
267c42d
fix: isolate reporter failures and coalesce samples atomically
austinywang Sep 13, 2026
dd4c3a7
test: use a cancellable publication database deadline
austinywang Sep 13, 2026
c0fcb84
Merge origin/main and resolve resource metrics conflicts
austinywang Sep 13, 2026
6b2a525
Fix compiler warnings blocking merged branch CI
austinywang Sep 13, 2026
470e754
Construct cloud banner defaults on the main actor
austinywang Sep 13, 2026
e2eeb5f
Merge latest main and preserve cloud initializer fix
austinywang Sep 13, 2026
d594a7e
test: expose lost resize resource confirmations
austinywang Sep 13, 2026
4c8e0b5
fix: address resource metrics review and CI findings
austinywang Sep 13, 2026
5f2f3a8
fix: quote the resource snapshot adapter project path
austinywang Sep 13, 2026
4bcbe02
test: simplify machine menu assertions for Swift Testing
austinywang Sep 13, 2026
7d9684d
test: format localized resize menu expectations
austinywang Sep 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLI/CMUXCLI+AgentHookDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ extension CMUXCLI {
noOpCommand: String = "echo '{}'",
failOpen: Bool = false
) -> String {
guard case .pinned(let marker) = def.dispatch else {
guard case .pinned = def.dispatch else {
return agentHookShellCommand(command, for: def, noOpCommand: noOpCommand)
}
let routedArguments = command.hasPrefix("cmux ") ? String(command.dropFirst("cmux ".count)) : command
Expand Down
2 changes: 0 additions & 2 deletions CLI/cmux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27878,7 +27878,6 @@ struct CMUXCLI {
let workspaceId = resolvedTarget.workspaceId
let resolvedSurface = resolvedTarget
let surfaceId = resolvedSurface.surfaceId
let claudePid = localClaudePID(mapped: mappedSession)
// Detected once (bounded process-ancestry walk) and reused for
// both the suppression gate and the notify payload's subagent
// tag, which stays accurate even when suppression is off.
Expand Down Expand Up @@ -39498,7 +39497,6 @@ export default CMUXSessionRestore;
socketPassword: String? = nil,
telemetry: CLISocketSentryTelemetry
) throws {
let invocationStartedAt = ProcessInfo.processInfo.systemUptime
_ = telemetry
let source = optionValue(commandArgs, name: "--source") ?? ""
guard !source.isEmpty else {
Expand Down
10 changes: 10 additions & 0 deletions Packages/macOS/CmuxCloudMachines/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@ let coordinator = CloudWorkspaceCoordinator(
)
let workspaceID = try await coordinator.createOnDefaultMachine(focus: true)
```

`CloudMachineResourcePresentation` validates and formats CPU, memory, and disk samples independently of app/provider types. The app maps its immutable machine snapshot at the UI boundary; missing and sleeping samples remain explicit. Localized labels use the host application's catalog.

```swift
let resources = CloudMachineResourcePresentation(
availability: .awake, cpuPercent: 25,
memoryUsedMb: 2048, memoryTotalMb: 4096
)
// resources.memory.percent == 50
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import Foundation

/// Pure presentation of the latest stats snapshot, shared by the row and its tooltip.
public struct CloudMachineResourcePresentation: Sendable {
/// Whether the latest sample can be presented as live usage.
public enum Availability: Equatable, Sendable {
/// The machine is awake and supports resource statistics.
case awake
/// The machine is sleeping, so prior readings are unavailable.
case asleep
/// A supported, current sample is not available.
case unavailable
}

/// One localized resource label, optional percentage, and accessible detail.
public struct Reading: Sendable {
/// The localized name of this resource.
public let label: String
/// A validated utilization percentage, or nil for an unavailable reading.
public let percent: Double?
/// Localized detail suitable for a tooltip or accessibility label.
public let detail: String

/// A whole percentage or the localized missing-value placeholder.
public var value: String {
guard let percent else {
return String(localized: "cloudTree.resources.missing", defaultValue: "—")
}
return (percent / 100).formatted(.percent.precision(.fractionLength(0)))
}
}

/// Current CPU utilization.
public let cpu: Reading
/// Current memory utilization and reported capacity.
public let memory: Reading
/// Current root disk utilization and reported capacity.
public let disk: Reading

/// The three resource details in display order, separated by newlines.
public var summary: String { [cpu.detail, memory.detail, disk.detail].joined(separator: "\n") }

/// Presents advisory resource readings without depending on app or provider types.
///
/// Localized text uses the host application's string catalog.
///
/// ```swift
/// let resources = CloudMachineResourcePresentation(availability: .awake, cpuPercent: 25)
/// // resources.cpu.percent == 25
/// ```
///
/// - Parameters:
/// - availability: Whether the sample is live, sleeping, or unavailable.
/// - cpuPercent: CPU utilization in the inclusive range 0 through 100.
/// - memoryUsedMb: Used memory in MiB, when sampled.
/// - memoryTotalMb: Provisioned memory in MiB, when known.
/// - diskUsedMb: Used root filesystem space in MiB, when sampled.
/// - diskTotalMb: Root filesystem capacity in MiB, when known.
public init(
availability: Availability,
cpuPercent: Double? = nil,
memoryUsedMb: Int? = nil,
memoryTotalMb: Int? = nil,
diskUsedMb: Int? = nil,
diskTotalMb: Int? = nil
) {
let available = availability == .awake
let unavailable = availability == .asleep
? String(localized: "cloudTree.resources.asleep", defaultValue: "Asleep")
: String(localized: "cloudTree.resources.unavailable", defaultValue: "Unavailable")
let cpuLabel = String(localized: "machines.stats.cpu", defaultValue: "CPU")
let cpuPercent = available ? cpuPercent.flatMap(Self.validCPU) : nil
cpu = Reading(
label: cpuLabel,
percent: cpuPercent,
detail: cpuPercent.map {
String(format: String(localized: "cloudTree.stats.cpu", defaultValue: "CPU %d%%"), Int($0.rounded()))
} ?? "\(cpuLabel): \(unavailable)"
)
memory = Self.capacity(
label: String(localized: "cloudTree.resources.ram", defaultValue: "RAM"),
used: available ? memoryUsedMb : nil,
total: available ? memoryTotalMb : nil,
format: String(localized: "cloudTree.stats.memory", defaultValue: "Mem %@/%@ GB"),
unavailable: unavailable
)
disk = Self.capacity(
label: String(localized: "machines.stats.disk", defaultValue: "Disk"),
used: available ? diskUsedMb : nil,
total: available ? diskTotalMb : nil,
format: String(localized: "cloudTree.stats.disk", defaultValue: "Disk %@/%@ GB"),
unavailable: unavailable
)
}

private static func validCPU(_ percent: Double) -> Double? {
guard percent.isFinite, (0...100).contains(percent) else { return nil }
return percent
}

private static func capacity(label: String, used: Int?, total: Int?, format: String, unavailable: String) -> Reading {
guard let used, let total, used >= 0, total > 0 else {
return Reading(label: label, percent: nil, detail: "\(label): \(unavailable)")
}
// Separate OS counters may straddle an update. Keep the percentage within capacity
// while preserving the actual reported amounts in the detail.
let percent = min(100, Double(used) / Double(total) * 100)
let value = Reading(label: label, percent: percent, detail: "").value
return Reading(
label: label,
percent: percent,
detail: "\(String(format: format, gb(used), gb(total))) (\(value))"
)
}

private static func gb(_ mb: Int) -> String {
(Double(mb) / 1024).formatted(.number.precision(.fractionLength(0...1)))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Foundation
import Testing
@testable import CmuxCloudMachines

@Suite("Cloud machine resource readings")
struct CloudMachineResourcePresentationTests {
@Test func awakeReadingsDescribeUsageRatherThanProvisionedCapacity() {
let result = CloudMachineResourcePresentation(
availability: .awake, cpuPercent: 9.4,
memoryUsedMb: 2048, memoryTotalMb: 4096,
diskUsedMb: 3072, diskTotalMb: 4096
)
#expect(result.cpu.percent == 9.4)
#expect(result.memory.percent == 50)
#expect(result.disk.percent == 75)
#expect(result.cpu.value == (0.094).formatted(.percent.precision(.fractionLength(0))))
#expect(result.memory.detail.contains("2/4"))
#expect(result.disk.detail.contains("3/4"))
}

@Test func zeroIsARealReadingAndMissingSamplesKeepTheirLabels() {
let result = CloudMachineResourcePresentation(
availability: .awake, cpuPercent: 0, memoryTotalMb: 4096,
diskUsedMb: 0, diskTotalMb: 4096
)
#expect(result.cpu.percent == 0)
#expect(result.memory.percent == nil)
#expect(result.disk.percent == 0)
#expect(result.cpu.value != result.memory.value)
#expect(!result.memory.label.isEmpty)
#expect(!result.memory.detail.isEmpty)
}

@Test(arguments: [CloudMachineResourcePresentation.Availability.asleep, .unavailable])
func inactiveSamplesNeverPresentOldValuesAsLive(availability: CloudMachineResourcePresentation.Availability) {
let result = CloudMachineResourcePresentation(
availability: availability, cpuPercent: 83,
memoryUsedMb: 2048, memoryTotalMb: 4096,
diskUsedMb: 3072, diskTotalMb: 4096
)
#expect(result.cpu.percent == nil)
#expect(result.memory.percent == nil)
#expect(result.disk.percent == nil)
}

@Test(arguments: [Double.nan, .infinity, -.infinity, -1, 101, .greatestFiniteMagnitude])
func malformedCPUIsUnavailableWithoutTrapping(cpu: Double) {
let result = CloudMachineResourcePresentation(availability: .awake, cpuPercent: cpu)
#expect(result.cpu.percent == nil)
#expect(!result.cpu.value.isEmpty)
}

@Test(arguments: [(0, 0), (2, -1), (-1, 1024)])
func invalidCapacityIsUnavailable(counts: (Int, Int)) {
let result = CloudMachineResourcePresentation(
availability: .awake, memoryUsedMb: counts.0, memoryTotalMb: counts.1,
diskUsedMb: counts.0, diskTotalMb: counts.1
)
#expect(result.memory.percent == nil)
#expect(result.disk.percent == nil)
}

@Test func capacityCounterRacesStayBounded() {
let result = CloudMachineResourcePresentation(
availability: .awake, memoryUsedMb: 4097, memoryTotalMb: 4096,
diskUsedMb: .max, diskTotalMb: 4096
)
#expect(result.memory.percent == 100)
#expect(result.disk.percent == 100)
}
}
Loading
Loading