dsh-plugin-teamflow 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/store.mjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.mjs","names":[],"sources":["../store.ts"],"sourcesContent":["/**\n * dsh-plugin-teamflow — 持久化层(纯 node:fs,无 cordis 依赖,可独立测试)。\n *\n * 职责:\n * - $DSH_HOME/teamflow 目录布局(backlog 产品目录 + runs 运行日志目录)\n * - 原子写 + .bak 备份 + 损坏自动恢复(backlog 与 journal 共用)\n * - journal 序列化/持久化/加载(断点续跑基座,LangGraph checkpointer 语义)\n */\n\nimport { join, dirname } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport {\n mkdirSync, readFileSync, writeFileSync, existsSync, copyFileSync, renameSync, readdirSync,\n} from 'node:fs'\nimport { homedir } from 'node:os'\n\n/** 运行阶段(journal 序列化/续跑重建单元)。 */\nexport interface JournalStage {\n seq: number\n label: string\n phase: string\n status: string\n outcome?: string | null\n childId?: string | null\n startedAt?: number | null\n endedAt?: number | null\n usage?: { input: number; cacheRead: number; cacheWrite: number; output: number; calls: number } | null\n handoff?: string | null\n summary?: string | null\n output?: string | null\n /** 单调用护栏中止原因(进行中退化检测触发时记录;outcome 相应为 degenerated/stalled)。 */\n guardReason?: string | null\n /** 护栏中止分类:degenerated(复读,可干净重试)/ stalled(挂死/空转,走预算门转人工)。 */\n guardOutcome?: 'degenerated' | 'stalled' | null\n}\n\n/** 运行日志(journal)——运行时对象与磁盘可持久化形态的公共形状。 */\nexport interface JournalRecord {\n id: string\n name: string\n status: string\n requirement?: string\n options?: Record<string, unknown>\n /** 工作区作用域:安全槽位(用作 $DSH_HOME/teamflow/<workspace>/ 目录键,backlog 按此隔离)。 */\n workspace?: string | null\n /** 工作区绝对路径(发起会话 cwd;docs/logs 落点与看板展示用)。 */\n workspacePath?: string | null\n /** 发起会话 id(阶段子代理的直接 parent;跨会话跳转判定用)。 */\n ownerSession?: string | null\n /** M0 状态核对结果(start 时快照;多人/场外提交检测)。 */\n sanity?: {\n ok?: boolean\n branch?: string | null\n hasDirty?: boolean\n dirty?: string\n recentCommits?: string\n summary?: string\n } | null\n /** M1/M2 架构蓝图(tech/architect 阶段产出,dev 继承)。 */\n blueprint?: {\n modules?: Record<string, unknown>\n tasks?: Array<{ title: string; files?: string[]; spec?: string }>\n } | null\n product?: string | null\n reqId?: string | null\n /** ADR-0008 任务夹相对路径(docs/teamflow/<yyyyMMdd>-r<N>[-<slug>];需求级身份,建夹后固定,重试/续跑复用)。 */\n runDocs?: string | null\n /** 单任务模型:需求关联的唯一(轮转)任务 id。 */\n taskId?: string | null\n taskMap?: Record<string, string>\n agentsStarted?: number\n humanIntervention?: boolean\n cancelled?: boolean\n interrupted?: boolean\n interruptedAt?: number | null\n supersededBy?: string | null\n startedAt?: number | null\n endedAt?: number | null\n error?: string | null\n stages?: JournalStage[]\n logs?: Array<{ t: number; level: string; message: string }>\n result?: unknown\n [key: string]: unknown\n}\n\nexport function dshHome(): string {\n return process.env.DSH_HOME || join(homedir(), '.dsh')\n}\nexport function teamflowRoot(): string {\n return join(dshHome(), 'teamflow')\n}\n\n/**\n * 工作区 → 稳定安全槽位:以路径 basename 为可读前缀 + sha1 短哈希,保证同路径稳定、\n * 跨路径唯一,且只含 [a-zA-Z0-9_-](可安全作目录段,不穿越 $DSH_HOME)。\n * 例:C:\\...\\tetris → ws-tetris-3f9a2c7b\n */\nexport function slugPath(p: string | null | undefined): string {\n const raw = String(p || '').replace(/\\\\/g, '/')\n const base = raw.split('/').filter(Boolean).pop() || 'root'\n const tag = base.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 24) || 'ws'\n const hash = createHash('sha1').update(raw || 'default').digest('hex').slice(0, 8)\n return `ws-${tag}-${hash}`\n}\n\n/** 工作区绝对路径 → 该工作区的 `$DSH_HOME/teamflow/<slug>/backlog` 等目录基座。 */\nexport function workspaceDir(workspacePath: string | null | undefined): string {\n return join(teamflowRoot(), slugPath(workspacePath || null))\n}\nexport function productDir(product: string | null | undefined): string {\n // 双保险:危险路径退化到 default(与 host normalizeRoot 白名单一致)。\n let safe = String(product || 'default').replace(/\\\\/g, '/').trim() || 'default'\n if (safe.startsWith('/') || safe.startsWith('.') || /^[a-zA-Z]:/.test(safe) || safe.includes('..')) safe = 'default'\n if (!safe.split('/').every((seg) => /^[a-zA-Z0-9_-]+$/.test(seg))) safe = 'default'\n return join(teamflowRoot(), safe)\n}\nexport function fileFor(product: string | null | undefined, name: string): string {\n return join(productDir(product), 'backlog', name)\n}\nexport function runsDir(): string {\n return join(teamflowRoot(), 'runs')\n}\nexport function journalFile(runId: string): string {\n return join(runsDir(), `${runId}.json`)\n}\n\n/* ── 原子读/写(备份 + 损坏自愈) ────────────────────────────────── */\n/** 通用 JSON 读取(对象或数组),主文件损坏自动 .bak 恢复。 */\nexport function readJsonAny<T>(file: string, fallback: T): T {\n try {\n if (!existsSync(file)) return fallback\n return JSON.parse(readFileSync(file, 'utf8')) as T\n } catch (e) {\n console.error('[teamflow] readJson 主文件损坏', file, (e as Error)?.message)\n try {\n if (existsSync(file + '.bak')) {\n const b = JSON.parse(readFileSync(file + '.bak', 'utf8')) as T\n console.warn('[teamflow] 已从 .bak 恢复', file)\n return b\n }\n } catch (e2) { /* 备份也损坏 */ }\n console.error('[teamflow] .bak 也损坏,返回空(数据可能丢失)', file)\n return fallback\n }\n}\n\n/** 数组 JSON 读取(backlog 专用),非数组视为无效。 */\nexport function readJson<T>(file: string, fallback: T): T {\n const v = readJsonAny<T>(file, fallback)\n return Array.isArray(v) ? v : fallback\n}\n\n/**\n * 原子写 JSON:先 .tmp 再 rename(崩溃无半截文件);\n * 只把「可解析的完整数组」备份为 .bak;损坏主文件改名 .corrupt-<ts> 保留现场。\n */\nexport function writeJson(file: string, value: unknown): boolean {\n try {\n mkdirSync(dirname(file), { recursive: true })\n if (existsSync(file)) {\n try {\n const cur = JSON.parse(readFileSync(file, 'utf8'))\n if (Array.isArray(cur)) copyFileSync(file, file + '.bak')\n } catch (e) {\n try { renameSync(file, `${file}.corrupt-${Date.now()}`) } catch (e2) { /* 保留现场失败可忽略 */ }\n }\n }\n const tmp = file + '.tmp'\n writeFileSync(tmp, JSON.stringify(value, null, 2), 'utf8')\n renameSync(tmp, file)\n return true\n } catch (e) {\n console.error('[teamflow] writeJson failed', file, (e as Error)?.message)\n return false\n }\n}\n\n/* ── journal(运行日志)序列化 / 持久化 / 加载 ────────────────────── */\n/** 阶段产物全文保留上限(dev 任务等大输出裁剪;重跑时足够重建上下文)。 */\nexport const STAGE_OUTPUT_CLIP = 50000\n\nfunction clip(text: unknown, n: number): string {\n const s = text === null || text === undefined ? '' : String(text)\n return s.length > n ? s.slice(0, n) : s\n}\n\n/** journal → 可持久化 JSON(阶段含 output,供断点续跑重建产物)。 */\nexport function serializeJournal(journal: JournalRecord): JournalRecord {\n return {\n id: journal.id,\n name: journal.name,\n status: journal.status,\n requirement: journal.requirement,\n options: journal.options,\n workspace: journal.workspace || null,\n workspacePath: journal.workspacePath || null,\n ownerSession: journal.ownerSession || null,\n product: journal.product || null,\n sanity: journal.sanity || null,\n blueprint: journal.blueprint || null,\n reqId: journal.reqId || null,\n runDocs: journal.runDocs || null,\n taskId: journal.taskId || null,\n taskMap: journal.taskMap || {},\n agentsStarted: journal.agentsStarted || 0,\n humanIntervention: journal.humanIntervention === true,\n cancelled: journal.cancelled === true,\n interrupted: journal.interrupted === true,\n interruptedAt: journal.interruptedAt || null,\n supersededBy: journal.supersededBy || null,\n startedAt: journal.startedAt,\n endedAt: journal.endedAt,\n error: journal.error || null,\n stages: (journal.stages || []).map((s) => ({\n seq: s.seq,\n label: s.label,\n phase: s.phase,\n status: s.status,\n outcome: s.outcome || null,\n childId: s.childId || null,\n startedAt: s.startedAt,\n endedAt: s.endedAt,\n usage: s.usage || null,\n handoff: clip(s.handoff || '', 2000),\n summary: clip(s.summary || '', 3000),\n output: clip(s.output || s.summary || '', STAGE_OUTPUT_CLIP),\n })),\n logs: (journal.logs || []).slice(-300).map((l) => ({ t: l.t, level: l.level, message: clip(l.message, 500) })),\n }\n}\n\n/** 通用文本写(原子写 + 递归建目录),供 run 日志等使用。 */\nexport function writeText(file: string, text: string): boolean {\n try {\n mkdirSync(dirname(file), { recursive: true })\n const tmp = file + '.tmp'\n writeFileSync(tmp, text, 'utf8')\n renameSync(tmp, file)\n return true\n } catch (e) {\n console.error('[teamflow] writeText failed', file, (e as Error)?.message)\n return false\n }\n}\n\n/** 工作区下日志落点:<工作区>/logs/teamflow/<runId>.log(存在 workspacePath 时才落)。 */\nexport function runLogFile(journal: JournalRecord): string | null {\n if (!journal.workspacePath) return null\n return join(journal.workspacePath, 'logs', 'teamflow', `${journal.id}.log`)\n}\n\n/** 把运行事件日志聚合写到 <工作区>/logs/teamflow/<runId>.log(logs 收口,防止污染宿主)。 */\nexport function persistRunLog(journal: JournalRecord): boolean {\n const file = runLogFile(journal)\n if (!file) return false\n const lines = [\n `# TeamFlow 运行日志(${journal.name})`,\n `runId=${journal.id}`,\n `workspace=${journal.workspacePath || ''}`,\n `product=${journal.product || ''}`,\n `status=${journal.status}`,\n `startedAt=${journal.startedAt ? new Date(journal.startedAt).toISOString() : ''}`,\n `endedAt=${journal.endedAt ? new Date(journal.endedAt).toISOString() : ''}`,\n `requirement=${journal.requirement || ''}`,\n '',\n ]\n for (const l of (journal.logs || []).slice(-300)) {\n lines.push(`[${l.t ? new Date(l.t).toISOString() : ''}] ${l.level}: ${l.message}`)\n }\n if (journal.error) lines.push('', `ERROR: ${journal.error}`)\n return writeText(file, lines.join('\\n') + '\\n')\n}\n\n/** 原子写 journal 文件(新 journal 写 per-project runs/,旧 journal 写全局 runs/)。 */\nexport function persistJournal(journal: JournalRecord): boolean {\n // 有 workspace 字段的新 journal → 写到 per-project runs/\n if (journal.workspace && journal.workspace !== 'default') {\n const projectDir = join(teamflowRoot(), journal.workspace, 'runs')\n const file = join(projectDir, `${journal.id}.json`)\n const ok = writeJson(file, serializeJournal(journal))\n if (ok && journal.workspacePath) persistRunLog(journal)\n return ok\n }\n // 旧格式 / 兜底 → 写到全局 runs/\n const ok = writeJson(journalFile(journal.id), serializeJournal(journal))\n if (ok && journal.workspacePath) persistRunLog(journal)\n return ok\n}\n\n/**\n * 启动时扫描磁盘 journal:\n * - 全局 $DSH_HOME/teamflow/runs/(兼容旧格式)\n * - 各 per-project $DSH_HOME/teamflow/<project>/runs/(新格式)\n * - 正常状态原样载入;running/pending → 标记 interrupted(进程崩溃/重启残留)。\n * @returns 全部历史 run(含是否本次被标记中断)。\n */\nexport function loadJournals(): Array<{ journal: JournalRecord; wasInterrupted: boolean }> {\n const out: Array<{ journal: JournalRecord; wasInterrupted: boolean }> = []\n const seen = new Set<string>() // 去重(同一 journal 不要重复加载)\n const loadDir = (dir: string) => {\n try {\n if (!existsSync(dir)) return\n for (const f of readdirSync(dir)) {\n if (!f.endsWith('.json') || f.includes('.bak') || f.includes('.tmp') || f.includes('.corrupt')) continue\n const j = readJsonAny<JournalRecord | null>(join(dir, f), null)\n if (!j || typeof j !== 'object' || typeof j.id !== 'string') continue\n if (seen.has(j.id)) continue\n seen.add(j.id)\n let wasInterrupted = false\n if (j.status === 'running' || j.status === 'pending') {\n j.status = 'interrupted'\n j.interrupted = true\n j.interruptedAt = Date.now()\n j.endedAt = j.endedAt || Date.now()\n for (const s of (j.stages || [])) {\n if (s.status === 'running') { s.status = 'interrupted'; s.endedAt = s.endedAt || Date.now() }\n }\n writeJson(join(dir, f), j)\n wasInterrupted = true\n out.push({ journal: j, wasInterrupted })\n } else {\n out.push({ journal: j, wasInterrupted })\n }\n }\n } catch (e) { /* 单目录失败不影响其他 */ }\n }\n try {\n // 1. 全局 runs/(兼容旧格式 journal)\n loadDir(runsDir())\n // 2. per-project runs/(新格式 journal)\n const root = teamflowRoot()\n if (existsSync(root)) {\n for (const entry of readdirSync(root)) {\n const sub = join(root, entry)\n try {\n if (entry === 'runs') continue // 全局已扫描\n if (existsSync(sub) && readdirSync(sub).includes('runs')) {\n loadDir(join(sub, 'runs'))\n }\n } catch (e) { /* 跳过非目录 */ }\n }\n }\n } catch (e) {\n console.error('[teamflow] loadJournals failed', (e as Error)?.message)\n }\n return out\n}\n"],"mappings":"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"}