@reepoe/plugin 1.1.4 ā 1.1.8
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/metrics.js +3 -3
- package/bin/reepoe.js +31 -132
- package/bin/start.js +28 -5
- package/lib/activation-client.js +326 -0
- package/package.json +2 -1
package/bin/metrics.js
CHANGED
|
@@ -24,9 +24,9 @@ async function showMetrics() {
|
|
|
24
24
|
console.log('\nš Loading your ReePoe metrics...\n');
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
// Use per-user metrics endpoint
|
|
28
|
-
const
|
|
29
|
-
const res = await axios.get(`${
|
|
27
|
+
// Use per-user metrics endpoint with api_base from activation
|
|
28
|
+
const API_BASE = activation.api_base || 'https://reepoe-api.onrender.com';
|
|
29
|
+
const res = await axios.get(`${API_BASE}/api/metrics/user/${activation.email}`);
|
|
30
30
|
const data = res.data;
|
|
31
31
|
|
|
32
32
|
// Per-user endpoint returns the correct structure already
|
package/bin/reepoe.js
CHANGED
|
@@ -1,143 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* ReePoe CLI - Main command wrapper
|
|
4
|
-
* Manages ReePoe binary execution and queries
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { spawn } = require('child_process');
|
|
8
|
-
const path = require('path');
|
|
2
|
+
const axios = require('axios');
|
|
9
3
|
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
10
5
|
const os = require('os');
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
function getBinaryPath() {
|
|
14
|
-
const platform = os.platform();
|
|
15
|
-
const arch = os.arch();
|
|
16
|
-
|
|
17
|
-
let binaryName;
|
|
18
|
-
if (platform === 'darwin') {
|
|
19
|
-
binaryName = arch === 'arm64' ? 'reepoe-macos-arm64' : 'reepoe-macos-x64';
|
|
20
|
-
} else if (platform === 'linux') {
|
|
21
|
-
binaryName = 'reepoe-linux-x64';
|
|
22
|
-
} else if (platform === 'win32') {
|
|
23
|
-
binaryName = 'reepoe-windows.exe';
|
|
24
|
-
} else {
|
|
25
|
-
console.error(`ā Unsupported platform: ${platform}`);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const binaryPath = path.join(__dirname, '../binaries', binaryName);
|
|
30
|
-
|
|
31
|
-
if (!fs.existsSync(binaryPath)) {
|
|
32
|
-
console.error(`ā Binary not found: ${binaryPath}`);
|
|
33
|
-
console.error(` Platform: ${platform}-${arch}`);
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return binaryPath;
|
|
38
|
-
}
|
|
7
|
+
const ACTIVATION_FILE = path.join(os.homedir(), '.reepoe', 'activation.json');
|
|
39
8
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
showHelp();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (args[0] === 'version' || args[0] === '--version') {
|
|
51
|
-
console.log('ReePoe Plugin v1.0.0');
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (args[0] === 'query') {
|
|
56
|
-
// Direct query command
|
|
57
|
-
const query = args.slice(1).join(' ');
|
|
58
|
-
makeQuery(query);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// For other commands, pass through to binary
|
|
63
|
-
console.log('ā¹ļø Use specific commands: reepoe-start, reepoe-stop, reepoe-status');
|
|
64
|
-
console.log(' Or: reepoe query "your question here"');
|
|
9
|
+
function loadActivation() {
|
|
10
|
+
if (!fs.existsSync(ACTIVATION_FILE)) {
|
|
11
|
+
console.error('ā Not activated. Run: reepoe-start');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
return JSON.parse(fs.readFileSync(ACTIVATION_FILE, 'utf8'));
|
|
65
15
|
}
|
|
66
16
|
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
MANAGEMENT:
|
|
85
|
-
reepoe-start Start server in background
|
|
86
|
-
reepoe-stop Stop server
|
|
87
|
-
reepoe-status Check if server is running
|
|
88
|
-
|
|
89
|
-
MORE INFO:
|
|
90
|
-
Documentation: https://reepoe.com/docs
|
|
91
|
-
API: http://localhost:<port>/describe
|
|
92
|
-
`);
|
|
17
|
+
async function query(instruction) {
|
|
18
|
+
const activation = loadActivation();
|
|
19
|
+
const API_BASE = activation.api_base || 'http://localhost:8000';
|
|
20
|
+
|
|
21
|
+
console.log(`š¤ ReePoe processing: "${instruction}"`);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const response = await axios.post(`${API_BASE}/query`, {
|
|
25
|
+
instruction,
|
|
26
|
+
user_email: activation.email
|
|
27
|
+
}, { timeout: 60000 });
|
|
28
|
+
|
|
29
|
+
console.log(response.data.response || JSON.stringify(response.data, null, 2));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('ā Query failed:', error.response?.data?.detail || error.message);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
93
34
|
}
|
|
94
35
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Read config to get port
|
|
100
|
-
let port = 8000;
|
|
101
|
-
if (fs.existsSync(configPath)) {
|
|
102
|
-
try {
|
|
103
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
104
|
-
port = config.api?.port || 8000;
|
|
105
|
-
} catch (e) {
|
|
106
|
-
// Use default port
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Load activation to get user email for per-user metrics
|
|
111
|
-
let userEmail = null;
|
|
112
|
-
const activationPath = path.join(os.homedir(), '.reepoe', 'activation.json');
|
|
113
|
-
if (fs.existsSync(activationPath)) {
|
|
114
|
-
try {
|
|
115
|
-
const activation = JSON.parse(fs.readFileSync(activationPath, 'utf8'));
|
|
116
|
-
userEmail = activation.email;
|
|
117
|
-
} catch (e) {
|
|
118
|
-
// No activation or invalid - metrics will be aggregate only
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const url = `http://localhost:${port}/query`;
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
console.log(`\nš¤ ReePoe processing: "${query}"\n`);
|
|
126
|
-
const requestBody = { instruction: query };
|
|
127
|
-
if (userEmail) {
|
|
128
|
-
requestBody.user_email = userEmail;
|
|
129
|
-
}
|
|
130
|
-
const response = await axios.post(url, requestBody);
|
|
131
|
-
console.log(response.data.response || response.data);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
if (error.code === 'ECONNREFUSED') {
|
|
134
|
-
console.error('ā ReePoe server not running. Start it with: reepoe-start');
|
|
135
|
-
} else {
|
|
136
|
-
console.error(`ā Query failed: ${error.message}`);
|
|
137
|
-
}
|
|
138
|
-
process.exit(1);
|
|
139
|
-
}
|
|
36
|
+
const args = process.argv.slice(2);
|
|
37
|
+
if (args.length === 0) {
|
|
38
|
+
console.log('Usage: reepoe query "your question"');
|
|
39
|
+
process.exit(1);
|
|
140
40
|
}
|
|
141
41
|
|
|
142
|
-
|
|
143
|
-
|
|
42
|
+
query(args.join(' '));
|
package/bin/start.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* ReePoe Start -
|
|
3
|
+
* ReePoe Start - Hybrid local/cloud startup
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { spawn } = require('child_process');
|
|
@@ -9,6 +9,15 @@ const fs = require('fs');
|
|
|
9
9
|
const os = require('os');
|
|
10
10
|
const axios = require('axios');
|
|
11
11
|
|
|
12
|
+
// Detect cloud environment
|
|
13
|
+
function isCloudEnvironment() {
|
|
14
|
+
return !!(
|
|
15
|
+
process.env.REPL_ID || // Replit
|
|
16
|
+
process.env.CODESPACE_NAME || // GitHub Codespaces
|
|
17
|
+
process.env.GITPOD_WORKSPACE_ID // Gitpod
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
// Get binary path
|
|
13
22
|
function getBinaryPath() {
|
|
14
23
|
const platform = os.platform();
|
|
@@ -23,7 +32,8 @@ function getBinaryPath() {
|
|
|
23
32
|
binaryName = 'reepoe-windows.exe';
|
|
24
33
|
}
|
|
25
34
|
|
|
26
|
-
|
|
35
|
+
const binaryPath = path.join(__dirname, '../binaries', binaryName);
|
|
36
|
+
return fs.existsSync(binaryPath) ? binaryPath : null;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
// Check if server is already running
|
|
@@ -181,6 +191,19 @@ async function main() {
|
|
|
181
191
|
console.log('ā¹ļø No configuration found, using defaults');
|
|
182
192
|
}
|
|
183
193
|
|
|
194
|
+
// Detect cloud environment
|
|
195
|
+
const isCloud = isCloudEnvironment();
|
|
196
|
+
|
|
197
|
+
if (isCloud) {
|
|
198
|
+
// Cloud environment - skip local server
|
|
199
|
+
console.log('\nāļø Cloud environment detected');
|
|
200
|
+
console.log('š” Using production ReePoe API: https://reepoe-api.onrender.com\n');
|
|
201
|
+
console.log('ā
Ready to use!');
|
|
202
|
+
console.log('\nš” Try:');
|
|
203
|
+
console.log(' reepoe query "test query"\n');
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
184
207
|
// Check if already running
|
|
185
208
|
const running = await isServerRunning(port);
|
|
186
209
|
if (running) {
|
|
@@ -193,9 +216,9 @@ async function main() {
|
|
|
193
216
|
// Get binary path
|
|
194
217
|
const binaryPath = getBinaryPath();
|
|
195
218
|
|
|
196
|
-
if (!
|
|
197
|
-
console.error(`ā ReePoe binary not found
|
|
198
|
-
console.error(' Try reinstalling: npm install -g
|
|
219
|
+
if (!binaryPath) {
|
|
220
|
+
console.error(`ā ReePoe binary not found for ${os.platform()}-${os.arch()}`);
|
|
221
|
+
console.error(' Try reinstalling: npm install -g reepoe');
|
|
199
222
|
process.exit(1);
|
|
200
223
|
}
|
|
201
224
|
|
|
@@ -0,0 +1,326 @@
|
|
|
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
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reepoe/plugin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "ReePoe AI Code Manager - Install in any codebase for instant AI agent integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
53
|
"bin",
|
|
54
|
+
"lib",
|
|
54
55
|
"scripts",
|
|
55
56
|
"binaries",
|
|
56
57
|
"README.md"
|