supersurf-daemon 1.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.
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ /**
3
+ * RequestScheduler — round-robin scheduler with tab ownership enforcement.
4
+ *
5
+ * Extracted from the multiplexer's leader-mode logic. Serializes all commands
6
+ * to the extension through a single drain loop, ensuring fair round-robin
7
+ * across sessions and automatic tab context-switching.
8
+ *
9
+ * @module scheduler
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.RequestScheduler = void 0;
13
+ const debugLog = (...args) => {
14
+ const logger = global.DAEMON_LOGGER;
15
+ if (logger)
16
+ logger.log('[Sched]', ...args);
17
+ else if (global.DAEMON_DEBUG)
18
+ console.error('[Sched]', ...args);
19
+ };
20
+ // Methods that modify tab ownership
21
+ const TAB_CLAIM_METHODS = new Set(['selectTab', 'createTab']);
22
+ const TAB_RELEASE_METHODS = new Set(['closeTab']);
23
+ // Methods where we need to ensure the correct tab is active before executing
24
+ const TAB_SCOPED_METHODS = new Set([
25
+ 'navigate', 'snapshot', 'evaluate', 'screenshot',
26
+ 'consoleMessages', 'networkRequests', 'clearNetwork',
27
+ 'performanceMetrics', 'waitForReady', 'capturePageState',
28
+ 'forwardCDPCommand', 'window', 'dialog',
29
+ 'listExtensions', 'reloadExtension', 'secure_fill',
30
+ ]);
31
+ /**
32
+ * Round-robin request scheduler with tab ownership and auto context-switching.
33
+ */
34
+ class RequestScheduler {
35
+ bridge;
36
+ sessions;
37
+ requestQueue = new Map();
38
+ sessionOrder = [];
39
+ currentSessionIdx = 0;
40
+ processingQueue = false;
41
+ currentExtensionTabId = null;
42
+ sessionGroupIds = new Map();
43
+ constructor(bridge, sessions) {
44
+ this.bridge = bridge;
45
+ this.sessions = sessions;
46
+ }
47
+ /** Register a session in the scheduler. */
48
+ addSession(sessionId) {
49
+ this.requestQueue.set(sessionId, []);
50
+ this.sessionOrder.push(sessionId);
51
+ }
52
+ /** Remove a session from the scheduler. Rejects queued requests. */
53
+ removeSession(sessionId) {
54
+ const queued = this.requestQueue.get(sessionId) || [];
55
+ for (const req of queued) {
56
+ req.reject(new Error('Session disconnected'));
57
+ }
58
+ this.requestQueue.delete(sessionId);
59
+ this.sessionOrder = this.sessionOrder.filter(s => s !== sessionId);
60
+ this.sessionGroupIds.delete(sessionId);
61
+ if (this.currentSessionIdx >= this.sessionOrder.length) {
62
+ this.currentSessionIdx = 0;
63
+ }
64
+ }
65
+ /** Enqueue a request and return a promise for the result. */
66
+ enqueue(sessionId, method, params, timeout = 30000) {
67
+ return new Promise((resolve, reject) => {
68
+ const queue = this.requestQueue.get(sessionId);
69
+ if (!queue) {
70
+ reject(new Error('Unknown session'));
71
+ return;
72
+ }
73
+ queue.push({ sessionId, method, params, timeout, resolve, reject });
74
+ this.drainQueue();
75
+ });
76
+ }
77
+ /** Process queued requests in round-robin order. Serialized — one at a time. */
78
+ async drainQueue() {
79
+ if (this.processingQueue)
80
+ return;
81
+ this.processingQueue = true;
82
+ try {
83
+ while (this.hasQueuedRequests()) {
84
+ const request = this.pickNextRequest();
85
+ if (!request)
86
+ break;
87
+ await this.executeRequest(request);
88
+ }
89
+ }
90
+ finally {
91
+ this.processingQueue = false;
92
+ }
93
+ }
94
+ hasQueuedRequests() {
95
+ for (const queue of this.requestQueue.values()) {
96
+ if (queue.length > 0)
97
+ return true;
98
+ }
99
+ return false;
100
+ }
101
+ /** Round-robin pick: advance through sessions, skip empty queues. */
102
+ pickNextRequest() {
103
+ if (this.sessionOrder.length === 0)
104
+ return null;
105
+ let checked = 0;
106
+ while (checked < this.sessionOrder.length) {
107
+ const sessionId = this.sessionOrder[this.currentSessionIdx];
108
+ const queue = this.requestQueue.get(sessionId);
109
+ this.currentSessionIdx = (this.currentSessionIdx + 1) % this.sessionOrder.length;
110
+ checked++;
111
+ if (queue && queue.length > 0) {
112
+ return queue.shift();
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+ /**
118
+ * Execute a single queued request:
119
+ * 1. Tab ownership check for tab-management methods
120
+ * 2. Auto context-switch if this session's tab != current extension tab
121
+ * 3. Inject _sessionId for extension-side group isolation
122
+ * 4. Execute the actual command
123
+ * 5. Track ownership changes
124
+ */
125
+ async executeRequest(request) {
126
+ const { sessionId, method, params, timeout, resolve, reject } = request;
127
+ try {
128
+ // Tab ownership enforcement
129
+ if (TAB_CLAIM_METHODS.has(method)) {
130
+ if (method === 'selectTab') {
131
+ const tabId = params.tabId;
132
+ if (tabId !== undefined) {
133
+ const owner = this.sessions.findTabOwner(tabId);
134
+ if (owner && owner !== sessionId) {
135
+ reject(new Error(`Tab ${tabId} is owned by session "${owner}". Cannot attach.`));
136
+ return;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ if (TAB_RELEASE_METHODS.has(method)) {
142
+ const tabId = params.tabId;
143
+ if (tabId !== undefined) {
144
+ const owner = this.sessions.findTabOwner(tabId);
145
+ if (owner && owner !== sessionId) {
146
+ reject(new Error(`Tab ${tabId} is owned by session "${owner}". Cannot close.`));
147
+ return;
148
+ }
149
+ }
150
+ }
151
+ // Auto context-switch
152
+ if (TAB_SCOPED_METHODS.has(method) || (!TAB_CLAIM_METHODS.has(method) && !TAB_RELEASE_METHODS.has(method) && method !== 'getTabs')) {
153
+ const sessionTabId = this.sessions.getAttachedTabId(sessionId);
154
+ if (sessionTabId !== null && sessionTabId !== this.currentExtensionTabId) {
155
+ debugLog(`Context-switch: tab ${this.currentExtensionTabId} -> ${sessionTabId} (session="${sessionId}")`);
156
+ try {
157
+ await this.bridge.sendCmd('selectTab', { tabId: sessionTabId, _sessionId: sessionId }, 5000);
158
+ this.currentExtensionTabId = sessionTabId;
159
+ }
160
+ catch (err) {
161
+ debugLog(`Context-switch failed: ${err.message}`);
162
+ }
163
+ }
164
+ }
165
+ // Inject _sessionId for extension-side group isolation
166
+ const enrichedParams = { ...params, _sessionId: sessionId };
167
+ // Execute the actual command
168
+ const result = await this.bridge.sendCmd(method, enrichedParams, timeout);
169
+ // Track ownership changes
170
+ if (method === 'createTab' || method === 'selectTab') {
171
+ const tabId = result?.attachedTab?.id ?? result?.id;
172
+ const groupId = result?.attachedTab?.groupId;
173
+ if (tabId) {
174
+ this.sessions.addOwnedTab(sessionId, tabId);
175
+ this.sessions.setAttachedTabId(sessionId, tabId);
176
+ this.currentExtensionTabId = tabId;
177
+ }
178
+ if (groupId !== undefined && groupId !== -1) {
179
+ this.sessionGroupIds.set(sessionId, groupId);
180
+ this.sessions.setGroupId(sessionId, groupId);
181
+ }
182
+ }
183
+ if (method === 'closeTab') {
184
+ const sessionTabId = this.sessions.getAttachedTabId(sessionId);
185
+ if (sessionTabId !== null) {
186
+ this.sessions.removeOwnedTab(sessionId, sessionTabId);
187
+ this.sessions.setAttachedTabId(sessionId, null);
188
+ }
189
+ }
190
+ // Filter getTabs results
191
+ if (method === 'getTabs') {
192
+ const tabsResult = result?.tabs ?? result;
193
+ if (Array.isArray(tabsResult)) {
194
+ const otherOwnedIds = this.sessions.getOtherOwnedTabIds(sessionId);
195
+ const filtered = tabsResult.filter((tab) => !otherOwnedIds.has(tab.id));
196
+ if (result?.tabs) {
197
+ resolve({ ...result, tabs: filtered });
198
+ }
199
+ else {
200
+ resolve(filtered);
201
+ }
202
+ return;
203
+ }
204
+ }
205
+ resolve(result);
206
+ }
207
+ catch (error) {
208
+ reject(error);
209
+ }
210
+ }
211
+ /** Drain and reject all queued requests. Called during shutdown. */
212
+ drainAll() {
213
+ for (const [, queue] of this.requestQueue) {
214
+ for (const req of queue) {
215
+ req.reject(new Error('Daemon shutting down'));
216
+ }
217
+ }
218
+ this.requestQueue.clear();
219
+ this.sessionOrder = [];
220
+ }
221
+ }
222
+ exports.RequestScheduler = RequestScheduler;
223
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Session registry — tracks connected MCP sessions and their tab ownership.
3
+ *
4
+ * @module session
5
+ */
6
+ import type net from 'net';
7
+ import type { DaemonSession } from './types';
8
+ /**
9
+ * Manages the set of active MCP sessions connected to the daemon.
10
+ * Each session owns a socket, a set of tabs, and an attached tab ID.
11
+ */
12
+ export declare class SessionRegistry {
13
+ private sessions;
14
+ /** Register a new session. Returns false if sessionId already exists. */
15
+ add(sessionId: string, socket: net.Socket): boolean;
16
+ /** Remove a session by ID. Returns the removed session or undefined. */
17
+ remove(sessionId: string): DaemonSession | undefined;
18
+ /** Get a session by ID. */
19
+ get(sessionId: string): DaemonSession | undefined;
20
+ /** Check if a session exists. */
21
+ has(sessionId: string): boolean;
22
+ /** Return the number of active sessions. */
23
+ get count(): number;
24
+ /** Return all session IDs. */
25
+ ids(): string[];
26
+ /** Iterate over all sessions. */
27
+ values(): IterableIterator<DaemonSession>;
28
+ /** Set the attached tab ID for a session. */
29
+ setAttachedTabId(sessionId: string, tabId: number | null): void;
30
+ /** Get the attached tab ID for a session. */
31
+ getAttachedTabId(sessionId: string): number | null;
32
+ /** Set the group ID for a session. */
33
+ setGroupId(sessionId: string, groupId: number | null): void;
34
+ /** Add a tab to a session's ownership set. */
35
+ addOwnedTab(sessionId: string, tabId: number): void;
36
+ /** Remove a tab from a session's ownership set. */
37
+ removeOwnedTab(sessionId: string, tabId: number): void;
38
+ /** Find which session owns a tab by its ID. Returns null if unowned. */
39
+ findTabOwner(tabId: number): string | null;
40
+ /** Get all tab IDs owned by sessions other than the given one. */
41
+ getOtherOwnedTabIds(sessionId: string): Set<number>;
42
+ }
43
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAyC;IAEzD,yEAAyE;IACzE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,OAAO;IAYnD,wEAAwE;IACxE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAQpD,2BAA2B;IAC3B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIjD,iCAAiC;IACjC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI/B,4CAA4C;IAC5C,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,8BAA8B;IAC9B,GAAG,IAAI,MAAM,EAAE;IAIf,iCAAiC;IACjC,MAAM,IAAI,gBAAgB,CAAC,aAAa,CAAC;IAIzC,6CAA6C;IAC7C,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAK/D,6CAA6C;IAC7C,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIlD,sCAAsC;IACtC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAK3D,8CAA8C;IAC9C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKnD,mDAAmD;IACnD,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKtD,wEAAwE;IACxE,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAO1C,kEAAkE;IAClE,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;CAUpD"}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ /**
3
+ * Session registry — tracks connected MCP sessions and their tab ownership.
4
+ *
5
+ * @module session
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.SessionRegistry = void 0;
9
+ /**
10
+ * Manages the set of active MCP sessions connected to the daemon.
11
+ * Each session owns a socket, a set of tabs, and an attached tab ID.
12
+ */
13
+ class SessionRegistry {
14
+ sessions = new Map();
15
+ /** Register a new session. Returns false if sessionId already exists. */
16
+ add(sessionId, socket) {
17
+ if (this.sessions.has(sessionId))
18
+ return false;
19
+ this.sessions.set(sessionId, {
20
+ sessionId,
21
+ socket,
22
+ ownedTabs: new Set(),
23
+ attachedTabId: null,
24
+ groupId: null,
25
+ });
26
+ return true;
27
+ }
28
+ /** Remove a session by ID. Returns the removed session or undefined. */
29
+ remove(sessionId) {
30
+ const session = this.sessions.get(sessionId);
31
+ if (session) {
32
+ this.sessions.delete(sessionId);
33
+ }
34
+ return session;
35
+ }
36
+ /** Get a session by ID. */
37
+ get(sessionId) {
38
+ return this.sessions.get(sessionId);
39
+ }
40
+ /** Check if a session exists. */
41
+ has(sessionId) {
42
+ return this.sessions.has(sessionId);
43
+ }
44
+ /** Return the number of active sessions. */
45
+ get count() {
46
+ return this.sessions.size;
47
+ }
48
+ /** Return all session IDs. */
49
+ ids() {
50
+ return [...this.sessions.keys()];
51
+ }
52
+ /** Iterate over all sessions. */
53
+ values() {
54
+ return this.sessions.values();
55
+ }
56
+ /** Set the attached tab ID for a session. */
57
+ setAttachedTabId(sessionId, tabId) {
58
+ const session = this.sessions.get(sessionId);
59
+ if (session)
60
+ session.attachedTabId = tabId;
61
+ }
62
+ /** Get the attached tab ID for a session. */
63
+ getAttachedTabId(sessionId) {
64
+ return this.sessions.get(sessionId)?.attachedTabId ?? null;
65
+ }
66
+ /** Set the group ID for a session. */
67
+ setGroupId(sessionId, groupId) {
68
+ const session = this.sessions.get(sessionId);
69
+ if (session)
70
+ session.groupId = groupId;
71
+ }
72
+ /** Add a tab to a session's ownership set. */
73
+ addOwnedTab(sessionId, tabId) {
74
+ const session = this.sessions.get(sessionId);
75
+ if (session)
76
+ session.ownedTabs.add(tabId);
77
+ }
78
+ /** Remove a tab from a session's ownership set. */
79
+ removeOwnedTab(sessionId, tabId) {
80
+ const session = this.sessions.get(sessionId);
81
+ if (session)
82
+ session.ownedTabs.delete(tabId);
83
+ }
84
+ /** Find which session owns a tab by its ID. Returns null if unowned. */
85
+ findTabOwner(tabId) {
86
+ for (const session of this.sessions.values()) {
87
+ if (session.ownedTabs.has(tabId))
88
+ return session.sessionId;
89
+ }
90
+ return null;
91
+ }
92
+ /** Get all tab IDs owned by sessions other than the given one. */
93
+ getOtherOwnedTabIds(sessionId) {
94
+ const result = new Set();
95
+ for (const [sid, session] of this.sessions) {
96
+ if (sid === sessionId)
97
+ continue;
98
+ for (const tabId of session.ownedTabs) {
99
+ result.add(tabId);
100
+ }
101
+ }
102
+ return result;
103
+ }
104
+ }
105
+ exports.SessionRegistry = SessionRegistry;
106
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { FileLogger, LOG_ROOT, sanitizeFilename, truncateString, replacer } from './logger/logger';
2
+ export type { DebugMode } from './logger/logger';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACnG,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replacer = exports.truncateString = exports.sanitizeFilename = exports.LOG_ROOT = exports.FileLogger = void 0;
4
+ var logger_1 = require("./logger/logger");
5
+ Object.defineProperty(exports, "FileLogger", { enumerable: true, get: function () { return logger_1.FileLogger; } });
6
+ Object.defineProperty(exports, "LOG_ROOT", { enumerable: true, get: function () { return logger_1.LOG_ROOT; } });
7
+ Object.defineProperty(exports, "sanitizeFilename", { enumerable: true, get: function () { return logger_1.sanitizeFilename; } });
8
+ Object.defineProperty(exports, "truncateString", { enumerable: true, get: function () { return logger_1.truncateString; } });
9
+ Object.defineProperty(exports, "replacer", { enumerable: true, get: function () { return logger_1.replacer; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;AAAA,0CAAmG;AAA1F,oGAAA,UAAU,OAAA;AAAE,kGAAA,QAAQ,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AAAE,wGAAA,cAAc,OAAA;AAAE,kGAAA,QAAQ,OAAA"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Core file logger — shared across daemon and server packages.
3
+ *
4
+ * Provides:
5
+ * - **FileLogger** — synchronous, append-only file logger with ISO timestamps
6
+ * - **DebugMode** — debug mode type (`false | 'truncate' | 'no_truncate'`)
7
+ * - Truncation helpers and JSON replacer for log-safe output
8
+ *
9
+ * Package-specific concerns (session routing, registries) belong in the
10
+ * consuming package, not here.
11
+ *
12
+ * @module shared/logger
13
+ */
14
+ export declare const LOG_ROOT: string;
15
+ /** Debug mode: false (off), 'truncate' (default debug), 'no_truncate' (full payloads). */
16
+ export type DebugMode = false | 'truncate' | 'no_truncate';
17
+ /**
18
+ * Synchronous, append-only file logger. Writes ISO-timestamped lines and
19
+ * also mirrors to stderr. Truncates the log file on construction to start fresh.
20
+ */
21
+ export declare class FileLogger {
22
+ logFilePath: string;
23
+ enabled: boolean;
24
+ private _truncate;
25
+ constructor(logFilePath: string);
26
+ get truncate(): boolean;
27
+ set truncate(value: boolean);
28
+ enable(): void;
29
+ disable(): void;
30
+ /** Append a timestamped log line. No-ops if logger is disabled. Also writes to stderr. */
31
+ log(...args: unknown[]): void;
32
+ /** Serialize an argument to a log-safe string, applying truncation if enabled. */
33
+ private formatArg;
34
+ }
35
+ /** Truncate a string, replacing the middle with "…" if over limit. */
36
+ export declare function truncateString(str: string, maxLen: number): string;
37
+ /** JSON.stringify replacer — redacts base64 and very long strings. */
38
+ export declare function replacer(_key: string, value: unknown): unknown;
39
+ /** Sanitize a string for use as a filename. */
40
+ export declare function sanitizeFilename(name: string): string;
41
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../logger/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,eAAO,MAAM,QAAQ,QAAgD,CAAC;AAGtE,0FAA0F;AAC1F,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,aAAa,CAAC;AAE3D;;;GAGG;AACH,qBAAa,UAAU;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAS;IACzB,OAAO,CAAC,SAAS,CAAiB;gBAEtB,WAAW,EAAE,MAAM;IAa/B,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,MAAM,IAAI,IAAI;IAKd,OAAO,IAAI,IAAI;IAIf,0FAA0F;IAC1F,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAa7B,kFAAkF;IAClF,OAAO,CAAC,SAAS;CAclB;AAID,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlE;AAED,sEAAsE;AACtE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAO9D;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD"}
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * Core file logger — shared across daemon and server packages.
4
+ *
5
+ * Provides:
6
+ * - **FileLogger** — synchronous, append-only file logger with ISO timestamps
7
+ * - **DebugMode** — debug mode type (`false | 'truncate' | 'no_truncate'`)
8
+ * - Truncation helpers and JSON replacer for log-safe output
9
+ *
10
+ * Package-specific concerns (session routing, registries) belong in the
11
+ * consuming package, not here.
12
+ *
13
+ * @module shared/logger
14
+ */
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.FileLogger = exports.LOG_ROOT = void 0;
20
+ exports.truncateString = truncateString;
21
+ exports.replacer = replacer;
22
+ exports.sanitizeFilename = sanitizeFilename;
23
+ const fs_1 = __importDefault(require("fs"));
24
+ const path_1 = __importDefault(require("path"));
25
+ const os_1 = __importDefault(require("os"));
26
+ exports.LOG_ROOT = path_1.default.join(os_1.default.homedir(), '.supersurf', 'logs');
27
+ const DEFAULT_TRUNCATE_LEN = 120;
28
+ /**
29
+ * Synchronous, append-only file logger. Writes ISO-timestamped lines and
30
+ * also mirrors to stderr. Truncates the log file on construction to start fresh.
31
+ */
32
+ class FileLogger {
33
+ logFilePath;
34
+ enabled = false;
35
+ _truncate = true;
36
+ constructor(logFilePath) {
37
+ this.logFilePath = logFilePath;
38
+ const logDir = path_1.default.dirname(this.logFilePath);
39
+ if (!fs_1.default.existsSync(logDir)) {
40
+ fs_1.default.mkdirSync(logDir, { recursive: true });
41
+ }
42
+ if (fs_1.default.existsSync(this.logFilePath)) {
43
+ fs_1.default.truncateSync(this.logFilePath, 0);
44
+ }
45
+ }
46
+ get truncate() {
47
+ return this._truncate;
48
+ }
49
+ set truncate(value) {
50
+ this._truncate = value;
51
+ }
52
+ enable() {
53
+ this.enabled = true;
54
+ this.log('[FileLogger] Logging enabled — writing to:', this.logFilePath);
55
+ }
56
+ disable() {
57
+ this.enabled = false;
58
+ }
59
+ /** Append a timestamped log line. No-ops if logger is disabled. Also writes to stderr. */
60
+ log(...args) {
61
+ if (!this.enabled)
62
+ return;
63
+ const timestamp = new Date().toISOString();
64
+ const message = args
65
+ .map((arg) => this.formatArg(arg))
66
+ .join(' ');
67
+ const logLine = `[${timestamp}] ${message}\n`;
68
+ fs_1.default.appendFileSync(this.logFilePath, logLine, 'utf8');
69
+ console.error(message);
70
+ }
71
+ /** Serialize an argument to a log-safe string, applying truncation if enabled. */
72
+ formatArg(arg) {
73
+ if (typeof arg === 'string') {
74
+ return this._truncate ? truncateString(arg, DEFAULT_TRUNCATE_LEN) : arg;
75
+ }
76
+ if (typeof arg === 'object' && arg !== null) {
77
+ try {
78
+ const json = JSON.stringify(arg, replacer, 2);
79
+ return this._truncate ? truncateString(json, DEFAULT_TRUNCATE_LEN * 4) : json;
80
+ }
81
+ catch {
82
+ return String(arg);
83
+ }
84
+ }
85
+ return String(arg);
86
+ }
87
+ }
88
+ exports.FileLogger = FileLogger;
89
+ // ─── Helpers ──────────────────────────────────────────────────
90
+ /** Truncate a string, replacing the middle with "…" if over limit. */
91
+ function truncateString(str, maxLen) {
92
+ if (str.length <= maxLen)
93
+ return str;
94
+ const half = Math.floor((maxLen - 3) / 2);
95
+ return str.slice(0, half) + '...' + str.slice(-half);
96
+ }
97
+ /** JSON.stringify replacer — redacts base64 and very long strings. */
98
+ function replacer(_key, value) {
99
+ if (typeof value === 'string') {
100
+ if (value.length > 200 && /^[A-Za-z0-9+/=]+$/.test(value.slice(0, 100))) {
101
+ return `[base64 ${value.length} chars]`;
102
+ }
103
+ }
104
+ return value;
105
+ }
106
+ /** Sanitize a string for use as a filename. */
107
+ function sanitizeFilename(name) {
108
+ return name.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 64);
109
+ }
110
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../logger/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared types for the daemon package.
3
+ *
4
+ * @module types
5
+ */
6
+ import type net from 'net';
7
+ /** State tracked per connected MCP session. */
8
+ export interface DaemonSession {
9
+ sessionId: string;
10
+ socket: net.Socket;
11
+ ownedTabs: Set<number>;
12
+ attachedTabId: number | null;
13
+ groupId: number | null;
14
+ }
15
+ /** A request waiting in the round-robin scheduler queue. */
16
+ export interface QueuedRequest {
17
+ sessionId: string;
18
+ method: string;
19
+ params: Record<string, unknown>;
20
+ timeout: number;
21
+ resolve: (value: unknown) => void;
22
+ reject: (reason: Error) => void;
23
+ }
24
+ /** Pending promise callbacks for an in-flight request to the extension. */
25
+ export interface InflightRequest {
26
+ resolve: (value: unknown) => void;
27
+ reject: (reason: Error) => void;
28
+ }
29
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC;IACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC"}
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Shared types for the daemon package.
4
+ *
5
+ * @module types
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;GAIG"}