chore: rename project to clawdbot

This commit is contained in:
Peter Steinberger
2026-01-04 14:32:47 +00:00
parent d48dc71fa4
commit 246adaa119
841 changed files with 4590 additions and 4328 deletions

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import Foundation
import Network
@@ -14,7 +14,7 @@ actor BridgeClient {
{
self.lineBuffer = Data()
let connection = NWConnection(to: endpoint, using: .tcp)
let queue = DispatchQueue(label: "com.clawdis.ios.bridge-client")
let queue = DispatchQueue(label: "com.clawdbot.ios.bridge-client")
defer { connection.cancel() }
try await self.withTimeout(seconds: 8, purpose: "connect") {
try await self.startAndWaitForReady(connection, queue: queue)

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import Darwin
import Foundation
import Network
@@ -99,7 +99,7 @@ final class BridgeConnectionController {
guard !instanceId.isEmpty else { return }
let token = KeychainStore.loadString(
service: "com.clawdis.bridge",
service: "com.clawdbot.bridge",
account: self.keychainAccount(instanceId: instanceId))?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !token.isEmpty else { return }
@@ -189,7 +189,7 @@ final class BridgeConnectionController {
if !refreshed.isEmpty, refreshed != token {
_ = KeychainStore.saveString(
refreshed,
service: "com.clawdis.bridge",
service: "com.clawdbot.bridge",
account: self.keychainAccount(instanceId: instanceId))
}
appModel.connectToBridge(endpoint: endpoint, hello: self.makeHello(token: resolvedToken))
@@ -217,46 +217,46 @@ final class BridgeConnectionController {
}
private func currentCaps() -> [String] {
var caps = [ClawdisCapability.canvas.rawValue, ClawdisCapability.screen.rawValue]
var caps = [ClawdbotCapability.canvas.rawValue, ClawdbotCapability.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(ClawdisCapability.camera.rawValue) }
if cameraEnabled { caps.append(ClawdbotCapability.camera.rawValue) }
let voiceWakeEnabled = UserDefaults.standard.bool(forKey: VoiceWakePreferences.enabledKey)
if voiceWakeEnabled { caps.append(ClawdisCapability.voiceWake.rawValue) }
if voiceWakeEnabled { caps.append(ClawdbotCapability.voiceWake.rawValue) }
let locationModeRaw = UserDefaults.standard.string(forKey: "location.enabledMode") ?? "off"
let locationMode = ClawdisLocationMode(rawValue: locationModeRaw) ?? .off
if locationMode != .off { caps.append(ClawdisCapability.location.rawValue) }
let locationMode = ClawdbotLocationMode(rawValue: locationModeRaw) ?? .off
if locationMode != .off { caps.append(ClawdbotCapability.location.rawValue) }
return caps
}
private func currentCommands() -> [String] {
var commands: [String] = [
ClawdisCanvasCommand.present.rawValue,
ClawdisCanvasCommand.hide.rawValue,
ClawdisCanvasCommand.navigate.rawValue,
ClawdisCanvasCommand.evalJS.rawValue,
ClawdisCanvasCommand.snapshot.rawValue,
ClawdisCanvasA2UICommand.push.rawValue,
ClawdisCanvasA2UICommand.pushJSONL.rawValue,
ClawdisCanvasA2UICommand.reset.rawValue,
ClawdisScreenCommand.record.rawValue,
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,
]
let caps = Set(self.currentCaps())
if caps.contains(ClawdisCapability.camera.rawValue) {
commands.append(ClawdisCameraCommand.list.rawValue)
commands.append(ClawdisCameraCommand.snap.rawValue)
commands.append(ClawdisCameraCommand.clip.rawValue)
if caps.contains(ClawdbotCapability.camera.rawValue) {
commands.append(ClawdbotCameraCommand.list.rawValue)
commands.append(ClawdbotCameraCommand.snap.rawValue)
commands.append(ClawdbotCameraCommand.clip.rawValue)
}
if caps.contains(ClawdisCapability.location.rawValue) {
commands.append(ClawdisLocationCommand.get.rawValue)
if caps.contains(ClawdbotCapability.location.rawValue) {
commands.append(ClawdbotLocationCommand.get.rawValue)
}
return commands

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import Foundation
import Network
import Observation
@@ -51,11 +51,11 @@ final class BridgeDiscoveryModel {
if !self.browsers.isEmpty { return }
self.appendDebugLog("start()")
for domain in ClawdisBonjour.bridgeServiceDomains {
for domain in ClawdbotBonjour.bridgeServiceDomains {
let params = NWParameters.tcp
params.includePeerToPeer = true
let browser = NWBrowser(
for: .bonjour(type: ClawdisBonjour.bridgeServiceType, domain: domain),
for: .bonjour(type: ClawdbotBonjour.bridgeServiceType, domain: domain),
using: params)
browser.stateUpdateHandler = { [weak self] state in
@@ -102,7 +102,7 @@ final class BridgeDiscoveryModel {
}
self.browsers[domain] = browser
browser.start(queue: DispatchQueue(label: "com.clawdis.ios.bridge-discovery.\(domain)"))
browser.start(queue: DispatchQueue(label: "com.clawdbot.ios.bridge-discovery.\(domain)"))
}
}
@@ -200,7 +200,7 @@ final class BridgeDiscoveryModel {
private static func prettifyInstanceName(_ decodedName: String) -> String {
let normalized = decodedName.split(whereSeparator: \.isWhitespace).joined(separator: " ")
let stripped = normalized.replacingOccurrences(of: " (Clawdis)", with: "")
let stripped = normalized.replacingOccurrences(of: " (Clawdbot)", with: "")
.replacingOccurrences(of: #"\s+\(\d+\)$"#, with: "", options: .regularExpression)
return stripped.trimmingCharacters(in: .whitespacesAndNewlines)
}

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import Foundation
import Network

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import Foundation
import Network
@@ -78,7 +78,7 @@ actor BridgeSession {
let params = NWParameters.tcp
params.includePeerToPeer = true
let connection = NWConnection(to: endpoint, using: params)
let queue = DispatchQueue(label: "com.clawdis.ios.bridge-session")
let queue = DispatchQueue(label: "com.clawdbot.ios.bridge-session")
self.connection = connection
self.queue = queue

View File

@@ -1,8 +1,8 @@
import Foundation
enum BridgeSettingsStore {
private static let bridgeService = "com.clawdis.bridge"
private static let nodeService = "com.clawdis.node"
private static let bridgeService = "com.clawdbot.bridge"
private static let nodeService = "com.clawdbot.node"
private static let instanceIdDefaultsKey = "node.instanceId"
private static let preferredBridgeStableIDDefaultsKey = "bridge.preferredStableID"

View File

@@ -1,5 +1,5 @@
import AVFoundation
import ClawdisKit
import ClawdbotKit
import Foundation
actor CameraController {
@@ -36,7 +36,7 @@ actor CameraController {
}
}
func snap(params: ClawdisCameraSnapParams) async throws -> (
func snap(params: ClawdbotCameraSnapParams) async throws -> (
format: String,
base64: String,
width: Int,
@@ -109,7 +109,7 @@ actor CameraController {
height: res.heightPx)
}
func clip(params: ClawdisCameraClipParams) async throws -> (
func clip(params: ClawdbotCameraClipParams) async throws -> (
format: String,
base64: String,
durationMs: Int,
@@ -161,9 +161,9 @@ actor CameraController {
await Self.warmUpCaptureSession()
let movURL = FileManager.default.temporaryDirectory
.appendingPathComponent("clawdis-camera-\(UUID().uuidString).mov")
.appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mov")
let mp4URL = FileManager.default.temporaryDirectory
.appendingPathComponent("clawdis-camera-\(UUID().uuidString).mp4")
.appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mp4")
defer {
try? FileManager.default.removeItem(at: movURL)
@@ -228,7 +228,7 @@ actor CameraController {
}
private nonisolated static func pickCamera(
facing: ClawdisCameraFacing,
facing: ClawdbotCameraFacing,
deviceId: String?) -> AVCaptureDevice?
{
if let deviceId, !deviceId.isEmpty {

View File

@@ -1,15 +1,15 @@
import ClawdisChatUI
import ClawdbotChatUI
import SwiftUI
struct ChatSheet: View {
@Environment(\.dismiss) private var dismiss
@State private var viewModel: ClawdisChatViewModel
@State private var viewModel: ClawdbotChatViewModel
private let userAccent: Color?
init(bridge: BridgeSession, sessionKey: String = "main", userAccent: Color? = nil) {
let transport = IOSBridgeChatTransport(bridge: bridge)
self._viewModel = State(
initialValue: ClawdisChatViewModel(
initialValue: ClawdbotChatViewModel(
sessionKey: sessionKey,
transport: transport))
self.userAccent = userAccent
@@ -17,7 +17,7 @@ struct ChatSheet: View {
var body: some View {
NavigationStack {
ClawdisChatView(
ClawdbotChatView(
viewModel: self.viewModel,
showsSessionSwitcher: true,
userAccent: self.userAccent)

View File

@@ -1,8 +1,8 @@
import ClawdisChatUI
import ClawdisKit
import ClawdbotChatUI
import ClawdbotKit
import Foundation
struct IOSBridgeChatTransport: ClawdisChatTransport, Sendable {
struct IOSBridgeChatTransport: ClawdbotChatTransport, Sendable {
private let bridge: BridgeSession
init(bridge: BridgeSession) {
@@ -19,7 +19,7 @@ struct IOSBridgeChatTransport: ClawdisChatTransport, Sendable {
_ = try await self.bridge.request(method: "chat.abort", paramsJSON: json, timeoutSeconds: 10)
}
func listSessions(limit: Int?) async throws -> ClawdisChatSessionsListResponse {
func listSessions(limit: Int?) async throws -> ClawdbotChatSessionsListResponse {
struct Params: Codable {
var includeGlobal: Bool
var includeUnknown: Bool
@@ -28,7 +28,7 @@ struct IOSBridgeChatTransport: ClawdisChatTransport, 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.bridge.request(method: "sessions.list", paramsJSON: json, timeoutSeconds: 15)
return try JSONDecoder().decode(ClawdisChatSessionsListResponse.self, from: res)
return try JSONDecoder().decode(ClawdbotChatSessionsListResponse.self, from: res)
}
func setActiveSessionKey(_ sessionKey: String) async throws {
@@ -38,12 +38,12 @@ struct IOSBridgeChatTransport: ClawdisChatTransport, Sendable {
try await self.bridge.sendEvent(event: "chat.subscribe", payloadJSON: json)
}
func requestHistory(sessionKey: String) async throws -> ClawdisChatHistoryPayload {
func requestHistory(sessionKey: String) async throws -> ClawdbotChatHistoryPayload {
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.bridge.request(method: "chat.history", paramsJSON: json, timeoutSeconds: 15)
return try JSONDecoder().decode(ClawdisChatHistoryPayload.self, from: res)
return try JSONDecoder().decode(ClawdbotChatHistoryPayload.self, from: res)
}
func sendMessage(
@@ -51,13 +51,13 @@ struct IOSBridgeChatTransport: ClawdisChatTransport, Sendable {
message: String,
thinking: String,
idempotencyKey: String,
attachments: [ClawdisChatAttachmentPayload]) async throws -> ClawdisChatSendResponse
attachments: [ClawdbotChatAttachmentPayload]) async throws -> ClawdbotChatSendResponse
{
struct Params: Codable {
var sessionKey: String
var message: String
var thinking: String
var attachments: [ClawdisChatAttachmentPayload]?
var attachments: [ClawdbotChatAttachmentPayload]?
var timeoutMs: Int
var idempotencyKey: String
}
@@ -72,16 +72,16 @@ struct IOSBridgeChatTransport: ClawdisChatTransport, Sendable {
let data = try JSONEncoder().encode(params)
let json = String(data: data, encoding: .utf8)
let res = try await self.bridge.request(method: "chat.send", paramsJSON: json, timeoutSeconds: 35)
return try JSONDecoder().decode(ClawdisChatSendResponse.self, from: res)
return try JSONDecoder().decode(ClawdbotChatSendResponse.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.bridge.request(method: "health", paramsJSON: nil, timeoutSeconds: seconds)
return (try? JSONDecoder().decode(ClawdisGatewayHealthOK.self, from: res))?.ok ?? true
return (try? JSONDecoder().decode(ClawdbotGatewayHealthOK.self, from: res))?.ok ?? true
}
func events() -> AsyncStream<ClawdisChatTransportEvent> {
func events() -> AsyncStream<ClawdbotChatTransportEvent> {
AsyncStream { continuation in
let task = Task {
let stream = await self.bridge.subscribeServerEvents()
@@ -94,16 +94,16 @@ struct IOSBridgeChatTransport: ClawdisChatTransport, Sendable {
continuation.yield(.seqGap)
case "health":
guard let json = evt.payloadJSON, let data = json.data(using: .utf8) else { break }
let ok = (try? JSONDecoder().decode(ClawdisGatewayHealthOK.self, from: data))?.ok ?? true
let ok = (try? JSONDecoder().decode(ClawdbotGatewayHealthOK.self, from: data))?.ok ?? true
continuation.yield(.health(ok: ok))
case "chat":
guard let json = evt.payloadJSON, let data = json.data(using: .utf8) else { break }
if let payload = try? JSONDecoder().decode(ClawdisChatEventPayload.self, from: data) {
if let payload = try? JSONDecoder().decode(ClawdbotChatEventPayload.self, from: data) {
continuation.yield(.chat(payload))
}
case "agent":
guard let json = evt.payloadJSON, let data = json.data(using: .utf8) else { break }
if let payload = try? JSONDecoder().decode(ClawdisAgentEventPayload.self, from: data) {
if let payload = try? JSONDecoder().decode(ClawdbotAgentEventPayload.self, from: data) {
continuation.yield(.agent(payload))
}
default:

View File

@@ -1,7 +1,7 @@
import SwiftUI
@main
struct ClawdisApp: App {
struct ClawdbotApp: App {
@State private var appModel: NodeAppModel
@State private var bridgeController: BridgeConnectionController
@Environment(\.scenePhase) private var scenePhase

View File

@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Clawdis</string>
<string>Clawdbot</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconName</key>
@@ -29,20 +29,20 @@
</dict>
<key>NSBonjourServices</key>
<array>
<string>_clawdis-bridge._tcp</string>
<string>_clawdbot-bridge._tcp</string>
</array>
<key>NSCameraUsageDescription</key>
<string>Clawdis can capture photos or short video clips when requested via the bridge.</string>
<string>Clawdbot can capture photos or short video clips when requested via the bridge.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Clawdis discovers and connects to your Clawdis bridge on the local network.</string>
<string>Clawdbot discovers and connects to your Clawdbot bridge on the local network.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Clawdis uses your location when you allow location sharing.</string>
<string>Clawdbot uses your location when you allow location sharing.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Clawdis can share your location in the background when you enable Always.</string>
<string>Clawdbot can share your location in the background when you enable Always.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Clawdis needs microphone access for voice wake.</string>
<string>Clawdbot needs microphone access for voice wake.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>Clawdis uses on-device speech recognition for voice wake.</string>
<string>Clawdbot uses on-device speech recognition for voice wake.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import CoreLocation
import Foundation
@@ -30,7 +30,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
return .fullAccuracy
}
func ensureAuthorization(mode: ClawdisLocationMode) async -> CLAuthorizationStatus {
func ensureAuthorization(mode: ClawdbotLocationMode) 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: ClawdisLocationGetParams,
desiredAccuracy: ClawdisLocationAccuracy,
params: ClawdbotLocationGetParams,
desiredAccuracy: ClawdbotLocationAccuracy,
maxAgeMs: Int?,
timeoutMs: Int?) async throws -> CLLocation
{
@@ -106,7 +106,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
}
}
private static func accuracyValue(_ accuracy: ClawdisLocationAccuracy) -> CLLocationAccuracy {
private static func accuracyValue(_ accuracy: ClawdbotLocationAccuracy) -> CLLocationAccuracy {
switch accuracy {
case .coarse:
return kCLLocationAccuracyKilometer

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import Network
import Observation
import SwiftUI
@@ -89,7 +89,7 @@ final class NodeAppModel {
}()
guard !userAction.isEmpty else { return }
guard let name = ClawdisCanvasA2UIAction.extractActionName(userAction) else { return }
guard let name = ClawdbotCanvasA2UIAction.extractActionName(userAction) else { return }
let actionId: String = {
let id = (userAction["id"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return id.isEmpty ? UUID().uuidString : id
@@ -108,15 +108,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 = ClawdisCanvasA2UIAction.compactJSON(userAction["context"])
let contextJSON = ClawdbotCanvasA2UIAction.compactJSON(userAction["context"])
let sessionKey = "main"
let messageContext = ClawdisCanvasA2UIAction.AgentMessageContext(
let messageContext = ClawdbotCanvasA2UIAction.AgentMessageContext(
actionName: name,
session: .init(key: sessionKey, surfaceId: surfaceId),
component: .init(id: sourceComponentId, host: host, instanceId: instanceId),
contextJSON: contextJSON)
let message = ClawdisCanvasA2UIAction.formatAgentMessage(messageContext)
let message = ClawdbotCanvasA2UIAction.formatAgentMessage(messageContext)
let ok: Bool
var errorText: String?
@@ -141,7 +141,7 @@ final class NodeAppModel {
}
}
let js = ClawdisCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId: actionId, ok: ok, error: errorText)
let js = ClawdbotCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId: actionId, ok: ok, error: errorText)
do {
_ = try await self.screen.eval(javaScript: js)
} catch {
@@ -153,7 +153,7 @@ final class NodeAppModel {
guard let raw = await self.bridge.currentCanvasHostUrl() else { return nil }
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty, let base = URL(string: trimmed) else { return nil }
return base.appendingPathComponent("__clawdis__/a2ui/").absoluteString + "?platform=ios"
return base.appendingPathComponent("__clawdbot__/a2ui/").absoluteString + "?platform=ios"
}
private func showA2UIOnConnectIfNeeded() async {
@@ -189,7 +189,7 @@ final class NodeAppModel {
self.talkMode.setEnabled(enabled)
}
func requestLocationPermissions(mode: ClawdisLocationMode) async -> Bool {
func requestLocationPermissions(mode: ClawdbotLocationMode) async -> Bool {
guard mode != .off else { return true }
let status = await self.locationService.ensureAuthorization(mode: mode)
switch status {
@@ -250,7 +250,7 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(code: .unavailable, message: "UNAVAILABLE: node not ready"))
error: ClawdbotNodeError(code: .unavailable, message: "UNAVAILABLE: node not ready"))
}
return await self.handleInvoke(req)
})
@@ -439,7 +439,7 @@ final class NodeAppModel {
}
// iOS bridge forwards to the gateway; no local auth prompts here.
// (Key-based unattended auth is handled on macOS for clawdis:// links.)
// (Key-based unattended auth is handled on macOS for clawdbot:// links.)
let data = try JSONEncoder().encode(link)
guard let json = String(bytes: data, encoding: .utf8) else {
throw NSError(domain: "NodeAppModel", code: 2, userInfo: [
@@ -464,7 +464,7 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .backgroundUnavailable,
message: "NODE_BACKGROUND_UNAVAILABLE: canvas/camera/screen commands require foreground"))
}
@@ -473,20 +473,20 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "CAMERA_DISABLED: enable Camera in iOS Settings → Camera → Allow Camera"))
}
do {
switch command {
case ClawdisLocationCommand.get.rawValue:
case ClawdbotLocationCommand.get.rawValue:
let mode = self.locationMode()
guard mode != .off else {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "LOCATION_DISABLED: enable Location in Settings"))
}
@@ -494,12 +494,12 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .backgroundUnavailable,
message: "LOCATION_BACKGROUND_UNAVAILABLE: background location requires Always"))
}
let params = (try? Self.decodeParams(ClawdisLocationGetParams.self, from: req.paramsJSON)) ??
ClawdisLocationGetParams()
let params = (try? Self.decodeParams(ClawdbotLocationGetParams.self, from: req.paramsJSON)) ??
ClawdbotLocationGetParams()
let desired = params.desiredAccuracy ??
(self.isLocationPreciseEnabled() ? .precise : .balanced)
let status = self.locationService.authorizationStatus()
@@ -507,7 +507,7 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "LOCATION_PERMISSION_REQUIRED: grant Location permission"))
}
@@ -515,7 +515,7 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "LOCATION_PERMISSION_REQUIRED: enable Always for background access"))
}
@@ -525,7 +525,7 @@ final class NodeAppModel {
maxAgeMs: params.maxAgeMs,
timeoutMs: params.timeoutMs)
let isPrecise = self.locationService.accuracyAuthorization() == .fullAccuracy
let payload = ClawdisLocationPayload(
let payload = ClawdbotLocationPayload(
lat: location.coordinate.latitude,
lon: location.coordinate.longitude,
accuracyMeters: location.horizontalAccuracy,
@@ -538,9 +538,9 @@ final class NodeAppModel {
let json = try Self.encodePayload(payload)
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: json)
case ClawdisCanvasCommand.present.rawValue:
let params = (try? Self.decodeParams(ClawdisCanvasPresentParams.self, from: req.paramsJSON)) ??
ClawdisCanvasPresentParams()
case ClawdbotCanvasCommand.present.rawValue:
let params = (try? Self.decodeParams(ClawdbotCanvasPresentParams.self, from: req.paramsJSON)) ??
ClawdbotCanvasPresentParams()
let url = params.url?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if url.isEmpty {
self.screen.showDefaultCanvas()
@@ -549,22 +549,22 @@ final class NodeAppModel {
}
return BridgeInvokeResponse(id: req.id, ok: true)
case ClawdisCanvasCommand.hide.rawValue:
case ClawdbotCanvasCommand.hide.rawValue:
return BridgeInvokeResponse(id: req.id, ok: true)
case ClawdisCanvasCommand.navigate.rawValue:
let params = try Self.decodeParams(ClawdisCanvasNavigateParams.self, from: req.paramsJSON)
case ClawdbotCanvasCommand.navigate.rawValue:
let params = try Self.decodeParams(ClawdbotCanvasNavigateParams.self, from: req.paramsJSON)
self.screen.navigate(to: params.url)
return BridgeInvokeResponse(id: req.id, ok: true)
case ClawdisCanvasCommand.evalJS.rawValue:
let params = try Self.decodeParams(ClawdisCanvasEvalParams.self, from: req.paramsJSON)
case ClawdbotCanvasCommand.evalJS.rawValue:
let params = try Self.decodeParams(ClawdbotCanvasEvalParams.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 ClawdisCanvasCommand.snapshot.rawValue:
let params = try? Self.decodeParams(ClawdisCanvasSnapshotParams.self, from: req.paramsJSON)
case ClawdbotCanvasCommand.snapshot.rawValue:
let params = try? Self.decodeParams(ClawdbotCanvasSnapshotParams.self, from: req.paramsJSON)
let format = params?.format ?? .jpeg
let maxWidth: CGFloat? = {
if let raw = params?.maxWidth, raw > 0 { return CGFloat(raw) }
@@ -585,12 +585,12 @@ final class NodeAppModel {
])
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
case ClawdisCanvasA2UICommand.reset.rawValue:
case ClawdbotCanvasA2UICommand.reset.rawValue:
guard let a2uiUrl = await self.resolveA2UIHostURL() else {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "A2UI_HOST_NOT_CONFIGURED: gateway did not advertise canvas host"))
}
@@ -599,32 +599,32 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "A2UI_HOST_UNAVAILABLE: A2UI host not reachable"))
}
let json = try await self.screen.eval(javaScript: """
(() => {
if (!globalThis.clawdisA2UI) return JSON.stringify({ ok: false, error: "missing clawdisA2UI" });
return JSON.stringify(globalThis.clawdisA2UI.reset());
if (!globalThis.clawdbotA2UI) return JSON.stringify({ ok: false, error: "missing clawdbotA2UI" });
return JSON.stringify(globalThis.clawdbotA2UI.reset());
})()
""")
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: json)
case ClawdisCanvasA2UICommand.push.rawValue, ClawdisCanvasA2UICommand.pushJSONL.rawValue:
case ClawdbotCanvasA2UICommand.push.rawValue, ClawdbotCanvasA2UICommand.pushJSONL.rawValue:
let messages: [AnyCodable]
if command == ClawdisCanvasA2UICommand.pushJSONL.rawValue {
let params = try Self.decodeParams(ClawdisCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
messages = try ClawdisCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
if command == ClawdbotCanvasA2UICommand.pushJSONL.rawValue {
let params = try Self.decodeParams(ClawdbotCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
messages = try ClawdbotCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
} else {
do {
let params = try Self.decodeParams(ClawdisCanvasA2UIPushParams.self, from: req.paramsJSON)
let params = try Self.decodeParams(ClawdbotCanvasA2UIPushParams.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(ClawdisCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
messages = try ClawdisCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
let params = try Self.decodeParams(ClawdbotCanvasA2UIPushJSONLParams.self, from: req.paramsJSON)
messages = try ClawdbotCanvasA2UIJSONL.decodeMessagesFromJSONL(params.jsonl)
}
}
@@ -632,7 +632,7 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "A2UI_HOST_NOT_CONFIGURED: gateway did not advertise canvas host"))
}
@@ -641,18 +641,18 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(
error: ClawdbotNodeError(
code: .unavailable,
message: "A2UI_HOST_UNAVAILABLE: A2UI host not reachable"))
}
let messagesJSON = try ClawdisCanvasA2UIJSONL.encodeMessagesJSONArray(messages)
let messagesJSON = try ClawdbotCanvasA2UIJSONL.encodeMessagesJSONArray(messages)
let js = """
(() => {
try {
if (!globalThis.clawdisA2UI) return JSON.stringify({ ok: false, error: "missing clawdisA2UI" });
if (!globalThis.clawdbotA2UI) return JSON.stringify({ ok: false, error: "missing clawdbotA2UI" });
const messages = \(messagesJSON);
return JSON.stringify(globalThis.clawdisA2UI.applyMessages(messages));
return JSON.stringify(globalThis.clawdbotA2UI.applyMessages(messages));
} catch (e) {
return JSON.stringify({ ok: false, error: String(e?.message ?? e) });
}
@@ -661,7 +661,7 @@ final class NodeAppModel {
let resultJSON = try await self.screen.eval(javaScript: js)
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: resultJSON)
case ClawdisCameraCommand.list.rawValue:
case ClawdbotCameraCommand.list.rawValue:
let devices = await self.camera.listDevices()
struct Payload: Codable {
var devices: [CameraController.CameraDeviceInfo]
@@ -669,11 +669,11 @@ final class NodeAppModel {
let payload = try Self.encodePayload(Payload(devices: devices))
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
case ClawdisCameraCommand.snap.rawValue:
case ClawdbotCameraCommand.snap.rawValue:
self.showCameraHUD(text: "Taking photo…", kind: .photo)
self.triggerCameraFlash()
let params = (try? Self.decodeParams(ClawdisCameraSnapParams.self, from: req.paramsJSON)) ??
ClawdisCameraSnapParams()
let params = (try? Self.decodeParams(ClawdbotCameraSnapParams.self, from: req.paramsJSON)) ??
ClawdbotCameraSnapParams()
let res = try await self.camera.snap(params: params)
struct Payload: Codable {
@@ -690,9 +690,9 @@ final class NodeAppModel {
self.showCameraHUD(text: "Photo captured", kind: .success, autoHideSeconds: 1.6)
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
case ClawdisCameraCommand.clip.rawValue:
let params = (try? Self.decodeParams(ClawdisCameraClipParams.self, from: req.paramsJSON)) ??
ClawdisCameraClipParams()
case ClawdbotCameraCommand.clip.rawValue:
let params = (try? Self.decodeParams(ClawdbotCameraClipParams.self, from: req.paramsJSON)) ??
ClawdbotCameraClipParams()
let suspended = (params.includeAudio ?? true) ? self.voiceWake.suspendForExternalAudioCapture() : false
defer { self.voiceWake.resumeAfterExternalAudioCapture(wasSuspended: suspended) }
@@ -714,9 +714,9 @@ final class NodeAppModel {
self.showCameraHUD(text: "Clip captured", kind: .success, autoHideSeconds: 1.8)
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload)
case ClawdisScreenCommand.record.rawValue:
let params = (try? Self.decodeParams(ClawdisScreenRecordParams.self, from: req.paramsJSON)) ??
ClawdisScreenRecordParams()
case ClawdbotScreenCommand.record.rawValue:
let params = (try? Self.decodeParams(ClawdbotScreenRecordParams.self, from: req.paramsJSON)) ??
ClawdbotScreenRecordParams()
if let format = params.format, format.lowercased() != "mp4" {
throw NSError(domain: "Screen", code: 30, userInfo: [
NSLocalizedDescriptionKey: "INVALID_REQUEST: screen format must be mp4",
@@ -754,7 +754,7 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
error: ClawdbotNodeError(code: .invalidRequest, message: "INVALID_REQUEST: unknown command"))
}
} catch {
if command.hasPrefix("camera.") {
@@ -764,13 +764,13 @@ final class NodeAppModel {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: ClawdisNodeError(code: .unavailable, message: error.localizedDescription))
error: ClawdbotNodeError(code: .unavailable, message: error.localizedDescription))
}
}
private func locationMode() -> ClawdisLocationMode {
private func locationMode() -> ClawdbotLocationMode {
let raw = UserDefaults.standard.string(forKey: "location.enabledMode") ?? "off"
return ClawdisLocationMode(rawValue: raw) ?? .off
return ClawdbotLocationMode(rawValue: raw) ?? .off
}
private func isLocationPreciseEnabled() -> Bool {

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import Observation
import SwiftUI
import WebKit
@@ -13,7 +13,7 @@ final class ScreenController {
var urlString: String = ""
var errorText: String?
/// Callback invoked when a clawdis:// deep link is tapped in the canvas
/// Callback invoked when a clawdbot:// 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.__clawdis;
const api = globalThis.__clawdbot;
if (!api) return;
if (typeof api.setDebugStatusEnabled === 'function') {
api.setDebugStatusEnabled(\(enabled ? "true" : "false"));
@@ -124,7 +124,7 @@ final class ScreenController {
let res = try await self.eval(javaScript: """
(() => {
try {
return !!globalThis.clawdisA2UI && typeof globalThis.clawdisA2UI.applyMessages === 'function';
return !!globalThis.clawdbotA2UI && typeof globalThis.clawdbotA2UI.applyMessages === 'function';
} catch (_) { return false; }
})()
""")
@@ -184,7 +184,7 @@ final class ScreenController {
func snapshotBase64(
maxWidth: CGFloat? = nil,
format: ClawdisCanvasSnapshotFormat,
format: ClawdbotCanvasSnapshotFormat,
quality: Double? = nil) async throws -> String
{
let config = WKSnapshotConfiguration()
@@ -229,7 +229,7 @@ final class ScreenController {
subdirectory: String)
-> URL?
{
let bundle = ClawdisKitResources.bundle
let bundle = ClawdbotKitResources.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 clawdis:// deep links from canvas
/// Handles navigation policy to intercept clawdbot:// 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 clawdis:// deep links
if url.scheme == "clawdis" {
// Intercept clawdbot:// deep links
if url.scheme == "clawdbot" {
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 = "clawdisCanvasA2UIAction"
static let messageName = "clawdbotCanvasA2UIAction"
static let legacyMessageNames = ["canvas", "a2ui", "userAction", "action"]
static let handlerNames = [messageName] + legacyMessageNames

View File

@@ -63,12 +63,12 @@ final class ScreenRecordService: @unchecked Sendable {
return URL(fileURLWithPath: outPath)
}
return FileManager.default.temporaryDirectory
.appendingPathComponent("clawdis-screen-record-\(UUID().uuidString).mp4")
.appendingPathComponent("clawdbot-screen-record-\(UUID().uuidString).mp4")
}()
try? FileManager.default.removeItem(at: outURL)
let state = CaptureState()
let recordQueue = DispatchQueue(label: "com.clawdis.screenrecord")
let recordQueue = DispatchQueue(label: "com.clawdbot.screenrecord")
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
let handler: @Sendable (CMSampleBuffer, RPSampleBufferType, Error?) -> Void = { sample, type, error in

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import SwiftUI
struct ScreenTab: View {

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
import SwiftUI
import WebKit

View File

@@ -1,4 +1,4 @@
import ClawdisKit
import ClawdbotKit
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 = ClawdisLocationMode.off.rawValue
@AppStorage("location.enabledMode") private var locationEnabledModeRaw: String = ClawdbotLocationMode.off.rawValue
@AppStorage("location.preciseEnabled") private var locationPreciseEnabled: Bool = true
@AppStorage("screen.preventSleep") private var preventSleep: Bool = true
@AppStorage("bridge.preferredStableID") private var preferredBridgeStableID: String = ""
@@ -36,7 +36,7 @@ struct SettingsTab: View {
@State private var connectStatus = ConnectStatusStore()
@State private var connectingBridgeID: String?
@State private var localIPAddress: String?
@State private var lastLocationModeRaw: String = ClawdisLocationMode.off.rawValue
@State private var lastLocationModeRaw: String = ClawdbotLocationMode.off.rawValue
var body: some View {
NavigationStack {
@@ -186,9 +186,9 @@ struct SettingsTab: View {
Section("Location") {
Picker("Location Access", selection: self.$locationEnabledModeRaw) {
Text("Off").tag(ClawdisLocationMode.off.rawValue)
Text("While Using").tag(ClawdisLocationMode.whileUsing.rawValue)
Text("Always").tag(ClawdisLocationMode.always.rawValue)
Text("Off").tag(ClawdbotLocationMode.off.rawValue)
Text("While Using").tag(ClawdbotLocationMode.whileUsing.rawValue)
Text("Always").tag(ClawdbotLocationMode.always.rawValue)
}
.pickerStyle(.segmented)
@@ -202,7 +202,7 @@ struct SettingsTab: View {
Section("Screen") {
Toggle("Prevent Sleep", isOn: self.$preventSleep)
Text("Keeps the screen awake while Clawdis is open.")
Text("Keeps the screen awake while Clawdbot is open.")
.font(.footnote)
.foregroundStyle(.secondary)
}
@@ -233,7 +233,7 @@ struct SettingsTab: View {
.onChange(of: self.locationEnabledModeRaw) { _, newValue in
let previous = self.lastLocationModeRaw
self.lastLocationModeRaw = newValue
guard let mode = ClawdisLocationMode(rawValue: newValue) else { return }
guard let mode = ClawdbotLocationMode(rawValue: newValue) else { return }
Task {
let granted = await self.appModel.requestLocationPermissions(mode: mode)
if !granted {
@@ -312,8 +312,8 @@ struct SettingsTab: View {
return "iOS \(v.majorVersion).\(v.minorVersion).\(v.patchVersion)"
}
private var locationMode: ClawdisLocationMode {
ClawdisLocationMode(rawValue: self.locationEnabledModeRaw) ?? .off
private var locationMode: ClawdbotLocationMode {
ClawdbotLocationMode(rawValue: self.locationEnabledModeRaw) ?? .off
}
private func appVersion() -> String {
@@ -342,38 +342,38 @@ struct SettingsTab: View {
}
private func currentCaps() -> [String] {
var caps = [ClawdisCapability.canvas.rawValue, ClawdisCapability.screen.rawValue]
var caps = [ClawdbotCapability.canvas.rawValue, ClawdbotCapability.screen.rawValue]
let cameraEnabled =
UserDefaults.standard.object(forKey: "camera.enabled") == nil
? true
: UserDefaults.standard.bool(forKey: "camera.enabled")
if cameraEnabled { caps.append(ClawdisCapability.camera.rawValue) }
if cameraEnabled { caps.append(ClawdbotCapability.camera.rawValue) }
let voiceWakeEnabled = UserDefaults.standard.bool(forKey: VoiceWakePreferences.enabledKey)
if voiceWakeEnabled { caps.append(ClawdisCapability.voiceWake.rawValue) }
if voiceWakeEnabled { caps.append(ClawdbotCapability.voiceWake.rawValue) }
return caps
}
private func currentCommands() -> [String] {
var commands: [String] = [
ClawdisCanvasCommand.present.rawValue,
ClawdisCanvasCommand.hide.rawValue,
ClawdisCanvasCommand.navigate.rawValue,
ClawdisCanvasCommand.evalJS.rawValue,
ClawdisCanvasCommand.snapshot.rawValue,
ClawdisCanvasA2UICommand.push.rawValue,
ClawdisCanvasA2UICommand.pushJSONL.rawValue,
ClawdisCanvasA2UICommand.reset.rawValue,
ClawdisScreenCommand.record.rawValue,
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,
]
let caps = Set(self.currentCaps())
if caps.contains(ClawdisCapability.camera.rawValue) {
commands.append(ClawdisCameraCommand.list.rawValue)
commands.append(ClawdisCameraCommand.snap.rawValue)
commands.append(ClawdisCameraCommand.clip.rawValue)
if caps.contains(ClawdbotCapability.camera.rawValue) {
commands.append(ClawdbotCameraCommand.list.rawValue)
commands.append(ClawdbotCameraCommand.snap.rawValue)
commands.append(ClawdbotCameraCommand.clip.rawValue)
}
return commands
@@ -391,7 +391,7 @@ struct SettingsTab: View {
do {
let statusStore = self.connectStatus
let existing = KeychainStore.loadString(
service: "com.clawdis.bridge",
service: "com.clawdbot.bridge",
account: self.keychainAccount())
let existingToken = (existing?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ?
existing :
@@ -419,7 +419,7 @@ struct SettingsTab: View {
if !token.isEmpty, token != existingToken {
_ = KeychainStore.saveString(
token,
service: "com.clawdis.bridge",
service: "com.clawdbot.bridge",
account: self.keychainAccount())
}
@@ -465,7 +465,7 @@ struct SettingsTab: View {
do {
let statusStore = self.connectStatus
let existing = KeychainStore.loadString(
service: "com.clawdis.bridge",
service: "com.clawdbot.bridge",
account: self.keychainAccount())
let existingToken = (existing?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ?
existing :
@@ -493,7 +493,7 @@ struct SettingsTab: View {
if !token.isEmpty, token != existingToken {
_ = KeychainStore.saveString(
token,
service: "com.clawdis.bridge",
service: "com.clawdbot.bridge",
account: self.keychainAccount())
}

View File

@@ -30,7 +30,7 @@ struct VoiceWakeWordsSettingsView: View {
Text("Wake Words")
} footer: {
Text(
"Clawdis reacts when any trigger appears in a transcription. "
"Clawdbot reacts when any trigger appears in a transcription. "
+ "Keep them short to avoid false positives.")
}
}

View File

@@ -1,5 +1,5 @@
import AVFAudio
import ClawdisKit
import ClawdbotKit
import Foundation
import Observation
import OSLog
@@ -47,7 +47,7 @@ final class TalkModeManager: NSObject {
private var chatSubscribedSessionKeys = Set<String>()
private let logger = Logger(subsystem: "com.clawdis", category: "TalkMode")
private let logger = Logger(subsystem: "com.clawdbot", category: "TalkMode")
func attachBridge(_ bridge: BridgeSession) {
self.bridge = bridge