imperium-crawl 1.5.2 → 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 (81) hide show
  1. package/README.md +370 -257
  2. package/dist/constants.d.ts +2 -1
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +3 -1
  5. package/dist/constants.js.map +1 -1
  6. package/dist/network/interceptor.d.ts +19 -0
  7. package/dist/network/interceptor.d.ts.map +1 -0
  8. package/dist/network/interceptor.js +82 -0
  9. package/dist/network/interceptor.js.map +1 -0
  10. package/dist/network/types.d.ts +27 -0
  11. package/dist/network/types.d.ts.map +1 -0
  12. package/dist/network/types.js +2 -0
  13. package/dist/network/types.js.map +1 -0
  14. package/dist/security/action-policy.d.ts +26 -0
  15. package/dist/security/action-policy.d.ts.map +1 -0
  16. package/dist/security/action-policy.js +136 -0
  17. package/dist/security/action-policy.js.map +1 -0
  18. package/dist/security/auth-vault.d.ts +49 -0
  19. package/dist/security/auth-vault.d.ts.map +1 -0
  20. package/dist/security/auth-vault.js +133 -0
  21. package/dist/security/auth-vault.js.map +1 -0
  22. package/dist/security/domain-filter.d.ts +19 -0
  23. package/dist/security/domain-filter.d.ts.map +1 -0
  24. package/dist/security/domain-filter.js +114 -0
  25. package/dist/security/domain-filter.js.map +1 -0
  26. package/dist/security/types.d.ts +19 -0
  27. package/dist/security/types.d.ts.map +1 -0
  28. package/dist/security/types.js +2 -0
  29. package/dist/security/types.js.map +1 -0
  30. package/dist/sessions/encryption.d.ts +37 -0
  31. package/dist/sessions/encryption.d.ts.map +1 -0
  32. package/dist/sessions/encryption.js +108 -0
  33. package/dist/sessions/encryption.js.map +1 -0
  34. package/dist/sessions/index.d.ts +1 -0
  35. package/dist/sessions/index.d.ts.map +1 -1
  36. package/dist/sessions/index.js +1 -0
  37. package/dist/sessions/index.js.map +1 -1
  38. package/dist/sessions/manager.d.ts +3 -0
  39. package/dist/sessions/manager.d.ts.map +1 -1
  40. package/dist/sessions/manager.js +28 -2
  41. package/dist/sessions/manager.js.map +1 -1
  42. package/dist/snapshot/annotator.d.ts +21 -0
  43. package/dist/snapshot/annotator.d.ts.map +1 -0
  44. package/dist/snapshot/annotator.js +152 -0
  45. package/dist/snapshot/annotator.js.map +1 -0
  46. package/dist/snapshot/boundary.d.ts +7 -0
  47. package/dist/snapshot/boundary.d.ts.map +1 -0
  48. package/dist/snapshot/boundary.js +12 -0
  49. package/dist/snapshot/boundary.js.map +1 -0
  50. package/dist/snapshot/differ.d.ts +40 -0
  51. package/dist/snapshot/differ.d.ts.map +1 -0
  52. package/dist/snapshot/differ.js +194 -0
  53. package/dist/snapshot/differ.js.map +1 -0
  54. package/dist/snapshot/extractor.d.ts +27 -0
  55. package/dist/snapshot/extractor.d.ts.map +1 -0
  56. package/dist/snapshot/extractor.js +265 -0
  57. package/dist/snapshot/extractor.js.map +1 -0
  58. package/dist/snapshot/index.d.ts +8 -0
  59. package/dist/snapshot/index.d.ts.map +1 -0
  60. package/dist/snapshot/index.js +6 -0
  61. package/dist/snapshot/index.js.map +1 -0
  62. package/dist/snapshot/store.d.ts +28 -0
  63. package/dist/snapshot/store.d.ts.map +1 -0
  64. package/dist/snapshot/store.js +65 -0
  65. package/dist/snapshot/store.js.map +1 -0
  66. package/dist/snapshot/types.d.ts +42 -0
  67. package/dist/snapshot/types.d.ts.map +1 -0
  68. package/dist/snapshot/types.js +2 -0
  69. package/dist/snapshot/types.js.map +1 -0
  70. package/dist/tools/index.d.ts.map +1 -1
  71. package/dist/tools/index.js +2 -0
  72. package/dist/tools/index.js.map +1 -1
  73. package/dist/tools/interact.d.ts +194 -5
  74. package/dist/tools/interact.d.ts.map +1 -1
  75. package/dist/tools/interact.js +355 -20
  76. package/dist/tools/interact.js.map +1 -1
  77. package/dist/tools/snapshot.d.ts +53 -0
  78. package/dist/tools/snapshot.d.ts.map +1 -0
  79. package/dist/tools/snapshot.js +160 -0
  80. package/dist/tools/snapshot.js.map +1 -0
  81. package/package.json +1 -1
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Domain Filter — Security sandbox for browser actions.
3
+ *
4
+ * Blocks requests to non-allowed domains via page.route().
5
+ * Also patches WebSocket/EventSource/sendBeacon to prevent data exfiltration.
6
+ */
7
+ /**
8
+ * Check if a hostname matches any of the allowed patterns.
9
+ * Supports exact match and wildcard (*.example.com).
10
+ */
11
+ export function isDomainAllowed(hostname, patterns) {
12
+ const lower = hostname.toLowerCase();
13
+ for (const pattern of patterns) {
14
+ const p = pattern.toLowerCase();
15
+ if (p === lower)
16
+ return true;
17
+ if (p.startsWith("*.")) {
18
+ const suffix = p.slice(2);
19
+ if (lower === suffix || lower.endsWith(`.${suffix}`))
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+ /**
26
+ * Install domain filter on a browser context.
27
+ * Blocks all requests to non-allowed domains + patches WebSocket/EventSource.
28
+ */
29
+ export async function installDomainFilter(context, allowedDomains) {
30
+ if (!allowedDomains.length)
31
+ return;
32
+ // Block HTTP requests to non-allowed domains
33
+ await context.route("**/*", (route) => {
34
+ try {
35
+ const url = new URL(route.request().url());
36
+ if (isDomainAllowed(url.hostname, allowedDomains)) {
37
+ route.continue();
38
+ }
39
+ else {
40
+ route.abort("blockedbyclient");
41
+ }
42
+ }
43
+ catch {
44
+ // Invalid URL — allow (probably internal)
45
+ route.continue();
46
+ }
47
+ });
48
+ // Patch WebSocket/EventSource/sendBeacon in page context
49
+ const initScript = `
50
+ (function() {
51
+ const allowedDomains = ${JSON.stringify(allowedDomains)};
52
+
53
+ function isDomainAllowed(hostname) {
54
+ const lower = hostname.toLowerCase();
55
+ for (const pattern of allowedDomains) {
56
+ const p = pattern.toLowerCase();
57
+ if (p === lower) return true;
58
+ if (p.startsWith('*.')) {
59
+ const suffix = p.slice(2);
60
+ if (lower === suffix || lower.endsWith('.' + suffix)) return true;
61
+ }
62
+ }
63
+ return false;
64
+ }
65
+
66
+ function checkUrl(urlStr) {
67
+ try {
68
+ const url = new URL(urlStr, window.location.href);
69
+ return isDomainAllowed(url.hostname);
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+
75
+ // Patch WebSocket
76
+ const OrigWebSocket = window.WebSocket;
77
+ window.WebSocket = function(url, protocols) {
78
+ if (!checkUrl(url)) {
79
+ console.warn('[imperium-crawl] Blocked WebSocket to:', url);
80
+ throw new DOMException('Blocked by domain filter', 'SecurityError');
81
+ }
82
+ return new OrigWebSocket(url, protocols);
83
+ };
84
+ window.WebSocket.prototype = OrigWebSocket.prototype;
85
+
86
+ // Patch EventSource
87
+ const OrigEventSource = window.EventSource;
88
+ if (OrigEventSource) {
89
+ window.EventSource = function(url, opts) {
90
+ if (!checkUrl(url)) {
91
+ console.warn('[imperium-crawl] Blocked EventSource to:', url);
92
+ throw new DOMException('Blocked by domain filter', 'SecurityError');
93
+ }
94
+ return new OrigEventSource(url, opts);
95
+ };
96
+ window.EventSource.prototype = OrigEventSource.prototype;
97
+ }
98
+
99
+ // Patch sendBeacon
100
+ const origSendBeacon = navigator.sendBeacon?.bind(navigator);
101
+ if (origSendBeacon) {
102
+ navigator.sendBeacon = function(url, data) {
103
+ if (!checkUrl(url)) {
104
+ console.warn('[imperium-crawl] Blocked sendBeacon to:', url);
105
+ return false;
106
+ }
107
+ return origSendBeacon(url, data);
108
+ };
109
+ }
110
+ })();
111
+ `;
112
+ await context.addInitScript(initScript);
113
+ }
114
+ //# sourceMappingURL=domain-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-filter.js","sourceRoot":"","sources":["../../src/security/domain-filter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,QAAkB;IAClE,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAErC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;QAC7B,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;QACpE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAuB,EACvB,cAAwB;IAExB,IAAI,CAAC,cAAc,CAAC,MAAM;QAAE,OAAO;IAEnC,6CAA6C;IAC7C,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;QACpC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;YAC3C,IAAI,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC;gBAClD,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;YAC1C,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,yDAAyD;IACzD,MAAM,UAAU,GAAG;;+BAEU,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4D1D,CAAC;IAEF,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,19 @@
1
+ /** Action policy decision */
2
+ export type PolicyDecision = "allow" | "deny" | "confirm";
3
+ /** Action policy configuration */
4
+ export interface ActionPolicyConfig {
5
+ /** Default decision for uncategorized actions */
6
+ default: "allow" | "deny";
7
+ /** Allowed categories override default */
8
+ allow?: string[];
9
+ /** Denied categories override default and allow */
10
+ deny?: string[];
11
+ /** Categories requiring explicit confirmation */
12
+ confirm?: string[];
13
+ }
14
+ /** Domain filter patterns */
15
+ export interface DomainFilterConfig {
16
+ /** Allowed domain patterns (exact or wildcard *.example.com) */
17
+ allowed_domains: string[];
18
+ }
19
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/security/types.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1D,kCAAkC;AAClC,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,6BAA6B;AAC7B,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/security/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Session Encryption — AES-256-GCM with scrypt key derivation.
3
+ *
4
+ * Auto-encrypts session files when SESSION_ENCRYPTION_KEY env var is set.
5
+ * Uses Node.js crypto module only, zero deps.
6
+ */
7
+ export interface EncryptedPayload {
8
+ version: 1;
9
+ algorithm: "aes-256-gcm";
10
+ iv: string;
11
+ authTag: string;
12
+ ciphertext: string;
13
+ salt: string;
14
+ }
15
+ /**
16
+ * Encrypt plaintext with AES-256-GCM.
17
+ */
18
+ export declare function encryptData(plaintext: string, userKey: string): EncryptedPayload;
19
+ /**
20
+ * Decrypt an encrypted payload.
21
+ */
22
+ export declare function decryptData(payload: EncryptedPayload, userKey: string): string;
23
+ /**
24
+ * Check if an object looks like an encrypted payload.
25
+ */
26
+ export declare function isEncryptedPayload(obj: unknown): obj is EncryptedPayload;
27
+ /**
28
+ * Get encryption key from env var or auto-generated key file.
29
+ * Returns undefined if no key is configured and no key file exists.
30
+ */
31
+ export declare function ensureEncryptionKey(): Promise<string | undefined>;
32
+ /**
33
+ * Generate a new encryption key and save to file.
34
+ * Only call this explicitly (e.g. from setup wizard).
35
+ */
36
+ export declare function generateEncryptionKey(): Promise<string>;
37
+ //# sourceMappingURL=encryption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/sessions/encryption.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,aAAa,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AASD;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAoBhF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAc9E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,gBAAgB,CAWxE;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAcvE;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS7D"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Session Encryption — AES-256-GCM with scrypt key derivation.
3
+ *
4
+ * Auto-encrypts session files when SESSION_ENCRYPTION_KEY env var is set.
5
+ * Uses Node.js crypto module only, zero deps.
6
+ */
7
+ import { randomBytes, createCipheriv, createDecipheriv, scryptSync, } from "node:crypto";
8
+ import fs from "node:fs/promises";
9
+ import path from "node:path";
10
+ import os from "node:os";
11
+ import { SKILLS_DIR_NAME } from "../constants.js";
12
+ const ALGORITHM = "aes-256-gcm";
13
+ const KEY_LENGTH = 32;
14
+ const IV_LENGTH = 12;
15
+ const SALT_LENGTH = 16;
16
+ const AUTH_TAG_LENGTH = 16;
17
+ const ENCRYPTION_KEY_FILENAME = "encryption.key";
18
+ /**
19
+ * Derive a 256-bit key from user password using scrypt.
20
+ */
21
+ function deriveKey(userKey, salt) {
22
+ return scryptSync(userKey, salt, KEY_LENGTH);
23
+ }
24
+ /**
25
+ * Encrypt plaintext with AES-256-GCM.
26
+ */
27
+ export function encryptData(plaintext, userKey) {
28
+ const salt = randomBytes(SALT_LENGTH);
29
+ const key = deriveKey(userKey, salt);
30
+ const iv = randomBytes(IV_LENGTH);
31
+ const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
32
+ const encrypted = Buffer.concat([
33
+ cipher.update(plaintext, "utf-8"),
34
+ cipher.final(),
35
+ ]);
36
+ const authTag = cipher.getAuthTag();
37
+ return {
38
+ version: 1,
39
+ algorithm: ALGORITHM,
40
+ iv: iv.toString("hex"),
41
+ authTag: authTag.toString("hex"),
42
+ ciphertext: encrypted.toString("hex"),
43
+ salt: salt.toString("hex"),
44
+ };
45
+ }
46
+ /**
47
+ * Decrypt an encrypted payload.
48
+ */
49
+ export function decryptData(payload, userKey) {
50
+ const salt = Buffer.from(payload.salt, "hex");
51
+ const key = deriveKey(userKey, salt);
52
+ const iv = Buffer.from(payload.iv, "hex");
53
+ const authTag = Buffer.from(payload.authTag, "hex");
54
+ const ciphertext = Buffer.from(payload.ciphertext, "hex");
55
+ const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
56
+ decipher.setAuthTag(authTag);
57
+ return Buffer.concat([
58
+ decipher.update(ciphertext),
59
+ decipher.final(),
60
+ ]).toString("utf-8");
61
+ }
62
+ /**
63
+ * Check if an object looks like an encrypted payload.
64
+ */
65
+ export function isEncryptedPayload(obj) {
66
+ if (!obj || typeof obj !== "object")
67
+ return false;
68
+ const o = obj;
69
+ return (o.version === 1 &&
70
+ o.algorithm === ALGORITHM &&
71
+ typeof o.iv === "string" &&
72
+ typeof o.authTag === "string" &&
73
+ typeof o.ciphertext === "string" &&
74
+ typeof o.salt === "string");
75
+ }
76
+ /**
77
+ * Get encryption key from env var or auto-generated key file.
78
+ * Returns undefined if no key is configured and no key file exists.
79
+ */
80
+ export async function ensureEncryptionKey() {
81
+ // Env var takes priority
82
+ const envKey = process.env.SESSION_ENCRYPTION_KEY?.trim();
83
+ if (envKey)
84
+ return envKey;
85
+ // Check for existing key file
86
+ const keyPath = path.join(os.homedir(), SKILLS_DIR_NAME, ENCRYPTION_KEY_FILENAME);
87
+ try {
88
+ const key = await fs.readFile(keyPath, "utf-8");
89
+ return key.trim();
90
+ }
91
+ catch {
92
+ // No key file — return undefined (encryption not configured)
93
+ return undefined;
94
+ }
95
+ }
96
+ /**
97
+ * Generate a new encryption key and save to file.
98
+ * Only call this explicitly (e.g. from setup wizard).
99
+ */
100
+ export async function generateEncryptionKey() {
101
+ const key = randomBytes(32).toString("hex");
102
+ const dir = path.join(os.homedir(), SKILLS_DIR_NAME);
103
+ const keyPath = path.join(dir, ENCRYPTION_KEY_FILENAME);
104
+ await fs.mkdir(dir, { recursive: true });
105
+ await fs.writeFile(keyPath, key, { encoding: "utf-8", mode: 0o600 });
106
+ return key;
107
+ }
108
+ //# sourceMappingURL=encryption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/sessions/encryption.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  export type { StoredSession, StoredCookie } from "./types.js";
2
2
  export { SessionManager, getSessionManager, resetSessionManager } from "./manager.js";
3
+ export { encryptData, decryptData, isEncryptedPayload, ensureEncryptionKey, generateEncryptionKey, } from "./encryption.js";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sessions/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sessions/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EACL,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export { SessionManager, getSessionManager, resetSessionManager } from "./manager.js";
2
+ export { encryptData, decryptData, isEncryptedPayload, ensureEncryptionKey, generateEncryptionKey, } from "./encryption.js";
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sessions/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sessions/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EACL,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC"}
@@ -2,7 +2,10 @@ import type { StoredSession, StoredCookie } from "./types.js";
2
2
  export declare class SessionManager {
3
3
  private cache;
4
4
  private dir;
5
+ private encryptionKey;
6
+ private keyLoaded;
5
7
  constructor(dir?: string);
8
+ private getEncryptionKey;
6
9
  private sessionPath;
7
10
  save(id: string, cookies: StoredCookie[], url: string): Promise<void>;
8
11
  load(id: string): Promise<StoredSession | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/sessions/manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,GAAG,CAAS;gBAER,GAAG,CAAC,EAAE,MAAM;IAIxB,OAAO,CAAC,WAAW;IAMb,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBrE,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAwB/C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAUhC;AAMD,wBAAgB,iBAAiB,IAAI,cAAc,CAKlD;AAED,oCAAoC;AACpC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/sessions/manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG9D,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,SAAS,CAAS;gBAEd,GAAG,CAAC,EAAE,MAAM;YAIV,gBAAgB;IAQ9B,OAAO,CAAC,WAAW;IAMb,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BrE,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAqC/C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAUhC;AAMD,wBAAgB,iBAAiB,IAAI,cAAc,CAKlD;AAED,oCAAoC;AACpC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
@@ -1,12 +1,22 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { getSessionsDir } from "../config.js";
4
+ import { encryptData, decryptData, isEncryptedPayload, ensureEncryptionKey } from "./encryption.js";
4
5
  export class SessionManager {
5
6
  cache = new Map();
6
7
  dir;
8
+ encryptionKey;
9
+ keyLoaded = false;
7
10
  constructor(dir) {
8
11
  this.dir = dir ?? getSessionsDir();
9
12
  }
13
+ async getEncryptionKey() {
14
+ if (!this.keyLoaded) {
15
+ this.encryptionKey = await ensureEncryptionKey();
16
+ this.keyLoaded = true;
17
+ }
18
+ return this.encryptionKey;
19
+ }
10
20
  sessionPath(id) {
11
21
  // Sanitize id to prevent path traversal
12
22
  const safe = id.replace(/[^a-zA-Z0-9_\-]/g, "_");
@@ -26,7 +36,11 @@ export class SessionManager {
26
36
  await fs.mkdir(this.dir, { recursive: true });
27
37
  const filePath = this.sessionPath(id);
28
38
  const tmpPath = filePath + ".tmp";
29
- await fs.writeFile(tmpPath, JSON.stringify(session, null, 2), "utf-8");
39
+ const key = await this.getEncryptionKey();
40
+ const content = key
41
+ ? JSON.stringify(encryptData(JSON.stringify(session), key), null, 2)
42
+ : JSON.stringify(session, null, 2);
43
+ await fs.writeFile(tmpPath, content, "utf-8");
30
44
  await fs.rename(tmpPath, filePath);
31
45
  }
32
46
  async load(id) {
@@ -34,7 +48,19 @@ export class SessionManager {
34
48
  return this.cache.get(id);
35
49
  try {
36
50
  const data = await fs.readFile(this.sessionPath(id), "utf-8");
37
- const session = JSON.parse(data);
51
+ const parsed = JSON.parse(data);
52
+ let session;
53
+ if (isEncryptedPayload(parsed)) {
54
+ const key = await this.getEncryptionKey();
55
+ if (!key) {
56
+ console.error("[sessions] Encrypted session found but no encryption key configured");
57
+ return null;
58
+ }
59
+ session = JSON.parse(decryptData(parsed, key));
60
+ }
61
+ else {
62
+ session = parsed;
63
+ }
38
64
  this.cache.set(id, session);
39
65
  return session;
40
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/sessions/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/sessions/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Annotated Screenshots — Inject visual overlay badges on interactive elements.
3
+ *
4
+ * Zero external deps — uses DOM injection via page.evaluate(),
5
+ * takes screenshot, then cleans up injected elements.
6
+ *
7
+ * Color coding:
8
+ * - Red (#e53e3e) = button, switch
9
+ * - Blue (#3182ce) = link, tab
10
+ * - Green (#38a169) = textbox, searchbox, combobox, listbox
11
+ * - Orange (#dd6b20) = other interactive (checkbox, radio, slider, etc.)
12
+ */
13
+ import type { RefMap } from "./types.js";
14
+ type Page = import("rebrowser-playwright").Page;
15
+ /**
16
+ * Inject annotation overlays on interactive elements, take screenshot, cleanup.
17
+ * Returns PNG buffer.
18
+ */
19
+ export declare function annotateScreenshot(page: Page, refs: RefMap): Promise<Buffer>;
20
+ export {};
21
+ //# sourceMappingURL=annotator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotator.d.ts","sourceRoot":"","sources":["../../src/snapshot/annotator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,KAAK,IAAI,GAAG,OAAO,sBAAsB,EAAE,IAAI,CAAC;AAiBhD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoIlF"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Annotated Screenshots — Inject visual overlay badges on interactive elements.
3
+ *
4
+ * Zero external deps — uses DOM injection via page.evaluate(),
5
+ * takes screenshot, then cleans up injected elements.
6
+ *
7
+ * Color coding:
8
+ * - Red (#e53e3e) = button, switch
9
+ * - Blue (#3182ce) = link, tab
10
+ * - Green (#38a169) = textbox, searchbox, combobox, listbox
11
+ * - Orange (#dd6b20) = other interactive (checkbox, radio, slider, etc.)
12
+ */
13
+ const ROLE_COLORS = {
14
+ button: "#e53e3e",
15
+ switch: "#e53e3e",
16
+ link: "#3182ce",
17
+ tab: "#3182ce",
18
+ textbox: "#38a169",
19
+ searchbox: "#38a169",
20
+ combobox: "#38a169",
21
+ listbox: "#38a169",
22
+ };
23
+ const DEFAULT_COLOR = "#dd6b20";
24
+ const ANNOTATION_ATTR = "data-imperium-annotation";
25
+ /**
26
+ * Inject annotation overlays on interactive elements, take screenshot, cleanup.
27
+ * Returns PNG buffer.
28
+ */
29
+ export async function annotateScreenshot(page, refs) {
30
+ // Build annotation data for injection
31
+ const annotations = Object.entries(refs).map(([ref, entry]) => ({
32
+ ref,
33
+ role: entry.role,
34
+ name: entry.name,
35
+ nth: entry.nth,
36
+ color: ROLE_COLORS[entry.role] ?? DEFAULT_COLOR,
37
+ }));
38
+ // Inject overlays via page.evaluate
39
+ await page.evaluate((data) => {
40
+ const ATTR = "data-imperium-annotation";
41
+ for (const { ref, role, name, nth, color } of data) {
42
+ // Find the element using ARIA queries
43
+ let elements;
44
+ try {
45
+ // Try aria role query
46
+ const selector = name
47
+ ? `[role="${role}"]`
48
+ : `[role="${role}"]`;
49
+ elements = Array.from(document.querySelectorAll(selector));
50
+ // Filter by accessible name if provided
51
+ if (name) {
52
+ elements = elements.filter((el) => {
53
+ const accName = el.getAttribute("aria-label") ||
54
+ el.getAttribute("title") ||
55
+ (el.textContent ?? "").trim();
56
+ return accName.includes(name) || name.includes(accName.slice(0, 80));
57
+ });
58
+ }
59
+ // Handle nth
60
+ if (nth !== undefined && elements[nth]) {
61
+ elements = [elements[nth]];
62
+ }
63
+ else if (elements.length > 0) {
64
+ elements = [elements[0]];
65
+ }
66
+ // Fallback: try matching by tag for common roles
67
+ if (elements.length === 0) {
68
+ const tagMap = {
69
+ button: "button",
70
+ link: "a",
71
+ textbox: "input,textarea",
72
+ searchbox: "input[type=search]",
73
+ checkbox: "input[type=checkbox]",
74
+ radio: "input[type=radio]",
75
+ };
76
+ if (tagMap[role]) {
77
+ const tagEls = document.querySelectorAll(tagMap[role]);
78
+ for (const el of tagEls) {
79
+ const accName = el.getAttribute("aria-label") ||
80
+ el.getAttribute("title") ||
81
+ (el.textContent ?? "").trim();
82
+ if (!name || accName.includes(name) || name.includes(accName.slice(0, 80))) {
83
+ elements = [el];
84
+ break;
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ catch {
91
+ elements = [];
92
+ }
93
+ if (elements.length === 0)
94
+ continue;
95
+ const el = elements[0];
96
+ const rect = el.getBoundingClientRect();
97
+ if (rect.width === 0 || rect.height === 0)
98
+ continue;
99
+ // Create border overlay
100
+ const border = document.createElement("div");
101
+ border.setAttribute(ATTR, ref);
102
+ Object.assign(border.style, {
103
+ position: "absolute",
104
+ top: `${rect.top + window.scrollY}px`,
105
+ left: `${rect.left + window.scrollX}px`,
106
+ width: `${rect.width}px`,
107
+ height: `${rect.height}px`,
108
+ border: `2px solid ${color}`,
109
+ borderRadius: "3px",
110
+ pointerEvents: "none",
111
+ zIndex: "2147483647",
112
+ boxSizing: "border-box",
113
+ });
114
+ // Create badge
115
+ const badge = document.createElement("div");
116
+ badge.setAttribute(ATTR, `${ref}-badge`);
117
+ const refNum = ref.replace("e", "");
118
+ badge.textContent = refNum;
119
+ Object.assign(badge.style, {
120
+ position: "absolute",
121
+ top: `${rect.top + window.scrollY - 10}px`,
122
+ left: `${rect.left + window.scrollX - 10}px`,
123
+ width: "20px",
124
+ height: "20px",
125
+ borderRadius: "50%",
126
+ backgroundColor: color,
127
+ color: "white",
128
+ fontSize: "11px",
129
+ fontWeight: "bold",
130
+ fontFamily: "Arial, sans-serif",
131
+ display: "flex",
132
+ alignItems: "center",
133
+ justifyContent: "center",
134
+ pointerEvents: "none",
135
+ zIndex: "2147483647",
136
+ lineHeight: "1",
137
+ });
138
+ document.body.appendChild(border);
139
+ document.body.appendChild(badge);
140
+ }
141
+ }, annotations);
142
+ // Take screenshot with annotations
143
+ const screenshot = await page.screenshot({ fullPage: false });
144
+ // Cleanup injected elements
145
+ await page.evaluate((attr) => {
146
+ const els = document.querySelectorAll(`[${attr}]`);
147
+ for (const el of els)
148
+ el.remove();
149
+ }, ANNOTATION_ATTR);
150
+ return screenshot;
151
+ }
152
+ //# sourceMappingURL=annotator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotator.js","sourceRoot":"","sources":["../../src/snapshot/annotator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Content boundaries — unique markers to wrap content sections.
3
+ * Prevents LLM confusion about where content starts/ends.
4
+ */
5
+ export declare function generateBoundary(): string;
6
+ export declare function wrapContent(content: string, boundary: string): string;
7
+ //# sourceMappingURL=boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/snapshot/boundary.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErE"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Content boundaries — unique markers to wrap content sections.
3
+ * Prevents LLM confusion about where content starts/ends.
4
+ */
5
+ import { randomBytes } from "node:crypto";
6
+ export function generateBoundary() {
7
+ return randomBytes(8).toString("hex");
8
+ }
9
+ export function wrapContent(content, boundary) {
10
+ return `<imperium-boundary:${boundary}>\n${content}\n</imperium-boundary:${boundary}>`;
11
+ }
12
+ //# sourceMappingURL=boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../src/snapshot/boundary.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,gBAAgB;IAC9B,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,QAAgB;IAC3D,OAAO,sBAAsB,QAAQ,MAAM,OAAO,yBAAyB,QAAQ,GAAG,CAAC;AACzF,CAAC"}