ownkey 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +9 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/commands/apply.d.ts +3 -0
- package/dist/commands/apply.d.ts.map +1 -0
- package/dist/commands/apply.js +26 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +142 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/scan.d.ts +3 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +97 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/suggest.d.ts +3 -0
- package/dist/commands/suggest.d.ts.map +1 -0
- package/dist/commands/suggest.js +34 -0
- package/dist/commands/suggest.js.map +1 -0
- package/dist/config/defaults.d.ts +12 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +74 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/keychain.d.ts +14 -0
- package/dist/config/keychain.d.ts.map +1 -0
- package/dist/config/keychain.js +112 -0
- package/dist/config/keychain.js.map +1 -0
- package/dist/config/manager.d.ts +21 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +144 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/core/scanner.d.ts +17 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +150 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/db/client.d.ts +15 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +130 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/operations.d.ts +14 -0
- package/dist/db/operations.d.ts.map +1 -0
- package/dist/db/operations.js +175 -0
- package/dist/db/operations.js.map +1 -0
- package/dist/db/schema.d.ts +683 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +107 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -2
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +138 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +37 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/encryption.d.ts +6 -0
- package/dist/utils/encryption.d.ts.map +1 -0
- package/dist/utils/encryption.js +39 -0
- package/dist/utils/encryption.js.map +1 -0
- package/dist/utils/logger.d.ts +25 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +97 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +64 -29
- package/src/commands/apply.ts +27 -0
- package/src/commands/config.ts +154 -0
- package/src/commands/scan.ts +114 -0
- package/src/commands/suggest.ts +37 -0
- package/src/config/defaults.ts +80 -0
- package/src/config/keychain.ts +125 -0
- package/src/config/manager.ts +166 -0
- package/src/core/scanner.ts +174 -0
- package/src/db/client.ts +151 -0
- package/src/db/migrations/001_init.sql +80 -0
- package/src/db/operations.ts +217 -0
- package/src/db/schema.ts +118 -0
- package/src/index.ts +74 -0
- package/src/types/index.ts +198 -0
- package/src/utils/encryption.ts +54 -0
- package/src/utils/logger.ts +111 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAW;IAClC,SAAS,EAAE;QACP,MAAM,EAAE;YACJ,KAAK,EAAE,aAAa;SACvB;QACD,SAAS,EAAE;YACP,KAAK,EAAE,0BAA0B;SACpC;QACD,MAAM,EAAE;YACJ,QAAQ,EAAE,wBAAwB;YAClC,KAAK,EAAE,WAAW;SACrB;KACJ;IACD,QAAQ,EAAE;QACN,WAAW,EAAE,OAAO;QACpB,QAAQ,EAAE,QAAoB;KACjC;CACJ,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AACpC,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AACzC,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;AACpC,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE/B,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC5B,MAAM;IACN,cAAc;IACd,MAAM;IACN,OAAO;IACP,QAAQ;IACR,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,OAAO;IACP,UAAU;IACV,MAAM;IACN,QAAQ;IACR,UAAU;IACV,WAAW;CACd,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC7B,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,QAAQ;IACR,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,QAAQ;AAEvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AIProvider } from '../types/index.js';
|
|
2
|
+
export declare class KeychainManager {
|
|
3
|
+
private keysFilePath;
|
|
4
|
+
private useKeychain;
|
|
5
|
+
constructor();
|
|
6
|
+
private ensureKeysDir;
|
|
7
|
+
setApiKey(provider: AIProvider, apiKey: string): Promise<void>;
|
|
8
|
+
getApiKey(provider: AIProvider): Promise<string | null>;
|
|
9
|
+
deleteApiKey(provider: AIProvider): Promise<void>;
|
|
10
|
+
private loadKeysFromFile;
|
|
11
|
+
private saveKeysToFile;
|
|
12
|
+
}
|
|
13
|
+
export declare const keychainManager: KeychainManager;
|
|
14
|
+
//# sourceMappingURL=keychain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../../src/config/keychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAoB/C,qBAAa,eAAe;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAU;;YAOf,aAAa;IAOrB,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9D,SAAS,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAyBvD,YAAY,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;YAmBzC,gBAAgB;YAehB,cAAc;CAK/B;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { Encryption } from '../utils/encryption.js';
|
|
7
|
+
import { KEYS_FILE, CONFIG_DIR } from './defaults.js';
|
|
8
|
+
// Try to use keytar, but fallback gracefully if not available
|
|
9
|
+
let keytar = null;
|
|
10
|
+
try {
|
|
11
|
+
keytar = await import('keytar');
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
logger.verbose('Keytar not available, using encrypted file storage for API keys');
|
|
15
|
+
}
|
|
16
|
+
const SERVICE_NAME = 'ownkey-cli';
|
|
17
|
+
const ENCRYPTION_PASSWORD = 'ownkey-local-encryption-key'; // In production, derive from machine ID
|
|
18
|
+
export class KeychainManager {
|
|
19
|
+
keysFilePath;
|
|
20
|
+
useKeychain;
|
|
21
|
+
constructor() {
|
|
22
|
+
this.keysFilePath = join(homedir(), CONFIG_DIR, KEYS_FILE);
|
|
23
|
+
this.useKeychain = keytar !== null;
|
|
24
|
+
}
|
|
25
|
+
async ensureKeysDir() {
|
|
26
|
+
const dir = join(homedir(), CONFIG_DIR);
|
|
27
|
+
if (!existsSync(dir)) {
|
|
28
|
+
await mkdir(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async setApiKey(provider, apiKey) {
|
|
32
|
+
const accountName = `${provider}-api-key`;
|
|
33
|
+
if (this.useKeychain) {
|
|
34
|
+
try {
|
|
35
|
+
await keytar.setPassword(SERVICE_NAME, accountName, apiKey);
|
|
36
|
+
logger.verbose(`Stored ${provider} API key in system keychain`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
logger.warn(`Failed to store in keychain, falling back to encrypted file`);
|
|
41
|
+
this.useKeychain = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Fallback to encrypted file
|
|
45
|
+
await this.ensureKeysDir();
|
|
46
|
+
const keys = await this.loadKeysFromFile();
|
|
47
|
+
keys[provider] = apiKey;
|
|
48
|
+
await this.saveKeysToFile(keys);
|
|
49
|
+
logger.verbose(`Stored ${provider} API key in encrypted file`);
|
|
50
|
+
}
|
|
51
|
+
async getApiKey(provider) {
|
|
52
|
+
const accountName = `${provider}-api-key`;
|
|
53
|
+
if (this.useKeychain) {
|
|
54
|
+
try {
|
|
55
|
+
const key = await keytar.getPassword(SERVICE_NAME, accountName);
|
|
56
|
+
if (key) {
|
|
57
|
+
logger.verbose(`Retrieved ${provider} API key from system keychain`);
|
|
58
|
+
return key;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
logger.warn(`Failed to retrieve from keychain, trying encrypted file`);
|
|
63
|
+
this.useKeychain = false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Fallback to encrypted file
|
|
67
|
+
const keys = await this.loadKeysFromFile();
|
|
68
|
+
const key = keys[provider] || null;
|
|
69
|
+
if (key) {
|
|
70
|
+
logger.verbose(`Retrieved ${provider} API key from encrypted file`);
|
|
71
|
+
}
|
|
72
|
+
return key;
|
|
73
|
+
}
|
|
74
|
+
async deleteApiKey(provider) {
|
|
75
|
+
const accountName = `${provider}-api-key`;
|
|
76
|
+
if (this.useKeychain) {
|
|
77
|
+
try {
|
|
78
|
+
await keytar.deletePassword(SERVICE_NAME, accountName);
|
|
79
|
+
logger.verbose(`Deleted ${provider} API key from system keychain`);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
// Ignore errors
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Also delete from file
|
|
86
|
+
const keys = await this.loadKeysFromFile();
|
|
87
|
+
delete keys[provider];
|
|
88
|
+
await this.saveKeysToFile(keys);
|
|
89
|
+
logger.verbose(`Deleted ${provider} API key from encrypted file`);
|
|
90
|
+
}
|
|
91
|
+
async loadKeysFromFile() {
|
|
92
|
+
if (!existsSync(this.keysFilePath)) {
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const encryptedData = await readFile(this.keysFilePath, 'utf-8');
|
|
97
|
+
const decrypted = await Encryption.decrypt(encryptedData, ENCRYPTION_PASSWORD);
|
|
98
|
+
return JSON.parse(decrypted);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
logger.warn('Failed to decrypt keys file, starting fresh');
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async saveKeysToFile(keys) {
|
|
106
|
+
const json = JSON.stringify(keys);
|
|
107
|
+
const encrypted = await Encryption.encrypt(json, ENCRYPTION_PASSWORD);
|
|
108
|
+
await writeFile(this.keysFilePath, encrypted, 'utf-8');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export const keychainManager = new KeychainManager();
|
|
112
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/config/keychain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Config, AIProvider } from '../types/index.js';
|
|
2
|
+
export declare class ConfigManager {
|
|
3
|
+
private configPath;
|
|
4
|
+
private config;
|
|
5
|
+
constructor();
|
|
6
|
+
ensureConfigDir(): Promise<void>;
|
|
7
|
+
exists(): Promise<boolean>;
|
|
8
|
+
load(): Promise<Config>;
|
|
9
|
+
save(config: Config): Promise<void>;
|
|
10
|
+
update(updates: Partial<Config>): Promise<void>;
|
|
11
|
+
setSupabase(url: string, serviceRoleKey: string): Promise<void>;
|
|
12
|
+
setProvider(provider: AIProvider, apiKey: string, model?: string): Promise<void>;
|
|
13
|
+
getApiKey(provider?: AIProvider): Promise<string | null>;
|
|
14
|
+
validate(): Promise<{
|
|
15
|
+
valid: boolean;
|
|
16
|
+
errors: string[];
|
|
17
|
+
}>;
|
|
18
|
+
reset(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export declare const configManager: ConfigManager;
|
|
21
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/config/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,UAAU,EAAE,MAAM,mBAAmB,CAAC;AASpE,qBAAa,aAAa;IACtB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAuB;;IAM/B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAQhC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAI1B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAoCvB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAanC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/C,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/D,WAAW,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBhF,SAAS,CAAC,QAAQ,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAWxD,QAAQ,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IA0BzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO/B;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { ConfigError } from '../types/index.js';
|
|
2
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { keychainManager } from './keychain.js';
|
|
8
|
+
import { DEFAULT_CONFIG, CONFIG_DIR, CONFIG_FILE } from './defaults.js';
|
|
9
|
+
export class ConfigManager {
|
|
10
|
+
configPath;
|
|
11
|
+
config = null;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.configPath = join(homedir(), CONFIG_DIR, CONFIG_FILE);
|
|
14
|
+
}
|
|
15
|
+
async ensureConfigDir() {
|
|
16
|
+
const dir = join(homedir(), CONFIG_DIR);
|
|
17
|
+
if (!existsSync(dir)) {
|
|
18
|
+
await mkdir(dir, { recursive: true });
|
|
19
|
+
logger.verbose(`Created config directory: ${dir}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async exists() {
|
|
23
|
+
return existsSync(this.configPath);
|
|
24
|
+
}
|
|
25
|
+
async load() {
|
|
26
|
+
if (this.config) {
|
|
27
|
+
return this.config;
|
|
28
|
+
}
|
|
29
|
+
if (!existsSync(this.configPath)) {
|
|
30
|
+
logger.verbose('No config file found, using defaults');
|
|
31
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
32
|
+
return this.config;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const content = await readFile(this.configPath, 'utf-8');
|
|
36
|
+
const loaded = JSON.parse(content);
|
|
37
|
+
// Merge with defaults to ensure all fields exist
|
|
38
|
+
this.config = {
|
|
39
|
+
...DEFAULT_CONFIG,
|
|
40
|
+
...loaded,
|
|
41
|
+
providers: {
|
|
42
|
+
...DEFAULT_CONFIG.providers,
|
|
43
|
+
...loaded.providers,
|
|
44
|
+
},
|
|
45
|
+
defaults: {
|
|
46
|
+
...DEFAULT_CONFIG.defaults,
|
|
47
|
+
...loaded.defaults,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
logger.verbose('Loaded configuration from file');
|
|
51
|
+
return this.config;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
throw new ConfigError('Failed to load configuration file', error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async save(config) {
|
|
58
|
+
await this.ensureConfigDir();
|
|
59
|
+
try {
|
|
60
|
+
const content = JSON.stringify(config, null, 2);
|
|
61
|
+
await writeFile(this.configPath, content, 'utf-8');
|
|
62
|
+
this.config = config;
|
|
63
|
+
logger.verbose('Saved configuration to file');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw new ConfigError('Failed to save configuration file', error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async update(updates) {
|
|
70
|
+
const current = await this.load();
|
|
71
|
+
const updated = {
|
|
72
|
+
...current,
|
|
73
|
+
...updates,
|
|
74
|
+
providers: {
|
|
75
|
+
...current.providers,
|
|
76
|
+
...updates.providers,
|
|
77
|
+
},
|
|
78
|
+
defaults: {
|
|
79
|
+
...current.defaults,
|
|
80
|
+
...updates.defaults,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
await this.save(updated);
|
|
84
|
+
}
|
|
85
|
+
async setSupabase(url, serviceRoleKey) {
|
|
86
|
+
const config = await this.load();
|
|
87
|
+
config.supabaseUrl = url;
|
|
88
|
+
config.supabaseServiceRoleKey = serviceRoleKey;
|
|
89
|
+
await this.save(config);
|
|
90
|
+
logger.success('Supabase configuration saved');
|
|
91
|
+
}
|
|
92
|
+
async setProvider(provider, apiKey, model) {
|
|
93
|
+
const config = await this.load();
|
|
94
|
+
config.activeProvider = provider;
|
|
95
|
+
if (model) {
|
|
96
|
+
if (!config.providers[provider]) {
|
|
97
|
+
config.providers[provider] = {};
|
|
98
|
+
}
|
|
99
|
+
config.providers[provider].model = model;
|
|
100
|
+
}
|
|
101
|
+
await this.save(config);
|
|
102
|
+
await keychainManager.setApiKey(provider, apiKey);
|
|
103
|
+
logger.success(`${provider} provider configured`);
|
|
104
|
+
}
|
|
105
|
+
async getApiKey(provider) {
|
|
106
|
+
const config = await this.load();
|
|
107
|
+
const targetProvider = provider || config.activeProvider;
|
|
108
|
+
if (!targetProvider) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return await keychainManager.getApiKey(targetProvider);
|
|
112
|
+
}
|
|
113
|
+
async validate() {
|
|
114
|
+
const errors = [];
|
|
115
|
+
try {
|
|
116
|
+
const config = await this.load();
|
|
117
|
+
// Check if at least one provider is configured
|
|
118
|
+
if (!config.activeProvider) {
|
|
119
|
+
errors.push('No active AI provider configured. Run: ownkey config');
|
|
120
|
+
}
|
|
121
|
+
// Check if API key exists for active provider
|
|
122
|
+
if (config.activeProvider) {
|
|
123
|
+
const apiKey = await this.getApiKey(config.activeProvider);
|
|
124
|
+
if (!apiKey) {
|
|
125
|
+
errors.push(`No API key found for ${config.activeProvider}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { valid: errors.length === 0, errors };
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
errors.push('Failed to load configuration');
|
|
132
|
+
return { valid: false, errors };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async reset() {
|
|
136
|
+
this.config = null;
|
|
137
|
+
if (existsSync(this.configPath)) {
|
|
138
|
+
await writeFile(this.configPath, JSON.stringify(DEFAULT_CONFIG, null, 2), 'utf-8');
|
|
139
|
+
logger.success('Configuration reset to defaults');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export const configManager = new ConfigManager();
|
|
144
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/config/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FileMetadata, FileContent, ScanResult } from '../types/index.js';
|
|
2
|
+
export declare class FileScanner {
|
|
3
|
+
private ig;
|
|
4
|
+
private maxFileSize;
|
|
5
|
+
private rootPath;
|
|
6
|
+
private skippedFiles;
|
|
7
|
+
private warnings;
|
|
8
|
+
constructor(rootPath: string, maxFileSize?: number);
|
|
9
|
+
initialize(): Promise<void>;
|
|
10
|
+
scan(): Promise<ScanResult>;
|
|
11
|
+
private scanDirectory;
|
|
12
|
+
private processFile;
|
|
13
|
+
private isTextFile;
|
|
14
|
+
loadFileContents(files: FileMetadata[]): Promise<FileContent[]>;
|
|
15
|
+
private formatBytes;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI1E,qBAAa,WAAW;IACpB,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,QAAQ,CAAgB;gBAEpB,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAE,MAA4B;IAOjE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;YA0BnB,aAAa;YA2Bb,WAAW;YAuCX,UAAU;IAkBlB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAkBrE,OAAO,CAAC,WAAW;CAKtB"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import ignore from 'ignore';
|
|
2
|
+
import { readFile, stat, readdir } from 'fs/promises';
|
|
3
|
+
import { join, relative, extname } from 'path';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { DEFAULT_EXCLUDES, BINARY_EXTENSIONS, MAX_FILE_SIZE_BYTES } from '../config/defaults.js';
|
|
7
|
+
export class FileScanner {
|
|
8
|
+
ig;
|
|
9
|
+
maxFileSize;
|
|
10
|
+
rootPath;
|
|
11
|
+
skippedFiles = [];
|
|
12
|
+
warnings = [];
|
|
13
|
+
constructor(rootPath, maxFileSize = MAX_FILE_SIZE_BYTES) {
|
|
14
|
+
this.rootPath = rootPath;
|
|
15
|
+
this.maxFileSize = maxFileSize;
|
|
16
|
+
this.ig = ignore();
|
|
17
|
+
this.ig.add(DEFAULT_EXCLUDES);
|
|
18
|
+
}
|
|
19
|
+
async initialize() {
|
|
20
|
+
// Load .gitignore
|
|
21
|
+
const gitignorePath = join(this.rootPath, '.gitignore');
|
|
22
|
+
if (existsSync(gitignorePath)) {
|
|
23
|
+
const content = await readFile(gitignorePath, 'utf-8');
|
|
24
|
+
this.ig.add(content);
|
|
25
|
+
logger.verbose('Loaded .gitignore patterns');
|
|
26
|
+
}
|
|
27
|
+
// Load .ownkeyignore
|
|
28
|
+
const ownkeyignorePath = join(this.rootPath, '.ownkeyignore');
|
|
29
|
+
if (existsSync(ownkeyignorePath)) {
|
|
30
|
+
const content = await readFile(ownkeyignorePath, 'utf-8');
|
|
31
|
+
this.ig.add(content);
|
|
32
|
+
logger.verbose('Loaded .ownkeyignore patterns');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async scan() {
|
|
36
|
+
await this.initialize();
|
|
37
|
+
const files = [];
|
|
38
|
+
let totalSize = 0;
|
|
39
|
+
await this.scanDirectory(this.rootPath, files);
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
totalSize += file.size;
|
|
42
|
+
}
|
|
43
|
+
logger.verbose(`Scanned ${files.length} files (${this.formatBytes(totalSize)})`);
|
|
44
|
+
if (this.skippedFiles.length > 0) {
|
|
45
|
+
logger.verbose(`Skipped ${this.skippedFiles.length} files`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
files,
|
|
49
|
+
totalSize,
|
|
50
|
+
skippedFiles: this.skippedFiles,
|
|
51
|
+
warnings: this.warnings,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async scanDirectory(dirPath, files) {
|
|
55
|
+
let entries;
|
|
56
|
+
try {
|
|
57
|
+
entries = await readdir(dirPath, { withFileTypes: true });
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
this.warnings.push(`Cannot read directory: ${dirPath}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const fullPath = join(dirPath, entry.name);
|
|
65
|
+
const relativePath = relative(this.rootPath, fullPath);
|
|
66
|
+
// Check if ignored
|
|
67
|
+
if (this.ig.ignores(relativePath)) {
|
|
68
|
+
logger.debug(`Ignored: ${relativePath}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (entry.isDirectory()) {
|
|
72
|
+
await this.scanDirectory(fullPath, files);
|
|
73
|
+
}
|
|
74
|
+
else if (entry.isFile()) {
|
|
75
|
+
await this.processFile(fullPath, relativePath, files);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async processFile(fullPath, relativePath, files) {
|
|
80
|
+
try {
|
|
81
|
+
const stats = await stat(fullPath);
|
|
82
|
+
// Check file size
|
|
83
|
+
if (stats.size > this.maxFileSize) {
|
|
84
|
+
this.skippedFiles.push(`${relativePath} (too large: ${this.formatBytes(stats.size)})`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Check if binary
|
|
88
|
+
const ext = extname(fullPath).toLowerCase();
|
|
89
|
+
if (BINARY_EXTENSIONS.includes(ext)) {
|
|
90
|
+
this.skippedFiles.push(`${relativePath} (binary file)`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Check if file is actually text (simple heuristic)
|
|
94
|
+
if (!(await this.isTextFile(fullPath))) {
|
|
95
|
+
this.skippedFiles.push(`${relativePath} (binary content)`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
files.push({
|
|
99
|
+
path: fullPath,
|
|
100
|
+
relativePath,
|
|
101
|
+
size: stats.size,
|
|
102
|
+
lastModified: stats.mtime,
|
|
103
|
+
extension: ext,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
this.warnings.push(`Cannot process file: ${relativePath}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async isTextFile(filePath) {
|
|
111
|
+
try {
|
|
112
|
+
const buffer = await readFile(filePath);
|
|
113
|
+
const sample = buffer.subarray(0, Math.min(8000, buffer.length));
|
|
114
|
+
// Check for null bytes (common in binary files)
|
|
115
|
+
for (let i = 0; i < sample.length; i++) {
|
|
116
|
+
if (sample[i] === 0) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async loadFileContents(files) {
|
|
127
|
+
const contents = [];
|
|
128
|
+
for (const file of files) {
|
|
129
|
+
try {
|
|
130
|
+
const content = await readFile(file.path, 'utf-8');
|
|
131
|
+
contents.push({
|
|
132
|
+
...file,
|
|
133
|
+
content,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
logger.warn(`Failed to read file: ${file.relativePath}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return contents;
|
|
141
|
+
}
|
|
142
|
+
formatBytes(bytes) {
|
|
143
|
+
if (bytes < 1024)
|
|
144
|
+
return `${bytes} B`;
|
|
145
|
+
if (bytes < 1024 * 1024)
|
|
146
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
147
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
2
|
+
export declare class DatabaseClient {
|
|
3
|
+
private sql;
|
|
4
|
+
private db;
|
|
5
|
+
private isLocalOnly;
|
|
6
|
+
connect(localOnly?: boolean): Promise<void>;
|
|
7
|
+
disconnect(): Promise<void>;
|
|
8
|
+
getDb(): ReturnType<typeof drizzle>;
|
|
9
|
+
isConnected(): boolean;
|
|
10
|
+
isLocalOnlyMode(): boolean;
|
|
11
|
+
private runMigrations;
|
|
12
|
+
private runMigration;
|
|
13
|
+
}
|
|
14
|
+
export declare const dbClient: DatabaseClient;
|
|
15
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAYlD,qBAAa,cAAc;IACvB,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,EAAE,CAA2C;IACrD,OAAO,CAAC,WAAW,CAAkB;IAE/B,OAAO,CAAC,SAAS,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA4ClD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC,KAAK,IAAI,UAAU,CAAC,OAAO,OAAO,CAAC;IAUnC,WAAW,IAAI,OAAO;IAItB,eAAe,IAAI,OAAO;YAIZ,aAAa;YA4Cb,YAAY;CAe7B;AAED,eAAO,MAAM,QAAQ,gBAAuB,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import postgres from 'postgres';
|
|
2
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
3
|
+
import { readFile } from 'fs/promises';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { DatabaseError } from '../types/index.js';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { configManager } from '../config/manager.js';
|
|
9
|
+
import * as schema from './schema.js';
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
export class DatabaseClient {
|
|
13
|
+
sql = null;
|
|
14
|
+
db = null;
|
|
15
|
+
isLocalOnly = false;
|
|
16
|
+
async connect(localOnly = false) {
|
|
17
|
+
if (localOnly) {
|
|
18
|
+
this.isLocalOnly = true;
|
|
19
|
+
logger.info('Running in local-only mode (no database persistence)');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const config = await configManager.load();
|
|
24
|
+
if (!config.supabaseUrl || !config.supabaseServiceRoleKey) {
|
|
25
|
+
logger.warn('Supabase not configured. Running in local-only mode.');
|
|
26
|
+
logger.info('Run "ownkey config" to set up database persistence.');
|
|
27
|
+
this.isLocalOnly = true;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Create connection string
|
|
31
|
+
const connectionString = `${config.supabaseUrl}?sslmode=require`;
|
|
32
|
+
this.sql = postgres(connectionString, {
|
|
33
|
+
password: config.supabaseServiceRoleKey,
|
|
34
|
+
max: 10,
|
|
35
|
+
idle_timeout: 20,
|
|
36
|
+
connect_timeout: 10,
|
|
37
|
+
});
|
|
38
|
+
this.db = drizzle(this.sql, { schema });
|
|
39
|
+
// Test connection
|
|
40
|
+
await this.sql `SELECT 1`;
|
|
41
|
+
logger.verbose('Database connection established');
|
|
42
|
+
// Run migrations
|
|
43
|
+
await this.runMigrations();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
logger.warn('Failed to connect to database. Running in local-only mode.');
|
|
47
|
+
logger.debug('Database error:', error);
|
|
48
|
+
this.isLocalOnly = true;
|
|
49
|
+
this.sql = null;
|
|
50
|
+
this.db = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async disconnect() {
|
|
54
|
+
if (this.sql) {
|
|
55
|
+
await this.sql.end();
|
|
56
|
+
this.sql = null;
|
|
57
|
+
this.db = null;
|
|
58
|
+
logger.verbose('Database connection closed');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
getDb() {
|
|
62
|
+
if (this.isLocalOnly) {
|
|
63
|
+
throw new DatabaseError('Database not available in local-only mode');
|
|
64
|
+
}
|
|
65
|
+
if (!this.db) {
|
|
66
|
+
throw new DatabaseError('Database not connected');
|
|
67
|
+
}
|
|
68
|
+
return this.db;
|
|
69
|
+
}
|
|
70
|
+
isConnected() {
|
|
71
|
+
return this.db !== null && !this.isLocalOnly;
|
|
72
|
+
}
|
|
73
|
+
isLocalOnlyMode() {
|
|
74
|
+
return this.isLocalOnly;
|
|
75
|
+
}
|
|
76
|
+
async runMigrations() {
|
|
77
|
+
if (!this.sql) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
logger.verbose('Checking database migrations...');
|
|
82
|
+
// Check if migrations table exists
|
|
83
|
+
const tableExists = await this.sql `
|
|
84
|
+
SELECT EXISTS (
|
|
85
|
+
SELECT FROM information_schema.tables
|
|
86
|
+
WHERE table_name = 'schema_migrations'
|
|
87
|
+
);
|
|
88
|
+
`;
|
|
89
|
+
if (!tableExists[0]?.exists) {
|
|
90
|
+
logger.info('Initializing database schema...');
|
|
91
|
+
await this.runMigration(1);
|
|
92
|
+
logger.success('Database schema initialized');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Check current version
|
|
96
|
+
const currentVersion = await this.sql `
|
|
97
|
+
SELECT MAX(version) as version FROM schema_migrations;
|
|
98
|
+
`;
|
|
99
|
+
const version = currentVersion[0]?.version || 0;
|
|
100
|
+
logger.verbose(`Current schema version: ${version}`);
|
|
101
|
+
// Run pending migrations
|
|
102
|
+
const latestVersion = 1; // Update this as we add more migrations
|
|
103
|
+
if (version < latestVersion) {
|
|
104
|
+
for (let v = version + 1; v <= latestVersion; v++) {
|
|
105
|
+
await this.runMigration(v);
|
|
106
|
+
logger.success(`Applied migration ${v}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
throw new DatabaseError('Failed to run migrations', error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async runMigration(version) {
|
|
115
|
+
if (!this.sql) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const migrationFile = join(__dirname, 'migrations', `${String(version).padStart(3, '0')}_init.sql`);
|
|
119
|
+
try {
|
|
120
|
+
const migrationSql = await readFile(migrationFile, 'utf-8');
|
|
121
|
+
await this.sql.unsafe(migrationSql);
|
|
122
|
+
logger.verbose(`Migration ${version} applied successfully`);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
throw new DatabaseError(`Failed to apply migration ${version}`, error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export const dbClient = new DatabaseClient();
|
|
130
|
+
//# sourceMappingURL=client.js.map
|