@lucenaone/coder 1.1.16 → 1.1.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucenaone/coder",
3
- "version": "1.1.16",
3
+ "version": "1.1.17",
4
4
  "description": "Private tunnel for connecting LucenaCoder.com to your local folder. Always remains folder scoped while providing full terminal access.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agent.js CHANGED
@@ -8,6 +8,7 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs';
8
8
  import { FIREBASE_CONFIG } from './config.js';
9
9
  import { buildIndex, reindexFile } from './cli-indexer.js';
10
10
  import { LucenaShell } from './lucena-shell.js';
11
+ import { storeProToken } from './pro-token.js';
11
12
 
12
13
  const IGNORED_PATTERNS = [
13
14
  'node_modules', '.git', '.next', '.wrangler', '.DS_Store',
@@ -145,6 +146,11 @@ export class LucenaAgent {
145
146
  if (typeof data.stripCwd === 'boolean') {
146
147
  this.stripCwd = data.stripCwd;
147
148
  }
149
+
150
+ if (data.proToken) {
151
+ await storeProToken({ tokenForPro: data.proToken, email: data.proEmail });
152
+ console.log(` ${'\x1b[36m'}PRO token stored. Future runs can auto-launch.${'\x1b[0m'}`);
153
+ }
148
154
 
149
155
  // Index is guaranteed ready (awaited in start()), push immediately
150
156
  if (this.indexData) {
@@ -218,11 +224,18 @@ export class LucenaAgent {
218
224
  case 'delete_file': return this.deleteFile(command);
219
225
  case 'mkdir': return this.mkdirCmd(command);
220
226
  case 'search': return this.searchCodebase(command);
227
+ case 'store_pro_token': return this.storeProTokenCmd(command);
221
228
  case 'ping': return this.pushResponse(messageId, 'pong', '');
222
229
  default: return this.pushResponse(messageId, 'error', `Unknown command type: ${type}`);
223
230
  }
224
231
  }
225
232
 
233
+ async storeProTokenCmd({ messageId, tokenForPro, email }) {
234
+ if (!tokenForPro) return this.pushResponse(messageId, 'error', 'tokenForPro is required');
235
+ await storeProToken({ tokenForPro, email });
236
+ this.pushResponse(messageId, 'done', 'PRO token stored');
237
+ }
238
+
226
239
  pushResponse(messageId, type, text, extra = {}) {
227
240
  const responsesRef = ref(this.db, `tunnels/${this.tunnelId}/responses`);
228
241
  push(responsesRef, {
package/src/main.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/main.js — CLI entry point for the Lucena agent
2
2
  import { LucenaAgent } from './agent.js';
3
- import { basename } from 'path';
3
+ import { spawn } from 'child_process';
4
+ import { validateStoredProToken } from './pro-token.js';
4
5
 
5
6
 
6
7
  // Standard ANSI Terminal Colors
@@ -35,13 +36,28 @@ ${c.dim} =========================================${c.reset}
35
36
  to your local folders.
36
37
  `;
37
38
 
39
+ function openBrowser(url) {
40
+ const command = process.platform === 'darwin'
41
+ ? 'open'
42
+ : process.platform === 'win32'
43
+ ? 'cmd'
44
+ : 'xdg-open';
45
+ const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
46
+ const child = spawn(command, args, { detached: true, stdio: 'ignore' });
47
+ child.unref();
48
+ }
49
+
38
50
  export async function main() {
39
51
  const cwd = process.cwd();
52
+ const proStatus = await validateStoredProToken();
40
53
 
41
54
  console.log(BANNER);
42
55
  console.log(` ${c.cyan}📍 Scoped to:${c.reset} ${cwd}`);
43
56
  console.log(` ${c.yellow}🛡️ Safe Mode:${c.reset} ON by default (All edits require approval)`);
44
57
  console.log(` ${c.dim}Optionally switch to YOLO on LucenaCoder.com${c.reset}\n`);
58
+ if (proStatus.valid) {
59
+ console.log(` ${c.cyan}PRO detected.${c.reset} Browser auto-launch enabled.\n`);
60
+ }
45
61
 
46
62
  const agent = new LucenaAgent(cwd);
47
63
 
@@ -72,11 +88,22 @@ export async function main() {
72
88
  const urlBoxWidth = urlLabel.length + webUrl.length + 5;
73
89
  const urlBorder = '─'.repeat(urlBoxWidth);
74
90
 
91
+ if (proStatus.valid) {
92
+ try {
93
+ openBrowser(webUrl);
94
+ console.log(`\n ${c.green}✔ Opening LucenaCoder...${c.reset}`);
95
+ } catch {
96
+ console.log(`\n ${c.yellow}Could not auto-open your browser.${c.reset}`);
97
+ }
98
+ }
99
+
75
100
  console.log(`\n ${c.dim}┌${urlBorder}┐${c.reset}`);
76
101
  console.log(` ${c.dim}│${c.reset} ${urlLabel}${c.reset} ${c.bold}${webUrl}${c.reset} ${c.dim}│${c.reset}`);
77
102
  console.log(` ${c.dim}└${urlBorder}┘${c.reset}`);
78
-
79
- console.log(`\n ${c.dim}Open the URL above in your browser to connect.${c.reset}`);
103
+
104
+ if (!proStatus.valid) {
105
+ console.log(`\n ${c.dim}Open the URL above in your browser to connect.${c.reset}`);
106
+ }
80
107
  console.log(` ${c.dim}Press Ctrl+C to disconnect${c.reset}\n`);
81
108
  } catch (err) {
82
109
  console.error(`\n ${c.yellow}✖ Failed to start tunnel: ${err.message}${c.reset}\n`);
@@ -0,0 +1,47 @@
1
+ import { mkdir, readFile, writeFile } from 'fs/promises';
2
+ import { dirname, join } from 'path';
3
+ import { homedir } from 'os';
4
+
5
+ const TOKEN_PATH = join(homedir(), '.lucenacoder', 'pro.json');
6
+ const VALIDATE_URL = process.env.LUCENA_VALIDATE_PRO_URL || 'https://lucenacoder.com/api/pro/validate-token';
7
+
8
+ export async function readStoredProToken() {
9
+ try {
10
+ const raw = await readFile(TOKEN_PATH, 'utf-8');
11
+ const data = JSON.parse(raw);
12
+ if (!data?.tokenForPro) return null;
13
+ return data;
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+
19
+ export async function storeProToken({ tokenForPro, email }) {
20
+ if (!tokenForPro) return null;
21
+ const data = {
22
+ tokenForPro,
23
+ email: email || '',
24
+ savedAt: new Date().toISOString(),
25
+ };
26
+ await mkdir(dirname(TOKEN_PATH), { recursive: true });
27
+ await writeFile(TOKEN_PATH, JSON.stringify(data, null, 2), 'utf-8');
28
+ return data;
29
+ }
30
+
31
+ export async function validateStoredProToken() {
32
+ const stored = await readStoredProToken();
33
+ if (!stored?.tokenForPro) return { valid: false };
34
+
35
+ try {
36
+ const response = await fetch(VALIDATE_URL, {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify({ tokenForPro: stored.tokenForPro }),
40
+ });
41
+ const payload = await response.json().catch(() => ({}));
42
+ if (!response.ok || !payload.valid) return { valid: false, stored };
43
+ return { valid: true, stored, ...payload };
44
+ } catch {
45
+ return { valid: false, stored };
46
+ }
47
+ }