Reimplement compose and add tiling windows
This commit is contained in:
@@ -10,22 +10,12 @@ from core.gateway.commands import (
|
||||
dispatch_gateway_command,
|
||||
)
|
||||
from core.models import (
|
||||
CodexPermissionRequest,
|
||||
CodexRun,
|
||||
DerivedTask,
|
||||
ExternalSyncEvent,
|
||||
Person,
|
||||
TaskProject,
|
||||
User,
|
||||
)
|
||||
from core.tasks.engine import create_task_record_and_sync, mark_task_completed_and_sync
|
||||
|
||||
APPROVAL_PROVIDER_COMMANDS = {
|
||||
".claude": "claude",
|
||||
".codex": "codex_cli",
|
||||
}
|
||||
APPROVAL_EVENT_PREFIX = "codex_approval"
|
||||
ACTION_TO_STATUS = {"approve": "approved", "reject": "denied"}
|
||||
TASK_COMMAND_MATCH_RE = re.compile(r"^\s*(?:\.tasks\b|\.l\b|\.list\b)", re.IGNORECASE)
|
||||
|
||||
|
||||
@@ -35,11 +25,6 @@ def gateway_help_lines() -> list[str]:
|
||||
" .contacts — list contacts",
|
||||
" .whoami — show current user",
|
||||
" .help — show this help",
|
||||
"Approval commands:",
|
||||
" .approval list-pending [all] — list pending approval requests",
|
||||
" .approval approve <key> — approve a request",
|
||||
" .approval reject <key> — reject a request",
|
||||
" .approval status <key> — check request status",
|
||||
"Task commands:",
|
||||
" .l — shortcut for open task list",
|
||||
" .tasks list [status] [limit] — list tasks",
|
||||
@@ -50,138 +35,6 @@ def gateway_help_lines() -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
def _resolve_request_provider(request):
|
||||
event = getattr(request, "external_sync_event", None)
|
||||
if event is None:
|
||||
return ""
|
||||
return str(getattr(event, "provider", "") or "").strip()
|
||||
|
||||
|
||||
async def _apply_approval_decision(request, decision):
|
||||
status = ACTION_TO_STATUS.get(decision, decision)
|
||||
request.status = status
|
||||
await sync_to_async(request.save)(update_fields=["status"])
|
||||
run = None
|
||||
if request.codex_run_id:
|
||||
run = await sync_to_async(CodexRun.objects.get)(pk=request.codex_run_id)
|
||||
run.status = "approved_waiting_resume" if status == "approved" else status
|
||||
await sync_to_async(run.save)(update_fields=["status"])
|
||||
if request.external_sync_event_id:
|
||||
evt = await sync_to_async(ExternalSyncEvent.objects.get)(
|
||||
pk=request.external_sync_event_id
|
||||
)
|
||||
evt.status = "ok"
|
||||
await sync_to_async(evt.save)(update_fields=["status"])
|
||||
user = await sync_to_async(User.objects.get)(pk=request.user_id)
|
||||
task = None
|
||||
if run is not None and run.task_id:
|
||||
task = await sync_to_async(DerivedTask.objects.get)(pk=run.task_id)
|
||||
ikey = f"{APPROVAL_EVENT_PREFIX}:{request.approval_key}:{status}"
|
||||
await sync_to_async(ExternalSyncEvent.objects.get_or_create)(
|
||||
idempotency_key=ikey,
|
||||
defaults={
|
||||
"user": user,
|
||||
"task": task,
|
||||
"provider": "codex_cli",
|
||||
"status": "pending",
|
||||
"payload": {},
|
||||
"error": "",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def _approval_list_pending(user, scope, emit):
|
||||
_ = scope
|
||||
requests = await sync_to_async(list)(
|
||||
CodexPermissionRequest.objects.filter(user=user, status="pending").order_by(
|
||||
"-requested_at"
|
||||
)[:20]
|
||||
)
|
||||
emit(f"pending={len(requests)}")
|
||||
for req in requests:
|
||||
emit(f" {req.approval_key}: {req.summary}")
|
||||
|
||||
|
||||
async def _approval_status(user, approval_key, emit):
|
||||
try:
|
||||
req = await sync_to_async(CodexPermissionRequest.objects.get)(
|
||||
user=user, approval_key=approval_key
|
||||
)
|
||||
emit(f"status={req.status} key={req.approval_key}")
|
||||
except CodexPermissionRequest.DoesNotExist:
|
||||
emit(f"approval_key_not_found:{approval_key}")
|
||||
|
||||
|
||||
async def handle_approval_command(user, body, emit):
|
||||
command = str(body or "").strip()
|
||||
for prefix, expected_provider in APPROVAL_PROVIDER_COMMANDS.items():
|
||||
if command.startswith(prefix + " ") or command == prefix:
|
||||
sub = command[len(prefix) :].strip()
|
||||
parts = sub.split()
|
||||
if len(parts) >= 2 and parts[0] in ("approve", "reject"):
|
||||
action, approval_key = parts[0], parts[1]
|
||||
try:
|
||||
req = await sync_to_async(
|
||||
CodexPermissionRequest.objects.select_related(
|
||||
"external_sync_event"
|
||||
).get
|
||||
)(user=user, approval_key=approval_key)
|
||||
except CodexPermissionRequest.DoesNotExist:
|
||||
emit(f"approval_key_not_found:{approval_key}")
|
||||
return True
|
||||
provider = _resolve_request_provider(req)
|
||||
if not provider.startswith(expected_provider):
|
||||
emit(
|
||||
f"approval_key_not_for_provider:{approval_key} provider={provider}"
|
||||
)
|
||||
return True
|
||||
await _apply_approval_decision(req, action)
|
||||
emit(f"{action}d: {approval_key}")
|
||||
return True
|
||||
emit(f"usage: {prefix} approve|reject <key>")
|
||||
return True
|
||||
|
||||
if not command.startswith(".approval"):
|
||||
return False
|
||||
|
||||
rest = command[len(".approval") :].strip()
|
||||
|
||||
if rest.split() and rest.split()[0] in ("approve", "reject"):
|
||||
parts = rest.split()
|
||||
action = parts[0]
|
||||
approval_key = parts[1] if len(parts) > 1 else ""
|
||||
if not approval_key:
|
||||
emit("usage: .approval approve|reject <key>")
|
||||
return True
|
||||
try:
|
||||
req = await sync_to_async(
|
||||
CodexPermissionRequest.objects.select_related("external_sync_event").get
|
||||
)(user=user, approval_key=approval_key)
|
||||
except CodexPermissionRequest.DoesNotExist:
|
||||
emit(f"approval_key_not_found:{approval_key}")
|
||||
return True
|
||||
await _apply_approval_decision(req, action)
|
||||
emit(f"{action}d: {approval_key}")
|
||||
return True
|
||||
|
||||
if rest.startswith("list-pending"):
|
||||
scope = rest[len("list-pending") :].strip() or "mine"
|
||||
await _approval_list_pending(user, scope, emit)
|
||||
return True
|
||||
|
||||
if rest.startswith("status "):
|
||||
approval_key = rest[len("status ") :].strip()
|
||||
await _approval_status(user, approval_key, emit)
|
||||
return True
|
||||
|
||||
emit(
|
||||
"approval: .approval approve|reject <key> | "
|
||||
".approval list-pending [all] | "
|
||||
".approval status <key>"
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def _parse_task_create(rest: str) -> tuple[str, str]:
|
||||
text = str(rest or "").strip()
|
||||
if not text.lower().startswith("add "):
|
||||
@@ -347,9 +200,6 @@ async def dispatch_builtin_gateway_command(
|
||||
out(str(user.__dict__))
|
||||
return True
|
||||
|
||||
async def _approval_handler(_ctx, out):
|
||||
return await handle_approval_command(user, text, out)
|
||||
|
||||
async def _tasks_handler(_ctx, out):
|
||||
return await handle_tasks_command(
|
||||
user,
|
||||
@@ -379,17 +229,6 @@ async def dispatch_builtin_gateway_command(
|
||||
matcher=lambda value: str(value or "").strip().lower() == ".whoami",
|
||||
handler=_whoami_handler,
|
||||
),
|
||||
GatewayCommandRoute(
|
||||
name="approval",
|
||||
scope_key="gateway.approval",
|
||||
matcher=lambda value: str(value or "").strip().lower().startswith(".approval")
|
||||
or any(
|
||||
str(value or "").strip().lower().startswith(prefix + " ")
|
||||
or str(value or "").strip().lower() == prefix
|
||||
for prefix in APPROVAL_PROVIDER_COMMANDS
|
||||
),
|
||||
handler=_approval_handler,
|
||||
),
|
||||
GatewayCommandRoute(
|
||||
name="tasks",
|
||||
scope_key="gateway.tasks",
|
||||
|
||||
Reference in New Issue
Block a user