reepoe 1.2.4 → 1.2.5
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/bin/start.js +154 -2
- package/package.json +1 -2
- package/lib/activation-client.js +0 -326
package/bin/start.js
CHANGED
|
@@ -176,11 +176,158 @@ async function checkActivationStatus() {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
// ========== BYOK WIZARD (FIX #1) ==========
|
|
180
|
+
async function checkAndSetupBYOK() {
|
|
181
|
+
const byokConfigPath = path.join(os.homedir(), '.reepoe', 'config.json');
|
|
182
|
+
|
|
183
|
+
if (fs.existsSync(byokConfigPath)) {
|
|
184
|
+
return; // Already configured
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const readline = require('readline');
|
|
188
|
+
const crypto = require('crypto');
|
|
189
|
+
|
|
190
|
+
console.log('\n╔══════════════════════════════════════════════════════════════╗');
|
|
191
|
+
console.log('║ ReePoe BYOK Setup - Bring Your Own Key ║');
|
|
192
|
+
console.log('╚══════════════════════════════════════════════════════════════╝\n');
|
|
193
|
+
console.log('💡 ReePoe can use YOUR OpenRouter API key for intelligent routing.');
|
|
194
|
+
console.log(' Benefits:');
|
|
195
|
+
console.log(' ✅ Use OpenRouter free tier (50 queries/day)');
|
|
196
|
+
console.log(' ✅ Full cost transparency and tracking');
|
|
197
|
+
console.log(' ✅ Uses google/gemma-3-4b-it:free for routing\n');
|
|
198
|
+
console.log(' ℹ️ Your key is stored locally and NEVER transmitted.');
|
|
199
|
+
console.log(' ℹ️ Get a free key at: https://openrouter.ai/keys\n');
|
|
200
|
+
|
|
201
|
+
const rl = readline.createInterface({
|
|
202
|
+
input: process.stdin,
|
|
203
|
+
output: process.stdout
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const apiKey = await new Promise((resolve) => {
|
|
207
|
+
rl.question('Enter OpenRouter API key (or press Enter to skip): ', (answer) => {
|
|
208
|
+
rl.close();
|
|
209
|
+
resolve(answer.trim());
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
if (apiKey && apiKey.startsWith('sk-or-v1-')) {
|
|
214
|
+
try {
|
|
215
|
+
const globalConfigDir = path.join(os.homedir(), '.reepoe');
|
|
216
|
+
if (!fs.existsSync(globalConfigDir)) {
|
|
217
|
+
fs.mkdirSync(globalConfigDir, { recursive: true, mode: 0o700 });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const userId = 'anon_' + crypto.createHash('sha256')
|
|
221
|
+
.update(crypto.randomBytes(32))
|
|
222
|
+
.digest('hex')
|
|
223
|
+
.substring(0, 16);
|
|
224
|
+
|
|
225
|
+
const encryptedKey = Buffer.from(apiKey).toString('base64');
|
|
226
|
+
|
|
227
|
+
const globalConfig = {
|
|
228
|
+
api_keys: { openrouter: encryptedKey, encrypted: true },
|
|
229
|
+
user_id: userId,
|
|
230
|
+
analytics: { enabled: true, anonymous: true },
|
|
231
|
+
created_at: new Date().toISOString()
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
fs.writeFileSync(byokConfigPath, JSON.stringify(globalConfig, null, 2), { mode: 0o600 });
|
|
235
|
+
console.log('\n✅ API key stored securely in ~/.reepoe/config.json');
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error(`\n❌ Failed to store API key: ${error.message}`);
|
|
238
|
+
}
|
|
239
|
+
} else if (apiKey) {
|
|
240
|
+
console.log('\n⚠️ Invalid key format (should start with sk-or-v1-)');
|
|
241
|
+
} else {
|
|
242
|
+
console.log('\n ℹ️ Skipping BYOK setup - using default routing');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ========== PROJECT SETUP (FIX #2) ==========
|
|
247
|
+
function detectProjectType(projectPath) {
|
|
248
|
+
const markers = {
|
|
249
|
+
'package.json': { language: 'javascript', name: 'Node.js' },
|
|
250
|
+
'requirements.txt': { language: 'python', name: 'Python' },
|
|
251
|
+
'go.mod': { language: 'go', name: 'Go' },
|
|
252
|
+
'Cargo.toml': { language: 'rust', name: 'Rust' },
|
|
253
|
+
'pom.xml': { language: 'java', name: 'Java' }
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
for (const [file, config] of Object.entries(markers)) {
|
|
257
|
+
if (fs.existsSync(path.join(projectPath, file))) {
|
|
258
|
+
let projectName = path.basename(projectPath);
|
|
259
|
+
|
|
260
|
+
if (file === 'package.json') {
|
|
261
|
+
try {
|
|
262
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(projectPath, file), 'utf8'));
|
|
263
|
+
projectName = pkg.name || projectName;
|
|
264
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
265
|
+
if (deps.express) config.framework = 'Express';
|
|
266
|
+
else if (deps.react) config.framework = 'React';
|
|
267
|
+
else if (deps.vue) config.framework = 'Vue';
|
|
268
|
+
} catch (e) {
|
|
269
|
+
// Use defaults
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { ...config, projectName };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return { language: 'unknown', name: 'Generic', projectName: path.basename(projectPath) };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function setupProjectIfNeeded(port) {
|
|
281
|
+
const configPath = path.join(process.cwd(), 'reepoe.config.json');
|
|
282
|
+
|
|
283
|
+
if (fs.existsSync(configPath)) {
|
|
284
|
+
return; // Already set up
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
console.log('🔍 First run in this directory - initializing project...\n');
|
|
288
|
+
|
|
289
|
+
const projectInfo = detectProjectType(process.cwd());
|
|
290
|
+
console.log(` ✅ ${projectInfo.name} project detected`);
|
|
291
|
+
if (projectInfo.framework) {
|
|
292
|
+
console.log(` Framework: ${projectInfo.framework}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const config = {
|
|
296
|
+
project: {
|
|
297
|
+
name: projectInfo.projectName,
|
|
298
|
+
type: projectInfo.language,
|
|
299
|
+
framework: projectInfo.framework || null,
|
|
300
|
+
root: process.cwd()
|
|
301
|
+
},
|
|
302
|
+
api: { port, host: '127.0.0.1' },
|
|
303
|
+
mini_rag: {
|
|
304
|
+
enabled: true,
|
|
305
|
+
confidence_threshold: 0.6,
|
|
306
|
+
cta_whitelist: ['run_tests', 'open_file', 'search_symbols', 'get_repo_stats']
|
|
307
|
+
},
|
|
308
|
+
scanner: { language: projectInfo.language, auto_scan: true }
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
312
|
+
console.log(` ✅ Configuration created`);
|
|
313
|
+
|
|
314
|
+
['data', 'logs'].forEach(dir => {
|
|
315
|
+
const dirPath = path.join(process.cwd(), dir);
|
|
316
|
+
if (!fs.existsSync(dirPath)) {
|
|
317
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
console.log(` ✅ Directories created\n`);
|
|
321
|
+
}
|
|
322
|
+
|
|
179
323
|
// Main function
|
|
180
324
|
async function main() {
|
|
181
325
|
// Check activation first (Phase 3B)
|
|
182
326
|
const email = await checkActivationStatus();
|
|
183
327
|
|
|
328
|
+
// FIX #1: Check and setup BYOK if needed
|
|
329
|
+
await checkAndSetupBYOK();
|
|
330
|
+
|
|
184
331
|
console.log('\n🚀 Starting ReePoe server...\n');
|
|
185
332
|
|
|
186
333
|
// Read configuration
|
|
@@ -202,6 +349,11 @@ async function main() {
|
|
|
202
349
|
// Detect cloud environment
|
|
203
350
|
const isCloud = isCloudEnvironment();
|
|
204
351
|
|
|
352
|
+
// FIX #2: Setup project if needed (before starting binary)
|
|
353
|
+
if (!isCloud) {
|
|
354
|
+
await setupProjectIfNeeded(port);
|
|
355
|
+
}
|
|
356
|
+
|
|
205
357
|
if (isCloud) {
|
|
206
358
|
// Cloud environment - skip local server
|
|
207
359
|
console.log('\n☁️ Cloud environment detected');
|
|
@@ -253,8 +405,8 @@ async function main() {
|
|
|
253
405
|
const pidFile = path.join(os.tmpdir(), `reepoe-${port}.pid`);
|
|
254
406
|
fs.writeFileSync(pidFile, child.pid.toString());
|
|
255
407
|
|
|
256
|
-
// Wait
|
|
257
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
408
|
+
// Wait for server to start (FIX #3: Increased timeout)
|
|
409
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
258
410
|
|
|
259
411
|
const started = await isServerRunning(port);
|
|
260
412
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reepoe",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "ReePoe AI Code Manager - Install in any codebase for instant AI agent integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
53
|
"bin",
|
|
54
|
-
"lib",
|
|
55
54
|
"scripts",
|
|
56
55
|
"binaries",
|
|
57
56
|
"README.md"
|
package/lib/activation-client.js
DELETED
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Activation API Client (Phase 3B)
|
|
4
|
-
* Handles communication with ReePoe activation API
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const axios = require('axios');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const os = require('os');
|
|
11
|
-
|
|
12
|
-
const API_BASE_URL = process.env.REEPOE_API_URL || 'https://reepoe-api.onrender.com';
|
|
13
|
-
|
|
14
|
-
class ActivationClient {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.apiUrl = API_BASE_URL;
|
|
17
|
-
this.activationFile = path.join(os.homedir(), '.reepoe', 'activation.json');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check if user is activated
|
|
22
|
-
*/
|
|
23
|
-
async checkActivation(email, version) {
|
|
24
|
-
try {
|
|
25
|
-
const response = await axios.post(`${this.apiUrl}/api/activate/check`, {
|
|
26
|
-
email,
|
|
27
|
-
version
|
|
28
|
-
});
|
|
29
|
-
return response.data;
|
|
30
|
-
} catch (error) {
|
|
31
|
-
if (error.response) {
|
|
32
|
-
return error.response.data;
|
|
33
|
-
}
|
|
34
|
-
throw new Error(`Activation check failed: ${error.message}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Redeem activation code
|
|
40
|
-
*/
|
|
41
|
-
async redeemCode(email, code, version) {
|
|
42
|
-
try {
|
|
43
|
-
const machineId = this.getMachineId();
|
|
44
|
-
|
|
45
|
-
const response = await axios.post(`${this.apiUrl}/api/activate/redeem`, {
|
|
46
|
-
email,
|
|
47
|
-
code,
|
|
48
|
-
version,
|
|
49
|
-
machine_id: machineId
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (response.data.success) {
|
|
53
|
-
// Save activation locally
|
|
54
|
-
this.saveActivation(response.data);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return response.data;
|
|
58
|
-
} catch (error) {
|
|
59
|
-
if (error.response) {
|
|
60
|
-
return error.response.data;
|
|
61
|
-
}
|
|
62
|
-
throw new Error(`Code redemption failed: ${error.message}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Join waitlist
|
|
68
|
-
*/
|
|
69
|
-
async joinWaitlist(email) {
|
|
70
|
-
try {
|
|
71
|
-
const response = await axios.post(`${this.apiUrl}/api/waitlist/join`, {
|
|
72
|
-
email
|
|
73
|
-
});
|
|
74
|
-
return response.data;
|
|
75
|
-
} catch (error) {
|
|
76
|
-
if (error.response) {
|
|
77
|
-
return error.response.data;
|
|
78
|
-
}
|
|
79
|
-
throw new Error(`Waitlist join failed: ${error.message}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Get user metrics
|
|
85
|
-
*/
|
|
86
|
-
async getUserMetrics(email) {
|
|
87
|
-
try {
|
|
88
|
-
const response = await axios.get(`${this.apiUrl}/api/metrics/user/${email}`);
|
|
89
|
-
return response.data;
|
|
90
|
-
} catch (error) {
|
|
91
|
-
if (error.response) {
|
|
92
|
-
return error.response.data;
|
|
93
|
-
}
|
|
94
|
-
throw new Error(`Failed to get metrics: ${error.message}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Request extension
|
|
100
|
-
*/
|
|
101
|
-
async requestExtension(email, reason) {
|
|
102
|
-
try {
|
|
103
|
-
const response = await axios.post(`${this.apiUrl}/api/users/extend-request`, {
|
|
104
|
-
email,
|
|
105
|
-
reason
|
|
106
|
-
});
|
|
107
|
-
return response.data;
|
|
108
|
-
} catch (error) {
|
|
109
|
-
if (error.response) {
|
|
110
|
-
return error.response.data;
|
|
111
|
-
}
|
|
112
|
-
throw new Error(`Extension request failed: ${error.message}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Admin: Login
|
|
118
|
-
*/
|
|
119
|
-
async adminLogin(email, adminCode) {
|
|
120
|
-
try {
|
|
121
|
-
const response = await axios.post(`${this.apiUrl}/api/admin/auth`, {
|
|
122
|
-
email,
|
|
123
|
-
admin_code: adminCode
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
if (response.data.success) {
|
|
127
|
-
// Save admin token
|
|
128
|
-
this.saveAdminToken(response.data.admin_token);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return response.data;
|
|
132
|
-
} catch (error) {
|
|
133
|
-
if (error.response) {
|
|
134
|
-
return error.response.data;
|
|
135
|
-
}
|
|
136
|
-
throw new Error(`Admin login failed: ${error.message}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Admin: List users
|
|
142
|
-
*/
|
|
143
|
-
async adminListUsers(status = null, limit = 50) {
|
|
144
|
-
try {
|
|
145
|
-
const token = this.loadAdminToken();
|
|
146
|
-
if (!token) {
|
|
147
|
-
throw new Error('Admin token not found. Run: reepoe admin login');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
let url = `${this.apiUrl}/api/admin/users?limit=${limit}`;
|
|
151
|
-
if (status) {
|
|
152
|
-
url += `&status=${status}`;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const response = await axios.get(url, {
|
|
156
|
-
headers: { 'Authorization': `Bearer ${token}` }
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return response.data;
|
|
160
|
-
} catch (error) {
|
|
161
|
-
if (error.response) {
|
|
162
|
-
return error.response.data;
|
|
163
|
-
}
|
|
164
|
-
throw new Error(`Failed to list users: ${error.message}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Admin: Generate codes
|
|
170
|
-
*/
|
|
171
|
-
async adminGenerateCodes(count, codeType = 'ALPHA', adminEmail, notes) {
|
|
172
|
-
try {
|
|
173
|
-
const token = this.loadAdminToken();
|
|
174
|
-
if (!token) {
|
|
175
|
-
throw new Error('Admin token not found. Run: reepoe admin login');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const response = await axios.post(`${this.apiUrl}/api/admin/codes/generate`, {
|
|
179
|
-
count,
|
|
180
|
-
code_type: codeType,
|
|
181
|
-
admin_email: adminEmail,
|
|
182
|
-
notes
|
|
183
|
-
}, {
|
|
184
|
-
headers: { 'Authorization': `Bearer ${token}` }
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
return response.data;
|
|
188
|
-
} catch (error) {
|
|
189
|
-
if (error.response) {
|
|
190
|
-
return error.response.data;
|
|
191
|
-
}
|
|
192
|
-
throw new Error(`Failed to generate codes: ${error.message}`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Admin: Extend user
|
|
198
|
-
*/
|
|
199
|
-
async adminExtendUser(email, days, adminEmail) {
|
|
200
|
-
try {
|
|
201
|
-
const token = this.loadAdminToken();
|
|
202
|
-
if (!token) {
|
|
203
|
-
throw new Error('Admin token not found. Run: reepoe admin login');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const response = await axios.post(`${this.apiUrl}/api/admin/users/extend`, {
|
|
207
|
-
email,
|
|
208
|
-
days,
|
|
209
|
-
admin_email: adminEmail
|
|
210
|
-
}, {
|
|
211
|
-
headers: { 'Authorization': `Bearer ${token}` }
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
return response.data;
|
|
215
|
-
} catch (error) {
|
|
216
|
-
if (error.response) {
|
|
217
|
-
return error.response.data;
|
|
218
|
-
}
|
|
219
|
-
throw new Error(`Failed to extend user: ${error.message}`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// ========== Local Storage ==========
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Save activation data locally
|
|
227
|
-
*/
|
|
228
|
-
saveActivation(activationData) {
|
|
229
|
-
const configDir = path.join(os.homedir(), '.reepoe');
|
|
230
|
-
if (!fs.existsSync(configDir)) {
|
|
231
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Detect cloud environment
|
|
235
|
-
const isCloud = !!(
|
|
236
|
-
process.env.REPL_ID ||
|
|
237
|
-
process.env.CODESPACE_NAME ||
|
|
238
|
-
process.env.GITPOD_WORKSPACE_ID
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
const data = {
|
|
242
|
-
email: activationData.user?.email || activationData.email,
|
|
243
|
-
token: activationData.token,
|
|
244
|
-
activated_at: activationData.user?.activated_at || new Date().toISOString(),
|
|
245
|
-
expires_at: activationData.user?.expires_at,
|
|
246
|
-
status: activationData.user?.status || 'active',
|
|
247
|
-
api_base: isCloud ? 'https://reepoe-api.onrender.com' : 'http://localhost:8000'
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
fs.writeFileSync(this.activationFile, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Load activation data
|
|
255
|
-
*/
|
|
256
|
-
loadActivation() {
|
|
257
|
-
if (!fs.existsSync(this.activationFile)) {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
const data = JSON.parse(fs.readFileSync(this.activationFile, 'utf8'));
|
|
263
|
-
return data;
|
|
264
|
-
} catch (error) {
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Save admin token
|
|
271
|
-
*/
|
|
272
|
-
saveAdminToken(token) {
|
|
273
|
-
const configDir = path.join(os.homedir(), '.reepoe');
|
|
274
|
-
const adminFile = path.join(configDir, 'admin_session.json');
|
|
275
|
-
|
|
276
|
-
if (!fs.existsSync(configDir)) {
|
|
277
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const data = {
|
|
281
|
-
token,
|
|
282
|
-
created_at: new Date().toISOString(),
|
|
283
|
-
expires_at: new Date(Date.now() + 3600000).toISOString() // 1 hour
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
fs.writeFileSync(adminFile, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Load admin token
|
|
291
|
-
*/
|
|
292
|
-
loadAdminToken() {
|
|
293
|
-
const adminFile = path.join(os.homedir(), '.reepoe', 'admin_session.json');
|
|
294
|
-
|
|
295
|
-
if (!fs.existsSync(adminFile)) {
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
const data = JSON.parse(fs.readFileSync(adminFile, 'utf8'));
|
|
301
|
-
|
|
302
|
-
// Check if expired
|
|
303
|
-
const expiresAt = new Date(data.expires_at);
|
|
304
|
-
if (new Date() > expiresAt) {
|
|
305
|
-
fs.unlinkSync(adminFile); // Delete expired token
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return data.token;
|
|
310
|
-
} catch (error) {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Get machine ID (for device tracking)
|
|
317
|
-
*/
|
|
318
|
-
getMachineId() {
|
|
319
|
-
const crypto = require('crypto');
|
|
320
|
-
const machineInfo = `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
321
|
-
return crypto.createHash('sha256').update(machineInfo).digest('hex').substring(0, 16);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
module.exports = ActivationClient;
|
|
326
|
-
|