Fix end-to-end startup: project registration, credentials, trust dialog, ready marker
- start.sh: auto-register project in ~/.config/context-studio/projects/ before launching Electron — without this acquireProjectLock() silently skips writing the lock file, waitForServers() never finds the registry port, all agent ports stay null (localhost:null errors) - start.sh: mount all known Claude Code credential locations into container (~/.claude/.credentials.json, ~/.claude.json, $CLAUDE_CONFIG_DIR variants) not just ~/.anthropic which was empty on this system - bin/claude: create /tmp/cs-ready-<agentId> on host after 3s delay so CS Core's CLI ready marker poll resolves instead of timing out after 10s - workflow.sh: add hasTrustDialogAccepted:true to all agent settings.json so claude goes straight to priming without the folder trust dialog - prereqs.sh: add ensure_api_key() — checks all credential locations, prompts with masked input if none found, offers to save to shell profile - wizard.sh: trap SIGINT for graceful abort — gum confirm popup, reverts created project dir and cloned core dir, leaves installed packages untouched - core.sh: set _WIZARD_CORE_CLONED=true before clone for cleanup tracking - electron-config.js: increase serverStartupTimeout 30s→90s (config file in core/config/, not source — safe to edit) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ab7b777ced
commit
7c9b61bfce
7 changed files with 325 additions and 80 deletions
|
|
@ -41,6 +41,21 @@ else
|
|||
WORKDIR="\$PROJECT_DIR"
|
||||
fi
|
||||
|
||||
# ── CS Core ready marker ────────────────────────────────────────────────
|
||||
# CS Core polls /tmp/cs-ready-<agentId> on the host to know when the CLI
|
||||
# banner is visible and /prime can be injected. Claude runs inside the
|
||||
# container so it cannot create this file on the host itself.
|
||||
# We infer the agent ID from the PTY working directory (CS Core sets it to
|
||||
# workflow/agents/<agentId>) and create the marker after a short delay.
|
||||
_is_interactive=true
|
||||
for _arg in "\$@"; do
|
||||
case "\$_arg" in --version|--help|-h|-v) _is_interactive=false; break ;; esac
|
||||
done
|
||||
if [[ "\$_is_interactive" == "true" && "\$PWD" == "\$PROJECT_DIR/workflow/agents/"* ]]; then
|
||||
_AGENT_ID="\$(basename "\$PWD")"
|
||||
(sleep 3 && touch "/tmp/cs-ready-\$_AGENT_ID") &
|
||||
fi
|
||||
|
||||
# Pass through TTY if available, relay working directory into container
|
||||
if [ -t 0 ]; then
|
||||
exec "\$RUNTIME" exec -it --workdir "\$WORKDIR" "\$CONTAINER_NAME" claude "\$@"
|
||||
|
|
@ -82,6 +97,22 @@ fi
|
|||
# ── Ensure ~/.anthropic exists (Claude Code stores auth/config here) ─────
|
||||
mkdir -p "\$HOME/.anthropic"
|
||||
|
||||
# ── Build credential mounts ───────────────────────────────────────────────
|
||||
# Claude Code may store credentials in various locations depending on version
|
||||
# and whether CLAUDE_CONFIG_DIR is set. Mount whichever files exist.
|
||||
_CREDS_ARGS=()
|
||||
_CREDS_ARGS+=("-v" "\$HOME/.anthropic:\$HOME/.anthropic:ro")
|
||||
_claude_dir="\${CLAUDE_CONFIG_DIR:-\$HOME/.claude}"
|
||||
if [[ -f "\$_claude_dir/.credentials.json" ]]; then
|
||||
_CREDS_ARGS+=("-v" "\$_claude_dir/.credentials.json:\$_claude_dir/.credentials.json:ro")
|
||||
fi
|
||||
if [[ -f "\$HOME/.claude.json" ]]; then
|
||||
_CREDS_ARGS+=("-v" "\$HOME/.claude.json:\$HOME/.claude.json:ro")
|
||||
fi
|
||||
if [[ -n "\${CLAUDE_CONFIG_DIR:-}" && -f "\$CLAUDE_CONFIG_DIR/.claude.json" ]]; then
|
||||
_CREDS_ARGS+=("-v" "\$CLAUDE_CONFIG_DIR/.claude.json:\$CLAUDE_CONFIG_DIR/.claude.json:ro")
|
||||
fi
|
||||
|
||||
# ── Start agents container ───────────────────────────────────────────────
|
||||
# Mount project at the same absolute path so host and container paths match.
|
||||
# CS Core sets agent working dirs to host paths; the wrapper relays PWD.
|
||||
|
|
@ -92,7 +123,7 @@ echo "→ Starting agents container '\$CONTAINER_NAME'..."
|
|||
--name "\$CONTAINER_NAME" \\
|
||||
--user "\$(id -u):\$(id -g)" \\
|
||||
-v "\$PROJECT_DIR:\$PROJECT_DIR" \\
|
||||
-v "\$HOME/.anthropic:\$HOME/.anthropic:ro" \\
|
||||
"\${_CREDS_ARGS[@]}" \\
|
||||
-e ANTHROPIC_API_KEY="\${ANTHROPIC_API_KEY:-}" \\
|
||||
-e CS_WORKFLOW_DIR="\$PROJECT_DIR/workflow" \\
|
||||
-e PROJECT_ROOT_DIR="\$PROJECT_DIR" \\
|
||||
|
|
@ -121,6 +152,45 @@ if [[ ! -d "\$CS_CORE/app/node_modules" ]]; then
|
|||
(cd "\$CS_CORE" && npm install) || { echo "npm install failed." >&2; exit 1; }
|
||||
fi
|
||||
|
||||
# ── Register project with Context Studio (required for lock file to be written) ──
|
||||
# CS Core's acquireProjectLock() skips writing the lock file if the project
|
||||
# isn't registered in ~/.config/context-studio/projects/<uuid>.json.
|
||||
# Without the lock file, waitForServers() can never find the registry port
|
||||
# and always times out — causing localhost:null errors in the UI.
|
||||
_CS_PROJECTS_DIR="\$HOME/.config/context-studio/projects"
|
||||
mkdir -p "\$_CS_PROJECTS_DIR"
|
||||
_WORKFLOW_DIR="\$PROJECT_DIR/workflow"
|
||||
_already_registered=false
|
||||
for _f in "\$_CS_PROJECTS_DIR"/*.json; do
|
||||
if [[ -f "\$_f" ]] && python3 -c "
|
||||
import json,sys
|
||||
d=json.load(open(sys.argv[1]))
|
||||
sys.exit(0 if d.get('workflowDir') == sys.argv[2] else 1)
|
||||
" "\$_f" "\$_WORKFLOW_DIR" 2>/dev/null; then
|
||||
_already_registered=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "\$_already_registered" == "false" ]]; then
|
||||
_UUID=\$(python3 -c "import uuid; print(uuid.uuid4())")
|
||||
_NOW=\$(python3 -c "from datetime import datetime,timezone; print(datetime.now(timezone.utc).isoformat())")
|
||||
python3 -c "
|
||||
import json, sys
|
||||
data = {
|
||||
'id': sys.argv[1],
|
||||
'name': sys.argv[2],
|
||||
'workflowDir': sys.argv[3],
|
||||
'user': 'default',
|
||||
'created': sys.argv[4],
|
||||
'lastOpened': sys.argv[4]
|
||||
}
|
||||
with open(sys.argv[5], 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
f.write('\n')
|
||||
" "\$_UUID" "\$(basename "\$PROJECT_DIR")" "\$_WORKFLOW_DIR" "\$_NOW" "\$_CS_PROJECTS_DIR/\$_UUID.json"
|
||||
echo "→ Registered project with Context Studio"
|
||||
fi
|
||||
|
||||
# ── Check display for Electron UI ───────────────────────────────────────
|
||||
if [[ -z "\${DISPLAY:-}" && -z "\${WAYLAND_DISPLAY:-}" ]]; then
|
||||
echo "⚠ No display detected (DISPLAY / WAYLAND_DISPLAY not set)."
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ setup_core() {
|
|||
|
||||
info "Cloning context-studio-core → $CS_CORE_DIR"
|
||||
mkdir -p "$CS_HOME"
|
||||
_WIZARD_CORE_CLONED=true
|
||||
spin "Cloning context-studio-core..." \
|
||||
git clone "$CS_CORE_REPO" "$CS_CORE_DIR" \
|
||||
|| die "Failed to clone context-studio-core. Check your SSH key and network."
|
||||
|
|
|
|||
|
|
@ -98,8 +98,73 @@ _install_docker() {
|
|||
warn "Added $USER to docker group — log out and back in for it to take effect."
|
||||
}
|
||||
|
||||
ensure_api_key() {
|
||||
if [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
|
||||
success "ANTHROPIC_API_KEY is set"
|
||||
return
|
||||
fi
|
||||
|
||||
# Resolve config dir — CLAUDE_CONFIG_DIR overrides default ~/.claude
|
||||
local claude_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
||||
|
||||
local creds_file=""
|
||||
if [[ -f "$claude_dir/.credentials.json" ]]; then creds_file="$claude_dir/.credentials.json"
|
||||
elif [[ -f "$HOME/.claude.json" ]]; then creds_file="$HOME/.claude.json"
|
||||
elif [[ -f "$claude_dir/.claude.json" ]]; then creds_file="$claude_dir/.claude.json"
|
||||
elif [[ -f "$HOME/.anthropic/.credentials.json" ]]; then creds_file="$HOME/.anthropic/.credentials.json"
|
||||
fi
|
||||
|
||||
if [[ -n "$creds_file" ]]; then
|
||||
success "Anthropic credentials found ($creds_file)"
|
||||
return
|
||||
fi
|
||||
|
||||
warn "ANTHROPIC_API_KEY is not set."
|
||||
echo ""
|
||||
gum style --foreground "$C_SKY" --margin "0 4" \
|
||||
"Claude Code needs an Anthropic API key to run inside the container." \
|
||||
"Get one at: https://console.anthropic.com/settings/api-keys"
|
||||
echo ""
|
||||
|
||||
local key
|
||||
key=$(gum input \
|
||||
--password \
|
||||
--placeholder "sk-ant-..." \
|
||||
--prompt " › " \
|
||||
--prompt.foreground "$C_MAUVE" \
|
||||
--cursor.foreground "$C_MAUVE" \
|
||||
--header " Anthropic API key" \
|
||||
--header.foreground "$C_SKY" \
|
||||
--width 70) || true
|
||||
|
||||
if [[ -z "$key" ]]; then
|
||||
warn "No API key entered — set ANTHROPIC_API_KEY before running ./start.sh"
|
||||
return
|
||||
fi
|
||||
|
||||
export ANTHROPIC_API_KEY="$key"
|
||||
success "ANTHROPIC_API_KEY set for this session"
|
||||
|
||||
ask_yn _save "Save to shell profile (~/.zshrc / ~/.bashrc)?" "y"
|
||||
if [[ "$_save" == "y" ]]; then
|
||||
local profile
|
||||
if [[ "$SHELL" == */zsh ]]; then
|
||||
profile="$HOME/.zshrc"
|
||||
else
|
||||
profile="$HOME/.bashrc"
|
||||
fi
|
||||
if grep -q "ANTHROPIC_API_KEY" "$profile" 2>/dev/null; then
|
||||
warn "ANTHROPIC_API_KEY already in $profile — not adding again."
|
||||
else
|
||||
printf '\nexport ANTHROPIC_API_KEY="%s"\n' "$key" >> "$profile"
|
||||
success "Saved to $profile"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_prerequisites() {
|
||||
header "Prerequisites"
|
||||
ensure_git
|
||||
ensure_container_runtime
|
||||
ensure_api_key
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ _create_agent_dir() {
|
|||
{
|
||||
"model": "sonnet",
|
||||
"spinnerTipsEnabled": false,
|
||||
"hasTrustDialogAccepted": true,
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(*)",
|
||||
|
|
@ -162,5 +163,6 @@ clone_workflow() {
|
|||
spin "Cloning workflow..." \
|
||||
git clone "$repo_url" "$workflow_dir" \
|
||||
|| die "Failed to clone workflow repo: $repo_url"
|
||||
|
||||
success "Workflow cloned"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue