@camox/cli 0.36.3 → 0.38.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.
@@ -0,0 +1,63 @@
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 };
@@ -0,0 +1,2 @@
1
+
2
+ export { };
package/dist/index.mjs CHANGED
@@ -1,5 +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-cCuk5aZy.mjs";
3
4
  import { object, or } from "@optique/core/constructs";
4
5
  import { message } from "@optique/core/message";
5
6
  import { defineProgram } from "@optique/core/program";
@@ -7,13 +8,12 @@ import { runSync } from "@optique/run";
7
8
  import { multiple, optional } from "@optique/core/modifiers";
8
9
  import { command, constant, option } from "@optique/core/primitives";
9
10
  import { choice, integer, string } from "@optique/core/valueparser";
10
- import { execSync, spawn } from "node:child_process";
11
- import fs from "node:fs";
11
+ import { execSync, spawn, spawnSync } from "node:child_process";
12
12
  import http from "node:http";
13
- import os from "node:os";
14
- import path from "node:path";
15
13
  import * as p from "@clack/prompts";
16
14
  import { log } from "@clack/prompts";
15
+ import fs from "node:fs";
16
+ import path from "node:path";
17
17
  import { ZodError, z } from "zod";
18
18
  import { fileURLToPath } from "node:url";
19
19
  import slugify from "slugify";
@@ -21,47 +21,14 @@ import slugify from "slugify";
21
21
  const CAMOX_URL = process.env.CAMOX_URL || "https://app.camox.dev";
22
22
  const CAMOX_API_URL = process.env.CAMOX_API_URL || "https://api.camox.dev";
23
23
  const AUTH_TIMEOUT_MS = 300 * 1e3;
24
- const AUTH_DIR = path.join(os.homedir(), ".camox");
25
- const AUTH_FILE = path.join(AUTH_DIR, "auth.json");
26
- function normalizeUrl(url) {
27
- return url.replace(/\/+$/, "");
28
- }
29
- function readAllTokens() {
30
- try {
31
- return JSON.parse(fs.readFileSync(AUTH_FILE, "utf-8"));
32
- } catch {
33
- return {};
34
- }
35
- }
36
- function writeAllTokens(tokens) {
37
- fs.mkdirSync(AUTH_DIR, { recursive: true });
38
- fs.writeFileSync(AUTH_FILE, JSON.stringify(tokens, null, 2), { mode: 384 });
39
- }
40
24
  function readAuthToken() {
41
25
  return readAuthTokenForUrl(CAMOX_URL);
42
26
  }
43
- /**
44
- * Look up a stored token by an explicit auth URL. Used by the tool dispatch
45
- * path, which sources the URL from the vite plugin sidecar rather than env
46
- * vars so the right credential is selected per project.
47
- */
48
- function readAuthTokenForUrl(authenticationUrl) {
49
- const entry = readAllTokens()[normalizeUrl(authenticationUrl)];
50
- if (entry?.token && entry?.name) return entry;
51
- return null;
52
- }
53
27
  function writeAuthToken(token) {
54
- const tokens = readAllTokens();
55
- tokens[normalizeUrl(CAMOX_URL)] = token;
56
- writeAllTokens(tokens);
28
+ writeAuthTokenForUrl(CAMOX_URL, token);
57
29
  }
58
30
  function removeAuthToken() {
59
- const tokens = readAllTokens();
60
- delete tokens[normalizeUrl(CAMOX_URL)];
61
- if (Object.keys(tokens).length === 0) try {
62
- fs.unlinkSync(AUTH_FILE);
63
- } catch {}
64
- else writeAllTokens(tokens);
31
+ removeAuthTokenForUrl(CAMOX_URL);
65
32
  }
66
33
  function openBrowser(url) {
67
34
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -107,24 +74,6 @@ function startCallbackServer() {
107
74
  server.on("error", reject);
108
75
  });
109
76
  }
110
- async function verifyOtt(token) {
111
- const res = await fetch(`${CAMOX_API_URL}/api/auth/one-time-token/verify`, {
112
- method: "POST",
113
- headers: { "Content-Type": "application/json" },
114
- body: JSON.stringify({ token })
115
- });
116
- if (!res.ok) throw new Error(`OTT verification failed: ${res.status}`);
117
- const data = await res.json();
118
- const user = data.user;
119
- if (!user?.name) throw new Error("No user info in verification response");
120
- const sessionToken = data.session?.token;
121
- if (!sessionToken) throw new Error("No session token in verification response");
122
- return {
123
- name: user.name,
124
- email: user.email ?? "",
125
- sessionToken
126
- };
127
- }
128
77
  async function authenticateUser() {
129
78
  const { port, ottPromise, close } = await startCallbackServer();
130
79
  const action = await p.select({
@@ -151,14 +100,9 @@ async function authenticateUser() {
151
100
  try {
152
101
  const ott = await Promise.race([ottPromise, new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("Authentication timed out")), AUTH_TIMEOUT_MS))]);
153
102
  s.message("Verifying...");
154
- const result = await verifyOtt(ott);
155
- const authToken = {
156
- token: result.sessionToken,
157
- name: result.name,
158
- email: result.email
159
- };
103
+ const authToken = await verifyOneTimeToken(CAMOX_API_URL, ott);
160
104
  writeAuthToken(authToken);
161
- s.stop(`Authenticated as ${result.name}`);
105
+ s.stop(`Authenticated as ${authToken.name}`);
162
106
  return authToken;
163
107
  } catch (err) {
164
108
  s.stop("Authentication failed.");
@@ -373,7 +317,7 @@ const projectFlag$3 = optional(option("--project", string({ metavar: "SLUG" })))
373
317
  const jsonFlag$3 = option("--json");
374
318
  const productionFlag$2 = option("--production");
375
319
  const liveFlag$1 = option("--live");
376
- const parser$7 = command("blocks", or(command("types", object({
320
+ const parser$8 = command("blocks", or(command("types", object({
377
321
  command: constant("blocks.types"),
378
322
  project: projectFlag$3,
379
323
  production: productionFlag$2,
@@ -464,7 +408,7 @@ function collectPositioningArgs(args) {
464
408
  toolArgs
465
409
  };
466
410
  }
467
- async function handler$7(args) {
411
+ async function handler$8(args) {
468
412
  const outputMode = args.json ? "json" : "auto";
469
413
  const projectFlag = args.project;
470
414
  const production = args.production;
@@ -589,7 +533,7 @@ async function handler$7(args) {
589
533
  const projectFlag$2 = optional(option("--project", string({ metavar: "SLUG" })));
590
534
  const jsonFlag$2 = option("--json");
591
535
  const yesFlag = option("--yes", "-y");
592
- const parser$6 = command("env", or(command("check", object({
536
+ const parser$7 = command("env", or(command("check", object({
593
537
  command: constant("env.check"),
594
538
  project: projectFlag$2,
595
539
  json: jsonFlag$2
@@ -604,7 +548,7 @@ const parser$6 = command("env", or(command("check", object({
604
548
  project: projectFlag$2,
605
549
  json: jsonFlag$2
606
550
  }))));
607
- async function handler$6(args) {
551
+ async function handler$7(args) {
608
552
  const outputMode = args.json ? "json" : "auto";
609
553
  const projectFlag = args.project;
610
554
  switch (args.command) {
@@ -634,6 +578,16 @@ async function handler$6(args) {
634
578
  }
635
579
  }
636
580
  //#endregion
581
+ //#region src/lib/agent-skills.ts
582
+ function createAgentSkillLinks(targetDir) {
583
+ const agentsSkillsDir = path.join(targetDir, ".agents", "skills");
584
+ const claudeSkillsDir = path.join(targetDir, ".claude", "skills");
585
+ fs.mkdirSync(agentsSkillsDir, { recursive: true });
586
+ fs.mkdirSync(claudeSkillsDir, { recursive: true });
587
+ fs.symlinkSync("../../node_modules/camox/skills/camox", path.join(agentsSkillsDir, "camox"));
588
+ fs.symlinkSync("../../.agents/skills/camox", path.join(claudeSkillsDir, "camox"));
589
+ }
590
+ //#endregion
637
591
  //#region src/lib/utils.ts
638
592
  const packageManagerVersions = {
639
593
  pnpm: "pnpm@11.1.3",
@@ -702,8 +656,8 @@ function copyDir(src, dest, replacements) {
702
656
  }
703
657
  //#endregion
704
658
  //#region src/commands/init.ts
705
- const parser$5 = command("init", object({ command: constant("init") }));
706
- const handler$5 = init;
659
+ const parser$6 = command("init", object({ command: constant("init") }));
660
+ const handler$6 = init;
707
661
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
708
662
  const ownPkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "..", "package.json"), "utf-8"));
709
663
  const PNPM_WORKSPACE = `allowBuilds:
@@ -900,6 +854,7 @@ async function init() {
900
854
  const s = p.spinner();
901
855
  s.start("Scaffolding project...");
902
856
  copyDir(path.resolve(__dirname, "..", "templates", "default"), targetDir, { "{{projectName}}": name });
857
+ createAgentSkillLinks(targetDir);
903
858
  const viteConfigPath = path.join(targetDir, "vite.config.ts");
904
859
  let viteConfig = fs.readFileSync(viteConfigPath, "utf-8");
905
860
  viteConfig = viteConfig.replace(/"[^"]*"(,?)[ \t]*\/\/[ \t]*camox-cli:replace-slug.*$/gm, `"${project.slug}"$1`);
@@ -913,7 +868,6 @@ async function init() {
913
868
  pkg.packageManager = packageManagerVersions[pm];
914
869
  fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
915
870
  if (pm === "pnpm") fs.writeFileSync(path.join(targetDir, "pnpm-workspace.yaml"), PNPM_WORKSPACE);
916
- fs.writeFileSync(path.join(targetDir, ".env"), `CAMOX_SYNC_SECRET=${project.syncSecret}\n`);
917
871
  fs.writeFileSync(path.join(targetDir, ".gitignore"), `node_modules
918
872
  .DS_Store
919
873
  dist
@@ -975,13 +929,13 @@ src/routeTree.gen.ts
975
929
  const projectFlag$1 = optional(option("--project", string({ metavar: "SLUG" })));
976
930
  const jsonFlag$1 = option("--json");
977
931
  const productionFlag$1 = option("--production");
978
- const parser$4 = command("layouts", or(command("list", object({
932
+ const parser$5 = command("layouts", or(command("list", object({
979
933
  command: constant("layouts.list"),
980
934
  project: projectFlag$1,
981
935
  production: productionFlag$1,
982
936
  json: jsonFlag$1
983
937
  }))));
984
- async function handler$4(args) {
938
+ async function handler$5(args) {
985
939
  const outputMode = args.json ? "json" : "auto";
986
940
  const projectFlag = args.project;
987
941
  const production = args.production;
@@ -997,8 +951,8 @@ async function handler$4(args) {
997
951
  }
998
952
  //#endregion
999
953
  //#region src/commands/login.ts
1000
- const parser$3 = command("login", object({ command: constant("login") }));
1001
- const handler$3 = login;
954
+ const parser$4 = command("login", object({ command: constant("login") }));
955
+ const handler$4 = login;
1002
956
  async function login() {
1003
957
  p.intro("camox login");
1004
958
  try {
@@ -1012,16 +966,27 @@ async function login() {
1012
966
  }
1013
967
  //#endregion
1014
968
  //#region src/commands/logout.ts
1015
- const parser$2 = command("logout", object({ command: constant("logout") }));
1016
- const handler$2 = logout;
969
+ const parser$3 = command("logout", object({ command: constant("logout") }));
970
+ const handler$3 = logout;
1017
971
  function logout() {
1018
972
  p.intro("camox logout");
1019
- const token = readAuthToken();
973
+ let authenticationUrl = null;
974
+ try {
975
+ authenticationUrl = loadRuntime().authenticationUrl;
976
+ } catch (error) {
977
+ if (error instanceof RuntimeMalformedError) {
978
+ log.error(error.message);
979
+ return;
980
+ }
981
+ if (!(error instanceof RuntimeNotFoundError)) throw error;
982
+ }
983
+ const token = authenticationUrl ? readAuthTokenForUrl(authenticationUrl) : readAuthToken();
1020
984
  if (!token) {
1021
985
  log.error("Not logged in.");
1022
986
  return;
1023
987
  }
1024
- removeAuthToken();
988
+ if (authenticationUrl) removeAuthTokenForUrl(authenticationUrl);
989
+ else removeAuthToken();
1025
990
  p.log.success(`Logged out from ${token.name}.`);
1026
991
  }
1027
992
  //#endregion
@@ -1070,7 +1035,7 @@ const discardChanges = command("discard-changes", object({
1070
1035
  production: productionFlag,
1071
1036
  json: jsonFlag
1072
1037
  }));
1073
- const parser$1 = command("pages", or(list, get, command("create", object({
1038
+ const parser$2 = command("pages", or(list, get, command("create", object({
1074
1039
  command: constant("pages.create"),
1075
1040
  nickname: optional(option("--nickname", string({ metavar: "TEXT" }))),
1076
1041
  pathSegment: option("--path-segment", string({ metavar: "SEGMENT" })),
@@ -1102,7 +1067,7 @@ const parser$1 = command("pages", or(list, get, command("create", object({
1102
1067
  production: productionFlag,
1103
1068
  json: jsonFlag
1104
1069
  })), publish, unpublish, discardChanges));
1105
- async function handler$1(args) {
1070
+ async function handler$2(args) {
1106
1071
  const outputMode = args.json ? "json" : "auto";
1107
1072
  const projectFlag = args.project;
1108
1073
  const production = args.production;
@@ -1224,6 +1189,34 @@ async function handler$1(args) {
1224
1189
  }
1225
1190
  }
1226
1191
  //#endregion
1192
+ //#region src/commands/release.ts
1193
+ const parser$1 = command("release", object({ command: constant("release") }));
1194
+ function handler$1() {
1195
+ p.intro("camox release");
1196
+ if (!process.env.CAMOX_DEPLOY_TOKEN) {
1197
+ p.log.error("CAMOX_DEPLOY_TOKEN is required to release to production.");
1198
+ process.exit(1);
1199
+ }
1200
+ p.log.info("Building and syncing the production environment...");
1201
+ const result = spawnSync(process.platform === "win32" ? "vp.cmd" : "vp", ["build"], {
1202
+ stdio: "inherit",
1203
+ env: {
1204
+ ...process.env,
1205
+ CAMOX_INTERNAL_RELEASE: "1"
1206
+ }
1207
+ });
1208
+ if (result.error) {
1209
+ p.log.error(`Could not start the production build: ${result.error.message}`);
1210
+ process.exit(1);
1211
+ }
1212
+ if (result.status !== 0) {
1213
+ p.log.error("Production release failed.");
1214
+ process.exit(result.status ?? 1);
1215
+ }
1216
+ p.outro("Production release prepared successfully.");
1217
+ process.exit(0);
1218
+ }
1219
+ //#endregion
1227
1220
  //#region src/commands/status.ts
1228
1221
  const parser = command("status", object({
1229
1222
  command: constant("status"),
@@ -1274,7 +1267,7 @@ async function handler(args) {
1274
1267
  //#endregion
1275
1268
  //#region src/index.ts
1276
1269
  const result = runSync(defineProgram({
1277
- parser: or(parser$5, parser$3, parser$2, parser, parser$1, parser$7, parser$4, parser$6),
1270
+ parser: or(parser$6, parser$4, parser$3, parser$1, parser, parser$2, parser$8, parser$5, parser$7),
1278
1271
  metadata: {
1279
1272
  name: "camox",
1280
1273
  brief: message`Camox CLI`
@@ -1282,13 +1275,16 @@ const result = runSync(defineProgram({
1282
1275
  }), { help: "both" });
1283
1276
  switch (result.command) {
1284
1277
  case "init":
1285
- await handler$5();
1278
+ await handler$6();
1286
1279
  break;
1287
1280
  case "login":
1288
- await handler$3();
1281
+ await handler$4();
1289
1282
  break;
1290
1283
  case "logout":
1291
- handler$2();
1284
+ handler$3();
1285
+ break;
1286
+ case "release":
1287
+ handler$1();
1292
1288
  break;
1293
1289
  case "status":
1294
1290
  await handler(result);
@@ -1302,7 +1298,7 @@ switch (result.command) {
1302
1298
  case "pages.publish":
1303
1299
  case "pages.unpublish":
1304
1300
  case "pages.discard-changes":
1305
- await handler$1(result);
1301
+ await handler$2(result);
1306
1302
  break;
1307
1303
  case "blocks.types":
1308
1304
  case "blocks.describe":
@@ -1312,15 +1308,15 @@ switch (result.command) {
1312
1308
  case "blocks.edit":
1313
1309
  case "blocks.move":
1314
1310
  case "blocks.delete":
1315
- await handler$7(result);
1311
+ await handler$8(result);
1316
1312
  break;
1317
1313
  case "layouts.list":
1318
- await handler$4(result);
1314
+ await handler$5(result);
1319
1315
  break;
1320
1316
  case "env.check":
1321
1317
  case "env.push":
1322
1318
  case "env.pull":
1323
- await handler$6(result);
1319
+ await handler$7(result);
1324
1320
  break;
1325
1321
  }
1326
1322
  //#endregion
@@ -0,0 +1,15 @@
1
+
2
+ //#region src/lib/auth-core.d.ts
3
+ interface AuthToken {
4
+ token: string;
5
+ name: string;
6
+ email: string;
7
+ }
8
+ declare function normalizeUrl(url: string): string;
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;
13
+ declare function verifyOneTimeToken(apiUrl: string, token: string): Promise<AuthToken>;
14
+ //#endregion
15
+ export { AuthToken, normalizeUrl, readAuthTokenForUrl, removeAuthTokenForUrl, verifyOneTimeToken, writeAuthTokenForUrl };
@@ -0,0 +1,3 @@
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-cCuk5aZy.mjs";
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.36.3",
3
+ "version": "0.38.0",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "camox": "./dist/index.mjs"
@@ -12,7 +12,11 @@
12
12
  "type": "module",
13
13
  "main": "./dist/index.mjs",
14
14
  "exports": {
15
- ".": "./dist/index.mjs"
15
+ ".": "./dist/index.mjs",
16
+ "./auth": {
17
+ "types": "./dist/lib/auth-core.d.mts",
18
+ "import": "./dist/lib/auth-core.mjs"
19
+ }
16
20
  },
17
21
  "dependencies": {
18
22
  "@clack/prompts": "^0.10.0",
@@ -27,7 +31,7 @@
27
31
  "@types/node": "^24.12.4",
28
32
  "@typescript/native-preview": "7.0.0-dev.20260412.1",
29
33
  "vite-plus": "^0.1.21",
30
- "@camox/api-contract": "0.36.3"
34
+ "@camox/api-contract": "0.38.0"
31
35
  },
32
36
  "nx": {
33
37
  "tags": [
@@ -1,2 +1,2 @@
1
- # Required — retrieve from https://app.camox.dev
2
- CAMOX_SYNC_SECRET=
1
+ # CI-only production releases — retrieve from https://app.camox.dev
2
+ CAMOX_DEPLOY_TOKEN=
@@ -8,6 +8,7 @@
8
8
  "dev": "vp dev --port 7400",
9
9
  "start": "node .output/server/index.mjs",
10
10
  "build": "vp build",
11
+ "release": "camox release",
11
12
  "serve": "vp preview",
12
13
  "lint": "vp lint",
13
14
  "check": "vp check"
@@ -3,9 +3,7 @@ import tailwindcss from "@tailwindcss/vite";
3
3
  import react, { reactCompilerPreset } from "@vitejs/plugin-react";
4
4
  import { camox } from "camox/vite";
5
5
  import { nitro } from "nitro/vite";
6
- import { defineConfig, loadEnv } from "vite-plus";
7
-
8
- const env = loadEnv(process.env.NODE_ENV!, process.cwd(), "CAMOX_");
6
+ import { defineConfig } from "vite-plus";
9
7
 
10
8
  export default defineConfig({
11
9
  resolve: { tsconfigPaths: true },
@@ -14,7 +12,6 @@ export default defineConfig({
14
12
  nitro(),
15
13
  camox({
16
14
  projectSlug: "camox-template-default-01", // camox-cli:replace-slug
17
- syncSecret: env.CAMOX_SYNC_SECRET,
18
15
  // camox-cli:dev-only-start
19
16
  _internal: {
20
17
  authenticationUrl: "http://localhost:3274",