octarin-cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -0
- package/assets/backfill.py +1113 -0
- package/assets/claude_code/hook.py +573 -0
- package/assets/codex/hook.mjs +487 -0
- package/assets/cursor/hook-handler.js +41 -0
- package/assets/cursor/lib/canonical.js +240 -0
- package/assets/cursor/lib/utils.js +138 -0
- package/assets/repo-template/dot-claude/octarin/hook.py +685 -0
- package/assets/repo-template/dot-claude/octarin/run.sh +41 -0
- package/assets/repo-template/dot-claude/settings.json +15 -0
- package/assets/repo-template/dot-codex/config.toml +6 -0
- package/assets/repo-template/dot-codex/hooks/hook.mjs +531 -0
- package/assets/repo-template/dot-codex/hooks/run.sh +38 -0
- package/assets/repo-template/dot-cursor/hooks/hook-handler.js +41 -0
- package/assets/repo-template/dot-cursor/hooks/lib/canonical.js +240 -0
- package/assets/repo-template/dot-cursor/hooks/lib/utils.js +196 -0
- package/assets/repo-template/dot-cursor/hooks/run.sh +41 -0
- package/assets/repo-template/dot-cursor/hooks.json +13 -0
- package/dist/args.js +85 -0
- package/dist/assets.js +28 -0
- package/dist/client.js +105 -0
- package/dist/envfile.js +94 -0
- package/dist/index.js +192 -0
- package/dist/init.js +314 -0
- package/dist/init_repo.js +348 -0
- package/dist/login.js +209 -0
- package/dist/output.js +56 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# octarin-cli
|
|
2
|
+
|
|
3
|
+
The per-user CLI for [Octarin](https://octarin.ai) AI-usage analytics (binary:
|
|
4
|
+
`octarin`). One
|
|
5
|
+
centralized, `npx`-based install path for AI-coding capture, plus a scoped,
|
|
6
|
+
**read-only SQL** layer over your own data.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx octarin-cli@latest init <oct_key> # personal capture install (this machine)
|
|
10
|
+
npx octarin-cli@latest init-repo <org/project> # team install (commits config, opens a PR)
|
|
11
|
+
npx octarin-cli@latest login # authorize a machine (team installs)
|
|
12
|
+
npx octarin-cli@latest sql query "<SELECT ...>" # read-only SQL over your own data
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
No `curl | bash`, no sha256-verify dance — version pinning (`@latest` /
|
|
16
|
+
`@x.y.z`) and npm provenance are the integrity story. Requires Node.js >= 18
|
|
17
|
+
(uses the built-in `fetch`); `init`'s history import additionally uses the
|
|
18
|
+
system `python3`.
|
|
19
|
+
|
|
20
|
+
## Capture install
|
|
21
|
+
|
|
22
|
+
`init` and `init-repo` install the Octarin capture hooks for Claude Code,
|
|
23
|
+
Cursor, and Codex entirely from the package's **bundled, version-pinned assets**
|
|
24
|
+
— nothing is fetched at install time.
|
|
25
|
+
|
|
26
|
+
### `octarin init <oct_key>`
|
|
27
|
+
|
|
28
|
+
Personal, per-machine install. Writes your write-only ingest key to
|
|
29
|
+
`~/.octarin/octarin.env` (mode 600, never git), copies the hook files into each
|
|
30
|
+
detected tool's config dir, **merges** the hook registration into that tool's
|
|
31
|
+
settings (no hand-editing), and imports your existing history.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx octarin-cli@latest init oct_xxx # all detected tools, last 90 days
|
|
35
|
+
npx octarin-cli@latest init oct_xxx --backfill off # skip the history import
|
|
36
|
+
npx octarin-cli@latest init oct_xxx --tools cursor # only Cursor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### `octarin init-repo <org/project>`
|
|
40
|
+
|
|
41
|
+
Team install. Run at a repo root: commits shared hook config + the public
|
|
42
|
+
project slug to `.claude/.cursor/.codex` + `.octarin/project` (no key, ever),
|
|
43
|
+
and opens a PR. Each teammate then runs `octarin login` once.
|
|
44
|
+
|
|
45
|
+
### `octarin login`
|
|
46
|
+
|
|
47
|
+
Device-code browser flow that mints a per-user ingest key into
|
|
48
|
+
`~/.octarin/octarin.env`. Reads the project from `.octarin/project` (or
|
|
49
|
+
`--project <org/project>`).
|
|
50
|
+
|
|
51
|
+
## SQL
|
|
52
|
+
|
|
53
|
+
A scoped, **read-only SQL** layer over your own data. Run SELECTs against your
|
|
54
|
+
traces, spans, sessions, and events — or pipe machine-readable JSON into `jq`
|
|
55
|
+
and your agents.
|
|
56
|
+
|
|
57
|
+
The CLI holds **only your API key**, never database credentials. All query
|
|
58
|
+
safety (single read-only SELECT, table allowlist, no `system.*`) and tenant
|
|
59
|
+
scoping (every result is filtered to your organization's projects) are enforced
|
|
60
|
+
**server-side** by the Octarin API. A query can only ever see your own data.
|
|
61
|
+
|
|
62
|
+
## Auth (SQL)
|
|
63
|
+
|
|
64
|
+
The `sql` commands authenticate with a read-only **query key** (`oct_...`) — a
|
|
65
|
+
key that can run scoped SELECTs over your org's data but can NEVER ingest. This
|
|
66
|
+
is a *distinct* key from the write-only **ingest key** used for capture (the one
|
|
67
|
+
`octarin init` writes to `~/.octarin/octarin.env`); an ingest key is rejected
|
|
68
|
+
here with a `403`.
|
|
69
|
+
|
|
70
|
+
Mint a query key in the dashboard under **Settings → CLI / Query keys** (it is
|
|
71
|
+
shown exactly once — copy it then). Then either:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
export OCTARIN_API_KEY=oct_xxxxxxxxxxxxxxxxxxxx
|
|
75
|
+
octarin sql query "SELECT count() FROM spans"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
or pass it per-command:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
octarin sql query "SELECT count() FROM spans" --api-key oct_xxxx
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Commands
|
|
85
|
+
|
|
86
|
+
### `octarin sql schema`
|
|
87
|
+
|
|
88
|
+
List the tables you can query and their columns.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
octarin sql schema # human-readable tables
|
|
92
|
+
octarin sql schema --json # machine-readable JSON
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Queryable tables: `spans`, `traces`, `session_insights`, `span_vectors`,
|
|
96
|
+
`events` (all in the `octarin` database; you may write them qualified as
|
|
97
|
+
`octarin.spans` or bare as `spans`).
|
|
98
|
+
|
|
99
|
+
### `octarin sql query "<SELECT ...>"`
|
|
100
|
+
|
|
101
|
+
Run a single **read-only** SELECT. Default output is a readable ASCII table;
|
|
102
|
+
`--json` emits structured JSON on stdout.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Top models by token-bearing spans
|
|
106
|
+
octarin sql query "SELECT model, count() AS c FROM spans GROUP BY model ORDER BY c DESC"
|
|
107
|
+
|
|
108
|
+
# Cap the rows
|
|
109
|
+
octarin sql query "SELECT * FROM traces ORDER BY start_time DESC" --limit 20
|
|
110
|
+
|
|
111
|
+
# JSON for piping
|
|
112
|
+
octarin sql query "SELECT model, count() AS c FROM spans GROUP BY model" --json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Pipe into `jq`
|
|
116
|
+
|
|
117
|
+
Structured output goes to **stdout**; all progress/log/error messages go to
|
|
118
|
+
**stderr**, so JSON pipes cleanly:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
octarin sql query "SELECT model, count() AS c FROM spans GROUP BY model" --json \
|
|
122
|
+
| jq '.rows[] | {model: .[0], spans: .[1]}'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The response shape is:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"columns": ["model", "c"],
|
|
130
|
+
"rows": [["claude-opus-4-8", 7594], ["claude-sonnet-4-6", 6188]],
|
|
131
|
+
"row_count": 2
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Options
|
|
136
|
+
|
|
137
|
+
| Option | Env var | Default | Description |
|
|
138
|
+
|--------|---------|---------|-------------|
|
|
139
|
+
| `--api-key <key>` | `OCTARIN_API_KEY` | — | Octarin **query** key (`oct_...`). Required. |
|
|
140
|
+
| `--base-url <url>` | `OCTARIN_BASE_URL` | `https://api.octarin.ai` | API base URL. |
|
|
141
|
+
| `--port <port>` | — | — | Override the port (self-host parity). |
|
|
142
|
+
| `--limit <N>` | — | server cap | Max rows (server caps at 10000). |
|
|
143
|
+
| `--json` | — | off | Emit JSON on stdout instead of a table. |
|
|
144
|
+
| `-h`, `--help` | — | — | Show help (on every command). |
|
|
145
|
+
| `--version` | — | — | Print the CLI version. |
|
|
146
|
+
|
|
147
|
+
Self-host example:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
octarin sql schema --base-url http://localhost:8000
|
|
151
|
+
octarin sql schema --port 8000 # shorthand for http://localhost:8000
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Agent-friendly I/O
|
|
155
|
+
|
|
156
|
+
- **stdout** — only the structured result (the table, or `--json` JSON).
|
|
157
|
+
- **stderr** — all human/progress/error messages.
|
|
158
|
+
- **exit code** — `0` on success, non-zero on failure (`2` for usage/auth
|
|
159
|
+
errors, `1` for request/execution errors).
|
|
160
|
+
|
|
161
|
+
This makes the CLI safe to drive from scripts and LLM agents: capture stdout,
|
|
162
|
+
check the exit code, read stderr for diagnostics.
|
|
163
|
+
|
|
164
|
+
## What you can and can't query
|
|
165
|
+
|
|
166
|
+
You can run exactly **one read-only `SELECT`** (a `WITH ... SELECT` CTE is fine)
|
|
167
|
+
over the allowlisted `octarin.*` tables. The server rejects, with a clear `400`:
|
|
168
|
+
|
|
169
|
+
- anything that isn't a single SELECT (no `INSERT/UPDATE/DELETE/ALTER/DROP/...`),
|
|
170
|
+
- multiple `;`-separated statements,
|
|
171
|
+
- references to other databases or tables — especially `system.*` and
|
|
172
|
+
`information_schema.*`,
|
|
173
|
+
- table functions (`url`, `file`, `remote`, `s3`, `numbers`, ...).
|
|
174
|
+
|
|
175
|
+
Every result is automatically scoped to your organization's projects — you
|
|
176
|
+
never need to (and cannot) widen it.
|
|
177
|
+
|
|
178
|
+
## Development
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npm install
|
|
182
|
+
npm run build # tsc -> dist/
|
|
183
|
+
node dist/index.js sql schema
|
|
184
|
+
# or run the local checkout via npx:
|
|
185
|
+
npx . sql schema
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Publishing (maintainers)
|
|
189
|
+
|
|
190
|
+
This package is publish-ready but not yet published. To release:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm version <patch|minor|major>
|
|
194
|
+
npm publish # runs prepublishOnly -> npm run build
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
(`files` ships `dist/`, the bundled `assets/`, and this README; `prepublishOnly`
|
|
198
|
+
rebuilds, which re-stages `assets/` from `../clients` via `copy-assets`.)
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|