summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-20 15:43:07 +0900
committerAdam Malczewski <[email protected]>2026-05-20 15:43:07 +0900
commit971a8f775d5e89e484e56a575e8ae2706fe2b53f (patch)
treed11f6cbbf230d73e53dee0db09e535dea9717599
parentadc8bd185b54935e7a31aae04da3175b7989927a (diff)
downloaddispatch-971a8f775d5e89e484e56a575e8ae2706fe2b53f.tar.gz
dispatch-971a8f775d5e89e484e56a575e8ae2706fe2b53f.zip
fix: sidebar layout, copilot auth script, config setup
- Sidebar uses absolute inset-0 with overflow-y-auto for proper scrolling - Border moved to inner div so it hides with sidebar - Copilot auth script uses portable sed instead of grep -P (macOS compat) - dispatch.toml with 3 keys, 4 models, fallback order - .env.dispatch with key placeholders and script reference - docker-compose loads .env.dispatch via env_file
-rw-r--r--.gitignore1
-rwxr-xr-xbin/copilot-auth84
-rw-r--r--dispatch.toml86
-rw-r--r--docker-compose.yml4
-rw-r--r--packages/frontend/src/App.svelte4
5 files changed, 175 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 405d693..3d7cb2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@ node_modules/
dist/
build/
.env
+.env.dispatch
*.db
*.sqlite
.DS_Store
diff --git a/bin/copilot-auth b/bin/copilot-auth
new file mode 100755
index 0000000..28d08a1
--- /dev/null
+++ b/bin/copilot-auth
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+CLIENT_ID="Ov23li8tweQw6odWQebz"
+ENV_FILE="$(cd "$(dirname "$0")/.." && pwd)/.env.dispatch"
+
+# Parse form-encoded key=value pairs
+parse() {
+ local key="$1" data="$2"
+ local val
+ val=$(echo "$data" | sed -n "s/.*${key}=\([^&]*\).*/\1/p")
+ python3 -c "import sys,urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))" <<< "$val"
+}
+
+echo "=== GitHub Copilot OAuth ==="
+echo ""
+
+# Step 1: Request device code
+echo "Requesting device code..."
+RESP=$(curl -s https://github.com/login/device/code \
+ -d "client_id=$CLIENT_ID" \
+ -d "scope=read:user")
+
+DEVICE_CODE=$(parse "device_code" "$RESP")
+
+if [ -z "$DEVICE_CODE" ]; then
+ echo "Failed to get device code: $RESP"
+ exit 1
+fi
+
+USER_CODE=$(parse "user_code" "$RESP")
+VERIF_URI=$(parse "verification_uri" "$RESP")
+INTERVAL=$(parse "interval" "$RESP")
+INTERVAL=${INTERVAL:-5}
+
+echo ""
+echo "Open this URL and enter the code:"
+echo " $VERIF_URI"
+echo " Code: $USER_CODE"
+echo ""
+echo "Waiting for you to authorize..."
+
+# Step 2: Poll for access token
+TOKEN=""
+ATTEMPTS=0
+while [ -z "$TOKEN" ] && [ $ATTEMPTS -lt 60 ]; do
+ sleep "$INTERVAL"
+ ATTEMPTS=$((ATTEMPTS + 1))
+
+ TOKEN_RESP=$(curl -s https://github.com/login/oauth/access_token \
+ -d "client_id=$CLIENT_ID" \
+ -d "device_code=$DEVICE_CODE" \
+ -d "grant_type=urn:ietf:params:oauth:grant-type:device_code")
+
+ TOKEN=$(parse "access_token" "$TOKEN_RESP" || true)
+ ERROR=$(parse "error" "$TOKEN_RESP" || true)
+
+ if [ "$ERROR" = "authorization_pending" ]; then
+ continue
+ elif [ "$ERROR" = "slow_down" ]; then
+ INTERVAL=$((INTERVAL + 5))
+ continue
+ elif [ -n "$ERROR" ]; then
+ echo "Error: $ERROR"
+ exit 1
+ fi
+done
+
+if [ -z "$TOKEN" ]; then
+ echo "Timed out waiting for authorization."
+ exit 1
+fi
+
+# Step 3: Write to .env.dispatch
+mkdir -p "$(dirname "$ENV_FILE")"
+if [ -f "$ENV_FILE" ] && grep -q "^COPILOT_TOKEN=" "$ENV_FILE" 2>/dev/null; then
+ sed -i "s/^COPILOT_TOKEN=.*/COPILOT_TOKEN=$TOKEN/" "$ENV_FILE"
+else
+ echo "COPILOT_TOKEN=$TOKEN" >> "$ENV_FILE"
+fi
+
+echo ""
+echo "Token saved to .env.dispatch"
+echo "Ready — run: docker compose up"
diff --git a/dispatch.toml b/dispatch.toml
new file mode 100644
index 0000000..1559578
--- /dev/null
+++ b/dispatch.toml
@@ -0,0 +1,86 @@
+# Dispatch — Model & Key Configuration
+# Keys reference env var names in .env.dispatch
+
+# ─── Fallback Order (highest priority first) ────────────────────
+# Exhaust opencode-1 first, then opencode-2, then copilot.
+# When all keys are exhausted the agent enters wait-for-refresh.
+# Must be declared BEFORE any [[keys]] / [[models]] blocks.
+
+fallback = ["opencode-1", "opencode-2", "copilot"]
+
+# ─── API Keys ───────────────────────────────────────────────────
+
+[[keys]]
+id = "opencode-1"
+provider = "opencode-go"
+env = "OPENCODE_KEY_1"
+base_url = "https://opencode.ai/zen/go/v1"
+
+[[keys]]
+id = "opencode-2"
+provider = "opencode-go"
+env = "OPENCODE_KEY_2"
+base_url = "https://opencode.ai/zen/go/v1"
+
+[[keys]]
+id = "copilot"
+provider = "github-copilot"
+env = "COPILOT_TOKEN"
+base_url = "https://api.githubcopilot.com"
+
+# ─── Models ─────────────────────────────────────────────────────
+
+[[models]]
+id = "deepseek-v4-pro"
+provider = "opencode-go"
+tags = ["heavy", "coding"]
+
+[[models]]
+id = "deepseek-v4-flash"
+provider = "opencode-go"
+tags = ["light", "quick"]
+
+[[models]]
+id = "gpt-4o"
+provider = "github-copilot"
+tags = ["heavy", "coding"]
+
+[[models]]
+id = "claude-3.5-sonnet"
+provider = "github-copilot"
+tags = ["heavy", "coding", "review"]
+
+# ─── Agent Templates ─────────────────────────────────────────────
+
+[agents.default]
+name = "Dispatch"
+description = "Default coding assistant"
+system_prompt = "You are a helpful AI coding assistant."
+tools = ["read_file", "write_file", "list_files", "run_shell", "task_list"]
+model_tag = "heavy"
+
+# ─── Permissions ─────────────────────────────────────────────────
+
+[permissions]
+read = "allow"
+
+[permissions.edit]
+"*" = "ask"
+"src/**" = "allow"
+
+[permissions.bash]
+"npm test" = "allow"
+"npm run *" = "allow"
+"git status" = "allow"
+"git diff" = "allow"
+"git log *" = "allow"
+"git branch *" = "allow"
+"git add *" = "allow"
+"git commit *" = "allow"
+"git push *" = "allow"
+"ls *" = "allow"
+"*" = "ask"
+
+[permissions.external_directory]
+"~/*" = "ask"
+"/tmp/*" = "allow"
diff --git a/docker-compose.yml b/docker-compose.yml
index e52d3f2..f669705 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,9 +8,9 @@ services:
- "3000:3000"
volumes:
- .:/app
+ env_file:
+ - .env.dispatch
environment:
- OPENCODE_API_KEY: ${OPENCODE_API_KEY:-}
- DISPATCH_MODEL: ${DISPATCH_MODEL:-deepseek-v4-flash}
DISPATCH_WORKING_DIR: /app
frontend:
diff --git a/packages/frontend/src/App.svelte b/packages/frontend/src/App.svelte
index 02a80ba..43fcc5d 100644
--- a/packages/frontend/src/App.svelte
+++ b/packages/frontend/src/App.svelte
@@ -92,12 +92,12 @@ onMount(() => {
<!-- Right sidebar — slides in/out while chat smoothly resizes -->
<div
- class="shrink-0 overflow-hidden border-l border-base-300 transition-[width] duration-300 ease-out"
+ class="shrink-0 overflow-x-hidden transition-[width] duration-300 ease-out relative"
class:w-80={sidebarOpen}
class:w-0={!sidebarOpen}
>
<div
- class="w-80 overflow-y-auto bg-base-100 px-2 py-2 flex flex-col gap-2 h-full transition-transform duration-300 ease-out"
+ class="w-80 absolute inset-0 overflow-y-auto bg-base-100 border-l border-base-300 px-2 py-2 flex flex-col gap-2 transition-transform duration-300 ease-out"
style="transform: translateX({sidebarOpen ? '0' : '100%'})"
>
<div class="collapse collapse-arrow bg-base-200">