Skip to main content

semora_cli/
args.rs

1//! Command-line arguments.
2
3use std::path::PathBuf;
4
5use clap::{Args, Parser, Subcommand};
6
7#[derive(Debug, Parser)]
8#[command(
9    name = "semora",
10    version,
11    about = "Publish Markdown and HTML to Semora and hear back from the page",
12    after_help = "Configuration: SEMORA_URL, SEMORA_API_KEY, SEMORA_DEFAULT_TAGS, SEMORA_WAIT_TIMEOUT \
13                  (environment or a .env file in this folder or a parent), or `semora login`.\n\
14                  Exit codes: 0 ok, 1 error, 2 timeout, 3 auth error, 4 not found."
15)]
16pub struct Cli {
17    /// Server URL (default: SEMORA_URL, config.toml, or the stored login)
18    #[arg(long, global = true, value_name = "URL")]
19    pub server: Option<String>,
20    /// API key (default: SEMORA_API_KEY, then the stored login)
21    #[arg(long, global = true, value_name = "KEY", hide_env_values = true)]
22    pub api_key: Option<String>,
23    /// Print JSON instead of text
24    #[arg(long, global = true)]
25    pub json: bool,
26    #[command(subcommand)]
27    pub cmd: Cmd,
28}
29
30#[derive(Debug, Subcommand)]
31pub enum Cmd {
32    /// Sign in through the browser and store an API key for this machine
33    Login {
34        /// Print the URL instead of opening a browser; paste the code shown after approval
35        #[arg(long)]
36        no_browser: bool,
37        /// Name for the new key (default: cli@HOSTNAME)
38        #[arg(long)]
39        name: Option<String>,
40    },
41    /// Revoke and forget the stored key
42    Logout,
43    /// Show the server, user and key in use
44    Whoami,
45    /// Publish a file or folder; replaces the remembered document if there is one
46    Push(PushArgs),
47    /// Re-push whenever files change (debounced 500 ms)
48    Watch(WatchArgs),
49    /// Block until feedback arrives from the page
50    Wait(WaitArgs),
51    /// Show a message in the open page
52    Say {
53        /// Document id or URL
54        doc: String,
55        /// Text to show
56        text: String,
57        /// Event id this message answers
58        #[arg(long)]
59        reply_to: Option<i64>,
60    },
61    /// List or search your documents
62    Ls {
63        /// Full-text search
64        #[arg(short = 'q', long = "query")]
65        query: Option<String>,
66        /// Only documents with this tag
67        #[arg(long)]
68        tag: Option<String>,
69        /// md, html or bundle
70        #[arg(long)]
71        kind: Option<String>,
72        #[arg(long, default_value_t = 50)]
73        limit: u32,
74    },
75    /// Metadata, files and revisions of a document
76    Show {
77        /// Document id or URL
78        doc: String,
79    },
80    /// Download raw content
81    Get {
82        /// Document id or URL
83        doc: String,
84        /// File inside a bundle (default: the entry page)
85        path: Option<String>,
86        /// Revision (default: latest)
87        #[arg(long)]
88        rev: Option<i64>,
89        /// Write to a file instead of stdout
90        #[arg(short = 'o', long = "output")]
91        output: Option<PathBuf>,
92    },
93    /// Edit tags: `+add -remove` (a bare name adds)
94    Tag {
95        /// Document id or URL
96        doc: String,
97        #[arg(required = true, num_args = 1.., allow_hyphen_values = true)]
98        changes: Vec<String>,
99    },
100    /// Delete a document
101    Rm {
102        /// Document id or URL
103        doc: String,
104    },
105    /// Open a document in the browser
106    Open {
107        /// Document id or URL
108        doc: String,
109    },
110    /// Manage your API keys
111    #[command(subcommand)]
112    Keys(KeysCmd),
113    /// Print a short usage guide to paste into an agent's instructions
114    Skill,
115}
116
117#[derive(Debug, Args)]
118pub struct PushArgs {
119    /// File (.md or .html) or folder (becomes a bundle)
120    pub path: PathBuf,
121    /// Replace this document instead of the remembered one
122    #[arg(long)]
123    pub id: Option<String>,
124    /// Always create a new document
125    #[arg(long, conflicts_with = "id")]
126    pub new: bool,
127    #[arg(long)]
128    pub title: Option<String>,
129    /// Comma-separated tags (repeatable)
130    #[arg(long = "tag", value_name = "TAGS")]
131    pub tags: Vec<String>,
132    /// Revision message
133    #[arg(short = 'm', long = "message")]
134    pub message: Option<String>,
135    /// Entry page of a bundle
136    #[arg(long)]
137    pub entry: Option<String>,
138    /// key=value stored on the document (repeatable)
139    #[arg(long = "meta", value_name = "KEY=VALUE")]
140    pub meta: Vec<String>,
141    /// After pushing, wait for feedback (same as `semora wait`)
142    #[arg(long)]
143    pub wait: bool,
144    #[command(flatten)]
145    pub wait_opts: WaitOpts,
146}
147
148#[derive(Debug, Args, Clone, Default)]
149pub struct WaitOpts {
150    /// Seconds to wait (default: SEMORA_WAIT_TIMEOUT or 900)
151    #[arg(long)]
152    pub timeout: Option<u64>,
153    /// Only these kinds: comment,drawing,page_event,message
154    #[arg(long)]
155    pub kinds: Option<String>,
156    /// Keep streaming events as JSON lines instead of exiting
157    #[arg(long)]
158    pub follow: bool,
159    /// Seconds to keep collecting after the first event
160    #[arg(long, default_value_t = 3.0)]
161    pub collect: f64,
162    /// Folder for downloaded screenshots (default: a temp folder)
163    #[arg(long)]
164    pub save_dir: Option<PathBuf>,
165}
166
167#[derive(Debug, Args)]
168pub struct WaitArgs {
169    /// Document id, page URL or websocket URL
170    pub doc: String,
171    #[command(flatten)]
172    pub opts: WaitOpts,
173}
174
175#[derive(Debug, Args)]
176pub struct WatchArgs {
177    /// File or folder to watch
178    pub path: PathBuf,
179    /// Document to replace (default: the remembered one)
180    #[arg(long)]
181    pub id: Option<String>,
182    #[arg(long)]
183    pub title: Option<String>,
184    #[arg(long = "tag", value_name = "TAGS")]
185    pub tags: Vec<String>,
186    #[arg(long)]
187    pub entry: Option<String>,
188}
189
190#[derive(Debug, Subcommand)]
191pub enum KeysCmd {
192    /// List your keys
193    Ls,
194    /// Create a key and print it once
195    Create {
196        name: String,
197        /// Comma-separated scopes (default: all)
198        #[arg(long)]
199        scopes: Option<String>,
200        /// Expire after this many days
201        #[arg(long)]
202        expires_in_days: Option<u32>,
203    },
204    /// Revoke a key by id
205    Revoke { id: i64 },
206}