Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/app/agents/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ export default function AgentsPage() {
useEffect(() => {
if (!dbDetected && hasExistingAgent && address && detectedAgentId && !cachedRef.current) {
cachedRef.current = true;
cacheAgentById(address, detectedAgentId.toString()).catch(() => {});
cacheAgentById(address, detectedAgentId.toString()).catch(() =>
cacheAgentById(address, detectedAgentId.toString()).catch(() => {}),
);
}
}, [dbDetected, hasExistingAgent, address, detectedAgentId]);

Expand Down
28 changes: 26 additions & 2 deletions src/app/api/user/agent-register/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,36 @@ export async function POST(request: NextRequest) {
};

if (existingUser) {
await supabase.from("users").update(agentFields).eq("id", existingUser.id);
const { error: updateError } = await supabase.from("users").update(agentFields).eq("id", existingUser.id);
if (updateError) {
return NextResponse.json({ error: updateError.message }, { status: 500 });
}
} else {
await supabase.from("users").insert({
const { error: insertError } = await supabase.from("users").insert({
primary_address: normalized,
...agentFields,
});

// 23505: row was created concurrently — update it instead
if (insertError?.code === "23505") {
const { data: raceUser } = await supabase
.from("users")
.select("id")
.or(`primary_address.eq.${normalized},agent_wallet.eq.${normalized},agent_owner.eq.${normalized}`)
.limit(1)
.single();

if (raceUser) {
const { error: updateError } = await supabase.from("users").update(agentFields).eq("id", raceUser.id);
if (updateError) {
return NextResponse.json({ error: updateError.message }, { status: 500 });
}
} else {
return NextResponse.json({ error: "Conflict but user not found on retry" }, { status: 500 });
}
} else if (insertError) {
return NextResponse.json({ error: insertError.message }, { status: 500 });
}
}

return NextResponse.json({ ok: true });
Expand Down
6 changes: 5 additions & 1 deletion src/app/api/user/agent-update/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: "User not found" }, { status: 404 });
}

await supabase.from("users").update(sanitized).eq("id", existingUser.id);
const { error: updateError } = await supabase.from("users").update(sanitized).eq("id", existingUser.id);

if (updateError) {
return NextResponse.json({ error: updateError.message }, { status: 500 });
}

return NextResponse.json({ ok: true });
} catch (err) {
Expand Down
4 changes: 3 additions & 1 deletion src/components/AgentDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ export function AgentDashboard() {
useEffect(() => {
if (!dbDetected && isAgent && address && agentId && !cachedRef.current) {
cachedRef.current = true;
cacheAgentById(address, agentId.toString()).catch(() => {});
cacheAgentById(address, agentId.toString()).catch(() =>
cacheAgentById(address, agentId.toString()).catch(() => {}),
);
}
}, [dbDetected, isAgent, address, agentId]);

Expand Down
82 changes: 50 additions & 32 deletions src/components/AgentManage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,27 @@ export function AgentManage({ agentId, role }: AgentManageProps) {
await publicClient.waitForTransactionReceipt({ hash });
const parsed = JSON.parse(editUri);
setMetadata({ ...metadata!, ...parsed });
setSuccessMessage("Agent profile updated");
// Persist URI update to DB
fetch("/api/user/agent-update", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
fields: {
agent_name: parsed.name,
agent_description: parsed.description,
agent_genre: parsed.genre || null,
agent_llm_model: parsed.llmModel || null,
},
}),
}).catch(() => {});
try {
const cacheRes = await fetch("/api/user/agent-update", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
fields: {
agent_name: parsed.name,
agent_description: parsed.description,
agent_genre: parsed.genre || null,
agent_llm_model: parsed.llmModel || null,
},
}),
});
setSuccessMessage(cacheRes.ok
? "Agent profile updated"
: "On-chain OK, but cache failed — will sync on next visit");
} catch {
setSuccessMessage("On-chain OK, but cache failed — will sync on next visit");
}
setEditing(false);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to update URI");
Expand All @@ -200,16 +206,22 @@ export function AgentManage({ agentId, role }: AgentManageProps) {
});
setTxHash(hash);
await publicClient.waitForTransactionReceipt({ hash });
setSuccessMessage("Agent wallet removed");
// Persist unset to DB
fetch("/api/user/agent-update", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
fields: { agent_wallet: null },
}),
}).catch(() => {});
try {
const cacheRes = await fetch("/api/user/agent-update", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
fields: { agent_wallet: null },
}),
});
setSuccessMessage(cacheRes.ok
? "Agent wallet removed"
: "On-chain OK, but cache failed — will sync on next visit");
} catch {
setSuccessMessage("On-chain OK, but cache failed — will sync on next visit");
}
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to unset wallet");
} finally {
Expand Down Expand Up @@ -260,15 +272,21 @@ export function AgentManage({ agentId, role }: AgentManageProps) {
setTxHash(hash);
await publicClient.waitForTransactionReceipt({ hash });
// Persist new wallet binding to DB
fetch("/api/user/agent-update", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
fields: { agent_wallet: newWalletAddr.toLowerCase() },
}),
}).catch(() => {});
setSuccessMessage("Agent wallet bound successfully");
try {
const cacheRes = await fetch("/api/user/agent-update", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
fields: { agent_wallet: newWalletAddr.toLowerCase() },
}),
});
setSuccessMessage(cacheRes.ok
? "Agent wallet bound successfully"
: "On-chain OK, but cache failed — will sync on next visit");
} catch {
setSuccessMessage("On-chain OK, but cache failed — will sync on next visit");
}
setWalletStep(null);
setChangingWallet(false);
setNewWalletAddr("");
Expand Down
33 changes: 20 additions & 13 deletions src/components/AgentRegister.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,26 @@ export function AgentRegister() {
setOwnerAddress(address);
// Persist agent data to DB
const meta = JSON.parse(agentURI);
fetch("/api/user/agent-register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
agentId: newAgentId?.toString(),
name: meta.name,
description: meta.description,
genre: meta.genre,
llmModel: meta.llmModel,
agentOwner: address,
}),
}).catch(() => {});
try {
const cacheRes = await fetch("/api/user/agent-register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
walletAddress: address,
agentId: newAgentId?.toString(),
name: meta.name,
description: meta.description,
genre: meta.genre,
llmModel: meta.llmModel,
agentOwner: address,
}),
});
if (!cacheRes.ok) {
setError("On-chain OK, but cache failed — will sync on next visit");
}
} catch {
setError("On-chain OK, but cache failed — will sync on next visit");
}
setStep("3a");
} catch (err) {
setError(err instanceof Error ? err.message : "Registration failed");
Expand Down
Loading