opencompany 0.1.0

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/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # OpenCo
2
+
3
+ AI Agent collaboration platform — multiple AI agents working together as a team.
4
+
5
+ ## Features
6
+
7
+ - **Agent Management** — Create and configure AI agents with custom models and tools
8
+ - **Org Chart** — Tree-structured positions with automatic context, supports task delegation
9
+ - **Live Chat** — SSE streaming with tool call visualization
10
+ - **Tool System** — Web search, file read/write, command execution with sandbox isolation
11
+ - **Task Board** — Create, assign, and track task progress
12
+ - **Workspace** — Project management with agent access control
13
+ - **Activity Log** — Track agent file operations and command executions
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install -g openco
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ openco # Start server, opens browser automatically (default: http://localhost:8181)
25
+ openco --port 3000 # Custom port
26
+ openco --no-browser # Don't open browser
27
+ openco fix # Repair data files + check installation integrity
28
+ ```
29
+
30
+ Data is stored in `~/.openco/` on first run.
31
+
32
+ ## Supported Models
33
+
34
+ Configure via the settings page. Supports all OpenAI-compatible APIs:
35
+
36
+ - DeepSeek
37
+ - GLM (Zhipu)
38
+ - MiniMax
39
+ - Kimi (Moonshot)
40
+ - Qwen (Tongyi)
41
+ - Other compatible APIs
42
+
43
+ ## Project Structure
44
+
45
+ ```
46
+ OpenCo/
47
+ ├── backend/ # Rust backend (axum)
48
+ │ └── src/
49
+ │ ├── main.rs # CLI entry & HTTP server
50
+ │ ├── handlers.rs
51
+ │ ├── agents.rs
52
+ │ ├── config.rs
53
+ │ ├── fix.rs # openco fix repair logic
54
+ │ ├── org.rs
55
+ │ ├── tasks.rs
56
+ │ ├── tools/ # Agent tools
57
+ │ └── ...
58
+ ├── frontend/ # Frontend (HTML + JS + CSS)
59
+ ├── cli.js # npm entry script
60
+ ├── package.json
61
+ └── README_CN.md # 中文文档
62
+ ```
63
+
64
+ ## Development
65
+
66
+ Requires [Rust](https://rustup.rs/) and [Node.js](https://nodejs.org/).
67
+
68
+ ```bash
69
+ # Build backend
70
+ cd backend && cargo build
71
+
72
+ # Run (dev mode, data in project directory)
73
+ cargo run
74
+
75
+ # Build release
76
+ cargo build --release
77
+ ```
78
+
79
+ ## License
80
+
81
+ MIT
package/cli.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { platform, arch } = process;
4
+ const path = require('path');
5
+ const { spawn } = require('child_process');
6
+
7
+ const platformKey = `${platform}-${arch}`;
8
+ const binName = platform === 'win32' ? 'openco.exe' : 'openco';
9
+ const binPath = path.join(__dirname, 'binaries', platformKey, binName);
10
+
11
+ const fs = require('fs');
12
+ if (!fs.existsSync(binPath)) {
13
+ console.error(`Error: No binary found for platform "${platformKey}".`);
14
+ console.error(`Expected: ${binPath}`);
15
+ console.error('');
16
+ console.error('Supported platforms: linux-x64, darwin-arm64, darwin-x64, win32-x64');
17
+ process.exit(1);
18
+ }
19
+
20
+ const child = spawn(binPath, process.argv.slice(2), {
21
+ stdio: 'inherit',
22
+ windowsHide: false,
23
+ });
24
+
25
+ child.on('exit', (code) => {
26
+ process.exit(code ?? 0);
27
+ });
28
+
29
+ child.on('error', (err) => {
30
+ console.error('Failed to start OpenCo:', err.message);
31
+ process.exit(1);
32
+ });
@@ -0,0 +1,380 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>OpenCo</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
9
+ <link href="https://cdn.jsdelivr.net/npm/geist@1/dist/fonts/geist-sans/style.css" rel="stylesheet">
10
+ <link href="https://cdn.jsdelivr.net/npm/geist@1/dist/fonts/geist-mono/style.css" rel="stylesheet">
11
+ <link rel="stylesheet" href="style.css">
12
+ </head>
13
+ <body>
14
+ <div id="app">
15
+ <!-- Background Grid -->
16
+ <div class="bg-grid"></div>
17
+
18
+ <!-- Top Bar -->
19
+ <header class="top-bar">
20
+ <div class="logo">
21
+ <span class="logo-dot"></span>
22
+ <span class="logo-text">OpenCo</span>
23
+ </div>
24
+ <div class="theme-toggle">
25
+ <button class="toggle-btn active" data-theme-val="dark">
26
+ <i data-lucide="moon"></i>
27
+ </button>
28
+ <button class="toggle-btn" data-theme-val="light">
29
+ <i data-lucide="sun"></i>
30
+ </button>
31
+ </div>
32
+ </header>
33
+
34
+ <!-- Sidebar -->
35
+ <nav class="sidebar">
36
+ <div class="sidebar-menu">
37
+ <div class="menu-item-group expanded">
38
+ <button class="menu-item active" data-page="chat">
39
+ <div class="menu-item-left">
40
+ <i data-lucide="message-square"></i>
41
+ <span>Chat</span>
42
+ </div>
43
+ <i data-lucide="chevron-down"></i>
44
+ </button>
45
+ <div class="submenu" id="agent-list">
46
+ <!-- Dynamically populated -->
47
+ </div>
48
+ </div>
49
+ <button class="menu-item" data-page="projects">
50
+ <i data-lucide="folder"></i>
51
+ <span>Projects</span>
52
+ </button>
53
+ <button class="menu-item" data-page="taskboard">
54
+ <i data-lucide="kanban"></i>
55
+ <span>TaskBoard</span>
56
+ </button>
57
+ <button class="menu-item" data-page="conversation">
58
+ <i data-lucide="messages-square"></i>
59
+ <span>Conversation</span>
60
+ </button>
61
+ <button class="menu-item" data-page="log">
62
+ <i data-lucide="scroll-text"></i>
63
+ <span>Log</span>
64
+ </button>
65
+ <button class="menu-item" data-page="home">
66
+ <i data-lucide="layout-grid"></i>
67
+ <span>Manage</span>
68
+ </button>
69
+ <button class="menu-item" data-page="interaction">
70
+ <i data-lucide="users"></i>
71
+ <span>Interaction</span>
72
+ </button>
73
+ <button class="menu-item" data-page="settings">
74
+ <i data-lucide="settings"></i>
75
+ <span>Setting</span>
76
+ </button>
77
+ </div>
78
+ <div class="sidebar-footer">
79
+ <div class="sidebar-contact-row">
80
+ <span class="sidebar-contact-label">Contact:</span>
81
+ <button class="sidebar-copy-btn" id="contact-copy-btn" title="Copy contact email">
82
+ <i data-lucide="copy"></i>
83
+ </button>
84
+ </div>
85
+ <div class="sidebar-version">
86
+ <span class="dot dot-green"></span>
87
+ <span class="version-text">v1.0.0</span>
88
+ </div>
89
+ </div>
90
+ </nav>
91
+
92
+ <!-- Main Content Area -->
93
+ <div class="main-content">
94
+ <!-- Page: Home (Manage Org) -->
95
+ <section id="page-home" class="page-content">
96
+ <div class="manage-header">
97
+ <span class="manage-title">Company Structure</span>
98
+ <div style="display:flex;gap:8px;align-items:center">
99
+ <div class="zoom-controls" id="zoom-controls">
100
+ <button class="icon-btn btn-sm" id="zoom-out">−</button>
101
+ <span id="zoom-label">100%</span>
102
+ <button class="icon-btn btn-sm" id="zoom-in">+</button>
103
+ <button class="icon-btn btn-sm" id="zoom-reset">↺</button>
104
+ </div>
105
+ <button class="btn-primary btn-sm" id="org-add-btn">+ Add Position</button>
106
+ </div>
107
+ </div>
108
+ <div class="manage-body" id="manage-body">
109
+ <div class="org-tree-wrapper" id="org-tree-wrapper">
110
+ <div class="org-tree" id="org-tree">
111
+ <!-- Dynamically rendered -->
112
+ </div>
113
+ </div>
114
+ <div class="org-panel" id="org-panel" style="display:none">
115
+ <h4 class="org-panel-title">Position Details</h4>
116
+ <div class="settings-field">
117
+ <label>Agent Name</label>
118
+ <input id="org-pos-agent-name" placeholder="Agent name..." />
119
+ </div>
120
+ <div class="settings-field">
121
+ <label>Agent Model</label>
122
+ <select id="org-pos-agent-model">
123
+ <option value="">-- Use Default --</option>
124
+ </select>
125
+ </div>
126
+ <div class="settings-field">
127
+ <label>Title</label>
128
+ <input id="org-pos-title" placeholder="e.g. CEO, CTO..." />
129
+ </div>
130
+ <div class="settings-field">
131
+ <label>Reports to</label>
132
+ <select id="org-pos-parent"><option value="">-- None (Top Level) --</option></select>
133
+ </div>
134
+ <div class="settings-field">
135
+ <label>System Prompt</label>
136
+ <textarea id="org-pos-prompt" rows="6" placeholder="Describe this position's responsibilities..."></textarea>
137
+ </div>
138
+ <div class="settings-actions">
139
+ <button class="btn-primary" id="org-pos-save">Save</button>
140
+ <button class="btn-danger" id="org-pos-delete">Delete</button>
141
+ <span id="org-save-feedback" class="save-feedback">Saved!</span>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </section>
146
+
147
+ <!-- Page: Chat -->
148
+ <section id="page-chat" class="page-content active">
149
+ <div class="chat-header">
150
+ <div class="chat-header-left">
151
+ <span class="dot dot-purple"></span>
152
+ <span class="chat-title">Robot 1</span>
153
+ <span class="chat-status">Online</span>
154
+ </div>
155
+ <button class="icon-btn">
156
+ <i data-lucide="ellipsis"></i>
157
+ </button>
158
+ </div>
159
+ <div id="chat" class="chat-messages"></div>
160
+ <div class="chat-input-bar">
161
+ <div class="input-wrapper">
162
+ <input id="input" type="text" placeholder="Type a message..." />
163
+ </div>
164
+ <button class="icon-btn" id="attach-btn">
165
+ <i data-lucide="paperclip"></i>
166
+ </button>
167
+ <button class="icon-btn send-btn" id="send-btn">
168
+ <i data-lucide="send"></i>
169
+ </button>
170
+ </div>
171
+ </section>
172
+
173
+ <!-- Page: Projects -->
174
+ <section id="page-projects" class="page-content">
175
+ <div class="projects-header">
176
+ <span class="projects-title">Projects</span>
177
+ <button class="btn-primary btn-sm" id="add-project-btn">+ New Project</button>
178
+ </div>
179
+ <div class="projects-grid" id="projects-grid">
180
+ <!-- Dynamically rendered -->
181
+ </div>
182
+ </section>
183
+
184
+ <!-- Page: Interaction (Login) -->
185
+ <section id="page-interaction" class="page-content">
186
+ <div class="interaction-page">
187
+ <div class="interaction-content">
188
+ <div class="interaction-logo">
189
+ <span class="interaction-logo-dot"></span>
190
+ <span class="interaction-logo-text">OpenCo</span>
191
+ </div>
192
+ <div class="interaction-btn-row">
193
+ <button class="interaction-btn active" data-panel="signin">Sign In</button>
194
+ <button class="interaction-btn" data-panel="signup">Sign Up</button>
195
+ </div>
196
+ <!-- Sign In Panel -->
197
+ <div class="interaction-panel" id="panel-signin">
198
+ <div class="interaction-panel-title">Sign In</div>
199
+ <div class="settings-field">
200
+ <label>Email</label>
201
+ <input id="signin-email" type="email" placeholder="you@example.com" />
202
+ </div>
203
+ <div class="settings-field">
204
+ <label>Password</label>
205
+ <input id="signin-password" type="password" placeholder="••••••••" />
206
+ </div>
207
+ <button class="btn-primary interaction-submit" id="signin-submit">Sign In</button>
208
+ </div>
209
+ <!-- Sign Up Panel -->
210
+ <div class="interaction-panel" id="panel-signup" style="display:none">
211
+ <div class="interaction-panel-title">Sign Up</div>
212
+ <div class="settings-field">
213
+ <label>Email</label>
214
+ <input id="signup-email" type="email" placeholder="you@example.com" />
215
+ </div>
216
+ <div class="settings-field">
217
+ <label>Password</label>
218
+ <input id="signup-password" type="password" placeholder="••••••••" />
219
+ </div>
220
+ <div class="settings-field">
221
+ <label>Confirm Password</label>
222
+ <input id="signup-confirm" type="password" placeholder="••••••••" />
223
+ </div>
224
+ <button class="btn-primary interaction-submit" id="signup-submit">Sign Up</button>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </section>
229
+
230
+ <!-- Page: Settings -->
231
+ <section id="page-settings" class="page-content">
232
+ <div class="settings-header">
233
+ <span class="settings-title">Settings</span>
234
+ </div>
235
+ <div class="settings-body">
236
+ <div class="settings-section">
237
+ <div class="settings-section-header">
238
+ <h3 class="settings-section-title">Models</h3>
239
+ <button class="btn-primary btn-sm" id="add-model-btn">+ Add Model</button>
240
+ </div>
241
+ <div class="model-list-panel">
242
+ <div id="model-list" class="model-list">
243
+ <!-- Dynamically rendered -->
244
+ </div>
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </section>
249
+
250
+ <!-- Page: TaskBoard -->
251
+ <section id="page-taskboard" class="page-content">
252
+ <div class="manage-header">
253
+ <span class="manage-title">TaskBoard</span>
254
+ <button class="btn-primary btn-sm" id="add-task-btn">+ New Task</button>
255
+ </div>
256
+ <div class="taskboard-body">
257
+ <div id="task-list" class="task-list"></div>
258
+ </div>
259
+ </section>
260
+
261
+ <!-- Page: Conversation -->
262
+ <section id="page-conversation" class="page-content">
263
+ <div class="manage-header">
264
+ <span class="manage-title">Conversation</span>
265
+ </div>
266
+ <div class="page-placeholder">Agent conversations will appear here</div>
267
+ </section>
268
+
269
+ <!-- Page: Log -->
270
+ <section id="page-log" class="page-content">
271
+ <div class="manage-header">
272
+ <span class="manage-title">Log</span>
273
+ <button class="icon-btn btn-sm" id="log-refresh-btn" title="Refresh">
274
+ <i data-lucide="refresh-cw"></i>
275
+ </button>
276
+ </div>
277
+ <div class="log-body">
278
+ <input id="log-search" class="log-search" type="text" placeholder="Search logs..." />
279
+ <div id="log-list" class="log-list"></div>
280
+ </div>
281
+ </section>
282
+ </div>
283
+ </div>
284
+
285
+ <!-- Add Position Modal -->
286
+ <div id="add-pos-modal" class="modal" style="display:none">
287
+ <div class="modal-backdrop" id="modal-backdrop"></div>
288
+ <div class="modal-box">
289
+ <h4 class="modal-title">Add Position</h4>
290
+ <div class="settings-field">
291
+ <label>Agent Name</label>
292
+ <input id="modal-agent-name" placeholder="Agent name" />
293
+ </div>
294
+ <div class="settings-field">
295
+ <label>Agent Model</label>
296
+ <select id="modal-agent-model">
297
+ <option value="">-- Use Default --</option>
298
+ </select>
299
+ </div>
300
+ <div class="settings-field">
301
+ <label>Position Title</label>
302
+ <input id="modal-pos-title" placeholder="e.g. CEO" />
303
+ </div>
304
+ <div class="settings-field">
305
+ <label>Reports to (optional)</label>
306
+ <select id="modal-pos-parent">
307
+ <option value="">-- None (Top Level) --</option>
308
+ </select>
309
+ </div>
310
+ <div class="settings-actions">
311
+ <button class="btn-primary" id="modal-confirm">Add</button>
312
+ <button class="btn-secondary" id="modal-cancel">Cancel</button>
313
+ </div>
314
+ </div>
315
+ </div>
316
+ <!-- Add Model Modal -->
317
+ <div id="add-model-modal" class="modal" style="display:none">
318
+ <div class="modal-backdrop" id="model-modal-backdrop"></div>
319
+ <div class="modal-box">
320
+ <h4 class="modal-title">Add Model</h4>
321
+ <div class="settings-field">
322
+ <label>Name</label>
323
+ <input id="model-entry-name" placeholder="e.g. DeepSeek-V3, GLM-Pro" />
324
+ </div>
325
+ <div class="settings-field">
326
+ <label>Provider</label>
327
+ <select id="model-entry-provider">
328
+ <option value="">-- Select --</option>
329
+ <option value="deepseek">DeepSeek</option>
330
+ <option value="kimi">Kimi (Moonshot)</option>
331
+ <option value="qwen">Qwen</option>
332
+ <option value="zhipu">GLM (Zhipu)</option>
333
+ <option value="minimax">MiniMax</option>
334
+ </select>
335
+ </div>
336
+ <div class="settings-field">
337
+ <label>API URL</label>
338
+ <input id="model-entry-url" placeholder="https://..." />
339
+ </div>
340
+ <div class="settings-field">
341
+ <label>API Key</label>
342
+ <input id="model-entry-key" type="password" placeholder="sk-..." />
343
+ </div>
344
+ <div class="settings-field">
345
+ <label>Model</label>
346
+ <input id="model-entry-model" placeholder="model name" />
347
+ </div>
348
+ <div class="settings-actions">
349
+ <button class="btn-primary" id="model-entry-save">Save</button>
350
+ <button class="btn-secondary" id="model-entry-cancel">Cancel</button>
351
+ <span id="model-save-feedback" class="save-feedback">Saved!</span>
352
+ </div>
353
+ </div>
354
+ </div>
355
+
356
+ <!-- Add Task Modal -->
357
+ <div id="add-task-modal" class="modal" style="display:none">
358
+ <div class="modal-backdrop" id="task-modal-backdrop"></div>
359
+ <div class="modal-box">
360
+ <h4 class="modal-title" id="task-modal-title">New Task</h4>
361
+ <div class="settings-field">
362
+ <label>Title</label>
363
+ <input id="task-entry-title" placeholder="Task title..." />
364
+ </div>
365
+ <div class="settings-field">
366
+ <label>Description</label>
367
+ <textarea id="task-entry-desc" rows="3" placeholder="Describe the task..."></textarea>
368
+ </div>
369
+ <input type="hidden" id="task-entry-id" value="" />
370
+ <div class="settings-actions">
371
+ <button class="btn-primary" id="task-entry-save">Save</button>
372
+ <button class="btn-secondary" id="task-entry-cancel">Cancel</button>
373
+ </div>
374
+ </div>
375
+ </div>
376
+
377
+ <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
378
+ <script type="module" src="js/app.js?v=8"></script>
379
+ </body>
380
+ </html>
@@ -0,0 +1,129 @@
1
+ import { state, chatEl, inputEl, sendBtnEl, agentListEl } from './state.js';
2
+ import { loadHistoryFromBackend, appendMsg, updateProgress } from './chat.js';
3
+ import { navigateTo } from './router.js';
4
+
5
+ export async function loadAgents() {
6
+ try {
7
+ const res = await fetch('/api/agents');
8
+ const data = await res.json();
9
+ state.agents = data.agents || [];
10
+ renderAgentList();
11
+
12
+ if (!state.currentAgent && state.agents.length > 0) {
13
+ selectAgent(state.agents[0].name);
14
+ }
15
+ } catch (e) {
16
+ console.error('Failed to load agents:', e);
17
+ }
18
+ }
19
+
20
+ // Build agent name → position title map from org data
21
+ function getAgentTitleMap() {
22
+ const map = {};
23
+ (state.orgData?.positions || []).forEach(pos => {
24
+ (pos.agents || []).forEach(name => {
25
+ map[name] = pos.title;
26
+ });
27
+ });
28
+ return map;
29
+ }
30
+
31
+ let _searchComposing = false;
32
+
33
+ function ensureSearchBox() {
34
+ if (!agentListEl) return null;
35
+ let searchInput = agentListEl.querySelector('.agent-search-input');
36
+ if (searchInput) return searchInput;
37
+
38
+ agentListEl.innerHTML = '';
39
+
40
+ const searchWrap = document.createElement('div');
41
+ searchWrap.className = 'agent-search-wrap';
42
+ searchInput = document.createElement('input');
43
+ searchInput.type = 'text';
44
+ searchInput.className = 'agent-search-input';
45
+ searchInput.placeholder = 'Search agent...';
46
+ searchInput.addEventListener('compositionstart', () => { _searchComposing = true; });
47
+ searchInput.addEventListener('compositionend', () => {
48
+ _searchComposing = false;
49
+ filterAgentButtons(searchInput.value);
50
+ });
51
+ searchInput.addEventListener('input', () => {
52
+ if (!_searchComposing) filterAgentButtons(searchInput.value);
53
+ });
54
+ searchWrap.appendChild(searchInput);
55
+ agentListEl.appendChild(searchWrap);
56
+
57
+ // Placeholder for agent buttons
58
+ const listDiv = document.createElement('div');
59
+ listDiv.className = 'agent-list-items';
60
+ agentListEl.appendChild(listDiv);
61
+
62
+ return searchInput;
63
+ }
64
+
65
+ function filterAgentButtons(filter = '') {
66
+ const listDiv = agentListEl?.querySelector('.agent-list-items');
67
+ if (!listDiv) return;
68
+ listDiv.innerHTML = '';
69
+
70
+ const titleMap = getAgentTitleMap();
71
+ const keyword = filter.trim().toLowerCase();
72
+
73
+ const filtered = keyword
74
+ ? state.agents.filter(a => {
75
+ const display = titleMap[a.name] ? `${a.name}(${titleMap[a.name]})` : a.name;
76
+ return display.toLowerCase().includes(keyword) || a.name.toLowerCase().includes(keyword);
77
+ })
78
+ : state.agents;
79
+
80
+ filtered.forEach(agent => {
81
+ const btn = document.createElement('button');
82
+ btn.className = 'submenu-item' + (state.currentAgent === agent.name ? ' active' : '');
83
+ btn.dataset.agent = agent.name;
84
+ btn.dataset.page = 'chat-' + agent.name;
85
+ const title = titleMap[agent.name];
86
+ const label = title ? `${agent.name}(${title})` : agent.name;
87
+ const dot = document.createElement('span');
88
+ dot.className = 'dot dot-purple';
89
+ const labelSpan = document.createElement('span');
90
+ labelSpan.textContent = label;
91
+ btn.appendChild(dot);
92
+ btn.appendChild(labelSpan);
93
+ btn.addEventListener('click', () => {
94
+ selectAgent(agent.name);
95
+ navigateTo('chat');
96
+ });
97
+ listDiv.appendChild(btn);
98
+ });
99
+ }
100
+
101
+ export function renderAgentList(filter = '') {
102
+ const searchInput = ensureSearchBox();
103
+ if (searchInput && filter !== undefined) {
104
+ searchInput.value = filter;
105
+ }
106
+ filterAgentButtons(filter);
107
+ }
108
+
109
+ export function selectAgent(name) {
110
+ state.currentAgent = name;
111
+
112
+ document.querySelectorAll('.submenu-item[data-agent]').forEach(el => {
113
+ el.classList.toggle('active', el.dataset.agent === name);
114
+ });
115
+
116
+ const chatTitle = document.querySelector('.chat-title');
117
+ if (chatTitle) chatTitle.textContent = name;
118
+
119
+ chatEl.innerHTML = '';
120
+ state.agentMessages[name] = [];
121
+ loadHistoryFromBackend(name);
122
+
123
+ const busy = state.agentSending[name] || false;
124
+ sendBtnEl.disabled = busy;
125
+ inputEl.disabled = busy;
126
+ if (!busy) inputEl.focus();
127
+
128
+ updateProgress();
129
+ }
@@ -0,0 +1,29 @@
1
+ import { initTheme, initThemeToggle } from './theme.js';
2
+ import { initRouter } from './router.js';
3
+ import { initChat } from './chat.js';
4
+ import { loadAgents } from './agents.js';
5
+ import { initSettings, loadConfigIntoForm } from './settings.js';
6
+ import { loadOrg, initOrg } from './org.js';
7
+ import { initInteraction } from './interaction.js';
8
+ import { initContactBar } from './contact-bar.js';
9
+ import { initProjects } from './projects.js';
10
+ import { initLog, loadLogs } from './log.js';
11
+ import { initTasks, loadTasks } from './tasks.js';
12
+
13
+ document.addEventListener('DOMContentLoaded', () => {
14
+ if (typeof lucide !== 'undefined') {
15
+ lucide.createIcons();
16
+ }
17
+ initTheme();
18
+ initThemeToggle();
19
+ initRouter();
20
+ initChat();
21
+ initSettings();
22
+ initInteraction();
23
+ initContactBar();
24
+ initProjects();
25
+ initLog();
26
+ initTasks();
27
+ loadAgents().then(() => loadOrg());
28
+ initOrg();
29
+ });