@oxy-hq/cli 0.9.0 → 0.10.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 +224 -25
- package/dist/main.mjs +10616 -3356
- package/dist/main.mjs.map +1 -1
- package/dist/{mcp-Bdp0rX0l.mjs → mcp-B2KBWNOU.mjs} +86 -3
- package/dist/mcp-B2KBWNOU.mjs.map +1 -0
- package/package.json +3 -2
- package/skills/oxy-cli/SKILL.md +8 -1
- package/skills/oxy-custom-apps/SKILL.md +209 -25
- package/skills/oxy-custom-apps/references/minimal-starter.md +10 -2
- package/skills/oxy-custom-apps/references/performance-and-usage.md +46 -0
- package/skills/oxy-custom-apps/references/sandbox-agent-loop.md +57 -17
- package/template/.claude/hooks/oxyc-cloud-session.sh +66 -0
- package/template/.claude/settings.json +16 -0
- package/template/.github/workflows/publish.yaml +1 -1
- package/template/.oxyc-managed +13 -0
- package/dist/mcp-Bdp0rX0l.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ manages customer workspace repos.
|
|
|
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`
|
|
10
|
-
- **Sandboxes** — `env`, `fn call`, `invocations`, `logs`
|
|
10
|
+
- **Sandboxes** — `env`, `fn dev`, `fn call`, `invocations`, `logs`
|
|
11
11
|
- **Workspace previews** — `preview`
|
|
12
12
|
- **Development** — `validate`, `mcp`, `guide`, `skills`
|
|
13
13
|
|
|
@@ -149,7 +149,7 @@ route's surface and fleet role.
|
|
|
149
149
|
## Authentication
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
|
-
oxyc login [--env <e>] [--login-env <e...>] [--assume <org> -r <why>]
|
|
152
|
+
oxyc login [--env <e>] [--login-env <e...>] [--assume <org> -r <why>] [--no-browser [--approver <email>]]
|
|
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
155
|
oxyc login-link [--next <path>] [--json] [--open] # a one-time URL that signs a browser in
|
|
@@ -158,8 +158,8 @@ oxyc tokens list [--json] # your personal access tokens
|
|
|
158
158
|
oxyc tokens revoke <id>
|
|
159
159
|
oxyc tokens revoke --current # end the token in OXY_TOKEN
|
|
160
160
|
oxyc tokens create # opens Account → Personal access tokens
|
|
161
|
-
oxyc tokens create --sandbox-agent --app <org>/<app> [--app …] [--hours 8] [--name …]
|
|
162
|
-
oxyc tokens create --agent [--standing] [--hours 8] [--name …]
|
|
161
|
+
oxyc tokens create --sandbox-agent --app <org>/<app> [--app …] [--hours 8] [--name …] [--no-browser [--approver <email>]]
|
|
162
|
+
oxyc tokens create --agent [--standing] [--hours 8] [--name …] [--no-browser [--approver <email>]]
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
**An agent runs on its own token, never on the person's `oxyc login`.** Which
|
|
@@ -264,6 +264,37 @@ writes the credentials file, and revokes no other token. `--app` is
|
|
|
264
264
|
`<org>/<app>` and repeats; arguments are checked before the browser opens
|
|
265
265
|
(exit `2`). Only staff who can open the app's sandboxes can approve.
|
|
266
266
|
|
|
267
|
+
**`--no-browser`** is the same approval for a machine no browser can reach
|
|
268
|
+
(a cloud container, a CI runner, a remote dev box), where the loopback's
|
|
269
|
+
`127.0.0.1` port is not the approver's. Instead of opening a page, `oxyc`
|
|
270
|
+
asks for a device code (`POST /api/auth/cli/device`) and prints, on stderr,
|
|
271
|
+
a URL (`<deployment>/cli-auth/device?code=…`) and an eight-letter code. The
|
|
272
|
+
operator opens the URL on any device where they are signed in, types the
|
|
273
|
+
code from the terminal (the page never shows it), and approves; `oxyc` polls
|
|
274
|
+
(`POST /api/auth/cli/device/poll`, with the PKCE verifier) and prints the
|
|
275
|
+
token exactly as above. A request lives ten minutes. A decline, or a refusal
|
|
276
|
+
on the page, stops the command with exit `4` (`access_denied`) and the
|
|
277
|
+
reason; nobody deciding in time is exit `7`. It works the same for
|
|
278
|
+
`tokens create --agent` and for `oxyc login`, with one difference: a token
|
|
279
|
+
approved by device code **never carries staff or partner access**, since
|
|
280
|
+
whoever started the request collects it and the approver cannot see who that
|
|
281
|
+
is. `--standing --no-browser` is a usage error (exit `2`), and a login made
|
|
282
|
+
this way reaches only what its approver's organization memberships do.
|
|
283
|
+
|
|
284
|
+
**`--approver <email>`** (env `OXY_APPROVER`, valid only with `--no-browser`;
|
|
285
|
+
anything else is a usage error, exit `2`) names who must approve, so nobody
|
|
286
|
+
has to hand them a URL: the platform tells that person itself, by push,
|
|
287
|
+
email and Slack DM where their organization connected it, and in their
|
|
288
|
+
Oxygen inbox. They type the code from your output on the approval page. The
|
|
289
|
+
notice never holds the code and links to `/cli-auth/device` without it, and
|
|
290
|
+
the request is then theirs alone: anyone else who finds the page is told
|
|
291
|
+
there is no such request. `oxyc` prints `Approval request sent to <email>`
|
|
292
|
+
beside the code and the URL; it prints that whether or not the deployment
|
|
293
|
+
knew the address, since the deployment never says. An address nobody holds
|
|
294
|
+
leaves a request nobody can approve: it lapses after ten minutes, so a typo
|
|
295
|
+
means running the command again with the right one. An agent still never
|
|
296
|
+
opens or approves the page.
|
|
297
|
+
|
|
267
298
|
What comes back is checked before anything is printed: it must be an
|
|
268
299
|
`oxy_sbx_`, described as `sandbox_agent` and not all-access, for exactly the
|
|
269
300
|
apps named, expiring no later than the hours asked for. Anything the check
|
|
@@ -276,14 +307,17 @@ what was minted, and exits `8`.
|
|
|
276
307
|
With that token in `OXY_TOKEN`, `oxyc` behaves differently in five ways:
|
|
277
308
|
|
|
278
309
|
- **An app resolves against the token's own list** (`GET /api/auth/token`),
|
|
279
|
-
never
|
|
310
|
+
never the app listing (`GET /api/customer-apps`), which it may not call. An
|
|
311
|
+
app it was not minted for is exit `5`.
|
|
280
312
|
- **Read-back uses `/api/customer-apps/{id}/…`** — `invocations list`,
|
|
281
|
-
`invocations held` and `checks run` —
|
|
313
|
+
`invocations held` and `checks run` — the same routes every other credential
|
|
314
|
+
uses; the token never reaches `/admin`.
|
|
282
315
|
- **Refused before any request, exit `2`:** an `--app-env` that is not
|
|
283
316
|
`dev-<handle>`; `publish` without `--app-env`, or with `--promote`;
|
|
284
|
-
`fn call`, `logs`, `invocations list` and `checks run` without
|
|
285
|
-
`env show` of production or staging; `
|
|
286
|
-
`tokens revoke <id>`; and `oxyc api` against
|
|
317
|
+
`fn call`, `fn run`, `logs`, `invocations list` and `checks run` without
|
|
318
|
+
`--app-env`; `env show` of production or staging; `env schedules`;
|
|
319
|
+
`oxyc apps`, `tokens list` and `tokens revoke <id>`; and `oxyc api` against
|
|
320
|
+
any path.
|
|
287
321
|
- **`whoami`** prints the token's description — `kind`, `expires_at`, the
|
|
288
322
|
`apps` it reaches and its `minter` — and `--json` is that document as the
|
|
289
323
|
server sent it.
|
|
@@ -541,6 +575,8 @@ the current directory while granting access to the customer's repo.
|
|
|
541
575
|
|
|
542
576
|
```bash
|
|
543
577
|
oxyc publish [--env <e>] [--dir <path>] [--promote] [--build-only | --prebuilt] [--json] [--allow-function-lint] [--app-env <dev-handle>]
|
|
578
|
+
oxyc promote <app> [--from staging|dev-<handle>] [--build <id>] [--remove-schedule <fn>]… [--dry-run] [--yes] [--json]
|
|
579
|
+
oxyc rollback <app> --app-env staging|production [--to <build>] [--remove-schedule <fn>]… [--dry-run] [--yes] [--json]
|
|
544
580
|
oxyc init-ci [--app <org>/<app>] [--environment <name>] [--promote] [--force] [--no-register] [--setup-action]
|
|
545
581
|
oxyc proxy [--port <n>] [--allow-writes] [--allow-events] [--yes]
|
|
546
582
|
```
|
|
@@ -549,11 +585,32 @@ oxyc proxy [--port <n>] [--allow-writes] [--allow-events] [--yes]
|
|
|
549
585
|
`pnpm install`, `pnpm build`, output `out/`), bundles each declared Oxy Function
|
|
550
586
|
with `pnpm exec esbuild` into `functions/<name>.js`, resolves the project from
|
|
551
587
|
the target's public `build-config`, and uploads the bundle — to the **draft**
|
|
552
|
-
channel
|
|
553
|
-
|
|
588
|
+
channel (staging). `--env` defaults to **production**; name it. Ship it with
|
|
589
|
+
`oxyc promote`, below. `--promote` still publishes and promotes in one request,
|
|
590
|
+
through the same server-side path, and the server answers it with a
|
|
591
|
+
deprecation warning: it shows nobody the plan.
|
|
592
|
+
|
|
593
|
+
- **The manifest** is read strictly: `oxy-app.json` is validated against the
|
|
594
|
+
JSON Schema `@oxy-hq/sdk` generates from its `OxyAppManifest` type (the same
|
|
595
|
+
check `oxyc validate` and `@oxy-hq/vite-plugin` make, in the same words). An
|
|
596
|
+
error — a wrong type, a malformed `slug` or function name, a missing
|
|
597
|
+
required field, a notification catalogue the server refuses — stops the
|
|
598
|
+
publish before the build with exit `2`, listing every problem; a field the
|
|
599
|
+
schema does not know is a warning, since the platform ignores what it does
|
|
600
|
+
not read.
|
|
554
601
|
- **Identity**: `--org` / `--app`, then `OXY_ORG` / `OXY_APP`, then the manifest's
|
|
555
602
|
`orgSlug` / `slug`, then an `apps/<org>/<app>/` working directory. `--org`
|
|
556
603
|
takes a slug or a UUID. `--project` pins the workspace (and implies its org).
|
|
604
|
+
- **First publish**: an app the target does not know yet makes the
|
|
605
|
+
build-config lookup 404. With a stored token, `publish` lists the org's
|
|
606
|
+
workspaces: exactly one, and it publishes there, saying so on stderr;
|
|
607
|
+
several, and it exits `5` listing them with their uuids beside the
|
|
608
|
+
`--project <uuid>` to pass. Under trusted publishing (no token) it exits `5`
|
|
609
|
+
with the two-step hint, as before.
|
|
610
|
+
- **Kit versions**: an app whose `@oxy-hq/sdk` or `@oxy-hq/vite-plugin` is more
|
|
611
|
+
than one minor behind the versions this `oxyc` was built beside gets one
|
|
612
|
+
warning per package before the build, naming the `pnpm add`. Read from the
|
|
613
|
+
app's `package.json` (the installed copy when there is one); no network.
|
|
557
614
|
- **`--dir`** publishes a pre-built directory instead of building; functions are
|
|
558
615
|
still bundled into it. **`--build-only`** stops after building and bundling —
|
|
559
616
|
no credential, no network unless the org must come from `--project`.
|
|
@@ -645,13 +702,42 @@ policies. Registration never fails the command.
|
|
|
645
702
|
**`--app-env <dev-handle>`** moves one sandbox's build pointer instead of the
|
|
646
703
|
draft/live channel — see [Sandboxes](#sandboxes). It needs a staff credential
|
|
647
704
|
(never a publish token or OIDC) and is refused together with `--promote`: a
|
|
648
|
-
sandbox build
|
|
649
|
-
|
|
705
|
+
sandbox build goes to production only through staging — `oxyc promote <app>
|
|
706
|
+
--from dev-<handle>`, then `oxyc promote <app>`.
|
|
707
|
+
|
|
708
|
+
**`promote`** moves a build up one environment: staging's to production (the
|
|
709
|
+
default), or a sandbox's to staging (`--from dev-<handle>`). Nothing else —
|
|
710
|
+
production is never promoted, and moving it backwards is `rollback`. It asks the
|
|
711
|
+
server for the **plan** first and prints it: the build (commit, branch,
|
|
712
|
+
publisher, how long the source has served it), what the target serves now,
|
|
713
|
+
functions and production schedules that change, the migrations production
|
|
714
|
+
applies **before** it moves, and `required` secrets production has no value for.
|
|
715
|
+
Then it asks on a terminal (off one it needs `--yes`, else exit `8`) and acts,
|
|
716
|
+
sending the plan's build and `expect_current` back — `409 target_moved` if the
|
|
717
|
+
target moved in between. `--dry-run` stops at the plan. A plan with blockers is
|
|
718
|
+
refused before any act (exit `6`): `staging_never_served` (production only
|
|
719
|
+
serves a build staging has served), `validation_not_passed`,
|
|
720
|
+
`required_secrets_unset`, and `schedule_removal_unconfirmed` — name each retired
|
|
721
|
+
schedule with `--remove-schedule <function>`. `--build` names an older build the
|
|
722
|
+
source served (publish id or UUID). Promoting the build already served is
|
|
723
|
+
`409 already_serving`.
|
|
724
|
+
|
|
725
|
+
**`rollback`** moves `--app-env` (staging or production; never a sandbox) back
|
|
726
|
+
to the build it served before its current one — twice swaps back — or `--to`
|
|
727
|
+
one it served earlier. Same plan, prompt and `--yes`. A rollback never reverses
|
|
728
|
+
a migration: the target's pending files are applied like any production move,
|
|
729
|
+
and the plan warns when the target predates files already applied. An unset
|
|
730
|
+
`required` secret only warns here (it is the emergency path).
|
|
731
|
+
|
|
732
|
+
Both need a staff credential: a publish token, a service account's and a sandbox
|
|
733
|
+
agent token are refused before any request (exit `2`). `--json` prints the
|
|
734
|
+
server's plan verbatim.
|
|
650
735
|
|
|
651
736
|
## Apps
|
|
652
737
|
|
|
653
|
-
Read-only
|
|
654
|
-
(the app-admin role). `<app>` is
|
|
738
|
+
Read-only except `domains add | verify | remove`: every other request these make
|
|
739
|
+
is a GET, against `/api/customer-apps/**` (the app-admin role). `<app>` is
|
|
740
|
+
`<org-slug>/<app-slug>` or an app UUID.
|
|
655
741
|
|
|
656
742
|
```bash
|
|
657
743
|
oxyc apps list [--org <slug>] [--published | --draft] [--builds] [--json]
|
|
@@ -660,6 +746,10 @@ oxyc apps builds <app> [--json]
|
|
|
660
746
|
oxyc apps health [<app>] [--needs-attention] [--json]
|
|
661
747
|
oxyc apps usage <app> [--json]
|
|
662
748
|
oxyc apps drift [<app>] [--org <slug>] [--dir <path>] [--refresh] [--json]
|
|
749
|
+
oxyc apps domains [list] <app> [--json]
|
|
750
|
+
oxyc apps domains add <app> <domain> [--json]
|
|
751
|
+
oxyc apps domains verify <app> <domain> [--json]
|
|
752
|
+
oxyc apps domains remove <app> <domain> [--json]
|
|
663
753
|
```
|
|
664
754
|
|
|
665
755
|
- **`list`** — every app you can see, across organizations, walking the paged
|
|
@@ -667,12 +757,17 @@ oxyc apps drift [<app>] [--org <slug>] [--dir <path>] [--refresh] [--json]
|
|
|
667
757
|
`draft` when it has none. `SOURCE` is the repository the app is *registered*
|
|
668
758
|
against. `--builds` adds the live build's id, the repository and commit it was
|
|
669
759
|
built from, who published it and when — one extra request per app, six at a
|
|
670
|
-
time.
|
|
760
|
+
time. `--json` rows also carry the server's `verdict` and, for a published
|
|
761
|
+
app that was measured, `availability`: request success over six windows and
|
|
762
|
+
the burn verdict. An absent `availability` means "not read", never healthy.
|
|
671
763
|
- **`show`** — one app on one screen: the registry row, the live build, the
|
|
672
764
|
draft build when it is newer than the live one, deployment integrity,
|
|
673
765
|
availability, and the 7-day usage summary. `last promote` is when the promote
|
|
674
766
|
or rollback action was last used; `oxyc publish --promote` does not update it,
|
|
675
|
-
so it can be older than the live build.
|
|
767
|
+
so it can be older than the live build. Availability is the registry row's
|
|
768
|
+
reading, not a request of its own: with none, `show` prints `no reading`, and
|
|
769
|
+
it is never in `failed`. `--json` has the registry row under `app` and its
|
|
770
|
+
`availability` (or `null`) under `availability`.
|
|
676
771
|
- **`builds`** — the build history, newest first. `CHANNEL` marks the build each
|
|
677
772
|
channel points at: `live`, `draft`, or both for one build.
|
|
678
773
|
- **`health`** — with no argument, the fleet table (published apps only):
|
|
@@ -682,6 +777,7 @@ oxyc apps drift [<app>] [--org <slug>] [--dir <path>] [--refresh] [--json]
|
|
|
682
777
|
of the last 24 hours.
|
|
683
778
|
- **`usage`** — the 7-day summary, the visitors, and the tracked events by name.
|
|
684
779
|
- **`drift`** — see below.
|
|
780
|
+
- **`domains`** — see [Custom domains](#custom-domains).
|
|
685
781
|
|
|
686
782
|
Times are UTC. `--json` prints one document on stdout; a table goes to stdout and
|
|
687
783
|
the count line to stderr.
|
|
@@ -698,6 +794,39 @@ a drifted app is an answer and exits `0` — read it from the output.
|
|
|
698
794
|
The health route answers **503 with its report** when a check fails; `oxyc`
|
|
699
795
|
prints that as a failing report, not as an outage.
|
|
700
796
|
|
|
797
|
+
### Custom domains
|
|
798
|
+
|
|
799
|
+
Serve an app on a domain the customer owns (`app.acme.com`) instead of its Oxy
|
|
800
|
+
subdomain. `<domain>` is a bare host name; `verify` and `remove` find the row by
|
|
801
|
+
that name (case-insensitive), so you never handle the domain's id.
|
|
802
|
+
|
|
803
|
+
```bash
|
|
804
|
+
oxyc apps domains acme/store # list (the default verb)
|
|
805
|
+
oxyc apps domains add acme/store app.acme.com # then create the two DNS records it prints
|
|
806
|
+
oxyc apps domains verify acme/store app.acme.com
|
|
807
|
+
oxyc apps domains remove acme/store app.acme.com
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
- **`list`** — each domain's `STATUS` (`pending` or `verified`), its URL and
|
|
811
|
+
when it was last checked. For every `pending` domain it prints the DNS records
|
|
812
|
+
to create — a `CNAME` from the domain to the deployment's target (a note to
|
|
813
|
+
ask Oxy ops when the deployment has none configured) and a `TXT` ownership
|
|
814
|
+
record — plus the checker's last error, if any.
|
|
815
|
+
- **`add`** — registers the domain as `pending` and prints the same two records
|
|
816
|
+
and the `verify` command to run next. A malformed name is `400
|
|
817
|
+
invalid_domain` and a duplicate `409 domain_exists`; both exit `6` with the
|
|
818
|
+
server's message.
|
|
819
|
+
- **`verify`** — checks DNS now. `verified` prints the URL and exits `0`. Still
|
|
820
|
+
`pending` is **not** success: it prints the records again and the checker's
|
|
821
|
+
reason, and exits `1` — DNS may not have propagated, so re-run it. `409
|
|
822
|
+
domain_taken` (another app already serves the domain) exits `6`.
|
|
823
|
+
- **`remove`** — stops serving the app on that domain. A name the app does not
|
|
824
|
+
have exits `5` and deletes nothing.
|
|
825
|
+
|
|
826
|
+
`--json` prints one document on stdout: `{app, app_id, domains, cname_target}`
|
|
827
|
+
for `list`, and `{app, app_id, domain, cname_target}` for `add` and `verify`
|
|
828
|
+
(`verify` still exits `1` while pending).
|
|
829
|
+
|
|
701
830
|
### `apps drift`
|
|
702
831
|
|
|
703
832
|
Compares what is live against source on **this machine**. For each app it takes
|
|
@@ -782,7 +911,7 @@ oxyc checks run acme/store --app-env dev-a1 --browser --expect-selector 'main'
|
|
|
782
911
|
```
|
|
783
912
|
|
|
784
913
|
**Auth** follows the same rule as `oxyc api`'s bearer surface, with one
|
|
785
|
-
difference: `checks run` talks to `/api/
|
|
914
|
+
difference: `checks run` talks to `/api/customer-apps/**`, which is not the
|
|
786
915
|
`/external/api/*` surface, so a configured API key is sent explicitly as
|
|
787
916
|
`X-API-Key` only when **no bearer resolves** — never both. A bearer from
|
|
788
917
|
`oxyc login` (or `--token-env`) always wins when one is present.
|
|
@@ -791,11 +920,12 @@ difference: `checks run` talks to `/api/admin/**`, which is not the
|
|
|
791
920
|
holding `id-token: write` signs itself in from its GitHub OIDC token — the same
|
|
792
921
|
two exchanges `oxyc publish` tries, in the same order: a service account's
|
|
793
922
|
trust policy first, the app's registered publisher second — and drives
|
|
794
|
-
`/api/customer-apps
|
|
795
|
-
|
|
796
|
-
`
|
|
797
|
-
|
|
798
|
-
|
|
923
|
+
`/api/customer-apps/**`, the same three routes a person's credential uses. A
|
|
924
|
+
service account's token (`oxy_sat_…`, `oxy_ci_…`) or a publish token set in
|
|
925
|
+
`OXY_TOKEN` is used without minting. What differs is the app lookup: a
|
|
926
|
+
personal token, a legacy API key or a session can list apps to resolve
|
|
927
|
+
`<org>/<app>`; a machine credential may not, so it names the app by UUID or
|
|
928
|
+
through its own grants. `OXY_API_KEY` is read **before** OIDC here: a job that set one has said
|
|
799
929
|
which credential it means.
|
|
800
930
|
|
|
801
931
|
The app id comes from the credential where it can. The publisher exchange
|
|
@@ -852,20 +982,33 @@ oxyc env create <app> <name> # name is dev-<handle>, 1-12 chars
|
|
|
852
982
|
oxyc env list <app>
|
|
853
983
|
oxyc env show <app> <name>
|
|
854
984
|
oxyc env delete <app> <name> [--yes] [--wait [seconds]]
|
|
985
|
+
oxyc env schedules <app> <name> [--on|--off] # staging's or a sandbox's switch; off by default
|
|
855
986
|
|
|
856
987
|
oxyc publish --app-env <name> [--env <e>] # see "Custom apps" above
|
|
988
|
+
oxyc fn dev <function> [--input <json|@file|->] [--env-file <path>] [--watch] [--read-through [--env <e>]] # local, in-process
|
|
857
989
|
oxyc fn call <app> <function> [--app-env <name>] [--data <json|@file|->]
|
|
990
|
+
oxyc fn run <app> <function> [--app-env <name>] [--as-schedule] [--data …] [--timeout <s>]
|
|
858
991
|
oxyc checks run <app> --app-env <name> # see "Checks" above
|
|
859
992
|
|
|
860
993
|
oxyc invocations list <app> [--app-env] [--build] [--function] [--limit]
|
|
861
994
|
oxyc invocations held <app> <invocation-id>
|
|
862
995
|
oxyc logs <app> [--app-env] [--invocation] [--request] [--hours] [--limit]
|
|
996
|
+
oxyc logs <app> [--app-env <name>] --follow [--timeout <seconds>] [--json]
|
|
863
997
|
|
|
864
998
|
oxyc env secret list <app> --app-env <name> # keys and flags, never a value
|
|
865
999
|
oxyc env secret set <app> <key> --app-env <name> (--value <text> | --value-env <VAR>)
|
|
866
1000
|
oxyc env secret delete <app> <key> --app-env <name>
|
|
867
1001
|
```
|
|
868
1002
|
|
|
1003
|
+
`logs --follow` is the live tail: each line as a function prints it,
|
|
1004
|
+
wherever the function runs, with no observability capture needed (the polled
|
|
1005
|
+
`logs` reads ClickHouse and answers `501` without it). `--json` prints one
|
|
1006
|
+
event per line — `open`, then `start` / `log` / `finish` per invocation,
|
|
1007
|
+
`dropped` when lines were lost, and `done` last, on which it exits `0`. A
|
|
1008
|
+
dropped connection is reopened with `Last-Event-ID`, and the server replays
|
|
1009
|
+
the outcomes of what finished meanwhile, never the lines. Production without
|
|
1010
|
+
`--app-env`; staging and sandboxes need staff with non-production reach.
|
|
1011
|
+
|
|
869
1012
|
A **sandbox** is a named, short-lived `dev-<handle>` environment of one custom
|
|
870
1013
|
app: its own build pointer, storage silo, secrets, Airhouse sibling and — in
|
|
871
1014
|
an org with an OLTP staging branch — its own copy of the app's database, with
|
|
@@ -893,8 +1036,8 @@ to sit in an agent's context.
|
|
|
893
1036
|
validated client-side — exit `2` on a malformed name) is a *different axis*
|
|
894
1037
|
from `--env`, which is the deployment: both can appear on the same command
|
|
895
1038
|
(`oxyc fn call acme/store f --env dev --app-env dev-a1`). It is not a global
|
|
896
|
-
flag — only `publish`, `fn call`, `checks run`, `invocations list`
|
|
897
|
-
take it. A publish-token credential is refused before any request for
|
|
1039
|
+
flag — only `publish`, `fn call`, `fn run`, `checks run`, `invocations list`
|
|
1040
|
+
and `logs` take it. A publish-token credential is refused before any request for
|
|
898
1041
|
`--app-env` other than `production` (exit `2`); `env`, `invocations` and
|
|
899
1042
|
`logs` refuse one outright, for any environment, because sandbox management
|
|
900
1043
|
is a staff console surface.
|
|
@@ -928,6 +1071,56 @@ distinct from a non-2xx on the POST itself (exit per the usual status mapping
|
|
|
928
1071
|
— e.g. `EnvironmentHasNoBuild` before the stream starts). The
|
|
929
1072
|
`x-oxy-invocation-id` response header becomes `invocationId` in the result.
|
|
930
1073
|
|
|
1074
|
+
**`fn dev`** runs a handler locally, from the app directory, with no server and
|
|
1075
|
+
no credentials: it bundles `functions/<name>.ts` with the esbuild call `oxyc
|
|
1076
|
+
publish` uses, runs it in-process on `createTestContext` from
|
|
1077
|
+
`@oxy-hq/sdk/testing`, and prints the response and every host op the handler
|
|
1078
|
+
made (`host ops: email.send`, one line per call). The context is seeded from
|
|
1079
|
+
`oxy-app.json` — the function's capabilities and destinations — and `ctx.env`
|
|
1080
|
+
from `--env-file` (default `.env`; never your shell). `--input` is the request
|
|
1081
|
+
body, as `fn call --data`. `--watch` re-runs on a change under the app
|
|
1082
|
+
directory. It exits `0` for a 2xx `Response`, `1` when the handler throws or
|
|
1083
|
+
answers non-2xx, `2` for a function the manifest does not declare. **A local run
|
|
1084
|
+
is not the isolate:** no timeout, memory or log limits, Node globals the isolate
|
|
1085
|
+
lacks, and every warehouse, semantic, storage, fetch and email call is stubbed
|
|
1086
|
+
in memory — nothing reaches a deployment. Check it where it really runs with
|
|
1087
|
+
`fn call` against a sandbox.
|
|
1088
|
+
|
|
1089
|
+
`fn dev --read-through [--env <name|url>]` sends exactly `ctx.query`,
|
|
1090
|
+
`ctx.warehouse.query` and `ctx.semantic.query` to that deployment, through
|
|
1091
|
+
`POST /api/projects/{project}/query` and `/semantic-query` (the routes a
|
|
1092
|
+
published app calls), authenticated as `oxyc api` is (`OXY_TOKEN` or your
|
|
1093
|
+
`oxyc login`). The server decides what you may read. The project is `--project`
|
|
1094
|
+
/ `OXY_PROJECT`, else looked up from `oxy-app.json` `slug` + `orgSlug`. Every
|
|
1095
|
+
other call — writes, transactions, storage, fetch, email — stays stubbed, the
|
|
1096
|
+
report marks each call `real` or `stubbed`, and a local run is still not the
|
|
1097
|
+
isolate.
|
|
1098
|
+
|
|
1099
|
+
**`env schedules`** reads, or with `--on` / `--off` flips, the schedules
|
|
1100
|
+
switch of staging or a sandbox (`GET`/`PUT
|
|
1101
|
+
/api/customer-apps/{id}/environments/{name}/schedules`). Off by default: a
|
|
1102
|
+
schedule outside production reads production data on a timer and calls third
|
|
1103
|
+
parties with staging's credentials, so an environment fires its functions'
|
|
1104
|
+
`schedule`s only once staff turn it on. On, it has one schedule row per
|
|
1105
|
+
function its build schedules, kept in step by every publish to it; `--off`
|
|
1106
|
+
removes them. Production has no switch (exit `2`) — its schedules always run.
|
|
1107
|
+
Staff only: a sandbox agent token is refused before any request (exit `2`).
|
|
1108
|
+
A scheduled fire, and a delivery to the environment's own webhook route
|
|
1109
|
+
(`/api/webhooks/apps-env/{environment}/{org}/{app}/{function}`, verified with
|
|
1110
|
+
that environment's own secret), are not a sandbox's activity.
|
|
1111
|
+
|
|
1112
|
+
**`fn run`** queues a function as a background run — the console's **Run
|
|
1113
|
+
now** — and polls it to a verdict (`--timeout`, default 300 s), unlike `fn
|
|
1114
|
+
call`, which is the function's own route. It runs as the system (the org
|
|
1115
|
+
owner, no caller), with the function's retry policy; outside production only a
|
|
1116
|
+
`"check": true` function runs. **`--as-schedule`** runs a function that
|
|
1117
|
+
declares a `schedule` once, as its cron would: no input (`--data` is exit
|
|
1118
|
+
`2`), the environment's build and policy, whether or not its switch is on —
|
|
1119
|
+
the way to test a scheduled function in a sandbox without turning schedules
|
|
1120
|
+
on. A function with no schedule is `409 not_scheduled` (exit `6`), production
|
|
1121
|
+
included. `--json` prints `{function, environment, runId, status, ok, trigger,
|
|
1122
|
+
answer, error?, invocationId}`; exit `1` when the run did not end `done`.
|
|
1123
|
+
|
|
931
1124
|
**`invocations held`** is what the non-production write policy did *not* do —
|
|
932
1125
|
production's would-be effect, recorded rather than performed. It is `[]` for a
|
|
933
1126
|
production invocation, since nothing is held there.
|
|
@@ -1149,6 +1342,12 @@ its own token (`tokens create --agent` or `--sandbox-agent`) does not: there,
|
|
|
1149
1342
|
| `OXYC_QUIET` | — | `1` is `--quiet` |
|
|
1150
1343
|
| `OXYC_DRY_RUN` | — | `1` makes `launch` print the command instead of running it |
|
|
1151
1344
|
| `NO_COLOR` / `FORCE_COLOR` | — | force plain / coloured output |
|
|
1345
|
+
| `HTTPS_PROXY` / `HTTP_PROXY` | — | requests go through it. On Node 22.21+ and 24.5+, `oxyc` turns on Node's proxy support itself (`NODE_USE_ENV_PROXY`), re-running once if it has to. Older Node cannot proxy `fetch`, and the error says so |
|
|
1346
|
+
|
|
1347
|
+
**In a Claude Code cloud session,** the repo's SessionStart hook installs
|
|
1348
|
+
`oxyc` and keeps an agent token across shell calls. The environment must allow
|
|
1349
|
+
the Oxy hosts. Setup: the `oxy-custom-apps` skill, "Headless and cloud
|
|
1350
|
+
sessions".
|
|
1152
1351
|
|
|
1153
1352
|
**An agent should set `OXY_AGENT`.** Every request `oxyc` makes to a deployment
|
|
1154
1353
|
carries the user agent `oxyc/<version>`, with `agent/<label>` after it when an
|