neoagent 2.1.18-beta.21 → 2.1.18-beta.22
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/docs/capabilities.md +3 -1
- package/docs/configuration.md +2 -0
- package/extensions/chrome-browser/background.mjs +209 -0
- package/extensions/chrome-browser/manifest.json +17 -0
- package/extensions/chrome-browser/popup.css +69 -0
- package/extensions/chrome-browser/popup.html +28 -0
- package/extensions/chrome-browser/popup.js +81 -0
- package/extensions/chrome-browser/protocol.mjs +309 -0
- package/package.json +4 -2
- package/server/config/origins.js +10 -0
- package/server/db/database.js +29 -0
- package/server/http/routes.js +1 -0
- package/server/index.js +2 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +31247 -31180
- package/server/routes/browser_extension.js +133 -0
- package/server/services/ai/capabilityHealth.js +27 -0
- package/server/services/browser/extension/gateway.js +65 -0
- package/server/services/browser/extension/protocol.js +49 -0
- package/server/services/browser/extension/provider.js +178 -0
- package/server/services/browser/extension/registry.js +353 -0
- package/server/services/browser/extension/zip.js +133 -0
- package/server/services/manager.js +18 -0
- package/server/services/runtime/manager.js +7 -31
- package/server/services/runtime/settings.js +0 -4
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
export const COMMANDS = Object.freeze({
|
|
2
|
+
LAUNCH: 'launch',
|
|
3
|
+
NAVIGATE: 'navigate',
|
|
4
|
+
CLICK: 'click',
|
|
5
|
+
CLICK_POINT: 'clickPoint',
|
|
6
|
+
TYPE: 'type',
|
|
7
|
+
TYPE_TEXT: 'typeText',
|
|
8
|
+
PRESS_KEY: 'pressKey',
|
|
9
|
+
SCROLL: 'scroll',
|
|
10
|
+
EXTRACT: 'extract',
|
|
11
|
+
EVALUATE: 'evaluate',
|
|
12
|
+
SCREENSHOT: 'screenshot',
|
|
13
|
+
CLOSE: 'close',
|
|
14
|
+
GET_PAGE_INFO: 'getPageInfo',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function chromeCall(chromeApi, namespace, method, ...args) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
chromeApi[namespace][method](...args, (result) => {
|
|
20
|
+
const error = chromeApi.runtime?.lastError;
|
|
21
|
+
if (error) {
|
|
22
|
+
reject(new Error(error.message || String(error)));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
resolve(result);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function delay(ms) {
|
|
31
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function jsString(value) {
|
|
35
|
+
return JSON.stringify(String(value ?? ''));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function keyCodeFor(key) {
|
|
39
|
+
const normalized = String(key || '').trim();
|
|
40
|
+
const map = {
|
|
41
|
+
Enter: 13,
|
|
42
|
+
Escape: 27,
|
|
43
|
+
Backspace: 8,
|
|
44
|
+
Tab: 9,
|
|
45
|
+
ArrowUp: 38,
|
|
46
|
+
ArrowDown: 40,
|
|
47
|
+
ArrowLeft: 37,
|
|
48
|
+
ArrowRight: 39,
|
|
49
|
+
};
|
|
50
|
+
return map[normalized] || (normalized.length === 1 ? normalized.toUpperCase().charCodeAt(0) : 0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function createBrowserProtocol(chromeApi) {
|
|
54
|
+
let attachedTabId = null;
|
|
55
|
+
let activeTabId = null;
|
|
56
|
+
|
|
57
|
+
const debuggee = () => ({ tabId: activeTabId });
|
|
58
|
+
|
|
59
|
+
async function ensureTab() {
|
|
60
|
+
if (activeTabId != null) {
|
|
61
|
+
try {
|
|
62
|
+
await chromeCall(chromeApi, 'tabs', 'get', activeTabId);
|
|
63
|
+
return activeTabId;
|
|
64
|
+
} catch {
|
|
65
|
+
activeTabId = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const tabs = await chromeCall(chromeApi, 'tabs', 'query', { active: true, currentWindow: true });
|
|
70
|
+
if (tabs && tabs[0]?.id != null) {
|
|
71
|
+
activeTabId = tabs[0].id;
|
|
72
|
+
return activeTabId;
|
|
73
|
+
}
|
|
74
|
+
const tab = await chromeCall(chromeApi, 'tabs', 'create', { url: 'about:blank', active: true });
|
|
75
|
+
activeTabId = tab.id;
|
|
76
|
+
return activeTabId;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function attach() {
|
|
80
|
+
await ensureTab();
|
|
81
|
+
if (attachedTabId === activeTabId) return;
|
|
82
|
+
if (attachedTabId != null) {
|
|
83
|
+
await chromeCall(chromeApi, 'debugger', 'detach', { tabId: attachedTabId }).catch(() => {});
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
await chromeCall(chromeApi, 'debugger', 'attach', debuggee(), '1.3');
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (!/another debugger|already attached|debugger is already attached/i.test(error.message)) {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
attachedTabId = activeTabId;
|
|
93
|
+
await send('Page.enable').catch(() => {});
|
|
94
|
+
await send('Runtime.enable').catch(() => {});
|
|
95
|
+
await send('DOM.enable').catch(() => {});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function send(method, params = {}) {
|
|
99
|
+
return chromeCall(chromeApi, 'debugger', 'sendCommand', debuggee(), method, params);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function evalJs(expression, options = {}) {
|
|
103
|
+
await attach();
|
|
104
|
+
const response = await send('Runtime.evaluate', {
|
|
105
|
+
expression,
|
|
106
|
+
awaitPromise: options.awaitPromise !== false,
|
|
107
|
+
returnByValue: true,
|
|
108
|
+
});
|
|
109
|
+
if (response?.exceptionDetails) {
|
|
110
|
+
throw new Error(response.exceptionDetails.text || 'JavaScript evaluation failed.');
|
|
111
|
+
}
|
|
112
|
+
return response?.result?.value;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function waitForLoad(timeoutMs = 30000) {
|
|
116
|
+
const started = Date.now();
|
|
117
|
+
while (Date.now() - started < timeoutMs) {
|
|
118
|
+
const ready = await evalJs('document.readyState === "complete" || document.readyState === "interactive"', { awaitPromise: false })
|
|
119
|
+
.catch(() => false);
|
|
120
|
+
if (ready) return;
|
|
121
|
+
await delay(250);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function waitForSelector(selector, timeoutMs = 10000) {
|
|
126
|
+
if (!selector) return;
|
|
127
|
+
const expression = `Boolean(document.querySelector(${jsString(selector)}))`;
|
|
128
|
+
const started = Date.now();
|
|
129
|
+
while (Date.now() - started < timeoutMs) {
|
|
130
|
+
if (await evalJs(expression, { awaitPromise: false }).catch(() => false)) return;
|
|
131
|
+
await delay(200);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function currentTab() {
|
|
136
|
+
await ensureTab();
|
|
137
|
+
return chromeCall(chromeApi, 'tabs', 'get', activeTabId);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function screenshotDataUrl(options = {}) {
|
|
141
|
+
await attach();
|
|
142
|
+
const capture = await send('Page.captureScreenshot', {
|
|
143
|
+
format: 'png',
|
|
144
|
+
captureBeyondViewport: options.fullPage === true,
|
|
145
|
+
fromSurface: true,
|
|
146
|
+
});
|
|
147
|
+
return `data:image/png;base64,${capture?.data || ''}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function pageSnapshot(options = {}) {
|
|
151
|
+
const tab = await currentTab();
|
|
152
|
+
const title = await evalJs('document.title || ""', { awaitPromise: false }).catch(() => tab.title || '');
|
|
153
|
+
const bodyText = await evalJs(`(() => {
|
|
154
|
+
const body = document.body;
|
|
155
|
+
if (!body) return '';
|
|
156
|
+
const clone = body.cloneNode(true);
|
|
157
|
+
clone.querySelectorAll('script, style, noscript').forEach((node) => node.remove());
|
|
158
|
+
return String(clone.innerText || '').slice(0, 10000);
|
|
159
|
+
})()`).catch(() => '');
|
|
160
|
+
const result = {
|
|
161
|
+
title: title || tab.title || '',
|
|
162
|
+
url: tab.url || '',
|
|
163
|
+
status: 0,
|
|
164
|
+
bodyText,
|
|
165
|
+
};
|
|
166
|
+
if (options.screenshot !== false) {
|
|
167
|
+
result.screenshotDataUrl = await screenshotDataUrl(options);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function locateTarget(payload = {}) {
|
|
173
|
+
const selector = String(payload.selector || '').trim();
|
|
174
|
+
const text = String(payload.text || '').trim().toLowerCase();
|
|
175
|
+
const expression = selector
|
|
176
|
+
? `(() => {
|
|
177
|
+
const el = document.querySelector(${jsString(selector)});
|
|
178
|
+
if (!el) return null;
|
|
179
|
+
const rect = el.getBoundingClientRect();
|
|
180
|
+
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
181
|
+
})()`
|
|
182
|
+
: `(() => {
|
|
183
|
+
const candidates = Array.from(document.querySelectorAll('a, button, [role="button"], input[type="submit"], [onclick]'));
|
|
184
|
+
const target = candidates.find((el) => String(el.innerText || el.value || el.getAttribute('aria-label') || '').toLowerCase().includes(${jsString(text)}));
|
|
185
|
+
if (!target) return null;
|
|
186
|
+
const rect = target.getBoundingClientRect();
|
|
187
|
+
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
188
|
+
})()`;
|
|
189
|
+
const point = await evalJs(expression);
|
|
190
|
+
if (!point || !Number.isFinite(Number(point.x)) || !Number.isFinite(Number(point.y))) {
|
|
191
|
+
throw new Error(selector ? `Element not found: ${selector}` : `No clickable element found with text: ${payload.text}`);
|
|
192
|
+
}
|
|
193
|
+
return { x: Math.round(point.x), y: Math.round(point.y) };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function clickPoint(x, y) {
|
|
197
|
+
await attach();
|
|
198
|
+
const px = Math.max(0, Math.round(Number(x) || 0));
|
|
199
|
+
const py = Math.max(0, Math.round(Number(y) || 0));
|
|
200
|
+
await send('Input.dispatchMouseEvent', { type: 'mouseMoved', x: px, y: py });
|
|
201
|
+
await send('Input.dispatchMouseEvent', { type: 'mousePressed', x: px, y: py, button: 'left', clickCount: 1 });
|
|
202
|
+
await send('Input.dispatchMouseEvent', { type: 'mouseReleased', x: px, y: py, button: 'left', clickCount: 1 });
|
|
203
|
+
await delay(500);
|
|
204
|
+
return { x: px, y: py };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function typeKey(key) {
|
|
208
|
+
await attach();
|
|
209
|
+
const normalized = String(key || '').trim();
|
|
210
|
+
const code = keyCodeFor(normalized);
|
|
211
|
+
await send('Input.dispatchKeyEvent', { type: 'keyDown', key: normalized, windowsVirtualKeyCode: code });
|
|
212
|
+
await send('Input.dispatchKeyEvent', { type: 'keyUp', key: normalized, windowsVirtualKeyCode: code });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function run(command, payload = {}) {
|
|
216
|
+
switch (command) {
|
|
217
|
+
case COMMANDS.LAUNCH:
|
|
218
|
+
await attach();
|
|
219
|
+
return pageSnapshot({ screenshot: false });
|
|
220
|
+
case COMMANDS.NAVIGATE:
|
|
221
|
+
await attach();
|
|
222
|
+
if (!payload.url) throw new Error('url required');
|
|
223
|
+
await send('Page.navigate', { url: String(payload.url) });
|
|
224
|
+
await waitForLoad();
|
|
225
|
+
await waitForSelector(payload.waitFor);
|
|
226
|
+
return pageSnapshot(payload);
|
|
227
|
+
case COMMANDS.CLICK: {
|
|
228
|
+
const point = await locateTarget(payload);
|
|
229
|
+
await clickPoint(point.x, point.y);
|
|
230
|
+
return pageSnapshot({ screenshot: payload.screenshot !== false });
|
|
231
|
+
}
|
|
232
|
+
case COMMANDS.CLICK_POINT:
|
|
233
|
+
await clickPoint(payload.x, payload.y);
|
|
234
|
+
return pageSnapshot({ screenshot: payload.screenshot !== false });
|
|
235
|
+
case COMMANDS.TYPE:
|
|
236
|
+
if (!payload.selector) throw new Error('selector required');
|
|
237
|
+
if (payload.clear !== false) {
|
|
238
|
+
await evalJs(`(() => {
|
|
239
|
+
const el = document.querySelector(${jsString(payload.selector)});
|
|
240
|
+
if (!el) throw new Error('Element not found: ${String(payload.selector).replace(/'/g, "\\'")}');
|
|
241
|
+
el.focus();
|
|
242
|
+
if ('value' in el) el.value = '';
|
|
243
|
+
})()`);
|
|
244
|
+
} else {
|
|
245
|
+
await evalJs(`document.querySelector(${jsString(payload.selector)})?.focus()`);
|
|
246
|
+
}
|
|
247
|
+
await send('Input.insertText', { text: String(payload.text || '') });
|
|
248
|
+
if (payload.pressEnter) await typeKey('Enter');
|
|
249
|
+
return pageSnapshot({ screenshot: payload.screenshot !== false });
|
|
250
|
+
case COMMANDS.TYPE_TEXT:
|
|
251
|
+
await attach();
|
|
252
|
+
await send('Input.insertText', { text: String(payload.text || '') });
|
|
253
|
+
if (payload.pressEnter) await typeKey('Enter');
|
|
254
|
+
return pageSnapshot({ screenshot: payload.screenshot !== false });
|
|
255
|
+
case COMMANDS.PRESS_KEY:
|
|
256
|
+
await typeKey(payload.key);
|
|
257
|
+
return pageSnapshot({ screenshot: payload.screenshot !== false });
|
|
258
|
+
case COMMANDS.SCROLL:
|
|
259
|
+
await evalJs(`window.scrollBy(${Math.round(Number(payload.deltaX) || 0)}, ${Math.round(Number(payload.deltaY) || 0)})`);
|
|
260
|
+
await delay(250);
|
|
261
|
+
return pageSnapshot({ screenshot: payload.screenshot !== false });
|
|
262
|
+
case COMMANDS.EXTRACT: {
|
|
263
|
+
const selector = payload.selector || 'body';
|
|
264
|
+
const attribute = payload.attribute || '';
|
|
265
|
+
const expression = `(() => {
|
|
266
|
+
const read = (el) => {
|
|
267
|
+
const attr = ${jsString(attribute)};
|
|
268
|
+
if (attr === 'innerHTML') return el.innerHTML;
|
|
269
|
+
if (attr === 'outerHTML') return el.outerHTML;
|
|
270
|
+
if (attr) return el.getAttribute(attr) || '';
|
|
271
|
+
return el.innerText || '';
|
|
272
|
+
};
|
|
273
|
+
const els = Array.from(document.querySelectorAll(${jsString(selector)}));
|
|
274
|
+
if (${payload.all === true}) return { results: els.slice(0, 100).map(read) };
|
|
275
|
+
return { result: els[0] ? String(read(els[0])).slice(0, 50000) : '' };
|
|
276
|
+
})()`;
|
|
277
|
+
return evalJs(expression);
|
|
278
|
+
}
|
|
279
|
+
case COMMANDS.EVALUATE: {
|
|
280
|
+
const value = await evalJs(String(payload.script || 'undefined'));
|
|
281
|
+
return { result: typeof value === 'object' ? JSON.stringify(value) : String(value) };
|
|
282
|
+
}
|
|
283
|
+
case COMMANDS.SCREENSHOT:
|
|
284
|
+
return { screenshotDataUrl: await screenshotDataUrl(payload), fullPage: payload.fullPage === true };
|
|
285
|
+
case COMMANDS.GET_PAGE_INFO: {
|
|
286
|
+
const tab = await currentTab();
|
|
287
|
+
return { url: tab.url || null, title: tab.title || null };
|
|
288
|
+
}
|
|
289
|
+
case COMMANDS.CLOSE:
|
|
290
|
+
if (attachedTabId != null) {
|
|
291
|
+
await chromeCall(chromeApi, 'debugger', 'detach', { tabId: attachedTabId }).catch(() => {});
|
|
292
|
+
}
|
|
293
|
+
attachedTabId = null;
|
|
294
|
+
return { success: true };
|
|
295
|
+
default:
|
|
296
|
+
throw new Error(`Unsupported command: ${command}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
run,
|
|
302
|
+
_test: {
|
|
303
|
+
ensureTab,
|
|
304
|
+
attach,
|
|
305
|
+
send,
|
|
306
|
+
evalJs,
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neoagent",
|
|
3
|
-
"version": "2.1.18-beta.
|
|
3
|
+
"version": "2.1.18-beta.22",
|
|
4
4
|
"description": "Proactive personal AI agent with no limits",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"lib",
|
|
13
13
|
"runtime",
|
|
14
14
|
"server",
|
|
15
|
+
"extensions",
|
|
15
16
|
"docs",
|
|
16
17
|
"com.neoagent.plist",
|
|
17
18
|
"LICENSE",
|
|
@@ -72,7 +73,8 @@
|
|
|
72
73
|
"socket.io": "^4.8.1",
|
|
73
74
|
"telegraf": "^4.16.3",
|
|
74
75
|
"telnyx": "^5.51.0",
|
|
75
|
-
"uuid": "^11.1.0"
|
|
76
|
+
"uuid": "^11.1.0",
|
|
77
|
+
"ws": "^8.19.0"
|
|
76
78
|
},
|
|
77
79
|
"overrides": {
|
|
78
80
|
"undici": "^6.24.0"
|
package/server/config/origins.js
CHANGED
|
@@ -14,10 +14,19 @@ function isLoopbackOrigin(origin) {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function isChromeExtensionOrigin(origin) {
|
|
18
|
+
try {
|
|
19
|
+
return new URL(origin).protocol === 'chrome-extension:';
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
function isAllowedOrigin(origin) {
|
|
18
26
|
if (!origin) return true;
|
|
19
27
|
if (configuredOrigins.includes(origin)) return true;
|
|
20
28
|
if (isLoopbackOrigin(origin)) return true;
|
|
29
|
+
if (isChromeExtensionOrigin(origin)) return true;
|
|
21
30
|
return false;
|
|
22
31
|
}
|
|
23
32
|
|
|
@@ -28,6 +37,7 @@ function validateOrigin(origin, callback) {
|
|
|
28
37
|
|
|
29
38
|
module.exports = {
|
|
30
39
|
configuredOrigins,
|
|
40
|
+
isChromeExtensionOrigin,
|
|
31
41
|
isAllowedOrigin,
|
|
32
42
|
isLoopbackOrigin,
|
|
33
43
|
validateOrigin
|
package/server/db/database.js
CHANGED
|
@@ -173,6 +173,33 @@ db.exec(`
|
|
|
173
173
|
FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
|
|
174
174
|
);
|
|
175
175
|
|
|
176
|
+
CREATE TABLE IF NOT EXISTS browser_extension_pairing_requests (
|
|
177
|
+
id TEXT PRIMARY KEY,
|
|
178
|
+
user_id INTEGER,
|
|
179
|
+
pairing_secret_hash TEXT NOT NULL,
|
|
180
|
+
status TEXT DEFAULT 'pending',
|
|
181
|
+
approved_at TEXT,
|
|
182
|
+
claimed_at TEXT,
|
|
183
|
+
expires_at TEXT NOT NULL,
|
|
184
|
+
metadata_json TEXT DEFAULT '{}',
|
|
185
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
186
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
CREATE TABLE IF NOT EXISTS browser_extension_tokens (
|
|
190
|
+
id TEXT PRIMARY KEY,
|
|
191
|
+
user_id INTEGER NOT NULL,
|
|
192
|
+
token_hash TEXT UNIQUE NOT NULL,
|
|
193
|
+
name TEXT DEFAULT 'Chrome Extension',
|
|
194
|
+
status TEXT DEFAULT 'active',
|
|
195
|
+
last_connected_at TEXT,
|
|
196
|
+
last_seen_at TEXT,
|
|
197
|
+
revoked_at TEXT,
|
|
198
|
+
metadata_json TEXT DEFAULT '{}',
|
|
199
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
200
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
201
|
+
);
|
|
202
|
+
|
|
176
203
|
CREATE TABLE IF NOT EXISTS scheduled_tasks (
|
|
177
204
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
178
205
|
user_id INTEGER NOT NULL,
|
|
@@ -245,6 +272,8 @@ db.exec(`
|
|
|
245
272
|
CREATE INDEX IF NOT EXISTS idx_agents_user ON agents(user_id, status, updated_at DESC);
|
|
246
273
|
CREATE INDEX IF NOT EXISTS idx_integration_oauth_states_state ON integration_oauth_states(state);
|
|
247
274
|
CREATE INDEX IF NOT EXISTS idx_integration_oauth_states_expires ON integration_oauth_states(expires_at);
|
|
275
|
+
CREATE INDEX IF NOT EXISTS idx_browser_extension_pairing_status ON browser_extension_pairing_requests(status, expires_at);
|
|
276
|
+
CREATE INDEX IF NOT EXISTS idx_browser_extension_tokens_user ON browser_extension_tokens(user_id, status, created_at DESC);
|
|
248
277
|
CREATE INDEX IF NOT EXISTS idx_messages_user ON messages(user_id, created_at DESC);
|
|
249
278
|
CREATE INDEX IF NOT EXISTS idx_messages_platform ON messages(platform, platform_chat_id);
|
|
250
279
|
CREATE INDEX IF NOT EXISTS idx_conv_messages ON conversation_messages(conversation_id, created_at);
|
package/server/http/routes.js
CHANGED
|
@@ -19,6 +19,7 @@ const routeRegistry = [
|
|
|
19
19
|
{ basePath: '/api/memory', modulePath: '../routes/memory' },
|
|
20
20
|
{ basePath: '/api/scheduler', modulePath: '../routes/scheduler' },
|
|
21
21
|
{ basePath: '/api/browser', modulePath: '../routes/browser' },
|
|
22
|
+
{ basePath: '/api/browser-extension', modulePath: '../routes/browser_extension' },
|
|
22
23
|
{ basePath: '/api/android', modulePath: '../routes/android' },
|
|
23
24
|
{ basePath: '/api/recordings', modulePath: '../routes/recordings' },
|
|
24
25
|
{ basePath: '/api/wearables', modulePath: '../routes/wearables' },
|
package/server/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const { registerApiRoutes } = require('./http/routes');
|
|
|
29
29
|
const { registerStaticRoutes } = require('./http/static');
|
|
30
30
|
const { registerErrorHandler } = require('./http/errors');
|
|
31
31
|
const { startServices, stopServices } = require('./services/manager');
|
|
32
|
+
const { bindBrowserExtensionGateway } = require('./services/browser/extension/gateway');
|
|
32
33
|
|
|
33
34
|
const PORT = Number(process.env.PORT) || 3333;
|
|
34
35
|
const SECURE_COOKIES = process.env.SECURE_COOKIES === 'true';
|
|
@@ -81,6 +82,7 @@ bindSocketSessions(io, sessionMiddleware);
|
|
|
81
82
|
registerApiRoutes(app);
|
|
82
83
|
registerStaticRoutes(app);
|
|
83
84
|
registerErrorHandler(app);
|
|
85
|
+
bindBrowserExtensionGateway(httpServer, app);
|
|
84
86
|
|
|
85
87
|
let shuttingDown = false;
|
|
86
88
|
let shutdownExitCode = 0;
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"425cfb54d01a9472b3e81d9e76fd63a4a44cfb
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "2124199773" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|