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.
Files changed (159) hide show
  1. package/.dockerignore +33 -0
  2. package/.nvmrc +1 -0
  3. package/ARCHITECTURE.md +394 -0
  4. package/Dockerfile +50 -0
  5. package/LICENSE +29 -0
  6. package/README.md +206 -0
  7. package/bin/i18n.js +46 -0
  8. package/bin/ideaco.js +494 -0
  9. package/deploy.sh +15 -0
  10. package/docker-compose.yml +30 -0
  11. package/electron/main.cjs +986 -0
  12. package/electron/preload.cjs +14 -0
  13. package/electron/web-backends.cjs +854 -0
  14. package/jsconfig.json +8 -0
  15. package/next.config.mjs +34 -0
  16. package/package.json +134 -0
  17. package/postcss.config.mjs +6 -0
  18. package/public/demo/dashboard.png +0 -0
  19. package/public/demo/employee.png +0 -0
  20. package/public/demo/messages.png +0 -0
  21. package/public/demo/office.png +0 -0
  22. package/public/demo/requirement.png +0 -0
  23. package/public/logo.jpeg +0 -0
  24. package/public/logo.png +0 -0
  25. package/scripts/prepare-electron.js +67 -0
  26. package/scripts/release.js +76 -0
  27. package/src/app/api/agents/[agentId]/chat/route.js +70 -0
  28. package/src/app/api/agents/[agentId]/conversations/route.js +35 -0
  29. package/src/app/api/agents/[agentId]/route.js +106 -0
  30. package/src/app/api/avatar/route.js +104 -0
  31. package/src/app/api/browse-dir/route.js +44 -0
  32. package/src/app/api/chat/route.js +265 -0
  33. package/src/app/api/company/factory-reset/route.js +43 -0
  34. package/src/app/api/company/route.js +82 -0
  35. package/src/app/api/departments/[deptId]/agents/[agentId]/dismiss/route.js +19 -0
  36. package/src/app/api/departments/route.js +92 -0
  37. package/src/app/api/group-chat-loop/events/route.js +70 -0
  38. package/src/app/api/group-chat-loop/route.js +94 -0
  39. package/src/app/api/mailbox/route.js +100 -0
  40. package/src/app/api/messages/route.js +14 -0
  41. package/src/app/api/providers/[id]/configure/route.js +21 -0
  42. package/src/app/api/providers/[id]/refresh-cookie/route.js +38 -0
  43. package/src/app/api/providers/[id]/test-cookie/route.js +28 -0
  44. package/src/app/api/providers/route.js +11 -0
  45. package/src/app/api/requirements/route.js +242 -0
  46. package/src/app/api/secretary/route.js +65 -0
  47. package/src/app/api/system/cli-backends/route.js +91 -0
  48. package/src/app/api/system/cron/route.js +110 -0
  49. package/src/app/api/system/knowledge/route.js +104 -0
  50. package/src/app/api/system/plugins/route.js +40 -0
  51. package/src/app/api/system/skills/route.js +46 -0
  52. package/src/app/api/system/status/route.js +46 -0
  53. package/src/app/api/talent-market/[profileId]/recall/route.js +22 -0
  54. package/src/app/api/talent-market/[profileId]/route.js +17 -0
  55. package/src/app/api/talent-market/route.js +26 -0
  56. package/src/app/api/teams/route.js +773 -0
  57. package/src/app/api/ws-files/[departmentId]/file/route.js +27 -0
  58. package/src/app/api/ws-files/[departmentId]/files/route.js +22 -0
  59. package/src/app/globals.css +130 -0
  60. package/src/app/layout.jsx +40 -0
  61. package/src/app/page.jsx +97 -0
  62. package/src/components/AgentChatModal.jsx +164 -0
  63. package/src/components/AgentDetailModal.jsx +425 -0
  64. package/src/components/AgentSpyModal.jsx +481 -0
  65. package/src/components/AvatarGrid.jsx +29 -0
  66. package/src/components/BossProfileModal.jsx +162 -0
  67. package/src/components/CachedAvatar.jsx +77 -0
  68. package/src/components/ChatPanel.jsx +219 -0
  69. package/src/components/ChatShared.jsx +255 -0
  70. package/src/components/DepartmentDetail.jsx +842 -0
  71. package/src/components/DepartmentView.jsx +367 -0
  72. package/src/components/FileReference.jsx +260 -0
  73. package/src/components/FilesView.jsx +465 -0
  74. package/src/components/GroupChatView.jsx +799 -0
  75. package/src/components/Mailbox.jsx +926 -0
  76. package/src/components/MessagesView.jsx +112 -0
  77. package/src/components/OnboardingGuide.jsx +209 -0
  78. package/src/components/OrgTree.jsx +151 -0
  79. package/src/components/Overview.jsx +391 -0
  80. package/src/components/PixelOffice.jsx +2281 -0
  81. package/src/components/ProviderGrid.jsx +551 -0
  82. package/src/components/ProvidersBoard.jsx +16 -0
  83. package/src/components/RequirementDetail.jsx +1279 -0
  84. package/src/components/RequirementsBoard.jsx +187 -0
  85. package/src/components/SecretarySettings.jsx +295 -0
  86. package/src/components/SetupWizard.jsx +388 -0
  87. package/src/components/Sidebar.jsx +169 -0
  88. package/src/components/SystemMonitor.jsx +808 -0
  89. package/src/components/TalentMarket.jsx +183 -0
  90. package/src/components/TeamDetail.jsx +697 -0
  91. package/src/core/agent/base-agent.js +104 -0
  92. package/src/core/agent/chat-store.js +602 -0
  93. package/src/core/agent/cli-agent/backends/claude-code/README.md +52 -0
  94. package/src/core/agent/cli-agent/backends/claude-code/config.js +27 -0
  95. package/src/core/agent/cli-agent/backends/codebuddy/README.md +236 -0
  96. package/src/core/agent/cli-agent/backends/codebuddy/config.js +27 -0
  97. package/src/core/agent/cli-agent/backends/codex/README.md +51 -0
  98. package/src/core/agent/cli-agent/backends/codex/config.js +27 -0
  99. package/src/core/agent/cli-agent/backends/index.js +27 -0
  100. package/src/core/agent/cli-agent/backends/registry.js +580 -0
  101. package/src/core/agent/cli-agent/index.js +154 -0
  102. package/src/core/agent/index.js +60 -0
  103. package/src/core/agent/llm-agent/client.js +320 -0
  104. package/src/core/agent/llm-agent/index.js +97 -0
  105. package/src/core/agent/message-bus.js +211 -0
  106. package/src/core/agent/session.js +608 -0
  107. package/src/core/agent/tools.js +596 -0
  108. package/src/core/agent/web-agent/backends/base-backend.js +180 -0
  109. package/src/core/agent/web-agent/backends/chatgpt/client.js +146 -0
  110. package/src/core/agent/web-agent/backends/chatgpt/config.js +148 -0
  111. package/src/core/agent/web-agent/backends/chatgpt/dom-scripts.js +303 -0
  112. package/src/core/agent/web-agent/backends/index.js +91 -0
  113. package/src/core/agent/web-agent/index.js +278 -0
  114. package/src/core/agent/web-agent/web-client.js +407 -0
  115. package/src/core/employee/base-employee.js +1088 -0
  116. package/src/core/employee/index.js +35 -0
  117. package/src/core/employee/knowledge.js +327 -0
  118. package/src/core/employee/lifecycle.js +990 -0
  119. package/src/core/employee/memory/index.js +642 -0
  120. package/src/core/employee/memory/store.js +143 -0
  121. package/src/core/employee/performance.js +224 -0
  122. package/src/core/employee/secretary.js +625 -0
  123. package/src/core/employee/skills.js +398 -0
  124. package/src/core/index.js +38 -0
  125. package/src/core/organization/company.js +2600 -0
  126. package/src/core/organization/department.js +737 -0
  127. package/src/core/organization/group-chat-loop.js +264 -0
  128. package/src/core/organization/index.js +8 -0
  129. package/src/core/organization/persistence.js +111 -0
  130. package/src/core/organization/team.js +267 -0
  131. package/src/core/organization/workforce/hr.js +377 -0
  132. package/src/core/organization/workforce/providers.js +468 -0
  133. package/src/core/organization/workforce/role-archetypes.js +805 -0
  134. package/src/core/organization/workforce/talent-market.js +205 -0
  135. package/src/core/prompts.js +532 -0
  136. package/src/core/requirement.js +1789 -0
  137. package/src/core/system/audit.js +483 -0
  138. package/src/core/system/cron.js +449 -0
  139. package/src/core/system/index.js +7 -0
  140. package/src/core/system/plugin.js +2183 -0
  141. package/src/core/utils/json-parse.js +188 -0
  142. package/src/core/workspace.js +239 -0
  143. package/src/lib/api-i18n.js +211 -0
  144. package/src/lib/avatar.js +268 -0
  145. package/src/lib/client-store.js +1025 -0
  146. package/src/lib/config-validator.js +483 -0
  147. package/src/lib/format-time.js +22 -0
  148. package/src/lib/hooks.js +414 -0
  149. package/src/lib/i18n.js +134 -0
  150. package/src/lib/paths.js +23 -0
  151. package/src/lib/store.js +72 -0
  152. package/src/locales/de.js +393 -0
  153. package/src/locales/en.js +1054 -0
  154. package/src/locales/es.js +393 -0
  155. package/src/locales/fr.js +393 -0
  156. package/src/locales/ja.js +501 -0
  157. package/src/locales/ko.js +513 -0
  158. package/src/locales/zh.js +828 -0
  159. package/tailwind.config.mjs +11 -0
package/jsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": ".",
4
+ "paths": {
5
+ "@/*": ["./src/*"]
6
+ }
7
+ }
8
+ }
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
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
+ }