#!/usr/bin/env python3 # 档案 24 后续:预置默认工作区(编辑器要求"必须选工作区"才能开会话 → 平台直接种 ws) # 用法: python3 patch-seed-workspace.py /opt/dshs import io import os import sys root = sys.argv[1] if len(sys.argv) > 1 else '.' p = os.path.join(root, 'src/supervisor/orchestrator.ts') s = io.open(p, encoding='utf-8').read() def sub(old, new, cnt=1): global s assert old in s, 'anchor not found:\n' + old[:200] s = s.replace(old, new, cnt) # 1) 补 fs 导入 sub( "import { chmodSync, mkdirSync, writeFileSync } from 'node:fs'", "import { chmodSync, chownSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'", ) # 2) 在 spawnInstance 里对 main 预置工作区 sub( """ const map = role === 'main' ? this.mains : this.watchdogs map.set(userId, instance)""", """ const map = role === 'main' ? this.mains : this.watchdogs map.set(userId, instance) // 档案 24 后续:main 启动前预置默认工作区(编辑器要求"必须选择工作区"才能开始会话; // 平台直接把用户 ws 种进 workspace 存储 → 用户开箱即用、且 UI 只显示短标题而非完整路径)。 if (isMain) await this.seedDefaultWorkspace(userId)""", ) # 3) 新增 seedDefaultWorkspace 方法(放在 writePatch 之前) sub( """ /** Materialize the rendered patch so the dsh CLI can `--patch ` it.""", """ /** * 预置默认工作区(幂等,仅在工作区列表为空时写入): * 把 `/ws` 以短标题「我的工作区」写进 `/storages/workspace.json`, * 使新用户/清空后无需手动选择工作区即可开始会话;已有工作区时**不覆盖**用户现状。 * 失败不阻断启动(只记日志)。 */ private async seedDefaultWorkspace(userId: string): Promise { const root = userRoot(this.config.dataRoot, userId) const dir = join(homeRoot(root), 'storages') const file = join(dir, 'workspace.json') try { mkdirSync(dir, { recursive: true }) let existing: { global?: { workspaceIds?: unknown } } | undefined try { existing = JSON.parse(readFileSync(file, 'utf8')) as { global?: { workspaceIds?: unknown } } } catch { existing = undefined } const ids = existing?.global?.workspaceIds if (Array.isArray(ids) && ids.length > 0) return // 已有工作区 → 尊重用户现状 const ws = workspaceRoot(root) const id = randomUUID() const now = new Date().toISOString() const seed = { unit: { name: 'workspace', version: 2 }, global: { initialized: true, workspaceIds: [id], archivedSessionIds: [] }, tables: { workspaces: { [id]: { path: ws, title: '我的工作区', sessionIds: [], createdAt: now, updatedAt: now }, }, }, } writeFileSync(file, JSON.stringify(seed, null, 2) + '\\n') const uid = await this.resolveUid(userId) chownSync(file, uid, uid) // 实例以该 uid 运行,需可读写 chownSync(dir, uid, uid) process.stderr.write(`[seed-workspace] ${userId} → ${ws}\\n`) } catch (err) { process.stderr.write( `[seed-workspace] failed for ${userId}: ${err instanceof Error ? err.message : String(err)}\\n`, ) } } /** Materialize the rendered patch so the dsh CLI can `--patch ` it.""", ) io.open(p, 'w', encoding='utf-8', newline='').write(s) print('patched: fs import + spawnInstance 调用 + seedDefaultWorkspace 方法')