cntx-ui 3.0.7 → 3.0.9
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/bin/cntx-ui.js +70 -0
- package/dist/lib/agent-runtime.js +269 -0
- package/dist/lib/agent-tools.js +162 -0
- package/dist/lib/api-router.js +387 -0
- package/dist/lib/bundle-manager.js +236 -0
- package/dist/lib/configuration-manager.js +230 -0
- package/dist/lib/database-manager.js +277 -0
- package/dist/lib/file-system-manager.js +305 -0
- package/dist/lib/function-level-chunker.js +144 -0
- package/dist/lib/heuristics-manager.js +491 -0
- package/dist/lib/mcp-server.js +159 -0
- package/dist/lib/mcp-transport.js +10 -0
- package/dist/lib/semantic-splitter.js +335 -0
- package/dist/lib/simple-vector-store.js +98 -0
- package/dist/lib/treesitter-semantic-chunker.js +277 -0
- package/dist/lib/websocket-manager.js +268 -0
- package/dist/server.js +225 -0
- package/package.json +18 -8
- package/bin/cntx-ui-mcp.sh +0 -3
- package/bin/cntx-ui.js +0 -123
- package/lib/agent-runtime.js +0 -371
- package/lib/agent-tools.js +0 -370
- package/lib/api-router.js +0 -1026
- package/lib/bundle-manager.js +0 -326
- package/lib/configuration-manager.js +0 -760
- package/lib/database-manager.js +0 -397
- package/lib/file-system-manager.js +0 -489
- package/lib/function-level-chunker.js +0 -406
- package/lib/heuristics-manager.js +0 -529
- package/lib/mcp-server.js +0 -1380
- package/lib/mcp-transport.js +0 -97
- package/lib/semantic-splitter.js +0 -304
- package/lib/simple-vector-store.js +0 -108
- package/lib/treesitter-semantic-chunker.js +0 -1485
- package/lib/websocket-manager.js +0 -470
- package/server.js +0 -687
package/dist/server.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refactored cntx-ui Server
|
|
3
|
+
* Lean orchestration layer using modular architecture
|
|
4
|
+
*/
|
|
5
|
+
import { createServer } from 'http';
|
|
6
|
+
import { join, dirname, extname } from 'path';
|
|
7
|
+
import { fileURLToPath, parse } from 'url';
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync } from 'fs';
|
|
9
|
+
// Import our modular components
|
|
10
|
+
import ConfigurationManager from './lib/configuration-manager.js';
|
|
11
|
+
import FileSystemManager from './lib/file-system-manager.js';
|
|
12
|
+
import BundleManager from './lib/bundle-manager.js';
|
|
13
|
+
import APIRouter from './lib/api-router.js';
|
|
14
|
+
import WebSocketManager from './lib/websocket-manager.js';
|
|
15
|
+
// Import existing lib modules
|
|
16
|
+
import SemanticSplitter from './lib/semantic-splitter.js';
|
|
17
|
+
import SimpleVectorStore from './lib/simple-vector-store.js';
|
|
18
|
+
import { MCPServer } from './lib/mcp-server.js';
|
|
19
|
+
import AgentRuntime from './lib/agent-runtime.js';
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
export class CntxServer {
|
|
22
|
+
CWD;
|
|
23
|
+
CNTX_DIR;
|
|
24
|
+
verbose;
|
|
25
|
+
mcpServerStarted;
|
|
26
|
+
mcpServer;
|
|
27
|
+
initMessages;
|
|
28
|
+
configManager;
|
|
29
|
+
databaseManager;
|
|
30
|
+
fileSystemManager;
|
|
31
|
+
bundleManager;
|
|
32
|
+
webSocketManager;
|
|
33
|
+
apiRouter;
|
|
34
|
+
semanticSplitter;
|
|
35
|
+
vectorStore;
|
|
36
|
+
agentRuntime;
|
|
37
|
+
semanticCache;
|
|
38
|
+
lastSemanticAnalysis;
|
|
39
|
+
vectorStoreInitialized;
|
|
40
|
+
semanticAnalysisManager;
|
|
41
|
+
activityManager;
|
|
42
|
+
constructor(cwd = process.cwd(), options = {}) {
|
|
43
|
+
this.CWD = cwd;
|
|
44
|
+
this.CNTX_DIR = join(cwd, '.cntx');
|
|
45
|
+
this.verbose = options.verbose || false;
|
|
46
|
+
this.mcpServerStarted = false;
|
|
47
|
+
this.mcpServer = null;
|
|
48
|
+
this.initMessages = [];
|
|
49
|
+
// Ensure directory exists
|
|
50
|
+
if (!existsSync(this.CNTX_DIR))
|
|
51
|
+
mkdirSync(this.CNTX_DIR, { recursive: true });
|
|
52
|
+
// Initialize modular components
|
|
53
|
+
this.configManager = new ConfigurationManager(cwd, { verbose: this.verbose });
|
|
54
|
+
this.databaseManager = this.configManager.dbManager;
|
|
55
|
+
this.fileSystemManager = new FileSystemManager(cwd, { verbose: this.verbose });
|
|
56
|
+
this.bundleManager = new BundleManager(this.configManager, this.fileSystemManager, this.verbose);
|
|
57
|
+
this.webSocketManager = new WebSocketManager(this.bundleManager, this.configManager, { verbose: this.verbose });
|
|
58
|
+
// AI Components
|
|
59
|
+
this.semanticSplitter = new SemanticSplitter({
|
|
60
|
+
maxChunkSize: 2000,
|
|
61
|
+
includeContext: true,
|
|
62
|
+
minFunctionSize: 50
|
|
63
|
+
});
|
|
64
|
+
this.vectorStore = new SimpleVectorStore(this.databaseManager, {
|
|
65
|
+
modelName: 'Xenova/all-MiniLM-L6-v2'
|
|
66
|
+
});
|
|
67
|
+
this.semanticCache = null;
|
|
68
|
+
this.lastSemanticAnalysis = null;
|
|
69
|
+
this.vectorStoreInitialized = false;
|
|
70
|
+
// Initialize Agent Runtime
|
|
71
|
+
this.agentRuntime = new AgentRuntime(this);
|
|
72
|
+
this.semanticAnalysisManager = this; // Self as manager
|
|
73
|
+
this.activityManager = this; // Simple mock for now
|
|
74
|
+
// Initialize API router
|
|
75
|
+
this.apiRouter = new APIRouter(this, this.configManager, this.bundleManager, this.fileSystemManager, this.semanticAnalysisManager, this.vectorStore, this.activityManager);
|
|
76
|
+
// Cross-module linkage
|
|
77
|
+
this.bundleManager.fileSystemManager = this.fileSystemManager;
|
|
78
|
+
this.bundleManager.webSocketManager = this.webSocketManager;
|
|
79
|
+
}
|
|
80
|
+
async init(options = {}) {
|
|
81
|
+
const { skipFileWatcher = false, skipBundleGeneration = false } = options;
|
|
82
|
+
// Load configs
|
|
83
|
+
this.configManager.loadConfig();
|
|
84
|
+
this.configManager.loadHiddenFilesConfig();
|
|
85
|
+
this.configManager.loadIgnorePatterns();
|
|
86
|
+
this.configManager.loadBundleStates();
|
|
87
|
+
if (!skipFileWatcher) {
|
|
88
|
+
this.startWatching();
|
|
89
|
+
// Trigger semantic analysis
|
|
90
|
+
if (!this.semanticCache) {
|
|
91
|
+
this.getSemanticAnalysis().catch(err => console.error('Initial semantic analysis failed:', err.message));
|
|
92
|
+
}
|
|
93
|
+
if (!skipBundleGeneration) {
|
|
94
|
+
await this.bundleManager.generateAllBundles();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Load reasoning/manifest
|
|
98
|
+
await this.agentRuntime.generateAgentManifest();
|
|
99
|
+
}
|
|
100
|
+
startWatching() {
|
|
101
|
+
this.fileSystemManager.startWatching((eventType, filename) => {
|
|
102
|
+
this.bundleManager.markBundlesChanged(filename);
|
|
103
|
+
this.webSocketManager.onFileChanged(filename, eventType);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async getSemanticAnalysis() {
|
|
107
|
+
// Try SQLite first
|
|
108
|
+
try {
|
|
109
|
+
const dbChunks = this.databaseManager.db.prepare('SELECT * FROM semantic_chunks').all();
|
|
110
|
+
if (dbChunks.length > 0) {
|
|
111
|
+
if (!this.semanticCache) {
|
|
112
|
+
this.semanticCache = {
|
|
113
|
+
chunks: dbChunks.map(row => this.databaseManager.mapChunkRow(row)),
|
|
114
|
+
summary: { totalChunks: dbChunks.length }
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return this.semanticCache;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (e) { }
|
|
121
|
+
// Fresh analysis
|
|
122
|
+
const files = this.fileSystemManager.getAllFiles().map(f => this.fileSystemManager.relativePath(f))
|
|
123
|
+
.filter(f => ['.js', '.jsx', '.ts', '.tsx', '.rs'].includes(extname(f).toLowerCase()));
|
|
124
|
+
let bundleConfig = null;
|
|
125
|
+
if (existsSync(this.configManager.CONFIG_FILE)) {
|
|
126
|
+
bundleConfig = JSON.parse(readFileSync(this.configManager.CONFIG_FILE, 'utf8'));
|
|
127
|
+
}
|
|
128
|
+
this.semanticCache = await this.semanticSplitter.extractSemanticChunks(this.CWD, files, bundleConfig);
|
|
129
|
+
this.lastSemanticAnalysis = Date.now();
|
|
130
|
+
if (this.semanticCache.chunks.length > 0) {
|
|
131
|
+
this.databaseManager.saveChunks(this.semanticCache.chunks);
|
|
132
|
+
}
|
|
133
|
+
this.enhanceSemanticChunksIfNeeded(this.semanticCache);
|
|
134
|
+
return this.semanticCache;
|
|
135
|
+
}
|
|
136
|
+
async enhanceSemanticChunksIfNeeded(analysis) {
|
|
137
|
+
if (!analysis || !analysis.chunks)
|
|
138
|
+
return;
|
|
139
|
+
if (!this.vectorStoreInitialized) {
|
|
140
|
+
await this.vectorStore.init();
|
|
141
|
+
this.vectorStoreInitialized = true;
|
|
142
|
+
}
|
|
143
|
+
for (const chunk of analysis.chunks) {
|
|
144
|
+
if (!this.databaseManager.getEmbedding(chunk.id)) {
|
|
145
|
+
await this.vectorStore.upsertChunk(chunk);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async refreshSemanticAnalysis() {
|
|
150
|
+
this.databaseManager.db.prepare('DELETE FROM semantic_chunks').run();
|
|
151
|
+
this.databaseManager.db.prepare('DELETE FROM vector_embeddings').run();
|
|
152
|
+
this.semanticCache = null;
|
|
153
|
+
return this.getSemanticAnalysis();
|
|
154
|
+
}
|
|
155
|
+
startMCPServer() {
|
|
156
|
+
if (!this.mcpServer) {
|
|
157
|
+
this.mcpServer = new MCPServer(this);
|
|
158
|
+
this.mcpServerStarted = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async listen(port = 3333, host = 'localhost') {
|
|
162
|
+
const server = createServer((req, res) => {
|
|
163
|
+
const url = parse(req.url || '/', true);
|
|
164
|
+
// Serve static files from web/dist
|
|
165
|
+
if (!url.pathname?.startsWith('/api/')) {
|
|
166
|
+
return this.handleStaticFile(req, res, url);
|
|
167
|
+
}
|
|
168
|
+
// Route API requests
|
|
169
|
+
this.apiRouter.handleRequest(req, res, url);
|
|
170
|
+
});
|
|
171
|
+
this.webSocketManager.initialize(server);
|
|
172
|
+
return new Promise((resolve) => {
|
|
173
|
+
server.listen(port, host, () => {
|
|
174
|
+
console.log(`🚀 cntx-ui server running at http://${host}:${port}`);
|
|
175
|
+
resolve(server);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
handleStaticFile(req, res, url) {
|
|
180
|
+
const webDir = join(__dirname, 'web/dist');
|
|
181
|
+
let filePath = join(webDir, url.pathname === '/' ? 'index.html' : url.pathname);
|
|
182
|
+
if (!existsSync(filePath)) {
|
|
183
|
+
filePath = join(webDir, 'index.html');
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const content = readFileSync(filePath);
|
|
187
|
+
const ext = extname(filePath).toLowerCase();
|
|
188
|
+
const contentType = this.getContentType(ext);
|
|
189
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
190
|
+
res.end(content);
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
res.writeHead(404);
|
|
194
|
+
res.end('Not Found');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
getContentType(ext) {
|
|
198
|
+
const types = {
|
|
199
|
+
'.html': 'text/html',
|
|
200
|
+
'.js': 'application/javascript',
|
|
201
|
+
'.css': 'text/css',
|
|
202
|
+
'.json': 'application/json',
|
|
203
|
+
'.png': 'image/png',
|
|
204
|
+
'.jpg': 'image/jpeg',
|
|
205
|
+
'.svg': 'image/svg+xml'
|
|
206
|
+
};
|
|
207
|
+
return types[ext] || 'text/plain';
|
|
208
|
+
}
|
|
209
|
+
// Activity Manager Mock
|
|
210
|
+
async loadActivities() {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export async function startServer(options = {}) {
|
|
215
|
+
const server = new CntxServer(options.cwd, options);
|
|
216
|
+
await server.init(options);
|
|
217
|
+
if (options.withMcp !== false)
|
|
218
|
+
server.startMCPServer();
|
|
219
|
+
return await server.listen(options.port, options.host);
|
|
220
|
+
}
|
|
221
|
+
export async function initConfig(cwd = process.cwd()) {
|
|
222
|
+
const server = new CntxServer(cwd);
|
|
223
|
+
// Implementation matches previous init logic
|
|
224
|
+
return [];
|
|
225
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cntx-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.
|
|
4
|
+
"version": "3.0.9",
|
|
5
5
|
"description": "Autonomous Repository Intelligence engine with web UI and MCP server. Unified semantic code understanding, local RAG, and agent working memory.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"repository-intelligence",
|
|
@@ -20,13 +20,12 @@
|
|
|
20
20
|
},
|
|
21
21
|
"author": "whaleen",
|
|
22
22
|
"license": "MIT",
|
|
23
|
+
"main": "dist/server.js",
|
|
23
24
|
"bin": {
|
|
24
|
-
"cntx-ui": "./bin/cntx-ui.js"
|
|
25
|
+
"cntx-ui": "./dist/bin/cntx-ui.js"
|
|
25
26
|
},
|
|
26
27
|
"files": [
|
|
27
|
-
"
|
|
28
|
-
"server.js",
|
|
29
|
-
"lib/",
|
|
28
|
+
"dist/",
|
|
30
29
|
"templates/",
|
|
31
30
|
"web/dist/",
|
|
32
31
|
"README.md",
|
|
@@ -36,12 +35,14 @@
|
|
|
36
35
|
"node": ">=18.0.0"
|
|
37
36
|
},
|
|
38
37
|
"scripts": {
|
|
39
|
-
"dev": "node
|
|
40
|
-
"build": "
|
|
38
|
+
"dev": "ts-node --esm bin/cntx-ui.ts",
|
|
39
|
+
"build:backend": "tsc && chmod +x dist/bin/cntx-ui.js",
|
|
40
|
+
"build:web": "cd web && npm install && npm run build",
|
|
41
|
+
"build": "npm run build:backend && npm run build:web",
|
|
41
42
|
"check:version-sync": "node scripts/check-version-sync.mjs",
|
|
42
43
|
"release:ci": "node scripts/release-from-package.mjs",
|
|
43
44
|
"prepublishOnly": "npm run build",
|
|
44
|
-
"test:local": "npm pack && npm install -g ./cntx-ui-3.0.
|
|
45
|
+
"test:local": "npm pack && npm install -g ./cntx-ui-3.0.8.tgz"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"@xenova/transformers": "^2.17.2",
|
|
@@ -49,7 +50,16 @@
|
|
|
49
50
|
"glob": "^9.0.0",
|
|
50
51
|
"tree-sitter": "^0.21.1",
|
|
51
52
|
"tree-sitter-javascript": "^0.23.1",
|
|
53
|
+
"tree-sitter-rust": "^0.21.0",
|
|
52
54
|
"tree-sitter-typescript": "^0.23.2",
|
|
53
55
|
"ws": "^8.13.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
59
|
+
"@types/glob": "^8.1.0",
|
|
60
|
+
"@types/node": "^25.3.2",
|
|
61
|
+
"@types/ws": "^8.18.1",
|
|
62
|
+
"ts-node": "^10.9.2",
|
|
63
|
+
"typescript": "^5.9.3"
|
|
54
64
|
}
|
|
55
65
|
}
|
package/bin/cntx-ui-mcp.sh
DELETED
package/bin/cntx-ui.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
4
|
-
import { dirname, join } from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
import { autoInitAndStart, startMCPServer, generateBundle, initConfig, getStatus } from '../server.js';
|
|
7
|
-
|
|
8
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const packagePath = join(__dirname, '..', 'package.json');
|
|
10
|
-
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
11
|
-
|
|
12
|
-
const args = process.argv.slice(2);
|
|
13
|
-
const command = args[0] || 'start';
|
|
14
|
-
const isVerbose = args.includes('--verbose');
|
|
15
|
-
|
|
16
|
-
// Graceful shutdown
|
|
17
|
-
process.on('SIGINT', () => {
|
|
18
|
-
console.log('\n Shutting down cntx-ui...');
|
|
19
|
-
process.exit(0);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
function showHelp() {
|
|
23
|
-
console.log(`cntx-ui v${packageJson.version}
|
|
24
|
-
|
|
25
|
-
${packageJson.description}
|
|
26
|
-
|
|
27
|
-
Usage:
|
|
28
|
-
cntx-ui [command] [options]
|
|
29
|
-
|
|
30
|
-
Commands:
|
|
31
|
-
(default) Auto-init if needed, then start web server
|
|
32
|
-
init Initialize configuration in current directory
|
|
33
|
-
mcp Start MCP server (stdio transport)
|
|
34
|
-
bundle [name] Generate specific bundle (default: master)
|
|
35
|
-
status Show current project status
|
|
36
|
-
|
|
37
|
-
Options:
|
|
38
|
-
--verbose Enable detailed logging
|
|
39
|
-
--version, -v Show version number
|
|
40
|
-
--help, -h Show this help message
|
|
41
|
-
|
|
42
|
-
Examples:
|
|
43
|
-
cntx-ui Start server (auto-inits if needed)
|
|
44
|
-
cntx-ui init Initialize a new project
|
|
45
|
-
cntx-ui mcp Start MCP server on stdio
|
|
46
|
-
cntx-ui bundle api Generate 'api' bundle
|
|
47
|
-
cntx-ui status Show project status
|
|
48
|
-
|
|
49
|
-
MCP Integration:
|
|
50
|
-
Running 'cntx-ui init' creates a .mcp.json file so Claude Code
|
|
51
|
-
can auto-discover the MCP server. Run 'cntx-ui mcp' for stdio mode.
|
|
52
|
-
|
|
53
|
-
Repository: ${packageJson.repository.url}
|
|
54
|
-
Author: ${packageJson.author}
|
|
55
|
-
License: ${packageJson.license}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function showVersion() {
|
|
59
|
-
console.log(`cntx-ui v${packageJson.version}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async function main() {
|
|
63
|
-
// Handle version flags
|
|
64
|
-
if (args.includes('--version') || args.includes('-v')) {
|
|
65
|
-
return showVersion();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Handle help flags
|
|
69
|
-
if (args.includes('--help') || args.includes('-h') || command === 'help') {
|
|
70
|
-
return showHelp();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
switch (command) {
|
|
75
|
-
case 'start':
|
|
76
|
-
case 'watch':
|
|
77
|
-
case 'w':
|
|
78
|
-
const port = parseInt(args[1]) || 3333;
|
|
79
|
-
await autoInitAndStart({ port, verbose: isVerbose });
|
|
80
|
-
break;
|
|
81
|
-
|
|
82
|
-
case 'mcp':
|
|
83
|
-
// MCP server mode - runs on stdio
|
|
84
|
-
await startMCPServer({ cwd: process.cwd(), verbose: isVerbose });
|
|
85
|
-
break;
|
|
86
|
-
|
|
87
|
-
case 'bundle':
|
|
88
|
-
case 'b':
|
|
89
|
-
const bundleName = args[1] || 'master';
|
|
90
|
-
try {
|
|
91
|
-
await generateBundle(bundleName);
|
|
92
|
-
console.log(`Bundle '${bundleName}' generated successfully`);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.error(`Failed to generate bundle '${bundleName}': ${error.message}`);
|
|
95
|
-
process.exit(1);
|
|
96
|
-
}
|
|
97
|
-
break;
|
|
98
|
-
|
|
99
|
-
case 'init':
|
|
100
|
-
case 'i':
|
|
101
|
-
await initConfig();
|
|
102
|
-
break;
|
|
103
|
-
|
|
104
|
-
case 'status':
|
|
105
|
-
case 's':
|
|
106
|
-
await getStatus();
|
|
107
|
-
break;
|
|
108
|
-
|
|
109
|
-
default:
|
|
110
|
-
console.error(`Unknown command: ${command}`);
|
|
111
|
-
console.log('Run "cntx-ui --help" for usage information.');
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
} catch (error) {
|
|
115
|
-
console.error(`Error: ${error.message}`);
|
|
116
|
-
if (isVerbose) {
|
|
117
|
-
console.error(error.stack);
|
|
118
|
-
}
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
main().catch(console.error);
|