kontexted 0.1.7 → 0.1.9

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.
@@ -1,4 +1,4 @@
1
- import { configExists, getDefaultConfig, saveConfig, getMigrationsDir, getPublicDir, runMigration, } from '../../lib/server/index.js';
1
+ import { configExists, getDefaultConfig, saveConfig, getMigrationsDir, getPublicDir, runMigration, validateNamingConvention, } from '../../lib/server/index.js';
2
2
  import { CONFIG_FILE, DATA_DIR } from '../../lib/server/constants.js';
3
3
  import * as readline from 'readline';
4
4
  // ============ Helper Functions ============
@@ -35,21 +35,31 @@ async function runInit(interactive) {
35
35
  const databaseUrl = urlAnswer.trim() || defaultUrl;
36
36
  const portAnswer = await promptQuestion(rl, 'Server port [4729]: ');
37
37
  const port = parseInt(portAnswer.trim(), 10) || 4729;
38
- const hostAnswer = await promptQuestion(rl, 'Server host [127.0.0.1]: ');
39
- const host = hostAnswer.trim() || '127.0.0.1';
38
+ const hostAnswer = await promptQuestion(rl, 'Server host [localhost]: ');
39
+ const host = hostAnswer.trim() || 'localhost';
40
40
  const levelAnswer = await promptQuestion(rl, 'Log level (debug/info/warn/error) [info]: ');
41
41
  const level = (levelAnswer.trim().toLowerCase() || 'info');
42
+ const namingAnswer = await promptQuestion(rl, 'Default naming convention (kebab-case/camelCase/snake_case/PascalCase) [kebab-case]: ');
43
+ const namingConvention = validateNamingConvention(namingAnswer.trim() || 'kebab-case');
42
44
  const migrationsDir = getMigrationsDir();
43
45
  const publicDir = getPublicDir();
44
46
  const defaultConfig = getDefaultConfig();
45
47
  const config = {
46
48
  database: { dialect, url: databaseUrl },
47
- server: { port, host },
49
+ server: {
50
+ port,
51
+ host,
52
+ trustedOrigins: defaultConfig.server.trustedOrigins,
53
+ },
48
54
  logging: { level },
49
55
  collab: { tokenSecret: defaultConfig.collab.tokenSecret },
50
56
  auth: {
51
57
  betterAuthSecret: defaultConfig.auth.betterAuthSecret,
52
58
  inviteCode: defaultConfig.auth.inviteCode,
59
+ method: defaultConfig.auth.method,
60
+ },
61
+ naming: {
62
+ defaultConvention: namingConvention,
53
63
  },
54
64
  paths: {
55
65
  migrationsDir: migrationsDir || undefined,
@@ -46,7 +46,7 @@ export const handler = async (argv) => {
46
46
  const pid = await startServer({ foreground: argv.foreground });
47
47
  // Load config to get host and port
48
48
  const config = loadConfig();
49
- const host = config?.server.host || '127.0.0.1';
49
+ const host = config?.server.host || 'localhost';
50
50
  const port = config?.server.port || 4729;
51
51
  const url = `http://${host}:${port}`;
52
52
  console.log(`Server started (PID: ${pid})`);
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Supported naming conventions for auto-generated names
3
+ */
4
+ export type NamingConvention = 'kebab-case' | 'camelCase' | 'snake_case' | 'PascalCase';
5
+ /**
6
+ * Validates a naming convention value
7
+ */
8
+ export declare function validateNamingConvention(value: unknown): NamingConvention;
1
9
  /**
2
10
  * Server configuration interface
3
11
  */
@@ -9,6 +17,7 @@ export interface ServerConfig {
9
17
  server: {
10
18
  port: number;
11
19
  host: string;
20
+ trustedOrigins?: string[];
12
21
  };
13
22
  logging: {
14
23
  level: 'debug' | 'info' | 'warn' | 'error';
@@ -18,7 +27,17 @@ export interface ServerConfig {
18
27
  };
19
28
  auth: {
20
29
  betterAuthSecret: string;
30
+ betterAuthUrl?: string;
21
31
  inviteCode: string;
32
+ method?: 'email-password' | 'keycloak';
33
+ keycloak?: {
34
+ clientId: string;
35
+ clientSecret: string;
36
+ issuer: string;
37
+ };
38
+ };
39
+ naming: {
40
+ defaultConvention: NamingConvention;
22
41
  };
23
42
  paths?: {
24
43
  publicDir?: string;
@@ -3,6 +3,16 @@ import { dirname } from 'path';
3
3
  import { randomBytes } from 'crypto';
4
4
  import { CONFIG_FILE, DATA_DIR, } from './constants.js';
5
5
  import { getMigrationsDir, getPublicDir } from './binary.js';
6
+ const VALID_NAMING_CONVENTIONS = ['kebab-case', 'camelCase', 'snake_case', 'PascalCase'];
7
+ /**
8
+ * Validates a naming convention value
9
+ */
10
+ export function validateNamingConvention(value) {
11
+ if (typeof value === 'string' && VALID_NAMING_CONVENTIONS.includes(value)) {
12
+ return value;
13
+ }
14
+ return 'kebab-case';
15
+ }
6
16
  /**
7
17
  * Generates a secure random token secret (32 bytes as hex string)
8
18
  */
@@ -40,7 +50,8 @@ export function getDefaultConfig() {
40
50
  },
41
51
  server: {
42
52
  port: 4729,
43
- host: '127.0.0.1',
53
+ host: 'localhost',
54
+ trustedOrigins: [],
44
55
  },
45
56
  logging: {
46
57
  level: 'info',
@@ -51,6 +62,10 @@ export function getDefaultConfig() {
51
62
  auth: {
52
63
  betterAuthSecret: generateBetterAuthSecret(),
53
64
  inviteCode: generateInviteCode(),
65
+ method: 'email-password',
66
+ },
67
+ naming: {
68
+ defaultConvention: 'kebab-case',
54
69
  },
55
70
  };
56
71
  // Add paths if platform package is available
@@ -87,14 +102,17 @@ export function loadConfig() {
87
102
  const tokenSecret = parsed.collab?.tokenSecret;
88
103
  const betterAuthSecret = parsed.auth?.betterAuthSecret;
89
104
  const inviteCode = parsed.auth?.inviteCode;
90
- return {
105
+ const config = {
91
106
  database: {
92
107
  dialect: parsed.database.dialect || 'sqlite',
93
108
  url: parsed.database.url,
94
109
  },
95
110
  server: {
96
111
  port: parsed.server.port || 4729,
97
- host: parsed.server.host || '127.0.0.1',
112
+ host: parsed.server.host || 'localhost',
113
+ ...(Array.isArray(parsed.server.trustedOrigins) && parsed.server.trustedOrigins.length > 0
114
+ ? { trustedOrigins: parsed.server.trustedOrigins }
115
+ : {}),
98
116
  },
99
117
  logging: {
100
118
  level: parsed.logging?.level || 'info',
@@ -105,12 +123,25 @@ export function loadConfig() {
105
123
  auth: {
106
124
  betterAuthSecret: betterAuthSecret || generateBetterAuthSecret(),
107
125
  inviteCode: inviteCode || generateInviteCode(),
126
+ ...(parsed.auth?.betterAuthUrl ? { betterAuthUrl: parsed.auth.betterAuthUrl } : {}),
127
+ ...(parsed.auth?.method ? { method: parsed.auth.method } : {}),
128
+ ...(parsed.auth?.keycloak ? {
129
+ keycloak: {
130
+ clientId: parsed.auth.keycloak.clientId,
131
+ clientSecret: parsed.auth.keycloak.clientSecret,
132
+ issuer: parsed.auth.keycloak.issuer,
133
+ },
134
+ } : {}),
135
+ },
136
+ naming: {
137
+ defaultConvention: validateNamingConvention(parsed.naming?.defaultConvention),
108
138
  },
109
139
  paths: {
110
140
  publicDir: parsed.paths?.publicDir,
111
141
  migrationsDir: parsed.paths?.migrationsDir,
112
142
  },
113
143
  };
144
+ return config;
114
145
  }
115
146
  catch (error) {
116
147
  console.warn('Failed to load config file:', error instanceof Error ? error.message : 'Unknown error');
@@ -2,6 +2,7 @@ import { spawn } from 'child_process';
2
2
  import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
3
3
  import { mkdirSync, closeSync, openSync } from 'fs';
4
4
  import { getBinaryPath } from './binary.js';
5
+ import { loadConfig } from './config.js';
5
6
  import { LOG_FILE, PID_FILE, LOGS_DIR, KONTEXTED_DIR } from './constants.js';
6
7
  /**
7
8
  * Ensures required directories exist
@@ -62,6 +63,33 @@ export async function startServer(options = {}) {
62
63
  throw new Error('Server binary not found. Please ensure the platform package is installed.');
63
64
  }
64
65
  ensureDirectories();
66
+ // Load config to get server host/port for Better Auth environment variables
67
+ const config = loadConfig();
68
+ const host = config?.server.host || 'localhost';
69
+ const port = config?.server.port || 4729;
70
+ const serverUrl = `http://${host}:${port}`;
71
+ // Build the trusted origins list from config or fall back to server URL
72
+ const trustedOrigins = config?.server.trustedOrigins?.length
73
+ ? config.server.trustedOrigins.join(',')
74
+ : serverUrl;
75
+ // Resolve the Better Auth URL (explicit config, or derived from host/port)
76
+ const betterAuthUrl = config?.auth.betterAuthUrl || serverUrl;
77
+ // Build environment variables for the server process
78
+ const serverEnv = {
79
+ ...process.env,
80
+ BETTER_AUTH_URL: betterAuthUrl,
81
+ BETTER_AUTH_TRUSTED_ORIGINS: process.env.BETTER_AUTH_TRUSTED_ORIGINS || trustedOrigins,
82
+ };
83
+ // Auth method configuration
84
+ if (config?.auth.method) {
85
+ serverEnv.AUTH_METHOD = config.auth.method;
86
+ }
87
+ // Keycloak OAuth configuration
88
+ if (config?.auth.keycloak) {
89
+ serverEnv.KEYCLOAK_CLIENT_ID = config.auth.keycloak.clientId;
90
+ serverEnv.KEYCLOAK_CLIENT_SECRET = config.auth.keycloak.clientSecret;
91
+ serverEnv.KEYCLOAK_ISSUER = config.auth.keycloak.issuer;
92
+ }
65
93
  // Check if already running
66
94
  const existingPid = getPid();
67
95
  if (existingPid && isRunning(existingPid)) {
@@ -74,6 +102,7 @@ export async function startServer(options = {}) {
74
102
  const child = spawn(binaryPath, [], {
75
103
  stdio: 'inherit',
76
104
  detached: false,
105
+ env: serverEnv,
77
106
  });
78
107
  child.on('error', (error) => {
79
108
  reject(new Error(`Failed to start server: ${error.message}`));
@@ -94,7 +123,7 @@ export async function startServer(options = {}) {
94
123
  const child = spawn(binaryPath, [], {
95
124
  stdio: ['ignore', logFd, logFd], // Use file descriptor, not stream
96
125
  detached: true,
97
- env: { ...process.env },
126
+ env: serverEnv,
98
127
  });
99
128
  // Close the file descriptor in parent - child has its own copy
100
129
  closeSync(logFd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kontexted",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "CLI tool for Kontexted - MCP proxy, workspaces management, and local server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,8 +43,8 @@
43
43
  "typescript": "^5.6.0"
44
44
  },
45
45
  "optionalDependencies": {
46
- "@kontexted/darwin-arm64": "0.1.7",
47
- "@kontexted/linux-x64": "0.1.7",
48
- "@kontexted/windows-x64": "0.1.7"
46
+ "@kontexted/darwin-arm64": "0.1.9",
47
+ "@kontexted/linux-x64": "0.1.9",
48
+ "@kontexted/windows-x64": "0.1.9"
49
49
  }
50
50
  }