mumpix 1.0.12 → 1.0.14
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/README.md +9 -2
- package/package.json +2 -1
- package/scripts/postinstall-auth.js +96 -0
- package/src/core/auth.js +46 -13
package/README.md
CHANGED
|
@@ -299,6 +299,7 @@ npm run release:publish-and-deprecate -- --otp=123456
|
|
|
299
299
|
# --skip-verify skip verify:claims
|
|
300
300
|
# --skip-publish only deprecate older versions
|
|
301
301
|
# --dry-run no writes to npm
|
|
302
|
+
# --remove-old try to unpublish old versions; deprecate on failure
|
|
302
303
|
```
|
|
303
304
|
|
|
304
305
|
---
|
|
@@ -376,13 +377,19 @@ mumpix auth login --license=<signed-license> # direct import
|
|
|
376
377
|
mumpix auth logout
|
|
377
378
|
```
|
|
378
379
|
|
|
380
|
+
For `--token` exchange, your backend should expose `/api/mumpix/auth/token/exchange` and map account tokens server-side (for example via `MUMPIX_AUTH_TOKEN_MAP`), not raw user IDs.
|
|
381
|
+
|
|
379
382
|
By default, auth state is stored at `~/.config/mumpix/auth.json` (or `%APPDATA%/mumpix/auth.json` on Windows).
|
|
380
383
|
|
|
381
384
|
### Install-time auth flow
|
|
382
385
|
|
|
383
386
|
On `npm install mumpix`, the package runs an install-time auth prompt in interactive terminals:
|
|
384
387
|
|
|
385
|
-
-
|
|
386
|
-
-
|
|
388
|
+
- Interactive terminals: install automatically opens browser auth and saves local signed license on success.
|
|
389
|
+
- If browser auth is cancelled/fails, install continues in `eventual` mode only.
|
|
387
390
|
|
|
388
391
|
Non-interactive installs (CI/containers) skip this prompt automatically and default to `eventual`.
|
|
392
|
+
|
|
393
|
+
Auth endpoint fallback:
|
|
394
|
+
- CLI tries `https://mumpixdb.com` first, then automatically retries `https://mumpixdb.com/benchmark` on 404.
|
|
395
|
+
- Override with `MUMPIX_AUTH_BASE_URL` if your auth API is hosted elsewhere.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mumpix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "SQLite for AI — embedded, zero-config memory database for AI agents and LLM applications",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"src/",
|
|
41
41
|
"bin/",
|
|
42
|
+
"scripts/postinstall-auth.js",
|
|
42
43
|
"examples/",
|
|
43
44
|
"CHANGELOG.md",
|
|
44
45
|
"README.md",
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const readline = require('readline');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
const {
|
|
7
|
+
loginWithDeviceFlow,
|
|
8
|
+
exchangeTokenForLicense,
|
|
9
|
+
authStatePath,
|
|
10
|
+
} = require('../src/core/auth');
|
|
11
|
+
|
|
12
|
+
function isInteractive() {
|
|
13
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY && process.env.CI !== 'true');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function fmtExpiry(ts) {
|
|
17
|
+
if (!ts) return '-';
|
|
18
|
+
const d = new Date(Number(ts));
|
|
19
|
+
if (Number.isNaN(d.getTime())) return '-';
|
|
20
|
+
return d.toISOString();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function openBrowser(url) {
|
|
24
|
+
if (!url) return false;
|
|
25
|
+
try {
|
|
26
|
+
if (process.platform === 'darwin') {
|
|
27
|
+
spawn('open', [url], { stdio: 'ignore', detached: true }).unref();
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (process.platform === 'win32') {
|
|
31
|
+
spawn('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true }).unref();
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
spawn('xdg-open', [url], { stdio: 'ignore', detached: true }).unref();
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ask(question) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
44
|
+
rl.question(question, (answer) => {
|
|
45
|
+
rl.close();
|
|
46
|
+
resolve(String(answer || '').trim());
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function run() {
|
|
52
|
+
try {
|
|
53
|
+
if (String(process.env.MUMPIX_POSTINSTALL_AUTH || '1') === '0') return;
|
|
54
|
+
|
|
55
|
+
const token = String(process.env.MUMPIX_AUTH_TOKEN || '').trim();
|
|
56
|
+
if (token) {
|
|
57
|
+
const state = await exchangeTokenForLicense(token, {});
|
|
58
|
+
console.log(`[mumpix] account linked via token. tier=${state.license.tier || '-'} exp=${fmtExpiry(state.license.exp)}`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!isInteractive()) {
|
|
63
|
+
console.log('[mumpix] install complete. auth skipped (non-interactive).');
|
|
64
|
+
console.log('[mumpix] run "mumpix auth login" later to unlock strict/verified. default mode remains eventual.');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log('\n[mumpix] attempting automatic account link (interactive install).');
|
|
69
|
+
console.log('[mumpix] if you close/cancel browser auth, install continues in eventual mode.');
|
|
70
|
+
|
|
71
|
+
const state = await loginWithDeviceFlow({
|
|
72
|
+
onPrompt: ({ userCode, verifyUrl, expiresInSec, intervalSec }) => {
|
|
73
|
+
const opened = openBrowser(verifyUrl);
|
|
74
|
+
if (opened) {
|
|
75
|
+
console.log(`[mumpix] opened browser: ${verifyUrl}`);
|
|
76
|
+
} else {
|
|
77
|
+
console.log(`[mumpix] open this URL in your browser: ${verifyUrl}`);
|
|
78
|
+
}
|
|
79
|
+
console.log(`[mumpix] enter code: ${userCode}`);
|
|
80
|
+
console.log(`[mumpix] waiting for approval (expires in ${expiresInSec}s, polling ${intervalSec}s)...`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log(`[mumpix] auth success. tier=${state.license.tier || state.account.tier || '-'} exp=${fmtExpiry(state.license.exp)}`);
|
|
85
|
+
console.log(`[mumpix] saved auth: ${authStatePath()}`);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
const msg = err && err.message ? err.message : 'unknown error';
|
|
88
|
+
console.log(`[mumpix] auth step failed (${msg}).`);
|
|
89
|
+
if (String(msg).includes('404')) {
|
|
90
|
+
console.log('[mumpix] auth endpoints not found on this host. check MUMPIX_AUTH_BASE_URL or server auth routes.');
|
|
91
|
+
}
|
|
92
|
+
console.log('[mumpix] continuing install in eventual mode. run "mumpix auth login" later.');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
run();
|
package/src/core/auth.js
CHANGED
|
@@ -56,7 +56,11 @@ function requestJson(method, targetUrl, body = null, headers = {}, timeoutMs = 1
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
const msg = json.error || json.message || `HTTP ${res.statusCode}`;
|
|
59
|
-
|
|
59
|
+
const err = new Error(msg);
|
|
60
|
+
err.statusCode = Number(res.statusCode || 0);
|
|
61
|
+
err.response = json;
|
|
62
|
+
err.url = targetUrl;
|
|
63
|
+
reject(err);
|
|
60
64
|
});
|
|
61
65
|
});
|
|
62
66
|
req.on('error', reject);
|
|
@@ -66,6 +70,13 @@ function requestJson(method, targetUrl, body = null, headers = {}, timeoutMs = 1
|
|
|
66
70
|
});
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
function authBaseCandidates(inputBase) {
|
|
74
|
+
const raw = String(inputBase || process.env.MUMPIX_AUTH_BASE_URL || 'https://mumpixdb.com').replace(/\/+$/, '');
|
|
75
|
+
const out = [raw];
|
|
76
|
+
if (!raw.endsWith('/benchmark')) out.push(`${raw}/benchmark`);
|
|
77
|
+
return Array.from(new Set(out));
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
function decodeLicenseClaims(licenseKey) {
|
|
70
81
|
if (!licenseKey || typeof licenseKey !== 'string') return null;
|
|
71
82
|
const [payloadB64] = licenseKey.split('.');
|
|
@@ -131,25 +142,48 @@ function upsertStoredLicense(licenseKey, account = {}) {
|
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
async function exchangeTokenForLicense(authToken, opts = {}) {
|
|
134
|
-
const base = String(opts.baseUrl || process.env.MUMPIX_AUTH_BASE_URL || 'https://mumpixdb.com').replace(/\/+$/, '');
|
|
135
145
|
const endpoint = String(opts.exchangePath || process.env.MUMPIX_AUTH_TOKEN_EXCHANGE_PATH || '/api/mumpix/auth/token/exchange');
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
const timeoutMs = Number(opts.timeoutMs || process.env.MUMPIX_AUTH_TIMEOUT_MS || 10000);
|
|
147
|
+
let lastErr = null;
|
|
148
|
+
for (const base of authBaseCandidates(opts.baseUrl)) {
|
|
149
|
+
try {
|
|
150
|
+
const body = await requestJson('POST', `${base}${endpoint}`, {}, {
|
|
151
|
+
authorization: `Bearer ${authToken}`
|
|
152
|
+
}, timeoutMs);
|
|
153
|
+
const licenseKey = body.licenseKey || body.license || null;
|
|
154
|
+
if (!licenseKey) throw new Error('No license key returned from auth exchange');
|
|
155
|
+
return upsertStoredLicense(licenseKey, body.account || {});
|
|
156
|
+
} catch (err) {
|
|
157
|
+
lastErr = err;
|
|
158
|
+
if (Number(err && err.statusCode) === 404) continue;
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw lastErr || new Error('Auth token exchange failed');
|
|
142
163
|
}
|
|
143
164
|
|
|
144
165
|
async function loginWithDeviceFlow(opts = {}) {
|
|
145
|
-
const base = String(opts.baseUrl || process.env.MUMPIX_AUTH_BASE_URL || 'https://mumpixdb.com').replace(/\/+$/, '');
|
|
146
166
|
const startPath = String(opts.startPath || process.env.MUMPIX_AUTH_DEVICE_START_PATH || '/api/mumpix/auth/device/start');
|
|
147
167
|
const pollPath = String(opts.pollPath || process.env.MUMPIX_AUTH_DEVICE_POLL_PATH || '/api/mumpix/auth/device/poll');
|
|
148
168
|
const timeoutMs = Number(opts.timeoutMs || process.env.MUMPIX_AUTH_TIMEOUT_MS || 10000);
|
|
149
169
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
170
|
+
let start = null;
|
|
171
|
+
let base = '';
|
|
172
|
+
let lastErr = null;
|
|
173
|
+
for (const candidate of authBaseCandidates(opts.baseUrl)) {
|
|
174
|
+
try {
|
|
175
|
+
start = await requestJson('POST', `${candidate}${startPath}`, {
|
|
176
|
+
client: 'mumpix-cli'
|
|
177
|
+
}, {}, timeoutMs);
|
|
178
|
+
base = candidate;
|
|
179
|
+
break;
|
|
180
|
+
} catch (err) {
|
|
181
|
+
lastErr = err;
|
|
182
|
+
if (Number(err && err.statusCode) === 404) continue;
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!start) throw lastErr || new Error('Device auth start failed');
|
|
153
187
|
|
|
154
188
|
const deviceCode = start.deviceCode || start.device_code;
|
|
155
189
|
const userCode = start.userCode || start.user_code;
|
|
@@ -196,4 +230,3 @@ module.exports = {
|
|
|
196
230
|
loginWithDeviceFlow,
|
|
197
231
|
decodeLicenseClaims,
|
|
198
232
|
};
|
|
199
|
-
|