cowork-dash 0.1.2__tar.gz → 0.1.3__tar.gz

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 (30) hide show
  1. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/CHANGELOG.md +10 -0
  2. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/PKG-INFO +1 -1
  3. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/app.py +71 -25
  4. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/config.py +8 -3
  5. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/layout.py +2 -2
  6. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/pyproject.toml +1 -1
  7. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/.claude/settings.local.json +0 -0
  8. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/.gitignore +0 -0
  9. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/LICENSE +0 -0
  10. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/MANIFEST.in +0 -0
  11. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/README.md +0 -0
  12. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/__init__.py +0 -0
  13. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/__main__.py +0 -0
  14. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/agent.py +0 -0
  15. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/assets/app.js +0 -0
  16. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/assets/favicon.svg +0 -0
  17. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/assets/styles.css +0 -0
  18. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/canvas.py +0 -0
  19. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/cli.py +0 -0
  20. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/components.py +0 -0
  21. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/file_utils.py +0 -0
  22. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/cowork_dash/tools.py +0 -0
  23. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/docs/CLI_USAGE.md +0 -0
  24. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/examples/example_agent.py +0 -0
  25. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/examples/python_api_example.py +0 -0
  26. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/requirements.txt +0 -0
  27. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/templates/index.html +0 -0
  28. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/tests/__init__.py +0 -0
  29. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/tests/conftest.py +0 -0
  30. {cowork_dash-0.1.2 → cowork_dash-0.1.3}/tests/test_core.py +0 -0
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.3] - 2026-01-20
9
+
10
+ ### Added
11
+ - Support for Python module format in agent spec (e.g., `mypackage.module.agent`)
12
+ - Agent spec now accepts both file path (`file.py:object`) and module path formats
13
+
14
+ ### Fixed
15
+ - Header layout on large screens - components now stay at edges instead of centering
16
+
8
17
  ## [0.1.2] - 2026-01-19
9
18
 
10
19
  ### Added
@@ -74,6 +83,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
74
83
  - Resizable split-pane interface
75
84
  - Upload/download functionality for files
76
85
 
86
+ [0.1.3]: https://github.com/dkedar7/cowork-dash/compare/v0.1.2...v0.1.3
77
87
  [0.1.2]: https://github.com/dkedar7/cowork-dash/compare/v0.1.1...v0.1.2
78
88
  [0.1.1]: https://github.com/dkedar7/cowork-dash/compare/v0.1.0...v0.1.1
79
89
  [0.1.0]: https://github.com/dkedar7/cowork-dash/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cowork-dash
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: AI Agent Web Interface with Filesystem and Canvas Visualization
5
5
  Project-URL: Homepage, https://github.com/dkedar7/cowork-dash
6
6
  Project-URL: Documentation, https://github.com/dkedar7/cowork-dash/blob/main/README.md
@@ -122,43 +122,83 @@ Examples:
122
122
 
123
123
  def load_agent_from_spec(agent_spec: str):
124
124
  """
125
- Load agent from specification string in format "path/to/file.py:object_name".
125
+ Load agent from specification string.
126
+
127
+ Supports two formats:
128
+ 1. File path format: "path/to/file.py:object_name"
129
+ 2. Module format: "mypackage.module.submodule.object_name"
126
130
 
127
131
  Args:
128
- agent_spec: String like "agent.py:agent" or "my_agents.py:custom_agent"
132
+ agent_spec: String like "agent.py:agent", "my_agents.py:custom_agent",
133
+ or "mypackage.agents.my_agent"
129
134
 
130
135
  Returns:
131
136
  tuple: (agent_object, error_message)
132
137
  """
133
138
  try:
134
- # Parse the spec
135
- if ":" not in agent_spec:
136
- return None, f"Invalid agent spec '{agent_spec}'. Expected format: 'path/to/file.py:object_name'"
139
+ # Determine format: file path (contains ":") vs module path (no ":" and no ".py")
140
+ if ":" in agent_spec:
141
+ # File path format: "path/to/file.py:object_name"
142
+ return _load_agent_from_file(agent_spec)
143
+ elif agent_spec.endswith(".py"):
144
+ # Looks like a file path without object name
145
+ return None, f"Invalid agent spec '{agent_spec}'. File path format requires object name: 'path/to/file.py:object_name'"
146
+ else:
147
+ # Module format: "mypackage.module.object_name"
148
+ return _load_agent_from_module(agent_spec)
149
+
150
+ except Exception as e:
151
+ return None, f"Failed to load agent from {agent_spec}: {e}"
137
152
 
138
- file_path, object_name = agent_spec.rsplit(":", 1)
139
- file_path = Path(file_path).resolve()
140
153
 
141
- if not file_path.exists():
142
- return None, f"Agent file not found: {file_path}"
154
+ def _load_agent_from_file(agent_spec: str):
155
+ """Load agent from file path format: 'path/to/file.py:object_name'"""
156
+ file_path, object_name = agent_spec.rsplit(":", 1)
157
+ file_path = Path(file_path).resolve()
143
158
 
144
- # Load the module
145
- spec = importlib.util.spec_from_file_location("custom_agent_module", file_path)
146
- if spec is None or spec.loader is None:
147
- return None, f"Failed to load module from {file_path}"
159
+ if not file_path.exists():
160
+ return None, f"Agent file not found: {file_path}"
148
161
 
149
- module = importlib.util.module_from_spec(spec)
150
- sys.modules["custom_agent_module"] = module
151
- spec.loader.exec_module(module)
162
+ # Load the module
163
+ spec = importlib.util.spec_from_file_location("custom_agent_module", file_path)
164
+ if spec is None or spec.loader is None:
165
+ return None, f"Failed to load module from {file_path}"
152
166
 
153
- # Get the object
154
- if not hasattr(module, object_name):
155
- return None, f"Object '{object_name}' not found in {file_path}"
167
+ module = importlib.util.module_from_spec(spec)
168
+ sys.modules["custom_agent_module"] = module
169
+ spec.loader.exec_module(module)
156
170
 
157
- agent = getattr(module, object_name)
158
- return agent, None
171
+ # Get the object
172
+ if not hasattr(module, object_name):
173
+ return None, f"Object '{object_name}' not found in {file_path}"
174
+
175
+ agent = getattr(module, object_name)
176
+ return agent, None
159
177
 
160
- except Exception as e:
161
- return None, f"Failed to load agent from {agent_spec}: {e}"
178
+
179
+ def _load_agent_from_module(agent_spec: str):
180
+ """Load agent from module format: 'mypackage.module.object_name'"""
181
+ parts = agent_spec.rsplit(".", 1)
182
+
183
+ if len(parts) < 2:
184
+ return None, f"Invalid module spec '{agent_spec}'. Expected format: 'module.object_name' or 'package.module.object_name'"
185
+
186
+ module_path, object_name = parts
187
+
188
+ try:
189
+ # Import the module
190
+ module = importlib.import_module(module_path)
191
+ except ModuleNotFoundError as e:
192
+ return None, f"Module '{module_path}' not found: {e}"
193
+ except ImportError as e:
194
+ return None, f"Failed to import module '{module_path}': {e}"
195
+
196
+ # Get the object
197
+ if not hasattr(module, object_name):
198
+ return None, f"Object '{object_name}' not found in module '{module_path}'"
199
+
200
+ agent = getattr(module, object_name)
201
+ return agent, None
162
202
 
163
203
  # Module-level configuration (uses config defaults)
164
204
  WORKSPACE_ROOT = config.WORKSPACE_ROOT
@@ -1621,7 +1661,10 @@ def run_app(
1621
1661
  Args:
1622
1662
  agent_instance (object, optional): Agent object instance (Python API only)
1623
1663
  workspace (str, optional): Workspace directory path
1624
- agent_spec (str, optional): Agent specification as "path:object" (overrides agent_instance)
1664
+ agent_spec (str, optional): Agent specification (overrides agent_instance).
1665
+ Supports two formats:
1666
+ - File path: "path/to/file.py:object_name"
1667
+ - Module path: "mypackage.module.object_name"
1625
1668
  port (int, optional): Port number
1626
1669
  host (str, optional): Host to bind to
1627
1670
  debug (bool, optional): Debug mode
@@ -1638,9 +1681,12 @@ def run_app(
1638
1681
  >>> my_agent = MyAgent()
1639
1682
  >>> run_app(my_agent, workspace="~/my-workspace")
1640
1683
 
1641
- >>> # Using agent spec
1684
+ >>> # Using agent spec (file path format)
1642
1685
  >>> run_app(agent_spec="my_agent.py:agent", port=8080)
1643
1686
 
1687
+ >>> # Using agent spec (module format)
1688
+ >>> run_app(agent_spec="mypackage.agents.my_agent", port=8080)
1689
+
1644
1690
  >>> # Without agent (manual mode)
1645
1691
  >>> run_app(workspace="~/my-workspace", debug=True)
1646
1692
  """
@@ -53,11 +53,16 @@ def get_config(key: str, default=None, type_cast=None):
53
53
  _workspace_path = get_config("workspace_root", default="./")
54
54
  WORKSPACE_ROOT = Path(_workspace_path).resolve() if _workspace_path else Path("./").resolve()
55
55
 
56
- # Agent specification (format: "module_path:variable_name")
56
+ # Agent specification - supports two formats:
57
+ # 1. File path: "path/to/file.py:object_name"
58
+ # 2. Module path: "mypackage.module.object_name"
57
59
  # Environment variable: DEEPAGENT_SPEC (or DEEPAGENT_AGENT_SPEC for backwards compatibility)
58
60
  # CLI argument: --agent
59
- # Default: None (manual mode, no agent)
60
- # Example: "mymodule:agent" or "/path/to/agent.py:my_agent"
61
+ # Default: package's built-in agent
62
+ # Examples:
63
+ # - "my_agents.py:agent" (file in current directory)
64
+ # - "/path/to/agent.py:my_agent" (absolute path)
65
+ # - "mypackage.agents.my_agent" (installed Python module)
61
66
  _default_agent = str(Path(__file__).parent / "agent.py") + ":agent"
62
67
  AGENT_SPEC = get_config("spec", default=None) or get_config("agent_spec", default=None) or _default_agent
63
68
 
@@ -100,8 +100,8 @@ Let's get started!"""
100
100
  ], style={"display": "flex", "alignItems": "center"})
101
101
  ], style={
102
102
  "display": "flex", "justifyContent": "space-between",
103
- "alignItems": "center", "maxWidth": "1600px",
104
- "margin": "0 auto", "padding": "0 12px",
103
+ "alignItems": "center", "width": "100%",
104
+ "padding": "0 12px",
105
105
  })
106
106
  ], id="header", style={
107
107
  "background": "var(--mantine-color-body)",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cowork-dash"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "AI Agent Web Interface with Filesystem and Canvas Visualization"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes
File without changes
File without changes