refactor: rename to openclaw
This commit is contained in:
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
@@ -6,8 +6,8 @@
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"image-name" : "moltbot-mac.png",
|
||||
"name" : "moltbot-mac",
|
||||
"image-name" : "openclaw-mac.png",
|
||||
"name" : "openclaw-mac",
|
||||
"position" : {
|
||||
"scale" : 1.07,
|
||||
"translation-in-points" : [
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// swift-tools-version: 6.2
|
||||
// Package manifest for the Moltbot macOS companion (menu bar app + IPC library).
|
||||
// Package manifest for the OpenClaw macOS companion (menu bar app + IPC library).
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Moltbot",
|
||||
name: "OpenClaw",
|
||||
platforms: [
|
||||
.macOS(.v15),
|
||||
],
|
||||
products: [
|
||||
.library(name: "MoltbotIPC", targets: ["MoltbotIPC"]),
|
||||
.library(name: "MoltbotDiscovery", targets: ["MoltbotDiscovery"]),
|
||||
.executable(name: "Moltbot", targets: ["Moltbot"]),
|
||||
.executable(name: "moltbot-mac", targets: ["MoltbotMacCLI"]),
|
||||
.library(name: "OpenClawIPC", targets: ["OpenClawIPC"]),
|
||||
.library(name: "OpenClawDiscovery", targets: ["OpenClawDiscovery"]),
|
||||
.executable(name: "OpenClaw", targets: ["OpenClaw"]),
|
||||
.executable(name: "openclaw-mac", targets: ["OpenClawMacCLI"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/orchetect/MenuBarExtraAccess", exact: "1.2.2"),
|
||||
@@ -20,33 +20,33 @@ let package = Package(
|
||||
.package(url: "https://github.com/apple/swift-log.git", from: "1.8.0"),
|
||||
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.8.1"),
|
||||
.package(url: "https://github.com/steipete/Peekaboo.git", branch: "main"),
|
||||
.package(path: "../shared/MoltbotKit"),
|
||||
.package(path: "../shared/OpenClawKit"),
|
||||
.package(path: "../../Swabble"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "MoltbotIPC",
|
||||
name: "OpenClawIPC",
|
||||
dependencies: [],
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.target(
|
||||
name: "MoltbotDiscovery",
|
||||
name: "OpenClawDiscovery",
|
||||
dependencies: [
|
||||
.product(name: "MoltbotKit", package: "MoltbotKit"),
|
||||
.product(name: "OpenClawKit", package: "OpenClawKit"),
|
||||
],
|
||||
path: "Sources/MoltbotDiscovery",
|
||||
path: "Sources/OpenClawDiscovery",
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.executableTarget(
|
||||
name: "Moltbot",
|
||||
name: "OpenClaw",
|
||||
dependencies: [
|
||||
"MoltbotIPC",
|
||||
"MoltbotDiscovery",
|
||||
.product(name: "MoltbotKit", package: "MoltbotKit"),
|
||||
.product(name: "MoltbotChatUI", package: "MoltbotKit"),
|
||||
.product(name: "MoltbotProtocol", package: "MoltbotKit"),
|
||||
"OpenClawIPC",
|
||||
"OpenClawDiscovery",
|
||||
.product(name: "OpenClawKit", package: "OpenClawKit"),
|
||||
.product(name: "OpenClawChatUI", package: "OpenClawKit"),
|
||||
.product(name: "OpenClawProtocol", package: "OpenClawKit"),
|
||||
.product(name: "SwabbleKit", package: "swabble"),
|
||||
.product(name: "MenuBarExtraAccess", package: "MenuBarExtraAccess"),
|
||||
.product(name: "Subprocess", package: "swift-subprocess"),
|
||||
@@ -59,30 +59,30 @@ let package = Package(
|
||||
"Resources/Info.plist",
|
||||
],
|
||||
resources: [
|
||||
.copy("Resources/Moltbot.icns"),
|
||||
.copy("Resources/OpenClaw.icns"),
|
||||
.copy("Resources/DeviceModels"),
|
||||
],
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.executableTarget(
|
||||
name: "MoltbotMacCLI",
|
||||
name: "OpenClawMacCLI",
|
||||
dependencies: [
|
||||
"MoltbotDiscovery",
|
||||
.product(name: "MoltbotKit", package: "MoltbotKit"),
|
||||
.product(name: "MoltbotProtocol", package: "MoltbotKit"),
|
||||
"OpenClawDiscovery",
|
||||
.product(name: "OpenClawKit", package: "OpenClawKit"),
|
||||
.product(name: "OpenClawProtocol", package: "OpenClawKit"),
|
||||
],
|
||||
path: "Sources/MoltbotMacCLI",
|
||||
path: "Sources/OpenClawMacCLI",
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.testTarget(
|
||||
name: "MoltbotIPCTests",
|
||||
name: "OpenClawIPCTests",
|
||||
dependencies: [
|
||||
"MoltbotIPC",
|
||||
"Moltbot",
|
||||
"MoltbotDiscovery",
|
||||
.product(name: "MoltbotProtocol", package: "MoltbotKit"),
|
||||
"OpenClawIPC",
|
||||
"OpenClaw",
|
||||
"OpenClawDiscovery",
|
||||
.product(name: "OpenClawProtocol", package: "OpenClawKit"),
|
||||
.product(name: "SwabbleKit", package: "swabble"),
|
||||
],
|
||||
swiftSettings: [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Moltbot macOS app (dev + signing)
|
||||
# OpenClaw macOS app (dev + signing)
|
||||
|
||||
## Quick dev run
|
||||
|
||||
@@ -20,7 +20,7 @@ scripts/restart-mac.sh --sign # force code signing (requires cert)
|
||||
scripts/package-mac-app.sh
|
||||
```
|
||||
|
||||
Creates `dist/Moltbot.app` and signs it via `scripts/codesign-mac-app.sh`.
|
||||
Creates `dist/OpenClaw.app` and signs it via `scripts/codesign-mac-app.sh`.
|
||||
|
||||
## Signing behavior
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
enum MoltbotEnv {
|
||||
static func path(_ key: String) -> String? {
|
||||
// Normalize env overrides once so UI + file IO stay consistent.
|
||||
guard let raw = getenv(key) else { return nil }
|
||||
let value = String(cString: raw).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !value.isEmpty
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
enum MoltbotPaths {
|
||||
private static let configPathEnv = "CLAWDBOT_CONFIG_PATH"
|
||||
private static let stateDirEnv = "CLAWDBOT_STATE_DIR"
|
||||
|
||||
static var stateDirURL: URL {
|
||||
if let override = MoltbotEnv.path(self.stateDirEnv) {
|
||||
return URL(fileURLWithPath: override, isDirectory: true)
|
||||
}
|
||||
return FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".clawdbot", isDirectory: true)
|
||||
}
|
||||
|
||||
static var configURL: URL {
|
||||
if let override = MoltbotEnv.path(self.configPathEnv) {
|
||||
return URL(fileURLWithPath: override)
|
||||
}
|
||||
return self.stateDirURL.appendingPathComponent("moltbot.json")
|
||||
}
|
||||
|
||||
static var workspaceURL: URL {
|
||||
self.stateDirURL.appendingPathComponent("workspace", isDirectory: true)
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
let launchdLabel = "bot.molt.mac"
|
||||
let gatewayLaunchdLabel = "bot.molt.gateway"
|
||||
let onboardingVersionKey = "moltbot.onboardingVersion"
|
||||
let currentOnboardingVersion = 7
|
||||
let pauseDefaultsKey = "moltbot.pauseEnabled"
|
||||
let iconAnimationsEnabledKey = "moltbot.iconAnimationsEnabled"
|
||||
let swabbleEnabledKey = "moltbot.swabbleEnabled"
|
||||
let swabbleTriggersKey = "moltbot.swabbleTriggers"
|
||||
let voiceWakeTriggerChimeKey = "moltbot.voiceWakeTriggerChime"
|
||||
let voiceWakeSendChimeKey = "moltbot.voiceWakeSendChime"
|
||||
let showDockIconKey = "moltbot.showDockIcon"
|
||||
let defaultVoiceWakeTriggers = ["clawd", "claude"]
|
||||
let voiceWakeMaxWords = 32
|
||||
let voiceWakeMaxWordLength = 64
|
||||
let voiceWakeMicKey = "moltbot.voiceWakeMicID"
|
||||
let voiceWakeMicNameKey = "moltbot.voiceWakeMicName"
|
||||
let voiceWakeLocaleKey = "moltbot.voiceWakeLocaleID"
|
||||
let voiceWakeAdditionalLocalesKey = "moltbot.voiceWakeAdditionalLocaleIDs"
|
||||
let voicePushToTalkEnabledKey = "moltbot.voicePushToTalkEnabled"
|
||||
let talkEnabledKey = "moltbot.talkEnabled"
|
||||
let iconOverrideKey = "moltbot.iconOverride"
|
||||
let connectionModeKey = "moltbot.connectionMode"
|
||||
let remoteTargetKey = "moltbot.remoteTarget"
|
||||
let remoteIdentityKey = "moltbot.remoteIdentity"
|
||||
let remoteProjectRootKey = "moltbot.remoteProjectRoot"
|
||||
let remoteCliPathKey = "moltbot.remoteCliPath"
|
||||
let canvasEnabledKey = "moltbot.canvasEnabled"
|
||||
let cameraEnabledKey = "moltbot.cameraEnabled"
|
||||
let systemRunPolicyKey = "moltbot.systemRunPolicy"
|
||||
let systemRunAllowlistKey = "moltbot.systemRunAllowlist"
|
||||
let systemRunEnabledKey = "moltbot.systemRunEnabled"
|
||||
let locationModeKey = "moltbot.locationMode"
|
||||
let locationPreciseKey = "moltbot.locationPreciseEnabled"
|
||||
let peekabooBridgeEnabledKey = "moltbot.peekabooBridgeEnabled"
|
||||
let deepLinkKeyKey = "moltbot.deepLinkKey"
|
||||
let modelCatalogPathKey = "moltbot.modelCatalogPath"
|
||||
let modelCatalogReloadKey = "moltbot.modelCatalogReload"
|
||||
let cliInstallPromptedVersionKey = "moltbot.cliInstallPromptedVersion"
|
||||
let heartbeatsEnabledKey = "moltbot.heartbeatsEnabled"
|
||||
let debugFileLogEnabledKey = "moltbot.debug.fileLogEnabled"
|
||||
let appLogLevelKey = "moltbot.debug.appLogLevel"
|
||||
let voiceWakeSupported: Bool = ProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 26
|
||||
@@ -1,5 +0,0 @@
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
|
||||
typealias ProtoAnyCodable = MoltbotProtocol.AnyCodable
|
||||
typealias KitAnyCodable = MoltbotKit.AnyCodable
|
||||
@@ -10,7 +10,7 @@ struct AboutSettings: View {
|
||||
VStack(spacing: 8) {
|
||||
let appIcon = NSApplication.shared.applicationIconImage ?? CritterIconRenderer.makeIcon(blink: 0)
|
||||
Button {
|
||||
if let url = URL(string: "https://github.com/moltbot/moltbot") {
|
||||
if let url = URL(string: "https://github.com/openclaw/openclaw") {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
} label: {
|
||||
@@ -29,7 +29,7 @@ struct AboutSettings: View {
|
||||
}
|
||||
|
||||
VStack(spacing: 3) {
|
||||
Text("Moltbot")
|
||||
Text("OpenClaw")
|
||||
.font(.title3.bold())
|
||||
Text("Version \(self.versionString)")
|
||||
.foregroundStyle(.secondary)
|
||||
@@ -49,8 +49,8 @@ struct AboutSettings: View {
|
||||
AboutLinkRow(
|
||||
icon: "chevron.left.slash.chevron.right",
|
||||
title: "GitHub",
|
||||
url: "https://github.com/moltbot/moltbot")
|
||||
AboutLinkRow(icon: "globe", title: "Website", url: "https://steipete.me")
|
||||
url: "https://github.com/openclaw/openclaw")
|
||||
AboutLinkRow(icon: "globe", title: "Website", url: "https://openclaw.ai")
|
||||
AboutLinkRow(icon: "bird", title: "Twitter", url: "https://twitter.com/steipete")
|
||||
AboutLinkRow(icon: "envelope", title: "Email", url: "mailto:peter@steipete.me")
|
||||
}
|
||||
@@ -108,7 +108,10 @@ struct AboutSettings: View {
|
||||
}
|
||||
|
||||
private var buildTimestamp: String? {
|
||||
guard let raw = Bundle.main.object(forInfoDictionaryKey: "MoltbotBuildTimestamp") as? String
|
||||
guard
|
||||
let raw =
|
||||
(Bundle.main.object(forInfoDictionaryKey: "OpenClawBuildTimestamp") as? String) ??
|
||||
(Bundle.main.object(forInfoDictionaryKey: "OpenClawBuildTimestamp") as? String)
|
||||
else { return nil }
|
||||
let parser = ISO8601DateFormatter()
|
||||
parser.formatOptions = [.withInternetDateTime]
|
||||
@@ -122,7 +125,9 @@ struct AboutSettings: View {
|
||||
}
|
||||
|
||||
private var gitCommit: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "MoltbotGitCommit") as? String ?? "unknown"
|
||||
(Bundle.main.object(forInfoDictionaryKey: "OpenClawGitCommit") as? String) ??
|
||||
(Bundle.main.object(forInfoDictionaryKey: "OpenClawGitCommit") as? String) ??
|
||||
"unknown"
|
||||
}
|
||||
|
||||
private var bundleID: String {
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
@@ -81,7 +81,7 @@ private struct EventRow: View {
|
||||
return f.string(from: date)
|
||||
}
|
||||
|
||||
private func prettyJSON(_ dict: [String: MoltbotProtocol.AnyCodable]) -> String? {
|
||||
private func prettyJSON(_ dict: [String: OpenClawProtocol.AnyCodable]) -> String? {
|
||||
let normalized = dict.mapValues { $0.value }
|
||||
guard JSONSerialization.isValidJSONObject(normalized),
|
||||
let data = try? JSONSerialization.data(withJSONObject: normalized, options: [.prettyPrinted]),
|
||||
@@ -99,8 +99,8 @@ struct AgentEventsWindow_Previews: PreviewProvider {
|
||||
stream: "tool",
|
||||
ts: Date().timeIntervalSince1970 * 1000,
|
||||
data: [
|
||||
"phase": MoltbotProtocol.AnyCodable("start"),
|
||||
"name": MoltbotProtocol.AnyCodable("bash"),
|
||||
"phase": OpenClawProtocol.AnyCodable("start"),
|
||||
"name": OpenClawProtocol.AnyCodable("bash"),
|
||||
],
|
||||
summary: nil)
|
||||
AgentEventStore.shared.append(sample)
|
||||
@@ -2,7 +2,7 @@ import Foundation
|
||||
import OSLog
|
||||
|
||||
enum AgentWorkspace {
|
||||
private static let logger = Logger(subsystem: "bot.molt", category: "workspace")
|
||||
private static let logger = Logger(subsystem: "ai.openclaw", category: "workspace")
|
||||
static let agentsFilename = "AGENTS.md"
|
||||
static let soulFilename = "SOUL.md"
|
||||
static let identityFilename = "IDENTITY.md"
|
||||
@@ -34,7 +34,7 @@ enum AgentWorkspace {
|
||||
|
||||
static func resolveWorkspaceURL(from userInput: String?) -> URL {
|
||||
let trimmed = userInput?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if trimmed.isEmpty { return MoltbotConfigFile.defaultWorkspaceURL() }
|
||||
if trimmed.isEmpty { return OpenClawConfigFile.defaultWorkspaceURL() }
|
||||
let expanded = (trimmed as NSString).expandingTildeInPath
|
||||
return URL(fileURLWithPath: expanded, isDirectory: true)
|
||||
}
|
||||
@@ -154,7 +154,7 @@ enum AgentWorkspace {
|
||||
|
||||
static func defaultTemplate() -> String {
|
||||
let fallback = """
|
||||
# AGENTS.md - Moltbot Workspace
|
||||
# AGENTS.md - OpenClaw Workspace
|
||||
|
||||
This folder is the assistant's working directory.
|
||||
|
||||
@@ -265,7 +265,7 @@ enum AgentWorkspace {
|
||||
- Timezone (optional)
|
||||
- Notes
|
||||
|
||||
3) ~/.clawdbot/moltbot.json
|
||||
3) ~/.openclaw/openclaw.json
|
||||
Set identity.name, identity.theme, identity.emoji to match IDENTITY.md.
|
||||
|
||||
## Cleanup
|
||||
@@ -6,7 +6,7 @@ import SwiftUI
|
||||
struct AnthropicAuthControls: View {
|
||||
let connectionMode: AppState.ConnectionMode
|
||||
|
||||
@State private var oauthStatus: MoltbotOAuthStore.AnthropicOAuthStatus = MoltbotOAuthStore.anthropicOAuthStatus()
|
||||
@State private var oauthStatus: OpenClawOAuthStore.AnthropicOAuthStatus = OpenClawOAuthStore.anthropicOAuthStatus()
|
||||
@State private var pkce: AnthropicOAuth.PKCE?
|
||||
@State private var code: String = ""
|
||||
@State private var busy = false
|
||||
@@ -42,10 +42,10 @@ struct AnthropicAuthControls: View {
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
Button("Reveal") {
|
||||
NSWorkspace.shared.activateFileViewerSelecting([MoltbotOAuthStore.oauthURL()])
|
||||
NSWorkspace.shared.activateFileViewerSelecting([OpenClawOAuthStore.oauthURL()])
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.disabled(!FileManager().fileExists(atPath: MoltbotOAuthStore.oauthURL().path))
|
||||
.disabled(!FileManager().fileExists(atPath: OpenClawOAuthStore.oauthURL().path))
|
||||
|
||||
Button("Refresh") {
|
||||
self.refresh()
|
||||
@@ -53,7 +53,7 @@ struct AnthropicAuthControls: View {
|
||||
.buttonStyle(.bordered)
|
||||
}
|
||||
|
||||
Text(MoltbotOAuthStore.oauthURL().path)
|
||||
Text(OpenClawOAuthStore.oauthURL().path)
|
||||
.font(.caption.monospaced())
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
@@ -130,8 +130,8 @@ struct AnthropicAuthControls: View {
|
||||
}
|
||||
|
||||
private func refresh() {
|
||||
let imported = MoltbotOAuthStore.importLegacyAnthropicOAuthIfNeeded()
|
||||
self.oauthStatus = MoltbotOAuthStore.anthropicOAuthStatus()
|
||||
let imported = OpenClawOAuthStore.importLegacyAnthropicOAuthIfNeeded()
|
||||
self.oauthStatus = OpenClawOAuthStore.anthropicOAuthStatus()
|
||||
if imported != nil {
|
||||
self.statusText = "Imported existing OAuth credentials."
|
||||
}
|
||||
@@ -172,11 +172,11 @@ struct AnthropicAuthControls: View {
|
||||
code: parsed.code,
|
||||
state: parsed.state,
|
||||
verifier: pkce.verifier)
|
||||
try MoltbotOAuthStore.saveAnthropicOAuth(creds)
|
||||
try OpenClawOAuthStore.saveAnthropicOAuth(creds)
|
||||
self.refresh()
|
||||
self.pkce = nil
|
||||
self.code = ""
|
||||
self.statusText = "Connected. Moltbot can now use Claude via OAuth."
|
||||
self.statusText = "Connected. OpenClaw can now use Claude via OAuth."
|
||||
} catch {
|
||||
self.statusText = "OAuth failed: \(error.localizedDescription)"
|
||||
}
|
||||
@@ -212,7 +212,7 @@ struct AnthropicAuthControls: View {
|
||||
extension AnthropicAuthControls {
|
||||
init(
|
||||
connectionMode: AppState.ConnectionMode,
|
||||
oauthStatus: MoltbotOAuthStore.AnthropicOAuthStatus,
|
||||
oauthStatus: OpenClawOAuthStore.AnthropicOAuthStatus,
|
||||
pkce: AnthropicOAuth.PKCE? = nil,
|
||||
code: String = "",
|
||||
busy: Bool = false,
|
||||
@@ -18,7 +18,7 @@ enum AnthropicAuthMode: Equatable {
|
||||
|
||||
var shortLabel: String {
|
||||
switch self {
|
||||
case .oauthFile: "OAuth (Moltbot token file)"
|
||||
case .oauthFile: "OAuth (OpenClaw token file)"
|
||||
case .oauthEnv: "OAuth (env var)"
|
||||
case .apiKeyEnv: "API key (env var)"
|
||||
case .missing: "Missing credentials"
|
||||
@@ -36,7 +36,7 @@ enum AnthropicAuthMode: Equatable {
|
||||
enum AnthropicAuthResolver {
|
||||
static func resolve(
|
||||
environment: [String: String] = ProcessInfo.processInfo.environment,
|
||||
oauthStatus: MoltbotOAuthStore.AnthropicOAuthStatus = MoltbotOAuthStore
|
||||
oauthStatus: OpenClawOAuthStore.AnthropicOAuthStatus = OpenClawOAuthStore
|
||||
.anthropicOAuthStatus()) -> AnthropicAuthMode
|
||||
{
|
||||
if oauthStatus.isConnected { return .oauthFile }
|
||||
@@ -58,7 +58,7 @@ enum AnthropicAuthResolver {
|
||||
}
|
||||
|
||||
enum AnthropicOAuth {
|
||||
private static let logger = Logger(subsystem: "bot.molt", category: "anthropic-oauth")
|
||||
private static let logger = Logger(subsystem: "ai.openclaw", category: "anthropic-oauth")
|
||||
|
||||
private static let clientId = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
|
||||
private static let authorizeURL = URL(string: "https://claude.ai/oauth/authorize")!
|
||||
@@ -194,10 +194,10 @@ enum AnthropicOAuth {
|
||||
}
|
||||
}
|
||||
|
||||
enum MoltbotOAuthStore {
|
||||
enum OpenClawOAuthStore {
|
||||
static let oauthFilename = "oauth.json"
|
||||
private static let providerKey = "anthropic"
|
||||
private static let moltbotOAuthDirEnv = "CLAWDBOT_OAUTH_DIR"
|
||||
private static let openclawOAuthDirEnv = "OPENCLAW_OAUTH_DIR"
|
||||
private static let legacyPiDirEnv = "PI_CODING_AGENT_DIR"
|
||||
|
||||
enum AnthropicOAuthStatus: Equatable {
|
||||
@@ -215,28 +215,28 @@ enum MoltbotOAuthStore {
|
||||
|
||||
var shortDescription: String {
|
||||
switch self {
|
||||
case .missingFile: "Moltbot OAuth token file not found"
|
||||
case .unreadableFile: "Moltbot OAuth token file not readable"
|
||||
case .invalidJSON: "Moltbot OAuth token file invalid"
|
||||
case .missingProviderEntry: "No Anthropic entry in Moltbot OAuth token file"
|
||||
case .missingFile: "OpenClaw OAuth token file not found"
|
||||
case .unreadableFile: "OpenClaw OAuth token file not readable"
|
||||
case .invalidJSON: "OpenClaw OAuth token file invalid"
|
||||
case .missingProviderEntry: "No Anthropic entry in OpenClaw OAuth token file"
|
||||
case .missingTokens: "Anthropic entry missing tokens"
|
||||
case .connected: "Moltbot OAuth credentials found"
|
||||
case .connected: "OpenClaw OAuth credentials found"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func oauthDir() -> URL {
|
||||
if let override = ProcessInfo.processInfo.environment[self.moltbotOAuthDirEnv]?
|
||||
if let override = ProcessInfo.processInfo.environment[self.openclawOAuthDirEnv]?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!override.isEmpty
|
||||
{
|
||||
let expanded = NSString(string: override).expandingTildeInPath
|
||||
return URL(fileURLWithPath: expanded, isDirectory: true)
|
||||
}
|
||||
|
||||
return FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".clawdbot", isDirectory: true)
|
||||
let home = FileManager().homeDirectoryForCurrentUser
|
||||
let preferred = home.appendingPathComponent(".openclaw", isDirectory: true)
|
||||
.appendingPathComponent("credentials", isDirectory: true)
|
||||
return preferred
|
||||
}
|
||||
|
||||
static func oauthURL() -> URL {
|
||||
@@ -1,10 +1,10 @@
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
|
||||
// Prefer the MoltbotKit wrapper to keep gateway request payloads consistent.
|
||||
typealias AnyCodable = MoltbotKit.AnyCodable
|
||||
typealias InstanceIdentity = MoltbotKit.InstanceIdentity
|
||||
// Prefer the OpenClawKit wrapper to keep gateway request payloads consistent.
|
||||
typealias AnyCodable = OpenClawKit.AnyCodable
|
||||
typealias InstanceIdentity = OpenClawKit.InstanceIdentity
|
||||
|
||||
extension AnyCodable {
|
||||
var stringValue: String? { self.value as? String }
|
||||
@@ -26,19 +26,19 @@ extension AnyCodable {
|
||||
}
|
||||
}
|
||||
|
||||
extension MoltbotProtocol.AnyCodable {
|
||||
extension OpenClawProtocol.AnyCodable {
|
||||
var stringValue: String? { self.value as? String }
|
||||
var boolValue: Bool? { self.value as? Bool }
|
||||
var intValue: Int? { self.value as? Int }
|
||||
var doubleValue: Double? { self.value as? Double }
|
||||
var dictionaryValue: [String: MoltbotProtocol.AnyCodable]? { self.value as? [String: MoltbotProtocol.AnyCodable] }
|
||||
var arrayValue: [MoltbotProtocol.AnyCodable]? { self.value as? [MoltbotProtocol.AnyCodable] }
|
||||
var dictionaryValue: [String: OpenClawProtocol.AnyCodable]? { self.value as? [String: OpenClawProtocol.AnyCodable] }
|
||||
var arrayValue: [OpenClawProtocol.AnyCodable]? { self.value as? [OpenClawProtocol.AnyCodable] }
|
||||
|
||||
var foundationValue: Any {
|
||||
switch self.value {
|
||||
case let dict as [String: MoltbotProtocol.AnyCodable]:
|
||||
case let dict as [String: OpenClawProtocol.AnyCodable]:
|
||||
dict.mapValues { $0.foundationValue }
|
||||
case let array as [MoltbotProtocol.AnyCodable]:
|
||||
case let array as [OpenClawProtocol.AnyCodable]:
|
||||
array.map(\.foundationValue)
|
||||
default:
|
||||
self.value
|
||||
@@ -41,13 +41,13 @@ final class AppState {
|
||||
}
|
||||
|
||||
var onboardingSeen: Bool {
|
||||
didSet { self.ifNotPreview { UserDefaults.standard.set(self.onboardingSeen, forKey: "moltbot.onboardingSeen") }
|
||||
didSet { self.ifNotPreview { UserDefaults.standard.set(self.onboardingSeen, forKey: onboardingSeenKey) }
|
||||
}
|
||||
}
|
||||
|
||||
var debugPaneEnabled: Bool {
|
||||
didSet {
|
||||
self.ifNotPreview { UserDefaults.standard.set(self.debugPaneEnabled, forKey: "moltbot.debugPaneEnabled") }
|
||||
self.ifNotPreview { UserDefaults.standard.set(self.debugPaneEnabled, forKey: debugPaneEnabledKey) }
|
||||
CanvasManager.shared.refreshDebugStatus()
|
||||
}
|
||||
}
|
||||
@@ -228,12 +228,16 @@ final class AppState {
|
||||
private var earBoostTask: Task<Void, Never>?
|
||||
|
||||
init(preview: Bool = false) {
|
||||
self.isPreview = preview || ProcessInfo.processInfo.isRunningTests
|
||||
let onboardingSeen = UserDefaults.standard.bool(forKey: "moltbot.onboardingSeen")
|
||||
let isPreview = preview || ProcessInfo.processInfo.isRunningTests
|
||||
self.isPreview = isPreview
|
||||
if !isPreview {
|
||||
migrateLegacyDefaults()
|
||||
}
|
||||
let onboardingSeen = UserDefaults.standard.bool(forKey: onboardingSeenKey)
|
||||
self.isPaused = UserDefaults.standard.bool(forKey: pauseDefaultsKey)
|
||||
self.launchAtLogin = false
|
||||
self.onboardingSeen = onboardingSeen
|
||||
self.debugPaneEnabled = UserDefaults.standard.bool(forKey: "moltbot.debugPaneEnabled")
|
||||
self.debugPaneEnabled = UserDefaults.standard.bool(forKey: debugPaneEnabledKey)
|
||||
let savedVoiceWake = UserDefaults.standard.bool(forKey: swabbleEnabledKey)
|
||||
self.swabbleEnabled = voiceWakeSupported ? savedVoiceWake : false
|
||||
self.swabbleTriggerWords = UserDefaults.standard
|
||||
@@ -275,7 +279,7 @@ final class AppState {
|
||||
UserDefaults.standard.set(IconOverrideSelection.system.rawValue, forKey: iconOverrideKey)
|
||||
}
|
||||
|
||||
let configRoot = MoltbotConfigFile.loadDict()
|
||||
let configRoot = OpenClawConfigFile.loadDict()
|
||||
let configRemoteUrl = GatewayRemoteConfig.resolveUrlString(root: configRoot)
|
||||
let configRemoteTransport = GatewayRemoteConfig.resolveTransport(root: configRoot)
|
||||
let resolvedConnectionMode = ConnectionModeResolver.resolve(root: configRoot).mode
|
||||
@@ -353,7 +357,7 @@ final class AppState {
|
||||
}
|
||||
|
||||
private func startConfigWatcher() {
|
||||
let configUrl = MoltbotConfigFile.url()
|
||||
let configUrl = OpenClawConfigFile.url()
|
||||
self.configWatcher = ConfigFileWatcher(url: configUrl) { [weak self] in
|
||||
Task { @MainActor in
|
||||
self?.applyConfigFromDisk()
|
||||
@@ -363,7 +367,7 @@ final class AppState {
|
||||
}
|
||||
|
||||
private func applyConfigFromDisk() {
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
self.applyConfigOverrides(root)
|
||||
}
|
||||
|
||||
@@ -451,7 +455,7 @@ final class AppState {
|
||||
|
||||
Task { @MainActor in
|
||||
// Keep app-only connection settings local to avoid overwriting remote gateway config.
|
||||
var root = MoltbotConfigFile.loadDict()
|
||||
var root = OpenClawConfigFile.loadDict()
|
||||
var gateway = root["gateway"] as? [String: Any] ?? [:]
|
||||
var changed = false
|
||||
|
||||
@@ -541,7 +545,7 @@ final class AppState {
|
||||
} else {
|
||||
root["gateway"] = gateway
|
||||
}
|
||||
MoltbotConfigFile.saveDict(root)
|
||||
OpenClawConfigFile.saveDict(root)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +689,7 @@ extension AppState {
|
||||
state.remoteTarget = "user@example.com"
|
||||
state.remoteUrl = "wss://gateway.example.ts.net"
|
||||
state.remoteIdentity = "~/.ssh/id_ed25519"
|
||||
state.remoteProjectRoot = "~/Projects/moltbot"
|
||||
state.remoteProjectRoot = "~/Projects/openclaw"
|
||||
state.remoteCliPath = ""
|
||||
return state
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import Foundation
|
||||
import OSLog
|
||||
|
||||
final class AudioInputDeviceObserver {
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "audio.devices")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "audio.devices")
|
||||
private var isActive = false
|
||||
private var devicesListener: AudioObjectPropertyListenerBlock?
|
||||
private var defaultInputListener: AudioObjectPropertyListenerBlock?
|
||||
@@ -5,7 +5,7 @@ import OSLog
|
||||
@MainActor
|
||||
final class CLIInstallPrompter {
|
||||
static let shared = CLIInstallPrompter()
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "cli.prompt")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "cli.prompt")
|
||||
private var isPrompting = false
|
||||
|
||||
func checkAndPromptIfNeeded(reason: String) {
|
||||
@@ -15,7 +15,7 @@ final class CLIInstallPrompter {
|
||||
UserDefaults.standard.set(version, forKey: cliInstallPromptedVersionKey)
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Install Moltbot CLI?"
|
||||
alert.messageText = "Install OpenClaw CLI?"
|
||||
alert.informativeText = "Local mode needs the CLI so launchd can run the gateway."
|
||||
alert.addButton(withTitle: "Install CLI")
|
||||
alert.addButton(withTitle: "Not now")
|
||||
@@ -62,7 +62,7 @@ final class CLIInstallPrompter {
|
||||
SettingsTabRouter.request(tab)
|
||||
SettingsWindowOpener.shared.open()
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .moltbotSelectSettingsTab, object: tab)
|
||||
NotificationCenter.default.post(name: .openclawSelectSettingsTab, object: tab)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ enum CLIInstaller {
|
||||
fileManager: FileManager) -> String?
|
||||
{
|
||||
for basePath in searchPaths {
|
||||
let candidate = URL(fileURLWithPath: basePath).appendingPathComponent("moltbot").path
|
||||
let candidate = URL(fileURLWithPath: basePath).appendingPathComponent("openclaw").path
|
||||
var isDirectory: ObjCBool = false
|
||||
|
||||
guard fileManager.fileExists(atPath: candidate, isDirectory: &isDirectory),
|
||||
@@ -37,14 +37,14 @@ enum CLIInstaller {
|
||||
static func install(statusHandler: @escaping @MainActor @Sendable (String) async -> Void) async {
|
||||
let expected = GatewayEnvironment.expectedGatewayVersionString() ?? "latest"
|
||||
let prefix = Self.installPrefix()
|
||||
await statusHandler("Installing moltbot CLI…")
|
||||
await statusHandler("Installing openclaw CLI…")
|
||||
let cmd = self.installScriptCommand(version: expected, prefix: prefix)
|
||||
let response = await ShellExecutor.runDetailed(command: cmd, cwd: nil, env: nil, timeout: 900)
|
||||
|
||||
if response.success {
|
||||
let parsed = self.parseInstallEvents(response.stdout)
|
||||
let installedVersion = parsed.last { $0.event == "done" }?.version
|
||||
let summary = installedVersion.map { "Installed moltbot \($0)." } ?? "Installed moltbot."
|
||||
let summary = installedVersion.map { "Installed openclaw \($0)." } ?? "Installed openclaw."
|
||||
await statusHandler(summary)
|
||||
return
|
||||
}
|
||||
@@ -62,7 +62,7 @@ enum CLIInstaller {
|
||||
|
||||
private static func installPrefix() -> String {
|
||||
FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".clawdbot")
|
||||
.appendingPathComponent(".openclaw")
|
||||
.path
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ enum CLIInstaller {
|
||||
let escapedVersion = self.shellEscape(version)
|
||||
let escapedPrefix = self.shellEscape(prefix)
|
||||
let script = """
|
||||
curl -fsSL https://molt.bot/install-cli.sh | \
|
||||
curl -fsSL https://openclaw.bot/install-cli.sh | \
|
||||
bash -s -- --json --no-onboard --prefix \(escapedPrefix) --version \(escapedVersion)
|
||||
"""
|
||||
return ["/bin/bash", "-lc", script]
|
||||
@@ -1,6 +1,6 @@
|
||||
import AVFoundation
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import OpenClawIPC
|
||||
import OpenClawKit
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
import OSLog
|
||||
@@ -36,7 +36,7 @@ actor CameraCaptureService {
|
||||
}
|
||||
}
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "camera")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "camera")
|
||||
|
||||
func listDevices() -> [CameraDeviceInfo] {
|
||||
Self.availableCameras().map { device in
|
||||
@@ -168,7 +168,7 @@ actor CameraCaptureService {
|
||||
await Self.warmUpCaptureSession()
|
||||
|
||||
let tmpMovURL = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("moltbot-camera-\(UUID().uuidString).mov")
|
||||
.appendingPathComponent("openclaw-camera-\(UUID().uuidString).mov")
|
||||
defer { try? FileManager().removeItem(at: tmpMovURL) }
|
||||
|
||||
let outputURL: URL = {
|
||||
@@ -176,7 +176,7 @@ actor CameraCaptureService {
|
||||
return URL(fileURLWithPath: outPath)
|
||||
}
|
||||
return FileManager().temporaryDirectory
|
||||
.appendingPathComponent("moltbot-camera-\(UUID().uuidString).mp4")
|
||||
.appendingPathComponent("openclaw-camera-\(UUID().uuidString).mp4")
|
||||
}()
|
||||
|
||||
// Ensure we don't fail exporting due to an existing file.
|
||||
@@ -1,11 +1,12 @@
|
||||
import AppKit
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import OpenClawIPC
|
||||
import OpenClawKit
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
static let messageName = "moltbotCanvasA2UIAction"
|
||||
static let messageName = "openclawCanvasA2UIAction"
|
||||
static let allMessageNames = [messageName]
|
||||
|
||||
private let sessionKey: String
|
||||
|
||||
@@ -15,11 +16,11 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
}
|
||||
|
||||
func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
guard message.name == Self.messageName else { return }
|
||||
guard Self.allMessageNames.contains(message.name) else { return }
|
||||
|
||||
// Only accept actions from local Canvas content (not arbitrary web pages).
|
||||
guard let webView = message.webView, let url = webView.url else { return }
|
||||
if url.scheme == CanvasScheme.scheme {
|
||||
if let scheme = url.scheme, CanvasScheme.allSchemes.contains(scheme) {
|
||||
// ok
|
||||
} else if Self.isLocalNetworkCanvasURL(url) {
|
||||
// ok
|
||||
@@ -52,7 +53,7 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
}()
|
||||
guard !userAction.isEmpty else { return }
|
||||
|
||||
guard let name = MoltbotCanvasA2UIAction.extractActionName(userAction) else { return }
|
||||
guard let name = OpenClawCanvasA2UIAction.extractActionName(userAction) else { return }
|
||||
let actionId =
|
||||
(userAction["id"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
|
||||
?? UUID().uuidString
|
||||
@@ -64,15 +65,15 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
let sourceComponentId = (userAction["sourceComponentId"] as? String)?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty ?? "-"
|
||||
let instanceId = InstanceIdentity.instanceId.lowercased()
|
||||
let contextJSON = MoltbotCanvasA2UIAction.compactJSON(userAction["context"])
|
||||
let contextJSON = OpenClawCanvasA2UIAction.compactJSON(userAction["context"])
|
||||
|
||||
// Token-efficient and unambiguous. The agent should treat this as a UI event and (by default) update Canvas.
|
||||
let messageContext = MoltbotCanvasA2UIAction.AgentMessageContext(
|
||||
let messageContext = OpenClawCanvasA2UIAction.AgentMessageContext(
|
||||
actionName: name,
|
||||
session: .init(key: self.sessionKey, surfaceId: surfaceId),
|
||||
component: .init(id: sourceComponentId, host: InstanceIdentity.displayName, instanceId: instanceId),
|
||||
contextJSON: contextJSON)
|
||||
let text = MoltbotCanvasA2UIAction.formatAgentMessage(messageContext)
|
||||
let text = OpenClawCanvasA2UIAction.formatAgentMessage(messageContext)
|
||||
|
||||
Task { [weak webView] in
|
||||
if AppStateStore.shared.connectionMode == .local {
|
||||
@@ -91,7 +92,7 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
|
||||
await MainActor.run {
|
||||
guard let webView else { return }
|
||||
let js = MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
let js = OpenClawCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
actionId: actionId,
|
||||
ok: result.ok,
|
||||
error: result.error)
|
||||
@@ -144,5 +145,5 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
return false
|
||||
}
|
||||
|
||||
// Formatting helpers live in MoltbotKit (`MoltbotCanvasA2UIAction`).
|
||||
// Formatting helpers live in OpenClawKit (`OpenClawCanvasA2UIAction`).
|
||||
}
|
||||
@@ -10,7 +10,7 @@ final class CanvasFileWatcher: @unchecked Sendable {
|
||||
|
||||
init(url: URL, onChange: @escaping () -> Void) {
|
||||
self.url = url
|
||||
self.queue = DispatchQueue(label: "bot.molt.canvaswatcher")
|
||||
self.queue = DispatchQueue(label: "ai.openclaw.canvaswatcher")
|
||||
self.onChange = onChange
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AppKit
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import OpenClawIPC
|
||||
import OpenClawKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
@@ -8,7 +8,7 @@ import OSLog
|
||||
final class CanvasManager {
|
||||
static let shared = CanvasManager()
|
||||
|
||||
private static let logger = Logger(subsystem: "bot.molt", category: "CanvasManager")
|
||||
private static let logger = Logger(subsystem: "ai.openclaw", category: "CanvasManager")
|
||||
|
||||
private var panelController: CanvasWindowController?
|
||||
private var panelSessionKey: String?
|
||||
@@ -26,7 +26,7 @@ final class CanvasManager {
|
||||
|
||||
private nonisolated static let canvasRoot: URL = {
|
||||
let base = FileManager().urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
||||
return base.appendingPathComponent("Moltbot/canvas", isDirectory: true)
|
||||
return base.appendingPathComponent("OpenClaw/canvas", isDirectory: true)
|
||||
}()
|
||||
|
||||
func show(sessionKey: String, path: String? = nil, placement: CanvasPlacement? = nil) throws -> String {
|
||||
@@ -231,7 +231,7 @@ final class CanvasManager {
|
||||
private static func resolveA2UIHostUrl(from raw: String?) -> String? {
|
||||
let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard !trimmed.isEmpty, let base = URL(string: trimmed) else { return nil }
|
||||
return base.appendingPathComponent("__moltbot__/a2ui/").absoluteString + "?platform=macos"
|
||||
return base.appendingPathComponent("__openclaw__/a2ui/").absoluteString + "?platform=macos"
|
||||
}
|
||||
|
||||
// MARK: - Anchoring
|
||||
@@ -1,7 +1,8 @@
|
||||
import Foundation
|
||||
|
||||
enum CanvasScheme {
|
||||
static let scheme = "moltbot-canvas"
|
||||
static let scheme = "openclaw-canvas"
|
||||
static let allSchemes = [scheme]
|
||||
|
||||
static func makeURL(session: String, path: String? = nil) -> URL? {
|
||||
var comps = URLComponents()
|
||||
@@ -1,9 +1,9 @@
|
||||
import MoltbotKit
|
||||
import OpenClawKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
import WebKit
|
||||
|
||||
private let canvasLogger = Logger(subsystem: "bot.molt", category: "Canvas")
|
||||
private let canvasLogger = Logger(subsystem: "ai.openclaw", category: "Canvas")
|
||||
|
||||
final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
private let root: URL
|
||||
@@ -45,7 +45,7 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
}
|
||||
|
||||
private func response(for url: URL) -> CanvasResponse {
|
||||
guard url.scheme == CanvasScheme.scheme else {
|
||||
guard let scheme = url.scheme, CanvasScheme.allSchemes.contains(scheme) else {
|
||||
return self.html("Invalid scheme.")
|
||||
}
|
||||
guard let session = url.host, !session.isEmpty else {
|
||||
@@ -222,7 +222,7 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
let name = fileURL.deletingPathExtension().lastPathComponent
|
||||
guard !name.isEmpty, !ext.isEmpty else { return nil }
|
||||
|
||||
let bundle = MoltbotKitResources.bundle
|
||||
let bundle = OpenClawKitResources.bundle
|
||||
let resourceURL =
|
||||
bundle.url(forResource: name, withExtension: ext, subdirectory: subdirectory)
|
||||
?? bundle.url(forResource: name, withExtension: ext)
|
||||
@@ -1,6 +1,6 @@
|
||||
import AppKit
|
||||
|
||||
let canvasWindowLogger = Logger(subsystem: "bot.molt", category: "Canvas")
|
||||
let canvasWindowLogger = Logger(subsystem: "ai.openclaw", category: "Canvas")
|
||||
|
||||
enum CanvasLayout {
|
||||
static let panelSize = NSSize(width: 520, height: 680)
|
||||
@@ -23,7 +23,7 @@ extension CanvasWindowController {
|
||||
}
|
||||
|
||||
static func storedFrameDefaultsKey(sessionKey: String) -> String {
|
||||
"moltbot.canvas.frame.\(self.sanitizeSessionKey(sessionKey))"
|
||||
"openclaw.canvas.frame.\(self.sanitizeSessionKey(sessionKey))"
|
||||
}
|
||||
|
||||
static func loadRestoredFrame(sessionKey: String) -> NSRect? {
|
||||
@@ -17,8 +17,9 @@ extension CanvasWindowController {
|
||||
let scheme = url.scheme?.lowercased()
|
||||
|
||||
// Deep links: allow local Canvas content to invoke the agent without bouncing through NSWorkspace.
|
||||
if scheme == "moltbot" {
|
||||
if self.webView.url?.scheme == CanvasScheme.scheme {
|
||||
if scheme == "openclaw" {
|
||||
if let currentScheme = self.webView.url?.scheme,
|
||||
CanvasScheme.allSchemes.contains(currentScheme) {
|
||||
Task { await DeepLinkHandler.shared.handle(url: url) }
|
||||
} else {
|
||||
canvasWindowLogger
|
||||
@@ -30,7 +31,7 @@ extension CanvasWindowController {
|
||||
|
||||
// Keep web content inside the panel when reasonable.
|
||||
// `about:blank` and friends are common internal navigations for WKWebView; never send them to NSWorkspace.
|
||||
if scheme == CanvasScheme.scheme
|
||||
if CanvasScheme.allSchemes.contains(scheme ?? "")
|
||||
|| scheme == "https"
|
||||
|| scheme == "http"
|
||||
|| scheme == "about"
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppKit
|
||||
import MoltbotIPC
|
||||
import OpenClawIPC
|
||||
|
||||
extension CanvasWindowController {
|
||||
// MARK: - Window
|
||||
@@ -12,7 +12,7 @@ extension CanvasWindowController {
|
||||
styleMask: [.titled, .closable, .resizable, .miniaturizable],
|
||||
backing: .buffered,
|
||||
defer: false)
|
||||
window.title = "Moltbot Canvas"
|
||||
window.title = "OpenClaw Canvas"
|
||||
window.isReleasedWhenClosed = false
|
||||
window.contentView = contentView
|
||||
window.center()
|
||||
@@ -1,6 +1,6 @@
|
||||
import AppKit
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import OpenClawIPC
|
||||
import OpenClawKit
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
@@ -43,7 +43,9 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
config.preferences.isElementFullscreenEnabled = true
|
||||
config.preferences.setValue(true, forKey: "developerExtrasEnabled")
|
||||
canvasWindowLogger.debug("CanvasWindowController init config ready")
|
||||
config.setURLSchemeHandler(self.schemeHandler, forURLScheme: CanvasScheme.scheme)
|
||||
for scheme in CanvasScheme.allSchemes {
|
||||
config.setURLSchemeHandler(self.schemeHandler, forURLScheme: scheme)
|
||||
}
|
||||
canvasWindowLogger.debug("CanvasWindowController init scheme handler installed")
|
||||
|
||||
// Bridge A2UI "a2uiaction" DOM events back into the native agent loop.
|
||||
@@ -56,9 +58,11 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
let bridgeScript = """
|
||||
(() => {
|
||||
try {
|
||||
if (location.protocol !== '\(CanvasScheme.scheme):') return;
|
||||
if (globalThis.__moltbotA2UIBridgeInstalled) return;
|
||||
globalThis.__moltbotA2UIBridgeInstalled = true;
|
||||
const allowedSchemes = \(String(describing: CanvasScheme.allSchemes));
|
||||
const protocol = location.protocol.replace(':', '');
|
||||
if (!allowedSchemes.includes(protocol)) return;
|
||||
if (globalThis.__openclawA2UIBridgeInstalled) return;
|
||||
globalThis.__openclawA2UIBridgeInstalled = true;
|
||||
|
||||
const deepLinkKey = \(Self.jsStringLiteral(deepLinkKey));
|
||||
const sessionKey = \(Self.jsStringLiteral(injectedSessionKey));
|
||||
@@ -85,11 +89,13 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
...(context.length ? { context } : {}),
|
||||
};
|
||||
|
||||
const handler = globalThis.webkit?.messageHandlers?.clawdbotCanvasA2UIAction;
|
||||
const handler = globalThis.webkit?.messageHandlers?.openclawCanvasA2UIAction;
|
||||
|
||||
// If the bundled A2UI shell is present, let it forward actions so we keep its richer
|
||||
// context resolution (data model path lookups, surface detection, etc.).
|
||||
const hasBundledA2UIHost = !!globalThis.clawdbotA2UI || !!document.querySelector('moltbot-a2ui-host');
|
||||
const hasBundledA2UIHost =
|
||||
!!globalThis.openclawA2UI ||
|
||||
!!document.querySelector('openclaw-a2ui-host');
|
||||
if (hasBundledA2UIHost && handler?.postMessage) return;
|
||||
|
||||
// Otherwise, forward directly when possible.
|
||||
@@ -115,7 +121,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
params.set('deliver', 'false');
|
||||
params.set('channel', 'last');
|
||||
params.set('key', deepLinkKey);
|
||||
location.href = 'moltbot://agent?' + params.toString();
|
||||
location.href = 'openclaw://agent?' + params.toString();
|
||||
} catch {}
|
||||
}, true);
|
||||
} catch {}
|
||||
@@ -137,7 +143,8 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
guard let webView else { return }
|
||||
|
||||
// Only auto-reload when we are showing local canvas content.
|
||||
guard webView.url?.scheme == CanvasScheme.scheme else { return }
|
||||
guard let scheme = webView.url?.scheme,
|
||||
CanvasScheme.allSchemes.contains(scheme) else { return }
|
||||
|
||||
let path = webView.url?.path ?? ""
|
||||
if path == "/" || path.isEmpty {
|
||||
@@ -161,7 +168,9 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
|
||||
let handler = CanvasA2UIActionMessageHandler(sessionKey: sessionKey)
|
||||
self.a2uiActionMessageHandler = handler
|
||||
self.webView.configuration.userContentController.add(handler, name: CanvasA2UIActionMessageHandler.messageName)
|
||||
for name in CanvasA2UIActionMessageHandler.allMessageNames {
|
||||
self.webView.configuration.userContentController.add(handler, name: name)
|
||||
}
|
||||
|
||||
self.webView.navigationDelegate = self
|
||||
self.window?.delegate = self
|
||||
@@ -177,8 +186,9 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
|
||||
|
||||
@MainActor deinit {
|
||||
self.webView.configuration.userContentController
|
||||
.removeScriptMessageHandler(forName: CanvasA2UIActionMessageHandler.messageName)
|
||||
for name in CanvasA2UIActionMessageHandler.allMessageNames {
|
||||
self.webView.configuration.userContentController.removeScriptMessageHandler(forName: name)
|
||||
}
|
||||
self.watcher.stop()
|
||||
}
|
||||
|
||||
@@ -268,7 +278,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
let js = """
|
||||
(() => {
|
||||
try {
|
||||
const api = globalThis.__moltbot;
|
||||
const api = globalThis.__openclaw;
|
||||
if (!api) return;
|
||||
if (typeof api.setDebugStatusEnabled === 'function') {
|
||||
api.setDebugStatusEnabled(\(enabled ? "true" : "false"));
|
||||
@@ -336,7 +346,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
path = outPath
|
||||
} else {
|
||||
let ts = Int(Date().timeIntervalSince1970)
|
||||
path = "/tmp/moltbot-canvas-\(CanvasWindowController.sanitizeSessionKey(self.sessionKey))-\(ts).png"
|
||||
path = "/tmp/openclaw-canvas-\(CanvasWindowController.sanitizeSessionKey(self.sessionKey))-\(ts).png"
|
||||
}
|
||||
|
||||
try png.write(to: URL(fileURLWithPath: path), options: [.atomic])
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import SwiftUI
|
||||
|
||||
extension ChannelsSettings {
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
|
||||
extension ChannelsStore {
|
||||
@@ -28,7 +28,7 @@ extension ChannelsStore {
|
||||
params: nil,
|
||||
timeoutMs: 10000)
|
||||
self.configStatus = snap.valid == false
|
||||
? "Config invalid; fix it in ~/.clawdbot/moltbot.json."
|
||||
? "Config invalid; fix it in ~/.openclaw/openclaw.json."
|
||||
: nil
|
||||
self.configRoot = snap.config?.mapValues { $0.foundationValue } ?? [:]
|
||||
self.configDraft = cloneConfigValue(self.configRoot) as? [String: Any] ?? self.configRoot
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
|
||||
extension ChannelsStore {
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import Foundation
|
||||
|
||||
enum CommandResolver {
|
||||
private static let projectRootDefaultsKey = "moltbot.gatewayProjectRootPath"
|
||||
private static let helperName = "moltbot"
|
||||
private static let projectRootDefaultsKey = "openclaw.gatewayProjectRootPath"
|
||||
private static let helperName = "openclaw"
|
||||
|
||||
static func gatewayEntrypoint(in root: URL) -> String? {
|
||||
let distEntry = root.appendingPathComponent("dist/index.js").path
|
||||
if FileManager().isReadableFile(atPath: distEntry) { return distEntry }
|
||||
let binEntry = root.appendingPathComponent("bin/moltbot.js").path
|
||||
let openclawEntry = root.appendingPathComponent("openclaw.mjs").path
|
||||
if FileManager().isReadableFile(atPath: openclawEntry) { return openclawEntry }
|
||||
let binEntry = root.appendingPathComponent("bin/openclaw.js").path
|
||||
if FileManager().isReadableFile(atPath: binEntry) { return binEntry }
|
||||
return nil
|
||||
}
|
||||
@@ -36,9 +38,9 @@ enum CommandResolver {
|
||||
|
||||
static func errorCommand(with message: String) -> [String] {
|
||||
let script = """
|
||||
cat <<'__CLAWDBOT_ERR__' >&2
|
||||
cat <<'__OPENCLAW_ERR__' >&2
|
||||
\(message)
|
||||
__CLAWDBOT_ERR__
|
||||
__OPENCLAW_ERR__
|
||||
exit 1
|
||||
"""
|
||||
return ["/bin/sh", "-c", script]
|
||||
@@ -52,7 +54,7 @@ enum CommandResolver {
|
||||
return url
|
||||
}
|
||||
let fallback = FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent("Projects/moltbot")
|
||||
.appendingPathComponent("Projects/openclaw")
|
||||
if FileManager().fileExists(atPath: fallback.path) {
|
||||
return fallback
|
||||
}
|
||||
@@ -87,26 +89,30 @@ enum CommandResolver {
|
||||
// Dev-only convenience. Avoid project-local PATH hijacking in release builds.
|
||||
extras.insert(projectRoot.appendingPathComponent("node_modules/.bin").path, at: 0)
|
||||
#endif
|
||||
let moltbotPaths = self.moltbotManagedPaths(home: home)
|
||||
if !moltbotPaths.isEmpty {
|
||||
extras.insert(contentsOf: moltbotPaths, at: 1)
|
||||
let openclawPaths = self.openclawManagedPaths(home: home)
|
||||
if !openclawPaths.isEmpty {
|
||||
extras.insert(contentsOf: openclawPaths, at: 1)
|
||||
}
|
||||
extras.insert(contentsOf: self.nodeManagerBinPaths(home: home), at: 1 + moltbotPaths.count)
|
||||
extras.insert(contentsOf: self.nodeManagerBinPaths(home: home), at: 1 + openclawPaths.count)
|
||||
var seen = Set<String>()
|
||||
// Preserve order while stripping duplicates so PATH lookups remain deterministic.
|
||||
return (extras + current).filter { seen.insert($0).inserted }
|
||||
}
|
||||
|
||||
private static func moltbotManagedPaths(home: URL) -> [String] {
|
||||
let base = home.appendingPathComponent(".clawdbot")
|
||||
let bin = base.appendingPathComponent("bin")
|
||||
let nodeBin = base.appendingPathComponent("tools/node/bin")
|
||||
private static func openclawManagedPaths(home: URL) -> [String] {
|
||||
let bases = [
|
||||
home.appendingPathComponent(".openclaw"),
|
||||
]
|
||||
var paths: [String] = []
|
||||
if FileManager().fileExists(atPath: bin.path) {
|
||||
paths.append(bin.path)
|
||||
}
|
||||
if FileManager().fileExists(atPath: nodeBin.path) {
|
||||
paths.append(nodeBin.path)
|
||||
for base in bases {
|
||||
let bin = base.appendingPathComponent("bin")
|
||||
let nodeBin = base.appendingPathComponent("tools/node/bin")
|
||||
if FileManager().fileExists(atPath: bin.path) {
|
||||
paths.append(bin.path)
|
||||
}
|
||||
if FileManager().fileExists(atPath: nodeBin.path) {
|
||||
paths.append(nodeBin.path)
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
@@ -187,11 +193,11 @@ enum CommandResolver {
|
||||
return nil
|
||||
}
|
||||
|
||||
static func moltbotExecutable(searchPaths: [String]? = nil) -> String? {
|
||||
static func openclawExecutable(searchPaths: [String]? = nil) -> String? {
|
||||
self.findExecutable(named: self.helperName, searchPaths: searchPaths)
|
||||
}
|
||||
|
||||
static func projectMoltbotExecutable(projectRoot: URL? = nil) -> String? {
|
||||
static func projectOpenClawExecutable(projectRoot: URL? = nil) -> String? {
|
||||
#if DEBUG
|
||||
let root = projectRoot ?? self.projectRoot()
|
||||
let candidate = root.appendingPathComponent("node_modules/.bin").appendingPathComponent(self.helperName).path
|
||||
@@ -202,12 +208,19 @@ enum CommandResolver {
|
||||
}
|
||||
|
||||
static func nodeCliPath() -> String? {
|
||||
let candidate = self.projectRoot().appendingPathComponent("bin/moltbot.js").path
|
||||
return FileManager().isReadableFile(atPath: candidate) ? candidate : nil
|
||||
let root = self.projectRoot()
|
||||
let candidates = [
|
||||
root.appendingPathComponent("openclaw.mjs").path,
|
||||
root.appendingPathComponent("bin/openclaw.js").path,
|
||||
]
|
||||
for candidate in candidates where FileManager().isReadableFile(atPath: candidate) {
|
||||
return candidate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func hasAnyMoltbotInvoker(searchPaths: [String]? = nil) -> Bool {
|
||||
if self.moltbotExecutable(searchPaths: searchPaths) != nil { return true }
|
||||
static func hasAnyOpenClawInvoker(searchPaths: [String]? = nil) -> Bool {
|
||||
if self.openclawExecutable(searchPaths: searchPaths) != nil { return true }
|
||||
if self.findExecutable(named: "pnpm", searchPaths: searchPaths) != nil { return true }
|
||||
if self.findExecutable(named: "node", searchPaths: searchPaths) != nil,
|
||||
self.nodeCliPath() != nil
|
||||
@@ -217,7 +230,7 @@ enum CommandResolver {
|
||||
return false
|
||||
}
|
||||
|
||||
static func moltbotNodeCommand(
|
||||
static func openclawNodeCommand(
|
||||
subcommand: String,
|
||||
extraArgs: [String] = [],
|
||||
defaults: UserDefaults = .standard,
|
||||
@@ -238,8 +251,8 @@ enum CommandResolver {
|
||||
switch runtimeResult {
|
||||
case let .success(runtime):
|
||||
let root = self.projectRoot()
|
||||
if let moltbotPath = self.projectMoltbotExecutable(projectRoot: root) {
|
||||
return [moltbotPath, subcommand] + extraArgs
|
||||
if let openclawPath = self.projectOpenClawExecutable(projectRoot: root) {
|
||||
return [openclawPath, subcommand] + extraArgs
|
||||
}
|
||||
|
||||
if let entry = self.gatewayEntrypoint(in: root) {
|
||||
@@ -251,14 +264,14 @@ enum CommandResolver {
|
||||
}
|
||||
if let pnpm = self.findExecutable(named: "pnpm", searchPaths: searchPaths) {
|
||||
// Use --silent to avoid pnpm lifecycle banners that would corrupt JSON outputs.
|
||||
return [pnpm, "--silent", "moltbot", subcommand] + extraArgs
|
||||
return [pnpm, "--silent", "openclaw", subcommand] + extraArgs
|
||||
}
|
||||
if let moltbotPath = self.moltbotExecutable(searchPaths: searchPaths) {
|
||||
return [moltbotPath, subcommand] + extraArgs
|
||||
if let openclawPath = self.openclawExecutable(searchPaths: searchPaths) {
|
||||
return [openclawPath, subcommand] + extraArgs
|
||||
}
|
||||
|
||||
let missingEntry = """
|
||||
moltbot entrypoint missing (looked for dist/index.js or bin/moltbot.js); run pnpm build.
|
||||
openclaw entrypoint missing (looked for dist/index.js or openclaw.mjs); run pnpm build.
|
||||
"""
|
||||
return self.errorCommand(with: missingEntry)
|
||||
|
||||
@@ -267,15 +280,14 @@ enum CommandResolver {
|
||||
}
|
||||
}
|
||||
|
||||
// Existing callers still refer to moltbotCommand; keep it as node alias.
|
||||
static func moltbotCommand(
|
||||
static func openclawCommand(
|
||||
subcommand: String,
|
||||
extraArgs: [String] = [],
|
||||
defaults: UserDefaults = .standard,
|
||||
configRoot: [String: Any]? = nil,
|
||||
searchPaths: [String]? = nil) -> [String]
|
||||
{
|
||||
self.moltbotNodeCommand(
|
||||
self.openclawNodeCommand(
|
||||
subcommand: subcommand,
|
||||
extraArgs: extraArgs,
|
||||
defaults: defaults,
|
||||
@@ -289,7 +301,7 @@ enum CommandResolver {
|
||||
guard !settings.target.isEmpty else { return nil }
|
||||
guard let parsed = self.parseSSHTarget(settings.target) else { return nil }
|
||||
|
||||
// Run the real moltbot CLI on the remote host.
|
||||
// Run the real openclaw CLI on the remote host.
|
||||
let exportedPath = [
|
||||
"/opt/homebrew/bin",
|
||||
"/usr/local/bin",
|
||||
@@ -306,7 +318,7 @@ enum CommandResolver {
|
||||
|
||||
let projectSection = if userPRJ.isEmpty {
|
||||
"""
|
||||
DEFAULT_PRJ="$HOME/Projects/moltbot"
|
||||
DEFAULT_PRJ="$HOME/Projects/openclaw"
|
||||
if [ -d "$DEFAULT_PRJ" ]; then
|
||||
PRJ="$DEFAULT_PRJ"
|
||||
cd "$PRJ" || { echo "Project root not found: $PRJ"; exit 127; }
|
||||
@@ -345,9 +357,9 @@ enum CommandResolver {
|
||||
CLI="";
|
||||
\(cliSection)
|
||||
\(projectSection)
|
||||
if command -v moltbot >/dev/null 2>&1; then
|
||||
CLI="$(command -v moltbot)"
|
||||
moltbot \(quotedArgs);
|
||||
if command -v openclaw >/dev/null 2>&1; then
|
||||
CLI="$(command -v openclaw)"
|
||||
openclaw \(quotedArgs);
|
||||
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/dist/index.js" ]; then
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
CLI="node $PRJ/dist/index.js"
|
||||
@@ -355,18 +367,25 @@ enum CommandResolver {
|
||||
else
|
||||
echo "Node >=22 required on remote host"; exit 127;
|
||||
fi
|
||||
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/bin/moltbot.js" ]; then
|
||||
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/openclaw.mjs" ]; then
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
CLI="node $PRJ/bin/moltbot.js"
|
||||
node "$PRJ/bin/moltbot.js" \(quotedArgs);
|
||||
CLI="node $PRJ/openclaw.mjs"
|
||||
node "$PRJ/openclaw.mjs" \(quotedArgs);
|
||||
else
|
||||
echo "Node >=22 required on remote host"; exit 127;
|
||||
fi
|
||||
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/bin/openclaw.js" ]; then
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
CLI="node $PRJ/bin/openclaw.js"
|
||||
node "$PRJ/bin/openclaw.js" \(quotedArgs);
|
||||
else
|
||||
echo "Node >=22 required on remote host"; exit 127;
|
||||
fi
|
||||
elif command -v pnpm >/dev/null 2>&1; then
|
||||
CLI="pnpm --silent moltbot"
|
||||
pnpm --silent moltbot \(quotedArgs);
|
||||
CLI="pnpm --silent openclaw"
|
||||
pnpm --silent openclaw \(quotedArgs);
|
||||
else
|
||||
echo "moltbot CLI missing on remote host"; exit 127;
|
||||
echo "openclaw CLI missing on remote host"; exit 127;
|
||||
fi
|
||||
"""
|
||||
let options: [String] = [
|
||||
@@ -394,7 +413,7 @@ enum CommandResolver {
|
||||
defaults: UserDefaults = .standard,
|
||||
configRoot: [String: Any]? = nil) -> RemoteSettings
|
||||
{
|
||||
let root = configRoot ?? MoltbotConfigFile.loadDict()
|
||||
let root = configRoot ?? OpenClawConfigFile.loadDict()
|
||||
let mode = ConnectionModeResolver.resolve(root: root, defaults: defaults).mode
|
||||
let target = defaults.string(forKey: remoteTargetKey) ?? ""
|
||||
let identity = defaults.string(forKey: remoteIdentityKey) ?? ""
|
||||
@@ -13,7 +13,7 @@ final class ConfigFileWatcher: @unchecked Sendable {
|
||||
|
||||
init(url: URL, onChange: @escaping () -> Void) {
|
||||
self.url = url
|
||||
self.queue = DispatchQueue(label: "bot.molt.configwatcher")
|
||||
self.queue = DispatchQueue(label: "ai.openclaw.configwatcher")
|
||||
self.onChange = onChange
|
||||
self.watchedDir = url.deletingLastPathComponent()
|
||||
self.targetPath = url.path
|
||||
@@ -153,7 +153,7 @@ extension ConfigSettings {
|
||||
.font(.title3.weight(.semibold))
|
||||
Text(self.isNixMode
|
||||
? "This tab is read-only in Nix mode. Edit config via Nix and rebuild."
|
||||
: "Edit ~/.clawdbot/moltbot.json using the schema-driven form.")
|
||||
: "Edit ~/.openclaw/openclaw.json using the schema-driven form.")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
|
||||
enum ConfigStore {
|
||||
@@ -44,7 +44,7 @@ enum ConfigStore {
|
||||
if let gateway = await self.loadFromGateway() {
|
||||
return gateway
|
||||
}
|
||||
return MoltbotConfigFile.loadDict()
|
||||
return OpenClawConfigFile.loadDict()
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@@ -63,7 +63,7 @@ enum ConfigStore {
|
||||
do {
|
||||
try await self.saveToGateway(root)
|
||||
} catch {
|
||||
MoltbotConfigFile.saveDict(root)
|
||||
OpenClawConfigFile.saveDict(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import OSLog
|
||||
final class ConnectionModeCoordinator {
|
||||
static let shared = ConnectionModeCoordinator()
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "connection")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "connection")
|
||||
private var lastMode: AppState.ConnectionMode?
|
||||
|
||||
/// Apply the requested connection mode by starting/stopping local gateway,
|
||||
@@ -43,7 +43,7 @@ enum ConnectionModeResolver {
|
||||
return EffectiveConnectionMode(mode: storedMode, source: .userDefaults)
|
||||
}
|
||||
|
||||
let seen = defaults.bool(forKey: "moltbot.onboardingSeen")
|
||||
let seen = defaults.bool(forKey: "openclaw.onboardingSeen")
|
||||
return EffectiveConnectionMode(mode: seen ? .local : .unconfigured, source: .onboarding)
|
||||
}
|
||||
}
|
||||
46
apps/macos/Sources/OpenClaw/Constants.swift
Normal file
46
apps/macos/Sources/OpenClaw/Constants.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
import Foundation
|
||||
|
||||
let launchdLabel = "ai.openclaw.mac"
|
||||
let gatewayLaunchdLabel = "ai.openclaw.gateway"
|
||||
let onboardingVersionKey = "openclaw.onboardingVersion"
|
||||
let onboardingSeenKey = "openclaw.onboardingSeen"
|
||||
let currentOnboardingVersion = 7
|
||||
let pauseDefaultsKey = "openclaw.pauseEnabled"
|
||||
let iconAnimationsEnabledKey = "openclaw.iconAnimationsEnabled"
|
||||
let swabbleEnabledKey = "openclaw.swabbleEnabled"
|
||||
let swabbleTriggersKey = "openclaw.swabbleTriggers"
|
||||
let voiceWakeTriggerChimeKey = "openclaw.voiceWakeTriggerChime"
|
||||
let voiceWakeSendChimeKey = "openclaw.voiceWakeSendChime"
|
||||
let showDockIconKey = "openclaw.showDockIcon"
|
||||
let defaultVoiceWakeTriggers = ["openclaw"]
|
||||
let voiceWakeMaxWords = 32
|
||||
let voiceWakeMaxWordLength = 64
|
||||
let voiceWakeMicKey = "openclaw.voiceWakeMicID"
|
||||
let voiceWakeMicNameKey = "openclaw.voiceWakeMicName"
|
||||
let voiceWakeLocaleKey = "openclaw.voiceWakeLocaleID"
|
||||
let voiceWakeAdditionalLocalesKey = "openclaw.voiceWakeAdditionalLocaleIDs"
|
||||
let voicePushToTalkEnabledKey = "openclaw.voicePushToTalkEnabled"
|
||||
let talkEnabledKey = "openclaw.talkEnabled"
|
||||
let iconOverrideKey = "openclaw.iconOverride"
|
||||
let connectionModeKey = "openclaw.connectionMode"
|
||||
let remoteTargetKey = "openclaw.remoteTarget"
|
||||
let remoteIdentityKey = "openclaw.remoteIdentity"
|
||||
let remoteProjectRootKey = "openclaw.remoteProjectRoot"
|
||||
let remoteCliPathKey = "openclaw.remoteCliPath"
|
||||
let canvasEnabledKey = "openclaw.canvasEnabled"
|
||||
let cameraEnabledKey = "openclaw.cameraEnabled"
|
||||
let systemRunPolicyKey = "openclaw.systemRunPolicy"
|
||||
let systemRunAllowlistKey = "openclaw.systemRunAllowlist"
|
||||
let systemRunEnabledKey = "openclaw.systemRunEnabled"
|
||||
let locationModeKey = "openclaw.locationMode"
|
||||
let locationPreciseKey = "openclaw.locationPreciseEnabled"
|
||||
let peekabooBridgeEnabledKey = "openclaw.peekabooBridgeEnabled"
|
||||
let deepLinkKeyKey = "openclaw.deepLinkKey"
|
||||
let modelCatalogPathKey = "openclaw.modelCatalogPath"
|
||||
let modelCatalogReloadKey = "openclaw.modelCatalogReload"
|
||||
let cliInstallPromptedVersionKey = "openclaw.cliInstallPromptedVersion"
|
||||
let heartbeatsEnabledKey = "openclaw.heartbeatsEnabled"
|
||||
let debugPaneEnabledKey = "openclaw.debugPaneEnabled"
|
||||
let debugFileLogEnabledKey = "openclaw.debug.fileLogEnabled"
|
||||
let appLogLevelKey = "openclaw.debug.appLogLevel"
|
||||
let voiceWakeSupported: Bool = ProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 26
|
||||
@@ -1,5 +1,5 @@
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
import Observation
|
||||
import SwiftUI
|
||||
@@ -20,7 +20,7 @@ struct ControlAgentEvent: Codable, Sendable, Identifiable {
|
||||
let seq: Int
|
||||
let stream: String
|
||||
let ts: Double
|
||||
let data: [String: MoltbotProtocol.AnyCodable]
|
||||
let data: [String: OpenClawProtocol.AnyCodable]
|
||||
let summary: String?
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ final class ControlChannel {
|
||||
private(set) var lastPingMs: Double?
|
||||
private(set) var authSourceLabel: String?
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "control")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "control")
|
||||
|
||||
private var eventTask: Task<Void, Never>?
|
||||
private var recoveryTask: Task<Void, Never>?
|
||||
@@ -163,8 +163,8 @@ final class ControlChannel {
|
||||
timeoutMs: Double? = nil) async throws -> Data
|
||||
{
|
||||
do {
|
||||
let rawParams = params?.reduce(into: [String: MoltbotKit.AnyCodable]()) {
|
||||
$0[$1.key] = MoltbotKit.AnyCodable($1.value.base)
|
||||
let rawParams = params?.reduce(into: [String: OpenClawKit.AnyCodable]()) {
|
||||
$0[$1.key] = OpenClawKit.AnyCodable($1.value.base)
|
||||
}
|
||||
let data = try await GatewayConnection.shared.request(
|
||||
method: method,
|
||||
@@ -194,9 +194,7 @@ final class ControlChannel {
|
||||
? "gateway.remote.token"
|
||||
: "gateway.auth.token"
|
||||
return
|
||||
"Gateway rejected token; set \(tokenKey) (or CLAWDBOT_GATEWAY_TOKEN) " +
|
||||
"or clear it on the gateway. " +
|
||||
"Reason: \(reason)"
|
||||
"Gateway rejected token; set \(tokenKey) or clear it on the gateway. Reason: \(reason)"
|
||||
}
|
||||
|
||||
// Common misfire: we connected to the configured localhost port but it is occupied
|
||||
@@ -400,20 +398,20 @@ final class ControlChannel {
|
||||
}
|
||||
|
||||
private static func bridgeToProtocolArgs(
|
||||
_ value: MoltbotProtocol.AnyCodable?) -> [String: MoltbotProtocol.AnyCodable]?
|
||||
_ value: OpenClawProtocol.AnyCodable?) -> [String: OpenClawProtocol.AnyCodable]?
|
||||
{
|
||||
guard let value else { return nil }
|
||||
if let dict = value.value as? [String: MoltbotProtocol.AnyCodable] {
|
||||
if let dict = value.value as? [String: OpenClawProtocol.AnyCodable] {
|
||||
return dict
|
||||
}
|
||||
if let dict = value.value as? [String: MoltbotKit.AnyCodable],
|
||||
if let dict = value.value as? [String: OpenClawKit.AnyCodable],
|
||||
let data = try? JSONEncoder().encode(dict),
|
||||
let decoded = try? JSONDecoder().decode([String: MoltbotProtocol.AnyCodable].self, from: data)
|
||||
let decoded = try? JSONDecoder().decode([String: OpenClawProtocol.AnyCodable].self, from: data)
|
||||
{
|
||||
return decoded
|
||||
}
|
||||
if let data = try? JSONEncoder().encode(value),
|
||||
let decoded = try? JSONDecoder().decode([String: MoltbotProtocol.AnyCodable].self, from: data)
|
||||
let decoded = try? JSONDecoder().decode([String: OpenClawProtocol.AnyCodable].self, from: data)
|
||||
{
|
||||
return decoded
|
||||
}
|
||||
@@ -422,6 +420,6 @@ final class ControlChannel {
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
static let controlHeartbeat = Notification.Name("moltbot.control.heartbeat")
|
||||
static let controlAgentEvent = Notification.Name("moltbot.control.agent")
|
||||
static let controlHeartbeat = Notification.Name("openclaw.control.heartbeat")
|
||||
static let controlAgentEvent = Notification.Name("openclaw.control.agent")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import Observation
|
||||
import SwiftUI
|
||||
|
||||
@@ -12,11 +12,11 @@ struct CronJobEditor: View {
|
||||
|
||||
let labelColumnWidth: CGFloat = 160
|
||||
static let introText =
|
||||
"Create a schedule that wakes clawd via the Gateway. "
|
||||
"Create a schedule that wakes OpenClaw via the Gateway. "
|
||||
+ "Use an isolated session for agent turns so your main chat stays clean."
|
||||
static let sessionTargetNote =
|
||||
"Main jobs post a system event into the current main session. "
|
||||
+ "Isolated jobs run clawd in a dedicated session and can deliver results (WhatsApp/Telegram/Discord/etc)."
|
||||
+ "Isolated jobs run OpenClaw in a dedicated session and can deliver results (WhatsApp/Telegram/Discord/etc)."
|
||||
static let scheduleKindNote =
|
||||
"“At” runs once, “Every” repeats with a duration, “Cron” uses a 5-field Unix expression."
|
||||
static let isolatedPayloadNote =
|
||||
@@ -322,7 +322,7 @@ struct CronJobEditor: View {
|
||||
Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 14, verticalSpacing: 10) {
|
||||
GridRow {
|
||||
self.gridLabel("Message")
|
||||
TextField("What should clawd do?", text: self.$agentMessage, axis: .vertical)
|
||||
TextField("What should OpenClaw do?", text: self.$agentMessage, axis: .vertical)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.lineLimit(3...7)
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -1,5 +1,5 @@
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
import Observation
|
||||
import OSLog
|
||||
@@ -22,7 +22,7 @@ final class CronJobsStore {
|
||||
var lastError: String?
|
||||
var statusMessage: String?
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "cron.ui")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "cron.ui")
|
||||
private var refreshTask: Task<Void, Never>?
|
||||
private var runsTask: Task<Void, Never>?
|
||||
private var eventTask: Task<Void, Never>?
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotProtocol
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
|
||||
extension CronSettings {
|
||||
@@ -57,7 +57,7 @@ extension CronSettings {
|
||||
static func exerciseForTesting() {
|
||||
let store = CronJobsStore(isPreview: true)
|
||||
store.schedulerEnabled = false
|
||||
store.schedulerStorePath = "/tmp/moltbot-cron-store.json"
|
||||
store.schedulerStorePath = "/tmp/openclaw-cron-store.json"
|
||||
|
||||
let job = CronJob(
|
||||
id: "job-1",
|
||||
@@ -3,9 +3,9 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
enum DebugActions {
|
||||
private static let verboseDefaultsKey = "moltbot.debug.verboseMain"
|
||||
private static let verboseDefaultsKey = "openclaw.debug.verboseMain"
|
||||
private static let sessionMenuLimit = 12
|
||||
private static let onboardingSeenKey = "moltbot.onboardingSeen"
|
||||
private static let onboardingSeenKey = "openclaw.onboardingSeen"
|
||||
|
||||
@MainActor
|
||||
static func openAgentEventsWindow() {
|
||||
@@ -38,9 +38,7 @@ enum DebugActions {
|
||||
|
||||
@MainActor
|
||||
static func openConfigFolder() {
|
||||
let url = FileManager()
|
||||
.homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".clawdbot", isDirectory: true)
|
||||
let url = OpenClawPaths.stateDirURL
|
||||
NSWorkspace.shared.activateFileViewerSelecting([url])
|
||||
}
|
||||
|
||||
@@ -63,7 +61,7 @@ enum DebugActions {
|
||||
}
|
||||
|
||||
static func sendTestNotification() async {
|
||||
_ = await NotificationManager().send(title: "Moltbot", body: "Test notification", sound: nil)
|
||||
_ = await NotificationManager().send(title: "OpenClaw", body: "Test notification", sound: nil)
|
||||
}
|
||||
|
||||
static func sendDebugVoice() async -> Result<String, DebugActionError> {
|
||||
@@ -195,8 +193,7 @@ enum DebugActions {
|
||||
@MainActor
|
||||
private static func resolveSessionStorePath() -> String {
|
||||
let defaultPath = SessionLoader.defaultStorePath
|
||||
let configURL = FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".clawdbot/moltbot.json")
|
||||
let configURL = OpenClawPaths.configURL
|
||||
guard
|
||||
let data = try? Data(contentsOf: configURL),
|
||||
let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
@@ -103,7 +103,7 @@ struct DebugSettings: View {
|
||||
}
|
||||
|
||||
Text(
|
||||
"When enabled, Moltbot won't install or manage \(gatewayLaunchdLabel). " +
|
||||
"When enabled, OpenClaw won't install or manage \(gatewayLaunchdLabel). " +
|
||||
"It will only attach to an existing Gateway.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
@@ -203,7 +203,7 @@ struct DebugSettings: View {
|
||||
Button("Copy sample URL") {
|
||||
let msg = "Hello from deep link"
|
||||
let encoded = msg.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? msg
|
||||
let url = "moltbot://agent?message=\(encoded)&key=\(key)"
|
||||
let url = "openclaw://agent?message=\(encoded)&key=\(key)"
|
||||
NSPasteboard.general.clearContents()
|
||||
NSPasteboard.general.setString(url, forType: .string)
|
||||
}
|
||||
@@ -211,7 +211,7 @@ struct DebugSettings: View {
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
|
||||
Text("Deep links (moltbot://…) are always enabled; the key controls unattended runs.")
|
||||
Text("Deep links (openclaw://…) are always enabled; the key controls unattended runs.")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
@@ -274,7 +274,7 @@ struct DebugSettings: View {
|
||||
Toggle("Write rolling diagnostics log (JSONL)", isOn: self.$diagnosticsFileLogEnabled)
|
||||
.toggleStyle(.checkbox)
|
||||
.help(
|
||||
"Writes a rotating, local-only log under ~/Library/Logs/Moltbot/. " +
|
||||
"Writes a rotating, local-only log under ~/Library/Logs/OpenClaw/. " +
|
||||
"Enable only while actively debugging.")
|
||||
|
||||
HStack(spacing: 8) {
|
||||
@@ -382,10 +382,10 @@ struct DebugSettings: View {
|
||||
GroupBox("Paths") {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text("Moltbot project root")
|
||||
Text("OpenClaw project root")
|
||||
.font(.caption.weight(.semibold))
|
||||
HStack(spacing: 8) {
|
||||
TextField("Path to moltbot repo", text: self.$gatewayRootInput)
|
||||
TextField("Path to openclaw repo", text: self.$gatewayRootInput)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.font(.caption.monospaced())
|
||||
.onSubmit { self.saveRelayRoot() }
|
||||
@@ -393,7 +393,7 @@ struct DebugSettings: View {
|
||||
.buttonStyle(.borderedProminent)
|
||||
Button("Reset") {
|
||||
let def = FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent("Projects/moltbot").path
|
||||
.appendingPathComponent("Projects/openclaw").path
|
||||
self.gatewayRootInput = def
|
||||
self.saveRelayRoot()
|
||||
}
|
||||
@@ -423,7 +423,7 @@ struct DebugSettings: View {
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Text("Used by the CLI session loader; stored in ~/.clawdbot/moltbot.json.")
|
||||
Text("Used by the CLI session loader; stored in ~/.openclaw/openclaw.json.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -524,15 +524,15 @@ struct DebugSettings: View {
|
||||
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text(
|
||||
"Note: macOS may require restarting Moltbot after enabling Accessibility or Screen Recording.")
|
||||
"Note: macOS may require restarting OpenClaw after enabling Accessibility or Screen Recording.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Button {
|
||||
LaunchdManager.startMoltbot()
|
||||
LaunchdManager.startOpenClaw()
|
||||
} label: {
|
||||
Label("Restart Moltbot", systemImage: "arrow.counterclockwise")
|
||||
Label("Restart OpenClaw", systemImage: "arrow.counterclockwise")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
@@ -830,9 +830,7 @@ struct DebugSettings: View {
|
||||
}
|
||||
|
||||
private func configURL() -> URL {
|
||||
FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".clawdbot")
|
||||
.appendingPathComponent("moltbot.json")
|
||||
OpenClawPaths.configURL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,7 +979,7 @@ extension DebugSettings {
|
||||
view.modelsCount = 3
|
||||
view.modelsLoading = false
|
||||
view.modelsError = "Failed to load models"
|
||||
view.gatewayRootInput = "/tmp/moltbot"
|
||||
view.gatewayRootInput = "/tmp/openclaw"
|
||||
view.sessionStorePath = "/tmp/sessions.json"
|
||||
view.sessionStoreSaveError = "Save failed"
|
||||
view.debugSendInFlight = true
|
||||
@@ -1,10 +1,10 @@
|
||||
import AppKit
|
||||
import MoltbotKit
|
||||
import OpenClawKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
import Security
|
||||
|
||||
private let deepLinkLogger = Logger(subsystem: "bot.molt", category: "DeepLink")
|
||||
private let deepLinkLogger = Logger(subsystem: "ai.openclaw", category: "DeepLink")
|
||||
|
||||
@MainActor
|
||||
final class DeepLinkHandler {
|
||||
@@ -23,7 +23,7 @@ final class DeepLinkHandler {
|
||||
return
|
||||
}
|
||||
guard !AppStateStore.shared.isPaused else {
|
||||
self.presentAlert(title: "Moltbot is paused", message: "Unpause Moltbot to run agent actions.")
|
||||
self.presentAlert(title: "OpenClaw is paused", message: "Unpause OpenClaw to run agent actions.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ final class DeepLinkHandler {
|
||||
let trimmed = messagePreview.count > 240 ? "\(messagePreview.prefix(240))…" : messagePreview
|
||||
let body =
|
||||
"Run the agent with this message?\n\n\(trimmed)\n\nURL:\n\(originalURL.absoluteString)"
|
||||
guard self.confirm(title: "Run Moltbot agent?", message: body) else { return }
|
||||
guard self.confirm(title: "Run OpenClaw agent?", message: body) else { return }
|
||||
}
|
||||
|
||||
if AppStateStore.shared.connectionMode == .local {
|
||||
@@ -1,6 +1,6 @@
|
||||
import AppKit
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
import Observation
|
||||
import OSLog
|
||||
@@ -10,7 +10,7 @@ import OSLog
|
||||
final class DevicePairingApprovalPrompter {
|
||||
static let shared = DevicePairingApprovalPrompter()
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "device-pairing")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "device-pairing")
|
||||
private var task: Task<Void, Never>?
|
||||
private var isStopping = false
|
||||
private var isPresenting = false
|
||||
@@ -24,7 +24,7 @@ actor DiagnosticsFileLog {
|
||||
?? FileManager().homeDirectoryForCurrentUser.appendingPathComponent("Library", isDirectory: true)
|
||||
return library
|
||||
.appendingPathComponent("Logs", isDirectory: true)
|
||||
.appendingPathComponent("Moltbot", isDirectory: true)
|
||||
.appendingPathComponent("OpenClaw", isDirectory: true)
|
||||
}
|
||||
|
||||
nonisolated static func logFileURL() -> URL {
|
||||
@@ -6,7 +6,7 @@ final class DockIconManager: NSObject, @unchecked Sendable {
|
||||
static let shared = DockIconManager()
|
||||
|
||||
private var windowsObservation: NSKeyValueObservation?
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "DockIconManager")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "DockIconManager")
|
||||
|
||||
override private init() {
|
||||
super.init()
|
||||
@@ -189,7 +189,7 @@ struct ExecApprovalsResolvedDefaults {
|
||||
}
|
||||
|
||||
enum ExecApprovalsStore {
|
||||
private static let logger = Logger(subsystem: "bot.molt", category: "exec-approvals")
|
||||
private static let logger = Logger(subsystem: "ai.openclaw", category: "exec-approvals")
|
||||
private static let defaultAgentId = "main"
|
||||
private static let defaultSecurity: ExecSecurity = .deny
|
||||
private static let defaultAsk: ExecAsk = .onMiss
|
||||
@@ -197,11 +197,11 @@ enum ExecApprovalsStore {
|
||||
private static let defaultAutoAllowSkills = false
|
||||
|
||||
static func fileURL() -> URL {
|
||||
MoltbotPaths.stateDirURL.appendingPathComponent("exec-approvals.json")
|
||||
OpenClawPaths.stateDirURL.appendingPathComponent("exec-approvals.json")
|
||||
}
|
||||
|
||||
static func socketPath() -> String {
|
||||
MoltbotPaths.stateDirURL.appendingPathComponent("exec-approvals.sock").path
|
||||
OpenClawPaths.stateDirURL.appendingPathComponent("exec-approvals.sock").path
|
||||
}
|
||||
|
||||
static func normalizeIncoming(_ file: ExecApprovalsFile) -> ExecApprovalsFile {
|
||||
@@ -1,5 +1,5 @@
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
import OSLog
|
||||
@@ -8,7 +8,7 @@ import OSLog
|
||||
final class ExecApprovalsGatewayPrompter {
|
||||
static let shared = ExecApprovalsGatewayPrompter()
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "exec-approvals.gateway")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "exec-approvals.gateway")
|
||||
private var task: Task<Void, Never>?
|
||||
|
||||
struct GatewayApprovalRequest: Codable, Sendable {
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppKit
|
||||
import MoltbotKit
|
||||
import OpenClawKit
|
||||
import CryptoKit
|
||||
import Darwin
|
||||
import Foundation
|
||||
@@ -589,7 +589,7 @@ private enum ExecHostExecutor {
|
||||
}
|
||||
|
||||
private final class ExecApprovalsSocketServer: @unchecked Sendable {
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "exec-approvals.socket")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "exec-approvals.socket")
|
||||
private let socketPath: String
|
||||
private let token: String
|
||||
private let onPrompt: @Sendable (ExecApprovalPromptRequest) async -> ExecApprovalDecision
|
||||
@@ -1,10 +1,10 @@
|
||||
import MoltbotChatUI
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import OpenClawChatUI
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
private let gatewayConnectionLogger = Logger(subsystem: "bot.molt", category: "gateway.connection")
|
||||
private let gatewayConnectionLogger = Logger(subsystem: "ai.openclaw", category: "gateway.connection")
|
||||
|
||||
enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
|
||||
case last
|
||||
@@ -272,7 +272,7 @@ actor GatewayConnection {
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
}
|
||||
|
||||
private func sessionDefaultString(_ defaults: [String: MoltbotProtocol.AnyCodable]?, key: String) -> String {
|
||||
private func sessionDefaultString(_ defaults: [String: OpenClawProtocol.AnyCodable]?, key: String) -> String {
|
||||
let raw = defaults?[key]?.value as? String
|
||||
return (raw ?? "").trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
}
|
||||
@@ -503,7 +503,7 @@ extension GatewayConnection {
|
||||
|
||||
func healthOK(timeoutMs: Int = 8000) async throws -> Bool {
|
||||
let data = try await self.requestRaw(method: .health, timeoutMs: Double(timeoutMs))
|
||||
return (try? self.decoder.decode(MoltbotGatewayHealthOK.self, from: data))?.ok ?? true
|
||||
return (try? self.decoder.decode(OpenClawGatewayHealthOK.self, from: data))?.ok ?? true
|
||||
}
|
||||
|
||||
// MARK: - Skills
|
||||
@@ -548,13 +548,13 @@ extension GatewayConnection {
|
||||
keys: [String],
|
||||
limit: Int? = nil,
|
||||
maxChars: Int? = nil,
|
||||
timeoutMs: Int? = nil) async throws -> MoltbotSessionsPreviewPayload
|
||||
timeoutMs: Int? = nil) async throws -> OpenClawSessionsPreviewPayload
|
||||
{
|
||||
let resolvedKeys = keys
|
||||
.map { self.canonicalizeSessionKey($0) }
|
||||
.filter { !$0.isEmpty }
|
||||
if resolvedKeys.isEmpty {
|
||||
return MoltbotSessionsPreviewPayload(ts: 0, previews: [])
|
||||
return OpenClawSessionsPreviewPayload(ts: 0, previews: [])
|
||||
}
|
||||
var params: [String: AnyCodable] = ["keys": AnyCodable(resolvedKeys)]
|
||||
if let limit { params["limit"] = AnyCodable(limit) }
|
||||
@@ -571,7 +571,7 @@ extension GatewayConnection {
|
||||
func chatHistory(
|
||||
sessionKey: String,
|
||||
limit: Int? = nil,
|
||||
timeoutMs: Int? = nil) async throws -> MoltbotChatHistoryPayload
|
||||
timeoutMs: Int? = nil) async throws -> OpenClawChatHistoryPayload
|
||||
{
|
||||
let resolvedKey = self.canonicalizeSessionKey(sessionKey)
|
||||
var params: [String: AnyCodable] = ["sessionKey": AnyCodable(resolvedKey)]
|
||||
@@ -588,8 +588,8 @@ extension GatewayConnection {
|
||||
message: String,
|
||||
thinking: String,
|
||||
idempotencyKey: String,
|
||||
attachments: [MoltbotChatAttachmentPayload],
|
||||
timeoutMs: Int = 30000) async throws -> MoltbotChatSendResponse
|
||||
attachments: [OpenClawChatAttachmentPayload],
|
||||
timeoutMs: Int = 30000) async throws -> OpenClawChatSendResponse
|
||||
{
|
||||
let resolvedKey = self.canonicalizeSessionKey(sessionKey)
|
||||
var params: [String: AnyCodable] = [
|
||||
@@ -7,7 +7,7 @@ import OSLog
|
||||
final class GatewayConnectivityCoordinator {
|
||||
static let shared = GatewayConnectivityCoordinator()
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "gateway.connectivity")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "gateway.connectivity")
|
||||
private var endpointTask: Task<Void, Never>?
|
||||
private var lastResolvedURL: URL?
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotDiscovery
|
||||
import OpenClawDiscovery
|
||||
import Foundation
|
||||
|
||||
enum GatewayDiscoveryHelpers {
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotDiscovery
|
||||
import OpenClawDiscovery
|
||||
import SwiftUI
|
||||
|
||||
struct GatewayDiscoveryInlineList: View {
|
||||
@@ -134,6 +134,6 @@ struct GatewayDiscoveryMenu: View {
|
||||
} label: {
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
}
|
||||
.help("Discover Moltbot gateways on your LAN")
|
||||
.help("Discover OpenClaw gateways on your LAN")
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ actor GatewayEndpointStore {
|
||||
"custom",
|
||||
]
|
||||
private static let remoteConnectingDetail = "Connecting to remote gateway…"
|
||||
private static let staticLogger = Logger(subsystem: "bot.molt", category: "gateway-endpoint")
|
||||
private static let staticLogger = Logger(subsystem: "ai.openclaw", category: "gateway-endpoint")
|
||||
private enum EnvOverrideWarningKind: Sendable {
|
||||
case token
|
||||
case password
|
||||
@@ -43,7 +43,7 @@ actor GatewayEndpointStore {
|
||||
static let live = Deps(
|
||||
mode: { await MainActor.run { AppStateStore.shared.connectionMode } },
|
||||
token: {
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
let isRemote = ConnectionModeResolver.resolve(root: root).mode == .remote
|
||||
return GatewayEndpointStore.resolveGatewayToken(
|
||||
isRemote: isRemote,
|
||||
@@ -52,7 +52,7 @@ actor GatewayEndpointStore {
|
||||
launchdSnapshot: GatewayLaunchAgentManager.launchdConfigSnapshot())
|
||||
},
|
||||
password: {
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
let isRemote = ConnectionModeResolver.resolve(root: root).mode == .remote
|
||||
return GatewayEndpointStore.resolveGatewayPassword(
|
||||
isRemote: isRemote,
|
||||
@@ -62,7 +62,7 @@ actor GatewayEndpointStore {
|
||||
},
|
||||
localPort: { GatewayEnvironment.gatewayPort() },
|
||||
localHost: {
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
let bind = GatewayEndpointStore.resolveGatewayBindMode(
|
||||
root: root,
|
||||
env: ProcessInfo.processInfo.environment)
|
||||
@@ -84,7 +84,7 @@ actor GatewayEndpointStore {
|
||||
env: [String: String],
|
||||
launchdSnapshot: LaunchAgentPlistSnapshot?) -> String?
|
||||
{
|
||||
let raw = env["CLAWDBOT_GATEWAY_PASSWORD"] ?? ""
|
||||
let raw = env["OPENCLAW_GATEWAY_PASSWORD"] ?? ""
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !trimmed.isEmpty {
|
||||
if let configPassword = self.resolveConfigPassword(isRemote: isRemote, root: root),
|
||||
@@ -92,7 +92,7 @@ actor GatewayEndpointStore {
|
||||
{
|
||||
self.warnEnvOverrideOnce(
|
||||
kind: .password,
|
||||
envVar: "CLAWDBOT_GATEWAY_PASSWORD",
|
||||
envVar: "OPENCLAW_GATEWAY_PASSWORD",
|
||||
configKey: isRemote ? "gateway.remote.password" : "gateway.auth.password")
|
||||
}
|
||||
return trimmed
|
||||
@@ -152,7 +152,7 @@ actor GatewayEndpointStore {
|
||||
env: [String: String],
|
||||
launchdSnapshot: LaunchAgentPlistSnapshot?) -> String?
|
||||
{
|
||||
let raw = env["CLAWDBOT_GATEWAY_TOKEN"] ?? ""
|
||||
let raw = env["OPENCLAW_GATEWAY_TOKEN"] ?? ""
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !trimmed.isEmpty {
|
||||
if let configToken = self.resolveConfigToken(isRemote: isRemote, root: root),
|
||||
@@ -161,7 +161,7 @@ actor GatewayEndpointStore {
|
||||
{
|
||||
self.warnEnvOverrideOnce(
|
||||
kind: .token,
|
||||
envVar: "CLAWDBOT_GATEWAY_TOKEN",
|
||||
envVar: "OPENCLAW_GATEWAY_TOKEN",
|
||||
configKey: isRemote ? "gateway.remote.token" : "gateway.auth.token")
|
||||
}
|
||||
return trimmed
|
||||
@@ -230,7 +230,7 @@ actor GatewayEndpointStore {
|
||||
}
|
||||
|
||||
private let deps: Deps
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "gateway-endpoint")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "gateway-endpoint")
|
||||
|
||||
private var state: GatewayEndpointState
|
||||
private var subscribers: [UUID: AsyncStream<GatewayEndpointState>.Continuation] = [:]
|
||||
@@ -243,17 +243,17 @@ actor GatewayEndpointStore {
|
||||
if let modeRaw {
|
||||
initialMode = AppState.ConnectionMode(rawValue: modeRaw) ?? .local
|
||||
} else {
|
||||
let seen = UserDefaults.standard.bool(forKey: "moltbot.onboardingSeen")
|
||||
let seen = UserDefaults.standard.bool(forKey: "openclaw.onboardingSeen")
|
||||
initialMode = seen ? .local : .unconfigured
|
||||
}
|
||||
|
||||
let port = deps.localPort()
|
||||
let bind = GatewayEndpointStore.resolveGatewayBindMode(
|
||||
root: MoltbotConfigFile.loadDict(),
|
||||
root: OpenClawConfigFile.loadDict(),
|
||||
env: ProcessInfo.processInfo.environment)
|
||||
let customBindHost = GatewayEndpointStore.resolveGatewayCustomBindHost(root: MoltbotConfigFile.loadDict())
|
||||
let customBindHost = GatewayEndpointStore.resolveGatewayCustomBindHost(root: OpenClawConfigFile.loadDict())
|
||||
let scheme = GatewayEndpointStore.resolveGatewayScheme(
|
||||
root: MoltbotConfigFile.loadDict(),
|
||||
root: OpenClawConfigFile.loadDict(),
|
||||
env: ProcessInfo.processInfo.environment)
|
||||
let host = GatewayEndpointStore.resolveLocalGatewayHost(
|
||||
bindMode: bind,
|
||||
@@ -303,7 +303,7 @@ actor GatewayEndpointStore {
|
||||
let port = self.deps.localPort()
|
||||
let host = await self.deps.localHost()
|
||||
let scheme = GatewayEndpointStore.resolveGatewayScheme(
|
||||
root: MoltbotConfigFile.loadDict(),
|
||||
root: OpenClawConfigFile.loadDict(),
|
||||
env: ProcessInfo.processInfo.environment)
|
||||
self.setState(.ready(
|
||||
mode: .local,
|
||||
@@ -311,7 +311,7 @@ actor GatewayEndpointStore {
|
||||
token: token,
|
||||
password: password))
|
||||
case .remote:
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
if GatewayRemoteConfig.resolveTransport(root: root) == .direct {
|
||||
guard let url = GatewayRemoteConfig.resolveGatewayUrl(root: root) else {
|
||||
self.cancelRemoteEnsure()
|
||||
@@ -332,7 +332,7 @@ actor GatewayEndpointStore {
|
||||
}
|
||||
self.cancelRemoteEnsure()
|
||||
let scheme = GatewayEndpointStore.resolveGatewayScheme(
|
||||
root: MoltbotConfigFile.loadDict(),
|
||||
root: OpenClawConfigFile.loadDict(),
|
||||
env: ProcessInfo.processInfo.environment)
|
||||
self.setState(.ready(
|
||||
mode: .remote,
|
||||
@@ -354,7 +354,7 @@ actor GatewayEndpointStore {
|
||||
code: 1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Remote mode is not enabled"])
|
||||
}
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
if GatewayRemoteConfig.resolveTransport(root: root) == .direct {
|
||||
guard let url = GatewayRemoteConfig.resolveGatewayUrl(root: root) else {
|
||||
throw NSError(
|
||||
@@ -433,7 +433,7 @@ actor GatewayEndpointStore {
|
||||
userInfo: [NSLocalizedDescriptionKey: "Remote mode is not enabled"])
|
||||
}
|
||||
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
if GatewayRemoteConfig.resolveTransport(root: root) == .direct {
|
||||
guard let url = GatewayRemoteConfig.resolveGatewayUrl(root: root) else {
|
||||
throw NSError(
|
||||
@@ -470,7 +470,7 @@ actor GatewayEndpointStore {
|
||||
let token = self.deps.token()
|
||||
let password = self.deps.password()
|
||||
let scheme = GatewayEndpointStore.resolveGatewayScheme(
|
||||
root: MoltbotConfigFile.loadDict(),
|
||||
root: OpenClawConfigFile.loadDict(),
|
||||
env: ProcessInfo.processInfo.environment)
|
||||
let url = URL(string: "\(scheme)://127.0.0.1:\(Int(forwarded))")!
|
||||
self.setState(.ready(mode: .remote, url: url, token: token, password: password))
|
||||
@@ -525,7 +525,7 @@ actor GatewayEndpointStore {
|
||||
let mode = await self.deps.mode()
|
||||
guard mode == .local else { return nil }
|
||||
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
let bind = GatewayEndpointStore.resolveGatewayBindMode(
|
||||
root: root,
|
||||
env: ProcessInfo.processInfo.environment)
|
||||
@@ -555,7 +555,7 @@ actor GatewayEndpointStore {
|
||||
root: [String: Any],
|
||||
env: [String: String]) -> String?
|
||||
{
|
||||
if let envBind = env["CLAWDBOT_GATEWAY_BIND"] {
|
||||
if let envBind = env["OPENCLAW_GATEWAY_BIND"] {
|
||||
let trimmed = envBind.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||
if self.supportedBindModes.contains(trimmed) {
|
||||
return trimmed
|
||||
@@ -586,7 +586,7 @@ actor GatewayEndpointStore {
|
||||
root: [String: Any],
|
||||
env: [String: String]) -> String
|
||||
{
|
||||
if let envValue = env["CLAWDBOT_GATEWAY_TLS"]?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
if let envValue = env["OPENCLAW_GATEWAY_TLS"]?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!envValue.isEmpty
|
||||
{
|
||||
return (envValue == "1" || envValue.lowercased() == "true") ? "wss" : "ws"
|
||||
@@ -1,4 +1,4 @@
|
||||
import MoltbotIPC
|
||||
import OpenClawIPC
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
@@ -68,15 +68,15 @@ struct GatewayCommandResolution {
|
||||
}
|
||||
|
||||
enum GatewayEnvironment {
|
||||
private static let logger = Logger(subsystem: "bot.molt", category: "gateway.env")
|
||||
private static let logger = Logger(subsystem: "ai.openclaw", category: "gateway.env")
|
||||
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
|
||||
|
||||
static func gatewayPort() -> Int {
|
||||
if let raw = ProcessInfo.processInfo.environment["CLAWDBOT_GATEWAY_PORT"] {
|
||||
if let raw = ProcessInfo.processInfo.environment["OPENCLAW_GATEWAY_PORT"] {
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if let parsed = Int(trimmed), parsed > 0 { return parsed }
|
||||
}
|
||||
if let configPort = MoltbotConfigFile.gatewayPort(), configPort > 0 {
|
||||
if let configPort = OpenClawConfigFile.gatewayPort(), configPort > 0 {
|
||||
return configPort
|
||||
}
|
||||
let stored = UserDefaults.standard.integer(forKey: "gatewayPort")
|
||||
@@ -123,7 +123,7 @@ enum GatewayEnvironment {
|
||||
requiredGateway: expectedString,
|
||||
message: RuntimeLocator.describeFailure(err))
|
||||
case let .success(runtime):
|
||||
let gatewayBin = CommandResolver.moltbotExecutable()
|
||||
let gatewayBin = CommandResolver.openclawExecutable()
|
||||
|
||||
if gatewayBin == nil, projectEntrypoint == nil {
|
||||
return GatewayEnvironmentStatus(
|
||||
@@ -131,7 +131,7 @@ enum GatewayEnvironment {
|
||||
nodeVersion: runtime.version.description,
|
||||
gatewayVersion: nil,
|
||||
requiredGateway: expectedString,
|
||||
message: "moltbot CLI not found in PATH; install the CLI.")
|
||||
message: "openclaw CLI not found in PATH; install the CLI.")
|
||||
}
|
||||
|
||||
let installed = gatewayBin.flatMap { self.readGatewayVersion(binary: $0) }
|
||||
@@ -181,7 +181,7 @@ enum GatewayEnvironment {
|
||||
let projectRoot = CommandResolver.projectRoot()
|
||||
let projectEntrypoint = CommandResolver.gatewayEntrypoint(in: projectRoot)
|
||||
let status = self.check()
|
||||
let gatewayBin = CommandResolver.moltbotExecutable()
|
||||
let gatewayBin = CommandResolver.openclawExecutable()
|
||||
let runtime = RuntimeLocator.resolve(searchPaths: CommandResolver.preferredPaths())
|
||||
|
||||
guard case .ok = status.kind else {
|
||||
@@ -210,14 +210,14 @@ enum GatewayEnvironment {
|
||||
if CommandResolver.connectionModeIsRemote() {
|
||||
return nil
|
||||
}
|
||||
if let env = ProcessInfo.processInfo.environment["CLAWDBOT_GATEWAY_BIND"] {
|
||||
if let env = ProcessInfo.processInfo.environment["OPENCLAW_GATEWAY_BIND"] {
|
||||
let trimmed = env.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||
if self.supportedBindModes.contains(trimmed) {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
if let gateway = root["gateway"] as? [String: Any],
|
||||
let bind = gateway["bind"] as? String
|
||||
{
|
||||
@@ -247,16 +247,16 @@ enum GatewayEnvironment {
|
||||
let bun = CommandResolver.findExecutable(named: "bun")
|
||||
let (label, cmd): (String, [String]) =
|
||||
if let npm {
|
||||
("npm", [npm, "install", "-g", "moltbot@\(target)"])
|
||||
("npm", [npm, "install", "-g", "openclaw@\(target)"])
|
||||
} else if let pnpm {
|
||||
("pnpm", [pnpm, "add", "-g", "moltbot@\(target)"])
|
||||
("pnpm", [pnpm, "add", "-g", "openclaw@\(target)"])
|
||||
} else if let bun {
|
||||
("bun", [bun, "add", "-g", "moltbot@\(target)"])
|
||||
("bun", [bun, "add", "-g", "openclaw@\(target)"])
|
||||
} else {
|
||||
("npm", ["npm", "install", "-g", "moltbot@\(target)"])
|
||||
("npm", ["npm", "install", "-g", "openclaw@\(target)"])
|
||||
}
|
||||
|
||||
statusHandler("Installing moltbot@\(target) via \(label)…")
|
||||
statusHandler("Installing openclaw@\(target) via \(label)…")
|
||||
|
||||
func summarize(_ text: String) -> String? {
|
||||
let lines = text
|
||||
@@ -270,7 +270,7 @@ enum GatewayEnvironment {
|
||||
|
||||
let response = await ShellExecutor.runDetailed(command: cmd, cwd: nil, env: ["PATH": preferred], timeout: 300)
|
||||
if response.success {
|
||||
statusHandler("Installed moltbot@\(target)")
|
||||
statusHandler("Installed openclaw@\(target)")
|
||||
} else {
|
||||
if response.timedOut {
|
||||
statusHandler("Install failed: timed out. Check your internet connection and try again.")
|
||||
@@ -1,8 +1,8 @@
|
||||
import Foundation
|
||||
|
||||
enum GatewayLaunchAgentManager {
|
||||
private static let logger = Logger(subsystem: "bot.molt", category: "gateway.launchd")
|
||||
private static let disableLaunchAgentMarker = ".clawdbot/disable-launchagent"
|
||||
private static let logger = Logger(subsystem: "ai.openclaw", category: "gateway.launchd")
|
||||
private static let disableLaunchAgentMarker = ".openclaw/disable-launchagent"
|
||||
|
||||
private static var disableLaunchAgentMarkerURL: URL {
|
||||
FileManager().homeDirectoryForCurrentUser
|
||||
@@ -15,7 +15,8 @@ enum GatewayLaunchAgentManager {
|
||||
}
|
||||
|
||||
static func isLaunchAgentWriteDisabled() -> Bool {
|
||||
FileManager().fileExists(atPath: self.disableLaunchAgentMarkerURL.path)
|
||||
if FileManager().fileExists(atPath: self.disableLaunchAgentMarkerURL.path) { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
static func setLaunchAgentWriteDisabled(_ disabled: Bool) -> String? {
|
||||
@@ -143,7 +144,7 @@ extension GatewayLaunchAgentManager {
|
||||
timeout: Double,
|
||||
quiet: Bool) async -> CommandResult
|
||||
{
|
||||
let command = CommandResolver.moltbotCommand(
|
||||
let command = CommandResolver.openclawCommand(
|
||||
subcommand: "gateway",
|
||||
extraArgs: self.withJsonFlag(args),
|
||||
// Launchd management must always run locally, even if remote mode is configured.
|
||||
@@ -45,7 +45,7 @@ final class GatewayProcessManager {
|
||||
#if DEBUG
|
||||
private var testingConnection: GatewayConnection?
|
||||
#endif
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "gateway.process")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "gateway.process")
|
||||
|
||||
private let logLimit = 20000 // characters to keep in-memory
|
||||
private let environmentRefreshMinInterval: TimeInterval = 30
|
||||
@@ -270,8 +270,8 @@ final class GatewayProcessManager {
|
||||
let lower = message.lowercased()
|
||||
if self.isGatewayAuthFailure(error) {
|
||||
return """
|
||||
Gateway on port \(port) rejected auth. Set gateway.auth.token (or CLAWDBOT_GATEWAY_TOKEN) \
|
||||
to match the running gateway (or clear it on the gateway) and retry.
|
||||
Gateway on port \(port) rejected auth. Set gateway.auth.token to match the running gateway \
|
||||
(or clear it on the gateway) and retry.
|
||||
"""
|
||||
}
|
||||
if lower.contains("protocol mismatch") {
|
||||
@@ -1,7 +1,7 @@
|
||||
import AppKit
|
||||
import MoltbotDiscovery
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import OpenClawDiscovery
|
||||
import OpenClawIPC
|
||||
import OpenClawKit
|
||||
import Observation
|
||||
import SwiftUI
|
||||
|
||||
@@ -24,8 +24,8 @@ struct GeneralSettings: View {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
SettingsToggleRow(
|
||||
title: "Moltbot active",
|
||||
subtitle: "Pause to stop the Moltbot gateway; no messages will be processed.",
|
||||
title: "OpenClaw active",
|
||||
subtitle: "Pause to stop the OpenClaw gateway; no messages will be processed.",
|
||||
binding: self.activeBinding)
|
||||
|
||||
self.connectionSection
|
||||
@@ -34,12 +34,12 @@ struct GeneralSettings: View {
|
||||
|
||||
SettingsToggleRow(
|
||||
title: "Launch at login",
|
||||
subtitle: "Automatically start Moltbot after you sign in.",
|
||||
subtitle: "Automatically start OpenClaw after you sign in.",
|
||||
binding: self.$state.launchAtLogin)
|
||||
|
||||
SettingsToggleRow(
|
||||
title: "Show Dock icon",
|
||||
subtitle: "Keep Moltbot visible in the Dock instead of menu-bar-only mode.",
|
||||
subtitle: "Keep OpenClaw visible in the Dock instead of menu-bar-only mode.",
|
||||
binding: self.$state.showDockIcon)
|
||||
|
||||
SettingsToggleRow(
|
||||
@@ -71,7 +71,7 @@ struct GeneralSettings: View {
|
||||
Spacer(minLength: 12)
|
||||
HStack {
|
||||
Spacer()
|
||||
Button("Quit Moltbot") { NSApp.terminate(nil) }
|
||||
Button("Quit OpenClaw") { NSApp.terminate(nil) }
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ struct GeneralSettings: View {
|
||||
|
||||
private var connectionSection: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text("Moltbot runs")
|
||||
Text("OpenClaw runs")
|
||||
.font(.title3.weight(.semibold))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
@@ -167,12 +167,12 @@ struct GeneralSettings: View {
|
||||
.frame(width: 280)
|
||||
}
|
||||
LabeledContent("Project root") {
|
||||
TextField("/home/you/Projects/moltbot", text: self.$state.remoteProjectRoot)
|
||||
TextField("/home/you/Projects/openclaw", text: self.$state.remoteProjectRoot)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(width: 280)
|
||||
}
|
||||
LabeledContent("CLI path") {
|
||||
TextField("/Applications/Moltbot.app/.../moltbot", text: self.$state.remoteCliPath)
|
||||
TextField("/Applications/OpenClaw.app/.../openclaw", text: self.$state.remoteCliPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(width: 280)
|
||||
}
|
||||
@@ -659,7 +659,7 @@ extension GeneralSettings {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Log file not found"
|
||||
alert.informativeText = """
|
||||
Looked for moltbot logs in /tmp/moltbot/.
|
||||
Looked for openclaw logs in /tmp/openclaw/.
|
||||
Run a health check or send a message to generate activity, then try again.
|
||||
"""
|
||||
alert.alertStyle = .informational
|
||||
@@ -683,7 +683,7 @@ extension GeneralSettings {
|
||||
host: host,
|
||||
port: gateway.sshPort)
|
||||
self.state.remoteCliPath = gateway.cliPath ?? ""
|
||||
MoltbotConfigFile.setRemoteGatewayUrl(host: host, port: gateway.gatewayPort)
|
||||
OpenClawConfigFile.setRemoteGatewayUrl(host: host, port: gateway.gatewayPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -711,8 +711,8 @@ extension GeneralSettings {
|
||||
state.remoteTarget = "user@host:2222"
|
||||
state.remoteUrl = "wss://gateway.example.ts.net"
|
||||
state.remoteIdentity = "/tmp/id_ed25519"
|
||||
state.remoteProjectRoot = "/tmp/moltbot"
|
||||
state.remoteCliPath = "/tmp/moltbot"
|
||||
state.remoteProjectRoot = "/tmp/openclaw"
|
||||
state.remoteCliPath = "/tmp/openclaw"
|
||||
|
||||
let view = GeneralSettings(state: state)
|
||||
view.gatewayStatus = GatewayEnvironmentStatus(
|
||||
@@ -72,7 +72,7 @@ enum HealthState: Equatable {
|
||||
final class HealthStore {
|
||||
static let shared = HealthStore()
|
||||
|
||||
private static let logger = Logger(subsystem: "bot.molt", category: "health")
|
||||
private static let logger = Logger(subsystem: "ai.openclaw", category: "health")
|
||||
|
||||
private(set) var snapshot: HealthSnapshot?
|
||||
private(set) var lastSuccess: Date?
|
||||
@@ -221,9 +221,9 @@ final class HealthStore {
|
||||
if let fallback = self.resolveFallbackChannel(snap, excluding: link.id) {
|
||||
let fallbackLabel = snap.channelLabels?[fallback.id] ?? fallback.id.capitalized
|
||||
let fallbackState = (fallback.summary.probe?.ok ?? true) ? "ok" : "degraded"
|
||||
return "\(fallbackLabel) \(fallbackState) · Not linked — run moltbot login"
|
||||
return "\(fallbackLabel) \(fallbackState) · Not linked — run openclaw login"
|
||||
}
|
||||
return "Not linked — run moltbot login"
|
||||
return "Not linked — run openclaw login"
|
||||
}
|
||||
let auth = link.summary.authAgeMs.map { msToAge($0) } ?? "unknown"
|
||||
if let probe = link.summary.probe, probe.ok == false {
|
||||
@@ -241,7 +241,7 @@ final class HealthStore {
|
||||
if lower.contains("connection refused") {
|
||||
let port = GatewayEnvironment.gatewayPort()
|
||||
let host = GatewayConnectivityCoordinator.shared.localEndpointHostLabel ?? "127.0.0.1:\(port)"
|
||||
return "The gateway control port (\(host)) isn’t listening — restart Moltbot to bring it back."
|
||||
return "The gateway control port (\(host)) isn’t listening — restart OpenClaw to bring it back."
|
||||
}
|
||||
if lower.contains("timeout") {
|
||||
return "Timed out waiting for the control server; the gateway may be crashed or still starting."
|
||||
@@ -253,7 +253,7 @@ final class HealthStore {
|
||||
|
||||
func describeFailure(from snap: HealthSnapshot, fallback: String?) -> String {
|
||||
if let link = self.resolveLinkChannel(snap), link.summary.linked != true {
|
||||
return "Not linked — run moltbot login"
|
||||
return "Not linked — run openclaw login"
|
||||
}
|
||||
if let link = self.resolveLinkChannel(snap), let probe = link.summary.probe, probe.ok == false {
|
||||
return Self.describeProbeFailure(probe)
|
||||
@@ -38,7 +38,7 @@ struct InstancesSettings: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Connected Instances")
|
||||
.font(.headline)
|
||||
Text("Latest presence beacons from Moltbot nodes. Updated periodically.")
|
||||
Text("Latest presence beacons from OpenClaw nodes. Updated periodically.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import Cocoa
|
||||
import Foundation
|
||||
import Observation
|
||||
@@ -41,7 +41,7 @@ final class InstancesStore {
|
||||
var statusMessage: String?
|
||||
var isLoading = false
|
||||
|
||||
private let logger = Logger(subsystem: "bot.molt", category: "instances")
|
||||
private let logger = Logger(subsystem: "ai.openclaw", category: "instances")
|
||||
private var task: Task<Void, Never>?
|
||||
private let interval: TimeInterval = 30
|
||||
private var eventTask: Task<Void, Never>?
|
||||
@@ -293,7 +293,7 @@ final class InstancesStore {
|
||||
}
|
||||
}
|
||||
|
||||
func handlePresenceEventPayload(_ payload: MoltbotProtocol.AnyCodable) {
|
||||
func handlePresenceEventPayload(_ payload: OpenClawProtocol.AnyCodable) {
|
||||
do {
|
||||
let wrapper = try GatewayPayloadDecoding.decode(payload, as: PresenceEventPayload.self)
|
||||
self.applyPresence(wrapper.presence)
|
||||
@@ -1,20 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
enum LaunchAgentManager {
|
||||
private static let legacyLaunchdLabels = [
|
||||
"com.steipete.clawdbot",
|
||||
"com.clawdbot.mac",
|
||||
]
|
||||
private static var plistURL: URL {
|
||||
FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent("Library/LaunchAgents/bot.molt.mac.plist")
|
||||
}
|
||||
|
||||
private static var legacyPlistURLs: [URL] {
|
||||
self.legacyLaunchdLabels.map { label in
|
||||
FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent("Library/LaunchAgents/\(label).plist")
|
||||
}
|
||||
.appendingPathComponent("Library/LaunchAgents/ai.openclaw.mac.plist")
|
||||
}
|
||||
|
||||
static func status() async -> Bool {
|
||||
@@ -25,12 +14,6 @@ enum LaunchAgentManager {
|
||||
|
||||
static func set(enabled: Bool, bundlePath: String) async {
|
||||
if enabled {
|
||||
for legacyLabel in self.legacyLaunchdLabels {
|
||||
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(legacyLabel)"])
|
||||
}
|
||||
for legacyURL in self.legacyPlistURLs {
|
||||
try? FileManager().removeItem(at: legacyURL)
|
||||
}
|
||||
self.writePlist(bundlePath: bundlePath)
|
||||
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(launchdLabel)"])
|
||||
_ = await self.runLaunchctl(["bootstrap", "gui/\(getuid())", self.plistURL.path])
|
||||
@@ -49,10 +32,10 @@ enum LaunchAgentManager {
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>bot.molt.mac</string>
|
||||
<string>ai.openclaw.mac</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>\(bundlePath)/Contents/MacOS/Moltbot</string>
|
||||
<string>\(bundlePath)/Contents/MacOS/OpenClaw</string>
|
||||
</array>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>\(FileManager().homeDirectoryForCurrentUser.path)</string>
|
||||
@@ -59,8 +59,8 @@ enum LaunchAgentPlist {
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
|
||||
let port = Self.extractFlagInt(programArguments, flag: "--port")
|
||||
let bind = Self.extractFlagString(programArguments, flag: "--bind")?.lowercased()
|
||||
let token = env["CLAWDBOT_GATEWAY_TOKEN"]?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
|
||||
let password = env["CLAWDBOT_GATEWAY_PASSWORD"]?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
|
||||
let token = env["OPENCLAW_GATEWAY_TOKEN"]?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
|
||||
let password = env["OPENCLAW_GATEWAY_PASSWORD"]?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
|
||||
return LaunchAgentPlistSnapshot(
|
||||
programArguments: programArguments,
|
||||
environment: env,
|
||||
@@ -8,12 +8,12 @@ enum LaunchdManager {
|
||||
try? process.run()
|
||||
}
|
||||
|
||||
static func startMoltbot() {
|
||||
static func startOpenClaw() {
|
||||
let userTarget = "gui/\(getuid())/\(launchdLabel)"
|
||||
self.runLaunchctl(["kickstart", "-k", userTarget])
|
||||
}
|
||||
|
||||
static func stopMoltbot() {
|
||||
static func stopOpenClaw() {
|
||||
let userTarget = "gui/\(getuid())/\(launchdLabel)"
|
||||
self.runLaunchctl(["stop", userTarget])
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user