Agent cards: shareable brain QR on the flip side
CI / policy (push) Has been cancelled
CI / backend (push) Has been cancelled
CI / profile (push) Has been cancelled
CI / mobile (push) Has been cancelled

- Backend brainhub: AgentBrain gains resourceUrl
  (https://clawbrainhub.com/brains/{owner}/{name}); set in parse_brain + fake.
- Mobile: AgentMeta gains brainUrl (carried from the pulled brain's resourceUrl).
- AgentBackTemplate: under Special capabilities, a "Share brain link" toggle
  reveals a QR (+ the URL) that anyone can scan to open the brain on
  ClawBrainHub.
- Gate green: mobile 28, backend brainhub tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-06 06:31:17 -05:00
co-authored by Claude Opus 4.8
parent 9765a08a5a
commit 3b858db6ab
5 changed files with 54 additions and 1 deletions
@@ -10,6 +10,8 @@ const DEFAULT_BASE: &str = "https://clawbrainhub.com/api/v1";
/// Reference brains the registry always ships — handy picks even when the user /// Reference brains the registry always ships — handy picks even when the user
/// hasn't published their own yet. /// hasn't published their own yet.
const REFERENCE_OWNER: &str = "redclawsystems"; const REFERENCE_OWNER: &str = "redclawsystems";
/// Public web base for a shareable brain link.
const WEB_BASE: &str = "https://clawbrainhub.com/brains";
/// A brain as shown in the picker list. /// A brain as shown in the picker list.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -34,6 +36,8 @@ pub struct AgentBrain {
pub skills: Vec<String>, pub skills: Vec<String>,
pub tools: Vec<String>, pub tools: Vec<String>,
pub capabilities: Vec<String>, pub capabilities: Vec<String>,
/// Public ClawBrainHub URL to access this brain (shareable via QR).
pub resource_url: String,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -226,6 +230,7 @@ fn parse_brain(owner: &str, name: &str, version: &str, json: &serde_json::Value)
skills, skills,
tools, tools,
capabilities, capabilities,
resource_url: format!("{WEB_BASE}/{owner}/{name}"),
} }
} }
@@ -311,6 +316,7 @@ impl BrainHubClient for FakeBrainHubClient {
skills: vec!["Web Search".into(), "Code".into()], skills: vec!["Web Search".into(), "Code".into()],
tools: vec![], tools: vec![],
capabilities: vec!["Writing".into(), "Analysis".into()], capabilities: vec!["Writing".into(), "Analysis".into()],
resource_url: format!("{WEB_BASE}/{owner}/{name}"),
}) })
} }
} }
+1
View File
@@ -193,6 +193,7 @@ export default function CardEditorScreen() {
skills: b.skills.map((s) => ({ name: s, level: 4 })), skills: b.skills.map((s) => ({ name: s, level: 4 })),
tools: b.tools, tools: b.tools,
capabilities: b.capabilities, capabilities: b.capabilities,
brainUrl: b.resourceUrl,
}); });
setShowing(false); setShowing(false);
draft.setPendingAgentBrain(null); draft.setPendingAgentBrain(null);
+2
View File
@@ -20,6 +20,8 @@ export interface AgentBrain {
skills: string[]; skills: string[];
tools: string[]; tools: string[];
capabilities: string[]; capabilities: string[];
/** Public ClawBrainHub URL to access this brain (shareable via QR). */
resourceUrl: string;
} }
export async function listBrains(): Promise<BrainSummary[]> { export async function listBrains(): Promise<BrainSummary[]> {
@@ -2,8 +2,10 @@
// skill bars, tools, and special capabilities (trading-card style). // skill bars, tools, and special capabilities (trading-card style).
import { MaterialCommunityIcons } from "@expo/vector-icons"; import { MaterialCommunityIcons } from "@expo/vector-icons";
import { ScrollView, StyleSheet, Text, View } from "react-native"; import { useState } from "react";
import { Pressable, ScrollView, StyleSheet, Switch, Text, View } from "react-native";
import { AgentMeta } from "../../stores/localCardsStore"; import { AgentMeta } from "../../stores/localCardsStore";
import { QRCodeView } from "./QRCodeView";
export function AgentBackTemplate({ export function AgentBackTemplate({
name, name,
@@ -14,6 +16,7 @@ export function AgentBackTemplate({
title: string; title: string;
agent: AgentMeta; agent: AgentMeta;
}) { }) {
const [showQr, setShowQr] = useState(false);
return ( return (
<View style={styles.root}> <View style={styles.root}>
<View style={styles.header}> <View style={styles.header}>
@@ -72,6 +75,33 @@ export function AgentBackTemplate({
))} ))}
</> </>
)} )}
{!!agent.brainUrl && (
<View style={styles.shareBlock}>
<Pressable style={styles.shareRow} onPress={() => setShowQr((v) => !v)}>
<View style={{ flex: 1 }}>
<Text style={styles.section}>Share brain link</Text>
<Text style={styles.shareHint}>QR to access this brain on ClawBrainHub</Text>
</View>
<Switch
value={showQr}
onValueChange={setShowQr}
trackColor={{ true: "#ff3b30", false: "#3a3a44" }}
thumbColor="#ffffff"
/>
</Pressable>
{showQr && (
<View style={styles.qrWrap}>
<View style={styles.qrCard}>
<QRCodeView value={agent.brainUrl} size={150} />
</View>
<Text style={styles.qrUrl} numberOfLines={1}>
{agent.brainUrl}
</Text>
</View>
)}
</View>
)}
</ScrollView> </ScrollView>
</View> </View>
); );
@@ -110,4 +140,16 @@ const styles = StyleSheet.create({
chipText: { color: "#f5f5f7", fontSize: 13, fontWeight: "600" }, chipText: { color: "#f5f5f7", fontSize: 13, fontWeight: "600" },
capRow: { flexDirection: "row", alignItems: "center", gap: 8 }, capRow: { flexDirection: "row", alignItems: "center", gap: 8 },
capText: { color: "#e0e0e4", fontSize: 14, flex: 1 }, capText: { color: "#e0e0e4", fontSize: 14, flex: 1 },
shareBlock: {
marginTop: 16,
paddingTop: 14,
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: "#2a2a30",
},
shareRow: { flexDirection: "row", alignItems: "center", gap: 12 },
shareHint: { color: "#9a9aa0", fontSize: 12, marginTop: 2 },
qrWrap: { alignItems: "center", marginTop: 14, gap: 8 },
qrCard: { backgroundColor: "#ffffff", padding: 12, borderRadius: 14 },
qrUrl: { color: "#9a9aa0", fontSize: 11 },
}); });
@@ -42,6 +42,8 @@ export interface AgentMeta {
skills: AgentSkill[]; skills: AgentSkill[];
tools: string[]; tools: string[];
capabilities: string[]; capabilities: string[];
/** ClawBrainHub URL to access the brain (shared via the flip-side QR). */
brainUrl?: string;
} }
export interface LocalCard { export interface LocalCard {