tx-ai-db-chat 1.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/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # tx-ai-db-chat
2
+
3
+ Free chat-focused SQLite wrapper for AI conversations.
4
+
5
+ **OpenAI API Compatible Format only.**
6
+
7
+ ## Features
8
+
9
+ - 3 simple operations: retrieve, update, delete
10
+ - Works out of the box
11
+ - Smart upsert logic (conversation_id detection)
12
+ - Manual conversation ID list (one ID per row)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install tx-ai-db-chat
18
+ ```
19
+
20
+ ## Setup
21
+
22
+ ```bash
23
+ npm run setup
24
+ ```
25
+
26
+ The setup wizard will:
27
+ 1. Ask for your SDK/API endpoint
28
+ 2. Fetch the schema
29
+ 3. List all fields containing "id"
30
+ 4. You select which field is your conversation ID
31
+ 5. Database is created
32
+
33
+ ## Usage
34
+
35
+ ### Retrieve Conversation
36
+ Drop a JSON file in `retrieve/` folder with:
37
+ ```json
38
+ {
39
+ "conversation_id": "conv_123"
40
+ }
41
+ ```
42
+
43
+ ### Update/Save Conversation
44
+ Drop a JSON file in `update/` folder with:
45
+ ```json
46
+ {
47
+ "conversation_id": "conv_123",
48
+ "messages": [...]
49
+ }
50
+ ```
51
+
52
+ ### Delete Conversation
53
+ Drop a JSON file in `delete/` folder with:
54
+ ```json
55
+ {
56
+ "conversation_id": "conv_123"
57
+ }
58
+ ```
59
+
60
+ ## Upgrade to Pro
61
+
62
+ Want auto-migration of 2,000+ conversations, search, and formatted responses?
63
+
64
+ Upgrade to **tx-ai-db-pro** ($40 one-time payment):
65
+ ```bash
66
+ npm install tx-ai-db-pro
67
+ ```
68
+
69
+ **Pro Features:**
70
+ - Auto-discover and import ALL conversations from a folder
71
+ - Search conversations with customizable response formatting
72
+ - File cleanup/backup after import
73
+ - Setup wizard with auto-migration
74
+ - 4 installations per license
75
+
76
+ **Unlock Pro Features:**
77
+ 1. Install: `npm install tx-ai-db-pro`
78
+ 2. Pay $40 to receive 4 license keys
79
+ 3. Create a `.license` file with all 4 keys (one per row)
80
+ 4. Run: `npm run unlock`
81
+ 5. Pro features unlocked!
82
+
83
+ ## Funding
84
+
85
+ If you find this useful, consider sponsoring: https://github.com/sponsors/TX-AI-Series
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "tx-ai-db-chat",
3
+ "version": "1.0.0",
4
+ "description": "Free chat-focused SQLite wrapper for AI conversations. OpenAI API Compatible Format only.",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "node tests/test.js",
8
+ "setup": "node src/setup-wizard.js",
9
+ "postinstall": "node src/postinstall.js"
10
+ },
11
+ "keywords": [
12
+ "sqlite",
13
+ "ai",
14
+ "chat",
15
+ "conversation",
16
+ "openai",
17
+ "database",
18
+ "file-driven"
19
+ ],
20
+ "author": "TX-AI-Series",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/TX-AI-Series/tx-ai-db-chat"
25
+ },
26
+ "funding": {
27
+ "type": "github",
28
+ "url": "https://github.com/sponsors/TX-AI-Series"
29
+ },
30
+ "dependencies": {
31
+ "better-sqlite3": "^9.4.3"
32
+ },
33
+ "files": [
34
+ "src/",
35
+ "README.md",
36
+ "sqlite-source/"
37
+ ]
38
+ }
@@ -0,0 +1,106 @@
1
+ const TXAIDbChat = require('./index');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ /**
6
+ * Import conversations from ID list file
7
+ */
8
+ async function importConversations() {
9
+ // Load config
10
+ const configPath = path.join(process.cwd(), 'chat-config.json');
11
+ if (!fs.existsSync(configPath)) {
12
+ console.error('Error: chat-config.json not found. Run "npm run setup" first.');
13
+ process.exit(1);
14
+ }
15
+
16
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
17
+
18
+ // Load conversation IDs
19
+ const idListPath = path.join(process.cwd(), 'conversation-ids.txt');
20
+ if (!fs.existsSync(idListPath)) {
21
+ console.error('Error: conversation-ids.txt not found. Add your conversation IDs first.');
22
+ process.exit(1);
23
+ }
24
+
25
+ const idContent = fs.readFileSync(idListPath, 'utf8');
26
+ const conversationIds = idContent
27
+ .split('\n')
28
+ .map(line => line.trim())
29
+ .filter(line => line && !line.startsWith('#'));
30
+
31
+ if (conversationIds.length === 0) {
32
+ console.log('No conversation IDs found in conversation-ids.txt');
33
+ process.exit(0);
34
+ }
35
+
36
+ console.log(`Found ${conversationIds.length} conversation IDs to import.\n`);
37
+
38
+ // Initialize database
39
+ const db = new TXAIDbChat({
40
+ dbPath: config.dbPath,
41
+ conversationIdField: config.conversationIdField
42
+ });
43
+
44
+ // Import each conversation
45
+ let successCount = 0;
46
+ let errorCount = 0;
47
+
48
+ for (const convId of conversationIds) {
49
+ try {
50
+ // Look for conversation file (named with ID)
51
+ const possiblePaths = [
52
+ path.join(config.folderPath, `${convId}.json`),
53
+ path.join(config.folderPath, convId, 'conversation.json'),
54
+ path.join(process.cwd(), 'conversations', `${convId}.json`)
55
+ ];
56
+
57
+ let conversationData = null;
58
+ let foundPath = null;
59
+
60
+ for (const filePath of possiblePaths) {
61
+ if (fs.existsSync(filePath)) {
62
+ conversationData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
63
+ foundPath = filePath;
64
+ break;
65
+ }
66
+ }
67
+
68
+ if (!conversationData) {
69
+ console.log(`⚠️ Not found: ${convId}`);
70
+ errorCount++;
71
+ continue;
72
+ }
73
+
74
+ // Ensure the conversation ID field is set
75
+ if (!conversationData[config.conversationIdField]) {
76
+ conversationData[config.conversationIdField] = convId;
77
+ }
78
+
79
+ // Save to database
80
+ db.update(conversationData);
81
+ console.log(`✅ Imported: ${convId}`);
82
+ successCount++;
83
+
84
+ // Optional: move file to backup after import
85
+ if (foundPath && config.backupAfterImport) {
86
+ const backupPath = foundPath.replace('/databases/', '/databases/imported/');
87
+ fs.mkdirSync(path.dirname(backupPath), { recursive: true });
88
+ fs.renameSync(foundPath, backupPath);
89
+ }
90
+ } catch (err) {
91
+ console.log(`❌ Error importing ${convId}: ${err.message}`);
92
+ errorCount++;
93
+ }
94
+ }
95
+
96
+ console.log(`\n========================================`);
97
+ console.log(` Import Complete`);
98
+ console.log(`========================================`);
99
+ console.log(` Success: ${successCount}`);
100
+ console.log(` Errors: ${errorCount}`);
101
+ console.log(`========================================\n`);
102
+
103
+ db.close();
104
+ }
105
+
106
+ importConversations().catch(console.error);
package/src/index.js ADDED
@@ -0,0 +1,151 @@
1
+ const Database = require('better-sqlite3');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ class TXAIDbChat {
6
+ constructor(config = {}) {
7
+ this.dbPath = config.dbPath || path.join(process.cwd(), 'chat.db');
8
+ this.conversationIdField = config.conversationIdField || 'id';
9
+ this.db = null;
10
+ this.init();
11
+ }
12
+
13
+ init() {
14
+ this.db = new Database(this.dbPath);
15
+ this.db.pragma('journal_mode = WAL');
16
+ this.db.pragma('foreign_keys = ON');
17
+ this.createTables();
18
+ }
19
+
20
+ createTables() {
21
+ this.db.exec(`
22
+ CREATE TABLE IF NOT EXISTS conversations (
23
+ id TEXT PRIMARY KEY,
24
+ raw_data JSON NOT NULL,
25
+ created_at TEXT DEFAULT (datetime('now')),
26
+ updated_at TEXT DEFAULT (datetime('now'))
27
+ );
28
+
29
+ CREATE TABLE IF NOT EXISTS messages (
30
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
31
+ conversation_id TEXT NOT NULL,
32
+ message_index INTEGER NOT NULL,
33
+ role TEXT NOT NULL,
34
+ content TEXT,
35
+ raw_data JSON NOT NULL,
36
+ FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
37
+ );
38
+
39
+ CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id);
40
+ CREATE INDEX IF NOT EXISTS idx_messages_role ON messages(role);
41
+ `);
42
+ }
43
+
44
+ /**
45
+ * Retrieve a conversation by ID
46
+ * @param {string} conversationId - The conversation ID to retrieve
47
+ * @returns {object|null} - The conversation data or null if not found
48
+ */
49
+ retrieve(conversationId) {
50
+ const conversation = this.db.prepare(
51
+ 'SELECT * FROM conversations WHERE id = ?'
52
+ ).get(conversationId);
53
+
54
+ if (!conversation) return null;
55
+
56
+ const messages = this.db.prepare(
57
+ 'SELECT * FROM messages WHERE conversation_id = ? ORDER BY message_index'
58
+ ).all(conversationId);
59
+
60
+ return {
61
+ ...conversation,
62
+ messages: messages.map(m => JSON.parse(m.raw_data))
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Update or insert a conversation (smart upsert)
68
+ * @param {object} conversationData - The conversation data (OpenAI API format)
69
+ * @returns {boolean} - True if successful
70
+ */
71
+ update(conversationData) {
72
+ const conversationId = conversationData[this.conversationIdField];
73
+ if (!conversationId) {
74
+ throw new Error(`Missing conversation ID field: ${this.conversationIdField}`);
75
+ }
76
+
77
+ const existing = this.db.prepare(
78
+ 'SELECT id FROM conversations WHERE id = ?'
79
+ ).get(conversationId);
80
+
81
+ const tx = this.db.transaction(() => {
82
+ if (existing) {
83
+ this.db.prepare(
84
+ 'UPDATE conversations SET raw_data = ?, updated_at = datetime(\'now\') WHERE id = ?'
85
+ ).run(JSON.stringify(conversationData), conversationId);
86
+
87
+ this.db.prepare(
88
+ 'DELETE FROM messages WHERE conversation_id = ?'
89
+ ).run(conversationId);
90
+ } else {
91
+ this.db.prepare(
92
+ 'INSERT INTO conversations (id, raw_data) VALUES (?, ?)'
93
+ ).run(conversationId, JSON.stringify(conversationData));
94
+ }
95
+
96
+ // Insert messages
97
+ const messages = conversationData.messages || [];
98
+ const insertMsg = this.db.prepare(
99
+ 'INSERT INTO messages (conversation_id, message_index, role, content, raw_data) VALUES (?, ?, ?, ?, ?)'
100
+ );
101
+
102
+ for (let i = 0; i < messages.length; i++) {
103
+ const msg = messages[i];
104
+ insertMsg.run(
105
+ conversationId,
106
+ i,
107
+ msg.role || 'unknown',
108
+ typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
109
+ JSON.stringify(msg)
110
+ );
111
+ }
112
+ });
113
+
114
+ tx();
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * Delete a conversation by ID
120
+ * @param {string} conversationId - The conversation ID to delete
121
+ * @returns {boolean} - True if deleted, false if not found
122
+ */
123
+ delete(conversationId) {
124
+ const result = this.db.prepare(
125
+ 'DELETE FROM conversations WHERE id = ?'
126
+ ).run(conversationId);
127
+
128
+ return result.changes > 0;
129
+ }
130
+
131
+ /**
132
+ * Get list of all conversation IDs
133
+ * @returns {string[]} - Array of conversation IDs
134
+ */
135
+ listConversations() {
136
+ return this.db.prepare(
137
+ 'SELECT id, created_at, updated_at FROM conversations ORDER BY updated_at DESC'
138
+ ).all();
139
+ }
140
+
141
+ /**
142
+ * Close the database connection
143
+ */
144
+ close() {
145
+ if (this.db) {
146
+ this.db.close();
147
+ }
148
+ }
149
+ }
150
+
151
+ module.exports = TXAIDbChat;
@@ -0,0 +1,20 @@
1
+ console.log(`
2
+ ╔═══════════════════════════════════════════════════════════╗
3
+ ║ ║
4
+ ║ tx-ai-db-chat installed successfully! ║
5
+ ║ ║
6
+ ║ Next steps: ║
7
+ ║ 1. Run setup: npm run setup ║
8
+ ║ 2. Add conversation IDs to conversation-ids.txt ║
9
+ ║ 3. Import: node src/import-conversations.js ║
10
+ ║ ║
11
+ ║ ───────────────────────────────────────────────────── ║
12
+ ║ ║
13
+ ║ 💚 Love this project? Consider sponsoring us! ║
14
+ ║ https://github.com/sponsors/TX-AI-Series ║
15
+ ║ ║
16
+ ║ Want auto-import for 2,000+ conversations? ║
17
+ ║ Upgrade to tx-ai-db-pro ($40) ║
18
+ ║ ║
19
+ ╚═══════════════════════════════════════════════════════════╝
20
+ `);
@@ -0,0 +1,186 @@
1
+ const readline = require('readline');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const https = require('https');
5
+ const http = require('http');
6
+
7
+ const rl = readline.createInterface({
8
+ input: process.stdin,
9
+ output: process.stdout
10
+ });
11
+
12
+ function askQuestion(question) {
13
+ return new Promise((resolve) => {
14
+ rl.question(question, (answer) => {
15
+ resolve(answer.trim());
16
+ });
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Fetch schema from SDK/API endpoint
22
+ */
23
+ async function fetchSchema(endpoint) {
24
+ return new Promise((resolve, reject) => {
25
+ const url = new URL(endpoint);
26
+ const lib = url.protocol === 'https:' ? https : http;
27
+
28
+ const options = {
29
+ method: 'GET',
30
+ hostname: url.hostname,
31
+ path: url.pathname + url.search,
32
+ headers: {
33
+ 'Accept': 'application/json'
34
+ }
35
+ };
36
+
37
+ const req = lib.request(options, (res) => {
38
+ let data = '';
39
+ res.on('data', (chunk) => { data += chunk; });
40
+ res.on('end', () => {
41
+ try {
42
+ resolve(JSON.parse(data));
43
+ } catch (e) {
44
+ reject(new Error('Failed to parse JSON response'));
45
+ }
46
+ });
47
+ });
48
+
49
+ req.on('error', reject);
50
+ req.end();
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Find all fields containing "id" in the schema
56
+ */
57
+ function findIdFields(schema, prefix = '') {
58
+ let idFields = [];
59
+
60
+ if (typeof schema === 'object' && schema !== null) {
61
+ if (Array.isArray(schema)) {
62
+ schema.forEach((item, index) => {
63
+ idFields = idFields.concat(findIdFields(item, `${prefix}[${index}]`));
64
+ });
65
+ } else {
66
+ for (const [key, value] of Object.entries(schema)) {
67
+ const fullPath = prefix ? `${prefix}.${key}` : key;
68
+ if (key.toLowerCase().includes('id')) {
69
+ idFields.push({
70
+ field: fullPath,
71
+ type: typeof value,
72
+ value: value
73
+ });
74
+ }
75
+ idFields = idFields.concat(findIdFields(value, fullPath));
76
+ }
77
+ }
78
+ }
79
+
80
+ return idFields;
81
+ }
82
+
83
+ /**
84
+ * Main setup wizard
85
+ */
86
+ async function runSetupWizard() {
87
+ console.log('========================================');
88
+ console.log(' tx-ai-db-chat Setup Wizard');
89
+ console.log('========================================\n');
90
+
91
+ // Step 1: Get SDK/API endpoint
92
+ console.log('Step 1: SDK/API Configuration');
93
+ console.log('Enter your OpenAI-compatible API endpoint to fetch schema.');
94
+ console.log('Or press Enter to skip and use default schema.\n');
95
+
96
+ const endpoint = await askQuestion('API Endpoint (or press Enter to skip): ');
97
+
98
+ let schema = null;
99
+ let idFields = [];
100
+
101
+ if (endpoint) {
102
+ try {
103
+ console.log('\nFetching schema from API...');
104
+ schema = await fetchSchema(endpoint);
105
+ idFields = findIdFields(schema);
106
+ console.log(`Found ${idFields.length} fields containing "id":\n`);
107
+
108
+ idFields.forEach((field, index) => {
109
+ console.log(` ${index + 1}. ${field.field} (${field.type}) - Example: ${JSON.stringify(field.value).substring(0, 50)}`);
110
+ });
111
+ } catch (err) {
112
+ console.log(`\nFailed to fetch schema: ${err.message}`);
113
+ console.log('Using default schema...\n');
114
+ }
115
+ }
116
+
117
+ // Default ID fields if no API provided
118
+ if (idFields.length === 0) {
119
+ idFields = [
120
+ { field: 'id', type: 'string', value: 'thread_abc123' },
121
+ { field: 'thread_id', type: 'string', value: 'thread_abc123' },
122
+ { field: 'conversation_id', type: 'string', value: 'conv_123' },
123
+ { field: 'session_id', type: 'string', value: 'sess_456' }
124
+ ];
125
+
126
+ console.log('Default conversation ID fields:');
127
+ idFields.forEach((field, index) => {
128
+ console.log(` ${index + 1}. ${field.field}`);
129
+ });
130
+ }
131
+
132
+ // Step 2: Select conversation ID field
133
+ console.log('\nStep 2: Select Conversation ID Field');
134
+ console.log('Which field identifies your conversations?\n');
135
+
136
+ const selection = await askQuestion(`Enter number (1-${idFields.length}): `);
137
+ const selectedIndex = parseInt(selection) - 1;
138
+
139
+ if (selectedIndex < 0 || selectedIndex >= idFields.length) {
140
+ console.log('Invalid selection. Using default: id');
141
+ idFields[0];
142
+ }
143
+
144
+ const conversationIdField = idFields[selectedIndex].field;
145
+ console.log(`\nSelected: ${conversationIdField}\n`);
146
+
147
+ // Step 3: Create config file
148
+ console.log('Step 3: Creating configuration...');
149
+
150
+ const config = {
151
+ conversationIdField: conversationIdField,
152
+ dbPath: path.join(process.cwd(), 'chat.db'),
153
+ folderPath: path.join(process.cwd(), 'databases'),
154
+ version: '1.0.0'
155
+ };
156
+
157
+ const configPath = path.join(process.cwd(), 'chat-config.json');
158
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
159
+ console.log(`Config saved to: ${configPath}\n`);
160
+
161
+ // Step 4: Create conversation ID list file
162
+ console.log('Step 4: Create Conversation ID List');
163
+ console.log('Create a file with one conversation ID per line:');
164
+ console.log(` ${path.join(process.cwd(), 'conversation-ids.txt')}\n`);
165
+
166
+ const idListPath = path.join(process.cwd(), 'conversation-ids.txt');
167
+ if (!fs.existsSync(idListPath)) {
168
+ fs.writeFileSync(idListPath, '# Add your conversation IDs here, one per line\n# Example:\n# conv_123\n# conv_456\n');
169
+ console.log(`Created empty ID list file: ${idListPath}`);
170
+ }
171
+
172
+ // Step 5: Upgrade prompt
173
+ console.log('\n========================================');
174
+ console.log(' Setup Complete!');
175
+ console.log('========================================\n');
176
+ console.log('To import conversations:');
177
+ console.log(` 1. Add conversation IDs to: ${idListPath}`);
178
+ console.log(' 2. Run: node src/import-conversations.js\n');
179
+ console.log('Want to auto-import 2,000+ conversations?');
180
+ console.log('Upgrade to tx-ai-db-pro ($40): https://github.com/sponsors/TX-AI-Series\n');
181
+
182
+ rl.close();
183
+ }
184
+
185
+ // Run the wizard
186
+ runSetupWizard().catch(console.error);