vektor-slipstream 1.4.4 → 2.0.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.
Files changed (56) hide show
  1. package/README.md +67 -306
  2. package/package.json +14 -146
  3. package/CHANGELOG.md +0 -139
  4. package/LICENSE +0 -33
  5. package/TENETS.md +0 -189
  6. package/audn-log.js +0 -143
  7. package/axon.js +0 -389
  8. package/boot-patch.js +0 -33
  9. package/boot-screen.html +0 -210
  10. package/briefing.js +0 -150
  11. package/cerebellum.js +0 -439
  12. package/cloak-behaviour.js +0 -596
  13. package/cloak-captcha.js +0 -541
  14. package/cloak-core.js +0 -499
  15. package/cloak-identity.js +0 -484
  16. package/cloak-index.js +0 -261
  17. package/cloak-llms.js +0 -163
  18. package/cloak-pattern-store.js +0 -471
  19. package/cloak-recorder-auto.js +0 -297
  20. package/cloak-recorder-snippet.js +0 -119
  21. package/cloak-turbo-quant.js +0 -357
  22. package/cloak-warmup.js +0 -240
  23. package/cortex.js +0 -221
  24. package/detect-hardware.js +0 -181
  25. package/entity-resolver.js +0 -298
  26. package/errors.js +0 -66
  27. package/examples/example-claude-mcp.js +0 -220
  28. package/examples/example-langchain-researcher.js +0 -82
  29. package/examples/example-openai-assistant.js +0 -84
  30. package/examples/examples-README.md +0 -161
  31. package/export-import.js +0 -221
  32. package/forget.js +0 -148
  33. package/inspect.js +0 -199
  34. package/mistral/README-mistral.md +0 -123
  35. package/mistral/mistral-bridge.js +0 -218
  36. package/mistral/mistral-setup.js +0 -220
  37. package/mistral/vektor-tool-manifest.json +0 -41
  38. package/models/model_quantized.onnx +0 -0
  39. package/models/vocab.json +0 -1
  40. package/namespace.js +0 -186
  41. package/pin.js +0 -91
  42. package/slipstream-core-extended.js +0 -134
  43. package/slipstream-core.js +0 -1
  44. package/slipstream-db.js +0 -140
  45. package/slipstream-embedder.js +0 -338
  46. package/sovereign.js +0 -142
  47. package/token.js +0 -322
  48. package/types/index.d.ts +0 -269
  49. package/vektor-banner-loader.js +0 -109
  50. package/vektor-cli.js +0 -259
  51. package/vektor-licence-prompt.js +0 -128
  52. package/vektor-licence.js +0 -192
  53. package/vektor-setup.js +0 -270
  54. package/vektor-slipstream.dxt +0 -0
  55. package/vektor-tui.js +0 -373
  56. package/visualize.js +0 -235
package/vektor-cli.js DELETED
@@ -1,259 +0,0 @@
1
- /**
2
- * vektor-cli.js (v1.5.x patch)
3
- * VEKTOR Slipstream
4
- * CLI router — drop-in replacement for the v1.4.x router.
5
- *
6
- * New commands added in v1.5.x:
7
- * npx vektor agent --tools full tool suite
8
- * npx vektor watch filesystem watcher
9
- * npx vektor sync P2P memory sync
10
- * npx vektor swarm multi-agent coordination
11
- *
12
- * All existing v1.4.x commands (chat, remember, ask, agent) are preserved.
13
- */
14
-
15
- 'use strict';
16
-
17
- const path = require('path');
18
-
19
- // ─── Command registry ─────────────────────────────────────────────────────────
20
-
21
- const COMMANDS = {
22
- // ── v1.4.x (existing) ──────────────────────────────────────────────────
23
- chat: () => require('./vektor-cli-chat.js').runChat,
24
- remember: () => require('./vektor-cli-chat.js').runRemember,
25
- ask: () => require('./vektor-cli-chat.js').runAsk,
26
-
27
- // ── v1.5.x (new) ───────────────────────────────────────────────────────
28
-
29
- /**
30
- * npx vektor agent [goal] [--tools web,fs,code] [--continue]
31
- * Autonomous agent with web search, file system, code execution.
32
- */
33
- agent: () => async (argv) => {
34
- const { default: runAgent, main } = (() => {
35
- try { return require('./vektor-agent.js'); } catch { return require('../vektor-agent.js'); }
36
- })();
37
- // Delegate to vektor-agent CLI main — it reads process.argv
38
- await main?.() ?? runAgent({
39
- goal: argv.filter(a => !a.startsWith('-')).join(' '),
40
- tools: ['web', 'fs', 'code', 'memory'],
41
- provider: process.env.VEKTOR_PROVIDER || 'ollama',
42
- });
43
- },
44
-
45
- /**
46
- * npx vektor watch [path] [--ext js,ts] [--no-llm]
47
- * Filesystem watcher — auto-stores change summaries into MAGMA.
48
- */
49
- watch: () => async (_argv) => {
50
- const { startWatch, parseArgs } = (() => {
51
- try { return require('./vektor-watch.js'); } catch { return require('../vektor-watch.js'); }
52
- })();
53
- const opts = parseArgs(process.argv);
54
- await startWatch(opts);
55
- },
56
-
57
- /**
58
- * npx vektor sync --host [--pin X]
59
- * npx vektor sync --connect <ip> [--pin X]
60
- * Encrypted P2P memory sync between machines.
61
- */
62
- sync: () => async (_argv) => {
63
- const { main } = (() => {
64
- try { return require('./vektor-sync.js'); } catch { return require('../vektor-sync.js'); }
65
- })();
66
- await main?.();
67
- },
68
-
69
- /**
70
- * npx vektor swarm "goal" [--agents 3] [--roles r1,r2,r3]
71
- * Multi-agent swarm coordination.
72
- */
73
- swarm: () => async (_argv) => {
74
- const { main } = (() => {
75
- try { return require('./vektor-swarm.js'); } catch { return require('../vektor-swarm.js'); }
76
- })();
77
- await main?.();
78
- },
79
-
80
- // ── Utility ────────────────────────────────────────────────────────────
81
-
82
- providers: () => async (_argv) => {
83
- await checkProviders();
84
- },
85
-
86
- version: () => async (_argv) => {
87
- const pkg = safeRequirePackageJson();
88
- console.log(`VEKTOR Slipstream v${pkg.version || '1.5.0'}`);
89
- },
90
-
91
- help: () => async (_argv) => {
92
- printHelp();
93
- },
94
- };
95
-
96
- // ─── Provider checker ─────────────────────────────────────────────────────────
97
-
98
- async function checkProviders() {
99
- const C = { green: '\x1b[32m', red: '\x1b[31m', dim: '\x1b[2m', reset: '\x1b[0m', bold: '\x1b[1m' };
100
- console.log(`\n${C.bold}VEKTOR Provider Status${C.reset}\n`);
101
-
102
- const checks = [
103
- { name: 'Claude (Anthropic)', key: 'ANTHROPIC_API_KEY', test: () => testClaude() },
104
- { name: 'OpenAI', key: 'OPENAI_API_KEY', test: () => testOpenAI() },
105
- { name: 'Groq', key: 'GROQ_API_KEY', test: () => testGroq() },
106
- { name: 'Google Gemini', key: 'GEMINI_API_KEY', test: () => testGemini() },
107
- { name: 'Ollama (local)', key: null, test: () => testOllama() },
108
- ];
109
-
110
- for (const check of checks) {
111
- const hasKey = check.key ? !!process.env[check.key] : true;
112
- if (!hasKey) {
113
- console.log(` ${C.red}✗${C.reset} ${check.name} ${C.dim}(${check.key} not set)${C.reset}`);
114
- continue;
115
- }
116
- process.stdout.write(` ⏳ ${check.name}…`);
117
- try {
118
- const model = await check.test();
119
- process.stdout.write(`\r ${C.green}✓${C.reset} ${check.name} ${C.dim}${model}${C.reset}\n`);
120
- } catch (err) {
121
- process.stdout.write(`\r ${C.red}✗${C.reset} ${check.name} ${C.dim}${err.message}${C.reset}\n`);
122
- }
123
- }
124
- console.log('');
125
- }
126
-
127
- async function testClaude() {
128
- const res = await fetchTimeout('https://api.anthropic.com/v1/models', {
129
- headers: { 'x-api-key': process.env.ANTHROPIC_API_KEY, 'anthropic-version': '2023-06-01' },
130
- }, 5000);
131
- const j = await res.json();
132
- const m = j.data?.[0]?.id || 'connected';
133
- return m;
134
- }
135
- async function testOpenAI() {
136
- const res = await fetchTimeout('https://api.openai.com/v1/models', {
137
- headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
138
- }, 5000);
139
- const j = await res.json();
140
- return j.data?.[0]?.id || 'connected';
141
- }
142
- async function testGroq() {
143
- const res = await fetchTimeout('https://api.groq.com/openai/v1/models', {
144
- headers: { Authorization: `Bearer ${process.env.GROQ_API_KEY}` },
145
- }, 5000);
146
- const j = await res.json();
147
- return j.data?.[0]?.id || 'connected';
148
- }
149
- async function testGemini() {
150
- const res = await fetchTimeout(`https://generativelanguage.googleapis.com/v1beta/models?key=${process.env.GEMINI_API_KEY}`, {}, 5000);
151
- const j = await res.json();
152
- return j.models?.[0]?.name || 'connected';
153
- }
154
- async function testOllama() {
155
- const res = await fetchTimeout('http://localhost:11434/api/tags', {}, 3000);
156
- const j = await res.json();
157
- const models = j.models?.map(m => m.name) || [];
158
- if (!models.length) throw new Error('No models installed. Run: ollama pull qwen3');
159
- return models.join(', ');
160
- }
161
-
162
- function fetchTimeout(url, opts, ms) {
163
- const ctrl = new AbortController();
164
- const t = setTimeout(() => ctrl.abort(), ms);
165
- return fetch(url, { ...opts, signal: ctrl.signal }).finally(() => clearTimeout(t));
166
- }
167
-
168
- // ─── Help ─────────────────────────────────────────────────────────────────────
169
-
170
- function printHelp() {
171
- const C = { bold: '\x1b[1m', dim: '\x1b[2m', cyan: '\x1b[36m', green: '\x1b[32m', reset: '\x1b[0m' };
172
- console.log(`
173
- ${C.bold}VEKTOR Slipstream CLI${C.reset}
174
-
175
- ${C.bold}Memory:${C.reset}
176
- ${C.cyan}npx vektor remember${C.reset} "text" Store a fact into MAGMA
177
- ${C.cyan}npx vektor ask${C.reset} "question" Recall + LLM answer
178
- ${C.cyan}npx vektor chat${C.reset} Interactive persistent-memory chat
179
-
180
- ${C.bold}Agent (v1.5):${C.reset}
181
- ${C.cyan}npx vektor agent${C.reset} "goal" Basic autonomous agent
182
- ${C.cyan}npx vektor agent${C.reset} "goal" ${C.dim}--tools${C.reset} Full tool suite (web+fs+code)
183
- ${C.cyan}npx vektor agent${C.reset} "goal" ${C.dim}--continue${C.reset} Resume last session
184
- ${C.cyan}npx vektor agent${C.reset} "goal" ${C.dim}--tools web,fs,code${C.reset}
185
-
186
- ${C.bold}Watch (v1.5):${C.reset}
187
- ${C.cyan}npx vektor watch${C.reset} [path] Watch filesystem, auto-store changes
188
- ${C.cyan}npx vektor watch${C.reset} src/ ${C.dim}--ext ts,tsx${C.reset}
189
- ${C.cyan}npx vektor watch${C.reset} ${C.dim}--no-llm${C.reset} Heuristic summaries only
190
-
191
- ${C.bold}Sync (v1.5):${C.reset}
192
- ${C.cyan}npx vektor sync${C.reset} --host Listen for incoming sync
193
- ${C.cyan}npx vektor sync${C.reset} --connect <ip> Sync with host machine
194
- ${C.cyan}npx vektor sync${C.reset} --host ${C.dim}--pin 123456${C.reset}
195
-
196
- ${C.bold}Swarm (v1.5):${C.reset}
197
- ${C.cyan}npx vektor swarm${C.reset} "goal" Multi-agent coordination
198
- ${C.cyan}npx vektor swarm${C.reset} "goal" ${C.dim}--agents 4${C.reset}
199
- ${C.cyan}npx vektor swarm${C.reset} "goal" ${C.dim}--roles researcher,coder,reviewer${C.reset}
200
-
201
- ${C.bold}Utilities:${C.reset}
202
- ${C.cyan}npx vektor providers${C.reset} Test all API key connections
203
- ${C.cyan}npx vektor version${C.reset} Show version
204
- ${C.cyan}npx vektor help${C.reset} Show this help
205
-
206
- ${C.bold}Providers:${C.reset} ${C.dim}--provider claude|openai|groq|gemini|ollama${C.reset}
207
- `);
208
- }
209
-
210
- // ─── Helpers ──────────────────────────────────────────────────────────────────
211
-
212
- function safeRequirePackageJson() {
213
- const candidates = [
214
- path.join(__dirname, 'package.json'),
215
- path.join(__dirname, '..', 'package.json'),
216
- ];
217
- for (const c of candidates) {
218
- try { return require(c); } catch { /* skip */ }
219
- }
220
- return {};
221
- }
222
-
223
- // ─── CLI dispatcher ───────────────────────────────────────────────────────────
224
-
225
- async function main() {
226
- const argv = process.argv.slice(2);
227
- const cmd = argv[0];
228
-
229
- if (!cmd || cmd === '--help' || cmd === '-h') {
230
- printHelp();
231
- return;
232
- }
233
- if (cmd === '--version' || cmd === '-v') {
234
- const pkg = safeRequirePackageJson();
235
- console.log(`v${pkg.version || '1.5.0'}`);
236
- return;
237
- }
238
-
239
- const factory = COMMANDS[cmd];
240
- if (!factory) {
241
- console.error(`Unknown command: ${cmd}\nRun 'npx vektor help' for usage.`);
242
- process.exit(1);
243
- }
244
-
245
- try {
246
- const handler = factory();
247
- await handler(argv.slice(1));
248
- } catch (err) {
249
- console.error(`Error in '${cmd}': ${err.message}`);
250
- if (process.env.DEBUG) console.error(err);
251
- process.exit(1);
252
- }
253
- }
254
-
255
- if (require.main === module) {
256
- main();
257
- }
258
-
259
- module.exports = { COMMANDS, main };
@@ -1,128 +0,0 @@
1
- 'use strict';
2
- /**
3
- * vektor-licence-prompt.js — fixed for MCP mode (stderr exit, no stdout corruption)
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const os = require('os');
9
- const readline = require('readline');
10
-
11
- const CACHE_DIR = path.join(os.homedir(), '.vektor');
12
- const CACHE_FILE = path.join(CACHE_DIR, 'licence.json');
13
- const PURCHASE_URL = 'https://vektormemory.com/product#pricing';
14
-
15
- function _getCachedKey() {
16
- try {
17
- if (!fs.existsSync(CACHE_FILE)) return null;
18
- const cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
19
- for (const entry of Object.values(cache)) {
20
- if (entry?.licence_key && entry?.valid === true) return entry.licence_key;
21
- }
22
- return null;
23
- } catch (_) { return null; }
24
- }
25
-
26
- function _saveCachedKey(licenceKey) {
27
- try {
28
- if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });
29
- let cache = {};
30
- if (fs.existsSync(CACHE_FILE)) {
31
- try { cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); } catch (_) {}
32
- }
33
- cache['_prompt_key'] = { licence_key: licenceKey, valid: true, saved_at: Date.now() };
34
- fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
35
- } catch (_) {}
36
- }
37
-
38
- function _prompt(question) {
39
- return new Promise((resolve) => {
40
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
41
- rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); });
42
- });
43
- }
44
-
45
- function _printLicenceBanner() {
46
- console.log('');
47
- console.log(' ╔══════════════════════════════════════════════════════╗');
48
- console.log(' ║ VEKTOR SLIPSTREAM — LICENCE REQUIRED ║');
49
- console.log(' ╚══════════════════════════════════════════════════════╝');
50
- console.log('');
51
- console.log(' A licence key is required to use Vektor Slipstream.');
52
- console.log(' Your key was emailed to you after purchase on Polar.');
53
- console.log('');
54
- console.log(' Purchase at: ' + PURCHASE_URL);
55
- console.log('');
56
- console.log(' Tip: Set VEKTOR_LICENCE_KEY env var to skip this prompt.');
57
- console.log(' e.g. VEKTOR_LICENCE_KEY=YOUR-KEY node your-agent.js');
58
- console.log('');
59
- }
60
-
61
- async function resolveAndValidateLicence(validateLicenceFn) {
62
-
63
- // Priority 1 — env var (silent, MCP/server/CI safe)
64
- const envKey = process.env.VEKTOR_LICENCE_KEY;
65
- if (envKey && envKey.trim().length >= 8) {
66
- await validateLicenceFn(envKey.trim());
67
- _saveCachedKey(envKey.trim());
68
- return;
69
- }
70
-
71
- // Priority 2 — cached key
72
- const cachedKey = _getCachedKey();
73
- if (cachedKey) {
74
- try {
75
- await validateLicenceFn(cachedKey);
76
- return;
77
- } catch (e) {
78
- process.stderr.write('\n [vektor] Cached licence key is no longer valid. Please re-enter.\n\n');
79
- }
80
- }
81
-
82
- // Priority 3 — non-TTY (MCP, CI, piped) — stderr only, never stdout
83
- if (!process.stdin.isTTY) {
84
- process.stderr.write(
85
- '\n[vektor] LICENCE REQUIRED — stdin is not interactive.\n' +
86
- '[vektor] Set VEKTOR_LICENCE_KEY env var:\n' +
87
- '[vektor] VEKTOR_LICENCE_KEY=YOUR-KEY node your-agent.js\n' +
88
- '[vektor] Purchase at: ' + PURCHASE_URL + '\n\n'
89
- );
90
- process.exit(1);
91
- }
92
-
93
- // Priority 4 — interactive prompt (TTY only)
94
- _printLicenceBanner();
95
-
96
- let attempts = 0;
97
- while (attempts < 3) {
98
- const input = await _prompt(' Paste your licence key › ');
99
- if (!input || input.length < 8) {
100
- console.log(' ✗ No key entered. Try again.\n');
101
- attempts++;
102
- continue;
103
- }
104
- try {
105
- await validateLicenceFn(input);
106
- _saveCachedKey(input);
107
- console.log(' ✓ Licence validated — this machine is now activated.\n');
108
- console.log(' Your key has been saved to ~/.vektor/licence.json');
109
- console.log(' You will not be prompted again on this machine.\n');
110
- return;
111
- } catch (e) {
112
- console.log('');
113
- console.log(e.message);
114
- attempts++;
115
- if (attempts < 3) {
116
- console.log(` Try again (${3 - attempts} attempt${3 - attempts === 1 ? '' : 's'} remaining)...\n`);
117
- }
118
- }
119
- }
120
-
121
- throw new Error(
122
- '\n Licence validation failed after 3 attempts.\n' +
123
- ' Purchase at: ' + PURCHASE_URL + '\n' +
124
- ' Already purchased? Contact hello@vektormemory.com\n'
125
- );
126
- }
127
-
128
- module.exports = { resolveAndValidateLicence };
package/vektor-licence.js DELETED
@@ -1,192 +0,0 @@
1
- 'use strict';
2
- /**
3
- * vektor-licence.js
4
- * Licence validation for VEKTOR Slipstream.
5
- * Validates against cached licence in ~/.vektor/licence.json
6
- * or VEKTOR_LICENCE_KEY environment variable.
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
- const os = require('os');
12
- const crypto = require('crypto');
13
-
14
- const CACHE_PATH = path.join(os.homedir(), '.vektor', 'licence.json');
15
- const PURCHASE_URL = 'https://vektormemory.com/product#pricing';
16
-
17
- // Known valid key format: UUID v4 uppercase
18
- const KEY_RE = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
19
-
20
- function readCache() {
21
- try {
22
- if (!fs.existsSync(CACHE_PATH)) return null;
23
- const raw = fs.readFileSync(CACHE_PATH, 'utf8');
24
- return JSON.parse(raw);
25
- } catch { return null; }
26
- }
27
-
28
- function writeCache(key, data) {
29
- try {
30
- const dir = path.dirname(CACHE_PATH);
31
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
32
- fs.writeFileSync(CACHE_PATH, JSON.stringify({ key, ...data, cachedAt: new Date().toISOString() }, null, 2), { mode: 0o600 });
33
- } catch { /* ignore */ }
34
- }
35
-
36
- function getCachedKey() {
37
- const cache = readCache();
38
- return cache?.key || null;
39
- }
40
-
41
- /**
42
- * Validate a licence key.
43
- * Checks format, then attempts Polar API verification.
44
- * Falls back to cached validation if offline.
45
- */
46
- async function validateLicence(key) {
47
- if (!key) {
48
- const envKey = process.env.VEKTOR_LICENCE_KEY;
49
- const cached = readCache();
50
- key = envKey || cached?.key;
51
- }
52
-
53
- if (!key) {
54
- throw new Error(
55
- `\n\n VEKTOR SLIPSTREAM — Licence Required\n` +
56
- ` ─────────────────────────────────────\n` +
57
- ` No licence key found.\n\n` +
58
- ` Purchase at: ${PURCHASE_URL}\n` +
59
- ` Then run: npx vektor setup\n`
60
- );
61
- }
62
-
63
- // Format check
64
- if (!KEY_RE.test(key.trim())) {
65
- throw new Error(`Invalid licence key format. Expected UUID format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`);
66
- }
67
-
68
- const cleanKey = key.trim().toUpperCase();
69
-
70
- // Check cache first (avoid network on every boot)
71
- const cache = readCache();
72
- if (cache?.key?.toUpperCase() === cleanKey && cache?.valid) {
73
- // Re-validate online max once per 24hrs
74
- const cachedAt = cache.cachedAt ? new Date(cache.cachedAt) : new Date(0);
75
- const ageHours = (Date.now() - cachedAt.getTime()) / 3600000;
76
- if (ageHours < 24) {
77
- return { valid: true, key: cleanKey, source: 'cache' };
78
- }
79
- }
80
-
81
- // Online validation via Polar
82
- try {
83
- const result = await _polarValidate(cleanKey);
84
- writeCache(cleanKey, { valid: true, plan: result.plan, activatedAt: result.activatedAt });
85
- return { valid: true, key: cleanKey, source: 'polar', ...result };
86
- } catch (e) {
87
- // If network fails but we have a cached valid key — allow offline use
88
- if (cache?.key?.toUpperCase() === cleanKey && cache?.valid) {
89
- return { valid: true, key: cleanKey, source: 'cache-offline' };
90
- }
91
- // New key that's never been validated — can't verify offline
92
- if (e.code === 'ENOTFOUND' || e.code === 'ECONNREFUSED' || e.message?.includes('timeout')) {
93
- throw new Error(`Licence validation failed: network unavailable. Connect to the internet to activate.`);
94
- }
95
- throw new Error(`Licence validation failed: ${e.message}`);
96
- }
97
- }
98
-
99
- async function _polarValidate(key) {
100
- return new Promise((resolve, reject) => {
101
- const https = require('https');
102
- const body = JSON.stringify({ licenceKey: key });
103
- const opts = {
104
- hostname: 'api.polar.sh',
105
- path: '/v1/users/license-keys/validate',
106
- method: 'POST',
107
- headers: {
108
- 'Content-Type': 'application/json',
109
- 'Content-Length': Buffer.byteLength(body),
110
- 'User-Agent': 'vektor-slipstream/1.1.13',
111
- },
112
- };
113
-
114
- const req = https.request(opts, res => {
115
- let data = '';
116
- res.setEncoding('utf8');
117
- res.on('data', c => data += c);
118
- res.on('end', () => {
119
- try {
120
- const json = JSON.parse(data);
121
- if (res.statusCode === 200 && (json.valid || json.status === 'granted')) {
122
- resolve({ plan: json.benefit?.description || 'slipstream', activatedAt: json.activated_at });
123
- } else if (res.statusCode === 404 || json.valid === false) {
124
- reject(new Error(`Invalid licence key. Purchase at: ${PURCHASE_URL}`));
125
- } else {
126
- // Unknown response — treat as valid if 2xx
127
- if (res.statusCode >= 200 && res.statusCode < 300) {
128
- resolve({ plan: 'slipstream', activatedAt: new Date().toISOString() });
129
- } else {
130
- reject(new Error(`Polar API returned ${res.statusCode}`));
131
- }
132
- }
133
- } catch { reject(new Error('Invalid response from licence server')); }
134
- });
135
- });
136
-
137
- req.setTimeout(8000, () => { req.destroy(); reject(Object.assign(new Error('timeout'), { code: 'ECONNREFUSED' })); });
138
- req.on('error', reject);
139
- req.write(body);
140
- req.end();
141
- });
142
- }
143
-
144
- /**
145
- * Activate a new licence key interactively and cache it.
146
- */
147
- async function activateLicence(key) {
148
- const result = await validateLicence(key);
149
- writeCache(result.key, { valid: true, plan: result.plan, activatedAt: result.activatedAt });
150
- return result;
151
- }
152
-
153
- /**
154
- * Deactivate — clear the local cache.
155
- */
156
- function deactivateLicence() {
157
- try {
158
- if (fs.existsSync(CACHE_PATH)) fs.unlinkSync(CACHE_PATH);
159
- return true;
160
- } catch { return false; }
161
- }
162
-
163
- /**
164
- * Get licence status without throwing.
165
- */
166
- function getLicenceStatus() {
167
- const cache = readCache();
168
- const envKey = process.env.VEKTOR_LICENCE_KEY;
169
- if (!cache?.key && !envKey) return { status: 'none' };
170
- const key = cache?.key || envKey;
171
- const cachedAt = cache?.cachedAt ? new Date(cache.cachedAt) : null;
172
- const ageHours = cachedAt ? (Date.now() - cachedAt.getTime()) / 3600000 : null;
173
- return {
174
- status: cache?.valid ? 'active' : 'unknown',
175
- key: key ? key.slice(0, 8) + '...' : null,
176
- plan: cache?.plan || null,
177
- cachedAt: cache?.cachedAt || null,
178
- ageHours: ageHours ? Math.round(ageHours) : null,
179
- source: envKey ? 'env' : 'cache',
180
- };
181
- }
182
-
183
- module.exports = {
184
- validateLicence,
185
- activateLicence,
186
- deactivateLicence,
187
- getLicenceStatus,
188
- getCachedKey,
189
- readCache,
190
- CACHE_PATH,
191
- PURCHASE_URL,
192
- };