agentgui 1.0.917 → 1.0.919

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 (53) hide show
  1. package/database-schema.js +0 -58
  2. package/lib/db-queries-cleanup.js +0 -12
  3. package/lib/db-queries-del.js +0 -1
  4. package/lib/db-queries.js +0 -4
  5. package/lib/http-handler.js +1 -10
  6. package/lib/plugins/acp-plugin.js +1 -2
  7. package/lib/plugins/database-plugin.js +1 -1
  8. package/lib/process-message.js +2 -2
  9. package/lib/provider-config.js +0 -16
  10. package/lib/recovery.js +2 -12
  11. package/lib/routes-agent-actions.js +2 -58
  12. package/lib/routes-debug.js +1 -7
  13. package/lib/routes-registry.js +5 -17
  14. package/lib/server-startup.js +1 -59
  15. package/lib/stream-event-handler.js +1 -3
  16. package/lib/ws-handlers-session2.js +2 -23
  17. package/lib/ws-handlers-util.js +106 -175
  18. package/lib/ws-legacy-handlers.js +1 -104
  19. package/lib/ws-setup.js +1 -3
  20. package/package.json +1 -15
  21. package/server.js +9 -26
  22. package/test.js +1 -21
  23. package/ecosystem.config.cjs +0 -22
  24. package/lib/checkpoint-manager.js +0 -182
  25. package/lib/db-queries-tools.js +0 -131
  26. package/lib/db-queries-voice.js +0 -85
  27. package/lib/oauth-codex.js +0 -164
  28. package/lib/oauth-common.js +0 -92
  29. package/lib/oauth-gemini.js +0 -199
  30. package/lib/plugins/auth-plugin.js +0 -132
  31. package/lib/plugins/speech-plugin.js +0 -72
  32. package/lib/plugins/tools-plugin.js +0 -120
  33. package/lib/pm2-manager.js +0 -170
  34. package/lib/routes-oauth.js +0 -105
  35. package/lib/routes-speech.js +0 -173
  36. package/lib/routes-tools.js +0 -184
  37. package/lib/speech-manager.js +0 -200
  38. package/lib/speech.js +0 -50
  39. package/lib/tool-install-machine.js +0 -157
  40. package/lib/tool-manager.js +0 -98
  41. package/lib/tool-provisioner.js +0 -98
  42. package/lib/tool-spawner.js +0 -163
  43. package/lib/tool-version-check.js +0 -196
  44. package/lib/tool-version-fetch.js +0 -68
  45. package/lib/ws-handlers-oauth.js +0 -76
  46. package/static/js/agent-auth-oauth.js +0 -159
  47. package/static/js/pm2-monitor.js +0 -151
  48. package/static/js/stt-handler.js +0 -147
  49. package/static/js/tool-install-machine.js +0 -155
  50. package/static/js/tools-manager-ui.js +0 -124
  51. package/static/js/tools-manager.js +0 -172
  52. package/static/js/voice-machine.js +0 -145
  53. package/static/js/voice.js +0 -134
@@ -1,163 +0,0 @@
1
- import { spawn } from 'child_process';
2
- import os from 'os';
3
- import { getCliVersion, getInstalledVersion, saveLastInstalledNpmVersion } from './tool-version-check.js';
4
- import { clearVersionCache, checkToolViaBunx } from './tool-version-fetch.js';
5
- import * as toolInstallMachine from './tool-install-machine.js';
6
-
7
- const isWindows = os.platform() === 'win32';
8
-
9
- const spawnNpmInstall = (pkg, onProgress) => new Promise((resolve) => {
10
- const cmd = isWindows ? 'npm.cmd' : 'npm';
11
- let completed = false, stderr = '', stdout = '';
12
- let proc;
13
- try {
14
- proc = spawn(cmd, ['install', '-g', pkg], { stdio: ['pipe', 'pipe', 'pipe'], timeout: 300000, shell: isWindows, windowsHide: true });
15
- } catch (err) {
16
- return resolve({ success: false, error: `Failed to spawn npm install: ${err.message}` });
17
- }
18
- if (!proc) return resolve({ success: false, error: 'Failed to spawn npm process' });
19
- const timer = setTimeout(() => { if (!completed) { completed = true; try { proc.kill('SIGKILL'); } catch (_) {} resolve({ success: false, error: 'Timeout (5min)' }); } }, 300000);
20
- const onData = (d) => { if (onProgress) onProgress({ type: 'progress', data: d.toString() }); };
21
- if (proc.stdout) proc.stdout.on('data', (d) => { stdout += d.toString(); onData(d); });
22
- if (proc.stderr) proc.stderr.on('data', (d) => { stderr += d.toString(); onData(d); });
23
- proc.on('close', (code) => {
24
- clearTimeout(timer);
25
- if (completed) return;
26
- completed = true;
27
- resolve(code === 0 ? { success: true, error: null, pkg } : { success: false, error: (stdout + stderr).substring(0, 1000) || 'Failed' });
28
- });
29
- proc.on('error', (err) => { clearTimeout(timer); if (!completed) { completed = true; resolve({ success: false, error: err.message }); } });
30
- });
31
-
32
- const BUNX_RUNNERS = [
33
- { cmd: isWindows ? 'bun.cmd' : 'bun', args: (pkg) => ['x', pkg] },
34
- { cmd: isWindows ? 'npx.cmd' : 'npx', args: (pkg) => ['-y', pkg] },
35
- ];
36
-
37
- const isMissingCmdError = (s) => /not recognized|ENOENT|command not found|cannot find/i.test(s || '');
38
-
39
- const spawnBunxOnce = (runner, pkg, onProgress) => new Promise((resolve) => {
40
- const cmd = runner.cmd;
41
- let completed = false, stderr = '', stdout = '';
42
- let lastDataTime = Date.now();
43
- let proc;
44
- try {
45
- proc = spawn(cmd, runner.args(pkg), { stdio: ['pipe', 'pipe', 'pipe'], timeout: 300000, shell: isWindows, windowsHide: true });
46
- } catch (err) {
47
- return resolve({ success: false, error: `Failed to spawn ${cmd}: ${err.message}`, missing: isMissingCmdError(err.message) });
48
- }
49
- if (!proc) return resolve({ success: false, error: `Failed to spawn ${cmd} process`, missing: true });
50
-
51
- const timer = setTimeout(() => {
52
- if (!completed) { completed = true; try { proc.kill('SIGKILL'); } catch (_) {} resolve({ success: false, error: 'Timeout (5min)' }); }
53
- }, 300000);
54
-
55
- const heartbeatTimer = setInterval(() => {
56
- if (completed) { clearInterval(heartbeatTimer); return; }
57
- const elapsed = Date.now() - lastDataTime;
58
- if (elapsed > 30000) console.warn(`[tool-manager] No output from ${cmd} ${pkg} for ${elapsed}ms - process may be hung`);
59
- }, 30000);
60
-
61
- const onData = (d) => { lastDataTime = Date.now(); if (onProgress) onProgress({ type: 'progress', data: d.toString() }); };
62
- if (proc.stdout) proc.stdout.on('data', (d) => { stdout += d.toString(); onData(d); });
63
- if (proc.stderr) proc.stderr.on('data', (d) => { stderr += d.toString(); onData(d); });
64
-
65
- proc.on('close', (code) => {
66
- clearTimeout(timer);
67
- clearInterval(heartbeatTimer);
68
- if (completed) return;
69
- completed = true;
70
- const output = stdout + stderr;
71
- const ok = [code === 0, output.includes('upgraded'), output.includes('registered'),
72
- output.includes('Hooks registered'), output.includes('successfully'),
73
- output.includes('Done'), code === 0 && !output.includes('error')].some(Boolean);
74
- resolve(ok ? { success: true, error: null, pkg } : { success: false, error: output.substring(0, 1000) || 'Failed', missing: isMissingCmdError(output) });
75
- });
76
-
77
- proc.on('error', (err) => {
78
- clearTimeout(timer);
79
- clearInterval(heartbeatTimer);
80
- if (!completed) { completed = true; resolve({ success: false, error: `Process error: ${err.message}`, missing: isMissingCmdError(err.message) }); }
81
- });
82
- });
83
-
84
- const spawnBunxProc = async (pkg, onProgress) => {
85
- let lastResult = null;
86
- for (const runner of BUNX_RUNNERS) {
87
- const result = await spawnBunxOnce(runner, pkg, onProgress);
88
- if (result.success) return result;
89
- lastResult = result;
90
- if (!result.missing) return result;
91
- }
92
- return lastResult || { success: false, error: 'No runner available' };
93
- };
94
-
95
- function spawnForTool(tool, onProgress) {
96
- const pkg = tool.installPkg || tool.pkg;
97
- return tool.category === 'cli' ? spawnNpmInstall(pkg, onProgress) : spawnBunxProc(pkg, onProgress);
98
- }
99
-
100
- async function postInstallRefresh(tool, statusCache, checkToolStatusAsync) {
101
- await new Promise(r => setTimeout(r, 500));
102
- statusCache.delete(tool.id);
103
- clearVersionCache();
104
- const version = tool.category === 'cli' ? getCliVersion(tool.pkg) : getInstalledVersion(tool.pkg, tool.pluginId, tool.frameWork);
105
- // Fetch with skipPublishedVersion=false so we have the npm version to persist
106
- const freshStatus = await checkToolStatusAsync(tool.id, false);
107
- // For plugin tools, save the npm published version as the "installed" version so future
108
- // checks don't false-positive when plugin.json versioning differs from the npm wrapper version.
109
- if (tool.category === 'plugin' && freshStatus.publishedVersion) {
110
- saveLastInstalledNpmVersion(tool.pkg, freshStatus.publishedVersion);
111
- }
112
- return { success: true, error: null, version: version || freshStatus.publishedVersion || 'unknown', ...freshStatus };
113
- }
114
-
115
- export function createInstaller(getTool, statusCache, checkToolStatusAsync) {
116
- async function install(toolId, onProgress) {
117
- const tool = getTool(toolId);
118
- if (!tool) return { success: false, error: 'Tool not found' };
119
- if (toolInstallMachine.isLocked(toolId)) return { success: false, error: 'Install in progress' };
120
- toolInstallMachine.send(toolId, { type: 'INSTALL_START' });
121
- try {
122
- const result = await spawnForTool(tool, onProgress);
123
- if (result.success) {
124
- const fresh = await postInstallRefresh(tool, statusCache, checkToolStatusAsync);
125
- toolInstallMachine.send(toolId, { type: 'INSTALL_COMPLETE', version: fresh.version });
126
- return fresh;
127
- }
128
- clearVersionCache();
129
- toolInstallMachine.send(toolId, { type: 'FAILED', error: result.error });
130
- return result;
131
- } catch (err) {
132
- clearVersionCache();
133
- toolInstallMachine.send(toolId, { type: 'FAILED', error: err.message });
134
- return { success: false, error: err.message };
135
- }
136
- }
137
-
138
- async function update(toolId, onProgress) {
139
- const tool = getTool(toolId);
140
- if (!tool) return { success: false, error: 'Tool not found' };
141
- const current = await checkToolStatusAsync(toolId);
142
- if (!current?.installed) return { success: false, error: 'Tool not installed' };
143
- if (toolInstallMachine.isLocked(toolId)) return { success: false, error: 'Install in progress' };
144
- toolInstallMachine.send(toolId, { type: 'UPDATE_START' });
145
- try {
146
- const result = await spawnForTool(tool, onProgress);
147
- if (result.success) {
148
- const fresh = await postInstallRefresh(tool, statusCache, checkToolStatusAsync);
149
- toolInstallMachine.send(toolId, { type: 'UPDATE_COMPLETE', version: fresh.version });
150
- return fresh;
151
- }
152
- clearVersionCache();
153
- toolInstallMachine.send(toolId, { type: 'FAILED', error: result.error });
154
- return result;
155
- } catch (err) {
156
- clearVersionCache();
157
- toolInstallMachine.send(toolId, { type: 'FAILED', error: err.message });
158
- return { success: false, error: err.message };
159
- }
160
- }
161
-
162
- return { install, update };
163
- }
@@ -1,196 +0,0 @@
1
- import os from 'os';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { execSync } from 'child_process';
5
-
6
- const isWindows = os.platform() === 'win32';
7
- const homeDir = os.homedir();
8
- const gmguiDir = path.join(homeDir, '.gmgui');
9
- const npmVersionsFile = path.join(gmguiDir, 'tool-npm-versions.json');
10
-
11
- /** Return the last npm-published version we successfully installed for a plugin tool pkg. */
12
- export function getLastInstalledNpmVersion(pkg) {
13
- try {
14
- if (!fs.existsSync(npmVersionsFile)) return null;
15
- return JSON.parse(fs.readFileSync(npmVersionsFile, 'utf-8'))[pkg] || null;
16
- } catch (_) {}
17
- return null;
18
- }
19
-
20
- /** Persist the npm-published version we just installed for a plugin tool pkg. */
21
- export function saveLastInstalledNpmVersion(pkg, version) {
22
- try {
23
- const data = fs.existsSync(npmVersionsFile) ? JSON.parse(fs.readFileSync(npmVersionsFile, 'utf-8')) : {};
24
- data[pkg] = version;
25
- fs.mkdirSync(gmguiDir, { recursive: true });
26
- fs.writeFileSync(npmVersionsFile, JSON.stringify(data, null, 2));
27
- } catch (_) {}
28
- }
29
-
30
- export const BIN_MAP = {
31
- '@anthropic-ai/claude-code': 'claude',
32
- 'opencode-ai': 'opencode',
33
- '@google/gemini-cli': 'gemini',
34
- '@kilocode/cli': 'kilo',
35
- '@openai/codex': 'codex',
36
- 'agent-browser': 'agent-browser',
37
- };
38
-
39
- function getClaudeInstalledPluginPath(pluginId) {
40
- try {
41
- const installedPluginsPath = path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json');
42
- if (!fs.existsSync(installedPluginsPath)) return null;
43
- const data = JSON.parse(fs.readFileSync(installedPluginsPath, 'utf-8'));
44
- const plugins = data.plugins || {};
45
- const key = Object.keys(plugins).find(k => k.endsWith('@' + pluginId));
46
- if (!key) return null;
47
- const entries = plugins[key];
48
- if (!entries || !entries.length) return null;
49
- return entries[0].installPath || null;
50
- } catch (_) {}
51
- return null;
52
- }
53
-
54
- export const FRAMEWORK_PATHS = {
55
- claude: {
56
- pluginDir: (pluginId) => getClaudeInstalledPluginPath(pluginId) || path.join(homeDir, '.claude', 'plugins', pluginId),
57
- versionFile: (pluginId) => {
58
- const installPath = getClaudeInstalledPluginPath(pluginId);
59
- return installPath ? path.join(installPath, 'plugin.json') : path.join(homeDir, '.claude', 'plugins', pluginId, 'plugin.json');
60
- },
61
- parseVersion: (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf-8')).version,
62
- },
63
- opencode: {
64
- pluginDir: (pluginId) => path.join(homeDir, '.config', 'opencode', 'agents', pluginId),
65
- markerFile: (pluginId) => path.join(homeDir, '.config', 'opencode', 'agents', pluginId + '.md'),
66
- versionFile: (pluginId) => path.join(homeDir, '.config', 'opencode', 'agents', pluginId, 'plugin.json'),
67
- parseVersion: (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf-8')).version,
68
- fallbackInstalled: true,
69
- },
70
- gemini: {
71
- pluginDir: (pluginId) => path.join(homeDir, '.gemini', 'extensions', pluginId),
72
- versionFile: (pluginId) => path.join(homeDir, '.gemini', 'extensions', pluginId, 'gemini-extension.json'),
73
- parseVersion: (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf-8')).version,
74
- },
75
- kilo: {
76
- pluginDir: (pluginId) => path.join(homeDir, '.config', 'kilo', 'agents', pluginId),
77
- markerFile: (pluginId) => path.join(homeDir, '.config', 'kilo', 'agents', pluginId + '.md'),
78
- versionFile: (pluginId) => path.join(homeDir, '.config', 'kilo', 'agents', pluginId, 'plugin.json'),
79
- parseVersion: (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf-8')).version,
80
- fallbackInstalled: true,
81
- },
82
- codex: {
83
- pluginDir: (pluginId) => path.join(homeDir, '.codex', 'plugins', pluginId),
84
- versionFile: (pluginId) => path.join(homeDir, '.codex', 'plugins', pluginId, 'plugin.json'),
85
- parseVersion: (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf-8')).version,
86
- fallbackInstalled: true,
87
- },
88
- };
89
-
90
- function tryReadVersion(fwConfig, pluginId) {
91
- try {
92
- const vFile = fwConfig.versionFile(pluginId);
93
- if (fs.existsSync(vFile)) {
94
- const ver = fwConfig.parseVersion(vFile);
95
- if (ver) return ver;
96
- }
97
- } catch (_) {}
98
- return null;
99
- }
100
-
101
- function tryBunCache(pkg) {
102
- try {
103
- const cachePath = path.join(homeDir, '.gmweb/cache/.bun/install/cache');
104
- const dirs = fs.readdirSync(cachePath).filter(d => d.startsWith(pkg + '@'));
105
- const latest = dirs.sort().reverse()[0];
106
- if (latest) {
107
- const pkgJson = JSON.parse(fs.readFileSync(path.join(cachePath, latest, 'package.json'), 'utf-8'));
108
- if (pkgJson.version) return pkgJson.version;
109
- }
110
- } catch (_) {}
111
- return null;
112
- }
113
-
114
- export function getInstalledVersion(pkg, pluginId = null, frameWork = null, tools = []) {
115
- try {
116
- const tool = tools.find(t => t.pkg === pkg);
117
- const actualPluginId = pluginId || tool?.pluginId || pkg;
118
- const actualFrameWork = frameWork || tool?.frameWork;
119
- const frameworks = actualFrameWork ? [actualFrameWork] : Object.keys(FRAMEWORK_PATHS);
120
- for (const fw of frameworks) {
121
- const fwConfig = FRAMEWORK_PATHS[fw];
122
- if (!fwConfig) continue;
123
- const version = tryReadVersion(fwConfig, actualPluginId);
124
- if (version) return version;
125
- if (fwConfig.fallbackInstalled) {
126
- const hasMarker = fwConfig.markerFile && fs.existsSync(fwConfig.markerFile(actualPluginId));
127
- const hasDir = fs.existsSync(fwConfig.pluginDir(actualPluginId));
128
- if (hasMarker || hasDir) {
129
- const cached = tryBunCache(pkg);
130
- if (cached) return cached;
131
- return 'installed';
132
- }
133
- }
134
- }
135
- } catch (_) {}
136
- return null;
137
- }
138
-
139
- export function checkCliInstalled(pkg) {
140
- try {
141
- const cmd = isWindows ? 'where' : 'which';
142
- const bin = BIN_MAP[pkg];
143
- if (bin) {
144
- execSync(`${cmd} ${bin}`, { stdio: 'pipe', timeout: 10000, windowsHide: true });
145
- return true;
146
- }
147
- } catch (_) {}
148
- return false;
149
- }
150
-
151
- export function getCliVersion(pkg) {
152
- try {
153
- const bin = BIN_MAP[pkg];
154
- if (!bin) return null;
155
- try {
156
- const versionFlag = pkg === 'agent-browser' ? '-V' : '--version';
157
- const out = execSync(`${bin} ${versionFlag}`, { stdio: 'pipe', timeout: 15000, encoding: 'utf8', windowsHide: true });
158
- const match = out.match(/(\d+\.\d+\.\d+)/);
159
- if (match) {
160
- console.log(`[tool-manager] CLI ${pkg} (${bin}) version: ${match[1]}`);
161
- return match[1];
162
- }
163
- } catch (err) {
164
- console.log(`[tool-manager] CLI ${pkg} (${bin}) version detection failed: ${err.message.split('\n')[0]}`);
165
- }
166
- } catch (err) {
167
- console.log(`[tool-manager] Error in getCliVersion for ${pkg}:`, err.message);
168
- }
169
- return null;
170
- }
171
-
172
- export function checkToolInstalled(pluginId, frameWork = null) {
173
- try {
174
- const frameworks = frameWork ? [frameWork] : Object.keys(FRAMEWORK_PATHS);
175
- for (const fw of frameworks) {
176
- const fwConfig = FRAMEWORK_PATHS[fw];
177
- if (!fwConfig) continue;
178
- if (fs.existsSync(fwConfig.pluginDir(pluginId))) return true;
179
- if (fwConfig.markerFile && fs.existsSync(fwConfig.markerFile(pluginId))) return true;
180
- }
181
- } catch (_) {}
182
- return false;
183
- }
184
-
185
- export function compareVersions(v1, v2) {
186
- if (!v1 || !v2) return false;
187
- const parts1 = v1.split('.').map(Number);
188
- const parts2 = v2.split('.').map(Number);
189
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
190
- const p1 = parts1[i] || 0;
191
- const p2 = parts2[i] || 0;
192
- if (p1 < p2) return true;
193
- if (p1 > p2) return false;
194
- }
195
- return false;
196
- }
@@ -1,68 +0,0 @@
1
- import https from 'https';
2
- import { checkCliInstalled, getCliVersion, checkToolInstalled, compareVersions, getInstalledVersion, getLastInstalledNpmVersion } from './tool-version-check.js';
3
-
4
- const versionCache = new Map();
5
-
6
- export function getPublishedVersion(pkg) {
7
- const cacheKey = `published-${pkg}`;
8
- const cached = versionCache.get(cacheKey);
9
- if (cached && Date.now() - cached.timestamp < 3600000) {
10
- return cached.version;
11
- }
12
- return null;
13
- }
14
-
15
- export async function fetchPublishedVersion(pkg) {
16
- const cacheKey = `published-${pkg}`;
17
- const cached = versionCache.get(cacheKey);
18
- if (cached && Date.now() - cached.timestamp < 3600000) {
19
- return cached.version;
20
- }
21
- try {
22
- const version = await new Promise((resolve, reject) => {
23
- const url = `https://registry.npmjs.org/${pkg}/latest`;
24
- https.get(url, { headers: { 'User-Agent': 'agentgui-version-check/1.0' } }, (res) => {
25
- let data = '';
26
- res.on('data', chunk => { data += chunk; });
27
- res.on('end', () => {
28
- try { resolve(JSON.parse(data).version || null); } catch (e) { reject(e); }
29
- });
30
- }).on('error', reject);
31
- });
32
- if (version) versionCache.set(cacheKey, { version, timestamp: Date.now() });
33
- return version;
34
- } catch (_) {}
35
- return null;
36
- }
37
-
38
- export function clearVersionCache() {
39
- versionCache.clear();
40
- }
41
-
42
- export async function checkToolViaBunx(pkg, pluginId = null, category = 'plugin', frameWork = null, skipPublishedVersion = false, tools = []) {
43
- try {
44
- const isCli = category === 'cli';
45
- const installed = isCli ? checkCliInstalled(pkg) : checkToolInstalled(pluginId || pkg, frameWork);
46
- let installedVersion = isCli ? getCliVersion(pkg) : getInstalledVersion(pkg, pluginId, frameWork, tools);
47
- // For plugin tools, prefer the last npm version we successfully installed over the plugin.json
48
- // version. This prevents infinite re-update loops when plugin.json versioning differs from
49
- // the npm wrapper package version (e.g. gm-codex@2.0.506 installs gm plugin.json@2.0.241).
50
- if (!isCli) {
51
- const lastNpm = getLastInstalledNpmVersion(pkg);
52
- if (lastNpm) installedVersion = lastNpm;
53
- }
54
- let publishedVersion = null;
55
- if (!skipPublishedVersion) {
56
- publishedVersion = await fetchPublishedVersion(pkg);
57
- }
58
- const needsUpdate = installed && publishedVersion && compareVersions(installedVersion, publishedVersion);
59
- const isUpToDate = installed && !needsUpdate;
60
- return { installed, isUpToDate, upgradeNeeded: needsUpdate, output: 'version-check', installedVersion, publishedVersion };
61
- } catch (err) {
62
- console.log(`[tool-manager] Error checking ${pkg}:`, err.message);
63
- const isCli = category === 'cli';
64
- const installed = isCli ? checkCliInstalled(pkg) : checkToolInstalled(pluginId || pkg, frameWork);
65
- const installedVersion = isCli ? getCliVersion(pkg) : getInstalledVersion(pkg, pluginId, frameWork, tools);
66
- return { installed, isUpToDate: false, upgradeNeeded: false, output: '', installedVersion, publishedVersion: null };
67
- }
68
- }
@@ -1,76 +0,0 @@
1
- function err(code, message) { const e = new Error(message); e.code = code; throw e; }
2
-
3
- export function register(router, deps) {
4
- const { startGeminiOAuth, exchangeGeminiOAuthCode, geminiOAuthState,
5
- startCodexOAuth, exchangeCodexOAuthCode, codexOAuthState } = deps;
6
-
7
- router.handle('gemini.start', async () => {
8
- try {
9
- const result = await startGeminiOAuth();
10
- return { authUrl: result.authUrl, mode: result.mode };
11
- } catch (e) { err(500, e.message); }
12
- });
13
-
14
- router.handle('gemini.status', () => {
15
- return typeof geminiOAuthState === 'function' ? geminiOAuthState() : geminiOAuthState;
16
- });
17
-
18
- router.handle('gemini.relay', async (p) => {
19
- const { code, state } = p;
20
- if (!code || !state) err(400, 'Missing code or state');
21
- try {
22
- const email = await exchangeGeminiOAuthCode(code, state);
23
- return { success: true, email };
24
- } catch (e) { err(400, e.message); }
25
- });
26
-
27
- router.handle('gemini.complete', async (p) => {
28
- const pastedUrl = (p.url || '').trim();
29
- if (!pastedUrl) err(400, 'No URL provided');
30
- let parsed;
31
- try { parsed = new URL(pastedUrl); } catch { err(400, 'Invalid URL. Paste the full URL from the browser address bar.'); }
32
- const urlError = parsed.searchParams.get('error');
33
- if (urlError) return { error: parsed.searchParams.get('error_description') || urlError };
34
- const code = parsed.searchParams.get('code');
35
- const state = parsed.searchParams.get('state');
36
- try {
37
- const email = await exchangeGeminiOAuthCode(code, state);
38
- return { success: true, email };
39
- } catch (e) { err(400, e.message); }
40
- });
41
-
42
- router.handle('codex.start', async () => {
43
- try {
44
- const result = await startCodexOAuth();
45
- return { authUrl: result.authUrl, mode: result.mode };
46
- } catch (e) { err(500, e.message); }
47
- });
48
-
49
- router.handle('codex.status', () => {
50
- return typeof codexOAuthState === 'function' ? codexOAuthState() : codexOAuthState;
51
- });
52
-
53
- router.handle('codex.relay', async (p) => {
54
- const { code, state } = p;
55
- if (!code || !state) err(400, 'Missing code or state');
56
- try {
57
- const email = await exchangeCodexOAuthCode(code, state);
58
- return { success: true, email };
59
- } catch (e) { err(400, e.message); }
60
- });
61
-
62
- router.handle('codex.complete', async (p) => {
63
- const pastedUrl = (p.url || '').trim();
64
- if (!pastedUrl) err(400, 'No URL provided');
65
- let parsed;
66
- try { parsed = new URL(pastedUrl); } catch { err(400, 'Invalid URL. Paste the full URL from the browser address bar.'); }
67
- const urlError = parsed.searchParams.get('error');
68
- if (urlError) return { error: parsed.searchParams.get('error_description') || urlError };
69
- const code = parsed.searchParams.get('code');
70
- const state = parsed.searchParams.get('state');
71
- try {
72
- const email = await exchangeCodexOAuthCode(code, state);
73
- return { success: true, email };
74
- } catch (e) { err(400, e.message); }
75
- });
76
- }
@@ -1,159 +0,0 @@
1
- (function() {
2
- var AUTH_CONV_ID = '__agent_auth__';
3
- var oauthPollInterval = null, oauthPollTimeout = null, oauthFallbackTimer = null;
4
-
5
- function state() { return window.__agentAuthState; }
6
-
7
- function cleanupOAuthPolling() {
8
- if (oauthPollInterval) { clearInterval(oauthPollInterval); oauthPollInterval = null; }
9
- if (oauthPollTimeout) { clearTimeout(oauthPollTimeout); oauthPollTimeout = null; }
10
- if (oauthFallbackTimer) { clearTimeout(oauthFallbackTimer); oauthFallbackTimer = null; }
11
- }
12
-
13
- function showOAuthWaitingModal() {
14
- removeOAuthModal();
15
- var overlay = document.createElement('div');
16
- overlay.id = 'oauthWaitingModal';
17
- overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:9999;';
18
- overlay.innerHTML = '<div style="background:var(--color-bg-secondary,#1f2937);border-radius:1rem;padding:2rem;max-width:28rem;width:calc(100% - 2rem);box-shadow:0 25px 50px rgba(0,0,0,0.5);color:var(--color-text-primary,white);font-family:system-ui,sans-serif;" onclick="event.stopPropagation()">' +
19
- '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">' +
20
- '<h2 style="font-size:1.125rem;font-weight:700;margin:0;">Google Sign-In</h2>' +
21
- '<button id="oauthWaitingClose" style="background:none;border:none;color:var(--color-text-secondary,#9ca3af);font-size:1.5rem;cursor:pointer;padding:0;line-height:1;">\u00d7</button></div>' +
22
- '<div id="oauthWaitingContent" style="text-align:center;padding:1.5rem 0;">' +
23
- '<div style="font-size:2rem;margin-bottom:1rem;animation:pulse 2s infinite;">&#9203;</div>' +
24
- '<p style="font-size:0.85rem;color:var(--color-text-secondary,#d1d5db);margin:0 0 0.5rem;">Waiting for Google sign-in to complete...</p>' +
25
- '<p style="font-size:0.75rem;color:var(--color-text-secondary,#6b7280);margin:0;">Complete the sign-in in the tab that just opened.</p>' +
26
- '<p style="font-size:0.75rem;color:var(--color-text-secondary,#6b7280);margin:0.25rem 0 0;">This dialog will close automatically when done.</p></div>' +
27
- '<div id="oauthPasteFallback" style="display:none;">' +
28
- '<div style="margin-bottom:1rem;padding:1rem;background:var(--color-bg-tertiary,rgba(255,255,255,0.05));border-radius:0.5rem;">' +
29
- '<p style="font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin:0 0 0.5rem;">The automatic relay did not complete. This can happen when accessing the server remotely.</p>' +
30
- '<p style="font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin:0;">Copy the <span style="color:white;font-weight:600;">entire URL</span> from the sign-in tab and paste it below.</p></div>' +
31
- '<input type="text" id="oauthPasteInput" placeholder="http://localhost:3000/gm/oauth2callback?code=..." style="width:100%;box-sizing:border-box;padding:0.75rem 1rem;background:var(--color-bg-primary,#374151);border:1px solid var(--color-border,#4b5563);border-radius:0.5rem;color:var(--color-text-primary,white);font-size:0.8rem;font-family:monospace;outline:none;" />' +
32
- '<p id="oauthPasteError" style="font-size:0.75rem;color:#ef4444;margin:0.5rem 0 0;display:none;"></p></div>' +
33
- '<div style="display:flex;gap:0.75rem;margin-top:1.25rem;">' +
34
- '<button id="oauthWaitingCancel" style="flex:1;padding:0.625rem;border-radius:0.5rem;border:1px solid var(--color-border,#4b5563);background:transparent;color:var(--color-text-primary,white);font-size:0.8rem;cursor:pointer;font-weight:600;">Cancel</button>' +
35
- '<button id="oauthPasteSubmit" style="flex:1;padding:0.625rem;border-radius:0.5rem;border:none;background:var(--color-primary,#3b82f6);color:white;font-size:0.8rem;cursor:pointer;font-weight:600;display:none;">Complete Sign-In</button></div>' +
36
- '<style>@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}</style></div>';
37
- document.body.appendChild(overlay);
38
- var dismiss = function() { cleanupOAuthPolling(); state().authRunning = false; removeOAuthModal(); };
39
- document.getElementById('oauthWaitingClose').addEventListener('click', dismiss);
40
- document.getElementById('oauthWaitingCancel').addEventListener('click', dismiss);
41
- document.getElementById('oauthPasteSubmit').addEventListener('click', submitOAuthPasteUrl);
42
- }
43
-
44
- function showOAuthPasteFallback() {
45
- var fallback = document.getElementById('oauthPasteFallback');
46
- var waitContent = document.getElementById('oauthWaitingContent');
47
- var submitBtn = document.getElementById('oauthPasteSubmit');
48
- if (fallback) fallback.style.display = 'block';
49
- if (waitContent) waitContent.style.display = 'none';
50
- if (submitBtn) submitBtn.style.display = 'block';
51
- var input = document.getElementById('oauthPasteInput');
52
- if (input) {
53
- input.addEventListener('keydown', function(e) { if (e.key === 'Enter') submitOAuthPasteUrl(); });
54
- setTimeout(function() { input.focus(); }, 100);
55
- }
56
- }
57
-
58
- function removeOAuthModal() {
59
- var el = document.getElementById('oauthWaitingModal');
60
- if (el) el.remove();
61
- }
62
-
63
- function submitOAuthPasteUrl() {
64
- var input = document.getElementById('oauthPasteInput');
65
- var errorEl = document.getElementById('oauthPasteError');
66
- var submitBtn = document.getElementById('oauthPasteSubmit');
67
- if (!input) return;
68
- var url = input.value.trim();
69
- if (!url) {
70
- if (errorEl) { errorEl.textContent = 'Please paste the URL from the redirected page.'; errorEl.style.display = 'block'; }
71
- return;
72
- }
73
- if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Verifying...'; }
74
- if (errorEl) errorEl.style.display = 'none';
75
- window.wsClient.rpc('gemini.complete', { url: url })
76
- .then(function(data) {
77
- if (data.success) {
78
- cleanupOAuthPolling();
79
- state().authRunning = false;
80
- removeOAuthModal();
81
- state().refresh();
82
- } else {
83
- if (errorEl) { errorEl.textContent = data.error || 'Failed to complete authentication.'; errorEl.style.display = 'block'; }
84
- if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Complete Sign-In'; }
85
- }
86
- }).catch(function(e) {
87
- if (errorEl) { errorEl.textContent = e.message; errorEl.style.display = 'block'; }
88
- if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Complete Sign-In'; }
89
- });
90
- }
91
-
92
- function triggerAuth(agentId) {
93
- if (state().authRunning) return;
94
- window.wsClient.rpc('agent.auth', { id: agentId })
95
- .then(function(data) {
96
- if (data.ok) {
97
- state().authRunning = true; showTerminalTab(); switchToTerminalView();
98
- var term = getTerminal();
99
- if (term) { term.clear(); term.writeln('\x1b[36m[authenticating ' + agentId + ']\x1b[0m\r\n'); }
100
- if (data.authUrl) {
101
- window.open(data.authUrl, '_blank');
102
- if (agentId === 'gemini') {
103
- showOAuthWaitingModal();
104
- cleanupOAuthPolling();
105
- oauthPollInterval = setInterval(function() {
106
- window.wsClient.rpc('gemini.status')
107
- .then(function(status) {
108
- if (status.status === 'success') {
109
- cleanupOAuthPolling(); state().authRunning = false; removeOAuthModal(); state().refresh();
110
- } else if (status.status === 'error') {
111
- cleanupOAuthPolling(); state().authRunning = false; removeOAuthModal();
112
- }
113
- }).catch(function() {});
114
- }, 1500);
115
- oauthFallbackTimer = setTimeout(function() {
116
- if (state().authRunning) showOAuthPasteFallback();
117
- }, 30000);
118
- oauthPollTimeout = setTimeout(function() {
119
- cleanupOAuthPolling();
120
- if (state().authRunning) { state().authRunning = false; removeOAuthModal(); }
121
- }, 5 * 60 * 1000);
122
- }
123
- }
124
- }
125
- }).catch(function() {});
126
- }
127
-
128
- function onWsMessage(e) {
129
- var data = e.detail;
130
- if (!data || data.conversationId !== AUTH_CONV_ID) return;
131
- if (data.type === 'script_started') {
132
- state().authRunning = true; showTerminalTab(); switchToTerminalView();
133
- var term = getTerminal();
134
- if (term) { term.clear(); term.writeln('\x1b[36m[authenticating ' + (data.agentId || '') + ']\x1b[0m\r\n'); }
135
- } else if (data.type === 'script_output') {
136
- showTerminalTab();
137
- var term = getTerminal();
138
- if (term) term.write(data.data);
139
- } else if (data.type === 'script_stopped') {
140
- state().authRunning = false;
141
- removeOAuthModal();
142
- cleanupOAuthPolling();
143
- var term = getTerminal();
144
- var msg = data.error ? data.error : ('exited with code ' + (data.code || 0));
145
- if (term) term.writeln('\r\n\x1b[90m[auth ' + msg + ']\x1b[0m');
146
- setTimeout(function() { state().refresh(); }, 1000);
147
- }
148
- }
149
-
150
- function showTerminalTab() { var t = document.getElementById('terminalTabBtn'); if (t) t.style.display = ''; }
151
- function switchToTerminalView() {
152
- var bar = document.getElementById('viewToggleBar');
153
- if (!bar) return;
154
- var t = bar.querySelector('[data-view="terminal"]'); if (t) t.click();
155
- }
156
- function getTerminal() { return window.scriptRunner ? window.scriptRunner.getTerminal() : null; }
157
-
158
- window.__agentAuthOAuth = { triggerAuth: triggerAuth, onWsMessage: onWsMessage };
159
- })();