@oxy-hq/cli 0.7.0 → 0.9.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.
Files changed (38) hide show
  1. package/README.md +131 -8
  2. package/dist/main.mjs +4909 -3551
  3. package/dist/main.mjs.map +1 -1
  4. package/dist/{mcp-xxWL3_dz.mjs → mcp-Bdp0rX0l.mjs} +2 -2
  5. package/dist/{mcp-xxWL3_dz.mjs.map → mcp-Bdp0rX0l.mjs.map} +1 -1
  6. package/package.json +5 -5
  7. package/skills/oxy-agents/SKILL.md +318 -0
  8. package/skills/oxy-agents/references/quick-reference.md +222 -0
  9. package/skills/oxy-agents/templates/agentic-template.yml +141 -0
  10. package/skills/oxy-airway-pipelines/SKILL.md +223 -316
  11. package/skills/oxy-automations/SKILL.md +61 -15
  12. package/skills/oxy-cli/SKILL.md +37 -4
  13. package/skills/oxy-custom-apps/SKILL.md +193 -291
  14. package/skills/oxy-custom-apps/references/minimal-starter.md +63 -0
  15. package/skills/oxy-custom-apps/references/performance-and-usage.md +105 -0
  16. package/skills/oxy-custom-apps/references/reading-data.md +106 -0
  17. package/skills/oxy-custom-apps/references/sandbox-agent-loop.md +267 -0
  18. package/skills/oxy-custom-apps/references/verifying-in-the-browser.md +65 -0
  19. package/skills/oxy-data-apps/SKILL.md +362 -0
  20. package/skills/oxy-data-apps/references/examples.md +263 -0
  21. package/skills/oxy-data-apps/references/quick-reference.md +171 -0
  22. package/skills/oxy-data-apps/templates/dashboard-with-controls.app.yml +128 -0
  23. package/skills/oxy-data-apps/templates/simple-dashboard.app.yml +102 -0
  24. package/skills/oxy-data-architecture/SKILL.md +137 -152
  25. package/skills/oxy-evals/SKILL.md +362 -0
  26. package/skills/oxy-evals/references/diagnosis.md +243 -0
  27. package/skills/oxy-evals/references/results-json.md +145 -0
  28. package/skills/oxy-evals/references/writing-expected.md +112 -0
  29. package/skills/oxy-semantic-modeling/SKILL.md +239 -59
  30. package/skills/oxy-semantic-modeling/references/warehouse-casts.md +57 -0
  31. package/skills/oxy-semantic-modeling/templates/topic-template.yml +38 -0
  32. package/skills/oxy-semantic-modeling/templates/view-template.yml +119 -0
  33. package/template/.github/workflows/publish.yaml +15 -13
  34. package/template/.nvmrc +1 -1
  35. package/template/.oxyc-managed +5 -5
  36. package/template/README.md +21 -10
  37. package/template/package.json +3 -2
  38. package/template/pnpm-workspace.yaml +28 -17
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  `oxyc` — a `gh api`-shaped client for the Oxy HTTP API, plus the tooling that
4
4
  manages customer workspace repos.
5
5
 
6
- - **API client** — `api`, `routes`, `schema`, `openapi`, `login`, `whoami`, `token`, `tokens`, `assume`, `oltp`
6
+ - **API client** — `api`, `routes`, `schema`, `openapi`, `login`, `whoami`, `token`, `login-link`, `tokens`, `assume`, `oltp`
7
7
  - **Customer workspaces** — `list`, `new`, `import`, `doctor`, `update`, `adopt`, `launch`
8
8
  - **Custom apps** — `publish`, `init-ci`, `proxy`, `apps`
9
9
  - **Checks** — `checks run`
@@ -152,14 +152,30 @@ route's surface and fleet role.
152
152
  oxyc login [--env <e>] [--login-env <e...>] [--assume <org> -r <why>]
153
153
  oxyc whoami [--json] # who the credential is, and what it can reach
154
154
  oxyc token # print the bearer, for a raw curl
155
+ oxyc login-link [--next <path>] [--json] [--open] # a one-time URL that signs a browser in
155
156
  oxyc logout # revoke the cached token, then forget it
156
157
  oxyc tokens list [--json] # your personal access tokens
157
158
  oxyc tokens revoke <id>
158
159
  oxyc tokens revoke --current # end the token in OXY_TOKEN
159
160
  oxyc tokens create # opens Account → Personal access tokens
160
161
  oxyc tokens create --sandbox-agent --app <org>/<app> [--app …] [--hours 8] [--name …]
162
+ oxyc tokens create --agent [--standing] [--hours 8] [--name …]
161
163
  ```
162
164
 
165
+ **An agent runs on its own token, never on the person's `oxyc login`.** Which
166
+ one depends on the task:
167
+
168
+ | The agent is… | It mints | Which reaches |
169
+ | --- | --- | --- |
170
+ | building a custom app in a sandbox | `tokens create --sandbox-agent --app …` (`oxy_sbx_…`) | the sandboxes of the apps it names, and nothing else |
171
+ | doing anything else: reading data with `oxyc api`, reproducing a bug against live data | `tokens create --agent` (`oxy_pat_…`) | everything its approver can, for hours |
172
+
173
+ Both are approved once in the browser by the person the agent works for, both
174
+ are printed once as `export OXY_TOKEN=…` and never stored, and both end with
175
+ `tokens revoke --current`. After a mint, pass `--token-env OXY_TOKEN` to every
176
+ command: a named variable is the only source, so a command that lost the
177
+ variable exits `4` instead of quietly running on the person's cached login.
178
+
163
179
  **Every command resolves its credential the same way, first match wins:**
164
180
 
165
181
  1. **`OXY_TOKEN`** (or the variable `--token-env` names). It holds any
@@ -167,7 +183,8 @@ oxyc tokens create --sandbox-agent --app <org>/<app> [--app …] [--hours 8] [--
167
183
  token (`oxy_pat_…`), a service account token (`oxy_sat_…`), a CI token
168
184
  (`oxy_ci_…`) or a sandbox agent token (`oxy_sbx_…`) — a legacy API key
169
185
  (`oxy_…`), a publish token, or a session token. A blank value counts as
170
- unset.
186
+ unset. An agent token is a personal access token by its prefix: only its
187
+ own description tells it from a login (`oxyc whoami`).
171
188
  2. **The login cache** — what `oxyc login` stored for this deployment.
172
189
  3. **GitHub OIDC**, in a GitHub Actions job granted `id-token: write`: the job's
173
190
  OIDC token is exchanged, once per process, for a fifteen-minute token acting
@@ -195,7 +212,7 @@ Settings → Workspace → Legacy API keys. Use an API token for anything new.
195
212
 
196
213
  **`login`** runs a browser loopback flow. Against a current deployment it
197
214
  receives a one-time code and exchanges it (PKCE) for a **personal access
198
- token** named `oxyc on <hostname>`: revocable, ninety days, and replacing that
215
+ token** named `oxyc on <hostname>`: revocable, a year, and replacing that
199
216
  machine's previous one. No credential travels in a URL. Against a deployment
200
217
  that predates the exchange it stores the session token the page hands back,
201
218
  exactly as before, and says so — nothing needs upgrading first, in either
@@ -211,7 +228,10 @@ rather than reading as fine from the cache. For a token it also prints what the
211
228
  token is and what it can reach — `all access`, or one line per grant with the
212
229
  role it is capped at, and any organization whose policy blocks it. For a legacy
213
230
  API key it prints `Legacy API key`, and that it reaches everything its owner
214
- can.
231
+ can. For an agent token it says `agent token`, who approved it, whether it
232
+ carries their staff or partner access, and when it expires. When a token in
233
+ `OXY_TOKEN` is refused (exit `4`), the advice is never to log in — a login
234
+ cannot replace what is in the variable. An agent stops and reports.
215
235
 
216
236
  **`token`** prints the bearer on stdout and nothing else. Inside a GitHub
217
237
  Actions job with nothing stored it prints the token the job's OIDC identity
@@ -275,6 +295,35 @@ A refusal from the server carries a hint an agent can act on — never "try
275
295
  `oxyc login` again". Exit `4` under this token means it expired, was revoked,
276
296
  or its minter lost access: stop and report.
277
297
 
298
+ **`tokens create --agent`** mints an **agent token** for everything that is not
299
+ a sandbox: a personal access token (`oxy_pat_…`) that reaches what its approver
300
+ reaches through their organization memberships, for 8 hours by default
301
+ (`--hours`, 1 to 168). It cannot be extended, edited or regenerated, and it
302
+ cannot create, extend or revoke tokens; it can open a browser session as itself
303
+ (`login-link`).
304
+
305
+ ```bash
306
+ eval "$(oxyc tokens create --agent --env dev)"
307
+ ```
308
+
309
+ The same browser loopback again, with the same guarantees: stdout is the single
310
+ line `export OXY_TOKEN=oxy_pat_…`, printed once, everything else is on stderr,
311
+ it holds no credential while it runs, never writes the credentials file and
312
+ revokes no other token. Arguments are checked before the browser opens (exit
313
+ `2`); `--agent` and `--sandbox-agent` together are a usage error.
314
+
315
+ `--standing` asks for the approver's staff or partner access as well. The
316
+ approver decides on the page, where it starts **off**: a token that comes back
317
+ without it is accepted, and stderr says so in one line. Without `--standing`, a
318
+ token that carries either is refused.
319
+
320
+ What comes back is checked before anything is printed: it must be a whole
321
+ `oxy_pat_`; it must describe itself (`GET /api/auth/token`) as `personal` with
322
+ `source` `oxyc_agent`; it must expire no later than the hours asked for; and it
323
+ must carry no standing that was not asked for. A deployment that predates agent
324
+ tokens would otherwise hand back an ordinary ninety-day login. On any mismatch
325
+ the command prints nothing, revokes what was minted, and exits `8`.
326
+
278
327
  Credentials live in the OS config directory under **`oxy`** — the same file the
279
328
  Rust `oxy login` wrote before it was removed, so an existing login still works:
280
329
 
@@ -285,6 +334,46 @@ An entry may now also carry `token_id` and `expires_at`; both are optional, and
285
334
  a file written before they existed loads unchanged. `OXY_CREDENTIALS_PATH`
286
335
  overrides the path. Caches are separate, under `oxyc` (`~/.cache/oxyc`).
287
336
 
337
+ ### A signed-in browser for an agent
338
+
339
+ Sign-in is passwordless — Google or GitHub OAuth, or a magic link — and an
340
+ automation agent can finish neither. **`login-link`** prints a one-time URL
341
+ that signs a browser in with the credential `oxyc` is running on, so the whole
342
+ sign-in is one navigation:
343
+
344
+ ```bash
345
+ browser_navigate("$(oxyc login-link --env dev --next /ide)") # Playwright MCP
346
+ ```
347
+
348
+ ```ts
349
+ // a Playwright script
350
+ const link = execFileSync("oxyc", ["login-link", "--env", "dev", "--next", "/ide"], { encoding: "utf8" });
351
+ await page.goto(link.trim());
352
+ ```
353
+
354
+ Stdout is the URL and nothing else; what the link is goes to stderr.
355
+
356
+ - **The link works once and expires in minutes** (five, at the time of
357
+ writing — the server's `expires_at`). The bearer is never in it: `oxyc`
358
+ trades the token for a single-use ticket (`POST /api/auth/browser-ticket`),
359
+ and the ticket and `--next` ride the URL's fragment, which is not sent to a
360
+ server and so reaches no access log.
361
+ - **The session is bounded by the token.** It reaches what the token reaches
362
+ and nothing more, lasts at most the `session_seconds` the server answers
363
+ (stderr states it in hours), ends sooner if the token is revoked or expires,
364
+ and cannot manage tokens.
365
+ - **It needs a personal access token** (`oxy_pat_…`) — what `oxyc login`
366
+ stores. Any other credential is exit `4` with the next step: a session token
367
+ from an older login, a service account, CI or publish token, a legacy API
368
+ key. A sandbox agent token is exit `2`, before any request. A deployment that
369
+ predates sign-in links is exit `5`.
370
+ - **`--next <path>`** is where the browser lands: a path on the deployment
371
+ starting with a single `/`. A URL, `//host` or a backslash is exit `2`, and
372
+ no ticket is spent on it.
373
+ - **`--json`** prints `{ "url", "expires_at", "session_seconds" }` instead.
374
+ **`--open`** also opens the link in your default browser — which uses it, so
375
+ the printed URL will not work a second time.
376
+
288
377
  ### CI without a stored secret
289
378
 
290
379
  In a GitHub Actions job granted `id-token: write`, with no `OXY_TOKEN` set,
@@ -507,7 +596,15 @@ lets it publish.
507
596
  *The workflow* is `.github/workflows/oxy-publish.yml` at the repo root: a
508
597
  `build` job (no id-token) that runs `publish --build-only`, and a `publish` job
509
598
  bound to a GitHub `environment:` (`--environment`, default `oxy-publish`) whose
510
- only work is `publish --prebuilt`. That job uses no third-party action: each
599
+ only work is `publish --prebuilt`. `build` installs pnpm with
600
+ `pnpm/action-setup@v6` — the pnpm the repo root's package.json pins
601
+ (`packageManager` or `devEngines.packageManager`), with no `version:` of its
602
+ own, since the action refuses a second, different one; pnpm 11 when nothing
603
+ pins it — and both jobs run the Node in `.nvmrc`, else 26 (pnpm 11 needs
604
+ Node 22.13 or later). It warns when nothing pins pnpm, and when
605
+ `pnpm-workspace.yaml` approves build scripts only with `onlyBuiltDependencies`,
606
+ which pnpm 11 ignores (the build's install would fail with
607
+ `ERR_PNPM_IGNORED_BUILDS`; move the entries to `allowBuilds`). The publish job uses no third-party action: each
511
608
  step runs `npx @oxy-hq/cli@<this version>` and `oxyc` exchanges the job's OIDC
512
609
  token for itself, acting as the service account `--service-account <org>/<name>`
513
610
  names — by default `<app's org>/deployer`. **Here, and only here, the account is
@@ -664,6 +761,26 @@ object to stdout instead:
664
761
  { "app": "acme/dashboards", "appId": "…", "checks": [{ "name": "canary", "runId": "…", "status": "done", "passed": true, "durationMs": 4123 }] }
665
762
  ```
666
763
 
764
+ **`--browser`** also loads the app's page in headless Chromium and proves it
765
+ mounted: the platform runtime's `oxy-app-ready` beacon fires, nothing on the
766
+ page throws, and `--expect-selector <css>` (optional) becomes visible. With
767
+ `--app-env` the page must also report that environment and the build it serves
768
+ (`oxyc env show`'s `build_uuid`) in `window.__OXY_APP__`. The credential and
769
+ `X-Oxy-App-Env` ride only on requests under the app's own base path
770
+ (`/customer-apps/<org>/<app>/`: the document, assets, beacon and `/fn` calls).
771
+ The rest of the deployment's API is on the same origin, and the code under test
772
+ is often a build someone else published, so its `/api/…` calls go without the
773
+ credential, as an anonymous visitor's would. The result is one more entry in `checks[]`, with `"kind": "browser"` and
774
+ the `/fn` calls the page made. Playwright is not a dependency: it is resolved
775
+ from the working directory (`playwright`, `playwright-core` or
776
+ `@playwright/test`), so run it where one is installed. The page loads as a
777
+ user, so a publish, service-account, CI or sandbox agent token is refused
778
+ before any request.
779
+
780
+ ```bash
781
+ oxyc checks run acme/store --app-env dev-a1 --browser --expect-selector 'main'
782
+ ```
783
+
667
784
  **Auth** follows the same rule as `oxyc api`'s bearer surface, with one
668
785
  difference: `checks run` talks to `/api/admin/**`, which is not the
669
786
  `/external/api/*` surface, so a configured API key is sent explicitly as
@@ -980,9 +1097,12 @@ them, and a tool that can only fail still costs its schema every turn. A dead
980
1097
  token ends the server at startup with exit `4`.
981
1098
 
982
1099
  **`guide`** prints a page to paste into `AGENTS.md` / `CLAUDE.md`.
983
- **`skills install`** symlinks the six bundled Claude skills into
1100
+ **`skills install`** symlinks the bundled Oxy platform skills into
984
1101
  `~/.claude/skills`; it refuses to run from an `npx` cache, whose symlinks dangle
985
- once npm reclaims it.
1102
+ once npm reclaims it. The same skills are published to
1103
+ [`oxy-hq/skills`](https://github.com/oxy-hq/skills) for every other agent —
1104
+ `npx skills add oxy-hq/skills`, or the Claude Code plugin `oxy@oxy-hq`. They are
1105
+ written in `skills/` here; see `internal-docs/platform-skills.md`.
986
1106
 
987
1107
  ## Exit codes
988
1108
 
@@ -998,7 +1118,10 @@ once npm reclaims it.
998
1118
  9 a check ran and failed or timed out (`oxyc checks run`)
999
1119
  ```
1000
1120
 
1001
- `4` almost always means the wrong `--env`. `7` is worth retrying; `6` never is.
1121
+ `4` almost always means the wrong `--env`. A person logs in again. An agent on
1122
+ its own token (`tokens create --agent` or `--sandbox-agent`) does not: there,
1123
+ `4` means the token expired or was revoked, and the agent stops and reports.
1124
+ `7` is worth retrying; `6` never is.
1002
1125
 
1003
1126
  ## Environment variables
1004
1127