@camox/cli 0.44.0 → 0.46.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 +129 -46
- package/dist/api-DNIQC1ea.mjs +104 -0
- package/dist/api-DeE66CPa.mjs +2 -0
- package/dist/{auth-core-4Jdf5q-u.mjs → auth-core-xLeorUUV.mjs} +35 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +633 -764
- package/dist/lib/auth-core.d.mts +12 -8
- package/dist/lib/auth-core.mjs +1 -2
- package/dist/local-dns-DQIT1ucr.mjs +22 -0
- package/package.json +23 -19
- package/dist/api-COfB7AKI.mjs +0 -82
- package/dist/api-LuNKejJE.mjs +0 -3
- package/templates/default/.env.example +0 -2
- package/templates/default/components.json +0 -25
- package/templates/default/package.json +0 -46
- package/templates/default/scripts/README.md +0 -35
- package/templates/default/scripts/production-smoke.mjs +0 -124
- package/templates/default/src/blocks/faq.tsx +0 -63
- package/templates/default/src/blocks/footer.tsx +0 -61
- package/templates/default/src/blocks/hero.tsx +0 -92
- package/templates/default/src/blocks/navbar.tsx +0 -110
- package/templates/default/src/blocks/youtube-video.tsx +0 -158
- package/templates/default/src/components/ui/accordion.tsx +0 -70
- package/templates/default/src/components/ui/button.tsx +0 -58
- package/templates/default/src/document.ts +0 -7
- package/templates/default/src/layouts/default.tsx +0 -88
- package/templates/default/src/lib/utils.ts +0 -6
- package/templates/default/src/styles.css +0 -162
- package/templates/default/tsconfig.json +0 -25
- package/templates/default/vite.config.ts +0 -25
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Runtime lookup and monorepos
|
|
4
4
|
|
|
5
|
-
Runtime-backed commands (`status`, `pages`, `blocks`, `layouts`, `
|
|
6
|
-
`preview`, and `logout`) accept `--cwd PATH`:
|
|
5
|
+
Runtime-backed commands (`status`, `pages`, `blocks`, `layouts`, `locales`, `files`,
|
|
6
|
+
`publish`, `discard`, `preview`, and `logout`) accept `--cwd PATH`:
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
9
|
camox pages list --cwd apps/site
|
|
@@ -19,12 +19,51 @@ build once to generate its runtime file.
|
|
|
19
19
|
Relative `--cwd` paths resolve against the shell's current directory. This option
|
|
20
20
|
only affects runtime lookup: other paths, such as `--file ./hero.webp`, still
|
|
21
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
|
-
your development environment by default, with explicit `--production` where supported.
|
|
22
|
+
instead of falling back to the shell's directory.
|
|
24
23
|
|
|
25
|
-
##
|
|
24
|
+
## Your development draft
|
|
26
25
|
|
|
27
|
-
|
|
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 discard it together.
|
|
34
|
+
|
|
35
|
+
## Publishing and discarding
|
|
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 discard # 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 discard` 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:
|
|
28
67
|
|
|
29
68
|
```sh
|
|
30
69
|
camox preview --url http://localhost:3000/about --json
|
|
@@ -32,46 +71,87 @@ camox preview --cwd apps/site --url http://localhost:3000/about --json
|
|
|
32
71
|
```
|
|
33
72
|
|
|
34
73
|
Open the returned `url` in the browser you use for verification. Wait for
|
|
35
|
-
`[data-camox-preview="ready"]`, which identifies the project and
|
|
36
|
-
|
|
74
|
+
`[data-camox-preview="ready"]`, which identifies the project and your
|
|
75
|
+
development draft after authenticated rendering. Errors appear as
|
|
37
76
|
`[data-camox-preview="error"]`; expired or consumed links require a fresh command.
|
|
38
77
|
The CLI and SDK must both support the preview handoff.
|
|
39
78
|
|
|
40
79
|
The URL transfers your signed-in session: treat it as a credential, do not share
|
|
41
80
|
or commit it, and use it within three minutes. Only HTTP(S) loopback URLs are
|
|
42
|
-
accepted. The destination must match the CLI's project
|
|
43
|
-
|
|
81
|
+
accepted. The destination must match the CLI's project and backend. There is no
|
|
82
|
+
automatic default-browser opening.
|
|
44
83
|
The local dev server must already be running.
|
|
45
84
|
|
|
46
85
|
## Page SEO
|
|
47
86
|
|
|
48
|
-
Use `pages get` to read metadata and `pages
|
|
87
|
+
Use `pages get` to read metadata and `pages set-seo` to edit it:
|
|
49
88
|
|
|
50
89
|
```sh
|
|
51
90
|
camox pages get --path /about --json
|
|
52
|
-
camox pages
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
camox pages
|
|
56
|
-
camox pages
|
|
57
|
-
camox pages
|
|
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
|
|
58
97
|
```
|
|
59
98
|
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
65
106
|
generation; the returned page contains current values, not necessarily generated
|
|
66
107
|
values. Use `pages get` to inspect them later.
|
|
67
|
-
- Changes
|
|
68
|
-
|
|
69
|
-
- Commands support `--project
|
|
70
|
-
|
|
71
|
-
selects the environment; it does not publish the draft.
|
|
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.
|
|
72
112
|
|
|
73
113
|
See `camox pages update --help` for options and examples.
|
|
74
114
|
|
|
115
|
+
## Shared navigation and footer content
|
|
116
|
+
|
|
117
|
+
`pages get` shows page-owned blocks. To find layout-owned blocks:
|
|
118
|
+
|
|
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.
|
|
133
|
+
|
|
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.
|
|
137
|
+
|
|
138
|
+
## Locales
|
|
139
|
+
|
|
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
|
+
```
|
|
147
|
+
|
|
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.
|
|
154
|
+
|
|
75
155
|
## Media
|
|
76
156
|
|
|
77
157
|
Run `camox login` and start your project's dev server (or build once) so the CLI
|
|
@@ -88,24 +168,27 @@ camox files upload --url https://example.com/hero.webp --alt "Product screenshot
|
|
|
88
168
|
camox files upload --url https://example.com/download --filename hero.webp --ai-metadata off
|
|
89
169
|
|
|
90
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.
|
|
91
172
|
camox files list
|
|
92
|
-
|
|
173
|
+
FILE_ID=0199f1c2-7b3e-7a10-9c4d-5e6f7a8b9c0d
|
|
174
|
+
camox files get --id "$FILE_ID"
|
|
93
175
|
|
|
94
|
-
camox files update --id
|
|
95
|
-
camox files update --id
|
|
96
|
-
camox files update --id
|
|
97
|
-
camox files update --id
|
|
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
|
|
98
180
|
|
|
99
181
|
# Replace the underlying file everywhere it is referenced, preserving its ID.
|
|
100
|
-
camox files update --id
|
|
101
|
-
camox files update --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
|
|
102
184
|
|
|
103
185
|
# Replace content and override metadata together.
|
|
104
|
-
camox files update --id
|
|
186
|
+
camox files update --id "$FILE_ID" --file ./new-hero.webp --filename hero.webp --alt "New screenshot"
|
|
105
187
|
```
|
|
106
188
|
|
|
107
189
|
- Upload requires exactly one of `--file` or `--url`. URLs are copied, not linked.
|
|
108
|
-
-
|
|
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.
|
|
109
192
|
It cannot be combined with `--ai-metadata on`. Manual edits are protected from
|
|
110
193
|
already-running AI jobs.
|
|
111
194
|
- Without metadata flags, raster-image uploads use automatic metadata. Other
|
|
@@ -114,26 +197,26 @@ camox files update --id 123 --file ./new-hero.webp --filename hero.webp --alt "N
|
|
|
114
197
|
generation has completed. Use `files get` to inspect the current record.
|
|
115
198
|
- Update requires at least one of `--file`, `--url`, `--filename`, `--alt` or
|
|
116
199
|
`--ai-metadata`. `--file` and `--url` are mutually exclusive; without either,
|
|
117
|
-
only metadata changes. Renaming alone preserves the file ID
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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;
|
|
121
205
|
URLs hardcoded outside Camox cannot be rewritten.
|
|
122
206
|
- Replacements preserve omitted alt text and AI settings. The filename defaults
|
|
123
207
|
to the new source filename; `--filename` overrides it. Binary metadata and
|
|
124
208
|
explicit metadata changes are saved together. If AI remains enabled, raster
|
|
125
209
|
replacements schedule generation on the original file ID; other types skip
|
|
126
210
|
generation. Disable AI to prevent future automatic renaming or alt changes.
|
|
127
|
-
- Files are shared assets
|
|
128
|
-
to it in
|
|
211
|
+
- Files are shared assets: replacing one affects every reference
|
|
212
|
+
to it in your development draft.
|
|
129
213
|
- Failed replacements leave the original file unchanged and remove the unused
|
|
130
214
|
uploaded binary. After a network timeout, use `files get --id ID` before retrying
|
|
131
215
|
because the server may already have completed the operation.
|
|
132
|
-
- All commands support `--project
|
|
133
|
-
|
|
134
|
-
project and environment.
|
|
216
|
+
- All commands support `--project PROJECT` (a Project ID or Project Slug) and
|
|
217
|
+
`--json`. IDs are scoped to the selected project.
|
|
135
218
|
- Uploads return the stored file record, including `id`, `url`, `mimeType`, `alt`,
|
|
136
|
-
and `
|
|
219
|
+
and `aiMetadata`. Output is JSON; errors are structured JSON on stderr.
|
|
137
220
|
- Files are limited to 100 MiB. URL downloads allow at most five redirects and
|
|
138
221
|
have a two-minute timeout; uploads have a separate two-minute timeout.
|
|
139
222
|
Downloads stream through a temporary file that is removed afterward.
|
|
@@ -0,0 +1,104 @@
|
|
|
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 ../api-contract/src/drafts.ts
|
|
6
|
+
/** Request header naming the draft a request reads and writes (ADR 0011). Absent means Live. */
|
|
7
|
+
const DRAFT_HEADER = "x-camox-draft";
|
|
8
|
+
/**
|
|
9
|
+
* The `x-camox-draft` value naming the caller's own development draft in the project a request
|
|
10
|
+
* acts on (ADR 0006, ADR 0011). Dev mode and the CLI always send it.
|
|
11
|
+
*/
|
|
12
|
+
const DEVELOPMENT_DRAFT = "development";
|
|
13
|
+
/**
|
|
14
|
+
* oRPC error code for a request naming the development draft before dev mode created it: the
|
|
15
|
+
* first dev-mode sync after login does, so the site has to run locally once first.
|
|
16
|
+
*/
|
|
17
|
+
const NO_DEVELOPMENT_DRAFT_ERROR = "NO_DEVELOPMENT_DRAFT";
|
|
18
|
+
/**
|
|
19
|
+
* The oRPC error code a request naming an ended draft fails with. Its `data` is an `EndedDraft`,
|
|
20
|
+
* so a page can render Live and say who published or discarded the draft.
|
|
21
|
+
*/
|
|
22
|
+
const DRAFT_ENDED_ERROR_CODE = "DRAFT_ENDED";
|
|
23
|
+
/**
|
|
24
|
+
* Why the publish tool published nothing of a draft with changes, and returned a fresh preview to
|
|
25
|
+
* review again, by the error code of each: Live changed since the preview, the draft has blockers,
|
|
26
|
+
* or translations are still running.
|
|
27
|
+
*/
|
|
28
|
+
const NOT_PUBLISHED_CODES = {
|
|
29
|
+
conflictsChanged: "CONFLICTS_CHANGED",
|
|
30
|
+
blocked: "PUBLISH_BLOCKED",
|
|
31
|
+
translating: "TRANSLATIONS_PENDING"
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/lib/api.ts
|
|
35
|
+
const CAMOX_API_URL = process.env.CAMOX_API_URL || "https://api.camox.dev";
|
|
36
|
+
installLocalhostDns(new URL(CAMOX_API_URL).hostname);
|
|
37
|
+
function authHeaders(token) {
|
|
38
|
+
return {
|
|
39
|
+
Authorization: `Bearer ${token}`,
|
|
40
|
+
"Content-Type": "application/json"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Headers of every request a command sends about a project: the CLI works only in the
|
|
45
|
+
* caller's development draft (ADR 0010).
|
|
46
|
+
*/
|
|
47
|
+
function developmentDraftHeaders(token, disableTelemetry) {
|
|
48
|
+
const headers = {
|
|
49
|
+
Authorization: `Bearer ${token}`,
|
|
50
|
+
"x-camox-client": "cli",
|
|
51
|
+
[DRAFT_HEADER]: DEVELOPMENT_DRAFT
|
|
52
|
+
};
|
|
53
|
+
if (disableTelemetry) headers["x-camox-telemetry-disabled"] = "1";
|
|
54
|
+
return headers;
|
|
55
|
+
}
|
|
56
|
+
function createRpcClient(token, apiUrl = CAMOX_API_URL, options = {}) {
|
|
57
|
+
installLocalhostDns(new URL(apiUrl).hostname);
|
|
58
|
+
const headers = options.developmentDraft ? developmentDraftHeaders(token, options.disableTelemetry) : {
|
|
59
|
+
Authorization: `Bearer ${token}`,
|
|
60
|
+
"x-camox-client": "cli"
|
|
61
|
+
};
|
|
62
|
+
const link = new RPCLink({
|
|
63
|
+
url: `${apiUrl}/rpc`,
|
|
64
|
+
headers
|
|
65
|
+
});
|
|
66
|
+
return createORPCClient(link);
|
|
67
|
+
}
|
|
68
|
+
async function verifySession(token) {
|
|
69
|
+
return (await fetch(`${CAMOX_API_URL}/api/auth/get-session`, {
|
|
70
|
+
method: "GET",
|
|
71
|
+
headers: authHeaders(token)
|
|
72
|
+
})).ok;
|
|
73
|
+
}
|
|
74
|
+
const UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
75
|
+
/**
|
|
76
|
+
* Load a project named by its Project ID or its Project Slug. A UUID-shaped name is a
|
|
77
|
+
* Project ID, which the API validates; anything else is a slug.
|
|
78
|
+
*/
|
|
79
|
+
async function getProject(token, project, apiUrl) {
|
|
80
|
+
const client = createRpcClient(token, apiUrl, { developmentDraft: true });
|
|
81
|
+
return UUID_SHAPE.test(project) ? client.projects.getByUuid({ projectUuid: project }) : client.projects.getBySlug({ slug: project });
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The caller's development draft in a project, summarized: it fails with
|
|
85
|
+
* `NO_DEVELOPMENT_DRAFT` until dev mode created it.
|
|
86
|
+
*/
|
|
87
|
+
async function getDevelopmentDraft(params) {
|
|
88
|
+
return createRpcClient(params.token, params.apiUrl, {
|
|
89
|
+
developmentDraft: true,
|
|
90
|
+
disableTelemetry: params.disableTelemetry
|
|
91
|
+
}).drafts.get({ projectId: params.projectId });
|
|
92
|
+
}
|
|
93
|
+
async function callTool(params) {
|
|
94
|
+
return await createRpcClient(params.token, params.apiUrl, {
|
|
95
|
+
developmentDraft: true,
|
|
96
|
+
disableTelemetry: params.disableTelemetry
|
|
97
|
+
}).agent.callTool({
|
|
98
|
+
projectId: params.projectId,
|
|
99
|
+
name: params.name,
|
|
100
|
+
arguments: params.args
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { verifySession as a, NOT_PUBLISHED_CODES as c, getProject as i, NO_DEVELOPMENT_DRAFT_ERROR as l, developmentDraftHeaders as n, DEVELOPMENT_DRAFT as o, getDevelopmentDraft as r, DRAFT_ENDED_ERROR_CODE as s, callTool as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { t as installLocalhostDns } from "./local-dns-DQIT1ucr.mjs";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
@@ -21,6 +22,38 @@ function localAuthFile(cwd) {
|
|
|
21
22
|
function normalizeUrl(url) {
|
|
22
23
|
return url.replace(/\/+$/, "");
|
|
23
24
|
}
|
|
25
|
+
function isLoopbackUrl(url) {
|
|
26
|
+
return ["http:", "https:"].includes(url.protocol) && ([
|
|
27
|
+
"localhost",
|
|
28
|
+
"127.0.0.1",
|
|
29
|
+
"[::1]"
|
|
30
|
+
].includes(url.hostname) || url.hostname.endsWith(".localhost")) && !url.username && !url.password;
|
|
31
|
+
}
|
|
32
|
+
async function createPreviewSignInUrl(destination, target, authToken, signal = AbortSignal.timeout(15e3)) {
|
|
33
|
+
const url = new URL(destination);
|
|
34
|
+
if (!isLoopbackUrl(url)) throw new Error("Preview requires an http(s) loopback URL without credentials.");
|
|
35
|
+
installLocalhostDns(new URL(target.apiUrl).hostname);
|
|
36
|
+
url.searchParams.set("camox-preview", JSON.stringify({
|
|
37
|
+
...target,
|
|
38
|
+
apiUrl: normalizeUrl(target.apiUrl)
|
|
39
|
+
}));
|
|
40
|
+
const response = await fetch(`${normalizeUrl(target.apiUrl)}/api/auth/cross-domain/studio-authorize`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: {
|
|
43
|
+
Authorization: `Bearer ${authToken}`,
|
|
44
|
+
"Content-Type": "application/json"
|
|
45
|
+
},
|
|
46
|
+
body: JSON.stringify({ callback: url.href }),
|
|
47
|
+
redirect: "error",
|
|
48
|
+
signal
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) throw new Error(`Could not create preview sign-in (${response.status}). Check your session with camox login.`);
|
|
51
|
+
const data = await response.json();
|
|
52
|
+
const token = typeof data.url === "string" ? URL.parse(data.url)?.searchParams.get("ott") : null;
|
|
53
|
+
if (!token) throw new Error("Authentication backend returned no one-time token.");
|
|
54
|
+
url.searchParams.set("ott", token);
|
|
55
|
+
return url.href;
|
|
56
|
+
}
|
|
24
57
|
function readAllTokens(file) {
|
|
25
58
|
if (!fs.existsSync(file)) return {};
|
|
26
59
|
const entries = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
@@ -65,6 +98,7 @@ function removeAuthTokenForUrl(authenticationUrl, cwd = process.cwd()) {
|
|
|
65
98
|
fs.rmSync(file, { force: true });
|
|
66
99
|
}
|
|
67
100
|
async function verifyOneTimeToken(apiUrl, token) {
|
|
101
|
+
installLocalhostDns(new URL(apiUrl).hostname);
|
|
68
102
|
const res = await fetch(`${normalizeUrl(apiUrl)}/api/auth/one-time-token/verify`, {
|
|
69
103
|
method: "POST",
|
|
70
104
|
headers: { "Content-Type": "application/json" },
|
|
@@ -83,4 +117,4 @@ async function verifyOneTimeToken(apiUrl, token) {
|
|
|
83
117
|
};
|
|
84
118
|
}
|
|
85
119
|
//#endregion
|
|
86
|
-
export {
|
|
120
|
+
export { removeAuthTokenForUrl as a, readAuthTokenForUrl as i, isLoopbackUrl as n, verifyOneTimeToken as o, normalizeUrl as r, writeAuthTokenForUrl as s, createPreviewSignInUrl as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
|
|
2
|
-
export {
|
|
2
|
+
export {}
|