agentgui 1.0.758 → 1.0.760

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,105 @@
1
+ import { startGeminiOAuth, exchangeGeminiOAuthCode, getGeminiOAuthState } from './oauth-gemini.js';
2
+ import { startCodexOAuth, exchangeCodexOAuthCode, getCodexOAuthState } from './oauth-codex.js';
3
+
4
+ export function register(deps) {
5
+ const { sendJSON, parseBody, PORT, BASE_URL, rootDir } = deps;
6
+ const routes = {};
7
+
8
+ routes['POST /api/gemini-oauth/start'] = async (req, res) => {
9
+ try {
10
+ const result = await startGeminiOAuth(req, { PORT, BASE_URL, rootDir });
11
+ sendJSON(req, res, 200, { authUrl: result.authUrl, mode: result.mode });
12
+ } catch (e) {
13
+ console.error('[gemini-oauth] /api/gemini-oauth/start failed:', e);
14
+ sendJSON(req, res, 500, { error: e.message });
15
+ }
16
+ };
17
+
18
+ routes['GET /api/gemini-oauth/status'] = async (req, res) => {
19
+ sendJSON(req, res, 200, getGeminiOAuthState());
20
+ };
21
+
22
+ routes['POST /api/gemini-oauth/relay'] = async (req, res) => {
23
+ try {
24
+ const body = await parseBody(req);
25
+ const { code, state: stateParam } = body;
26
+ if (!code || !stateParam) { sendJSON(req, res, 400, { error: 'Missing code or state' }); return; }
27
+ const email = await exchangeGeminiOAuthCode(code, stateParam);
28
+ sendJSON(req, res, 200, { success: true, email });
29
+ } catch (e) {
30
+ sendJSON(req, res, 400, { error: e.message });
31
+ }
32
+ };
33
+
34
+ routes['POST /api/gemini-oauth/complete'] = async (req, res) => {
35
+ try {
36
+ const body = await parseBody(req);
37
+ const pastedUrl = (body.url || '').trim();
38
+ if (!pastedUrl) { sendJSON(req, res, 400, { error: 'No URL provided' }); return; }
39
+ let parsed;
40
+ try { parsed = new URL(pastedUrl); } catch (_) {
41
+ sendJSON(req, res, 400, { error: 'Invalid URL. Paste the full URL from the browser address bar.' }); return;
42
+ }
43
+ const error = parsed.searchParams.get('error');
44
+ if (error) { sendJSON(req, res, 200, { error: parsed.searchParams.get('error_description') || error }); return; }
45
+ const code = parsed.searchParams.get('code');
46
+ const state = parsed.searchParams.get('state');
47
+ const email = await exchangeGeminiOAuthCode(code, state);
48
+ sendJSON(req, res, 200, { success: true, email });
49
+ } catch (e) {
50
+ sendJSON(req, res, 400, { error: e.message });
51
+ }
52
+ };
53
+
54
+ routes['POST /api/codex-oauth/start'] = async (req, res) => {
55
+ try {
56
+ const result = await startCodexOAuth(req, { PORT, BASE_URL });
57
+ sendJSON(req, res, 200, { authUrl: result.authUrl, mode: result.mode });
58
+ } catch (e) {
59
+ console.error('[codex-oauth] /api/codex-oauth/start failed:', e);
60
+ sendJSON(req, res, 500, { error: e.message });
61
+ }
62
+ };
63
+
64
+ routes['GET /api/codex-oauth/status'] = async (req, res) => {
65
+ sendJSON(req, res, 200, getCodexOAuthState());
66
+ };
67
+
68
+ routes['POST /api/codex-oauth/relay'] = async (req, res) => {
69
+ try {
70
+ const body = await parseBody(req);
71
+ const { code, state: stateParam } = body;
72
+ if (!code || !stateParam) { sendJSON(req, res, 400, { error: 'Missing code or state' }); return; }
73
+ const email = await exchangeCodexOAuthCode(code, stateParam);
74
+ sendJSON(req, res, 200, { success: true, email });
75
+ } catch (e) {
76
+ sendJSON(req, res, 400, { error: e.message });
77
+ }
78
+ };
79
+
80
+ routes['POST /api/codex-oauth/complete'] = async (req, res) => {
81
+ try {
82
+ const body = await parseBody(req);
83
+ const pastedUrl = (body.url || '').trim();
84
+ if (!pastedUrl) { sendJSON(req, res, 400, { error: 'No URL provided' }); return; }
85
+ let parsed;
86
+ try { parsed = new URL(pastedUrl); } catch (_) {
87
+ sendJSON(req, res, 400, { error: 'Invalid URL. Paste the full URL from the browser address bar.' }); return;
88
+ }
89
+ const error = parsed.searchParams.get('error');
90
+ if (error) { sendJSON(req, res, 200, { error: parsed.searchParams.get('error_description') || error }); return; }
91
+ const code = parsed.searchParams.get('code');
92
+ const state = parsed.searchParams.get('state');
93
+ const email = await exchangeCodexOAuthCode(code, state);
94
+ sendJSON(req, res, 200, { success: true, email });
95
+ } catch (e) {
96
+ sendJSON(req, res, 400, { error: e.message });
97
+ }
98
+ };
99
+
100
+ routes['_match'] = (method, pathOnly) => {
101
+ return routes[`${method} ${pathOnly}`] || null;
102
+ };
103
+
104
+ return routes;
105
+ }
@@ -0,0 +1,106 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { execSync } from 'child_process';
5
+
6
+ export function register(deps) {
7
+ const { sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION } = deps;
8
+ const routes = {};
9
+
10
+ routes['GET /api/import/claude-code'] = async (req, res) => {
11
+ const result = queries.importClaudeCodeConversations();
12
+ sendJSON(req, res, 200, { imported: result });
13
+ };
14
+
15
+ routes['GET /api/discover/claude-code'] = async (req, res) => {
16
+ const discovered = queries.discoverClaudeCodeConversations();
17
+ sendJSON(req, res, 200, { discovered });
18
+ };
19
+
20
+ routes['GET /api/home'] = async (req, res) => {
21
+ sendJSON(req, res, 200, { home: os.homedir(), cwd: STARTUP_CWD });
22
+ };
23
+
24
+ routes['GET /api/version'] = async (req, res) => {
25
+ sendJSON(req, res, 200, { version: PKG_VERSION });
26
+ };
27
+
28
+ routes['POST /api/clone'] = async (req, res) => {
29
+ const body = await parseBody(req);
30
+ const repo = (body.repo || '').trim();
31
+ if (!repo || !/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo)) {
32
+ sendJSON(req, res, 400, { error: 'Invalid repo format. Use org/repo or user/repo' });
33
+ return;
34
+ }
35
+ const cloneDir = STARTUP_CWD || os.homedir();
36
+ const repoName = repo.split('/')[1];
37
+ const targetPath = path.join(cloneDir, repoName);
38
+ if (fs.existsSync(targetPath)) {
39
+ sendJSON(req, res, 409, { error: `Directory already exists: ${repoName}`, path: targetPath });
40
+ return;
41
+ }
42
+ try {
43
+ const isWindows = os.platform() === 'win32';
44
+ execSync('git clone https://github.com/' + repo + '.git', {
45
+ cwd: cloneDir, encoding: 'utf-8', timeout: 120000,
46
+ stdio: ['pipe', 'pipe', 'pipe'],
47
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
48
+ shell: isWindows
49
+ });
50
+ sendJSON(req, res, 200, { ok: true, repo, path: targetPath, name: repoName });
51
+ } catch (err) {
52
+ sendJSON(req, res, 500, { error: (err.stderr || err.message || 'Clone failed').trim() });
53
+ }
54
+ };
55
+
56
+ routes['POST /api/folders'] = async (req, res) => {
57
+ const body = await parseBody(req);
58
+ const folderPath = body.path || STARTUP_CWD;
59
+ try {
60
+ const expandedPath = folderPath.startsWith('~') ? folderPath.replace('~', os.homedir()) : folderPath;
61
+ const entries = fs.readdirSync(expandedPath, { withFileTypes: true });
62
+ const folders = entries
63
+ .filter(e => e.isDirectory() && !e.name.startsWith('.'))
64
+ .map(e => ({ name: e.name }))
65
+ .sort((a, b) => a.name.localeCompare(b.name));
66
+ sendJSON(req, res, 200, { folders });
67
+ } catch (err) {
68
+ sendJSON(req, res, 400, { error: err.message });
69
+ }
70
+ };
71
+
72
+ routes['GET /api/git/check-remote-ownership'] = async (req, res) => {
73
+ try {
74
+ const isWindows = os.platform() === 'win32';
75
+ const result = execSync('git remote get-url origin' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
76
+ const remoteUrl = result.trim();
77
+ const statusResult = execSync('git status --porcelain' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
78
+ const hasChanges = statusResult.trim().length > 0;
79
+ const unpushedResult = execSync('git rev-list --count --not --remotes' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
80
+ const hasUnpushed = parseInt(unpushedResult.trim() || '0', 10) > 0;
81
+ const ownsRemote = !remoteUrl.includes('github.com/') || remoteUrl.includes(process.env.GITHUB_USER || '');
82
+ sendJSON(req, res, 200, { ownsRemote, hasChanges, hasUnpushed, remoteUrl });
83
+ } catch {
84
+ sendJSON(req, res, 200, { ownsRemote: false, hasChanges: false, hasUnpushed: false, remoteUrl: '' });
85
+ }
86
+ };
87
+
88
+ routes['POST /api/git/push'] = async (req, res) => {
89
+ try {
90
+ const isWindows = os.platform() === 'win32';
91
+ const gitCommand = isWindows
92
+ ? 'git add -A & git commit -m "Auto-commit" & git push'
93
+ : 'git add -A && git commit -m "Auto-commit" && git push';
94
+ execSync(gitCommand, { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
95
+ sendJSON(req, res, 200, { success: true });
96
+ } catch (err) {
97
+ sendJSON(req, res, 500, { error: err.message });
98
+ }
99
+ };
100
+
101
+ routes['_match'] = (method, pathOnly) => {
102
+ return routes[`${method} ${pathOnly}`] || null;
103
+ };
104
+
105
+ return routes;
106
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.758",
3
+ "version": "1.0.760",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
package/server.js CHANGED
@@ -20,6 +20,8 @@ import { findCommand, queryACPServerAgents, discoverAgents, discoverExternalACPS
20
20
  import { getGeminiOAuthCreds, startGeminiOAuth, exchangeGeminiOAuthCode, handleGeminiOAuthCallback, getGeminiOAuthStatus, getGeminiOAuthState } from './lib/oauth-gemini.js';
21
21
  import { initSpeechManager, getSpeech, ensurePocketTtsSetup, voiceCacheManager, modelDownloadState, broadcastModelProgress, ensureModelsDownloaded, eagerTTS } from './lib/speech-manager.js';
22
22
  import { register as registerSpeechRoutes } from './lib/routes-speech.js';
23
+ import { register as registerOAuthRoutes } from './lib/routes-oauth.js';
24
+ import { register as registerUtilRoutes } from './lib/routes-util.js';
23
25
  import { startCodexOAuth, exchangeCodexOAuthCode, handleCodexOAuthCallback, getCodexOAuthStatus, getCodexOAuthState, CODEX_HOME, CODEX_AUTH_FILE } from './lib/oauth-codex.js';
24
26
  import { WSOptimizer } from './lib/ws-optimizer.js';
25
27
  import { WsRouter } from './lib/ws-protocol.js';
@@ -1999,130 +2001,8 @@ const server = http.createServer(async (req, res) => {
1999
2001
  return;
2000
2002
  }
2001
2003
 
2002
- if (pathOnly === '/api/gemini-oauth/start' && req.method === 'POST') {
2003
- try {
2004
- const result = await startGeminiOAuth(req, { PORT, BASE_URL, rootDir });
2005
- sendJSON(req, res, 200, { authUrl: result.authUrl, mode: result.mode });
2006
- } catch (e) {
2007
- console.error('[gemini-oauth] /api/gemini-oauth/start failed:', e);
2008
- sendJSON(req, res, 500, { error: e.message });
2009
- }
2010
- return;
2011
- }
2012
-
2013
- if (pathOnly === '/api/gemini-oauth/status' && req.method === 'GET') {
2014
- sendJSON(req, res, 200, getGeminiOAuthState());
2015
- return;
2016
- }
2017
-
2018
- if (pathOnly === '/api/gemini-oauth/relay' && req.method === 'POST') {
2019
- try {
2020
- const body = await parseBody(req);
2021
- const { code, state: stateParam } = body;
2022
- if (!code || !stateParam) {
2023
- sendJSON(req, res, 400, { error: 'Missing code or state' });
2024
- return;
2025
- }
2026
- const email = await exchangeGeminiOAuthCode(code, stateParam);
2027
- sendJSON(req, res, 200, { success: true, email });
2028
- } catch (e) {
2029
- sendJSON(req, res, 400, { error: e.message });
2030
- }
2031
- return;
2032
- }
2033
-
2034
- if (pathOnly === '/api/gemini-oauth/complete' && req.method === 'POST') {
2035
- try {
2036
- const body = await parseBody(req);
2037
- const pastedUrl = (body.url || '').trim();
2038
- if (!pastedUrl) {
2039
- sendJSON(req, res, 400, { error: 'No URL provided' });
2040
- return;
2041
- }
2042
-
2043
- let parsed;
2044
- try { parsed = new URL(pastedUrl); } catch (_) {
2045
- sendJSON(req, res, 400, { error: 'Invalid URL. Paste the full URL from the browser address bar.' });
2046
- return;
2047
- }
2048
-
2049
- const error = parsed.searchParams.get('error');
2050
- if (error) {
2051
- const desc = parsed.searchParams.get('error_description') || error;
2052
- sendJSON(req, res, 200, { error: desc });
2053
- return;
2054
- }
2055
-
2056
- const code = parsed.searchParams.get('code');
2057
- const state = parsed.searchParams.get('state');
2058
- const email = await exchangeGeminiOAuthCode(code, state);
2059
- sendJSON(req, res, 200, { success: true, email });
2060
- } catch (e) {
2061
- sendJSON(req, res, 400, { error: e.message });
2062
- }
2063
- return;
2064
- }
2065
-
2066
- if (pathOnly === '/api/codex-oauth/start' && req.method === 'POST') {
2067
- try {
2068
- const result = await startCodexOAuth(req, { PORT, BASE_URL });
2069
- sendJSON(req, res, 200, { authUrl: result.authUrl, mode: result.mode });
2070
- } catch (e) {
2071
- console.error('[codex-oauth] /api/codex-oauth/start failed:', e);
2072
- sendJSON(req, res, 500, { error: e.message });
2073
- }
2074
- return;
2075
- }
2076
-
2077
- if (pathOnly === '/api/codex-oauth/status' && req.method === 'GET') {
2078
- sendJSON(req, res, 200, getCodexOAuthState());
2079
- return;
2080
- }
2081
-
2082
- if (pathOnly === '/api/codex-oauth/relay' && req.method === 'POST') {
2083
- try {
2084
- const body = await parseBody(req);
2085
- const { code, state: stateParam } = body;
2086
- if (!code || !stateParam) {
2087
- sendJSON(req, res, 400, { error: 'Missing code or state' });
2088
- return;
2089
- }
2090
- const email = await exchangeCodexOAuthCode(code, stateParam);
2091
- sendJSON(req, res, 200, { success: true, email });
2092
- } catch (e) {
2093
- sendJSON(req, res, 400, { error: e.message });
2094
- }
2095
- return;
2096
- }
2097
-
2098
- if (pathOnly === '/api/codex-oauth/complete' && req.method === 'POST') {
2099
- try {
2100
- const body = await parseBody(req);
2101
- const pastedUrl = (body.url || '').trim();
2102
- if (!pastedUrl) {
2103
- sendJSON(req, res, 400, { error: 'No URL provided' });
2104
- return;
2105
- }
2106
- let parsed;
2107
- try { parsed = new URL(pastedUrl); } catch (_) {
2108
- sendJSON(req, res, 400, { error: 'Invalid URL. Paste the full URL from the browser address bar.' });
2109
- return;
2110
- }
2111
- const error = parsed.searchParams.get('error');
2112
- if (error) {
2113
- const desc = parsed.searchParams.get('error_description') || error;
2114
- sendJSON(req, res, 200, { error: desc });
2115
- return;
2116
- }
2117
- const code = parsed.searchParams.get('code');
2118
- const state = parsed.searchParams.get('state');
2119
- const email = await exchangeCodexOAuthCode(code, state);
2120
- sendJSON(req, res, 200, { success: true, email });
2121
- } catch (e) {
2122
- sendJSON(req, res, 400, { error: e.message });
2123
- }
2124
- return;
2125
- }
2004
+ const oauthHandler = _oauthRoutes._match(req.method, pathOnly);
2005
+ if (oauthHandler) { await oauthHandler(req, res); return; }
2126
2006
 
2127
2007
  const agentAuthMatch = pathOnly.match(/^\/api\/agents\/([^/]+)\/auth$/);
2128
2008
  if (agentAuthMatch && req.method === 'POST') {
@@ -2296,113 +2176,12 @@ const server = http.createServer(async (req, res) => {
2296
2176
  return;
2297
2177
  }
2298
2178
 
2299
- if (pathOnly === '/api/import/claude-code' && req.method === 'GET') {
2300
- const result = queries.importClaudeCodeConversations();
2301
- sendJSON(req, res, 200, { imported: result });
2302
- return;
2303
- }
2304
-
2305
- if (pathOnly === '/api/discover/claude-code' && req.method === 'GET') {
2306
- const discovered = queries.discoverClaudeCodeConversations();
2307
- sendJSON(req, res, 200, { discovered });
2308
- return;
2309
- }
2310
-
2311
- if (pathOnly === '/api/home' && req.method === 'GET') {
2312
- sendJSON(req, res, 200, { home: os.homedir(), cwd: STARTUP_CWD });
2313
- return;
2314
- }
2315
-
2316
- if (pathOnly === '/api/version' && req.method === 'GET') {
2317
- sendJSON(req, res, 200, { version: PKG_VERSION });
2318
- return;
2319
- }
2320
-
2321
2179
  const speechHandler = _speechRoutes._match(req.method, pathOnly);
2322
2180
  if (speechHandler) { await speechHandler(req, res, pathOnly); return; }
2323
2181
 
2324
- if (pathOnly === '/api/clone' && req.method === 'POST') {
2325
- const body = await parseBody(req);
2326
- const repo = (body.repo || '').trim();
2327
- if (!repo || !/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo)) {
2328
- sendJSON(req, res, 400, { error: 'Invalid repo format. Use org/repo or user/repo' });
2329
- return;
2330
- }
2331
- const cloneDir = STARTUP_CWD || os.homedir();
2332
- const repoName = repo.split('/')[1];
2333
- const targetPath = path.join(cloneDir, repoName);
2334
- if (fs.existsSync(targetPath)) {
2335
- sendJSON(req, res, 409, { error: `Directory already exists: ${repoName}`, path: targetPath });
2336
- return;
2337
- }
2338
- try {
2339
- const isWindows = os.platform() === 'win32';
2340
- execSync('git clone https://github.com/' + repo + '.git', {
2341
- cwd: cloneDir,
2342
- encoding: 'utf-8',
2343
- timeout: 120000,
2344
- stdio: ['pipe', 'pipe', 'pipe'],
2345
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
2346
- shell: isWindows
2347
- });
2348
- sendJSON(req, res, 200, { ok: true, repo, path: targetPath, name: repoName });
2349
- } catch (err) {
2350
- const stderr = err.stderr || err.message || 'Clone failed';
2351
- sendJSON(req, res, 500, { error: stderr.trim() });
2352
- }
2353
- return;
2354
- }
2355
-
2356
- if (pathOnly === '/api/folders' && req.method === 'POST') {
2357
- const body = await parseBody(req);
2358
- const folderPath = body.path || STARTUP_CWD;
2359
- try {
2360
- const expandedPath = folderPath.startsWith('~') ?
2361
- folderPath.replace('~', os.homedir()) : folderPath;
2362
- const entries = fs.readdirSync(expandedPath, { withFileTypes: true });
2363
- const folders = entries
2364
- .filter(e => e.isDirectory() && !e.name.startsWith('.'))
2365
- .map(e => ({ name: e.name }))
2366
- .sort((a, b) => a.name.localeCompare(b.name));
2367
- sendJSON(req, res, 200, { folders });
2368
- } catch (err) {
2369
- sendJSON(req, res, 400, { error: err.message });
2370
- }
2371
- return;
2372
- }
2182
+ const utilHandler = _utilRoutes._match(req.method, pathOnly);
2183
+ if (utilHandler) { await utilHandler(req, res); return; }
2373
2184
 
2374
- if (pathOnly === '/api/git/check-remote-ownership' && req.method === 'GET') {
2375
- try {
2376
- const isWindows = os.platform() === 'win32';
2377
- const result = execSync('git remote get-url origin' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
2378
- const remoteUrl = result.trim();
2379
- const statusResult = execSync('git status --porcelain' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
2380
- const hasChanges = statusResult.trim().length > 0;
2381
- const unpushedResult = execSync('git rev-list --count --not --remotes' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
2382
- const hasUnpushed = parseInt(unpushedResult.trim() || '0', 10) > 0;
2383
- const ownsRemote = !remoteUrl.includes('github.com/') || remoteUrl.includes(process.env.GITHUB_USER || '');
2384
- sendJSON(req, res, 200, { ownsRemote, hasChanges, hasUnpushed, remoteUrl });
2385
- } catch {
2386
- sendJSON(req, res, 200, { ownsRemote: false, hasChanges: false, hasUnpushed: false, remoteUrl: '' });
2387
- }
2388
- return;
2389
- }
2390
-
2391
- if (pathOnly === '/api/git/push' && req.method === 'POST') {
2392
- try {
2393
- const isWindows = os.platform() === 'win32';
2394
- const gitCommand = isWindows
2395
- ? 'git add -A & git commit -m "Auto-commit" & git push'
2396
- : 'git add -A && git commit -m "Auto-commit" && git push';
2397
- execSync(gitCommand, { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
2398
- sendJSON(req, res, 200, { success: true });
2399
- } catch (err) {
2400
- sendJSON(req, res, 500, { error: err.message });
2401
- }
2402
- return;
2403
- }
2404
-
2405
- // ============================================================
2406
2185
  // THREAD API ENDPOINTS (ACP v0.2.3)
2407
2186
  // ============================================================
2408
2187
 
@@ -3545,6 +3324,8 @@ const wsRouter = new WsRouter();
3545
3324
 
3546
3325
  initSpeechManager({ broadcastSync, syncClients, queries });
3547
3326
  const _speechRoutes = registerSpeechRoutes({ sendJSON, parseBody, broadcastSync, debugLog });
3327
+ const _oauthRoutes = registerOAuthRoutes({ sendJSON, parseBody, PORT, BASE_URL, rootDir });
3328
+ const _utilRoutes = registerUtilRoutes({ sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION });
3548
3329
 
3549
3330
  registerConvHandlers(wsRouter, {
3550
3331
  queries, activeExecutions, rateLimitState,