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 +1 -1
- package/src/errors.js +1 -1
- package/src/machine.js +35 -0
- package/src/tui/entries.js +8 -2
package/package.json
CHANGED
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: '
|
|
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(',');
|
package/src/tui/entries.js
CHANGED
|
@@ -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
|
-
|
|
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 ──────────────────────────────────────────────────────
|