@camox/cli 0.8.0 → 0.9.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.
|
@@ -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";
|
|
@@ -10,6 +10,7 @@ import path from "node:path";
|
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import * as p from "@clack/prompts";
|
|
12
12
|
import { command, constant } from "@optique/core/primitives";
|
|
13
|
+
import slugify from "slugify";
|
|
13
14
|
import http from "node:http";
|
|
14
15
|
import os from "node:os";
|
|
15
16
|
//#region \0rolldown/runtime.js
|
|
@@ -172,7 +173,7 @@ async function authenticateUser() {
|
|
|
172
173
|
async function getOrAuthenticate() {
|
|
173
174
|
const stored = readAuthToken();
|
|
174
175
|
if (stored) {
|
|
175
|
-
const { verifySession } = await import("./api-
|
|
176
|
+
const { verifySession } = await import("./api-CB7vjDU7.mjs");
|
|
176
177
|
if (await verifySession(stored.token)) {
|
|
177
178
|
p.log.info(`Authenticated as ${stored.name}`);
|
|
178
179
|
return stored;
|
|
@@ -202,9 +203,6 @@ const pmCommands = {
|
|
|
202
203
|
dev: "yarn dev"
|
|
203
204
|
}
|
|
204
205
|
};
|
|
205
|
-
function slugify(name) {
|
|
206
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
207
|
-
}
|
|
208
206
|
function copyDir(src, dest, replacements) {
|
|
209
207
|
fs.mkdirSync(dest, { recursive: true });
|
|
210
208
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
@@ -266,7 +264,10 @@ async function promptCreateOrganization(token) {
|
|
|
266
264
|
}
|
|
267
265
|
});
|
|
268
266
|
if (p.isCancel(orgName)) return onCancel();
|
|
269
|
-
const org = await createOrganization(token, orgName, slugify(orgName
|
|
267
|
+
const org = await createOrganization(token, orgName, slugify(orgName, {
|
|
268
|
+
lower: true,
|
|
269
|
+
strict: true
|
|
270
|
+
}));
|
|
270
271
|
p.log.success(`Created organization: ${org.name}`);
|
|
271
272
|
return org.id;
|
|
272
273
|
}
|
|
@@ -275,40 +276,63 @@ async function init() {
|
|
|
275
276
|
const stored = readAuthToken();
|
|
276
277
|
if (stored) p.log.info(`Welcome back, ${stored.name}!`);
|
|
277
278
|
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);
|
|
279
|
+
const name = await p.text({
|
|
280
|
+
message: "Project display name",
|
|
281
|
+
placeholder: "My Website",
|
|
282
|
+
validate: (value) => {
|
|
283
|
+
if (!value.trim()) return "Project name is required";
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
if (p.isCancel(name)) return onCancel();
|
|
295
287
|
const auth = await getOrAuthenticate();
|
|
296
288
|
const orgId = await selectOrCreateOrganization(auth.token);
|
|
289
|
+
let projectSlug;
|
|
290
|
+
while (true) {
|
|
291
|
+
const slugInput = await p.text({
|
|
292
|
+
message: "Project slug",
|
|
293
|
+
initialValue: slugify(name, {
|
|
294
|
+
lower: true,
|
|
295
|
+
strict: true
|
|
296
|
+
}) || "my-site",
|
|
297
|
+
validate: (value) => {
|
|
298
|
+
if (!value.trim()) return "Slug is required";
|
|
299
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) return "Slug must be lowercase alphanumeric with hyphens";
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
if (p.isCancel(slugInput)) return onCancel();
|
|
303
|
+
const s = p.spinner();
|
|
304
|
+
s.start("Checking slug availability...");
|
|
305
|
+
const { available } = await checkSlugAvailability(auth.token, slugInput);
|
|
306
|
+
if (available) {
|
|
307
|
+
s.stop("Slug is available!");
|
|
308
|
+
projectSlug = slugInput;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
s.stop(`Slug "${slugInput}" is already taken. Please choose another.`);
|
|
312
|
+
}
|
|
313
|
+
const projectPath = await p.text({
|
|
314
|
+
message: "Project path",
|
|
315
|
+
initialValue: `./${projectSlug}`,
|
|
316
|
+
validate: (value) => {
|
|
317
|
+
if (!value.trim()) return "Path is required";
|
|
318
|
+
const resolved = path.resolve(value);
|
|
319
|
+
if (fs.existsSync(resolved) && fs.readdirSync(resolved).length > 0) return "Directory is not empty";
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
if (p.isCancel(projectPath)) return onCancel();
|
|
323
|
+
const resolvedPath = projectPath;
|
|
324
|
+
const targetDir = path.resolve(resolvedPath);
|
|
297
325
|
const s0 = p.spinner();
|
|
298
326
|
s0.start("Creating project...");
|
|
299
327
|
let project;
|
|
300
328
|
try {
|
|
301
|
-
project = await createProject(auth.token,
|
|
329
|
+
project = await createProject(auth.token, name, projectSlug, orgId);
|
|
302
330
|
s0.stop(`Project created with slug: ${project.slug}`);
|
|
303
331
|
} catch (err) {
|
|
304
332
|
s0.stop("Failed to create project.");
|
|
305
333
|
p.log.error(err instanceof Error ? err.message : "Unknown error");
|
|
306
334
|
process.exit(1);
|
|
307
335
|
}
|
|
308
|
-
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
|
309
|
-
p.cancel(`Directory ${targetDir} is not empty.`);
|
|
310
|
-
process.exit(1);
|
|
311
|
-
}
|
|
312
336
|
const selected = await p.select({
|
|
313
337
|
message: "Which package manager?",
|
|
314
338
|
options: [
|
|
@@ -335,7 +359,7 @@ async function init() {
|
|
|
335
359
|
const s = p.spinner();
|
|
336
360
|
s.start("Scaffolding project...");
|
|
337
361
|
copyDir(path.resolve(__dirname, "..", "template"), targetDir, {
|
|
338
|
-
"{{projectName}}":
|
|
362
|
+
"{{projectName}}": name,
|
|
339
363
|
"{{projectSlug}}": project.slug,
|
|
340
364
|
"{{camoxVersion}}": ownPkg.version
|
|
341
365
|
});
|
|
@@ -363,7 +387,7 @@ src/routeTree.gen.ts
|
|
|
363
387
|
s.stop("Project scaffolded!");
|
|
364
388
|
function dropIntoProject() {
|
|
365
389
|
const shell = process.env.SHELL || "/bin/bash";
|
|
366
|
-
p.log.info(`Dropping you into ${
|
|
390
|
+
p.log.info(`Dropping you into ${resolvedPath}`);
|
|
367
391
|
spawnSync(shell, [], {
|
|
368
392
|
cwd: targetDir,
|
|
369
393
|
stdio: "inherit"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camox/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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.0"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
32
|
"build": "tsdown",
|
package/dist/api-BB93iDN7.mjs
DELETED