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.
- package/README.md +67 -306
- package/package.json +14 -146
- package/CHANGELOG.md +0 -139
- package/LICENSE +0 -33
- package/TENETS.md +0 -189
- package/audn-log.js +0 -143
- package/axon.js +0 -389
- package/boot-patch.js +0 -33
- package/boot-screen.html +0 -210
- package/briefing.js +0 -150
- package/cerebellum.js +0 -439
- package/cloak-behaviour.js +0 -596
- package/cloak-captcha.js +0 -541
- package/cloak-core.js +0 -499
- package/cloak-identity.js +0 -484
- package/cloak-index.js +0 -261
- package/cloak-llms.js +0 -163
- package/cloak-pattern-store.js +0 -471
- package/cloak-recorder-auto.js +0 -297
- package/cloak-recorder-snippet.js +0 -119
- package/cloak-turbo-quant.js +0 -357
- package/cloak-warmup.js +0 -240
- package/cortex.js +0 -221
- package/detect-hardware.js +0 -181
- package/entity-resolver.js +0 -298
- package/errors.js +0 -66
- package/examples/example-claude-mcp.js +0 -220
- package/examples/example-langchain-researcher.js +0 -82
- package/examples/example-openai-assistant.js +0 -84
- package/examples/examples-README.md +0 -161
- package/export-import.js +0 -221
- package/forget.js +0 -148
- package/inspect.js +0 -199
- package/mistral/README-mistral.md +0 -123
- package/mistral/mistral-bridge.js +0 -218
- package/mistral/mistral-setup.js +0 -220
- package/mistral/vektor-tool-manifest.json +0 -41
- package/models/model_quantized.onnx +0 -0
- package/models/vocab.json +0 -1
- package/namespace.js +0 -186
- package/pin.js +0 -91
- package/slipstream-core-extended.js +0 -134
- package/slipstream-core.js +0 -1
- package/slipstream-db.js +0 -140
- package/slipstream-embedder.js +0 -338
- package/sovereign.js +0 -142
- package/token.js +0 -322
- package/types/index.d.ts +0 -269
- package/vektor-banner-loader.js +0 -109
- package/vektor-cli.js +0 -259
- package/vektor-licence-prompt.js +0 -128
- package/vektor-licence.js +0 -192
- package/vektor-setup.js +0 -270
- package/vektor-slipstream.dxt +0 -0
- package/vektor-tui.js +0 -373
- package/visualize.js +0 -235
package/cloak-recorder-auto.js
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* cloak-recorder-auto.js — Silent automatic session recorder
|
|
5
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
-
* Injects a silent recorder into every Playwright session via addInitScript().
|
|
7
|
-
* Captures mouse/scroll events during the real task, then exports them back
|
|
8
|
-
* to Node.js via Playwright's exposeFunction() bridge.
|
|
9
|
-
*
|
|
10
|
-
* Outcome detection checks for:
|
|
11
|
-
* - reCAPTCHA block signals (HTTP 403, challenge page DOM markers)
|
|
12
|
-
* - Cloudflare challenge (cf-mitigated header, title "Just a moment")
|
|
13
|
-
* - DataDome block (ddg-blocked, dd-cookie absent)
|
|
14
|
-
* - Generic block signals (HTTP 429, 503)
|
|
15
|
-
* - Successful pass (200 + no block markers)
|
|
16
|
-
*
|
|
17
|
-
* Usage (inside any cloak session):
|
|
18
|
-
* const recorder = require('./cloak-recorder-auto');
|
|
19
|
-
* await recorder.attach(page, context, { site: 'example.com', category: 'login' });
|
|
20
|
-
* // ... do real work ...
|
|
21
|
-
* const result = await recorder.detach(page);
|
|
22
|
-
* // result: { events, outcome, patternId }
|
|
23
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
let _store = null;
|
|
27
|
-
function getStore() { if (!_store) _store = require('./cloak-pattern-store'); return _store; }
|
|
28
|
-
|
|
29
|
-
// In-page recorder script — injected via addInitScript
|
|
30
|
-
// Runs in the browser context, not Node.js
|
|
31
|
-
const RECORDER_SCRIPT = `
|
|
32
|
-
(function() {
|
|
33
|
-
if (window.__cloakAutoRecorder) return;
|
|
34
|
-
|
|
35
|
-
const T0 = Date.now();
|
|
36
|
-
const events = [];
|
|
37
|
-
let lastMoveT = 0;
|
|
38
|
-
|
|
39
|
-
function onMove(e) {
|
|
40
|
-
const now = Date.now();
|
|
41
|
-
if (now - lastMoveT < 50) return; // 20fps throttle
|
|
42
|
-
lastMoveT = now;
|
|
43
|
-
events.push({ t: now - T0, type: 'move', x: e.clientX, y: e.clientY });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function onWheel(e) {
|
|
47
|
-
events.push({ t: Date.now() - T0, type: 'scroll',
|
|
48
|
-
x: e.clientX, y: e.clientY, dy: Math.round(e.deltaY) });
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function onClick(e) {
|
|
52
|
-
events.push({ t: Date.now() - T0, type: 'click', x: e.clientX, y: e.clientY });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
document.addEventListener('mousemove', onMove, { passive: true, capture: true });
|
|
56
|
-
document.addEventListener('wheel', onWheel, { passive: true, capture: true });
|
|
57
|
-
document.addEventListener('click', onClick, { passive: true, capture: true });
|
|
58
|
-
|
|
59
|
-
window.__cloakAutoRecorder = {
|
|
60
|
-
getEvents: () => JSON.stringify({
|
|
61
|
-
events,
|
|
62
|
-
viewport: { w: window.innerWidth, h: window.innerHeight },
|
|
63
|
-
duration: Date.now() - T0,
|
|
64
|
-
url: window.location.href,
|
|
65
|
-
}),
|
|
66
|
-
stop: () => {
|
|
67
|
-
document.removeEventListener('mousemove', onMove, true);
|
|
68
|
-
document.removeEventListener('wheel', onWheel, true);
|
|
69
|
-
document.removeEventListener('click', onClick, true);
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
})();
|
|
73
|
-
`;
|
|
74
|
-
|
|
75
|
-
// Block signal DOM markers
|
|
76
|
-
const BLOCK_MARKERS = [
|
|
77
|
-
'#challenge-form', // Cloudflare challenge
|
|
78
|
-
'.cf-error-code', // Cloudflare error
|
|
79
|
-
'#ddg-block-page', // DataDome block
|
|
80
|
-
'.recaptcha-checkbox-border',// reCAPTCHA v2 visible
|
|
81
|
-
'#captcha-box', // generic captcha
|
|
82
|
-
'[class*="blocked"]', // generic block page
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
// Block signal title strings
|
|
86
|
-
const BLOCK_TITLES = [
|
|
87
|
-
'just a moment',
|
|
88
|
-
'access denied',
|
|
89
|
-
'blocked',
|
|
90
|
-
'forbidden',
|
|
91
|
-
'robot',
|
|
92
|
-
'captcha',
|
|
93
|
-
'security check',
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
// ── Outcome detection ─────────────────────────────────────────────────────────
|
|
97
|
-
|
|
98
|
-
async function detectOutcome(page, response = null) {
|
|
99
|
-
// Check HTTP status
|
|
100
|
-
if (response) {
|
|
101
|
-
const status = response.status();
|
|
102
|
-
if (status === 403 || status === 429 || status === 503) {
|
|
103
|
-
return { outcome: 'block', signal: `HTTP ${status}` };
|
|
104
|
-
}
|
|
105
|
-
// Cloudflare mitigation header
|
|
106
|
-
const cfMitigated = response.headers()['cf-mitigated'];
|
|
107
|
-
if (cfMitigated === 'challenge') {
|
|
108
|
-
return { outcome: 'block', signal: 'cf-mitigated: challenge' };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Check DOM for block markers
|
|
113
|
-
try {
|
|
114
|
-
for (const sel of BLOCK_MARKERS) {
|
|
115
|
-
const el = await page.$(sel).catch(() => null);
|
|
116
|
-
if (el) return { outcome: 'captcha', signal: `DOM: ${sel}` };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Check page title
|
|
120
|
-
const title = await page.title().catch(() => '');
|
|
121
|
-
const titleLower = title.toLowerCase();
|
|
122
|
-
for (const marker of BLOCK_TITLES) {
|
|
123
|
-
if (titleLower.includes(marker)) {
|
|
124
|
-
return { outcome: 'block', signal: `title: "${title}"` };
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Check for reCAPTCHA v3 score in page source
|
|
129
|
-
const content = await page.content().catch(() => '');
|
|
130
|
-
if (content.includes('grecaptcha') && content.includes('score')) {
|
|
131
|
-
// v3 present — can't read score directly, but presence is neutral
|
|
132
|
-
return { outcome: 'pass', signal: 'recaptcha-v3-present-no-block' };
|
|
133
|
-
}
|
|
134
|
-
} catch { /* page may have navigated */ }
|
|
135
|
-
|
|
136
|
-
return { outcome: 'pass', signal: 'no-block-signals' };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ── Session attachment ────────────────────────────────────────────────────────
|
|
140
|
-
|
|
141
|
-
const _sessions = new Map(); // page → session data
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Attach recorder to a Playwright page.
|
|
145
|
-
* Call after page.goto() — records all input from this point.
|
|
146
|
-
*
|
|
147
|
-
* @param {import('playwright').Page} page
|
|
148
|
-
* @param {object} opts
|
|
149
|
-
* @param {string} opts.site — target hostname (for logging)
|
|
150
|
-
* @param {string} opts.category — behaviour category hint
|
|
151
|
-
* @param {string} opts.patternId — if replaying a known pattern, pass its ID for scoring
|
|
152
|
-
*/
|
|
153
|
-
async function attach(page, opts = {}) {
|
|
154
|
-
// Inject recorder script
|
|
155
|
-
await page.addInitScript(RECORDER_SCRIPT).catch(() => {});
|
|
156
|
-
|
|
157
|
-
// Also evaluate immediately (for pages already loaded)
|
|
158
|
-
await page.evaluate(RECORDER_SCRIPT).catch(() => {});
|
|
159
|
-
|
|
160
|
-
_sessions.set(page, {
|
|
161
|
-
site: opts.site || '',
|
|
162
|
-
category: opts.category || 'custom',
|
|
163
|
-
patternId: opts.patternId || null,
|
|
164
|
-
attachedAt: Date.now(),
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Detach recorder, capture events, detect outcome, save to pattern store.
|
|
170
|
-
*
|
|
171
|
-
* @param {import('playwright').Page} page
|
|
172
|
-
* @param {object} opts
|
|
173
|
-
* @param {import('playwright').Response} opts.lastResponse — last navigation response for HTTP status check
|
|
174
|
-
* @param {boolean} opts.save — save to pattern store (default true)
|
|
175
|
-
* @returns {{ events, outcome, signal, patternId, saved }}
|
|
176
|
-
*/
|
|
177
|
-
async function detach(page, opts = {}) {
|
|
178
|
-
const session = _sessions.get(page) || {};
|
|
179
|
-
_sessions.delete(page);
|
|
180
|
-
|
|
181
|
-
// Stop recorder and grab events from page
|
|
182
|
-
let recorded = null;
|
|
183
|
-
try {
|
|
184
|
-
const raw = await page.evaluate(() => {
|
|
185
|
-
if (!window.__cloakAutoRecorder) return null;
|
|
186
|
-
window.__cloakAutoRecorder.stop();
|
|
187
|
-
return window.__cloakAutoRecorder.getEvents();
|
|
188
|
-
});
|
|
189
|
-
if (raw) recorded = JSON.parse(raw);
|
|
190
|
-
} catch { /* page may have closed */ }
|
|
191
|
-
|
|
192
|
-
// Detect outcome
|
|
193
|
-
const { outcome, signal } = await detectOutcome(page, opts.lastResponse);
|
|
194
|
-
|
|
195
|
-
// Score existing pattern if we were replaying one
|
|
196
|
-
if (session.patternId) {
|
|
197
|
-
getStore().recordOutcome(session.patternId, outcome, session.site);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Save new pattern from recording (only on pass + enough events)
|
|
201
|
-
let savedId = null;
|
|
202
|
-
if (opts.save !== false && outcome === 'pass' && recorded?.events?.length >= 10) {
|
|
203
|
-
// Normalise pixel coords to 0-1
|
|
204
|
-
const vp = recorded.viewport || { w: 1440, h: 900 };
|
|
205
|
-
const events = recorded.events.map(e => ({
|
|
206
|
-
dt: e.t,
|
|
207
|
-
type: e.type,
|
|
208
|
-
x: e.x !== undefined ? +(e.x / vp.w).toFixed(4) : undefined,
|
|
209
|
-
y: e.y !== undefined ? +(e.y / vp.h).toFixed(4) : undefined,
|
|
210
|
-
dy: e.dy,
|
|
211
|
-
}));
|
|
212
|
-
|
|
213
|
-
// Convert absolute timestamps to dt deltas
|
|
214
|
-
let lastT = 0;
|
|
215
|
-
const normalised = events.map(e => {
|
|
216
|
-
const dt = e.dt - lastT;
|
|
217
|
-
lastT = e.dt;
|
|
218
|
-
return { ...e, dt };
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const result = getStore().addPattern({
|
|
222
|
-
name: `auto-${session.site}-${Date.now()}`,
|
|
223
|
-
category: session.category,
|
|
224
|
-
description: `Auto-recorded on ${session.site} — ${new Date().toISOString()}`,
|
|
225
|
-
events: normalised,
|
|
226
|
-
viewport: vp,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
if (result && !result.duplicate) {
|
|
230
|
-
savedId = result.id;
|
|
231
|
-
// Give it an immediate win since it just passed
|
|
232
|
-
getStore().recordOutcome(result.id, 'pass', session.site);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
outcome,
|
|
238
|
-
signal,
|
|
239
|
-
events: recorded?.events?.length || 0,
|
|
240
|
-
patternId: session.patternId,
|
|
241
|
-
savedId,
|
|
242
|
-
site: session.site,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// ── Integration helpers ───────────────────────────────────────────────────────
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Full auto-session: pick pattern from store, attach recorder, run callback,
|
|
250
|
-
* detach and score. The callback receives (page, pattern) and does the real work.
|
|
251
|
-
*
|
|
252
|
-
* @param {import('playwright').Page} page
|
|
253
|
-
* @param {import('playwright').Context} context
|
|
254
|
-
* @param {Function} taskFn — async (page) => void
|
|
255
|
-
* @param {object} opts — { site, category }
|
|
256
|
-
*/
|
|
257
|
-
async function autoSession(page, context, taskFn, opts = {}) {
|
|
258
|
-
const { injectBehaviour, replayPattern, syntheticBrowse } = require('./cloak-behaviour');
|
|
259
|
-
|
|
260
|
-
// Ensure builtins are seeded
|
|
261
|
-
getStore().seedBuiltins();
|
|
262
|
-
|
|
263
|
-
// Pick a pattern weighted by tier
|
|
264
|
-
const pattern = getStore().pickPattern(opts.category);
|
|
265
|
-
|
|
266
|
-
// Attach recorder
|
|
267
|
-
await attach(page, {
|
|
268
|
-
site: opts.site || '',
|
|
269
|
-
category: opts.category || 'reading',
|
|
270
|
-
patternId: pattern?.id || null,
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Replay the picked pattern (or synthetic if store empty)
|
|
274
|
-
let behaviourResult;
|
|
275
|
-
if (pattern) {
|
|
276
|
-
behaviourResult = await replayPattern(page, pattern.name, { skipClicks: true })
|
|
277
|
-
.catch(() => syntheticBrowse(page, { durationMs: 4000 }));
|
|
278
|
-
} else {
|
|
279
|
-
behaviourResult = await syntheticBrowse(page, { durationMs: 4000 });
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Run the actual task
|
|
283
|
-
let lastResponse = null;
|
|
284
|
-
page.on('response', r => { lastResponse = r; });
|
|
285
|
-
await taskFn(page);
|
|
286
|
-
|
|
287
|
-
// Detach, score, save
|
|
288
|
-
const result = await detach(page, { lastResponse, save: true });
|
|
289
|
-
|
|
290
|
-
return {
|
|
291
|
-
...result,
|
|
292
|
-
pattern: pattern ? { id: pattern.id, name: pattern.name, tier: pattern.tier } : null,
|
|
293
|
-
behaviour: behaviourResult,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
module.exports = { attach, detach, autoSession, detectOutcome };
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cloak-recorder-snippet.js
|
|
3
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
-
* Paste this into Chrome DevTools console on any page to record a real human
|
|
5
|
-
* browsing session. When done, call stopRecording() to get the JSON output.
|
|
6
|
-
*
|
|
7
|
-
* Recorded JSON can be loaded into cloak-behaviour.js with:
|
|
8
|
-
* const { loadCustomPattern } = require('./cloak-behaviour');
|
|
9
|
-
* loadCustomPattern('my-pattern', recordedJSON);
|
|
10
|
-
*
|
|
11
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
-
* HOW TO USE:
|
|
13
|
-
* 1. Open Chrome DevTools (F12)
|
|
14
|
-
* 2. Paste this entire file into the Console tab
|
|
15
|
-
* 3. Press Enter — recording starts immediately
|
|
16
|
-
* 4. Browse naturally for 10–30 seconds
|
|
17
|
-
* 5. Type: stopRecording()
|
|
18
|
-
* 6. The JSON is copied to clipboard AND printed to console
|
|
19
|
-
* 7. Save to a .json file and load with loadCustomPattern()
|
|
20
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
(function() {
|
|
24
|
-
if (window.__cloakRecorder) {
|
|
25
|
-
console.warn('[cloak-recorder] Already running. Call stopRecording() first.');
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const SESSION_START = Date.now();
|
|
30
|
-
const VIEWPORT = { w: window.innerWidth, h: window.innerHeight };
|
|
31
|
-
const events = [];
|
|
32
|
-
|
|
33
|
-
// Throttle mousemove — record at most every 50ms (20fps) to keep size manageable
|
|
34
|
-
let lastMoveTime = 0;
|
|
35
|
-
|
|
36
|
-
function onMouseMove(e) {
|
|
37
|
-
const now = Date.now();
|
|
38
|
-
if (now - lastMoveTime < 50) return;
|
|
39
|
-
lastMoveTime = now;
|
|
40
|
-
events.push({
|
|
41
|
-
t: now - SESSION_START,
|
|
42
|
-
type: 'move',
|
|
43
|
-
x: e.clientX,
|
|
44
|
-
y: e.clientY,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function onWheel(e) {
|
|
49
|
-
events.push({
|
|
50
|
-
t: Date.now() - SESSION_START,
|
|
51
|
-
type: 'scroll',
|
|
52
|
-
x: e.clientX,
|
|
53
|
-
y: e.clientY,
|
|
54
|
-
dy: Math.round(e.deltaY),
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function onClick(e) {
|
|
59
|
-
events.push({
|
|
60
|
-
t: Date.now() - SESSION_START,
|
|
61
|
-
type: 'click',
|
|
62
|
-
x: e.clientX,
|
|
63
|
-
y: e.clientY,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Register listeners
|
|
68
|
-
document.addEventListener('mousemove', onMouseMove, { passive: true });
|
|
69
|
-
document.addEventListener('wheel', onWheel, { passive: true });
|
|
70
|
-
document.addEventListener('click', onClick, { passive: true });
|
|
71
|
-
|
|
72
|
-
window.__cloakRecorder = { onMouseMove, onWheel, onClick, events, start: SESSION_START };
|
|
73
|
-
|
|
74
|
-
console.log('%c[cloak-recorder] Recording started', 'color: #4ade80; font-weight: bold');
|
|
75
|
-
console.log('%cBrowse naturally, then call: stopRecording()', 'color: #94a3b8');
|
|
76
|
-
|
|
77
|
-
window.stopRecording = function(name, category) {
|
|
78
|
-
// Remove listeners
|
|
79
|
-
document.removeEventListener('mousemove', window.__cloakRecorder.onMouseMove);
|
|
80
|
-
document.removeEventListener('wheel', window.__cloakRecorder.onWheel);
|
|
81
|
-
document.removeEventListener('click', window.__cloakRecorder.onClick);
|
|
82
|
-
|
|
83
|
-
const totalMs = Date.now() - SESSION_START;
|
|
84
|
-
const output = {
|
|
85
|
-
name: name || 'recorded-' + Date.now(),
|
|
86
|
-
category: category || 'custom',
|
|
87
|
-
description: 'Recorded on ' + window.location.hostname + ' — ' + new Date().toISOString(),
|
|
88
|
-
viewport: VIEWPORT,
|
|
89
|
-
durationMs: totalMs,
|
|
90
|
-
events: window.__cloakRecorder.events,
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const json = JSON.stringify(output, null, 2);
|
|
94
|
-
|
|
95
|
-
// Copy to clipboard
|
|
96
|
-
try {
|
|
97
|
-
navigator.clipboard.writeText(json).then(() => {
|
|
98
|
-
console.log('%c[cloak-recorder] JSON copied to clipboard!', 'color: #4ade80; font-weight: bold');
|
|
99
|
-
}).catch(() => {
|
|
100
|
-
console.log('%c[cloak-recorder] Clipboard failed — see console output below', 'color: #f97316');
|
|
101
|
-
});
|
|
102
|
-
} catch (_) {}
|
|
103
|
-
|
|
104
|
-
console.log('%c[cloak-recorder] Recording complete', 'color: #4ade80; font-weight: bold');
|
|
105
|
-
console.log(` Events: ${output.events.length} | Duration: ${(totalMs/1000).toFixed(1)}s | Viewport: ${VIEWPORT.w}×${VIEWPORT.h}`);
|
|
106
|
-
console.log('');
|
|
107
|
-
console.log('%cTo load in Node.js:', 'color: #94a3b8');
|
|
108
|
-
console.log("%c const { loadCustomPattern } = require('./cloak-behaviour');", 'color: #64748b');
|
|
109
|
-
console.log("%c loadCustomPattern('" + (name || 'my-pattern') + "', recordedJSON);", 'color: #64748b');
|
|
110
|
-
console.log('');
|
|
111
|
-
console.log('JSON output:');
|
|
112
|
-
console.log(json);
|
|
113
|
-
|
|
114
|
-
delete window.__cloakRecorder;
|
|
115
|
-
delete window.stopRecording;
|
|
116
|
-
|
|
117
|
-
return output;
|
|
118
|
-
};
|
|
119
|
-
})();
|