ideaco 1.1.5
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/.dockerignore +33 -0
- package/.nvmrc +1 -0
- package/ARCHITECTURE.md +394 -0
- package/Dockerfile +50 -0
- package/LICENSE +29 -0
- package/README.md +206 -0
- package/bin/i18n.js +46 -0
- package/bin/ideaco.js +494 -0
- package/deploy.sh +15 -0
- package/docker-compose.yml +30 -0
- package/electron/main.cjs +986 -0
- package/electron/preload.cjs +14 -0
- package/electron/web-backends.cjs +854 -0
- package/jsconfig.json +8 -0
- package/next.config.mjs +34 -0
- package/package.json +134 -0
- package/postcss.config.mjs +6 -0
- package/public/demo/dashboard.png +0 -0
- package/public/demo/employee.png +0 -0
- package/public/demo/messages.png +0 -0
- package/public/demo/office.png +0 -0
- package/public/demo/requirement.png +0 -0
- package/public/logo.jpeg +0 -0
- package/public/logo.png +0 -0
- package/scripts/prepare-electron.js +67 -0
- package/scripts/release.js +76 -0
- package/src/app/api/agents/[agentId]/chat/route.js +70 -0
- package/src/app/api/agents/[agentId]/conversations/route.js +35 -0
- package/src/app/api/agents/[agentId]/route.js +106 -0
- package/src/app/api/avatar/route.js +104 -0
- package/src/app/api/browse-dir/route.js +44 -0
- package/src/app/api/chat/route.js +265 -0
- package/src/app/api/company/factory-reset/route.js +43 -0
- package/src/app/api/company/route.js +82 -0
- package/src/app/api/departments/[deptId]/agents/[agentId]/dismiss/route.js +19 -0
- package/src/app/api/departments/route.js +92 -0
- package/src/app/api/group-chat-loop/events/route.js +70 -0
- package/src/app/api/group-chat-loop/route.js +94 -0
- package/src/app/api/mailbox/route.js +100 -0
- package/src/app/api/messages/route.js +14 -0
- package/src/app/api/providers/[id]/configure/route.js +21 -0
- package/src/app/api/providers/[id]/refresh-cookie/route.js +38 -0
- package/src/app/api/providers/[id]/test-cookie/route.js +28 -0
- package/src/app/api/providers/route.js +11 -0
- package/src/app/api/requirements/route.js +242 -0
- package/src/app/api/secretary/route.js +65 -0
- package/src/app/api/system/cli-backends/route.js +91 -0
- package/src/app/api/system/cron/route.js +110 -0
- package/src/app/api/system/knowledge/route.js +104 -0
- package/src/app/api/system/plugins/route.js +40 -0
- package/src/app/api/system/skills/route.js +46 -0
- package/src/app/api/system/status/route.js +46 -0
- package/src/app/api/talent-market/[profileId]/recall/route.js +22 -0
- package/src/app/api/talent-market/[profileId]/route.js +17 -0
- package/src/app/api/talent-market/route.js +26 -0
- package/src/app/api/teams/route.js +773 -0
- package/src/app/api/ws-files/[departmentId]/file/route.js +27 -0
- package/src/app/api/ws-files/[departmentId]/files/route.js +22 -0
- package/src/app/globals.css +130 -0
- package/src/app/layout.jsx +40 -0
- package/src/app/page.jsx +97 -0
- package/src/components/AgentChatModal.jsx +164 -0
- package/src/components/AgentDetailModal.jsx +425 -0
- package/src/components/AgentSpyModal.jsx +481 -0
- package/src/components/AvatarGrid.jsx +29 -0
- package/src/components/BossProfileModal.jsx +162 -0
- package/src/components/CachedAvatar.jsx +77 -0
- package/src/components/ChatPanel.jsx +219 -0
- package/src/components/ChatShared.jsx +255 -0
- package/src/components/DepartmentDetail.jsx +842 -0
- package/src/components/DepartmentView.jsx +367 -0
- package/src/components/FileReference.jsx +260 -0
- package/src/components/FilesView.jsx +465 -0
- package/src/components/GroupChatView.jsx +799 -0
- package/src/components/Mailbox.jsx +926 -0
- package/src/components/MessagesView.jsx +112 -0
- package/src/components/OnboardingGuide.jsx +209 -0
- package/src/components/OrgTree.jsx +151 -0
- package/src/components/Overview.jsx +391 -0
- package/src/components/PixelOffice.jsx +2281 -0
- package/src/components/ProviderGrid.jsx +551 -0
- package/src/components/ProvidersBoard.jsx +16 -0
- package/src/components/RequirementDetail.jsx +1279 -0
- package/src/components/RequirementsBoard.jsx +187 -0
- package/src/components/SecretarySettings.jsx +295 -0
- package/src/components/SetupWizard.jsx +388 -0
- package/src/components/Sidebar.jsx +169 -0
- package/src/components/SystemMonitor.jsx +808 -0
- package/src/components/TalentMarket.jsx +183 -0
- package/src/components/TeamDetail.jsx +697 -0
- package/src/core/agent/base-agent.js +104 -0
- package/src/core/agent/chat-store.js +602 -0
- package/src/core/agent/cli-agent/backends/claude-code/README.md +52 -0
- package/src/core/agent/cli-agent/backends/claude-code/config.js +27 -0
- package/src/core/agent/cli-agent/backends/codebuddy/README.md +236 -0
- package/src/core/agent/cli-agent/backends/codebuddy/config.js +27 -0
- package/src/core/agent/cli-agent/backends/codex/README.md +51 -0
- package/src/core/agent/cli-agent/backends/codex/config.js +27 -0
- package/src/core/agent/cli-agent/backends/index.js +27 -0
- package/src/core/agent/cli-agent/backends/registry.js +580 -0
- package/src/core/agent/cli-agent/index.js +154 -0
- package/src/core/agent/index.js +60 -0
- package/src/core/agent/llm-agent/client.js +320 -0
- package/src/core/agent/llm-agent/index.js +97 -0
- package/src/core/agent/message-bus.js +211 -0
- package/src/core/agent/session.js +608 -0
- package/src/core/agent/tools.js +596 -0
- package/src/core/agent/web-agent/backends/base-backend.js +180 -0
- package/src/core/agent/web-agent/backends/chatgpt/client.js +146 -0
- package/src/core/agent/web-agent/backends/chatgpt/config.js +148 -0
- package/src/core/agent/web-agent/backends/chatgpt/dom-scripts.js +303 -0
- package/src/core/agent/web-agent/backends/index.js +91 -0
- package/src/core/agent/web-agent/index.js +278 -0
- package/src/core/agent/web-agent/web-client.js +407 -0
- package/src/core/employee/base-employee.js +1088 -0
- package/src/core/employee/index.js +35 -0
- package/src/core/employee/knowledge.js +327 -0
- package/src/core/employee/lifecycle.js +990 -0
- package/src/core/employee/memory/index.js +642 -0
- package/src/core/employee/memory/store.js +143 -0
- package/src/core/employee/performance.js +224 -0
- package/src/core/employee/secretary.js +625 -0
- package/src/core/employee/skills.js +398 -0
- package/src/core/index.js +38 -0
- package/src/core/organization/company.js +2600 -0
- package/src/core/organization/department.js +737 -0
- package/src/core/organization/group-chat-loop.js +264 -0
- package/src/core/organization/index.js +8 -0
- package/src/core/organization/persistence.js +111 -0
- package/src/core/organization/team.js +267 -0
- package/src/core/organization/workforce/hr.js +377 -0
- package/src/core/organization/workforce/providers.js +468 -0
- package/src/core/organization/workforce/role-archetypes.js +805 -0
- package/src/core/organization/workforce/talent-market.js +205 -0
- package/src/core/prompts.js +532 -0
- package/src/core/requirement.js +1789 -0
- package/src/core/system/audit.js +483 -0
- package/src/core/system/cron.js +449 -0
- package/src/core/system/index.js +7 -0
- package/src/core/system/plugin.js +2183 -0
- package/src/core/utils/json-parse.js +188 -0
- package/src/core/workspace.js +239 -0
- package/src/lib/api-i18n.js +211 -0
- package/src/lib/avatar.js +268 -0
- package/src/lib/client-store.js +1025 -0
- package/src/lib/config-validator.js +483 -0
- package/src/lib/format-time.js +22 -0
- package/src/lib/hooks.js +414 -0
- package/src/lib/i18n.js +134 -0
- package/src/lib/paths.js +23 -0
- package/src/lib/store.js +72 -0
- package/src/locales/de.js +393 -0
- package/src/locales/en.js +1054 -0
- package/src/locales/es.js +393 -0
- package/src/locales/fr.js +393 -0
- package/src/locales/ja.js +501 -0
- package/src/locales/ko.js +513 -0
- package/src/locales/zh.js +828 -0
- package/tailwind.config.mjs +11 -0
package/jsconfig.json
ADDED
package/next.config.mjs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** @type {import('next').NextConfig} */
|
|
2
|
+
const nextConfig = {
|
|
3
|
+
output: 'standalone', // Required for Docker deployment
|
|
4
|
+
experimental: {
|
|
5
|
+
serverComponentsExternalPackages: [
|
|
6
|
+
'uuid',
|
|
7
|
+
'puppeteer',
|
|
8
|
+
'pdf-parse',
|
|
9
|
+
'openai',
|
|
10
|
+
'puppeteer-core',
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
webpack: (config, { isServer }) => {
|
|
14
|
+
if (isServer) {
|
|
15
|
+
// 将可选依赖标记为 external,避免 webpack 尝试解析/打包它们
|
|
16
|
+
const optionalDeps = ['puppeteer', 'puppeteer-core', 'pdf-parse', 'openai'];
|
|
17
|
+
const existingExternals = config.externals || [];
|
|
18
|
+
|
|
19
|
+
config.externals = [
|
|
20
|
+
...existingExternals,
|
|
21
|
+
// 函数形式的 externals,拦截可选依赖
|
|
22
|
+
function ({ request }, callback) {
|
|
23
|
+
if (optionalDeps.some(dep => request === dep || request.startsWith(dep + '/'))) {
|
|
24
|
+
return callback(null, 'commonjs ' + request);
|
|
25
|
+
}
|
|
26
|
+
callback();
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
return config;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default nextConfig;
|
package/package.json
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ideaco",
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "Idea Unlimited - An LLM-powered AI enterprise simulator where you boss around AI employees",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "ymssx",
|
|
7
|
+
"email": "ymssx@users.noreply.github.com"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "electron/main.cjs",
|
|
12
|
+
"bin": {
|
|
13
|
+
"ideaco": "bin/ideaco.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "next dev -p 9999",
|
|
17
|
+
"build": "next build",
|
|
18
|
+
"start": "next start -p 9999",
|
|
19
|
+
"electron:dev": "electron .",
|
|
20
|
+
"electron:build": "npm run build && node scripts/prepare-electron.js && electron-builder",
|
|
21
|
+
"electron:build:mac": "npm run build && node scripts/prepare-electron.js && electron-builder --mac",
|
|
22
|
+
"electron:build:win": "npm run build && node scripts/prepare-electron.js && electron-builder --win",
|
|
23
|
+
"electron:build:linux": "npm run build && node scripts/prepare-electron.js && electron-builder --linux",
|
|
24
|
+
"release": "node scripts/release.js"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@monaco-editor/react": "^4.7.0",
|
|
28
|
+
"autoprefixer": "^10.4.27",
|
|
29
|
+
"chalk": "^5.3.0",
|
|
30
|
+
"electron": "^33.0.0",
|
|
31
|
+
"eventemitter3": "^5.0.1",
|
|
32
|
+
"jimp": "^1.6.0",
|
|
33
|
+
"next": "^15.5.12",
|
|
34
|
+
"openai": "^4.0.0",
|
|
35
|
+
"postcss": "^8.5.8",
|
|
36
|
+
"react": "^18.3.0",
|
|
37
|
+
"react-dom": "^18.3.0",
|
|
38
|
+
"react-markdown": "^10.1.0",
|
|
39
|
+
"remark-gfm": "^4.0.1",
|
|
40
|
+
"tailwindcss": "^3.4.19",
|
|
41
|
+
"uuid": "^9.0.0",
|
|
42
|
+
"zustand": "^5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"electron-builder": "^25.1.0"
|
|
46
|
+
},
|
|
47
|
+
"build": {
|
|
48
|
+
"appId": "com.ideaco.app",
|
|
49
|
+
"productName": "IdeaCo",
|
|
50
|
+
"directories": {
|
|
51
|
+
"output": "release"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"electron/**/*",
|
|
55
|
+
"!node_modules",
|
|
56
|
+
"!src",
|
|
57
|
+
"!.next",
|
|
58
|
+
"!.git",
|
|
59
|
+
"!vendor",
|
|
60
|
+
"!data",
|
|
61
|
+
"!workspace"
|
|
62
|
+
],
|
|
63
|
+
"extraResources": [
|
|
64
|
+
{
|
|
65
|
+
"from": "electron-dist/",
|
|
66
|
+
"to": "app",
|
|
67
|
+
"filter": [
|
|
68
|
+
"**/*"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"mac": {
|
|
73
|
+
"target": [
|
|
74
|
+
{
|
|
75
|
+
"target": "dmg",
|
|
76
|
+
"arch": [
|
|
77
|
+
"x64",
|
|
78
|
+
"arm64"
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"target": "zip",
|
|
83
|
+
"arch": [
|
|
84
|
+
"x64",
|
|
85
|
+
"arm64"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"icon": "public/logo.png",
|
|
90
|
+
"category": "public.app-category.developer-tools"
|
|
91
|
+
},
|
|
92
|
+
"win": {
|
|
93
|
+
"target": [
|
|
94
|
+
{
|
|
95
|
+
"target": "nsis",
|
|
96
|
+
"arch": [
|
|
97
|
+
"x64"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"target": "zip",
|
|
102
|
+
"arch": [
|
|
103
|
+
"x64"
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"icon": "public/logo.png"
|
|
108
|
+
},
|
|
109
|
+
"linux": {
|
|
110
|
+
"target": [
|
|
111
|
+
{
|
|
112
|
+
"target": "AppImage",
|
|
113
|
+
"arch": [
|
|
114
|
+
"x64"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"target": "deb",
|
|
119
|
+
"arch": [
|
|
120
|
+
"x64"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"icon": "public/logo.png",
|
|
125
|
+
"category": "Development"
|
|
126
|
+
},
|
|
127
|
+
"nsis": {
|
|
128
|
+
"oneClick": false,
|
|
129
|
+
"allowToChangeInstallationDirectory": true,
|
|
130
|
+
"createDesktopShortcut": true,
|
|
131
|
+
"createStartMenuShortcut": true
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/public/logo.jpeg
ADDED
|
Binary file
|
package/public/logo.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prepare Next.js standalone output for Electron packaging.
|
|
3
|
+
*
|
|
4
|
+
* Copies .next/standalone, .next/static, and public into electron-dist/
|
|
5
|
+
* so electron-builder can bundle them as extraResources.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
const root = path.join(__dirname, '..');
|
|
15
|
+
|
|
16
|
+
const outDir = path.join(root, 'electron-dist');
|
|
17
|
+
|
|
18
|
+
function copyRecursive(src, dest) {
|
|
19
|
+
if (!fs.existsSync(src)) return;
|
|
20
|
+
|
|
21
|
+
const stat = fs.statSync(src);
|
|
22
|
+
if (stat.isDirectory()) {
|
|
23
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
24
|
+
for (const child of fs.readdirSync(src)) {
|
|
25
|
+
copyRecursive(path.join(src, child), path.join(dest, child));
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
29
|
+
fs.copyFileSync(src, dest);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log('Preparing Electron distribution...');
|
|
34
|
+
|
|
35
|
+
// Clean previous build
|
|
36
|
+
if (fs.existsSync(outDir)) {
|
|
37
|
+
fs.rmSync(outDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
40
|
+
|
|
41
|
+
// 1. Copy standalone output (includes server.js + node_modules)
|
|
42
|
+
const standaloneDir = path.join(root, '.next', 'standalone');
|
|
43
|
+
if (!fs.existsSync(standaloneDir)) {
|
|
44
|
+
console.error('ERROR: .next/standalone not found. Run "npm run build" first.');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
copyRecursive(standaloneDir, outDir);
|
|
48
|
+
|
|
49
|
+
// 2. Copy static assets
|
|
50
|
+
const staticDir = path.join(root, '.next', 'static');
|
|
51
|
+
copyRecursive(staticDir, path.join(outDir, '.next', 'static'));
|
|
52
|
+
|
|
53
|
+
// 3. Copy public folder
|
|
54
|
+
const publicDir = path.join(root, 'public');
|
|
55
|
+
copyRecursive(publicDir, path.join(outDir, 'public'));
|
|
56
|
+
|
|
57
|
+
// 4. Remove data/workspace directories if they were copied from standalone
|
|
58
|
+
const removeDirs = ['data', 'workspace'];
|
|
59
|
+
for (const dir of removeDirs) {
|
|
60
|
+
const dirPath = path.join(outDir, dir);
|
|
61
|
+
if (fs.existsSync(dirPath)) {
|
|
62
|
+
fs.rmSync(dirPath, { recursive: true });
|
|
63
|
+
console.log(`Removed ${dir}/ from distribution (runtime data, not for packaging)`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log('Electron distribution prepared at:', outDir);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const readline = require('readline');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const rl = readline.createInterface({
|
|
6
|
+
input: process.stdin,
|
|
7
|
+
output: process.stdout
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const packageJsonPath = path.resolve(__dirname, '../package.json');
|
|
11
|
+
const packageJson = require(packageJsonPath);
|
|
12
|
+
|
|
13
|
+
console.log(`\n📦 Current version: ${packageJson.version}`);
|
|
14
|
+
|
|
15
|
+
function runCommand(command) {
|
|
16
|
+
try {
|
|
17
|
+
execSync(command, { stdio: 'inherit' });
|
|
18
|
+
return true;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error(`❌ Failed to execute command: ${command}`);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function release() {
|
|
26
|
+
// Check for uncommitted changes
|
|
27
|
+
try {
|
|
28
|
+
const status = execSync('git status --porcelain').toString();
|
|
29
|
+
if (status) {
|
|
30
|
+
console.error('❌ Git working directory is not clean. Please commit or stash changes first.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('❌ Failed to check git status.');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
rl.question('Select release type (patch/minor/major) [patch]: ', (answer) => {
|
|
39
|
+
const type = answer.trim().toLowerCase() || 'patch';
|
|
40
|
+
|
|
41
|
+
if (!['patch', 'minor', 'major'].includes(type)) {
|
|
42
|
+
console.error('❌ Invalid release type. Please choose patch, minor, or major.');
|
|
43
|
+
rl.close();
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(`\n🚀 Releasing ${type} version...`);
|
|
48
|
+
|
|
49
|
+
// npm version handles updating package.json, creating a git commit, and tagging
|
|
50
|
+
// %s will be replaced by the new version number
|
|
51
|
+
const versionCommand = `npm version ${type} -m "chore(release): %s"`;
|
|
52
|
+
|
|
53
|
+
if (!runCommand(versionCommand)) {
|
|
54
|
+
console.error('❌ Version bump failed.');
|
|
55
|
+
rl.close();
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log('\n📤 Pushing changes and tags to GitHub...');
|
|
60
|
+
|
|
61
|
+
// Push both commits and tags
|
|
62
|
+
// Using --follow-tags pushes only annotated tags that are reachable from the pushed commits,
|
|
63
|
+
// but npm version creates annotated tags by default, so git push --follow-tags is safer/better than git push && git push --tags?
|
|
64
|
+
// Actually git push --follow-tags is often enough if configured, but explicit is better.
|
|
65
|
+
|
|
66
|
+
if (runCommand('git push') && runCommand('git push --tags')) {
|
|
67
|
+
console.log('\n✅ Release successful! Version updated and pushed.');
|
|
68
|
+
} else {
|
|
69
|
+
console.error('\n❌ Failed to push to remote.');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
rl.close();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
release();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* POST /api/agents/[agentId]/chat - Chat with an agent
|
|
7
|
+
*/
|
|
8
|
+
export async function POST(request, { params }) {
|
|
9
|
+
const t = getApiT(request);
|
|
10
|
+
const company = getCompany();
|
|
11
|
+
if (!company) {
|
|
12
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const { agentId } = await params;
|
|
16
|
+
const { message } = await request.json();
|
|
17
|
+
if (!message) {
|
|
18
|
+
return NextResponse.json({ error: t('api.messageRequired') }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
const result = await company.chatWithAgent(agentId, message);
|
|
21
|
+
const history = company.getAgentChatHistory(agentId, 30);
|
|
22
|
+
return NextResponse.json({
|
|
23
|
+
success: true,
|
|
24
|
+
data: {
|
|
25
|
+
reply: result,
|
|
26
|
+
chatHistory: history,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* GET /api/agents/[agentId]/chat - Get chat history with agent
|
|
36
|
+
*/
|
|
37
|
+
export async function GET(request, { params }) {
|
|
38
|
+
const t = getApiT(request);
|
|
39
|
+
const company = getCompany();
|
|
40
|
+
if (!company) {
|
|
41
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const { agentId } = await params;
|
|
45
|
+
const url = new URL(request.url);
|
|
46
|
+
const limit = parseInt(url.searchParams.get('limit') || '30');
|
|
47
|
+
const history = company.getAgentChatHistory(agentId, limit);
|
|
48
|
+
return NextResponse.json({ data: history });
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* PUT /api/agents/[agentId]/chat - Mark agent chat as read
|
|
56
|
+
*/
|
|
57
|
+
export async function PUT(request, { params }) {
|
|
58
|
+
const t = getApiT(request);
|
|
59
|
+
const company = getCompany();
|
|
60
|
+
if (!company) {
|
|
61
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const { agentId } = await params;
|
|
65
|
+
company.markAgentChatRead(agentId);
|
|
66
|
+
return NextResponse.json({ success: true });
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* GET /api/agents/[agentId]/conversations - Get all chat sessions between an agent and others
|
|
7
|
+
* Query params:
|
|
8
|
+
* - sessionId: if provided, return chat history for that session
|
|
9
|
+
* - limit: message count limit (default 50)
|
|
10
|
+
*/
|
|
11
|
+
export async function GET(request, { params }) {
|
|
12
|
+
const t = getApiT(request);
|
|
13
|
+
const company = getCompany();
|
|
14
|
+
if (!company) {
|
|
15
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const { agentId } = await params;
|
|
19
|
+
const url = new URL(request.url);
|
|
20
|
+
const sessionId = url.searchParams.get('sessionId');
|
|
21
|
+
const limit = parseInt(url.searchParams.get('limit') || '50');
|
|
22
|
+
|
|
23
|
+
if (sessionId) {
|
|
24
|
+
// Return chat history for a specific session (with participant info)
|
|
25
|
+
const result = company.getAgentAgentChatHistory(sessionId, limit);
|
|
26
|
+
return NextResponse.json({ data: result });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Return all chat sessions for this agent
|
|
30
|
+
const conversations = company.getAgentConversations(agentId);
|
|
31
|
+
return NextResponse.json({ data: conversations });
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
4
|
+
|
|
5
|
+
export async function GET(request, { params }) {
|
|
6
|
+
const t = getApiT(request);
|
|
7
|
+
const company = getCompany();
|
|
8
|
+
if (!company) return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const { agentId } = await params;
|
|
12
|
+
for (const dept of company.departments.values()) {
|
|
13
|
+
const agent = dept.agents.get(agentId);
|
|
14
|
+
if (agent) {
|
|
15
|
+
const reviews = company.performanceSystem.getReviews(agent.id);
|
|
16
|
+
return NextResponse.json({
|
|
17
|
+
data: {
|
|
18
|
+
id: agent.id,
|
|
19
|
+
name: agent.name,
|
|
20
|
+
role: agent.role,
|
|
21
|
+
avatar: agent.avatar,
|
|
22
|
+
gender: agent.gender,
|
|
23
|
+
age: agent.age,
|
|
24
|
+
personality: agent.personality,
|
|
25
|
+
signature: agent.signature,
|
|
26
|
+
prompt: agent.prompt,
|
|
27
|
+
skills: agent.skills,
|
|
28
|
+
status: agent.status,
|
|
29
|
+
provider: agent.getProviderDisplayInfo(),
|
|
30
|
+
cliBackend: agent.cliBackend || null,
|
|
31
|
+
fallbackProvider: agent.getFallbackProviderName(),
|
|
32
|
+
department: dept.name,
|
|
33
|
+
departmentId: dept.id,
|
|
34
|
+
memory: agent.memory.getSummary(),
|
|
35
|
+
performanceHistory: agent.performanceHistory,
|
|
36
|
+
reviews: reviews.map(r => r.getSummary()),
|
|
37
|
+
taskHistory: agent.taskHistory.map(t => ({
|
|
38
|
+
task: t.task,
|
|
39
|
+
completedAt: t.completedAt,
|
|
40
|
+
success: t.result?.success,
|
|
41
|
+
toolsUsed: t.result?.toolResults?.length || 0,
|
|
42
|
+
})),
|
|
43
|
+
tokenUsage: { ...agent.tokenUsage },
|
|
44
|
+
avgScore: agent.performanceHistory.length > 0
|
|
45
|
+
? Math.round(agent.performanceHistory.reduce((s, p) => s + p.score, 0) / agent.performanceHistory.length)
|
|
46
|
+
: null,
|
|
47
|
+
// Incentives: generated based on performance records (high scores earn flowers)
|
|
48
|
+
incentives: agent.performanceHistory
|
|
49
|
+
.filter(p => p.score >= 80)
|
|
50
|
+
.map(p => ({
|
|
51
|
+
type: 'flower',
|
|
52
|
+
emoji: '🌸',
|
|
53
|
+
label: p.score >= 90 ? 'outstanding' : 'excellent',
|
|
54
|
+
task: p.task,
|
|
55
|
+
score: p.score,
|
|
56
|
+
level: p.level,
|
|
57
|
+
date: p.date,
|
|
58
|
+
})),
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return NextResponse.json({ error: t('api.agentNotFound') }, { status: 404 });
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* PUT /api/agents/[agentId] - Update Agent configuration (e.g. CLI backend)
|
|
71
|
+
*/
|
|
72
|
+
export async function PUT(request, { params }) {
|
|
73
|
+
const t = getApiT(request);
|
|
74
|
+
const company = getCompany();
|
|
75
|
+
if (!company) return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const { agentId } = await params;
|
|
79
|
+
const body = await request.json();
|
|
80
|
+
|
|
81
|
+
for (const dept of company.departments.values()) {
|
|
82
|
+
const agent = dept.agents.get(agentId);
|
|
83
|
+
if (agent) {
|
|
84
|
+
// Set CLI backend (only supported for CLI agents)
|
|
85
|
+
if ('cliBackend' in body && agent.agentType === 'cli') {
|
|
86
|
+
agent.agent.cliBackend = body.cliBackend || null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Persist
|
|
90
|
+
company.save();
|
|
91
|
+
|
|
92
|
+
return NextResponse.json({
|
|
93
|
+
data: {
|
|
94
|
+
id: agent.id,
|
|
95
|
+
name: agent.name,
|
|
96
|
+
cliBackend: agent.cliBackend,
|
|
97
|
+
message: t('api.agentConfigUpdated'),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return NextResponse.json({ error: t('api.agentNotFound') }, { status: 404 });
|
|
103
|
+
} catch (e) {
|
|
104
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Avatar Proxy API
|
|
5
|
+
* 代理 DiceBear Micah 头像请求,支持传递详细外观参数
|
|
6
|
+
*
|
|
7
|
+
* 基础用法: /api/avatar?style=micah&seed=xxx
|
|
8
|
+
* 详细参数: /api/avatar?style=micah&seed=xxx&hair=fonze&hairColor=000000&facialHair=beard...
|
|
9
|
+
*/
|
|
10
|
+
export async function GET(request) {
|
|
11
|
+
const { searchParams } = new URL(request.url);
|
|
12
|
+
const style = searchParams.get('style') || 'micah';
|
|
13
|
+
const seed = searchParams.get('seed') || 'default';
|
|
14
|
+
|
|
15
|
+
// 构建 DiceBear URL,传递所有 Micah 参数
|
|
16
|
+
let url = `https://api.dicebear.com/7.x/${encodeURIComponent(style)}/svg?seed=${encodeURIComponent(seed)}`;
|
|
17
|
+
|
|
18
|
+
// DiceBear 7.x Micah 参数值白名单(防止无效值导致 400 错误)
|
|
19
|
+
const VALID_VALUES = {
|
|
20
|
+
hair: ['fonze', 'mrT', 'dougFunny', 'mrClean', 'dannyPhantom', 'full', 'pixie'],
|
|
21
|
+
facialHair: ['beard', 'scruff'],
|
|
22
|
+
earrings: ['hoop', 'stud'],
|
|
23
|
+
glasses: ['round', 'square'],
|
|
24
|
+
mouth: ['smile', 'laughing', 'nervous', 'pucker', 'sad', 'smirk', 'surprised', 'frown'],
|
|
25
|
+
eyes: ['eyes', 'round', 'smiling', 'eyesShadow'],
|
|
26
|
+
eyebrows: ['up', 'down', 'eyelashesUp', 'eyelashesDown'],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// 转发所有 Micah 外观参数(含值校验)
|
|
30
|
+
const micahParams = [
|
|
31
|
+
'hair', 'hairColor', 'facialHair', 'earrings', 'glasses',
|
|
32
|
+
'mouth', 'eyes', 'eyebrows', 'baseColor', 'shirtColor',
|
|
33
|
+
];
|
|
34
|
+
for (const param of micahParams) {
|
|
35
|
+
const val = searchParams.get(param);
|
|
36
|
+
if (!val) continue;
|
|
37
|
+
// 对有白名单的参数做校验,颜色参数(hairColor/baseColor/shirtColor)直接放行
|
|
38
|
+
if (VALID_VALUES[param] && !VALID_VALUES[param].includes(val)) continue;
|
|
39
|
+
url += `&${param}=${encodeURIComponent(val)}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 转发 probability 控制参数(0-100 整数,控制对应元素是否出现)
|
|
43
|
+
const probParams = ['facialHairProbability', 'earringsProbability', 'glassesProbability'];
|
|
44
|
+
for (const param of probParams) {
|
|
45
|
+
const val = searchParams.get(param);
|
|
46
|
+
if (val !== null && val !== undefined) {
|
|
47
|
+
const num = parseInt(val, 10);
|
|
48
|
+
if (!isNaN(num) && num >= 0 && num <= 100) {
|
|
49
|
+
url += `&${param}=${num}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const resp = await fetch(url, {
|
|
56
|
+
headers: { 'Accept': 'image/svg+xml' },
|
|
57
|
+
signal: AbortSignal.timeout(5000),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (!resp.ok) {
|
|
61
|
+
return generateFallbackAvatar(seed, style);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const svg = await resp.text();
|
|
65
|
+
return new NextResponse(svg, {
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'image/svg+xml',
|
|
68
|
+
'Cache-Control': 'public, max-age=86400, immutable',
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
} catch {
|
|
72
|
+
return generateFallbackAvatar(seed, style);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 外部 API 不可用时,生成简单的本地 SVG 头像
|
|
78
|
+
*/
|
|
79
|
+
function generateFallbackAvatar(seed, style) {
|
|
80
|
+
let hash = 0;
|
|
81
|
+
for (let i = 0; i < seed.length; i++) {
|
|
82
|
+
hash = seed.charCodeAt(i) + ((hash << 5) - hash);
|
|
83
|
+
}
|
|
84
|
+
const hue = Math.abs(hash) % 360;
|
|
85
|
+
const saturation = 60 + (Math.abs(hash >> 8) % 30);
|
|
86
|
+
const lightness = 40 + (Math.abs(hash >> 16) % 20);
|
|
87
|
+
|
|
88
|
+
const initial = seed.charAt(0).toUpperCase();
|
|
89
|
+
|
|
90
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
91
|
+
<rect width="100" height="100" rx="50" fill="hsl(${hue}, ${saturation}%, ${lightness}%)"/>
|
|
92
|
+
<text x="50" y="50" text-anchor="middle" dominant-baseline="central"
|
|
93
|
+
font-size="42" font-family="sans-serif" fill="white" font-weight="bold">
|
|
94
|
+
${initial}
|
|
95
|
+
</text>
|
|
96
|
+
</svg>`;
|
|
97
|
+
|
|
98
|
+
return new NextResponse(svg, {
|
|
99
|
+
headers: {
|
|
100
|
+
'Content-Type': 'image/svg+xml',
|
|
101
|
+
'Cache-Control': 'public, max-age=86400',
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|