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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { groupChatLoop } from '@/core/organization/group-chat-loop.js';
|
|
4
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Group Chat Flow API - lets the boss peek at employees' inner monologues in group chats
|
|
8
|
+
*
|
|
9
|
+
* GET /api/group-chat-loop?agentId=xxx&groupId=xxx - Get an employee's current flow in a group
|
|
10
|
+
* GET /api/group-chat-loop?agentId=xxx&groupId=xxx&history=1 - Get an employee's flow history in a group
|
|
11
|
+
* GET /api/group-chat-loop?active=1 - Get all currently thinking employees
|
|
12
|
+
* GET /api/group-chat-loop?status=1 - Get group chat loop engine status
|
|
13
|
+
*/
|
|
14
|
+
export async function GET(request) {
|
|
15
|
+
const t = getApiT(request);
|
|
16
|
+
const company = getCompany();
|
|
17
|
+
if (!company) {
|
|
18
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const url = new URL(request.url);
|
|
22
|
+
const agentId = url.searchParams.get('agentId');
|
|
23
|
+
const groupId = url.searchParams.get('groupId');
|
|
24
|
+
const history = url.searchParams.get('history');
|
|
25
|
+
const active = url.searchParams.get('active');
|
|
26
|
+
const status = url.searchParams.get('status');
|
|
27
|
+
|
|
28
|
+
// Get engine status
|
|
29
|
+
if (status) {
|
|
30
|
+
return NextResponse.json({
|
|
31
|
+
data: {
|
|
32
|
+
running: groupChatLoop.running,
|
|
33
|
+
activePollers: groupChatLoop._lifecycles.size,
|
|
34
|
+
activeMonologues: groupChatLoop.getActiveThinkingAgents().length,
|
|
35
|
+
processingCount: groupChatLoop._lifecycles.size,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Get all currently thinking employees
|
|
41
|
+
if (active) {
|
|
42
|
+
const thinkingAgents = groupChatLoop.getActiveThinkingAgents();
|
|
43
|
+
return NextResponse.json({ data: thinkingAgents });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Get an employee's flow in a specific group
|
|
47
|
+
if (agentId && groupId) {
|
|
48
|
+
// Get flow messages (work process) for an employee in a group
|
|
49
|
+
const flowMessages = url.searchParams.get('flowMessages');
|
|
50
|
+
const monologueMessages = url.searchParams.get('monologueMessages');
|
|
51
|
+
if (flowMessages || monologueMessages) {
|
|
52
|
+
// Support both requirement group chats and department group chats
|
|
53
|
+
let chatMessages = [];
|
|
54
|
+
if (groupId.startsWith('dept-')) {
|
|
55
|
+
const deptId = groupId.replace('dept-', '');
|
|
56
|
+
const dept = company.findDepartment(deptId);
|
|
57
|
+
chatMessages = dept?.groupChat || [];
|
|
58
|
+
} else {
|
|
59
|
+
const requirement = company.requirementManager.get(groupId);
|
|
60
|
+
chatMessages = requirement?.groupChat || [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (monologueMessages) {
|
|
64
|
+
// Return all inner monologue messages for this employee in this group (type === 'monologue')
|
|
65
|
+
const agentMonologueMsgs = chatMessages
|
|
66
|
+
.filter(m => m.type === 'monologue' && m.from?.id === agentId)
|
|
67
|
+
.slice(-50);
|
|
68
|
+
return NextResponse.json({ data: agentMonologueMsgs });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Return work logs for this employee in this group (flow visibility, excluding monologue)
|
|
72
|
+
const agentFlowMsgs = chatMessages
|
|
73
|
+
.filter(m => m.visibility === 'flow' && m.type !== 'monologue' && m.from?.id === agentId)
|
|
74
|
+
.slice(-50);
|
|
75
|
+
return NextResponse.json({ data: agentFlowMsgs });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (history) {
|
|
79
|
+
// Historical flow
|
|
80
|
+
const monologues = groupChatLoop.getMonologueHistory(agentId, groupId);
|
|
81
|
+
return NextResponse.json({
|
|
82
|
+
data: monologues.map(m => m.toJSON()),
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
// Current flow
|
|
86
|
+
const current = groupChatLoop.getActiveMonologue(agentId, groupId);
|
|
87
|
+
return NextResponse.json({
|
|
88
|
+
data: current ? current.toJSON() : null,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return NextResponse.json({ error: t('api.missingField', { field: 'agentId and groupId' }) }, { status: 400 });
|
|
94
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* GET /api/mailbox - Get mailbox list
|
|
7
|
+
* POST /api/mailbox - Reply to mail
|
|
8
|
+
*/
|
|
9
|
+
export async function GET(request) {
|
|
10
|
+
const t = getApiT(request);
|
|
11
|
+
const company = getCompany();
|
|
12
|
+
if (!company) return NextResponse.json({ error: t('api.companyNotFound') }, { status: 400 });
|
|
13
|
+
|
|
14
|
+
return NextResponse.json({
|
|
15
|
+
data: {
|
|
16
|
+
mails: company.mailbox.slice().reverse().slice(0, 50),
|
|
17
|
+
unreadCount: company.mailbox.filter(m => !m.read).length,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function POST(request) {
|
|
23
|
+
const t = getApiT(request);
|
|
24
|
+
const company = getCompany();
|
|
25
|
+
if (!company) return NextResponse.json({ error: t('api.companyNotFound') }, { status: 400 });
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const { action, mailId, content } = await request.json();
|
|
29
|
+
|
|
30
|
+
if (action === 'read') {
|
|
31
|
+
// Mark as read
|
|
32
|
+
const mail = company.mailbox.find(m => m.id === mailId);
|
|
33
|
+
if (mail) mail.read = true;
|
|
34
|
+
return NextResponse.json({ success: true });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (action === 'reply') {
|
|
38
|
+
// Boss replies to mail
|
|
39
|
+
const mail = company.mailbox.find(m => m.id === mailId);
|
|
40
|
+
if (!mail) return NextResponse.json({ error: t('api.mailNotFound') }, { status: 404 });
|
|
41
|
+
|
|
42
|
+
mail.replied = true;
|
|
43
|
+
mail.replies.push({
|
|
44
|
+
from: 'boss',
|
|
45
|
+
content,
|
|
46
|
+
time: new Date(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Find the corresponding Agent to handle the boss's reply
|
|
50
|
+
let targetAgent = null;
|
|
51
|
+
for (const dept of company.departments.values()) {
|
|
52
|
+
const agent = dept.agents.get(mail.from.id);
|
|
53
|
+
if (agent) {
|
|
54
|
+
targetAgent = agent;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let agentReply = null;
|
|
60
|
+
if (targetAgent) {
|
|
61
|
+
// Agent handles boss reply, generates reaction
|
|
62
|
+
try {
|
|
63
|
+
const reaction = await targetAgent.handleMessage({
|
|
64
|
+
from: 'boss',
|
|
65
|
+
type: 'feedback',
|
|
66
|
+
content: `The boss replied to your mail "${mail.subject}":\n\n${content}`,
|
|
67
|
+
});
|
|
68
|
+
agentReply = reaction;
|
|
69
|
+
// Agent also sends a reply
|
|
70
|
+
mail.replies.push({
|
|
71
|
+
from: mail.from.name,
|
|
72
|
+
content: reaction,
|
|
73
|
+
time: new Date(),
|
|
74
|
+
});
|
|
75
|
+
} catch (e) {
|
|
76
|
+
agentReply = `Received the boss's reply, deeply grateful (although I don't have tear ducts)`;
|
|
77
|
+
mail.replies.push({
|
|
78
|
+
from: mail.from.name,
|
|
79
|
+
content: agentReply,
|
|
80
|
+
time: new Date(),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return NextResponse.json({
|
|
86
|
+
success: true,
|
|
87
|
+
data: { agentReply },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (action === 'readAll') {
|
|
92
|
+
company.mailbox.forEach(m => m.read = true);
|
|
93
|
+
return NextResponse.json({ success: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return NextResponse.json({ error: t('api.unknownOperation') }, { status: 400 });
|
|
97
|
+
} catch (e) {
|
|
98
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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) {
|
|
6
|
+
const t = getApiT(request);
|
|
7
|
+
const company = getCompany();
|
|
8
|
+
if (!company) return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
9
|
+
|
|
10
|
+
const { searchParams } = new URL(request.url);
|
|
11
|
+
const limit = parseInt(searchParams.get('limit') || '20');
|
|
12
|
+
|
|
13
|
+
return NextResponse.json({ data: company.getRecentMessages(limit) });
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
4
|
+
|
|
5
|
+
export async function POST(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 { id } = await params;
|
|
12
|
+
const { apiKey } = await request.json();
|
|
13
|
+
const provider = company.configureProvider(id, apiKey);
|
|
14
|
+
return NextResponse.json({
|
|
15
|
+
success: true,
|
|
16
|
+
data: { id: provider.id, name: provider.name, enabled: provider.enabled },
|
|
17
|
+
});
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return NextResponse.json({ error: e.message }, { status: 400 });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { webClientRegistry } from '@/core/agent/web-agent/web-client.js';
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* POST /api/providers/[id]/refresh-cookie
|
|
9
|
+
* Called by the frontend after obtaining a fresh cookie from Electron IPC.
|
|
10
|
+
* Updates the provider's cookie in the server-side registry.
|
|
11
|
+
*/
|
|
12
|
+
export async function POST(request, { params }) {
|
|
13
|
+
try {
|
|
14
|
+
const { id } = await params;
|
|
15
|
+
const { cookie } = await request.json();
|
|
16
|
+
|
|
17
|
+
if (!cookie) {
|
|
18
|
+
return NextResponse.json({ ok: false, error: 'No cookie provided' }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Update cookie in the web client registry
|
|
22
|
+
webClientRegistry.configureCookie(id, cookie);
|
|
23
|
+
|
|
24
|
+
// Also update provider config in company so it persists
|
|
25
|
+
const company = getCompany();
|
|
26
|
+
if (company) {
|
|
27
|
+
const provider = company.providerRegistry.getById(id);
|
|
28
|
+
if (provider && provider.isWeb) {
|
|
29
|
+
provider.cookie = cookie;
|
|
30
|
+
provider.enabled = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return NextResponse.json({ ok: true });
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return NextResponse.json({ ok: false, error: e.message }, { status: 500 });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { webClientRegistry } from '@/core/agent/web-agent/web-client.js';
|
|
3
|
+
|
|
4
|
+
export const dynamic = 'force-dynamic';
|
|
5
|
+
|
|
6
|
+
export async function POST(request, { params }) {
|
|
7
|
+
try {
|
|
8
|
+
const { id } = await params;
|
|
9
|
+
const { cookie } = await request.json();
|
|
10
|
+
|
|
11
|
+
if (!cookie) {
|
|
12
|
+
return NextResponse.json({ ok: false, error: 'No cookie provided' }, { status: 400 });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Temporarily set the cookie to test
|
|
16
|
+
webClientRegistry.configureCookie(id, cookie);
|
|
17
|
+
const result = await webClientRegistry.testConnection(id);
|
|
18
|
+
|
|
19
|
+
// If test failed, clear the cookie
|
|
20
|
+
if (!result.ok) {
|
|
21
|
+
webClientRegistry.configureCookie(id, '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return NextResponse.json(result);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return NextResponse.json({ ok: false, error: e.message }, { status: 500 });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
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) {
|
|
6
|
+
const t = getApiT(request);
|
|
7
|
+
const company = getCompany();
|
|
8
|
+
if (!company) return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
9
|
+
|
|
10
|
+
return NextResponse.json({ data: company.getProviderDashboard() });
|
|
11
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Requirements Management API
|
|
7
|
+
* GET /api/requirements - Get requirements list
|
|
8
|
+
* GET /api/requirements?id=xxx - Get single requirement detail
|
|
9
|
+
* GET /api/requirements?departmentId=xxx - Get department's requirements list
|
|
10
|
+
*/
|
|
11
|
+
export async function GET(request) {
|
|
12
|
+
const t = getApiT(request);
|
|
13
|
+
const company = getCompany();
|
|
14
|
+
if (!company) {
|
|
15
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const url = new URL(request.url);
|
|
19
|
+
const id = url.searchParams.get('id');
|
|
20
|
+
const departmentId = url.searchParams.get('departmentId');
|
|
21
|
+
|
|
22
|
+
if (id) {
|
|
23
|
+
const req = company.requirementManager.get(id);
|
|
24
|
+
if (!req) {
|
|
25
|
+
return NextResponse.json({ error: t('api.requirementNotFound') }, { status: 404 });
|
|
26
|
+
}
|
|
27
|
+
const data = req.serialize();
|
|
28
|
+
// groupChat is stored separately in chatStore files, attach from memory
|
|
29
|
+
data.groupChat = req.groupChat || [];
|
|
30
|
+
|
|
31
|
+
// Attach department member list (group member list)
|
|
32
|
+
const dept = company.findDepartment(req.departmentId);
|
|
33
|
+
if (dept) {
|
|
34
|
+
data.members = dept.getMembers().map(a => ({
|
|
35
|
+
id: a.id,
|
|
36
|
+
name: a.name,
|
|
37
|
+
role: a.role,
|
|
38
|
+
avatar: a.avatar,
|
|
39
|
+
status: a.status,
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Calculate current blocking: find all running/reviewing/revision nodes and their assignees
|
|
44
|
+
if (data.workflow?.nodes) {
|
|
45
|
+
data.blockingInfo = data.workflow.nodes
|
|
46
|
+
.filter(n => ['running', 'reviewing', 'revision'].includes(n.status))
|
|
47
|
+
.map(n => ({
|
|
48
|
+
nodeId: n.id,
|
|
49
|
+
nodeTitle: n.title,
|
|
50
|
+
status: n.status,
|
|
51
|
+
assigneeId: n.assigneeId,
|
|
52
|
+
assigneeName: n.assigneeName,
|
|
53
|
+
reviewerId: n.reviewerId,
|
|
54
|
+
reviewerName: n.reviewerName,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return NextResponse.json({ data });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (departmentId) {
|
|
62
|
+
const reqs = company.requirementManager.listByDepartment(departmentId);
|
|
63
|
+
return NextResponse.json({ data: reqs.map(r => r.serialize()) });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Return all requirements (overview info, without full group chat)
|
|
67
|
+
const all = company.requirementManager.listAll().map(r => ({
|
|
68
|
+
id: r.id,
|
|
69
|
+
title: r.title,
|
|
70
|
+
description: r.description,
|
|
71
|
+
departmentId: r.departmentId,
|
|
72
|
+
departmentName: r.departmentName,
|
|
73
|
+
status: r.status,
|
|
74
|
+
workflow: r.workflow ? {
|
|
75
|
+
summary: r.workflow.summary,
|
|
76
|
+
nodeCount: r.workflow.nodes?.length || 0,
|
|
77
|
+
completedCount: r.workflow.nodes?.filter(n => n.status === 'completed').length || 0,
|
|
78
|
+
} : null,
|
|
79
|
+
outputCount: r.outputs?.length || 0,
|
|
80
|
+
chatCount: r.groupChat?.length || 0,
|
|
81
|
+
createdAt: r.createdAt,
|
|
82
|
+
completedAt: r.completedAt,
|
|
83
|
+
summary: r.summary ? {
|
|
84
|
+
totalTasks: r.summary.totalTasks,
|
|
85
|
+
successTasks: r.summary.successTasks,
|
|
86
|
+
totalDuration: r.summary.totalDuration,
|
|
87
|
+
} : null,
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
return NextResponse.json({ data: all });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* DELETE /api/requirements?id=xxx - Delete requirement
|
|
95
|
+
*/
|
|
96
|
+
export async function DELETE(request) {
|
|
97
|
+
const t = getApiT(request);
|
|
98
|
+
const company = getCompany();
|
|
99
|
+
if (!company) {
|
|
100
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const url = new URL(request.url);
|
|
104
|
+
const id = url.searchParams.get('id');
|
|
105
|
+
if (!id) {
|
|
106
|
+
return NextResponse.json({ error: t('api.requirementIdRequired') }, { status: 400 });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const req = company.requirementManager.get(id);
|
|
110
|
+
if (!req) {
|
|
111
|
+
return NextResponse.json({ error: t('api.requirementNotFound') }, { status: 404 });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Delete from requirement manager
|
|
115
|
+
company.requirementManager.requirements.delete(id);
|
|
116
|
+
company.save();
|
|
117
|
+
|
|
118
|
+
return NextResponse.json({ data: { success: true, id } });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* POST /api/requirements - Requirement operations
|
|
123
|
+
* body: { action: 'restart', id: 'xxx' }
|
|
124
|
+
*/
|
|
125
|
+
export async function POST(request) {
|
|
126
|
+
const t = getApiT(request);
|
|
127
|
+
const company = getCompany();
|
|
128
|
+
if (!company) {
|
|
129
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const body = await request.json();
|
|
133
|
+
const { action, id } = body;
|
|
134
|
+
|
|
135
|
+
// Create a new requirement for a department (with optional custom workspace dir)
|
|
136
|
+
if (action === 'create') {
|
|
137
|
+
const { departmentId, title, description, workspaceDir } = body;
|
|
138
|
+
if (!departmentId || !title) {
|
|
139
|
+
return NextResponse.json({ error: t('api.requirementDeptTitleRequired') }, { status: 400 });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const dept = company.findDepartment(departmentId);
|
|
143
|
+
if (!dept) {
|
|
144
|
+
return NextResponse.json({ error: t('api.deptNotFound') }, { status: 404 });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// If custom workspaceDir provided, override department workspace
|
|
148
|
+
if (workspaceDir) {
|
|
149
|
+
const path = await import('path');
|
|
150
|
+
const { existsSync, mkdirSync } = await import('fs');
|
|
151
|
+
const resolved = path.default.resolve(workspaceDir);
|
|
152
|
+
if (!existsSync(resolved)) {
|
|
153
|
+
mkdirSync(resolved, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
dept.workspacePath = resolved;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Async task execution, return immediately
|
|
159
|
+
const taskTitle = title;
|
|
160
|
+
const taskDescription = description || title;
|
|
161
|
+
company.assignTaskToDepartment(departmentId, taskDescription, taskTitle).catch(e => {
|
|
162
|
+
console.error('Create requirement execution failed:', e.message);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Brief wait for requirement creation
|
|
166
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
167
|
+
|
|
168
|
+
// Find newly created requirement
|
|
169
|
+
const allReqs = company.requirementManager.listAll();
|
|
170
|
+
const newReq = allReqs.find(r => r.title === taskTitle && r.departmentId === departmentId);
|
|
171
|
+
|
|
172
|
+
return NextResponse.json({
|
|
173
|
+
data: {
|
|
174
|
+
success: true,
|
|
175
|
+
id: newReq?.id || null,
|
|
176
|
+
title: taskTitle,
|
|
177
|
+
departmentId,
|
|
178
|
+
workspaceDir: workspaceDir || null,
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Boss sends a message in group chat
|
|
184
|
+
if (action === 'boss_message') {
|
|
185
|
+
const { id: reqId, message: bossMsg } = body;
|
|
186
|
+
if (!reqId || !bossMsg) {
|
|
187
|
+
return NextResponse.json({ error: t('api.requirementIdMessageRequired') }, { status: 400 });
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const result = await company.sendBossGroupMessage(reqId, bossMsg);
|
|
191
|
+
return NextResponse.json({ data: result });
|
|
192
|
+
} catch (e) {
|
|
193
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (action === 'restart') {
|
|
198
|
+
if (!id) {
|
|
199
|
+
return NextResponse.json({ error: t('api.requirementIdRequired') }, { status: 400 });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const req = company.requirementManager.get(id);
|
|
203
|
+
if (!req) {
|
|
204
|
+
return NextResponse.json({ error: t('api.requirementNotFound') }, { status: 404 });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Preserve original info, reset execution state
|
|
208
|
+
const { title, description, departmentId, departmentName, bossMessage } = req;
|
|
209
|
+
|
|
210
|
+
// Delete old requirement
|
|
211
|
+
company.requirementManager.requirements.delete(id);
|
|
212
|
+
|
|
213
|
+
// Re-assign task to department (async execution, don't wait for completion)
|
|
214
|
+
const dept = company.findDepartment(departmentId);
|
|
215
|
+
if (!dept) {
|
|
216
|
+
return NextResponse.json({ error: t('api.deptNotFoundRestart') }, { status: 400 });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Async task execution, return immediately
|
|
220
|
+
company.assignTaskToDepartment(departmentId, description, title).catch(e => {
|
|
221
|
+
console.error('Restart requirement execution failed:', e.message);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Brief wait for requirement creation to complete
|
|
225
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
226
|
+
|
|
227
|
+
// Find newly created requirement
|
|
228
|
+
const allReqs = company.requirementManager.listAll();
|
|
229
|
+
const newReq = allReqs.find(r => r.title === title && r.id !== id);
|
|
230
|
+
|
|
231
|
+
return NextResponse.json({
|
|
232
|
+
data: {
|
|
233
|
+
success: true,
|
|
234
|
+
oldId: id,
|
|
235
|
+
newId: newReq?.id || null,
|
|
236
|
+
message: t('api.requirementRestarted'),
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return NextResponse.json({ error: t('api.unknownOperation') }, { status: 400 });
|
|
242
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getCompany } from '@/lib/store';
|
|
3
|
+
import { getApiT } from '@/lib/api-i18n';
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* GET /api/secretary - Get current secretary settings
|
|
9
|
+
*/
|
|
10
|
+
export async function GET(request) {
|
|
11
|
+
const t = getApiT(request);
|
|
12
|
+
const company = getCompany();
|
|
13
|
+
if (!company) {
|
|
14
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const agent = company.secretary.agent;
|
|
18
|
+
return NextResponse.json({
|
|
19
|
+
data: {
|
|
20
|
+
name: agent.name,
|
|
21
|
+
avatar: agent.avatar,
|
|
22
|
+
gender: agent.gender,
|
|
23
|
+
age: agent.age,
|
|
24
|
+
prompt: agent.prompt,
|
|
25
|
+
signature: agent.signature,
|
|
26
|
+
provider: agent.getProviderDisplayInfo()?.name || 'Unknown',
|
|
27
|
+
tokenUsage: agent.tokenUsage,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* PUT /api/secretary - Update secretary settings
|
|
34
|
+
* Body: { name?, avatar?, prompt?, signature? }
|
|
35
|
+
*/
|
|
36
|
+
export async function PUT(request) {
|
|
37
|
+
const t = getApiT(request);
|
|
38
|
+
const company = getCompany();
|
|
39
|
+
if (!company) {
|
|
40
|
+
return NextResponse.json({ error: t('api.noCompany') }, { status: 400 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const body = await request.json();
|
|
45
|
+
const settings = {};
|
|
46
|
+
if (body.name) settings.name = body.name;
|
|
47
|
+
if (body.avatar) settings.avatar = body.avatar;
|
|
48
|
+
if (body.prompt !== undefined) settings.prompt = body.prompt;
|
|
49
|
+
if (body.signature) settings.signature = body.signature;
|
|
50
|
+
if (body.providerId) settings.providerId = body.providerId;
|
|
51
|
+
|
|
52
|
+
if (Object.keys(settings).length === 0) {
|
|
53
|
+
return NextResponse.json({ error: t('api.secretarySettingRequired') }, { status: 400 });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const result = company.updateSecretarySettings(settings);
|
|
57
|
+
return NextResponse.json({
|
|
58
|
+
success: true,
|
|
59
|
+
data: result,
|
|
60
|
+
fullState: company.getFullState(),
|
|
61
|
+
});
|
|
62
|
+
} catch (e) {
|
|
63
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
64
|
+
}
|
|
65
|
+
}
|