dlab-cli 0.1.3__tar.gz → 0.2.0__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 (57) hide show
  1. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/PKG-INFO +3 -1
  2. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/cli.py +71 -0
  3. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/docker.py +2 -1
  4. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/js/parallel-agents.ts +6 -1
  5. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/local.py +2 -1
  6. dlab_cli-0.2.0/dlab/opencode_logparser.py +625 -0
  7. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/timeline.py +91 -157
  8. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/app.py +7 -0
  9. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/log_watcher.py +18 -23
  10. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/models.py +26 -6
  11. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/widgets/agent_list.py +14 -6
  12. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/widgets/artifacts_pane.py +24 -3
  13. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/widgets/log_view.py +119 -17
  14. dlab_cli-0.2.0/dlab/viewer/__init__.py +7 -0
  15. dlab_cli-0.2.0/dlab/viewer/html/__init__.py +0 -0
  16. dlab_cli-0.2.0/dlab/viewer/html/viewer.html +498 -0
  17. dlab_cli-0.2.0/dlab/viewer/layout.py +121 -0
  18. dlab_cli-0.2.0/dlab/viewer/server.py +408 -0
  19. dlab_cli-0.2.0/dlab/viewer/session_data.py +890 -0
  20. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab_cli.egg-info/PKG-INFO +3 -1
  21. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab_cli.egg-info/SOURCES.txt +10 -1
  22. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab_cli.egg-info/requires.txt +2 -0
  23. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/pyproject.toml +4 -1
  24. dlab_cli-0.2.0/tests/test_opencode_logparser.py +724 -0
  25. dlab_cli-0.2.0/tests/test_viewer.py +806 -0
  26. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/LICENSE +0 -0
  27. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/README.md +0 -0
  28. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/__init__.py +0 -0
  29. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/config.py +0 -0
  30. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/create_dpack.py +0 -0
  31. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/create_dpack_wizard.py +0 -0
  32. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/create_parallel_agent_wizard.py +0 -0
  33. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/data/__init__.py +0 -0
  34. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/data/models.json +0 -0
  35. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/js/__init__.py +0 -0
  36. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/model_fallback.py +0 -0
  37. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/parallel_tool.py +0 -0
  38. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/session.py +0 -0
  39. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/__init__.py +0 -0
  40. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/widgets/__init__.py +0 -0
  41. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/widgets/search_popup.py +0 -0
  42. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab/tui/widgets/status_bar.py +0 -0
  43. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab_cli.egg-info/dependency_links.txt +0 -0
  44. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab_cli.egg-info/entry_points.txt +0 -0
  45. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/dlab_cli.egg-info/top_level.txt +0 -0
  46. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/setup.cfg +0 -0
  47. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_cli.py +0 -0
  48. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_config.py +0 -0
  49. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_create_dpack.py +0 -0
  50. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_create_dpack_wizard.py +0 -0
  51. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_create_parallel_agent_wizard.py +0 -0
  52. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_docker.py +0 -0
  53. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_generate_dpack_integration.py +0 -0
  54. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_integration.py +0 -0
  55. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_model_fallback.py +0 -0
  56. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_parallel_tool.py +0 -0
  57. {dlab_cli-0.1.3 → dlab_cli-0.2.0}/tests/test_session.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dlab-cli
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: A harness for agentic data science — run coding agents with domain skills, parallel subagents, and frozen Docker environments
5
5
  Author: DecisionAI
6
6
  License: Apache-2.0
@@ -18,6 +18,8 @@ Requires-Dist: pyyaml>=6.0
18
18
  Requires-Dist: textual>=2.0
19
19
  Requires-Dist: modal>=0.70
20
20
  Requires-Dist: dhub-cli>=0.10
21
+ Requires-Dist: fastapi>=0.100
22
+ Requires-Dist: uvicorn[standard]>=0.20
21
23
  Provides-Extra: dev
22
24
  Requires-Dist: pytest>=7.0; extra == "dev"
23
25
  Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
@@ -273,6 +273,34 @@ def create_parser() -> argparse.ArgumentParser:
273
273
  help="Path to session work directory (default: cwd if it has _opencode_logs)",
274
274
  )
275
275
 
276
+ # View subcommand (browser-based session viewer)
277
+ view_parser = subparsers.add_parser(
278
+ "view",
279
+ help="Open browser-based session viewer with DAG visualization",
280
+ )
281
+ view_parser.add_argument(
282
+ "work_dir",
283
+ metavar="WORK_DIR",
284
+ help="Path to session work directory",
285
+ )
286
+ view_parser.add_argument(
287
+ "--port",
288
+ type=int,
289
+ default=0,
290
+ help="Port for the viewer server (default: auto-select)",
291
+ )
292
+ view_parser.add_argument(
293
+ "--no-open",
294
+ action="store_true",
295
+ help="Start server without opening browser",
296
+ )
297
+ view_parser.add_argument(
298
+ "--export",
299
+ metavar="FILE",
300
+ default=None,
301
+ help="Export self-contained HTML file instead of starting server",
302
+ )
303
+
276
304
  # Create decision-pack subcommand
277
305
  create_dpack_parser = subparsers.add_parser(
278
306
  "create-dpack",
@@ -897,6 +925,47 @@ def cmd_create_parallel_agent(args: argparse.Namespace) -> int:
897
925
  return 0
898
926
 
899
927
 
928
+ def cmd_view(args: argparse.Namespace) -> int:
929
+ """
930
+ Handle view mode - browser-based session viewer with DAG visualization.
931
+
932
+ Parameters
933
+ ----------
934
+ args : argparse.Namespace
935
+ Parsed command-line arguments.
936
+
937
+ Returns
938
+ -------
939
+ int
940
+ Exit code (0 for success, non-zero for failure).
941
+ """
942
+ work_dir: Path = Path(args.work_dir).resolve()
943
+
944
+ if not work_dir.exists():
945
+ print(f"Error: Work directory not found: {work_dir}", file=sys.stderr)
946
+ return 1
947
+
948
+ logs_dir: Path = work_dir / "_opencode_logs"
949
+ if not logs_dir.exists():
950
+ print(f"Error: No logs directory found: {logs_dir}", file=sys.stderr)
951
+ print("Make sure this is a valid dlab session directory.", file=sys.stderr)
952
+ return 1
953
+
954
+ # Export mode — no server dependencies needed
955
+ if args.export:
956
+ from dlab.viewer.server import export_viewer
957
+ output_path: Path = Path(args.export)
958
+ return export_viewer(work_dir, output_path)
959
+
960
+ from dlab.viewer import run_viewer
961
+
962
+ return run_viewer(
963
+ work_dir,
964
+ port=args.port,
965
+ open_browser=not args.no_open,
966
+ )
967
+
968
+
900
969
  def cmd_timeline(args: argparse.Namespace) -> int:
901
970
  """
902
971
  Handle timeline mode - display execution timeline for a session.
@@ -1066,6 +1135,8 @@ def main() -> None:
1066
1135
  exit_code = cmd_create_dpack(args)
1067
1136
  elif args.command == "timeline":
1068
1137
  exit_code = cmd_timeline(args)
1138
+ elif args.command == "view":
1139
+ exit_code = cmd_view(args)
1069
1140
  elif args.dpack or args.data or args.prompt or args.prompt_file or args.continue_dir:
1070
1141
  exit_code = cmd_run(args)
1071
1142
  else:
@@ -527,7 +527,8 @@ def build_runner_script(
527
527
  return f'''#!/bin/bash
528
528
  set -o pipefail
529
529
  prompt=$(cat {prompt_file})
530
- opencode run --format json --log-level DEBUG --model "{model}" "$prompt" 2>&1 | tee /_opencode_logs/{log_prefix}.log
530
+ echo "$prompt" | python3 -c "import json,sys; print(json.dumps({{'type':'dlab_start','timestamp':int(__import__('time').time()*1000),'model':'{model}','agent':'{log_prefix}','prompt':sys.stdin.read().strip()}}))" > /_opencode_logs/{log_prefix}.log
531
+ opencode run --format json --log-level DEBUG --model "{model}" "$prompt" 2>&1 | tee -a /_opencode_logs/{log_prefix}.log
531
532
  '''
532
533
 
533
534
 
@@ -269,9 +269,11 @@ CRITICAL OUTPUT RULES:
269
269
  `
270
270
  const fullPrompt = subagentContext + args.prompts[i] + "\n\n" + (config.subagent_suffix_prompt || "")
271
271
 
272
- // Spawn - log file starts with JSON directly (no header)
273
272
  const logFile = join(logsDir, `instance-${i + 1}.log`)
274
273
 
274
+ // Write dlab_start event as first line (model, agent, prompt)
275
+ writeFileSync(logFile, JSON.stringify({type: "dlab_start", timestamp: Date.now(), model, agent: args.agent, prompt: fullPrompt}) + "\n")
276
+
275
277
  const proc = Bun.spawn(["opencode", "run", "--format", "json", "--log-level", "DEBUG", "--model", model, fullPrompt], {
276
278
  cwd: instanceDir,
277
279
  stdout: "pipe",
@@ -355,6 +357,9 @@ RULES:
355
357
 
356
358
  const consLogFile = join(logsDir, "consolidator.log")
357
359
 
360
+ // Write dlab_start event as first line
361
+ writeFileSync(consLogFile, JSON.stringify({type: "dlab_start", timestamp: Date.now(), model: consolidatorModel, agent: "consolidator", prompt: consolidatorPrompt}) + "\n")
362
+
358
363
  const consProc = Bun.spawn(["opencode", "run", "--format", "json", "--log-level", "DEBUG", "--model", consolidatorModel, consolidatorPrompt], {
359
364
  cwd: runDir,
360
365
  stdout: "pipe",
@@ -251,7 +251,8 @@ def run_opencode_local(
251
251
  runner_script: str = f'''#!/bin/bash
252
252
  set -o pipefail
253
253
  prompt=$(cat "{prompt_file}")
254
- opencode run --format json --log-level DEBUG --model "{model}" "$prompt" 2>&1 | tee "{log_path}"
254
+ echo "$prompt" | python3 -c "import json,sys; print(json.dumps({{'type':'dlab_start','timestamp':int(__import__('time').time()*1000),'model':'{model}','agent':'{log_prefix}','prompt':sys.stdin.read().strip()}}))" > "{log_path}"
255
+ opencode run --format json --log-level DEBUG --model "{model}" "$prompt" 2>&1 | tee -a "{log_path}"
255
256
  '''
256
257
  runner_file: Path = work_path / ".run_opencode.sh"
257
258
  runner_file.write_text(runner_script)