Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ nonisolated enum CloudActionCatalog: ActionCatalogGroup {
"cloudTunnelAttach", "cloudTunnelDetach", "cloudTunnelRotateKey", "cloudNetworkList",
"cloudFirewallList", "cloudFirewallGet", "cloudFirewallCreate", "cloudFirewallDelete",
"cloudDomainList", "cloudPublicationList",
// A CLI or agent run answers with the result: the machine's facts, the command's
// output, the new machine's or workspace's id (CloudHandlers+Results.swift).
"palette.cloud.status", "palette.cloud.ports", "palette.cloud.tools", "palette.cloud.handoff",
"palette.cloud.snapshot", "palette.cloud.promoteTemplate", "cloudExec", "newCloudMachine", "cloudNewTerminal",
]
for index in actions.indices where waitsForResult.contains(actions[index].id) {
actions[index].waitsForResult = true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation

/// What a CLI or agent run of a Cloud action hears back. Keys live in
/// Resources/Cloud.xcstrings with every app locale.
extension CloudStrings {
/// `cloud new-machine` from a CLI or agent: the person said no in the confirmation, or
/// dismissed the creation, so no machine was made.
static var createDeclined: String {
String(localized: "cloud.cli.createDeclined", defaultValue: "No Cloud machine was created: the person declined or dismissed it in cmux.",
table: "Cloud", bundle: .module)
}

/// `cloud exec` from a CLI or agent on a machine whose provider runs no commands through the
/// API: typing it into a tab would give the caller nothing to read.
static var execUnavailable: String {
String(localized: "cloud.cli.execUnavailable",
defaultValue: "This Cloud machine can't run a command for the CLI and return its output. Open a terminal on it with cmux cloud open-machine instead.",
table: "Cloud", bundle: .module)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ extension CloudHandlers {
bind("newCloudMachine", registry, reason: reason) { invocation in
// The new machine's progress shows in the window at once; its
// first workspace opens there when its daemon is ready.
creationFlow(context, startedByPerson: invocation.origin == .user).start(in: invocation.allowsViewChange ? context.services.windows.active?.state : nil, existing: nil)
let creation = creationFlow(context, startedByPerson: invocation.origin == .user).start(in: invocation.allowsViewChange ? context.services.windows.active?.state : nil, existing: nil)
reportCreatedMachine(creation, context)
}
bind("newCloudWorkspace", registry, reason: reason) { invocation in
let target = try? machine(invocation, context)
// Keeps the active-window fallback for any caller: it only opens a workspace,
// and with no machine a CLI run would start a new one instead.
let target = try? machine(invocation, context, fallsBack: true)
let window = invocation.allowsViewChange ? context.services.windows.active?.state : nil
guard let session = target, session.daemon.connection != nil else {
// A new machine, or one still on its way: show its progress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@ import CmuxNextCloud
extension CloudHandlers {
static func bindDomainActions(into registry: ActionRegistry, context: AppActionContext, reason: @escaping @MainActor () -> String?) {
let cloud = context.services.cloud
bind("cloudDomainList", registry, reason: reason) { _ in
runTracked("list Cloud domains", context) {
bind("cloudDomainList", registry, reason: reason) { invocation in
answer("list Cloud domains", invocation, context) {
let domains = try await cloud.api.listDomains()
let lines = domains.map { domain in
let name = domain.hostname ?? domain.id ?? "(unnamed)"
let verification = domain.verificationState ?? "unknown"
let certificate = domain.certificateState ?? "unknown"
return "\(name) (verification: \(verification), certificate: \(certificate))"
}
CloudPresenter.show("Cloud Domains", lines.isEmpty ? "(none)" : lines.joined(separator: "\n"), copyable: true, in: window(context))
return CloudAnswer(title: "Cloud Domains", text: lines.isEmpty ? "(none)" : lines.joined(separator: "\n"), fields: ["count": .int(domains.count)])
}
}
bind("cloudPublicationList", registry, reason: reason) { _ in
runTracked("list Cloud publications", context) {
bind("cloudPublicationList", registry, reason: reason) { invocation in
answer("list Cloud publications", invocation, context) {
let publications = try await cloud.api.listPublications()
let lines = publications.map { publication in
let name = publication.hostname ?? publication.id ?? "(unnamed)"
let state = publication.state ?? "unknown"
let access = publication.accessMode.map { ", access: \($0)" } ?? ""
return "\(name) (\(state)\(access))"
}
CloudPresenter.show("Cloud Publications", lines.isEmpty ? "(none)" : lines.joined(separator: "\n"), copyable: true, in: window(context))
return CloudAnswer(title: "Cloud Publications", text: lines.isEmpty ? "(none)" : lines.joined(separator: "\n"), fields: ["count": .int(publications.count)])
}
}
}
Expand Down
Loading