claude-controller 0.1.2 → 0.3.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 +2 -2
- package/bin/autoloop.sh +382 -0
- package/bin/ctl +1189 -0
- package/bin/native-app.py +6 -3
- package/bin/watchdog.sh +357 -0
- package/cognitive/__init__.py +14 -0
- package/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/dispatcher.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/evaluator.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/goal_engine.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/learning.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/orchestrator.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/planner.cpython-314.pyc +0 -0
- package/cognitive/dispatcher.py +192 -0
- package/cognitive/evaluator.py +289 -0
- package/cognitive/goal_engine.py +232 -0
- package/cognitive/learning.py +189 -0
- package/cognitive/orchestrator.py +303 -0
- package/cognitive/planner.py +207 -0
- package/cognitive/prompts/analyst.md +31 -0
- package/cognitive/prompts/coder.md +22 -0
- package/cognitive/prompts/reviewer.md +33 -0
- package/cognitive/prompts/tester.md +21 -0
- package/cognitive/prompts/writer.md +25 -0
- package/config.sh +6 -1
- package/dag/__init__.py +5 -0
- package/dag/__pycache__/__init__.cpython-314.pyc +0 -0
- package/dag/__pycache__/graph.cpython-314.pyc +0 -0
- package/dag/graph.py +222 -0
- package/lib/jobs.sh +12 -1
- package/package.json +11 -5
- package/postinstall.sh +1 -1
- package/service/controller.sh +43 -11
- package/web/audit.py +122 -0
- package/web/checkpoint.py +80 -0
- package/web/config.py +2 -5
- package/web/handler.py +634 -473
- package/web/handler_fs.py +153 -0
- package/web/handler_goals.py +203 -0
- package/web/handler_jobs.py +372 -0
- package/web/handler_memory.py +203 -0
- package/web/handler_sessions.py +132 -0
- package/web/jobs.py +585 -13
- package/web/personas.py +419 -0
- package/web/pipeline.py +981 -0
- package/web/presets.py +506 -0
- package/web/projects.py +246 -0
- package/web/static/api.js +141 -0
- package/web/static/app.js +25 -1937
- package/web/static/attachments.js +144 -0
- package/web/static/base.css +497 -0
- package/web/static/context.js +204 -0
- package/web/static/dirs.js +246 -0
- package/web/static/form.css +763 -0
- package/web/static/goals.css +363 -0
- package/web/static/goals.js +300 -0
- package/web/static/i18n.js +625 -0
- package/web/static/index.html +215 -13
- package/web/static/{styles.css → jobs.css} +746 -1141
- package/web/static/jobs.js +1270 -0
- package/web/static/memoryview.js +117 -0
- package/web/static/personas.js +228 -0
- package/web/static/pipeline.css +338 -0
- package/web/static/pipelines.js +487 -0
- package/web/static/presets.js +244 -0
- package/web/static/send.js +135 -0
- package/web/static/settings-style.css +291 -0
- package/web/static/settings.js +81 -0
- package/web/static/stream.js +534 -0
- package/web/static/utils.js +131 -0
- package/web/webhook.py +210 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session 목록 HTTP 핸들러 Mixin
|
|
3
|
+
|
|
4
|
+
Claude Code 네이티브 세션 + history.log + job meta 파일을 통합하여 세션 목록을 제공한다.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from config import LOGS_DIR, SESSIONS_DIR, CLAUDE_PROJECTS_DIR
|
|
11
|
+
from utils import parse_meta_file, cwd_to_project_dir, scan_claude_sessions
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SessionHandlerMixin:
|
|
15
|
+
|
|
16
|
+
def _handle_sessions(self, filter_cwd=None):
|
|
17
|
+
seen = {}
|
|
18
|
+
|
|
19
|
+
# 0) Claude Code 네이티브 세션 스캔
|
|
20
|
+
if filter_cwd:
|
|
21
|
+
proj_name = cwd_to_project_dir(filter_cwd)
|
|
22
|
+
project_dirs = [CLAUDE_PROJECTS_DIR / proj_name]
|
|
23
|
+
else:
|
|
24
|
+
if CLAUDE_PROJECTS_DIR.exists():
|
|
25
|
+
all_dirs = sorted(
|
|
26
|
+
(d for d in CLAUDE_PROJECTS_DIR.iterdir() if d.is_dir()),
|
|
27
|
+
key=lambda d: d.stat().st_mtime,
|
|
28
|
+
reverse=True,
|
|
29
|
+
)
|
|
30
|
+
project_dirs = all_dirs[:15]
|
|
31
|
+
else:
|
|
32
|
+
project_dirs = []
|
|
33
|
+
|
|
34
|
+
for pd in project_dirs:
|
|
35
|
+
native = scan_claude_sessions(pd, limit=60)
|
|
36
|
+
for sid, info in native.items():
|
|
37
|
+
if sid not in seen:
|
|
38
|
+
seen[sid] = info
|
|
39
|
+
|
|
40
|
+
# 1) Job meta 파일에서 보강
|
|
41
|
+
if LOGS_DIR.exists():
|
|
42
|
+
meta_files = sorted(
|
|
43
|
+
LOGS_DIR.glob("job_*.meta"),
|
|
44
|
+
key=lambda f: int(f.stem.split("_")[1]),
|
|
45
|
+
reverse=True,
|
|
46
|
+
)
|
|
47
|
+
for mf in meta_files:
|
|
48
|
+
meta = parse_meta_file(mf)
|
|
49
|
+
if not meta:
|
|
50
|
+
continue
|
|
51
|
+
sid = meta.get("SESSION_ID", "").strip()
|
|
52
|
+
if not sid:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
status = meta.get("STATUS", "unknown")
|
|
56
|
+
if status == "running" and meta.get("PID"):
|
|
57
|
+
try:
|
|
58
|
+
os.kill(int(meta["PID"]), 0)
|
|
59
|
+
except (ProcessLookupError, ValueError, OSError):
|
|
60
|
+
status = "done"
|
|
61
|
+
|
|
62
|
+
job_id = meta.get("JOB_ID", "")
|
|
63
|
+
cost_usd = None
|
|
64
|
+
if status in ("done", "failed"):
|
|
65
|
+
out_file = LOGS_DIR / f"job_{job_id}.out"
|
|
66
|
+
if out_file.exists():
|
|
67
|
+
try:
|
|
68
|
+
for line in open(out_file, "r"):
|
|
69
|
+
try:
|
|
70
|
+
obj = json.loads(line.strip())
|
|
71
|
+
if obj.get("type") == "result":
|
|
72
|
+
cost_usd = obj.get("total_cost_usd")
|
|
73
|
+
except json.JSONDecodeError:
|
|
74
|
+
continue
|
|
75
|
+
except OSError:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
entry = {
|
|
79
|
+
"session_id": sid,
|
|
80
|
+
"job_id": job_id,
|
|
81
|
+
"prompt": meta.get("PROMPT", ""),
|
|
82
|
+
"timestamp": meta.get("CREATED_AT", ""),
|
|
83
|
+
"status": status,
|
|
84
|
+
"cwd": meta.get("CWD", ""),
|
|
85
|
+
"cost_usd": cost_usd,
|
|
86
|
+
"slug": "",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if sid not in seen:
|
|
90
|
+
seen[sid] = entry
|
|
91
|
+
else:
|
|
92
|
+
existing = seen[sid]
|
|
93
|
+
if existing.get("job_id") is None:
|
|
94
|
+
existing.update({"job_id": job_id, "status": status, "cost_usd": cost_usd})
|
|
95
|
+
else:
|
|
96
|
+
try:
|
|
97
|
+
if int(job_id) > int(existing.get("job_id", 0)):
|
|
98
|
+
seen[sid] = entry
|
|
99
|
+
except (ValueError, TypeError):
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
# 2) history.log 보충
|
|
103
|
+
history_file = SESSIONS_DIR / "history.log"
|
|
104
|
+
if history_file.exists():
|
|
105
|
+
try:
|
|
106
|
+
for line in history_file.read_text("utf-8").strip().split("\n"):
|
|
107
|
+
parts = line.split("|", 2)
|
|
108
|
+
if len(parts) >= 2:
|
|
109
|
+
ts, sid = parts[0].strip(), parts[1].strip()
|
|
110
|
+
if not sid:
|
|
111
|
+
continue
|
|
112
|
+
prompt = parts[2].strip() if len(parts) > 2 else ""
|
|
113
|
+
if sid not in seen:
|
|
114
|
+
seen[sid] = {
|
|
115
|
+
"session_id": sid, "job_id": None,
|
|
116
|
+
"prompt": prompt, "timestamp": ts,
|
|
117
|
+
"status": "done", "cwd": None,
|
|
118
|
+
"cost_usd": None, "slug": "",
|
|
119
|
+
}
|
|
120
|
+
except OSError:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
# cwd 필터 적용
|
|
124
|
+
if filter_cwd:
|
|
125
|
+
norm = os.path.normpath(filter_cwd)
|
|
126
|
+
seen = {
|
|
127
|
+
sid: s for sid, s in seen.items()
|
|
128
|
+
if s.get("cwd") and os.path.normpath(s["cwd"]) == norm
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
sessions = sorted(seen.values(), key=lambda s: s.get("timestamp") or "", reverse=True)
|
|
132
|
+
self._json_response(sessions[:50])
|