diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | adenosine-cli/README.md | 79 | ||||
-rw-r--r-- | adenosine-cli/src/bin/adenosine.rs | 82 | ||||
-rw-r--r-- | extra/adenosine.1 | 216 | ||||
-rw-r--r-- | extra/adenosine.1.md | 235 | ||||
-rw-r--r-- | extra/adenosine.1.scdoc | 168 |
6 files changed, 747 insertions, 40 deletions
@@ -30,11 +30,14 @@ completions: build ## generate shell completions ./target/debug/adenosine --shell-completions bash status > extra/adenosine.bash_completions ./target/debug/adenosine --shell-completions bash status > extra/adenosine.zsh_completions -extra/adenosine.1: extra/adenosine.1.scdoc +extra/%.1: extra/%.1.scdoc scdoc < extra/adenosine.1.scdoc > extra/adenosine.1 +extra/adenosine.1.md: extra/adenosine.1 + pandoc extra/adenosine.1 -o extra/adenosine.1.md + .PHONY: manpage -manpage: extra/adenosine.1 ## Rebuild manpage using scdoc +manpage: extra/adenosine.1.md ## Rebuild manpages using scdoc and pandoc .PHONY: deb deb: ## Build debian packages (.deb files) diff --git a/adenosine-cli/README.md b/adenosine-cli/README.md index 30fb999..6b526e8 100644 --- a/adenosine-cli/README.md +++ b/adenosine-cli/README.md @@ -1,5 +1,78 @@ -`adenosine-cli`: atproto command-line client -============================================ +`adenosine-cli`: command-line client for AT protocol (atproto.com) +================================================================== + +This is a simple, enthusiast-grade CLI client for the work-in-progress AT +Protocol ([atproto.com](https://atproto.com)). Sort of like the `http` command +([HTTPie](https://httpie.io/)). + +It is an entirely "delegated" client, which means that it does not store or +cache any user content locally; everything works by making requests to a +Personal Data Server (PDS), which is usually a remote service. + +The only real utility of this tool currently is messing around with prototype +implementations, possibly while developing them. + +This client does not currently do any schema validation of Lexicons, either at +compile time or runtime (eg, dynamically fetching Lexicons). The Bluesky +Lexicon (bsky.app) is partially supported with helper commands. + + +## Example Commands + +Generic atproto/XRPC commands: + + # configure host we are talking to + export ATP_HOST=http://localhost:2583 + + # register a new account + adenosine account register -u voltaire.test -p bogus -e voltaire@example.com + export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOm1qMzVmcmo3Ymd1NmMyd3N6YnRha3QzZSIsImlhdCI6MTY2NjkzNzQxMn0.908LeimAXg1txMMH4k0_IcZAVJaFw1k7pVkScGMNcmE + + # or, login + unset ATP_AUTH_TOKEN + adenosine account login -u voltaire.test -p bogus + export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOm1qMzVmcmo3Ymd1NmMyd3N6YnRha3QzZSIsImlhdCI6MTY2NjkzNzcyMn0.7lXJ9xl6c-hrzUAR9YGLc4iFBn4nOJPbFX8TmYDHgdE + + # check config and setup + adenosine status + + # not implemented server-side (yet?) + adenosine account delete + adenosine account logout + + # create, list, delete, update some records + adenosine create com.example.thing a=123 b=cream + + adenosine ls at://hyde.test/app.bsky.post + # TODO: bug in serve implementation? says "Could not find user" + + adenosine delete at://did:plc:mj35frj7bgu6c2wszbtakt3e/app.bsky.post/3jg4dqseut22a + + adenosine describe + + adenosine get at://hyde.test/app.bsky.post/asdf + + adenosine resolve voltaire.test + + adenosine repo root + + adenosine repo root did:plc:mj35frj7bgu6c2wszbtakt3e + + adenosine repo export did:plc:mj35frj7bgu6c2wszbtakt3e > example_repo.car + + adenosine xrpc get app.bsky.getHomeFeed + + +Example commands in bsky.app Lexicon: + + adenosine bsky feed + + adenosine bsky follow did:plc:mj35frj7bgu6c2wszbtakt3e + + adenosine bsky profile + + adenosine bsky profile voltaire.test + + adenosine get at://did:plc:mj35frj7bgu6c2wszbtakt3e/app.bsky.post/3jg4dqseut22a -Like the `http` command (HTTPie), but for the AT Protocol (<https://atproto.com>). diff --git a/adenosine-cli/src/bin/adenosine.rs b/adenosine-cli/src/bin/adenosine.rs index 1d6e22d..0b79cf9 100644 --- a/adenosine-cli/src/bin/adenosine.rs +++ b/adenosine-cli/src/bin/adenosine.rs @@ -10,17 +10,21 @@ use structopt::StructOpt; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; #[derive(StructOpt)] -#[structopt(rename_all = "kebab-case", about = "CLI interface for AT Protocol")] +#[structopt( + rename_all = "kebab-case", + about = "command-line client for AT protocol (atproto.com)" +)] struct Opt { + /// HTTP(S) URL of Personal Data Server to connect to #[structopt( global = true, long = "--host", env = "ATP_HOST", - default_value = "https://localhost:8080" + default_value = "http://localhost:2583" )] atp_host: String, - // API auth tokens can be generated from the account page in the fatcat.wiki web interface + /// Authentication session token (JWT), for operations that need it #[structopt( global = true, long = "--auth-token", @@ -46,6 +50,11 @@ struct Opt { #[derive(StructOpt)] enum AccountCommand { /// Register a new account + /// + /// Does not (yet) support invite codes or email verification. + /// + /// This will return a JWT token that you should assign to the `ATP_AUTH_TOKEN` environment + /// variable. Register { #[structopt(long, short)] email: String, @@ -56,7 +65,12 @@ enum AccountCommand { #[structopt(long, short)] password: String, }, + /// Delete the currently logged-in account (danger!) Delete, + /// Create a new authenticated session + /// + /// This will return a JWT token that you should assign to the `ATP_AUTH_TOKEN` environment + /// variable Login { #[structopt(long, short)] username: String, @@ -64,23 +78,33 @@ enum AccountCommand { #[structopt(long, short)] password: String, }, + /// Deletes the current login session Logout, + /// Fetches account metadata for the current session Info, // TODO: CreateRevocationKey or CreateDid } #[derive(StructOpt)] enum RepoCommand { + /// Get the current 'root' commit for a DID + /// Root { + /// Repository DID, or uses the current session account did: Option<DidOrHost>, }, + /// Dump raw binary repository as CAR format to stdout Export { + /// Repository DID, or uses the current session account did: Option<DidOrHost>, + /// CID of a prior commit; only newer updates are included #[structopt(long)] from: Option<String>, }, + /// Read raw binary repository as CAR format from stdin, and import to PDS Import { // TODO: could accept either path or stdin? + /// Repository DID, or uses the current session account #[structopt(long)] did: Option<DidOrHost>, }, @@ -88,12 +112,18 @@ enum RepoCommand { #[derive(StructOpt)] enum BskyCommand { + /// Fetch the home feed, or account feed for a specific user Feed { name: Option<DidOrHost> }, + /// Fetch notification feed Notifications, + /// Create a new 'post' record Post { text: String }, + /// Create a 'repost' record for the target by AT URI Repost { uri: AtUri }, + /// Create a 'like' record for the target by AT URI Like { uri: AtUri }, // TODO: Repost { uri: String, }, + /// Create a 'follow' record for the target by AT URI Follow { uri: DidOrHost }, // TODO: Unfollow { uri: String, }, /* TODO: @@ -104,67 +134,77 @@ enum BskyCommand { name: String, }, */ + /// Display a profile record (or self if not provided) Profile { name: Option<DidOrHost> }, + /// Query by partial username SearchUsers { query: String }, } #[derive(StructOpt)] enum Command { + /// Summarize connection and authentication with API + Status, + + /// List all collections for a user, or all records for a collection + Ls { uri: AtUri }, + /// Fetch and display a generic record by full AT URI Get { uri: AtUri, + /// Specific version of record to fetch #[structopt(long)] cid: Option<String>, }, - - Ls { - uri: AtUri, - }, - + /// Generic record creation Create { collection: String, + + /// Set of object fields (keys) and values to construct the object from fields: Vec<ArgField>, }, + /// Generic mutation of an existing record Update { uri: AtUri, + + /// Set of object fields (keys) and values to update in the record fields: Vec<ArgField>, }, - Delete { - uri: AtUri, - }, + /// Generic record deletion + Delete { uri: AtUri }, - Describe { - name: Option<DidOrHost>, - }, + /// Print user/repository-level description (including DID document) + Describe { name: Option<DidOrHost> }, - Resolve { - name: DidOrHost, - }, + /// Have PDS resolve the DID for a username + Resolve { name: DidOrHost }, + /// Generic HTTP XRPC helper, printing any result Xrpc { + /// 'get' or 'post' method: XrpcMethod, + /// Name of method to call nsid: String, + /// Set of query parameters and body fields for the request fields: Vec<ArgField>, }, - /// Sub-commands for managing account + /// Manage user account and sessions Account { #[structopt(subcommand)] cmd: AccountCommand, }, + /// Direct access to binary repository content Repo { #[structopt(subcommand)] cmd: RepoCommand, }, + /// Helper commands for bsky.app Lexicon Bsky { #[structopt(subcommand)] cmd: BskyCommand, }, - - /// Summarize connection and authentication with API - Status, } fn main() -> Result<()> { diff --git a/extra/adenosine.1 b/extra/adenosine.1 index 7a490d3..dcac2dd 100644 --- a/extra/adenosine.1 +++ b/extra/adenosine.1 @@ -5,7 +5,7 @@ .nh .ad l .\" Begin generated content: -.TH "adenosine" "1" "2022-10-27" "adenosine Client Tool Manual Page" +.TH "adenosine" "1" "2022-10-28" "adenosine CLI Client Manual Page" .P .SH NAME .P @@ -17,18 +17,170 @@ adenosine [OPTIONS] <COMMAND> <ARGS> .P .SH DESCRIPTION .P -TODO +This is a simple, enthusiast-grade CLI client for the work-in-progress AT +Protocol (atproto.\&com).\& It is an entirely "delegated" client, which means that +it does not store or cache any user content locally; everything works by making +HTTP/XRPC requests to a Personal Data Server (PDS), which is usually a remote +service.\& +.P +The only real utility of this tool currently is messing around with prototype +implementations, possibly while developing them.\& +.P +This client does not currently do any schema validation of Lexicons, either at +compile time or runtime (eg, dynamically fetching Lexicons).\& The Bluesky +Lexicon (bsky.\&app) is partially supported with helper commands.\& +.P +.SH FIELD SYNTAX +.P +Several commands accept generic key/value fields which are passed through as +either query parameters or combined together in to a request body JSON object.\& +Escaping and other corner-cases aren't handled.\& +.P +\fB==\fR +.RS 4 +Query parameter.\& Key and Value both passed as strings.\& +.P +.RE +\fB=\fR +.RS 4 +Body fields, combined together as a JSON object.\& Keys are strings, values are parsed as JSON with fall-through to string type.\& +.P +.RE +For example, the argument list \fBlimit==25 title="regarding documentation" year=2022 tags='["blue", "green"]'\fR +would be interpreted as a single query parameter "limit" with value "25", and a +JSON object with keys "title" (string value), "year" (number value), "tags" +(array of strings).\& .P .SH COMMANDS .P -.SS Other Commands +\fBstatus\fR +.RS 4 +Summarizes configuration, and (TODO) connection and authentication to the API server.\& Useful for debugging.\& +.P +.RE +\fBdescribe\fR [name] +.RS 4 +Prints repository description fetched from PDS +.P +.RE +\fBresolve\fR <name> +.RS 4 +Has PDS resolve username to a DID +.P +.RE +.SS Generic XRPC Requests +.P +It is possible to construct and submit a generic XRPC request to the PDS, and +prints any response.\&See field syntax section above about query parameters and +body fields.\& Body fields only used for "post" requests.\& +.P +\fBxrpc\fR <"get"|"post"> <nsid> [fields]+ +.P +.SS Generic Record Interaction +.P +\fBls\fR <at-uri> +.RS 4 +List either collections under a repository, or records under a collection +.P +.RE +\fBget\fR <at-uri> +.RS 4 +Fetch and print a single record +.P +.RE +\fBcreate\fR <collection> [fields].\&.\&.\& +.RS 4 +Construct and create a generic record, printing the resulting AT-URI and CID +.P +.RE +\fBupdate\fR <at-uri> [fields].\&.\&.\& +.RS 4 +Fetch record, update fields, "put" back to same record path +.P +.RE +\fBdelete\fR <at-uri> +.RS 4 +Delete a single record from repository +.P +.RE +.SS Bluesky (bsky.app) +.P +\fBbsky feed\fR +.RS 4 +Fetch the home feed, or account feed for a specific user +.P +.RE +\fBbsky follow\fR +.RS 4 +Create a 'follow' record for the target by AT URI +.P +.RE +\fBbsky like\fR +.RS 4 +Create a 'like' record for the target by AT URI +.P +.RE +\fBbsky notifications\fR +.RS 4 +Fetch notification feed +.P +.RE +\fBbsky post\fR +.RS 4 +Create a new 'post' record +.P +.RE +\fBbsky profile\fR +.RS 4 +Display a profile record (or self if not provided) +.P +.RE +\fBbsky repost\fR +.RS 4 +Create a 'repost' record for the target by AT URI +.P +.RE +\fBbsky search-users\fR +.RS 4 +Query by partial username +.P +.P +.RE +.SS Account Management +.P +\fBaccount register --email <email> --password <password> --username <username>\fR .P +\fBaccount info\fR .RS 4 -\fBstatus\fR [--json] +Fetches account metadata for the current session +.P +.RE +\fBaccount login --password <password> --username <username>\fR +.RS 4 +Create a new authenticated session +.P +.RE +\fBaccount logout\fR +.RS 4 +Deletes the current login session +.P +.RE +.SS Raw Repository Management +.P +\fBrepo export [did]\fR +.RS 4 +Dump raw binary repository as CAR format to stdout +.P +.RE +\fBrepo import [did]\fR .RS 4 -Summarizes connection and authentication to the API server.\& Useful for debugging +Read raw binary repository as CAR format from stdin, and import to PDS .P .RE +\fBrepo root [did]\fR +.RS 4 +Get the current 'root' commit for a DID +.P .RE .SH OPTIONS .P @@ -52,6 +204,56 @@ By default, it'll only report errors.\& Passing `-v` one time also prints warnin .P \fB--auth-token <auth-token>\fR [env: ATP_AUTH_TOKEN] .P -.SH EXAMPLES +.SH GETTING STARTED .P -TODO +To start interacting with a PDS, set the `ATP_HOST` environment variable.\& Then +either register a test account, or create a new session for an existing +account, and save the JWT token to the `ATP_AUTH_TOKEN`: +.P +.nf +.RS 4 +# default port for bluesky-social/atproto implementation +export ATP_HOST=http://localhost:2583 + +# register a new account +adenosine account register -u voltaire\&.test -p bogus -e voltaire@example\&.com +{ + "did": "did:plc:yqtuksvatmmgngd5nkkw75hn", + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\&.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjMwNn0\&.MMQa4JIQdwvhy-rjJ0kO-z8-KdoOL0Lto9JtOkK-lwE", + "username": "voltaire\&.test" +} + +export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\&.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjMwNn0\&.MMQa4JIQdwvhy-rjJ0kO-z8-KdoOL0Lto9JtOkK-lwE + +# to clear the auth token env variable +unset ATP_AUTH_TOKEN + +# create a new session (login) for existing account +adenosine account login -u voltaire\&.test -p bogus +{ + "did": "did:plc:yqtuksvatmmgngd5nkkw75hn", + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\&.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjQxNX0\&.j2wcF1g9NxT_1AvYRiplNf_jtK6S81y3L38AkcBwOqY", + "name": "voltaire\&.test" +} + +export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\&.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjQxNX0\&.j2wcF1g9NxT_1AvYRiplNf_jtK6S81y3L38AkcBwOqY +.fi +.RE +.P +You could save the `ATP_HOST` and `ATP_AUTH_TOKEN` values in `~/.\&bashrc` so you +don't need to enter them every time.\& +.P +Now you can start posting and poking around: +.P +.nf +.RS 4 +adenosine bsky post "gruel again for breakfast" +{ + "cid": "bafyreig2aqlsg4arslck64wbo2hnhe6k2a4z3z2sjfzh3uapv3a4zjld7e", + "uri": "at://did:plc:yqtuksvatmmgngd5nkkw75hn/app\&.bsky\&.post/3jg5zkr322c2a" +} + +adenosine ls at://voltaire\&.test +app\&.bsky\&.post +.fi +.RE diff --git a/extra/adenosine.1.md b/extra/adenosine.1.md new file mode 100644 index 0000000..dc44e59 --- /dev/null +++ b/extra/adenosine.1.md @@ -0,0 +1,235 @@ +NAME +==== + +adenosine - command-line client for AT protocol (atproto.com) + +SYNOPSIS +======== + +adenosine \[OPTIONS\] \<COMMAND\> \<ARGS\> + +DESCRIPTION +=========== + +This is a simple, enthusiast-grade CLI client for the work-in-progress +AT Protocol (atproto.com). It is an entirely \"delegated\" client, which +means that it does not store or cache any user content locally; +everything works by making HTTP/XRPC requests to a Personal Data Server +(PDS), which is usually a remote service. + +The only real utility of this tool currently is messing around with +prototype implementations, possibly while developing them. + +This client does not currently do any schema validation of Lexicons, +either at compile time or runtime (eg, dynamically fetching Lexicons). +The Bluesky Lexicon (bsky.app) is partially supported with helper +commands. + +FIELD SYNTAX +============ + +Several commands accept generic key/value fields which are passed +through as either query parameters or combined together in to a request +body JSON object. Escaping and other corner-cases aren\'t handled. + +**==** + +> Query parameter. Key and Value both passed as strings. + +**=** + +> Body fields, combined together as a JSON object. Keys are strings, +> values are parsed as JSON with fall-through to string type. + +For example, the argument list **limit==25 title=\"regarding +documentation\" year=2022 tags=\'\[\"blue\", \"green\"\]\'** would be +interpreted as a single query parameter \"limit\" with value \"25\", and +a JSON object with keys \"title\" (string value), \"year\" (number +value), \"tags\" (array of strings). + +COMMANDS +======== + +**status** + +> Summarizes configuration, and (TODO) connection and authentication to +> the API server. Useful for debugging. + +**describe** \[name\] + +> Prints repository description fetched from PDS + +**resolve** \<name\> + +> Has PDS resolve username to a DID + +Generic XRPC Requests +--------------------- + +It is possible to construct and submit a generic XRPC request to the +PDS, and prints any response.See field syntax section above about query +parameters and body fields. Body fields only used for \"post\" requests. + +**xrpc** \<\"get\"\|\"post\"\> \<nsid\> \[fields\]+ + +Generic Record Interaction +-------------------------- + +**ls** \<at-uri\> + +> List either collections under a repository, or records under a +> collection + +**get** \<at-uri\> + +> Fetch and print a single record + +**create** \<collection\> \[fields\]\... + +> Construct and create a generic record, printing the resulting AT-URI +> and CID + +**update** \<at-uri\> \[fields\]\... + +> Fetch record, update fields, \"put\" back to same record path + +**delete** \<at-uri\> + +> Delete a single record from repository + +Bluesky (bsky.app) +------------------ + +**bsky feed** + +> Fetch the home feed, or account feed for a specific user + +**bsky follow** + +> Create a \'follow\' record for the target by AT URI + +**bsky like** + +> Create a \'like\' record for the target by AT URI + +**bsky notifications** + +> Fetch notification feed + +**bsky post** + +> Create a new \'post\' record + +**bsky profile** + +> Display a profile record (or self if not provided) + +**bsky repost** + +> Create a \'repost\' record for the target by AT URI + +**bsky search-users** + +> Query by partial username + +Account Management +------------------ + +**account register \--email \<email\> \--password \<password\> +\--username \<username\>** + +**account info** + +> Fetches account metadata for the current session + +**account login \--password \<password\> \--username \<username\>** + +> Create a new authenticated session + +**account logout** + +> Deletes the current login session + +Raw Repository Management +------------------------- + +**repo export \[did\]** + +> Dump raw binary repository as CAR format to stdout + +**repo import \[did\]** + +> Read raw binary repository as CAR format from stdin, and import to PDS + +**repo root \[did\]** + +> Get the current \'root\' commit for a DID + +OPTIONS +======= + +**-h, \--help** + +> Prints help information + +**-V, \--version** + +> Prints version information + +**-v, \--verbose** + +> Pass many times for more log output By default, it\'ll only report +> errors. Passing \`-v\` one time also prints warnings, \`-vv\` enables +> info logging, \`-vvv\` debug, and \`-vvvv\` trace. + +**\--host \<atp-host\>** \[env: ATP\_HOST\] + +**\--auth-token \<auth-token\>** \[env: ATP\_AUTH\_TOKEN\] + +GETTING STARTED +=============== + +To start interacting with a PDS, set the \`ATP\_HOST\` environment +variable. Then either register a test account, or create a new session +for an existing account, and save the JWT token to the +\`ATP\_AUTH\_TOKEN\`: + + # default port for bluesky-social/atproto implementation + export ATP_HOST=http://localhost:2583 + + # register a new account + adenosine account register -u voltaire.test -p bogus -e voltaire@example.com + { + "did": "did:plc:yqtuksvatmmgngd5nkkw75hn", + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjMwNn0.MMQa4JIQdwvhy-rjJ0kO-z8-KdoOL0Lto9JtOkK-lwE", + "username": "voltaire.test" + } + + export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjMwNn0.MMQa4JIQdwvhy-rjJ0kO-z8-KdoOL0Lto9JtOkK-lwE + + # to clear the auth token env variable + unset ATP_AUTH_TOKEN + + # create a new session (login) for existing account + adenosine account login -u voltaire.test -p bogus + { + "did": "did:plc:yqtuksvatmmgngd5nkkw75hn", + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjQxNX0.j2wcF1g9NxT_1AvYRiplNf_jtK6S81y3L38AkcBwOqY", + "name": "voltaire.test" + } + + export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjQxNX0.j2wcF1g9NxT_1AvYRiplNf_jtK6S81y3L38AkcBwOqY + +You could save the \`ATP\_HOST\` and \`ATP\_AUTH\_TOKEN\` values in +\`\~/.bashrc\` so you don\'t need to enter them every time. + +Now you can start posting and poking around: + + adenosine bsky post "gruel again for breakfast" + { + "cid": "bafyreig2aqlsg4arslck64wbo2hnhe6k2a4z3z2sjfzh3uapv3a4zjld7e", + "uri": "at://did:plc:yqtuksvatmmgngd5nkkw75hn/app.bsky.post/3jg5zkr322c2a" + } + + adenosine ls at://voltaire.test + app.bsky.post diff --git a/extra/adenosine.1.scdoc b/extra/adenosine.1.scdoc index 2934e19..2c55801 100644 --- a/extra/adenosine.1.scdoc +++ b/extra/adenosine.1.scdoc @@ -1,4 +1,4 @@ -adenosine(1) "adenosine Client Tool Manual Page" +adenosine(1) "adenosine CLI Client Manual Page" # NAME @@ -10,14 +10,122 @@ adenosine [OPTIONS] <COMMAND> <ARGS> # DESCRIPTION -TODO +This is a simple, enthusiast-grade CLI client for the work-in-progress AT +Protocol (atproto.com). It is an entirely "delegated" client, which means that +it does not store or cache any user content locally; everything works by making +HTTP/XRPC requests to a Personal Data Server (PDS), which is usually a remote +service. + +The only real utility of this tool currently is messing around with prototype +implementations, possibly while developing them. + +This client does not currently do any schema validation of Lexicons, either at +compile time or runtime (eg, dynamically fetching Lexicons). The Bluesky +Lexicon (bsky.app) is partially supported with helper commands. + +# FIELD SYNTAX + +Several commands accept generic key/value fields which are passed through as +either query parameters or combined together in to a request body JSON object. +Escaping and other corner-cases aren't handled. + +*==* + Query parameter. Key and Value both passed as strings. + +*=* + Body fields, combined together as a JSON object. Keys are strings, values are parsed as JSON with fall-through to string type. + +For example, the argument list *limit==25 title="regarding documentation" year=2022 tags='["blue", "green"]'* +would be interpreted as a single query parameter "limit" with value "25", and a +JSON object with keys "title" (string value), "year" (number value), "tags" +(array of strings). # COMMANDS -## Other Commands +*status* + Summarizes configuration, and (TODO) connection and authentication to the API server. Useful for debugging. + +*describe* [name] + Prints repository description fetched from PDS + +*resolve* <name> + Has PDS resolve username to a DID + +## Generic XRPC Requests + +It is possible to construct and submit a generic XRPC request to the PDS, and +prints any response.See field syntax section above about query parameters and +body fields. Body fields only used for "post" requests. + +*xrpc* <"get"|"post"> <nsid> [fields]+ + +## Generic Record Interaction + +*ls* <at-uri> + List either collections under a repository, or records under a collection + +*get* <at-uri> + Fetch and print a single record + +*create* <collection> [fields]... + Construct and create a generic record, printing the resulting AT-URI and CID + +*update* <at-uri> [fields]... + Fetch record, update fields, "put" back to same record path + +*delete* <at-uri> + Delete a single record from repository + +## Bluesky (bsky.app) + +*bsky feed* + Fetch the home feed, or account feed for a specific user + +*bsky follow* + Create a 'follow' record for the target by AT URI + +*bsky like* + Create a 'like' record for the target by AT URI + +*bsky notifications* + Fetch notification feed - *status* [--json] - Summarizes connection and authentication to the API server. Useful for debugging +*bsky post* + Create a new 'post' record + +*bsky profile* + Display a profile record (or self if not provided) + +*bsky repost* + Create a 'repost' record for the target by AT URI + +*bsky search-users* + Query by partial username + + +## Account Management + +*account register --email <email> --password <password> --username <username>* + +*account info* + Fetches account metadata for the current session + +*account login --password <password> --username <username>* + Create a new authenticated session + +*account logout* + Deletes the current login session + +## Raw Repository Management + +*repo export [did]* + Dump raw binary repository as CAR format to stdout + +*repo import [did]* + Read raw binary repository as CAR format from stdin, and import to PDS + +*repo root [did]* + Get the current 'root' commit for a DID # OPTIONS @@ -35,6 +143,52 @@ TODO *--auth-token <auth-token>* [env: ATP_AUTH_TOKEN] -# EXAMPLES +# GETTING STARTED + +To start interacting with a PDS, set the `ATP_HOST` environment variable. Then +either register a test account, or create a new session for an existing +account, and save the JWT token to the `ATP_AUTH_TOKEN`: + +``` +# default port for bluesky-social/atproto implementation +export ATP_HOST=http://localhost:2583 + +# register a new account +adenosine account register -u voltaire.test -p bogus -e voltaire@example.com +{ + "did": "did:plc:yqtuksvatmmgngd5nkkw75hn", + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjMwNn0.MMQa4JIQdwvhy-rjJ0kO-z8-KdoOL0Lto9JtOkK-lwE", + "username": "voltaire.test" +} + +export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjMwNn0.MMQa4JIQdwvhy-rjJ0kO-z8-KdoOL0Lto9JtOkK-lwE + +# to clear the auth token env variable +unset ATP_AUTH_TOKEN + +# create a new session (login) for existing account +adenosine account login -u voltaire.test -p bogus +{ + "did": "did:plc:yqtuksvatmmgngd5nkkw75hn", + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjQxNX0.j2wcF1g9NxT_1AvYRiplNf_jtK6S81y3L38AkcBwOqY", + "name": "voltaire.test" +} + +export ATP_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6cGxjOnlxdHVrc3ZhdG1tZ25nZDVua2t3NzVobiIsImlhdCI6MTY2Njk5NjQxNX0.j2wcF1g9NxT_1AvYRiplNf_jtK6S81y3L38AkcBwOqY +``` + +You could save the `ATP_HOST` and `ATP_AUTH_TOKEN` values in `~/.bashrc` so you +don't need to enter them every time. + +Now you can start posting and poking around: + +``` +adenosine bsky post "gruel again for breakfast" +{ + "cid": "bafyreig2aqlsg4arslck64wbo2hnhe6k2a4z3z2sjfzh3uapv3a4zjld7e", + "uri": "at://did:plc:yqtuksvatmmgngd5nkkw75hn/app.bsky.post/3jg5zkr322c2a" +} -TODO +adenosine ls at://voltaire.test +app.bsky.post +``` |