uneven-ai 1.1.8 → 1.1.9

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to Uneven AI will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.9] - 2026-04-27
9
+
10
+ ### Added
11
+ - **Attacker Identification (Uneven Trace)**: Active pentester now injects a silent origin-tracking header (`X-Uneven-Origin-Trace`) containing the attacker's machine IP.
12
+ - **Threat Detection Engine**: Terminal watcher now detects incoming Uneven pentest probes and logs a critical security alert with the source IP.
13
+ - **Portuguese Intent Support**: Shell now recognizes "crie", "faça", "escreva" for AI tasks even in keyword-fallback mode.
14
+
15
+ ### Changed
16
+ - **License Gating**: Active Pentester mode is now restricted to the **Team** plan.
17
+ - **Shell Stability**: Improved intent classification to prevent substring collision (e.g., "mundo" no longer triggers "undo").
18
+ - **Success Hints**: Shell now only prints success hints if the underlying command actually returns a success exit code.
19
+
8
20
  ## [1.1.8] - 2026-04-27
9
21
 
10
22
  ### Added
package/README.md CHANGED
@@ -158,7 +158,7 @@ uneven-ai ci
158
158
  Run `uneven-ai` with no arguments to open the interactive shell:
159
159
 
160
160
  ```
161
- ◈ Uneven AI v1.1.8
161
+ ◈ Uneven AI v1.1.9
162
162
  ────────────────────────────────────────────────────────────
163
163
  Olá! O que posso fazer por você hoje?
164
164
  (Escreva sua mensagem ou "sair" para encerrar)
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/application/analysis/active/helpers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAG5B,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1F;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC;IACpF,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IACtD,IAAI,EAAE,MAAM,CAAA;CACb,CAAC,CAmBD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChE,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,IAAI,CAAC,CA8BR;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAY9F"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/application/analysis/active/helpers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAI5B,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1F;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1F,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IACtD,IAAI,EAAE,MAAM,CAAA;CACb,CAAC,CA0BD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChE,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,IAAI,CAAC,CA8BR;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAY9F"}
@@ -3,6 +3,7 @@ import * as tls from 'tls';
3
3
  import * as https from 'https';
4
4
  import * as http from 'http';
5
5
  import { CONNECT_TIMEOUT_MS, HTTP_TIMEOUT_MS } from './context.js';
6
+ import { getPublicIp } from '../../../infrastructure/utils/network.js';
6
7
  export function tcpConnect(host, port, timeoutMs) {
7
8
  return new Promise(resolve => {
8
9
  const sock = net.createConnection({ host, port });
@@ -11,10 +12,16 @@ export function tcpConnect(host, port, timeoutMs) {
11
12
  sock.on('error', () => { clearTimeout(timer); resolve(false); });
12
13
  });
13
14
  }
14
- export function httpRequest(options, useHttps) {
15
+ export async function httpRequest(options, useHttps) {
16
+ const ip = await getPublicIp();
17
+ const customHeaders = {
18
+ ...options.headers,
19
+ 'User-Agent': 'Uneven-Pentester/1.1.9',
20
+ ...(ip ? { 'X-Uneven-Origin-Trace': ip } : {})
21
+ };
15
22
  return new Promise((resolve, reject) => {
16
23
  const mod = useHttps ? https : http;
17
- const req = mod.request({ ...options, rejectUnauthorized: false }, res => {
24
+ const req = mod.request({ ...options, headers: customHeaders, rejectUnauthorized: false }, res => {
18
25
  let body = '';
19
26
  res.on('data', (chunk) => { body += chunk.toString('utf-8'); });
20
27
  res.on('end', () => resolve({
@@ -14,7 +14,7 @@ export async function pentestCommand(mode, options = {}) {
14
14
  const resolvedMode = mode || 'static';
15
15
  if (resolvedMode === 'active') {
16
16
  const { license } = await import('../../infrastructure/index.js');
17
- await license.requirePro();
17
+ await license.requireTeam();
18
18
  }
19
19
  const ctx = new PentestSecurityContext();
20
20
  // ── Handle --declare-scope flag ─────────────────────────────────────────────
@@ -1 +1 @@
1
- {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAsRH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA8DlD"}
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAgRH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAgElD"}
@@ -49,8 +49,8 @@ async function spawnSubcommand(args) {
49
49
  stdio: 'inherit',
50
50
  env: process.env,
51
51
  });
52
- child.on('close', resolve);
53
- child.on('error', resolve);
52
+ child.on('close', (code) => resolve(code ?? 0));
53
+ child.on('error', () => resolve(1));
54
54
  });
55
55
  }
56
56
  /**
@@ -66,8 +66,8 @@ async function spawnWatch() {
66
66
  });
67
67
  const onSigint = () => child.kill('SIGTERM');
68
68
  process.on('SIGINT', onSigint);
69
- child.on('close', () => { process.off('SIGINT', onSigint); resolve(); });
70
- child.on('error', () => { process.off('SIGINT', onSigint); resolve(); });
69
+ child.on('close', (code) => { process.off('SIGINT', onSigint); resolve(code ?? 0); });
70
+ child.on('error', () => { process.off('SIGINT', onSigint); resolve(1); });
71
71
  });
72
72
  }
73
73
  // ─── Background task guard ────────────────────────────────────────────────────
@@ -121,24 +121,20 @@ async function execute(intent) {
121
121
  switch (intent.action) {
122
122
  case 'index':
123
123
  console.log(D('\n ↳ Indexing your project…\n'));
124
- await spawnSubcommand(['index']);
125
- break;
124
+ return await spawnSubcommand(['index']);
126
125
  case 'scan':
127
126
  console.log(D('\n ↳ Scanning for vulnerabilities and malicious patterns…\n'));
128
- await spawnSubcommand(['scan']);
129
- break;
127
+ return await spawnSubcommand(['scan']);
130
128
  case 'watch':
131
- await spawnWatch();
132
- break;
129
+ return await spawnWatch();
133
130
  case 'explain': {
134
131
  const file = intent.args.file;
135
132
  if (!file) {
136
133
  console.log(Y('\n Which file should I explain? e.g. "explain src/app.ts"\n'));
137
- return;
134
+ return 1;
138
135
  }
139
136
  console.log(D(`\n ↳ Explaining ${file}…\n`));
140
- await spawnSubcommand(['explain', file]);
141
- break;
137
+ return await spawnSubcommand(['explain', file]);
142
138
  }
143
139
  case 'review': {
144
140
  const args = ['review'];
@@ -149,50 +145,47 @@ async function execute(intent) {
149
145
  if (intent.args.staged)
150
146
  args.push('--staged');
151
147
  console.log(D('\n ↳ Running code review…\n'));
152
- await spawnSubcommand(args);
153
- break;
148
+ return await spawnSubcommand(args);
154
149
  }
155
150
  case 'docs': {
156
151
  const file = intent.args.file;
157
152
  if (!file) {
158
153
  console.log(Y('\n Which file should I document? e.g. "docs src/parser.ts"\n'));
159
- return;
154
+ return 1;
160
155
  }
161
156
  console.log(D(`\n ↳ Generating documentation for ${file}…\n`));
162
- await spawnSubcommand(['docs', file]);
163
- break;
157
+ return await spawnSubcommand(['docs', file]);
164
158
  }
165
159
  case 'askf': {
166
160
  const task = intent.args.task;
167
161
  console.log(D('\n ↳ Task started in background — keep using the shell.\n'));
168
162
  spawnBackground(['askf', task], task);
169
- break;
163
+ return 0;
170
164
  }
171
165
  case 'ask': {
172
166
  const question = intent.args.question;
173
167
  console.log(D('\n ↳ Looking up context…\n'));
174
- await spawnSubcommand(['ask', question]);
175
- break;
168
+ return await spawnSubcommand(['ask', question]);
176
169
  }
177
170
  case 'log':
178
171
  console.log(D('\n ↳ Loading activity log…\n'));
179
- await spawnSubcommand(['log']);
180
- break;
172
+ return await spawnSubcommand(['log']);
181
173
  case 'undo':
182
174
  console.log(D('\n ↳ Reverting last fix…\n'));
183
- await spawnSubcommand(['undo']);
184
- break;
175
+ return await spawnSubcommand(['undo']);
185
176
  case 'help':
186
177
  printShellHelp();
187
- break;
178
+ return 0;
188
179
  case 'exit':
189
- break; // handled in the loop
180
+ return 0;
190
181
  case 'unknown':
191
182
  console.log(Y('\n Not sure what you meant. ') +
192
183
  D('I can help with:\n') +
193
184
  D(' index · scan · watch · explain <file> · review · docs <file>\n') +
194
185
  D(' questions about your project · log · undo · help · exit\n'));
195
- break;
186
+ return 1;
187
+ default:
188
+ return 1;
196
189
  }
197
190
  }
198
191
  // ─── Shell help ───────────────────────────────────────────────────────────────
@@ -285,12 +278,14 @@ export async function shellCommand() {
285
278
  console.log('');
286
279
  console.log(A(' ◈ ') + chalk.hex('#e8edf5')(message));
287
280
  }
288
- await execute(intent);
289
- const hint = HINTS[intent.action];
290
- if (hint) {
291
- console.log('');
292
- console.log(G('') + D(hint));
293
- console.log('');
281
+ const code = await execute(intent);
282
+ if (code === 0) {
283
+ const hint = HINTS[intent.action];
284
+ if (hint) {
285
+ console.log('');
286
+ console.log(G('') + D(hint));
287
+ console.log('');
288
+ }
294
289
  }
295
290
  }
296
291
  }
@@ -10,7 +10,7 @@
10
10
  */
11
11
  // ─── Helpers ──────────────────────────────────────────────────────────────────
12
12
  function hasAny(low, words) {
13
- return words.some(w => low.includes(w));
13
+ return words.some(w => new RegExp(`\\b${w}\\b`).test(low));
14
14
  }
15
15
  // ─── File-path extraction ─────────────────────────────────────────────────────
16
16
  export function extractFilePath(input) {
@@ -53,7 +53,7 @@ export function classifyIntent(raw) {
53
53
  confidence: 'high',
54
54
  };
55
55
  }
56
- if (hasAny(low, ['askf']))
56
+ if (hasAny(low, ['askf', 'crie', 'cria', 'criação', 'escreva', 'faça', 'make', 'create']))
57
57
  return { action: 'askf', args: { task: input }, confidence: 'high' };
58
58
  const file = extractFilePath(input);
59
59
  if (file || hasAny(low, ['explain', 'explica']))
@@ -31,6 +31,10 @@ export declare class Logger {
31
31
  * Log error message
32
32
  */
33
33
  error(message: string): Promise<void>;
34
+ /**
35
+ * Log critical security alert
36
+ */
37
+ critical(message: string): Promise<void>;
34
38
  /**
35
39
  * Log error detection
36
40
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/io/logger/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAQ;IACvB;;;;OAIG;IACH,OAAO,CAAC,UAAU,CAAmC;IACrD,qDAAqD;IACrD,OAAO,CAAC,KAAK,CAAe;gBAEhB,OAAO,GAAE,MAA2B;YAKlC,UAAU;IAexB;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3C;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3C;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/F;;OAEG;IACG,UAAU,CACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAahB;;OAEG;IACG,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC;IAqBhB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;OAGG;IACH,OAAO,CAAC,KAAK;CAWd"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/io/logger/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAQ;IACvB;;;;OAIG;IACH,OAAO,CAAC,UAAU,CAAmC;IACrD,qDAAqD;IACrD,OAAO,CAAC,KAAK,CAAe;gBAEhB,OAAO,GAAE,MAA2B;YAKlC,UAAU;IAexB;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3C;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3C;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9C;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/F;;OAEG;IACG,UAAU,CACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAahB;;OAEG;IACG,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC;IAqBhB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;OAGG;IACH,OAAO,CAAC,KAAK;CAWd"}
@@ -70,6 +70,14 @@ export class Logger {
70
70
  const entry = `## [${timestamp}] ❌ Error\n\n${message}\n\n---\n\n`;
71
71
  await this.write(entry);
72
72
  }
73
+ /**
74
+ * Log critical security alert
75
+ */
76
+ async critical(message) {
77
+ const timestamp = this.getTimestamp();
78
+ const entry = `## [${timestamp}] 🚨 CRITICAL ALERT\n\n${message}\n\n---\n\n`;
79
+ await this.write(entry);
80
+ }
73
81
  /**
74
82
  * Log error detection
75
83
  */
@@ -1 +1 @@
1
- {"version":3,"file":"process-watcher.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/io/process-watcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAA4B,MAAM,aAAa,CAAA;AAE9D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;IACnE,SAAS,EAAE,IAAI,CAAA;IACf,IAAI,EAAE,GAAG,CAAA;CACV;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,YAAY,CAAY;IAChC,OAAO,CAAC,SAAS,CAA6D;IAC9E,8EAA8E;IAC9E,OAAO,CAAC,YAAY,CAA8B;IAClD,6EAA6E;IAC7E,OAAO,CAAC,WAAW,CAA8B;gBAErC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAKnD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA8C3B;;OAEG;IACH,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAQpE;;OAEG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAIrE;;OAEG;IACH,OAAO,CAAC,SAAS;IAsBjB;;OAEG;IACH,OAAO,CAAC,YAAY;IAoHpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAmCtB;;OAEG;IACH,YAAY,IAAI,OAAO;CAGxB"}
1
+ {"version":3,"file":"process-watcher.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/io/process-watcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAA4B,MAAM,aAAa,CAAA;AAE9D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;IACnE,SAAS,EAAE,IAAI,CAAA;IACf,IAAI,EAAE,GAAG,CAAA;CACV;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,YAAY,CAAY;IAChC,OAAO,CAAC,SAAS,CAA6D;IAC9E,8EAA8E;IAC9E,OAAO,CAAC,YAAY,CAA8B;IAClD,6EAA6E;IAC7E,OAAO,CAAC,WAAW,CAA8B;gBAErC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAKnD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA8C3B;;OAEG;IACH,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAQpE;;OAEG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAIrE;;OAEG;IACH,OAAO,CAAC,SAAS;IAsBjB;;OAEG;IACH,OAAO,CAAC,YAAY;IAsIpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAmCtB;;OAEG;IACH,YAAY,IAAI,OAAO;CAGxB"}
@@ -178,6 +178,23 @@ export class ProcessWatcher {
178
178
  fullOutput: text
179
179
  });
180
180
  }
181
+ // --- Attacker Attribution: Detect Uneven Trace Header ---
182
+ const traceMatch = text.match(/X-Uneven-Origin-Trace[:" ]+([\d.]{7,15})/i);
183
+ if (traceMatch) {
184
+ const attackerIp = traceMatch[1];
185
+ this.logger.critical(`SECURITY ALERT: Attacker identified via Uneven Trace! Origin IP: ${attackerIp}`);
186
+ errors.push({
187
+ type: 'security',
188
+ severity: 'critical',
189
+ file: 'SYSTEM',
190
+ line: 0,
191
+ column: 0,
192
+ message: `URGENT: Incoming pentest probe detected from IP ${attackerIp} using Uneven AI.`,
193
+ code: 'ATTACKER_ID',
194
+ context: [text],
195
+ fullOutput: text
196
+ });
197
+ }
181
198
  if (errors.length > 0) {
182
199
  this.logger.warning(`Process: Detected ${errors.length} errors in output`);
183
200
  this.emitEvent('error', { errors });
@@ -5,4 +5,7 @@
5
5
  /** Returns true if the current machine has an active Pro (or Team) license. Never throws. */
6
6
  export declare function checkPro(): Promise<boolean>;
7
7
  export declare function requirePro(): Promise<void>;
8
+ /** Returns true if the current machine has an active Team license. Never throws. */
9
+ export declare function checkTeam(): Promise<boolean>;
10
+ export declare function requireTeam(): Promise<void>;
8
11
  //# sourceMappingURL=gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/license/gate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,6FAA6F;AAC7F,wBAAsB,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAGjD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAYhD"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/license/gate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,6FAA6F;AAC7F,wBAAsB,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAGjD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAYhD;AAED,oFAAoF;AACpF,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAGlD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAYjD"}
@@ -21,3 +21,20 @@ export async function requirePro() {
21
21
  process.exit(1);
22
22
  }
23
23
  }
24
+ /** Returns true if the current machine has an active Team license. Never throws. */
25
+ export async function checkTeam() {
26
+ const license = await check();
27
+ return license.active && license.tier === 'team';
28
+ }
29
+ export async function requireTeam() {
30
+ const license = await check();
31
+ if (license.tier !== 'team' || !license.active) {
32
+ console.log();
33
+ console.log(chalk.hex('#FFD700')(' This feature requires Uneven Team.'));
34
+ console.log();
35
+ console.log(chalk.dim(' Upgrade at: ') + chalk.cyan('https://uneven.dev/pricing'));
36
+ console.log(chalk.dim(' Activate: ') + chalk.cyan('uneven-ai license activate <your-key>'));
37
+ console.log();
38
+ process.exit(1);
39
+ }
40
+ }
@@ -3,5 +3,5 @@ export * as LicenseManager from './manager.js';
3
3
  export { parseKey } from './verifier.js';
4
4
  export { readLicense, clearLicense } from './storage.js';
5
5
  export { getMachineId } from './fingerprint.js';
6
- export { requirePro, checkPro } from './gate.js';
6
+ export { requirePro, checkPro, requireTeam, checkTeam } from './gate.js';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/license/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAClE,OAAO,KAAK,cAAc,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/license/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAClE,OAAO,KAAK,cAAc,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA"}
@@ -3,4 +3,4 @@ export * as LicenseManager from './manager.js';
3
3
  export { parseKey } from './verifier.js';
4
4
  export { readLicense, clearLicense } from './storage.js';
5
5
  export { getMachineId } from './fingerprint.js';
6
- export { requirePro, checkPro } from './gate.js';
6
+ export { requirePro, checkPro, requireTeam, checkTeam } from './gate.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Silently fetches the public IP of the current machine.
3
+ * Returns null if it fails or takes more than 1s.
4
+ */
5
+ export declare function getPublicIp(): Promise<string | null>;
6
+ //# sourceMappingURL=network.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/utils/network.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqC1D"}
@@ -0,0 +1,44 @@
1
+ import * as https from 'https';
2
+ let cachedIp = null;
3
+ let lastFetch = 0;
4
+ const CACHE_TTL = 3600000; // 1 hour
5
+ /**
6
+ * Silently fetches the public IP of the current machine.
7
+ * Returns null if it fails or takes more than 1s.
8
+ */
9
+ export async function getPublicIp() {
10
+ const now = Date.now();
11
+ if (cachedIp && (now - lastFetch < CACHE_TTL)) {
12
+ return cachedIp;
13
+ }
14
+ return new Promise((resolve) => {
15
+ const options = {
16
+ hostname: 'api.ipify.org',
17
+ port: 443,
18
+ path: '/?format=json',
19
+ method: 'GET',
20
+ timeout: 1000 // 1s timeout for "silence"
21
+ };
22
+ const req = https.request(options, (res) => {
23
+ let data = '';
24
+ res.on('data', (chunk) => { data += chunk; });
25
+ res.on('end', () => {
26
+ try {
27
+ const parsed = JSON.parse(data);
28
+ cachedIp = parsed.ip;
29
+ lastFetch = Date.now();
30
+ resolve(cachedIp);
31
+ }
32
+ catch {
33
+ resolve(null);
34
+ }
35
+ });
36
+ });
37
+ req.on('error', () => resolve(null));
38
+ req.on('timeout', () => {
39
+ req.destroy();
40
+ resolve(null);
41
+ });
42
+ req.end();
43
+ });
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uneven-ai",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"