cowork-dash 0.1.2__py3-none-any.whl
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.
- cowork_dash/__init__.py +35 -0
- cowork_dash/__main__.py +9 -0
- cowork_dash/agent.py +117 -0
- cowork_dash/app.py +1776 -0
- cowork_dash/assets/app.js +237 -0
- cowork_dash/assets/favicon.svg +1 -0
- cowork_dash/assets/styles.css +915 -0
- cowork_dash/canvas.py +318 -0
- cowork_dash/cli.py +273 -0
- cowork_dash/components.py +568 -0
- cowork_dash/config.py +91 -0
- cowork_dash/file_utils.py +226 -0
- cowork_dash/layout.py +250 -0
- cowork_dash/tools.py +699 -0
- cowork_dash-0.1.2.dist-info/METADATA +238 -0
- cowork_dash-0.1.2.dist-info/RECORD +19 -0
- cowork_dash-0.1.2.dist-info/WHEEL +4 -0
- cowork_dash-0.1.2.dist-info/entry_points.txt +2 -0
- cowork_dash-0.1.2.dist-info/licenses/LICENSE +21 -0
cowork_dash/canvas.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""Canvas utilities for parsing, exporting, and loading canvas objects."""
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import json
|
|
5
|
+
import base64
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, List
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
def parse_canvas_object(obj: Any, workspace_root: Path) -> Dict[str, Any]:
|
|
12
|
+
"""Parse Python objects into canvas-renderable format.
|
|
13
|
+
|
|
14
|
+
Supports:
|
|
15
|
+
- pd.DataFrame (inline in markdown)
|
|
16
|
+
- matplotlib.figure.Figure (saved to .canvas/ folder)
|
|
17
|
+
- plotly.graph_objects.Figure (saved to .canvas/ folder)
|
|
18
|
+
- PIL.Image (saved to .canvas/ folder)
|
|
19
|
+
- dict (Plotly JSON - saved to .canvas/ folder)
|
|
20
|
+
- str (Markdown with Mermaid support - inline)
|
|
21
|
+
"""
|
|
22
|
+
obj_type = type(obj).__name__
|
|
23
|
+
module = type(obj).__module__
|
|
24
|
+
|
|
25
|
+
# Ensure .canvas directory exists
|
|
26
|
+
canvas_dir = workspace_root / ".canvas"
|
|
27
|
+
canvas_dir.mkdir(exist_ok=True)
|
|
28
|
+
|
|
29
|
+
# Pandas DataFrame - keep inline
|
|
30
|
+
if module.startswith('pandas') and obj_type == 'DataFrame':
|
|
31
|
+
return {
|
|
32
|
+
"type": "dataframe",
|
|
33
|
+
"data": obj.to_dict('records'),
|
|
34
|
+
"columns": list(obj.columns),
|
|
35
|
+
"html": obj.to_html(index=False, classes="dataframe-table")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Matplotlib Figure - save to file
|
|
39
|
+
elif module.startswith('matplotlib') and 'Figure' in obj_type:
|
|
40
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
|
41
|
+
filename = f"matplotlib_{timestamp}.png"
|
|
42
|
+
filepath = canvas_dir / filename
|
|
43
|
+
|
|
44
|
+
obj.savefig(filepath, format='png', bbox_inches='tight', dpi=100)
|
|
45
|
+
|
|
46
|
+
# Also store base64 for in-memory rendering
|
|
47
|
+
buf = io.BytesIO()
|
|
48
|
+
obj.savefig(buf, format='png', bbox_inches='tight', dpi=100)
|
|
49
|
+
buf.seek(0)
|
|
50
|
+
img_base64 = base64.b64encode(buf.read()).decode('utf-8')
|
|
51
|
+
buf.close()
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
"type": "matplotlib",
|
|
55
|
+
"file": filename, # Relative to .canvas/ directory where canvas.md lives
|
|
56
|
+
"data": img_base64 # Keep for current session rendering
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Plotly Figure - save to file
|
|
60
|
+
elif module.startswith('plotly') and 'Figure' in obj_type:
|
|
61
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
|
62
|
+
filename = f"plotly_{timestamp}.json"
|
|
63
|
+
filepath = canvas_dir / filename
|
|
64
|
+
|
|
65
|
+
plotly_data = json.loads(obj.to_json())
|
|
66
|
+
filepath.write_text(json.dumps(plotly_data, indent=2))
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
"type": "plotly",
|
|
70
|
+
"file": filename, # Relative to .canvas/ directory where canvas.md lives
|
|
71
|
+
"data": plotly_data # Keep for current session rendering
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# PIL Image - save to file
|
|
75
|
+
elif module.startswith('PIL') and 'Image' in obj_type:
|
|
76
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
|
77
|
+
filename = f"image_{timestamp}.png"
|
|
78
|
+
filepath = canvas_dir / filename
|
|
79
|
+
|
|
80
|
+
obj.save(filepath, format='PNG')
|
|
81
|
+
|
|
82
|
+
# Also store base64 for in-memory rendering
|
|
83
|
+
buf = io.BytesIO()
|
|
84
|
+
obj.save(buf, format='PNG')
|
|
85
|
+
buf.seek(0)
|
|
86
|
+
img_base64 = base64.b64encode(buf.read()).decode('utf-8')
|
|
87
|
+
buf.close()
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
"type": "image",
|
|
91
|
+
"file": filename, # Relative to .canvas/ directory where canvas.md lives
|
|
92
|
+
"data": img_base64 # Keep for current session rendering
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Plotly dict format - save to file
|
|
96
|
+
elif isinstance(obj, dict) and ('data' in obj or 'layout' in obj):
|
|
97
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
|
98
|
+
filename = f"plotly_{timestamp}.json"
|
|
99
|
+
filepath = canvas_dir / filename
|
|
100
|
+
|
|
101
|
+
filepath.write_text(json.dumps(obj, indent=2))
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
"type": "plotly",
|
|
105
|
+
"file": filename, # Relative to .canvas/ directory where canvas.md lives
|
|
106
|
+
"data": obj # Keep for current session rendering
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# Markdown string - check for Mermaid diagrams - keep inline
|
|
110
|
+
elif isinstance(obj, str):
|
|
111
|
+
# Check if it's a Mermaid diagram
|
|
112
|
+
if re.search(r'```mermaid', obj, re.IGNORECASE):
|
|
113
|
+
# Extract mermaid code - more flexible pattern
|
|
114
|
+
match = re.search(r'```mermaid\s*\n?(.*?)```', obj, re.DOTALL | re.IGNORECASE)
|
|
115
|
+
if match:
|
|
116
|
+
mermaid_code = match.group(1).strip()
|
|
117
|
+
return {
|
|
118
|
+
"type": "mermaid",
|
|
119
|
+
"data": mermaid_code
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
"type": "markdown",
|
|
124
|
+
"data": obj
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Unknown type - convert to string - keep inline
|
|
128
|
+
else:
|
|
129
|
+
return {
|
|
130
|
+
"type": "markdown",
|
|
131
|
+
"data": f"```\n{str(obj)}\n```"
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def export_canvas_to_markdown(canvas_items: List[Dict], workspace_root: Path, output_path: str = None):
|
|
136
|
+
"""Export canvas to markdown file with file references."""
|
|
137
|
+
# Ensure .canvas directory exists
|
|
138
|
+
canvas_dir = workspace_root / ".canvas"
|
|
139
|
+
canvas_dir.mkdir(exist_ok=True)
|
|
140
|
+
|
|
141
|
+
if not output_path:
|
|
142
|
+
output_path = canvas_dir / "canvas.md"
|
|
143
|
+
|
|
144
|
+
lines = [
|
|
145
|
+
"# Canvas Export",
|
|
146
|
+
f"\n*Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
for i, parsed in enumerate(canvas_items):
|
|
150
|
+
item_type = parsed.get("type", "unknown")
|
|
151
|
+
|
|
152
|
+
# Add title if present
|
|
153
|
+
if "title" in parsed:
|
|
154
|
+
lines.append(f"\n## {parsed['title']}\n")
|
|
155
|
+
|
|
156
|
+
if item_type == "markdown":
|
|
157
|
+
lines.append(f"\n{parsed.get('data', '')}\n")
|
|
158
|
+
|
|
159
|
+
elif item_type == "mermaid":
|
|
160
|
+
lines.append(f"\n```mermaid\n{parsed.get('data', '')}\n```\n")
|
|
161
|
+
|
|
162
|
+
elif item_type == "dataframe":
|
|
163
|
+
lines.append(f"\n{parsed.get('html', '')}\n")
|
|
164
|
+
|
|
165
|
+
elif item_type == "matplotlib" or item_type == "image":
|
|
166
|
+
# Reference the file instead of embedding base64
|
|
167
|
+
file_ref = parsed.get("file", "")
|
|
168
|
+
if file_ref:
|
|
169
|
+
lines.append(f"\n\n")
|
|
170
|
+
else:
|
|
171
|
+
# Fallback to base64 if no file
|
|
172
|
+
img_data = parsed.get("data", "")
|
|
173
|
+
lines.append(f"\n\n")
|
|
174
|
+
|
|
175
|
+
elif item_type == "plotly":
|
|
176
|
+
# Reference the file
|
|
177
|
+
file_ref = parsed.get("file", "")
|
|
178
|
+
if file_ref:
|
|
179
|
+
lines.append(f"\n```plotly\n{file_ref}\n```\n")
|
|
180
|
+
else:
|
|
181
|
+
# Fallback to inline
|
|
182
|
+
lines.append(f"\n```json\n{json.dumps(parsed.get('data'), indent=2)}\n```\n")
|
|
183
|
+
|
|
184
|
+
# Write to file
|
|
185
|
+
output_file = Path(output_path)
|
|
186
|
+
output_file.write_text("\n".join(lines))
|
|
187
|
+
return str(output_file)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def load_canvas_from_markdown(workspace_root: Path, markdown_path: str = None) -> List[Dict]:
|
|
191
|
+
"""Load canvas from markdown file and referenced assets."""
|
|
192
|
+
if not markdown_path:
|
|
193
|
+
markdown_path = workspace_root / ".canvas" / "canvas.md"
|
|
194
|
+
else:
|
|
195
|
+
markdown_path = Path(markdown_path)
|
|
196
|
+
|
|
197
|
+
if not markdown_path.exists():
|
|
198
|
+
return []
|
|
199
|
+
|
|
200
|
+
content = markdown_path.read_text()
|
|
201
|
+
canvas_items = []
|
|
202
|
+
|
|
203
|
+
# First, extract all code blocks to process them separately
|
|
204
|
+
code_blocks = []
|
|
205
|
+
|
|
206
|
+
# Find all mermaid blocks
|
|
207
|
+
for match in re.finditer(r'```mermaid\s*\n(.*?)```', content, re.DOTALL | re.IGNORECASE):
|
|
208
|
+
start, end = match.span()
|
|
209
|
+
code_blocks.append({
|
|
210
|
+
'type': 'mermaid',
|
|
211
|
+
'start': start,
|
|
212
|
+
'end': end,
|
|
213
|
+
'content': match.group(1).strip()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
# Find all plotly blocks (supports both relative filenames and legacy .canvas/ paths)
|
|
217
|
+
for match in re.finditer(r'```plotly\s*\n([^\n]+)\n```', content, re.DOTALL):
|
|
218
|
+
start, end = match.span()
|
|
219
|
+
code_blocks.append({
|
|
220
|
+
'type': 'plotly_file',
|
|
221
|
+
'start': start,
|
|
222
|
+
'end': end,
|
|
223
|
+
'content': match.group(1).strip()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
# Find all image references (supports both relative filenames and legacy .canvas/ paths)
|
|
227
|
+
for match in re.finditer(r'!\[.*?\]\(([^)]+)\)', content):
|
|
228
|
+
start, end = match.span()
|
|
229
|
+
file_ref = match.group(1)
|
|
230
|
+
# Skip data: URLs (base64 embedded images)
|
|
231
|
+
if not file_ref.startswith('data:'):
|
|
232
|
+
code_blocks.append({
|
|
233
|
+
'type': 'image_file',
|
|
234
|
+
'start': start,
|
|
235
|
+
'end': end,
|
|
236
|
+
'content': file_ref
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
# Find all HTML tables
|
|
240
|
+
for match in re.finditer(r'<table.*?</table>', content, re.DOTALL):
|
|
241
|
+
start, end = match.span()
|
|
242
|
+
code_blocks.append({
|
|
243
|
+
'type': 'table',
|
|
244
|
+
'start': start,
|
|
245
|
+
'end': end,
|
|
246
|
+
'content': match.group(0)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
# Sort blocks by position
|
|
250
|
+
code_blocks.sort(key=lambda x: x['start'])
|
|
251
|
+
|
|
252
|
+
# Process content in order
|
|
253
|
+
last_pos = 0
|
|
254
|
+
for block in code_blocks:
|
|
255
|
+
# Add any markdown content before this block
|
|
256
|
+
if block['start'] > last_pos:
|
|
257
|
+
markdown_text = content[last_pos:block['start']].strip()
|
|
258
|
+
# Clean up metadata lines but keep actual content
|
|
259
|
+
lines = markdown_text.split('\n')
|
|
260
|
+
filtered_lines = []
|
|
261
|
+
for line in lines:
|
|
262
|
+
# Skip only the exact metadata lines
|
|
263
|
+
if line.strip() in ['# Canvas Export', ''] or line.strip().startswith('*Generated:'):
|
|
264
|
+
continue
|
|
265
|
+
filtered_lines.append(line)
|
|
266
|
+
|
|
267
|
+
cleaned_text = '\n'.join(filtered_lines).strip()
|
|
268
|
+
if cleaned_text:
|
|
269
|
+
canvas_items.append({
|
|
270
|
+
"type": "markdown",
|
|
271
|
+
"data": cleaned_text
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
# Add the block itself
|
|
275
|
+
if block['type'] == 'mermaid':
|
|
276
|
+
canvas_items.append({
|
|
277
|
+
"type": "mermaid",
|
|
278
|
+
"data": block['content']
|
|
279
|
+
})
|
|
280
|
+
elif block['type'] == 'plotly_file':
|
|
281
|
+
file_ref = block['content']
|
|
282
|
+
file_path = markdown_path.parent / file_ref
|
|
283
|
+
if file_path.exists():
|
|
284
|
+
plotly_data = json.loads(file_path.read_text())
|
|
285
|
+
canvas_items.append({
|
|
286
|
+
"type": "plotly",
|
|
287
|
+
"file": file_ref,
|
|
288
|
+
"data": plotly_data
|
|
289
|
+
})
|
|
290
|
+
elif block['type'] == 'image_file':
|
|
291
|
+
file_ref = block['content']
|
|
292
|
+
file_path = markdown_path.parent / file_ref
|
|
293
|
+
if file_path.exists():
|
|
294
|
+
with open(file_path, 'rb') as f:
|
|
295
|
+
img_base64 = base64.b64encode(f.read()).decode('utf-8')
|
|
296
|
+
canvas_items.append({
|
|
297
|
+
"type": "image",
|
|
298
|
+
"file": file_ref,
|
|
299
|
+
"data": img_base64
|
|
300
|
+
})
|
|
301
|
+
elif block['type'] == 'table':
|
|
302
|
+
canvas_items.append({
|
|
303
|
+
"type": "dataframe",
|
|
304
|
+
"html": block['content']
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
last_pos = block['end']
|
|
308
|
+
|
|
309
|
+
# Add any remaining markdown after the last block
|
|
310
|
+
if last_pos < len(content):
|
|
311
|
+
markdown_text = content[last_pos:].strip()
|
|
312
|
+
if markdown_text:
|
|
313
|
+
canvas_items.append({
|
|
314
|
+
"type": "markdown",
|
|
315
|
+
"data": markdown_text
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
return canvas_items
|
cowork_dash/cli.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Command-line interface for Cowork Dash (formerly DeepAgent Dash)."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import argparse
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def init_project(name: str, template: str = "default"):
|
|
11
|
+
"""Initialize a new Cowork Dash project."""
|
|
12
|
+
project_dir = Path(name).resolve()
|
|
13
|
+
|
|
14
|
+
if project_dir.exists():
|
|
15
|
+
print(f"❌ Error: Directory '{name}' already exists")
|
|
16
|
+
return 1
|
|
17
|
+
|
|
18
|
+
print(f"📦 Creating project: {project_dir}")
|
|
19
|
+
|
|
20
|
+
# Create project structure
|
|
21
|
+
project_dir.mkdir(parents=True)
|
|
22
|
+
workspace_dir = project_dir / "workspace"
|
|
23
|
+
workspace_dir.mkdir()
|
|
24
|
+
|
|
25
|
+
# Copy config template
|
|
26
|
+
import cowork_dash
|
|
27
|
+
package_dir = Path(cowork_dash.__file__).parent
|
|
28
|
+
template_file = package_dir / "config.py"
|
|
29
|
+
|
|
30
|
+
if not template_file.exists():
|
|
31
|
+
print(f"❌ Error: Template not found at {template_file}")
|
|
32
|
+
return 1
|
|
33
|
+
|
|
34
|
+
shutil.copy(template_file, project_dir / "config.py")
|
|
35
|
+
|
|
36
|
+
# Create .env template
|
|
37
|
+
env_template = """# Cowork Dash Environment Variables
|
|
38
|
+
|
|
39
|
+
# API Keys
|
|
40
|
+
ANTHROPIC_API_KEY=your_api_key_here
|
|
41
|
+
|
|
42
|
+
# Optional: Override config.py settings (uses DEEPAGENT_* prefix for compatibility)
|
|
43
|
+
# DEEPAGENT_WORKSPACE_ROOT=./workspace
|
|
44
|
+
# DEEPAGENT_PORT=8050
|
|
45
|
+
# DEEPAGENT_HOST=localhost
|
|
46
|
+
# DEEPAGENT_DEBUG=False
|
|
47
|
+
"""
|
|
48
|
+
(project_dir / ".env.example").write_text(env_template)
|
|
49
|
+
|
|
50
|
+
# Create .gitignore
|
|
51
|
+
gitignore = """# Python
|
|
52
|
+
__pycache__/
|
|
53
|
+
*.py[cod]
|
|
54
|
+
*$py.class
|
|
55
|
+
*.so
|
|
56
|
+
.Python
|
|
57
|
+
env/
|
|
58
|
+
venv/
|
|
59
|
+
ENV/
|
|
60
|
+
|
|
61
|
+
# Cowork Dash
|
|
62
|
+
.env
|
|
63
|
+
workspace/
|
|
64
|
+
canvas.md
|
|
65
|
+
.canvas/
|
|
66
|
+
*.log
|
|
67
|
+
|
|
68
|
+
# IDE
|
|
69
|
+
.vscode/
|
|
70
|
+
.idea/
|
|
71
|
+
*.swp
|
|
72
|
+
*.swo
|
|
73
|
+
"""
|
|
74
|
+
(project_dir / ".gitignore").write_text(gitignore)
|
|
75
|
+
|
|
76
|
+
# Create README
|
|
77
|
+
readme = f"""# {name}
|
|
78
|
+
|
|
79
|
+
A Cowork Dash project.
|
|
80
|
+
|
|
81
|
+
## Setup
|
|
82
|
+
|
|
83
|
+
1. **Configure your API key** (if using DeepAgents):
|
|
84
|
+
```bash
|
|
85
|
+
cp .env.example .env
|
|
86
|
+
# Edit .env and add your ANTHROPIC_API_KEY
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
2. **Edit config.py** to customize your agent and settings
|
|
90
|
+
|
|
91
|
+
3. **Run the application**:
|
|
92
|
+
```bash
|
|
93
|
+
cowork-dash run
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Run with defaults from config.py
|
|
100
|
+
cowork-dash run
|
|
101
|
+
|
|
102
|
+
# Override settings
|
|
103
|
+
cowork-dash run --port 8080 --debug
|
|
104
|
+
|
|
105
|
+
# Use custom agent
|
|
106
|
+
cowork-dash run --agent my_agent.py:agent
|
|
107
|
+
|
|
108
|
+
# See all options
|
|
109
|
+
cowork-dash run --help
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Project Structure
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
{name}/
|
|
116
|
+
├── config.py # Main configuration (edit this)
|
|
117
|
+
├── workspace/ # Your agent's workspace
|
|
118
|
+
├── .env.example # Environment variables template
|
|
119
|
+
└── .gitignore # Git ignore patterns
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Documentation
|
|
123
|
+
|
|
124
|
+
- [Cowork Dash Documentation](https://github.com/dkedar7/cowork-dash)
|
|
125
|
+
- [CLI Usage Guide](https://github.com/dkedar7/cowork-dash/blob/main/docs/CLI_USAGE.md)
|
|
126
|
+
"""
|
|
127
|
+
(project_dir / "README.md").write_text(readme)
|
|
128
|
+
|
|
129
|
+
print(f"✓ Created project structure")
|
|
130
|
+
print(f"✓ Created config.py")
|
|
131
|
+
print(f"✓ Created workspace/")
|
|
132
|
+
print(f"✓ Created .env.example")
|
|
133
|
+
print(f"✓ Created .gitignore")
|
|
134
|
+
print(f"✓ Created README.md")
|
|
135
|
+
print(f"\n{'='*50}")
|
|
136
|
+
print(f"🎉 Project '{name}' created successfully!")
|
|
137
|
+
print(f"{'='*50}\n")
|
|
138
|
+
print(f"Next steps:")
|
|
139
|
+
print(f" 1. cd {name}")
|
|
140
|
+
print(f" 2. cp .env.example .env # If using DeepAgents")
|
|
141
|
+
print(f" 3. Edit .env and add your ANTHROPIC_API_KEY")
|
|
142
|
+
print(f" 4. Edit config.py to customize your agent")
|
|
143
|
+
print(f" 5. cowork-dash run")
|
|
144
|
+
print()
|
|
145
|
+
|
|
146
|
+
return 0
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def run_app_cli(args):
|
|
150
|
+
"""Run the application with CLI arguments."""
|
|
151
|
+
# Import here to avoid loading Dash when just running init
|
|
152
|
+
from .app import run_app
|
|
153
|
+
|
|
154
|
+
return run_app(
|
|
155
|
+
workspace=args.workspace,
|
|
156
|
+
agent_spec=args.agent,
|
|
157
|
+
port=args.port,
|
|
158
|
+
host=args.host,
|
|
159
|
+
debug=args.debug,
|
|
160
|
+
title=args.title,
|
|
161
|
+
config_file=args.config
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def main():
|
|
166
|
+
"""Main CLI entry point."""
|
|
167
|
+
parser = argparse.ArgumentParser(
|
|
168
|
+
prog="cowork-dash",
|
|
169
|
+
description="Cowork Dash - AI Agent Web Interface",
|
|
170
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
171
|
+
epilog="""
|
|
172
|
+
Examples:
|
|
173
|
+
# Initialize a new project
|
|
174
|
+
cowork-dash init my-agent-project
|
|
175
|
+
|
|
176
|
+
# Run with defaults from config.py
|
|
177
|
+
cowork-dash run
|
|
178
|
+
|
|
179
|
+
# Run with custom settings
|
|
180
|
+
cowork-dash run --workspace ~/projects --port 8080
|
|
181
|
+
|
|
182
|
+
# Run with custom agent
|
|
183
|
+
cowork-dash run --agent my_agent.py:agent
|
|
184
|
+
|
|
185
|
+
# Debug mode
|
|
186
|
+
cowork-dash run --debug
|
|
187
|
+
|
|
188
|
+
For more help: https://github.com/dkedar7/cowork-dash
|
|
189
|
+
"""
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
|
193
|
+
|
|
194
|
+
# cowork-dash init
|
|
195
|
+
init_parser = subparsers.add_parser(
|
|
196
|
+
"init",
|
|
197
|
+
help="Initialize a new project",
|
|
198
|
+
description="Create a new Cowork Dash project with config template"
|
|
199
|
+
)
|
|
200
|
+
init_parser.add_argument("name", help="Project name/directory")
|
|
201
|
+
init_parser.add_argument(
|
|
202
|
+
"--template",
|
|
203
|
+
default="default",
|
|
204
|
+
help="Template to use (default: default)"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# cowork-dash run
|
|
208
|
+
run_parser = subparsers.add_parser(
|
|
209
|
+
"run",
|
|
210
|
+
help="Run the application",
|
|
211
|
+
description="Run Cowork Dash with optional configuration overrides"
|
|
212
|
+
)
|
|
213
|
+
run_parser.add_argument(
|
|
214
|
+
"--workspace",
|
|
215
|
+
type=str,
|
|
216
|
+
help="Workspace directory path (overrides config.py)"
|
|
217
|
+
)
|
|
218
|
+
run_parser.add_argument(
|
|
219
|
+
"--agent",
|
|
220
|
+
type=str,
|
|
221
|
+
metavar="PATH:OBJECT",
|
|
222
|
+
help='Agent specification as "path/to/file.py:object_name"'
|
|
223
|
+
)
|
|
224
|
+
run_parser.add_argument(
|
|
225
|
+
"--port",
|
|
226
|
+
type=int,
|
|
227
|
+
help="Port to run on (overrides config.py)"
|
|
228
|
+
)
|
|
229
|
+
run_parser.add_argument(
|
|
230
|
+
"--host",
|
|
231
|
+
type=str,
|
|
232
|
+
help="Host to bind to (overrides config.py)"
|
|
233
|
+
)
|
|
234
|
+
run_parser.add_argument(
|
|
235
|
+
"--debug",
|
|
236
|
+
action="store_true",
|
|
237
|
+
help="Enable debug mode"
|
|
238
|
+
)
|
|
239
|
+
run_parser.add_argument(
|
|
240
|
+
"--no-debug",
|
|
241
|
+
action="store_true",
|
|
242
|
+
help="Disable debug mode"
|
|
243
|
+
)
|
|
244
|
+
run_parser.add_argument(
|
|
245
|
+
"--title",
|
|
246
|
+
type=str,
|
|
247
|
+
help="Application title (overrides config.py)"
|
|
248
|
+
)
|
|
249
|
+
run_parser.add_argument(
|
|
250
|
+
"--config",
|
|
251
|
+
type=str,
|
|
252
|
+
default="./config.py",
|
|
253
|
+
help="Config file path (default: ./config.py)"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Parse arguments
|
|
257
|
+
args = parser.parse_args()
|
|
258
|
+
|
|
259
|
+
# Handle commands
|
|
260
|
+
if args.command == "init":
|
|
261
|
+
return init_project(args.name, args.template)
|
|
262
|
+
|
|
263
|
+
elif args.command == "run":
|
|
264
|
+
return run_app_cli(args)
|
|
265
|
+
|
|
266
|
+
else:
|
|
267
|
+
# No command provided - show help
|
|
268
|
+
parser.print_help()
|
|
269
|
+
return 1
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
if __name__ == "__main__":
|
|
273
|
+
sys.exit(main())
|