neofeed-cli 0.1.0
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/index.js +501 -0
- package/neofeed-cli-0.1.0.tgz +0 -0
- package/package.json +39 -0
- package/publish_otp.exp +6 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const axios_1 = __importDefault(require("axios"));
|
|
43
|
+
const fs_1 = __importDefault(require("fs"));
|
|
44
|
+
const path_1 = __importDefault(require("path"));
|
|
45
|
+
const os_1 = __importDefault(require("os"));
|
|
46
|
+
const program = new commander_1.Command();
|
|
47
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.neofeed');
|
|
48
|
+
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
49
|
+
// Helper to get API URL
|
|
50
|
+
const getApiUrl = () => {
|
|
51
|
+
return process.env.NEOFEED_API_URL || 'https://neofeed.app/api';
|
|
52
|
+
};
|
|
53
|
+
// Helper to get stored config
|
|
54
|
+
const getConfig = () => {
|
|
55
|
+
if (!fs_1.default.existsSync(CONFIG_FILE)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return JSON.parse(fs_1.default.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
59
|
+
};
|
|
60
|
+
program
|
|
61
|
+
.name('neofeed')
|
|
62
|
+
.description('NeoFeed CLI - The Context Intersection Hub')
|
|
63
|
+
.version('0.1.0');
|
|
64
|
+
program
|
|
65
|
+
.command('login')
|
|
66
|
+
.description('Login with your NeoFeed API Key or via browser')
|
|
67
|
+
.argument('[api-key]', 'Your NeoFeed API Key (optional, will open browser if omitted)')
|
|
68
|
+
.action(async (apiKey) => {
|
|
69
|
+
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
70
|
+
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
if (apiKey) {
|
|
73
|
+
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify({ apiKey }));
|
|
74
|
+
console.log(chalk_1.default.green(`β
Successfully logged in via API key. (Key saved to ${CONFIG_FILE})`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log(chalk_1.default.blue('π Starting browser authentication...'));
|
|
78
|
+
const express = require('express');
|
|
79
|
+
const cors = require('cors');
|
|
80
|
+
const { default: open } = await Promise.resolve().then(() => __importStar(require('open')));
|
|
81
|
+
const app = express();
|
|
82
|
+
app.use(cors());
|
|
83
|
+
const port = 3456;
|
|
84
|
+
const tokenPromise = new Promise((resolve) => {
|
|
85
|
+
app.get('/callback', (req, res) => {
|
|
86
|
+
const token = req.query.token;
|
|
87
|
+
if (!token) {
|
|
88
|
+
res.status(400).send('<body style="background:#050505;color:white;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;"><div style="text-align:center"><h1 style="color:#ef4444">ζζε€±θ΄₯</h1><p style="color:rgba(255,255,255,0.6)">ζͺζΆε°ζζη身份什η</p></div></body>');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
res.send(`
|
|
92
|
+
<body style="background:#050505;color:white;font-family:system-ui, -apple-system, sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;">
|
|
93
|
+
<div style="text-align:center;max-width:400px;padding:40px;background:#080808;border:1px solid rgba(255,255,255,0.1);border-radius:24px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5);">
|
|
94
|
+
<div style="width:64px;height:64px;background:white;color:black;display:flex;align-items:center;justify-content:center;font-weight:bold;font-size:28px;border-radius:16px;margin:0 auto 24px;">N</div>
|
|
95
|
+
<h1 style="margin:0 0 12px;font-size:24px;">ζζζε</h1>
|
|
96
|
+
<p style="margin:0 0 24px;color:rgba(255,255,255,0.6);line-height:1.5;">NeoFeed CLI η°ε·²θΏζ₯θ³ζ¨η账ζ·γ<br>ζ¨ε―δ»₯ε
³ιζ€ηͺε£εΉΆθΏεη»η«―γ</p>
|
|
97
|
+
<div style="border-top:1px solid rgba(255,255,255,0.1);padding-top:24px;">
|
|
98
|
+
<button onclick="window.close()" style="background:white;color:black;border:none;padding:12px 24px;border-radius:12px;font-weight:bold;cursor:pointer;width:100%;transition:background 0.2s;">ε
³ιηͺε£</button>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
<script>setTimeout(() => window.close(), 3000)</script>
|
|
102
|
+
</body>
|
|
103
|
+
`);
|
|
104
|
+
resolve(token);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
const server = app.listen(port, async () => {
|
|
108
|
+
const webAuthUrl = process.env.NEOFEED_WEB_URL || 'https://neofeed.app';
|
|
109
|
+
const authUrl = `${webAuthUrl}/cli/auth?port=${port}`;
|
|
110
|
+
console.log(chalk_1.default.yellow(`Opening browser to: ${authUrl}`));
|
|
111
|
+
console.log(chalk_1.default.gray('Waiting for authentication...'));
|
|
112
|
+
try {
|
|
113
|
+
await open(authUrl);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
console.log(chalk_1.default.yellow(`Could not open browser automatically. Please visit manually: ${authUrl}`));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
try {
|
|
120
|
+
const token = await tokenPromise;
|
|
121
|
+
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify({ apiKey: token }));
|
|
122
|
+
console.log(chalk_1.default.green(`β
Successfully logged in via browser! (Key saved to ${CONFIG_FILE})`));
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
server.close();
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
program
|
|
130
|
+
.command('list')
|
|
131
|
+
.description('List recent items in your knowledge base')
|
|
132
|
+
.option('--limit <number>', 'Number of items to return', '10')
|
|
133
|
+
.option('--json', 'Output in JSON format')
|
|
134
|
+
.action(async (options) => {
|
|
135
|
+
const config = getConfig();
|
|
136
|
+
if (!config?.apiKey) {
|
|
137
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login <api-key>` first.'));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
if (!options.json) {
|
|
142
|
+
console.log(chalk_1.default.blue(`π Fetching your recent knowledge base items...`));
|
|
143
|
+
}
|
|
144
|
+
const response = await axios_1.default.get(`${getApiUrl()}/cli/list?limit=${options.limit}`, {
|
|
145
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
146
|
+
});
|
|
147
|
+
const data = response.data;
|
|
148
|
+
if (options.json) {
|
|
149
|
+
console.log(JSON.stringify(data, null, 2));
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.log('\n' + chalk_1.default.bold('π Your NeoFeed Directory:'));
|
|
153
|
+
console.log(chalk_1.default.gray('----------------------------------------'));
|
|
154
|
+
if (data.length === 0) {
|
|
155
|
+
console.log(chalk_1.default.gray(' No items found.'));
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
data.forEach((item, index) => {
|
|
159
|
+
const icon = item.type === 'agent' ? 'π€' : 'π';
|
|
160
|
+
console.log(`${chalk_1.default.gray(`[${index + 1}]`)} ${icon} ${chalk_1.default.white(item.title)}`);
|
|
161
|
+
console.log(` ${chalk_1.default.gray(`ID: ${item.id} | Date: ${new Date(item.created_at).toLocaleDateString()}`)}`);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
console.log(chalk_1.default.gray('----------------------------------------'));
|
|
165
|
+
console.log(chalk_1.default.green(`\n⨠Showing ${data.length} items. Use --limit to see more.`));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
const msg = error.response?.data?.error || error.message;
|
|
170
|
+
console.error(chalk_1.default.red(`β Failed to fetch data: ${msg}`));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
program
|
|
175
|
+
.command('search')
|
|
176
|
+
.description('Search your NeoFeed knowledge base')
|
|
177
|
+
.argument('<query>', 'Search query')
|
|
178
|
+
.option('--json', 'Output in JSON format')
|
|
179
|
+
.action(async (query, options) => {
|
|
180
|
+
const config = getConfig();
|
|
181
|
+
if (!config?.apiKey) {
|
|
182
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login <api-key>` first.'));
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
if (!options.json) {
|
|
186
|
+
console.log(chalk_1.default.blue(`π Searching for: "${query}"...`));
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const response = await axios_1.default.get(`${getApiUrl()}/cli/search?q=${encodeURIComponent(query)}`, {
|
|
190
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
191
|
+
});
|
|
192
|
+
const data = response.data;
|
|
193
|
+
if (options.json) {
|
|
194
|
+
console.log(JSON.stringify(data, null, 2));
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
if (data.length === 0) {
|
|
198
|
+
console.log(chalk_1.default.yellow('\nNo results found.'));
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
data.forEach((item, index) => {
|
|
202
|
+
console.log(chalk_1.default.yellow(`\n[${index + 1}] ${item.title}`));
|
|
203
|
+
// Truncate content for display
|
|
204
|
+
const preview = item.content_raw ? item.content_raw.substring(0, 150).replace(/\n/g, ' ') + '...' : 'No content preview';
|
|
205
|
+
console.log(chalk_1.default.gray(preview));
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const msg = error.response?.data?.error || error.message;
|
|
211
|
+
console.error(chalk_1.default.red(`β Search failed: ${msg}`));
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
program
|
|
216
|
+
.command('read')
|
|
217
|
+
.alias('get')
|
|
218
|
+
.description('Read the full content and summary of a specific article by ID')
|
|
219
|
+
.argument('<id>', 'The ID of the item to read')
|
|
220
|
+
.option('--json', 'Output in JSON format')
|
|
221
|
+
.action(async (id, options) => {
|
|
222
|
+
const config = getConfig();
|
|
223
|
+
if (!config?.apiKey) {
|
|
224
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login` first.'));
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
if (!options.json) {
|
|
229
|
+
console.log(chalk_1.default.blue(`π Reading article: ${id}...`));
|
|
230
|
+
}
|
|
231
|
+
const response = await axios_1.default.get(`${getApiUrl()}/cli/read?id=${id}`, {
|
|
232
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
233
|
+
});
|
|
234
|
+
const { type, data } = response.data;
|
|
235
|
+
if (options.json) {
|
|
236
|
+
console.log(JSON.stringify(data, null, 2));
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
console.log('\n' + chalk_1.default.bold.white('=================================================='));
|
|
240
|
+
console.log(chalk_1.default.bold.green(data.title || 'Untitled'));
|
|
241
|
+
console.log(chalk_1.default.gray(`ID: ${data.id} | Type: ${type} | Date: ${new Date(data.created_at).toLocaleString()}`));
|
|
242
|
+
if (data.url)
|
|
243
|
+
console.log(chalk_1.default.blue(`URL: ${data.url}`));
|
|
244
|
+
if (data.tags && data.tags.length > 0)
|
|
245
|
+
console.log(chalk_1.default.cyan(`Tags: ${data.tags.join(', ')}`));
|
|
246
|
+
console.log(chalk_1.default.bold.white('==================================================\n'));
|
|
247
|
+
if (data.summary) {
|
|
248
|
+
console.log(chalk_1.default.bold.yellow('π‘ AI Summary:'));
|
|
249
|
+
console.log(data.summary + '\n');
|
|
250
|
+
}
|
|
251
|
+
if (data.takeaways && data.takeaways.length > 0) {
|
|
252
|
+
console.log(chalk_1.default.bold.yellow('π Key Takeaways:'));
|
|
253
|
+
data.takeaways.forEach((t) => console.log(`- ${t}`));
|
|
254
|
+
console.log('\n');
|
|
255
|
+
}
|
|
256
|
+
console.log(chalk_1.default.bold.white('π Full Content:'));
|
|
257
|
+
console.log(data.content_raw || chalk_1.default.gray('(No content available)'));
|
|
258
|
+
console.log('\n' + chalk_1.default.bold.white('==================================================\n'));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
const msg = error.response?.data?.error || error.message;
|
|
263
|
+
console.error(chalk_1.default.red(`β Failed to read article: ${msg}`));
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
program
|
|
268
|
+
.command('memo')
|
|
269
|
+
.description('Save a quick memo or agent interaction')
|
|
270
|
+
.argument('<content>', 'The content to save')
|
|
271
|
+
.option('-t, --title <title>', 'Title of the memo')
|
|
272
|
+
.option('--tags <tags>', 'Comma separated tags')
|
|
273
|
+
.action(async (content, options) => {
|
|
274
|
+
const config = getConfig();
|
|
275
|
+
if (!config?.apiKey) {
|
|
276
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login <api-key>` first.'));
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
const response = await axios_1.default.post(`${getApiUrl()}/cli/memo`, {
|
|
281
|
+
content,
|
|
282
|
+
title: options.title,
|
|
283
|
+
tags: options.tags
|
|
284
|
+
}, {
|
|
285
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
286
|
+
});
|
|
287
|
+
console.log(chalk_1.default.green('β
Memo saved successfully!'));
|
|
288
|
+
console.log(chalk_1.default.gray(`ID: ${response.data.id}`));
|
|
289
|
+
console.log(chalk_1.default.gray(`Title: ${options.title || 'CLI Memo'}`));
|
|
290
|
+
console.log(chalk_1.default.gray(`Content length: ${content.length} chars`));
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
const msg = error.response?.data?.error || error.message;
|
|
294
|
+
console.error(chalk_1.default.red(`β Failed to save memo: ${msg}`));
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
program
|
|
299
|
+
.command('delete')
|
|
300
|
+
.alias('rm')
|
|
301
|
+
.description('Delete an item from your knowledge base by ID')
|
|
302
|
+
.argument('<id>', 'The ID of the item to delete')
|
|
303
|
+
.action(async (id) => {
|
|
304
|
+
const config = getConfig();
|
|
305
|
+
if (!config?.apiKey) {
|
|
306
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login` first.'));
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
console.log(chalk_1.default.blue(`ποΈ Deleting item: ${id}...`));
|
|
311
|
+
await axios_1.default.delete(`${getApiUrl()}/cli/delete?id=${id}`, {
|
|
312
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
313
|
+
});
|
|
314
|
+
console.log(chalk_1.default.green('β
Item successfully deleted!'));
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
const msg = error.response?.data?.error || error.message;
|
|
318
|
+
console.error(chalk_1.default.red(`β Failed to delete item: ${msg}`));
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
program
|
|
323
|
+
.command('logout')
|
|
324
|
+
.description('Logout and remove your NeoFeed API Key')
|
|
325
|
+
.action(() => {
|
|
326
|
+
if (fs_1.default.existsSync(CONFIG_FILE)) {
|
|
327
|
+
fs_1.default.unlinkSync(CONFIG_FILE);
|
|
328
|
+
console.log(chalk_1.default.green('β
Successfully logged out.'));
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
console.log(chalk_1.default.yellow('βΉοΈ You are not logged in.'));
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
program
|
|
335
|
+
.command('whoami')
|
|
336
|
+
.alias('status')
|
|
337
|
+
.description('Show current user and knowledge base statistics')
|
|
338
|
+
.action(async () => {
|
|
339
|
+
const config = getConfig();
|
|
340
|
+
if (!config?.apiKey) {
|
|
341
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login` first.'));
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
console.log(chalk_1.default.blue('π€ Fetching your profile...'));
|
|
346
|
+
const response = await axios_1.default.get(`${getApiUrl()}/cli/me`, {
|
|
347
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
348
|
+
});
|
|
349
|
+
const data = response.data;
|
|
350
|
+
console.log('\n' + chalk_1.default.bold('=== NeoFeed Status ==='));
|
|
351
|
+
console.log(chalk_1.default.gray('Email: ') + chalk_1.default.white(data.email));
|
|
352
|
+
console.log(chalk_1.default.gray('User ID: ') + chalk_1.default.white(data.id));
|
|
353
|
+
console.log(chalk_1.default.gray('----------------------'));
|
|
354
|
+
console.log(chalk_1.default.gray('Total Feeds: ') + chalk_1.default.cyan(data.stats.feeds));
|
|
355
|
+
console.log(chalk_1.default.gray('Agent Interactions: ') + chalk_1.default.cyan(data.stats.agent_interactions));
|
|
356
|
+
console.log(chalk_1.default.gray('----------------------'));
|
|
357
|
+
console.log(chalk_1.default.green(`Total Items: ${data.stats.total_items}`));
|
|
358
|
+
console.log(chalk_1.default.bold('======================\n'));
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
const msg = error.response?.data?.error || error.message;
|
|
362
|
+
console.error(chalk_1.default.red(`β Failed to fetch profile: ${msg}`));
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
program
|
|
367
|
+
.command('capture')
|
|
368
|
+
.alias('add')
|
|
369
|
+
.description('Capture a webpage URL and add it to your knowledge base')
|
|
370
|
+
.argument('<url>', 'The URL to capture')
|
|
371
|
+
.action(async (url) => {
|
|
372
|
+
const config = getConfig();
|
|
373
|
+
if (!config?.apiKey) {
|
|
374
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login` first.'));
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
console.log(chalk_1.default.blue(`π Capturing URL: ${url}...`));
|
|
379
|
+
await axios_1.default.post(`${getApiUrl().replace('/cli', '')}/ingest-url`, { url }, {
|
|
380
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
381
|
+
});
|
|
382
|
+
console.log(chalk_1.default.green('β
URL successfully sent to NeoFeed for processing!'));
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
const msg = error.response?.data?.error || error.message;
|
|
386
|
+
console.error(chalk_1.default.red(`β Failed to capture URL: ${msg}`));
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
program
|
|
391
|
+
.command('push')
|
|
392
|
+
.description('Push a local file to your knowledge base as a memo')
|
|
393
|
+
.argument('<filepath>', 'Path to the local file')
|
|
394
|
+
.action(async (filepath) => {
|
|
395
|
+
const config = getConfig();
|
|
396
|
+
if (!config?.apiKey) {
|
|
397
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login` first.'));
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
if (!fs_1.default.existsSync(filepath)) {
|
|
401
|
+
console.error(chalk_1.default.red(`β File not found: ${filepath}`));
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
const content = fs_1.default.readFileSync(filepath, 'utf-8');
|
|
406
|
+
const filename = path_1.default.basename(filepath);
|
|
407
|
+
console.log(chalk_1.default.blue(`π Pushing file: ${filename} (${content.length} chars)...`));
|
|
408
|
+
const response = await axios_1.default.post(`${getApiUrl()}/cli/memo`, {
|
|
409
|
+
title: filename,
|
|
410
|
+
content: content,
|
|
411
|
+
tags: 'cli-push'
|
|
412
|
+
}, {
|
|
413
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
414
|
+
});
|
|
415
|
+
console.log(chalk_1.default.green('β
File successfully pushed to NeoFeed!'));
|
|
416
|
+
console.log(chalk_1.default.gray(`ID: ${response.data.id}`));
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
const msg = error.response?.data?.error || error.message;
|
|
420
|
+
console.error(chalk_1.default.red(`β Failed to push file: ${msg}`));
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
program
|
|
425
|
+
.command('export')
|
|
426
|
+
.description('Export all knowledge base items to local markdown files')
|
|
427
|
+
.argument('<dir>', 'Directory to export files to')
|
|
428
|
+
.action(async (dir) => {
|
|
429
|
+
const config = getConfig();
|
|
430
|
+
if (!config?.apiKey) {
|
|
431
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login` first.'));
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
console.log(chalk_1.default.blue(`π¦ Fetching all knowledge base data...`));
|
|
436
|
+
const response = await axios_1.default.get(`${getApiUrl()}/cli/export`, {
|
|
437
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
438
|
+
});
|
|
439
|
+
const data = response.data;
|
|
440
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
441
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
442
|
+
}
|
|
443
|
+
let count = 0;
|
|
444
|
+
const processItems = (items, folder, typeName) => {
|
|
445
|
+
const outDir = path_1.default.join(dir, folder);
|
|
446
|
+
if (items.length > 0 && !fs_1.default.existsSync(outDir))
|
|
447
|
+
fs_1.default.mkdirSync(outDir);
|
|
448
|
+
items.forEach((item) => {
|
|
449
|
+
const safeTitle = (item.title || 'Untitled').replace(/[/\\?%*:|"<>]/g, '-');
|
|
450
|
+
const dateStr = new Date(item.created_at).toISOString().split('T')[0];
|
|
451
|
+
const filename = `${dateStr}-${safeTitle.substring(0, 50)}.md`;
|
|
452
|
+
let frontmatter = `---\n`;
|
|
453
|
+
frontmatter += `id: ${item.id}\n`;
|
|
454
|
+
frontmatter += `title: "${item.title?.replace(/"/g, '\\"')}"\n`;
|
|
455
|
+
frontmatter += `date: ${item.created_at}\n`;
|
|
456
|
+
frontmatter += `type: ${typeName}\n`;
|
|
457
|
+
if (item.url)
|
|
458
|
+
frontmatter += `url: ${item.url}\n`;
|
|
459
|
+
if (item.source_type)
|
|
460
|
+
frontmatter += `source: ${item.source_type}\n`;
|
|
461
|
+
if (item.tags && item.tags.length > 0)
|
|
462
|
+
frontmatter += `tags: [${item.tags.join(', ')}]\n`;
|
|
463
|
+
frontmatter += `---\n\n`;
|
|
464
|
+
fs_1.default.writeFileSync(path_1.default.join(outDir, filename), frontmatter + (item.content_raw || ''));
|
|
465
|
+
count++;
|
|
466
|
+
});
|
|
467
|
+
};
|
|
468
|
+
processItems(data.feeds, 'feeds', 'web_feed');
|
|
469
|
+
processItems(data.agents, 'memos', 'agent_memo');
|
|
470
|
+
console.log(chalk_1.default.green(`β
Successfully exported ${count} files to ${dir}`));
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
const msg = error.response?.data?.error || error.message;
|
|
474
|
+
console.error(chalk_1.default.red(`β Failed to export: ${msg}`));
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
program
|
|
479
|
+
.command('trigger')
|
|
480
|
+
.description('Trigger a background agent task')
|
|
481
|
+
.argument('<task>', 'The name of the task (e.g. weekly-report)')
|
|
482
|
+
.action(async (task) => {
|
|
483
|
+
const config = getConfig();
|
|
484
|
+
if (!config?.apiKey) {
|
|
485
|
+
console.error(chalk_1.default.red('β Not logged in. Please run `neofeed login` first.'));
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
console.log(chalk_1.default.blue(`π Triggering task: ${task}...`));
|
|
490
|
+
await axios_1.default.post(`${getApiUrl().replace('/cli', '')}/trigger-${task}`, {}, {
|
|
491
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
492
|
+
});
|
|
493
|
+
console.log(chalk_1.default.green(`β
Task '${task}' successfully triggered!`));
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
const msg = error.response?.data?.error || error.message;
|
|
497
|
+
console.error(chalk_1.default.red(`β Failed to trigger task: ${msg}`));
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
program.parse();
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "neofeed-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Command line interface for NeoFeed - The Context Intersection Hub",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"neofeed": "./dist/index.js",
|
|
8
|
+
"nf": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"neofeed",
|
|
17
|
+
"cli",
|
|
18
|
+
"agent",
|
|
19
|
+
"context"
|
|
20
|
+
],
|
|
21
|
+
"author": "NeoFeed Team",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"axios": "^1.16.0",
|
|
25
|
+
"chalk": "^4.1.2",
|
|
26
|
+
"commander": "^14.0.3",
|
|
27
|
+
"cors": "^2.8.6",
|
|
28
|
+
"dotenv": "^17.4.2",
|
|
29
|
+
"express": "^5.2.1",
|
|
30
|
+
"open": "^11.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/cors": "^2.8.19",
|
|
34
|
+
"@types/express": "^5.0.6",
|
|
35
|
+
"@types/node": "^25.7.0",
|
|
36
|
+
"tsx": "^4.21.0",
|
|
37
|
+
"typescript": "^6.0.3"
|
|
38
|
+
}
|
|
39
|
+
}
|