octo-shared · diff

v0.5.0 to v0.5.0

12 added, 9 removed. Audit A to A.

---
name: octo-shared
version: 0.5.0
description: Shared knowledge for using the octo CLI — authentication, unified gateway config, output envelopes, universal flags, error handling, and common patterns. Load before invoking any octo domain skill.
metadata:
requires:
bins: ["octo-cli"]
---
# octo-shared — CLI fundamentals for AI Agents
`octo-cli` is a thin REST client that exposes the Octo ecosystem (matters, messaging, groups, threads, files, bot, events, docs, html) as a single binary. Every service command is auto-generated from an embedded OpenAPI registry; output is a JSON envelope designed to be parsed by agents.
> **The `matter` domain is temporarily withheld** while its backend API stabilizes — `octo-cli matter ...` is not registered and the `octo-matter` skill is not listed. Do not emit `matter` commands until it is re-enabled. The examples below use other domains.
**Bots cannot delete documents, even documents they created or own/administer.**
Do not invoke `docs delete`, `html rm`, or equivalent raw `api DELETE` calls to
delete a document. Ask a human with document admin permission to delete it in
Octo instead. Do not switch credentials/routes or clear the document's contents
as a workaround. This rule concerns whole documents, not normal editing or
otherwise-permitted comment, version, asset, or sheet row/column removal.
## 1. Authentication
Bots authenticate with a bearer token. There is no interactive user login — but besides the two bot tokens (`app_*`, `bf_*`) there is a third kind, a **user API key** (`uk_*`), which carries a real person's identity and is used mainly for `message search`. Two ways to supply any of them:
**Stored profile (recommended).** A human (or provisioning step) logs the token in once; it is encrypted at rest under `~/.octo-cli`, and the raw token never appears in any command line, shell history, or transcript afterward:
```bash
# Operator setup (token read from a hidden prompt, or --with-token < file):
octo-cli auth login --bot-id cli_xxxxxxxx # robot id you got when creating the bot
echo "$TOKEN" | octo-cli auth login --bot-id cli_xxxxxxxx --with-token # non-interactive
# A user API key (uk_) is stored the same way — encrypted profile, bot_kind shows
# user_key. Use a friendly --profile name since it has no robot id:
echo "$UK_TOKEN" | octo-cli auth login --profile alice-search --with-token
```
Then, at runtime, select which bot to act as — the agent passes its own **robot id**, which it knows:
```bash
- octo-cli --bot-id cli_xxxxxxxx matter list # or env OCTO_BOT_ID=cli_xxxxxxxx
- octo-cli --profile myname matter list # or by the friendly profile name
+ octo-cli --bot-id cli_xxxxxxxx group list # or env OCTO_BOT_ID=cli_xxxxxxxx
+ octo-cli --profile myname group list # or by the friendly profile name
```
With exactly one stored profile, the selector is optional. With **two or more, you must pass `--bot-id` or `--profile`** — omitting it is a hard error (the CLI never guesses which identity to use).
**Environment credential.** Outside an explicit `--bot-id` / `--profile`
selection, an environment credential overrides stored profiles. `OCTO_TOKEN`
is canonical; `OCTO_BOT_TOKEN` is a fully compatible lower-priority alias:
```bash
export OCTO_TOKEN=app_xxxxxxxxxxxxxxxxxxxx # App Bot (DM-only)
export OCTO_TOKEN=bf_xxxxxxxxxxxxxxxxxxxxx # User Bot (full access)
export OCTO_TOKEN=uk_xxxxxxxxxxxxxxxxxxxxx # User API key (real person; message search)
```
> `OCTO_BOT_ID` is not a secret. Without an environment credential it selects a
> stored profile; an environment credential takes precedence over that implicit
> selector.
Token prefix determines capability — the CLI does NOT enforce this locally (the backend rejects unsupported operations with `FORBIDDEN`), with **one exception**: an `app_*` token running `message search` is rejected locally with a `validation` error before any request.
| Prefix | Type | DM msg | Group read | Group write | Thread | Voice | Search |
|---------|--------------|--------|------------|-------------|--------|-------|--------|
| `app_*` | App Bot | yes | yes | **no** | **no** | **no**| **no** |
| `bf_*` | User Bot | yes | yes | yes | yes | yes | yes |
| `uk_*` | User API key | — | — | — | — | — | yes |
`uk_*` carries a real person's identity and is meaningful mainly for `message search` (routed to `/v1/user/*`); for other domains use a bot token. `bf_*` can also search on behalf of a person with `--on-behalf-of <uid>`. `identity.bot_kind` reflects the kind: `app_bot`, `user_bot`, or `user_key`.
Outside `OCTO_CREDENTIAL_MODE=task`, inspect `octo-cli auth status` (or
`octo-cli config show`) to confirm the active identity. Daemon task mode does
not expose profile diagnostics; use the `identity` echoed by each success
envelope and report authentication failures to the daemon instead (see §3).
For task workflow and collaboration in a daemon execution, select
`OCTO_CREDENTIAL_MODE=task` for each CLI invocation. It reads only the injected
`OCTO_TASK_TOKEN`; independent business operations use ordinary authentication.
Do not export mode for the session or switch identity to retry an auth failure.
Follow the daemon's scoped workflow examples. Outside task mode, the existing
environment and profile credential rules remain unchanged.
## 2. Unified gateway configuration
All Octo domains use one gateway. Override it only for test or self-hosted
deployments:
```bash
# Production defaults to https://im.deepminer.com.cn. Override for test or
# self-hosted deployments:
export OCTO_API_BASE_URL=https://im-test.deepminer.com.cn
export OCTO_SPACE_ID=space_xxx # only for platform-scoped bots
export OCTO_FORMAT=json # default output format
```
Routing: all services go through `OCTO_API_BASE_URL`. The `--service` flag on `octo-cli api` is for documentation only — all traffic routes to the same gateway.
## 3. Output: the JSON envelope
Every successful invocation prints a single JSON object to stdout:
```json
{
"ok": true,
"identity": { "type": "bot", "profile": "prod", "robot_id": "cli_xxx", "bot_kind": "app_bot", "source": "profile:prod" },
"data": { ... or [...] },
"_pagination": { "has_more": true, "next_cursor": "..." },
"_rate_limit": { "remaining": 99, "reset": 1730000000 }
}
```
`identity` echoes the bot the command actually ran as — check it to catch acting as the wrong identity. It is **always an object**: a stored profile fills in `profile` / `robot_id` / `source: "profile:<name>"`; a raw environment credential reports the alias that supplied it (`source: "env:OCTO_TOKEN"` or `source: "env:OCTO_BOT_TOKEN"`) and has no verified `profile`/`robot_id`; a command that resolves no credential (e.g. `version`) yields the minimal `{ "type": "bot" }`.
Every failure prints an error envelope to **stderr** and exits non-zero:
```json
{
"ok": false,
"error": {
"type": "validation",
"code": "VALIDATION_ERROR",
"message": "title is required",
"hint": "check params with `octo-cli schema <op>`",
"detail": { ...original backend payload... }
}
}
```
Parse `ok` first. On failure, branch on `error.type` (a small fixed taxonomy) or `error.code` (a string, may come straight from the backend).
`error.code: bot_delete_forbidden` (`error.type: permission`, HTTP 403) is a
terminal policy denial, not missing membership or a transient error. Do not
retry, request a higher bot role, or change `uid`, owner, Space, credentials, or
routes; ask a human document admin to perform the deletion. Do not claim success,
even if the document was already deleted.
Backends differ in their raw error shape. The CLI normalizes both:
- **matters** (structured): `{error:{code, message, details}}` → passes through into `detail` unchanged.
- **dmworkim** (flat): `{msg, status}` → mapped to `code`/`message` via HTTP status.
## 4. Universal flags
These flags work on every command (they are root-level persistent flags):
| Flag | Purpose |
|-----------------|-------------------------------------------------------------------------|
| `--format` | `json` (default) · `table` · `csv` · `ndjson` |
| `--jq`, `-q` | Apply a jq expression to the success envelope before formatting |
| `--dry-run` | Print the resolved request instead of sending it |
| `--verbose` | Log request/response trace to stderr |
| `--timeout` | Per-request deadline, e.g. `30s`, `2m` |
| `--no-retry` | Disable the default retry-on-transient policy |
| `--space` | Override `OCTO_SPACE_ID` for this invocation |
| `--bot-id` | Select/assert the stored credential by robot id (env `OCTO_BOT_ID`) |
| `--profile` | Select the stored credential by profile name |
- Paginated operations additionally expose:
+ Cursor-paginated operations that register the convenience flags expose:
| Flag | Purpose |
|----------------|----------------------------------------------------------|
| `--page-all` | Walk pages until `has_more=false`, emit one merged array |
| `--page-limit` | Hard cap on pages fetched with `--page-all` (default 10) |
## 5. Error taxonomy and exit codes
| `error.type` | Exit | Typical `error.code` |
|----------------|------|----------------------------------------------|
- | `auth_error` | 3 | `UNAUTHORIZED`, `AUTH_UNAVAILABLE` |
+ | `auth_error` | 3 | `UNAUTHORIZED` |
| `validation` | 2 | `VALIDATION_ERROR`, `PAYLOAD_TOO_LARGE` |
| `config` | 2 | missing env vars |
| `permission` | 1 | `FORBIDDEN`, `SPACE_FORBIDDEN` |
| `rate_limited` | 1 | `RATE_LIMITED` |
- | `network` | 1 | `NETWORK_ERROR`, `UPSTREAM_UNAVAILABLE` |
+ | `network` | 1 | `AUTH_UNAVAILABLE`, `NETWORK_ERROR`, `UPSTREAM_UNAVAILABLE` |
| `api_error` | 1 | `MATTER_NOT_FOUND`, `NOT_FOUND`, `INTERNAL_ERROR` |
| `internal` | 1 | CLI-side bug |
Agents should switch on **`error.code` first** (specific, deterministic), then `error.type` (broad), then `exit_code` (coarse).
- The `hint` field is a one-line next action meant for an agent: follow it literally where it applies. E.g. `MATTER_NOT_FOUND` → "verify ID with `octo-cli matters list`".
+ The `hint` field is a one-line next action meant for an agent: follow it literally where it applies. E.g. `MATTER_NOT_FOUND` → "inspect the operation schema with `octo-cli schema matter.get`; matter commands are currently withheld".
## 6. Input patterns
### Promoted flags vs `--data`
Simple top-level body fields auto-promote to typed flags (strings, integers, booleans, `[]string`). For objects, arrays-of-objects, or when sending a large payload, use `--data`:
```bash
octo-cli thread create group-abc --name "design review"
octo-cli message send --data '{"channel_id":"chat-1","channel_type":1,"payload":{"type":1,"content":"hi"}}'
octo-cli message send --data @body.json
octo-cli some-cmd --data @- # read JSON from stdin
```
- Explicit flags override fields set in `--data`. The `--data` escape hatch exists on every non-multipart command.
+ The `--data` escape hatch is available on commands with a JSON request body, not on every non-multipart operation. Check command help or the operation schema; handwritten commands may have different input shapes.
### Piping with `--jq`
```bash
- octo-cli group list --jq '.data[].id' | xargs -I{} octo-cli group get {}
+ groups=$(octo-cli group list --format json --jq '[.data[].group_no]')
+ while IFS= read -r group_no; do
+ octo-cli group get "$group_no"
+ done < <(jq -r '.[]' <<<"$groups")
```
### Paginating
```bash
octo-cli docs search --keyword "spec" --page-all --page-limit 20
```
- `--page-all` applies to any list operation that reports a cursor in `_pagination`. The merged output drops `_pagination` — you get a flat `data` array.
+ Use `--page-all` only on operations that expose it. It walks cursor pages until `has_more=false`; page-number operations such as Loop task lists use their own page/page-size flags.
### Dry-run for agent self-verification
```bash
octo-cli message send --data '{"channel_id":"chat-1","channel_type":1,"payload":{"type":1,"content":"foo"}}' --dry-run
```
Prints the exact HTTP request body and URL, emits no side effect.
## 7. Discovering the API
The registry is embedded in the binary — no network needed:
```bash
octo-cli schema --list # all services + operation IDs
octo-cli schema --list message # operations in one domain
octo-cli schema message.send # full request/response schema
octo-cli config show # resolved config (token masked)
octo-cli auth status # active bot identity (whoami)
octo-cli auth list # stored profiles (no tokens)
```
When an operation isn't auto-registered yet or you need low-level control:
```bash
octo-cli api GET /api/v1/messages --params '{"chat_id":"chat-1"}'
octo-cli api POST /api/v1/messages --data @body.json
octo-cli api GET /fleet/api/v1/custom --workspace-id <workspace-uuid>
```
For a workspace-scoped passthrough endpoint, pass `--workspace-id` explicitly;
the CLI sends it as `X-Workspace-ID` and does not infer a workspace.
## 8. Domain skills
Once these fundamentals are understood, load the skill for the domain you need:
- `octo-matter` — matters (todos/tasks), assignees, channels, timeline, AI extract — **temporarily withheld** (backend API stabilizing; not currently loadable)
- `octo-messaging` — message send/edit/sync/read-receipt, groups, threads, events
- `octo-files` — file upload/download, presigned credentials, bot housekeeping
- `octo-docs` — docs domain (CRDT/Yjs): documents, spreadsheets, whiteboard scenes, members/sharing, comments, versions, attachments
- `octo-html` — HTML docs domain (octo-doc, a DIFFERENT backend from octo-docs): self-contained interactive HTML documents, share codes, media assets, comments, agent element read/replace
- `octo-summary` — create owner-only summaries from explicit sources, then discover, read, and cite summaries visible to the personal Agent's human owner — **temporarily withheld** (create backend at Mininglamp-OSS/octo-smart-summary#181 not yet merged/deployed/enabled; not currently loadable)