@camox/cli 0.43.0 → 0.44.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 +22 -1
- package/dist/auth-core-4Jdf5q-u.mjs +86 -0
- package/dist/index.mjs +129 -30
- package/dist/lib/auth-core.d.mts +3 -3
- package/dist/lib/auth-core.mjs +1 -1
- package/package.json +2 -2
- package/templates/default/vite.config.ts +2 -2
- package/dist/auth-core-cCuk5aZy.mjs +0 -63
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Runtime lookup and monorepos
|
|
4
4
|
|
|
5
5
|
Runtime-backed commands (`status`, `pages`, `blocks`, `layouts`, `env`, `files`,
|
|
6
|
-
and `logout`) accept `--cwd PATH`:
|
|
6
|
+
`preview`, and `logout`) accept `--cwd PATH`:
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
9
|
camox pages list --cwd apps/site
|
|
@@ -22,6 +22,27 @@ resolve against the shell's current directory. Missing or non-directory paths fa
|
|
|
22
22
|
instead of falling back to the shell's directory. Environment selection is unchanged:
|
|
23
23
|
your development environment by default, with explicit `--production` where supported.
|
|
24
24
|
|
|
25
|
+
## Authenticated local draft preview
|
|
26
|
+
|
|
27
|
+
CLI login does not sign in a fresh browser. To verify draft writes without publishing:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
camox preview --url http://localhost:3000/about --json
|
|
31
|
+
camox preview --cwd apps/site --url http://localhost:3000/about --json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Open the returned `url` in the browser you use for verification. Wait for
|
|
35
|
+
`[data-camox-preview="ready"]`, which identifies the project and developer
|
|
36
|
+
environment after authenticated draft rendering. Errors appear as
|
|
37
|
+
`[data-camox-preview="error"]`; expired or consumed links require a fresh command.
|
|
38
|
+
The CLI and SDK must both support the preview handoff.
|
|
39
|
+
|
|
40
|
+
The URL transfers your signed-in session: treat it as a credential, do not share
|
|
41
|
+
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, backend, and developer
|
|
43
|
+
environment. There is no production mode or automatic default-browser opening.
|
|
44
|
+
The local dev server must already be running.
|
|
45
|
+
|
|
25
46
|
## Page SEO
|
|
26
47
|
|
|
27
48
|
Use `pages get` to read metadata and `pages update` to edit it:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
//#region src/lib/auth-core.ts
|
|
6
|
+
function globalAuthFile() {
|
|
7
|
+
return path.join(os.homedir(), ".camox", "auth.json");
|
|
8
|
+
}
|
|
9
|
+
function localAuthFile(cwd) {
|
|
10
|
+
let directory = path.resolve(cwd);
|
|
11
|
+
while (true) {
|
|
12
|
+
if (fs.existsSync(path.join(directory, ".git"))) {
|
|
13
|
+
const file = path.join(directory, ".camox", "auth.json");
|
|
14
|
+
return fs.existsSync(file) ? file : null;
|
|
15
|
+
}
|
|
16
|
+
const parent = path.dirname(directory);
|
|
17
|
+
if (parent === directory) return null;
|
|
18
|
+
directory = parent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function normalizeUrl(url) {
|
|
22
|
+
return url.replace(/\/+$/, "");
|
|
23
|
+
}
|
|
24
|
+
function readAllTokens(file) {
|
|
25
|
+
if (!fs.existsSync(file)) return {};
|
|
26
|
+
const entries = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
27
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries)) throw new Error(`Invalid Camox credentials file: ${file}`);
|
|
28
|
+
return entries;
|
|
29
|
+
}
|
|
30
|
+
function writeAllTokens(file, tokens) {
|
|
31
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
32
|
+
fs.writeFileSync(file, JSON.stringify(tokens, null, 2), { mode: 384 });
|
|
33
|
+
fs.chmodSync(file, 384);
|
|
34
|
+
}
|
|
35
|
+
function credentialFile(authenticationUrl, cwd) {
|
|
36
|
+
const local = localAuthFile(cwd);
|
|
37
|
+
if (local && Object.hasOwn(readAllTokens(local), normalizeUrl(authenticationUrl))) return local;
|
|
38
|
+
return globalAuthFile();
|
|
39
|
+
}
|
|
40
|
+
/** Look up stored credentials for a specific Camox authentication backend. */
|
|
41
|
+
function readAuthTokenForUrl(authenticationUrl, cwd = process.cwd()) {
|
|
42
|
+
const entry = readAllTokens(credentialFile(authenticationUrl, cwd))[normalizeUrl(authenticationUrl)];
|
|
43
|
+
if (entry?.token && entry?.name && typeof entry.email === "string") return entry;
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function writeAuthTokenForUrl(authenticationUrl, token, cwd = process.cwd()) {
|
|
47
|
+
const file = credentialFile(authenticationUrl, cwd);
|
|
48
|
+
const tokens = readAllTokens(file);
|
|
49
|
+
tokens[normalizeUrl(authenticationUrl)] = token;
|
|
50
|
+
writeAllTokens(file, tokens);
|
|
51
|
+
}
|
|
52
|
+
function removeAuthTokenForUrl(authenticationUrl, cwd = process.cwd()) {
|
|
53
|
+
const file = credentialFile(authenticationUrl, cwd);
|
|
54
|
+
const tokens = readAllTokens(file);
|
|
55
|
+
if (file !== globalAuthFile()) {
|
|
56
|
+
tokens[normalizeUrl(authenticationUrl)] = null;
|
|
57
|
+
writeAllTokens(file, tokens);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
delete tokens[normalizeUrl(authenticationUrl)];
|
|
61
|
+
if (Object.keys(tokens).length > 0) {
|
|
62
|
+
writeAllTokens(file, tokens);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
fs.rmSync(file, { force: true });
|
|
66
|
+
}
|
|
67
|
+
async function verifyOneTimeToken(apiUrl, token) {
|
|
68
|
+
const res = await fetch(`${normalizeUrl(apiUrl)}/api/auth/one-time-token/verify`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "Content-Type": "application/json" },
|
|
71
|
+
body: JSON.stringify({ token })
|
|
72
|
+
});
|
|
73
|
+
if (!res.ok) throw new Error(`OTT verification failed: ${res.status}`);
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
const user = data.user;
|
|
76
|
+
if (!user?.name) throw new Error("No user info in verification response");
|
|
77
|
+
const sessionToken = data.session?.token;
|
|
78
|
+
if (!sessionToken) throw new Error("No session token in verification response");
|
|
79
|
+
return {
|
|
80
|
+
name: user.name,
|
|
81
|
+
email: user.email ?? "",
|
|
82
|
+
token: sessionToken
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { writeAuthTokenForUrl as a, verifyOneTimeToken as i, readAuthTokenForUrl as n, removeAuthTokenForUrl as r, normalizeUrl as t };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as getProjectBySlug, i as createProject, n as checkSlugAvailability, o as listOrganizations, r as createOrganization, s as setActiveOrganization, t as callTool } from "./api-COfB7AKI.mjs";
|
|
3
|
-
import { a as writeAuthTokenForUrl, i as verifyOneTimeToken, n as readAuthTokenForUrl, r as removeAuthTokenForUrl } from "./auth-core-
|
|
3
|
+
import { a as writeAuthTokenForUrl, i as verifyOneTimeToken, n as readAuthTokenForUrl, r as removeAuthTokenForUrl, t as normalizeUrl } from "./auth-core-4Jdf5q-u.mjs";
|
|
4
4
|
import { object, or } from "@optique/core/constructs";
|
|
5
5
|
import { message } from "@optique/core/message";
|
|
6
6
|
import { defineProgram } from "@optique/core/program";
|
|
@@ -302,7 +302,7 @@ async function resolveCommandContext(opts) {
|
|
|
302
302
|
throw err;
|
|
303
303
|
}
|
|
304
304
|
const slug = opts.projectFlag?.trim() || process.env.CAMOX_PROJECT?.trim() || runtime.projectSlug;
|
|
305
|
-
const token = readAuthTokenForUrl(runtime.authenticationUrl);
|
|
305
|
+
const token = readAuthTokenForUrl(runtime.authenticationUrl, opts.cwd);
|
|
306
306
|
if (!token) return fail$1({
|
|
307
307
|
code: "NOT_AUTHENTICATED",
|
|
308
308
|
message: `No stored credentials for ${runtime.authenticationUrl}. Run \`camox login\` against this backend first.`
|
|
@@ -343,7 +343,7 @@ const projectFlag$3 = optional(option("--project", string({ metavar: "SLUG" })))
|
|
|
343
343
|
const jsonFlag$3 = option("--json");
|
|
344
344
|
const productionFlag$2 = option("--production");
|
|
345
345
|
const liveFlag$1 = option("--live");
|
|
346
|
-
const parser$
|
|
346
|
+
const parser$11 = command("blocks", or(command("types", object({
|
|
347
347
|
command: constant("blocks.types"),
|
|
348
348
|
cwd: cwdFlag,
|
|
349
349
|
project: projectFlag$3,
|
|
@@ -442,7 +442,7 @@ function collectPositioningArgs(args) {
|
|
|
442
442
|
toolArgs
|
|
443
443
|
};
|
|
444
444
|
}
|
|
445
|
-
async function handler$
|
|
445
|
+
async function handler$11(args) {
|
|
446
446
|
const outputMode = args.json ? "json" : "auto";
|
|
447
447
|
const cwd = args.cwd;
|
|
448
448
|
const projectFlag = args.project;
|
|
@@ -572,11 +572,48 @@ async function handler$9(args) {
|
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
574
|
//#endregion
|
|
575
|
+
//#region src/commands/comments.ts
|
|
576
|
+
const common$1 = {
|
|
577
|
+
cwd: cwdFlag,
|
|
578
|
+
project: optional(option("--project", string({ metavar: "SLUG" }))),
|
|
579
|
+
production: option("--production"),
|
|
580
|
+
json: option("--json")
|
|
581
|
+
};
|
|
582
|
+
const parser$10 = command("comments", or(command("list", object({
|
|
583
|
+
command: constant("comments.list"),
|
|
584
|
+
pageId: option("--page-id", integer({
|
|
585
|
+
min: 1,
|
|
586
|
+
metavar: "ID"
|
|
587
|
+
})),
|
|
588
|
+
...common$1
|
|
589
|
+
}), { description: message`List all comments (including resolved ones) for a page.` }), command("resolve", object({
|
|
590
|
+
command: constant("comments.resolve"),
|
|
591
|
+
pageId: option("--page-id", integer({
|
|
592
|
+
min: 1,
|
|
593
|
+
metavar: "ID"
|
|
594
|
+
})),
|
|
595
|
+
id: option("--id", string({ metavar: "UUID" })),
|
|
596
|
+
...common$1
|
|
597
|
+
}), { description: message`Resolve a comment by its UUID and page ID. Use comments list to find both.` })), { description: message`Read and resolve page comments in the selected environment. Defaults to development; use --production to target production.` });
|
|
598
|
+
async function handler$10(args) {
|
|
599
|
+
return dispatch({
|
|
600
|
+
toolName: args.command === "comments.list" ? "listComments" : "resolveComment",
|
|
601
|
+
args: {
|
|
602
|
+
pageId: args.pageId,
|
|
603
|
+
...args.command === "comments.resolve" ? { id: args.id } : {}
|
|
604
|
+
},
|
|
605
|
+
cwd: args.cwd,
|
|
606
|
+
projectFlag: args.project,
|
|
607
|
+
production: args.production,
|
|
608
|
+
outputMode: args.json ? "json" : "auto"
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
//#endregion
|
|
575
612
|
//#region src/commands/env.ts
|
|
576
613
|
const projectFlag$2 = optional(option("--project", string({ metavar: "SLUG" })));
|
|
577
614
|
const jsonFlag$2 = option("--json");
|
|
578
615
|
const yesFlag = option("--yes", "-y");
|
|
579
|
-
const parser$
|
|
616
|
+
const parser$9 = command("env", or(command("check", object({
|
|
580
617
|
command: constant("env.check"),
|
|
581
618
|
cwd: cwdFlag,
|
|
582
619
|
project: projectFlag$2,
|
|
@@ -594,7 +631,7 @@ const parser$8 = command("env", or(command("check", object({
|
|
|
594
631
|
project: projectFlag$2,
|
|
595
632
|
json: jsonFlag$2
|
|
596
633
|
}))));
|
|
597
|
-
async function handler$
|
|
634
|
+
async function handler$9(args) {
|
|
598
635
|
const outputMode = args.json ? "json" : "auto";
|
|
599
636
|
const cwd = args.cwd;
|
|
600
637
|
const projectFlag = args.project;
|
|
@@ -795,7 +832,7 @@ const metadata = {
|
|
|
795
832
|
alt: optional(option("--alt", string({ metavar: "TEXT" }), { description: message`Alt text (including an empty string). Disables automatic metadata.` })),
|
|
796
833
|
aiMetadata: optional(option("--ai-metadata", choice(["on", "off"]), { description: message`Enable or disable automatic metadata. Cannot enable alongside --alt. Enabling schedules generation for raster images.` }))
|
|
797
834
|
};
|
|
798
|
-
const parser$
|
|
835
|
+
const parser$8 = command("files", or(command("upload", object({
|
|
799
836
|
command: constant("files.upload"),
|
|
800
837
|
file: optional(option("--file", string({ metavar: "PATH" }), { description: message`Local file to upload. Pass exactly one of --file or --url.` })),
|
|
801
838
|
url: optional(option("--url", string({ metavar: "URL" }), { description: message`HTTP(S) URL to download and copy into Camox, not an external reference.` })),
|
|
@@ -824,7 +861,7 @@ const parser$7 = command("files", or(command("upload", object({
|
|
|
824
861
|
command: constant("files.list"),
|
|
825
862
|
...common
|
|
826
863
|
}), { description: message`List files in the selected project and environment.` })), { description: message`Upload and manage media. Defaults to your development environment; --production explicitly targets production.` });
|
|
827
|
-
async function handler$
|
|
864
|
+
async function handler$8(args) {
|
|
828
865
|
const outputMode = args.json ? "json" : "auto";
|
|
829
866
|
const options = {
|
|
830
867
|
cwd: args.cwd,
|
|
@@ -959,8 +996,8 @@ function copyDir(src, dest, replacements) {
|
|
|
959
996
|
}
|
|
960
997
|
//#endregion
|
|
961
998
|
//#region src/commands/init.ts
|
|
962
|
-
const parser$
|
|
963
|
-
const handler$
|
|
999
|
+
const parser$7 = command("init", object({ command: constant("init") }));
|
|
1000
|
+
const handler$7 = init;
|
|
964
1001
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
965
1002
|
const ownPkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
966
1003
|
const PNPM_WORKSPACE = `allowBuilds:
|
|
@@ -1232,14 +1269,14 @@ src/routeTree.gen.ts
|
|
|
1232
1269
|
const projectFlag$1 = optional(option("--project", string({ metavar: "SLUG" })));
|
|
1233
1270
|
const jsonFlag$1 = option("--json");
|
|
1234
1271
|
const productionFlag$1 = option("--production");
|
|
1235
|
-
const parser$
|
|
1272
|
+
const parser$6 = command("layouts", or(command("list", object({
|
|
1236
1273
|
command: constant("layouts.list"),
|
|
1237
1274
|
cwd: cwdFlag,
|
|
1238
1275
|
project: projectFlag$1,
|
|
1239
1276
|
production: productionFlag$1,
|
|
1240
1277
|
json: jsonFlag$1
|
|
1241
1278
|
}))));
|
|
1242
|
-
async function handler$
|
|
1279
|
+
async function handler$6(args) {
|
|
1243
1280
|
const outputMode = args.json ? "json" : "auto";
|
|
1244
1281
|
const cwd = args.cwd;
|
|
1245
1282
|
const projectFlag = args.project;
|
|
@@ -1257,8 +1294,8 @@ async function handler$5(args) {
|
|
|
1257
1294
|
}
|
|
1258
1295
|
//#endregion
|
|
1259
1296
|
//#region src/commands/login.ts
|
|
1260
|
-
const parser$
|
|
1261
|
-
const handler$
|
|
1297
|
+
const parser$5 = command("login", object({ command: constant("login") }));
|
|
1298
|
+
const handler$5 = login;
|
|
1262
1299
|
async function login() {
|
|
1263
1300
|
p.intro("camox login");
|
|
1264
1301
|
try {
|
|
@@ -1272,11 +1309,11 @@ async function login() {
|
|
|
1272
1309
|
}
|
|
1273
1310
|
//#endregion
|
|
1274
1311
|
//#region src/commands/logout.ts
|
|
1275
|
-
const parser$
|
|
1312
|
+
const parser$4 = command("logout", object({
|
|
1276
1313
|
command: constant("logout"),
|
|
1277
1314
|
cwd: cwdFlag
|
|
1278
1315
|
}));
|
|
1279
|
-
const handler$
|
|
1316
|
+
const handler$4 = logout;
|
|
1280
1317
|
function logout(args = {}) {
|
|
1281
1318
|
p.intro("camox logout");
|
|
1282
1319
|
let authenticationUrl = null;
|
|
@@ -1289,12 +1326,12 @@ function logout(args = {}) {
|
|
|
1289
1326
|
}
|
|
1290
1327
|
if (!(error instanceof RuntimeNotFoundError)) throw error;
|
|
1291
1328
|
}
|
|
1292
|
-
const token = authenticationUrl ? readAuthTokenForUrl(authenticationUrl) : readAuthToken();
|
|
1329
|
+
const token = authenticationUrl ? readAuthTokenForUrl(authenticationUrl, args.cwd) : readAuthToken();
|
|
1293
1330
|
if (!token) {
|
|
1294
1331
|
log.error("Not logged in.");
|
|
1295
1332
|
return;
|
|
1296
1333
|
}
|
|
1297
|
-
if (authenticationUrl) removeAuthTokenForUrl(authenticationUrl);
|
|
1334
|
+
if (authenticationUrl) removeAuthTokenForUrl(authenticationUrl, args.cwd);
|
|
1298
1335
|
else removeAuthToken();
|
|
1299
1336
|
p.log.success(`Logged out from ${token.name}.`);
|
|
1300
1337
|
}
|
|
@@ -1349,7 +1386,7 @@ const discardChanges = command("discard-changes", object({
|
|
|
1349
1386
|
production: productionFlag,
|
|
1350
1387
|
json: jsonFlag
|
|
1351
1388
|
}));
|
|
1352
|
-
const parser$
|
|
1389
|
+
const parser$3 = command("pages", or(list, get, command("create", object({
|
|
1353
1390
|
command: constant("pages.create"),
|
|
1354
1391
|
nickname: optional(option("--nickname", string({ metavar: "TEXT" }))),
|
|
1355
1392
|
pathSegment: option("--path-segment", string({ metavar: "SEGMENT" })),
|
|
@@ -1392,7 +1429,7 @@ const parser$2 = command("pages", or(list, get, command("create", object({
|
|
|
1392
1429
|
production: productionFlag,
|
|
1393
1430
|
json: jsonFlag
|
|
1394
1431
|
})), publish, unpublish, discardChanges));
|
|
1395
|
-
async function handler$
|
|
1432
|
+
async function handler$3(args) {
|
|
1396
1433
|
const outputMode = args.json ? "json" : "auto";
|
|
1397
1434
|
const cwd = args.cwd;
|
|
1398
1435
|
const projectFlag = args.project;
|
|
@@ -1556,6 +1593,61 @@ async function handler$2(args) {
|
|
|
1556
1593
|
}
|
|
1557
1594
|
}
|
|
1558
1595
|
//#endregion
|
|
1596
|
+
//#region src/commands/preview.ts
|
|
1597
|
+
const parser$2 = command("preview", object({
|
|
1598
|
+
command: constant("preview"),
|
|
1599
|
+
cwd: cwdFlag,
|
|
1600
|
+
url: option("--url", string()),
|
|
1601
|
+
json: option("--json")
|
|
1602
|
+
}), { description: message`Create a single-use sign-in URL for local draft verification. Open it in your automation browser within three minutes. The URL is a credential; do not share it.` });
|
|
1603
|
+
async function handler$2(args) {
|
|
1604
|
+
try {
|
|
1605
|
+
const url = new URL(args.url);
|
|
1606
|
+
if (!["http:", "https:"].includes(url.protocol) || ![
|
|
1607
|
+
"localhost",
|
|
1608
|
+
"127.0.0.1",
|
|
1609
|
+
"[::1]"
|
|
1610
|
+
].includes(url.hostname) || url.username || url.password) throw new Error("Preview requires an http(s) loopback URL without credentials.");
|
|
1611
|
+
const runtime = loadRuntime(args.cwd);
|
|
1612
|
+
const auth = readAuthTokenForUrl(runtime.authenticationUrl, args.cwd);
|
|
1613
|
+
if (!auth?.email) {
|
|
1614
|
+
printError({
|
|
1615
|
+
code: "NOT_AUTHENTICATED",
|
|
1616
|
+
message: `Run camox login for ${runtime.authenticationUrl} first.`
|
|
1617
|
+
});
|
|
1618
|
+
process.exitCode = 2;
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const response = await fetch(`${normalizeUrl(runtime.apiUrl)}/api/auth/one-time-token/generate`, {
|
|
1622
|
+
headers: { Authorization: `Bearer ${auth.token}` },
|
|
1623
|
+
redirect: "error",
|
|
1624
|
+
signal: AbortSignal.timeout(15e3)
|
|
1625
|
+
});
|
|
1626
|
+
if (!response.ok) throw new Error(`Could not create preview sign-in (${response.status}). Check your session with camox login.`);
|
|
1627
|
+
const data = await response.json();
|
|
1628
|
+
if (typeof data.token !== "string" || !data.token) throw new Error("Authentication backend returned no one-time token.");
|
|
1629
|
+
const target = {
|
|
1630
|
+
projectSlug: runtime.projectSlug,
|
|
1631
|
+
environmentName: `dev:${auth.email}`,
|
|
1632
|
+
apiUrl: normalizeUrl(runtime.apiUrl)
|
|
1633
|
+
};
|
|
1634
|
+
url.searchParams.set("camox-preview", JSON.stringify(target));
|
|
1635
|
+
url.searchParams.set("ott", data.token);
|
|
1636
|
+
printResult({
|
|
1637
|
+
url: url.href,
|
|
1638
|
+
...target,
|
|
1639
|
+
source: "draft",
|
|
1640
|
+
warning: "Single-use sign-in credential, valid for three minutes. Open in your verification browser; do not share or commit."
|
|
1641
|
+
}, args.json ? "json" : "auto");
|
|
1642
|
+
} catch (error) {
|
|
1643
|
+
printError({
|
|
1644
|
+
code: "PREVIEW_FAILED",
|
|
1645
|
+
message: error instanceof Error ? error.message : "Could not create preview."
|
|
1646
|
+
});
|
|
1647
|
+
process.exitCode = 2;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
//#endregion
|
|
1559
1651
|
//#region src/commands/release.ts
|
|
1560
1652
|
const parser$1 = command("release", object({ command: constant("release") }));
|
|
1561
1653
|
function handler$1() {
|
|
@@ -1612,7 +1704,7 @@ async function handler(args) {
|
|
|
1612
1704
|
}
|
|
1613
1705
|
throw err;
|
|
1614
1706
|
}
|
|
1615
|
-
const token = readAuthTokenForUrl(runtime.authenticationUrl);
|
|
1707
|
+
const token = readAuthTokenForUrl(runtime.authenticationUrl, args.cwd);
|
|
1616
1708
|
const environmentName = args.production ? "production" : token?.email ? `dev:${token.email}` : null;
|
|
1617
1709
|
const status = {
|
|
1618
1710
|
projectSlug: runtime.projectSlug,
|
|
@@ -1642,7 +1734,7 @@ async function handler(args) {
|
|
|
1642
1734
|
//#endregion
|
|
1643
1735
|
//#region src/index.ts
|
|
1644
1736
|
const result = runSync(defineProgram({
|
|
1645
|
-
parser: or(parser$
|
|
1737
|
+
parser: or(parser$7, parser$5, parser$4, parser$1, parser, parser$2, parser$3, parser$11, parser$10, parser$6, parser$9, parser$8),
|
|
1646
1738
|
metadata: {
|
|
1647
1739
|
name: "camox",
|
|
1648
1740
|
brief: message`Camox CLI`
|
|
@@ -1650,17 +1742,20 @@ const result = runSync(defineProgram({
|
|
|
1650
1742
|
}), { help: "both" });
|
|
1651
1743
|
switch (result.command) {
|
|
1652
1744
|
case "init":
|
|
1653
|
-
await handler$
|
|
1745
|
+
await handler$7();
|
|
1654
1746
|
break;
|
|
1655
1747
|
case "login":
|
|
1656
|
-
await handler$
|
|
1748
|
+
await handler$5();
|
|
1657
1749
|
break;
|
|
1658
1750
|
case "logout":
|
|
1659
|
-
handler$
|
|
1751
|
+
handler$4(result);
|
|
1660
1752
|
break;
|
|
1661
1753
|
case "release":
|
|
1662
1754
|
handler$1();
|
|
1663
1755
|
break;
|
|
1756
|
+
case "preview":
|
|
1757
|
+
await handler$2(result);
|
|
1758
|
+
break;
|
|
1664
1759
|
case "status":
|
|
1665
1760
|
await handler(result);
|
|
1666
1761
|
break;
|
|
@@ -1673,7 +1768,7 @@ switch (result.command) {
|
|
|
1673
1768
|
case "pages.publish":
|
|
1674
1769
|
case "pages.unpublish":
|
|
1675
1770
|
case "pages.discard-changes":
|
|
1676
|
-
await handler$
|
|
1771
|
+
await handler$3(result);
|
|
1677
1772
|
break;
|
|
1678
1773
|
case "blocks.types":
|
|
1679
1774
|
case "blocks.describe":
|
|
@@ -1683,21 +1778,25 @@ switch (result.command) {
|
|
|
1683
1778
|
case "blocks.edit":
|
|
1684
1779
|
case "blocks.move":
|
|
1685
1780
|
case "blocks.delete":
|
|
1686
|
-
await handler$
|
|
1781
|
+
await handler$11(result);
|
|
1782
|
+
break;
|
|
1783
|
+
case "comments.list":
|
|
1784
|
+
case "comments.resolve":
|
|
1785
|
+
await handler$10(result);
|
|
1687
1786
|
break;
|
|
1688
1787
|
case "files.upload":
|
|
1689
1788
|
case "files.update":
|
|
1690
1789
|
case "files.get":
|
|
1691
1790
|
case "files.list":
|
|
1692
|
-
await handler$
|
|
1791
|
+
await handler$8(result);
|
|
1693
1792
|
break;
|
|
1694
1793
|
case "layouts.list":
|
|
1695
|
-
await handler$
|
|
1794
|
+
await handler$6(result);
|
|
1696
1795
|
break;
|
|
1697
1796
|
case "env.check":
|
|
1698
1797
|
case "env.push":
|
|
1699
1798
|
case "env.pull":
|
|
1700
|
-
await handler$
|
|
1799
|
+
await handler$9(result);
|
|
1701
1800
|
break;
|
|
1702
1801
|
}
|
|
1703
1802
|
//#endregion
|
package/dist/lib/auth-core.d.mts
CHANGED
|
@@ -7,9 +7,9 @@ interface AuthToken {
|
|
|
7
7
|
}
|
|
8
8
|
declare function normalizeUrl(url: string): string;
|
|
9
9
|
/** Look up stored credentials for a specific Camox authentication backend. */
|
|
10
|
-
declare function readAuthTokenForUrl(authenticationUrl: string): AuthToken | null;
|
|
11
|
-
declare function writeAuthTokenForUrl(authenticationUrl: string, token: AuthToken): void;
|
|
12
|
-
declare function removeAuthTokenForUrl(authenticationUrl: string): void;
|
|
10
|
+
declare function readAuthTokenForUrl(authenticationUrl: string, cwd?: string): AuthToken | null;
|
|
11
|
+
declare function writeAuthTokenForUrl(authenticationUrl: string, token: AuthToken, cwd?: string): void;
|
|
12
|
+
declare function removeAuthTokenForUrl(authenticationUrl: string, cwd?: string): void;
|
|
13
13
|
declare function verifyOneTimeToken(apiUrl: string, token: string): Promise<AuthToken>;
|
|
14
14
|
//#endregion
|
|
15
15
|
export { AuthToken, normalizeUrl, readAuthTokenForUrl, removeAuthTokenForUrl, verifyOneTimeToken, writeAuthTokenForUrl };
|
package/dist/lib/auth-core.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as writeAuthTokenForUrl, i as verifyOneTimeToken, n as readAuthTokenForUrl, r as removeAuthTokenForUrl, t as normalizeUrl } from "../auth-core-
|
|
2
|
+
import { a as writeAuthTokenForUrl, i as verifyOneTimeToken, n as readAuthTokenForUrl, r as removeAuthTokenForUrl, t as normalizeUrl } from "../auth-core-4Jdf5q-u.mjs";
|
|
3
3
|
export { normalizeUrl, readAuthTokenForUrl, removeAuthTokenForUrl, verifyOneTimeToken, writeAuthTokenForUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camox/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"camox": "./dist/index.mjs"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@types/node": "^24.12.4",
|
|
32
32
|
"@typescript/native-preview": "7.0.0-dev.20260412.1",
|
|
33
33
|
"vite-plus": "^0.1.21",
|
|
34
|
-
"@camox/api-contract": "0.
|
|
34
|
+
"@camox/api-contract": "0.44.0"
|
|
35
35
|
},
|
|
36
36
|
"nx": {
|
|
37
37
|
"tags": [
|
|
@@ -14,8 +14,8 @@ export default defineConfig({
|
|
|
14
14
|
projectSlug: "camox-template-default-01", // camox-cli:replace-slug
|
|
15
15
|
// camox-cli:dev-only-start
|
|
16
16
|
_internal: {
|
|
17
|
-
authenticationUrl: "http://localhost:3274",
|
|
18
|
-
apiUrl: "http://localhost:8787",
|
|
17
|
+
authenticationUrl: process.env.VITE_DASHBOARD_URL ?? "http://localhost:3274",
|
|
18
|
+
apiUrl: process.env.VITE_API_URL ?? "http://localhost:8787",
|
|
19
19
|
},
|
|
20
20
|
// camox-cli:dev-only-end
|
|
21
21
|
}),
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
//#region src/lib/auth-core.ts
|
|
6
|
-
const AUTH_DIR = path.join(os.homedir(), ".camox");
|
|
7
|
-
const AUTH_FILE = path.join(AUTH_DIR, "auth.json");
|
|
8
|
-
function normalizeUrl(url) {
|
|
9
|
-
return url.replace(/\/+$/, "");
|
|
10
|
-
}
|
|
11
|
-
function readAllTokens() {
|
|
12
|
-
try {
|
|
13
|
-
return JSON.parse(fs.readFileSync(AUTH_FILE, "utf-8"));
|
|
14
|
-
} catch {
|
|
15
|
-
return {};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function writeAllTokens(tokens) {
|
|
19
|
-
fs.mkdirSync(AUTH_DIR, { recursive: true });
|
|
20
|
-
fs.writeFileSync(AUTH_FILE, JSON.stringify(tokens, null, 2), { mode: 384 });
|
|
21
|
-
}
|
|
22
|
-
/** Look up stored credentials for a specific Camox authentication backend. */
|
|
23
|
-
function readAuthTokenForUrl(authenticationUrl) {
|
|
24
|
-
const entry = readAllTokens()[normalizeUrl(authenticationUrl)];
|
|
25
|
-
if (entry?.token && entry?.name && typeof entry.email === "string") return entry;
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
function writeAuthTokenForUrl(authenticationUrl, token) {
|
|
29
|
-
const tokens = readAllTokens();
|
|
30
|
-
tokens[normalizeUrl(authenticationUrl)] = token;
|
|
31
|
-
writeAllTokens(tokens);
|
|
32
|
-
}
|
|
33
|
-
function removeAuthTokenForUrl(authenticationUrl) {
|
|
34
|
-
const tokens = readAllTokens();
|
|
35
|
-
delete tokens[normalizeUrl(authenticationUrl)];
|
|
36
|
-
if (Object.keys(tokens).length > 0) {
|
|
37
|
-
writeAllTokens(tokens);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
fs.unlinkSync(AUTH_FILE);
|
|
42
|
-
} catch {}
|
|
43
|
-
}
|
|
44
|
-
async function verifyOneTimeToken(apiUrl, token) {
|
|
45
|
-
const res = await fetch(`${normalizeUrl(apiUrl)}/api/auth/one-time-token/verify`, {
|
|
46
|
-
method: "POST",
|
|
47
|
-
headers: { "Content-Type": "application/json" },
|
|
48
|
-
body: JSON.stringify({ token })
|
|
49
|
-
});
|
|
50
|
-
if (!res.ok) throw new Error(`OTT verification failed: ${res.status}`);
|
|
51
|
-
const data = await res.json();
|
|
52
|
-
const user = data.user;
|
|
53
|
-
if (!user?.name) throw new Error("No user info in verification response");
|
|
54
|
-
const sessionToken = data.session?.token;
|
|
55
|
-
if (!sessionToken) throw new Error("No session token in verification response");
|
|
56
|
-
return {
|
|
57
|
-
name: user.name,
|
|
58
|
-
email: user.email ?? "",
|
|
59
|
-
token: sessionToken
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
//#endregion
|
|
63
|
-
export { writeAuthTokenForUrl as a, verifyOneTimeToken as i, readAuthTokenForUrl as n, removeAuthTokenForUrl as r, normalizeUrl as t };
|