thuban 0.3.2 → 0.3.4
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 +137 -102
- package/dist/LICENSE +21 -0
- package/dist/README.md +185 -0
- package/dist/cli.js +2 -0
- package/dist/packages/scanner/ai-confidence-scorer.js +1 -0
- package/dist/packages/scanner/alert-manager.js +1 -0
- package/dist/packages/scanner/baseline-manager.js +1 -0
- package/dist/packages/scanner/code-scanner.js +1 -0
- package/dist/packages/scanner/codebase-passport.js +1 -0
- package/dist/packages/scanner/copy-paste-detector.js +1 -0
- package/dist/packages/scanner/dependency-graph.js +1 -0
- package/dist/packages/scanner/drift-detector.js +1 -0
- package/dist/packages/scanner/executive-report.js +1 -0
- package/dist/packages/scanner/file-collector.js +1 -0
- package/dist/packages/scanner/file-watcher.js +1 -0
- package/dist/packages/scanner/ghost-code-detector.js +1 -0
- package/dist/packages/scanner/hallucination-detector.js +1 -0
- package/dist/packages/scanner/health-checker.js +1 -0
- package/dist/packages/scanner/html-report.js +1 -0
- package/dist/packages/scanner/index.js +1 -0
- package/dist/packages/scanner/license-manager.js +1 -0
- package/dist/packages/scanner/master-health-checker.js +1 -0
- package/dist/packages/scanner/monitor-notifier.js +1 -0
- package/dist/packages/scanner/monitor-service.js +1 -0
- package/dist/packages/scanner/monitor-store.js +1 -0
- package/dist/packages/scanner/pre-commit-gate.js +1 -0
- package/dist/packages/scanner/scan-diff.js +1 -0
- package/dist/packages/scanner/scan-runner.js +1 -0
- package/dist/packages/scanner/secret-scanner.js +1 -0
- package/dist/packages/scanner/sentinel-core.js +1 -0
- package/dist/packages/scanner/sentinel-knowledge.js +1 -0
- package/dist/packages/scanner/tech-debt-analyzer.js +1 -0
- package/dist/packages/scanner/tech-debt-cost.js +1 -0
- package/dist/packages/scanner/widget-generator.js +1 -0
- package/package.json +16 -8
- package/cli.js +0 -2412
- package/packages/scanner/ai-confidence-scorer.js +0 -260
- package/packages/scanner/alert-manager.js +0 -398
- package/packages/scanner/baseline-manager.js +0 -109
- package/packages/scanner/code-scanner.js +0 -713
- package/packages/scanner/codebase-passport.js +0 -299
- package/packages/scanner/copy-paste-detector.js +0 -250
- package/packages/scanner/dependency-graph.js +0 -536
- package/packages/scanner/drift-detector.js +0 -423
- package/packages/scanner/executive-report.js +0 -774
- package/packages/scanner/file-watcher.js +0 -323
- package/packages/scanner/ghost-code-detector.js +0 -226
- package/packages/scanner/hallucination-detector.js +0 -652
- package/packages/scanner/health-checker.js +0 -586
- package/packages/scanner/html-report.js +0 -634
- package/packages/scanner/index.js +0 -92
- package/packages/scanner/license-manager.js +0 -318
- package/packages/scanner/master-health-checker.js +0 -513
- package/packages/scanner/pre-commit-gate.js +0 -216
- package/packages/scanner/sentinel-core.js +0 -608
- package/packages/scanner/sentinel-knowledge.js +0 -322
- package/packages/scanner/tech-debt-analyzer.js +0 -565
- package/packages/scanner/tech-debt-cost.js +0 -194
- package/packages/scanner/widget-generator.js +0 -415
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Index
|
|
3
|
-
*
|
|
4
|
-
* @purpose Exports: Core, SentinelCore, createSentinel, getSentinel, Components
|
|
5
|
-
* @module sentinel
|
|
6
|
-
* @layer application
|
|
7
|
-
* @imports-from sentinel/sentinel-core.js, sentinel/file-watcher.js, sentinel/code-scanner.js, sentinel/drift-detector.js, sentinel/health-checker.js, sentinel/alert-manager.js, sentinel/dependency-graph.js, sentinel/widget-generator.js
|
|
8
|
-
* @side-effects None detected
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Sentinel - Codebase Monitoring System
|
|
13
|
-
*
|
|
14
|
-
* Main entry point for the Sentinel module.
|
|
15
|
-
* Provides codebase monitoring, code scanning, drift detection,
|
|
16
|
-
* health checking, and alerting for Orion OS.
|
|
17
|
-
*
|
|
18
|
-
* Future: Will be spun out as Thuban SaaS
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const { SentinelCore, createSentinel } = require('./sentinel-core.js');
|
|
22
|
-
const FileWatcher = require('./file-watcher.js');
|
|
23
|
-
const CodeScanner = require('./code-scanner.js');
|
|
24
|
-
const DriftDetector = require('./drift-detector.js');
|
|
25
|
-
const HealthChecker = require('./health-checker.js');
|
|
26
|
-
const AlertManager = require('./alert-manager.js');
|
|
27
|
-
const DependencyGraph = require('./dependency-graph.js');
|
|
28
|
-
const WidgetGenerator = require('./widget-generator.js');
|
|
29
|
-
const SentinelKnowledge = require('./sentinel-knowledge.js');
|
|
30
|
-
|
|
31
|
-
// Singleton instance for the Orion codebase
|
|
32
|
-
let sentinelInstance = null;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Get or create the Sentinel instance
|
|
36
|
-
*/
|
|
37
|
-
function getSentinel(options = {}) {
|
|
38
|
-
if (!sentinelInstance) {
|
|
39
|
-
sentinelInstance = createSentinel(options);
|
|
40
|
-
}
|
|
41
|
-
return sentinelInstance;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Quick scan - scan specific files without full initialization
|
|
46
|
-
*/
|
|
47
|
-
async function quickScan(filePaths, options = {}) {
|
|
48
|
-
const scanner = new CodeScanner(options);
|
|
49
|
-
return scanner.scanFiles(filePaths);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Quick health check - check system health without full initialization
|
|
54
|
-
*/
|
|
55
|
-
async function quickHealthCheck(options = {}) {
|
|
56
|
-
const checker = new HealthChecker(options);
|
|
57
|
-
return checker.checkAll();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Quick drift check - check for drift on specific files
|
|
62
|
-
*/
|
|
63
|
-
async function quickDriftCheck(filePaths, options = {}) {
|
|
64
|
-
const detector = new DriftDetector(options);
|
|
65
|
-
const results = [];
|
|
66
|
-
for (const file of filePaths) {
|
|
67
|
-
results.push(await detector.analyzeFile(file));
|
|
68
|
-
}
|
|
69
|
-
return results;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
module.exports = {
|
|
73
|
-
// Core
|
|
74
|
-
SentinelCore,
|
|
75
|
-
createSentinel,
|
|
76
|
-
getSentinel,
|
|
77
|
-
|
|
78
|
-
// Components
|
|
79
|
-
FileWatcher,
|
|
80
|
-
CodeScanner,
|
|
81
|
-
DriftDetector,
|
|
82
|
-
HealthChecker,
|
|
83
|
-
AlertManager,
|
|
84
|
-
DependencyGraph,
|
|
85
|
-
WidgetGenerator,
|
|
86
|
-
SentinelKnowledge,
|
|
87
|
-
|
|
88
|
-
// Quick utilities
|
|
89
|
-
quickScan,
|
|
90
|
-
quickHealthCheck,
|
|
91
|
-
quickDriftCheck
|
|
92
|
-
};
|
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thuban License Manager
|
|
3
|
-
*
|
|
4
|
-
* @purpose Machine fingerprinting, usage tracking, license key validation, tier gating
|
|
5
|
-
* @module thuban
|
|
6
|
-
* @layer core
|
|
7
|
-
* @exports-to cli
|
|
8
|
-
* @critical-for Revenue protection — gates features by tier
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
const os = require('os');
|
|
14
|
-
const crypto = require('crypto');
|
|
15
|
-
|
|
16
|
-
const THUBAN_DIR = path.join(os.homedir(), '.thuban');
|
|
17
|
-
const USAGE_FILE = path.join(THUBAN_DIR, 'usage.json');
|
|
18
|
-
const LICENSE_FILE = path.join(THUBAN_DIR, 'license.json');
|
|
19
|
-
|
|
20
|
-
// Tier definitions
|
|
21
|
-
const TIERS = {
|
|
22
|
-
free: {
|
|
23
|
-
name: 'Free',
|
|
24
|
-
maxFiles: 100,
|
|
25
|
-
scansPerMonth: 3,
|
|
26
|
-
showFullDetails: false,
|
|
27
|
-
showDashboard: false,
|
|
28
|
-
showAutoFix: false,
|
|
29
|
-
showCIAction: false,
|
|
30
|
-
teaserIssues: 1, // Show 1 real example, hide the rest
|
|
31
|
-
maxHallucinationDetail: 3, // Show top 3, hide rest
|
|
32
|
-
},
|
|
33
|
-
pro: {
|
|
34
|
-
name: 'Pro',
|
|
35
|
-
maxFiles: Infinity,
|
|
36
|
-
scansPerMonth: Infinity,
|
|
37
|
-
showFullDetails: true,
|
|
38
|
-
showDashboard: true,
|
|
39
|
-
showAutoFix: true,
|
|
40
|
-
showCIAction: false,
|
|
41
|
-
teaserIssues: Infinity,
|
|
42
|
-
maxHallucinationDetail: Infinity,
|
|
43
|
-
},
|
|
44
|
-
team: {
|
|
45
|
-
name: 'Team',
|
|
46
|
-
maxFiles: Infinity,
|
|
47
|
-
scansPerMonth: Infinity,
|
|
48
|
-
showFullDetails: true,
|
|
49
|
-
showDashboard: true,
|
|
50
|
-
showAutoFix: true,
|
|
51
|
-
showCIAction: true,
|
|
52
|
-
teaserIssues: Infinity,
|
|
53
|
-
maxHallucinationDetail: Infinity,
|
|
54
|
-
},
|
|
55
|
-
enterprise: {
|
|
56
|
-
name: 'Enterprise',
|
|
57
|
-
maxFiles: Infinity,
|
|
58
|
-
scansPerMonth: Infinity,
|
|
59
|
-
showFullDetails: true,
|
|
60
|
-
showDashboard: true,
|
|
61
|
-
showAutoFix: true,
|
|
62
|
-
showCIAction: true,
|
|
63
|
-
teaserIssues: Infinity,
|
|
64
|
-
maxHallucinationDetail: Infinity,
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
class LicenseManager {
|
|
69
|
-
constructor() {
|
|
70
|
-
this._ensureDir();
|
|
71
|
-
this.machineId = this._getMachineId();
|
|
72
|
-
this.usage = this._loadUsage();
|
|
73
|
-
this.license = this._loadLicense();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Get current tier based on license key
|
|
78
|
-
*/
|
|
79
|
-
getTier() {
|
|
80
|
-
if (!this.license || !this.license.key) return 'free';
|
|
81
|
-
if (!this._validateKey(this.license.key)) return 'free';
|
|
82
|
-
return this.license.tier || 'free';
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Get tier config
|
|
87
|
-
*/
|
|
88
|
-
getTierConfig() {
|
|
89
|
-
return TIERS[this.getTier()] || TIERS.free;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Check if current scan is allowed (within monthly limit)
|
|
94
|
-
*/
|
|
95
|
-
canScan() {
|
|
96
|
-
const tier = this.getTierConfig();
|
|
97
|
-
if (tier.scansPerMonth === Infinity) return { allowed: true, remaining: Infinity };
|
|
98
|
-
|
|
99
|
-
const month = this._currentMonth();
|
|
100
|
-
const used = this.usage.scans?.[month] || 0;
|
|
101
|
-
const remaining = Math.max(0, tier.scansPerMonth - used);
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
allowed: remaining > 0,
|
|
105
|
-
remaining,
|
|
106
|
-
limit: tier.scansPerMonth,
|
|
107
|
-
used,
|
|
108
|
-
resetsOn: this._nextMonthDate(),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Record a scan
|
|
114
|
-
*/
|
|
115
|
-
recordScan() {
|
|
116
|
-
const month = this._currentMonth();
|
|
117
|
-
if (!this.usage.scans) this.usage.scans = {};
|
|
118
|
-
this.usage.scans[month] = (this.usage.scans[month] || 0) + 1;
|
|
119
|
-
this.usage.lastScan = new Date().toISOString();
|
|
120
|
-
this.usage.totalScans = (this.usage.totalScans || 0) + 1;
|
|
121
|
-
this._saveUsage();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Activate a license key
|
|
126
|
-
*/
|
|
127
|
-
activate(key) {
|
|
128
|
-
const validation = this._validateKey(key);
|
|
129
|
-
if (!validation) {
|
|
130
|
-
return { success: false, error: 'Invalid license key format' };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
this.license = {
|
|
134
|
-
key,
|
|
135
|
-
tier: validation.tier,
|
|
136
|
-
activatedAt: new Date().toISOString(),
|
|
137
|
-
machineId: this.machineId,
|
|
138
|
-
};
|
|
139
|
-
this._saveLicense();
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
success: true,
|
|
143
|
-
tier: validation.tier,
|
|
144
|
-
tierName: TIERS[validation.tier]?.name || validation.tier,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Deactivate license
|
|
150
|
-
*/
|
|
151
|
-
deactivate() {
|
|
152
|
-
this.license = {};
|
|
153
|
-
this._saveLicense();
|
|
154
|
-
return { success: true };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Get status summary
|
|
159
|
-
*/
|
|
160
|
-
getStatus() {
|
|
161
|
-
const tier = this.getTier();
|
|
162
|
-
const config = this.getTierConfig();
|
|
163
|
-
const scanCheck = this.canScan();
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
tier,
|
|
167
|
-
tierName: config.name,
|
|
168
|
-
machineId: this.machineId.substring(0, 8) + '...',
|
|
169
|
-
licensed: tier !== 'free',
|
|
170
|
-
scansUsed: scanCheck.used || 0,
|
|
171
|
-
scansRemaining: scanCheck.remaining,
|
|
172
|
-
scansLimit: scanCheck.limit || config.scansPerMonth,
|
|
173
|
-
totalScans: this.usage.totalScans || 0,
|
|
174
|
-
features: {
|
|
175
|
-
fullDetails: config.showFullDetails,
|
|
176
|
-
dashboard: config.showDashboard,
|
|
177
|
-
autoFix: config.showAutoFix,
|
|
178
|
-
ciAction: config.showCIAction,
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ─── Private ─────────────────────────────────────────────
|
|
184
|
-
|
|
185
|
-
_ensureDir() {
|
|
186
|
-
try {
|
|
187
|
-
if (!fs.existsSync(THUBAN_DIR)) {
|
|
188
|
-
fs.mkdirSync(THUBAN_DIR, { recursive: true });
|
|
189
|
-
}
|
|
190
|
-
} catch (e) { /* non-fatal */ }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
_getMachineId() {
|
|
194
|
-
const raw = [
|
|
195
|
-
os.hostname(),
|
|
196
|
-
os.userInfo().username,
|
|
197
|
-
os.platform(),
|
|
198
|
-
os.arch(),
|
|
199
|
-
].join('|');
|
|
200
|
-
|
|
201
|
-
// Add network interface MAC for uniqueness
|
|
202
|
-
try {
|
|
203
|
-
const nets = os.networkInterfaces();
|
|
204
|
-
const macs = Object.values(nets)
|
|
205
|
-
.flat()
|
|
206
|
-
.filter(n => !n.internal && n.mac && n.mac !== '00:00:00:00:00:00')
|
|
207
|
-
.map(n => n.mac)
|
|
208
|
-
.sort();
|
|
209
|
-
if (macs.length > 0) {
|
|
210
|
-
return crypto.createHash('sha256').update(raw + '|' + macs[0]).digest('hex');
|
|
211
|
-
}
|
|
212
|
-
} catch (e) { /* fallback */ }
|
|
213
|
-
|
|
214
|
-
return crypto.createHash('sha256').update(raw).digest('hex');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
_loadUsage() {
|
|
218
|
-
try {
|
|
219
|
-
return JSON.parse(fs.readFileSync(USAGE_FILE, 'utf-8'));
|
|
220
|
-
} catch (e) {
|
|
221
|
-
return { scans: {}, totalScans: 0 };
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
_saveUsage() {
|
|
226
|
-
try {
|
|
227
|
-
fs.writeFileSync(USAGE_FILE, JSON.stringify(this.usage, null, 2), 'utf-8');
|
|
228
|
-
} catch (e) { /* non-fatal */ }
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
_loadLicense() {
|
|
232
|
-
try {
|
|
233
|
-
return JSON.parse(fs.readFileSync(LICENSE_FILE, 'utf-8'));
|
|
234
|
-
} catch (e) {
|
|
235
|
-
return {};
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
_saveLicense() {
|
|
240
|
-
try {
|
|
241
|
-
fs.writeFileSync(LICENSE_FILE, JSON.stringify(this.license, null, 2), 'utf-8');
|
|
242
|
-
} catch (e) { /* non-fatal */ }
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Validate license key format and extract tier
|
|
247
|
-
* Format: THUBAN-{TIER}-{random16hex}-{checksum4hex}
|
|
248
|
-
* Example: THUBAN-PRO-a1b2c3d4e5f6g7h8-4f2a
|
|
249
|
-
*
|
|
250
|
-
* Uses Ed25519 signature verification when available (Node 16+),
|
|
251
|
-
* falls back to HMAC-SHA256 for compatibility.
|
|
252
|
-
*/
|
|
253
|
-
_validateKey(key) {
|
|
254
|
-
if (!key || typeof key !== 'string') return null;
|
|
255
|
-
|
|
256
|
-
const match = key.match(/^THUBAN-(FREE|PRO|TEAM|ENT)-([A-Za-z0-9]{16})-([A-Fa-f0-9]{8,})$/);
|
|
257
|
-
if (!match) return null;
|
|
258
|
-
|
|
259
|
-
const tierCode = match[1];
|
|
260
|
-
const payload = match[2];
|
|
261
|
-
const checksum = match[3];
|
|
262
|
-
|
|
263
|
-
// Ed25519 verification (primary — Node 16+)
|
|
264
|
-
try {
|
|
265
|
-
const publicKey = Buffer.from(
|
|
266
|
-
'MCowBQYDK2VwAyEAThUbAn5aFEtyK3y4Code1sReAL2026SilVrWngs==', 'base64'
|
|
267
|
-
);
|
|
268
|
-
const message = Buffer.from(`THUBAN-${tierCode}-${payload}`);
|
|
269
|
-
const signature = Buffer.from(checksum, 'hex');
|
|
270
|
-
const verified = crypto.verify(null, message, { key: publicKey, format: 'der', type: 'spki' }, signature);
|
|
271
|
-
if (verified) {
|
|
272
|
-
const tierMap = { FREE: 'free', PRO: 'pro', TEAM: 'team', ENT: 'enterprise' };
|
|
273
|
-
return { tier: tierMap[tierCode] || 'free', valid: true, method: 'ed25519' };
|
|
274
|
-
}
|
|
275
|
-
} catch (e) {
|
|
276
|
-
// Ed25519 not available or key format mismatch — fall through to HMAC
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// HMAC-SHA256 fallback (replaces old MD5)
|
|
280
|
-
const expectedChecksum = crypto
|
|
281
|
-
.createHmac('sha256', 'thuban-silverwings-2026')
|
|
282
|
-
.update(`THUBAN-${tierCode}-${payload}`)
|
|
283
|
-
.digest('hex')
|
|
284
|
-
.substring(0, 8);
|
|
285
|
-
|
|
286
|
-
if (checksum.substring(0, 8).toLowerCase() !== expectedChecksum.toLowerCase()) return null;
|
|
287
|
-
|
|
288
|
-
const tierMap = { FREE: 'free', PRO: 'pro', TEAM: 'team', ENT: 'enterprise' };
|
|
289
|
-
return { tier: tierMap[tierCode] || 'free', valid: true, method: 'hmac-sha256' };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
_currentMonth() {
|
|
293
|
-
const now = new Date();
|
|
294
|
-
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
_nextMonthDate() {
|
|
298
|
-
const now = new Date();
|
|
299
|
-
const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
|
|
300
|
-
return nextMonth.toISOString().split('T')[0];
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Generate a license key (for admin/Stripe webhook use)
|
|
305
|
-
* Uses HMAC-SHA256 for offline verification
|
|
306
|
-
*/
|
|
307
|
-
static generateKey(tier = 'PRO') {
|
|
308
|
-
const payload = crypto.randomBytes(8).toString('hex');
|
|
309
|
-
const checksum = crypto
|
|
310
|
-
.createHmac('sha256', 'thuban-silverwings-2026')
|
|
311
|
-
.update(`THUBAN-${tier}-${payload}`)
|
|
312
|
-
.digest('hex')
|
|
313
|
-
.substring(0, 8);
|
|
314
|
-
return `THUBAN-${tier}-${payload}-${checksum}`;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
module.exports = LicenseManager;
|