refactor: rename clawdbot to moltbot with legacy compat
This commit is contained in:
@@ -65,7 +65,7 @@ androidComponents {
|
||||
val versionName = output.versionName.orNull ?: "0"
|
||||
val buildType = variant.buildType
|
||||
|
||||
val outputFileName = "clawdbot-${versionName}-${buildType}.apk"
|
||||
val outputFileName = "moltbot-${versionName}-${buildType}.apk"
|
||||
output.outputFileName = outputFileName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/Theme.ClawdbotNode">
|
||||
android:theme="@style/Theme.MoltbotNode">
|
||||
<service
|
||||
android:name=".NodeForegroundService"
|
||||
android:exported="false"
|
||||
|
||||
@@ -19,7 +19,7 @@ import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.clawdbot.android.ui.RootScreen
|
||||
import com.clawdbot.android.ui.ClawdbotTheme
|
||||
import com.clawdbot.android.ui.MoltbotTheme
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@@ -56,7 +56,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
setContent {
|
||||
ClawdbotTheme {
|
||||
MoltbotTheme {
|
||||
Surface(modifier = Modifier) {
|
||||
RootScreen(viewModel = viewModel)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class NodeForegroundService : Service() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
ensureChannel()
|
||||
val initial = buildNotification(title = "Clawdbot Node", text = "Starting…")
|
||||
val initial = buildNotification(title = "Moltbot Node", text = "Starting…")
|
||||
startForegroundWithTypes(notification = initial, requiresMic = false)
|
||||
|
||||
val runtime = (application as NodeApp).runtime
|
||||
@@ -44,7 +44,7 @@ class NodeForegroundService : Service() {
|
||||
) { status, server, connected, voiceMode, voiceListening ->
|
||||
Quint(status, server, connected, voiceMode, voiceListening)
|
||||
}.collect { (status, server, connected, voiceMode, voiceListening) ->
|
||||
val title = if (connected) "Clawdbot Node · Connected" else "Clawdbot Node"
|
||||
val title = if (connected) "Moltbot Node · Connected" else "Moltbot Node"
|
||||
val voiceSuffix =
|
||||
if (voiceMode == VoiceWakeMode.Always) {
|
||||
if (voiceListening) " · Voice Wake: Listening" else " · Voice Wake: Paused"
|
||||
@@ -91,7 +91,7 @@ class NodeForegroundService : Service() {
|
||||
"Connection",
|
||||
NotificationManager.IMPORTANCE_LOW,
|
||||
).apply {
|
||||
description = "Clawdbot node connection status"
|
||||
description = "Moltbot node connection status"
|
||||
setShowBadge(false)
|
||||
}
|
||||
mgr.createNotificationChannel(channel)
|
||||
|
||||
@@ -26,14 +26,14 @@ import com.clawdbot.android.BuildConfig
|
||||
import com.clawdbot.android.node.CanvasController
|
||||
import com.clawdbot.android.node.ScreenRecordManager
|
||||
import com.clawdbot.android.node.SmsManager
|
||||
import com.clawdbot.android.protocol.ClawdbotCapability
|
||||
import com.clawdbot.android.protocol.ClawdbotCameraCommand
|
||||
import com.clawdbot.android.protocol.ClawdbotCanvasA2UIAction
|
||||
import com.clawdbot.android.protocol.ClawdbotCanvasA2UICommand
|
||||
import com.clawdbot.android.protocol.ClawdbotCanvasCommand
|
||||
import com.clawdbot.android.protocol.ClawdbotScreenCommand
|
||||
import com.clawdbot.android.protocol.ClawdbotLocationCommand
|
||||
import com.clawdbot.android.protocol.ClawdbotSmsCommand
|
||||
import com.clawdbot.android.protocol.MoltbotCapability
|
||||
import com.clawdbot.android.protocol.MoltbotCameraCommand
|
||||
import com.clawdbot.android.protocol.MoltbotCanvasA2UIAction
|
||||
import com.clawdbot.android.protocol.MoltbotCanvasA2UICommand
|
||||
import com.clawdbot.android.protocol.MoltbotCanvasCommand
|
||||
import com.clawdbot.android.protocol.MoltbotScreenCommand
|
||||
import com.clawdbot.android.protocol.MoltbotLocationCommand
|
||||
import com.clawdbot.android.protocol.MoltbotSmsCommand
|
||||
import com.clawdbot.android.voice.TalkModeManager
|
||||
import com.clawdbot.android.voice.VoiceWakeManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -451,38 +451,38 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
private fun buildInvokeCommands(): List<String> =
|
||||
buildList {
|
||||
add(ClawdbotCanvasCommand.Present.rawValue)
|
||||
add(ClawdbotCanvasCommand.Hide.rawValue)
|
||||
add(ClawdbotCanvasCommand.Navigate.rawValue)
|
||||
add(ClawdbotCanvasCommand.Eval.rawValue)
|
||||
add(ClawdbotCanvasCommand.Snapshot.rawValue)
|
||||
add(ClawdbotCanvasA2UICommand.Push.rawValue)
|
||||
add(ClawdbotCanvasA2UICommand.PushJSONL.rawValue)
|
||||
add(ClawdbotCanvasA2UICommand.Reset.rawValue)
|
||||
add(ClawdbotScreenCommand.Record.rawValue)
|
||||
add(MoltbotCanvasCommand.Present.rawValue)
|
||||
add(MoltbotCanvasCommand.Hide.rawValue)
|
||||
add(MoltbotCanvasCommand.Navigate.rawValue)
|
||||
add(MoltbotCanvasCommand.Eval.rawValue)
|
||||
add(MoltbotCanvasCommand.Snapshot.rawValue)
|
||||
add(MoltbotCanvasA2UICommand.Push.rawValue)
|
||||
add(MoltbotCanvasA2UICommand.PushJSONL.rawValue)
|
||||
add(MoltbotCanvasA2UICommand.Reset.rawValue)
|
||||
add(MoltbotScreenCommand.Record.rawValue)
|
||||
if (cameraEnabled.value) {
|
||||
add(ClawdbotCameraCommand.Snap.rawValue)
|
||||
add(ClawdbotCameraCommand.Clip.rawValue)
|
||||
add(MoltbotCameraCommand.Snap.rawValue)
|
||||
add(MoltbotCameraCommand.Clip.rawValue)
|
||||
}
|
||||
if (locationMode.value != LocationMode.Off) {
|
||||
add(ClawdbotLocationCommand.Get.rawValue)
|
||||
add(MoltbotLocationCommand.Get.rawValue)
|
||||
}
|
||||
if (sms.canSendSms()) {
|
||||
add(ClawdbotSmsCommand.Send.rawValue)
|
||||
add(MoltbotSmsCommand.Send.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildCapabilities(): List<String> =
|
||||
buildList {
|
||||
add(ClawdbotCapability.Canvas.rawValue)
|
||||
add(ClawdbotCapability.Screen.rawValue)
|
||||
if (cameraEnabled.value) add(ClawdbotCapability.Camera.rawValue)
|
||||
if (sms.canSendSms()) add(ClawdbotCapability.Sms.rawValue)
|
||||
add(MoltbotCapability.Canvas.rawValue)
|
||||
add(MoltbotCapability.Screen.rawValue)
|
||||
if (cameraEnabled.value) add(MoltbotCapability.Camera.rawValue)
|
||||
if (sms.canSendSms()) add(MoltbotCapability.Sms.rawValue)
|
||||
if (voiceWakeMode.value != VoiceWakeMode.Off && hasRecordAudioPermission()) {
|
||||
add(ClawdbotCapability.VoiceWake.rawValue)
|
||||
add(MoltbotCapability.VoiceWake.rawValue)
|
||||
}
|
||||
if (locationMode.value != LocationMode.Off) {
|
||||
add(ClawdbotCapability.Location.rawValue)
|
||||
add(MoltbotCapability.Location.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ class NodeRuntime(context: Context) {
|
||||
val version = resolvedVersionName()
|
||||
val release = Build.VERSION.RELEASE?.trim().orEmpty()
|
||||
val releaseLabel = if (release.isEmpty()) "unknown" else release
|
||||
return "ClawdbotAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})"
|
||||
return "MoltbotAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})"
|
||||
}
|
||||
|
||||
private fun buildClientInfo(clientId: String, clientMode: String): GatewayClientInfo {
|
||||
@@ -529,7 +529,7 @@ class NodeRuntime(context: Context) {
|
||||
caps = buildCapabilities(),
|
||||
commands = buildInvokeCommands(),
|
||||
permissions = emptyMap(),
|
||||
client = buildClientInfo(clientId = "clawdbot-android", clientMode = "node"),
|
||||
client = buildClientInfo(clientId = "moltbot-android", clientMode = "node"),
|
||||
userAgent = buildUserAgent(),
|
||||
)
|
||||
}
|
||||
@@ -541,7 +541,7 @@ class NodeRuntime(context: Context) {
|
||||
caps = emptyList(),
|
||||
commands = emptyList(),
|
||||
permissions = emptyMap(),
|
||||
client = buildClientInfo(clientId = "clawdbot-control-ui", clientMode = "ui"),
|
||||
client = buildClientInfo(clientId = "moltbot-control-ui", clientMode = "ui"),
|
||||
userAgent = buildUserAgent(),
|
||||
)
|
||||
}
|
||||
@@ -665,7 +665,7 @@ class NodeRuntime(context: Context) {
|
||||
val actionId = (userActionObj["id"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty {
|
||||
java.util.UUID.randomUUID().toString()
|
||||
}
|
||||
val name = ClawdbotCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch
|
||||
val name = MoltbotCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch
|
||||
|
||||
val surfaceId =
|
||||
(userActionObj["surfaceId"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty { "main" }
|
||||
@@ -675,7 +675,7 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
val sessionKey = resolveMainSessionKey()
|
||||
val message =
|
||||
ClawdbotCanvasA2UIAction.formatAgentMessage(
|
||||
MoltbotCanvasA2UIAction.formatAgentMessage(
|
||||
actionName = name,
|
||||
sessionKey = sessionKey,
|
||||
surfaceId = surfaceId,
|
||||
@@ -709,7 +709,7 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
try {
|
||||
canvas.eval(
|
||||
ClawdbotCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
actionId = actionId,
|
||||
ok = connected && error == null,
|
||||
error = error,
|
||||
@@ -827,10 +827,10 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
private suspend fun handleInvoke(command: String, paramsJson: String?): GatewaySession.InvokeResult {
|
||||
if (
|
||||
command.startsWith(ClawdbotCanvasCommand.NamespacePrefix) ||
|
||||
command.startsWith(ClawdbotCanvasA2UICommand.NamespacePrefix) ||
|
||||
command.startsWith(ClawdbotCameraCommand.NamespacePrefix) ||
|
||||
command.startsWith(ClawdbotScreenCommand.NamespacePrefix)
|
||||
command.startsWith(MoltbotCanvasCommand.NamespacePrefix) ||
|
||||
command.startsWith(MoltbotCanvasA2UICommand.NamespacePrefix) ||
|
||||
command.startsWith(MoltbotCameraCommand.NamespacePrefix) ||
|
||||
command.startsWith(MoltbotScreenCommand.NamespacePrefix)
|
||||
) {
|
||||
if (!isForeground.value) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
@@ -839,13 +839,13 @@ class NodeRuntime(context: Context) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if (command.startsWith(ClawdbotCameraCommand.NamespacePrefix) && !cameraEnabled.value) {
|
||||
if (command.startsWith(MoltbotCameraCommand.NamespacePrefix) && !cameraEnabled.value) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
code = "CAMERA_DISABLED",
|
||||
message = "CAMERA_DISABLED: enable Camera in Settings",
|
||||
)
|
||||
}
|
||||
if (command.startsWith(ClawdbotLocationCommand.NamespacePrefix) &&
|
||||
if (command.startsWith(MoltbotLocationCommand.NamespacePrefix) &&
|
||||
locationMode.value == LocationMode.Off
|
||||
) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
@@ -855,18 +855,18 @@ class NodeRuntime(context: Context) {
|
||||
}
|
||||
|
||||
return when (command) {
|
||||
ClawdbotCanvasCommand.Present.rawValue -> {
|
||||
MoltbotCanvasCommand.Present.rawValue -> {
|
||||
val url = CanvasController.parseNavigateUrl(paramsJson)
|
||||
canvas.navigate(url)
|
||||
GatewaySession.InvokeResult.ok(null)
|
||||
}
|
||||
ClawdbotCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null)
|
||||
ClawdbotCanvasCommand.Navigate.rawValue -> {
|
||||
MoltbotCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null)
|
||||
MoltbotCanvasCommand.Navigate.rawValue -> {
|
||||
val url = CanvasController.parseNavigateUrl(paramsJson)
|
||||
canvas.navigate(url)
|
||||
GatewaySession.InvokeResult.ok(null)
|
||||
}
|
||||
ClawdbotCanvasCommand.Eval.rawValue -> {
|
||||
MoltbotCanvasCommand.Eval.rawValue -> {
|
||||
val js =
|
||||
CanvasController.parseEvalJs(paramsJson)
|
||||
?: return GatewaySession.InvokeResult.error(
|
||||
@@ -884,7 +884,7 @@ class NodeRuntime(context: Context) {
|
||||
}
|
||||
GatewaySession.InvokeResult.ok("""{"result":${result.toJsonString()}}""")
|
||||
}
|
||||
ClawdbotCanvasCommand.Snapshot.rawValue -> {
|
||||
MoltbotCanvasCommand.Snapshot.rawValue -> {
|
||||
val snapshotParams = CanvasController.parseSnapshotParams(paramsJson)
|
||||
val base64 =
|
||||
try {
|
||||
@@ -901,7 +901,7 @@ class NodeRuntime(context: Context) {
|
||||
}
|
||||
GatewaySession.InvokeResult.ok("""{"format":"${snapshotParams.format.rawValue}","base64":"$base64"}""")
|
||||
}
|
||||
ClawdbotCanvasA2UICommand.Reset.rawValue -> {
|
||||
MoltbotCanvasA2UICommand.Reset.rawValue -> {
|
||||
val a2uiUrl = resolveA2uiHostUrl()
|
||||
?: return GatewaySession.InvokeResult.error(
|
||||
code = "A2UI_HOST_NOT_CONFIGURED",
|
||||
@@ -917,7 +917,7 @@ class NodeRuntime(context: Context) {
|
||||
val res = canvas.eval(a2uiResetJS)
|
||||
GatewaySession.InvokeResult.ok(res)
|
||||
}
|
||||
ClawdbotCanvasA2UICommand.Push.rawValue, ClawdbotCanvasA2UICommand.PushJSONL.rawValue -> {
|
||||
MoltbotCanvasA2UICommand.Push.rawValue, MoltbotCanvasA2UICommand.PushJSONL.rawValue -> {
|
||||
val messages =
|
||||
try {
|
||||
decodeA2uiMessages(command, paramsJson)
|
||||
@@ -940,7 +940,7 @@ class NodeRuntime(context: Context) {
|
||||
val res = canvas.eval(js)
|
||||
GatewaySession.InvokeResult.ok(res)
|
||||
}
|
||||
ClawdbotCameraCommand.Snap.rawValue -> {
|
||||
MoltbotCameraCommand.Snap.rawValue -> {
|
||||
showCameraHud(message = "Taking photo…", kind = CameraHudKind.Photo)
|
||||
triggerCameraFlash()
|
||||
val res =
|
||||
@@ -954,7 +954,7 @@ class NodeRuntime(context: Context) {
|
||||
showCameraHud(message = "Photo captured", kind = CameraHudKind.Success, autoHideMs = 1600)
|
||||
GatewaySession.InvokeResult.ok(res.payloadJson)
|
||||
}
|
||||
ClawdbotCameraCommand.Clip.rawValue -> {
|
||||
MoltbotCameraCommand.Clip.rawValue -> {
|
||||
val includeAudio = paramsJson?.contains("\"includeAudio\":true") != false
|
||||
if (includeAudio) externalAudioCaptureActive.value = true
|
||||
try {
|
||||
@@ -973,7 +973,7 @@ class NodeRuntime(context: Context) {
|
||||
if (includeAudio) externalAudioCaptureActive.value = false
|
||||
}
|
||||
}
|
||||
ClawdbotLocationCommand.Get.rawValue -> {
|
||||
MoltbotLocationCommand.Get.rawValue -> {
|
||||
val mode = locationMode.value
|
||||
if (!isForeground.value && mode != LocationMode.Always) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
@@ -1026,7 +1026,7 @@ class NodeRuntime(context: Context) {
|
||||
GatewaySession.InvokeResult.error(code = "LOCATION_UNAVAILABLE", message = message)
|
||||
}
|
||||
}
|
||||
ClawdbotScreenCommand.Record.rawValue -> {
|
||||
MoltbotScreenCommand.Record.rawValue -> {
|
||||
// Status pill mirrors screen recording state so it stays visible without overlay stacking.
|
||||
_screenRecordActive.value = true
|
||||
try {
|
||||
@@ -1042,7 +1042,7 @@ class NodeRuntime(context: Context) {
|
||||
_screenRecordActive.value = false
|
||||
}
|
||||
}
|
||||
ClawdbotSmsCommand.Send.rawValue -> {
|
||||
MoltbotSmsCommand.Send.rawValue -> {
|
||||
val res = sms.send(paramsJson)
|
||||
if (res.ok) {
|
||||
GatewaySession.InvokeResult.ok(res.payloadJson)
|
||||
@@ -1115,7 +1115,7 @@ class NodeRuntime(context: Context) {
|
||||
val raw = if (nodeRaw.isNotBlank()) nodeRaw else operatorRaw
|
||||
if (raw.isBlank()) return null
|
||||
val base = raw.trimEnd('/')
|
||||
return "${base}/__clawdbot__/a2ui/?platform=android"
|
||||
return "${base}/__moltbot__/a2ui/?platform=android"
|
||||
}
|
||||
|
||||
private suspend fun ensureA2uiReady(a2uiUrl: String): Boolean {
|
||||
@@ -1150,7 +1150,7 @@ class NodeRuntime(context: Context) {
|
||||
val jsonlField = (obj["jsonl"] as? JsonPrimitive)?.content?.trim().orEmpty()
|
||||
val hasMessagesArray = obj["messages"] is JsonArray
|
||||
|
||||
if (command == ClawdbotCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) {
|
||||
if (command == MoltbotCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) {
|
||||
val jsonl = jsonlField
|
||||
if (jsonl.isBlank()) throw IllegalArgumentException("INVALID_REQUEST: jsonl required")
|
||||
val messages =
|
||||
@@ -1218,7 +1218,7 @@ private const val a2uiResetJS: String =
|
||||
"""
|
||||
(() => {
|
||||
try {
|
||||
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing clawdbotA2UI" };
|
||||
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" };
|
||||
return globalThis.clawdbotA2UI.reset();
|
||||
} catch (e) {
|
||||
return { ok: false, error: String(e?.message ?? e) };
|
||||
@@ -1230,7 +1230,7 @@ private fun a2uiApplyMessagesJS(messagesJson: String): String {
|
||||
return """
|
||||
(() => {
|
||||
try {
|
||||
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing clawdbotA2UI" };
|
||||
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" };
|
||||
const messages = $messagesJson;
|
||||
return globalThis.clawdbotA2UI.applyMessages(messages);
|
||||
} catch (e) {
|
||||
|
||||
@@ -115,7 +115,7 @@ class PermissionRequester(private val activity: ComponentActivity) {
|
||||
|
||||
private fun buildRationaleMessage(permissions: List<String>): String {
|
||||
val labels = permissions.map { permissionLabel(it) }
|
||||
return "Clawdbot needs ${labels.joinToString(", ")} permissions to continue."
|
||||
return "Moltbot needs ${labels.joinToString(", ")} permissions to continue."
|
||||
}
|
||||
|
||||
private fun buildSettingsMessage(permissions: List<String>): String {
|
||||
|
||||
@@ -55,7 +55,7 @@ class ScreenCaptureRequester(private val activity: ComponentActivity) {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle("Screen recording required")
|
||||
.setMessage("Clawdbot needs to record the screen for this command.")
|
||||
.setMessage("Moltbot needs to record the screen for this command.")
|
||||
.setPositiveButton("Continue") { _, _ -> cont.resume(true) }
|
||||
.setNegativeButton("Not now") { _, _ -> cont.resume(false) }
|
||||
.setOnCancelListener { cont.resume(false) }
|
||||
|
||||
@@ -31,7 +31,7 @@ class SecurePrefs(context: Context) {
|
||||
private val prefs =
|
||||
EncryptedSharedPreferences.create(
|
||||
context,
|
||||
"clawdbot.node.secure",
|
||||
"moltbot.node.secure",
|
||||
masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
||||
|
||||
@@ -21,7 +21,7 @@ data class DeviceIdentity(
|
||||
|
||||
class DeviceIdentityStore(context: Context) {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
private val identityFile = File(context.filesDir, "clawdbot/identity/device.json")
|
||||
private val identityFile = File(context.filesDir, "moltbot/identity/device.json")
|
||||
|
||||
@Synchronized
|
||||
fun loadOrCreate(): DeviceIdentity {
|
||||
|
||||
@@ -51,9 +51,9 @@ class GatewayDiscovery(
|
||||
private val nsd = context.getSystemService(NsdManager::class.java)
|
||||
private val connectivity = context.getSystemService(ConnectivityManager::class.java)
|
||||
private val dns = DnsResolver.getInstance()
|
||||
private val serviceType = "_clawdbot-gw._tcp."
|
||||
private val wideAreaDomain = "clawdbot.internal."
|
||||
private val logTag = "Clawdbot/GatewayDiscovery"
|
||||
private val serviceType = "_moltbot-gw._tcp."
|
||||
private val wideAreaDomain = "moltbot.internal."
|
||||
private val logTag = "Moltbot/GatewayDiscovery"
|
||||
|
||||
private val localById = ConcurrentHashMap<String, GatewayEndpoint>()
|
||||
private val unicastById = ConcurrentHashMap<String, GatewayEndpoint>()
|
||||
|
||||
@@ -148,7 +148,7 @@ class GatewaySession(
|
||||
try {
|
||||
conn.request("node.event", params, timeoutMs = 8_000)
|
||||
} catch (err: Throwable) {
|
||||
Log.w("ClawdbotGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}")
|
||||
Log.w("MoltbotGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ class GatewaySession(
|
||||
private val connectNonceDeferred = CompletableDeferred<String?>()
|
||||
private val client: OkHttpClient = buildClient()
|
||||
private var socket: WebSocket? = null
|
||||
private val loggerTag = "ClawdbotGateway"
|
||||
private val loggerTag = "MoltbotGateway"
|
||||
|
||||
val remoteAddress: String =
|
||||
if (endpoint.host.contains(":")) {
|
||||
|
||||
@@ -155,7 +155,7 @@ class CameraCaptureManager(private val context: Context) {
|
||||
provider.unbindAll()
|
||||
provider.bindToLifecycle(owner, selector, videoCapture)
|
||||
|
||||
val file = File.createTempFile("clawdbot-clip-", ".mp4")
|
||||
val file = File.createTempFile("moltbot-clip-", ".mp4")
|
||||
val outputOptions = FileOutputOptions.Builder(file).build()
|
||||
|
||||
val finalized = kotlinx.coroutines.CompletableDeferred<VideoRecordEvent.Finalize>()
|
||||
@@ -285,7 +285,7 @@ private suspend fun Context.cameraProvider(): ProcessCameraProvider =
|
||||
/** Returns (jpegBytes, exifOrientation) so caller can rotate the decoded bitmap. */
|
||||
private suspend fun ImageCapture.takeJpegWithExif(executor: Executor): Pair<ByteArray, Int> =
|
||||
suspendCancellableCoroutine { cont ->
|
||||
val file = File.createTempFile("clawdbot-snap-", ".jpg")
|
||||
val file = File.createTempFile("moltbot-snap-", ".jpg")
|
||||
val options = ImageCapture.OutputFileOptions.Builder(file).build()
|
||||
takePicture(
|
||||
options,
|
||||
|
||||
@@ -84,12 +84,12 @@ class CanvasController {
|
||||
withWebViewOnMain { wv ->
|
||||
if (currentUrl == null) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("ClawdbotCanvas", "load scaffold: $scaffoldAssetUrl")
|
||||
Log.d("MoltbotCanvas", "load scaffold: $scaffoldAssetUrl")
|
||||
}
|
||||
wv.loadUrl(scaffoldAssetUrl)
|
||||
} else {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("ClawdbotCanvas", "load url: $currentUrl")
|
||||
Log.d("MoltbotCanvas", "load url: $currentUrl")
|
||||
}
|
||||
wv.loadUrl(currentUrl)
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class CanvasController {
|
||||
val js = """
|
||||
(() => {
|
||||
try {
|
||||
const api = globalThis.__clawdbot;
|
||||
const api = globalThis.__moltbot;
|
||||
if (!api) return;
|
||||
if (typeof api.setDebugStatusEnabled === 'function') {
|
||||
api.setDebugStatusEnabled(${if (enabled) "true" else "false"});
|
||||
|
||||
@@ -63,7 +63,7 @@ class ScreenRecordManager(private val context: Context) {
|
||||
val height = metrics.heightPixels
|
||||
val densityDpi = metrics.densityDpi
|
||||
|
||||
val file = File.createTempFile("clawdbot-screen-", ".mp4")
|
||||
val file = File.createTempFile("moltbot-screen-", ".mp4")
|
||||
if (includeAudio) ensureMicPermission()
|
||||
|
||||
val recorder = createMediaRecorder()
|
||||
@@ -90,7 +90,7 @@ class ScreenRecordManager(private val context: Context) {
|
||||
val surface = recorder.surface
|
||||
virtualDisplay =
|
||||
projection.createVirtualDisplay(
|
||||
"clawdbot-screen",
|
||||
"moltbot-screen",
|
||||
width,
|
||||
height,
|
||||
densityDpi,
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.clawdbot.android.protocol
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
object ClawdbotCanvasA2UIAction {
|
||||
object MoltbotCanvasA2UIAction {
|
||||
fun extractActionName(userAction: JsonObject): String? {
|
||||
val name =
|
||||
(userAction["name"] as? JsonPrimitive)
|
||||
@@ -61,6 +61,6 @@ object ClawdbotCanvasA2UIAction {
|
||||
val err = (error ?: "").replace("\\", "\\\\").replace("\"", "\\\"")
|
||||
val okLiteral = if (ok) "true" else "false"
|
||||
val idEscaped = actionId.replace("\\", "\\\\").replace("\"", "\\\"")
|
||||
return "window.dispatchEvent(new CustomEvent('clawdbot:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));"
|
||||
return "window.dispatchEvent(new CustomEvent('moltbot:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.clawdbot.android.protocol
|
||||
|
||||
enum class ClawdbotCapability(val rawValue: String) {
|
||||
enum class MoltbotCapability(val rawValue: String) {
|
||||
Canvas("canvas"),
|
||||
Camera("camera"),
|
||||
Screen("screen"),
|
||||
@@ -9,7 +9,7 @@ enum class ClawdbotCapability(val rawValue: String) {
|
||||
Location("location"),
|
||||
}
|
||||
|
||||
enum class ClawdbotCanvasCommand(val rawValue: String) {
|
||||
enum class MoltbotCanvasCommand(val rawValue: String) {
|
||||
Present("canvas.present"),
|
||||
Hide("canvas.hide"),
|
||||
Navigate("canvas.navigate"),
|
||||
@@ -22,7 +22,7 @@ enum class ClawdbotCanvasCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class ClawdbotCanvasA2UICommand(val rawValue: String) {
|
||||
enum class MoltbotCanvasA2UICommand(val rawValue: String) {
|
||||
Push("canvas.a2ui.push"),
|
||||
PushJSONL("canvas.a2ui.pushJSONL"),
|
||||
Reset("canvas.a2ui.reset"),
|
||||
@@ -33,7 +33,7 @@ enum class ClawdbotCanvasA2UICommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class ClawdbotCameraCommand(val rawValue: String) {
|
||||
enum class MoltbotCameraCommand(val rawValue: String) {
|
||||
Snap("camera.snap"),
|
||||
Clip("camera.clip"),
|
||||
;
|
||||
@@ -43,7 +43,7 @@ enum class ClawdbotCameraCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class ClawdbotScreenCommand(val rawValue: String) {
|
||||
enum class MoltbotScreenCommand(val rawValue: String) {
|
||||
Record("screen.record"),
|
||||
;
|
||||
|
||||
@@ -52,7 +52,7 @@ enum class ClawdbotScreenCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class ClawdbotSmsCommand(val rawValue: String) {
|
||||
enum class MoltbotSmsCommand(val rawValue: String) {
|
||||
Send("sms.send"),
|
||||
;
|
||||
|
||||
@@ -61,7 +61,7 @@ enum class ClawdbotSmsCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class ClawdbotLocationCommand(val rawValue: String) {
|
||||
enum class MoltbotLocationCommand(val rawValue: String) {
|
||||
Get("location.get"),
|
||||
;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@Composable
|
||||
fun ClawdbotTheme(content: @Composable () -> Unit) {
|
||||
fun MoltbotTheme(content: @Composable () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val isDark = isSystemInDarkTheme()
|
||||
val colorScheme = if (isDark) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
|
||||
@@ -333,7 +333,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
disableForceDarkIfSupported(settings)
|
||||
}
|
||||
if (isDebuggable) {
|
||||
Log.d("ClawdbotWebView", "userAgent: ${settings.userAgentString}")
|
||||
Log.d("MoltbotWebView", "userAgent: ${settings.userAgentString}")
|
||||
}
|
||||
isScrollContainer = true
|
||||
overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS
|
||||
@@ -348,7 +348,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
) {
|
||||
if (!isDebuggable) return
|
||||
if (!request.isForMainFrame) return
|
||||
Log.e("ClawdbotWebView", "onReceivedError: ${error.errorCode} ${error.description} ${request.url}")
|
||||
Log.e("MoltbotWebView", "onReceivedError: ${error.errorCode} ${error.description} ${request.url}")
|
||||
}
|
||||
|
||||
override fun onReceivedHttpError(
|
||||
@@ -359,14 +359,14 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
if (!isDebuggable) return
|
||||
if (!request.isForMainFrame) return
|
||||
Log.e(
|
||||
"ClawdbotWebView",
|
||||
"MoltbotWebView",
|
||||
"onReceivedHttpError: ${errorResponse.statusCode} ${errorResponse.reasonPhrase} ${request.url}",
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String?) {
|
||||
if (isDebuggable) {
|
||||
Log.d("ClawdbotWebView", "onPageFinished: $url")
|
||||
Log.d("MoltbotWebView", "onPageFinished: $url")
|
||||
}
|
||||
viewModel.canvas.onPageFinished()
|
||||
}
|
||||
@@ -377,7 +377,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
): Boolean {
|
||||
if (isDebuggable) {
|
||||
Log.e(
|
||||
"ClawdbotWebView",
|
||||
"MoltbotWebView",
|
||||
"onRenderProcessGone didCrash=${detail.didCrash()} priorityAtExit=${detail.rendererPriorityAtExit()}",
|
||||
)
|
||||
}
|
||||
@@ -390,7 +390,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
if (!isDebuggable) return false
|
||||
val msg = consoleMessage ?: return false
|
||||
Log.d(
|
||||
"ClawdbotWebView",
|
||||
"MoltbotWebView",
|
||||
"console ${msg.messageLevel()} @ ${msg.sourceId()}:${msg.lineNumber()} ${msg.message()}",
|
||||
)
|
||||
return false
|
||||
@@ -428,7 +428,7 @@ private class CanvasA2UIActionBridge(private val onMessage: (String) -> Unit) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val interfaceName: String = "clawdbotCanvasA2UIAction"
|
||||
const val interfaceName: String = "moltbotCanvasA2UIAction"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -457,7 +457,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth()) {
|
||||
ListItem(
|
||||
headlineContent = { Text("Foreground Only") },
|
||||
supportingContent = { Text("Listens only while Clawdbot is open.") },
|
||||
supportingContent = { Text("Listens only while Moltbot is open.") },
|
||||
trailingContent = {
|
||||
RadioButton(
|
||||
selected = voiceWakeMode == VoiceWakeMode.Foreground,
|
||||
@@ -603,7 +603,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text("While Using") },
|
||||
supportingContent = { Text("Only while Clawdbot is open.") },
|
||||
supportingContent = { Text("Only while Moltbot is open.") },
|
||||
trailingContent = {
|
||||
RadioButton(
|
||||
selected = locationMode == LocationMode.WhileUsing,
|
||||
@@ -650,7 +650,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = { Text("Prevent Sleep") },
|
||||
supportingContent = { Text("Keeps the screen awake while Clawdbot is open.") },
|
||||
supportingContent = { Text("Keeps the screen awake while Moltbot is open.") },
|
||||
trailingContent = { Switch(checked = preventSleep, onCheckedChange = viewModel::setPreventSleep) },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Clawdbot Node</string>
|
||||
<string name="app_name">Moltbot Node</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<resources>
|
||||
<style name="Theme.ClawdbotNode" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<style name="Theme.MoltbotNode" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightStatusBar">false</item>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<base-config cleartextTrafficPermitted="true" tools:ignore="InsecureBaseConfiguration" />
|
||||
<!-- Allow HTTP for tailnet/local dev endpoints (e.g. canvas/background web). -->
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">clawdbot.internal</domain>
|
||||
<domain includeSubdomains="true">moltbot.internal</domain>
|
||||
</domain-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">ts.net</domain>
|
||||
|
||||
@@ -12,7 +12,7 @@ class BonjourEscapesTest {
|
||||
|
||||
@Test
|
||||
fun decodeDecodesDecimalEscapes() {
|
||||
assertEquals("Clawdbot Gateway", BonjourEscapes.decode("Clawdbot\\032Gateway"))
|
||||
assertEquals("Moltbot Gateway", BonjourEscapes.decode("Moltbot\\032Gateway"))
|
||||
assertEquals("A B", BonjourEscapes.decode("A\\032B"))
|
||||
assertEquals("Peter\u2019s Mac", BonjourEscapes.decode("Peter\\226\\128\\153s Mac"))
|
||||
}
|
||||
|
||||
@@ -5,24 +5,24 @@ import kotlinx.serialization.json.jsonObject
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ClawdbotCanvasA2UIActionTest {
|
||||
class MoltbotCanvasA2UIActionTest {
|
||||
@Test
|
||||
fun extractActionNameAcceptsNameOrAction() {
|
||||
val nameObj = Json.parseToJsonElement("{\"name\":\"Hello\"}").jsonObject
|
||||
assertEquals("Hello", ClawdbotCanvasA2UIAction.extractActionName(nameObj))
|
||||
assertEquals("Hello", MoltbotCanvasA2UIAction.extractActionName(nameObj))
|
||||
|
||||
val actionObj = Json.parseToJsonElement("{\"action\":\"Wave\"}").jsonObject
|
||||
assertEquals("Wave", ClawdbotCanvasA2UIAction.extractActionName(actionObj))
|
||||
assertEquals("Wave", MoltbotCanvasA2UIAction.extractActionName(actionObj))
|
||||
|
||||
val fallbackObj =
|
||||
Json.parseToJsonElement("{\"name\":\" \",\"action\":\"Fallback\"}").jsonObject
|
||||
assertEquals("Fallback", ClawdbotCanvasA2UIAction.extractActionName(fallbackObj))
|
||||
assertEquals("Fallback", MoltbotCanvasA2UIAction.extractActionName(fallbackObj))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun formatAgentMessageMatchesSharedSpec() {
|
||||
val msg =
|
||||
ClawdbotCanvasA2UIAction.formatAgentMessage(
|
||||
MoltbotCanvasA2UIAction.formatAgentMessage(
|
||||
actionName = "Get Weather",
|
||||
sessionKey = "main",
|
||||
surfaceId = "main",
|
||||
@@ -40,9 +40,9 @@ class ClawdbotCanvasA2UIActionTest {
|
||||
|
||||
@Test
|
||||
fun jsDispatchA2uiStatusIsStable() {
|
||||
val js = ClawdbotCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId = "a1", ok = true, error = null)
|
||||
val js = MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId = "a1", ok = true, error = null)
|
||||
assertEquals(
|
||||
"window.dispatchEvent(new CustomEvent('clawdbot:a2ui-action-status', { detail: { id: \"a1\", ok: true, error: \"\" } }));",
|
||||
"window.dispatchEvent(new CustomEvent('moltbot:a2ui-action-status', { detail: { id: \"a1\", ok: true, error: \"\" } }));",
|
||||
js,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,33 +3,33 @@ package com.clawdbot.android.protocol
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ClawdbotProtocolConstantsTest {
|
||||
class MoltbotProtocolConstantsTest {
|
||||
@Test
|
||||
fun canvasCommandsUseStableStrings() {
|
||||
assertEquals("canvas.present", ClawdbotCanvasCommand.Present.rawValue)
|
||||
assertEquals("canvas.hide", ClawdbotCanvasCommand.Hide.rawValue)
|
||||
assertEquals("canvas.navigate", ClawdbotCanvasCommand.Navigate.rawValue)
|
||||
assertEquals("canvas.eval", ClawdbotCanvasCommand.Eval.rawValue)
|
||||
assertEquals("canvas.snapshot", ClawdbotCanvasCommand.Snapshot.rawValue)
|
||||
assertEquals("canvas.present", MoltbotCanvasCommand.Present.rawValue)
|
||||
assertEquals("canvas.hide", MoltbotCanvasCommand.Hide.rawValue)
|
||||
assertEquals("canvas.navigate", MoltbotCanvasCommand.Navigate.rawValue)
|
||||
assertEquals("canvas.eval", MoltbotCanvasCommand.Eval.rawValue)
|
||||
assertEquals("canvas.snapshot", MoltbotCanvasCommand.Snapshot.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun a2uiCommandsUseStableStrings() {
|
||||
assertEquals("canvas.a2ui.push", ClawdbotCanvasA2UICommand.Push.rawValue)
|
||||
assertEquals("canvas.a2ui.pushJSONL", ClawdbotCanvasA2UICommand.PushJSONL.rawValue)
|
||||
assertEquals("canvas.a2ui.reset", ClawdbotCanvasA2UICommand.Reset.rawValue)
|
||||
assertEquals("canvas.a2ui.push", MoltbotCanvasA2UICommand.Push.rawValue)
|
||||
assertEquals("canvas.a2ui.pushJSONL", MoltbotCanvasA2UICommand.PushJSONL.rawValue)
|
||||
assertEquals("canvas.a2ui.reset", MoltbotCanvasA2UICommand.Reset.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun capabilitiesUseStableStrings() {
|
||||
assertEquals("canvas", ClawdbotCapability.Canvas.rawValue)
|
||||
assertEquals("camera", ClawdbotCapability.Camera.rawValue)
|
||||
assertEquals("screen", ClawdbotCapability.Screen.rawValue)
|
||||
assertEquals("voiceWake", ClawdbotCapability.VoiceWake.rawValue)
|
||||
assertEquals("canvas", MoltbotCapability.Canvas.rawValue)
|
||||
assertEquals("camera", MoltbotCapability.Camera.rawValue)
|
||||
assertEquals("screen", MoltbotCapability.Screen.rawValue)
|
||||
assertEquals("voiceWake", MoltbotCapability.VoiceWake.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun screenCommandsUseStableStrings() {
|
||||
assertEquals("screen.record", ClawdbotScreenCommand.Record.rawValue)
|
||||
assertEquals("screen.record", MoltbotScreenCommand.Record.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "ClawdbotNodeAndroid"
|
||||
rootProject.name = "MoltbotNodeAndroid"
|
||||
include(":app")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AVFoundation
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
|
||||
actor CameraController {
|
||||
@@ -36,7 +36,7 @@ actor CameraController {
|
||||
}
|
||||
}
|
||||
|
||||
func snap(params: ClawdbotCameraSnapParams) async throws -> (
|
||||
func snap(params: MoltbotCameraSnapParams) async throws -> (
|
||||
format: String,
|
||||
base64: String,
|
||||
width: Int,
|
||||
@@ -109,7 +109,7 @@ actor CameraController {
|
||||
height: res.heightPx)
|
||||
}
|
||||
|
||||
func clip(params: ClawdbotCameraClipParams) async throws -> (
|
||||
func clip(params: MoltbotCameraClipParams) async throws -> (
|
||||
format: String,
|
||||
base64: String,
|
||||
durationMs: Int,
|
||||
@@ -161,9 +161,9 @@ actor CameraController {
|
||||
await Self.warmUpCaptureSession()
|
||||
|
||||
let movURL = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mov")
|
||||
.appendingPathComponent("moltbot-camera-\(UUID().uuidString).mov")
|
||||
let mp4URL = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mp4")
|
||||
.appendingPathComponent("moltbot-camera-\(UUID().uuidString).mp4")
|
||||
|
||||
defer {
|
||||
try? FileManager().removeItem(at: movURL)
|
||||
@@ -221,7 +221,7 @@ actor CameraController {
|
||||
}
|
||||
|
||||
private nonisolated static func pickCamera(
|
||||
facing: ClawdbotCameraFacing,
|
||||
facing: MoltbotCameraFacing,
|
||||
deviceId: String?) -> AVCaptureDevice?
|
||||
{
|
||||
if let deviceId, !deviceId.isEmpty {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import ClawdbotChatUI
|
||||
import ClawdbotKit
|
||||
import MoltbotChatUI
|
||||
import MoltbotKit
|
||||
import SwiftUI
|
||||
|
||||
struct ChatSheet: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var viewModel: ClawdbotChatViewModel
|
||||
@State private var viewModel: MoltbotChatViewModel
|
||||
private let userAccent: Color?
|
||||
|
||||
init(gateway: GatewayNodeSession, sessionKey: String, userAccent: Color? = nil) {
|
||||
let transport = IOSGatewayChatTransport(gateway: gateway)
|
||||
self._viewModel = State(
|
||||
initialValue: ClawdbotChatViewModel(
|
||||
initialValue: MoltbotChatViewModel(
|
||||
sessionKey: sessionKey,
|
||||
transport: transport))
|
||||
self.userAccent = userAccent
|
||||
@@ -18,7 +18,7 @@ struct ChatSheet: View {
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ClawdbotChatView(
|
||||
MoltbotChatView(
|
||||
viewModel: self.viewModel,
|
||||
showsSessionSwitcher: true,
|
||||
userAccent: self.userAccent)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import ClawdbotChatUI
|
||||
import ClawdbotKit
|
||||
import ClawdbotProtocol
|
||||
import MoltbotChatUI
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
|
||||
struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
struct IOSGatewayChatTransport: MoltbotChatTransport, Sendable {
|
||||
private let gateway: GatewayNodeSession
|
||||
|
||||
init(gateway: GatewayNodeSession) {
|
||||
@@ -20,7 +20,7 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
_ = try await self.gateway.request(method: "chat.abort", paramsJSON: json, timeoutSeconds: 10)
|
||||
}
|
||||
|
||||
func listSessions(limit: Int?) async throws -> ClawdbotChatSessionsListResponse {
|
||||
func listSessions(limit: Int?) async throws -> MoltbotChatSessionsListResponse {
|
||||
struct Params: Codable {
|
||||
var includeGlobal: Bool
|
||||
var includeUnknown: Bool
|
||||
@@ -29,7 +29,7 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
let data = try JSONEncoder().encode(Params(includeGlobal: true, includeUnknown: false, limit: limit))
|
||||
let json = String(data: data, encoding: .utf8)
|
||||
let res = try await self.gateway.request(method: "sessions.list", paramsJSON: json, timeoutSeconds: 15)
|
||||
return try JSONDecoder().decode(ClawdbotChatSessionsListResponse.self, from: res)
|
||||
return try JSONDecoder().decode(MoltbotChatSessionsListResponse.self, from: res)
|
||||
}
|
||||
|
||||
func setActiveSessionKey(_ sessionKey: String) async throws {
|
||||
@@ -39,12 +39,12 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
await self.gateway.sendEvent(event: "chat.subscribe", payloadJSON: json)
|
||||
}
|
||||
|
||||
func requestHistory(sessionKey: String) async throws -> ClawdbotChatHistoryPayload {
|
||||
func requestHistory(sessionKey: String) async throws -> MoltbotChatHistoryPayload {
|
||||
struct Params: Codable { var sessionKey: String }
|
||||
let data = try JSONEncoder().encode(Params(sessionKey: sessionKey))
|
||||
let json = String(data: data, encoding: .utf8)
|
||||
let res = try await self.gateway.request(method: "chat.history", paramsJSON: json, timeoutSeconds: 15)
|
||||
return try JSONDecoder().decode(ClawdbotChatHistoryPayload.self, from: res)
|
||||
return try JSONDecoder().decode(MoltbotChatHistoryPayload.self, from: res)
|
||||
}
|
||||
|
||||
func sendMessage(
|
||||
@@ -52,13 +52,13 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
message: String,
|
||||
thinking: String,
|
||||
idempotencyKey: String,
|
||||
attachments: [ClawdbotChatAttachmentPayload]) async throws -> ClawdbotChatSendResponse
|
||||
attachments: [MoltbotChatAttachmentPayload]) async throws -> MoltbotChatSendResponse
|
||||
{
|
||||
struct Params: Codable {
|
||||
var sessionKey: String
|
||||
var message: String
|
||||
var thinking: String
|
||||
var attachments: [ClawdbotChatAttachmentPayload]?
|
||||
var attachments: [MoltbotChatAttachmentPayload]?
|
||||
var timeoutMs: Int
|
||||
var idempotencyKey: String
|
||||
}
|
||||
@@ -73,16 +73,16 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
let data = try JSONEncoder().encode(params)
|
||||
let json = String(data: data, encoding: .utf8)
|
||||
let res = try await self.gateway.request(method: "chat.send", paramsJSON: json, timeoutSeconds: 35)
|
||||
return try JSONDecoder().decode(ClawdbotChatSendResponse.self, from: res)
|
||||
return try JSONDecoder().decode(MoltbotChatSendResponse.self, from: res)
|
||||
}
|
||||
|
||||
func requestHealth(timeoutMs: Int) async throws -> Bool {
|
||||
let seconds = max(1, Int(ceil(Double(timeoutMs) / 1000.0)))
|
||||
let res = try await self.gateway.request(method: "health", paramsJSON: nil, timeoutSeconds: seconds)
|
||||
return (try? JSONDecoder().decode(ClawdbotGatewayHealthOK.self, from: res))?.ok ?? true
|
||||
return (try? JSONDecoder().decode(MoltbotGatewayHealthOK.self, from: res))?.ok ?? true
|
||||
}
|
||||
|
||||
func events() -> AsyncStream<ClawdbotChatTransportEvent> {
|
||||
func events() -> AsyncStream<MoltbotChatTransportEvent> {
|
||||
AsyncStream { continuation in
|
||||
let task = Task {
|
||||
let stream = await self.gateway.subscribeServerEvents()
|
||||
@@ -97,13 +97,13 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
guard let payload = evt.payload else { break }
|
||||
let ok = (try? GatewayPayloadDecoding.decode(
|
||||
payload,
|
||||
as: ClawdbotGatewayHealthOK.self))?.ok ?? true
|
||||
as: MoltbotGatewayHealthOK.self))?.ok ?? true
|
||||
continuation.yield(.health(ok: ok))
|
||||
case "chat":
|
||||
guard let payload = evt.payload else { break }
|
||||
if let chatPayload = try? GatewayPayloadDecoding.decode(
|
||||
payload,
|
||||
as: ClawdbotChatEventPayload.self)
|
||||
as: MoltbotChatEventPayload.self)
|
||||
{
|
||||
continuation.yield(.chat(chatPayload))
|
||||
}
|
||||
@@ -111,7 +111,7 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable {
|
||||
guard let payload = evt.payload else { break }
|
||||
if let agentPayload = try? GatewayPayloadDecoding.decode(
|
||||
payload,
|
||||
as: ClawdbotAgentEventPayload.self)
|
||||
as: MoltbotAgentEventPayload.self)
|
||||
{
|
||||
continuation.yield(.agent(agentPayload))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct ClawdbotApp: App {
|
||||
struct MoltbotApp: App {
|
||||
@State private var appModel: NodeAppModel
|
||||
@State private var gatewayController: GatewayConnectionController
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Darwin
|
||||
import Foundation
|
||||
import Network
|
||||
@@ -283,7 +283,7 @@ final class GatewayConnectionController {
|
||||
caps: self.currentCaps(),
|
||||
commands: self.currentCommands(),
|
||||
permissions: [:],
|
||||
clientId: "clawdbot-ios",
|
||||
clientId: "moltbot-ios",
|
||||
clientMode: "node",
|
||||
clientDisplayName: displayName)
|
||||
}
|
||||
@@ -304,51 +304,51 @@ final class GatewayConnectionController {
|
||||
}
|
||||
|
||||
private func currentCaps() -> [String] {
|
||||
var caps = [ClawdbotCapability.canvas.rawValue, ClawdbotCapability.screen.rawValue]
|
||||
var caps = [MoltbotCapability.canvas.rawValue, MoltbotCapability.screen.rawValue]
|
||||
|
||||
// Default-on: if the key doesn't exist yet, treat it as enabled.
|
||||
let cameraEnabled =
|
||||
UserDefaults.standard.object(forKey: "camera.enabled") == nil
|
||||
? true
|
||||
: UserDefaults.standard.bool(forKey: "camera.enabled")
|
||||
if cameraEnabled { caps.append(ClawdbotCapability.camera.rawValue) }
|
||||
if cameraEnabled { caps.append(MoltbotCapability.camera.rawValue) }
|
||||
|
||||
let voiceWakeEnabled = UserDefaults.standard.bool(forKey: VoiceWakePreferences.enabledKey)
|
||||
if voiceWakeEnabled { caps.append(ClawdbotCapability.voiceWake.rawValue) }
|
||||
if voiceWakeEnabled { caps.append(MoltbotCapability.voiceWake.rawValue) }
|
||||
|
||||
let locationModeRaw = UserDefaults.standard.string(forKey: "location.enabledMode") ?? "off"
|
||||
let locationMode = ClawdbotLocationMode(rawValue: locationModeRaw) ?? .off
|
||||
if locationMode != .off { caps.append(ClawdbotCapability.location.rawValue) }
|
||||
let locationMode = MoltbotLocationMode(rawValue: locationModeRaw) ?? .off
|
||||
if locationMode != .off { caps.append(MoltbotCapability.location.rawValue) }
|
||||
|
||||
return caps
|
||||
}
|
||||
|
||||
private func currentCommands() -> [String] {
|
||||
var commands: [String] = [
|
||||
ClawdbotCanvasCommand.present.rawValue,
|
||||
ClawdbotCanvasCommand.hide.rawValue,
|
||||
ClawdbotCanvasCommand.navigate.rawValue,
|
||||
ClawdbotCanvasCommand.evalJS.rawValue,
|
||||
ClawdbotCanvasCommand.snapshot.rawValue,
|
||||
ClawdbotCanvasA2UICommand.push.rawValue,
|
||||
ClawdbotCanvasA2UICommand.pushJSONL.rawValue,
|
||||
ClawdbotCanvasA2UICommand.reset.rawValue,
|
||||
ClawdbotScreenCommand.record.rawValue,
|
||||
ClawdbotSystemCommand.notify.rawValue,
|
||||
ClawdbotSystemCommand.which.rawValue,
|
||||
ClawdbotSystemCommand.run.rawValue,
|
||||
ClawdbotSystemCommand.execApprovalsGet.rawValue,
|
||||
ClawdbotSystemCommand.execApprovalsSet.rawValue,
|
||||
MoltbotCanvasCommand.present.rawValue,
|
||||
MoltbotCanvasCommand.hide.rawValue,
|
||||
MoltbotCanvasCommand.navigate.rawValue,
|
||||
MoltbotCanvasCommand.evalJS.rawValue,
|
||||
MoltbotCanvasCommand.snapshot.rawValue,
|
||||
MoltbotCanvasA2UICommand.push.rawValue,
|
||||
MoltbotCanvasA2UICommand.pushJSONL.rawValue,
|
||||
MoltbotCanvasA2UICommand.reset.rawValue,
|
||||
MoltbotScreenCommand.record.rawValue,
|
||||
MoltbotSystemCommand.notify.rawValue,
|
||||
MoltbotSystemCommand.which.rawValue,
|
||||
MoltbotSystemCommand.run.rawValue,
|
||||
MoltbotSystemCommand.execApprovalsGet.rawValue,
|
||||
MoltbotSystemCommand.execApprovalsSet.rawValue,
|
||||
]
|
||||
|
||||
let caps = Set(self.currentCaps())
|
||||
if caps.contains(ClawdbotCapability.camera.rawValue) {
|
||||
commands.append(ClawdbotCameraCommand.list.rawValue)
|
||||
commands.append(ClawdbotCameraCommand.snap.rawValue)
|
||||
commands.append(ClawdbotCameraCommand.clip.rawValue)
|
||||
if caps.contains(MoltbotCapability.camera.rawValue) {
|
||||
commands.append(MoltbotCameraCommand.list.rawValue)
|
||||
commands.append(MoltbotCameraCommand.snap.rawValue)
|
||||
commands.append(MoltbotCameraCommand.clip.rawValue)
|
||||
}
|
||||
if caps.contains(ClawdbotCapability.location.rawValue) {
|
||||
commands.append(ClawdbotLocationCommand.get.rawValue)
|
||||
if caps.contains(MoltbotCapability.location.rawValue) {
|
||||
commands.append(MoltbotLocationCommand.get.rawValue)
|
||||
}
|
||||
|
||||
return commands
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import Network
|
||||
import Observation
|
||||
@@ -52,11 +52,11 @@ final class GatewayDiscoveryModel {
|
||||
if !self.browsers.isEmpty { return }
|
||||
self.appendDebugLog("start()")
|
||||
|
||||
for domain in ClawdbotBonjour.gatewayServiceDomains {
|
||||
for domain in MoltbotBonjour.gatewayServiceDomains {
|
||||
let params = NWParameters.tcp
|
||||
params.includePeerToPeer = true
|
||||
let browser = NWBrowser(
|
||||
for: .bonjour(type: ClawdbotBonjour.gatewayServiceType, domain: domain),
|
||||
for: .bonjour(type: MoltbotBonjour.gatewayServiceType, domain: domain),
|
||||
using: params)
|
||||
|
||||
browser.stateUpdateHandler = { [weak self] state in
|
||||
@@ -202,7 +202,7 @@ final class GatewayDiscoveryModel {
|
||||
|
||||
private static func prettifyInstanceName(_ decodedName: String) -> String {
|
||||
let normalized = decodedName.split(whereSeparator: \.isWhitespace).joined(separator: " ")
|
||||
let stripped = normalized.replacingOccurrences(of: " (Clawdbot)", with: "")
|
||||
let stripped = normalized.replacingOccurrences(of: " (Moltbot)", with: "")
|
||||
.replacingOccurrences(of: #"\s+\(\d+\)$"#, with: "", options: .regularExpression)
|
||||
return stripped.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Clawdbot</string>
|
||||
<string>Moltbot</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconName</key>
|
||||
@@ -29,20 +29,20 @@
|
||||
</dict>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_clawdbot-gw._tcp</string>
|
||||
<string>_moltbot-gw._tcp</string>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Clawdbot can capture photos or short video clips when requested via the gateway.</string>
|
||||
<string>Moltbot can capture photos or short video clips when requested via the gateway.</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Clawdbot discovers and connects to your Clawdbot gateway on the local network.</string>
|
||||
<string>Moltbot discovers and connects to your Moltbot gateway on the local network.</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>Clawdbot can share your location in the background when you enable Always.</string>
|
||||
<string>Moltbot can share your location in the background when you enable Always.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Clawdbot uses your location when you allow location sharing.</string>
|
||||
<string>Moltbot uses your location when you allow location sharing.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Clawdbot needs microphone access for voice wake.</string>
|
||||
<string>Moltbot needs microphone access for voice wake.</string>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>Clawdbot uses on-device speech recognition for voice wake.</string>
|
||||
<string>Moltbot uses on-device speech recognition for voice wake.</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import CoreLocation
|
||||
import Foundation
|
||||
|
||||
@@ -30,7 +30,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
|
||||
return .fullAccuracy
|
||||
}
|
||||
|
||||
func ensureAuthorization(mode: ClawdbotLocationMode) async -> CLAuthorizationStatus {
|
||||
func ensureAuthorization(mode: MoltbotLocationMode) async -> CLAuthorizationStatus {
|
||||
guard CLLocationManager.locationServicesEnabled() else { return .denied }
|
||||
|
||||
let status = self.manager.authorizationStatus
|
||||
@@ -53,8 +53,8 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
|
||||
}
|
||||
|
||||
func currentLocation(
|
||||
params: ClawdbotLocationGetParams,
|
||||
desiredAccuracy: ClawdbotLocationAccuracy,
|
||||
params: MoltbotLocationGetParams,
|
||||
desiredAccuracy: MoltbotLocationAccuracy,
|
||||
maxAgeMs: Int?,
|
||||
timeoutMs: Int?) async throws -> CLLocation
|
||||
{
|
||||
@@ -93,7 +93,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
|
||||
try await AsyncTimeout.withTimeoutMs(timeoutMs: timeoutMs, onTimeout: { Error.timeout }, operation: operation)
|
||||
}
|
||||
|
||||
private static func accuracyValue(_ accuracy: ClawdbotLocationAccuracy) -> CLLocationAccuracy {
|
||||
private static func accuracyValue(_ accuracy: MoltbotLocationAccuracy) -> CLLocationAccuracy {
|
||||
switch accuracy {
|
||||
case .coarse:
|
||||
kCLLocationAccuracyKilometer
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Network
|
||||
import Observation
|
||||
import SwiftUI
|
||||
@@ -90,7 +90,7 @@ final class NodeAppModel {
|
||||
}()
|
||||
guard !userAction.isEmpty else { return }
|
||||
|
||||
guard let name = ClawdbotCanvasA2UIAction.extractActionName(userAction) else { return }
|
||||
guard let name = MoltbotCanvasA2UIAction.extractActionName(userAction) else { return }
|
||||
let actionId: String = {
|
||||
let id = (userAction["id"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return id.isEmpty ? UUID().uuidString : id
|
||||
@@ -109,15 +109,15 @@ final class NodeAppModel {
|
||||
|
||||
let host = UserDefaults.standard.string(forKey: "node.displayName") ?? UIDevice.current.name
|
||||
let instanceId = (UserDefaults.standard.string(forKey: "node.instanceId") ?? "ios-node").lowercased()
|
||||
let contextJSON = ClawdbotCanvasA2UIAction.compactJSON(userAction["context"])
|
||||
let contextJSON = MoltbotCanvasA2UIAction.compactJSON(userAction["context"])
|
||||
let sessionKey = self.mainSessionKey
|
||||
|
||||
let messageContext = ClawdbotCanvasA2UIAction.AgentMessageContext(
|
||||
let messageContext = MoltbotCanvasA2UIAction.AgentMessageContext(
|
||||
actionName: name,
|
||||
session: .init(key: sessionKey, surfaceId: surfaceId),
|
||||
component: .init(id: sourceComponentId, host: host, instanceId: instanceId),
|
||||
contextJSON: contextJSON)
|
||||
let message = ClawdbotCanvasA2UIAction.formatAgentMessage(messageContext)
|
||||
let message = MoltbotCanvasA2UIAction.formatAgentMessage(messageContext)
|
||||
|
||||
let ok: Bool
|
||||
var errorText: String?
|
||||
@@ -142,7 +142,7 @@ final class NodeAppModel {
|
||||
}
|
||||
}
|
||||
|
||||
let js = ClawdbotCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId: actionId, ok: ok, error: errorText)
|
||||
let js = MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId: actionId, ok: ok, error: errorText)
|
||||
do {
|
||||
_ = try await self.screen.eval(javaScript: js)
|
||||
} catch {
|
||||
@@ -154,7 +154,7 @@ final class NodeAppModel {
|
||||
guard let raw = await self.gateway.currentCanvasHostUrl() else { return nil }
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty, let base = URL(string: trimmed) else { return nil }
|
||||
return base.appendingPathComponent("__clawdbot__/a2ui/").absoluteString + "?platform=ios"
|
||||
return base.appendingPathComponent("__moltbot__/a2ui/").absoluteString + "?platform=ios"
|
||||
}
|
||||
|
||||
private func showA2UIOnConnectIfNeeded() async {
|
||||
@@ -190,7 +190,7 @@ final class NodeAppModel {
|
||||
self.talkMode.setEnabled(enabled)
|
||||
}
|
||||
|
||||
func requestLocationPermissions(mode: ClawdbotLocationMode) async -> Bool {
|
||||
func requestLocationPermissions(mode: MoltbotLocationMode) async -> Bool {
|
||||
guard mode != .off else { return true }
|
||||
let status = await self.locationService.ensureAuthorization(mode: mode)
|
||||
switch status {
|
||||
@@ -272,7 +272,7 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "UNAVAILABLE: node not ready"))
|
||||
}
|
||||
@@ -487,7 +487,7 @@ final class NodeAppModel {
|
||||
}
|
||||
|
||||
// iOS gateway forwards to the gateway; no local auth prompts here.
|
||||
// (Key-based unattended auth is handled on macOS for clawdbot:// links.)
|
||||
// (Key-based unattended auth is handled on macOS for moltbot:// links.)
|
||||
let data = try JSONEncoder().encode(link)
|
||||
guard let json = String(bytes: data, encoding: .utf8) else {
|
||||
throw NSError(domain: "NodeAppModel", code: 2, userInfo: [
|
||||
@@ -508,7 +508,7 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .backgroundUnavailable,
|
||||
message: "NODE_BACKGROUND_UNAVAILABLE: canvas/camera/screen commands require foreground"))
|
||||
}
|
||||
@@ -517,36 +517,36 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "CAMERA_DISABLED: enable Camera in iOS Settings → Camera → Allow Camera"))
|
||||
}
|
||||
|
||||
do {
|
||||
switch command {
|
||||
case ClawdbotLocationCommand.get.rawValue:
|
||||
case MoltbotLocationCommand.get.rawValue:
|
||||
return try await self.handleLocationInvoke(req)
|
||||
case ClawdbotCanvasCommand.present.rawValue,
|
||||
ClawdbotCanvasCommand.hide.rawValue,
|
||||
ClawdbotCanvasCommand.navigate.rawValue,
|
||||
ClawdbotCanvasCommand.evalJS.rawValue,
|
||||
ClawdbotCanvasCommand.snapshot.rawValue:
|
||||
case MoltbotCanvasCommand.present.rawValue,
|
||||
MoltbotCanvasCommand.hide.rawValue,
|
||||
MoltbotCanvasCommand.navigate.rawValue,
|
||||
MoltbotCanvasCommand.evalJS.rawValue,
|
||||
MoltbotCanvasCommand.snapshot.rawValue:
|
||||
return try await self.handleCanvasInvoke(req)
|
||||
case ClawdbotCanvasA2UICommand.reset.rawValue,
|
||||
ClawdbotCanvasA2UICommand.push.rawValue,
|
||||
ClawdbotCanvasA2UICommand.pushJSONL.rawValue:
|
||||
case MoltbotCanvasA2UICommand.reset.rawValue,
|
||||
MoltbotCanvasA2UICommand.push.rawValue,
|
||||
MoltbotCanvasA2UICommand.pushJSONL.rawValue:
|
||||
return try await self.handleCanvasA2UIInvoke(req)
|
||||
case ClawdbotCameraCommand.list.rawValue,
|
||||
ClawdbotCameraCommand.snap.rawValue,
|
||||
ClawdbotCameraCommand.clip.rawValue:
|
||||
case MoltbotCameraCommand.list.rawValue,
|
||||
MoltbotCameraCommand.snap.rawValue,
|
||||
MoltbotCameraCommand.clip.rawValue:
|
||||
return try await self.handleCameraInvoke(req)
|
||||
case ClawdbotScreenCommand.record.rawValue:
|
||||
case MoltbotScreenCommand.record.rawValue:
|
||||
return try await self.handleScreenRecordInvoke(req)
|
||||
default:
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
error: MoltbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
}
|
||||
} catch {
|
||||
if command.hasPrefix("camera.") {
|
||||
@@ -556,7 +556,7 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(code: .unavailable, message: error.localizedDescription))
|
||||
error: MoltbotNodeError(code: .unavailable, message: error.localizedDescription))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +570,7 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "LOCATION_DISABLED: enable Location in Settings"))
|
||||
}
|
||||
@@ -578,12 +578,12 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .backgroundUnavailable,
|
||||
message: "LOCATION_BACKGROUND_UNAVAILABLE: background location requires Always"))
|
||||
}
|
||||
let params = (try? Self.decodeParams(ClawdbotLocationGetParams.self, from: req.paramsJSON)) ??
|
||||
ClawdbotLocationGetParams()
|
||||
let params = (try? Self.decodeParams(MoltbotLocationGetParams.self, from: req.paramsJSON)) ??
|
||||
MoltbotLocationGetParams()
|
||||
let desired = params.desiredAccuracy ??
|
||||
(self.isLocationPreciseEnabled() ? .precise : .balanced)
|
||||
let status = self.locationService.authorizationStatus()
|
||||
@@ -591,7 +591,7 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "LOCATION_PERMISSION_REQUIRED: grant Location permission"))
|
||||
}
|
||||
@@ -599,7 +599,7 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "LOCATION_PERMISSION_REQUIRED: enable Always for background access"))
|
||||
}
|
||||
@@ -609,7 +609,7 @@ final class NodeAppModel {
|
||||
maxAgeMs: params.maxAgeMs,
|
||||
timeoutMs: params.timeoutMs)
|
||||
let isPrecise = self.locationService.accuracyAuthorization() == .fullAccuracy
|
||||
let payload = ClawdbotLocationPayload(
|
||||
let payload = MoltbotLocationPayload(
|
||||
lat: location.coordinate.latitude,
|
||||
lon: location.coordinate.longitude,
|
||||
accuracyMeters: location.horizontalAccuracy,
|
||||
@@ -625,9 +625,9 @@ final class NodeAppModel {
|
||||
|
||||
private func handleCanvasInvoke(_ req: BridgeInvokeRequest) async throws -> BridgeInvokeResponse {
|
||||
switch req.command {
|
||||
case ClawdbotCanvasCommand.present.rawValue:
|
||||
let params = (try? Self.decodeParams(ClawdbotCanvasPresentParams.self, from: req.paramsJSON)) ??
|
||||
ClawdbotCanvasPresentParams()
|
||||
case MoltbotCanvasCommand.present.rawValue:
|
||||
let params = (try? Self.decodeParams(MoltbotCanvasPresentParams.self, from: req.paramsJSON)) ??
|
||||
MoltbotCanvasPresentParams()
|
||||
let url = params.url?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if url.isEmpty {
|
||||
self.screen.showDefaultCanvas()
|
||||
@@ -635,19 +635,19 @@ final class NodeAppModel {
|
||||
self.screen.navigate(to: url)
|
||||
}
|
||||
return BridgeInvokeResponse(id: req.id, ok: true)
|
||||
case ClawdbotCanvasCommand.hide.rawValue:
|
||||
case MoltbotCanvasCommand.hide.rawValue:
|
||||
return BridgeInvokeResponse(id: req.id, ok: true)
|
||||
case ClawdbotCanvasCommand.navigate.rawValue:
|
||||
let params = try Self.decodeParams(ClawdbotCanvasNavigateParams.self, from: req.paramsJSON)
|
||||
case MoltbotCanvasCommand.navigate.rawValue:
|
||||
let params = try Self.decodeParams(MoltbotCanvasNavigateParams.self, from: req.paramsJSON)
|
||||
self.screen.navigate(to: params.url)
|
||||
return BridgeInvokeResponse(id: req.id, ok: true)
|
||||
case ClawdbotCanvasCommand.evalJS.rawValue:
|
||||
let params = try Self.decodeParams(ClawdbotCanvasEvalParams.self, from: req.paramsJSON)
|
||||
case MoltbotCanvasCommand.evalJS.rawValue:
|
||||
let params = try Self.decodeParams(MoltbotCanvasEvalParams.self, from: req.paramsJSON)
|
||||
let result = try await self.screen.eval(javaScript: params.javaScript)
|
||||
let payload = try Self.encodePayload(["result": result])
|
||||
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
|
||||
case ClawdbotCanvasCommand.snapshot.rawValue:
|
||||
let params = try? Self.decodeParams(ClawdbotCanvasSnapshotParams.self, from: req.paramsJSON)
|
||||
case MoltbotCanvasCommand.snapshot.rawValue:
|
||||
let params = try? Self.decodeParams(MoltbotCanvasSnapshotParams.self, from: req.paramsJSON)
|
||||
let format = params?.format ?? .jpeg
|
||||
let maxWidth: CGFloat? = {
|
||||
if let raw = params?.maxWidth, raw > 0 { return CGFloat(raw) }
|
||||
@@ -671,19 +671,19 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
error: MoltbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
}
|
||||
}
|
||||
|
||||
private func handleCanvasA2UIInvoke(_ req: BridgeInvokeRequest) async throws -> BridgeInvokeResponse {
|
||||
let command = req.command
|
||||
switch command {
|
||||
case ClawdbotCanvasA2UICommand.reset.rawValue:
|
||||
case MoltbotCanvasA2UICommand.reset.rawValue:
|
||||
guard let a2uiUrl = await self.resolveA2UIHostURL() else {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "A2UI_HOST_NOT_CONFIGURED: gateway did not advertise canvas host"))
|
||||
}
|
||||
@@ -692,31 +692,31 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "A2UI_HOST_UNAVAILABLE: A2UI host not reachable"))
|
||||
}
|
||||
|
||||
let json = try await self.screen.eval(javaScript: """
|
||||
(() => {
|
||||
if (!globalThis.clawdbotA2UI) return JSON.stringify({ ok: false, error: "missing clawdbotA2UI" });
|
||||
if (!globalThis.clawdbotA2UI) return JSON.stringify({ ok: false, error: "missing moltbotA2UI" });
|
||||
return JSON.stringify(globalThis.clawdbotA2UI.reset());
|
||||
})()
|
||||
""")
|
||||
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: json)
|
||||
case ClawdbotCanvasA2UICommand.push.rawValue, ClawdbotCanvasA2UICommand.pushJSONL.rawValue:
|
||||
case MoltbotCanvasA2UICommand.push.rawValue, MoltbotCanvasA2UICommand.pushJSONL.rawValue:
|
||||
let messages: [AnyCodable]
|
||||
if command == ClawdbotCanvasA2UICommand.pushJSONL.rawValue {
|
||||
let params = try Self.decodeParams(ClawdbotCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
|
||||
messages = try ClawdbotCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
|
||||
if command == MoltbotCanvasA2UICommand.pushJSONL.rawValue {
|
||||
let params = try Self.decodeParams(MoltbotCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
|
||||
messages = try MoltbotCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
|
||||
} else {
|
||||
do {
|
||||
let params = try Self.decodeParams(ClawdbotCanvasA2UIPushParams.self, from: req.paramsJSON)
|
||||
let params = try Self.decodeParams(MoltbotCanvasA2UIPushParams.self, from: req.paramsJSON)
|
||||
messages = params.messages
|
||||
} catch {
|
||||
// Be forgiving: some clients still send JSONL payloads to `canvas.a2ui.push`.
|
||||
let params = try Self.decodeParams(ClawdbotCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
|
||||
messages = try ClawdbotCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
|
||||
let params = try Self.decodeParams(MoltbotCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
|
||||
messages = try MoltbotCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +724,7 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "A2UI_HOST_NOT_CONFIGURED: gateway did not advertise canvas host"))
|
||||
}
|
||||
@@ -733,16 +733,16 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(
|
||||
error: MoltbotNodeError(
|
||||
code: .unavailable,
|
||||
message: "A2UI_HOST_UNAVAILABLE: A2UI host not reachable"))
|
||||
}
|
||||
|
||||
let messagesJSON = try ClawdbotCanvasA2UIJSONL.encodeMessagesJSONArray(messages)
|
||||
let messagesJSON = try MoltbotCanvasA2UIJSONL.encodeMessagesJSONArray(messages)
|
||||
let js = """
|
||||
(() => {
|
||||
try {
|
||||
if (!globalThis.clawdbotA2UI) return JSON.stringify({ ok: false, error: "missing clawdbotA2UI" });
|
||||
if (!globalThis.clawdbotA2UI) return JSON.stringify({ ok: false, error: "missing moltbotA2UI" });
|
||||
const messages = \(messagesJSON);
|
||||
return JSON.stringify(globalThis.clawdbotA2UI.applyMessages(messages));
|
||||
} catch (e) {
|
||||
@@ -756,24 +756,24 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
error: MoltbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
}
|
||||
}
|
||||
|
||||
private func handleCameraInvoke(_ req: BridgeInvokeRequest) async throws -> BridgeInvokeResponse {
|
||||
switch req.command {
|
||||
case ClawdbotCameraCommand.list.rawValue:
|
||||
case MoltbotCameraCommand.list.rawValue:
|
||||
let devices = await self.camera.listDevices()
|
||||
struct Payload: Codable {
|
||||
var devices: [CameraController.CameraDeviceInfo]
|
||||
}
|
||||
let payload = try Self.encodePayload(Payload(devices: devices))
|
||||
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
|
||||
case ClawdbotCameraCommand.snap.rawValue:
|
||||
case MoltbotCameraCommand.snap.rawValue:
|
||||
self.showCameraHUD(text: "Taking photo…", kind: .photo)
|
||||
self.triggerCameraFlash()
|
||||
let params = (try? Self.decodeParams(ClawdbotCameraSnapParams.self, from: req.paramsJSON)) ??
|
||||
ClawdbotCameraSnapParams()
|
||||
let params = (try? Self.decodeParams(MoltbotCameraSnapParams.self, from: req.paramsJSON)) ??
|
||||
MoltbotCameraSnapParams()
|
||||
let res = try await self.camera.snap(params: params)
|
||||
|
||||
struct Payload: Codable {
|
||||
@@ -789,9 +789,9 @@ final class NodeAppModel {
|
||||
height: res.height))
|
||||
self.showCameraHUD(text: "Photo captured", kind: .success, autoHideSeconds: 1.6)
|
||||
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
|
||||
case ClawdbotCameraCommand.clip.rawValue:
|
||||
let params = (try? Self.decodeParams(ClawdbotCameraClipParams.self, from: req.paramsJSON)) ??
|
||||
ClawdbotCameraClipParams()
|
||||
case MoltbotCameraCommand.clip.rawValue:
|
||||
let params = (try? Self.decodeParams(MoltbotCameraClipParams.self, from: req.paramsJSON)) ??
|
||||
MoltbotCameraClipParams()
|
||||
|
||||
let suspended = (params.includeAudio ?? true) ? self.voiceWake.suspendForExternalAudioCapture() : false
|
||||
defer { self.voiceWake.resumeAfterExternalAudioCapture(wasSuspended: suspended) }
|
||||
@@ -816,13 +816,13 @@ final class NodeAppModel {
|
||||
return BridgeInvokeResponse(
|
||||
id: req.id,
|
||||
ok: false,
|
||||
error: ClawdbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
error: MoltbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
|
||||
}
|
||||
}
|
||||
|
||||
private func handleScreenRecordInvoke(_ req: BridgeInvokeRequest) async throws -> BridgeInvokeResponse {
|
||||
let params = (try? Self.decodeParams(ClawdbotScreenRecordParams.self, from: req.paramsJSON)) ??
|
||||
ClawdbotScreenRecordParams()
|
||||
let params = (try? Self.decodeParams(MoltbotScreenRecordParams.self, from: req.paramsJSON)) ??
|
||||
MoltbotScreenRecordParams()
|
||||
if let format = params.format, format.lowercased() != "mp4" {
|
||||
throw NSError(domain: "Screen", code: 30, userInfo: [
|
||||
NSLocalizedDescriptionKey: "INVALID_REQUEST: screen format must be mp4",
|
||||
@@ -860,9 +860,9 @@ final class NodeAppModel {
|
||||
}
|
||||
|
||||
private extension NodeAppModel {
|
||||
func locationMode() -> ClawdbotLocationMode {
|
||||
func locationMode() -> MoltbotLocationMode {
|
||||
let raw = UserDefaults.standard.string(forKey: "location.enabledMode") ?? "off"
|
||||
return ClawdbotLocationMode(rawValue: raw) ?? .off
|
||||
return MoltbotLocationMode(rawValue: raw) ?? .off
|
||||
}
|
||||
|
||||
func isLocationPreciseEnabled() -> Bool {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Observation
|
||||
import SwiftUI
|
||||
import WebKit
|
||||
@@ -13,7 +13,7 @@ final class ScreenController {
|
||||
var urlString: String = ""
|
||||
var errorText: String?
|
||||
|
||||
/// Callback invoked when a clawdbot:// deep link is tapped in the canvas
|
||||
/// Callback invoked when a moltbot:// deep link is tapped in the canvas
|
||||
var onDeepLink: ((URL) -> Void)?
|
||||
|
||||
/// Callback invoked when the user clicks an A2UI action (e.g. button) inside the canvas web UI.
|
||||
@@ -101,7 +101,7 @@ final class ScreenController {
|
||||
let js = """
|
||||
(() => {
|
||||
try {
|
||||
const api = globalThis.__clawdbot;
|
||||
const api = globalThis.__moltbot;
|
||||
if (!api) return;
|
||||
if (typeof api.setDebugStatusEnabled === 'function') {
|
||||
api.setDebugStatusEnabled(\(enabled ? "true" : "false"));
|
||||
@@ -184,7 +184,7 @@ final class ScreenController {
|
||||
|
||||
func snapshotBase64(
|
||||
maxWidth: CGFloat? = nil,
|
||||
format: ClawdbotCanvasSnapshotFormat,
|
||||
format: MoltbotCanvasSnapshotFormat,
|
||||
quality: Double? = nil) async throws -> String
|
||||
{
|
||||
let config = WKSnapshotConfiguration()
|
||||
@@ -229,7 +229,7 @@ final class ScreenController {
|
||||
subdirectory: String)
|
||||
-> URL?
|
||||
{
|
||||
let bundle = ClawdbotKitResources.bundle
|
||||
let bundle = MoltbotKitResources.bundle
|
||||
return bundle.url(forResource: name, withExtension: ext, subdirectory: subdirectory)
|
||||
?? bundle.url(forResource: name, withExtension: ext)
|
||||
}
|
||||
@@ -342,7 +342,7 @@ extension Double {
|
||||
|
||||
// MARK: - Navigation Delegate
|
||||
|
||||
/// Handles navigation policy to intercept clawdbot:// deep links from canvas
|
||||
/// Handles navigation policy to intercept moltbot:// deep links from canvas
|
||||
@MainActor
|
||||
private final class ScreenNavigationDelegate: NSObject, WKNavigationDelegate {
|
||||
weak var controller: ScreenController?
|
||||
@@ -357,8 +357,8 @@ private final class ScreenNavigationDelegate: NSObject, WKNavigationDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
// Intercept clawdbot:// deep links
|
||||
if url.scheme == "clawdbot" {
|
||||
// Intercept moltbot:// deep links
|
||||
if url.scheme == "moltbot" {
|
||||
decisionHandler(.cancel)
|
||||
self.controller?.onDeepLink?(url)
|
||||
return
|
||||
@@ -386,7 +386,7 @@ private final class ScreenNavigationDelegate: NSObject, WKNavigationDelegate {
|
||||
}
|
||||
|
||||
private final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
static let messageName = "clawdbotCanvasA2UIAction"
|
||||
static let messageName = "moltbotCanvasA2UIAction"
|
||||
static let legacyMessageNames = ["canvas", "a2ui", "userAction", "action"]
|
||||
static let handlerNames = [messageName] + legacyMessageNames
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ final class ScreenRecordService: @unchecked Sendable {
|
||||
return URL(fileURLWithPath: outPath)
|
||||
}
|
||||
return FileManager().temporaryDirectory
|
||||
.appendingPathComponent("clawdbot-screen-record-\(UUID().uuidString).mp4")
|
||||
.appendingPathComponent("moltbot-screen-record-\(UUID().uuidString).mp4")
|
||||
}
|
||||
|
||||
private func startCapture(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import SwiftUI
|
||||
|
||||
struct ScreenTab: View {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import SwiftUI
|
||||
import WebKit
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Network
|
||||
import Observation
|
||||
import SwiftUI
|
||||
@@ -23,7 +23,7 @@ struct SettingsTab: View {
|
||||
@AppStorage("talk.enabled") private var talkEnabled: Bool = false
|
||||
@AppStorage("talk.button.enabled") private var talkButtonEnabled: Bool = true
|
||||
@AppStorage("camera.enabled") private var cameraEnabled: Bool = true
|
||||
@AppStorage("location.enabledMode") private var locationEnabledModeRaw: String = ClawdbotLocationMode.off.rawValue
|
||||
@AppStorage("location.enabledMode") private var locationEnabledModeRaw: String = MoltbotLocationMode.off.rawValue
|
||||
@AppStorage("location.preciseEnabled") private var locationPreciseEnabled: Bool = true
|
||||
@AppStorage("screen.preventSleep") private var preventSleep: Bool = true
|
||||
@AppStorage("gateway.preferredStableID") private var preferredGatewayStableID: String = ""
|
||||
@@ -37,7 +37,7 @@ struct SettingsTab: View {
|
||||
@State private var connectStatus = ConnectStatusStore()
|
||||
@State private var connectingGatewayID: String?
|
||||
@State private var localIPAddress: String?
|
||||
@State private var lastLocationModeRaw: String = ClawdbotLocationMode.off.rawValue
|
||||
@State private var lastLocationModeRaw: String = MoltbotLocationMode.off.rawValue
|
||||
@State private var gatewayToken: String = ""
|
||||
@State private var gatewayPassword: String = ""
|
||||
|
||||
@@ -197,9 +197,9 @@ struct SettingsTab: View {
|
||||
|
||||
Section("Location") {
|
||||
Picker("Location Access", selection: self.$locationEnabledModeRaw) {
|
||||
Text("Off").tag(ClawdbotLocationMode.off.rawValue)
|
||||
Text("While Using").tag(ClawdbotLocationMode.whileUsing.rawValue)
|
||||
Text("Always").tag(ClawdbotLocationMode.always.rawValue)
|
||||
Text("Off").tag(MoltbotLocationMode.off.rawValue)
|
||||
Text("While Using").tag(MoltbotLocationMode.whileUsing.rawValue)
|
||||
Text("Always").tag(MoltbotLocationMode.always.rawValue)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
@@ -213,7 +213,7 @@ struct SettingsTab: View {
|
||||
|
||||
Section("Screen") {
|
||||
Toggle("Prevent Sleep", isOn: self.$preventSleep)
|
||||
Text("Keeps the screen awake while Clawdbot is open.")
|
||||
Text("Keeps the screen awake while Moltbot is open.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ struct SettingsTab: View {
|
||||
.onChange(of: self.locationEnabledModeRaw) { _, newValue in
|
||||
let previous = self.lastLocationModeRaw
|
||||
self.lastLocationModeRaw = newValue
|
||||
guard let mode = ClawdbotLocationMode(rawValue: newValue) else { return }
|
||||
guard let mode = MoltbotLocationMode(rawValue: newValue) else { return }
|
||||
Task {
|
||||
let granted = await self.appModel.requestLocationPermissions(mode: mode)
|
||||
if !granted {
|
||||
@@ -336,8 +336,8 @@ struct SettingsTab: View {
|
||||
return "iOS \(v.majorVersion).\(v.minorVersion).\(v.patchVersion)"
|
||||
}
|
||||
|
||||
private var locationMode: ClawdbotLocationMode {
|
||||
ClawdbotLocationMode(rawValue: self.locationEnabledModeRaw) ?? .off
|
||||
private var locationMode: MoltbotLocationMode {
|
||||
MoltbotLocationMode(rawValue: self.locationEnabledModeRaw) ?? .off
|
||||
}
|
||||
|
||||
private func appVersion() -> String {
|
||||
|
||||
@@ -36,7 +36,7 @@ struct VoiceWakeWordsSettingsView: View {
|
||||
Text("Wake Words")
|
||||
} footer: {
|
||||
Text(
|
||||
"Clawdbot reacts when any trigger appears in a transcription. "
|
||||
"Moltbot reacts when any trigger appears in a transcription. "
|
||||
+ "Keep them short to avoid false positives.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AVFAudio
|
||||
import ClawdbotKit
|
||||
import ClawdbotProtocol
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
import Observation
|
||||
import OSLog
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import SwiftUI
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct AppCoverageTests {
|
||||
@Test @MainActor func nodeAppModelUpdatesBackgroundedState() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct CameraControllerClampTests {
|
||||
@Test func clampQualityDefaultsAndBounds() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct CameraControllerErrorTests {
|
||||
@Test func errorDescriptionsAreStable() {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@Suite struct DeepLinkParserTests {
|
||||
@Test func parseRejectsUnknownHost() {
|
||||
let url = URL(string: "clawdbot://nope?message=hi")!
|
||||
let url = URL(string: "moltbot://nope?message=hi")!
|
||||
#expect(DeepLinkParser.parse(url) == nil)
|
||||
}
|
||||
|
||||
@Test func parseHostIsCaseInsensitive() {
|
||||
let url = URL(string: "clawdbot://AGENT?message=Hello")!
|
||||
let url = URL(string: "moltbot://AGENT?message=Hello")!
|
||||
#expect(DeepLinkParser.parse(url) == .agent(.init(
|
||||
message: "Hello",
|
||||
sessionKey: nil,
|
||||
@@ -21,19 +21,19 @@ import Testing
|
||||
key: nil)))
|
||||
}
|
||||
|
||||
@Test func parseRejectsNonClawdbotScheme() {
|
||||
@Test func parseRejectsNonMoltbotScheme() {
|
||||
let url = URL(string: "https://example.com/agent?message=hi")!
|
||||
#expect(DeepLinkParser.parse(url) == nil)
|
||||
}
|
||||
|
||||
@Test func parseRejectsEmptyMessage() {
|
||||
let url = URL(string: "clawdbot://agent?message=%20%20%0A")!
|
||||
let url = URL(string: "moltbot://agent?message=%20%20%0A")!
|
||||
#expect(DeepLinkParser.parse(url) == nil)
|
||||
}
|
||||
|
||||
@Test func parseAgentLinkParsesCommonFields() {
|
||||
let url =
|
||||
URL(string: "clawdbot://agent?message=Hello&deliver=1&sessionKey=node-test&thinking=low&timeoutSeconds=30")!
|
||||
URL(string: "moltbot://agent?message=Hello&deliver=1&sessionKey=node-test&thinking=low&timeoutSeconds=30")!
|
||||
#expect(
|
||||
DeepLinkParser.parse(url) == .agent(
|
||||
.init(
|
||||
@@ -50,7 +50,7 @@ import Testing
|
||||
@Test func parseAgentLinkParsesTargetRoutingFields() {
|
||||
let url =
|
||||
URL(
|
||||
string: "clawdbot://agent?message=Hello%20World&deliver=1&to=%2B15551234567&channel=whatsapp&key=secret")!
|
||||
string: "moltbot://agent?message=Hello%20World&deliver=1&to=%2B15551234567&channel=whatsapp&key=secret")!
|
||||
#expect(
|
||||
DeepLinkParser.parse(url) == .agent(
|
||||
.init(
|
||||
@@ -65,7 +65,7 @@ import Testing
|
||||
}
|
||||
|
||||
@Test func parseRejectsNegativeTimeoutSeconds() {
|
||||
let url = URL(string: "clawdbot://agent?message=Hello&timeoutSeconds=-1")!
|
||||
let url = URL(string: "moltbot://agent?message=Hello&timeoutSeconds=-1")!
|
||||
#expect(DeepLinkParser.parse(url) == .agent(.init(
|
||||
message: "Hello",
|
||||
sessionKey: nil,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import Testing
|
||||
import UIKit
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws -> T) rethrows -> T {
|
||||
let defaults = UserDefaults.standard
|
||||
@@ -49,31 +49,31 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
"node.instanceId": "ios-test",
|
||||
"node.displayName": "Test Node",
|
||||
"camera.enabled": true,
|
||||
"location.enabledMode": ClawdbotLocationMode.always.rawValue,
|
||||
"location.enabledMode": MoltbotLocationMode.always.rawValue,
|
||||
VoiceWakePreferences.enabledKey: true,
|
||||
]) {
|
||||
let appModel = NodeAppModel()
|
||||
let controller = GatewayConnectionController(appModel: appModel, startDiscovery: false)
|
||||
let caps = Set(controller._test_currentCaps())
|
||||
|
||||
#expect(caps.contains(ClawdbotCapability.canvas.rawValue))
|
||||
#expect(caps.contains(ClawdbotCapability.screen.rawValue))
|
||||
#expect(caps.contains(ClawdbotCapability.camera.rawValue))
|
||||
#expect(caps.contains(ClawdbotCapability.location.rawValue))
|
||||
#expect(caps.contains(ClawdbotCapability.voiceWake.rawValue))
|
||||
#expect(caps.contains(MoltbotCapability.canvas.rawValue))
|
||||
#expect(caps.contains(MoltbotCapability.screen.rawValue))
|
||||
#expect(caps.contains(MoltbotCapability.camera.rawValue))
|
||||
#expect(caps.contains(MoltbotCapability.location.rawValue))
|
||||
#expect(caps.contains(MoltbotCapability.voiceWake.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
@Test @MainActor func currentCommandsIncludeLocationWhenEnabled() {
|
||||
withUserDefaults([
|
||||
"node.instanceId": "ios-test",
|
||||
"location.enabledMode": ClawdbotLocationMode.whileUsing.rawValue,
|
||||
"location.enabledMode": MoltbotLocationMode.whileUsing.rawValue,
|
||||
]) {
|
||||
let appModel = NodeAppModel()
|
||||
let controller = GatewayConnectionController(appModel: appModel, startDiscovery: false)
|
||||
let commands = Set(controller._test_currentCommands())
|
||||
|
||||
#expect(commands.contains(ClawdbotLocationCommand.get.rawValue))
|
||||
#expect(commands.contains(MoltbotLocationCommand.get.rawValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite(.serialized) struct GatewayDiscoveryModelTests {
|
||||
@Test @MainActor func debugLoggingCapturesLifecycleAndResets() {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Network
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct GatewayEndpointIDTests {
|
||||
@Test func stableIDForServiceDecodesAndNormalizesName() {
|
||||
let endpoint = NWEndpoint.service(
|
||||
name: "Clawdbot\\032Gateway \\032 Node\n",
|
||||
type: "_clawdbot-gw._tcp",
|
||||
name: "Moltbot\\032Gateway \\032 Node\n",
|
||||
type: "_moltbot-gw._tcp",
|
||||
domain: "local.",
|
||||
interface: nil)
|
||||
|
||||
#expect(GatewayEndpointID.stableID(endpoint) == "_clawdbot-gw._tcp|local.|Clawdbot Gateway Node")
|
||||
#expect(GatewayEndpointID.stableID(endpoint) == "_moltbot-gw._tcp|local.|Moltbot Gateway Node")
|
||||
}
|
||||
|
||||
@Test func stableIDForNonServiceUsesEndpointDescription() {
|
||||
@@ -21,8 +21,8 @@ import Testing
|
||||
|
||||
@Test func prettyDescriptionDecodesBonjourEscapes() {
|
||||
let endpoint = NWEndpoint.service(
|
||||
name: "Clawdbot\\032Gateway",
|
||||
type: "_clawdbot-gw._tcp",
|
||||
name: "Moltbot\\032Gateway",
|
||||
type: "_moltbot-gw._tcp",
|
||||
domain: "local.",
|
||||
interface: nil)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
private struct KeychainEntry: Hashable {
|
||||
let service: String
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct IOSGatewayChatTransportTests {
|
||||
@Test func requestsFailFastWhenGatewayNotConnected() async {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>ClawdbotTests</string>
|
||||
<string>MoltbotTests</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct KeychainStoreTests {
|
||||
@Test func saveLoadUpdateDeleteRoundTrip() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import Testing
|
||||
import UIKit
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws -> T) rethrows -> T {
|
||||
let defaults = UserDefaults.standard
|
||||
@@ -32,7 +32,7 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
@Suite(.serialized) struct NodeAppModelInvokeTests {
|
||||
@Test @MainActor func decodeParamsFailsWithoutJSON() {
|
||||
#expect(throws: Error.self) {
|
||||
_ = try NodeAppModel._test_decodeParams(ClawdbotCanvasNavigateParams.self, from: nil)
|
||||
_ = try NodeAppModel._test_decodeParams(MoltbotCanvasNavigateParams.self, from: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
let appModel = NodeAppModel()
|
||||
appModel.setScenePhase(.background)
|
||||
|
||||
let req = BridgeInvokeRequest(id: "bg", command: ClawdbotCanvasCommand.present.rawValue)
|
||||
let req = BridgeInvokeRequest(id: "bg", command: MoltbotCanvasCommand.present.rawValue)
|
||||
let res = await appModel._test_handleInvoke(req)
|
||||
#expect(res.ok == false)
|
||||
#expect(res.error?.code == .backgroundUnavailable)
|
||||
@@ -56,7 +56,7 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
|
||||
@Test @MainActor func handleInvokeRejectsCameraWhenDisabled() async {
|
||||
let appModel = NodeAppModel()
|
||||
let req = BridgeInvokeRequest(id: "cam", command: ClawdbotCameraCommand.snap.rawValue)
|
||||
let req = BridgeInvokeRequest(id: "cam", command: MoltbotCameraCommand.snap.rawValue)
|
||||
|
||||
let defaults = UserDefaults.standard
|
||||
let key = "camera.enabled"
|
||||
@@ -78,13 +78,13 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
|
||||
@Test @MainActor func handleInvokeRejectsInvalidScreenFormat() async {
|
||||
let appModel = NodeAppModel()
|
||||
let params = ClawdbotScreenRecordParams(format: "gif")
|
||||
let params = MoltbotScreenRecordParams(format: "gif")
|
||||
let data = try? JSONEncoder().encode(params)
|
||||
let json = data.flatMap { String(data: $0, encoding: .utf8) }
|
||||
|
||||
let req = BridgeInvokeRequest(
|
||||
id: "screen",
|
||||
command: ClawdbotScreenCommand.record.rawValue,
|
||||
command: MoltbotScreenCommand.record.rawValue,
|
||||
paramsJSON: json)
|
||||
|
||||
let res = await appModel._test_handleInvoke(req)
|
||||
@@ -96,28 +96,28 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
let appModel = NodeAppModel()
|
||||
appModel.screen.navigate(to: "http://example.com")
|
||||
|
||||
let present = BridgeInvokeRequest(id: "present", command: ClawdbotCanvasCommand.present.rawValue)
|
||||
let present = BridgeInvokeRequest(id: "present", command: MoltbotCanvasCommand.present.rawValue)
|
||||
let presentRes = await appModel._test_handleInvoke(present)
|
||||
#expect(presentRes.ok == true)
|
||||
#expect(appModel.screen.urlString.isEmpty)
|
||||
|
||||
let navigateParams = ClawdbotCanvasNavigateParams(url: "http://localhost:18789/")
|
||||
let navigateParams = MoltbotCanvasNavigateParams(url: "http://localhost:18789/")
|
||||
let navData = try JSONEncoder().encode(navigateParams)
|
||||
let navJSON = String(decoding: navData, as: UTF8.self)
|
||||
let navigate = BridgeInvokeRequest(
|
||||
id: "nav",
|
||||
command: ClawdbotCanvasCommand.navigate.rawValue,
|
||||
command: MoltbotCanvasCommand.navigate.rawValue,
|
||||
paramsJSON: navJSON)
|
||||
let navRes = await appModel._test_handleInvoke(navigate)
|
||||
#expect(navRes.ok == true)
|
||||
#expect(appModel.screen.urlString == "http://localhost:18789/")
|
||||
|
||||
let evalParams = ClawdbotCanvasEvalParams(javaScript: "1+1")
|
||||
let evalParams = MoltbotCanvasEvalParams(javaScript: "1+1")
|
||||
let evalData = try JSONEncoder().encode(evalParams)
|
||||
let evalJSON = String(decoding: evalData, as: UTF8.self)
|
||||
let eval = BridgeInvokeRequest(
|
||||
id: "eval",
|
||||
command: ClawdbotCanvasCommand.evalJS.rawValue,
|
||||
command: MoltbotCanvasCommand.evalJS.rawValue,
|
||||
paramsJSON: evalJSON)
|
||||
let evalRes = await appModel._test_handleInvoke(eval)
|
||||
#expect(evalRes.ok == true)
|
||||
@@ -129,18 +129,18 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
@Test @MainActor func handleInvokeA2UICommandsFailWhenHostMissing() async throws {
|
||||
let appModel = NodeAppModel()
|
||||
|
||||
let reset = BridgeInvokeRequest(id: "reset", command: ClawdbotCanvasA2UICommand.reset.rawValue)
|
||||
let reset = BridgeInvokeRequest(id: "reset", command: MoltbotCanvasA2UICommand.reset.rawValue)
|
||||
let resetRes = await appModel._test_handleInvoke(reset)
|
||||
#expect(resetRes.ok == false)
|
||||
#expect(resetRes.error?.message.contains("A2UI_HOST_NOT_CONFIGURED") == true)
|
||||
|
||||
let jsonl = "{\"beginRendering\":{}}"
|
||||
let pushParams = ClawdbotCanvasA2UIPushJSONLParams(jsonl: jsonl)
|
||||
let pushParams = MoltbotCanvasA2UIPushJSONLParams(jsonl: jsonl)
|
||||
let pushData = try JSONEncoder().encode(pushParams)
|
||||
let pushJSON = String(decoding: pushData, as: UTF8.self)
|
||||
let push = BridgeInvokeRequest(
|
||||
id: "push",
|
||||
command: ClawdbotCanvasA2UICommand.pushJSONL.rawValue,
|
||||
command: MoltbotCanvasA2UICommand.pushJSONL.rawValue,
|
||||
paramsJSON: pushJSON)
|
||||
let pushRes = await appModel._test_handleInvoke(push)
|
||||
#expect(pushRes.ok == false)
|
||||
@@ -157,7 +157,7 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
|
||||
@Test @MainActor func handleDeepLinkSetsErrorWhenNotConnected() async {
|
||||
let appModel = NodeAppModel()
|
||||
let url = URL(string: "clawdbot://agent?message=hello")!
|
||||
let url = URL(string: "moltbot://agent?message=hello")!
|
||||
await appModel.handleDeepLink(url: url)
|
||||
#expect(appModel.screen.errorText?.contains("Gateway not connected") == true)
|
||||
}
|
||||
@@ -165,7 +165,7 @@ private func withUserDefaults<T>(_ updates: [String: Any?], _ body: () throws ->
|
||||
@Test @MainActor func handleDeepLinkRejectsOversizedMessage() async {
|
||||
let appModel = NodeAppModel()
|
||||
let msg = String(repeating: "a", count: 20001)
|
||||
let url = URL(string: "clawdbot://agent?message=\(msg)")!
|
||||
let url = URL(string: "moltbot://agent?message=\(msg)")!
|
||||
await appModel.handleDeepLink(url: url)
|
||||
#expect(appModel.screen.errorText?.contains("Deep link too large") == true)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Testing
|
||||
import WebKit
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct ScreenControllerTests {
|
||||
@Test @MainActor func canvasModeConfiguresWebViewForTouch() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite(.serialized) struct ScreenRecordServiceTests {
|
||||
@Test func clampDefaultsAndBounds() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct SettingsNetworkingHelpersTests {
|
||||
@Test func parseHostPortParsesIPv4() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import SwiftUI
|
||||
import Testing
|
||||
import UIKit
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct SwiftUIRenderSmokeTests {
|
||||
@MainActor private static func host(_ view: some View) -> UIWindow {
|
||||
@@ -75,7 +75,7 @@ import UIKit
|
||||
}
|
||||
|
||||
@Test @MainActor func voiceWakeToastBuildsAViewHierarchy() {
|
||||
let root = VoiceWakeToast(command: "clawdbot: do something")
|
||||
let root = VoiceWakeToast(command: "moltbot: do something")
|
||||
_ = Self.host(root)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct VoiceWakeGatewaySyncTests {
|
||||
@Test func decodeGatewayTriggersFromJSONSanitizes() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
import SwabbleKit
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct VoiceWakeManagerExtractCommandTests {
|
||||
@Test func extractCommandReturnsNilWhenNoTriggerFound() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
import SwabbleKit
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite(.serialized) struct VoiceWakeManagerStateTests {
|
||||
@Test @MainActor func suspendAndResumeCycleUpdatesState() async {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import Clawdbot
|
||||
@testable import Moltbot
|
||||
|
||||
@Suite struct VoiceWakePreferencesTests {
|
||||
@Test func sanitizeTriggerWordsTrimsAndDropsEmpty() {
|
||||
|
||||
@@ -72,8 +72,8 @@ platform :ios do
|
||||
UI.user_error!("Missing IOS_DEVELOPMENT_TEAM (Apple Team ID). Add it to fastlane/.env or export it in your shell.") if team_id.nil? || team_id.strip.empty?
|
||||
|
||||
build_app(
|
||||
project: "Clawdbot.xcodeproj",
|
||||
scheme: "Clawdbot",
|
||||
project: "Moltbot.xcodeproj",
|
||||
scheme: "Moltbot",
|
||||
export_method: "app-store",
|
||||
clean: true,
|
||||
xcargs: "DEVELOPMENT_TEAM=#{team_id} -allowProvisioningUpdates",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# fastlane setup (Clawdbot iOS)
|
||||
# fastlane setup (Moltbot iOS)
|
||||
|
||||
Install:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Clawdbot
|
||||
name: Moltbot
|
||||
options:
|
||||
bundleIdPrefix: com.clawdbot
|
||||
deploymentTarget:
|
||||
@@ -10,33 +10,33 @@ settings:
|
||||
SWIFT_VERSION: "6.0"
|
||||
|
||||
packages:
|
||||
ClawdbotKit:
|
||||
MoltbotKit:
|
||||
path: ../shared/ClawdbotKit
|
||||
Swabble:
|
||||
path: ../../Swabble
|
||||
|
||||
schemes:
|
||||
Clawdbot:
|
||||
Moltbot:
|
||||
shared: true
|
||||
build:
|
||||
targets:
|
||||
Clawdbot: all
|
||||
Moltbot: all
|
||||
test:
|
||||
targets:
|
||||
- ClawdbotTests
|
||||
- MoltbotTests
|
||||
|
||||
targets:
|
||||
Clawdbot:
|
||||
Moltbot:
|
||||
type: application
|
||||
platform: iOS
|
||||
sources:
|
||||
- path: Sources
|
||||
dependencies:
|
||||
- package: ClawdbotKit
|
||||
- package: ClawdbotKit
|
||||
product: ClawdbotChatUI
|
||||
- package: ClawdbotKit
|
||||
product: ClawdbotProtocol
|
||||
- package: MoltbotKit
|
||||
- package: MoltbotKit
|
||||
product: MoltbotChatUI
|
||||
- package: MoltbotKit
|
||||
product: MoltbotProtocol
|
||||
- package: Swabble
|
||||
product: SwabbleKit
|
||||
- sdk: AppIntents.framework
|
||||
@@ -79,7 +79,7 @@ targets:
|
||||
info:
|
||||
path: Sources/Info.plist
|
||||
properties:
|
||||
CFBundleDisplayName: Clawdbot
|
||||
CFBundleDisplayName: Moltbot
|
||||
CFBundleIconName: AppIcon
|
||||
CFBundleShortVersionString: "2026.1.26"
|
||||
CFBundleVersion: "20260126"
|
||||
@@ -88,16 +88,16 @@ targets:
|
||||
UIApplicationSupportsMultipleScenes: false
|
||||
UIBackgroundModes:
|
||||
- audio
|
||||
NSLocalNetworkUsageDescription: Clawdbot discovers and connects to your Clawdbot gateway on the local network.
|
||||
NSLocalNetworkUsageDescription: Moltbot discovers and connects to your Moltbot gateway on the local network.
|
||||
NSAppTransportSecurity:
|
||||
NSAllowsArbitraryLoadsInWebContent: true
|
||||
NSBonjourServices:
|
||||
- _clawdbot-gw._tcp
|
||||
NSCameraUsageDescription: Clawdbot can capture photos or short video clips when requested via the gateway.
|
||||
NSLocationWhenInUseUsageDescription: Clawdbot uses your location when you allow location sharing.
|
||||
NSLocationAlwaysAndWhenInUseUsageDescription: Clawdbot can share your location in the background when you enable Always.
|
||||
NSMicrophoneUsageDescription: Clawdbot needs microphone access for voice wake.
|
||||
NSSpeechRecognitionUsageDescription: Clawdbot uses on-device speech recognition for voice wake.
|
||||
- _moltbot-gw._tcp
|
||||
NSCameraUsageDescription: Moltbot can capture photos or short video clips when requested via the gateway.
|
||||
NSLocationWhenInUseUsageDescription: Moltbot uses your location when you allow location sharing.
|
||||
NSLocationAlwaysAndWhenInUseUsageDescription: Moltbot can share your location in the background when you enable Always.
|
||||
NSMicrophoneUsageDescription: Moltbot needs microphone access for voice wake.
|
||||
NSSpeechRecognitionUsageDescription: Moltbot uses on-device speech recognition for voice wake.
|
||||
UISupportedInterfaceOrientations:
|
||||
- UIInterfaceOrientationPortrait
|
||||
- UIInterfaceOrientationPortraitUpsideDown
|
||||
@@ -109,13 +109,13 @@ targets:
|
||||
- UIInterfaceOrientationLandscapeLeft
|
||||
- UIInterfaceOrientationLandscapeRight
|
||||
|
||||
ClawdbotTests:
|
||||
MoltbotTests:
|
||||
type: bundle.unit-test
|
||||
platform: iOS
|
||||
sources:
|
||||
- path: Tests
|
||||
dependencies:
|
||||
- target: Clawdbot
|
||||
- target: Moltbot
|
||||
- package: Swabble
|
||||
product: SwabbleKit
|
||||
- sdk: AppIntents.framework
|
||||
@@ -124,11 +124,11 @@ targets:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: com.clawdbot.ios.tests
|
||||
SWIFT_VERSION: "6.0"
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/Clawdbot.app/Clawdbot"
|
||||
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/Moltbot.app/Moltbot"
|
||||
BUNDLE_LOADER: "$(TEST_HOST)"
|
||||
info:
|
||||
path: Tests/Info.plist
|
||||
properties:
|
||||
CFBundleDisplayName: ClawdbotTests
|
||||
CFBundleDisplayName: MoltbotTests
|
||||
CFBundleShortVersionString: "2026.1.26"
|
||||
CFBundleVersion: "20260126"
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"image-name" : "clawdbot-mac.png",
|
||||
"name" : "clawdbot-mac",
|
||||
"image-name" : "moltbot-mac.png",
|
||||
"name" : "moltbot-mac",
|
||||
"position" : {
|
||||
"scale" : 1.07,
|
||||
"translation-in-points" : [
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// swift-tools-version: 6.2
|
||||
// Package manifest for the Clawdbot macOS companion (menu bar app + IPC library).
|
||||
// Package manifest for the Moltbot macOS companion (menu bar app + IPC library).
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Clawdbot",
|
||||
name: "Moltbot",
|
||||
platforms: [
|
||||
.macOS(.v15),
|
||||
],
|
||||
products: [
|
||||
.library(name: "ClawdbotIPC", targets: ["ClawdbotIPC"]),
|
||||
.library(name: "ClawdbotDiscovery", targets: ["ClawdbotDiscovery"]),
|
||||
.executable(name: "Clawdbot", targets: ["Clawdbot"]),
|
||||
.executable(name: "clawdbot-mac", targets: ["ClawdbotMacCLI"]),
|
||||
.library(name: "MoltbotIPC", targets: ["MoltbotIPC"]),
|
||||
.library(name: "MoltbotDiscovery", targets: ["MoltbotDiscovery"]),
|
||||
.executable(name: "Moltbot", targets: ["Moltbot"]),
|
||||
.executable(name: "moltbot-mac", targets: ["MoltbotMacCLI"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/orchetect/MenuBarExtraAccess", exact: "1.2.2"),
|
||||
@@ -25,28 +25,28 @@ let package = Package(
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "ClawdbotIPC",
|
||||
name: "MoltbotIPC",
|
||||
dependencies: [],
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.target(
|
||||
name: "ClawdbotDiscovery",
|
||||
name: "MoltbotDiscovery",
|
||||
dependencies: [
|
||||
.product(name: "ClawdbotKit", package: "ClawdbotKit"),
|
||||
.product(name: "MoltbotKit", package: "MoltbotKit"),
|
||||
],
|
||||
path: "Sources/ClawdbotDiscovery",
|
||||
path: "Sources/MoltbotDiscovery",
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.executableTarget(
|
||||
name: "Clawdbot",
|
||||
name: "Moltbot",
|
||||
dependencies: [
|
||||
"ClawdbotIPC",
|
||||
"ClawdbotDiscovery",
|
||||
.product(name: "ClawdbotKit", package: "ClawdbotKit"),
|
||||
.product(name: "ClawdbotChatUI", package: "ClawdbotKit"),
|
||||
.product(name: "ClawdbotProtocol", package: "ClawdbotKit"),
|
||||
"MoltbotIPC",
|
||||
"MoltbotDiscovery",
|
||||
.product(name: "MoltbotKit", package: "MoltbotKit"),
|
||||
.product(name: "MoltbotChatUI", package: "MoltbotKit"),
|
||||
.product(name: "MoltbotProtocol", package: "MoltbotKit"),
|
||||
.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/Clawdbot.icns"),
|
||||
.copy("Resources/Moltbot.icns"),
|
||||
.copy("Resources/DeviceModels"),
|
||||
],
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.executableTarget(
|
||||
name: "ClawdbotMacCLI",
|
||||
name: "MoltbotMacCLI",
|
||||
dependencies: [
|
||||
"ClawdbotDiscovery",
|
||||
.product(name: "ClawdbotKit", package: "ClawdbotKit"),
|
||||
.product(name: "ClawdbotProtocol", package: "ClawdbotKit"),
|
||||
"MoltbotDiscovery",
|
||||
.product(name: "MoltbotKit", package: "MoltbotKit"),
|
||||
.product(name: "MoltbotProtocol", package: "MoltbotKit"),
|
||||
],
|
||||
path: "Sources/ClawdbotMacCLI",
|
||||
path: "Sources/MoltbotMacCLI",
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
]),
|
||||
.testTarget(
|
||||
name: "ClawdbotIPCTests",
|
||||
name: "MoltbotIPCTests",
|
||||
dependencies: [
|
||||
"ClawdbotIPC",
|
||||
"Clawdbot",
|
||||
"ClawdbotDiscovery",
|
||||
.product(name: "ClawdbotProtocol", package: "ClawdbotKit"),
|
||||
"MoltbotIPC",
|
||||
"Moltbot",
|
||||
"MoltbotDiscovery",
|
||||
.product(name: "MoltbotProtocol", package: "MoltbotKit"),
|
||||
.product(name: "SwabbleKit", package: "swabble"),
|
||||
],
|
||||
swiftSettings: [
|
||||
|
||||
@@ -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/clawdbot/clawdbot") {
|
||||
if let url = URL(string: "https://github.com/moltbot/moltbot") {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
} label: {
|
||||
@@ -29,7 +29,7 @@ struct AboutSettings: View {
|
||||
}
|
||||
|
||||
VStack(spacing: 3) {
|
||||
Text("Clawdbot")
|
||||
Text("Moltbot")
|
||||
.font(.title3.bold())
|
||||
Text("Version \(self.versionString)")
|
||||
.foregroundStyle(.secondary)
|
||||
@@ -49,7 +49,7 @@ struct AboutSettings: View {
|
||||
AboutLinkRow(
|
||||
icon: "chevron.left.slash.chevron.right",
|
||||
title: "GitHub",
|
||||
url: "https://github.com/clawdbot/clawdbot")
|
||||
url: "https://github.com/moltbot/moltbot")
|
||||
AboutLinkRow(icon: "globe", title: "Website", url: "https://steipete.me")
|
||||
AboutLinkRow(icon: "bird", title: "Twitter", url: "https://twitter.com/steipete")
|
||||
AboutLinkRow(icon: "envelope", title: "Email", url: "mailto:peter@steipete.me")
|
||||
@@ -108,7 +108,7 @@ struct AboutSettings: View {
|
||||
}
|
||||
|
||||
private var buildTimestamp: String? {
|
||||
guard let raw = Bundle.main.object(forInfoDictionaryKey: "ClawdbotBuildTimestamp") as? String
|
||||
guard let raw = Bundle.main.object(forInfoDictionaryKey: "MoltbotBuildTimestamp") as? String
|
||||
else { return nil }
|
||||
let parser = ISO8601DateFormatter()
|
||||
parser.formatOptions = [.withInternetDateTime]
|
||||
@@ -122,7 +122,7 @@ struct AboutSettings: View {
|
||||
}
|
||||
|
||||
private var gitCommit: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "ClawdbotGitCommit") as? String ?? "unknown"
|
||||
Bundle.main.object(forInfoDictionaryKey: "MoltbotGitCommit") as? String ?? "unknown"
|
||||
}
|
||||
|
||||
private var bundleID: String {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
@@ -81,7 +81,7 @@ private struct EventRow: View {
|
||||
return f.string(from: date)
|
||||
}
|
||||
|
||||
private func prettyJSON(_ dict: [String: ClawdbotProtocol.AnyCodable]) -> String? {
|
||||
private func prettyJSON(_ dict: [String: MoltbotProtocol.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": ClawdbotProtocol.AnyCodable("start"),
|
||||
"name": ClawdbotProtocol.AnyCodable("bash"),
|
||||
"phase": MoltbotProtocol.AnyCodable("start"),
|
||||
"name": MoltbotProtocol.AnyCodable("bash"),
|
||||
],
|
||||
summary: nil)
|
||||
AgentEventStore.shared.append(sample)
|
||||
|
||||
@@ -34,7 +34,7 @@ enum AgentWorkspace {
|
||||
|
||||
static func resolveWorkspaceURL(from userInput: String?) -> URL {
|
||||
let trimmed = userInput?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if trimmed.isEmpty { return ClawdbotConfigFile.defaultWorkspaceURL() }
|
||||
if trimmed.isEmpty { return MoltbotConfigFile.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 - Clawdbot Workspace
|
||||
# AGENTS.md - Moltbot Workspace
|
||||
|
||||
This folder is the assistant's working directory.
|
||||
|
||||
@@ -265,7 +265,7 @@ enum AgentWorkspace {
|
||||
- Timezone (optional)
|
||||
- Notes
|
||||
|
||||
3) ~/.clawdbot/clawdbot.json
|
||||
3) ~/.clawdbot/moltbot.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: ClawdbotOAuthStore.AnthropicOAuthStatus = ClawdbotOAuthStore.anthropicOAuthStatus()
|
||||
@State private var oauthStatus: MoltbotOAuthStore.AnthropicOAuthStatus = MoltbotOAuthStore.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([ClawdbotOAuthStore.oauthURL()])
|
||||
NSWorkspace.shared.activateFileViewerSelecting([MoltbotOAuthStore.oauthURL()])
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.disabled(!FileManager().fileExists(atPath: ClawdbotOAuthStore.oauthURL().path))
|
||||
.disabled(!FileManager().fileExists(atPath: MoltbotOAuthStore.oauthURL().path))
|
||||
|
||||
Button("Refresh") {
|
||||
self.refresh()
|
||||
@@ -53,7 +53,7 @@ struct AnthropicAuthControls: View {
|
||||
.buttonStyle(.bordered)
|
||||
}
|
||||
|
||||
Text(ClawdbotOAuthStore.oauthURL().path)
|
||||
Text(MoltbotOAuthStore.oauthURL().path)
|
||||
.font(.caption.monospaced())
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
@@ -130,8 +130,8 @@ struct AnthropicAuthControls: View {
|
||||
}
|
||||
|
||||
private func refresh() {
|
||||
let imported = ClawdbotOAuthStore.importLegacyAnthropicOAuthIfNeeded()
|
||||
self.oauthStatus = ClawdbotOAuthStore.anthropicOAuthStatus()
|
||||
let imported = MoltbotOAuthStore.importLegacyAnthropicOAuthIfNeeded()
|
||||
self.oauthStatus = MoltbotOAuthStore.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 ClawdbotOAuthStore.saveAnthropicOAuth(creds)
|
||||
try MoltbotOAuthStore.saveAnthropicOAuth(creds)
|
||||
self.refresh()
|
||||
self.pkce = nil
|
||||
self.code = ""
|
||||
self.statusText = "Connected. Clawdbot can now use Claude via OAuth."
|
||||
self.statusText = "Connected. Moltbot 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: ClawdbotOAuthStore.AnthropicOAuthStatus,
|
||||
oauthStatus: MoltbotOAuthStore.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 (Clawdbot token file)"
|
||||
case .oauthFile: "OAuth (Moltbot 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: ClawdbotOAuthStore.AnthropicOAuthStatus = ClawdbotOAuthStore
|
||||
oauthStatus: MoltbotOAuthStore.AnthropicOAuthStatus = MoltbotOAuthStore
|
||||
.anthropicOAuthStatus()) -> AnthropicAuthMode
|
||||
{
|
||||
if oauthStatus.isConnected { return .oauthFile }
|
||||
@@ -194,10 +194,10 @@ enum AnthropicOAuth {
|
||||
}
|
||||
}
|
||||
|
||||
enum ClawdbotOAuthStore {
|
||||
enum MoltbotOAuthStore {
|
||||
static let oauthFilename = "oauth.json"
|
||||
private static let providerKey = "anthropic"
|
||||
private static let clawdbotOAuthDirEnv = "CLAWDBOT_OAUTH_DIR"
|
||||
private static let moltbotOAuthDirEnv = "CLAWDBOT_OAUTH_DIR"
|
||||
private static let legacyPiDirEnv = "PI_CODING_AGENT_DIR"
|
||||
|
||||
enum AnthropicOAuthStatus: Equatable {
|
||||
@@ -215,12 +215,12 @@ enum ClawdbotOAuthStore {
|
||||
|
||||
var shortDescription: String {
|
||||
switch self {
|
||||
case .missingFile: "Clawdbot OAuth token file not found"
|
||||
case .unreadableFile: "Clawdbot OAuth token file not readable"
|
||||
case .invalidJSON: "Clawdbot OAuth token file invalid"
|
||||
case .missingProviderEntry: "No Anthropic entry in Clawdbot OAuth token file"
|
||||
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 .missingTokens: "Anthropic entry missing tokens"
|
||||
case .connected: "Clawdbot OAuth credentials found"
|
||||
case .connected: "Moltbot OAuth credentials found"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import ClawdbotKit
|
||||
import ClawdbotProtocol
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
|
||||
// Prefer the ClawdbotKit wrapper to keep gateway request payloads consistent.
|
||||
typealias AnyCodable = ClawdbotKit.AnyCodable
|
||||
typealias InstanceIdentity = ClawdbotKit.InstanceIdentity
|
||||
// Prefer the MoltbotKit wrapper to keep gateway request payloads consistent.
|
||||
typealias AnyCodable = MoltbotKit.AnyCodable
|
||||
typealias InstanceIdentity = MoltbotKit.InstanceIdentity
|
||||
|
||||
extension AnyCodable {
|
||||
var stringValue: String? { self.value as? String }
|
||||
@@ -26,19 +26,19 @@ extension AnyCodable {
|
||||
}
|
||||
}
|
||||
|
||||
extension ClawdbotProtocol.AnyCodable {
|
||||
extension MoltbotProtocol.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: ClawdbotProtocol.AnyCodable]? { self.value as? [String: ClawdbotProtocol.AnyCodable] }
|
||||
var arrayValue: [ClawdbotProtocol.AnyCodable]? { self.value as? [ClawdbotProtocol.AnyCodable] }
|
||||
var dictionaryValue: [String: MoltbotProtocol.AnyCodable]? { self.value as? [String: MoltbotProtocol.AnyCodable] }
|
||||
var arrayValue: [MoltbotProtocol.AnyCodable]? { self.value as? [MoltbotProtocol.AnyCodable] }
|
||||
|
||||
var foundationValue: Any {
|
||||
switch self.value {
|
||||
case let dict as [String: ClawdbotProtocol.AnyCodable]:
|
||||
case let dict as [String: MoltbotProtocol.AnyCodable]:
|
||||
dict.mapValues { $0.foundationValue }
|
||||
case let array as [ClawdbotProtocol.AnyCodable]:
|
||||
case let array as [MoltbotProtocol.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: "clawdbot.onboardingSeen") }
|
||||
didSet { self.ifNotPreview { UserDefaults.standard.set(self.onboardingSeen, forKey: "moltbot.onboardingSeen") }
|
||||
}
|
||||
}
|
||||
|
||||
var debugPaneEnabled: Bool {
|
||||
didSet {
|
||||
self.ifNotPreview { UserDefaults.standard.set(self.debugPaneEnabled, forKey: "clawdbot.debugPaneEnabled") }
|
||||
self.ifNotPreview { UserDefaults.standard.set(self.debugPaneEnabled, forKey: "moltbot.debugPaneEnabled") }
|
||||
CanvasManager.shared.refreshDebugStatus()
|
||||
}
|
||||
}
|
||||
@@ -229,11 +229,11 @@ final class AppState {
|
||||
|
||||
init(preview: Bool = false) {
|
||||
self.isPreview = preview || ProcessInfo.processInfo.isRunningTests
|
||||
let onboardingSeen = UserDefaults.standard.bool(forKey: "clawdbot.onboardingSeen")
|
||||
let onboardingSeen = UserDefaults.standard.bool(forKey: "moltbot.onboardingSeen")
|
||||
self.isPaused = UserDefaults.standard.bool(forKey: pauseDefaultsKey)
|
||||
self.launchAtLogin = false
|
||||
self.onboardingSeen = onboardingSeen
|
||||
self.debugPaneEnabled = UserDefaults.standard.bool(forKey: "clawdbot.debugPaneEnabled")
|
||||
self.debugPaneEnabled = UserDefaults.standard.bool(forKey: "moltbot.debugPaneEnabled")
|
||||
let savedVoiceWake = UserDefaults.standard.bool(forKey: swabbleEnabledKey)
|
||||
self.swabbleEnabled = voiceWakeSupported ? savedVoiceWake : false
|
||||
self.swabbleTriggerWords = UserDefaults.standard
|
||||
@@ -275,7 +275,7 @@ final class AppState {
|
||||
UserDefaults.standard.set(IconOverrideSelection.system.rawValue, forKey: iconOverrideKey)
|
||||
}
|
||||
|
||||
let configRoot = ClawdbotConfigFile.loadDict()
|
||||
let configRoot = MoltbotConfigFile.loadDict()
|
||||
let configRemoteUrl = GatewayRemoteConfig.resolveUrlString(root: configRoot)
|
||||
let configRemoteTransport = GatewayRemoteConfig.resolveTransport(root: configRoot)
|
||||
let resolvedConnectionMode = ConnectionModeResolver.resolve(root: configRoot).mode
|
||||
@@ -353,7 +353,7 @@ final class AppState {
|
||||
}
|
||||
|
||||
private func startConfigWatcher() {
|
||||
let configUrl = ClawdbotConfigFile.url()
|
||||
let configUrl = MoltbotConfigFile.url()
|
||||
self.configWatcher = ConfigFileWatcher(url: configUrl) { [weak self] in
|
||||
Task { @MainActor in
|
||||
self?.applyConfigFromDisk()
|
||||
@@ -363,7 +363,7 @@ final class AppState {
|
||||
}
|
||||
|
||||
private func applyConfigFromDisk() {
|
||||
let root = ClawdbotConfigFile.loadDict()
|
||||
let root = MoltbotConfigFile.loadDict()
|
||||
self.applyConfigOverrides(root)
|
||||
}
|
||||
|
||||
@@ -451,7 +451,7 @@ final class AppState {
|
||||
|
||||
Task { @MainActor in
|
||||
// Keep app-only connection settings local to avoid overwriting remote gateway config.
|
||||
var root = ClawdbotConfigFile.loadDict()
|
||||
var root = MoltbotConfigFile.loadDict()
|
||||
var gateway = root["gateway"] as? [String: Any] ?? [:]
|
||||
var changed = false
|
||||
|
||||
@@ -541,7 +541,7 @@ final class AppState {
|
||||
} else {
|
||||
root["gateway"] = gateway
|
||||
}
|
||||
ClawdbotConfigFile.saveDict(root)
|
||||
MoltbotConfigFile.saveDict(root)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +685,7 @@ extension AppState {
|
||||
state.remoteTarget = "user@example.com"
|
||||
state.remoteUrl = "wss://gateway.example.ts.net"
|
||||
state.remoteIdentity = "~/.ssh/id_ed25519"
|
||||
state.remoteProjectRoot = "~/Projects/clawdbot"
|
||||
state.remoteProjectRoot = "~/Projects/moltbot"
|
||||
state.remoteCliPath = ""
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ final class CLIInstallPrompter {
|
||||
UserDefaults.standard.set(version, forKey: cliInstallPromptedVersionKey)
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Install Clawdbot CLI?"
|
||||
alert.messageText = "Install Moltbot CLI?"
|
||||
alert.informativeText = "Local mode needs the CLI so launchd can run the gateway."
|
||||
alert.addButton(withTitle: "Install CLI")
|
||||
alert.addButton(withTitle: "Not now")
|
||||
|
||||
@@ -13,7 +13,7 @@ enum CLIInstaller {
|
||||
fileManager: FileManager) -> String?
|
||||
{
|
||||
for basePath in searchPaths {
|
||||
let candidate = URL(fileURLWithPath: basePath).appendingPathComponent("clawdbot").path
|
||||
let candidate = URL(fileURLWithPath: basePath).appendingPathComponent("moltbot").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 clawdbot CLI…")
|
||||
await statusHandler("Installing moltbot 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 clawdbot \($0)." } ?? "Installed clawdbot."
|
||||
let summary = installedVersion.map { "Installed moltbot \($0)." } ?? "Installed moltbot."
|
||||
await statusHandler(summary)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AVFoundation
|
||||
import ClawdbotIPC
|
||||
import ClawdbotKit
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
import OSLog
|
||||
@@ -168,7 +168,7 @@ actor CameraCaptureService {
|
||||
await Self.warmUpCaptureSession()
|
||||
|
||||
let tmpMovURL = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mov")
|
||||
.appendingPathComponent("moltbot-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("clawdbot-camera-\(UUID().uuidString).mp4")
|
||||
.appendingPathComponent("moltbot-camera-\(UUID().uuidString).mp4")
|
||||
}()
|
||||
|
||||
// Ensure we don't fail exporting due to an existing file.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import AppKit
|
||||
import ClawdbotIPC
|
||||
import ClawdbotKit
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
static let messageName = "clawdbotCanvasA2UIAction"
|
||||
static let messageName = "moltbotCanvasA2UIAction"
|
||||
|
||||
private let sessionKey: String
|
||||
|
||||
@@ -52,7 +52,7 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
}()
|
||||
guard !userAction.isEmpty else { return }
|
||||
|
||||
guard let name = ClawdbotCanvasA2UIAction.extractActionName(userAction) else { return }
|
||||
guard let name = MoltbotCanvasA2UIAction.extractActionName(userAction) else { return }
|
||||
let actionId =
|
||||
(userAction["id"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty
|
||||
?? UUID().uuidString
|
||||
@@ -64,15 +64,15 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
let sourceComponentId = (userAction["sourceComponentId"] as? String)?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty ?? "-"
|
||||
let instanceId = InstanceIdentity.instanceId.lowercased()
|
||||
let contextJSON = ClawdbotCanvasA2UIAction.compactJSON(userAction["context"])
|
||||
let contextJSON = MoltbotCanvasA2UIAction.compactJSON(userAction["context"])
|
||||
|
||||
// Token-efficient and unambiguous. The agent should treat this as a UI event and (by default) update Canvas.
|
||||
let messageContext = ClawdbotCanvasA2UIAction.AgentMessageContext(
|
||||
let messageContext = MoltbotCanvasA2UIAction.AgentMessageContext(
|
||||
actionName: name,
|
||||
session: .init(key: self.sessionKey, surfaceId: surfaceId),
|
||||
component: .init(id: sourceComponentId, host: InstanceIdentity.displayName, instanceId: instanceId),
|
||||
contextJSON: contextJSON)
|
||||
let text = ClawdbotCanvasA2UIAction.formatAgentMessage(messageContext)
|
||||
let text = MoltbotCanvasA2UIAction.formatAgentMessage(messageContext)
|
||||
|
||||
Task { [weak webView] in
|
||||
if AppStateStore.shared.connectionMode == .local {
|
||||
@@ -91,7 +91,7 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
|
||||
await MainActor.run {
|
||||
guard let webView else { return }
|
||||
let js = ClawdbotCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
let js = MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
actionId: actionId,
|
||||
ok: result.ok,
|
||||
error: result.error)
|
||||
@@ -144,5 +144,5 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
return false
|
||||
}
|
||||
|
||||
// Formatting helpers live in ClawdbotKit (`ClawdbotCanvasA2UIAction`).
|
||||
// Formatting helpers live in MoltbotKit (`MoltbotCanvasA2UIAction`).
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AppKit
|
||||
import ClawdbotIPC
|
||||
import ClawdbotKit
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
@@ -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("Clawdbot/canvas", isDirectory: true)
|
||||
return base.appendingPathComponent("Moltbot/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("__clawdbot__/a2ui/").absoluteString + "?platform=macos"
|
||||
return base.appendingPathComponent("__moltbot__/a2ui/").absoluteString + "?platform=macos"
|
||||
}
|
||||
|
||||
// MARK: - Anchoring
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
enum CanvasScheme {
|
||||
static let scheme = "clawdbot-canvas"
|
||||
static let scheme = "moltbot-canvas"
|
||||
|
||||
static func makeURL(session: String, path: String? = nil) -> URL? {
|
||||
var comps = URLComponents()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotKit
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
import WebKit
|
||||
@@ -222,7 +222,7 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
let name = fileURL.deletingPathExtension().lastPathComponent
|
||||
guard !name.isEmpty, !ext.isEmpty else { return nil }
|
||||
|
||||
let bundle = ClawdbotKitResources.bundle
|
||||
let bundle = MoltbotKitResources.bundle
|
||||
let resourceURL =
|
||||
bundle.url(forResource: name, withExtension: ext, subdirectory: subdirectory)
|
||||
?? bundle.url(forResource: name, withExtension: ext)
|
||||
|
||||
@@ -23,7 +23,7 @@ extension CanvasWindowController {
|
||||
}
|
||||
|
||||
static func storedFrameDefaultsKey(sessionKey: String) -> String {
|
||||
"clawdbot.canvas.frame.\(self.sanitizeSessionKey(sessionKey))"
|
||||
"moltbot.canvas.frame.\(self.sanitizeSessionKey(sessionKey))"
|
||||
}
|
||||
|
||||
static func loadRestoredFrame(sessionKey: String) -> NSRect? {
|
||||
|
||||
@@ -17,7 +17,7 @@ extension CanvasWindowController {
|
||||
let scheme = url.scheme?.lowercased()
|
||||
|
||||
// Deep links: allow local Canvas content to invoke the agent without bouncing through NSWorkspace.
|
||||
if scheme == "clawdbot" {
|
||||
if scheme == "moltbot" {
|
||||
if self.webView.url?.scheme == CanvasScheme.scheme {
|
||||
Task { await DeepLinkHandler.shared.handle(url: url) }
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppKit
|
||||
import ClawdbotIPC
|
||||
import MoltbotIPC
|
||||
|
||||
extension CanvasWindowController {
|
||||
// MARK: - Window
|
||||
@@ -12,7 +12,7 @@ extension CanvasWindowController {
|
||||
styleMask: [.titled, .closable, .resizable, .miniaturizable],
|
||||
backing: .buffered,
|
||||
defer: false)
|
||||
window.title = "Clawdbot Canvas"
|
||||
window.title = "Moltbot Canvas"
|
||||
window.isReleasedWhenClosed = false
|
||||
window.contentView = contentView
|
||||
window.center()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AppKit
|
||||
import ClawdbotIPC
|
||||
import ClawdbotKit
|
||||
import MoltbotIPC
|
||||
import MoltbotKit
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
@@ -57,8 +57,8 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
(() => {
|
||||
try {
|
||||
if (location.protocol !== '\(CanvasScheme.scheme):') return;
|
||||
if (globalThis.__clawdbotA2UIBridgeInstalled) return;
|
||||
globalThis.__clawdbotA2UIBridgeInstalled = true;
|
||||
if (globalThis.__moltbotA2UIBridgeInstalled) return;
|
||||
globalThis.__moltbotA2UIBridgeInstalled = true;
|
||||
|
||||
const deepLinkKey = \(Self.jsStringLiteral(deepLinkKey));
|
||||
const sessionKey = \(Self.jsStringLiteral(injectedSessionKey));
|
||||
@@ -89,7 +89,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
|
||||
// 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('clawdbot-a2ui-host');
|
||||
const hasBundledA2UIHost = !!globalThis.clawdbotA2UI || !!document.querySelector('moltbot-a2ui-host');
|
||||
if (hasBundledA2UIHost && handler?.postMessage) return;
|
||||
|
||||
// Otherwise, forward directly when possible.
|
||||
@@ -115,7 +115,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
params.set('deliver', 'false');
|
||||
params.set('channel', 'last');
|
||||
params.set('key', deepLinkKey);
|
||||
location.href = 'clawdbot://agent?' + params.toString();
|
||||
location.href = 'moltbot://agent?' + params.toString();
|
||||
} catch {}
|
||||
}, true);
|
||||
} catch {}
|
||||
@@ -268,7 +268,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
let js = """
|
||||
(() => {
|
||||
try {
|
||||
const api = globalThis.__clawdbot;
|
||||
const api = globalThis.__moltbot;
|
||||
if (!api) return;
|
||||
if (typeof api.setDebugStatusEnabled === 'function') {
|
||||
api.setDebugStatusEnabled(\(enabled ? "true" : "false"));
|
||||
@@ -336,7 +336,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
||||
path = outPath
|
||||
} else {
|
||||
let ts = Int(Date().timeIntervalSince1970)
|
||||
path = "/tmp/clawdbot-canvas-\(CanvasWindowController.sanitizeSessionKey(self.sessionKey))-\(ts).png"
|
||||
path = "/tmp/moltbot-canvas-\(CanvasWindowController.sanitizeSessionKey(self.sessionKey))-\(ts).png"
|
||||
}
|
||||
|
||||
try png.write(to: URL(fileURLWithPath: path), options: [.atomic])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import SwiftUI
|
||||
|
||||
extension ChannelsSettings {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
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/clawdbot.json."
|
||||
? "Config invalid; fix it in ~/.clawdbot/moltbot.json."
|
||||
: nil
|
||||
self.configRoot = snap.config?.mapValues { $0.foundationValue } ?? [:]
|
||||
self.configDraft = cloneConfigValue(self.configRoot) as? [String: Any] ?? self.configRoot
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
|
||||
extension ChannelsStore {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
|
||||
enum ClawdbotConfigFile {
|
||||
enum MoltbotConfigFile {
|
||||
private static let logger = Logger(subsystem: "com.clawdbot", category: "config")
|
||||
|
||||
static func url() -> URL {
|
||||
ClawdbotPaths.configURL
|
||||
MoltbotPaths.configURL
|
||||
}
|
||||
|
||||
static func stateDirURL() -> URL {
|
||||
ClawdbotPaths.stateDirURL
|
||||
MoltbotPaths.stateDirURL
|
||||
}
|
||||
|
||||
static func defaultWorkspaceURL() -> URL {
|
||||
ClawdbotPaths.workspaceURL
|
||||
MoltbotPaths.workspaceURL
|
||||
}
|
||||
|
||||
static func loadDict() -> [String: Any] {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
enum ClawdbotEnv {
|
||||
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 }
|
||||
@@ -13,12 +13,12 @@ enum ClawdbotEnv {
|
||||
}
|
||||
}
|
||||
|
||||
enum ClawdbotPaths {
|
||||
enum MoltbotPaths {
|
||||
private static let configPathEnv = "CLAWDBOT_CONFIG_PATH"
|
||||
private static let stateDirEnv = "CLAWDBOT_STATE_DIR"
|
||||
|
||||
static var stateDirURL: URL {
|
||||
if let override = ClawdbotEnv.path(self.stateDirEnv) {
|
||||
if let override = MoltbotEnv.path(self.stateDirEnv) {
|
||||
return URL(fileURLWithPath: override, isDirectory: true)
|
||||
}
|
||||
return FileManager().homeDirectoryForCurrentUser
|
||||
@@ -26,10 +26,10 @@ enum ClawdbotPaths {
|
||||
}
|
||||
|
||||
static var configURL: URL {
|
||||
if let override = ClawdbotEnv.path(self.configPathEnv) {
|
||||
if let override = MoltbotEnv.path(self.configPathEnv) {
|
||||
return URL(fileURLWithPath: override)
|
||||
}
|
||||
return self.stateDirURL.appendingPathComponent("clawdbot.json")
|
||||
return self.stateDirURL.appendingPathComponent("moltbot.json")
|
||||
}
|
||||
|
||||
static var workspaceURL: URL {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Foundation
|
||||
|
||||
enum CommandResolver {
|
||||
private static let projectRootDefaultsKey = "clawdbot.gatewayProjectRootPath"
|
||||
private static let helperName = "clawdbot"
|
||||
private static let projectRootDefaultsKey = "moltbot.gatewayProjectRootPath"
|
||||
private static let helperName = "moltbot"
|
||||
|
||||
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/clawdbot.js").path
|
||||
let binEntry = root.appendingPathComponent("bin/moltbot.js").path
|
||||
if FileManager().isReadableFile(atPath: binEntry) { return binEntry }
|
||||
return nil
|
||||
}
|
||||
@@ -52,7 +52,7 @@ enum CommandResolver {
|
||||
return url
|
||||
}
|
||||
let fallback = FileManager().homeDirectoryForCurrentUser
|
||||
.appendingPathComponent("Projects/clawdbot")
|
||||
.appendingPathComponent("Projects/moltbot")
|
||||
if FileManager().fileExists(atPath: fallback.path) {
|
||||
return fallback
|
||||
}
|
||||
@@ -87,17 +87,17 @@ 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 clawdbotPaths = self.clawdbotManagedPaths(home: home)
|
||||
if !clawdbotPaths.isEmpty {
|
||||
extras.insert(contentsOf: clawdbotPaths, at: 1)
|
||||
let moltbotPaths = self.clawdbotManagedPaths(home: home)
|
||||
if !moltbotPaths.isEmpty {
|
||||
extras.insert(contentsOf: moltbotPaths, at: 1)
|
||||
}
|
||||
extras.insert(contentsOf: self.nodeManagerBinPaths(home: home), at: 1 + clawdbotPaths.count)
|
||||
extras.insert(contentsOf: self.nodeManagerBinPaths(home: home), at: 1 + moltbotPaths.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 clawdbotManagedPaths(home: URL) -> [String] {
|
||||
private static func moltbotManagedPaths(home: URL) -> [String] {
|
||||
let base = home.appendingPathComponent(".clawdbot")
|
||||
let bin = base.appendingPathComponent("bin")
|
||||
let nodeBin = base.appendingPathComponent("tools/node/bin")
|
||||
@@ -187,11 +187,11 @@ enum CommandResolver {
|
||||
return nil
|
||||
}
|
||||
|
||||
static func clawdbotExecutable(searchPaths: [String]? = nil) -> String? {
|
||||
static func moltbotExecutable(searchPaths: [String]? = nil) -> String? {
|
||||
self.findExecutable(named: self.helperName, searchPaths: searchPaths)
|
||||
}
|
||||
|
||||
static func projectClawdbotExecutable(projectRoot: URL? = nil) -> String? {
|
||||
static func projectMoltbotExecutable(projectRoot: URL? = nil) -> String? {
|
||||
#if DEBUG
|
||||
let root = projectRoot ?? self.projectRoot()
|
||||
let candidate = root.appendingPathComponent("node_modules/.bin").appendingPathComponent(self.helperName).path
|
||||
@@ -202,11 +202,11 @@ enum CommandResolver {
|
||||
}
|
||||
|
||||
static func nodeCliPath() -> String? {
|
||||
let candidate = self.projectRoot().appendingPathComponent("bin/clawdbot.js").path
|
||||
let candidate = self.projectRoot().appendingPathComponent("bin/moltbot.js").path
|
||||
return FileManager().isReadableFile(atPath: candidate) ? candidate : nil
|
||||
}
|
||||
|
||||
static func hasAnyClawdbotInvoker(searchPaths: [String]? = nil) -> Bool {
|
||||
static func hasAnyMoltbotInvoker(searchPaths: [String]? = nil) -> Bool {
|
||||
if self.clawdbotExecutable(searchPaths: searchPaths) != nil { return true }
|
||||
if self.findExecutable(named: "pnpm", searchPaths: searchPaths) != nil { return true }
|
||||
if self.findExecutable(named: "node", searchPaths: searchPaths) != nil,
|
||||
@@ -217,7 +217,7 @@ enum CommandResolver {
|
||||
return false
|
||||
}
|
||||
|
||||
static func clawdbotNodeCommand(
|
||||
static func moltbotNodeCommand(
|
||||
subcommand: String,
|
||||
extraArgs: [String] = [],
|
||||
defaults: UserDefaults = .standard,
|
||||
@@ -238,8 +238,8 @@ enum CommandResolver {
|
||||
switch runtimeResult {
|
||||
case let .success(runtime):
|
||||
let root = self.projectRoot()
|
||||
if let clawdbotPath = self.projectClawdbotExecutable(projectRoot: root) {
|
||||
return [clawdbotPath, subcommand] + extraArgs
|
||||
if let moltbotPath = self.projectMoltbotExecutable(projectRoot: root) {
|
||||
return [moltbotPath, subcommand] + extraArgs
|
||||
}
|
||||
|
||||
if let entry = self.gatewayEntrypoint(in: root) {
|
||||
@@ -251,14 +251,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", "clawdbot", subcommand] + extraArgs
|
||||
return [pnpm, "--silent", "moltbot", subcommand] + extraArgs
|
||||
}
|
||||
if let clawdbotPath = self.clawdbotExecutable(searchPaths: searchPaths) {
|
||||
return [clawdbotPath, subcommand] + extraArgs
|
||||
if let moltbotPath = self.clawdbotExecutable(searchPaths: searchPaths) {
|
||||
return [moltbotPath, subcommand] + extraArgs
|
||||
}
|
||||
|
||||
let missingEntry = """
|
||||
clawdbot entrypoint missing (looked for dist/index.js or bin/clawdbot.js); run pnpm build.
|
||||
moltbot entrypoint missing (looked for dist/index.js or bin/moltbot.js); run pnpm build.
|
||||
"""
|
||||
return self.errorCommand(with: missingEntry)
|
||||
|
||||
@@ -267,8 +267,8 @@ enum CommandResolver {
|
||||
}
|
||||
}
|
||||
|
||||
// Existing callers still refer to clawdbotCommand; keep it as node alias.
|
||||
static func clawdbotCommand(
|
||||
// Existing callers still refer to moltbotCommand; keep it as node alias.
|
||||
static func moltbotCommand(
|
||||
subcommand: String,
|
||||
extraArgs: [String] = [],
|
||||
defaults: UserDefaults = .standard,
|
||||
@@ -289,7 +289,7 @@ enum CommandResolver {
|
||||
guard !settings.target.isEmpty else { return nil }
|
||||
guard let parsed = self.parseSSHTarget(settings.target) else { return nil }
|
||||
|
||||
// Run the real clawdbot CLI on the remote host.
|
||||
// Run the real moltbot CLI on the remote host.
|
||||
let exportedPath = [
|
||||
"/opt/homebrew/bin",
|
||||
"/usr/local/bin",
|
||||
@@ -306,7 +306,7 @@ enum CommandResolver {
|
||||
|
||||
let projectSection = if userPRJ.isEmpty {
|
||||
"""
|
||||
DEFAULT_PRJ="$HOME/Projects/clawdbot"
|
||||
DEFAULT_PRJ="$HOME/Projects/moltbot"
|
||||
if [ -d "$DEFAULT_PRJ" ]; then
|
||||
PRJ="$DEFAULT_PRJ"
|
||||
cd "$PRJ" || { echo "Project root not found: $PRJ"; exit 127; }
|
||||
@@ -345,9 +345,9 @@ enum CommandResolver {
|
||||
CLI="";
|
||||
\(cliSection)
|
||||
\(projectSection)
|
||||
if command -v clawdbot >/dev/null 2>&1; then
|
||||
CLI="$(command -v clawdbot)"
|
||||
clawdbot \(quotedArgs);
|
||||
if command -v moltbot >/dev/null 2>&1; then
|
||||
CLI="$(command -v moltbot)"
|
||||
moltbot \(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 +355,18 @@ enum CommandResolver {
|
||||
else
|
||||
echo "Node >=22 required on remote host"; exit 127;
|
||||
fi
|
||||
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/bin/clawdbot.js" ]; then
|
||||
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/bin/moltbot.js" ]; then
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
CLI="node $PRJ/bin/clawdbot.js"
|
||||
node "$PRJ/bin/clawdbot.js" \(quotedArgs);
|
||||
CLI="node $PRJ/bin/moltbot.js"
|
||||
node "$PRJ/bin/moltbot.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 clawdbot"
|
||||
pnpm --silent clawdbot \(quotedArgs);
|
||||
CLI="pnpm --silent moltbot"
|
||||
pnpm --silent moltbot \(quotedArgs);
|
||||
else
|
||||
echo "clawdbot CLI missing on remote host"; exit 127;
|
||||
echo "moltbot CLI missing on remote host"; exit 127;
|
||||
fi
|
||||
"""
|
||||
let options: [String] = [
|
||||
@@ -394,7 +394,7 @@ enum CommandResolver {
|
||||
defaults: UserDefaults = .standard,
|
||||
configRoot: [String: Any]? = nil) -> RemoteSettings
|
||||
{
|
||||
let root = configRoot ?? ClawdbotConfigFile.loadDict()
|
||||
let root = configRoot ?? MoltbotConfigFile.loadDict()
|
||||
let mode = ConnectionModeResolver.resolve(root: root, defaults: defaults).mode
|
||||
let target = defaults.string(forKey: remoteTargetKey) ?? ""
|
||||
let identity = defaults.string(forKey: remoteIdentityKey) ?? ""
|
||||
|
||||
@@ -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/clawdbot.json using the schema-driven form.")
|
||||
: "Edit ~/.clawdbot/moltbot.json using the schema-driven form.")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
|
||||
enum ConfigStore {
|
||||
@@ -44,7 +44,7 @@ enum ConfigStore {
|
||||
if let gateway = await self.loadFromGateway() {
|
||||
return gateway
|
||||
}
|
||||
return ClawdbotConfigFile.loadDict()
|
||||
return MoltbotConfigFile.loadDict()
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@@ -63,7 +63,7 @@ enum ConfigStore {
|
||||
do {
|
||||
try await self.saveToGateway(root)
|
||||
} catch {
|
||||
ClawdbotConfigFile.saveDict(root)
|
||||
MoltbotConfigFile.saveDict(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ enum ConnectionModeResolver {
|
||||
return EffectiveConnectionMode(mode: storedMode, source: .userDefaults)
|
||||
}
|
||||
|
||||
let seen = defaults.bool(forKey: "clawdbot.onboardingSeen")
|
||||
let seen = defaults.bool(forKey: "moltbot.onboardingSeen")
|
||||
return EffectiveConnectionMode(mode: seen ? .local : .unconfigured, source: .onboarding)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,43 +2,43 @@ import Foundation
|
||||
|
||||
let launchdLabel = "com.clawdbot.mac"
|
||||
let gatewayLaunchdLabel = "com.clawdbot.gateway"
|
||||
let onboardingVersionKey = "clawdbot.onboardingVersion"
|
||||
let onboardingVersionKey = "moltbot.onboardingVersion"
|
||||
let currentOnboardingVersion = 7
|
||||
let pauseDefaultsKey = "clawdbot.pauseEnabled"
|
||||
let iconAnimationsEnabledKey = "clawdbot.iconAnimationsEnabled"
|
||||
let swabbleEnabledKey = "clawdbot.swabbleEnabled"
|
||||
let swabbleTriggersKey = "clawdbot.swabbleTriggers"
|
||||
let voiceWakeTriggerChimeKey = "clawdbot.voiceWakeTriggerChime"
|
||||
let voiceWakeSendChimeKey = "clawdbot.voiceWakeSendChime"
|
||||
let showDockIconKey = "clawdbot.showDockIcon"
|
||||
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 = "clawdbot.voiceWakeMicID"
|
||||
let voiceWakeMicNameKey = "clawdbot.voiceWakeMicName"
|
||||
let voiceWakeLocaleKey = "clawdbot.voiceWakeLocaleID"
|
||||
let voiceWakeAdditionalLocalesKey = "clawdbot.voiceWakeAdditionalLocaleIDs"
|
||||
let voicePushToTalkEnabledKey = "clawdbot.voicePushToTalkEnabled"
|
||||
let talkEnabledKey = "clawdbot.talkEnabled"
|
||||
let iconOverrideKey = "clawdbot.iconOverride"
|
||||
let connectionModeKey = "clawdbot.connectionMode"
|
||||
let remoteTargetKey = "clawdbot.remoteTarget"
|
||||
let remoteIdentityKey = "clawdbot.remoteIdentity"
|
||||
let remoteProjectRootKey = "clawdbot.remoteProjectRoot"
|
||||
let remoteCliPathKey = "clawdbot.remoteCliPath"
|
||||
let canvasEnabledKey = "clawdbot.canvasEnabled"
|
||||
let cameraEnabledKey = "clawdbot.cameraEnabled"
|
||||
let systemRunPolicyKey = "clawdbot.systemRunPolicy"
|
||||
let systemRunAllowlistKey = "clawdbot.systemRunAllowlist"
|
||||
let systemRunEnabledKey = "clawdbot.systemRunEnabled"
|
||||
let locationModeKey = "clawdbot.locationMode"
|
||||
let locationPreciseKey = "clawdbot.locationPreciseEnabled"
|
||||
let peekabooBridgeEnabledKey = "clawdbot.peekabooBridgeEnabled"
|
||||
let deepLinkKeyKey = "clawdbot.deepLinkKey"
|
||||
let modelCatalogPathKey = "clawdbot.modelCatalogPath"
|
||||
let modelCatalogReloadKey = "clawdbot.modelCatalogReload"
|
||||
let cliInstallPromptedVersionKey = "clawdbot.cliInstallPromptedVersion"
|
||||
let heartbeatsEnabledKey = "clawdbot.heartbeatsEnabled"
|
||||
let debugFileLogEnabledKey = "clawdbot.debug.fileLogEnabled"
|
||||
let appLogLevelKey = "clawdbot.debug.appLogLevel"
|
||||
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 +1,5 @@
|
||||
import ClawdbotKit
|
||||
import ClawdbotProtocol
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
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: ClawdbotProtocol.AnyCodable]
|
||||
let data: [String: MoltbotProtocol.AnyCodable]
|
||||
let summary: String?
|
||||
}
|
||||
|
||||
@@ -163,8 +163,8 @@ final class ControlChannel {
|
||||
timeoutMs: Double? = nil) async throws -> Data
|
||||
{
|
||||
do {
|
||||
let rawParams = params?.reduce(into: [String: ClawdbotKit.AnyCodable]()) {
|
||||
$0[$1.key] = ClawdbotKit.AnyCodable($1.value.base)
|
||||
let rawParams = params?.reduce(into: [String: MoltbotKit.AnyCodable]()) {
|
||||
$0[$1.key] = MoltbotKit.AnyCodable($1.value.base)
|
||||
}
|
||||
let data = try await GatewayConnection.shared.request(
|
||||
method: method,
|
||||
@@ -400,20 +400,20 @@ final class ControlChannel {
|
||||
}
|
||||
|
||||
private static func bridgeToProtocolArgs(
|
||||
_ value: ClawdbotProtocol.AnyCodable?) -> [String: ClawdbotProtocol.AnyCodable]?
|
||||
_ value: MoltbotProtocol.AnyCodable?) -> [String: MoltbotProtocol.AnyCodable]?
|
||||
{
|
||||
guard let value else { return nil }
|
||||
if let dict = value.value as? [String: ClawdbotProtocol.AnyCodable] {
|
||||
if let dict = value.value as? [String: MoltbotProtocol.AnyCodable] {
|
||||
return dict
|
||||
}
|
||||
if let dict = value.value as? [String: ClawdbotKit.AnyCodable],
|
||||
if let dict = value.value as? [String: MoltbotKit.AnyCodable],
|
||||
let data = try? JSONEncoder().encode(dict),
|
||||
let decoded = try? JSONDecoder().decode([String: ClawdbotProtocol.AnyCodable].self, from: data)
|
||||
let decoded = try? JSONDecoder().decode([String: MoltbotProtocol.AnyCodable].self, from: data)
|
||||
{
|
||||
return decoded
|
||||
}
|
||||
if let data = try? JSONEncoder().encode(value),
|
||||
let decoded = try? JSONDecoder().decode([String: ClawdbotProtocol.AnyCodable].self, from: data)
|
||||
let decoded = try? JSONDecoder().decode([String: MoltbotProtocol.AnyCodable].self, from: data)
|
||||
{
|
||||
return decoded
|
||||
}
|
||||
@@ -422,6 +422,6 @@ final class ControlChannel {
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
static let controlHeartbeat = Notification.Name("clawdbot.control.heartbeat")
|
||||
static let controlAgentEvent = Notification.Name("clawdbot.control.agent")
|
||||
static let controlHeartbeat = Notification.Name("moltbot.control.heartbeat")
|
||||
static let controlAgentEvent = Notification.Name("moltbot.control.agent")
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ClawdbotProtocol
|
||||
import MoltbotProtocol
|
||||
import Observation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import ClawdbotKit
|
||||
import ClawdbotProtocol
|
||||
import MoltbotKit
|
||||
import MoltbotProtocol
|
||||
import Foundation
|
||||
import Observation
|
||||
import OSLog
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user