mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-30 04:06:26 +01:00
launch: build copilot in compile; wait for CDP in launch.sh (#318272)
* launch: build copilot in compile; wait for CDP in launch.sh Two related improvements to the agent dev-loop workflow: - npm run compile now builds the Copilot extension in parallel with the rest of the client, mirroring the structure of npm run watch. Previously only the watch path included extensions/copilot, so a one-shot build left the Copilot extension stale. - .agents/skills/launch/scripts/launch.sh now runs preLaunch.ts in the foreground (surfacing any failure synchronously with a log tail), then launches Code OSS with VSCODE_SKIP_PRELAUNCH=1 and blocks until the renderer's CDP endpoint responds (90s timeout) before printing the JSON. Previously the script returned in ~1s while Electron was still mid- bootstrap, which made it racy for agents driving the workbench via @playwright/cli and could let an early-dying child go unnoticed. SKILL.md updated to drop the now-unnecessary attach retry loop and to document the new error-surfacing behavior. (Written by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * launch: clarify launcher's actual external deps in SKILL.md Per code review, `launch.sh` does not invoke `lsof` or `jq` itself those are used only by the example caller snippets. Reword the prerequisites bullet so it reflects what the script actually requires (`rsync`, `curl`, `nohup`, Node) and notes `jq` / `lsof` as optional for the caller-side examples. (Written by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -18,8 +18,8 @@ The clone is **slim**: workspace storage, browser caches, file history, cached V
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- macOS or Linux. The launcher is a bash script and depends on `rsync`, `lsof`, `jq`, `nohup`, and Node on `PATH`.
|
||||
- A VS Code checkout with sources built. Run `npm run watch` in another terminal (or rely on `./scripts/code.sh` to compile the first time).
|
||||
- macOS or Linux. The launcher is a bash script and depends on `rsync`, `curl`, `nohup`, and Node on `PATH`. The example caller snippets below also use `jq` (parse the JSON output) and `lsof` (kill-by-port fallback) — install those if you plan to use them, but the launcher itself does not require them.
|
||||
- A VS Code checkout with sources built. Run `npm run compile` once (one-shot) or `npm run watch` for incremental rebuilds. Both build the full client **and** the Copilot extension. The launcher also runs `node build/lib/preLaunch.ts` before starting Code OSS, which auto-runs `npm run compile` if `out/` is missing and downloads Electron + built-in extensions.
|
||||
- An **authenticated** Code OSS profile to seed from. By default the launcher uses `~/.vscode-oss-dev`, which is the user-data-dir the repo's `launch.json` configs use - if the user has ever signed in to Copilot in a dev build, this should work. Only pass `--source-user-data-dir <path>` (or set `$CODE_OSS_DEV_AUTHED_USER_DATA_DIR`) when you specifically want to seed from a different profile (e.g. your regular `~/Library/Application Support/Code` install).
|
||||
- `@playwright/cli` available (it's a devDependency in the vscode repo - `npm install` then use `npx @playwright/cli`).
|
||||
- For debugger work: `dap-cli` on `PATH`. If debugger support would be useful but the `dap-cli` skill is not present, prompt the user to install it from https://github.com/roblourens/dap-cli.
|
||||
@@ -61,13 +61,13 @@ Excluded (transient, regenerable, or known-not-needed):
|
||||
|
||||
> If the launched window says "language model unavailable" or otherwise looks unauthed, ask the user to sign in.
|
||||
|
||||
The script prints one JSON line on stdout (logs go to stderr):
|
||||
The script runs pre-launch (electron download, compile-if-missing, built-in extensions) **in the foreground**, then starts Code OSS detached and **blocks until the renderer's CDP endpoint is responding** (up to ~90s) before printing the JSON line on stdout. If anything fails — preLaunch errors, code.sh exits early, CDP never opens — the script exits non-zero and dumps the relevant log tail to stderr.
|
||||
|
||||
```json
|
||||
{"pid":12345,"cdpPort":53111,"extHostPort":53112,"mainPort":53113,"agentHostPort":53114,"userDataDir":".../user-data","extensionsDir":".../extensions","sharedDataDir":".../shared-data","runDir":"...","logFile":".../code.log","repo":"...","agents":false}
|
||||
```
|
||||
|
||||
Capture it with `jq`:
|
||||
Capture it with `jq` — no retry loop needed, CDP is already up when the JSON is printed:
|
||||
|
||||
```bash
|
||||
INFO=$("$LAUNCH" | tail -n1)
|
||||
@@ -93,11 +93,8 @@ PID=$(jq -r .pid <<<"$INFO")
|
||||
Use the dynamic `cdpPort` from the launch JSON. The normal loop is: attach, confirm the target, snapshot, interact, then re-snapshot after meaningful UI changes.
|
||||
|
||||
```bash
|
||||
# Wait for Code OSS to start, retry until attached
|
||||
for i in 1 2 3 4 5; do
|
||||
npx @playwright/cli attach --cdp=http://127.0.0.1:$CDP 2>/dev/null && break || sleep 3
|
||||
done
|
||||
|
||||
# launch.sh blocks until CDP is ready, so a single attach is enough.
|
||||
npx @playwright/cli attach --cdp=http://127.0.0.1:$CDP
|
||||
npx @playwright/cli tab-list
|
||||
npx @playwright/cli snapshot
|
||||
```
|
||||
@@ -234,9 +231,7 @@ kill "$PID" 2>/dev/null || true
|
||||
INFO=$("$LAUNCH" | tail -n1)
|
||||
CDP=$(jq -r .cdpPort <<<"$INFO")
|
||||
PID=$(jq -r .pid <<<"$INFO")
|
||||
for i in 1 2 3 4 5; do
|
||||
npx @playwright/cli attach --cdp=http://127.0.0.1:$CDP 2>/dev/null && break || sleep 3
|
||||
done
|
||||
npx @playwright/cli attach --cdp=http://127.0.0.1:$CDP
|
||||
npx @playwright/cli tab-list
|
||||
npx @playwright/cli snapshot
|
||||
```
|
||||
@@ -266,8 +261,8 @@ Code OSS is a full Electron app and easily eats 1-4 GB. Always clean up.
|
||||
|
||||
- **"Sent env to running instance. Terminating..."** - The dynamic `--user-data-dir` should prevent this. If you see it, another Code OSS is using the same profile path; pass `--source-user-data-dir` to a different source or check that the temp copy actually happened (`ls "$(jq -r .userDataDir <<<"$INFO")"`).
|
||||
- **Renderer ESM errors / `import { Menu } from 'electron'`** - `ELECTRON_RUN_AS_NODE` is set in your env. The launcher unsets it for the child, but if you spawn `code.sh` yourself, do the same.
|
||||
- **Built-in extension fails to load (`Cannot find module .../extensions/.../out/extension.js`)** - extensions weren't compiled. Run `npm run watch-extensions` (or `npm run compile-extensions`).
|
||||
- **CDP connect refused** - give it a few seconds; the launcher returns before the renderer is ready. Use the retry loop above.
|
||||
- **Built-in extension fails to load (`Cannot find module .../extensions/.../out/extension.js`)** - extensions weren't compiled. Run `npm run compile` (one-shot, also rebuilds the Copilot extension) or `npm run watch` (incremental).
|
||||
- **`launch.sh` exits non-zero with a log tail** - either pre-launch failed, `code.sh` died before CDP came up, or CDP never opened within 90s. The tail printed to stderr is from `runDir/code.log` - read it to diagnose.
|
||||
- **Snapshot shows the wrong page or no expected controls** - use `tab-list`, switch with `tab-select <index>` if needed, then re-snapshot before interacting.
|
||||
- **CLI typing commands complete but the input stays empty** - focus chat with the platform shortcut, use `press` or clipboard paste rather than `fill` / `type`, then verify the input state before sending.
|
||||
- **Auth missing in the launched window** - confirm the source profile is actually authed (`ls "$SOURCE_UDD"` should contain `User/`, and `ls "$SOURCE_UDD/User/globalStorage"` should show persisted extension state). Some auth lives in the OS keychain - that's per-user, so it follows automatically as long as you're running as the same user.
|
||||
|
||||
@@ -150,8 +150,48 @@ LOG_FILE="$RUN_DIR/code.log"
|
||||
echo "[launch.sh] launching: $CODE_SH ${ARGS[*]}" >&2
|
||||
echo "[launch.sh] logs: $LOG_FILE" >&2
|
||||
|
||||
nohup "$CODE_SH" "${ARGS[@]}" >"$LOG_FILE" 2>&1 &
|
||||
# Run pre-launch (electron download, compile-if-missing, built-in extensions) in the
|
||||
# foreground so any errors surface synchronously. Then skip code.sh's own pre-launch.
|
||||
echo "[launch.sh] running pre-launch (ensures electron + compiled output + built-ins)..." >&2
|
||||
if ! ( cd "$REPO" && node build/lib/preLaunch.ts ) >>"$LOG_FILE" 2>&1; then
|
||||
echo "[launch.sh] pre-launch FAILED. Log tail:" >&2
|
||||
tail -n 80 "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Launch code.sh in the background. Detaching with `nohup ... & disown` is
|
||||
# sufficient: by the time we return below, CDP is up and Electron is fully
|
||||
# forked into its own process tree, so it's robust to its launching shell
|
||||
# going away. (Earlier failures came from returning while Electron was still
|
||||
# mid-bootstrap, not from process-group concerns.)
|
||||
nohup env VSCODE_SKIP_PRELAUNCH=1 "$CODE_SH" "${ARGS[@]}" \
|
||||
</dev/null >>"$LOG_FILE" 2>&1 &
|
||||
PID=$!
|
||||
disown $PID 2>/dev/null || true
|
||||
|
||||
# Block until the renderer's CDP endpoint is responding so the caller can attach
|
||||
# immediately. If code.sh dies or we time out, dump the log so the failure is
|
||||
# visible.
|
||||
echo "[launch.sh] waiting for CDP on port $CDP_PORT (timeout 90s)..." >&2
|
||||
READY=0
|
||||
for i in $(seq 1 90); do
|
||||
if ! kill -0 "$PID" 2>/dev/null; then
|
||||
echo "[launch.sh] code.sh (PID $PID) exited before CDP came up. Log tail:" >&2
|
||||
tail -n 80 "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
if curl -sf -o /dev/null --max-time 1 "http://127.0.0.1:$CDP_PORT/json/version" 2>/dev/null; then
|
||||
READY=1
|
||||
echo "[launch.sh] CDP ready after ${i}s" >&2
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [[ "$READY" != "1" ]]; then
|
||||
echo "[launch.sh] timed out waiting for CDP on port $CDP_PORT. Log tail:" >&2
|
||||
tail -n 80 "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
node -e '
|
||||
console.log(JSON.stringify({
|
||||
|
||||
+3
-1
@@ -19,7 +19,9 @@
|
||||
"check-cyclic-dependencies": "node build/lib/checkCyclicDependencies.ts out",
|
||||
"preinstall": "node build/npm/preinstall.ts",
|
||||
"postinstall": "node build/npm/postinstall.ts",
|
||||
"compile": "npm run gulp compile",
|
||||
"compile": "npm-run-all2 -lp compile-client compile-copilot",
|
||||
"compile-client": "npm run gulp compile",
|
||||
"compile-copilot": "npm --prefix extensions/copilot run compile",
|
||||
"compile-check-ts-native": "tsgo --project ./src/tsconfig.json --noEmit --skipLibCheck",
|
||||
"watch": "npm-run-all2 -lp watch-client-transpile watch-client watch-extensions watch-copilot",
|
||||
"watchd": "deemon npm run watch",
|
||||
|
||||
Reference in New Issue
Block a user