dlab-cli 0.1.2__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.
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/PKG-INFO +3 -1
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/__init__.py +1 -1
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/cli.py +80 -5
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/create_dpack.py +10 -2
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/docker.py +2 -1
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/js/parallel-agents.ts +6 -1
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/local.py +2 -1
- dlab_cli-0.2.0/dlab/opencode_logparser.py +625 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/timeline.py +91 -157
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/app.py +7 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/log_watcher.py +18 -23
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/models.py +26 -6
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/widgets/agent_list.py +14 -6
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/widgets/artifacts_pane.py +24 -3
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/widgets/log_view.py +119 -17
- dlab_cli-0.2.0/dlab/viewer/__init__.py +7 -0
- dlab_cli-0.2.0/dlab/viewer/html/__init__.py +0 -0
- dlab_cli-0.2.0/dlab/viewer/html/viewer.html +498 -0
- dlab_cli-0.2.0/dlab/viewer/layout.py +121 -0
- dlab_cli-0.2.0/dlab/viewer/server.py +408 -0
- dlab_cli-0.2.0/dlab/viewer/session_data.py +890 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab_cli.egg-info/PKG-INFO +3 -1
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab_cli.egg-info/SOURCES.txt +10 -1
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab_cli.egg-info/requires.txt +2 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/pyproject.toml +4 -1
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_cli.py +189 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_create_dpack.py +42 -0
- dlab_cli-0.2.0/tests/test_opencode_logparser.py +724 -0
- dlab_cli-0.2.0/tests/test_viewer.py +806 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/LICENSE +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/README.md +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/config.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/create_dpack_wizard.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/create_parallel_agent_wizard.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/data/__init__.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/data/models.json +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/js/__init__.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/model_fallback.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/parallel_tool.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/session.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/__init__.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/widgets/__init__.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/widgets/search_popup.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab/tui/widgets/status_bar.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab_cli.egg-info/dependency_links.txt +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab_cli.egg-info/entry_points.txt +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/dlab_cli.egg-info/top_level.txt +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/setup.cfg +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_config.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_create_dpack_wizard.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_create_parallel_agent_wizard.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_docker.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_generate_dpack_integration.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_integration.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_model_fallback.py +0 -0
- {dlab_cli-0.1.2 → dlab_cli-0.2.0}/tests/test_parallel_tool.py +0 -0
- {dlab_cli-0.1.2 → 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.
|
|
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",
|
|
@@ -401,13 +429,18 @@ def cmd_run(args: argparse.Namespace) -> int:
|
|
|
401
429
|
if continue_mode:
|
|
402
430
|
continue_dir = Path(args.continue_dir).resolve()
|
|
403
431
|
if not continue_dir.exists():
|
|
404
|
-
|
|
432
|
+
console.print(f"Oops, continue directory [bold]{args.continue_dir}[/bold] not found.")
|
|
433
|
+
return 1
|
|
405
434
|
|
|
406
435
|
if args.work_dir:
|
|
407
436
|
# Copy continue-dir to work-dir, then continue from there
|
|
408
437
|
work_path = Path(args.work_dir).resolve()
|
|
409
438
|
if work_path.exists():
|
|
410
|
-
|
|
439
|
+
console.print(
|
|
440
|
+
f"Oops, work directory [bold]{args.work_dir}[/bold] already exists.\n"
|
|
441
|
+
f"You can remove it with: [cyan]rm -rf {args.work_dir}[/cyan]"
|
|
442
|
+
)
|
|
443
|
+
return 1
|
|
411
444
|
shutil.copytree(continue_dir, work_path)
|
|
412
445
|
work_dir = str(work_path)
|
|
413
446
|
print(f"Copied {continue_dir} to {work_dir}")
|
|
@@ -423,10 +456,9 @@ def cmd_run(args: argparse.Namespace) -> int:
|
|
|
423
456
|
# Overwrite .opencode with latest from decision-pack (agent prompts may have changed)
|
|
424
457
|
opencode_dir = Path(work_dir) / ".opencode"
|
|
425
458
|
if opencode_dir.exists():
|
|
426
|
-
|
|
427
|
-
# Local mode: files are user-owned
|
|
459
|
+
try:
|
|
428
460
|
shutil.rmtree(opencode_dir)
|
|
429
|
-
|
|
461
|
+
except PermissionError:
|
|
430
462
|
# Docker mode: files may be root-owned (e.g. node_modules/)
|
|
431
463
|
subprocess.run(
|
|
432
464
|
["sudo", "rm", "-rf", str(opencode_dir)],
|
|
@@ -893,6 +925,47 @@ def cmd_create_parallel_agent(args: argparse.Namespace) -> int:
|
|
|
893
925
|
return 0
|
|
894
926
|
|
|
895
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
|
+
|
|
896
969
|
def cmd_timeline(args: argparse.Namespace) -> int:
|
|
897
970
|
"""
|
|
898
971
|
Handle timeline mode - display execution timeline for a session.
|
|
@@ -1062,6 +1135,8 @@ def main() -> None:
|
|
|
1062
1135
|
exit_code = cmd_create_dpack(args)
|
|
1063
1136
|
elif args.command == "timeline":
|
|
1064
1137
|
exit_code = cmd_timeline(args)
|
|
1138
|
+
elif args.command == "view":
|
|
1139
|
+
exit_code = cmd_view(args)
|
|
1065
1140
|
elif args.dpack or args.data or args.prompt or args.prompt_file or args.continue_dir:
|
|
1066
1141
|
exit_code = cmd_run(args)
|
|
1067
1142
|
else:
|
|
@@ -884,8 +884,16 @@ export default tool({
|
|
|
884
884
|
args: {
|
|
885
885
|
input: tool.schema.string().describe("Input to process"),
|
|
886
886
|
},
|
|
887
|
-
async
|
|
888
|
-
|
|
887
|
+
async execute(args) {
|
|
888
|
+
// Run a CLI command with Bun shell (e.g. Python, bash, etc.)
|
|
889
|
+
const result = await Bun.$`echo "Processing: ${args.input}"`.nothrow()
|
|
890
|
+
const stdout = result.stdout.toString()
|
|
891
|
+
const stderr = result.stderr.toString()
|
|
892
|
+
|
|
893
|
+
if (result.exitCode !== 0) {
|
|
894
|
+
return `ERROR (exit code ${result.exitCode}):\\n${stderr}`
|
|
895
|
+
}
|
|
896
|
+
return stdout.trim()
|
|
889
897
|
},
|
|
890
898
|
})
|
|
891
899
|
"""
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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)
|