this repo has no description
0
fork

Configure Feed

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

CLI: Accept 2FA code, remove leftover @atproto/crypto use

futurGH eb7b1628 09c55f95

+40 -44
+21 -16
src/bin.ts
··· 5 5 declareLabeler, 6 6 deleteLabelerDeclaration, 7 7 getLabelerLabelDefinitions, 8 + LoginCredentials, 8 9 plcClearLabeler, 9 10 plcRequestToken, 10 11 plcSetupLabeler, ··· 16 17 const [command, subcommand] = args; 17 18 18 19 if (command === "setup" || command === "clear") { 19 - const { did, password, pds } = await promptAuthInfo(); 20 + const credentials = await promptCredentials(); 20 21 21 - await plcRequestToken({ identifier: did, password, pds }); 22 + await plcRequestToken(credentials); 22 23 23 24 const { plcToken } = await prompt({ 24 25 type: "text", ··· 44 45 }], { onCancel: () => process.exit(1) }); 45 46 46 47 const operation = await plcSetupLabeler({ 47 - did, 48 - password, 49 - pds, 48 + ...credentials, 50 49 plcToken, 51 50 endpoint, 52 51 privateKey, ··· 66 65 ); 67 66 const labelDefinitions = await promptLabelDefinitions(); 68 67 if (labelDefinitions.length) { 69 - await declareLabeler({ identifier: did, password, pds }, labelDefinitions, true); 68 + await declareLabeler(credentials, labelDefinitions, true); 70 69 } else { 71 70 console.log( 72 71 "No labels were defined. You can use the `label add` command later to define new labels.", ··· 79 78 } 80 79 } else { 81 80 try { 82 - await plcClearLabeler({ did, password, pds, plcToken }); 83 - await deleteLabelerDeclaration({ identifier: did, password, pds }); 81 + await plcClearLabeler({ ...credentials, plcToken }); 82 + await deleteLabelerDeclaration(credentials); 84 83 console.log("Labeler data cleared."); 85 84 } catch (error) { 86 85 console.error("Error setting up labeler:", error); 87 86 } 88 87 } 89 88 } else if (command === "label" && (subcommand === "add" || subcommand === "delete")) { 90 - const { did, password, pds } = await promptAuthInfo(); 91 - const labelDefinitions = await getLabelerLabelDefinitions({ identifier: did, password, pds }) 92 - ?? []; 89 + const credentials = await promptCredentials(); 90 + const labelDefinitions = await getLabelerLabelDefinitions(credentials) ?? []; 93 91 94 92 if (subcommand === "add") { 95 93 console.log( ··· 98 96 const newDefinitions = await promptLabelDefinitions(); 99 97 if (newDefinitions.length) { 100 98 const definitions = [...labelDefinitions, ...newDefinitions]; 101 - await setLabelerLabelDefinitions({ identifier: did, password, pds }, definitions); 99 + await setLabelerLabelDefinitions(credentials, definitions); 102 100 console.log("Declared label(s):", definitions.map((d) => d.identifier).join(", ")); 103 101 } else { 104 102 console.log( ··· 128 126 129 127 try { 130 128 if (definitions.length) { 131 - await setLabelerLabelDefinitions({ identifier: did, password, pds }, definitions); 129 + await setLabelerLabelDefinitions(credentials, definitions); 132 130 console.log("Deleted label(s):", identifiers.join(", ")); 133 131 } else { 134 132 console.log("No labels were selected. Nothing to delete."); ··· 146 144 console.log(" label delete - Remove label declarations from a labeler account."); 147 145 } 148 146 149 - async function promptAuthInfo() { 147 + async function promptCredentials(): Promise<LoginCredentials> { 150 148 let did: string | undefined; 151 149 while (!did) { 152 150 const { did: didOrHandle } = await prompt({ ··· 164 162 } 165 163 } 166 164 167 - const { password, pds } = await prompt([{ 165 + const { password, pds, code } = await prompt([{ 168 166 type: "password", 169 167 name: "password", 170 168 message: "Account password (cannot be an app password):", ··· 174 172 message: "URL of the PDS where the account is located:", 175 173 initial: "https://bsky.social", 176 174 validate: (value) => value.startsWith("https://") || "Must be a valid HTTPS URL.", 175 + }, { 176 + type: "text", 177 + name: "code", 178 + message: "2FA code (leave blank if 2FA is not enabled):", 179 + initial: "", 177 180 }], { onCancel: () => process.exit(1) }); 178 181 179 - return { did, password, pds }; 182 + const credentials: LoginCredentials = { identifier: did, password, pds }; 183 + if (code) credentials.code = code.replaceAll(/\s+/g, ""); 184 + return credentials; 180 185 } 181 186 182 187 async function confirm(message: string) {
+14 -25
src/scripts/plc.ts
··· 1 1 import type { ComAtprotoIdentitySignPlcOperation } from "@atcute/client/lexicons"; 2 - import { Secp256k1Keypair } from "@atproto/crypto"; 2 + import { secp256k1 as k256 } from "@noble/curves/secp256k1"; 3 3 import * as ui8 from "uint8arrays"; 4 + import { formatDidKey, SECP256K1_JWT_ALG } from "../util/crypto.js"; 4 5 import { loginAgent, LoginCredentials } from "./util.js"; 5 6 6 7 /** Options for the {@link plcSetupLabeler} function. */ 7 - export interface PlcSetupLabelerOptions { 8 + export interface PlcSetupLabelerOptions extends LoginCredentials { 8 9 /** The HTTPS URL where the labeler is hosted. */ 9 10 endpoint: string; 10 11 ··· 14 15 */ 15 16 plcToken: string; 16 17 17 - /** The URL of the PDS where the labeler account is located, if different from bsky.social. */ 18 - pds?: string; 19 - /** The DID of the labeler account. */ 20 - did: string; 21 - /** The password of the labeler account. Cannot be an app password. */ 22 - password: string; 23 - 24 18 /** 25 19 * You may choose to provide your own hex-encoded secp256k1 signing key to use for the labeler. 26 20 * Leave this empty to generate a new keypair. ··· 31 25 } 32 26 33 27 /** Options for the {@link plcClearLabeler} function. */ 34 - export interface PlcClearLabelerOptions { 28 + export interface PlcClearLabelerOptions extends LoginCredentials { 35 29 /** 36 30 * The token to use to sign the PLC operation. 37 31 * If you don't have a token, first call {@link plcRequestToken} to receive one via email. 38 32 */ 39 33 plcToken: string; 40 - 41 - /** The URL of the PDS where the labeler account is located, if different from bsky.social. */ 42 - pds?: string; 43 - /** The DID of the labeler account. */ 44 - did: string; 45 - /** The password to the labeler account. Cannot be an app password. */ 46 - password: string; 47 34 } 48 35 49 36 /** ··· 58 45 export async function plcSetupLabeler(options: PlcSetupLabelerOptions) { 59 46 const { agent } = await loginAgent({ 60 47 pds: options.pds, 61 - identifier: options.did, 48 + identifier: options.identifier, 62 49 password: options.password, 63 50 }); 64 51 65 - const keypair = options.privateKey 66 - ? await Secp256k1Keypair.import(options.privateKey) 67 - : await Secp256k1Keypair.create({ exportable: true }); 52 + const privateKey = options.privateKey 53 + ? options.privateKey instanceof Uint8Array 54 + ? options.privateKey 55 + : ui8.fromString(options.privateKey, "hex") 56 + : k256.utils.randomPrivateKey(); 68 57 69 - const keyDid = keypair.did(); 58 + const keyDid = formatDidKey(SECP256K1_JWT_ALG, privateKey); 70 59 71 60 const operation: ComAtprotoIdentitySignPlcOperation.Input = {}; 72 61 ··· 114 103 }); 115 104 116 105 if (!options.privateKey && operation.verificationMethods) { 117 - const privateKey = ui8.toString(await keypair.export(), "hex"); 106 + const privateKeyString = ui8.toString(privateKey, "hex"); 118 107 console.log( 119 108 "This is your labeler's signing key. It will be needed to sign any labels you create.", 120 109 "You will not be able to retrieve this key again, so make sure to save it somewhere safe.", 121 110 "If you lose this key, you can run this again to generate a new one.", 122 111 ); 123 - console.log("Signing key:", privateKey); 112 + console.log("Signing key:", privateKeyString); 124 113 } 125 114 126 115 return operation; ··· 134 123 export async function plcClearLabeler(options: PlcClearLabelerOptions) { 135 124 const { agent } = await loginAgent({ 136 125 pds: options.pds, 137 - identifier: options.did, 126 + identifier: options.identifier, 138 127 password: options.password, 139 128 }); 140 129
+2
src/scripts/util.ts
··· 7 7 identifier: string; 8 8 /** The account password. */ 9 9 password: string; 10 + /** The 2FA code, if 2FA is enabled. */ 11 + code?: string; 10 12 } 11 13 12 14 let xrpc: XRPC | undefined;
+3 -3
src/util/crypto.ts
··· 9 9 const BASE58_MULTIBASE_PREFIX = "z"; 10 10 const DID_KEY_PREFIX = "did:key:"; 11 11 12 - const P256_JWT_ALG = "ES256"; 13 - const SECP256K1_JWT_ALG = "ES256K"; 12 + export const P256_JWT_ALG = "ES256"; 13 + export const SECP256K1_JWT_ALG = "ES256K"; 14 14 15 15 const didToSigningKeyCache = new Map<string, { key: string; expires: number }>(); 16 16 ··· 193 193 * @param jwtAlg The JWT algorithm used by the signing key. 194 194 * @param keyBytes The bytes of the pubkey. 195 195 */ 196 - const formatDidKey = ( 196 + export const formatDidKey = ( 197 197 jwtAlg: typeof P256_JWT_ALG | typeof SECP256K1_JWT_ALG, 198 198 keyBytes: Uint8Array, 199 199 ): string => DID_KEY_PREFIX + formatMultikey(jwtAlg, keyBytes);