Agent cards: shareable brain QR on the flip side
- 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:
co-authored by
Claude Opus 4.8
parent
9765a08a5a
commit
3b858db6ab
@@ -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}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user