88 lines
3.5 KiB
Python
88 lines
3.5 KiB
Python
#!/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 <file>` it.""",
|
|||
|
|
""" /**
|
|||
|
|
* 预置默认工作区(幂等,仅在工作区列表为空时写入):
|
|||
|
|
* 把 `<userRoot>/ws` 以短标题「我的工作区」写进 `<home>/storages/workspace.json`,
|
|||
|
|
* 使新用户/清空后无需手动选择工作区即可开始会话;已有工作区时**不覆盖**用户现状。
|
|||
|
|
* 失败不阻断启动(只记日志)。
|
|||
|
|
*/
|
|||
|
|
private async seedDefaultWorkspace(userId: string): Promise<void> {
|
|||
|
|
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 <file>` it.""",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
io.open(p, 'w', encoding='utf-8', newline='').write(s)
|
|||
|
|
print('patched: fs import + spawnInstance 调用 + seedDefaultWorkspace 方法')
|