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 [
|
|
39
|
-
const host = hostAnswer.trim() || '
|
|
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: {
|
|
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 || '
|
|
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: '
|
|
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
|
-
|
|
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 || '
|
|
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:
|
|
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.
|
|
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.
|
|
47
|
-
"@kontexted/linux-x64": "0.1.
|
|
48
|
-
"@kontexted/windows-x64": "0.1.
|
|
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
|
}
|