alif-digest 1.1.1 → 1.1.2
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/.github/workflows/alif-scheduled-run.yml +68 -0
- package/README.md +19 -1
- package/dist/cli/commands/init.d.ts +3 -1
- package/dist/cli/commands/init.js +158 -94
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/index.js +3 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config-manager.js +3 -1
- package/dist/core/config-manager.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/init.ts +165 -95
- package/src/cli/index.ts +3 -2
- package/src/core/config-manager.ts +3 -1
- package/tests/config-manager.test.ts +14 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Scheduled Run
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 8 * * *' # Example: 8:00 AM UTC daily
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
run-alif:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout Code
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Setup Node.js
|
|
16
|
+
uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 22
|
|
19
|
+
|
|
20
|
+
- name: Cache Alif Data Directory
|
|
21
|
+
uses: actions/cache@v4
|
|
22
|
+
with:
|
|
23
|
+
path: .alif-data
|
|
24
|
+
key: ${{ runner.os }}-alif-data-${{ github.run_id }}
|
|
25
|
+
restore-keys: |
|
|
26
|
+
${{ runner.os }}-alif-data-
|
|
27
|
+
|
|
28
|
+
- name: Install Alif Digest
|
|
29
|
+
run: npm install -g alif-digest
|
|
30
|
+
|
|
31
|
+
- name: Initialize Alif Configuration
|
|
32
|
+
env:
|
|
33
|
+
ALIF_CONFIG_DIR: ${{ github.workspace }}/.alif-data
|
|
34
|
+
ALIF_LLM_PROVIDER: ${{ vars.ALIF_LLM_PROVIDER || 'anthropic' }}
|
|
35
|
+
ALIF_LLM_MODEL: ${{ vars.ALIF_LLM_MODEL || '' }}
|
|
36
|
+
ALIF_LLM_BASE_URL: ${{ vars.ALIF_LLM_BASE_URL || '' }}
|
|
37
|
+
ALIF_SIGNAL_THRESHOLD: ${{ vars.ALIF_SIGNAL_THRESHOLD || '60' }}
|
|
38
|
+
ALIF_MAX_ITEMS_PER_RUN: ${{ vars.ALIF_MAX_ITEMS_PER_RUN || '10' }}
|
|
39
|
+
ALIF_SOURCE_COOLDOWN_MINUTES: ${{ vars.ALIF_SOURCE_COOLDOWN_MINUTES || '5' }}
|
|
40
|
+
ALIF_SEQUENTIAL_ANALYSIS: ${{ vars.ALIF_SEQUENTIAL_ANALYSIS || 'false' }}
|
|
41
|
+
ALIF_ENABLE_AI_SCORING: ${{ vars.ALIF_ENABLE_AI_SCORING || 'true' }}
|
|
42
|
+
ALIF_LOG_LEVEL: ${{ vars.ALIF_LOG_LEVEL || 'verbose' }}
|
|
43
|
+
ALIF_CUSTOM_KEYWORDS: ${{ vars.ALIF_CUSTOM_KEYWORDS || '{}' }}
|
|
44
|
+
ALIF_NEGATIVE_KEYWORDS: ${{ vars.ALIF_NEGATIVE_KEYWORDS || '{}' }}
|
|
45
|
+
ALIF_NO_COLOR: ${{ vars.ALIF_NO_COLOR || 'false' }}
|
|
46
|
+
|
|
47
|
+
ALIF_LLM_API_KEY: ${{ secrets.ALIF_LLM_API_KEY || '' }}
|
|
48
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY || '' }}
|
|
49
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY || '' }}
|
|
50
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL || '' }}
|
|
51
|
+
GENERIC_WEBHOOK_URL: ${{ secrets.GENERIC_WEBHOOK_URL || '' }}
|
|
52
|
+
run: alif init --non-interactive
|
|
53
|
+
|
|
54
|
+
- name: Run Alif
|
|
55
|
+
env:
|
|
56
|
+
ALIF_CONFIG_DIR: ${{ github.workspace }}/.alif-data
|
|
57
|
+
ALIF_LLM_API_KEY: ${{ secrets.ALIF_LLM_API_KEY }}
|
|
58
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
59
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
60
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
61
|
+
run: alif run
|
|
62
|
+
|
|
63
|
+
- name: Update Cache
|
|
64
|
+
if: always()
|
|
65
|
+
uses: actions/cache/save@v4
|
|
66
|
+
with:
|
|
67
|
+
path: .alif-data
|
|
68
|
+
key: ${{ runner.os }}-alif-data-${{ github.run_id }}
|
package/README.md
CHANGED
|
@@ -156,13 +156,31 @@ Config is stored at `~/.config/alif/config.json`. Most values can be changed wit
|
|
|
156
156
|
}
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
+
## ☁️ Running on GitHub Actions (Free)
|
|
160
|
+
|
|
161
|
+
You can run Alif entirely in the cloud for free using GitHub Actions, without ever installing it locally.
|
|
162
|
+
|
|
163
|
+
1. **Fork this repository.**
|
|
164
|
+
2. Navigate to your fork's **Settings > Secrets and variables > Actions**.
|
|
165
|
+
3. Add your sensitive API keys as **Repository Secrets**:
|
|
166
|
+
- `ALIF_LLM_API_KEY` (or `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)
|
|
167
|
+
- `SLACK_WEBHOOK_URL` (or `GENERIC_WEBHOOK_URL`)
|
|
168
|
+
4. Configure your preferences as **Repository Variables** (optional):
|
|
169
|
+
- `ALIF_LLM_PROVIDER` (default: anthropic)
|
|
170
|
+
- `ALIF_LLM_MODEL`
|
|
171
|
+
- `ALIF_SIGNAL_THRESHOLD` (default: 60)
|
|
172
|
+
- `ALIF_MAX_ITEMS_PER_RUN` (default: 10)
|
|
173
|
+
- `ALIF_SOURCE_COOLDOWN_MINUTES` (default: 5)
|
|
174
|
+
|
|
175
|
+
The included workflow (`.github/workflows/alif-scheduled-run.yml`) automatically runs every day at 8:00 AM UTC (feel free to change the schedule in your forked version), caches your database/state, and delivers your customized digest!
|
|
176
|
+
|
|
159
177
|
---
|
|
160
178
|
|
|
161
179
|
## 🤝 Architecture & Contributing
|
|
162
180
|
|
|
163
181
|
Interested in how Alif works under the hood or want to run it locally?
|
|
164
182
|
|
|
165
|
-
Check out our [**Contributing Guidelines**](CONTRIBUTING.md) for information on the architecture, local development setup, and how to submit pull requests!
|
|
183
|
+
Check out our [**Contributing Guidelines**](https://github.com/qaribhaider/alif/blob/main/CONTRIBUTING.md) for information on the architecture, local development setup, and how to submit pull requests!
|
|
166
184
|
|
|
167
185
|
---
|
|
168
186
|
|
|
@@ -2,106 +2,170 @@ import prompts from 'prompts';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { ConfigManager } from '../../core/config-manager.js';
|
|
4
4
|
import { logger } from '../../core/logger.js';
|
|
5
|
-
export async function initCommand() {
|
|
5
|
+
export async function initCommand(options) {
|
|
6
6
|
const configManager = ConfigManager.getInstance();
|
|
7
7
|
const configDir = configManager.getConfigDir();
|
|
8
8
|
logger.log('\n--- Alif Initialization ---');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
let config;
|
|
10
|
+
if (options?.nonInteractive) {
|
|
11
|
+
logger.info('Running in non-interactive mode. Reading from environment variables...');
|
|
12
|
+
const provider = (process.env.ALIF_LLM_PROVIDER || 'anthropic');
|
|
13
|
+
let apiKey = process.env.ALIF_LLM_API_KEY;
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
if (provider === 'anthropic')
|
|
16
|
+
apiKey = process.env.ANTHROPIC_API_KEY;
|
|
17
|
+
else if (provider === 'openrouter')
|
|
18
|
+
apiKey = process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY;
|
|
19
|
+
}
|
|
20
|
+
const model = process.env.ALIF_LLM_MODEL ||
|
|
21
|
+
(provider === 'ollama'
|
|
22
|
+
? 'llama3'
|
|
23
|
+
: provider === 'anthropic'
|
|
24
|
+
? 'claude-3-5-sonnet-20240620'
|
|
25
|
+
: 'meta-llama/llama-3-70b-instruct');
|
|
26
|
+
const baseUrl = process.env.ALIF_LLM_BASE_URL ||
|
|
27
|
+
(provider === 'ollama' ? 'http://localhost:11434' : undefined);
|
|
28
|
+
const deliveryConfigs = [];
|
|
29
|
+
if (process.env.SLACK_WEBHOOK_URL) {
|
|
30
|
+
deliveryConfigs.push({ type: 'slack', webhookUrl: process.env.SLACK_WEBHOOK_URL });
|
|
31
|
+
}
|
|
32
|
+
if (process.env.GENERIC_WEBHOOK_URL) {
|
|
33
|
+
deliveryConfigs.push({
|
|
34
|
+
type: 'webhook',
|
|
35
|
+
webhookUrl: process.env.GENERIC_WEBHOOK_URL,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
config = {
|
|
39
|
+
llm: {
|
|
40
|
+
provider,
|
|
41
|
+
apiKey,
|
|
42
|
+
model,
|
|
43
|
+
baseUrl,
|
|
35
44
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
name: 'deliveryProviders',
|
|
62
|
-
message: 'Where should we deliver the digest?',
|
|
63
|
-
choices: [
|
|
64
|
-
{ title: 'Slack', value: 'slack' },
|
|
65
|
-
{ title: 'Generic Webhook', value: 'webhook' },
|
|
66
|
-
],
|
|
67
|
-
min: 1,
|
|
68
|
-
},
|
|
69
|
-
]);
|
|
70
|
-
if (!response.llmProvider || !response.deliveryProviders) {
|
|
71
|
-
logger.warn('Initialization cancelled.');
|
|
72
|
-
return;
|
|
45
|
+
delivery: deliveryConfigs,
|
|
46
|
+
preferences: {
|
|
47
|
+
signalThreshold: process.env.ALIF_SIGNAL_THRESHOLD
|
|
48
|
+
? parseInt(process.env.ALIF_SIGNAL_THRESHOLD, 10)
|
|
49
|
+
: 60,
|
|
50
|
+
maxItemsPerRun: process.env.ALIF_MAX_ITEMS_PER_RUN
|
|
51
|
+
? parseInt(process.env.ALIF_MAX_ITEMS_PER_RUN, 10)
|
|
52
|
+
: 10,
|
|
53
|
+
sourceCooldownMinutes: process.env.ALIF_SOURCE_COOLDOWN_MINUTES
|
|
54
|
+
? parseInt(process.env.ALIF_SOURCE_COOLDOWN_MINUTES, 10)
|
|
55
|
+
: 5,
|
|
56
|
+
sequentialAnalysis: process.env.ALIF_SEQUENTIAL_ANALYSIS === 'true' || provider === 'ollama',
|
|
57
|
+
enableAIArticlesScoring: process.env.ALIF_ENABLE_AI_SCORING !== 'false',
|
|
58
|
+
customKeywords: process.env.ALIF_CUSTOM_KEYWORDS
|
|
59
|
+
? JSON.parse(process.env.ALIF_CUSTOM_KEYWORDS)
|
|
60
|
+
: {},
|
|
61
|
+
negativeKeywords: process.env.ALIF_NEGATIVE_KEYWORDS
|
|
62
|
+
? JSON.parse(process.env.ALIF_NEGATIVE_KEYWORDS)
|
|
63
|
+
: {},
|
|
64
|
+
logLevel: process.env.ALIF_LOG_LEVEL || 'normal',
|
|
65
|
+
noColor: process.env.ALIF_NO_COLOR === 'true',
|
|
66
|
+
},
|
|
67
|
+
dbPath: path.join(configDir, 'alif.db'),
|
|
68
|
+
feedsPath: path.join(configDir, 'feeds.json'),
|
|
69
|
+
};
|
|
73
70
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
else {
|
|
72
|
+
const response = await prompts([
|
|
73
|
+
{
|
|
74
|
+
type: 'select',
|
|
75
|
+
name: 'llmProvider',
|
|
76
|
+
message: 'Which LLM provider would you like to use?',
|
|
77
|
+
choices: [
|
|
78
|
+
{ title: 'Ollama (Local)', value: 'ollama' },
|
|
79
|
+
{ title: 'Anthropic', value: 'anthropic' },
|
|
80
|
+
{ title: 'OpenRouter', value: 'openrouter' },
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: (prev) => (prev !== 'ollama' ? 'text' : null),
|
|
85
|
+
name: 'apiKey',
|
|
86
|
+
message: 'Enter your API Key:',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: 'text',
|
|
90
|
+
name: 'model',
|
|
91
|
+
message: 'Enter the model name (e.g., llama3, claude-3-opus-20240229):',
|
|
92
|
+
initial: (prev, values) => {
|
|
93
|
+
if (values.llmProvider === 'ollama')
|
|
94
|
+
return 'llama3';
|
|
95
|
+
if (values.llmProvider === 'anthropic')
|
|
96
|
+
return 'claude-3-5-sonnet-20240620';
|
|
97
|
+
return 'meta-llama/llama-3-70b-instruct';
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
type: (prev, values) => (values.llmProvider === 'ollama' ? 'text' : null),
|
|
102
|
+
name: 'baseUrl',
|
|
103
|
+
message: 'Enter Ollama base URL:',
|
|
104
|
+
initial: 'http://localhost:11434',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
type: 'toggle',
|
|
108
|
+
name: 'sequentialAnalysis',
|
|
109
|
+
message: 'Enable sequential (one-by-one) LLM processing? (Recommended for small local models)',
|
|
110
|
+
initial: (prev, values) => values.llmProvider === 'ollama',
|
|
111
|
+
active: 'yes',
|
|
112
|
+
inactive: 'no',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'toggle',
|
|
116
|
+
name: 'enableAIArticlesScoring',
|
|
117
|
+
message: 'Enable AI Article Scoring? (Uses your LLM to intelligently score articles)',
|
|
118
|
+
initial: true,
|
|
119
|
+
active: 'yes',
|
|
120
|
+
inactive: 'no',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'multiselect',
|
|
124
|
+
name: 'deliveryProviders',
|
|
125
|
+
message: 'Where should we deliver the digest?',
|
|
126
|
+
choices: [
|
|
127
|
+
{ title: 'Slack', value: 'slack' },
|
|
128
|
+
{ title: 'Generic Webhook', value: 'webhook' },
|
|
129
|
+
],
|
|
130
|
+
min: 1,
|
|
131
|
+
},
|
|
132
|
+
]);
|
|
133
|
+
if (!response.llmProvider || !response.deliveryProviders) {
|
|
134
|
+
logger.warn('Initialization cancelled.');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const deliveryConfigs = [];
|
|
138
|
+
for (const provider of response.deliveryProviders) {
|
|
139
|
+
const { webhookUrl } = await prompts({
|
|
140
|
+
type: 'text',
|
|
141
|
+
name: 'webhookUrl',
|
|
142
|
+
message: `Enter ${provider} Webhook URL:`,
|
|
143
|
+
});
|
|
144
|
+
deliveryConfigs.push({ type: provider, webhookUrl });
|
|
145
|
+
}
|
|
146
|
+
config = {
|
|
147
|
+
llm: {
|
|
148
|
+
provider: response.llmProvider,
|
|
149
|
+
apiKey: response.apiKey,
|
|
150
|
+
model: response.model,
|
|
151
|
+
baseUrl: response.baseUrl,
|
|
152
|
+
},
|
|
153
|
+
delivery: deliveryConfigs,
|
|
154
|
+
preferences: {
|
|
155
|
+
signalThreshold: 60,
|
|
156
|
+
maxItemsPerRun: 10,
|
|
157
|
+
sourceCooldownMinutes: 5,
|
|
158
|
+
sequentialAnalysis: response.sequentialAnalysis,
|
|
159
|
+
enableAIArticlesScoring: response.enableAIArticlesScoring,
|
|
160
|
+
customKeywords: {},
|
|
161
|
+
negativeKeywords: {},
|
|
162
|
+
logLevel: 'normal',
|
|
163
|
+
noColor: false,
|
|
164
|
+
},
|
|
165
|
+
dbPath: path.join(configDir, 'alif.db'),
|
|
166
|
+
feedsPath: path.join(configDir, 'feeds.json'),
|
|
167
|
+
};
|
|
82
168
|
}
|
|
83
|
-
const config = {
|
|
84
|
-
llm: {
|
|
85
|
-
provider: response.llmProvider,
|
|
86
|
-
apiKey: response.apiKey,
|
|
87
|
-
model: response.model,
|
|
88
|
-
baseUrl: response.baseUrl,
|
|
89
|
-
},
|
|
90
|
-
delivery: deliveryConfigs,
|
|
91
|
-
preferences: {
|
|
92
|
-
signalThreshold: 60,
|
|
93
|
-
maxItemsPerRun: 10,
|
|
94
|
-
sourceCooldownMinutes: 5,
|
|
95
|
-
sequentialAnalysis: response.sequentialAnalysis,
|
|
96
|
-
enableAIArticlesScoring: response.enableAIArticlesScoring,
|
|
97
|
-
customKeywords: {},
|
|
98
|
-
negativeKeywords: {},
|
|
99
|
-
logLevel: 'normal',
|
|
100
|
-
noColor: false,
|
|
101
|
-
},
|
|
102
|
-
dbPath: path.join(configDir, 'alif.db'),
|
|
103
|
-
feedsPath: path.join(configDir, 'feeds.json'),
|
|
104
|
-
};
|
|
105
169
|
configManager.save(config);
|
|
106
170
|
logger.success(`Configuration saved to ${configManager.getConfigFile()}`);
|
|
107
171
|
logger.info(`Database will be located at ${config.dbPath}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,WAAW;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -14,8 +14,9 @@ program.name('alif').description('Alif - Daily AI Signal Digest CLI').version(ve
|
|
|
14
14
|
program
|
|
15
15
|
.command('init')
|
|
16
16
|
.description('Initialize Alif configuration')
|
|
17
|
-
.
|
|
18
|
-
|
|
17
|
+
.option('--non-interactive', 'Run without interactive prompts using environment variables')
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
await initCommand(options);
|
|
19
20
|
});
|
|
20
21
|
program
|
|
21
22
|
.command('run')
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,mCAAmC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvF,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,+BAA+B,CAAC;KAC5C,MAAM,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,mCAAmC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvF,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,+BAA+B,CAAC;KAC5C,MAAM,CAAC,mBAAmB,EAAE,6DAA6D,CAAC;KAC1F,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,aAAa,EAAE,wBAAwB,CAAC;KAC/C,MAAM,CAAC,eAAe,EAAE,4BAA4B,CAAC;KACrD,MAAM,CAAC,aAAa,EAAE,mCAAmC,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,mBAAmB,CAAC;KAC5B,WAAW,CAAC,yBAAyB,CAAC;KACtC,WAAW,CAAC,OAAO,EAAE,qCAAqC,CAAC;KAC3D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;IACvB,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,eAAe,EAAE,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -8,7 +8,9 @@ export class ConfigManager {
|
|
|
8
8
|
configDir;
|
|
9
9
|
configFile;
|
|
10
10
|
constructor() {
|
|
11
|
-
this.configDir =
|
|
11
|
+
this.configDir = process.env.ALIF_CONFIG_DIR
|
|
12
|
+
? path.resolve(process.env.ALIF_CONFIG_DIR)
|
|
13
|
+
: path.join(os.homedir(), '.config', 'alif');
|
|
12
14
|
this.configFile = path.join(this.configDir, 'config.json');
|
|
13
15
|
}
|
|
14
16
|
static getInstance() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/core/config-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAU,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,OAAO,aAAa;IAChB,MAAM,CAAC,QAAQ,CAAgB;IAC/B,MAAM,GAAkB,IAAI,CAAC;IAC7B,SAAS,CAAS;IAClB,UAAU,CAAS;IAE3B;QACE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/core/config-manager.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli/commands/init.ts
CHANGED
|
@@ -4,110 +4,180 @@ import { ConfigManager } from '../../core/config-manager.js';
|
|
|
4
4
|
import { Config } from '../../core/config-schema.js';
|
|
5
5
|
import { logger } from '../../core/logger.js';
|
|
6
6
|
|
|
7
|
-
export async function initCommand() {
|
|
7
|
+
export async function initCommand(options?: { nonInteractive?: boolean }) {
|
|
8
8
|
const configManager = ConfigManager.getInstance();
|
|
9
9
|
const configDir = configManager.getConfigDir();
|
|
10
10
|
|
|
11
11
|
logger.log('\n--- Alif Initialization ---');
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
13
|
+
let config: Config;
|
|
14
|
+
|
|
15
|
+
if (options?.nonInteractive) {
|
|
16
|
+
logger.info('Running in non-interactive mode. Reading from environment variables...');
|
|
17
|
+
|
|
18
|
+
const provider = (process.env.ALIF_LLM_PROVIDER || 'anthropic') as any;
|
|
19
|
+
let apiKey = process.env.ALIF_LLM_API_KEY;
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
if (provider === 'anthropic') apiKey = process.env.ANTHROPIC_API_KEY;
|
|
22
|
+
else if (provider === 'openrouter')
|
|
23
|
+
apiKey = process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const model =
|
|
27
|
+
process.env.ALIF_LLM_MODEL ||
|
|
28
|
+
(provider === 'ollama'
|
|
29
|
+
? 'llama3'
|
|
30
|
+
: provider === 'anthropic'
|
|
31
|
+
? 'claude-3-5-sonnet-20240620'
|
|
32
|
+
: 'meta-llama/llama-3-70b-instruct');
|
|
33
|
+
const baseUrl =
|
|
34
|
+
process.env.ALIF_LLM_BASE_URL ||
|
|
35
|
+
(provider === 'ollama' ? 'http://localhost:11434' : undefined);
|
|
36
|
+
|
|
37
|
+
const deliveryConfigs = [];
|
|
38
|
+
if (process.env.SLACK_WEBHOOK_URL) {
|
|
39
|
+
deliveryConfigs.push({ type: 'slack' as const, webhookUrl: process.env.SLACK_WEBHOOK_URL });
|
|
40
|
+
}
|
|
41
|
+
if (process.env.GENERIC_WEBHOOK_URL) {
|
|
42
|
+
deliveryConfigs.push({
|
|
43
|
+
type: 'webhook' as const,
|
|
44
|
+
webhookUrl: process.env.GENERIC_WEBHOOK_URL,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
config = {
|
|
49
|
+
llm: {
|
|
50
|
+
provider,
|
|
51
|
+
apiKey,
|
|
52
|
+
model,
|
|
53
|
+
baseUrl,
|
|
54
|
+
},
|
|
55
|
+
delivery: deliveryConfigs,
|
|
56
|
+
preferences: {
|
|
57
|
+
signalThreshold: process.env.ALIF_SIGNAL_THRESHOLD
|
|
58
|
+
? parseInt(process.env.ALIF_SIGNAL_THRESHOLD, 10)
|
|
59
|
+
: 60,
|
|
60
|
+
maxItemsPerRun: process.env.ALIF_MAX_ITEMS_PER_RUN
|
|
61
|
+
? parseInt(process.env.ALIF_MAX_ITEMS_PER_RUN, 10)
|
|
62
|
+
: 10,
|
|
63
|
+
sourceCooldownMinutes: process.env.ALIF_SOURCE_COOLDOWN_MINUTES
|
|
64
|
+
? parseInt(process.env.ALIF_SOURCE_COOLDOWN_MINUTES, 10)
|
|
65
|
+
: 5,
|
|
66
|
+
sequentialAnalysis:
|
|
67
|
+
process.env.ALIF_SEQUENTIAL_ANALYSIS === 'true' || provider === 'ollama',
|
|
68
|
+
enableAIArticlesScoring: process.env.ALIF_ENABLE_AI_SCORING !== 'false',
|
|
69
|
+
customKeywords: process.env.ALIF_CUSTOM_KEYWORDS
|
|
70
|
+
? JSON.parse(process.env.ALIF_CUSTOM_KEYWORDS)
|
|
71
|
+
: {},
|
|
72
|
+
negativeKeywords: process.env.ALIF_NEGATIVE_KEYWORDS
|
|
73
|
+
? JSON.parse(process.env.ALIF_NEGATIVE_KEYWORDS)
|
|
74
|
+
: {},
|
|
75
|
+
logLevel: (process.env.ALIF_LOG_LEVEL as any) || 'normal',
|
|
76
|
+
noColor: process.env.ALIF_NO_COLOR === 'true',
|
|
77
|
+
},
|
|
78
|
+
dbPath: path.join(configDir, 'alif.db'),
|
|
79
|
+
feedsPath: path.join(configDir, 'feeds.json'),
|
|
80
|
+
};
|
|
81
|
+
} else {
|
|
82
|
+
const response = await prompts([
|
|
83
|
+
{
|
|
84
|
+
type: 'select',
|
|
85
|
+
name: 'llmProvider',
|
|
86
|
+
message: 'Which LLM provider would you like to use?',
|
|
87
|
+
choices: [
|
|
88
|
+
{ title: 'Ollama (Local)', value: 'ollama' },
|
|
89
|
+
{ title: 'Anthropic', value: 'anthropic' },
|
|
90
|
+
{ title: 'OpenRouter', value: 'openrouter' },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: (prev) => (prev !== 'ollama' ? 'text' : null),
|
|
95
|
+
name: 'apiKey',
|
|
96
|
+
message: 'Enter your API Key:',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'text',
|
|
100
|
+
name: 'model',
|
|
101
|
+
message: 'Enter the model name (e.g., llama3, claude-3-opus-20240229):',
|
|
102
|
+
initial: (prev, values) => {
|
|
103
|
+
if (values.llmProvider === 'ollama') return 'llama3';
|
|
104
|
+
if (values.llmProvider === 'anthropic') return 'claude-3-5-sonnet-20240620';
|
|
105
|
+
return 'meta-llama/llama-3-70b-instruct';
|
|
106
|
+
},
|
|
37
107
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
]);
|
|
108
|
+
{
|
|
109
|
+
type: (prev, values) => (values.llmProvider === 'ollama' ? 'text' : null),
|
|
110
|
+
name: 'baseUrl',
|
|
111
|
+
message: 'Enter Ollama base URL:',
|
|
112
|
+
initial: 'http://localhost:11434',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'toggle',
|
|
116
|
+
name: 'sequentialAnalysis',
|
|
117
|
+
message:
|
|
118
|
+
'Enable sequential (one-by-one) LLM processing? (Recommended for small local models)',
|
|
119
|
+
initial: (prev, values) => values.llmProvider === 'ollama',
|
|
120
|
+
active: 'yes',
|
|
121
|
+
inactive: 'no',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
type: 'toggle',
|
|
125
|
+
name: 'enableAIArticlesScoring',
|
|
126
|
+
message: 'Enable AI Article Scoring? (Uses your LLM to intelligently score articles)',
|
|
127
|
+
initial: true,
|
|
128
|
+
active: 'yes',
|
|
129
|
+
inactive: 'no',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
type: 'multiselect',
|
|
133
|
+
name: 'deliveryProviders',
|
|
134
|
+
message: 'Where should we deliver the digest?',
|
|
135
|
+
choices: [
|
|
136
|
+
{ title: 'Slack', value: 'slack' },
|
|
137
|
+
{ title: 'Generic Webhook', value: 'webhook' },
|
|
138
|
+
],
|
|
139
|
+
min: 1,
|
|
140
|
+
},
|
|
141
|
+
]);
|
|
73
142
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
143
|
+
if (!response.llmProvider || !response.deliveryProviders) {
|
|
144
|
+
logger.warn('Initialization cancelled.');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
78
147
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
148
|
+
const deliveryConfigs = [];
|
|
149
|
+
for (const provider of response.deliveryProviders) {
|
|
150
|
+
const { webhookUrl } = await prompts({
|
|
151
|
+
type: 'text',
|
|
152
|
+
name: 'webhookUrl',
|
|
153
|
+
message: `Enter ${provider} Webhook URL:`,
|
|
154
|
+
});
|
|
155
|
+
deliveryConfigs.push({ type: provider, webhookUrl });
|
|
156
|
+
}
|
|
88
157
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
158
|
+
config = {
|
|
159
|
+
llm: {
|
|
160
|
+
provider: response.llmProvider,
|
|
161
|
+
apiKey: response.apiKey,
|
|
162
|
+
model: response.model,
|
|
163
|
+
baseUrl: response.baseUrl,
|
|
164
|
+
},
|
|
165
|
+
delivery: deliveryConfigs,
|
|
166
|
+
preferences: {
|
|
167
|
+
signalThreshold: 60,
|
|
168
|
+
maxItemsPerRun: 10,
|
|
169
|
+
sourceCooldownMinutes: 5,
|
|
170
|
+
sequentialAnalysis: response.sequentialAnalysis,
|
|
171
|
+
enableAIArticlesScoring: response.enableAIArticlesScoring,
|
|
172
|
+
customKeywords: {},
|
|
173
|
+
negativeKeywords: {},
|
|
174
|
+
logLevel: 'normal' as const,
|
|
175
|
+
noColor: false,
|
|
176
|
+
},
|
|
177
|
+
dbPath: path.join(configDir, 'alif.db'),
|
|
178
|
+
feedsPath: path.join(configDir, 'feeds.json'),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
111
181
|
|
|
112
182
|
configManager.save(config);
|
|
113
183
|
|
package/src/cli/index.ts
CHANGED
|
@@ -18,8 +18,9 @@ program.name('alif').description('Alif - Daily AI Signal Digest CLI').version(ve
|
|
|
18
18
|
program
|
|
19
19
|
.command('init')
|
|
20
20
|
.description('Initialize Alif configuration')
|
|
21
|
-
.
|
|
22
|
-
|
|
21
|
+
.option('--non-interactive', 'Run without interactive prompts using environment variables')
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
await initCommand(options);
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
program
|
|
@@ -10,7 +10,9 @@ export class ConfigManager {
|
|
|
10
10
|
private configFile: string;
|
|
11
11
|
|
|
12
12
|
private constructor() {
|
|
13
|
-
this.configDir =
|
|
13
|
+
this.configDir = process.env.ALIF_CONFIG_DIR
|
|
14
|
+
? path.resolve(process.env.ALIF_CONFIG_DIR)
|
|
15
|
+
: path.join(os.homedir(), '.config', 'alif');
|
|
14
16
|
this.configFile = path.join(this.configDir, 'config.json');
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -46,6 +46,20 @@ describe('ConfigManager', () => {
|
|
|
46
46
|
expect(manager.getConfigFile()).toBe(mockConfigFile);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
it('should override config path using ALIF_CONFIG_DIR', () => {
|
|
50
|
+
// @ts-expect-error - reset singleton for tests
|
|
51
|
+
ConfigManager['instance'] = undefined;
|
|
52
|
+
process.env.ALIF_CONFIG_DIR = '/custom/config/path';
|
|
53
|
+
|
|
54
|
+
const manager = ConfigManager.getInstance();
|
|
55
|
+
expect(manager.getConfigDir()).toBe(path.resolve('/custom/config/path'));
|
|
56
|
+
expect(manager.getConfigFile()).toBe(
|
|
57
|
+
path.join(path.resolve('/custom/config/path'), 'config.json'),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
delete process.env.ALIF_CONFIG_DIR;
|
|
61
|
+
});
|
|
62
|
+
|
|
49
63
|
it('should throw if loading non-existent config', () => {
|
|
50
64
|
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
51
65
|
const manager = ConfigManager.getInstance();
|