@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 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 (NOT aggregate)
28
- const API_URL = process.env.REEPOE_API_URL || 'https://reepoe-api.onrender.com';
29
- const res = await axios.get(`${API_URL}/api/metrics/user/${activation.email}`);
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
- // Get binary path based on platform
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
- // Main CLI handler
41
- function main() {
42
- const args = process.argv.slice(2);
43
-
44
- // Handle special commands
45
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
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 showHelp() {
68
- console.log(`
69
- ╔══════════════════════════════════════════════════════════════╗
70
- ā•‘ ReePoe CLI - Help ā•‘
71
- ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
72
-
73
- COMMANDS:
74
- reepoe-start Start ReePoe server
75
- reepoe-stop Stop ReePoe server
76
- reepoe-status Check ReePoe server status
77
- reepoe query "..." Send query to ReePoe
78
-
79
- EXAMPLES:
80
- reepoe query "what does this codebase do?"
81
- reepoe query "show me all API endpoints"
82
- reepoe query "find all test files"
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
- async function makeQuery(query) {
96
- const axios = require('axios');
97
- const configPath = path.join(process.cwd(), 'reepoe.config.json');
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
- main();
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 - Starts ReePoe server in background
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
- return path.join(__dirname, '../binaries', binaryName);
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 (!fs.existsSync(binaryPath)) {
197
- console.error(`āŒ ReePoe binary not found: ${binaryPath}`);
198
- console.error(' Try reinstalling: npm install -g @reepoe/plugin');
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.4",
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"