@lincoln504/pi-research 0.0.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/LICENSE +21 -0
- package/README.md +131 -0
- package/config/searxng/default-settings.yml +189 -0
- package/config/searxng/limiter.toml +34 -0
- package/config/tooling/eslint.config.js +73 -0
- package/config/tooling/vitest.config.integration.ts +17 -0
- package/config/tooling/vitest.config.ts +40 -0
- package/config/tooling/vitest.config.unit.ts +12 -0
- package/docs/ARCHITECTURE.md +109 -0
- package/docs/assets/README-banner.jpg +0 -0
- package/package.json +92 -0
- package/scripts/setup.js +174 -0
- package/src/config.ts +206 -0
- package/src/constants.ts +148 -0
- package/src/healthcheck/index.ts +309 -0
- package/src/index.ts +71 -0
- package/src/infrastructure/searxng-lifecycle.ts +577 -0
- package/src/infrastructure/searxng-manager.ts +1173 -0
- package/src/infrastructure/state-manager.ts +918 -0
- package/src/logger.ts +246 -0
- package/src/orchestration/deep-research-orchestrator.ts +848 -0
- package/src/orchestration/deep-research-reducer.ts +129 -0
- package/src/orchestration/deep-research-types.ts +54 -0
- package/src/orchestration/id-utils.ts +63 -0
- package/src/orchestration/researcher.ts +104 -0
- package/src/orchestration/session-context.ts +43 -0
- package/src/prompts/research-tool-usage.md +45 -0
- package/src/prompts/researcher.md +166 -0
- package/src/prompts/system-coordinator.md +24 -0
- package/src/prompts/system-lead-evaluator.md +225 -0
- package/src/security/cisa-kev.ts +242 -0
- package/src/security/github-advisories.ts +607 -0
- package/src/security/index.ts +406 -0
- package/src/security/nvd.ts +532 -0
- package/src/security/osv.ts +487 -0
- package/src/security/types.ts +138 -0
- package/src/stackexchange/cache.ts +111 -0
- package/src/stackexchange/index.ts +352 -0
- package/src/stackexchange/output/compact.ts +64 -0
- package/src/stackexchange/output/index.ts +7 -0
- package/src/stackexchange/output/json.ts +21 -0
- package/src/stackexchange/output/table.ts +123 -0
- package/src/stackexchange/queries.ts +168 -0
- package/src/stackexchange/rest-client.ts +127 -0
- package/src/stackexchange/types.ts +129 -0
- package/src/tool.ts +430 -0
- package/src/tools/grep.ts +335 -0
- package/src/tools/index.ts +77 -0
- package/src/tools/scrape.ts +451 -0
- package/src/tools/search.ts +188 -0
- package/src/tools/security.ts +292 -0
- package/src/tools/stackexchange.ts +101 -0
- package/src/tui/research-panel.ts +576 -0
- package/src/tui/searxng-status.ts +80 -0
- package/src/types/extension-context.ts +60 -0
- package/src/types/llm.ts +111 -0
- package/src/utils/inject-date.ts +29 -0
- package/src/utils/input-validation.ts +103 -0
- package/src/utils/json-utils.ts +262 -0
- package/src/utils/make-resource-loader.ts +43 -0
- package/src/utils/research-error.ts +205 -0
- package/src/utils/research-export.ts +199 -0
- package/src/utils/searxng-config.ts +94 -0
- package/src/utils/session-state.ts +295 -0
- package/src/utils/shared-links.ts +123 -0
- package/src/utils/shutdown-manager.ts +54 -0
- package/src/utils/text-utils.ts +83 -0
- package/src/utils/tool-usage-tracker.ts +135 -0
- package/src/web-research/dependencies.ts +20 -0
- package/src/web-research/retry-utils.ts +232 -0
- package/src/web-research/scrapers.ts +935 -0
- package/src/web-research/search.ts +235 -0
- package/src/web-research/types.ts +47 -0
- package/src/web-research/utils.ts +158 -0
- package/tsconfig.json +40 -0
package/src/logger.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger — scoped file-based diagnostics
|
|
3
|
+
*
|
|
4
|
+
* Silent by default. When --verbose or PI_RESEARCH_VERBOSE=1 is set,
|
|
5
|
+
* writes timestamped lines to {tmpdir}/pi-research-debug-{hash}.log where {hash}
|
|
6
|
+
* is a random 4-character alphanumeric suffix (a-z, 0-9) to keep logs separate per run.
|
|
7
|
+
*
|
|
8
|
+
* When NOT verbose: logFile is null, preventing all temp-dir writes.
|
|
9
|
+
* This module never patches process-global console.* methods.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { appendFileSync } from 'node:fs';
|
|
13
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
14
|
+
import { randomBytes } from 'node:crypto';
|
|
15
|
+
import * as os from 'node:os';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Log level enum
|
|
20
|
+
*/
|
|
21
|
+
export enum LogLevel {
|
|
22
|
+
INFO = 'INFO',
|
|
23
|
+
ERROR = 'ERROR',
|
|
24
|
+
WARN = 'WARN',
|
|
25
|
+
DEBUG = 'DEBUG',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Logger interface for dependency injection
|
|
30
|
+
*/
|
|
31
|
+
export interface ILogger {
|
|
32
|
+
log(...args: unknown[]): void;
|
|
33
|
+
info(...args: unknown[]): void;
|
|
34
|
+
error(...args: unknown[]): void;
|
|
35
|
+
warn(...args: unknown[]): void;
|
|
36
|
+
debug(...args: unknown[]): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Logger configuration options
|
|
41
|
+
*/
|
|
42
|
+
export interface LoggerOptions {
|
|
43
|
+
verbose: boolean;
|
|
44
|
+
logFilePath?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface LogContext {
|
|
48
|
+
sessionId?: string;
|
|
49
|
+
sessionFile?: string;
|
|
50
|
+
cwd?: string;
|
|
51
|
+
researchRunId?: string;
|
|
52
|
+
toolName?: string;
|
|
53
|
+
phase?: string;
|
|
54
|
+
eventName?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const logContextStorage = new AsyncLocalStorage<LogContext>();
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generate a 4-character alphanumeric hash for uniqueness
|
|
61
|
+
*/
|
|
62
|
+
function generateHash(): string {
|
|
63
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
64
|
+
let hash = '';
|
|
65
|
+
for (let i = 0; i < 4; i++) {
|
|
66
|
+
hash += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
67
|
+
}
|
|
68
|
+
return hash;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildDefaultDebugLogPath(hash: string): string {
|
|
72
|
+
return path.join(os.tmpdir(), `pi-research-debug-${hash}.log`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function getDefaultDebugLogPathTemplate(): string {
|
|
76
|
+
return buildDefaultDebugLogPath('{hash}');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if verbose mode is enabled from environment
|
|
81
|
+
*/
|
|
82
|
+
export function isVerboseFromEnv(): boolean {
|
|
83
|
+
return process.argv.includes('--verbose') || process.env['PI_RESEARCH_VERBOSE'] === '1';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function createResearchRunId(): string {
|
|
87
|
+
return `run-${randomBytes(4).toString('hex')}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getLogContext(): LogContext {
|
|
91
|
+
return logContextStorage.getStore() ?? {};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function runWithLogContext<T>(context: LogContext, callback: () => T): T {
|
|
95
|
+
const parent = logContextStorage.getStore() ?? {};
|
|
96
|
+
return logContextStorage.run({ ...parent, ...context }, callback);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function safeJsonStringify(value: unknown): string {
|
|
100
|
+
try {
|
|
101
|
+
return JSON.stringify(value, null, 0);
|
|
102
|
+
} catch {
|
|
103
|
+
return '[unserializable]';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatArg(arg: unknown): string {
|
|
108
|
+
if (arg instanceof Error) {
|
|
109
|
+
return arg.stack ?? arg.message;
|
|
110
|
+
}
|
|
111
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
112
|
+
return safeJsonStringify(arg);
|
|
113
|
+
}
|
|
114
|
+
return String(arg);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Logger implementation — writes to file when verbose, silent otherwise
|
|
119
|
+
*/
|
|
120
|
+
export class Logger implements ILogger {
|
|
121
|
+
private verbose: boolean;
|
|
122
|
+
private logFile: string | null;
|
|
123
|
+
|
|
124
|
+
constructor(options: Partial<LoggerOptions> = {}) {
|
|
125
|
+
this.verbose = options.verbose ?? isVerboseFromEnv();
|
|
126
|
+
// Only set logFile path if verbose mode is enabled
|
|
127
|
+
if (this.verbose) {
|
|
128
|
+
const hash = generateHash();
|
|
129
|
+
this.logFile = options.logFilePath ?? buildDefaultDebugLogPath(hash);
|
|
130
|
+
} else {
|
|
131
|
+
this.logFile = null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Emit a log message to file (if verbose) or nowhere (if silent)
|
|
137
|
+
* @param level - Log level (INFO, ERROR, WARN, DEBUG)
|
|
138
|
+
* @param args - Arguments to log
|
|
139
|
+
*/
|
|
140
|
+
private emit(level: string, ...args: unknown[]): void {
|
|
141
|
+
if (!this.verbose || !this.logFile) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const timestamp = new Date().toISOString();
|
|
146
|
+
const firstError = args.find((arg): arg is Error => arg instanceof Error);
|
|
147
|
+
const entry = {
|
|
148
|
+
timestamp,
|
|
149
|
+
level,
|
|
150
|
+
...getLogContext(),
|
|
151
|
+
message: args.map(formatArg).join(' '),
|
|
152
|
+
...(firstError
|
|
153
|
+
? { errorMessage: firstError.message, errorStack: firstError.stack }
|
|
154
|
+
: {}),
|
|
155
|
+
};
|
|
156
|
+
const line = `${safeJsonStringify(entry)}\n`;
|
|
157
|
+
|
|
158
|
+
// Write to file
|
|
159
|
+
try {
|
|
160
|
+
appendFileSync(this.logFile, line);
|
|
161
|
+
} catch {
|
|
162
|
+
// Silently ignore file write errors
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
log(...args: unknown[]): void {
|
|
167
|
+
this.emit(LogLevel.INFO, ...args);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
info(...args: unknown[]): void {
|
|
171
|
+
this.emit(LogLevel.INFO, ...args);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
error(...args: unknown[]): void {
|
|
175
|
+
this.emit(LogLevel.ERROR, ...args);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
warn(...args: unknown[]): void {
|
|
179
|
+
this.emit(LogLevel.WARN, ...args);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
debug(...args: unknown[]): void {
|
|
183
|
+
this.emit(LogLevel.DEBUG, ...args);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get the log file path
|
|
188
|
+
*/
|
|
189
|
+
getLogFilePath(): string | null {
|
|
190
|
+
return this.logFile;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Check if verbose mode is enabled
|
|
195
|
+
*/
|
|
196
|
+
isVerbose(): boolean {
|
|
197
|
+
return this.verbose;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Global logger instance
|
|
203
|
+
*/
|
|
204
|
+
let globalLogger: Logger | null = null;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Create a new logger instance
|
|
208
|
+
*/
|
|
209
|
+
export function createLogger(options: Partial<LoggerOptions> = {}): Logger {
|
|
210
|
+
return new Logger(options);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get the global logger instance (singleton)
|
|
215
|
+
*/
|
|
216
|
+
export function getLogger(): Logger {
|
|
217
|
+
if (!globalLogger) {
|
|
218
|
+
globalLogger = createLogger({ verbose: isVerboseFromEnv() });
|
|
219
|
+
}
|
|
220
|
+
return globalLogger;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Set the global logger instance (for testing)
|
|
225
|
+
*/
|
|
226
|
+
export function setLogger(logger: Logger): void {
|
|
227
|
+
globalLogger = logger;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Reset the global logger instance (for testing)
|
|
232
|
+
*/
|
|
233
|
+
export function resetLogger(): void {
|
|
234
|
+
globalLogger = null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Logger singleton for backward compatibility
|
|
239
|
+
*/
|
|
240
|
+
export const logger = {
|
|
241
|
+
log: (...args: unknown[]) => getLogger().log(...args),
|
|
242
|
+
info: (...args: unknown[]) => getLogger().info(...args),
|
|
243
|
+
error: (...args: unknown[]) => getLogger().error(...args),
|
|
244
|
+
warn: (...args: unknown[]) => getLogger().warn(...args),
|
|
245
|
+
debug: (...args: unknown[]) => getLogger().debug(...args),
|
|
246
|
+
};
|