Compare commits
5 Commits
18d0874142
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f4cd16b89 | ||
|
|
09688a09ca | ||
|
|
1cc3f61b81 | ||
|
|
3a72a262a9 | ||
|
|
e952cf0a42 |
10
package-lock.json
generated
@@ -24,6 +24,7 @@
|
|||||||
"@uiw/react-codemirror": "^4.23.13",
|
"@uiw/react-codemirror": "^4.23.13",
|
||||||
"@xterm/addon-clipboard": "^0.1.0",
|
"@xterm/addon-clipboard": "^0.1.0",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
|
"@xterm/addon-web-links": "^0.11.0",
|
||||||
"@xterm/addon-webgl": "^0.18.0",
|
"@xterm/addon-webgl": "^0.18.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
@@ -3024,6 +3025,15 @@
|
|||||||
"@xterm/xterm": "^5.0.0"
|
"@xterm/xterm": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xterm/addon-web-links": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@xterm/xterm": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xterm/addon-webgl": {
|
"node_modules/@xterm/addon-webgl": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.18.0.tgz",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
"@uiw/react-codemirror": "^4.23.13",
|
"@uiw/react-codemirror": "^4.23.13",
|
||||||
"@xterm/addon-clipboard": "^0.1.0",
|
"@xterm/addon-clipboard": "^0.1.0",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
|
"@xterm/addon-web-links": "^0.11.0",
|
||||||
"@xterm/addon-webgl": "^0.18.0",
|
"@xterm/addon-webgl": "^0.18.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
|
|||||||
BIN
public/logo-128.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/logo-256.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
public/logo-32.png
Normal file
|
After Width: | Height: | Size: 496 B |
BIN
public/logo-512.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
public/logo-64.png
Normal file
|
After Width: | Height: | Size: 870 B |
@@ -1,9 +1,17 @@
|
|||||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg
|
||||||
<rect width="32" height="32" rx="8" fill="hsl(262.1 83.3% 57.8%)"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path d="M8 9C8 8.44772 8.44772 8 9 8H23C23.5523 8 24 8.44772 24 9V18C24 18.5523 23.5523 19 23 19H12L8 23V9Z"
|
width="32"
|
||||||
stroke="white"
|
height="32"
|
||||||
stroke-width="2"
|
viewBox="0 0 32 32"
|
||||||
stroke-linecap="round"
|
fill="none"
|
||||||
stroke-linejoin="round"
|
>
|
||||||
fill="none"/>
|
<rect width="32" height="32" rx="8" fill="hsl(221.2 83.2% 53.3%)"/>
|
||||||
</svg>
|
<path
|
||||||
|
d="M8 9C8 8.44772 8.44772 8 9 8H23C23.5523 8 24 8.44772 24 9V18C24 18.5523 23.5523 19 23 19H12L8 23V9Z"
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 422 B After Width: | Height: | Size: 413 B |
@@ -4,7 +4,7 @@ import path from 'path';
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { apiKeysDb, githubTokensDb } from '../database/db.js';
|
import { userDb, apiKeysDb, githubTokensDb } from '../database/db.js';
|
||||||
import { addProjectManually } from '../projects.js';
|
import { addProjectManually } from '../projects.js';
|
||||||
import { queryClaudeSDK } from '../claude-sdk.js';
|
import { queryClaudeSDK } from '../claude-sdk.js';
|
||||||
import { spawnCursor } from '../cursor-cli.js';
|
import { spawnCursor } from '../cursor-cli.js';
|
||||||
@@ -12,8 +12,35 @@ import { Octokit } from '@octokit/rest';
|
|||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Middleware to validate API key for external requests
|
/**
|
||||||
|
* Middleware to authenticate agent API requests.
|
||||||
|
*
|
||||||
|
* Supports two authentication modes:
|
||||||
|
* 1. Platform mode (VITE_IS_PLATFORM=true): For managed/hosted deployments where
|
||||||
|
* authentication is handled by an external proxy. Requests are trusted and
|
||||||
|
* the default user context is used.
|
||||||
|
*
|
||||||
|
* 2. API key mode (default): For self-hosted deployments where users authenticate
|
||||||
|
* via API keys created in the UI. Keys are validated against the local database.
|
||||||
|
*/
|
||||||
const validateExternalApiKey = (req, res, next) => {
|
const validateExternalApiKey = (req, res, next) => {
|
||||||
|
// Platform mode: Authentication is handled externally (e.g., by a proxy layer).
|
||||||
|
// Trust the request and use the default user context.
|
||||||
|
if (process.env.VITE_IS_PLATFORM === 'true') {
|
||||||
|
try {
|
||||||
|
const user = userDb.getFirstUser();
|
||||||
|
if (!user) {
|
||||||
|
return res.status(500).json({ error: 'Platform mode: No user found in database' });
|
||||||
|
}
|
||||||
|
req.user = user;
|
||||||
|
return next();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Platform mode error:', error);
|
||||||
|
return res.status(500).json({ error: 'Platform mode: Failed to fetch user' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self-hosted mode: Validate API key from header or query parameter
|
||||||
const apiKey = req.headers['x-api-key'] || req.query.apiKey;
|
const apiKey = req.headers['x-api-key'] || req.query.apiKey;
|
||||||
|
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
|
|||||||
@@ -89,7 +89,8 @@ function AppContent() {
|
|||||||
window.navigator.standalone ||
|
window.navigator.standalone ||
|
||||||
document.referrer.includes('android-app://');
|
document.referrer.includes('android-app://');
|
||||||
setIsPWA(isStandalone);
|
setIsPWA(isStandalone);
|
||||||
|
document.addEventListener('touchstart', {});
|
||||||
|
|
||||||
// Add class to html and body for CSS targeting
|
// Add class to html and body for CSS targeting
|
||||||
if (isStandalone) {
|
if (isStandalone) {
|
||||||
document.documentElement.classList.add('pwa-mode');
|
document.documentElement.classList.add('pwa-mode');
|
||||||
@@ -966,4 +967,4 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
import { FitAddon } from '@xterm/addon-fit';
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
import { ClipboardAddon } from '@xterm/addon-clipboard';
|
|
||||||
import { WebglAddon } from '@xterm/addon-webgl';
|
import { WebglAddon } from '@xterm/addon-webgl';
|
||||||
|
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||||
import '@xterm/xterm/css/xterm.css';
|
import '@xterm/xterm/css/xterm.css';
|
||||||
|
|
||||||
const xtermStyles = `
|
const xtermStyles = `
|
||||||
@@ -267,11 +267,12 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
|
|||||||
});
|
});
|
||||||
|
|
||||||
fitAddon.current = new FitAddon();
|
fitAddon.current = new FitAddon();
|
||||||
const clipboardAddon = new ClipboardAddon();
|
|
||||||
const webglAddon = new WebglAddon();
|
const webglAddon = new WebglAddon();
|
||||||
|
const webLinksAddon = new WebLinksAddon();
|
||||||
|
|
||||||
terminal.current.loadAddon(fitAddon.current);
|
terminal.current.loadAddon(fitAddon.current);
|
||||||
terminal.current.loadAddon(clipboardAddon);
|
terminal.current.loadAddon(webLinksAddon);
|
||||||
|
// Note: ClipboardAddon removed - we handle clipboard operations manually in attachCustomKeyEventHandler
|
||||||
|
|
||||||
try {
|
try {
|
||||||
terminal.current.loadAddon(webglAddon);
|
terminal.current.loadAddon(webglAddon);
|
||||||
|
|||||||