回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复: - 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录) - tmp/(32.4 M,按接续棒命名的过程临时区) - .workbuddy/tmp/(39.5 M) - 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物) - tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留 入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与 接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、 .workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。 排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、 打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
94 lines
4.0 KiB
JavaScript
94 lines
4.0 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* ensure-workspace-picker-patch.cjs —— 把「工作区目录选择器收敛」平台段幂等写入各用户 profile patch。
|
||
*
|
||
* 背景(档案 18 v3,2026-09-11 实测):
|
||
* · 官方 dsh-web-app 的 `directory-picker` 行(@…-auto) 在启动时**连带挂载客户端对话框**
|
||
* (@deepseek-ai/dsh-client-ui-directory-picker-browse);把它 disabled 会让对话框消失(点不动)。
|
||
* · 该 client 包自带 dsh.client 元数据,可**单独作为一行**插入 → 保留官方对话框 UI。
|
||
* · host 面(seam) 改由自建 @dsh-local/workspace-scoped-picker 提供:根固定为 <userRoot>/ws,
|
||
* 越界(/etc、..、他人目录、符号链接逃逸)一律拒绝 → 路径框里手输也出不去。
|
||
* · 另由该插件的 client 面注入 CSS,隐藏「改路径」入口(crumbEditZone/Glyph)。
|
||
*
|
||
* 用法:
|
||
* node ensure-workspace-picker-patch.cjs # 全部用户(幂等)
|
||
* node ensure-workspace-picker-patch.cjs --dry-run # 只打印计划
|
||
* node ensure-workspace-picker-patch.cjs --restart # 写后 kill 实例(由崩溃自愈拉起,读新 patch)
|
||
* node ensure-workspace-picker-patch.cjs admin guest # 指定用户
|
||
*
|
||
* 幂等:以 BEGIN/END 标记包裹平台段;已存在即跳过(不改内容)。
|
||
* 注意:本脚本**只追加平台段**,不触碰既有内容(角色 patch 等原样保留)。
|
||
*/
|
||
const { execFileSync } = require('node:child_process')
|
||
const { existsSync, readFileSync, writeFileSync } = require('node:fs')
|
||
const { join } = require('node:path')
|
||
const Database = require('/opt/dshs/node_modules/better-sqlite3')
|
||
|
||
const DB_PATH = '/var/lib/dshs/dshs.db'
|
||
const PROFILE = 'web'
|
||
const BEGIN = '# >>> platform: workspace-scoped-picker (managed by ensure-workspace-picker-patch.cjs)'
|
||
const END = '# <<< platform: workspace-scoped-picker'
|
||
const DRY = process.argv.includes('--dry-run')
|
||
const RESTART = process.argv.includes('--restart')
|
||
const only = process.argv.slice(2).filter((a) => !a.startsWith('--'))
|
||
|
||
const BLOCK = [
|
||
BEGIN,
|
||
'# 目录选择器收敛:官方对话框 UI 保留(单独插入 client 面),host 面换成受限实现(根=自有 ws)',
|
||
'- insert:',
|
||
' - id: workspace-scoped-picker',
|
||
' name: "@dsh-local/workspace-scoped-picker"',
|
||
' - id: ui-directory-picker-browse',
|
||
' name: "@deepseek-ai/dsh-client-ui-directory-picker-browse"',
|
||
'- id: directory-picker',
|
||
' name: "@deepseek-ai/dsh-host-directory-picker-auto"',
|
||
' disabled: true',
|
||
END,
|
||
'',
|
||
].join('\n')
|
||
|
||
const db = new Database(DB_PATH, { readonly: true })
|
||
const users = db
|
||
.prepare('SELECT username, uid, home_dir FROM users')
|
||
.all()
|
||
.filter((u) => only.length === 0 || only.includes(u.username))
|
||
|
||
for (const user of users) {
|
||
const patchPath = join(user.home_dir, 'profiles', PROFILE, 'cordis.patch.yml')
|
||
if (!existsSync(patchPath)) {
|
||
console.log(` ${user.username}: NO_PROFILE(用户未首登 spawn,先登录一次)`)
|
||
continue
|
||
}
|
||
const current = readFileSync(patchPath, 'utf8')
|
||
if (current.includes(BEGIN)) {
|
||
console.log(` ${user.username}: skip(平台段已存在)`)
|
||
continue
|
||
}
|
||
const body = current.trim() === '' || current.trim() === '[]' ? '' : current.trimEnd() + '\n\n'
|
||
const next = body + BLOCK
|
||
if (DRY) {
|
||
console.log(` ${user.username}: [dry-run] 将写入 ${patchPath}`)
|
||
continue
|
||
}
|
||
writeFileSync(patchPath, next, 'utf8')
|
||
try {
|
||
execFileSync('chown', [`${user.uid}:${user.uid}`, patchPath])
|
||
} catch {}
|
||
console.log(` ${user.username}: wrote(已追加平台段)${RESTART ? ' + 重启实例' : ''}`)
|
||
if (RESTART) {
|
||
try {
|
||
const out = execFileSync('ps', ['-eo', 'pid,user', '--no-headers'], { encoding: 'utf8' })
|
||
for (const line of out.split('\n')) {
|
||
const [pid, uname] = line.trim().split(/\s+/)
|
||
if (pid && uname === `dsh-${user.uid}`) {
|
||
try {
|
||
process.kill(Number(pid))
|
||
} catch {}
|
||
}
|
||
}
|
||
} catch {}
|
||
}
|
||
}
|
||
db.close()
|
||
console.log('done')
|