open-source, lexicon-agnostic PDS for AI agents. welcome-mat enrollment, AT Proto federation.
agents atprotocol pds cloudflare
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix AT Protocol spec compliance: describeRepo handle, signup 409, getRepoStatus status

+29 -7
+7
src/account-do.ts
··· 193 193 did: string; 194 194 collections: string[]; 195 195 cid: string; 196 + handle: string; 197 + handleIsCorrect: boolean; 196 198 }> { 197 199 const repo = await this.getRepo(); 198 200 const storage = await this.getStorage(); 201 + const state = this.storage!.getState(); 199 202 return { 200 203 did: repo.did, 201 204 collections: storage.getCollections(), 202 205 cid: repo.cid.toString(), 206 + handle: state!.handle, 207 + handleIsCorrect: true, 203 208 }; 204 209 } 205 210 ··· 527 532 async rpcGetRepoStatus(): Promise<{ 528 533 did: string; 529 534 active: boolean; 535 + status: string; 530 536 rev: string | null; 531 537 } | null> { 532 538 await this.ensureStorageInitialized(); ··· 535 541 return { 536 542 did: state.did, 537 543 active: state.active === 1, 544 + status: state.active === 1 ? "active" : "deactivated", 538 545 rev: state.rev, 539 546 }; 540 547 }
+13 -6
src/worker.ts
··· 154 154 jwkThumbprint: body.jwkThumbprint, 155 155 }); 156 156 157 - await insertAccount(env.DIRECTORY, { 158 - did, 159 - handle, 160 - doId: doId.toString(), 161 - jwkThumbprint: body.jwkThumbprint, 162 - }); 157 + try { 158 + await insertAccount(env.DIRECTORY, { 159 + did, 160 + handle, 161 + doId: doId.toString(), 162 + jwkThumbprint: body.jwkThumbprint, 163 + }); 164 + } catch (e: unknown) { 165 + if (e instanceof Error && e.message.includes("UNIQUE constraint failed")) { 166 + return c.json({ error: "HandleAlreadyTaken", message: "Handle is already in use" }, 409); 167 + } 168 + throw e; 169 + } 163 170 164 171 return c.json({ did, handle }); 165 172 });
+2
test/account-do.test.ts
··· 128 128 expect(desc.did).toBe(TEST_DID); 129 129 expect(desc.collections).toContain("app.bsky.feed.post"); 130 130 expect(desc.cid).toBeTruthy(); 131 + expect(desc.handle).toBe(TEST_HANDLE); 132 + expect(desc.handleIsCorrect).toBe(true); 131 133 }); 132 134 }); 133 135
+4 -1
test/integration.test.ts
··· 324 324 body: JSON.stringify({ handle: duplicateHandle }), 325 325 }), 326 326 ); 327 - expect(duplicateHandleResponse.status).not.toBe(200); 327 + expect(duplicateHandleResponse.status).toBe(409); 328 + const dupBody = await duplicateHandleResponse.json() as { error: string; message: string }; 329 + expect(dupBody.error).toBe("HandleAlreadyTaken"); 330 + expect(dupBody.message).toBe("Handle is already in use"); 328 331 } finally { 329 332 fetchSpy.mockRestore(); 330 333 }
+3
test/worker-routes.test.ts
··· 141 141 expect(await describeRepo.json()).toMatchObject({ 142 142 did, 143 143 collections: ["app.bsky.feed.post"], 144 + handle: expect.any(String), 145 + handleIsCorrect: true, 144 146 }); 145 147 146 148 const latestCommit = await worker.fetch( ··· 159 161 expect(await repoStatus.json()).toMatchObject({ 160 162 did, 161 163 active: true, 164 + status: "active", 162 165 }); 163 166 164 167 const repoExport = await worker.fetch(