@camox/cli 0.58.1 → 0.59.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 CHANGED
@@ -1,229 +1,71 @@
1
1
  # Camox CLI
2
2
 
3
- ## Runtime lookup and monorepos
4
-
5
- Runtime-backed commands (`status`, `pages`, `blocks`, `layouts`, `locales`, `files`,
6
- `publish`, `delete`, `preview`, and `logout`) accept `--cwd PATH`:
7
-
8
- ```sh
9
- camox pages list --cwd apps/site
10
- camox status --cwd apps/site
11
- camox files upload --cwd apps/site --file ./hero.webp --alt "Product screenshot"
12
- ```
13
-
14
- The CLI searches for `node_modules/.camox/runtime.json` starting in that directory
15
- and walking upward. Without `--cwd`, lookup starts in the shell's current directory.
16
- There is no workspace or child-directory discovery. Run the app's dev server or
17
- build once to generate its runtime file.
18
-
19
- Relative `--cwd` paths resolve against the shell's current directory. This option
20
- only affects runtime lookup: other paths, such as `--file ./hero.webp`, still
21
- resolve against the shell's current directory. Missing or non-directory paths fail
22
- instead of falling back to the shell's directory.
23
-
24
- ## Your development draft
25
-
26
- Every command reads and writes your development draft: it shows Live wherever
27
- it changes nothing, and publishing it lands on Live. No command takes a draft
28
- flag. Dev mode creates the draft on its first sync after `camox login`, so run
29
- your site locally once (e.g. `pnpm dev`) before using the CLI; until then,
30
- commands fail with `NO_DEVELOPMENT_DRAFT`.
31
-
32
- Agents in parallel worktrees share their developer's development draft, and
33
- publish or delete it together.
34
-
35
- ## Publishing and deleting
36
-
37
- ```sh
38
- camox status # whether your development draft exists, and how many changes it holds
39
- camox publish # what publishing would do, with a token
40
- camox publish --confirm TOKEN # publish to Live
41
- camox delete # empty your development draft (asks first; --yes skips the question)
42
- ```
43
-
44
- `camox publish` lists, in order:
45
-
46
- - **Blockers**, which prevent publishing until they're fixed in the draft. They
47
- include definitions that differ from Live's: your local code changed a block,
48
- layout or collection, and only a release (`camox release`) can ship it to Live.
49
- - **Conflicts**: content Live changed or deleted since your draft changed it,
50
- with who published it and when. Publishing overwrites Live's version.
51
- - **Lost changes**, which publishing drops because what they belong to is gone.
52
- - **Warnings**: the same problems already on Live, which don't block.
53
- - **Changes**: everything your draft creates, changes or deletes.
54
-
55
- The token records the conflicts the list showed. `camox publish --confirm TOKEN`
56
- publishes the whole draft, which then stays open, emptied. If Live changed since
57
- the list was printed, nothing is published: the command prints the new list and
58
- token, fails with `CONFLICTS_CHANGED`, and you review it before confirming again.
59
-
60
- `camox delete` empties your development draft without touching Live. It asks
61
- for confirmation in a terminal; elsewhere it fails with `CONFIRMATION_REQUIRED`
62
- unless you pass `--yes`.
63
-
64
- ## Authenticated local preview
65
-
66
- CLI login does not sign in a fresh browser. To verify writes in your development draft:
3
+ The CLI does what the hosted platform needs from a terminal. Projects are created
4
+ from the dashboard at [camox.ai](https://camox.ai), and their content is edited
5
+ in the studio.
67
6
 
68
7
  ```sh
69
- camox preview --url http://localhost:3000/about --json
70
- camox preview --cwd apps/site --url http://localhost:3000/about --json
8
+ camox login # sign in to Camox in the browser
9
+ camox logout # sign out
10
+ camox release # build the website and sync its definitions to Live
11
+ camox release --dry-run # build it as a release would, without syncing anything
12
+ camox clone PROJECT # clone a project's code, where its project allows it
13
+ camox push # push the current branch's commits to a new draft
71
14
  ```
72
15
 
73
- Open the returned `url` in the browser you use for verification. Wait for
74
- `[data-camox-preview="ready"]`, which identifies the project and your
75
- development draft after authenticated rendering. Errors appear as
76
- `[data-camox-preview="error"]`; expired or consumed links require a fresh command.
77
- The CLI and SDK must both support the preview handoff.
16
+ ## Signing in
78
17
 
79
- The URL transfers your signed-in session: treat it as a credential, do not share
80
- or commit it, and use it within three minutes. Only HTTP(S) loopback URLs are
81
- accepted. The destination must match the CLI's project and backend. There is no
82
- automatic default-browser opening.
83
- The local dev server must already be running.
18
+ `camox login` opens the browser and stores the session in `~/.camox/auth.json`,
19
+ keyed by the Camox URL. A repository can override it for a given URL with its
20
+ own `.camox/auth.json`, next to its `.git`.
84
21
 
85
- ## Page SEO
22
+ `camox logout` signs out of the authentication URL of the app it finds:
23
+ it looks for `node_modules/.camox/runtime.json` from the current directory, or
24
+ from `--cwd PATH`, walking upward. Without one, it signs out of camox.ai.
86
25
 
87
- Use `pages get` to read metadata and `pages set-seo` to edit it:
26
+ ## Releasing
88
27
 
89
- ```sh
90
- camox pages get --path /about --json
91
- camox pages set-seo --path /about --meta-title "About us" --meta-description "Meet the team."
92
- # Page and block IDs are UUIDv7s; anything else is rejected.
93
- PAGE_ID=0199f1c2-7b3e-7a10-9c4d-5e6f7a8b9c0a
94
- camox pages set-seo --id "$PAGE_ID" --meta-description ""
95
- camox pages set-seo --id "$PAGE_ID" --ai-seo on
96
- camox pages set-seo --id "$PAGE_ID" --ai-seo off
97
- ```
98
-
99
- - `set-seo` requires exactly one of `--id` or `--path` and at least one of
100
- `--meta-title`, `--meta-description` or `--ai-seo`. An empty meta title blocks
101
- publishing.
102
- - The meta title and description are one group: both generated or both manual.
103
- Writing either field makes **both** manual, keeping the current value of the
104
- other. Manual metadata cannot be combined with `--ai-seo on`.
105
- - `--ai-seo off` keeps current metadata as manual. `--ai-seo on` schedules asynchronous
106
- generation; the returned page contains current values, not necessarily generated
107
- values. Use `pages get` to inspect them later.
108
- - Changes are visible in your development draft right away, and reach Live
109
- when you publish it.
110
- - Commands support `--project PROJECT` (a Project ID or Project Slug) and
111
- `--json`, with structured JSON output/errors.
112
-
113
- See `camox pages update --help` for options and examples.
114
-
115
- ## Shared navigation and footer content
28
+ `camox release` runs `vp build` in release mode. It needs `CAMOX_DEPLOY_TOKEN`
29
+ and is what the release and Upgrade sandboxes run, not something members run
30
+ by hand. `--dry-run` builds the same way without a token and without syncing
31
+ definitions to Live, to check that a website builds.
116
32
 
117
- `pages get` shows page-owned blocks. To find layout-owned blocks:
33
+ ## Cloning a project's code
118
34
 
119
- ```sh
120
- camox layouts list
121
- camox layouts get --layout-id default --json
122
- BLOCK_ID=0199f1c2-7b3e-7a10-9c4d-5e6f7a8b9c0b
123
- camox blocks get --id "$BLOCK_ID"
124
- camox blocks edit --id "$BLOCK_ID" --content '{"title": "Updated title"}'
125
- ```
126
-
127
- A layout is identified by the name its code gives it: the `layoutId` from
128
- `layouts list`, which is also a page's `layoutId`. `layouts get` returns
129
- `blocks` with IDs, types, layout slot, and content, along with ordered
130
- `layout.beforeBlockIds` and `layout.afterBlockIds`. Use a returned block `id`
131
- with `blocks get` and `blocks edit`, inspecting its schema and current content
132
- before editing.
35
+ On a project whose code can be worked on outside Camox (a flag set by hand on
36
+ the project), a member of its organization can clone its code
37
+ (ADR 0026). `camox clone PROJECT [DIRECTORY]` takes its Project Slug or Project
38
+ ID, clones its repo into `DIRECTORY` (its Project Slug by default), then runs
39
+ `pnpm install`.
133
40
 
134
- Commands accept `--cwd`, `--project`, and `--json`.
135
- Keep the project consistent when using discovered IDs.
136
- Editing a shared layout block changes it for all pages using that layout.
41
+ The clone authenticates with short-lived read tokens. `camox clone` sets
42
+ `camox git-credential` as the credential helper for the repo's host in the
43
+ clone's own `.git/config`, never in the global one, so `git fetch` and
44
+ `git pull` get a fresh token from the API with your Camox login when they need
45
+ one. `git-credential` is the helper Git runs, not a command to run by hand.
137
46
 
138
- ## Locales
47
+ ## Pushing to a draft
139
48
 
140
- ```sh
141
- camox locales list
142
- camox locales add --tag de
143
- camox pages get --path /pricing --locale de
144
- camox locales remove --tag de
145
- camox locales set-default-tag --tag en-GB
146
- ```
49
+ From a clone, `camox push` sends the current branch's commits to Camox, which
50
+ opens a draft named after the branch with them (ADR 0026). It reads the Project
51
+ ID from the checkout's `vite.config.ts`, fetches `main`, and sends the commits
52
+ since the branch's merge base with `main` as a Git bundle to the API, never to
53
+ the repo. The API checks them first and refuses, writing nothing, a branch that
54
+ changes a platform-owned file or anything under `src/` other than TypeScript or
55
+ CSS, that starts from a commit never on `main`, that is missing an Upgrade on
56
+ `main` (merge `main` first), that conflicts with `main`, or whose head doesn't
57
+ build. An accepted push lands the commits as they are on the draft's branch and
58
+ builds the head as one revision. `camox push` waits for that build, sets the
59
+ branch's upstream to the draft's branch, and prints the draft's studio URL. A
60
+ refusal prints its message, with the build's errors when it doesn't build, and
61
+ exits non-zero.
147
62
 
148
- Locales are added and removed in your development draft, like content. Camox
149
- translates the default locale into every other locale of the draft, when a
150
- locale is added and after every change to the default locale. Content commands
151
- (`pages`, `blocks`, `layouts get`) take `--locale <TAG>` and act on the default
152
- locale without it. Correcting the default locale's tag applies at once
153
- and is only possible while it's the only locale.
63
+ ## The SDK's auth module
154
64
 
155
- ## Media
65
+ `@camox/cli/auth` exports the token storage and sign-in helpers the SDK's Vite
66
+ plugin imports.
156
67
 
157
- Run `camox login` and start your project's dev server (or build once) so the CLI
158
- can discover the project's runtime configuration.
159
-
160
- ```sh
161
- # Upload a local file with manual alt text.
162
- camox files upload --file ./hero.webp --alt "Product screenshot" --json
163
-
164
- # Download a remote file and store a copy in Camox.
165
- camox files upload --url https://example.com/hero.webp --alt "Product screenshot"
166
-
167
- # Optionally override the uploaded filename.
168
- camox files upload --url https://example.com/download --filename hero.webp --ai-metadata off
169
-
170
- # Inspect the returned ID and URL; use the ID in your block's media field.
171
- # File IDs are UUIDv7s; anything else is rejected.
172
- camox files list
173
- FILE_ID=0199f1c2-7b3e-7a10-9c4d-5e6f7a8b9c0d
174
- camox files get --id "$FILE_ID"
175
-
176
- camox files update --id "$FILE_ID" --filename hero.webp
177
- camox files update --id "$FILE_ID" --alt "Updated description"
178
- camox files update --id "$FILE_ID" --alt ""
179
- camox files update --id "$FILE_ID" --ai-metadata on
180
-
181
- # Replace the underlying file everywhere it is referenced, preserving its ID.
182
- camox files update --id "$FILE_ID" --file ./new-hero.webp
183
- camox files update --id "$FILE_ID" --url https://example.com/new-hero.webp
184
-
185
- # Replace content and override metadata together.
186
- camox files update --id "$FILE_ID" --file ./new-hero.webp --filename hero.webp --alt "New screenshot"
187
- ```
68
+ ## The Template's copy
188
69
 
189
- - Upload requires exactly one of `--file` or `--url`. URLs are copied, not linked.
190
- - Alt text and filename are one group: both generated or both manual. `--alt`
191
- (even an empty string) makes both manual, keeping the current filename.
192
- It cannot be combined with `--ai-metadata on`. Manual edits are protected from
193
- already-running AI jobs.
194
- - Without metadata flags, raster-image uploads use automatic metadata. Other
195
- file types do not. AI may update both the filename and alt text asynchronously.
196
- - `--ai-metadata on` schedules generation for raster images; success does not mean
197
- generation has completed. Use `files get` to inspect the current record.
198
- - Update requires at least one of `--file`, `--url`, `--filename`, `--alt` or
199
- `--ai-metadata`. `--file` and `--url` are mutually exclusive; without either,
200
- only metadata changes. Renaming alone preserves the file ID and URL and makes
201
- both alt text and filename manual.
202
- - Replacing content preserves the file ID and all `_fileId` references in your
203
- development draft. The URL changes to avoid stale caches. Direct URLs in
204
- stored block/repeatable-item content are migrated within that draft;
205
- URLs hardcoded outside Camox cannot be rewritten.
206
- - Replacements preserve omitted alt text and AI settings. The filename defaults
207
- to the new source filename; `--filename` overrides it. Binary metadata and
208
- explicit metadata changes are saved together. If AI remains enabled, raster
209
- replacements schedule generation on the original file ID; other types skip
210
- generation. Disable AI to prevent future automatic renaming or alt changes.
211
- - Files are shared assets: replacing one affects every reference
212
- to it in your development draft.
213
- - Failed replacements leave the original file unchanged and remove the unused
214
- uploaded binary. After a network timeout, use `files get --id ID` before retrying
215
- because the server may already have completed the operation.
216
- - All commands support `--project PROJECT` (a Project ID or Project Slug) and
217
- `--json`. IDs are scoped to the selected project.
218
- - Uploads return the stored file record, including `id`, `url`, `mimeType`, `alt`,
219
- and `aiMetadata`. Output is JSON; errors are structured JSON on stderr.
220
- - Files are limited to 100 MiB. URL downloads allow at most five redirects and
221
- have a two-minute timeout; uploads have a separate two-minute timeout.
222
- Downloads stream through a temporary file that is removed afterward.
223
- - Only HTTP(S) URLs without embedded credentials are supported. Signed query
224
- URLs work and are not included in download errors. Camox credentials are never
225
- sent to the source URL. Remote authentication flags and batch uploads are not
226
- supported.
227
-
228
- See `camox files --help`, `camox files upload --help`, and
229
- `camox files update --help` for command options.
70
+ `scripts/` refreshes `template-default` on Cloudflare Artifacts, which new
71
+ projects fork. See [scripts/README.md](scripts/README.md).
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { r as verifySession } from "./api-DU0iBFMw.mjs";
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import { t as installLocalhostDns } from "./local-dns-DQIT1ucr.mjs";
3
+ import { createORPCClient } from "@orpc/client";
4
+ import { RPCLink } from "@orpc/client/fetch";
5
+ //#region src/lib/uuid.ts
6
+ /** A UUID's text, in either case, for the patterns that find one. */
7
+ const UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
8
+ //#endregion
9
+ //#region src/lib/api.ts
10
+ const CAMOX_API_URL = process.env.CAMOX_API_URL || "https://api.camox.ai";
11
+ installLocalhostDns(new URL(CAMOX_API_URL).hostname);
12
+ function createRpcClient(token) {
13
+ const link = new RPCLink({
14
+ url: `${CAMOX_API_URL}/rpc`,
15
+ headers: {
16
+ Authorization: `Bearer ${token}`,
17
+ "x-camox-client": "cli"
18
+ }
19
+ });
20
+ return createORPCClient(link);
21
+ }
22
+ async function verifySession(token) {
23
+ return (await fetch(`${CAMOX_API_URL}/api/auth/get-session`, {
24
+ method: "GET",
25
+ headers: {
26
+ Authorization: `Bearer ${token}`,
27
+ "Content-Type": "application/json"
28
+ }
29
+ })).ok;
30
+ }
31
+ const UUID_SHAPE = new RegExp(`^${UUID}$`, "i");
32
+ /** The Project ID of a project named by its Project ID or its Project Slug. */
33
+ async function projectIdOf(token, project) {
34
+ if (UUID_SHAPE.test(project)) return project.toLowerCase();
35
+ const { uuid } = await createRpcClient(token).projects.getBySlug({ slug: project });
36
+ return uuid;
37
+ }
38
+ /**
39
+ * A short-lived read token for a project's repo, with the repo's Git remote: only for a member,
40
+ * on a project whose code can be worked on outside Camox (ADR 0026).
41
+ */
42
+ async function createCodeReadToken(token, project) {
43
+ const projectUuid = await projectIdOf(token, project);
44
+ return createRpcClient(token).projects.createCodeReadToken({ projectUuid });
45
+ }
46
+ /**
47
+ * Pushes a branch's commits, as a Git bundle, to a draft of a project whose code can be worked
48
+ * on outside Camox (ADR 0026): a new one, or the open draft `draftId` names. Resolves to the
49
+ * push's events, where it stands, then the push once its head built; a refusal throws as they're
50
+ * read, with its message, and its build errors for a head that doesn't build.
51
+ */
52
+ function pushCode(token, input) {
53
+ return createRpcClient(token).code.push(input);
54
+ }
55
+ //#endregion
56
+ export { UUID as i, pushCode as n, verifySession as r, createCodeReadToken as t };