@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.
@@ -1,8 +1,21 @@
1
- interface Config {
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;
@@ -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
- console.log(`Provider: ${existing.provider}`);
46
- console.log(`Model: ${existing.model}`);
47
- console.log(`API Key: ${existing.apiKey ? 'āœ… Set (' + '*'.repeat(Math.min(existing.apiKey.length - 4, 8)) + existing.apiKey.slice(-4) + ')' : 'āŒ Not set'}`);
48
- console.log('');
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 provider = await select({
55
- message: 'Select LLM provider:',
56
- choices: PROVIDERS.map(p => ({ name: p.name, value: p.value })),
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
- const config = { provider: provider, model, apiKey };
70
- saveConfig(config);
71
- console.log(`\nāœ… Configuration saved for ${providerInfo.name}`);
72
- console.log(`Model: ${model}`);
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.5",
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.3",
42
+ "@narrative-os/engine": "0.1.4",
43
43
  "commander": "^12.0.0"
44
44
  },
45
45
  "devDependencies": {