Skip to content

Commit 6afc7b4

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/model-error-ux
2 parents c205bd4 + 4574556 commit 6afc7b4

17 files changed

Lines changed: 258 additions & 129 deletions

File tree

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ body:
1212
id: version
1313
attributes:
1414
label: What version of eigent are you using?
15-
placeholder: E.g., 0.0.85
15+
placeholder: E.g., 0.0.86
1616
validations:
1717
required: true
1818

.github/workflows/build.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ jobs:
116116
fi
117117
ulimit -n 65536 2>/dev/null || ulimit -n 10240 2>/dev/null || true
118118
echo "File descriptor limit: $(ulimit -n) (hard: $(ulimit -Hn 2>/dev/null || echo 'N/A'))"
119-
npm run build -- --arch ${{ matrix.arch }}
119+
npm run build -- --${{ matrix.arch }}
120120
env:
121121
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
122122
CSC_LINK: ${{ secrets.CERT_P12 }}
@@ -125,6 +125,7 @@ jobs:
125125
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
126126
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
127127
VITE_BASE_URL: ${{ secrets.VITE_BASE_URL }}
128+
VITE_PROXY_URL: ${{ secrets.VITE_PROXY_URL }}
128129
VITE_STACK_PROJECT_ID: ${{ secrets.VITE_STACK_PROJECT_ID }}
129130
VITE_STACK_PUBLISHABLE_CLIENT_KEY: ${{ secrets.VITE_STACK_PUBLISHABLE_CLIENT_KEY }}
130131
VITE_STACK_SECRET_SERVER_KEY: ${{ secrets.VITE_STACK_SECRET_SERVER_KEY }}
@@ -134,10 +135,11 @@ jobs:
134135
- name: Build Release Files (Windows without signing)
135136
if: runner.os == 'Windows'
136137
timeout-minutes: 90
137-
run: npm run build -- --arch ${{ matrix.arch }}
138+
run: npm run build -- --${{ matrix.arch }}
138139
env:
139140
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
140141
VITE_BASE_URL: ${{ secrets.VITE_BASE_URL }}
142+
VITE_PROXY_URL: ${{ secrets.VITE_PROXY_URL }}
141143
VITE_STACK_PROJECT_ID: ${{ secrets.VITE_STACK_PROJECT_ID }}
142144
VITE_STACK_PUBLISHABLE_CLIENT_KEY: ${{ secrets.VITE_STACK_PUBLISHABLE_CLIENT_KEY }}
143145
VITE_STACK_SECRET_SERVER_KEY: ${{ secrets.VITE_STACK_SECRET_SERVER_KEY }}
@@ -151,6 +153,7 @@ jobs:
151153
env:
152154
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
153155
VITE_BASE_URL: ${{ secrets.VITE_BASE_URL }}
156+
VITE_PROXY_URL: ${{ secrets.VITE_PROXY_URL }}
154157
VITE_STACK_PROJECT_ID: ${{ secrets.VITE_STACK_PROJECT_ID }}
155158
VITE_STACK_PUBLISHABLE_CLIENT_KEY: ${{ secrets.VITE_STACK_PUBLISHABLE_CLIENT_KEY }}
156159
VITE_STACK_SECRET_SERVER_KEY: ${{ secrets.VITE_STACK_SECRET_SERVER_KEY }}
@@ -344,7 +347,7 @@ jobs:
344347
files: |
345348
gh-release-assets/*
346349
347-
# Extract version from tag (e.g., v0.0.85 -> 0.0.85)
350+
# Extract version from tag (e.g., v0.0.85 -> 0.0.86)
348351
- name: Extract version
349352
if: startsWith(github.ref, 'refs/tags/')
350353
id: version

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Built on [CAMEL-AI][camel-site]'s acclaimed open-source project, our system intr
3737
-**Zero Setup** - No technical configuration required
3838
-**Multi-Agent Coordination** - Handle complex multi-agent workflows
3939
-**Enterprise Feature** - SSO/Access control
40-
-**Local Deploymen**t
40+
-**Local Deployment**
4141
-**Open Source**
4242
-**Custom Model Support**
4343
-**MCP Integration**
@@ -70,7 +70,7 @@ Built on [CAMEL-AI][camel-site]'s acclaimed open-source project, our system intr
7070
- [🗺️ Roadmap - Open Source Cowork](#%EF%B8%8F-roadmap---open-source-Cowork)
7171
- [📖 Contributing](#-contributing)
7272
- [Main Contributors](#main-contributors)
73-
- [Distinguished amabssador](#distinguished-amabssador)
73+
- [Distinguished ambassador](#distinguished-ambassador)
7474
- [Ecosystem](#ecosystem)
7575
- [📄 Open Source License](#-open-source-license)
7676
- [🌐 Community & contact](#-community--contact)

backend/app/agent/toolkit/terminal_toolkit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
# App version - should match electron app version
4343
# TODO: Consider getting this from a shared config
44-
APP_VERSION = "0.0.85"
44+
APP_VERSION = "0.0.86"
4545

4646

4747
def get_terminal_base_venv_path() -> str:

electron/main/utils/process.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// limitations under the License.
1313
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
1414

15-
import { execSync, spawn } from 'child_process';
15+
import { execFileSync, execSync, spawn } from 'child_process';
1616
import { app } from 'electron';
1717
import log from 'electron-log';
1818
import fs from 'fs';
@@ -111,6 +111,24 @@ export async function getBinaryPath(name?: string): Promise<string> {
111111
}
112112
}
113113

114+
// In dev: prefer system PATH uv (e.g. Homebrew) for speed - reuses existing cache
115+
if (!app.isPackaged && name === 'uv') {
116+
try {
117+
const whichCmd = process.platform === 'win32' ? 'where.exe' : 'which';
118+
const found = execFileSync(whichCmd, [name], {
119+
encoding: 'utf-8',
120+
stdio: ['pipe', 'pipe', 'pipe'],
121+
}).trim();
122+
const systemPath = found.split(/\r?\n/)[0]?.trim();
123+
if (systemPath && fs.existsSync(systemPath)) {
124+
log.info(`[DEV] Using system uv from PATH: ${systemPath}`);
125+
return systemPath;
126+
}
127+
} catch {
128+
// Not found on PATH, fall through to .eigent/bin
129+
}
130+
}
131+
114132
const binariesDir = path.join(os.homedir(), '.eigent', 'bin');
115133

116134
// Ensure .eigent/bin directory exists
@@ -943,7 +961,8 @@ export function ensureNpmWrappersForBrowserToolkit(
943961
const eigentBinDir = path.join(os.homedir(), '.eigent', 'bin');
944962
fs.mkdirSync(eigentBinDir, { recursive: true });
945963

946-
const wrapperVersion = '1';
964+
// Store wrapper target so wrappers are recreated when venv path changes (e.g. app upgrade)
965+
const wrapperVersion = `wrapper:${pythonPath}`;
947966
const versionFile = path.join(eigentBinDir, '.npm_wrapper_version');
948967
const storedVersion = fs.existsSync(versionFile)
949968
? fs.readFileSync(versionFile, 'utf-8').trim()
@@ -958,10 +977,14 @@ export function ensureNpmWrappersForBrowserToolkit(
958977
process.platform === 'win32' ? 'npx.cmd' : 'npx'
959978
);
960979

980+
// Recreate wrappers when: version changed, wrappers missing, or existing shebang points to wrong Python
961981
const needsUpdate =
962982
storedVersion !== wrapperVersion ||
963983
!fs.existsSync(npmWrapper) ||
964-
!fs.existsSync(npxWrapper);
984+
!fs.existsSync(npxWrapper) ||
985+
(process.platform !== 'win32' &&
986+
fs.existsSync(npmWrapper) &&
987+
!fs.readFileSync(npmWrapper, 'utf-8').startsWith(`#!${pythonPath}`));
965988

966989
if (needsUpdate) {
967990
try {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "eigent",
3-
"version": "0.0.85",
3+
"version": "0.0.86",
44
"main": "dist-electron/main/index.js",
55
"description": "Eigent",
66
"author": "Eigent.AI",

server/app/controller/user/user_controller.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def get_privacy(session: Session = Depends(session), auth: Auth = Depends(auth_m
7777
return UserPrivacySettings.default_settings()
7878

7979
logger.debug("Privacy settings retrieved", extra={"user_id": user_id})
80-
return model.pricacy_setting
80+
return UserPrivacySettings(**model.pricacy_setting).to_response()
8181

8282

8383
@router.put("/user/privacy", name="update user privacy")
@@ -97,7 +97,7 @@ def put_privacy(data: UserPrivacySettings, session: Session = Depends(session),
9797
model.save(session)
9898
logger.info("Privacy settings created", extra={"user_id": user_id})
9999

100-
return model.pricacy_setting
100+
return UserPrivacySettings(**model.pricacy_setting).to_response()
101101

102102

103103
@router.get("/user/current_credits", name="get user current credits")

server/app/model/user/privacy.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
2-
# Licensed under the Apache License, Version 2.0 (the "License");
3-
# you may not use this file except in compliance with the License.
4-
# You may obtain a copy of the License at
5-
#
6-
# http://www.apache.org/licenses/LICENSE-2.0
7-
#
8-
# Unless required by applicable law or agreed to in writing, software
9-
# distributed under the License is distributed on an "AS IS" BASIS,
10-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11-
# See the License for the specific language governing permissions and
12-
# limitations under the License.
131
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
14+
15+
from typing import ClassVar
1416

1517
from pydantic import BaseModel
1618
from sqlalchemy import JSON
@@ -31,6 +33,21 @@ class UserPrivacySettings(BaseModel):
3133
access_your_address: bool | None = False
3234
password_storage: bool | None = False
3335

36+
# Fields that must all be True for the user to proceed
37+
REQUIRED_FIELDS: ClassVar[list[str]] = [
38+
'take_screenshot',
39+
'access_local_software',
40+
'access_your_address',
41+
'password_storage',
42+
]
43+
3444
@classmethod
3545
def default_settings(cls) -> dict:
36-
return cls().model_dump()
46+
instance = cls()
47+
return {**instance.model_dump(), "all_required_granted": instance.all_required_granted()}
48+
49+
def all_required_granted(self) -> bool:
50+
return all(getattr(self, f) for f in self.REQUIRED_FIELDS)
51+
52+
def to_response(self) -> dict:
53+
return {**self.model_dump(), "all_required_granted": self.all_required_granted()}

server/tests/user.http

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
### ============================================
2+
### Eigent Server API Tests
3+
### ============================================
4+
### VSCode Extension: "REST Client" by Huachao Mao
5+
### Extension ID: humao.rest-client
6+
### ============================================
7+
8+
@baseUrl = http://localhost:3001/api
9+
@token = YOUR_TOKEN_HERE
10+
11+
### ──────────────────────────────────────────
12+
### Auth (no token required)
13+
### ──────────────────────────────────────────
14+
15+
### Register a new user
16+
POST {{baseUrl}}/register
17+
Content-Type: application/json
18+
19+
{
20+
"email": "test@example.com",
21+
"password": "testpassword123"
22+
}
23+
24+
### Login
25+
# @name login
26+
POST {{baseUrl}}/login
27+
Content-Type: application/json
28+
29+
{
30+
"email": "test@example.com",
31+
"password": "testpassword123"
32+
}
33+
34+
### Save token from login response (use this after running login)
35+
@authToken = {{login.response.body.token}}
36+
37+
### ──────────────────────────────────────────
38+
### User
39+
### ──────────────────────────────────────────
40+
41+
### Get current user info
42+
GET {{baseUrl}}/user
43+
Authorization: Bearer {{authToken}}
44+
45+
### ──────────────────────────────────────────
46+
### Privacy Settings
47+
### ──────────────────────────────────────────
48+
49+
### Get privacy settings
50+
GET {{baseUrl}}/user/privacy
51+
Authorization: Bearer {{authToken}}
52+
53+
### Update privacy settings
54+
PUT {{baseUrl}}/user/privacy
55+
Authorization: Bearer {{authToken}}
56+
Content-Type: application/json
57+
58+
{
59+
"take_screenshot": true,
60+
"access_local_software": false,
61+
"access_your_address": false,
62+
"password_storage": false
63+
}
64+
65+
### Update only help_improve
66+
PUT {{baseUrl}}/user/privacy
67+
Authorization: Bearer {{authToken}}
68+
Content-Type: application/json
69+
70+
{
71+
"help_improve": true
72+
}
73+
74+
### ──────────────────────────────────────────
75+
### User Stats
76+
### ──────────────────────────────────────────
77+
78+
### Get user stats
79+
GET {{baseUrl}}/user/stat
80+
Authorization: Bearer {{authToken}}

0 commit comments

Comments
 (0)