neoagent 1.2.0 → 1.3.0

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/.env.example CHANGED
@@ -1,9 +1,9 @@
1
- PORT=3060
1
+ PORT=3333
2
2
  NODE_ENV=development
3
3
  SESSION_SECRET=change-this-to-a-random-secret-in-production
4
4
 
5
5
  # Comma-separated list of allowed CORS origins (leave empty to block all cross-origin requests)
6
- # Example: ALLOWED_ORIGINS=http://localhost:3060,https://yourdomain.com
6
+ # Example: ALLOWED_ORIGINS=http://localhost:3333,https://yourdomain.com
7
7
  ALLOWED_ORIGINS=
8
8
 
9
9
  # xAI API key — used for:
@@ -8,7 +8,7 @@ All settings live in `.env` at the project root. Run `neoagent setup` to regener
8
8
 
9
9
  | Variable | Default | Description |
10
10
  |---|---|---|
11
- | `PORT` | `3060` | HTTP port |
11
+ | `PORT` | `3333` | HTTP port |
12
12
  | `SESSION_SECRET` | *(required)* | Random string for session signing — generate with `openssl rand -hex 32` |
13
13
  | `NODE_ENV` | `production` | Set to `development` to enable verbose logs |
14
14
  | `SECURE_COOKIES` | `false` | Set `true` when behind a TLS-terminating proxy |
@@ -39,7 +39,7 @@ Telegram, Discord, and WhatsApp tokens are stored in the database via the web UI
39
39
  ## Minimal `.env` example
40
40
 
41
41
  ```dotenv
42
- PORT=3060
42
+ PORT=3333
43
43
  SESSION_SECRET=change-me-to-something-random
44
44
  ANTHROPIC_API_KEY=sk-ant-...
45
45
  ```
package/lib/manager.js CHANGED
@@ -58,15 +58,67 @@ function loadEnvPort() {
58
58
  try {
59
59
  const env = fs.readFileSync(ENV_FILE, 'utf8');
60
60
  const line = env.split('\n').find((entry) => entry.startsWith('PORT='));
61
- if (!line) return 3060;
61
+ if (!line) return 3333;
62
62
  const raw = line.split('=')[1]?.trim();
63
63
  const num = Number(raw);
64
- return Number.isFinite(num) && num > 0 ? num : 3060;
64
+ return Number.isFinite(num) && num > 0 ? num : 3333;
65
65
  } catch {
66
- return 3060;
66
+ return 3333;
67
67
  }
68
68
  }
69
69
 
70
+ function readEnvFileRaw() {
71
+ if (!fs.existsSync(ENV_FILE)) return '';
72
+ return fs.readFileSync(ENV_FILE, 'utf8');
73
+ }
74
+
75
+ function parseEnv(raw) {
76
+ const lines = raw.split('\n');
77
+ const map = new Map();
78
+ for (const line of lines) {
79
+ if (!line || line.startsWith('#') || !line.includes('=')) continue;
80
+ const idx = line.indexOf('=');
81
+ const key = line.slice(0, idx).trim();
82
+ const value = line.slice(idx + 1);
83
+ if (key) map.set(key, value);
84
+ }
85
+ return map;
86
+ }
87
+
88
+ function upsertEnvValue(key, value) {
89
+ const raw = readEnvFileRaw();
90
+ const lines = raw ? raw.split('\n') : [];
91
+ let replaced = false;
92
+
93
+ for (let i = 0; i < lines.length; i++) {
94
+ if (lines[i].startsWith(`${key}=`)) {
95
+ lines[i] = `${key}=${value}`;
96
+ replaced = true;
97
+ break;
98
+ }
99
+ }
100
+
101
+ if (!replaced) lines.push(`${key}=${value}`);
102
+ const output = lines.filter((_, idx, arr) => idx !== arr.length - 1 || arr[idx] !== '').join('\n') + '\n';
103
+ fs.writeFileSync(ENV_FILE, output, { mode: 0o600 });
104
+ }
105
+
106
+ function removeEnvValue(key) {
107
+ const raw = readEnvFileRaw();
108
+ if (!raw) return false;
109
+ const lines = raw.split('\n').filter((line) => !line.startsWith(`${key}=`));
110
+ const output = lines.filter((_, idx, arr) => idx !== arr.length - 1 || arr[idx] !== '').join('\n') + '\n';
111
+ fs.writeFileSync(ENV_FILE, output, { mode: 0o600 });
112
+ return true;
113
+ }
114
+
115
+ function maskEnvValue(key, value) {
116
+ if (!/(KEY|TOKEN|SECRET|PASSWORD)/i.test(key)) return value;
117
+ const text = String(value || '');
118
+ if (text.length <= 8) return '********';
119
+ return `${text.slice(0, 4)}...${text.slice(-4)}`;
120
+ }
121
+
70
122
  function runOrThrow(cmd, args, options = {}) {
71
123
  const res = spawnSync(cmd, args, { stdio: 'inherit', cwd: APP_DIR, ...options });
72
124
  if (res.status !== 0) {
@@ -159,7 +211,7 @@ async function cmdSetup() {
159
211
  }
160
212
  }
161
213
 
162
- const port = await ask('Server port', current.PORT || '3060');
214
+ const port = await ask('Server port', current.PORT || '3333');
163
215
  const sessionSecret = await ask('Session secret', current.SESSION_SECRET || randomSecret());
164
216
  const anthropic = await ask('Anthropic API key', current.ANTHROPIC_API_KEY || '');
165
217
  const openai = await ask('OpenAI API key', current.OPENAI_API_KEY || '');
@@ -419,10 +471,61 @@ function cmdUpdate() {
419
471
  cmdRestart();
420
472
  }
421
473
 
474
+ async function cmdEnv(args = []) {
475
+ heading('Environment Variables');
476
+ let action = args[0];
477
+
478
+ if (!action) {
479
+ const picked = await ask('Action (list/get/set/unset)', 'list');
480
+ action = (picked || 'list').trim().toLowerCase();
481
+ }
482
+
483
+ if (action === 'list') {
484
+ const env = parseEnv(readEnvFileRaw());
485
+ if (env.size === 0) {
486
+ logWarn(`No .env found at ${ENV_FILE}`);
487
+ return;
488
+ }
489
+ for (const [k, v] of [...env.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
490
+ console.log(`${k}=${maskEnvValue(k, v)}`);
491
+ }
492
+ return;
493
+ }
494
+
495
+ if (action === 'get') {
496
+ const key = args[1] || await ask('Key', 'PORT');
497
+ if (!key) throw new Error('Usage: neoagent env get <KEY>');
498
+ const env = parseEnv(readEnvFileRaw());
499
+ if (!env.has(key)) throw new Error(`Key not found: ${key}`);
500
+ console.log(env.get(key));
501
+ return;
502
+ }
503
+
504
+ if (action === 'set') {
505
+ const key = args[1] || await ask('Key', 'PORT');
506
+ const value = args.slice(2).join(' ') || await ask('Value', key === 'PORT' ? '3333' : '');
507
+ if (!key || !value) throw new Error('Usage: neoagent env set <KEY> <VALUE>');
508
+ upsertEnvValue(key, value);
509
+ logOk(`Set ${key} in ${ENV_FILE}`);
510
+ return;
511
+ }
512
+
513
+ if (action === 'unset') {
514
+ const key = args[1] || await ask('Key', 'PORT');
515
+ if (!key) throw new Error('Usage: neoagent env unset <KEY>');
516
+ removeEnvValue(key);
517
+ logOk(`Removed ${key} from ${ENV_FILE}`);
518
+ return;
519
+ }
520
+
521
+ throw new Error('Usage: neoagent env [list|get|set|unset] ...');
522
+ }
523
+
422
524
  function printHelp() {
423
525
  console.log(`${APP_NAME} manager`);
424
526
  console.log('Usage: neoagent <command>');
425
- console.log('Commands: install | setup | update | restart | start | stop | status | logs | uninstall');
527
+ console.log('Commands: install | setup | env | update | restart | start | stop | status | logs | uninstall');
528
+ console.log('Env usage: neoagent env list | neoagent env get PORT | neoagent env set PORT 3333 | neoagent env unset PORT');
426
529
  }
427
530
 
428
531
  async function runCLI(argv) {
@@ -435,6 +538,9 @@ async function runCLI(argv) {
435
538
  case 'setup':
436
539
  await cmdSetup();
437
540
  break;
541
+ case 'env':
542
+ await cmdEnv(argv.slice(1));
543
+ break;
438
544
  case 'update':
439
545
  cmdUpdate();
440
546
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
package/server/index.js CHANGED
@@ -34,7 +34,7 @@ if (!process.env.SESSION_SECRET) {
34
34
  console.warn('WARNING: SESSION_SECRET not set — using insecure default. Set it in .env before exposing this server.');
35
35
  }
36
36
 
37
- const PORT = process.env.PORT || 3060;
37
+ const PORT = process.env.PORT || 3333;
38
38
  const DATA_DIR = path.join(__dirname, '../data');
39
39
  if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
40
40