{"id":50830608,"url":"https://github.com/simenandre/robin-cli","last_synced_at":"2026-06-13T22:31:09.455Z","repository":{"id":355786696,"uuid":"1223359555","full_name":"simenandre/robin-cli","owner":"simenandre","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-31T18:34:16.000Z","size":125,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T20:18:19.938Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simenandre.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-28T08:47:08.000Z","updated_at":"2026-05-31T18:34:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/simenandre/robin-cli","commit_stats":null,"previous_names":["simenandre/robin-cli"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/simenandre/robin-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simenandre%2Frobin-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simenandre%2Frobin-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simenandre%2Frobin-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simenandre%2Frobin-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simenandre","download_url":"https://codeload.github.com/simenandre/robin-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simenandre%2Frobin-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34303279,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-13T22:31:08.532Z","updated_at":"2026-06-13T22:31:09.447Z","avatar_url":"https://github.com/simenandre.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# robin\n\nBook [Robin](https://robinpowered.com) meeting rooms from your terminal.\n\nDesigned for users who don't have admin-issued API tokens. Authenticates\nwith your normal Robin email/password using the same `/auth/users`\nendpoint the web dashboard uses internally.\n\n```sh\n$ robin book\n✓ booked Meeting Room 5 — Wed Apr 29 12:00 to 13:00 (1h0m0s)\n```\n\n## Install\n\n```sh\ngo install github.com/simenandre/robin-cli/cmd/robin@latest\n```\n\nRequires Go 1.26+.\n\n## Quick start\n\n```sh\nrobin init     # save org slug, email, password\nrobin login    # exchange them for a cached access token\nrobin book     # book the best priority room right now\n               # (also available as 'robin now')\n```\n\nCredentials and the access token live under your OS config directory\n(macOS `~/Library/Application Support/robin/`, Linux `~/.config/robin/`),\nmode `0600`. See [Config](#config) below to enable auto-pick.\n\n## Commands\n\n```\nrobin init                          save credentials to a config file\nrobin login / logout                manage the cached access token\nrobin whoami                        print the current user\n\nrobin orgs                          list organizations\nrobin locations                     list locations across orgs\nrobin spaces [-l LOCATION]          list bookable spaces\n\nrobin book                          book a meeting room (alias: robin now)\n```\n\nEvery list command supports `--json` for machine-readable output. Every\ncommand supports `-v` / `--verbose`, `-q` / `--quiet`, `--no-color`,\n`--no-input`, `--help`. Run `robin help \u003ccommand\u003e` for the full surface\nof any subcommand.\n\n### `robin book` — book a meeting room\n\nOne command, two modes. Mode is picked by whether `--space` is set.\n\n**Auto-pick** (no `--space`) — finds the best room based on your\n`quick_book` config. Falls back to other meeting rooms if every priority\nroom is busy.\n\n```sh\nrobin book                                # best room, starting now\nrobin book --start \"tomorrow 9am\"         # best room tomorrow at 9\nrobin book --start \"in 2 hours\"           # best room 2 hours from now\nrobin book --start now --duration 1h      # exactly 1h, starting now\nrobin book --max 60                       # cap booking length at 60 min\nrobin book --prioritize-length            # take longest slot anywhere\nrobin book --dry-run                      # see the pick without booking\n```\n\n`robin now` is a Cobra alias of `robin book` — same command, more\nmemorable for the daily-driver case.\n\n**Specific** (`--space ID`) — books that exact room. Requires `--start`\nplus either `--duration` or `--end`. Confirms before posting unless\n`--yes`.\n\n```sh\nrobin book --space 172344 --start \"tomorrow 9am\" --duration 30m\nrobin book --space 172344 --start \"14:00\" --duration 1h --yes\nrobin book --space 172344 \\\n           --start \"2026-04-29T14:00:00+02:00\" \\\n           --end   \"2026-04-29T15:00:00+02:00\" --title \"Sync\"\n```\n\n#### Time inputs\n\n`--start`, `--end`, and `--duration` resolve in this order:\n\n1. `now` or empty → current time\n2. Go duration: `1h`, `30m`, `2h30m`\n3. Clock time today: `14:00`, `09:30`\n4. Strict datetime: `2026-04-29 09:00`, RFC3339\n5. **Natural language** (future-direction): `tomorrow`, `tomorrow 9am`,\n   `in 2 hours`, `monday 9am`, `next friday`, `9am`, `yesterday`.\n   Powered by [`go-naturaldate`](https://github.com/tj/go-naturaldate);\n   for ambiguous phrases use the explicit `2026-04-30 14:00` form.\n\n#### Auto-pick algorithm\n\n`--start` is the **earliest acceptable start**. The picker queries each\nconfigured room (priority list first, then fallback meeting rooms), looks\nfor a free slot starting in `[--start, --start + window]` of at least\n`--min` minutes. Among rooms with availability, the **longest slot**\nwins (capped at `--max`); priority order breaks ties. With\n`--prioritize-length`, all rooms compete equally on length and priority\nis only a tiebreaker.\n\nIf `--duration` is set in auto-pick mode, the picker locks to that exact\nlength instead of maximizing.\n\n#### Specific-mode flags\n\n| flag | meaning |\n|---|---|\n| `--space`, `-s` | space ID to book |\n| `--start` | exact start (required) |\n| `--duration`, `-d` | event length (mutually exclusive with `--end`) |\n| `--end` | exact end |\n| `--yes`, `-y` | skip confirmation |\n| `--title`, `--description` | event metadata |\n| `--time-zone` | IANA timezone (default: from config, else `Europe/Oslo`) |\n\n#### Auto-pick flags\n\n| flag | meaning |\n|---|---|\n| `--start` | Earliest acceptable start (default: now). |\n| `--duration`, `-d` | Lock booking to a fixed length instead of maximizing. |\n| `--min N` | Minimum acceptable slot length (minutes). |\n| `--max N` | Cap on booking length (minutes). |\n| `--window N` | How far past `--start` a slot may start (minutes). |\n| `--prioritize-length`, `-L` | Rank all rooms by length, ignoring priority order. |\n| `--dry-run`, `-n` | Find the best room and print it; don't book. |\n\n## Config\n\n`robin init` writes a starter `config.json`. To enable auto-pick, add a\n`quick_book` block:\n\n```json\n{\n  \"org\": \"your-org-slug\",\n  \"email\": \"you@example.com\",\n  \"password\": \"...\",\n  \"quick_book\": {\n    \"location\": 22847,\n    \"priority\": [6, 7, 5, 4, 11, 10, 8, 9],\n    \"min_duration_minutes\": 30,\n    \"max_duration_minutes\": 120,\n    \"window_minutes\": 30,\n    \"time_zone\": \"Europe/Oslo\"\n  }\n}\n```\n\n| field | meaning |\n|---|---|\n| `location` | Location ID to search in (`robin locations` shows yours). |\n| `priority` | Room numbers in preference order. The matcher looks for `Meeting Room N` in space names. |\n| `min_duration_minutes` | Skip rooms with less free time than this. |\n| `max_duration_minutes` | Cap on booking length. |\n| `window_minutes` | How far past the search anchor a slot may start. |\n| `time_zone` | IANA timezone used for booking start/end. |\n| `title` | Optional default event title. |\n\n## Output, color, and scripts\n\n`robin` follows the [Command Line Interface Guidelines](https://clig.dev):\n\n- **Human output by default**, formatted tables, color sparingly (errors\n  red, success green). Auto-disables when not connected to a terminal,\n  or when `NO_COLOR` is set, or with `--no-color`.\n- **`--json`** on every command that prints data. Pipe-friendly and\n  stable for scripts.\n- **`--quiet`** suppresses non-essential output; exit code is the source\n  of truth for success.\n- **`--verbose`** shows the HTTP exchange and per-room availability.\n- **`--no-input`** fails rather than prompting. Combine with `--yes` for\n  CI / scripts that need `robin book`.\n- **stdout** for results, **stderr** for status, progress, and errors.\n\nShell completion:\n\n```sh\nrobin completion bash \u003e /etc/bash_completion.d/robin\nrobin completion zsh  \u003e \"${fpath[1]}/_robin\"\nrobin completion fish \u003e ~/.config/fish/completions/robin.fish\n```\n\n## How auth works\n\nRobin's [public API](https://docs.robinpowered.com) requires admin-issued\naccess tokens. This tool instead replays the dashboard's own login flow:\n\n1. `POST https://api.robinpowered.com/v1.0/auth/users` with\n   `Authorization: Basic base64(email:password)` and a JSON body\n   `{remember_me: false, organization: null}`.\n2. The response yields `{access_token, expire_at, account_id}`.\n3. Subsequent calls use `Authorization: Access-Token \u003ctoken\u003e`.\n\nThe token is cached in `session.json` next to the config file. When it\nexpires, you'll see a 401 and a hint to run `robin login` again.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimenandre%2Frobin-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimenandre%2Frobin-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimenandre%2Frobin-cli/lists"}