@narrative-os/cli 0.1.5 ā 0.1.7
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.
- package/dist/commands/config.d.ts +14 -1
- package/dist/commands/config.js +114 -28
- package/package.json +2 -2
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
interface
|
|
1
|
+
interface ModelConfig {
|
|
2
|
+
name: string;
|
|
2
3
|
provider: 'openai' | 'deepseek';
|
|
3
4
|
apiKey: string;
|
|
5
|
+
baseURL?: string;
|
|
4
6
|
model: string;
|
|
7
|
+
purpose: 'reasoning' | 'chat' | 'fast';
|
|
5
8
|
}
|
|
9
|
+
interface MultiModelConfig {
|
|
10
|
+
models: ModelConfig[];
|
|
11
|
+
defaultModel: string;
|
|
12
|
+
}
|
|
13
|
+
interface LegacyConfig {
|
|
14
|
+
provider: 'openai' | 'deepseek';
|
|
15
|
+
apiKey: string;
|
|
16
|
+
model: string;
|
|
17
|
+
}
|
|
18
|
+
type Config = LegacyConfig | MultiModelConfig;
|
|
6
19
|
export declare function configCommand(showOnly?: boolean): Promise<void>;
|
|
7
20
|
export declare function getConfig(): Config | null;
|
|
8
21
|
export declare function applyConfig(): void;
|
package/dist/commands/config.js
CHANGED
|
@@ -12,10 +12,6 @@ const PROVIDERS = [
|
|
|
12
12
|
{ name: 'OpenAI', value: 'openai', models: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo'] },
|
|
13
13
|
{ name: 'DeepSeek', value: 'deepseek', models: ['deepseek-chat', 'deepseek-reasoner'] },
|
|
14
14
|
];
|
|
15
|
-
const DEEPSEEK_MODEL_MAP = {
|
|
16
|
-
'deepseek-chat': 'deepseek-chat',
|
|
17
|
-
'deepseek-reasoner': 'deepseek-reasoner',
|
|
18
|
-
};
|
|
19
15
|
function loadConfig() {
|
|
20
16
|
if (!(0, fs_1.existsSync)(CONFIG_FILE))
|
|
21
17
|
return null;
|
|
@@ -31,6 +27,9 @@ function saveConfig(config) {
|
|
|
31
27
|
(0, fs_1.mkdirSync)(CONFIG_DIR, { recursive: true });
|
|
32
28
|
(0, fs_1.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
33
29
|
}
|
|
30
|
+
function isMultiModelConfig(config) {
|
|
31
|
+
return 'models' in config && Array.isArray(config.models);
|
|
32
|
+
}
|
|
34
33
|
async function configCommand(showOnly = false) {
|
|
35
34
|
const existing = loadConfig();
|
|
36
35
|
// Show current configuration
|
|
@@ -42,41 +41,128 @@ async function configCommand(showOnly = false) {
|
|
|
42
41
|
}
|
|
43
42
|
console.log('\nš Current Configuration:');
|
|
44
43
|
console.log('========================');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
if (isMultiModelConfig(existing)) {
|
|
45
|
+
console.log(`Mode: Multi-Model (${existing.models.length} models configured)`);
|
|
46
|
+
console.log('');
|
|
47
|
+
for (const model of existing.models) {
|
|
48
|
+
const apiKeyDisplay = model.apiKey
|
|
49
|
+
? 'ā
Set (' + '*'.repeat(Math.min(model.apiKey.length - 4, 8)) + model.apiKey.slice(-4) + ')'
|
|
50
|
+
: 'ā Not set';
|
|
51
|
+
console.log(` [${model.purpose.toUpperCase()}] ${model.name}`);
|
|
52
|
+
console.log(` Provider: ${model.provider}`);
|
|
53
|
+
console.log(` Model: ${model.model}`);
|
|
54
|
+
console.log(` API Key: ${apiKeyDisplay}`);
|
|
55
|
+
console.log('');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
console.log(`Mode: Single Model`);
|
|
60
|
+
console.log(`Provider: ${existing.provider}`);
|
|
61
|
+
console.log(`Model: ${existing.model}`);
|
|
62
|
+
console.log(`API Key: ${existing.apiKey ? 'ā
Set (' + '*'.repeat(Math.min(existing.apiKey.length - 4, 8)) + existing.apiKey.slice(-4) + ')' : 'ā Not set'}`);
|
|
63
|
+
}
|
|
49
64
|
console.log(`Config file: ${CONFIG_FILE}`);
|
|
50
65
|
return;
|
|
51
66
|
}
|
|
52
67
|
// Interactive configuration
|
|
53
|
-
const { select, password } = await import('@inquirer/prompts');
|
|
54
|
-
const
|
|
55
|
-
message: '
|
|
56
|
-
|
|
57
|
-
default: existing?.provider,
|
|
58
|
-
});
|
|
59
|
-
const providerInfo = PROVIDERS.find(p => p.value === provider);
|
|
60
|
-
const model = await select({
|
|
61
|
-
message: 'Select model:',
|
|
62
|
-
choices: providerInfo.models.map(m => ({ name: m, value: m })),
|
|
63
|
-
default: existing?.model || providerInfo.models[0],
|
|
64
|
-
});
|
|
65
|
-
const apiKey = await password({
|
|
66
|
-
message: `Enter ${providerInfo.name} API key:`,
|
|
67
|
-
mask: '*',
|
|
68
|
+
const { select, password, confirm } = await import('@inquirer/prompts');
|
|
69
|
+
const useMultiModel = await confirm({
|
|
70
|
+
message: 'Configure multiple models (reasoning + chat)?',
|
|
71
|
+
default: true,
|
|
68
72
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
if (useMultiModel) {
|
|
74
|
+
// Multi-model configuration
|
|
75
|
+
const provider = await select({
|
|
76
|
+
message: 'Select LLM provider:',
|
|
77
|
+
choices: PROVIDERS.map(p => ({ name: p.name, value: p.value })),
|
|
78
|
+
});
|
|
79
|
+
const providerInfo = PROVIDERS.find(p => p.value === provider);
|
|
80
|
+
const apiKey = await password({
|
|
81
|
+
message: `Enter ${providerInfo.name} API key:`,
|
|
82
|
+
mask: '*',
|
|
83
|
+
});
|
|
84
|
+
const reasoningModel = await select({
|
|
85
|
+
message: 'Select REASONING model (for generation/planning):',
|
|
86
|
+
choices: providerInfo.models.map(m => ({ name: m, value: m })),
|
|
87
|
+
default: provider === 'deepseek' ? 'deepseek-reasoner' : 'gpt-4o',
|
|
88
|
+
});
|
|
89
|
+
const chatModel = await select({
|
|
90
|
+
message: 'Select CHAT model (for validation/summarization):',
|
|
91
|
+
choices: providerInfo.models.map(m => ({ name: m, value: m })),
|
|
92
|
+
default: provider === 'deepseek' ? 'deepseek-chat' : 'gpt-4o-mini',
|
|
93
|
+
});
|
|
94
|
+
const baseURL = provider === 'deepseek' ? 'https://api.deepseek.com' : undefined;
|
|
95
|
+
const config = {
|
|
96
|
+
models: [
|
|
97
|
+
{
|
|
98
|
+
name: 'reasoning',
|
|
99
|
+
provider: provider,
|
|
100
|
+
apiKey,
|
|
101
|
+
baseURL,
|
|
102
|
+
model: reasoningModel,
|
|
103
|
+
purpose: 'reasoning',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'chat',
|
|
107
|
+
provider: provider,
|
|
108
|
+
apiKey,
|
|
109
|
+
baseURL,
|
|
110
|
+
model: chatModel,
|
|
111
|
+
purpose: 'chat',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
defaultModel: 'chat',
|
|
115
|
+
};
|
|
116
|
+
saveConfig(config);
|
|
117
|
+
console.log(`\nā
Multi-model configuration saved!`);
|
|
118
|
+
console.log(` Reasoning: ${reasoningModel}`);
|
|
119
|
+
console.log(` Chat: ${chatModel}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Single model configuration (legacy)
|
|
123
|
+
const provider = await select({
|
|
124
|
+
message: 'Select LLM provider:',
|
|
125
|
+
choices: PROVIDERS.map(p => ({ name: p.name, value: p.value })),
|
|
126
|
+
default: existing?.provider,
|
|
127
|
+
});
|
|
128
|
+
const providerInfo = PROVIDERS.find(p => p.value === provider);
|
|
129
|
+
const model = await select({
|
|
130
|
+
message: 'Select model:',
|
|
131
|
+
choices: providerInfo.models.map(m => ({ name: m, value: m })),
|
|
132
|
+
default: existing?.model || providerInfo.models[0],
|
|
133
|
+
});
|
|
134
|
+
const apiKey = await password({
|
|
135
|
+
message: `Enter ${providerInfo.name} API key:`,
|
|
136
|
+
mask: '*',
|
|
137
|
+
});
|
|
138
|
+
const config = { provider: provider, model, apiKey };
|
|
139
|
+
saveConfig(config);
|
|
140
|
+
console.log(`\nā
Configuration saved for ${providerInfo.name}`);
|
|
141
|
+
console.log(`Model: ${model}`);
|
|
142
|
+
}
|
|
73
143
|
}
|
|
74
144
|
function getConfig() {
|
|
75
145
|
return loadConfig();
|
|
76
146
|
}
|
|
77
147
|
function applyConfig() {
|
|
78
148
|
const config = loadConfig();
|
|
79
|
-
if (config)
|
|
149
|
+
if (!config)
|
|
150
|
+
return;
|
|
151
|
+
if (isMultiModelConfig(config)) {
|
|
152
|
+
// Set multi-model config as environment variable
|
|
153
|
+
process.env.LLM_MODELS_CONFIG = JSON.stringify(config);
|
|
154
|
+
// Also set individual API keys for backward compatibility
|
|
155
|
+
for (const model of config.models) {
|
|
156
|
+
if (model.provider === 'openai') {
|
|
157
|
+
process.env.OPENAI_API_KEY = model.apiKey;
|
|
158
|
+
}
|
|
159
|
+
else if (model.provider === 'deepseek') {
|
|
160
|
+
process.env.DEEPSEEK_API_KEY = model.apiKey;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// Legacy single-model config
|
|
80
166
|
process.env.LLM_PROVIDER = config.provider;
|
|
81
167
|
process.env.LLM_MODEL = config.model;
|
|
82
168
|
if (config.provider === 'openai') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narrative-os/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "AI-native narrative engine for long-form story generation",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@inquirer/prompts": "^8.3.0",
|
|
42
|
-
"@narrative-os/engine": "0.1.
|
|
42
|
+
"@narrative-os/engine": "0.1.4",
|
|
43
43
|
"commander": "^12.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|