jaitus 0.3.16 → 0.3.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": "jaitus",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "description": "Jaitus — a coding agent in your terminal.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/errors.js CHANGED
@@ -13,7 +13,7 @@ const CATALOG = {
13
13
  account_suspended: { title: 'Account suspended', hint: 'Contact support if you think this is a mistake.' },
14
14
  email_unverified: { title: 'Email not verified', hint: 'Open the verification link in your inbox, then retry.' },
15
15
  free_limit_reached: { title: 'Free prompts used up', hint: 'Subscribe on the website, then retry this turn.' },
16
- free_machine_limit: { title: 'No free prompts on this account', hint: 'This computer already has its free accounts. Subscribe on the website to use this one.' },
16
+ free_machine_limit: { title: 'No free prompts on this account', hint: 'Subscribe on the website to keep using Jaitus on this account.' },
17
17
  free_needs_cli: { title: 'Update Jaitus for free prompts', hint: 'Run npm i -g jaitus, then try again.' },
18
18
  free_request_too_large: { title: 'Too large for the free plan', hint: 'Run /compact or start a new session, or subscribe.' },
19
19
  plan_limit_reached: { title: 'Monthly allowance used', hint: 'It resets at the start of your next period, or upgrade on the website. /usage shows where you are.' },
package/src/machine.js CHANGED
@@ -11,6 +11,8 @@
11
11
  // folder), each restoring the other
12
12
  // n the address of each physical network adapter (virtual and randomised ones are
13
13
  // skipped: they change by themselves)
14
+ // g the git identity this computer commits as (global user.email) — the same on
15
+ // every computer one person sets up, so it recognises the person, not the machine
14
16
  //
15
17
  // A VPN, a new network or a new email changes none of these. They are reported by this
16
18
  // program, so they identify a computer for an honest client — the same ceiling every
@@ -126,6 +128,32 @@ function adapterIds() {
126
128
  return [...out].sort().slice(0, 3);
127
129
  }
128
130
 
131
+ /** Hostname, OS user, CPU model, memory and OS — together, never one alone. */
132
+ function softFingerprint() {
133
+ try {
134
+ let user = '';
135
+ try { user = os.userInfo().username; } catch { /* no user database */ }
136
+ const cpu = (os.cpus()?.[0]?.model || '').replace(/\s+/g, ' ').trim();
137
+ const gb = Math.round(os.totalmem() / 2 ** 30);
138
+ const host = os.hostname();
139
+ if (!host && !user) return null;
140
+ return [process.platform, os.arch(), host, user, cpu, `${gb}GB`].join('|');
141
+ } catch { return null; }
142
+ }
143
+
144
+ // Addresses git ships as examples or that many people leave unset-looking: not a person.
145
+ const PLACEHOLDER_EMAIL = /(^|@)(example\.(com|org|net)|localhost)$|^(you|your[._-]?email|email|user|root|test|admin)@/i;
146
+
147
+ /** The global git identity (user.email), or null when unset or a placeholder. */
148
+ function gitIdentity() {
149
+ try {
150
+ const r = spawnSync('git', ['config', '--global', 'user.email'], { encoding: 'utf8', timeout: 2000, windowsHide: true });
151
+ const v = String(r.stdout || '').trim().toLowerCase();
152
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || PLACEHOLDER_EMAIL.test(v)) return null;
153
+ return v;
154
+ } catch { return null; }
155
+ }
156
+
129
157
  /**
130
158
  * Where the install id's second copy lives: outside ~/.jaitus, in the OS's app-data
131
159
  * folder. Always under os.homedir() (not LOCALAPPDATA / XDG_DATA_HOME), so a test run
@@ -180,6 +208,13 @@ export function machineHeader() {
180
208
  if (h) parts.push(`h=${h}`);
181
209
  parts.push(`i=${hash('i', installId())}`);
182
210
  for (const mac of adapterIds()) parts.push(`n=${hash('n', mac)}`);
211
+ // s: the computer as it describes itself — what an id-reset tool leaves alone. The
212
+ // server links it only together with a network both accounts were used from, so two
213
+ // people with the same laptop model are never taken for one.
214
+ const soft = softFingerprint();
215
+ if (soft) parts.push(`s=${hash('s', soft)}`);
216
+ const git = gitIdentity();
217
+ if (git) parts.push(`g=${hash('g', git)}`);
183
218
  if (m && cache.m !== m) writeCache({ ...cache, m, ...(h ? { h } : {}) });
184
219
  } catch { /* whatever was gathered */ }
185
220
  header = parts.join(',');
@@ -10,7 +10,7 @@
10
10
 
11
11
  import { palette, mix, ramp } from './theme.js';
12
12
  import { renderMarkdown, inlineSpans } from './markdown.js';
13
- import { ansiToRows, wrapRows, wrapRow, truncateRow, stringWidth, plain } from './ansi.js';
13
+ import { ansiToRows, wrapRows, wrapRow, truncateRow, stringWidth, plain, padRow } from './ansi.js';
14
14
  import { highlightLine, languageForPath } from './highlight.js';
15
15
  import { wordRows, wordSize } from './wordmark.js';
16
16
  import { MASCOT_ROWS } from '../brand.js';
@@ -150,7 +150,13 @@ const BUILDERS = {
150
150
  if (data.attachments?.length) {
151
151
  rows.push([indent, { t: data.attachments.map((a) => `⧉ ${a}`).join(' '), color: p.purple }]);
152
152
  }
153
- return rows;
153
+ // The whole message sits on a faint band the full width of the window, with a row of
154
+ // it above and below, so what the person said never reads as part of the answer. The
155
+ // band is drawn, never copied (the padding is decoration).
156
+ const band = p.raised;
157
+ const shade = (row) => padRow(row.map((span) => ({ ...span, backgroundColor: span.backgroundColor || band })), width, { backgroundColor: band, copy: '' });
158
+ const edge = [{ t: ' '.repeat(Math.max(1, width)), backgroundColor: band, copy: '' }];
159
+ return [edge, ...rows.map(shade), edge];
154
160
  },
155
161
 
156
162
  // ── What the model said ──────────────────────────────────────────────────────