plotlink-ows 1.0.16 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/routes/settings.ts
CHANGED
|
@@ -8,6 +8,21 @@ import { db } from "../db";
|
|
|
8
8
|
import {
|
|
9
9
|
signMessage as owsSignMsg,
|
|
10
10
|
} from "@open-wallet-standard/core";
|
|
11
|
+
import { CONFIG_DIR } from "../lib/paths";
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import path from "path";
|
|
14
|
+
|
|
15
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
16
|
+
|
|
17
|
+
function readConfig(): Record<string, unknown> {
|
|
18
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8")); } catch { return {}; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function writeConfig(updates: Record<string, unknown>) {
|
|
22
|
+
const config = readConfig();
|
|
23
|
+
Object.assign(config, updates);
|
|
24
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
25
|
+
}
|
|
11
26
|
|
|
12
27
|
const ERC_8004 = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432" as const;
|
|
13
28
|
const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL || "https://mainnet.base.org";
|
|
@@ -41,10 +56,15 @@ settings.post("/generate-binding", async (c) => {
|
|
|
41
56
|
const result = owsSignMsg(wallet.name, "eip155:8453", message, passphrase);
|
|
42
57
|
const signature = result.signature.startsWith("0x") ? result.signature : `0x${result.signature}`;
|
|
43
58
|
|
|
59
|
+
// Include agentId from config.json if available
|
|
60
|
+
const config = readConfig();
|
|
61
|
+
const agentId = config.agentId ? Number(config.agentId) : undefined;
|
|
62
|
+
|
|
44
63
|
return c.json({
|
|
45
64
|
message,
|
|
46
65
|
signature,
|
|
47
66
|
owsWallet,
|
|
67
|
+
agentId,
|
|
48
68
|
});
|
|
49
69
|
} catch (err: unknown) {
|
|
50
70
|
const msg = err instanceof Error ? err.message : "Failed to generate binding proof";
|
|
@@ -133,12 +153,8 @@ settings.post("/register-agent", async (c) => {
|
|
|
133
153
|
return c.json({ error: "Transaction succeeded but Registered event not found" }, 500);
|
|
134
154
|
}
|
|
135
155
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
where: { key: "agent_id" },
|
|
139
|
-
update: { value: String(agentId) },
|
|
140
|
-
create: { key: "agent_id", value: String(agentId) },
|
|
141
|
-
});
|
|
156
|
+
// Cache agentId in config.json (survives npx reinstalls, no Prisma dependency)
|
|
157
|
+
writeConfig({ agentId });
|
|
142
158
|
|
|
143
159
|
return c.json({
|
|
144
160
|
agentId,
|
|
@@ -161,6 +177,13 @@ settings.get("/link-status", async (c) => {
|
|
|
161
177
|
const address = getBaseAddress(wallet);
|
|
162
178
|
if (!address) return c.json({ linked: false, error: "No EVM address" });
|
|
163
179
|
|
|
180
|
+
// Check config.json cache first (survives npx reinstalls + RPC rate limits)
|
|
181
|
+
const config = readConfig();
|
|
182
|
+
if (config.agentId) {
|
|
183
|
+
return c.json({ linked: true, agentId: Number(config.agentId), owsWallet: address });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// RPC: try agentIdByWallet (for bound wallets)
|
|
164
187
|
try {
|
|
165
188
|
const agentId = await publicClient.readContract({
|
|
166
189
|
address: ERC_8004,
|
|
@@ -170,21 +193,12 @@ settings.get("/link-status", async (c) => {
|
|
|
170
193
|
}) as bigint;
|
|
171
194
|
|
|
172
195
|
if (agentId > 0n) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
owner = await publicClient.readContract({
|
|
177
|
-
address: ERC_8004,
|
|
178
|
-
abi: [{ type: "function", name: "ownerOf", stateMutability: "view", inputs: [{ name: "tokenId", type: "uint256" }], outputs: [{ name: "", type: "address" }] }] as const,
|
|
179
|
-
functionName: "ownerOf",
|
|
180
|
-
args: [agentId],
|
|
181
|
-
}) as string;
|
|
182
|
-
} catch { /* best effort */ }
|
|
183
|
-
return c.json({ linked: true, agentId: Number(agentId), owsWallet: address, owner });
|
|
196
|
+
writeConfig({ agentId: Number(agentId) });
|
|
197
|
+
return c.json({ linked: true, agentId: Number(agentId), owsWallet: address });
|
|
184
198
|
}
|
|
185
199
|
} catch { /* agentIdByWallet may revert if not bound */ }
|
|
186
200
|
|
|
187
|
-
//
|
|
201
|
+
// RPC fallback: check balanceOf (for owned but unbound NFTs)
|
|
188
202
|
try {
|
|
189
203
|
const balance = await publicClient.readContract({
|
|
190
204
|
address: ERC_8004,
|
|
@@ -194,7 +208,7 @@ settings.get("/link-status", async (c) => {
|
|
|
194
208
|
}) as bigint;
|
|
195
209
|
|
|
196
210
|
if (balance > 0n) {
|
|
197
|
-
// Try to get token ID
|
|
211
|
+
// Try to get token ID
|
|
198
212
|
let agentId: number | undefined;
|
|
199
213
|
try {
|
|
200
214
|
const tokenId = await publicClient.readContract({
|
|
@@ -204,11 +218,14 @@ settings.get("/link-status", async (c) => {
|
|
|
204
218
|
args: [address as `0x${string}`, 0n],
|
|
205
219
|
}) as bigint;
|
|
206
220
|
agentId = Number(tokenId);
|
|
207
|
-
} catch { /* ERC-721 Enumerable not supported
|
|
221
|
+
} catch { /* ERC-721 Enumerable not supported */ }
|
|
208
222
|
|
|
223
|
+
if (agentId !== undefined) {
|
|
224
|
+
writeConfig({ agentId });
|
|
225
|
+
}
|
|
209
226
|
return c.json({ linked: true, agentId, owsWallet: address });
|
|
210
227
|
}
|
|
211
|
-
} catch { /*
|
|
228
|
+
} catch { /* RPC failed — rate limited or unavailable */ }
|
|
212
229
|
|
|
213
230
|
return c.json({ linked: false, owsWallet: address });
|
|
214
231
|
} catch (err: unknown) {
|
|
@@ -18,7 +18,7 @@ export function Settings({ token, onLogout }: { token: string; onLogout: () => v
|
|
|
18
18
|
|
|
19
19
|
// Link to PlotLink (binding proof for DB link)
|
|
20
20
|
const [humanWallet, setHumanWallet] = useState("");
|
|
21
|
-
const [bindingResult, setBindingResult] = useState<{ message: string; signature: string; owsWallet: string } | null>(null);
|
|
21
|
+
const [bindingResult, setBindingResult] = useState<{ message: string; signature: string; owsWallet: string; agentId?: number } | null>(null);
|
|
22
22
|
const [generating, setGenerating] = useState(false);
|
|
23
23
|
const [bindingError, setBindingError] = useState<string | null>(null);
|
|
24
24
|
const [copied, setCopied] = useState<"signature" | "wallet" | null>(null);
|
|
@@ -268,8 +268,24 @@ export function Settings({ token, onLogout }: { token: string; onLogout: () => v
|
|
|
268
268
|
</button>
|
|
269
269
|
</div>
|
|
270
270
|
</div>
|
|
271
|
+
{bindingResult.agentId && (
|
|
272
|
+
<div>
|
|
273
|
+
<label className="text-muted text-xs block mb-1">Agent ID</label>
|
|
274
|
+
<div className="relative">
|
|
275
|
+
<div className="bg-surface border-border rounded border p-2 text-xs font-mono text-foreground pr-16">
|
|
276
|
+
{bindingResult.agentId}
|
|
277
|
+
</div>
|
|
278
|
+
<button
|
|
279
|
+
onClick={() => copyToClipboard(String(bindingResult.agentId), "agentId")}
|
|
280
|
+
className="absolute top-1 right-1 text-xs px-2 py-1 rounded border border-border text-muted hover:text-accent hover:border-accent transition-colors"
|
|
281
|
+
>
|
|
282
|
+
{copied === "agentId" ? "Copied!" : "Copy"}
|
|
283
|
+
</button>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
)}
|
|
271
287
|
<p className="text-xs text-accent">
|
|
272
|
-
Now go to plotlink.xyz/agents and paste
|
|
288
|
+
Now go to plotlink.xyz/agents and paste the values in the "Link AI Writer" section.
|
|
273
289
|
</p>
|
|
274
290
|
</div>
|
|
275
291
|
)}
|