From 51bdf01e2e5f7faf745899309bf77653ec8cf9ba Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 17 Dec 2025 21:17:51 +0100 Subject: [PATCH] Presence: add device identity fields --- .../clawdis/node/bridge/BridgePairingClient.kt | 3 +++ .../steipete/clawdis/node/bridge/BridgeSession.kt | 3 +++ .../clawdis/node/bridge/BridgePairingClientTest.kt | 5 ++++- .../Sources/Bridge/BridgeConnectionController.swift | 1 + apps/macos/Sources/Clawdis/InstancesSettings.swift | 6 +++--- src/gateway/protocol/schema.ts | 2 ++ src/infra/system-presence.ts | 13 +++++++++++++ 7 files changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgePairingClient.kt b/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgePairingClient.kt index 9284c69af..d80447bc4 100644 --- a/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgePairingClient.kt +++ b/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgePairingClient.kt @@ -28,6 +28,9 @@ class BridgePairingClient { val deviceFamily: String?, val modelIdentifier: String?, val caps: List?, + val deviceFamily: String?, + val modelIdentifier: String?, + val caps: List?, ) data class PairResult(val ok: Boolean, val token: String?, val error: String? = null) diff --git a/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgeSession.kt b/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgeSession.kt index 3384c0b14..f8b1c1d9d 100644 --- a/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgeSession.kt +++ b/apps/android/app/src/main/java/com/steipete/clawdis/node/bridge/BridgeSession.kt @@ -43,6 +43,9 @@ class BridgeSession( val deviceFamily: String?, val modelIdentifier: String?, val caps: List?, + val deviceFamily: String?, + val modelIdentifier: String?, + val caps: List?, ) data class InvokeRequest(val id: String, val command: String, val paramsJson: String?) diff --git a/apps/android/app/src/test/java/com/steipete/clawdis/node/bridge/BridgePairingClientTest.kt b/apps/android/app/src/test/java/com/steipete/clawdis/node/bridge/BridgePairingClientTest.kt index 9644bb7c9..aae427c88 100644 --- a/apps/android/app/src/test/java/com/steipete/clawdis/node/bridge/BridgePairingClientTest.kt +++ b/apps/android/app/src/test/java/com/steipete/clawdis/node/bridge/BridgePairingClientTest.kt @@ -46,6 +46,8 @@ class BridgePairingClientTest { token = "token-123", platform = "Android", version = "test", + deviceFamily = "Android", + modelIdentifier = "SM-X000", ), ) assertTrue(res.ok) @@ -91,6 +93,8 @@ class BridgePairingClientTest { token = null, platform = "Android", version = "test", + deviceFamily = "Android", + modelIdentifier = "SM-X000", ), ) assertTrue(res.ok) @@ -98,4 +102,3 @@ class BridgePairingClientTest { server.await() } } - diff --git a/apps/ios/Sources/Bridge/BridgeConnectionController.swift b/apps/ios/Sources/Bridge/BridgeConnectionController.swift index f35f99a66..bc11a9f8a 100644 --- a/apps/ios/Sources/Bridge/BridgeConnectionController.swift +++ b/apps/ios/Sources/Bridge/BridgeConnectionController.swift @@ -1,4 +1,5 @@ import ClawdisKit +import Darwin import Foundation import Network import Observation diff --git a/apps/macos/Sources/Clawdis/InstancesSettings.swift b/apps/macos/Sources/Clawdis/InstancesSettings.swift index 5276aa5ba..684093213 100644 --- a/apps/macos/Sources/Clawdis/InstancesSettings.swift +++ b/apps/macos/Sources/Clawdis/InstancesSettings.swift @@ -136,12 +136,12 @@ struct InstancesSettings: View { } private func deviceDescription(_ inst: InstanceInfo) -> String? { - let model = inst.modelIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - if !model.isEmpty { return model } let family = inst.deviceFamily?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + let model = inst.modelIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + if !family.isEmpty, !model.isEmpty { return "\(family) (\(model))" } + if !model.isEmpty { return model } return family.isEmpty ? nil : family } - private func prettyPlatform(_ raw: String) -> String? { let (prefix, version) = self.parsePlatform(raw) if prefix.isEmpty { return nil } diff --git a/src/gateway/protocol/schema.ts b/src/gateway/protocol/schema.ts index ce9bea42c..0fa9b69a1 100644 --- a/src/gateway/protocol/schema.ts +++ b/src/gateway/protocol/schema.ts @@ -69,6 +69,8 @@ export const ConnectParamsSchema = Type.Object( modelIdentifier: Type.Optional(NonEmptyString), mode: NonEmptyString, instanceId: Type.Optional(NonEmptyString), + deviceFamily: Type.Optional(NonEmptyString), + modelIdentifier: Type.Optional(NonEmptyString), }, { additionalProperties: false }, ), diff --git a/src/infra/system-presence.ts b/src/infra/system-presence.ts index 7722b7ba9..cb24ad5af 100644 --- a/src/infra/system-presence.ts +++ b/src/infra/system-presence.ts @@ -1,3 +1,4 @@ +import { spawnSync } from "node:child_process"; import os from "node:os"; export type SystemPresence = { @@ -49,6 +50,17 @@ function initSelfPresence() { const ip = resolvePrimaryIPv4() ?? undefined; const version = process.env.CLAWDIS_VERSION ?? process.env.npm_package_version ?? "unknown"; + const modelIdentifier = (() => { + const p = os.platform(); + if (p === "darwin") { + const res = spawnSync("sysctl", ["-n", "hw.model"], { + encoding: "utf-8", + }); + const out = typeof res.stdout === "string" ? res.stdout.trim() : ""; + return out.length > 0 ? out : undefined; + } + return os.arch(); + })(); const platform = (() => { const p = os.platform(); const rel = os.release(); @@ -70,6 +82,7 @@ function initSelfPresence() { version, platform, deviceFamily, + modelIdentifier, mode: "gateway", reason: "self", text,