liferewind 0.1.0 → 0.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/dist/cli/commands/collect.d.ts.map +1 -1
- package/dist/cli/commands/collect.js +14 -7
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/config.js +203 -21
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +0 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +23 -62
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +0 -1
- package/dist/cli/index.js +0 -1
- package/dist/cli/utils/api.d.ts +9 -0
- package/dist/cli/utils/api.d.ts.map +1 -0
- package/dist/cli/utils/api.js +31 -0
- package/dist/cli/utils/prompts.d.ts +63 -0
- package/dist/cli/utils/prompts.d.ts.map +1 -0
- package/dist/cli/utils/prompts.js +91 -0
- package/dist/core/collector.d.ts +3 -3
- package/dist/core/collector.d.ts.map +1 -1
- package/dist/core/collector.js +5 -3
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/types.d.ts +12 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/sources/git/index.d.ts +2 -0
- package/dist/sources/git/index.d.ts.map +1 -1
- package/dist/sources/git/index.js +18 -6
- package/package.json +16 -17
- package/dist/cli/utils/path.d.ts +0 -9
- package/dist/cli/utils/path.js +0 -23
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/collect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/collect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,eAAO,MAAM,cAAc,SAyDvB,CAAC"}
|
|
@@ -4,14 +4,22 @@ import { Collector } from '../../core/collector.js';
|
|
|
4
4
|
import { loadConfig } from '../../config/loader.js';
|
|
5
5
|
import { createLogger } from '../../utils/logger.js';
|
|
6
6
|
import { registerBuiltinSources } from '../../sources/index.js';
|
|
7
|
-
import { printError, printSuccess } from '../utils/output.js';
|
|
7
|
+
import { printError, printSuccess, printInfo } from '../utils/output.js';
|
|
8
8
|
import { SOURCE_TYPES } from '../../core/types.js';
|
|
9
|
+
function showSkippedInfo(result, verbose) {
|
|
10
|
+
if (!verbose || !result?.skipped?.count)
|
|
11
|
+
return;
|
|
12
|
+
printInfo(`Skipped ${result.skipped.count} items:`);
|
|
13
|
+
for (const item of result.skipped.items) {
|
|
14
|
+
console.log(` - ${item.path} (${item.reason})`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
9
17
|
export const collectCommand = new Command('collect')
|
|
10
18
|
.description('Manually trigger data collection')
|
|
11
19
|
.argument('[source]', 'specific source to collect (git, browser, filesystem, chatbot)')
|
|
12
20
|
.action(async (source, _options, cmd) => {
|
|
13
21
|
const globalOpts = cmd.optsWithGlobals();
|
|
14
|
-
|
|
22
|
+
const verbose = !!globalOpts.verbose;
|
|
15
23
|
if (source && !SOURCE_TYPES.includes(source)) {
|
|
16
24
|
printError(`Invalid source: ${source}`);
|
|
17
25
|
console.log(`Valid sources: ${SOURCE_TYPES.join(', ')}`);
|
|
@@ -19,7 +27,6 @@ export const collectCommand = new Command('collect')
|
|
|
19
27
|
}
|
|
20
28
|
try {
|
|
21
29
|
const config = loadConfig(globalOpts.config);
|
|
22
|
-
// Apply global log level overrides
|
|
23
30
|
let logLevel = config.logging.level;
|
|
24
31
|
if (globalOpts.verbose)
|
|
25
32
|
logLevel = 'debug';
|
|
@@ -36,21 +43,21 @@ export const collectCommand = new Command('collect')
|
|
|
36
43
|
}
|
|
37
44
|
const spinner = ora();
|
|
38
45
|
if (source) {
|
|
39
|
-
// Collect specific source
|
|
40
46
|
if (!enabledSources.includes(source)) {
|
|
41
47
|
printError(`Source '${source}' is not enabled or not validated.`);
|
|
42
48
|
process.exit(1);
|
|
43
49
|
}
|
|
44
50
|
spinner.start(`Collecting from ${source}...`);
|
|
45
|
-
await collector.triggerCollection(source);
|
|
51
|
+
const result = await collector.triggerCollection(source);
|
|
46
52
|
spinner.succeed(`Collection from ${source} complete`);
|
|
53
|
+
showSkippedInfo(result, verbose);
|
|
47
54
|
}
|
|
48
55
|
else {
|
|
49
|
-
// Collect all enabled sources
|
|
50
56
|
for (const src of enabledSources) {
|
|
51
57
|
spinner.start(`Collecting from ${src}...`);
|
|
52
|
-
await collector.triggerCollection(src);
|
|
58
|
+
const result = await collector.triggerCollection(src);
|
|
53
59
|
spinner.succeed(`Collection from ${src} complete`);
|
|
60
|
+
showSkippedInfo(result, verbose);
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
printSuccess('All collections complete.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,eAAO,MAAM,aAAa,SAA4D,CAAC"}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
2
|
import { existsSync } from 'node:fs';
|
|
4
3
|
import pc from 'picocolors';
|
|
4
|
+
import { confirm, select, checkbox, Separator } from '@inquirer/prompts';
|
|
5
5
|
import { loadConfig, findConfigPath } from '../../config/loader.js';
|
|
6
|
+
import { writeConfig } from '../../config/writer.js';
|
|
6
7
|
import { getUserConfigPath, getAllConfigPaths } from '../../config/paths.js';
|
|
7
8
|
import { printError, printSuccess, printInfo, printDim } from '../utils/output.js';
|
|
9
|
+
import { detectInstalledBrowsers, detectGitInstalled, detectChatbotClients } from '../detect/index.js';
|
|
10
|
+
import { maskApiKey, SCHEDULE_CHOICES, GIT_PATH_PRESETS, FILESYSTEM_PATH_PRESETS, selectPaths, } from '../utils/prompts.js';
|
|
11
|
+
import { inputApiUrl, inputApiKey } from '../utils/api.js';
|
|
8
12
|
export const configCommand = new Command('config').description('Manage configuration');
|
|
9
13
|
function printConfigSummary(config, revealSecrets = false) {
|
|
10
14
|
console.log(pc.bold('\nAPI Configuration'));
|
|
11
15
|
console.log(` Base URL: ${config.api.baseUrl}`);
|
|
12
|
-
|
|
13
|
-
? config.api.apiKey
|
|
14
|
-
: config.api.apiKey.length > 12
|
|
15
|
-
? config.api.apiKey.slice(0, 8) + '...' + config.api.apiKey.slice(-4)
|
|
16
|
-
: '***';
|
|
17
|
-
console.log(` API Key: ${apiKey}`);
|
|
16
|
+
console.log(` API Key: ${revealSecrets ? config.api.apiKey : maskApiKey(config.api.apiKey)}`);
|
|
18
17
|
console.log(` Timeout: ${config.api.timeout}ms`);
|
|
19
18
|
console.log(` Retries: ${config.api.retryAttempts}`);
|
|
20
19
|
console.log(pc.bold('\nData Sources'));
|
|
@@ -41,8 +40,6 @@ configCommand
|
|
|
41
40
|
if (configPath) {
|
|
42
41
|
printDim(`Loaded from: ${configPath}`);
|
|
43
42
|
}
|
|
44
|
-
// Helper to mask API key
|
|
45
|
-
const maskApiKey = (key) => key.length > 12 ? key.slice(0, 8) + '...' + key.slice(-4) : '***';
|
|
46
43
|
if (options.json) {
|
|
47
44
|
const output = {
|
|
48
45
|
...config,
|
|
@@ -77,23 +74,208 @@ configCommand
|
|
|
77
74
|
});
|
|
78
75
|
configCommand
|
|
79
76
|
.command('edit')
|
|
80
|
-
.description('
|
|
81
|
-
.action((_options, cmd) => {
|
|
77
|
+
.description('Interactive configuration editor')
|
|
78
|
+
.action(async (_options, cmd) => {
|
|
82
79
|
const globalOpts = cmd.optsWithGlobals();
|
|
83
|
-
const configPath = findConfigPath(globalOpts.config);
|
|
84
|
-
|
|
85
|
-
printError('No config file found.');
|
|
86
|
-
printInfo("Run 'liferewind init' to create one.");
|
|
87
|
-
process.exit(1);
|
|
88
|
-
}
|
|
89
|
-
const editor = process.env['EDITOR'] || process.env['VISUAL'] || (process.platform === 'darwin' ? 'open' : 'vi');
|
|
80
|
+
const configPath = findConfigPath(globalOpts.config) || getUserConfigPath();
|
|
81
|
+
let config;
|
|
90
82
|
try {
|
|
91
|
-
|
|
83
|
+
config = loadConfig(globalOpts.config);
|
|
84
|
+
printDim(`Editing: ${configPath}`);
|
|
92
85
|
}
|
|
93
|
-
catch
|
|
94
|
-
printError(
|
|
86
|
+
catch {
|
|
87
|
+
printError('No valid config file found.');
|
|
88
|
+
printInfo("Run 'liferewind init' to create one.");
|
|
95
89
|
process.exit(1);
|
|
96
90
|
}
|
|
91
|
+
let hasChanges = false;
|
|
92
|
+
while (true) {
|
|
93
|
+
const choice = await select({
|
|
94
|
+
message: 'What would you like to configure?',
|
|
95
|
+
loop: false,
|
|
96
|
+
choices: [
|
|
97
|
+
{ name: `API Settings ${pc.dim(`(${config.api.baseUrl})`)}`, value: 'api' },
|
|
98
|
+
{
|
|
99
|
+
name: `Browser Collection ${config.sources.browser.enabled ? pc.green('✓') : pc.dim('✗')}`,
|
|
100
|
+
value: 'browser',
|
|
101
|
+
},
|
|
102
|
+
{ name: `Git Collection ${config.sources.git.enabled ? pc.green('✓') : pc.dim('✗')}`, value: 'git' },
|
|
103
|
+
{
|
|
104
|
+
name: `Filesystem Collection ${config.sources.filesystem.enabled ? pc.green('✓') : pc.dim('✗')}`,
|
|
105
|
+
value: 'filesystem',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: `Chatbot Collection ${config.sources.chatbot.enabled ? pc.green('✓') : pc.dim('✗')}`,
|
|
109
|
+
value: 'chatbot',
|
|
110
|
+
},
|
|
111
|
+
{ name: `Logging ${pc.dim(`(${config.logging.level})`)}`, value: 'logging' },
|
|
112
|
+
new Separator(),
|
|
113
|
+
{ name: hasChanges ? pc.green('Save and Exit') : 'Exit', value: 'exit' },
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
if (choice === 'exit') {
|
|
117
|
+
if (hasChanges) {
|
|
118
|
+
const shouldSave = await confirm({ message: 'Save changes?', default: true });
|
|
119
|
+
if (shouldSave) {
|
|
120
|
+
writeConfig(config, configPath);
|
|
121
|
+
printSuccess('Configuration saved!');
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
printInfo('Changes discarded.');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
if (choice === 'api') {
|
|
130
|
+
const newUrl = await inputApiUrl(config.api.baseUrl);
|
|
131
|
+
const changeKey = await confirm({ message: 'Change API Key?', default: false });
|
|
132
|
+
let newKey = config.api.apiKey;
|
|
133
|
+
if (changeKey) {
|
|
134
|
+
newKey = await inputApiKey();
|
|
135
|
+
}
|
|
136
|
+
if (newUrl !== config.api.baseUrl || newKey !== config.api.apiKey) {
|
|
137
|
+
config.api.baseUrl = newUrl;
|
|
138
|
+
config.api.apiKey = newKey;
|
|
139
|
+
hasChanges = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (choice === 'browser') {
|
|
143
|
+
const detectedBrowsers = detectInstalledBrowsers();
|
|
144
|
+
if (detectedBrowsers.length > 0) {
|
|
145
|
+
printDim(` Detected: ${detectedBrowsers.join(', ')}`);
|
|
146
|
+
}
|
|
147
|
+
const enabled = await confirm({
|
|
148
|
+
message: 'Enable browser history collection?',
|
|
149
|
+
default: config.sources.browser.enabled,
|
|
150
|
+
});
|
|
151
|
+
if (enabled) {
|
|
152
|
+
const currentBrowsers = config.sources.browser.options.browsers || [];
|
|
153
|
+
const selectedBrowsers = await checkbox({
|
|
154
|
+
message: 'Select browsers:',
|
|
155
|
+
loop: false,
|
|
156
|
+
choices: ['chrome', 'safari', 'arc', 'dia', 'comet'].map((b) => ({
|
|
157
|
+
name: b.charAt(0).toUpperCase() + b.slice(1),
|
|
158
|
+
value: b,
|
|
159
|
+
checked: currentBrowsers.includes(b),
|
|
160
|
+
})),
|
|
161
|
+
});
|
|
162
|
+
const schedule = await select({
|
|
163
|
+
message: 'Collection schedule:',
|
|
164
|
+
choices: SCHEDULE_CHOICES,
|
|
165
|
+
loop: false,
|
|
166
|
+
default: config.sources.browser.schedule,
|
|
167
|
+
});
|
|
168
|
+
config.sources.browser = {
|
|
169
|
+
enabled: true,
|
|
170
|
+
schedule,
|
|
171
|
+
options: { ...config.sources.browser.options, browsers: selectedBrowsers },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
config.sources.browser.enabled = false;
|
|
176
|
+
}
|
|
177
|
+
hasChanges = true;
|
|
178
|
+
}
|
|
179
|
+
if (choice === 'git') {
|
|
180
|
+
const gitInstalled = detectGitInstalled();
|
|
181
|
+
printDim(gitInstalled ? ' Git is installed' : ' Git not found');
|
|
182
|
+
const enabled = await confirm({
|
|
183
|
+
message: 'Enable git commit collection?',
|
|
184
|
+
default: config.sources.git.enabled,
|
|
185
|
+
});
|
|
186
|
+
if (enabled) {
|
|
187
|
+
const currentPaths = config.sources.git.options.scanPaths || [];
|
|
188
|
+
const scanPaths = await selectPaths('Select paths to scan for git repositories:', GIT_PATH_PRESETS, currentPaths);
|
|
189
|
+
const schedule = await select({
|
|
190
|
+
message: 'Collection schedule:',
|
|
191
|
+
choices: SCHEDULE_CHOICES,
|
|
192
|
+
loop: false,
|
|
193
|
+
default: config.sources.git.schedule,
|
|
194
|
+
});
|
|
195
|
+
config.sources.git = {
|
|
196
|
+
enabled: true,
|
|
197
|
+
schedule,
|
|
198
|
+
options: { ...config.sources.git.options, scanPaths },
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
config.sources.git.enabled = false;
|
|
203
|
+
}
|
|
204
|
+
hasChanges = true;
|
|
205
|
+
}
|
|
206
|
+
if (choice === 'filesystem') {
|
|
207
|
+
const enabled = await confirm({
|
|
208
|
+
message: 'Enable filesystem monitoring?',
|
|
209
|
+
default: config.sources.filesystem.enabled,
|
|
210
|
+
});
|
|
211
|
+
if (enabled) {
|
|
212
|
+
const currentPaths = config.sources.filesystem.options.watchPaths || [];
|
|
213
|
+
const watchPaths = await selectPaths('Select paths to monitor:', FILESYSTEM_PATH_PRESETS, currentPaths);
|
|
214
|
+
const schedule = await select({
|
|
215
|
+
message: 'Collection schedule:',
|
|
216
|
+
choices: SCHEDULE_CHOICES,
|
|
217
|
+
loop: false,
|
|
218
|
+
default: config.sources.filesystem.schedule,
|
|
219
|
+
});
|
|
220
|
+
config.sources.filesystem = {
|
|
221
|
+
enabled: true,
|
|
222
|
+
schedule,
|
|
223
|
+
options: { ...config.sources.filesystem.options, watchPaths },
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
config.sources.filesystem.enabled = false;
|
|
228
|
+
}
|
|
229
|
+
hasChanges = true;
|
|
230
|
+
}
|
|
231
|
+
if (choice === 'chatbot') {
|
|
232
|
+
const detectedChatbots = detectChatbotClients();
|
|
233
|
+
if (detectedChatbots.length > 0) {
|
|
234
|
+
printDim(` Detected: ${detectedChatbots.join(', ')}`);
|
|
235
|
+
}
|
|
236
|
+
const enabled = await confirm({
|
|
237
|
+
message: 'Enable chatbot history collection?',
|
|
238
|
+
default: config.sources.chatbot.enabled,
|
|
239
|
+
});
|
|
240
|
+
if (enabled) {
|
|
241
|
+
const schedule = await select({
|
|
242
|
+
message: 'Collection schedule:',
|
|
243
|
+
choices: SCHEDULE_CHOICES,
|
|
244
|
+
loop: false,
|
|
245
|
+
default: config.sources.chatbot.schedule,
|
|
246
|
+
});
|
|
247
|
+
config.sources.chatbot = {
|
|
248
|
+
enabled: true,
|
|
249
|
+
schedule,
|
|
250
|
+
options: {
|
|
251
|
+
...config.sources.chatbot.options,
|
|
252
|
+
clients: detectedChatbots.length > 0 ? detectedChatbots : ['chatwise'],
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
config.sources.chatbot.enabled = false;
|
|
258
|
+
}
|
|
259
|
+
hasChanges = true;
|
|
260
|
+
}
|
|
261
|
+
if (choice === 'logging') {
|
|
262
|
+
const level = await select({
|
|
263
|
+
message: 'Log level:',
|
|
264
|
+
loop: false,
|
|
265
|
+
choices: [
|
|
266
|
+
{ name: 'Debug (verbose)', value: 'debug' },
|
|
267
|
+
{ name: 'Info (default)', value: 'info' },
|
|
268
|
+
{ name: 'Warn', value: 'warn' },
|
|
269
|
+
{ name: 'Error (quiet)', value: 'error' },
|
|
270
|
+
],
|
|
271
|
+
default: config.logging.level,
|
|
272
|
+
});
|
|
273
|
+
if (level !== config.logging.level) {
|
|
274
|
+
config.logging.level = level;
|
|
275
|
+
hasChanges = true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
97
279
|
});
|
|
98
280
|
configCommand
|
|
99
281
|
.command('validate')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoHpC,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoHpC,eAAO,MAAM,aAAa,SA+CtB,CAAC"}
|
|
@@ -107,7 +107,6 @@ export const doctorCommand = new Command('doctor')
|
|
|
107
107
|
const globalOpts = cmd.optsWithGlobals();
|
|
108
108
|
const customPath = globalOpts.config;
|
|
109
109
|
console.log('Running diagnostics...\n');
|
|
110
|
-
// Synchronous checks
|
|
111
110
|
const syncChecks = [
|
|
112
111
|
{ name: 'Node.js version', result: checkNodeVersion() },
|
|
113
112
|
{ name: 'Configuration file', result: checkConfigExists(customPath) },
|
|
@@ -128,7 +127,6 @@ export const doctorCommand = new Command('doctor')
|
|
|
128
127
|
hasIssues = true;
|
|
129
128
|
}
|
|
130
129
|
}
|
|
131
|
-
// Async check for API
|
|
132
130
|
const apiResult = await checkApiConnection(customPath);
|
|
133
131
|
if (apiResult.ok) {
|
|
134
132
|
printSuccess(`API connectivity: ${apiResult.message}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,eAAO,MAAM,WAAW,SAqNpB,CAAC"}
|
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
|
-
import {
|
|
4
|
-
import { input, confirm, select, checkbox, password } from '@inquirer/prompts';
|
|
3
|
+
import { confirm, select, checkbox } from '@inquirer/prompts';
|
|
5
4
|
import { detectInstalledBrowsers, detectGitInstalled, detectChatbotClients } from '../detect/index.js';
|
|
6
5
|
import { writeConfig } from '../../config/writer.js';
|
|
7
6
|
import { getUserConfigPath } from '../../config/paths.js';
|
|
8
7
|
import { printBanner, printSection, printSuccess, printInfo, printDim, printWarning } from '../utils/output.js';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
for (const p of paths) {
|
|
12
|
-
const expanded = expandPath(p);
|
|
13
|
-
if (!existsSync(expanded)) {
|
|
14
|
-
printWarning(`Path does not exist: ${p}`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
8
|
+
import { SCHEDULE_CHOICES_WITH_HINT, GIT_PATH_PRESETS, FILESYSTEM_PATH_PRESETS, selectPaths, } from '../utils/prompts.js';
|
|
9
|
+
import { inputApiUrl, inputApiKey } from '../utils/api.js';
|
|
18
10
|
export const initCommand = new Command('init')
|
|
19
11
|
.description('Initialize configuration with interactive wizard')
|
|
20
12
|
.option('--force', 'overwrite existing configuration')
|
|
21
13
|
.action(async (options, cmd) => {
|
|
22
14
|
const globalOpts = cmd.optsWithGlobals();
|
|
23
15
|
const configPath = globalOpts.config || getUserConfigPath();
|
|
24
|
-
// Check if config already exists
|
|
25
16
|
if (existsSync(configPath) && !options.force) {
|
|
26
17
|
printInfo(`Configuration already exists at ${configPath}`);
|
|
27
18
|
const overwrite = await confirm({
|
|
@@ -55,23 +46,8 @@ export const initCommand = new Command('init')
|
|
|
55
46
|
};
|
|
56
47
|
// Step 1: API Configuration
|
|
57
48
|
printSection('Step 1/5: API Configuration');
|
|
58
|
-
config.api.baseUrl = await
|
|
59
|
-
|
|
60
|
-
default: 'http://localhost:3000',
|
|
61
|
-
validate: (value) => {
|
|
62
|
-
try {
|
|
63
|
-
new URL(value);
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
return 'Please enter a valid URL';
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
config.api.apiKey = await password({
|
|
72
|
-
message: 'API Key:',
|
|
73
|
-
validate: (value) => (value.length > 0 ? true : 'API Key is required'),
|
|
74
|
-
});
|
|
49
|
+
config.api.baseUrl = await inputApiUrl();
|
|
50
|
+
config.api.apiKey = await inputApiKey();
|
|
75
51
|
// Step 2: Browser History
|
|
76
52
|
printSection('Step 2/5: Browser History');
|
|
77
53
|
const detectedBrowsers = detectInstalledBrowsers();
|
|
@@ -88,6 +64,7 @@ export const initCommand = new Command('init')
|
|
|
88
64
|
if (enableBrowser) {
|
|
89
65
|
const selectedBrowsers = await checkbox({
|
|
90
66
|
message: 'Select browsers to collect:',
|
|
67
|
+
loop: false,
|
|
91
68
|
choices: [
|
|
92
69
|
{ name: 'Chrome', value: 'chrome', checked: detectedBrowsers.includes('chrome') },
|
|
93
70
|
{ name: 'Safari', value: 'safari', checked: detectedBrowsers.includes('safari') },
|
|
@@ -96,19 +73,14 @@ export const initCommand = new Command('init')
|
|
|
96
73
|
{ name: 'Comet', value: 'comet', checked: detectedBrowsers.includes('comet') },
|
|
97
74
|
],
|
|
98
75
|
});
|
|
99
|
-
// If no browsers selected, disable the source
|
|
100
76
|
if (selectedBrowsers.length === 0) {
|
|
101
77
|
printWarning('No browsers selected, browser collection will be disabled.');
|
|
102
78
|
}
|
|
103
79
|
else {
|
|
104
80
|
const browserSchedule = await select({
|
|
105
81
|
message: 'Collection schedule:',
|
|
106
|
-
choices:
|
|
107
|
-
|
|
108
|
-
{ name: 'Daily (recommended)', value: 'daily' },
|
|
109
|
-
{ name: 'Weekly', value: 'weekly' },
|
|
110
|
-
{ name: 'Manual only', value: 'manual' },
|
|
111
|
-
],
|
|
82
|
+
choices: SCHEDULE_CHOICES_WITH_HINT,
|
|
83
|
+
loop: false,
|
|
112
84
|
default: 'daily',
|
|
113
85
|
});
|
|
114
86
|
config.sources.browser = {
|
|
@@ -131,25 +103,15 @@ export const initCommand = new Command('init')
|
|
|
131
103
|
default: true,
|
|
132
104
|
});
|
|
133
105
|
if (enableGit) {
|
|
134
|
-
const
|
|
135
|
-
const scanPathsInput = await input({
|
|
136
|
-
message: 'Paths to scan for git repositories (comma-separated):',
|
|
137
|
-
default: defaultPaths.join(',') || `${homedir()}/Projects`,
|
|
138
|
-
});
|
|
139
|
-
const scanPaths = parsePaths(scanPathsInput);
|
|
106
|
+
const scanPaths = await selectPaths('Select paths to scan for git repositories:', GIT_PATH_PRESETS);
|
|
140
107
|
if (scanPaths.length === 0) {
|
|
141
|
-
printWarning('No paths
|
|
108
|
+
printWarning('No paths selected, git collection will be disabled.');
|
|
142
109
|
}
|
|
143
110
|
else {
|
|
144
|
-
warnMissingPaths(scanPaths);
|
|
145
111
|
const gitSchedule = await select({
|
|
146
112
|
message: 'Collection schedule:',
|
|
147
|
-
choices:
|
|
148
|
-
|
|
149
|
-
{ name: 'Daily (recommended)', value: 'daily' },
|
|
150
|
-
{ name: 'Weekly', value: 'weekly' },
|
|
151
|
-
{ name: 'Manual only', value: 'manual' },
|
|
152
|
-
],
|
|
113
|
+
choices: SCHEDULE_CHOICES_WITH_HINT,
|
|
114
|
+
loop: false,
|
|
153
115
|
default: 'daily',
|
|
154
116
|
});
|
|
155
117
|
config.sources.git = {
|
|
@@ -162,26 +124,27 @@ export const initCommand = new Command('init')
|
|
|
162
124
|
};
|
|
163
125
|
}
|
|
164
126
|
}
|
|
165
|
-
// Step 4: Filesystem
|
|
127
|
+
// Step 4: Filesystem
|
|
166
128
|
printSection('Step 4/5: Filesystem Changes');
|
|
167
129
|
const enableFilesystem = await confirm({
|
|
168
130
|
message: 'Enable filesystem monitoring?',
|
|
169
131
|
default: true,
|
|
170
132
|
});
|
|
171
133
|
if (enableFilesystem) {
|
|
172
|
-
const
|
|
173
|
-
message: 'Paths to monitor (comma-separated):',
|
|
174
|
-
default: `${homedir()}/Documents,${homedir()}/Desktop`,
|
|
175
|
-
});
|
|
176
|
-
const watchPaths = parsePaths(watchPathsInput);
|
|
134
|
+
const watchPaths = await selectPaths('Select paths to monitor:', FILESYSTEM_PATH_PRESETS);
|
|
177
135
|
if (watchPaths.length === 0) {
|
|
178
|
-
printWarning('No paths
|
|
136
|
+
printWarning('No paths selected, filesystem monitoring will be disabled.');
|
|
179
137
|
}
|
|
180
138
|
else {
|
|
181
|
-
|
|
139
|
+
const fsSchedule = await select({
|
|
140
|
+
message: 'Collection schedule:',
|
|
141
|
+
choices: SCHEDULE_CHOICES_WITH_HINT,
|
|
142
|
+
loop: false,
|
|
143
|
+
default: 'daily',
|
|
144
|
+
});
|
|
182
145
|
config.sources.filesystem = {
|
|
183
146
|
enabled: true,
|
|
184
|
-
schedule:
|
|
147
|
+
schedule: fsSchedule,
|
|
185
148
|
options: {
|
|
186
149
|
watchPaths,
|
|
187
150
|
excludePatterns: ['**/node_modules/**', '**/.git/**', '**/Library/**'],
|
|
@@ -191,7 +154,7 @@ export const initCommand = new Command('init')
|
|
|
191
154
|
};
|
|
192
155
|
}
|
|
193
156
|
}
|
|
194
|
-
// Step 5: Chatbot
|
|
157
|
+
// Step 5: Chatbot
|
|
195
158
|
printSection('Step 5/5: Chatbot History');
|
|
196
159
|
const detectedChatbots = detectChatbotClients();
|
|
197
160
|
if (detectedChatbots.length > 0) {
|
|
@@ -215,7 +178,6 @@ export const initCommand = new Command('init')
|
|
|
215
178
|
},
|
|
216
179
|
};
|
|
217
180
|
}
|
|
218
|
-
// Summary
|
|
219
181
|
printSection('Configuration Summary');
|
|
220
182
|
console.log(` API: ${config.api.baseUrl}`);
|
|
221
183
|
console.log(' Sources enabled:');
|
|
@@ -223,7 +185,6 @@ export const initCommand = new Command('init')
|
|
|
223
185
|
console.log(` ${config.sources.git.enabled ? '✓' : '✗'} Git`);
|
|
224
186
|
console.log(` ${config.sources.filesystem.enabled ? '✓' : '✗'} Filesystem`);
|
|
225
187
|
console.log(` ${config.sources.chatbot.enabled ? '✓' : '✗'} Chatbot`);
|
|
226
|
-
// Save
|
|
227
188
|
const shouldSave = await confirm({
|
|
228
189
|
message: `Save configuration to ${configPath}?`,
|
|
229
190
|
default: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,YAAY,SA6DrB,CAAC"}
|
|
@@ -12,7 +12,6 @@ export const startCommand = new Command('start')
|
|
|
12
12
|
const globalOpts = cmd.optsWithGlobals();
|
|
13
13
|
try {
|
|
14
14
|
const config = loadConfig(globalOpts.config);
|
|
15
|
-
// Apply global log level overrides
|
|
16
15
|
let logLevel = config.logging.level;
|
|
17
16
|
if (globalOpts.verbose)
|
|
18
17
|
logLevel = 'debug';
|
package/dist/cli/index.js
CHANGED
|
@@ -9,7 +9,6 @@ import { collectCommand } from './commands/collect.js';
|
|
|
9
9
|
import { configCommand } from './commands/config.js';
|
|
10
10
|
import { statusCommand } from './commands/status.js';
|
|
11
11
|
import { doctorCommand } from './commands/doctor.js';
|
|
12
|
-
// Read version from package.json
|
|
13
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
13
|
const packagePath = join(__dirname, '../../package.json');
|
|
15
14
|
const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input API URL with validation
|
|
3
|
+
*/
|
|
4
|
+
export declare function inputApiUrl(defaultUrl?: string): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Input API Key with masked confirmation
|
|
7
|
+
*/
|
|
8
|
+
export declare function inputApiKey(): Promise<string>;
|
|
9
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAsB,WAAW,CAAC,UAAU,SAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAavF;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAOnD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { input } from '@inquirer/prompts';
|
|
2
|
+
import { showMaskedKey } from './prompts.js';
|
|
3
|
+
/**
|
|
4
|
+
* Input API URL with validation
|
|
5
|
+
*/
|
|
6
|
+
export async function inputApiUrl(defaultUrl = 'http://localhost:3000') {
|
|
7
|
+
return input({
|
|
8
|
+
message: 'API Base URL:',
|
|
9
|
+
default: defaultUrl,
|
|
10
|
+
validate: (value) => {
|
|
11
|
+
try {
|
|
12
|
+
new URL(value);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return 'Please enter a valid URL';
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Input API Key with masked confirmation
|
|
23
|
+
*/
|
|
24
|
+
export async function inputApiKey() {
|
|
25
|
+
const apiKey = await input({
|
|
26
|
+
message: 'API Key:',
|
|
27
|
+
validate: (value) => (value.length > 0 ? true : 'API Key is required'),
|
|
28
|
+
});
|
|
29
|
+
showMaskedKey(apiKey);
|
|
30
|
+
return apiKey;
|
|
31
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mask an API key for display (show first 8 and last 4 chars)
|
|
3
|
+
*/
|
|
4
|
+
export declare function maskApiKey(key: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Show masked API key confirmation
|
|
7
|
+
*/
|
|
8
|
+
export declare function showMaskedKey(key: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Standard schedule choices for select prompts
|
|
11
|
+
*/
|
|
12
|
+
export declare const SCHEDULE_CHOICES: ({
|
|
13
|
+
name: string;
|
|
14
|
+
value: "hourly";
|
|
15
|
+
} | {
|
|
16
|
+
name: string;
|
|
17
|
+
value: "daily";
|
|
18
|
+
} | {
|
|
19
|
+
name: string;
|
|
20
|
+
value: "weekly";
|
|
21
|
+
} | {
|
|
22
|
+
name: string;
|
|
23
|
+
value: "monthly";
|
|
24
|
+
} | {
|
|
25
|
+
name: string;
|
|
26
|
+
value: "manual";
|
|
27
|
+
})[];
|
|
28
|
+
/**
|
|
29
|
+
* Schedule choices with (recommended) hint for init wizard
|
|
30
|
+
*/
|
|
31
|
+
export declare const SCHEDULE_CHOICES_WITH_HINT: ({
|
|
32
|
+
name: string;
|
|
33
|
+
value: "hourly";
|
|
34
|
+
} | {
|
|
35
|
+
name: string;
|
|
36
|
+
value: "daily";
|
|
37
|
+
} | {
|
|
38
|
+
name: string;
|
|
39
|
+
value: "weekly";
|
|
40
|
+
} | {
|
|
41
|
+
name: string;
|
|
42
|
+
value: "manual";
|
|
43
|
+
})[];
|
|
44
|
+
/** Preset paths for Git repository scanning */
|
|
45
|
+
export declare const GIT_PATH_PRESETS: {
|
|
46
|
+
path: string;
|
|
47
|
+
defaultChecked: boolean;
|
|
48
|
+
}[];
|
|
49
|
+
/** Preset paths for Filesystem monitoring */
|
|
50
|
+
export declare const FILESYSTEM_PATH_PRESETS: {
|
|
51
|
+
path: string;
|
|
52
|
+
defaultChecked: boolean;
|
|
53
|
+
}[];
|
|
54
|
+
interface PathPreset {
|
|
55
|
+
path: string;
|
|
56
|
+
defaultChecked: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Interactive path selector with presets and custom path support
|
|
60
|
+
*/
|
|
61
|
+
export declare function selectPaths(message: string, presets: PathPreset[], currentPaths?: string[]): Promise<string[]>;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/prompts.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;IAM5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;IAKtC,CAAC;AAIF,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB;;;GAI5B,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,uBAAuB;;;GAInC,CAAC;AAIF,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,UAAU,EAAE,EACrB,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC,CAyCnB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { checkbox, input } from '@inquirer/prompts';
|
|
4
|
+
import { printDim } from './output.js';
|
|
5
|
+
import { expandPath } from '../../utils/path.js';
|
|
6
|
+
/**
|
|
7
|
+
* Mask an API key for display (show first 8 and last 4 chars)
|
|
8
|
+
*/
|
|
9
|
+
export function maskApiKey(key) {
|
|
10
|
+
return key.length > 12 ? key.slice(0, 8) + '***...' + key.slice(-4) : '***';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Show masked API key confirmation
|
|
14
|
+
*/
|
|
15
|
+
export function showMaskedKey(key) {
|
|
16
|
+
printDim(` Saved as: ${maskApiKey(key)}`);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Standard schedule choices for select prompts
|
|
20
|
+
*/
|
|
21
|
+
export const SCHEDULE_CHOICES = [
|
|
22
|
+
{ name: 'Hourly', value: 'hourly' },
|
|
23
|
+
{ name: 'Daily', value: 'daily' },
|
|
24
|
+
{ name: 'Weekly', value: 'weekly' },
|
|
25
|
+
{ name: 'Monthly', value: 'monthly' },
|
|
26
|
+
{ name: 'Manual only', value: 'manual' },
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Schedule choices with (recommended) hint for init wizard
|
|
30
|
+
*/
|
|
31
|
+
export const SCHEDULE_CHOICES_WITH_HINT = [
|
|
32
|
+
{ name: 'Hourly', value: 'hourly' },
|
|
33
|
+
{ name: 'Daily (recommended)', value: 'daily' },
|
|
34
|
+
{ name: 'Weekly', value: 'weekly' },
|
|
35
|
+
{ name: 'Manual only', value: 'manual' },
|
|
36
|
+
];
|
|
37
|
+
const home = homedir();
|
|
38
|
+
/** Preset paths for Git repository scanning */
|
|
39
|
+
export const GIT_PATH_PRESETS = [
|
|
40
|
+
{ path: `${home}/Documents`, defaultChecked: true },
|
|
41
|
+
{ path: `${home}/Desktop`, defaultChecked: false },
|
|
42
|
+
{ path: `${home}/Developer`, defaultChecked: false },
|
|
43
|
+
];
|
|
44
|
+
/** Preset paths for Filesystem monitoring */
|
|
45
|
+
export const FILESYSTEM_PATH_PRESETS = [
|
|
46
|
+
{ path: `${home}/Documents`, defaultChecked: true },
|
|
47
|
+
{ path: `${home}/Desktop`, defaultChecked: true },
|
|
48
|
+
{ path: `${home}/Downloads`, defaultChecked: true },
|
|
49
|
+
];
|
|
50
|
+
const ADD_CUSTOM_PATH = '__add_custom__';
|
|
51
|
+
/**
|
|
52
|
+
* Interactive path selector with presets and custom path support
|
|
53
|
+
*/
|
|
54
|
+
export async function selectPaths(message, presets, currentPaths) {
|
|
55
|
+
const existingPresets = presets.filter((p) => existsSync(p.path));
|
|
56
|
+
const choices = existingPresets.map((preset) => {
|
|
57
|
+
const isChecked = currentPaths ? currentPaths.includes(preset.path) : preset.defaultChecked;
|
|
58
|
+
return {
|
|
59
|
+
name: preset.path.replace(home, '~'),
|
|
60
|
+
value: preset.path,
|
|
61
|
+
checked: isChecked,
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
choices.push({
|
|
65
|
+
name: '── Add custom path...',
|
|
66
|
+
value: ADD_CUSTOM_PATH,
|
|
67
|
+
checked: false,
|
|
68
|
+
});
|
|
69
|
+
const selected = await checkbox({
|
|
70
|
+
message,
|
|
71
|
+
choices,
|
|
72
|
+
loop: false,
|
|
73
|
+
});
|
|
74
|
+
if (selected.includes(ADD_CUSTOM_PATH)) {
|
|
75
|
+
const filtered = selected.filter((p) => p !== ADD_CUSTOM_PATH);
|
|
76
|
+
const customPath = await input({
|
|
77
|
+
message: 'Enter custom path:',
|
|
78
|
+
validate: (value) => {
|
|
79
|
+
if (!value.trim())
|
|
80
|
+
return 'Path cannot be empty';
|
|
81
|
+
const expanded = expandPath(value.trim());
|
|
82
|
+
if (!existsSync(expanded)) {
|
|
83
|
+
return `Path does not exist: ${value}`;
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return [...filtered, expandPath(customPath.trim())];
|
|
89
|
+
}
|
|
90
|
+
return selected;
|
|
91
|
+
}
|
package/dist/core/collector.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CollectorConfig } from '../config/schema.js';
|
|
2
|
-
import type { SourceType } from './types.js';
|
|
2
|
+
import type { CollectionResult, SourceType } from './types.js';
|
|
3
3
|
import type { Logger } from '../utils/logger.js';
|
|
4
4
|
export declare class Collector {
|
|
5
5
|
private config;
|
|
@@ -11,9 +11,9 @@ export declare class Collector {
|
|
|
11
11
|
validateSources(): Promise<void>;
|
|
12
12
|
start(): Promise<void>;
|
|
13
13
|
stop(): Promise<void>;
|
|
14
|
-
collectAndPush(sourceType: SourceType): Promise<
|
|
14
|
+
collectAndPush(sourceType: SourceType): Promise<CollectionResult | undefined>;
|
|
15
15
|
collectAll(): Promise<void>;
|
|
16
|
-
triggerCollection(sourceType: SourceType): Promise<
|
|
16
|
+
triggerCollection(sourceType: SourceType): Promise<CollectionResult | undefined>;
|
|
17
17
|
getEnabledSources(): SourceType[];
|
|
18
18
|
}
|
|
19
19
|
//# sourceMappingURL=collector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/core/collector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/core/collector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAA0C;gBAE7C,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM;IAO7C,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBhC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAKrB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IA4B7E,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAItF,iBAAiB,IAAI,UAAU,EAAE;CAGlC"}
|
package/dist/core/collector.js
CHANGED
|
@@ -55,18 +55,20 @@ export class Collector {
|
|
|
55
55
|
const result = await source.collect();
|
|
56
56
|
if (!result.success) {
|
|
57
57
|
this.logger.error(`Collection failed for ${sourceType}`, result.error);
|
|
58
|
-
return;
|
|
58
|
+
return result;
|
|
59
59
|
}
|
|
60
60
|
if (result.itemsCollected === 0) {
|
|
61
61
|
this.logger.info(`No new items from ${sourceType}`);
|
|
62
|
-
return;
|
|
62
|
+
return result;
|
|
63
63
|
}
|
|
64
64
|
this.logger.info(`Collected ${result.itemsCollected} items from ${sourceType}`);
|
|
65
65
|
const response = await this.apiClient.pushData(result);
|
|
66
66
|
this.logger.info(`Pushed to API: ${response.data.itemsReceived} received, ${response.data.itemsInserted} inserted`);
|
|
67
|
+
return result;
|
|
67
68
|
}
|
|
68
69
|
catch (error) {
|
|
69
70
|
this.logger.error(`Error in collect/push cycle for ${sourceType}`, error);
|
|
71
|
+
return undefined;
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
async collectAll() {
|
|
@@ -75,7 +77,7 @@ export class Collector {
|
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
async triggerCollection(sourceType) {
|
|
78
|
-
|
|
80
|
+
return this.collectAndPush(sourceType);
|
|
79
81
|
}
|
|
80
82
|
getEnabledSources() {
|
|
81
83
|
return Array.from(this.sources.keys());
|
package/dist/core/scheduler.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare class Scheduler {
|
|
|
4
4
|
private tasks;
|
|
5
5
|
private logger;
|
|
6
6
|
constructor(logger: Logger);
|
|
7
|
-
schedule(sourceType: SourceType, frequency: ScheduleFrequency, handler: () => Promise<
|
|
7
|
+
schedule(sourceType: SourceType, frequency: ScheduleFrequency, handler: () => Promise<unknown>): void;
|
|
8
8
|
unschedule(sourceType: SourceType): void;
|
|
9
9
|
startAll(): void;
|
|
10
10
|
stopAll(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAgBjD,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAI1B,QAAQ,CACN,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,MAAM,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAgBjD,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAI1B,QAAQ,CACN,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAC9B,IAAI;IAoBP,UAAU,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAQxC,QAAQ,IAAI,IAAI;IAMhB,OAAO,IAAI,IAAI;CAKhB"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -10,6 +10,16 @@ export interface CollectedItem {
|
|
|
10
10
|
timestamp: Date;
|
|
11
11
|
data: unknown;
|
|
12
12
|
}
|
|
13
|
+
/** A single skipped item with path and reason */
|
|
14
|
+
export interface SkippedItem {
|
|
15
|
+
path: string;
|
|
16
|
+
reason: string;
|
|
17
|
+
}
|
|
18
|
+
/** Information about skipped items during collection */
|
|
19
|
+
export interface SkippedInfo {
|
|
20
|
+
count: number;
|
|
21
|
+
items: SkippedItem[];
|
|
22
|
+
}
|
|
13
23
|
/** Result from a collection operation */
|
|
14
24
|
export interface CollectionResult {
|
|
15
25
|
sourceType: SourceType;
|
|
@@ -18,6 +28,8 @@ export interface CollectionResult {
|
|
|
18
28
|
items: CollectedItem[];
|
|
19
29
|
error?: Error;
|
|
20
30
|
collectedAt: Date;
|
|
31
|
+
/** Information about items that were skipped (e.g., empty repos) */
|
|
32
|
+
skipped?: SkippedInfo;
|
|
21
33
|
}
|
|
22
34
|
/** Configuration for a specific data source */
|
|
23
35
|
export interface SourceConfig {
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;AAEtE,qCAAqC;AACrC,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAyD,CAAC;AAExG,6CAA6C;AAC7C,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErF,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,yCAAyC;AACzC,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,WAAW,EAAE,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;AAEtE,qCAAqC;AACrC,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAyD,CAAC;AAExG,6CAA6C;AAC7C,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErF,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wDAAwD;AACxD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,yCAAyC;AACzC,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,WAAW,EAAE,IAAI,CAAC;IAClB,oEAAoE;IACpE,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,+CAA+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -5,11 +5,13 @@ export declare class GitSource extends DataSource<GitSourceOptions> {
|
|
|
5
5
|
readonly type: "git";
|
|
6
6
|
readonly name = "Git Commits";
|
|
7
7
|
private discoveredRepos;
|
|
8
|
+
private skippedRepos;
|
|
8
9
|
validate(): Promise<boolean>;
|
|
9
10
|
private discoverRepositories;
|
|
10
11
|
collect(): Promise<CollectionResult>;
|
|
11
12
|
private execGit;
|
|
12
13
|
private getCommitsFromRepo;
|
|
14
|
+
private isEmptyRepoError;
|
|
13
15
|
private parseGitLog;
|
|
14
16
|
private getCommitStats;
|
|
15
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sources/git/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sources/git/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAe,MAAM,qBAAqB,CAAC;AACzE,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9D,qBAAa,SAAU,SAAQ,UAAU,CAAC,gBAAgB,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,YAAY,CAAqB;IAEnC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAqClC,OAAO,CAAC,oBAAoB;IA6BtB,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC;IA+B1C,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,kBAAkB;IAkC1B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,cAAc;CA2BvB"}
|
|
@@ -7,6 +7,7 @@ export class GitSource extends DataSource {
|
|
|
7
7
|
type = 'git';
|
|
8
8
|
name = 'Git Commits';
|
|
9
9
|
discoveredRepos = [];
|
|
10
|
+
skippedRepos = [];
|
|
10
11
|
async validate() {
|
|
11
12
|
try {
|
|
12
13
|
this.execGit('.', ['--version']);
|
|
@@ -68,15 +69,13 @@ export class GitSource extends DataSource {
|
|
|
68
69
|
const items = [];
|
|
69
70
|
const sinceDate = new Date();
|
|
70
71
|
sinceDate.setDate(sinceDate.getDate() - this.options.sinceDays);
|
|
72
|
+
this.skippedRepos = [];
|
|
71
73
|
for (const repoPath of this.discoveredRepos) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const commits = this.getCommitsFromRepo(repoPath, sinceDate);
|
|
75
|
+
items.push(...commits);
|
|
76
|
+
if (commits.length > 0) {
|
|
75
77
|
this.context.logger.debug(`Found ${commits.length} commits in ${repoPath}`);
|
|
76
78
|
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
this.context.logger.error(`Failed to get commits from ${repoPath}`, error);
|
|
79
|
-
}
|
|
80
79
|
}
|
|
81
80
|
return {
|
|
82
81
|
sourceType: this.type,
|
|
@@ -88,12 +87,16 @@ export class GitSource extends DataSource {
|
|
|
88
87
|
data: commit,
|
|
89
88
|
})),
|
|
90
89
|
collectedAt: new Date(),
|
|
90
|
+
skipped: this.skippedRepos.length > 0
|
|
91
|
+
? { count: this.skippedRepos.length, items: this.skippedRepos }
|
|
92
|
+
: undefined,
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
95
|
execGit(repoPath, args) {
|
|
94
96
|
return execFileSync('git', ['-C', repoPath, ...args], {
|
|
95
97
|
encoding: 'utf-8',
|
|
96
98
|
maxBuffer: 10 * 1024 * 1024,
|
|
99
|
+
stdio: ['pipe', 'pipe', 'pipe'], // Capture stderr instead of inheriting
|
|
97
100
|
});
|
|
98
101
|
}
|
|
99
102
|
getCommitsFromRepo(repoPath, since) {
|
|
@@ -117,10 +120,19 @@ export class GitSource extends DataSource {
|
|
|
117
120
|
});
|
|
118
121
|
}
|
|
119
122
|
catch (error) {
|
|
123
|
+
if (this.isEmptyRepoError(error)) {
|
|
124
|
+
this.skippedRepos.push({ path: repoPath, reason: 'empty repository' });
|
|
125
|
+
this.context.logger.debug(`Skipping empty repository: ${repoPath}`);
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
120
128
|
this.context.logger.warn(`Failed to get git log from ${repoPath}`, error);
|
|
121
129
|
return [];
|
|
122
130
|
}
|
|
123
131
|
}
|
|
132
|
+
isEmptyRepoError(error) {
|
|
133
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
134
|
+
return message.includes('does not have any commits yet');
|
|
135
|
+
}
|
|
124
136
|
parseGitLog(output, repoPath) {
|
|
125
137
|
const commits = [];
|
|
126
138
|
const records = output.split('\0\0').filter(Boolean);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liferewind",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "AI-powered personal life review tool - collect your digital footprints from git, browser history, documents, and AI chatbots",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -43,18 +43,6 @@
|
|
|
43
43
|
"LICENSE",
|
|
44
44
|
"README.md"
|
|
45
45
|
],
|
|
46
|
-
"scripts": {
|
|
47
|
-
"build": "tsc",
|
|
48
|
-
"dev": "tsx watch src/cli/index.ts",
|
|
49
|
-
"start": "node dist/cli/index.js start",
|
|
50
|
-
"lint": "eslint . --max-warnings 0",
|
|
51
|
-
"typecheck": "tsc --noEmit",
|
|
52
|
-
"prepublishOnly": "npm run build",
|
|
53
|
-
"pm2:start": "pm2 start ecosystem.config.cjs",
|
|
54
|
-
"pm2:stop": "pm2 stop liferewind-collector",
|
|
55
|
-
"pm2:restart": "pm2 restart liferewind-collector",
|
|
56
|
-
"pm2:logs": "pm2 logs liferewind-collector"
|
|
57
|
-
},
|
|
58
46
|
"dependencies": {
|
|
59
47
|
"@inquirer/prompts": "^8.1.0",
|
|
60
48
|
"better-sqlite3": "^12.5.0",
|
|
@@ -69,13 +57,24 @@
|
|
|
69
57
|
"@types/better-sqlite3": "^7.6.13",
|
|
70
58
|
"@types/node": "^20.19.25",
|
|
71
59
|
"@types/node-cron": "^3.0.11",
|
|
72
|
-
"@workspace/eslint-config": "workspace:*",
|
|
73
|
-
"@workspace/typescript-config": "workspace:*",
|
|
74
60
|
"eslint": "^9.39.1",
|
|
75
61
|
"tsx": "^4.19.0",
|
|
76
|
-
"typescript": "^5.9.3"
|
|
62
|
+
"typescript": "^5.9.3",
|
|
63
|
+
"@workspace/typescript-config": "0.0.0",
|
|
64
|
+
"@workspace/eslint-config": "0.0.0"
|
|
77
65
|
},
|
|
78
66
|
"engines": {
|
|
79
67
|
"node": ">=20"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "tsc",
|
|
71
|
+
"dev": "tsx watch src/cli/index.ts",
|
|
72
|
+
"start": "node dist/cli/index.js start",
|
|
73
|
+
"lint": "eslint . --max-warnings 0",
|
|
74
|
+
"typecheck": "tsc --noEmit",
|
|
75
|
+
"pm2:start": "pm2 start ecosystem.config.cjs",
|
|
76
|
+
"pm2:stop": "pm2 stop liferewind-collector",
|
|
77
|
+
"pm2:restart": "pm2 restart liferewind-collector",
|
|
78
|
+
"pm2:logs": "pm2 logs liferewind-collector"
|
|
80
79
|
}
|
|
81
|
-
}
|
|
80
|
+
}
|
package/dist/cli/utils/path.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Expand a path that may start with ~ or ~/ to an absolute path.
|
|
3
|
-
*/
|
|
4
|
-
export declare function expandPath(path: string): string;
|
|
5
|
-
/**
|
|
6
|
-
* Parse comma-separated paths and trim whitespace
|
|
7
|
-
*/
|
|
8
|
-
export declare function parsePaths(input: string): string[];
|
|
9
|
-
//# sourceMappingURL=path.d.ts.map
|
package/dist/cli/utils/path.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
/**
|
|
4
|
-
* Expand a path that may start with ~ or ~/ to an absolute path.
|
|
5
|
-
*/
|
|
6
|
-
export function expandPath(path) {
|
|
7
|
-
if (path === '~') {
|
|
8
|
-
return homedir();
|
|
9
|
-
}
|
|
10
|
-
if (path.startsWith('~/')) {
|
|
11
|
-
return resolve(homedir(), path.slice(2));
|
|
12
|
-
}
|
|
13
|
-
return resolve(path);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Parse comma-separated paths and trim whitespace
|
|
17
|
-
*/
|
|
18
|
-
export function parsePaths(input) {
|
|
19
|
-
return input
|
|
20
|
-
.split(',')
|
|
21
|
-
.map((p) => p.trim())
|
|
22
|
-
.filter(Boolean);
|
|
23
|
-
}
|