@camox/cli 0.58.0 → 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 +52 -210
- package/dist/api-DOHUrl0V.mjs +2 -0
- package/dist/api-DU0iBFMw.mjs +56 -0
- package/dist/index.mjs +273 -1374
- package/dist/lib/auth-core.mjs +1 -1
- package/package.json +2 -2
- package/dist/api-CBYfBQrH.mjs +0 -105
- package/dist/api-Ph2yMPXw.mjs +0 -2
- package/dist/{auth-core-xLeorUUV.mjs → auth-core-BpVsNVnR.mjs} +1 -1
package/README.md
CHANGED
|
@@ -1,229 +1,71 @@
|
|
|
1
1
|
# Camox CLI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
70
|
-
camox
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
## Releasing
|
|
88
27
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
33
|
+
## Cloning a project's code
|
|
118
34
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
camox
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
##
|
|
47
|
+
## Pushing to a draft
|
|
139
48
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
+
`@camox/cli/auth` exports the token storage and sign-in helpers the SDK's Vite
|
|
66
|
+
plugin imports.
|
|
156
67
|
|
|
157
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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,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 };
|