ownkey 0.2.0 → 0.5.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.
Files changed (49) hide show
  1. package/README.md +64 -32
  2. package/dist/ai/factory.d.ts +23 -0
  3. package/dist/ai/factory.d.ts.map +1 -0
  4. package/dist/ai/factory.js +96 -0
  5. package/dist/ai/factory.js.map +1 -0
  6. package/dist/ai/gemini.d.ts +21 -0
  7. package/dist/ai/gemini.d.ts.map +1 -0
  8. package/dist/ai/gemini.js +87 -0
  9. package/dist/ai/gemini.js.map +1 -0
  10. package/dist/ai/provider.d.ts +39 -0
  11. package/dist/ai/provider.d.ts.map +1 -0
  12. package/dist/ai/provider.js +99 -0
  13. package/dist/ai/provider.js.map +1 -0
  14. package/dist/commands/config.js +3 -1
  15. package/dist/commands/config.js.map +1 -1
  16. package/dist/commands/suggest.d.ts.map +1 -1
  17. package/dist/commands/suggest.js +102 -10
  18. package/dist/commands/suggest.js.map +1 -1
  19. package/dist/config/defaults.d.ts.map +1 -1
  20. package/dist/config/defaults.js +5 -2
  21. package/dist/config/defaults.js.map +1 -1
  22. package/dist/config/keychain.d.ts.map +1 -1
  23. package/dist/config/keychain.js +12 -0
  24. package/dist/config/keychain.js.map +1 -1
  25. package/dist/db/client.d.ts.map +1 -1
  26. package/dist/db/client.js +41 -15
  27. package/dist/db/client.js.map +1 -1
  28. package/dist/types/index.d.ts +4 -1
  29. package/dist/types/index.d.ts.map +1 -1
  30. package/dist/types/index.js.map +1 -1
  31. package/package.json +18 -2
  32. package/.prettierrc +0 -9
  33. package/src/commands/apply.ts +0 -27
  34. package/src/commands/config.ts +0 -154
  35. package/src/commands/scan.ts +0 -114
  36. package/src/commands/suggest.ts +0 -37
  37. package/src/config/defaults.ts +0 -80
  38. package/src/config/keychain.ts +0 -125
  39. package/src/config/manager.ts +0 -166
  40. package/src/core/scanner.ts +0 -174
  41. package/src/db/client.ts +0 -151
  42. package/src/db/operations.ts +0 -217
  43. package/src/db/schema.ts +0 -118
  44. package/src/index.ts +0 -74
  45. package/src/types/index.ts +0 -198
  46. package/src/utils/encryption.ts +0 -54
  47. package/src/utils/logger.ts +0 -111
  48. package/tsconfig.json +0 -33
  49. /package/{src → dist}/db/migrations/001_init.sql +0 -0
package/src/index.ts DELETED
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import { logger } from './utils/logger.js';
5
- import { configManager } from './config/manager.js';
6
- import { dbClient } from './db/client.js';
7
- import chalk from 'chalk';
8
-
9
- // Import commands
10
- import { configCommand } from './commands/config.js';
11
- import { scanCommand } from './commands/scan.js';
12
- import { suggestCommand } from './commands/suggest.js';
13
- import { applyCommand } from './commands/apply.js';
14
-
15
- const program = new Command();
16
-
17
- program
18
- .name('ownkey')
19
- .description('Local-first AI coding agent for intelligent codebase analysis')
20
- .version('0.2.0');
21
-
22
- // Global options
23
- program.option('--local-only', 'Run without database persistence');
24
- program.option('--verbose', 'Enable verbose logging');
25
- program.option('--debug', 'Enable debug logging');
26
- program.option('--quiet', 'Suppress non-essential output');
27
-
28
- // Commands
29
- program.addCommand(configCommand);
30
- program.addCommand(scanCommand);
31
- program.addCommand(suggestCommand);
32
- program.addCommand(applyCommand);
33
-
34
- // Pre-parse hook to set up logging and check config
35
- program.hook('preAction', async (thisCommand) => {
36
- await logger.init();
37
-
38
- const opts = thisCommand.opts();
39
-
40
- // Set log level
41
- if (opts.debug) {
42
- logger.setLevel('debug');
43
- } else if (opts.verbose) {
44
- logger.setLevel('verbose');
45
- } else if (opts.quiet) {
46
- logger.setLevel('quiet');
47
- }
48
-
49
- // Check if this is the config command
50
- const isConfigCommand = thisCommand.name() === 'config';
51
-
52
- // For non-config commands, check if config exists
53
- if (!isConfigCommand) {
54
- const configExists = await configManager.exists();
55
-
56
- if (!configExists) {
57
- console.log(chalk.yellow('\n⚠️ No configuration found.\n'));
58
- console.log('OwnKey needs to be configured before use.');
59
- console.log(chalk.cyan('\nRun: ownkey config\n'));
60
- process.exit(1);
61
- }
62
-
63
- // Connect to database (or enter local-only mode)
64
- await dbClient.connect(opts.localOnly);
65
- }
66
- });
67
-
68
- // Post-action hook to clean up
69
- program.hook('postAction', async () => {
70
- await dbClient.disconnect();
71
- });
72
-
73
- // Parse arguments
74
- program.parse();
@@ -1,198 +0,0 @@
1
- // Core type definitions for OwnKey CLI
2
-
3
- export type LogLevel = 'quiet' | 'normal' | 'verbose' | 'debug';
4
-
5
- export type AIProvider = 'openai' | 'anthropic' | 'ollama';
6
-
7
- export type SuggestionType =
8
- | 'bug'
9
- | 'security'
10
- | 'performance'
11
- | 'refactor'
12
- | 'style'
13
- | 'architecture'
14
- | 'test'
15
- | 'documentation';
16
-
17
- export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
18
-
19
- export type RunStatus = 'running' | 'completed' | 'failed';
20
-
21
- export type SuggestionAction = 'applied' | 'rejected' | 'undone';
22
-
23
- // Configuration types
24
- export interface Config {
25
- supabaseUrl?: string;
26
- supabaseServiceRoleKey?: string;
27
- providers: {
28
- openai?: {
29
- model?: string;
30
- };
31
- anthropic?: {
32
- model?: string;
33
- };
34
- ollama?: {
35
- endpoint?: string;
36
- model?: string;
37
- };
38
- };
39
- activeProvider?: AIProvider;
40
- defaults: {
41
- maxFileSize: string;
42
- logLevel: LogLevel;
43
- };
44
- }
45
-
46
- // File scanning types
47
- export interface FileMetadata {
48
- path: string;
49
- relativePath: string;
50
- size: number;
51
- lastModified: Date;
52
- extension: string;
53
- }
54
-
55
- export interface FileContent extends FileMetadata {
56
- content: string;
57
- }
58
-
59
- export interface ScanResult {
60
- files: FileMetadata[];
61
- totalSize: number;
62
- skippedFiles: string[];
63
- warnings: string[];
64
- }
65
-
66
- // Code context types
67
- export interface CodeContext {
68
- files: FileContent[];
69
- projectInfo: ProjectMetadata;
70
- totalTokens?: number;
71
- }
72
-
73
- export interface ProjectMetadata {
74
- name: string;
75
- rootPath: string;
76
- fileCount: number;
77
- language?: string;
78
- framework?: string;
79
- }
80
-
81
- // AI provider types
82
- export interface Suggestion {
83
- type: SuggestionType;
84
- severity: Severity;
85
- content: string;
86
- diff: string;
87
- filePath: string;
88
- rationale: string;
89
- tags?: Record<string, any>;
90
- }
91
-
92
- export interface AnalyzeOptions {
93
- streaming?: boolean;
94
- maxTokens?: number;
95
- temperature?: number;
96
- }
97
-
98
- export interface AIProviderInterface {
99
- analyze(context: CodeContext, options?: AnalyzeOptions): AsyncGenerator<Suggestion>;
100
- testConnection(): Promise<boolean>;
101
- }
102
-
103
- // Database types
104
- export interface Project {
105
- id: string;
106
- name: string;
107
- rootPath: string;
108
- createdAt: Date;
109
- updatedAt: Date;
110
- }
111
-
112
- export interface Run {
113
- id: string;
114
- projectId: string;
115
- command: string;
116
- status: RunStatus;
117
- summary?: string;
118
- startedAt: Date;
119
- finishedAt?: Date;
120
- }
121
-
122
- export interface SuggestionRecord extends Suggestion {
123
- id: string;
124
- projectId: string;
125
- createdAt: Date;
126
- }
127
-
128
- export interface SuggestionHistory {
129
- id: string;
130
- suggestionId: string;
131
- action: SuggestionAction;
132
- details?: string;
133
- actedAt: Date;
134
- }
135
-
136
- // Diff types
137
- export interface DiffResult {
138
- filePath: string;
139
- hunks: DiffHunk[];
140
- additions: number;
141
- deletions: number;
142
- }
143
-
144
- export interface DiffHunk {
145
- oldStart: number;
146
- oldLines: number;
147
- newStart: number;
148
- newLines: number;
149
- lines: string[];
150
- }
151
-
152
- // Backup types
153
- export interface BackupInfo {
154
- filePath: string;
155
- backupPath: string;
156
- timestamp: Date;
157
- size: number;
158
- }
159
-
160
- // Error types
161
- export class OwnKeyError extends Error {
162
- constructor(
163
- message: string,
164
- public code: string,
165
- public details?: any
166
- ) {
167
- super(message);
168
- this.name = 'OwnKeyError';
169
- }
170
- }
171
-
172
- export class ConfigError extends OwnKeyError {
173
- constructor(message: string, details?: any) {
174
- super(message, 'CONFIG_ERROR', details);
175
- this.name = 'ConfigError';
176
- }
177
- }
178
-
179
- export class DatabaseError extends OwnKeyError {
180
- constructor(message: string, details?: any) {
181
- super(message, 'DATABASE_ERROR', details);
182
- this.name = 'DatabaseError';
183
- }
184
- }
185
-
186
- export class AIProviderError extends OwnKeyError {
187
- constructor(message: string, details?: any) {
188
- super(message, 'AI_PROVIDER_ERROR', details);
189
- this.name = 'AIProviderError';
190
- }
191
- }
192
-
193
- export class ScanError extends OwnKeyError {
194
- constructor(message: string, details?: any) {
195
- super(message, 'SCAN_ERROR', details);
196
- this.name = 'ScanError';
197
- }
198
- }
@@ -1,54 +0,0 @@
1
- import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';
2
- import { promisify } from 'util';
3
-
4
- const scryptAsync = promisify(scrypt);
5
-
6
- const ALGORITHM = 'aes-256-gcm';
7
- const KEY_LENGTH = 32;
8
- const IV_LENGTH = 16;
9
- const SALT_LENGTH = 32;
10
- const TAG_LENGTH = 16;
11
-
12
- export class Encryption {
13
- private static async deriveKey(password: string, salt: Buffer): Promise<Buffer> {
14
- return (await scryptAsync(password, salt, KEY_LENGTH)) as Buffer;
15
- }
16
-
17
- static async encrypt(text: string, password: string): Promise<string> {
18
- const salt = randomBytes(SALT_LENGTH);
19
- const iv = randomBytes(IV_LENGTH);
20
- const key = await this.deriveKey(password, salt);
21
-
22
- const cipher = createCipheriv(ALGORITHM, key, iv);
23
- let encrypted = cipher.update(text, 'utf8', 'hex');
24
- encrypted += cipher.final('hex');
25
-
26
- const tag = cipher.getAuthTag();
27
-
28
- // Combine salt + iv + tag + encrypted
29
- const result = Buffer.concat([salt, iv, tag, Buffer.from(encrypted, 'hex')]);
30
- return result.toString('base64');
31
- }
32
-
33
- static async decrypt(encryptedData: string, password: string): Promise<string> {
34
- const buffer = Buffer.from(encryptedData, 'base64');
35
-
36
- const salt = buffer.subarray(0, SALT_LENGTH);
37
- const iv = buffer.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
38
- const tag = buffer.subarray(
39
- SALT_LENGTH + IV_LENGTH,
40
- SALT_LENGTH + IV_LENGTH + TAG_LENGTH
41
- );
42
- const encrypted = buffer.subarray(SALT_LENGTH + IV_LENGTH + TAG_LENGTH);
43
-
44
- const key = await this.deriveKey(password, salt);
45
-
46
- const decipher = createDecipheriv(ALGORITHM, key, iv);
47
- decipher.setAuthTag(tag);
48
-
49
- let decrypted = decipher.update(encrypted.toString('hex'), 'hex', 'utf8');
50
- decrypted += decipher.final('utf8');
51
-
52
- return decrypted;
53
- }
54
- }
@@ -1,111 +0,0 @@
1
- import chalk from 'chalk';
2
- import { LogLevel } from '../types/index.js';
3
- import { mkdir, appendFile } from 'fs/promises';
4
- import { join } from 'path';
5
- import { homedir } from 'os';
6
- import { existsSync } from 'fs';
7
-
8
- class Logger {
9
- private level: LogLevel = 'normal';
10
- private logDir: string;
11
- private logFile: string;
12
-
13
- constructor() {
14
- this.logDir = join(homedir(), '.ownkey', 'logs');
15
- const date = new Date().toISOString().split('T')[0];
16
- this.logFile = join(this.logDir, `ownkey-${date}.log`);
17
- }
18
-
19
- async init(): Promise<void> {
20
- if (!existsSync(this.logDir)) {
21
- await mkdir(this.logDir, { recursive: true });
22
- }
23
- }
24
-
25
- setLevel(level: LogLevel): void {
26
- this.level = level;
27
- }
28
-
29
- getLevel(): LogLevel {
30
- return this.level;
31
- }
32
-
33
- private shouldLog(messageLevel: LogLevel): boolean {
34
- const levels: LogLevel[] = ['quiet', 'normal', 'verbose', 'debug'];
35
- const currentIndex = levels.indexOf(this.level);
36
- const messageIndex = levels.indexOf(messageLevel);
37
- return messageIndex <= currentIndex;
38
- }
39
-
40
- private async writeToFile(message: string, level: string): Promise<void> {
41
- try {
42
- const timestamp = new Date().toISOString();
43
- const logEntry = JSON.stringify({ timestamp, level, message }) + '\n';
44
- await appendFile(this.logFile, logEntry);
45
- } catch (error) {
46
- // Silently fail if we can't write to log file
47
- }
48
- }
49
-
50
- info(message: string): void {
51
- if (this.shouldLog('normal')) {
52
- console.log(chalk.blue('ℹ'), message);
53
- }
54
- this.writeToFile(message, 'info');
55
- }
56
-
57
- success(message: string): void {
58
- if (this.shouldLog('normal')) {
59
- console.log(chalk.green('✓'), message);
60
- }
61
- this.writeToFile(message, 'success');
62
- }
63
-
64
- warn(message: string): void {
65
- if (this.shouldLog('normal')) {
66
- console.log(chalk.yellow('⚠'), message);
67
- }
68
- this.writeToFile(message, 'warn');
69
- }
70
-
71
- error(message: string, error?: Error): void {
72
- if (this.shouldLog('quiet')) {
73
- console.error(chalk.red('✗'), message);
74
- if (error && this.level === 'debug') {
75
- console.error(chalk.gray(error.stack || error.message));
76
- }
77
- }
78
- this.writeToFile(`${message} ${error?.message || ''}`, 'error');
79
- }
80
-
81
- verbose(message: string): void {
82
- if (this.shouldLog('verbose')) {
83
- console.log(chalk.gray('→'), message);
84
- }
85
- this.writeToFile(message, 'verbose');
86
- }
87
-
88
- debug(message: string, data?: any): void {
89
- if (this.shouldLog('debug')) {
90
- console.log(chalk.magenta('🐛'), message);
91
- if (data) {
92
- console.log(chalk.gray(JSON.stringify(data, null, 2)));
93
- }
94
- }
95
- this.writeToFile(`${message} ${data ? JSON.stringify(data) : ''}`, 'debug');
96
- }
97
-
98
- spinner(message: string): { succeed: (msg: string) => void; fail: (msg: string) => void } {
99
- // Simple spinner replacement for ora
100
- if (this.shouldLog('normal')) {
101
- console.log(chalk.cyan('⏳'), message);
102
- }
103
- return {
104
- succeed: (msg: string) => this.success(msg),
105
- fail: (msg: string) => this.error(msg),
106
- };
107
- }
108
- }
109
-
110
- // Singleton instance
111
- export const logger = new Logger();
package/tsconfig.json DELETED
@@ -1,33 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "lib": [
6
- "ES2022"
7
- ],
8
- "moduleResolution": "bundler",
9
- "rootDir": "./src",
10
- "outDir": "./dist",
11
- "declaration": true,
12
- "declarationMap": true,
13
- "sourceMap": true,
14
- "strict": true,
15
- "esModuleInterop": true,
16
- "skipLibCheck": true,
17
- "forceConsistentCasingInFileNames": true,
18
- "resolveJsonModule": true,
19
- "allowSyntheticDefaultImports": true,
20
- "noUnusedLocals": true,
21
- "noUnusedParameters": true,
22
- "noImplicitReturns": true,
23
- "noFallthroughCasesInSwitch": true
24
- },
25
- "include": [
26
- "src/**/*"
27
- ],
28
- "exclude": [
29
- "node_modules",
30
- "dist",
31
- "tests"
32
- ]
33
- }
File without changes