alif-digest 1.0.1
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/publish.yml +33 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +88 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/run.d.ts +4 -0
- package/dist/cli/commands/run.js +46 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/schedule.d.ts +1 -0
- package/dist/cli/commands/schedule.js +94 -0
- package/dist/cli/commands/schedule.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +29 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/config-manager.d.ts +14 -0
- package/dist/core/config-manager.js +65 -0
- package/dist/core/config-manager.js.map +1 -0
- package/dist/core/config-schema.d.ts +40 -0
- package/dist/core/config-schema.js +24 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/default-keywords.d.ts +1 -0
- package/dist/core/default-keywords.js +10 -0
- package/dist/core/default-keywords.js.map +1 -0
- package/dist/core/filters/deduplicator.d.ts +10 -0
- package/dist/core/filters/deduplicator.js +34 -0
- package/dist/core/filters/deduplicator.js.map +1 -0
- package/dist/core/filters/keywords.d.ts +6 -0
- package/dist/core/filters/keywords.js +17 -0
- package/dist/core/filters/keywords.js.map +1 -0
- package/dist/core/orchestrator.d.ts +6 -0
- package/dist/core/orchestrator.js +44 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/pipeline.d.ts +15 -0
- package/dist/core/pipeline.js +140 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/scheduler.d.ts +9 -0
- package/dist/core/scheduler.js +64 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/scraper-types.d.ts +27 -0
- package/dist/core/scraper-types.js +3 -0
- package/dist/core/scraper-types.js.map +1 -0
- package/dist/core/scrapers/api-scraper.d.ts +4 -0
- package/dist/core/scrapers/api-scraper.js +46 -0
- package/dist/core/scrapers/api-scraper.js.map +1 -0
- package/dist/core/scrapers/arxiv-scraper.d.ts +4 -0
- package/dist/core/scrapers/arxiv-scraper.js +34 -0
- package/dist/core/scrapers/arxiv-scraper.js.map +1 -0
- package/dist/core/scrapers/json-scraper.d.ts +4 -0
- package/dist/core/scrapers/json-scraper.js +56 -0
- package/dist/core/scrapers/json-scraper.js.map +1 -0
- package/dist/core/scrapers/rss-scraper.d.ts +6 -0
- package/dist/core/scrapers/rss-scraper.js +32 -0
- package/dist/core/scrapers/rss-scraper.js.map +1 -0
- package/dist/core/scrapers/scrape-scraper.d.ts +4 -0
- package/dist/core/scrapers/scrape-scraper.js +49 -0
- package/dist/core/scrapers/scrape-scraper.js.map +1 -0
- package/dist/db/article-store.d.ts +22 -0
- package/dist/db/article-store.js +43 -0
- package/dist/db/article-store.js.map +1 -0
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.js +15 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrate.d.ts +2 -0
- package/dist/db/migrate.js +60 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schedule-store.d.ts +17 -0
- package/dist/db/schedule-store.js +23 -0
- package/dist/db/schedule-store.js.map +1 -0
- package/dist/db/source-health-store.d.ts +16 -0
- package/dist/db/source-health-store.js +31 -0
- package/dist/db/source-health-store.js.map +1 -0
- package/dist/providers/delivery/index.d.ts +18 -0
- package/dist/providers/delivery/index.js +2 -0
- package/dist/providers/delivery/index.js.map +1 -0
- package/dist/providers/delivery/slack.d.ts +6 -0
- package/dist/providers/delivery/slack.js +52 -0
- package/dist/providers/delivery/slack.js.map +1 -0
- package/dist/providers/delivery/webhook.d.ts +6 -0
- package/dist/providers/delivery/webhook.js +16 -0
- package/dist/providers/delivery/webhook.js.map +1 -0
- package/dist/providers/factory.d.ts +7 -0
- package/dist/providers/factory.js +33 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/llm/anthropic.d.ts +12 -0
- package/dist/providers/llm/anthropic.js +43 -0
- package/dist/providers/llm/anthropic.js.map +1 -0
- package/dist/providers/llm/index.d.ts +10 -0
- package/dist/providers/llm/index.js +2 -0
- package/dist/providers/llm/index.js.map +1 -0
- package/dist/providers/llm/ollama.d.ts +12 -0
- package/dist/providers/llm/ollama.js +42 -0
- package/dist/providers/llm/ollama.js.map +1 -0
- package/dist/providers/llm/openrouter.d.ts +13 -0
- package/dist/providers/llm/openrouter.js +53 -0
- package/dist/providers/llm/openrouter.js.map +1 -0
- package/dist/providers/llm/utils.d.ts +6 -0
- package/dist/providers/llm/utils.js +45 -0
- package/dist/providers/llm/utils.js.map +1 -0
- package/dist/resources/default-feeds.json +650 -0
- package/dist/resources/index.d.ts +2 -0
- package/dist/resources/index.js +3 -0
- package/dist/resources/index.js.map +1 -0
- package/eslint.config.mjs +29 -0
- package/package.json +66 -0
- package/src/cli/commands/init.ts +94 -0
- package/src/cli/commands/run.ts +52 -0
- package/src/cli/commands/schedule.ts +99 -0
- package/src/cli/index.ts +34 -0
- package/src/core/config-manager.ts +72 -0
- package/src/core/config-schema.ts +31 -0
- package/src/core/default-keywords.ts +9 -0
- package/src/core/filters/deduplicator.ts +39 -0
- package/src/core/filters/keywords.ts +18 -0
- package/src/core/orchestrator.ts +47 -0
- package/src/core/pipeline.ts +171 -0
- package/src/core/scheduler.ts +74 -0
- package/src/core/scraper-types.ts +30 -0
- package/src/core/scrapers/api-scraper.ts +45 -0
- package/src/core/scrapers/arxiv-scraper.ts +35 -0
- package/src/core/scrapers/json-scraper.ts +54 -0
- package/src/core/scrapers/rss-scraper.ts +34 -0
- package/src/core/scrapers/scrape-scraper.ts +50 -0
- package/src/db/article-store.ts +75 -0
- package/src/db/connection.ts +17 -0
- package/src/db/migrate.ts +68 -0
- package/src/db/schedule-store.ts +41 -0
- package/src/db/source-health-store.ts +42 -0
- package/src/providers/delivery/index.ts +19 -0
- package/src/providers/delivery/slack.ts +55 -0
- package/src/providers/delivery/webhook.ts +16 -0
- package/src/providers/factory.ts +37 -0
- package/src/providers/llm/anthropic.ts +48 -0
- package/src/providers/llm/index.ts +8 -0
- package/src/providers/llm/ollama.ts +44 -0
- package/src/providers/llm/openrouter.ts +56 -0
- package/src/providers/llm/utils.ts +54 -0
- package/src/resources/default-feeds.json +650 -0
- package/src/resources/index.ts +3 -0
- package/tests/config-manager.test.ts +70 -0
- package/tests/db-integration.test.ts +72 -0
- package/tests/filters.test.ts +53 -0
- package/tests/llm-provider.test.ts +115 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish to NPM
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout repository
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Setup Node.js
|
|
16
|
+
uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: '20'
|
|
19
|
+
registry-url: 'https://registry.npmjs.org'
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: npm ci
|
|
23
|
+
|
|
24
|
+
- name: Run tests
|
|
25
|
+
run: npm test
|
|
26
|
+
|
|
27
|
+
- name: Build project
|
|
28
|
+
run: npm run build
|
|
29
|
+
|
|
30
|
+
- name: Publish to NPM
|
|
31
|
+
run: npm publish --access public
|
|
32
|
+
env:
|
|
33
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/.prettierrc
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 qarib
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# 🚀 Alif-Digest
|
|
2
|
+
|
|
3
|
+
**The Autonomous AI Signal Digest CLI.**
|
|
4
|
+
|
|
5
|
+
Alif (ألف) scours the web for high-signal AI developments, selects the most relevant breakthroughs using LLMs, and delivers a curated digest directly to your workspace.
|
|
6
|
+
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ⚡️ Quick Start
|
|
13
|
+
|
|
14
|
+
Alif helps you track AI breakthroughs by aggregating and analyzing high-signal sources. Follow these steps to get started:
|
|
15
|
+
|
|
16
|
+
### 1. Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g alif-digest
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. Setup
|
|
23
|
+
|
|
24
|
+
Initialize your environment. Alif will guide you through connecting an LLM (Local Ollama, Anthropic, or OpenRouter) and setting up your delivery channels.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
alif init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 3. Run
|
|
31
|
+
|
|
32
|
+
Generate your daily digest. Alif will scrape all sources, filter the noise, analyze the breakthroughs, and deliver the results.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
alif run
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To bypass the source rate limiting (default 5m cooldown), use the force flag:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
alif run --force
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 4. Schedule
|
|
45
|
+
|
|
46
|
+
Keep the signals flowing by checking for scheduled runs.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
alif schedule add
|
|
50
|
+
alif schedule check
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🛠 For Tinkers (Customize & Contribute)
|
|
56
|
+
|
|
57
|
+
Alif is built with extreme modularity. Everything from scrapers to LLM providers follows a strict factory pattern.
|
|
58
|
+
|
|
59
|
+
### Architecture
|
|
60
|
+
|
|
61
|
+
- **Inspiration**: Built to solve the "noise" problem in AI news.
|
|
62
|
+
- **Persistence**: Local SQLite database handles article deduplication and history.
|
|
63
|
+
- **Workflow**: `Scraper` → `Deduplicator` → `Keyword Scorer` → `LLM Analyzer` → `Delivery`.
|
|
64
|
+
|
|
65
|
+
### Customizing Sources & Keywords
|
|
66
|
+
|
|
67
|
+
Your feeds are stored in `~/.config/alif/feeds.json`. You can add any source using the supported types: `rss`, `api`, `json`, or `scrape`.
|
|
68
|
+
|
|
69
|
+
**Keyword Signal Overrides**
|
|
70
|
+
Alif ships with a set of default base keywords. If you want to change what Alif considers "high-signal" (or silence certain topics), add a `customKeywords` object to the `preferences` section in `~/.config/alif/config.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
"preferences": {
|
|
74
|
+
"signalThreshold": 60,
|
|
75
|
+
"customKeywords": {
|
|
76
|
+
"my-favorite-framework": 100,
|
|
77
|
+
"topic-i-want-to-ignore": 0,
|
|
78
|
+
"gpt-5": 50
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
_Note: Your custom keywords will be merged with the base keywords. If you define a key that already exists, your weight will override the default._
|
|
84
|
+
|
|
85
|
+
### Project Structure
|
|
86
|
+
|
|
87
|
+
- `src/core/scrapers/`: Logic for data ingestion.
|
|
88
|
+
- `src/providers/llm/`: Support for Ollama, Anthropic, and OpenRouter.
|
|
89
|
+
- `src/providers/delivery/`: Slack Block Kit and generic Webhook support.
|
|
90
|
+
|
|
91
|
+
### Local Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Install dependencies
|
|
95
|
+
npm install
|
|
96
|
+
|
|
97
|
+
# Run the CLI from source (requires one-time initialization)
|
|
98
|
+
npm run dev -- init
|
|
99
|
+
npm run dev -- run
|
|
100
|
+
|
|
101
|
+
# Execute tests
|
|
102
|
+
npm run test
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
> [!TIP]
|
|
106
|
+
> **Developer Hint**: The CLI looks for configuration in `~/.config/alif/config.json`. If you are running in development, you still need to run `npm run dev -- init` once to generate your local config and database path.
|
|
107
|
+
|
|
108
|
+
### Troubleshooting Husky (GUI / GitHub Desktop)
|
|
109
|
+
|
|
110
|
+
If you're using a GUI client like GitHub Desktop on macOS and the pre-commit hooks fail because `npm` or `node` is not found, you need to ensure Husky can find your PATH.
|
|
111
|
+
|
|
112
|
+
Run the following in your terminal:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
mkdir -p ~/.config/husky
|
|
116
|
+
echo 'export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"' > ~/.config/husky/init.sh
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Contributing
|
|
120
|
+
|
|
121
|
+
1. Fork the repo.
|
|
122
|
+
2. Create your feature branch (`git checkout -b feature/amazing-scraper`).
|
|
123
|
+
3. Commit your changes (`git commit -m 'Add support for NewsAPI'`).
|
|
124
|
+
4. Push to the branch (`git push origin feature/amazing-scraper`).
|
|
125
|
+
5. Open a Pull Request.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 📄 License
|
|
130
|
+
|
|
131
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initCommand(): Promise<void>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { ConfigManager } from '../../core/config-manager.js';
|
|
4
|
+
export async function initCommand() {
|
|
5
|
+
const configManager = ConfigManager.getInstance();
|
|
6
|
+
const configDir = configManager.getConfigDir();
|
|
7
|
+
console.log('--- Alif Initialization ---');
|
|
8
|
+
const response = await prompts([
|
|
9
|
+
{
|
|
10
|
+
type: 'select',
|
|
11
|
+
name: 'llmProvider',
|
|
12
|
+
message: 'Which LLM provider would you like to use?',
|
|
13
|
+
choices: [
|
|
14
|
+
{ title: 'Ollama (Local)', value: 'ollama' },
|
|
15
|
+
{ title: 'Anthropic', value: 'anthropic' },
|
|
16
|
+
{ title: 'OpenRouter', value: 'openrouter' },
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: (prev) => (prev !== 'ollama' ? 'text' : null),
|
|
21
|
+
name: 'apiKey',
|
|
22
|
+
message: 'Enter your API Key:',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
type: 'text',
|
|
26
|
+
name: 'model',
|
|
27
|
+
message: 'Enter the model name (e.g., llama3, claude-3-opus-20240229):',
|
|
28
|
+
initial: (prev, values) => {
|
|
29
|
+
if (values.llmProvider === 'ollama')
|
|
30
|
+
return 'llama3';
|
|
31
|
+
if (values.llmProvider === 'anthropic')
|
|
32
|
+
return 'claude-3-5-sonnet-20240620';
|
|
33
|
+
return 'meta-llama/llama-3-70b-instruct';
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: (prev, values) => (values.llmProvider === 'ollama' ? 'text' : null),
|
|
38
|
+
name: 'baseUrl',
|
|
39
|
+
message: 'Enter Ollama base URL:',
|
|
40
|
+
initial: 'http://localhost:11434',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'multiselect',
|
|
44
|
+
name: 'deliveryProviders',
|
|
45
|
+
message: 'Where should we deliver the digest?',
|
|
46
|
+
choices: [
|
|
47
|
+
{ title: 'Slack', value: 'slack' },
|
|
48
|
+
{ title: 'Generic Webhook', value: 'webhook' },
|
|
49
|
+
],
|
|
50
|
+
min: 1,
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
if (!response.llmProvider) {
|
|
54
|
+
console.log('Initialization cancelled.');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const deliveryConfigs = [];
|
|
58
|
+
for (const provider of response.deliveryProviders) {
|
|
59
|
+
const { webhookUrl } = await prompts({
|
|
60
|
+
type: 'text',
|
|
61
|
+
name: 'webhookUrl',
|
|
62
|
+
message: `Enter ${provider} Webhook URL:`,
|
|
63
|
+
});
|
|
64
|
+
deliveryConfigs.push({ type: provider, webhookUrl });
|
|
65
|
+
}
|
|
66
|
+
const config = {
|
|
67
|
+
llm: {
|
|
68
|
+
provider: response.llmProvider,
|
|
69
|
+
apiKey: response.apiKey,
|
|
70
|
+
model: response.model,
|
|
71
|
+
baseUrl: response.baseUrl,
|
|
72
|
+
},
|
|
73
|
+
delivery: deliveryConfigs,
|
|
74
|
+
preferences: {
|
|
75
|
+
signalThreshold: 60,
|
|
76
|
+
maxItemsPerCategory: 5,
|
|
77
|
+
sourceCooldownMinutes: 5,
|
|
78
|
+
customKeywords: {},
|
|
79
|
+
},
|
|
80
|
+
dbPath: path.join(configDir, 'alif.db'),
|
|
81
|
+
feedsPath: path.join(configDir, 'feeds.json'),
|
|
82
|
+
};
|
|
83
|
+
configManager.save(config);
|
|
84
|
+
console.log(`\nConfiguration saved to ${configManager.getConfigFile()}`);
|
|
85
|
+
console.log(`Database will be located at ${config.dbPath}`);
|
|
86
|
+
console.log('\nAlif is ready! Run "alif run" to start (after adding feeds).');
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { ConfigManager } from '../../core/config-manager.js';
|
|
3
|
+
import { createDatabase } from '../../db/connection.js';
|
|
4
|
+
import { runMigrations } from '../../db/migrate.js';
|
|
5
|
+
import { Pipeline } from '../../core/pipeline.js';
|
|
6
|
+
export async function runPipeline(config, db, force = false) {
|
|
7
|
+
const pipeline = new Pipeline(config, db);
|
|
8
|
+
// Load feeds
|
|
9
|
+
if (!fs.existsSync(config.feedsPath)) {
|
|
10
|
+
console.log(`[Alif] Feeds file not found at ${config.feedsPath}. Initializing with default sources...`);
|
|
11
|
+
const { defaultFeeds } = await import('../../resources/index.js');
|
|
12
|
+
fs.writeFileSync(config.feedsPath, JSON.stringify(defaultFeeds, null, 2));
|
|
13
|
+
console.log(`[Alif] Created default feeds.json at ${config.feedsPath} with ${defaultFeeds.length} sources.`);
|
|
14
|
+
}
|
|
15
|
+
const feeds = JSON.parse(fs.readFileSync(config.feedsPath, 'utf-8'));
|
|
16
|
+
await pipeline.run(feeds, force);
|
|
17
|
+
}
|
|
18
|
+
export async function runCommand(options = {}) {
|
|
19
|
+
const configManager = ConfigManager.getInstance();
|
|
20
|
+
if (!configManager.exists()) {
|
|
21
|
+
console.error('Alif is not initialized. Run "alif init" first.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
let db;
|
|
25
|
+
try {
|
|
26
|
+
const config = configManager.load();
|
|
27
|
+
db = createDatabase(config.dbPath);
|
|
28
|
+
runMigrations(db);
|
|
29
|
+
await runPipeline(config, db, options.force);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error instanceof Error) {
|
|
33
|
+
console.error(`Error: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.error('An unknown error occurred.');
|
|
37
|
+
}
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
if (db)
|
|
42
|
+
db.close();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/cli/commands/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAW,EAAE,EAAO,EAAE,KAAK,GAAG,KAAK;IACnE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAE1C,aAAa;IACb,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CACT,kCAAkC,MAAM,CAAC,SAAS,wCAAwC,CAC3F,CAAC;QACF,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;QAClE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CACT,wCAAwC,MAAM,CAAC,SAAS,SAAS,YAAY,CAAC,MAAM,WAAW,CAChG,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IACrE,MAAM,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAA+B,EAAE;IAChE,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,EAAE,CAAC;IAElD,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,EAAE,CAAC;IACP,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;QACpC,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACnC,aAAa,CAAC,EAAE,CAAC,CAAC;QAElB,MAAM,WAAW,CAAC,MAAM,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;YAAS,CAAC;QACT,IAAI,EAAE;YAAE,EAAE,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function scheduleCommand(action: string): Promise<void>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import { ConfigManager } from '../../core/config-manager.js';
|
|
3
|
+
import { createDatabase } from '../../db/connection.js';
|
|
4
|
+
import { runMigrations } from '../../db/migrate.js';
|
|
5
|
+
import { Scheduler } from '../../core/scheduler.js';
|
|
6
|
+
import { runPipeline } from './run.js';
|
|
7
|
+
export async function scheduleCommand(action) {
|
|
8
|
+
const configManager = ConfigManager.getInstance();
|
|
9
|
+
if (!configManager.exists()) {
|
|
10
|
+
console.error('Alif is not initialized. Run "alif init" first.');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const config = configManager.load();
|
|
14
|
+
const db = createDatabase(config.dbPath);
|
|
15
|
+
try {
|
|
16
|
+
runMigrations(db);
|
|
17
|
+
const scheduler = new Scheduler(db);
|
|
18
|
+
if (action === 'add') {
|
|
19
|
+
const response = await prompts([
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
name: 'name',
|
|
23
|
+
message: 'Name for this schedule:',
|
|
24
|
+
initial: 'Daily Digest',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'text',
|
|
28
|
+
name: 'cron',
|
|
29
|
+
message: 'Enter frequency (e.g. daily, hourly):',
|
|
30
|
+
initial: 'daily',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: (prev) => (prev === 'daily' ? 'text' : null),
|
|
34
|
+
name: 'time',
|
|
35
|
+
message: 'At what time? (HH:mm format, 24h):',
|
|
36
|
+
initial: '09:00',
|
|
37
|
+
validate: (val) => /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/.test(val) ? true : 'Please enter valid HH:mm time',
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
if (response.name && response.cron) {
|
|
41
|
+
const id = await scheduler.add(response.name, response.cron, response.time);
|
|
42
|
+
console.log(`Schedule added! ID: ${id} (Runs ${response.cron}${response.time ? ` at ${response.time}` : ''})`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if (action === 'list') {
|
|
46
|
+
const schedules = scheduler.list();
|
|
47
|
+
if (schedules.length === 0) {
|
|
48
|
+
console.log('No schedules found.');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.table(schedules.map((s) => ({
|
|
52
|
+
ID: s.id,
|
|
53
|
+
Name: s.name,
|
|
54
|
+
Frequency: s.cron,
|
|
55
|
+
Time: s.scheduled_time || '-',
|
|
56
|
+
Active: s.active ? 'Yes' : 'No',
|
|
57
|
+
'Last Run': s.last_run || 'Never',
|
|
58
|
+
})));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (action === 'delete') {
|
|
62
|
+
const schedules = scheduler.list();
|
|
63
|
+
if (schedules.length === 0) {
|
|
64
|
+
console.log('No schedules to delete.');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const { id } = await prompts({
|
|
68
|
+
type: 'select',
|
|
69
|
+
name: 'id',
|
|
70
|
+
message: 'Select schedule to delete:',
|
|
71
|
+
choices: schedules.map((s) => ({ title: s.name, value: s.id })),
|
|
72
|
+
});
|
|
73
|
+
if (id) {
|
|
74
|
+
scheduler.remove(id);
|
|
75
|
+
console.log(`Schedule ${id} deleted.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (action === 'check') {
|
|
79
|
+
console.log('[Scheduler] Checking for due tasks...');
|
|
80
|
+
await scheduler.checkAndRun(async () => {
|
|
81
|
+
await runPipeline(config, db);
|
|
82
|
+
});
|
|
83
|
+
console.log('[Scheduler] Check complete.');
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.error(`Unknown action: ${action}. Available: add, list, delete, check`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
db.close();
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../../src/cli/commands/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc;IAClD,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,EAAE,CAAC;IAClD,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACjE,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;IACpC,MAAM,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,aAAa,CAAC,EAAE,CAAC,CAAC;QAClB,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC;QAEpC,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC;gBAC7B;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,yBAAyB;oBAClC,OAAO,EAAE,cAAc;iBACxB;gBACD;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,uCAAuC;oBAChD,OAAO,EAAE,OAAO;iBACjB;gBACD;oBACE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;oBAClD,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,oCAAoC;oBAC7C,OAAO,EAAE,OAAO;oBAChB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAChB,mCAAmC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,+BAA+B;iBACzF;aACF,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnC,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC5E,OAAO,CAAC,GAAG,CACT,uBAAuB,EAAE,UAAU,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAClG,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YACrC,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CACX,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACpB,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,SAAS,EAAE,CAAC,CAAC,IAAI;oBACjB,IAAI,EAAE,CAAC,CAAC,cAAc,IAAI,GAAG;oBAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;oBAC/B,UAAU,EAAE,CAAC,CAAC,QAAQ,IAAI,OAAO;iBAClC,CAAC,CAAC,CACJ,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;gBACvC,OAAO;YACT,CAAC;YAED,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,OAAO,CAAC;gBAC3B,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,4BAA4B;gBACrC,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;aAChE,CAAC,CAAC;YAEH,IAAI,EAAE,EAAE,CAAC;gBACP,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACrD,MAAM,SAAS,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE;gBACrC,MAAM,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,uCAAuC,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { initCommand } from './commands/init.js';
|
|
4
|
+
import { runCommand } from './commands/run.js';
|
|
5
|
+
import { scheduleCommand } from './commands/schedule.js';
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program.name('alif').description('Alif - Daily AI Signal Digest CLI').version('1.0.0');
|
|
8
|
+
program
|
|
9
|
+
.command('init')
|
|
10
|
+
.description('Initialize Alif configuration')
|
|
11
|
+
.action(async () => {
|
|
12
|
+
await initCommand();
|
|
13
|
+
});
|
|
14
|
+
program
|
|
15
|
+
.command('run')
|
|
16
|
+
.description('Run the AI Signal Digest pipeline')
|
|
17
|
+
.option('-f, --force', 'Bypass source cooldown')
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
await runCommand(options);
|
|
20
|
+
});
|
|
21
|
+
program
|
|
22
|
+
.command('schedule <action>')
|
|
23
|
+
.description('Manage digest schedules')
|
|
24
|
+
.addHelpText('after', '\nActions: add, list, delete, check')
|
|
25
|
+
.action(async (action) => {
|
|
26
|
+
await scheduleCommand(action);
|
|
27
|
+
});
|
|
28
|
+
program.parse(process.argv);
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,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,IAAI,EAAE;IACjB,MAAM,WAAW,EAAE,CAAC;AACtB,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,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,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Config } from './config-schema.js';
|
|
2
|
+
export declare class ConfigManager {
|
|
3
|
+
private static instance;
|
|
4
|
+
private config;
|
|
5
|
+
private configDir;
|
|
6
|
+
private configFile;
|
|
7
|
+
private constructor();
|
|
8
|
+
static getInstance(): ConfigManager;
|
|
9
|
+
getConfigDir(): string;
|
|
10
|
+
getConfigFile(): string;
|
|
11
|
+
load(): Config;
|
|
12
|
+
save(config: Config): void;
|
|
13
|
+
exists(): boolean;
|
|
14
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { ConfigSchema } from './config-schema.js';
|
|
5
|
+
export class ConfigManager {
|
|
6
|
+
static instance;
|
|
7
|
+
config = null;
|
|
8
|
+
configDir;
|
|
9
|
+
configFile;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.configDir = path.join(os.homedir(), '.config', 'alif');
|
|
12
|
+
this.configFile = path.join(this.configDir, 'config.json');
|
|
13
|
+
}
|
|
14
|
+
static getInstance() {
|
|
15
|
+
if (!ConfigManager.instance) {
|
|
16
|
+
ConfigManager.instance = new ConfigManager();
|
|
17
|
+
}
|
|
18
|
+
return ConfigManager.instance;
|
|
19
|
+
}
|
|
20
|
+
getConfigDir() {
|
|
21
|
+
return this.configDir;
|
|
22
|
+
}
|
|
23
|
+
getConfigFile() {
|
|
24
|
+
return this.configFile;
|
|
25
|
+
}
|
|
26
|
+
load() {
|
|
27
|
+
if (this.config)
|
|
28
|
+
return this.config;
|
|
29
|
+
if (!fs.existsSync(this.configFile)) {
|
|
30
|
+
throw new Error(`Configuration file not found at ${this.configFile}. Run 'alif init' first.`);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const raw = fs.readFileSync(this.configFile, 'utf-8');
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
this.config = ConfigSchema.parse(parsed);
|
|
36
|
+
return this.config;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error instanceof Error) {
|
|
40
|
+
throw new Error(`Failed to load configuration: ${error.message}`, { cause: error });
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
save(config) {
|
|
46
|
+
if (!fs.existsSync(this.configDir)) {
|
|
47
|
+
fs.mkdirSync(this.configDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
ConfigSchema.parse(config);
|
|
51
|
+
fs.writeFileSync(this.configFile, JSON.stringify(config, null, 2), 'utf-8');
|
|
52
|
+
this.config = config;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof Error) {
|
|
56
|
+
throw new Error(`Failed to save configuration: ${error.message}`, { cause: error });
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exists() {
|
|
62
|
+
return fs.existsSync(this.configFile);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=config-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/core/config-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const LlmProviderType: z.ZodEnum<{
|
|
3
|
+
ollama: "ollama";
|
|
4
|
+
anthropic: "anthropic";
|
|
5
|
+
openrouter: "openrouter";
|
|
6
|
+
}>;
|
|
7
|
+
export declare const DeliveryProviderType: z.ZodEnum<{
|
|
8
|
+
slack: "slack";
|
|
9
|
+
webhook: "webhook";
|
|
10
|
+
}>;
|
|
11
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
12
|
+
llm: z.ZodObject<{
|
|
13
|
+
provider: z.ZodEnum<{
|
|
14
|
+
ollama: "ollama";
|
|
15
|
+
anthropic: "anthropic";
|
|
16
|
+
openrouter: "openrouter";
|
|
17
|
+
}>;
|
|
18
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
19
|
+
model: z.ZodString;
|
|
20
|
+
baseUrl: z.ZodOptional<z.ZodString>;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
delivery: z.ZodArray<z.ZodObject<{
|
|
23
|
+
type: z.ZodEnum<{
|
|
24
|
+
slack: "slack";
|
|
25
|
+
webhook: "webhook";
|
|
26
|
+
}>;
|
|
27
|
+
webhookUrl: z.ZodString;
|
|
28
|
+
}, z.core.$strip>>;
|
|
29
|
+
preferences: z.ZodObject<{
|
|
30
|
+
signalThreshold: z.ZodDefault<z.ZodNumber>;
|
|
31
|
+
maxItemsPerCategory: z.ZodDefault<z.ZodNumber>;
|
|
32
|
+
sourceCooldownMinutes: z.ZodDefault<z.ZodNumber>;
|
|
33
|
+
customKeywords: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
34
|
+
}, z.core.$strip>;
|
|
35
|
+
dbPath: z.ZodString;
|
|
36
|
+
feedsPath: z.ZodString;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
39
|
+
export type LlmProvider = z.infer<typeof LlmProviderType>;
|
|
40
|
+
export type DeliveryProvider = z.infer<typeof DeliveryProviderType>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const LlmProviderType = z.enum(['ollama', 'anthropic', 'openrouter']);
|
|
3
|
+
export const DeliveryProviderType = z.enum(['slack', 'webhook']);
|
|
4
|
+
export const ConfigSchema = z.object({
|
|
5
|
+
llm: z.object({
|
|
6
|
+
provider: LlmProviderType,
|
|
7
|
+
apiKey: z.string().optional(),
|
|
8
|
+
model: z.string(),
|
|
9
|
+
baseUrl: z.string().url().optional(),
|
|
10
|
+
}),
|
|
11
|
+
delivery: z.array(z.object({
|
|
12
|
+
type: DeliveryProviderType,
|
|
13
|
+
webhookUrl: z.string().url(),
|
|
14
|
+
})),
|
|
15
|
+
preferences: z.object({
|
|
16
|
+
signalThreshold: z.number().min(0).max(100).default(60),
|
|
17
|
+
maxItemsPerCategory: z.number().min(1).default(5),
|
|
18
|
+
sourceCooldownMinutes: z.number().min(0).default(5),
|
|
19
|
+
customKeywords: z.record(z.string(), z.number()).default({}),
|
|
20
|
+
}),
|
|
21
|
+
dbPath: z.string(),
|
|
22
|
+
feedsPath: z.string(),
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=config-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/core/config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;AAEjE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC;QACZ,QAAQ,EAAE,eAAe;QACzB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;KACrC,CAAC;IACF,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,oBAAoB;QAC1B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;KAC7B,CAAC,CACH;IACD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACjD,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;KAC7D,CAAC;IACF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const BASE_KEYWORDS: Record<string, number>;
|