Files
platform/src/servers/api/users/provision.ts
T
pastilhasandClaude Opus 4.8 ab261cf52d move the pty sidecar under src/servers/sidecar
it was the only sidecar living outside src/servers/sidecar/ — it sat in
api/terminal/ next to the bridge that talks to it, which is the one place a reader
looking for "the sidecars" would not check. now src/servers/sidecar/pty/index.mjs,
matching every peer, with a note on the pm2 entry about why this one is node and
.mjs (node-pty is a native addon) rather than bun and typescript like the rest.

the templates/ directory went to api/users/, next to provision.ts:seedShellConfigs,
which is now its only consumer — the sidecar's duplicate seeder went with the
sandbox branch in the previous commit. api/terminal/ is left holding exactly one
thing: the websocket bridge.

no behaviour change. the pm2 entry's script path changed, so `pm2 restart
officer-pty` is not enough — pm2 remembers the old path until the entry is deleted
and started again. commands are in SIDECAR_WORK_LOG.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 05:03:03 +00:00

86 lines
3.1 KiB
TypeScript

import { existsSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
import { DATA_PATH, getHomeDir, toShellUsername } from '@@/data-path';
import { generateContainerContext, generateClaudeSettings } from '@@/generate-container-context';
const TEMPLATE_DIR = join(import.meta.dir, 'templates');
const copyTemplate = async (src: string, dest: string) => {
if (existsSync(dest)) return;
const content = await Bun.file(src).text();
await Bun.write(dest, content);
};
export async function provisionUserEnvironment(email: string, username: string): Promise<boolean> {
const shellUsername = toShellUsername(username, email);
const homeDir = getHomeDir(email);
const userRoot = join(DATA_PATH, email);
console.log(`[provision] provisioning environment for ${email}`);
// Ensure data directories exist
mkdirSync(userRoot, { recursive: true });
mkdirSync(homeDir, { recursive: true });
// Seed shell config files
await seedShellConfigs(homeDir);
// Generate and write CLAUDE.md + settings.json
const contextFile = generateContainerContext(email);
const claudeDir = join(homeDir, '.claude');
mkdirSync(claudeDir, { recursive: true });
const contextContent = await Bun.file(contextFile).text();
await Bun.write(join(claudeDir, 'CLAUDE.md'), contextContent);
const settingsFile = generateClaudeSettings(email, shellUsername);
const settingsContent = await Bun.file(settingsFile).text();
await Bun.write(join(claudeDir, 'settings.json'), settingsContent);
console.log(`[provision] provisioning complete for ${email}`);
return true;
}
async function seedShellConfigs(homeDir: string): Promise<void> {
// .zshenv (must be first — prevents system compinit before oh-my-zsh)
await copyTemplate(join(TEMPLATE_DIR, '.zshenv'), join(homeDir, '.zshenv'));
// .zshrc
await copyTemplate(join(TEMPLATE_DIR, '.zshrc'), join(homeDir, '.zshrc'));
// .tmux.conf
await copyTemplate(join(TEMPLATE_DIR, '.tmux.conf'), join(homeDir, '.tmux.conf'));
// starship config
const configDir = join(homeDir, '.config');
mkdirSync(configDir, { recursive: true });
await copyTemplate(join(TEMPLATE_DIR, 'starship-officer.toml'), join(configDir, 'starship-officer.toml'));
// Oh My Zsh — copy from host install
const ohMyZshDest = join(homeDir, '.oh-my-zsh');
if (!existsSync(ohMyZshDest)) {
const hostOhMyZsh = join(process.env.HOME ?? '', '.oh-my-zsh');
if (existsSync(hostOhMyZsh)) {
Bun.spawnSync({ cmd: ['cp', '-r', hostOhMyZsh, ohMyZshDest], stdout: 'ignore', stderr: 'ignore' });
} else {
Bun.spawnSync({
cmd: ['git', 'clone', '--depth=1', 'https://github.com/ohmyzsh/ohmyzsh.git', ohMyZshDest],
stdout: 'ignore',
stderr: 'ignore',
});
}
}
// LazyVim config
const nvimDir = join(homeDir, '.config', 'nvim');
if (!existsSync(nvimDir)) {
const hostNvim = join(process.env.HOME ?? '', '.config', 'nvim');
if (existsSync(hostNvim)) {
Bun.spawnSync({ cmd: ['cp', '-r', hostNvim, nvimDir], stdout: 'ignore', stderr: 'ignore' });
}
}
// Ensure .local/bin exists
mkdirSync(join(homeDir, '.local', 'bin'), { recursive: true });
}