@camox/cli 0.8.0 → 0.9.1
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.
|
@@ -52,11 +52,15 @@ async function setActiveOrganization(token, organizationId) {
|
|
|
52
52
|
});
|
|
53
53
|
if (!res.ok) throw new Error(`Failed to set active organization: ${res.status}`);
|
|
54
54
|
}
|
|
55
|
-
async function
|
|
55
|
+
async function checkSlugAvailability(token, slug) {
|
|
56
|
+
return createRpcClient(token).projects.checkSlugAvailability({ slug });
|
|
57
|
+
}
|
|
58
|
+
async function createProject(token, name, slug, organizationId) {
|
|
56
59
|
return await createRpcClient(token).projects.create({
|
|
57
60
|
name,
|
|
61
|
+
slug,
|
|
58
62
|
organizationId
|
|
59
63
|
});
|
|
60
64
|
}
|
|
61
65
|
//#endregion
|
|
62
|
-
export {
|
|
66
|
+
export { setActiveOrganization as a, listOrganizations as i, createOrganization as n, verifySession as o, createProject as r, checkSlugAvailability as t };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { a as setActiveOrganization, i as listOrganizations, n as createOrganization, r as createProject, t as checkSlugAvailability } from "./api-QINint5w.mjs";
|
|
3
3
|
import { object, or } from "@optique/core/constructs";
|
|
4
4
|
import { message } from "@optique/core/message";
|
|
5
5
|
import { defineProgram } from "@optique/core/program";
|
|
@@ -9,7 +9,9 @@ import fs from "node:fs";
|
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import * as p from "@clack/prompts";
|
|
12
|
+
import { log } from "@clack/prompts";
|
|
12
13
|
import { command, constant } from "@optique/core/primitives";
|
|
14
|
+
import slugify from "slugify";
|
|
13
15
|
import http from "node:http";
|
|
14
16
|
import os from "node:os";
|
|
15
17
|
//#region \0rolldown/runtime.js
|
|
@@ -172,7 +174,7 @@ async function authenticateUser() {
|
|
|
172
174
|
async function getOrAuthenticate() {
|
|
173
175
|
const stored = readAuthToken();
|
|
174
176
|
if (stored) {
|
|
175
|
-
const { verifySession } = await import("./api-
|
|
177
|
+
const { verifySession } = await import("./api-CB7vjDU7.mjs");
|
|
176
178
|
if (await verifySession(stored.token)) {
|
|
177
179
|
p.log.info(`Authenticated as ${stored.name}`);
|
|
178
180
|
return stored;
|
|
@@ -202,9 +204,6 @@ const pmCommands = {
|
|
|
202
204
|
dev: "yarn dev"
|
|
203
205
|
}
|
|
204
206
|
};
|
|
205
|
-
function slugify(name) {
|
|
206
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
207
|
-
}
|
|
208
207
|
function copyDir(src, dest, replacements) {
|
|
209
208
|
fs.mkdirSync(dest, { recursive: true });
|
|
210
209
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
@@ -266,7 +265,10 @@ async function promptCreateOrganization(token) {
|
|
|
266
265
|
}
|
|
267
266
|
});
|
|
268
267
|
if (p.isCancel(orgName)) return onCancel();
|
|
269
|
-
const org = await createOrganization(token, orgName, slugify(orgName
|
|
268
|
+
const org = await createOrganization(token, orgName, slugify(orgName, {
|
|
269
|
+
lower: true,
|
|
270
|
+
strict: true
|
|
271
|
+
}));
|
|
270
272
|
p.log.success(`Created organization: ${org.name}`);
|
|
271
273
|
return org.id;
|
|
272
274
|
}
|
|
@@ -275,40 +277,63 @@ async function init() {
|
|
|
275
277
|
const stored = readAuthToken();
|
|
276
278
|
if (stored) p.log.info(`Welcome back, ${stored.name}!`);
|
|
277
279
|
p.log.info("Let's create your Camox application.");
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
path: ({ results }) => p.text({
|
|
287
|
-
message: "Project path",
|
|
288
|
-
initialValue: `./${slugify(results.name ?? "") || "my-site"}`,
|
|
289
|
-
validate: (value) => {
|
|
290
|
-
if (!value.trim()) return "Path is required";
|
|
291
|
-
}
|
|
292
|
-
})
|
|
293
|
-
}, { onCancel });
|
|
294
|
-
const targetDir = path.resolve(result.path);
|
|
280
|
+
const name = await p.text({
|
|
281
|
+
message: "Project display name",
|
|
282
|
+
placeholder: "My Website",
|
|
283
|
+
validate: (value) => {
|
|
284
|
+
if (!value.trim()) return "Project name is required";
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (p.isCancel(name)) return onCancel();
|
|
295
288
|
const auth = await getOrAuthenticate();
|
|
296
289
|
const orgId = await selectOrCreateOrganization(auth.token);
|
|
290
|
+
let projectSlug;
|
|
291
|
+
while (true) {
|
|
292
|
+
const slugInput = await p.text({
|
|
293
|
+
message: "Project slug",
|
|
294
|
+
initialValue: slugify(name, {
|
|
295
|
+
lower: true,
|
|
296
|
+
strict: true
|
|
297
|
+
}) || "my-site",
|
|
298
|
+
validate: (value) => {
|
|
299
|
+
if (!value.trim()) return "Slug is required";
|
|
300
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) return "Slug must be lowercase alphanumeric with hyphens";
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
if (p.isCancel(slugInput)) return onCancel();
|
|
304
|
+
const s = p.spinner();
|
|
305
|
+
s.start("Checking slug availability...");
|
|
306
|
+
const { available } = await checkSlugAvailability(auth.token, slugInput);
|
|
307
|
+
if (available) {
|
|
308
|
+
s.stop("Slug is available!");
|
|
309
|
+
projectSlug = slugInput;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
s.stop(`Slug "${slugInput}" is already taken. Please choose another.`);
|
|
313
|
+
}
|
|
314
|
+
const projectPath = await p.text({
|
|
315
|
+
message: "Project path",
|
|
316
|
+
initialValue: `./${projectSlug}`,
|
|
317
|
+
validate: (value) => {
|
|
318
|
+
if (!value.trim()) return "Path is required";
|
|
319
|
+
const resolved = path.resolve(value);
|
|
320
|
+
if (fs.existsSync(resolved) && fs.readdirSync(resolved).length > 0) return "Directory is not empty";
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
if (p.isCancel(projectPath)) return onCancel();
|
|
324
|
+
const resolvedPath = projectPath;
|
|
325
|
+
const targetDir = path.resolve(resolvedPath);
|
|
297
326
|
const s0 = p.spinner();
|
|
298
327
|
s0.start("Creating project...");
|
|
299
328
|
let project;
|
|
300
329
|
try {
|
|
301
|
-
project = await createProject(auth.token,
|
|
330
|
+
project = await createProject(auth.token, name, projectSlug, orgId);
|
|
302
331
|
s0.stop(`Project created with slug: ${project.slug}`);
|
|
303
332
|
} catch (err) {
|
|
304
333
|
s0.stop("Failed to create project.");
|
|
305
334
|
p.log.error(err instanceof Error ? err.message : "Unknown error");
|
|
306
335
|
process.exit(1);
|
|
307
336
|
}
|
|
308
|
-
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
|
309
|
-
p.cancel(`Directory ${targetDir} is not empty.`);
|
|
310
|
-
process.exit(1);
|
|
311
|
-
}
|
|
312
337
|
const selected = await p.select({
|
|
313
338
|
message: "Which package manager?",
|
|
314
339
|
options: [
|
|
@@ -335,7 +360,7 @@ async function init() {
|
|
|
335
360
|
const s = p.spinner();
|
|
336
361
|
s.start("Scaffolding project...");
|
|
337
362
|
copyDir(path.resolve(__dirname, "..", "template"), targetDir, {
|
|
338
|
-
"{{projectName}}":
|
|
363
|
+
"{{projectName}}": name,
|
|
339
364
|
"{{projectSlug}}": project.slug,
|
|
340
365
|
"{{camoxVersion}}": ownPkg.version
|
|
341
366
|
});
|
|
@@ -363,7 +388,7 @@ src/routeTree.gen.ts
|
|
|
363
388
|
s.stop("Project scaffolded!");
|
|
364
389
|
function dropIntoProject() {
|
|
365
390
|
const shell = process.env.SHELL || "/bin/bash";
|
|
366
|
-
p.log.info(`Dropping you into ${
|
|
391
|
+
p.log.info(`Dropping you into ${resolvedPath}`);
|
|
367
392
|
spawnSync(shell, [], {
|
|
368
393
|
cwd: targetDir,
|
|
369
394
|
stdio: "inherit"
|
|
@@ -432,13 +457,14 @@ var logout_exports = /* @__PURE__ */ __exportAll({
|
|
|
432
457
|
const parser = command("logout", object({ command: constant("logout") }));
|
|
433
458
|
const handler = logout;
|
|
434
459
|
function logout() {
|
|
460
|
+
p.intro("camox logout");
|
|
435
461
|
const token = readAuthToken();
|
|
436
462
|
if (!token) {
|
|
437
|
-
|
|
463
|
+
log.error("Not logged in.");
|
|
438
464
|
return;
|
|
439
465
|
}
|
|
440
466
|
removeAuthToken();
|
|
441
|
-
|
|
467
|
+
p.log.success(`Logged out from ${token.name}.`);
|
|
442
468
|
}
|
|
443
469
|
//#endregion
|
|
444
470
|
//#region src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camox/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"bin": {
|
|
5
5
|
"camox": "./dist/index.mjs"
|
|
6
6
|
},
|
|
@@ -18,14 +18,15 @@
|
|
|
18
18
|
"@optique/core": "*",
|
|
19
19
|
"@optique/run": "*",
|
|
20
20
|
"@orpc/client": "^1.13.14",
|
|
21
|
-
"@orpc/server": "^1.13.14"
|
|
21
|
+
"@orpc/server": "^1.13.14",
|
|
22
|
+
"slugify": "^1.6.9"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^24.12.2",
|
|
25
26
|
"@typescript/native-preview": "7.0.0-dev.20260412.1",
|
|
26
27
|
"oxlint": "^0.15.0",
|
|
27
28
|
"tsdown": "^0.21.8",
|
|
28
|
-
"@camox/api-contract": "0.
|
|
29
|
+
"@camox/api-contract": "0.9.1"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
32
|
"build": "tsdown",
|
package/template/src/styles.css
CHANGED
package/dist/api-BB93iDN7.mjs
DELETED