mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
Linked worktrees now get a per-worktree userData profile under
orca-dev-wt/<hash>, a deterministic collision-checked renderer port
(15200+) and CDP port (9333+), so parallel `pnpm dev` runs no longer hit
the single-instance lock. The main checkout keeps the shared orca-dev
profile; --shared-profile / --worktree-profile / ORCA_DEV_PROFILE override.
The runner prints `ORCA_DEV_INSTANCE {json}` on stdout and mirrors it to
out/dev-instance.json so agents can attach over CDP. orca-dev.mjs and the
terminal wrappers follow the profile the worktree's dev app used.
Adds profile seeding (pnpm dev:seed-profile, pnpm dev --seed-profile,
pnpm dev:fresh --seed-from): an allowlisted slim copy of projects,
workspaces, settings, SSH targets, and the account session, written as
orca-data.json for Orca's own import; the source is only read.
Storage reclaim removes per-worktree profiles once their worktree is gone
and no app, runtime, or daemon still uses them. dev-fresh-profile.sh is
replaced by a cross-platform dev-fresh-profile.mjs.
96 lines
3.3 KiB
JavaScript
96 lines
3.3 KiB
JavaScript
#!/usr/bin/env node
|
|
// Launch `pnpm dev` on a fresh, isolated userData profile so the app behaves like a first-time
|
|
// install (onboarding paints, no persisted repos or sessions). Other dev profiles stay untouched.
|
|
import { existsSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import path from 'node:path'
|
|
import { spawnProcess } from '@orca/process-host'
|
|
import { getDefaultSeedSource, seedDevProfile } from './dev-profile-seed.mjs'
|
|
|
|
const USAGE = `Usage: pnpm dev:fresh [--keep] [--seed-from[=<profile dir>]] [-- <pnpm dev args>]
|
|
|
|
--keep keep the temporary profile after exit
|
|
--seed-from[=<dir>] start from a slim copy of a profile (projects, workspaces, settings,
|
|
SSH targets, account session); default: orca-dev, else the installed app
|
|
ORCA_FRESH_PROFILE_DIR=<dir> use (and keep) a fixed profile directory`
|
|
|
|
const TEMP_PREFIX = 'orca-fresh-profile-'
|
|
|
|
function parseArgs(argv) {
|
|
const options = { keep: false, seedFrom: null, devArgs: [] }
|
|
for (let index = 0; index < argv.length; index++) {
|
|
const arg = argv[index]
|
|
if (arg === '--') {
|
|
options.devArgs = argv.slice(index + 1)
|
|
break
|
|
}
|
|
if (arg === '--keep') {
|
|
options.keep = true
|
|
} else if (arg === '--seed-from') {
|
|
options.seedFrom = getDefaultSeedSource()
|
|
} else if (arg.startsWith('--seed-from=')) {
|
|
options.seedFrom = path.resolve(arg.slice('--seed-from='.length))
|
|
} else if (arg === '-h' || arg === '--help') {
|
|
options.help = true
|
|
} else {
|
|
throw new Error(`Unknown argument: ${arg}\n${USAGE}`)
|
|
}
|
|
}
|
|
return options
|
|
}
|
|
|
|
const options = parseArgs(process.argv.slice(2))
|
|
if (options.help) {
|
|
console.log(USAGE)
|
|
process.exit(0)
|
|
}
|
|
|
|
const fixedDir = process.env.ORCA_FRESH_PROFILE_DIR
|
|
const profileDir = fixedDir ? path.resolve(fixedDir) : mkdtempSync(path.join(tmpdir(), TEMP_PREFIX))
|
|
mkdirSync(profileDir, { recursive: true })
|
|
|
|
function cleanup() {
|
|
const removable = !options.keep && !fixedDir && path.basename(profileDir).startsWith(TEMP_PREFIX)
|
|
if (removable && existsSync(profileDir)) {
|
|
rmSync(profileDir, { recursive: true, force: true })
|
|
console.error(`[dev-fresh-profile] removed ${profileDir}`)
|
|
} else {
|
|
console.error(`[dev-fresh-profile] kept ${profileDir}`)
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (options.seedFrom) {
|
|
await seedDevProfile({ sourceDir: options.seedFrom, targetDir: profileDir })
|
|
}
|
|
} catch (error) {
|
|
console.error(`[dev-fresh-profile] ${error.message}`)
|
|
cleanup()
|
|
process.exit(1)
|
|
}
|
|
|
|
console.error(`[dev-fresh-profile] using userData=${profileDir}`)
|
|
const child = spawnProcess({
|
|
program: 'pnpm',
|
|
args: ['dev', ...options.devArgs],
|
|
env: {
|
|
...process.env,
|
|
ORCA_DEV_USER_DATA_PATH: profileDir,
|
|
// Why: seeded profiles already finished onboarding; only empty ones should show first-run education.
|
|
...(options.seedFrom ? {} : { ORCA_DEV_SHOW_FIRST_RUN_EDUCATION: '1' })
|
|
},
|
|
stdio: 'inherit'
|
|
})
|
|
// Why: Ctrl+C reaches pnpm through the shared terminal; wait for it so cleanup sees a closed profile.
|
|
process.on('SIGINT', () => {})
|
|
process.on('SIGTERM', () => child.kill('SIGTERM'))
|
|
child.on('exit', (code, signal) => {
|
|
cleanup()
|
|
process.exit(code ?? (signal ? 1 : 0))
|
|
})
|
|
child.on('error', (error) => {
|
|
console.error(`[dev-fresh-profile] ${error.message}`)
|
|
cleanup()
|
|
process.exit(1)
|
|
})
|