@cogenta/cli 0.1.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.
Files changed (41) hide show
  1. package/dist/bin.d.ts +3 -0
  2. package/dist/bin.d.ts.map +1 -0
  3. package/dist/bin.js +31 -0
  4. package/dist/bin.js.map +1 -0
  5. package/dist/commands/doctor.d.ts +45 -0
  6. package/dist/commands/doctor.d.ts.map +1 -0
  7. package/dist/commands/doctor.js +147 -0
  8. package/dist/commands/doctor.js.map +1 -0
  9. package/dist/commands/generate.d.ts +19 -0
  10. package/dist/commands/generate.d.ts.map +1 -0
  11. package/dist/commands/generate.js +60 -0
  12. package/dist/commands/generate.js.map +1 -0
  13. package/dist/commands/import.d.ts +22 -0
  14. package/dist/commands/import.d.ts.map +1 -0
  15. package/dist/commands/import.js +77 -0
  16. package/dist/commands/import.js.map +1 -0
  17. package/dist/commands/migrate.d.ts +38 -0
  18. package/dist/commands/migrate.d.ts.map +1 -0
  19. package/dist/commands/migrate.js +272 -0
  20. package/dist/commands/migrate.js.map +1 -0
  21. package/dist/commands/serve.d.ts +81 -0
  22. package/dist/commands/serve.d.ts.map +1 -0
  23. package/dist/commands/serve.js +515 -0
  24. package/dist/commands/serve.js.map +1 -0
  25. package/dist/commands/skin.d.ts +24 -0
  26. package/dist/commands/skin.d.ts.map +1 -0
  27. package/dist/commands/skin.js +199 -0
  28. package/dist/commands/skin.js.map +1 -0
  29. package/dist/commands/users.d.ts +22 -0
  30. package/dist/commands/users.d.ts.map +1 -0
  31. package/dist/commands/users.js +107 -0
  32. package/dist/commands/users.js.map +1 -0
  33. package/dist/index.d.ts +41 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +244 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/output.d.ts +17 -0
  38. package/dist/output.d.ts.map +1 -0
  39. package/dist/output.js +39 -0
  40. package/dist/output.js.map +1 -0
  41. package/package.json +53 -0
@@ -0,0 +1,199 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname, join, resolve as resolvePath } from 'node:path';
3
+ import process from 'node:process';
4
+ import { createAnthropicClient, createGoogleClient, createOpenAiClient, generateSkin, } from '@cogenta/agents';
5
+ import { CogentaError, isCogentaError, loadConfig } from '@cogenta/core';
6
+ import { TOKEN_GROUPS, validateSkin } from '@cogenta/render';
7
+ const TOKENS_FILE = 'theme.tokens.json';
8
+ const USAGE = `Usage
9
+ cogenta skin list Show the site's active skin
10
+ cogenta skin validate <tokens.json> Check a token file against contract D
11
+ cogenta skin apply <tokens.json> Validate, then make it the active skin
12
+ cogenta skin generate --description "…" Generate a skin from a description
13
+
14
+ "generate" needs an LLM provider configured (cogenta.config's llm block, or
15
+ COGENTA_LLM_* environment variables) — the CMS works without one (R2), so
16
+ "skin generate" is the one skin subcommand that refuses without it.
17
+ `;
18
+ async function resolveProjectRoot(options) {
19
+ const env = options.env ?? process.env;
20
+ const loaded = await loadConfig({
21
+ ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
22
+ env,
23
+ });
24
+ return loaded.path === null ? resolvePath(options.cwd ?? process.cwd()) : dirname(loaded.path);
25
+ }
26
+ function reportCogentaError(error, stderr) {
27
+ if (isCogentaError(error)) {
28
+ stderr(`${error.code}: ${error.message}\n`);
29
+ if (error.hint !== undefined)
30
+ stderr(`${error.hint}\n`);
31
+ }
32
+ else {
33
+ stderr(`${error instanceof Error ? error.stack : String(error)}\n`);
34
+ }
35
+ }
36
+ async function readTokensFile(path) {
37
+ const raw = await readFile(path, 'utf8');
38
+ return JSON.parse(raw);
39
+ }
40
+ /** Validates, then writes — a skin is never applied without passing contract D first. */
41
+ async function applyTokens(tokens, projectRoot) {
42
+ const path = join(projectRoot, TOKENS_FILE);
43
+ await writeFile(path, `${JSON.stringify(tokens, null, 2)}\n`, 'utf8');
44
+ return path;
45
+ }
46
+ async function runList(options) {
47
+ const { out, stderr } = options;
48
+ const projectRoot = await resolveProjectRoot(options);
49
+ const path = join(projectRoot, TOKENS_FILE);
50
+ let candidate;
51
+ try {
52
+ candidate = await readTokensFile(path);
53
+ }
54
+ catch (error) {
55
+ stderr(`Could not read ${path}: ${error instanceof Error ? error.message : String(error)}\n`);
56
+ return 1;
57
+ }
58
+ try {
59
+ const tokens = validateSkin(candidate);
60
+ out.heading('Active skin');
61
+ out.line(path);
62
+ for (const group of TOKEN_GROUPS) {
63
+ out.line(`${group}: ${JSON.stringify(tokens[group])}`);
64
+ }
65
+ return 0;
66
+ }
67
+ catch (error) {
68
+ reportCogentaError(error, stderr);
69
+ return 1;
70
+ }
71
+ }
72
+ async function runValidate(options) {
73
+ const { out, stderr } = options;
74
+ if (options.file === undefined || options.file.trim().length === 0) {
75
+ stderr(`A file path is required.\n\n${USAGE}`);
76
+ return 2;
77
+ }
78
+ let candidate;
79
+ try {
80
+ candidate = await readTokensFile(options.file);
81
+ }
82
+ catch (error) {
83
+ stderr(`Could not read "${options.file}": ${error instanceof Error ? error.message : String(error)}\n`);
84
+ return 1;
85
+ }
86
+ try {
87
+ validateSkin(candidate);
88
+ out.ok(`${options.file} is a valid skin.`);
89
+ return 0;
90
+ }
91
+ catch (error) {
92
+ reportCogentaError(error, stderr);
93
+ return 1;
94
+ }
95
+ }
96
+ async function runApply(options) {
97
+ const { out, stderr } = options;
98
+ if (options.file === undefined || options.file.trim().length === 0) {
99
+ stderr(`A file path is required.\n\n${USAGE}`);
100
+ return 2;
101
+ }
102
+ let candidate;
103
+ try {
104
+ candidate = await readTokensFile(options.file);
105
+ }
106
+ catch (error) {
107
+ stderr(`Could not read "${options.file}": ${error instanceof Error ? error.message : String(error)}\n`);
108
+ return 1;
109
+ }
110
+ try {
111
+ const tokens = validateSkin(candidate);
112
+ const projectRoot = await resolveProjectRoot(options);
113
+ const written = await applyTokens(tokens, projectRoot);
114
+ out.ok(`Applied. ${written}`);
115
+ return 0;
116
+ }
117
+ catch (error) {
118
+ reportCogentaError(error, stderr);
119
+ return 1;
120
+ }
121
+ }
122
+ /** The same adapter construction `create-cogenta`'s `llm-setup.ts` uses for its own provider client — kept as a small, local switch rather than a shared dependency, since the CLI's only need is "one client for the configured provider," with its own test seam. */
123
+ function clientFor(provider, apiKey, model, fetchImpl) {
124
+ const config = { apiKey, model, ...(fetchImpl === undefined ? {} : { fetchImpl }) };
125
+ if (provider === 'anthropic')
126
+ return createAnthropicClient(config);
127
+ if (provider === 'openai')
128
+ return createOpenAiClient(config);
129
+ if (provider === 'google')
130
+ return createGoogleClient(config);
131
+ throw new CogentaError({
132
+ code: 'PROVIDER_UNKNOWN',
133
+ message: `No provider named "${provider}" is configured for this site.`,
134
+ hint: 'Set llm.provider in cogenta.config to "anthropic", "openai" or "google".',
135
+ });
136
+ }
137
+ async function runGenerateSkin(options) {
138
+ const { out, stderr } = options;
139
+ if (options.description === undefined || options.description.trim().length === 0) {
140
+ stderr(`--description is required.\n\n${USAGE}`);
141
+ return 2;
142
+ }
143
+ const env = options.env ?? process.env;
144
+ const loaded = await loadConfig({
145
+ ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
146
+ env,
147
+ });
148
+ if (loaded.config.llm === undefined || loaded.config.llm.apiKey === undefined) {
149
+ stderr('No LLM provider is configured for this site — the CMS works without one (R2).\n');
150
+ stderr('Set llm.provider/llm.model in cogenta.config and COGENTA_LLM_API_KEY to use this.\n');
151
+ return 1;
152
+ }
153
+ const projectRoot = loaded.path === null ? resolvePath(options.cwd ?? process.cwd()) : dirname(loaded.path);
154
+ try {
155
+ const client = clientFor(loaded.config.llm.provider, loaded.config.llm.apiKey, loaded.config.llm.model, options.fetchImpl);
156
+ out.detail(`Generating a skin from your description…`);
157
+ const result = await generateSkin({
158
+ client,
159
+ model: loaded.config.llm.model,
160
+ description: options.description,
161
+ blueprintLabel: loaded.config.site.name,
162
+ });
163
+ if (!result.ok) {
164
+ stderr(`${result.attempts} attempt${result.attempts === 1 ? '' : 's'} all failed validation: ${result.reason}\n`);
165
+ stderr('The site keeps its current skin — nothing was written.\n');
166
+ return 1;
167
+ }
168
+ const written = await applyTokens(result.tokens, projectRoot);
169
+ out.ok(`Skin generated and validated in ${result.attempts} attempt${result.attempts === 1 ? '' : 's'}. Applied. ${written}`);
170
+ return 0;
171
+ }
172
+ catch (error) {
173
+ reportCogentaError(error, stderr);
174
+ return 1;
175
+ }
176
+ }
177
+ /**
178
+ * `cogenta skin` — 0 the operation succeeded (including a valid `validate`).
179
+ * 1 an invalid skin, a missing file, or a real failure. 2 the command line
180
+ * was wrong.
181
+ */
182
+ export async function runSkin(options) {
183
+ const { stderr } = options;
184
+ if (options.subcommand === undefined) {
185
+ stderr(`cogenta skin needs a subcommand.\n\n${USAGE}`);
186
+ return 2;
187
+ }
188
+ if (options.subcommand === 'list')
189
+ return runList(options);
190
+ if (options.subcommand === 'validate')
191
+ return runValidate(options);
192
+ if (options.subcommand === 'apply')
193
+ return runApply(options);
194
+ if (options.subcommand === 'generate')
195
+ return runGenerateSkin(options);
196
+ stderr(`Unknown subcommand "${options.subcommand}".\n\n${USAGE}`);
197
+ return 2;
198
+ }
199
+ //# sourceMappingURL=skin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skin.js","sourceRoot":"","sources":["../../src/commands/skin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type Logger } from '@cogenta/core';
2
+ import type { Output, Writer } from '../output.js';
3
+ export type UsersSubcommand = 'create';
4
+ export interface UsersOptions {
5
+ readonly subcommand: string | undefined;
6
+ readonly cwd?: string;
7
+ readonly env?: Record<string, string | undefined>;
8
+ readonly logger?: Logger;
9
+ readonly out: Output;
10
+ readonly stderr: Writer;
11
+ readonly email?: string;
12
+ readonly roles?: string;
13
+ readonly admin?: boolean;
14
+ }
15
+ /**
16
+ * Runs one `users` subcommand and returns its exit code.
17
+ *
18
+ * 0 succeeded, 1 the database or the store said no, 2 the command line was
19
+ * wrong — same convention as `migrate` and `doctor`.
20
+ */
21
+ export declare function runUsers(options: UsersOptions): Promise<number>;
22
+ //# sourceMappingURL=users.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/commands/users.ts"],"names":[],"mappings":"AAGA,OAAO,EAKL,KAAK,MAAM,EAEZ,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAElD,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAA;AAEtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CACzB;AAuDD;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAsDrE"}
@@ -0,0 +1,107 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import process from 'node:process';
3
+ import { createCredentialStore, createUserStore, ensureAuthTables } from '@cogenta/auth';
4
+ import { createDatabaseRegistry, createLogger, isCogentaError, loadConfig, } from '@cogenta/core';
5
+ const USAGE = `Usage
6
+ cogenta users create --email <email> [--roles <role,role>] [--admin]
7
+
8
+ The very first admin account is created this way — there is no other way in,
9
+ since the admin UI needs one to sign in to. A random password is generated and
10
+ printed once; it is never stored anywhere but the credential table's hash.
11
+
12
+ Options
13
+ --admin Shorthand for --roles admin
14
+ `;
15
+ /** Base64url, so it is safe to read off a terminal and paste back with no escaping. */
16
+ function generatePassword() {
17
+ return randomBytes(24).toString('base64url');
18
+ }
19
+ function parseRoles(options) {
20
+ if (options.admin === true && options.roles !== undefined) {
21
+ return { error: '--admin and --roles are mutually exclusive — --admin already means admin.' };
22
+ }
23
+ if (options.admin === true)
24
+ return ['admin'];
25
+ if (options.roles === undefined) {
26
+ return { error: 'Name at least one role with --roles, or pass --admin for the first account.' };
27
+ }
28
+ const roles = options.roles
29
+ .split(',')
30
+ .map((role) => role.trim())
31
+ .filter((role) => role.length > 0);
32
+ if (roles.length === 0) {
33
+ return { error: '--roles was given but named no role.' };
34
+ }
35
+ return roles;
36
+ }
37
+ async function withDatabase(options, logger, use) {
38
+ const env = options.env ?? process.env;
39
+ const loaded = await loadConfig({
40
+ ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
41
+ env,
42
+ });
43
+ const selection = await createDatabaseRegistry({ logger }).select(loaded.config.database);
44
+ try {
45
+ return await use(selection.instance);
46
+ }
47
+ finally {
48
+ await selection.dispose();
49
+ }
50
+ }
51
+ /**
52
+ * Runs one `users` subcommand and returns its exit code.
53
+ *
54
+ * 0 succeeded, 1 the database or the store said no, 2 the command line was
55
+ * wrong — same convention as `migrate` and `doctor`.
56
+ */
57
+ export async function runUsers(options) {
58
+ const { out, stderr } = options;
59
+ if (options.subcommand === undefined) {
60
+ stderr(`cogenta users needs a subcommand.\n\n${USAGE}`);
61
+ return 2;
62
+ }
63
+ if (options.subcommand !== 'create') {
64
+ stderr(`Unknown subcommand "${options.subcommand}".\n\n${USAGE}`);
65
+ return 2;
66
+ }
67
+ if (options.email === undefined || options.email.trim().length === 0) {
68
+ stderr(`--email is required.\n\n${USAGE}`);
69
+ return 2;
70
+ }
71
+ const roles = parseRoles(options);
72
+ if ('error' in roles) {
73
+ stderr(`${roles.error}\n\n${USAGE}`);
74
+ return 2;
75
+ }
76
+ const logger = options.logger ?? createLogger({ level: 'silent' });
77
+ const password = generatePassword();
78
+ try {
79
+ return await withDatabase(options, logger, async (db) => {
80
+ await ensureAuthTables(db);
81
+ const users = createUserStore(db);
82
+ const credentials = createCredentialStore(db);
83
+ const user = await users.create({ email: options.email, roles });
84
+ await credentials.setPassword(user.id, password);
85
+ out.heading('User created');
86
+ out.ok(`${user.email} — ${user.roles.join(', ')}`);
87
+ out.line();
88
+ out.line(`Password: ${password}`);
89
+ out.line();
90
+ out.warn('This password is shown once. It is stored only as a salted hash.');
91
+ out.detail('A role that can publish, or admin, will be asked to set up a second factor at first sign-in.');
92
+ return 0;
93
+ });
94
+ }
95
+ catch (error) {
96
+ if (isCogentaError(error)) {
97
+ stderr(`${error.code}: ${error.message}\n`);
98
+ if (error.hint !== undefined)
99
+ stderr(`${error.hint}\n`);
100
+ }
101
+ else {
102
+ stderr(`${error instanceof Error ? error.stack : String(error)}\n`);
103
+ }
104
+ return 1;
105
+ }
106
+ }
107
+ //# sourceMappingURL=users.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/commands/users.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { type Writer } from './output.js';
2
+ export type { DoctorCheck, DoctorOptions, DoctorReport } from './commands/doctor.js';
3
+ export { formatDoctorReport, runDoctor } from './commands/doctor.js';
4
+ export type { GenerateOptions, GenerateSubcommand } from './commands/generate.js';
5
+ export { runGenerate } from './commands/generate.js';
6
+ export type { ImportOptions, ImportSubcommand } from './commands/import.js';
7
+ export { runImport } from './commands/import.js';
8
+ export type { MigrateOptions, MigrateSubcommand } from './commands/migrate.js';
9
+ export { loadMigrations, MIGRATIONS_DIRECTORY, runMigrate } from './commands/migrate.js';
10
+ export type { ServeOptions } from './commands/serve.js';
11
+ export { loadCollections, runServe } from './commands/serve.js';
12
+ export type { SkinOptions, SkinSubcommand } from './commands/skin.js';
13
+ export { runSkin } from './commands/skin.js';
14
+ export type { UsersOptions, UsersSubcommand } from './commands/users.js';
15
+ export { runUsers } from './commands/users.js';
16
+ export type { Output, Writer } from './output.js';
17
+ export { createOutput, shouldUseColour } from './output.js';
18
+ export interface RunOptions {
19
+ readonly argv: readonly string[];
20
+ readonly stdout?: Writer;
21
+ readonly stderr?: Writer;
22
+ readonly env?: Record<string, string | undefined>;
23
+ readonly isTty?: boolean;
24
+ readonly version?: string;
25
+ /** Stops `serve` when aborted. Ignored by every other command. */
26
+ readonly signal?: AbortSignal;
27
+ /** `serve` only: reports the bound address once listening (tests need the OS-assigned port). */
28
+ readonly onListening?: (address: {
29
+ port: number;
30
+ host: string;
31
+ }) => void;
32
+ }
33
+ /**
34
+ * Runs one command and returns its exit code.
35
+ *
36
+ * Nothing here calls `process.exit` or writes to a stream directly: the streams
37
+ * are injected, so the whole CLI is testable without spawning a process or
38
+ * capturing stdout.
39
+ */
40
+ export declare function run(options: RunOptions): Promise<number>;
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAiC,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AAExE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACpF,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AACpE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACxF,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAkD3D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACzE;AAED;;;;;;GAMG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAuL9D"}
package/dist/index.js ADDED
@@ -0,0 +1,244 @@
1
+ import process from 'node:process';
2
+ import { parseArgs } from 'node:util';
3
+ import { createLogger, isCogentaError } from '@cogenta/core';
4
+ import { formatDoctorReport, runDoctor } from './commands/doctor.js';
5
+ import { runGenerate } from './commands/generate.js';
6
+ import { runImport } from './commands/import.js';
7
+ import { runMigrate } from './commands/migrate.js';
8
+ import { runServe } from './commands/serve.js';
9
+ import { runSkin } from './commands/skin.js';
10
+ import { runUsers } from './commands/users.js';
11
+ import { createOutput, shouldUseColour } from './output.js';
12
+ export { formatDoctorReport, runDoctor } from './commands/doctor.js';
13
+ export { runGenerate } from './commands/generate.js';
14
+ export { runImport } from './commands/import.js';
15
+ export { loadMigrations, MIGRATIONS_DIRECTORY, runMigrate } from './commands/migrate.js';
16
+ export { loadCollections, runServe } from './commands/serve.js';
17
+ export { runSkin } from './commands/skin.js';
18
+ export { runUsers } from './commands/users.js';
19
+ export { createOutput, shouldUseColour } from './output.js';
20
+ const USAGE = `cogenta — the command line for a Cogenta site
21
+
22
+ Usage
23
+ cogenta <command> [options]
24
+
25
+ Commands
26
+ doctor Report which driver is running for each need, and why
27
+ migrate status List every migration and whether it ran here
28
+ migrate up Apply the pending migrations
29
+ migrate down Revert applied migrations
30
+ users create Create a user — the first admin account is made this way
31
+ import wordpress <file.xml> Import a WordPress WXR export, with a report
32
+ generate types Write TypeScript declarations for the content schema
33
+ skin list Show the site's active skin
34
+ skin validate <tokens.json> Check a token file against contract D
35
+ skin apply <tokens.json> Validate, then make it the active skin
36
+ skin generate --description "…" Generate a skin from a description
37
+ serve, dev Run the content and auth API over HTTP
38
+ help Show this message
39
+ version Print the version
40
+
41
+ Not built yet: build, backup, upgrade, deploy, theme, agent, and
42
+ generate schema/generate migrations — see CLAUDE.md for why each is
43
+ deferred rather than stubbed.
44
+
45
+ Options
46
+ --cwd <path> Run as if from this directory
47
+ --no-color Never colour the output (NO_COLOR is honoured too)
48
+ --verbose Send structured driver logs to stderr
49
+ --out <path> generate types: where to write the declarations
50
+ --description <text> skin generate: free text describing the site
51
+
52
+ Migration options
53
+ --to <id> Stop at this migration, inclusive
54
+ --steps <n> How many migrations "migrate down" reverts (default 1)
55
+ --confirm-destructive The impact of every destructive migration has been read
56
+ --backup-verified A backup was taken and verified to restore
57
+
58
+ User options
59
+ --email <email> The new user's email
60
+ --roles <role,role> Comma-separated role names
61
+ --admin Shorthand for --roles admin
62
+
63
+ Serve options
64
+ --port <n> Port to listen on (default 4000)
65
+ --host <host> Host to bind to (default 127.0.0.1)
66
+ `;
67
+ /**
68
+ * Runs one command and returns its exit code.
69
+ *
70
+ * Nothing here calls `process.exit` or writes to a stream directly: the streams
71
+ * are injected, so the whole CLI is testable without spawning a process or
72
+ * capturing stdout.
73
+ */
74
+ export async function run(options) {
75
+ const env = options.env ?? process.env;
76
+ const stdout = options.stdout ?? ((text) => void process.stdout.write(text));
77
+ const stderr = options.stderr ?? ((text) => void process.stderr.write(text));
78
+ let parsed;
79
+ try {
80
+ parsed = parseArgs({
81
+ args: [...options.argv],
82
+ allowPositionals: true,
83
+ strict: true,
84
+ options: {
85
+ cwd: { type: 'string' },
86
+ 'no-color': { type: 'boolean' },
87
+ verbose: { type: 'boolean' },
88
+ help: { type: 'boolean', short: 'h' },
89
+ version: { type: 'boolean', short: 'v' },
90
+ to: { type: 'string' },
91
+ steps: { type: 'string' },
92
+ 'confirm-destructive': { type: 'boolean' },
93
+ 'backup-verified': { type: 'boolean' },
94
+ email: { type: 'string' },
95
+ roles: { type: 'string' },
96
+ admin: { type: 'boolean' },
97
+ port: { type: 'string' },
98
+ host: { type: 'string' },
99
+ out: { type: 'string' },
100
+ description: { type: 'string' },
101
+ },
102
+ });
103
+ }
104
+ catch (error) {
105
+ stderr(`${error instanceof Error ? error.message : String(error)}\n\n${USAGE}`);
106
+ return 2;
107
+ }
108
+ const colour = parsed.values['no-color'] === true ? false : shouldUseColour(env, options.isTty ?? false);
109
+ const out = createOutput(stdout, colour);
110
+ const command = parsed.positionals[0] ?? (parsed.values.version === true ? 'version' : 'help');
111
+ if (parsed.values.help === true || command === 'help') {
112
+ stdout(USAGE);
113
+ return 0;
114
+ }
115
+ if (command === 'version') {
116
+ stdout(`${options.version ?? '0.0.0'}\n`);
117
+ return 0;
118
+ }
119
+ const verboseLogger = parsed.values.verbose === true
120
+ ? createLogger({ level: 'debug', destination: stderr })
121
+ : undefined;
122
+ if (command === 'migrate') {
123
+ // `--steps` is a string here because parseArgs has no number type. A
124
+ // non-number is a usage error, not a silent 0 that would revert nothing.
125
+ let steps;
126
+ if (typeof parsed.values.steps === 'string') {
127
+ steps = Number(parsed.values.steps);
128
+ if (!Number.isInteger(steps) || steps < 1) {
129
+ stderr(`--steps must be a whole number of migrations, not "${parsed.values.steps}".\n`);
130
+ return 2;
131
+ }
132
+ }
133
+ return runMigrate({
134
+ subcommand: parsed.positionals[1],
135
+ out,
136
+ stderr,
137
+ env,
138
+ ...(typeof parsed.values.cwd === 'string' ? { cwd: parsed.values.cwd } : {}),
139
+ ...(typeof parsed.values.to === 'string' ? { to: parsed.values.to } : {}),
140
+ ...(steps === undefined ? {} : { steps }),
141
+ ...(parsed.values['confirm-destructive'] === true ? { confirmDestructive: true } : {}),
142
+ ...(parsed.values['backup-verified'] === true ? { backupVerified: true } : {}),
143
+ ...(verboseLogger === undefined ? {} : { logger: verboseLogger }),
144
+ });
145
+ }
146
+ if (command === 'users') {
147
+ return runUsers({
148
+ subcommand: parsed.positionals[1],
149
+ out,
150
+ stderr,
151
+ env,
152
+ ...(typeof parsed.values.cwd === 'string' ? { cwd: parsed.values.cwd } : {}),
153
+ ...(typeof parsed.values.email === 'string' ? { email: parsed.values.email } : {}),
154
+ ...(typeof parsed.values.roles === 'string' ? { roles: parsed.values.roles } : {}),
155
+ ...(parsed.values.admin === true ? { admin: true } : {}),
156
+ ...(verboseLogger === undefined ? {} : { logger: verboseLogger }),
157
+ });
158
+ }
159
+ if (command === 'import') {
160
+ return runImport({
161
+ subcommand: parsed.positionals[1],
162
+ file: parsed.positionals[2],
163
+ out,
164
+ stderr,
165
+ env,
166
+ ...(typeof parsed.values.cwd === 'string' ? { cwd: parsed.values.cwd } : {}),
167
+ ...(verboseLogger === undefined ? {} : { logger: verboseLogger }),
168
+ });
169
+ }
170
+ if (command === 'generate') {
171
+ return runGenerate({
172
+ subcommand: parsed.positionals[1],
173
+ out,
174
+ stderr,
175
+ env,
176
+ ...(typeof parsed.values.cwd === 'string' ? { cwd: parsed.values.cwd } : {}),
177
+ ...(typeof parsed.values.out === 'string' ? { outFile: parsed.values.out } : {}),
178
+ ...(verboseLogger === undefined ? {} : { logger: verboseLogger }),
179
+ });
180
+ }
181
+ if (command === 'skin') {
182
+ return runSkin({
183
+ subcommand: parsed.positionals[1],
184
+ file: parsed.positionals[2],
185
+ out,
186
+ stderr,
187
+ env,
188
+ ...(typeof parsed.values.cwd === 'string' ? { cwd: parsed.values.cwd } : {}),
189
+ ...(typeof parsed.values.description === 'string'
190
+ ? { description: parsed.values.description }
191
+ : {}),
192
+ ...(verboseLogger === undefined ? {} : { logger: verboseLogger }),
193
+ });
194
+ }
195
+ if (command === 'serve' || command === 'dev') {
196
+ let port;
197
+ if (typeof parsed.values.port === 'string') {
198
+ port = Number(parsed.values.port);
199
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
200
+ stderr(`--port must be a whole number between 0 and 65535, not "${parsed.values.port}".\n`);
201
+ return 2;
202
+ }
203
+ }
204
+ return runServe({
205
+ out,
206
+ stderr,
207
+ env,
208
+ ...(typeof parsed.values.cwd === 'string' ? { cwd: parsed.values.cwd } : {}),
209
+ ...(port === undefined ? {} : { port }),
210
+ ...(typeof parsed.values.host === 'string' ? { host: parsed.values.host } : {}),
211
+ ...(verboseLogger === undefined ? {} : { logger: verboseLogger }),
212
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
213
+ ...(options.onListening === undefined ? {} : { onListening: options.onListening }),
214
+ });
215
+ }
216
+ if (command !== 'doctor') {
217
+ stderr(`Unknown command "${command}".\n\n${USAGE}`);
218
+ return 2;
219
+ }
220
+ try {
221
+ const report = await runDoctor({
222
+ env,
223
+ ...(typeof parsed.values.cwd === 'string' ? { cwd: parsed.values.cwd } : {}),
224
+ // Structured logs go to stderr so the report on stdout stays readable
225
+ // when it is piped.
226
+ ...(verboseLogger === undefined ? {} : { logger: verboseLogger }),
227
+ });
228
+ formatDoctorReport(report, out);
229
+ return report.problems.length === 0 ? 0 : 1;
230
+ }
231
+ catch (error) {
232
+ // Anything that reaches here is a bug in doctor itself, not a diagnosis.
233
+ if (isCogentaError(error)) {
234
+ stderr(`${error.code}: ${error.message}\n`);
235
+ if (error.hint !== undefined)
236
+ stderr(`${error.hint}\n`);
237
+ }
238
+ else {
239
+ stderr(`${error instanceof Error ? error.stack : String(error)}\n`);
240
+ }
241
+ return 1;
242
+ }
243
+ }
244
+ //# sourceMappingURL=index.js.map