Command reference
oko-cli calendar connect
Connects one of the user's own calendars: the macOS system calendars, Google Calendar, Microsoft 365, a CalDAV collection, or an ICS subscription.
Invocation
oko-cli calendar connect {eventkit|google|microsoft|caldav|ics} [--url U] [--username N] [--password-stdin] [--client-id ID] [--client-secret S] [--skarbiec-item ITEM] [--id ID] [--label L] [--request-access] [--no-open] [--timeout SEC] [--redirect-port PORT]Inputs and options
- eventkit: --request-access is the only path that triggers the one-time macOS consent dialog; without granted access the command refuses instead of prompting.
- google / microsoft: --client-id (or OKO_GOOGLE_CLIENT_ID / OKO_MICROSOFT_CLIENT_ID) names the installation's own OAuth client; the PKCE loopback flow prints the sign-in URL, opens the default browser unless --no-open, and waits for the redirect.
- google: Google issues installed-app clients a client secret and requires it at code exchange; pass --client-secret, set OKO_GOOGLE_CLIENT_SECRET, or keep it in the Skarbiec item as client_secret. Microsoft public clients need none.
- --timeout bounds the wait for the OAuth redirect in seconds (default 300, clamped to 60-3600); a real person signing in on another device usually needs more than the default.
- caldav: --url names the collection and --username with --password-stdin carry Basic-auth credentials; ics: --url names the subscription.
- --skarbiec-item points the account at a Skarbiec bundle item (fields: url, username, password for caldav/ics; client_id, refresh_token for google/microsoft); sync resolves them at run time, and a google/microsoft item that already holds a refresh_token connects with no browser flow at all.
- --redirect-port pins the loopback port the OAuth redirect URI names, instead of taking any free one. This is what lets the browser run on another machine: the redirect URI is otherwise unknown until the flow is already running, and an encrypted reverse forward (stado host forward-local --remote-port P --local-port P) cannot be opened for a port nobody can name in advance. Google's installed-app clients accept any loopback port, so pinning one keeps the redirect URI valid.
- Driving the sign-in on another machine, once the OAuth client carries a loopback redirect URI, is four commands: stado host forward-local <host> oko-oauth --remote-port P --local-port P to bridge that host's loopback back to this one; oko-cli calendar connect google --skarbiec-item <item> --redirect-port P --no-open, which prints the sign-in URL and waits; stado host weles-browser-task <host> --url <that URL> --objective @objective.txt --session-label oko-calendar --allow-login to open it where the browser runs; then oko-cli calendar sync and oko-cli calendar list. Close the bridge afterwards with stado host forward-close <host> oko-oauth. The objective should instruct the browser agent to stop and report rather than act if a two-factor push, device approval, passkey or SMS verification is requested.
- Google Calendar can also be connected with no OAuth client at all: oko-cli calendar connect ics --url <private iCal address> subscribes to the calendar's own secret address, which needs no client registration, no consent screen and no browser. It is read-only, which is the whole of what sync and list need.
- Which client to use: a first-party Wisent client belongs in the company's own Google Cloud project wisent-480400 (project number 1080673333190), which already carries the OAuth consent brand titled 'Wisent' with support email lukasz.bartoszcze@wisent.ai. Its application type must be **Desktop app**: Google grants installed-app clients the loopback exemption, accepting any 127.0.0.1 port without registering each one, which is what makes --redirect-port work and removes the redirect-URI problem for oko and skrzynka in one step. A Web-type client has none of that and must register every redirect URI literally. The client id both products have been pointed at so far, 903183433368-5nt0jdbqtli8rm39oh2s0limiljap3l9.apps.googleusercontent.com, is not in that project at all: the 903183433368 prefix is the project number of controlai-406621 ('ControlAI'), so the grant is being minted through a client owned by an unrelated project, and nothing in oko asserts otherwise because OKO_GOOGLE_CLIENT_ID has no committed default. No Google Cloud API can add a loopback redirect URI to that client — the only OAuth-client APIs are IAP's, whose clients are locked to IAP and expose no redirect-URI field, so the correct client has to be created once in the Console under wisent-480400.
- --id and --label override the derived account id and display label.
Output and state effect
- Writes the account to the user's own ~/.oko/calendar/accounts.json (owner-only, OKO_CALENDAR_DIR override); with --skarbiec-item only the pointer is stored and no secret is written. Oko ships no provider credentials — Skarbiec is an optional home for the account's secrets on machines that have it.
- For google / microsoft with --skarbiec-item: if the OAuth flow completes and produces a refresh_token, the token is written to the vault item, making the grant durable across machines. Subsequent connections from any machine with vault access connect with no browser flow.
Refusals and safe exits
- google/microsoft without a client id refuse naming the flag and environment variable and stating that the OAuth client belongs to your installation.
- eventkit without granted access and without --request-access refuses naming the flag and the System Settings path; a declined dialog fails with: calendar access was not granted.
- caldav/ics without --url or a resolvable pointer, and a duplicate account id, refuse with their exact sentences.
- --redirect-port that cannot be bound refuses with the kernel's reason and never falls back to another port: the number was requested so that forwards or redirect URIs could be prepared for it, and quietly binding a different one produces a flow that waits forever for a redirect delivered somewhere else.
- A redirect URI the OAuth client has not registered no longer times out silently. Google refuses it inside the browser, so no redirect ever reaches the listener; the flow now asks Google why and refuses naming the client id, the redirect URI it presented, that the client has no loopback redirect URI registered, and the single fix: register a loopback redirect URI for that client, or issue a Desktop app client, which accepts any loopback port. OKO_OAUTH_REDIRECT_PATH names the path the client registered, since Google matches the path exactly and ignores the port.
- If a grant is written to a vault item and the write fails, the command refuses with the exact sentence naming the item and the field: the consumer may lack write access on the item (grant it with skarbiec token-mint oko-calendar-client --capabilities write:<item>#refresh_token), the item may be read-only, or no vault route may be available.