refactor: rename bundle identifiers to com.clawdis
This commit is contained in:
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Breaking
|
||||||
|
- Identifiers: rename bundle IDs and internal domains to `com.clawdis.*` (macOS: `com.clawdis.mac`, iOS: `com.clawdis.ios`, Android: `com.clawdis.android`) and update the gateway LaunchAgent label to `com.clawdis.gateway`.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- Gateway: support `gateway.port` + `CLAWDIS_GATEWAY_PORT` across CLI, TUI, and macOS app.
|
- Gateway: support `gateway.port` + `CLAWDIS_GATEWAY_PORT` across CLI, TUI, and macOS app.
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.steipete.clawdis.node"
|
namespace = "com.clawdis.android"
|
||||||
compileSdk = 36
|
compileSdk = 36
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -16,7 +16,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.steipete.clawdis.node"
|
applicationId = "com.clawdis.android"
|
||||||
minSdk = 31
|
minSdk = 31
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
enum class CameraHudKind {
|
enum class CameraHudKind {
|
||||||
Photo,
|
Photo,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
@@ -18,8 +18,8 @@ import androidx.core.view.WindowInsetsControllerCompat
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.steipete.clawdis.node.ui.RootScreen
|
import com.clawdis.android.ui.RootScreen
|
||||||
import com.steipete.clawdis.node.ui.ClawdisTheme
|
import com.clawdis.android.ui.ClawdisTheme
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import com.steipete.clawdis.node.bridge.BridgeEndpoint
|
import com.clawdis.android.bridge.BridgeEndpoint
|
||||||
import com.steipete.clawdis.node.chat.OutgoingAttachment
|
import com.clawdis.android.chat.OutgoingAttachment
|
||||||
import com.steipete.clawdis.node.node.CameraCaptureManager
|
import com.clawdis.android.node.CameraCaptureManager
|
||||||
import com.steipete.clawdis.node.node.CanvasController
|
import com.clawdis.android.node.CanvasController
|
||||||
import com.steipete.clawdis.node.node.ScreenRecordManager
|
import com.clawdis.android.node.ScreenRecordManager
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
class MainViewModel(app: Application) : AndroidViewModel(app) {
|
class MainViewModel(app: Application) : AndroidViewModel(app) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
@@ -146,7 +146,7 @@ class NodeForegroundService : Service() {
|
|||||||
private const val CHANNEL_ID = "connection"
|
private const val CHANNEL_ID = "connection"
|
||||||
private const val NOTIFICATION_ID = 1
|
private const val NOTIFICATION_ID = 1
|
||||||
|
|
||||||
private const val ACTION_STOP = "com.steipete.clawdis.node.action.STOP"
|
private const val ACTION_STOP = "com.clawdis.android.action.STOP"
|
||||||
|
|
||||||
fun start(context: Context) {
|
fun start(context: Context) {
|
||||||
val intent = Intent(context, NodeForegroundService::class.java)
|
val intent = Intent(context, NodeForegroundService::class.java)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -6,27 +6,27 @@ import android.content.pm.PackageManager
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.steipete.clawdis.node.chat.ChatController
|
import com.clawdis.android.chat.ChatController
|
||||||
import com.steipete.clawdis.node.chat.ChatMessage
|
import com.clawdis.android.chat.ChatMessage
|
||||||
import com.steipete.clawdis.node.chat.ChatPendingToolCall
|
import com.clawdis.android.chat.ChatPendingToolCall
|
||||||
import com.steipete.clawdis.node.chat.ChatSessionEntry
|
import com.clawdis.android.chat.ChatSessionEntry
|
||||||
import com.steipete.clawdis.node.chat.OutgoingAttachment
|
import com.clawdis.android.chat.OutgoingAttachment
|
||||||
import com.steipete.clawdis.node.bridge.BridgeDiscovery
|
import com.clawdis.android.bridge.BridgeDiscovery
|
||||||
import com.steipete.clawdis.node.bridge.BridgeEndpoint
|
import com.clawdis.android.bridge.BridgeEndpoint
|
||||||
import com.steipete.clawdis.node.bridge.BridgePairingClient
|
import com.clawdis.android.bridge.BridgePairingClient
|
||||||
import com.steipete.clawdis.node.bridge.BridgeSession
|
import com.clawdis.android.bridge.BridgeSession
|
||||||
import com.steipete.clawdis.node.node.CameraCaptureManager
|
import com.clawdis.android.node.CameraCaptureManager
|
||||||
import com.steipete.clawdis.node.BuildConfig
|
import com.clawdis.android.BuildConfig
|
||||||
import com.steipete.clawdis.node.node.CanvasController
|
import com.clawdis.android.node.CanvasController
|
||||||
import com.steipete.clawdis.node.node.ScreenRecordManager
|
import com.clawdis.android.node.ScreenRecordManager
|
||||||
import com.steipete.clawdis.node.protocol.ClawdisCapability
|
import com.clawdis.android.protocol.ClawdisCapability
|
||||||
import com.steipete.clawdis.node.protocol.ClawdisCameraCommand
|
import com.clawdis.android.protocol.ClawdisCameraCommand
|
||||||
import com.steipete.clawdis.node.protocol.ClawdisCanvasA2UIAction
|
import com.clawdis.android.protocol.ClawdisCanvasA2UIAction
|
||||||
import com.steipete.clawdis.node.protocol.ClawdisCanvasA2UICommand
|
import com.clawdis.android.protocol.ClawdisCanvasA2UICommand
|
||||||
import com.steipete.clawdis.node.protocol.ClawdisCanvasCommand
|
import com.clawdis.android.protocol.ClawdisCanvasCommand
|
||||||
import com.steipete.clawdis.node.protocol.ClawdisScreenCommand
|
import com.clawdis.android.protocol.ClawdisScreenCommand
|
||||||
import com.steipete.clawdis.node.voice.TalkModeManager
|
import com.clawdis.android.voice.TalkModeManager
|
||||||
import com.steipete.clawdis.node.voice.VoiceWakeManager
|
import com.clawdis.android.voice.VoiceWakeManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
@file:Suppress("DEPRECATION")
|
@file:Suppress("DEPRECATION")
|
||||||
|
|
||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
enum class VoiceWakeMode(val rawValue: String) {
|
enum class VoiceWakeMode(val rawValue: String) {
|
||||||
Off("off"),
|
Off("off"),
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
object WakeWords {
|
object WakeWords {
|
||||||
const val maxWords: Int = 32
|
const val maxWords: Int = 32
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
object BonjourEscapes {
|
object BonjourEscapes {
|
||||||
fun decode(input: String): String {
|
fun decode(input: String): String {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
data class BridgeEndpoint(
|
data class BridgeEndpoint(
|
||||||
val stableId: String,
|
val stableId: String,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -11,7 +11,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import com.steipete.clawdis.node.BuildConfig
|
import com.clawdis.android.BuildConfig
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.steipete.clawdis.node.chat
|
package com.clawdis.android.chat
|
||||||
|
|
||||||
import com.steipete.clawdis.node.bridge.BridgeSession
|
import com.clawdis.android.bridge.BridgeSession
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.chat
|
package com.clawdis.android.chat
|
||||||
|
|
||||||
data class ChatMessage(
|
data class ChatMessage(
|
||||||
val id: String,
|
val id: String,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.node
|
package com.clawdis.android.node
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -20,7 +20,7 @@ import androidx.camera.video.VideoRecordEvent
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.ContextCompat.checkSelfPermission
|
import androidx.core.content.ContextCompat.checkSelfPermission
|
||||||
import androidx.core.graphics.scale
|
import androidx.core.graphics.scale
|
||||||
import com.steipete.clawdis.node.PermissionRequester
|
import com.clawdis.android.PermissionRequester
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.node
|
package com.clawdis.android.node
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
@@ -17,7 +17,7 @@ import kotlinx.serialization.json.Json
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import com.steipete.clawdis.node.BuildConfig
|
import com.clawdis.android.BuildConfig
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
class CanvasController {
|
class CanvasController {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.node
|
package com.clawdis.android.node
|
||||||
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.steipete.clawdis.node.node
|
package com.clawdis.android.node
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.display.DisplayManager
|
import android.hardware.display.DisplayManager
|
||||||
import android.media.MediaRecorder
|
import android.media.MediaRecorder
|
||||||
import android.media.projection.MediaProjectionManager
|
import android.media.projection.MediaProjectionManager
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import com.steipete.clawdis.node.ScreenCaptureRequester
|
import com.clawdis.android.ScreenCaptureRequester
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -16,13 +16,13 @@ class ScreenRecordManager(private val context: Context) {
|
|||||||
data class Payload(val payloadJson: String)
|
data class Payload(val payloadJson: String)
|
||||||
|
|
||||||
@Volatile private var screenCaptureRequester: ScreenCaptureRequester? = null
|
@Volatile private var screenCaptureRequester: ScreenCaptureRequester? = null
|
||||||
@Volatile private var permissionRequester: com.steipete.clawdis.node.PermissionRequester? = null
|
@Volatile private var permissionRequester: com.clawdis.android.PermissionRequester? = null
|
||||||
|
|
||||||
fun attachScreenCaptureRequester(requester: ScreenCaptureRequester) {
|
fun attachScreenCaptureRequester(requester: ScreenCaptureRequester) {
|
||||||
screenCaptureRequester = requester
|
screenCaptureRequester = requester
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attachPermissionRequester(requester: com.steipete.clawdis.node.PermissionRequester) {
|
fun attachPermissionRequester(requester: com.clawdis.android.PermissionRequester) {
|
||||||
permissionRequester = requester
|
permissionRequester = requester
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.protocol
|
package com.clawdis.android.protocol
|
||||||
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.protocol
|
package com.clawdis.android.protocol
|
||||||
|
|
||||||
enum class ClawdisCapability(val rawValue: String) {
|
enum class ClawdisCapability(val rawValue: String) {
|
||||||
Canvas("canvas"),
|
Canvas("canvas"),
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui
|
package com.clawdis.android.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.steipete.clawdis.node.ui
|
package com.clawdis.android.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import com.steipete.clawdis.node.MainViewModel
|
import com.clawdis.android.MainViewModel
|
||||||
import com.steipete.clawdis.node.ui.chat.ChatSheetContent
|
import com.clawdis.android.ui.chat.ChatSheetContent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatSheet(viewModel: MainViewModel) {
|
fun ChatSheet(viewModel: MainViewModel) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui
|
package com.clawdis.android.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui
|
package com.clawdis.android.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
@@ -65,8 +65,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
|||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
import androidx.compose.ui.window.PopupProperties
|
import androidx.compose.ui.window.PopupProperties
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.steipete.clawdis.node.CameraHudKind
|
import com.clawdis.android.CameraHudKind
|
||||||
import com.steipete.clawdis.node.MainViewModel
|
import com.clawdis.android.MainViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui
|
package com.clawdis.android.ui
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@@ -47,10 +47,10 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.steipete.clawdis.node.BuildConfig
|
import com.clawdis.android.BuildConfig
|
||||||
import com.steipete.clawdis.node.MainViewModel
|
import com.clawdis.android.MainViewModel
|
||||||
import com.steipete.clawdis.node.NodeForegroundService
|
import com.clawdis.android.NodeForegroundService
|
||||||
import com.steipete.clawdis.node.VoiceWakeMode
|
import com.clawdis.android.VoiceWakeMode
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsSheet(viewModel: MainViewModel) {
|
fun SettingsSheet(viewModel: MainViewModel) {
|
||||||
@@ -381,7 +381,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
|||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
val parsed = com.steipete.clawdis.node.WakeWords.parseCommaSeparated(wakeWordsText)
|
val parsed = com.clawdis.android.WakeWords.parseCommaSeparated(wakeWordsText)
|
||||||
viewModel.setWakeWords(parsed)
|
viewModel.setWakeWords(parsed)
|
||||||
},
|
},
|
||||||
enabled = isConnected,
|
enabled = isConnected,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui
|
package com.clawdis.android.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui
|
package com.clawdis.android.ui
|
||||||
|
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
import androidx.compose.animation.core.RepeatMode
|
import androidx.compose.animation.core.RepeatMode
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -38,7 +38,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.steipete.clawdis.node.chat.ChatSessionEntry
|
import com.clawdis.android.chat.ChatSessionEntry
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatComposer(
|
fun ChatComposer(
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -20,8 +20,8 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.steipete.clawdis.node.chat.ChatMessage
|
import com.clawdis.android.chat.ChatMessage
|
||||||
import com.steipete.clawdis.node.chat.ChatPendingToolCall
|
import com.clawdis.android.chat.ChatPendingToolCall
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatMessageListCard(
|
fun ChatMessageListCard(
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
@@ -31,9 +31,9 @@ import androidx.compose.ui.layout.ContentScale
|
|||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import com.steipete.clawdis.node.chat.ChatMessage
|
import com.clawdis.android.chat.ChatMessage
|
||||||
import com.steipete.clawdis.node.chat.ChatMessageContent
|
import com.clawdis.android.chat.ChatMessageContent
|
||||||
import com.steipete.clawdis.node.chat.ChatPendingToolCall
|
import com.clawdis.android.chat.ChatPendingToolCall
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.steipete.clawdis.node.chat.ChatSessionEntry
|
import com.clawdis.android.chat.ChatSessionEntry
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatSessionsDialog(
|
fun ChatSessionsDialog(
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -19,8 +19,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.steipete.clawdis.node.MainViewModel
|
import com.clawdis.android.MainViewModel
|
||||||
import com.steipete.clawdis.node.chat.OutgoingAttachment
|
import com.clawdis.android.chat.OutgoingAttachment
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import com.steipete.clawdis.node.chat.ChatSessionEntry
|
import com.clawdis.android.chat.ChatSessionEntry
|
||||||
|
|
||||||
private const val MAIN_SESSION_KEY = "main"
|
private const val MAIN_SESSION_KEY = "main"
|
||||||
private const val RECENT_WINDOW_MS = 24 * 60 * 60 * 1000L
|
private const val RECENT_WINDOW_MS = 24 * 60 * 60 * 1000L
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.voice
|
package com.clawdis.android.voice
|
||||||
|
|
||||||
import android.media.MediaDataSource
|
import android.media.MediaDataSource
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.voice
|
package com.clawdis.android.voice
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.voice
|
package com.clawdis.android.voice
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -20,7 +20,7 @@ import android.speech.tts.TextToSpeech
|
|||||||
import android.speech.tts.UtteranceProgressListener
|
import android.speech.tts.UtteranceProgressListener
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.steipete.clawdis.node.bridge.BridgeSession
|
import com.clawdis.android.bridge.BridgeSession
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.voice
|
package com.clawdis.android.voice
|
||||||
|
|
||||||
object VoiceWakeCommandExtractor {
|
object VoiceWakeCommandExtractor {
|
||||||
fun extractCommand(text: String, triggerWords: List<String>): String? {
|
fun extractCommand(text: String, triggerWords: List<String>): String? {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.voice
|
package com.clawdis.android.voice
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node
|
package com.clawdis.android
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
import io.kotest.core.spec.style.StringSpec
|
import io.kotest.core.spec.style.StringSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.bridge
|
package com.clawdis.android.bridge
|
||||||
|
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.node
|
package com.clawdis.android.node
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.node
|
package com.clawdis.android.node
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.protocol
|
package com.clawdis.android.protocol
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.protocol
|
package com.clawdis.android.protocol
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.steipete.clawdis.node.ui.chat
|
package com.clawdis.android.ui.chat
|
||||||
|
|
||||||
import com.steipete.clawdis.node.chat.ChatSessionEntry
|
import com.clawdis.android.chat.ChatSessionEntry
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.voice
|
package com.clawdis.android.voice
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.steipete.clawdis.node.voice
|
package com.clawdis.android.voice
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
@@ -14,7 +14,7 @@ actor BridgeClient {
|
|||||||
{
|
{
|
||||||
self.lineBuffer = Data()
|
self.lineBuffer = Data()
|
||||||
let connection = NWConnection(to: endpoint, using: .tcp)
|
let connection = NWConnection(to: endpoint, using: .tcp)
|
||||||
let queue = DispatchQueue(label: "com.steipete.clawdis.ios.bridge-client")
|
let queue = DispatchQueue(label: "com.clawdis.ios.bridge-client")
|
||||||
defer { connection.cancel() }
|
defer { connection.cancel() }
|
||||||
try await self.withTimeout(seconds: 8, purpose: "connect") {
|
try await self.withTimeout(seconds: 8, purpose: "connect") {
|
||||||
try await self.startAndWaitForReady(connection, queue: queue)
|
try await self.startAndWaitForReady(connection, queue: queue)
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ final class BridgeConnectionController {
|
|||||||
guard !instanceId.isEmpty else { return }
|
guard !instanceId.isEmpty else { return }
|
||||||
|
|
||||||
let token = KeychainStore.loadString(
|
let token = KeychainStore.loadString(
|
||||||
service: "com.steipete.clawdis.bridge",
|
service: "com.clawdis.bridge",
|
||||||
account: self.keychainAccount(instanceId: instanceId))?
|
account: self.keychainAccount(instanceId: instanceId))?
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
guard !token.isEmpty else { return }
|
guard !token.isEmpty else { return }
|
||||||
@@ -189,7 +189,7 @@ final class BridgeConnectionController {
|
|||||||
if !refreshed.isEmpty, refreshed != token {
|
if !refreshed.isEmpty, refreshed != token {
|
||||||
_ = KeychainStore.saveString(
|
_ = KeychainStore.saveString(
|
||||||
refreshed,
|
refreshed,
|
||||||
service: "com.steipete.clawdis.bridge",
|
service: "com.clawdis.bridge",
|
||||||
account: self.keychainAccount(instanceId: instanceId))
|
account: self.keychainAccount(instanceId: instanceId))
|
||||||
}
|
}
|
||||||
appModel.connectToBridge(endpoint: endpoint, hello: self.makeHello(token: resolvedToken))
|
appModel.connectToBridge(endpoint: endpoint, hello: self.makeHello(token: resolvedToken))
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ final class BridgeDiscoveryModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.browsers[domain] = browser
|
self.browsers[domain] = browser
|
||||||
browser.start(queue: DispatchQueue(label: "com.steipete.clawdis.ios.bridge-discovery.\(domain)"))
|
browser.start(queue: DispatchQueue(label: "com.clawdis.ios.bridge-discovery.\(domain)"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ actor BridgeSession {
|
|||||||
let params = NWParameters.tcp
|
let params = NWParameters.tcp
|
||||||
params.includePeerToPeer = true
|
params.includePeerToPeer = true
|
||||||
let connection = NWConnection(to: endpoint, using: params)
|
let connection = NWConnection(to: endpoint, using: params)
|
||||||
let queue = DispatchQueue(label: "com.steipete.clawdis.ios.bridge-session")
|
let queue = DispatchQueue(label: "com.clawdis.ios.bridge-session")
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum BridgeSettingsStore {
|
enum BridgeSettingsStore {
|
||||||
private static let bridgeService = "com.steipete.clawdis.bridge"
|
private static let bridgeService = "com.clawdis.bridge"
|
||||||
private static let nodeService = "com.steipete.clawdis.node"
|
private static let nodeService = "com.clawdis.node"
|
||||||
|
|
||||||
private static let instanceIdDefaultsKey = "node.instanceId"
|
private static let instanceIdDefaultsKey = "node.instanceId"
|
||||||
private static let preferredBridgeStableIDDefaultsKey = "bridge.preferredStableID"
|
private static let preferredBridgeStableIDDefaultsKey = "bridge.preferredStableID"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ final class ScreenRecordService: @unchecked Sendable {
|
|||||||
try? FileManager.default.removeItem(at: outURL)
|
try? FileManager.default.removeItem(at: outURL)
|
||||||
|
|
||||||
let state = CaptureState()
|
let state = CaptureState()
|
||||||
let recordQueue = DispatchQueue(label: "com.steipete.clawdis.screenrecord")
|
let recordQueue = DispatchQueue(label: "com.clawdis.screenrecord")
|
||||||
|
|
||||||
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
|
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
|
||||||
let handler: @Sendable (CMSampleBuffer, RPSampleBufferType, Error?) -> Void = { sample, type, error in
|
let handler: @Sendable (CMSampleBuffer, RPSampleBufferType, Error?) -> Void = { sample, type, error in
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ struct SettingsTab: View {
|
|||||||
do {
|
do {
|
||||||
let statusStore = self.connectStatus
|
let statusStore = self.connectStatus
|
||||||
let existing = KeychainStore.loadString(
|
let existing = KeychainStore.loadString(
|
||||||
service: "com.steipete.clawdis.bridge",
|
service: "com.clawdis.bridge",
|
||||||
account: self.keychainAccount())
|
account: self.keychainAccount())
|
||||||
let existingToken = (existing?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ?
|
let existingToken = (existing?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ?
|
||||||
existing :
|
existing :
|
||||||
@@ -381,7 +381,7 @@ struct SettingsTab: View {
|
|||||||
if !token.isEmpty, token != existingToken {
|
if !token.isEmpty, token != existingToken {
|
||||||
_ = KeychainStore.saveString(
|
_ = KeychainStore.saveString(
|
||||||
token,
|
token,
|
||||||
service: "com.steipete.clawdis.bridge",
|
service: "com.clawdis.bridge",
|
||||||
account: self.keychainAccount())
|
account: self.keychainAccount())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@ struct SettingsTab: View {
|
|||||||
do {
|
do {
|
||||||
let statusStore = self.connectStatus
|
let statusStore = self.connectStatus
|
||||||
let existing = KeychainStore.loadString(
|
let existing = KeychainStore.loadString(
|
||||||
service: "com.steipete.clawdis.bridge",
|
service: "com.clawdis.bridge",
|
||||||
account: self.keychainAccount())
|
account: self.keychainAccount())
|
||||||
let existingToken = (existing?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ?
|
let existingToken = (existing?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ?
|
||||||
existing :
|
existing :
|
||||||
@@ -455,7 +455,7 @@ struct SettingsTab: View {
|
|||||||
if !token.isEmpty, token != existingToken {
|
if !token.isEmpty, token != existingToken {
|
||||||
_ = KeychainStore.saveString(
|
_ = KeychainStore.saveString(
|
||||||
token,
|
token,
|
||||||
service: "com.steipete.clawdis.bridge",
|
service: "com.clawdis.bridge",
|
||||||
account: self.keychainAccount())
|
account: self.keychainAccount())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ final class TalkModeManager: NSObject {
|
|||||||
|
|
||||||
private var chatSubscribedSessionKeys = Set<String>()
|
private var chatSubscribedSessionKeys = Set<String>()
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "TalkMode")
|
private let logger = Logger(subsystem: "com.clawdis", category: "TalkMode")
|
||||||
|
|
||||||
func attachBridge(_ bridge: BridgeSession) {
|
func attachBridge(_ bridge: BridgeSession) {
|
||||||
self.bridge = bridge
|
self.bridge = bridge
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Testing
|
|||||||
|
|
||||||
@Suite struct BridgeClientTests {
|
@Suite struct BridgeClientTests {
|
||||||
private final class LineServer: @unchecked Sendable {
|
private final class LineServer: @unchecked Sendable {
|
||||||
private let queue = DispatchQueue(label: "com.steipete.clawdis.tests.bridge-client-server")
|
private let queue = DispatchQueue(label: "com.clawdis.tests.bridge-client-server")
|
||||||
private let listener: NWListener
|
private let listener: NWListener
|
||||||
private var connection: NWConnection?
|
private var connection: NWConnection?
|
||||||
private var buffer = Data()
|
private var buffer = Data()
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ private struct KeychainEntry: Hashable {
|
|||||||
let account: String
|
let account: String
|
||||||
}
|
}
|
||||||
|
|
||||||
private let bridgeService = "com.steipete.clawdis.bridge"
|
private let bridgeService = "com.clawdis.bridge"
|
||||||
private let nodeService = "com.steipete.clawdis.node"
|
private let nodeService = "com.clawdis.node"
|
||||||
private let instanceIdEntry = KeychainEntry(service: nodeService, account: "instanceId")
|
private let instanceIdEntry = KeychainEntry(service: nodeService, account: "instanceId")
|
||||||
private let preferredBridgeEntry = KeychainEntry(service: bridgeService, account: "preferredStableID")
|
private let preferredBridgeEntry = KeychainEntry(service: bridgeService, account: "preferredStableID")
|
||||||
private let lastBridgeEntry = KeychainEntry(service: bridgeService, account: "lastDiscoveredStableID")
|
private let lastBridgeEntry = KeychainEntry(service: bridgeService, account: "lastDiscoveredStableID")
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ private struct KeychainEntry: Hashable {
|
|||||||
let account: String
|
let account: String
|
||||||
}
|
}
|
||||||
|
|
||||||
private let bridgeService = "com.steipete.clawdis.bridge"
|
private let bridgeService = "com.clawdis.bridge"
|
||||||
private let nodeService = "com.steipete.clawdis.node"
|
private let nodeService = "com.clawdis.node"
|
||||||
private let instanceIdEntry = KeychainEntry(service: nodeService, account: "instanceId")
|
private let instanceIdEntry = KeychainEntry(service: nodeService, account: "instanceId")
|
||||||
private let preferredBridgeEntry = KeychainEntry(service: bridgeService, account: "preferredStableID")
|
private let preferredBridgeEntry = KeychainEntry(service: bridgeService, account: "preferredStableID")
|
||||||
private let lastBridgeEntry = KeychainEntry(service: bridgeService, account: "lastDiscoveredStableID")
|
private let lastBridgeEntry = KeychainEntry(service: bridgeService, account: "lastDiscoveredStableID")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Testing
|
|||||||
|
|
||||||
@Suite struct KeychainStoreTests {
|
@Suite struct KeychainStoreTests {
|
||||||
@Test func saveLoadUpdateDeleteRoundTrip() {
|
@Test func saveLoadUpdateDeleteRoundTrip() {
|
||||||
let service = "com.steipete.clawdis.tests.\(UUID().uuidString)"
|
let service = "com.clawdis.tests.\(UUID().uuidString)"
|
||||||
let account = "value"
|
let account = "value"
|
||||||
|
|
||||||
#expect(KeychainStore.delete(service: service, account: account))
|
#expect(KeychainStore.delete(service: service, account: account))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
app_identifier("com.steipete.clawdis.ios")
|
app_identifier("com.clawdis.ios")
|
||||||
|
|
||||||
# Auth is expected via App Store Connect API key.
|
# Auth is expected via App Store Connect API key.
|
||||||
# Provide either:
|
# Provide either:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: Clawdis
|
name: Clawdis
|
||||||
options:
|
options:
|
||||||
bundleIdPrefix: com.steipete.clawdis
|
bundleIdPrefix: com.clawdis
|
||||||
deploymentTarget:
|
deploymentTarget:
|
||||||
iOS: "17.0"
|
iOS: "17.0"
|
||||||
xcodeVersion: "16.0"
|
xcodeVersion: "16.0"
|
||||||
@@ -65,8 +65,8 @@ targets:
|
|||||||
CODE_SIGN_IDENTITY: "Apple Development"
|
CODE_SIGN_IDENTITY: "Apple Development"
|
||||||
CODE_SIGN_STYLE: Manual
|
CODE_SIGN_STYLE: Manual
|
||||||
DEVELOPMENT_TEAM: Y5PE65HELJ
|
DEVELOPMENT_TEAM: Y5PE65HELJ
|
||||||
PRODUCT_BUNDLE_IDENTIFIER: com.steipete.clawdis.ios
|
PRODUCT_BUNDLE_IDENTIFIER: com.clawdis.ios
|
||||||
PROVISIONING_PROFILE_SPECIFIER: "com.steipete.clawdis.ios Development"
|
PROVISIONING_PROFILE_SPECIFIER: "com.clawdis.ios Development"
|
||||||
SWIFT_VERSION: "6.0"
|
SWIFT_VERSION: "6.0"
|
||||||
info:
|
info:
|
||||||
path: Sources/Info.plist
|
path: Sources/Info.plist
|
||||||
@@ -98,7 +98,7 @@ targets:
|
|||||||
product: SwabbleKit
|
product: SwabbleKit
|
||||||
settings:
|
settings:
|
||||||
base:
|
base:
|
||||||
PRODUCT_BUNDLE_IDENTIFIER: com.steipete.clawdis.ios.tests
|
PRODUCT_BUNDLE_IDENTIFIER: com.clawdis.ios.tests
|
||||||
SWIFT_VERSION: "6.0"
|
SWIFT_VERSION: "6.0"
|
||||||
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/Clawdis.app/Clawdis"
|
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/Clawdis.app/Clawdis"
|
||||||
BUNDLE_LOADER: "$(TEST_HOST)"
|
BUNDLE_LOADER: "$(TEST_HOST)"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Foundation
|
|||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
enum AgentWorkspace {
|
enum AgentWorkspace {
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "workspace")
|
private static let logger = Logger(subsystem: "com.clawdis", category: "workspace")
|
||||||
static let agentsFilename = "AGENTS.md"
|
static let agentsFilename = "AGENTS.md"
|
||||||
static let soulFilename = "SOUL.md"
|
static let soulFilename = "SOUL.md"
|
||||||
static let identityFilename = "IDENTITY.md"
|
static let identityFilename = "IDENTITY.md"
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ enum AnthropicAuthResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum AnthropicOAuth {
|
enum AnthropicOAuth {
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "anthropic-oauth")
|
private static let logger = Logger(subsystem: "com.clawdis", category: "anthropic-oauth")
|
||||||
|
|
||||||
private static let clientId = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
|
private static let clientId = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
|
||||||
private static let authorizeURL = URL(string: "https://claude.ai/oauth/authorize")!
|
private static let authorizeURL = URL(string: "https://claude.ai/oauth/authorize")!
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ actor BridgeConnectionHandler {
|
|||||||
private let logger: Logger
|
private let logger: Logger
|
||||||
private let decoder = JSONDecoder()
|
private let decoder = JSONDecoder()
|
||||||
private let encoder = JSONEncoder()
|
private let encoder = JSONEncoder()
|
||||||
private let queue = DispatchQueue(label: "com.steipete.clawdis.bridge.connection")
|
private let queue = DispatchQueue(label: "com.clawdis.bridge.connection")
|
||||||
|
|
||||||
private var buffer = Data()
|
private var buffer = Data()
|
||||||
private var isAuthenticated = false
|
private var isAuthenticated = false
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import OSLog
|
|||||||
actor BridgeServer {
|
actor BridgeServer {
|
||||||
static let shared = BridgeServer()
|
static let shared = BridgeServer()
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "bridge")
|
private let logger = Logger(subsystem: "com.clawdis", category: "bridge")
|
||||||
private var listener: NWListener?
|
private var listener: NWListener?
|
||||||
private var isRunning = false
|
private var isRunning = false
|
||||||
private var store: PairedNodesStore?
|
private var store: PairedNodesStore?
|
||||||
@@ -42,7 +42,7 @@ actor BridgeServer {
|
|||||||
Task { await self.handleListenerState(state) }
|
Task { await self.handleListenerState(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
listener.start(queue: DispatchQueue(label: "com.steipete.clawdis.bridge"))
|
listener.start(queue: DispatchQueue(label: "com.clawdis.bridge"))
|
||||||
self.listener = listener
|
self.listener = listener
|
||||||
} catch {
|
} catch {
|
||||||
self.logger.error("bridge start failed: \(error.localizedDescription, privacy: .public)")
|
self.logger.error("bridge start failed: \(error.localizedDescription, privacy: .public)")
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ actor CameraCaptureService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "camera")
|
private let logger = Logger(subsystem: "com.clawdis", category: "camera")
|
||||||
|
|
||||||
func listDevices() -> [CameraDeviceInfo] {
|
func listDevices() -> [CameraDeviceInfo] {
|
||||||
Self.availableCameras().map { device in
|
Self.availableCameras().map { device in
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ final class CanvasFileWatcher: @unchecked Sendable {
|
|||||||
|
|
||||||
init(url: URL, onChange: @escaping () -> Void) {
|
init(url: URL, onChange: @escaping () -> Void) {
|
||||||
self.url = url
|
self.url = url
|
||||||
self.queue = DispatchQueue(label: "com.steipete.clawdis.canvaswatcher")
|
self.queue = DispatchQueue(label: "com.clawdis.canvaswatcher")
|
||||||
self.onChange = onChange
|
self.onChange = onChange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import OSLog
|
|||||||
final class CanvasManager {
|
final class CanvasManager {
|
||||||
static let shared = CanvasManager()
|
static let shared = CanvasManager()
|
||||||
|
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "CanvasManager")
|
private static let logger = Logger(subsystem: "com.clawdis", category: "CanvasManager")
|
||||||
|
|
||||||
private var panelController: CanvasWindowController?
|
private var panelController: CanvasWindowController?
|
||||||
private var panelSessionKey: String?
|
private var panelSessionKey: String?
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Foundation
|
|||||||
import OSLog
|
import OSLog
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
private let canvasLogger = Logger(subsystem: "com.steipete.clawdis", category: "Canvas")
|
private let canvasLogger = Logger(subsystem: "com.clawdis", category: "Canvas")
|
||||||
|
|
||||||
final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||||
private let root: URL
|
private let root: URL
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
let canvasWindowLogger = Logger(subsystem: "com.steipete.clawdis", category: "Canvas")
|
let canvasWindowLogger = Logger(subsystem: "com.clawdis", category: "Canvas")
|
||||||
|
|
||||||
enum CanvasLayout {
|
enum CanvasLayout {
|
||||||
static let panelSize = NSSize(width: 520, height: 680)
|
static let panelSize = NSSize(width: 520, height: 680)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ClawdisConfigFile {
|
enum ClawdisConfigFile {
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "config")
|
private static let logger = Logger(subsystem: "com.clawdis", category: "config")
|
||||||
|
|
||||||
static func url() -> URL {
|
static func url() -> URL {
|
||||||
ClawdisPaths.configURL
|
ClawdisPaths.configURL
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import OSLog
|
|||||||
final class ConnectionModeCoordinator {
|
final class ConnectionModeCoordinator {
|
||||||
static let shared = ConnectionModeCoordinator()
|
static let shared = ConnectionModeCoordinator()
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "connection")
|
private let logger = Logger(subsystem: "com.clawdis", category: "connection")
|
||||||
|
|
||||||
/// Apply the requested connection mode by starting/stopping local gateway,
|
/// Apply the requested connection mode by starting/stopping local gateway,
|
||||||
/// managing the control-channel SSH tunnel, and cleaning up chat windows/panels.
|
/// managing the control-channel SSH tunnel, and cleaning up chat windows/panels.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
let launchdLabel = "com.steipete.clawdis"
|
let launchdLabel = "com.clawdis.mac"
|
||||||
let gatewayLaunchdLabel = "com.steipete.clawdis.gateway"
|
let gatewayLaunchdLabel = "com.clawdis.gateway"
|
||||||
let onboardingVersionKey = "clawdis.onboardingVersion"
|
let onboardingVersionKey = "clawdis.onboardingVersion"
|
||||||
let currentOnboardingVersion = 7
|
let currentOnboardingVersion = 7
|
||||||
let pauseDefaultsKey = "clawdis.pauseEnabled"
|
let pauseDefaultsKey = "clawdis.pauseEnabled"
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ final class ControlChannel {
|
|||||||
}
|
}
|
||||||
private(set) var lastPingMs: Double?
|
private(set) var lastPingMs: Double?
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "control")
|
private let logger = Logger(subsystem: "com.clawdis", category: "control")
|
||||||
|
|
||||||
private var eventTask: Task<Void, Never>?
|
private var eventTask: Task<Void, Never>?
|
||||||
private var recoveryTask: Task<Void, Never>?
|
private var recoveryTask: Task<Void, Never>?
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ final class CronJobsStore {
|
|||||||
var lastError: String?
|
var lastError: String?
|
||||||
var statusMessage: String?
|
var statusMessage: String?
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "cron.ui")
|
private let logger = Logger(subsystem: "com.clawdis", category: "cron.ui")
|
||||||
private var refreshTask: Task<Void, Never>?
|
private var refreshTask: Task<Void, Never>?
|
||||||
private var runsTask: Task<Void, Never>?
|
private var runsTask: Task<Void, Never>?
|
||||||
private var eventTask: Task<Void, Never>?
|
private var eventTask: Task<Void, Never>?
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Foundation
|
|||||||
import OSLog
|
import OSLog
|
||||||
import Security
|
import Security
|
||||||
|
|
||||||
private let deepLinkLogger = Logger(subsystem: "com.steipete.clawdis", category: "DeepLink")
|
private let deepLinkLogger = Logger(subsystem: "com.clawdis", category: "DeepLink")
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class DeepLinkHandler {
|
final class DeepLinkHandler {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ final class DockIconManager: NSObject, @unchecked Sendable {
|
|||||||
static let shared = DockIconManager()
|
static let shared = DockIconManager()
|
||||||
|
|
||||||
private var windowsObservation: NSKeyValueObservation?
|
private var windowsObservation: NSKeyValueObservation?
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "DockIconManager")
|
private let logger = Logger(subsystem: "com.clawdis", category: "DockIconManager")
|
||||||
|
|
||||||
override private init() {
|
override private init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ struct WebSocketSessionBox: @unchecked Sendable {
|
|||||||
private typealias ProtoAnyCodable = ClawdisProtocol.AnyCodable
|
private typealias ProtoAnyCodable = ClawdisProtocol.AnyCodable
|
||||||
|
|
||||||
actor GatewayChannelActor {
|
actor GatewayChannelActor {
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "gateway")
|
private let logger = Logger(subsystem: "com.clawdis", category: "gateway")
|
||||||
private var task: WebSocketTaskBox?
|
private var task: WebSocketTaskBox?
|
||||||
private var pending: [String: CheckedContinuation<GatewayFrame, Error>] = [:]
|
private var pending: [String: CheckedContinuation<GatewayFrame, Error>] = [:]
|
||||||
private var connected = false
|
private var connected = false
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import ClawdisProtocol
|
|||||||
import Foundation
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
private let gatewayConnectionLogger = Logger(subsystem: "com.steipete.clawdis", category: "gateway.connection")
|
private let gatewayConnectionLogger = Logger(subsystem: "com.clawdis", category: "gateway.connection")
|
||||||
|
|
||||||
enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
|
enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
|
||||||
case last
|
case last
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ final class GatewayDiscoveryModel {
|
|||||||
private var localIdentity: LocalIdentity
|
private var localIdentity: LocalIdentity
|
||||||
private var resolvedTXTByID: [String: [String: String]] = [:]
|
private var resolvedTXTByID: [String: [String: String]] = [:]
|
||||||
private var pendingTXTResolvers: [String: GatewayTXTResolver] = [:]
|
private var pendingTXTResolvers: [String: GatewayTXTResolver] = [:]
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "gateway-discovery")
|
private let logger = Logger(subsystem: "com.clawdis", category: "gateway-discovery")
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.localIdentity = Self.buildLocalIdentityFast()
|
self.localIdentity = Self.buildLocalIdentityFast()
|
||||||
@@ -69,7 +69,7 @@ final class GatewayDiscoveryModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.browsers[domain] = browser
|
self.browsers[domain] = browser
|
||||||
browser.start(queue: DispatchQueue(label: "com.steipete.clawdis.macos.gateway-discovery.\(domain)"))
|
browser.start(queue: DispatchQueue(label: "com.clawdis.macos.gateway-discovery.\(domain)"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ actor GatewayEndpointStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let deps: Deps
|
private let deps: Deps
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "gateway-endpoint")
|
private let logger = Logger(subsystem: "com.clawdis", category: "gateway-endpoint")
|
||||||
|
|
||||||
private var state: GatewayEndpointState
|
private var state: GatewayEndpointState
|
||||||
private var subscribers: [UUID: AsyncStream<GatewayEndpointState>.Continuation] = [:]
|
private var subscribers: [UUID: AsyncStream<GatewayEndpointState>.Continuation] = [:]
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ struct GatewayCommandResolution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GatewayEnvironment {
|
enum GatewayEnvironment {
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "gateway.env")
|
private static let logger = Logger(subsystem: "com.clawdis", category: "gateway.env")
|
||||||
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
|
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
|
||||||
|
|
||||||
static func bundledGatewayExecutable() -> String? {
|
static func bundledGatewayExecutable() -> String? {
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum GatewayLaunchAgentManager {
|
enum GatewayLaunchAgentManager {
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "gateway.launchd")
|
private static let logger = Logger(subsystem: "com.clawdis", category: "gateway.launchd")
|
||||||
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
|
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
|
||||||
|
private static let legacyGatewayLaunchdLabel = "com.steipete.clawdis.gateway"
|
||||||
|
|
||||||
private static var plistURL: URL {
|
private static var plistURL: URL {
|
||||||
FileManager.default.homeDirectoryForCurrentUser
|
FileManager.default.homeDirectoryForCurrentUser
|
||||||
.appendingPathComponent("Library/LaunchAgents/\(gatewayLaunchdLabel).plist")
|
.appendingPathComponent("Library/LaunchAgents/\(gatewayLaunchdLabel).plist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static var legacyPlistURL: URL {
|
||||||
|
FileManager.default.homeDirectoryForCurrentUser
|
||||||
|
.appendingPathComponent("Library/LaunchAgents/\(legacyGatewayLaunchdLabel).plist")
|
||||||
|
}
|
||||||
|
|
||||||
private static func gatewayExecutablePath(bundlePath: String) -> String {
|
private static func gatewayExecutablePath(bundlePath: String) -> String {
|
||||||
"\(bundlePath)/Contents/Resources/Relay/clawdis"
|
"\(bundlePath)/Contents/Resources/Relay/clawdis"
|
||||||
}
|
}
|
||||||
@@ -45,6 +51,8 @@ enum GatewayLaunchAgentManager {
|
|||||||
|
|
||||||
static func set(enabled: Bool, bundlePath: String, port: Int) async -> String? {
|
static func set(enabled: Bool, bundlePath: String, port: Int) async -> String? {
|
||||||
if enabled {
|
if enabled {
|
||||||
|
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(legacyGatewayLaunchdLabel)"])
|
||||||
|
try? FileManager.default.removeItem(at: self.legacyPlistURL)
|
||||||
let gatewayBin = self.gatewayExecutablePath(bundlePath: bundlePath)
|
let gatewayBin = self.gatewayExecutablePath(bundlePath: bundlePath)
|
||||||
guard FileManager.default.isExecutableFile(atPath: gatewayBin) else {
|
guard FileManager.default.isExecutableFile(atPath: gatewayBin) else {
|
||||||
self.logger.error("launchd enable failed: gateway missing at \(gatewayBin)")
|
self.logger.error("launchd enable failed: gateway missing at \(gatewayBin)")
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ final class GatewayProcessManager {
|
|||||||
private var environmentRefreshTask: Task<Void, Never>?
|
private var environmentRefreshTask: Task<Void, Never>?
|
||||||
private var lastEnvironmentRefresh: Date?
|
private var lastEnvironmentRefresh: Date?
|
||||||
private var logRefreshTask: Task<Void, Never>?
|
private var logRefreshTask: Task<Void, Never>?
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "gateway.process")
|
private let logger = Logger(subsystem: "com.clawdis", category: "gateway.process")
|
||||||
|
|
||||||
private let logLimit = 20000 // characters to keep in-memory
|
private let logLimit = 20000 // characters to keep in-memory
|
||||||
private let environmentRefreshMinInterval: TimeInterval = 30
|
private let environmentRefreshMinInterval: TimeInterval = 30
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ enum HealthState: Equatable {
|
|||||||
final class HealthStore {
|
final class HealthStore {
|
||||||
static let shared = HealthStore()
|
static let shared = HealthStore()
|
||||||
|
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "health")
|
private static let logger = Logger(subsystem: "com.clawdis", category: "health")
|
||||||
|
|
||||||
private(set) var snapshot: HealthSnapshot?
|
private(set) var snapshot: HealthSnapshot?
|
||||||
private(set) var lastSuccess: Date?
|
private(set) var lastSuccess: Date?
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Darwin
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum InstanceIdentity {
|
enum InstanceIdentity {
|
||||||
private static let suiteName = "com.steipete.clawdis.shared"
|
private static let suiteName = "com.clawdis.shared"
|
||||||
private static let instanceIdKey = "instanceId"
|
private static let instanceIdKey = "instanceId"
|
||||||
|
|
||||||
private static var defaults: UserDefaults {
|
private static var defaults: UserDefaults {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ final class InstancesStore {
|
|||||||
var statusMessage: String?
|
var statusMessage: String?
|
||||||
var isLoading = false
|
var isLoading = false
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "instances")
|
private let logger = Logger(subsystem: "com.clawdis", category: "instances")
|
||||||
private var task: Task<Void, Never>?
|
private var task: Task<Void, Never>?
|
||||||
private let interval: TimeInterval = 30
|
private let interval: TimeInterval = 30
|
||||||
private var eventTask: Task<Void, Never>?
|
private var eventTask: Task<Void, Never>?
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum LaunchAgentManager {
|
enum LaunchAgentManager {
|
||||||
|
private static let legacyLaunchdLabel = "com.steipete.clawdis"
|
||||||
private static var plistURL: URL {
|
private static var plistURL: URL {
|
||||||
FileManager.default.homeDirectoryForCurrentUser
|
FileManager.default.homeDirectoryForCurrentUser
|
||||||
.appendingPathComponent("Library/LaunchAgents/com.steipete.clawdis.plist")
|
.appendingPathComponent("Library/LaunchAgents/com.clawdis.mac.plist")
|
||||||
|
}
|
||||||
|
private static var legacyPlistURL: URL {
|
||||||
|
FileManager.default.homeDirectoryForCurrentUser
|
||||||
|
.appendingPathComponent("Library/LaunchAgents/\(legacyLaunchdLabel).plist")
|
||||||
}
|
}
|
||||||
|
|
||||||
static func status() async -> Bool {
|
static func status() async -> Bool {
|
||||||
@@ -14,6 +19,8 @@ enum LaunchAgentManager {
|
|||||||
|
|
||||||
static func set(enabled: Bool, bundlePath: String) async {
|
static func set(enabled: Bool, bundlePath: String) async {
|
||||||
if enabled {
|
if enabled {
|
||||||
|
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(legacyLaunchdLabel)"])
|
||||||
|
try? FileManager.default.removeItem(at: self.legacyPlistURL)
|
||||||
self.writePlist(bundlePath: bundlePath)
|
self.writePlist(bundlePath: bundlePath)
|
||||||
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(launchdLabel)"])
|
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(launchdLabel)"])
|
||||||
_ = await self.runLaunchctl(["bootstrap", "gui/\(getuid())", self.plistURL.path])
|
_ = await self.runLaunchctl(["bootstrap", "gui/\(getuid())", self.plistURL.path])
|
||||||
@@ -32,7 +39,7 @@ enum LaunchAgentManager {
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>com.steipete.clawdis</string>
|
<string>com.clawdis.mac</string>
|
||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
<array>
|
<array>
|
||||||
<string>\(bundlePath)/Contents/MacOS/Clawdis</string>
|
<string>\(bundlePath)/Contents/MacOS/Clawdis</string>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ enum ClawdisLogging {
|
|||||||
|
|
||||||
static func parseLabel(_ label: String) -> (String, String) {
|
static func parseLabel(_ label: String) -> (String, String) {
|
||||||
guard let range = label.range(of: Self.labelSeparator) else {
|
guard let range = label.range(of: Self.labelSeparator) else {
|
||||||
return ("com.steipete.clawdis", label)
|
return ("com.clawdis", label)
|
||||||
}
|
}
|
||||||
let subsystem = String(label[..<range.lowerBound])
|
let subsystem = String(label[..<range.lowerBound])
|
||||||
let category = String(label[range.upperBound...])
|
let category = String(label[range.upperBound...])
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user