Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
0688f25
issue #685 - point 8
murd0cc Aug 9, 2026
04aa5fa
PR requested changes implemented
murd0cc Aug 11, 2026
9411e41
Maintainability & Code Quality
murd0cc Aug 11, 2026
ca2ed95
coverage improvements
murd0cc Aug 11, 2026
d763846
Merge branch 'main' into issue/685
murd0cc Aug 11, 2026
d4023d1
refactor(types): canonicalize shared provider settings identifiers (#…
WebMad Aug 12, 2026
c35f6e8
[Feat] Add GLM 5.3 support to Z AI and OpenCode Go (#1244)
zoomote[bot] Aug 14, 2026
13cc3fe
[Feat] Add Gemini 3.7 Flash support (#1241)
zoomote[bot] Aug 14, 2026
c4daefc
[Feat] Add Qwen3.8 Max to OpenCode Go (#1245)
zoomote[bot] Aug 14, 2026
63634be
[Fix] DeepSeek V4 costs are underestimated after pricing changes (#1237)
zoomote[bot] Aug 14, 2026
f7239c8
fix: update deepseek-v4-pro reasoning efforts and mappings (#1236)
WHMHammer Aug 14, 2026
193c2f2
feat(api): add NanoGPT provider support (#1239)
taltas Aug 15, 2026
732651f
fix(config): use zoo-code-settings.json for settings export/import (#…
Rafael-Silva-Oliveira Aug 15, 2026
e064cf0
chore: prepare v3.78.0 release (#1249)
taltas Aug 15, 2026
d52f659
fix(task): recover stale delegated children after restart (#1210)
edelauna Aug 16, 2026
73602a9
fixed screenshots
murd0cc Aug 16, 2026
52da89d
Merge branch 'issue/685' of https://github.com/murd0cc/Zoo-Code into …
murd0cc Aug 16, 2026
43c865b
workaround - webview visual test throws error when deleting snapshots.
murd0cc Aug 16, 2026
c427ecb
fixed format from jpeg to png
murd0cc Aug 16, 2026
c9ebb87
fixed snapshot tests
murd0cc Aug 16, 2026
1f99585
test(settings): harden OpenAICompatible mocks with partial-actual imp…
murd0cc Aug 16, 2026
8cac2ac
fix(webview): throttle state pushes to prevent gray screen OOM (#1078)
JunyongParkDev Aug 16, 2026
61cad5b
test: wire AutoApprove visual tests to manual follow-up timer snapshots
murd0cc Aug 16, 2026
3901fec
missing snapshot
murd0cc Aug 16, 2026
dfc839e
Updated screenshot assertion
murd0cc Aug 16, 2026
9081dde
refactor(webview): canonicalize provider and model message identifier…
WebMad Aug 16, 2026
5b6a506
coverage
murd0cc Aug 16, 2026
3e8144a
feat(api): support official GLM-5.3 Z.ai release (#1284)
zoomote[bot] Aug 18, 2026
dbad4af
feat(api): introduce RequestConfigBuilder for SDK-agnostic abort sign…
easonLiangWorldedtech Aug 18, 2026
38d5ee0
fix(shell): delegate VS Code profile reads to Terminal methods (issue…
edelauna Aug 19, 2026
959826b
removed manual snapshots
murd0cc Aug 19, 2026
775f565
generated screenshots
murd0cc Aug 19, 2026
eac2f0c
regenerated snapshots with correct playwright image
murd0cc Aug 19, 2026
05f8a3e
[Fix] Mermaid labels are unreadable in light IDE themes (#1285)
zoomote[bot] Aug 19, 2026
b7f78f8
refactor(providers): canonicalize model-router calls (#1165)
WebMad Aug 20, 2026
252c69b
fix: stream reasoning_content in LM Studio provider (#1175)
daewoongoh Aug 20, 2026
afdede5
lint(providers): enforce canonical identifiers (#1297)
WebMad Aug 20, 2026
63aca68
fix(semble): increase archive download limit (#1306)
navedmerchant Aug 20, 2026
21d35c4
fix(task-history): atomic per-task merge and drop shared index file (…
edelauna Aug 20, 2026
871bb98
[Fix] Chat controls fade into light IDE themes (#1298)
zoomote[bot] Aug 20, 2026
39bdfb1
[Fix] Remaining chat controls fade into light IDE themes (#1312)
zoomote[bot] Aug 21, 2026
1ad8f52
fix(telemetry): default telemetry to opt-out with explicit consent UI…
edelauna Aug 21, 2026
db52d7f
feat(models): add Gemini 3.5 Flash Lite and 3.1 Flash Lite to Gemini …
easonLiangWorldedtech Aug 22, 2026
3dcac60
[Chore] Enforce safe async handling in core tools (#1255)
zoomote[bot] Aug 22, 2026
6ad8a6e
fix(zoo-gateway): stop inventing UI cost from default model prices (#…
JamesRobert20 Aug 22, 2026
fec4e13
[Improve] Keep shared controls legible across IDE themes (#1333)
zoomote[bot] Aug 22, 2026
3aacd91
Reverted the loose pixel tolerance
murd0cc Aug 22, 2026
351d9f3
initialized the i18next instance with the real English settings.json
murd0cc Aug 22, 2026
a9e9462
regenerated snapshots
murd0cc Aug 22, 2026
7da96a1
issue #685 - point 8
murd0cc Aug 9, 2026
2aa8138
PR requested changes implemented
murd0cc Aug 11, 2026
7f3a22a
Maintainability & Code Quality
murd0cc Aug 11, 2026
301d8ae
coverage improvements
murd0cc Aug 11, 2026
aeeec15
fixed screenshots
murd0cc Aug 16, 2026
20d1987
workaround - webview visual test throws error when deleting snapshots.
murd0cc Aug 16, 2026
8e01947
fixed format from jpeg to png
murd0cc Aug 16, 2026
dfc2c63
fixed snapshot tests
murd0cc Aug 16, 2026
04698d2
test(settings): harden OpenAICompatible mocks with partial-actual imp…
murd0cc Aug 16, 2026
e815e91
test: wire AutoApprove visual tests to manual follow-up timer snapshots
murd0cc Aug 16, 2026
19a7e3d
Updated screenshot assertion
murd0cc Aug 16, 2026
8f08b2b
coverage
murd0cc Aug 16, 2026
989d451
removed manual snapshots
murd0cc Aug 19, 2026
a5f2a42
generated screenshots
murd0cc Aug 19, 2026
835d680
regenerated snapshots with correct playwright image
murd0cc Aug 19, 2026
e9a3103
Reverted the loose pixel tolerance
murd0cc Aug 22, 2026
8705bfc
initialized the i18next instance with the real English settings.json
murd0cc Aug 22, 2026
907f420
regenerated snapshots
murd0cc Aug 22, 2026
c8c69b3
Merge branch 'issue/685' of https://github.com/murd0cc/Zoo-Code into …
murd0cc Aug 22, 2026
a16d6e4
regen snapshots in docker
murd0cc Aug 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ jobs:
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
run: xvfb-run -a pnpm --filter @roo-code/vscode-e2e test:ci:mock

- name: Run mocked restart-persistence E2E test
# Reuse the runner built by the full mocked suite; this direct invocation avoids
# test:run's dotenv loading and does not repeat the bundle or webview build.
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
run: TEST_FILE=restart-persistence.test USE_MOCK=true xvfb-run -a pnpm --filter @roo-code/vscode-e2e exec node ./out/runTest.js

- name: Explain skipped mocked E2E pass marker
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success' && steps.vscode-fallback.outputs.used == 'true'
run: echo "Skipping mocked E2E pass marker because tests ran against a stale cached VS Code binary (VS Code download endpoints unreachable)."
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/visual-regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ on:
pull_request:
types: [opened, reopened, ready_for_review, synchronize]
paths:
- "apps/vscode-e2e/package.json"
- "apps/vscode-e2e/src/theme-fixtures/**"
- "packages/types/src/api.ts"
- "packages/types/src/vscode-extension-host.ts"
- "src/core/webview/ClineProvider.ts"
- "src/core/webview/webviewMessageHandler.ts"
- "src/extension/api.ts"
- "webview-ui/**"
- "src/shared/**"
- "package.json"
Expand Down Expand Up @@ -55,3 +62,32 @@ jobs:
webview-ui/playwright-report
webview-ui/test-results
if-no-files-found: ignore

theme-fixtures:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
with:
install-args: "--frozen-lockfile"
- name: Install xvfb
run: sudo apt-get install -y xvfb
- name: Get pinned VS Code version
id: vscode-version
run: |
VERSION=$(node -p 'require("./apps/vscode-e2e/package.json").devDependencies["@types/vscode"]')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Cache pinned VS Code
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: apps/vscode-e2e/.vscode-test/
key: vscode-theme-fixtures-${{ runner.os }}-${{ steps.vscode-version.outputs.version }}-v1
- name: Test theme fixture serialization
run: pnpm --filter @roo-code/vscode-e2e themes:test
- name: Check generated VS Code theme fixtures
run: xvfb-run -a pnpm --filter @roo-code/vscode-e2e themes:check
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
# Zoo Code Changelog

## [3.78.0]

### Minor Changes

- Add NanoGPT as a configurable provider with dynamic model discovery, streaming and prompt completions, and routing preferences for speed, price, latency, throughput, tool support, and caching (PR #1239 by @taltas)
- Add the new Gemini 3.7 Flash model to Google Gemini and Vertex AI with a 1M context window, multimodal input, prompt caching, and configurable reasoning (PR #1241 by @app/zoomote)
- Add the new GLM 5.3 model to Z AI coding plans and OpenCode Go with a 1M context window, prompt caching, and extended reasoning controls (PR #1244 by @app/zoomote)
- Add the new Qwen3.8 Max model to OpenCode Go with multimodal input, caching, streamed reasoning, and Anthropic Messages routing (PR #1245 by @app/zoomote)
- Fix Azure OpenAI resource endpoints and improve Azure-specific setup guidance in OpenAI Compatible settings (#1191 by @edelauna, PR #1192 by @app/zoomote)
- Preserve task-history titles when rapidly navigating away before a task's messages finish loading (#1180 by @edelauna, PR #1181 by @edelauna)
- Correct Kimi Code output-token defaults and honor model limits returned by the server (#1215 by @myk1yt, PR #1217 by @myk1yt)
- Update DeepSeek V4 Pro reasoning efforts and normalize medium, high, and extended reasoning mappings (#1235 by @WHMHammer, PR #1236 by @WHMHammer)
- Correct DeepSeek V4 pricing and expand provider coverage for the V4 Pro 0813 checkpoint (PR #1237 by @app/zoomote)
- Rename the default settings import/export file to `zoo-code-settings.json` throughout the extension (#1176 by @Rafael-Silva-Oliveira, PR #1177 by @Rafael-Silva-Oliveira)
- Add Destructive Command Guard support for Intel-based macOS systems (PR #1213 by @app/zoomote)
- Update `undici` to 6.28.0 to address security vulnerabilities (PR #1161 by @app/renovate)
- Update Mermaid to 11.16.1 to address a prototype-pollution vulnerability (PR #1193 by @app/renovate)
- Record tool usage centrally to prevent duplicate telemetry and sanitize raw tool names (PR #1073 by @edelauna)
- Canonicalize shared provider settings identifiers and add registry-alignment coverage (PR #1109 by @WebMad)
- Canonicalize provider identifiers across CLI configuration, environment mappings, and model selection (PR #1110 by @WebMad)
- Complete the webview migration to canonical provider identifiers across provider settings and routing (PR #1141 by @WebMad)
- Use canonical provider identifiers throughout API options and add focused interaction coverage (PR #1146 by @WebMad)
- Canonicalize provider model configuration identifiers and provider-specific settings behavior (PR #1147 by @WebMad)
- Migrate model-selection UI hooks to canonical provider identifiers (PR #1148 by @WebMad)
- Reuse the retired Roo provider identifier registry while preserving migration compatibility (PR #1166 by @WebMad)
- Introduce typed shared test utilities for API options, filesystem mocks, reset operations, VS Code doubles, and webview rendering (PR #1171 by @app/zoomote)
- Reuse shared API option factories across Requesty, OpenRouter, and Vercel AI Gateway provider tests (PR #1178 by @app/zoomote)
- Reuse the shared Responses client mock in X.AI provider tests (PR #1182 by @app/zoomote)
- Reuse shared VS Code context, URI, and reset helpers in custom-mode configuration tests (PR #1190 by @app/zoomote)
- Reuse shared reset helpers across code-index embedder tests (PR #1194 by @app/zoomote)
- Reuse shared config test helpers and remove obsolete lint suppressions (PR #1195 by @app/zoomote)
- Reuse shared webview render helpers across focused chat tests (PR #1196 by @app/zoomote)
- Reuse shared reset helpers across terminal integration tests (PR #1197 by @app/zoomote)
- Reuse shared VS Code and reset helpers in settings import/export tests (PR #1198 by @app/zoomote)
- Reuse shared reset helpers across additional code-index tests (PR #1199 by @app/zoomote)
- Complete another batch of shared reset-helper adoption in code-index tests (PR #1200 by @app/zoomote)
- Finish reset-helper adoption in Semble and terminal test suites (PR #1201 by @app/zoomote)
- Complete shared reset-helper adoption across provider tests (PR #1202 by @app/zoomote)
- Reuse shared webview render helpers across chat and settings tests (PR #1203 by @app/zoomote)
- Complete shared webview render-helper adoption in the remaining settings tests (PR #1204 by @app/zoomote)
- Merge the v3.76.0 release preparation branch into `main` (PR #1173 by @navedmerchant)

## [3.76.0]

### Minor Changes
Expand Down
18 changes: 11 additions & 7 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Zoo Code Privacy Policy

**Last Updated: May 13th, 2026**
**Last Updated: August 15th, 2026**

Zoo Code respects your privacy and is committed to transparency about how we
handle your data. Below is a simple breakdown of where key pieces of data
Expand Down Expand Up @@ -32,14 +32,18 @@ go—and, importantly, where they don't.
- **API Keys & Credentials**: If you enter an API key (e.g., to connect an AI
model), it is stored locally on your device and never sent to us or any third
party, except the provider you have chosen.
- **Telemetry (Usage Data)**: We collect feature usage and error data to help
us improve Zoo Code. This telemetry is powered by PostHog and includes your
VS Code machine ID, feature usage patterns, and exception reports. The VS Code
- **Telemetry (Usage Data)**: We collect feature usage and error data to help us
improve Zoo Code. This telemetry is powered by PostHog and includes your VS
Code machine ID, feature usage patterns, and exception reports. The VS Code
machine ID is a persistent identifier and may be considered personal data in
some jurisdictions; we use it only for product analytics and error grouping.
We retain telemetry only as long as needed for product analytics and debugging.
Telemetry does **not** collect your code or AI prompts, and you can opt out at
any time through the settings.
PostHog event data is retained for 12 months under our current project
configuration. This PostHog-based telemetry does **not** collect your code or AI
prompts. Telemetry is on by default. To turn it off, choose "Disabled" in
the settings or in the notice shown on first use. The notice remains until
you explicitly Accept or Decline. Telemetry also follows VS Code's own
global telemetry setting: if you turn that off, Zoo Code stops sending
telemetry right away, even if the Zoo Code setting says "Enabled."
- **Marketplace Requests**: When you browse or search the Marketplace for Model
Configuration Profiles (MCPs) or Custom Modes, Zoo Code makes a secure API
call to Zoo Code's backend servers to retrieve listing information. These
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ Zoo Code builds on the foundation created by Roo Code and continues to expand it
- **More dependable terminal and editing workflows** — fixes for premature terminal completion, task-state races, context management, diff editing, and provider-specific tool use.
- **More control over your workspace** — rules management, per-mode MCP restrictions, multi-root path controls, model reasoning options, and completion change review actions.

## What's New in v3.76.0
## What's New in v3.78.0

- **Run longer, uninterrupted tasks with Destructive Command Guard (DCG)** — DCG blocks dangerous commands while letting Zoo keep working without you continuously pressing approval buttons, backed by hardened managed-binary downloads and installation.
- **Better provider controls and reliability** — choose OpenAI Codex response speed, use updated DeepSeek configurations, and benefit from stronger isolation between provider-profile changes and running tasks.
- **Critical terminal execution fix** — Zoo now waits for terminal commands to finish before starting the next step, preventing overlapping work and premature model continuation.
- Smarter batching groups related tool approvals while keeping unrelated requests separate.
- Telemetry delivery and model-cache fetching are more resilient under failures and concurrent requests.
- **Three major new models have arrived** — use the brand-new Gemini 3.7 Flash, GLM 5.3, and Qwen3.8 Max models, plus updated DeepSeek V4 reasoning, pricing, and provider coverage.
- **Connect to NanoGPT** — use dynamic model discovery, streaming and prompt completions, and routing preferences for speed, price, latency, throughput, tool support, and caching.
- **More reliable providers and tasks** — fixes improve Azure OpenAI endpoint setup, Kimi Code output limits, task-history title preservation, and Zoo settings import/export.
- Destructive Command Guard now supports Intel-based Macs.
- Security updates address vulnerabilities in `undici` and Mermaid.

<details>
<summary>🌐 Available languages</summary>
Expand Down
1 change: 1 addition & 0 deletions apps/cli/src/lib/utils/__tests__/context-window.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe("getContextWindow", () => {
[providerIdentifiers.vercelAiGateway, "vercelAiGatewayModelId"],
[providerIdentifiers.opencodeGo, "opencodeGoModelId"],
[providerIdentifiers.kenari, "kenariModelId"],
[providerIdentifiers.nanogpt, "nanoGptModelId"],
[providerIdentifiers.zooGateway, "zooGatewayModelId"],
] as const)("uses the provider-specific model field for %s", (provider, modelField) => {
const config = { apiProvider: provider, [modelField]: "selected-model" } as ProviderSettings
Expand Down
2 changes: 2 additions & 0 deletions apps/cli/src/lib/utils/context-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ function getModelIdForProvider(config: ProviderSettings): string | undefined {
return config.opencodeGoModelId
case providerIdentifiers.kenari:
return config.kenariModelId
case providerIdentifiers.nanogpt:
return config.nanoGptModelId
case providerIdentifiers.zooGateway:
return config.zooGatewayModelId
case providerIdentifiers.anthropic:
Expand Down
18 changes: 18 additions & 0 deletions apps/vscode-e2e/fixtures/restart-persistence.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"fixtures": [
{
"match": {
"userMessage": "RESTART_PERSISTENCE_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "attempt_completion",
"arguments": "{\"result\":\"RESTART_PERSISTENCE_MARKER\"}",
"id": "call_restart_persistence_done"
}
]
}
}
]
}
4 changes: 4 additions & 0 deletions apps/vscode-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
"test:ci:mock": "pnpm -w bundle && pnpm --filter @roo-code/vscode-webview build && USE_MOCK=true pnpm test:run",
"test:record": "AIMOCK_RECORD=true pnpm test:ci",
"test:run": "rimraf out && tsc -p tsconfig.json && dotenv -e .env.local -- node ./out/runTest.js",
"themes:build": "pnpm -w bundle && pnpm --filter @roo-code/vscode-webview build && rimraf out && tsc -p tsconfig.json",
"themes:update": "pnpm themes:build && node ./out/theme-fixtures/generate.js update",
"themes:check": "pnpm themes:build && node ./out/theme-fixtures/generate.js check",
"themes:test": "pnpm --filter @roo-code/types build && rimraf out && tsc -p tsconfig.json && node --test ./out/theme-fixtures/fixtures.test.js",
"clean": "rimraf out .turbo"
},
"devDependencies": {
Expand Down
107 changes: 107 additions & 0 deletions apps/vscode-e2e/src/restart/phaseProtocol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as path from "path"
import * as fs from "fs/promises"

import { createWriteStream } from "fs"

export const PHASE_RESULT_VERSION = 1 as const

export type RestartPhase = "create" | "verify"
export type PhaseStatus = "passed" | "failed"

export type PhaseError = {
message: string
stack?: string
}

export type PhaseResult = {
version: typeof PHASE_RESULT_VERSION
phase: RestartPhase
status: PhaseStatus
values?: Record<string, string>
error?: PhaseError
}

const phaseResultNames: Record<RestartPhase, string> = {
create: "01-create.json",
verify: "02-verify.json",
}

export function getPhaseResultPath(resultsDir: string, phase: RestartPhase): string {
const resolvedResultsDir = path.resolve(resultsDir)
const resultPath = path.resolve(resolvedResultsDir, phaseResultNames[phase])
if (path.dirname(resultPath) !== resolvedResultsDir) {
throw new Error(`Phase result path escaped the results directory: ${phase}`)
}
return resultPath
}

export function serializePhaseError(error: unknown): PhaseError {
if (error instanceof Error) {
return {
message: error.message.slice(0, 2_000),
...(error.stack && { stack: error.stack.slice(0, 4_000) }),
}
}

return { message: String(error).slice(0, 2_000) }
}

function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null
}

export function validatePhaseResult(value: unknown): asserts value is PhaseResult {
if (!isRecord(value) || value.version !== PHASE_RESULT_VERSION) {
throw new Error("Invalid phase result version")
}
if (value.phase !== "create" && value.phase !== "verify") {
throw new Error("Invalid phase result phase")
}
if (value.status !== "passed" && value.status !== "failed") {
throw new Error("Invalid phase result status")
}
if (value.values !== undefined) {
if (!isRecord(value.values) || Object.values(value.values).some((entry) => typeof entry !== "string")) {
throw new Error("Phase result values must be string-valued")
}
}
if (value.error !== undefined) {
if (!isRecord(value.error) || typeof value.error.message !== "string") {
throw new Error("Invalid phase result error")
}
if (value.error.stack !== undefined && typeof value.error.stack !== "string") {
throw new Error("Invalid phase result error stack")
}
}
}

export async function writePhaseResult(resultsDir: string, result: PhaseResult): Promise<void> {
validatePhaseResult(result)
const targetPath = getPhaseResultPath(resultsDir, result.phase)
const temporaryPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`
try {
await writeJsonAtomically(temporaryPath, result)
await fs.rename(temporaryPath, targetPath)
} finally {
await fs.rm(temporaryPath, { force: true })
}
}

async function writeJsonAtomically(filePath: string, value: PhaseResult): Promise<void> {
await fs.mkdir(path.dirname(filePath), { recursive: true })
await new Promise<void>((resolve, reject) => {
const stream = createWriteStream(filePath, { encoding: "utf8" })
stream.once("error", reject)
stream.once("finish", resolve)
stream.end(JSON.stringify(value))
})
}

export async function readPhaseResult(resultsDir: string, phase: RestartPhase): Promise<PhaseResult> {
const result = JSON.parse(await fs.readFile(getPhaseResultPath(resultsDir, phase), "utf8")) as unknown
validatePhaseResult(result)
if (result.phase !== phase) {
throw new Error(`Phase result does not match requested phase: ${phase}`)
}
return result
}
58 changes: 58 additions & 0 deletions apps/vscode-e2e/src/restart/scenarioWorkspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as os from "os"
import * as path from "path"
import * as fs from "fs/promises"

const SCENARIO_ROOT_PREFIX = "roo-vscode-e2e-restart-"

export type ScenarioWorkspace = {
root: string
workspace: string
userData: string
extensions: string
results: string
}

function childPath(root: string, name: string): string {
const resolvedRoot = path.resolve(root)
const resolvedChild = path.resolve(resolvedRoot, name)
if (path.dirname(resolvedChild) !== resolvedRoot) {
throw new Error(`Scenario path escaped its root: ${name}`)
}
return resolvedChild
}

export async function createScenarioWorkspace(): Promise<ScenarioWorkspace> {
const root = await fs.mkdtemp(path.join(os.tmpdir(), SCENARIO_ROOT_PREFIX))
const scenarioWorkspace: ScenarioWorkspace = {
root,
workspace: childPath(root, "workspace"),
userData: childPath(root, "user-data"),
extensions: childPath(root, "extensions"),
results: childPath(root, "results"),
}

await Promise.all(
[
scenarioPath(scenarioWorkspace, "workspace"),
scenarioPath(scenarioWorkspace, "userData"),
scenarioPath(scenarioWorkspace, "extensions"),
scenarioPath(scenarioWorkspace, "results"),
].map((directory) => fs.mkdir(directory, { recursive: true })),
)

return scenarioWorkspace
}

function scenarioPath(scenarioWorkspace: ScenarioWorkspace, key: "workspace" | "userData" | "extensions" | "results") {
return scenarioWorkspace[key]
}

export async function removeScenarioWorkspace(scenarioWorkspace: ScenarioWorkspace): Promise<void> {
const root = path.resolve(scenarioWorkspace.root)
const tempRoot = path.resolve(os.tmpdir())
if (path.dirname(root) !== tempRoot || !path.basename(root).startsWith(SCENARIO_ROOT_PREFIX)) {
throw new Error(`Refusing to remove an unowned scenario root: ${scenarioWorkspace.root}`)
}

await fs.rm(root, { recursive: true, force: true })
}
Loading
Loading