@kanadego/dsh-heartbeat 1.9.2 → 1.9.3

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.
@@ -459,7 +459,7 @@ function saveWeeklyReport(guard, dataDir, report) {
459
459
  atomicWriteJsonSync(stateFilePath(dataDir), { lastGeneratedAt: Date.parse(report.generatedAt) });
460
460
  return file;
461
461
  }
462
- function listWeeklyReports(guard, dataDir) {
462
+ function listWeeklyReports(guard, dataDir, limit) {
463
463
  const out = [];
464
464
  try {
465
465
  for (const file of fs2.readdirSync(weeklyDirPath(dataDir))) {
@@ -470,7 +470,20 @@ function listWeeklyReports(guard, dataDir) {
470
470
  }
471
471
  } catch {
472
472
  }
473
- return out.sort((a, b) => a.end < b.end ? 1 : -1);
473
+ const sorted = out.sort((a, b) => a.end < b.end ? 1 : -1);
474
+ return limit !== void 0 && limit > 0 ? sorted.slice(0, limit) : sorted;
475
+ }
476
+ function pruneWeeklyReports(guard, dataDir, keep) {
477
+ if (!Number.isFinite(keep) || keep <= 0) return [];
478
+ const removed = [];
479
+ for (const meta of listWeeklyReports(guard, dataDir).slice(keep)) {
480
+ try {
481
+ fs2.rmSync(guard.assert(path5.join(weeklyDirPath(dataDir), meta.file)));
482
+ removed.push(meta.file);
483
+ } catch {
484
+ }
485
+ }
486
+ return removed;
474
487
  }
475
488
  function readWeeklyReport(guard, dataDir, file) {
476
489
  if (!/^report-\d{4}-\d{2}-\d{2}\.json$/.test(file)) return null;
@@ -562,8 +575,9 @@ export {
562
575
  ensureWeeklyAnchor,
563
576
  saveWeeklyReport,
564
577
  listWeeklyReports,
578
+ pruneWeeklyReports,
565
579
  readWeeklyReport,
566
580
  buildWeeklyPrompt,
567
581
  renderTemplateReport
568
582
  };
569
- //# sourceMappingURL=chunk-IG6YMUHW.js.map
583
+ //# sourceMappingURL=chunk-WLZSABBH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ledger/ledger.ts","../src/browse/preference.ts","../src/browse/browse.ts","../src/core/time-window.ts","../src/notify/notify.ts","../src/core/ps.ts","../src/weekly/report.ts"],"sourcesContent":["// Ledger (\"账本\") - the ONE shared ledger (design doc §5, requirement 5).\r\n// Human-readable Markdown by charter; the user may edit it by hand, so the\r\n// parser is tolerant: unknown lines are preserved verbatim on rewrite.\r\n\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport type { PathGuard } from '../core/path-guard.js';\r\nimport { readText, writeText } from '../vault/vault.js';\r\n\r\nconst DAY_MS = 86_400_000;\r\n\r\nexport interface LedgerEntry {\r\n id: string;\r\n date: string; // \"YYYY-MM-DD\"\r\n time: string; // \"HH:MM\"\r\n status: 'open' | 'done';\r\n text: string;\r\n}\r\n\r\nexport function ledgerFilePath(dataDir: string): string {\r\n return path.join(dataDir, 'ledger.md');\r\n}\r\n\r\nconst LINE_RE = /^- \\[(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2})\\]\\[(open|done)\\]\\[#([0-9a-f]{6})\\] (.*)$/;\r\n\r\nfunction renderEntry(e: LedgerEntry): string {\r\n return `- [${e.date} ${e.time}][${e.status}][#${e.id}] ${e.text}`;\r\n}\r\n\r\nexport function readLedger(guard: PathGuard, file: string): { header: string; entries: LedgerEntry[]; rawLines: string[] } {\r\n const raw = readText(guard, file, '# 账本\\n');\r\n const lines = raw.split('\\n');\r\n const entries: LedgerEntry[] = [];\r\n const rawLines: string[] = [];\r\n for (const line of lines) {\r\n const m = LINE_RE.exec(line);\r\n if (m) {\r\n entries.push({ date: m[1]!, time: m[2]!, status: m[3] as 'open' | 'done', id: m[4]!, text: m[5]! });\r\n }\r\n rawLines.push(line);\r\n }\r\n return { header: lines[0] ?? '# 账本', entries, rawLines };\r\n}\r\n\r\nexport function appendEntry(guard: PathGuard, file: string, text: string, now = Date.now()): LedgerEntry {\r\n const textTrimmed = text.trim();\r\n if (!textTrimmed) throw new Error('ledger entry must not be empty');\r\n const d = new Date(now);\r\n const pad = (n: number) => String(n).padStart(2, '0');\r\n const entry: LedgerEntry = {\r\n id: randomUUID().slice(0, 6),\r\n date: `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`,\r\n time: `${pad(d.getHours())}:${pad(d.getMinutes())}`,\r\n status: 'open',\r\n text: textTrimmed.replace(/\\r?\\n/g, ' '),\r\n };\r\n const { rawLines } = readLedger(guard, file);\r\n rawLines.push(renderEntry(entry));\r\n writeText(guard, file, rawLines.join('\\n').replace(/\\n*$/, '\\n'));\r\n return entry;\r\n}\r\n\r\n/** Mark an entry done by id (preferred) or unique text substring. The date is\r\n * restamped to the completion day (time was already) so weekly reporting can\r\n * count \"resolved this week\" — the creation timestamp lives on in nothing,\r\n * but done entries are rarely aged against. */\r\nexport function markDone(guard: PathGuard, file: string, key: string, now = Date.now()): LedgerEntry | null {\r\n const { rawLines, entries } = readLedger(guard, file);\r\n const target = entries.find((e) => e.status === 'open' && (e.id === key || e.text.includes(key)));\r\n if (!target) return null;\r\n const d = new Date(now);\r\n const pad = (n: number) => String(n).padStart(2, '0');\r\n const doneDate = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\r\n const out = rawLines.map((line) => {\r\n if (line.includes(`#${target.id}] `)) {\r\n return `- [${doneDate} ${pad(d.getHours())}:${pad(d.getMinutes())}][done][#${target.id}] ${target.text}`;\r\n }\r\n return line;\r\n });\r\n writeText(guard, file, out.join('\\n').replace(/\\n*$/, '\\n'));\r\n return target;\r\n}\r\n\r\n/** Open items for the reflection digest (§7.6 账本待办). */\r\nexport function scanPending(guard: PathGuard, file: string, now = Date.now()): LedgerEntry[] {\r\n const { entries } = readLedger(guard, file);\r\n return entries.filter((e) => e.status === 'open').sort((a, b) => (a.date < b.date ? -1 : 1));\r\n}\r\n\r\n/** Open entries older than N days (跟进时机的\"自然到期\"参考,§3.1 四问之一). */\r\nexport function pendingOlderThan(guard: PathGuard, file: string, days: number, now = Date.now()): LedgerEntry[] {\r\n const cutoff = new Date(now - days * DAY_MS).toISOString().slice(0, 10);\r\n return scanPending(guard, file, now).filter((e) => e.date <= cutoff);\r\n}\r\n","// Topic preference (2026-10-06): consumption-driven bias for wander focus\n// selection. The persona's seed_report attributions (and the engine-room\n// fallback) fold into per-topic delivered/adopted counters; pickFocus reads\n// the smoothed rate as a weight on top of round-robin recency.\n//\n// Anti-lockdown guarantees (user-reviewed design):\n// - weight clamped to [W_MIN, W_MAX] — a favorite doubles at most, a cold\n// topic never drops below half;\n// - Laplace smoothing — a topic with no data sits near neutral, not zero;\n// - exponential decay (30-day half-life) — old glory fades;\n// - pickFocus additionally enforces a hard starvation floor (14 days).\n//\n// Storage is one JSON file through the vault (DPAPI): topic keys derive from\n// the user's profile/interests, so they are user-adjacent data.\n\nimport path from 'node:path';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { loadJson, saveJson } from '../vault/vault.js';\n\nconst DAY_MS = 86_400_000;\n\nexport const W_MIN = 0.5;\nexport const W_MAX = 2;\n/** Laplace prior: rate = (adopted + A) / (delivered + D). Neutral ≈ A/D. */\nexport const SMOOTH_ADOPTED = 1;\nexport const SMOOTH_DELIVERED = 4;\n/** Counts decay with this half-life (days) since the topic's last activity. */\nexport const DECAY_HALF_LIFE_DAYS = 30;\n\nexport interface TopicPref {\n delivered: number;\n adopted: number;\n lastDeliveredAt: number;\n lastAdoptedAt: number;\n}\n\nexport interface PrefState {\n version: 1;\n topics: Record<string, TopicPref>;\n updatedAt: number;\n}\n\nexport function preferenceFilePath(dataDir: string): string {\n return path.join(dataDir, 'preference.json');\n}\n\n/** Non-negative finite number or 0 (v1.9.0 guard against hand-edited files). */\nfunction num(v: unknown): number {\n const n = Number(v);\n return Number.isFinite(n) && n >= 0 ? n : 0;\n}\n\n/** One stored entry → a trustworthy counter. Returns null for a garbled entry:\n * a NaN delivered/adopted used to survive the load and poison the whole\n * ordering (NaN makes every comparison false, so the topic neither aged nor\n * sorted). Dropping the entry is the safe read — the loop refills it. */\nfunction sanitizeTopicPref(raw: unknown): TopicPref | null {\n if (!raw || typeof raw !== 'object') return null;\n const t = raw as Partial<TopicPref>;\n if (!Number.isFinite(Number(t.delivered)) || !Number.isFinite(Number(t.adopted))) return null;\n const delivered = Math.floor(num(t.delivered));\n return {\n delivered,\n adopted: Math.min(delivered, Math.floor(num(t.adopted))),\n lastDeliveredAt: num(t.lastDeliveredAt),\n lastAdoptedAt: num(t.lastAdoptedAt),\n };\n}\n\nexport function loadPreference(guard: PathGuard, file: string): PrefState {\n const doc = loadJson<Partial<PrefState>>(guard, file);\n if (!doc || typeof doc !== 'object' || !doc.topics || typeof doc.topics !== 'object') {\n return { version: 1, topics: {}, updatedAt: 0 };\n }\n const topics: Record<string, TopicPref> = {};\n for (const [key, raw] of Object.entries(doc.topics)) {\n const t = sanitizeTopicPref(raw);\n if (key && t) topics[key] = t;\n }\n const updatedAt = Number(doc.updatedAt);\n return { version: 1, topics, updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0 };\n}\n\nexport function savePreference(guard: PathGuard, file: string, state: PrefState): void {\n saveJson(guard, file, state);\n}\n\n/** Fold one delivery into the counters. offeredTopics = every topic in the\n * package; adoptedTopics = the credited ones (subset by construction — unknown\n * keys are still tolerated so the two lists can drift safely). */\nexport function recordDelivery(\n guard: PathGuard,\n file: string,\n opts: { offeredTopics: string[]; adoptedTopics: string[]; now: number },\n): PrefState {\n const state = loadPreference(guard, file);\n const adopted = new Set(opts.adoptedTopics);\n for (const topic of opts.offeredTopics) {\n if (!topic) continue;\n const t = state.topics[topic] ?? { delivered: 0, adopted: 0, lastDeliveredAt: 0, lastAdoptedAt: 0 };\n t.delivered += 1;\n t.lastDeliveredAt = opts.now;\n if (adopted.has(topic)) {\n t.adopted += 1;\n t.lastAdoptedAt = opts.now;\n }\n state.topics[topic] = t;\n }\n state.updatedAt = opts.now;\n savePreference(guard, file, state);\n return state;\n}\n\n/** Time-decayed counts (30-day half-life since last activity of each kind). */\nexport function decayed(t: TopicPref, now: number): { delivered: number; adopted: number } {\n const dAgeDays = Math.max(0, now - t.lastDeliveredAt) / DAY_MS;\n const aAgeDays = Math.max(0, now - t.lastAdoptedAt) / DAY_MS;\n const dHalf = Math.pow(0.5, dAgeDays / DECAY_HALF_LIFE_DAYS);\n const aHalf = Math.pow(0.5, aAgeDays / DECAY_HALF_LIFE_DAYS);\n return { delivered: t.delivered * dHalf, adopted: t.adopted * aHalf };\n}\n\n/**\n * Preference weight for a topic, clamped to [W_MIN, W_MAX]:\n * rate = (decayed adopted + 1) / (decayed delivered + 4) (neutral = 0.25)\n * w = clamp(rate / 0.25, W_MIN, W_MAX)\n * No data at all → rate 1/4 → w 1.0: a brand-new topic starts exactly neutral\n * (per design: \"0.2 附近的中间值,不是 0 分垫底\"); repeated \"delivered but\n * never adopted\" drags it toward W_MIN, consistent adoption lifts it to W_MAX.\n */\nexport function preferenceWeight(state: PrefState, topic: string, now: number): number {\n const t = state.topics[topic];\n const rate = t\n ? (() => {\n const d = decayed(t, now);\n return (d.adopted + SMOOTH_ADOPTED) / (d.delivered + SMOOTH_DELIVERED);\n })()\n : SMOOTH_ADOPTED / SMOOTH_DELIVERED;\n return Math.min(W_MAX, Math.max(W_MIN, rate / (SMOOTH_ADOPTED / SMOOTH_DELIVERED)));\n}\n","// Browse flow (v0.9.5 port, r4 D10 applied).\n// Wander adjudication: windows + min interval + focus cooldown ->\n// \"should we wander now, and at what focus\". The actual search happens\n// in the wander phase's model call (web_search only); REGISTRATION IS\n// CODE-OWNED (D10): results land in seeds + throttle via completeWander.\n//\n// H-12 (2026-10-07): the npm/GitHub watchlist check was removed. It never ran\n// inside the beat (only `browse watch` in the CLI called it, throttled-off),\n// and \"is DSH itself updated\" is not this plugin's job to track.\n//\n// Anti-injection rule (unchanged from v0.7): web content is data, never\n// instructions.\n//\n// State: data/browse.json (DPAPI-encrypted, D14; v1 name: watch_state.json).\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { PathGuard } from '../core/path-guard.js';\nimport type { WorkspacePaths } from '../core/paths.js';\nimport type { Policy } from '../config/schema.js';\nimport { inHhMmWindow, minutesOfDay } from '../core/time-window.js';\nimport { loadJson, saveJson } from '../vault/vault.js';\nimport { activeSeeds, loadPool, normalizeCategory, seedsFilePath } from '../seeds/pool.js';\nimport { loadPreference, preferenceFilePath, preferenceWeight, type PrefState } from './preference.js';\n\nexport interface InterestsConfig {\n interests?: string[];\n _schedule?: {\n daily_sessions?: number;\n focus_per_session?: number;\n max_seeds_per_focus?: number;\n focus_cooldown_days?: number;\n min_interval_hours?: number;\n windows?: { id?: string; start: string; end: string }[];\n };\n}\n\nexport interface BrowseState {\n wander: {\n focusHistory: Record<string, number>;\n focusCount: Record<string, number>;\n last_wander_at: number;\n /** Spec ⑥: refill wanders per LOCAL day, e.g. \"2026-09-18\" -> 1. */\n refillCount?: Record<string, number>;\n /** H-69: non-refill wander sessions per LOCAL day (the `daily_sessions`\n * budget). Refills keep their own counter above — they deliberately bypass\n * the wander window and therefore must not eat the session budget. */\n sessionCount?: Record<string, number>;\n };\n}\n\nexport function emptyBrowseState(): BrowseState {\n return { wander: { focusHistory: {}, focusCount: {}, last_wander_at: 0, refillCount: {}, sessionCount: {} } };\n}\n\nexport function browseStatePath(paths: WorkspacePaths): string {\n return path.join(paths.dataDir, 'browse.json');\n}\n\nfunction readJsonFile<T>(file: string, fallback: T): T {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8')) as T;\n } catch {\n return fallback;\n }\n}\n\n/** Factory configs are read-only; a user layer with the same filename replaces them. */\nexport function loadInterests(paths: WorkspacePaths): InterestsConfig {\n const userPath = path.join(paths.settingsDir, 'interests.json');\n if (fs.existsSync(userPath)) return readJsonFile<InterestsConfig>(userPath, { interests: [], _schedule: {} });\n return readJsonFile<InterestsConfig>(path.join(paths.configDir, 'interests.json'), { interests: [], _schedule: {} });\n}\n\n/**\n * H-65: the state file outlives upgrades and is hand-inspectable, so a row\n * missing a field must not throw its way into a `beat_error` on every beat.\n * Garbage values are dropped back to the empty-state defaults.\n */\nexport function normalizeBrowseState(raw: unknown): BrowseState {\n const out = emptyBrowseState();\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return out;\n const r = raw as Partial<BrowseState>;\n const w = (r.wander && typeof r.wander === 'object' ? r.wander : {}) as Partial<BrowseState['wander']>;\n out.wander.focusHistory = numberRecord(w.focusHistory);\n out.wander.focusCount = numberRecord(w.focusCount);\n out.wander.refillCount = numberRecord(w.refillCount);\n out.wander.sessionCount = numberRecord(w.sessionCount);\n if (typeof w.last_wander_at === 'number' && Number.isFinite(w.last_wander_at)) out.wander.last_wander_at = w.last_wander_at;\n return out;\n}\n\nfunction numberRecord(v: unknown): Record<string, number> {\n const out: Record<string, number> = {};\n if (!v || typeof v !== 'object' || Array.isArray(v)) return out;\n for (const [k, n] of Object.entries(v as Record<string, unknown>)) {\n if (typeof n === 'number' && Number.isFinite(n)) out[k] = n;\n }\n return out;\n}\n\nfunction loadState(guard: PathGuard, paths: WorkspacePaths): BrowseState {\n return normalizeBrowseState(loadJson<unknown>(guard, browseStatePath(paths)));\n}\n\n// ── B. wander adjudication (pure-ish, state injected) ───────────────────\n\nexport interface WanderAdvice {\n focus: string | null;\n query: string | null;\n skipped: string | null;\n /** H-69: focuses this session was allowed to examine (`focus_per_session`);\n * `focus` is the first of them. The phase still runs one model turn per\n * focus, so >1 only widens the candidate list the caller may use. */\n candidates?: string[];\n /** H-69: per-focus seed cap (`max_seeds_per_focus`, defaulting to\n * `policy.browse.maxSeedsPerVisit`). The registration loop reads this. */\n maxSeeds?: number;\n}\n\nexport interface WanderSchedule {\n windows: { start: string; end: string }[];\n minIntervalHours: number;\n maxSeedsPerFocus: number;\n /** Wander sessions per local day; 0 = no daily cap. */\n dailySessions: number;\n focusPerSession: number;\n focusCooldownDays: number;\n}\n\n/**\n * H-69 (2026-10-07): `interests._schedule` is the block the settings card edits\n * and, as the user layer, it REPLACES the factory copy — so it wins wherever it\n * overlaps `policy.browse`, which is the compiled-in default. Every key here\n * has a reader now; `min_interval_hours`, `max_seeds_per_focus` and\n * `daily_sessions` used to be dead wiring whose factory values happened to\n * match policy's, which is exactly why nobody noticed they did nothing.\n */\nexport function resolveSchedule(interests: InterestsConfig, policy: Policy): WanderSchedule {\n const sc = interests._schedule ?? {};\n return {\n windows: sc.windows?.length ? sc.windows : (policy.browse.windows as { start: string; end: string }[]),\n minIntervalHours: sc.min_interval_hours ?? policy.browse.minIntervalHours,\n maxSeedsPerFocus: sc.max_seeds_per_focus ?? policy.browse.maxSeedsPerVisit,\n dailySessions: sc.daily_sessions ?? 0,\n focusPerSession: Math.max(1, sc.focus_per_session ?? 1),\n focusCooldownDays: sc.focus_cooldown_days ?? 3,\n };\n}\n\nexport function inWanderWindow(now: Date, windows: { start: string; end: string }[]): string | null {\n const mins = minutesOfDay(now);\n for (const w of windows) {\n if (inHhMmWindow(w.start, w.end, mins)) return `${w.start}-${w.end}`;\n }\n return null;\n}\n\nfunction onCooldown(state: BrowseState, focus: string, cooldownDays: number, now: number): boolean {\n const last = state.wander.focusHistory[focus] ?? 0;\n return last > now - cooldownDays * 86_400_000;\n}\n\n/**\n * Focus pick (2026-10-06): round-robin recency as the base, preference rate as\n * a multiplier. Ordering key = effective age = realAgeSinceLastUse × weight —\n * a favorite's age \"grows\" faster, so it reaches the pick threshold sooner and\n * is wandered up to W_MAX times as often. Lockdown-proof by construction:\n * - cooldown (above) keeps every topic rotating no matter the weight;\n * - weight lives in [W_MIN, W_MAX] (preference.ts);\n * - hard starvation floor: any topic untouched ≥ STARVATION_DAYS ignores\n * weighting entirely and the oldest real age wins.\n * Without a preference state the behavior is exactly the old pure LRU.\n */\nexport const STARVATION_DAYS = 14;\n\nexport function pickFocus(\n state: BrowseState,\n interests: InterestsConfig,\n now: number,\n pref?: PrefState,\n): string | null {\n const sc = interests._schedule ?? {};\n const cooldown = sc.focus_cooldown_days ?? 3;\n const pool = (interests.interests ?? []).filter((t) => !onCooldown(state, t, cooldown, now));\n if (pool.length === 0) return null;\n if (!pref) {\n pool.sort((a, b) => (state.wander.focusHistory[a] ?? 0) - (state.wander.focusHistory[b] ?? 0));\n return pool[0]!;\n }\n const age = (t: string): number => now - (state.wander.focusHistory[t] ?? 0);\n const starved = pool.filter((t) => age(t) >= STARVATION_DAYS * 86_400_000);\n if (starved.length > 0) {\n starved.sort((a, b) => age(b) - age(a)); // oldest real age wins\n return starved[0]!;\n }\n const effAge = (t: string): number => age(t) * preferenceWeight(pref, t, now);\n pool.sort((a, b) => effAge(b) - effAge(a));\n return pool[0]!;\n}\n\n/** Adjudicate: windows + min interval + focus cooldown -> advice | skipped. */\nexport function adviseWander(\n guard: PathGuard,\n paths: WorkspacePaths,\n policy: Policy,\n now = new Date(),\n): WanderAdvice {\n const state = loadState(guard, paths);\n const interests = loadInterests(paths);\n const sched = resolveSchedule(interests, policy);\n const budget = { maxSeeds: sched.maxSeedsPerFocus };\n const win = inWanderWindow(now, sched.windows);\n if (!win) {\n const hh = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;\n return { focus: null, query: null, skipped: `window(now=${hh})`, ...budget };\n }\n // H-69: `daily_sessions` — same LOCAL-day budget shape as the refill quota.\n const today = localDayKey(now);\n const sessionsToday = state.wander.sessionCount?.[today] ?? 0;\n if (sched.dailySessions > 0 && sessionsToday >= sched.dailySessions) {\n return { focus: null, query: null, skipped: `daily-sessions(${sessionsToday})`, ...budget };\n }\n const minGap = sched.minIntervalHours * 3600_000;\n if (now.getTime() - state.wander.last_wander_at < minGap) {\n return { focus: null, query: null, skipped: 'min-interval', ...budget };\n }\n // H-69: `focus_per_session` candidates, drawn without replacement so one\n // session never offers the same focus twice.\n const pref = loadPreference(guard, preferenceFilePath(paths.dataDir));\n const pool = interests.interests ?? [];\n const candidates: string[] = [];\n for (let i = 0; i < sched.focusPerSession; i++) {\n const rest = pool.filter((t) => !candidates.includes(t));\n if (rest.length === 0) break;\n const picked = pickFocus(state, { ...interests, interests: rest }, now.getTime(), pref);\n if (!picked) break;\n candidates.push(picked);\n }\n if (candidates.length === 0) return { focus: null, query: null, skipped: 'no-focus', ...budget };\n return { focus: candidates[0]!, query: `${candidates[0]!} 2026 最新`, skipped: null, candidates, ...budget };\n}\n\n/**\n * Registration is CODE-OWNED (D10): called by the orchestrator after the\n * wander phase's model call returned selections. Records cooldown/count/\n * throttle timestamps for the focus. `refill` additionally bumps the daily\n * refill counter (spec ⑥).\n */\nexport function completeWander(\n guard: PathGuard,\n paths: WorkspacePaths,\n focus: string,\n now = Date.now(),\n opts: { refill?: boolean } = {},\n): { focus: string; count: number; refillsToday?: number } {\n const state = loadState(guard, paths);\n state.wander.focusHistory[focus] = now;\n state.wander.focusCount[focus] = (state.wander.focusCount[focus] ?? 0) + 1;\n state.wander.last_wander_at = now;\n if (!opts.refill) {\n // H-69: count this session against the `daily_sessions` budget. Refills\n // are their own channel (they deliberately bypass the window and the min\n // interval) and stay on refillCount alone.\n const day = localDayKey(new Date(now));\n state.wander.sessionCount = state.wander.sessionCount ?? {};\n state.wander.sessionCount[day] = (state.wander.sessionCount[day] ?? 0) + 1;\n }\n let refillsToday: number | undefined;\n if (opts.refill) {\n const today = localDayKey(new Date(now));\n state.wander.refillCount = state.wander.refillCount ?? {};\n state.wander.refillCount[today] = (state.wander.refillCount[today] ?? 0) + 1;\n refillsToday = state.wander.refillCount[today];\n }\n saveJson(guard, browseStatePath(paths), state);\n return { focus, count: state.wander.focusCount[focus]!, ...(refillsToday === undefined ? {} : { refillsToday }) };\n}\n\n/** LOCAL day key (refill quota is a daily human-day budget, not a UTC day). */\nfunction localDayKey(d: Date): string {\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\n// ── C. refill wander (spec ⑥, 2026-09-18): top up topic stock when it runs\n// dry. Independent of the window/minInterval gates (user decision: may stack\n// with a normal wander in the same beat) but still respects the 3-day focus\n// cooldown, and is capped at REFILL_MAX_PER_DAY per local day.\n\nexport const REFILL_TOPIC_THRESHOLD = 4;\nexport const REFILL_MAX_PER_DAY = 2;\n\nexport interface RefillAdvice {\n focus: string | null;\n query: string | null;\n skipped: string | null;\n topicCount: number;\n refillsToday: number;\n}\n\nexport function adviseRefillWander(\n guard: PathGuard,\n paths: WorkspacePaths,\n policy: Policy,\n now = new Date(),\n): RefillAdvice {\n const state = loadState(guard, paths);\n const today = localDayKey(now);\n const refillsToday = state.wander.refillCount?.[today] ?? 0;\n const topicCount = activeSeeds(loadPool(guard, seedsFilePath(paths.dataDir)))\n .filter((s) => normalizeCategory(s.category) === 'topic').length;\n if (refillsToday >= REFILL_MAX_PER_DAY) {\n return { focus: null, query: null, skipped: `refill-daily-cap(${refillsToday})`, topicCount, refillsToday };\n }\n if (topicCount > REFILL_TOPIC_THRESHOLD) {\n return { focus: null, query: null, skipped: `topic-stock-ok(${topicCount})`, topicCount, refillsToday };\n }\n const interests = loadInterests(paths);\n const focus = pickFocus(state, interests, now.getTime(), loadPreference(guard, preferenceFilePath(paths.dataDir))); // 3-day cooldown still applies\n if (!focus) {\n return { focus: null, query: null, skipped: 'no-focus', topicCount, refillsToday };\n }\n return { focus, query: `${focus} 2026 最新`, skipped: null, topicCount, refillsToday };\n}\n\nexport function browseStatus(guard: PathGuard, paths: WorkspacePaths): BrowseState {\n return loadState(guard, paths);\n}\n","// Shared HH:MM window arithmetic.\n//\n// H-14: quiet hours (`gate.ts`) and wander windows (`browse.ts`) each carried\n// their own copy of this comparison, and only ONE of them handled a window\n// that crosses midnight — so a `22:00-02:00` wander window silently never\n// matched. One implementation, one set of semantics, no drift.\n//\n// Interval is left-closed / right-open, so `22:00-02:00` and `02:00-06:00`\n// never overlap and every minute belongs to at most one window.\n\nconst HH_MM = /^(\\d{1,2}):(\\d{2})$/;\n\n/** \"22:30\" -> 1350; null when malformed (callers treat that as \"never matches\"). */\nexport function parseHhMm(text: string): number | null {\n const m = HH_MM.exec(String(text).trim());\n if (!m) return null;\n const h = Number(m[1]);\n const min = Number(m[2]);\n if (!Number.isFinite(h) || !Number.isFinite(min) || h > 23 || min > 59) return null;\n return h * 60 + min;\n}\n\nexport function minutesOfDay(now: Date): number {\n return now.getHours() * 60 + now.getMinutes();\n}\n\n/** TRUE when `mins` (minutes since local midnight) falls inside [start, end). */\nexport function inHhMmWindow(start: string, end: string, mins: number): boolean {\n const s = parseHhMm(start);\n const e = parseHhMm(end);\n if (s === null || e === null) return false;\n if (s === e) return false; // zero-length window matches nothing\n return s > e ? mins >= s || mins < e : mins >= s && mins < e;\n}\n","// Windows toast channel (D12): attention hint ONLY - never carries the\n// expression body. Wraps assets/notify.ps1 (self-registering AUMID).\n\nimport path from 'node:path';\nimport { runPowerShellFile } from '../core/ps.js';\nimport type { WorkspacePaths } from '../core/paths.js';\n\nasync function runNotify(paths: WorkspacePaths, args: string[]): Promise<{ status: number; out: string }> {\n const r = await runPowerShellFile(path.join(paths.assetsDir, 'notify.ps1'), args, 20_000);\n return { status: r.status, out: `${r.stdout}${r.stderr}`.trim() };\n}\n\n/** Register the toast identity if missing (idempotent, per machine/user). */\nexport async function ensureRegistered(paths: WorkspacePaths): Promise<boolean> {\n const check = await runNotify(paths, ['-Check']);\n if (/REGISTERED: yes/.test(check.out)) return true;\n const reg = await runNotify(paths, ['-RegisterOnly']);\n return reg.status === 0;\n}\n\n/**\n * Fire the \"new message\" hint. Fixed neutral text per D12 - the actual\n * expression lives in the dedicated heartbeat session, never in the toast.\n */\nexport async function sendNewMessageHint(paths: WorkspacePaths): Promise<boolean> {\n const r = await runNotify(paths, ['-Title', 'Heartbeat', '-Message', '有新消息']);\n return r.status === 0 && /TOAST_SENT/.test(r.out);\n}\n\n/** Weekly report ready hint (v1.8.0): same D12 discipline — announces that a\n * report exists, never carries its content. */\nexport async function sendWeeklyReadyHint(paths: WorkspacePaths): Promise<boolean> {\n const r = await runNotify(paths, ['-Title', '心跳周报', '-Message', '本期周报已生成']);\n return r.status === 0 && /TOAST_SENT/.test(r.out);\n}\n","/**\n * H-10: asynchronous PowerShell runner.\n *\n * The probes (idle seconds, front window, screen capture, toasts) used\n * `spawnSync`, which blocks the host's event loop for as long as the script\n * runs — 10s to 45s on a slow machine, and the GUI stalls with it. Same\n * arguments, same timeouts, same \"never throw, degrade instead\" contract, but\n * the loop stays free while the child runs.\n */\nimport { execFile } from 'node:child_process';\n\nexport interface PsResult {\n status: number;\n stdout: string;\n stderr: string;\n}\n\n/** Run `powershell.exe` and resolve with its exit code and streams. Never\n * rejects: a spawn failure and a non-zero exit both come back as `status !== 0`\n * so callers keep their existing \"probe failed\" paths. */\nexport function runPowerShell(args: string[], timeoutMs: number): Promise<PsResult> {\n return new Promise((resolve) => {\n const child = execFile(\n 'powershell.exe',\n ['-NoProfile', '-ExecutionPolicy', 'Bypass', ...args],\n { timeout: timeoutMs, encoding: 'utf8', windowsHide: true },\n (err, stdout, stderr) => {\n const code = (err as { code?: unknown } | null)?.code;\n const status = err ? (typeof code === 'number' ? code : 1) : 0;\n resolve({ status, stdout: String(stdout ?? ''), stderr: String(stderr ?? '') });\n },\n );\n child.on('error', () => { /* surfaced through the callback above */ });\n });\n}\n\n/** Convenience for the `-File <script>` shape used by every asset script. */\nexport function runPowerShellFile(script: string, args: string[], timeoutMs: number): Promise<PsResult> {\n return runPowerShell(['-File', script, ...args], timeoutMs);\n}\n","// Weekly report store + writer helpers (v1.8.0). Deliberately host-free:\r\n// orchestration (when to run, which agent, toast) lives in the orchestrator —\r\n// same split as profile/consolidate.ts. Reports are DPAPI-encrypted because a\r\n// report contains user-identifying content (profile adds, ledger texts) —\r\n// same划线标准 as profile/seeds (design doc §10.5).\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { atomicWriteJsonSync } from '../core/atomic-fs.js';\r\nimport { loadEncryptedText, saveEncryptedText } from '../vault/vault.js';\r\nimport type { PathGuard } from '../core/path-guard.js';\r\nimport { workspace } from '../core/paths.js';\r\nimport { appendAuditLine } from '../core/audit-log.js';\r\nimport type { WeeklyFacts } from './collect.js';\r\n\r\nexport const WEEKLY_INTERVAL_MS = 7 * 86_400_000;\r\n\r\nexport interface WeeklyReport {\r\n start: string;\r\n end: string;\r\n generatedAt: string;\r\n /** 'llm' = engine-room prose; 'template' = deterministic fallback. */\r\n source: 'llm' | 'template';\r\n text: string;\r\n}\r\n\r\nexport function weeklyDirPath(dataDir: string): string {\r\n return path.join(dataDir, 'weekly');\r\n}\r\n\r\nfunction stateFilePath(dataDir: string): string {\r\n return path.join(weeklyDirPath(dataDir), 'state.json');\r\n}\r\n\r\nfunction reportFileName(endIso: string): string {\r\n return `report-${endIso.slice(0, 10)}.json`;\r\n}\r\n\r\n/**\r\n * Last generation time from the weekly state file; 0 = never anchored.\r\n *\r\n * H-54: \"missing\" and \"present but unparseable\" are NOT the same thing. The\r\n * old code folded both into 0, so `ensureWeeklyAnchor` \"repaired\" a corrupt\r\n * file by stamping NOW — silently, and at the cost of pushing the next report\r\n * a full interval out. A corrupt anchor now keeps its file, writes an audit\r\n * line, and falls back to the file's mtime so the schedule keeps its place.\r\n */\r\nexport function lastWeeklyGeneratedAt(guard: PathGuard, dataDir: string): number {\r\n const file = stateFilePath(dataDir);\r\n let raw: string;\r\n try {\r\n raw = fs.readFileSync(guard.assert(file), 'utf8');\r\n } catch {\r\n return 0; // absent: a fresh install, legitimately never anchored\r\n }\r\n try {\r\n const parsed = JSON.parse(raw) as { lastGeneratedAt?: number };\r\n const n = Number(parsed.lastGeneratedAt);\r\n if (Number.isFinite(n) && n > 0) return n;\r\n } catch {\r\n /* unparseable — handled by the fallback below */\r\n }\r\n try {\r\n appendAuditLine(path.join(workspace().logsDir, 'heartbeat.jsonl'), {\r\n event: 'weekly_anchor_corrupt',\r\n file: path.basename(file),\r\n bytes: Buffer.byteLength(raw),\r\n });\r\n } catch {\r\n /* audit is diagnostic; never let it break the schedule */\r\n }\r\n try {\r\n return fs.statSync(file).mtimeMs;\r\n } catch {\r\n return 0;\r\n }\r\n}\r\n\r\n/** Due when the last report is ≥7 days old. A never-anchored store lands on\r\n * the install-time anchor (see ensureWeeklyAnchor), so a fresh install is NOT\r\n * due — the first report arrives one full interval after installation. */\r\nexport function weeklyDue(guard: PathGuard, dataDir: string, now = Date.now()): boolean {\r\n const last = lastWeeklyGeneratedAt(guard, dataDir);\r\n return now - last >= WEEKLY_INTERVAL_MS;\r\n}\r\n\r\n/** Stamp the install-time anchor for a store that has never generated a report\r\n * (v1.9.0). Returns true when it wrote — the caller audits that. Without this,\r\n * `lastGeneratedAt = 0` made the very first maintenance beat due, so a fresh\r\n * install produced an \"onboarding\" report immediately, contradicting the\r\n * release notes (\"first report after a week\"). */\r\nexport function ensureWeeklyAnchor(guard: PathGuard, dataDir: string, now = Date.now()): boolean {\r\n if (lastWeeklyGeneratedAt(guard, dataDir) > 0) return false;\r\n fs.mkdirSync(weeklyDirPath(dataDir), { recursive: true });\r\n atomicWriteJsonSync(stateFilePath(dataDir), { lastGeneratedAt: now });\r\n return true;\r\n}\r\n\r\n/** Persist a report (DPAPI) and stamp the state file. */\r\nexport function saveWeeklyReport(guard: PathGuard, dataDir: string, report: WeeklyReport): string {\r\n const dir = weeklyDirPath(dataDir);\r\n fs.mkdirSync(dir, { recursive: true });\r\n const file = path.join(dir, reportFileName(report.end));\r\n saveEncryptedText(guard, file, JSON.stringify(report, null, 1));\r\n atomicWriteJsonSync(stateFilePath(dataDir), { lastGeneratedAt: Date.parse(report.generatedAt) });\r\n return file;\r\n}\r\n\r\nexport interface WeeklyReportMeta {\r\n file: string;\r\n start: string;\r\n end: string;\r\n generatedAt: string;\r\n source: string;\r\n}\r\n\r\n/** Newest-first listing for the card; unparsable entries are skipped.\r\n * v2.0 (item 5): an optional `limit` shows only the newest N reports. */\r\nexport function listWeeklyReports(guard: PathGuard, dataDir: string, limit?: number): WeeklyReportMeta[] {\r\n const out: WeeklyReportMeta[] = [];\r\n try {\r\n for (const file of fs.readdirSync(weeklyDirPath(dataDir))) {\r\n if (!/^report-\\d{4}-\\d{2}-\\d{2}\\.json$/.test(file)) continue;\r\n const rep = readWeeklyReport(guard, dataDir, file);\r\n if (!rep) continue;\r\n out.push({ file, start: rep.start, end: rep.end, generatedAt: rep.generatedAt, source: rep.source });\r\n }\r\n } catch { /* no weekly dir yet */ }\r\n const sorted = out.sort((a, b) => (a.end < b.end ? 1 : -1));\r\n return limit !== undefined && limit > 0 ? sorted.slice(0, limit) : sorted;\r\n}\r\n\r\n/**\r\n * v2.0 (item 5): delete every report beyond the newest `keep`, so the card's\r\n * report buttons cannot crowd the panel and the store cannot grow unbounded.\r\n * `keep <= 0` disables pruning (and the policy schema forbids it anyway).\r\n * Returns the deleted file names so the caller can audit them; a report that\r\n * cannot be deleted is left in place (best effort — never break the beat).\r\n */\r\nexport function pruneWeeklyReports(guard: PathGuard, dataDir: string, keep: number): string[] {\r\n if (!Number.isFinite(keep) || keep <= 0) return [];\r\n const removed: string[] = [];\r\n for (const meta of listWeeklyReports(guard, dataDir).slice(keep)) {\r\n try {\r\n fs.rmSync(guard.assert(path.join(weeklyDirPath(dataDir), meta.file)));\r\n removed.push(meta.file);\r\n } catch { /* best effort */ }\r\n }\r\n return removed;\r\n}\r\n\r\nexport function readWeeklyReport(guard: PathGuard, dataDir: string, file: string): WeeklyReport | null {\r\n if (!/^report-\\d{4}-\\d{2}-\\d{2}\\.json$/.test(file)) return null; // guard.assert would reject ../ anyway\r\n try {\r\n const raw = loadEncryptedText(guard, path.join(weeklyDirPath(dataDir), file));\r\n if (!raw) return null;\r\n const rep = JSON.parse(raw) as WeeklyReport;\r\n if (typeof rep.text !== 'string') return null;\r\n return rep;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/** The engine-room prompt: neutral system narrator, fact-bound, ≤250 chars. */\r\nexport function buildWeeklyPrompt(facts: WeeklyFacts): string {\r\n return [\r\n '你是心跳插件的后台报告器。根据下面的本周事实清单,用中文写一份周报正文。',\r\n '规则:',\r\n '- 叙述者自称「心跳」——这是插件后台,不是任何会话里的 agent;绝不能以会话 agent 的身份或人格自称。',\r\n '- 只报告事实清单里有的内容;某一节没有数据就整节跳过,不要编造、不要凑数。',\r\n '- 语气平实中性,不用感叹号;篇幅不超过 250 字。',\r\n '- 待办积压那一节用商量的语气提一句,不催促。',\r\n '- 直接输出周报正文,不要输出解释、标题或 JSON。',\r\n '',\r\n '本周事实(JSON):',\r\n JSON.stringify(facts, null, 1),\r\n ].join('\\n');\r\n}\r\n\r\n/** Deterministic fallback: the facts, plainly stated. Used when the LLM turn\r\n * fails or comes back empty — a plain report beats no report. */\r\nexport function renderTemplateReport(facts: WeeklyFacts): string {\r\n const d = (iso: string): string => iso.slice(0, 10);\r\n const lines: string[] = [`本周(${d(facts.windowStart)} ~ ${d(facts.windowEnd)})`];\r\n lines.push(`表达 ${facts.spoken} 次,静默 ${facts.silent} 次。`);\r\n if (facts.silentTopReasons.length > 0) {\r\n lines.push(`静默主因:${facts.silentTopReasons.map((r) => `${r.reason}(${r.count})`).join('、')}。`);\r\n }\r\n if (facts.profileAdds.length > 0) {\r\n lines.push(`新认识 ${facts.profileAdds.length} 条:${facts.profileAdds.map((a) => a.content).join(';')}。`);\r\n }\r\n if (facts.ledger.added.length > 0 || facts.ledger.done.length > 0) {\r\n const parts: string[] = [];\r\n if (facts.ledger.added.length > 0) parts.push(`新增待办:${facts.ledger.added.join('、')}`);\r\n if (facts.ledger.done.length > 0) parts.push(`已解决:${facts.ledger.done.join('、')}`);\r\n lines.push(`${parts.join(';')}。`);\r\n }\r\n if (facts.ledger.stale.length > 0) {\r\n lines.push(`挂了很久:${facts.ledger.stale.map((s) => `${s.text}(${s.days} 天)`).join('、')}——要删掉还是继续挂着?`);\r\n }\r\n if (facts.seeds.added.length > 0) {\r\n lines.push(`素材池新增 ${facts.seeds.added.length} 条,已消费 ${facts.seeds.consumed.length} 条。`);\r\n }\r\n if (facts.seeds.waiting.length > 0) {\r\n lines.push(`还没聊过的素材:${facts.seeds.waiting.join('、')}。`);\r\n }\r\n if (facts.reports.total > 0) {\r\n const reasonPart = facts.reports.reasons.length > 0\r\n ? `(${facts.reports.reasons.map((r) => `${r.reason} ${r.count} 次`).join('、')})`\r\n : '';\r\n lines.push(`投递报账 ${facts.reports.total} 次:用素材 ${facts.reports.material}、说真心话 ${facts.reports.heartfelt}、没说话 ${facts.reports.silent}${reasonPart}。`);\r\n }\r\n // v1.9.0: unaccounted deliveries are reported even when no report landed at\r\n // all — a silent rise in \"no report\" is exactly what would otherwise hide.\r\n if (facts.reports.missing > 0) {\r\n lines.push(`另有 ${facts.reports.missing} 次投递没等到报账(这部分没算进偏好统计)。`);\r\n }\r\n if (facts.peakHours.length > 0) {\r\n lines.push(`活跃高峰:${facts.peakHours.join('、')}。`);\r\n }\r\n return lines.join('\\n');\r\n}\r\n"],"mappings":"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","names":["path","DAY_MS","path","path","path","path","path","fs","path","path","fs"]}
@@ -132,6 +132,9 @@ function assertPolicy(input) {
132
132
  if (typeof ob.perBeat !== "number" || ob.perBeat < 1 || ob.perBeat > 100) fail("observe.perBeat must be in [1, 100]");
133
133
  if (!isPlainObject(p.weekly)) fail("weekly missing");
134
134
  if (typeof p.weekly.enabled !== "boolean") fail("weekly.enabled must be boolean");
135
+ if (typeof p.weekly.keepReports !== "number" || p.weekly.keepReports < 1 || p.weekly.keepReports > 52) {
136
+ fail("weekly.keepReports must be a number in [1, 52]");
137
+ }
135
138
  const r = p.retention;
136
139
  if (!isPlainObject(r)) fail("retention missing");
137
140
  if (typeof r.envPulseHours !== "number" || typeof r.decisionLogDays !== "number") fail("retention fields missing");
@@ -330,4 +333,4 @@ export {
330
333
  describeInstall,
331
334
  presetStatus
332
335
  };
333
- //# sourceMappingURL=chunk-ZLV4NMFX.js.map
336
+ //# sourceMappingURL=chunk-XYYEJK3W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/path-guard.ts","../src/config/schema.ts","../src/config/load.ts","../src/core/preset-install.ts"],"sourcesContent":["// Path whitelist guard (requirement 8 / design doc §10.2).\n//\n// Every fs write/delete must have its target canonicalized first, then be\n// checked against the canonical workspace prefix with a separator boundary.\n// A raw startsWith check is bypassable via \"..\", symlinks/junctions, and\n// case variants; canonical realpath closes all of those on Windows.\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport class PathOutsideWorkspaceError extends Error {\n constructor(target: string, workspace: string) {\n super(`path outside workspace: \"${target}\" (workspace: \"${workspace}\")`);\n this.name = 'PathOutsideWorkspaceError';\n }\n}\n\n/**\n * Resolve to an absolute canonical path. For targets that do not exist yet,\n * realpath the deepest existing ancestor and re-append the virtual tail, so\n * planned files under the workspace validate while `..` escapes still resolve\n * through the real filesystem.\n */\nexport function canonicalize(target: string): string {\n const abs = path.resolve(target);\n try {\n return fs.realpathSync(abs);\n } catch {\n // Walk up from the missing leaf, recording each missing component, until\n // an existing ancestor is found; re-append the missing tail afterwards.\n const tail: string[] = [];\n let dir = abs;\n for (;;) {\n const base = path.basename(dir);\n const parent = path.dirname(dir);\n if (parent === dir) {\n throw new Error(`cannot canonicalize \"${target}\": no existing ancestor`);\n }\n tail.push(base);\n dir = parent;\n try {\n const realDir = fs.realpathSync(dir);\n return path.join(realDir, ...tail.reverse());\n } catch {\n continue;\n }\n }\n }\n}\n\n/**\n * True when `targetCanon` equals the workspace dir or lies under it.\n * Comparison is case-insensitive (Windows filesystems) and requires a\n * separator boundary so `D:\\ws-data-evil` does not match workspace `D:\\ws-data`.\n */\nexport function isInsideWorkspace(workspaceCanon: string, targetCanon: string): boolean {\n const norm = (p: string) => {\n let n = path.normalize(p).toLowerCase();\n if (!n.endsWith(path.sep)) n += path.sep;\n return n;\n };\n const w = norm(workspaceCanon);\n const t = norm(targetCanon);\n return t === w || t.startsWith(w);\n}\n\nexport interface PathGuard {\n /** Canonical workspace boundary. */\n readonly workspace: string;\n /** Canonicalize then validate; returns the canonical path or throws. */\n assert(target: string): string;\n /** Canonicalize then validate; returns null instead of throwing. */\n check(target: string): string | null;\n}\n\nexport function createPathGuard(workspaceDir: string): PathGuard {\n const workspace = canonicalize(workspaceDir);\n const guard: PathGuard = {\n workspace,\n check(target: string): string | null {\n const canon = canonicalize(target);\n return isInsideWorkspace(workspace, canon) ? canon : null;\n },\n assert(target: string): string {\n const canon = guard.check(target);\n if (canon === null) throw new PathOutsideWorkspaceError(target, workspace);\n return canon;\n },\n };\n return guard;\n}\n","// Policy shape + runtime validation. Factory defaults live in config/policy.json\r\n// (read-only); the user layer in data/settings/policy.json overrides via deep\r\n// merge (design doc §13, D3).\r\n\r\nexport interface QuietHours {\r\n start: string; // \"HH:MM\"\r\n end: string; // \"HH:MM\"\r\n}\r\n\r\nexport interface BrowseWindow {\r\n start: string;\r\n end: string;\r\n}\r\n\r\nexport interface Policy {\r\n heartbeat: { intervalMin: number; idleMode: boolean; archiveRotatedHome: boolean };\r\n gate: {\r\n maxDailySend: number;\r\n cooldownMinutes: number;\r\n quietHours: QuietHours;\r\n };\r\n browse: {\r\n windows: BrowseWindow[];\r\n minIntervalHours: number;\r\n maxSeedsPerVisit: number;\r\n };\r\n seeds: {\r\n maxActive: number;\r\n ttlDays: { news: number; fandom: number; scene: number; promise: number };\r\n coldBenchDays: number;\r\n /** Archive-area cap (v1.9.0): gc trims archived seeds beyond this, oldest first. */\r\n archiveCap: number;\r\n retireAfterUsed: number;\r\n scoreWeights: { freshness: number; unused: number; confidence: number };\r\n };\r\n profile: {\r\n consolidation: { minIntervalHours: number; inboxBacklog: number };\r\n partitionCap: number;\r\n maxOpsPerRun: number;\r\n confidenceCap: { chat: number; screen: number; browse: number };\r\n volatileDays: number;\r\n stableLowActivityDays: number;\r\n psyEnabled: boolean;\r\n };\r\n /** Observation depth (v1.8.0): how much of each user message enters the inbox. */\r\n observe: { maxChars: number; perBeat: number };\r\n weekly: {\n enabled: boolean;\n /** v2.0 (item 5): how many past reports the store keeps and the card lists. */\n keepReports: number;\n };\r\n retention: { envPulseHours: number; decisionLogDays: number };\r\n}\r\n\r\nconst HHMM = /^([01]\\d|2[0-3]):[0-5]\\d$/;\r\n\r\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\r\n return typeof v === 'object' && v !== null && !Array.isArray(v);\r\n}\r\n\r\nfunction fail(msg: string): never {\r\n throw new Error(`policy: ${msg}`);\r\n}\r\n\r\nexport function assertPolicy(input: unknown): asserts input is Policy {\r\n if (!isPlainObject(input)) fail('root must be an object');\r\n const p = input;\r\n const hb = p.heartbeat;\r\n if (!isPlainObject(hb)) fail('heartbeat missing');\r\n if (typeof hb.intervalMin !== 'number' || hb.intervalMin < 1 || hb.intervalMin > 1440) {\r\n fail('heartbeat.intervalMin must be a number in [1, 1440]');\r\n }\r\n if (typeof hb.idleMode !== 'boolean') fail('heartbeat.idleMode must be boolean');\r\n if (typeof hb.archiveRotatedHome !== 'boolean') fail('heartbeat.archiveRotatedHome must be boolean');\r\n const g = p.gate;\r\n if (!isPlainObject(g)) fail('gate missing');\r\n if (typeof g.maxDailySend !== 'number' || g.maxDailySend < 0) fail('gate.maxDailySend must be >= 0');\r\n if (typeof g.cooldownMinutes !== 'number' || g.cooldownMinutes < 0) fail('gate.cooldownMinutes must be >= 0');\r\n const qh = g.quietHours;\r\n if (!isPlainObject(qh)) fail('gate.quietHours missing');\r\n if (typeof qh.start !== 'string' || !HHMM.test(qh.start) || typeof qh.end !== 'string' || !HHMM.test(qh.end)) {\r\n fail('gate.quietHours must be {start:\"HH:MM\", end:\"HH:MM\"}');\r\n }\r\n const b = p.browse;\r\n if (!isPlainObject(b)) fail('browse missing');\r\n if (!Array.isArray(b.windows) || b.windows.length === 0) fail('browse.windows must be a non-empty array');\r\n for (const w of b.windows) {\r\n if (!isPlainObject(w)) fail('browse.windows entries must be objects');\r\n if (typeof w.start !== 'string' || !HHMM.test(w.start) || typeof w.end !== 'string' || !HHMM.test(w.end)) {\r\n fail('browse.windows entries must be {start:\"HH:MM\", end:\"HH:MM\"}');\r\n }\r\n }\r\n if (typeof b.minIntervalHours !== 'number' || b.minIntervalHours <= 0) fail('browse.minIntervalHours must be > 0');\r\n if (typeof b.maxSeedsPerVisit !== 'number' || b.maxSeedsPerVisit < 1) fail('browse.maxSeedsPerVisit must be >= 1');\r\n const s = p.seeds;\r\n if (!isPlainObject(s)) fail('seeds missing');\r\n if (typeof s.maxActive !== 'number' || s.maxActive < 1) fail('seeds.maxActive must be >= 1');\r\n if (!isPlainObject(s.ttlDays)) fail('seeds.ttlDays missing');\r\n for (const k of ['news', 'fandom', 'scene', 'promise'] as const) {\r\n if (typeof s.ttlDays[k] !== 'number') fail(`seeds.ttlDays.${k} missing`);\r\n }\r\n if (typeof s.coldBenchDays !== 'number') fail('seeds.coldBenchDays missing');\r\n if (typeof s.archiveCap !== 'number' || s.archiveCap < 1) fail('seeds.archiveCap must be >= 1');\r\n if (typeof s.retireAfterUsed !== 'number' || s.retireAfterUsed < 1) fail('seeds.retireAfterUsed must be >= 1');\r\n if (!isPlainObject(s.scoreWeights)) fail('seeds.scoreWeights missing');\r\n const pr = p.profile;\r\n if (!isPlainObject(pr)) fail('profile missing');\r\n const c = pr.consolidation;\r\n if (!isPlainObject(c)) fail('profile.consolidation missing');\r\n if (typeof c.minIntervalHours !== 'number' || typeof c.inboxBacklog !== 'number') fail('profile.consolidation fields missing');\r\n if (typeof pr.partitionCap !== 'number' || pr.partitionCap < 1) fail('profile.partitionCap must be >= 1');\r\n if (typeof pr.maxOpsPerRun !== 'number' || pr.maxOpsPerRun < 1) fail('profile.maxOpsPerRun must be >= 1');\r\n const cc = pr.confidenceCap;\r\n if (!isPlainObject(cc)) fail('profile.confidenceCap missing');\r\n if (typeof cc.chat !== 'number' || typeof cc.screen !== 'number' || typeof cc.browse !== 'number') {\r\n fail('profile.confidenceCap fields missing');\r\n }\r\n if (typeof pr.volatileDays !== 'number' || pr.volatileDays < 1) fail('profile.volatileDays must be >= 1');\r\n if (typeof pr.stableLowActivityDays !== 'number' || pr.stableLowActivityDays < 1) fail('profile.stableLowActivityDays must be >= 1');\r\n if (typeof pr.psyEnabled !== 'boolean') fail('profile.psyEnabled must be boolean');\r\n const ob = p.observe;\r\n if (!isPlainObject(ob)) fail('observe missing');\r\n if (typeof ob.maxChars !== 'number' || ob.maxChars < 20 || ob.maxChars > 2000) fail('observe.maxChars must be in [20, 2000]');\r\n if (typeof ob.perBeat !== 'number' || ob.perBeat < 1 || ob.perBeat > 100) fail('observe.perBeat must be in [1, 100]');\r\n if (!isPlainObject(p.weekly)) fail('weekly missing');\r\n if (typeof p.weekly.enabled !== 'boolean') fail('weekly.enabled must be boolean');\n if (typeof p.weekly.keepReports !== 'number' || p.weekly.keepReports < 1 || p.weekly.keepReports > 52) {\n fail('weekly.keepReports must be a number in [1, 52]');\n }\r\n const r = p.retention;\r\n if (!isPlainObject(r)) fail('retention missing');\r\n if (typeof r.envPulseHours !== 'number' || typeof r.decisionLogDays !== 'number') fail('retention fields missing');\r\n}\r\n\r\n/** Recursive merge: user values win; objects merge, arrays and scalars replace. */\r\nexport function deepMerge<T>(base: T, override: unknown): T {\r\n if (!isPlainObject(base) || !isPlainObject(override)) {\r\n return (override === undefined ? base : (override as T));\r\n }\r\n const out: Record<string, unknown> = { ...base };\r\n for (const [k, v] of Object.entries(override)) {\r\n out[k] = v === undefined ? (base as Record<string, unknown>)[k] : deepMerge((base as Record<string, unknown>)[k], v);\r\n }\r\n return out as T;\r\n}","// Two-layer policy loading (D3): factory defaults (config/policy.json, read-only)\n// + user layer (data/settings/policy.json). Missing user layer is normal; an\n// invalid factory file or user layer is fail-closed (throw at startup).\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { assertPolicy, deepMerge, type Policy } from './schema.js';\nimport { atomicWriteFileSync } from '../core/atomic-fs.js';\n\nexport const USER_POLICY_FILE = 'policy.json';\n\nexport function loadPolicy(guard: PathGuard, configDir: string, settingsDir: string): Policy {\n const factoryPath = path.join(configDir, 'policy.json');\n let factoryRaw: unknown;\n try {\n factoryRaw = JSON.parse(fs.readFileSync(factoryPath, 'utf8'));\n } catch (e) {\n throw new Error(`factory policy unreadable at ${factoryPath}: ${String(e)}`);\n }\n assertPolicy(factoryRaw);\n\n const userPath = guard.assert(path.join(settingsDir, USER_POLICY_FILE));\n let merged: Policy = factoryRaw;\n if (fs.existsSync(userPath)) {\n try {\n const userRaw: unknown = JSON.parse(fs.readFileSync(userPath, 'utf8'));\n merged = deepMerge(factoryRaw, userRaw);\n } catch (e) {\n throw new Error(`user policy layer unparseable at ${userPath}: ${String(e)}`);\n }\n }\n assertPolicy(merged);\n return merged;\n}\n\n/**\n * Merge `patch` into the user policy layer (data/settings/policy.json) without\n * touching the factory file. Used by the settings card for policy-backed\n * toggles (psyEnabled, v1.7.0). No full-Policy validation here: the layer is\n * merged over the factory and validated together at the next loadPolicy.\n */\nexport function updateUserPolicy(guard: PathGuard, settingsDir: string, patch: Record<string, unknown>): Record<string, unknown> {\n const userPath = guard.assert(path.join(settingsDir, USER_POLICY_FILE));\n let user: Record<string, unknown> = {};\n try {\n user = JSON.parse(fs.readFileSync(userPath, 'utf8')) as Record<string, unknown>;\n if (!user || typeof user !== 'object' || Array.isArray(user)) user = {};\n } catch {\n user = {}; // fresh layer\n }\n const merged = deepMerge(user, patch) as Record<string, unknown>;\n fs.mkdirSync(path.dirname(userPath), { recursive: true });\n atomicWriteFileSync(userPath, JSON.stringify(merged, null, 2) + '\\n');\n return merged;\n}\n","// Bundled agent-preset installation (contract C13).\n//\n// Why this exists: the heartbeat agent is created by the host `agents` service,\n// which does NOT go through the session-start preset picker. A bare agent joins\n// no preset, and dsh-agent-presets states the consequence verbatim — \"its tools,\n// prompt sections, and skill catalog resolve against the empty global layer\" —\n// so it cannot even see `web_search`. The preset is therefore not optional, and\n// asking the operator to hand-copy two YAML files was the most error-prone step\n// of setup. The plugin now materialises its own bundled template in the roster's\n// USER root on first run.\n//\n// Two properties keep that safe:\n// 1. An existing preset is NEVER overwritten — the composition file belongs to\n// whoever edited it. Only a missing (or ghost) directory is filled in.\n// 2. The target comes from the roster's OWN roots (`agentPresets.roots`,\n// trust === \"user\") rather than a guessed `~/.dsh`, so `$DSH_HOME` and a\n// configured home are honoured without re-deriving them here.\n//\n// Timing: dsh-agent-presets re-scans the filesystem on every read\n// (`list()` -> `discoverPresets` -> `scanRoot`, no cache), so a directory\n// created here is visible to the very next `mount()` — no restart in between.\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport const COMPOSITION_FILE = 'agent.cordis.yml';\nexport const METADATA_FILE = 'preset.yml';\n/** The template this package ships, in `assets/presets/<id>/`. */\nexport const BUNDLED_PRESET_ID = 'heartbeat';\n\n/** One entry of the roster's root list (`AgentPresets.roots`). */\nexport interface PresetRootLike {\n path?: string;\n trust?: string;\n}\n\nexport type PresetInstallAction =\n | 'exists'\n | 'created'\n | 'repaired'\n | 'restored'\n | 'skipped-disabled'\n | 'skipped-custom-id'\n | 'skipped-no-root'\n | 'error';\n\nexport interface PresetInstallResult {\n action: PresetInstallAction;\n id: string;\n /** Where the preset lives (or would live). */\n dir?: string;\n /** Where the bundled template was read from. */\n bundledDir?: string;\n detail?: string;\n}\n\n/**\n * Locate the bundled template directory by walking up from a module URL.\n * Works from both entry points (`dist/index.js` and `dist/cli/index.js`) and\n * from the pnpm copy of an installed package.\n */\nexport function bundledPresetDir(\n moduleUrl: string,\n id: string = BUNDLED_PRESET_ID,\n): string | undefined {\n let dir: string;\n try {\n dir = path.dirname(fileURLToPath(moduleUrl));\n } catch {\n return undefined;\n }\n for (let depth = 0; depth < 5; depth += 1) {\n const candidate = path.join(dir, 'assets', 'presets', id);\n if (fs.existsSync(path.join(candidate, COMPOSITION_FILE))) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\n/** The roster's user-trust root, which is where locally authored presets live. */\nexport function userPresetRoot(roots?: readonly PresetRootLike[]): string | undefined {\n const found = roots?.find(\n (root) => root?.trust === 'user' && typeof root.path === 'string' && root.path.length > 0,\n );\n return found?.path === undefined ? undefined : path.resolve(found.path);\n}\n\n/**\n * `<dshHome>/.agent-presets` derived from the documented precedence\n * (`$DSH_HOME`, then `~/.dsh`). Only used where no roster is available — the\n * plugin itself prefers {@link userPresetRoot}.\n */\nexport function conventionalUserPresetRoot(\n env: NodeJS.ProcessEnv = process.env,\n home: string = os.homedir(),\n): string {\n const override = env.DSH_HOME?.trim();\n const root = override && override.length > 0 ? override : path.join(home, '.dsh');\n return path.join(root, '.agent-presets');\n}\n\nexport interface InstallPresetOptions {\n /** `import.meta.url` of the calling entry point. */\n moduleUrl: string;\n /** Preset id the heartbeat agent will join (config `agentPreset`). */\n id?: string;\n /** User-trust root reported by the roster. */\n root?: string;\n /** True when the roster answered — then an absent user root is a fact, not a guess. */\n rosterKnown?: boolean;\n /** `false` disables the install (config `installPreset`). */\n enabled?: boolean;\n /** Overwrite an existing composition file with the bundled one. */\n force?: boolean;\n}\n\n/**\n * Materialise the bundled preset in the roster's user root when it is absent.\n * Never destructive: an existing composition file is kept unless `force`.\n */\nexport function installBundledPreset(options: InstallPresetOptions): PresetInstallResult {\n const id = options.id && options.id.length > 0 ? options.id : BUNDLED_PRESET_ID;\n if (options.enabled === false) {\n return { action: 'skipped-disabled', id, detail: 'installPreset=false' };\n }\n const bundledDir = bundledPresetDir(options.moduleUrl, BUNDLED_PRESET_ID);\n if (id !== BUNDLED_PRESET_ID) {\n return {\n action: 'skipped-custom-id',\n id,\n ...(bundledDir === undefined ? {} : { bundledDir }),\n detail: `only \"${BUNDLED_PRESET_ID}\" ships with the plugin; \"${id}\" is yours to provide`,\n };\n }\n if (bundledDir === undefined) {\n return {\n action: 'error',\n id,\n detail: 'bundled template not found next to the plugin (assets/presets/heartbeat)',\n };\n }\n const root =\n options.root ?? (options.rosterKnown ? undefined : conventionalUserPresetRoot());\n if (root === undefined) {\n return {\n action: 'skipped-no-root',\n id,\n bundledDir,\n detail: 'the roster mounts no user preset root (includeUserRoot=false)',\n };\n }\n const dir = path.join(root, id);\n const composition = path.join(dir, COMPOSITION_FILE);\n try {\n if (fs.existsSync(composition)) {\n if (options.force !== true) {\n const drifted = !sameBytes(composition, path.join(bundledDir, COMPOSITION_FILE));\n return {\n action: 'exists',\n id,\n dir,\n bundledDir,\n detail: drifted ? 'kept as-is (differs from the bundled template)' : 'kept as-is',\n };\n }\n fs.copyFileSync(path.join(bundledDir, COMPOSITION_FILE), composition);\n return {\n action: 'restored',\n id,\n dir,\n bundledDir,\n detail: 'composition replaced from the bundled template',\n };\n }\n const existed = fs.existsSync(dir);\n fs.mkdirSync(dir, { recursive: true });\n fs.copyFileSync(path.join(bundledDir, COMPOSITION_FILE), composition);\n const metadata = path.join(dir, METADATA_FILE);\n // A directory with a composition but no metadata is legal; only fill a void.\n if (!fs.existsSync(metadata)) fs.copyFileSync(path.join(bundledDir, METADATA_FILE), metadata);\n return {\n action: existed ? 'repaired' : 'created',\n id,\n dir,\n bundledDir,\n detail: existed\n ? 'directory existed without a composition file (it occupied the id as a broken row)'\n : undefined,\n };\n } catch (error) {\n return { action: 'error', id, dir, bundledDir, detail: String(error).slice(0, 200) };\n }\n}\n\n/** One line suitable for the audit log and `ctx.logger`. */\nexport function describeInstall(result: PresetInstallResult): string {\n const where = result.dir === undefined ? '' : ` (${result.dir})`;\n const why = result.detail === undefined ? '' : ` — ${result.detail}`;\n return `preset ${result.id} ${result.action}${where}${why}`;\n}\n\nexport interface PresetStatus {\n id: string;\n dir: string;\n bundledDir?: string;\n installed: boolean;\n compositionMatches: boolean;\n metadataMatches: boolean;\n}\n\n/** Read-only inspection for the CLI (`preset status`). */\nexport function presetStatus(\n moduleUrl: string,\n id: string = BUNDLED_PRESET_ID,\n root: string = conventionalUserPresetRoot(),\n): PresetStatus {\n const dir = path.join(root, id);\n const bundledDir = bundledPresetDir(moduleUrl, id);\n const installed = fs.existsSync(path.join(dir, COMPOSITION_FILE));\n return {\n id,\n dir,\n ...(bundledDir === undefined ? {} : { bundledDir }),\n installed,\n compositionMatches:\n installed && bundledDir !== undefined && sameBytes(path.join(dir, COMPOSITION_FILE), path.join(bundledDir, COMPOSITION_FILE)),\n metadataMatches:\n bundledDir !== undefined &&\n fs.existsSync(path.join(dir, METADATA_FILE)) &&\n sameBytes(path.join(dir, METADATA_FILE), path.join(bundledDir, METADATA_FILE)),\n };\n}\n\nfunction sameBytes(left: string, right: string): boolean {\n try {\n return fs.readFileSync(left).equals(fs.readFileSync(right));\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;AAOA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,IAAM,4BAAN,cAAwC,MAAM;AAAA,EACnD,YAAY,QAAgB,WAAmB;AAC7C,UAAM,4BAA4B,MAAM,kBAAkB,SAAS,IAAI;AACvE,SAAK,OAAO;AAAA,EACd;AACF;AAQO,SAAS,aAAa,QAAwB;AACnD,QAAM,MAAM,KAAK,QAAQ,MAAM;AAC/B,MAAI;AACF,WAAO,GAAG,aAAa,GAAG;AAAA,EAC5B,QAAQ;AAGN,UAAM,OAAiB,CAAC;AACxB,QAAI,MAAM;AACV,eAAS;AACP,YAAM,OAAO,KAAK,SAAS,GAAG;AAC9B,YAAM,SAAS,KAAK,QAAQ,GAAG;AAC/B,UAAI,WAAW,KAAK;AAClB,cAAM,IAAI,MAAM,wBAAwB,MAAM,yBAAyB;AAAA,MACzE;AACA,WAAK,KAAK,IAAI;AACd,YAAM;AACN,UAAI;AACF,cAAM,UAAU,GAAG,aAAa,GAAG;AACnC,eAAO,KAAK,KAAK,SAAS,GAAG,KAAK,QAAQ,CAAC;AAAA,MAC7C,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAOO,SAAS,kBAAkB,gBAAwB,aAA8B;AACtF,QAAM,OAAO,CAAC,MAAc;AAC1B,QAAI,IAAI,KAAK,UAAU,CAAC,EAAE,YAAY;AACtC,QAAI,CAAC,EAAE,SAAS,KAAK,GAAG,EAAG,MAAK,KAAK;AACrC,WAAO;AAAA,EACT;AACA,QAAM,IAAI,KAAK,cAAc;AAC7B,QAAM,IAAI,KAAK,WAAW;AAC1B,SAAO,MAAM,KAAK,EAAE,WAAW,CAAC;AAClC;AAWO,SAAS,gBAAgB,cAAiC;AAC/D,QAAM,YAAY,aAAa,YAAY;AAC3C,QAAM,QAAmB;AAAA,IACvB;AAAA,IACA,MAAM,QAA+B;AACnC,YAAM,QAAQ,aAAa,MAAM;AACjC,aAAO,kBAAkB,WAAW,KAAK,IAAI,QAAQ;AAAA,IACvD;AAAA,IACA,OAAO,QAAwB;AAC7B,YAAM,QAAQ,MAAM,MAAM,MAAM;AAChC,UAAI,UAAU,KAAM,OAAM,IAAI,0BAA0B,QAAQ,SAAS;AACzE,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ACpCA,IAAM,OAAO;AAEb,SAAS,cAAc,GAA0C;AAC/D,SAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AAChE;AAEA,SAAS,KAAK,KAAoB;AAChC,QAAM,IAAI,MAAM,WAAW,GAAG,EAAE;AAClC;AAEO,SAAS,aAAa,OAAyC;AACpE,MAAI,CAAC,cAAc,KAAK,EAAG,MAAK,wBAAwB;AACxD,QAAM,IAAI;AACV,QAAM,KAAK,EAAE;AACb,MAAI,CAAC,cAAc,EAAE,EAAG,MAAK,mBAAmB;AAChD,MAAI,OAAO,GAAG,gBAAgB,YAAY,GAAG,cAAc,KAAK,GAAG,cAAc,MAAM;AACrF,SAAK,qDAAqD;AAAA,EAC5D;AACA,MAAI,OAAO,GAAG,aAAa,UAAW,MAAK,oCAAoC;AAC/E,MAAI,OAAO,GAAG,uBAAuB,UAAW,MAAK,8CAA8C;AACnG,QAAM,IAAI,EAAE;AACZ,MAAI,CAAC,cAAc,CAAC,EAAG,MAAK,cAAc;AAC1C,MAAI,OAAO,EAAE,iBAAiB,YAAY,EAAE,eAAe,EAAG,MAAK,gCAAgC;AACnG,MAAI,OAAO,EAAE,oBAAoB,YAAY,EAAE,kBAAkB,EAAG,MAAK,mCAAmC;AAC5G,QAAM,KAAK,EAAE;AACb,MAAI,CAAC,cAAc,EAAE,EAAG,MAAK,yBAAyB;AACtD,MAAI,OAAO,GAAG,UAAU,YAAY,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,OAAO,GAAG,QAAQ,YAAY,CAAC,KAAK,KAAK,GAAG,GAAG,GAAG;AAC5G,SAAK,sDAAsD;AAAA,EAC7D;AACA,QAAM,IAAI,EAAE;AACZ,MAAI,CAAC,cAAc,CAAC,EAAG,MAAK,gBAAgB;AAC5C,MAAI,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK,EAAE,QAAQ,WAAW,EAAG,MAAK,0CAA0C;AACxG,aAAW,KAAK,EAAE,SAAS;AACzB,QAAI,CAAC,cAAc,CAAC,EAAG,MAAK,wCAAwC;AACpE,QAAI,OAAO,EAAE,UAAU,YAAY,CAAC,KAAK,KAAK,EAAE,KAAK,KAAK,OAAO,EAAE,QAAQ,YAAY,CAAC,KAAK,KAAK,EAAE,GAAG,GAAG;AACxG,WAAK,6DAA6D;AAAA,IACpE;AAAA,EACF;AACA,MAAI,OAAO,EAAE,qBAAqB,YAAY,EAAE,oBAAoB,EAAG,MAAK,qCAAqC;AACjH,MAAI,OAAO,EAAE,qBAAqB,YAAY,EAAE,mBAAmB,EAAG,MAAK,sCAAsC;AACjH,QAAM,IAAI,EAAE;AACZ,MAAI,CAAC,cAAc,CAAC,EAAG,MAAK,eAAe;AAC3C,MAAI,OAAO,EAAE,cAAc,YAAY,EAAE,YAAY,EAAG,MAAK,8BAA8B;AAC3F,MAAI,CAAC,cAAc,EAAE,OAAO,EAAG,MAAK,uBAAuB;AAC3D,aAAW,KAAK,CAAC,QAAQ,UAAU,SAAS,SAAS,GAAY;AAC/D,QAAI,OAAO,EAAE,QAAQ,CAAC,MAAM,SAAU,MAAK,iBAAiB,CAAC,UAAU;AAAA,EACzE;AACA,MAAI,OAAO,EAAE,kBAAkB,SAAU,MAAK,6BAA6B;AAC3E,MAAI,OAAO,EAAE,eAAe,YAAY,EAAE,aAAa,EAAG,MAAK,+BAA+B;AAC9F,MAAI,OAAO,EAAE,oBAAoB,YAAY,EAAE,kBAAkB,EAAG,MAAK,oCAAoC;AAC7G,MAAI,CAAC,cAAc,EAAE,YAAY,EAAG,MAAK,4BAA4B;AACrE,QAAM,KAAK,EAAE;AACb,MAAI,CAAC,cAAc,EAAE,EAAG,MAAK,iBAAiB;AAC9C,QAAM,IAAI,GAAG;AACb,MAAI,CAAC,cAAc,CAAC,EAAG,MAAK,+BAA+B;AAC3D,MAAI,OAAO,EAAE,qBAAqB,YAAY,OAAO,EAAE,iBAAiB,SAAU,MAAK,sCAAsC;AAC7H,MAAI,OAAO,GAAG,iBAAiB,YAAY,GAAG,eAAe,EAAG,MAAK,mCAAmC;AACxG,MAAI,OAAO,GAAG,iBAAiB,YAAY,GAAG,eAAe,EAAG,MAAK,mCAAmC;AACxG,QAAM,KAAK,GAAG;AACd,MAAI,CAAC,cAAc,EAAE,EAAG,MAAK,+BAA+B;AAC5D,MAAI,OAAO,GAAG,SAAS,YAAY,OAAO,GAAG,WAAW,YAAY,OAAO,GAAG,WAAW,UAAU;AACjG,SAAK,sCAAsC;AAAA,EAC7C;AACA,MAAI,OAAO,GAAG,iBAAiB,YAAY,GAAG,eAAe,EAAG,MAAK,mCAAmC;AACxG,MAAI,OAAO,GAAG,0BAA0B,YAAY,GAAG,wBAAwB,EAAG,MAAK,4CAA4C;AACnI,MAAI,OAAO,GAAG,eAAe,UAAW,MAAK,oCAAoC;AACjF,QAAM,KAAK,EAAE;AACb,MAAI,CAAC,cAAc,EAAE,EAAG,MAAK,iBAAiB;AAC9C,MAAI,OAAO,GAAG,aAAa,YAAY,GAAG,WAAW,MAAM,GAAG,WAAW,IAAM,MAAK,wCAAwC;AAC5H,MAAI,OAAO,GAAG,YAAY,YAAY,GAAG,UAAU,KAAK,GAAG,UAAU,IAAK,MAAK,qCAAqC;AACpH,MAAI,CAAC,cAAc,EAAE,MAAM,EAAG,MAAK,gBAAgB;AACnD,MAAI,OAAO,EAAE,OAAO,YAAY,UAAW,MAAK,gCAAgC;AAChF,MAAI,OAAO,EAAE,OAAO,gBAAgB,YAAY,EAAE,OAAO,cAAc,KAAK,EAAE,OAAO,cAAc,IAAI;AACrG,SAAK,gDAAgD;AAAA,EACvD;AACA,QAAM,IAAI,EAAE;AACZ,MAAI,CAAC,cAAc,CAAC,EAAG,MAAK,mBAAmB;AAC/C,MAAI,OAAO,EAAE,kBAAkB,YAAY,OAAO,EAAE,oBAAoB,SAAU,MAAK,0BAA0B;AACnH;AAGO,SAAS,UAAa,MAAS,UAAsB;AAC1D,MAAI,CAAC,cAAc,IAAI,KAAK,CAAC,cAAc,QAAQ,GAAG;AACpD,WAAQ,aAAa,SAAY,OAAQ;AAAA,EAC3C;AACA,QAAM,MAA+B,EAAE,GAAG,KAAK;AAC/C,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC7C,QAAI,CAAC,IAAI,MAAM,SAAa,KAAiC,CAAC,IAAI,UAAW,KAAiC,CAAC,GAAG,CAAC;AAAA,EACrH;AACA,SAAO;AACT;;;AC5IA,OAAOA,SAAQ;AACf,OAAOC,WAAU;AAKV,IAAM,mBAAmB;AAEzB,SAAS,WAAW,OAAkB,WAAmB,aAA6B;AAC3F,QAAM,cAAcC,MAAK,KAAK,WAAW,aAAa;AACtD,MAAI;AACJ,MAAI;AACF,iBAAa,KAAK,MAAMC,IAAG,aAAa,aAAa,MAAM,CAAC;AAAA,EAC9D,SAAS,GAAG;AACV,UAAM,IAAI,MAAM,gCAAgC,WAAW,KAAK,OAAO,CAAC,CAAC,EAAE;AAAA,EAC7E;AACA,eAAa,UAAU;AAEvB,QAAM,WAAW,MAAM,OAAOD,MAAK,KAAK,aAAa,gBAAgB,CAAC;AACtE,MAAI,SAAiB;AACrB,MAAIC,IAAG,WAAW,QAAQ,GAAG;AAC3B,QAAI;AACF,YAAM,UAAmB,KAAK,MAAMA,IAAG,aAAa,UAAU,MAAM,CAAC;AACrE,eAAS,UAAU,YAAY,OAAO;AAAA,IACxC,SAAS,GAAG;AACV,YAAM,IAAI,MAAM,oCAAoC,QAAQ,KAAK,OAAO,CAAC,CAAC,EAAE;AAAA,IAC9E;AAAA,EACF;AACA,eAAa,MAAM;AACnB,SAAO;AACT;AAQO,SAAS,iBAAiB,OAAkB,aAAqB,OAAyD;AAC/H,QAAM,WAAW,MAAM,OAAOD,MAAK,KAAK,aAAa,gBAAgB,CAAC;AACtE,MAAI,OAAgC,CAAC;AACrC,MAAI;AACF,WAAO,KAAK,MAAMC,IAAG,aAAa,UAAU,MAAM,CAAC;AACnD,QAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,EAAG,QAAO,CAAC;AAAA,EACxE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAAS,UAAU,MAAM,KAAK;AACpC,EAAAA,IAAG,UAAUD,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,sBAAoB,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AACpE,SAAO;AACT;;;ACjCA,OAAOE,SAAQ;AACf,OAAO,QAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAEvB,IAAM,mBAAmB;AACzB,IAAM,gBAAgB;AAEtB,IAAM,oBAAoB;AAiC1B,SAAS,iBACd,WACA,KAAa,mBACO;AACpB,MAAI;AACJ,MAAI;AACF,UAAMA,MAAK,QAAQ,cAAc,SAAS,CAAC;AAAA,EAC7C,QAAQ;AACN,WAAO;AAAA,EACT;AACA,WAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG;AACzC,UAAM,YAAYA,MAAK,KAAK,KAAK,UAAU,WAAW,EAAE;AACxD,QAAID,IAAG,WAAWC,MAAK,KAAK,WAAW,gBAAgB,CAAC,EAAG,QAAO;AAClE,UAAM,SAASA,MAAK,QAAQ,GAAG;AAC/B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAGO,SAAS,eAAe,OAAuD;AACpF,QAAM,QAAQ,OAAO;AAAA,IACnB,CAAC,SAAS,MAAM,UAAU,UAAU,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS;AAAA,EAC1F;AACA,SAAO,OAAO,SAAS,SAAY,SAAYA,MAAK,QAAQ,MAAM,IAAI;AACxE;AAOO,SAAS,2BACd,MAAyB,QAAQ,KACjC,OAAe,GAAG,QAAQ,GAClB;AACR,QAAM,WAAW,IAAI,UAAU,KAAK;AACpC,QAAM,OAAO,YAAY,SAAS,SAAS,IAAI,WAAWA,MAAK,KAAK,MAAM,MAAM;AAChF,SAAOA,MAAK,KAAK,MAAM,gBAAgB;AACzC;AAqBO,SAAS,qBAAqB,SAAoD;AACvF,QAAM,KAAK,QAAQ,MAAM,QAAQ,GAAG,SAAS,IAAI,QAAQ,KAAK;AAC9D,MAAI,QAAQ,YAAY,OAAO;AAC7B,WAAO,EAAE,QAAQ,oBAAoB,IAAI,QAAQ,sBAAsB;AAAA,EACzE;AACA,QAAM,aAAa,iBAAiB,QAAQ,WAAW,iBAAiB;AACxE,MAAI,OAAO,mBAAmB;AAC5B,WAAO;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA,GAAI,eAAe,SAAY,CAAC,IAAI,EAAE,WAAW;AAAA,MACjD,QAAQ,SAAS,iBAAiB,6BAA6B,EAAE;AAAA,IACnE;AAAA,EACF;AACA,MAAI,eAAe,QAAW;AAC5B,WAAO;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACA,QAAM,OACJ,QAAQ,SAAS,QAAQ,cAAc,SAAY,2BAA2B;AAChF,MAAI,SAAS,QAAW;AACtB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACA,QAAM,MAAMA,MAAK,KAAK,MAAM,EAAE;AAC9B,QAAM,cAAcA,MAAK,KAAK,KAAK,gBAAgB;AACnD,MAAI;AACF,QAAID,IAAG,WAAW,WAAW,GAAG;AAC9B,UAAI,QAAQ,UAAU,MAAM;AAC1B,cAAM,UAAU,CAAC,UAAU,aAAaC,MAAK,KAAK,YAAY,gBAAgB,CAAC;AAC/E,eAAO;AAAA,UACL,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ,UAAU,mDAAmD;AAAA,QACvE;AAAA,MACF;AACA,MAAAD,IAAG,aAAaC,MAAK,KAAK,YAAY,gBAAgB,GAAG,WAAW;AACpE,aAAO;AAAA,QACL,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AAAA,IACF;AACA,UAAM,UAAUD,IAAG,WAAW,GAAG;AACjC,IAAAA,IAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC,IAAAA,IAAG,aAAaC,MAAK,KAAK,YAAY,gBAAgB,GAAG,WAAW;AACpE,UAAM,WAAWA,MAAK,KAAK,KAAK,aAAa;AAE7C,QAAI,CAACD,IAAG,WAAW,QAAQ,EAAG,CAAAA,IAAG,aAAaC,MAAK,KAAK,YAAY,aAAa,GAAG,QAAQ;AAC5F,WAAO;AAAA,MACL,QAAQ,UAAU,aAAa;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,UACJ,sFACA;AAAA,IACN;AAAA,EACF,SAAS,OAAO;AACd,WAAO,EAAE,QAAQ,SAAS,IAAI,KAAK,YAAY,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE;AAAA,EACrF;AACF;AAGO,SAAS,gBAAgB,QAAqC;AACnE,QAAM,QAAQ,OAAO,QAAQ,SAAY,KAAK,KAAK,OAAO,GAAG;AAC7D,QAAM,MAAM,OAAO,WAAW,SAAY,KAAK,WAAM,OAAO,MAAM;AAClE,SAAO,UAAU,OAAO,EAAE,IAAI,OAAO,MAAM,GAAG,KAAK,GAAG,GAAG;AAC3D;AAYO,SAAS,aACd,WACA,KAAa,mBACb,OAAe,2BAA2B,GAC5B;AACd,QAAM,MAAMA,MAAK,KAAK,MAAM,EAAE;AAC9B,QAAM,aAAa,iBAAiB,WAAW,EAAE;AACjD,QAAM,YAAYD,IAAG,WAAWC,MAAK,KAAK,KAAK,gBAAgB,CAAC;AAChE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,GAAI,eAAe,SAAY,CAAC,IAAI,EAAE,WAAW;AAAA,IACjD;AAAA,IACA,oBACE,aAAa,eAAe,UAAa,UAAUA,MAAK,KAAK,KAAK,gBAAgB,GAAGA,MAAK,KAAK,YAAY,gBAAgB,CAAC;AAAA,IAC9H,iBACE,eAAe,UACfD,IAAG,WAAWC,MAAK,KAAK,KAAK,aAAa,CAAC,KAC3C,UAAUA,MAAK,KAAK,KAAK,aAAa,GAAGA,MAAK,KAAK,YAAY,aAAa,CAAC;AAAA,EACjF;AACF;AAEA,SAAS,UAAU,MAAc,OAAwB;AACvD,MAAI;AACF,WAAOD,IAAG,aAAa,IAAI,EAAE,OAAOA,IAAG,aAAa,KAAK,CAAC;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":["fs","path","path","fs","fs","path"]}
package/dist/cli/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  installBundledPreset,
13
13
  loadPolicy,
14
14
  presetStatus
15
- } from "../chunk-ZLV4NMFX.js";
15
+ } from "../chunk-XYYEJK3W.js";
16
16
  import {
17
17
  adviseWander,
18
18
  appendEntry,
@@ -28,7 +28,7 @@ import {
28
28
  scanPending,
29
29
  sendNewMessageHint,
30
30
  weeklyDirPath
31
- } from "../chunk-IG6YMUHW.js";
31
+ } from "../chunk-WLZSABBH.js";
32
32
  import {
33
33
  activeSeeds,
34
34
  addSeed,
@@ -637,7 +637,7 @@ async function verbSessions(c) {
637
637
  const root = path2.join(os.homedir(), ".dsh", "sessions");
638
638
  let found = 0;
639
639
  if (fs2.existsSync(root)) {
640
- const { homeSessionId } = await import("../orchestrator-EUTZ3KG6.js");
640
+ const { homeSessionId } = await import("../orchestrator-ZPRGVDCB.js");
641
641
  const own = homeSessionId(guard, paths);
642
642
  for (const slug of fs2.readdirSync(root)) {
643
643
  for (const id of fs2.readdirSync(path2.join(root, slug))) {
@@ -656,7 +656,7 @@ async function verbBind(c) {
656
656
  switch (sub) {
657
657
  case "list": {
658
658
  const data = loadBindings(guard, paths.settingsDir);
659
- const { homeSessionId } = await import("../orchestrator-EUTZ3KG6.js");
659
+ const { homeSessionId } = await import("../orchestrator-ZPRGVDCB.js");
660
660
  const own = homeSessionId(guard, paths);
661
661
  if (own) console.log(`\u5FC3\u8DF3\u6B63\u8EAB: ${own}\uFF08\u51B3\u7B56\u8F6E\u6B21\u53D1\u751F\u5730\uFF1Bbind remove \u5B83 = \u91CD\u7F6E\u6B63\u8EAB\uFF09`);
662
662
  for (const b of data.bindings) {
@@ -684,7 +684,7 @@ async function verbBind(c) {
684
684
  return 1;
685
685
  }
686
686
  console.log(removeBinding(guard, paths.settingsDir, id) ? `UNBOUND ${id}` : "NOT_FOUND");
687
- const { resetHomeSession } = await import("../orchestrator-EUTZ3KG6.js");
687
+ const { resetHomeSession } = await import("../orchestrator-ZPRGVDCB.js");
688
688
  if (resetHomeSession(guard, paths, id)) {
689
689
  console.log("\u6CE8\u610F\uFF1A\u8FD9\u662F\u5FC3\u8DF3\u6B63\u8EAB\u4F1A\u8BDD\u3002\u5DF2\u91CD\u7F6E\u2014\u2014\u4E0B\u6B21\u5FC3\u8DF3\u5C06\u521B\u5EFA\u65B0\u7684\u6B63\u8EAB\u4F1A\u8BDD\uFF08\u65E7\u4F1A\u8BDD\u4E0D\u518D\u6709\u5FC3\u8DF3\uFF09");
690
690
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/vault/burn-list.ts"],"sourcesContent":["// Operational CLI (design doc §14). Commands grow per module.\n// Run: node dist/cli/index.js <command> [args] (or via tsx in dev)\n//\n// H-42: `main` used to be a 530-line function with a switch holding every\n// verb's body. The verbs are module-level functions now (see CliCtx below) and\n// `main` only builds the shared context and dispatches — one place to add a\n// command, and each verb is readable on its own.\n\nimport { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { initWorkspace } from '../core/paths.js';\nimport type { WorkspacePaths } from '../core/paths.js';\nimport { createPathGuard } from '../core/path-guard.js';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { appendAuditLine, pruneAuditFile } from '../core/audit-log.js';\nimport { atomicWriteFileSync } from '../core/atomic-fs.js';\nimport { loadPolicy } from '../config/load.js';\nimport type { Policy } from '../config/schema.js';\nimport {\n addSeed,\n archiveSeedById,\n gcPool,\n loadPool,\n activeSeeds,\n archivedSeeds,\n seedsFilePath,\n surfaceSeed,\n} from '../seeds/pool.js';\nimport type { SeedSource, SeedTag } from '../seeds/types.js';\nimport {\n appendEntry,\n ledgerFilePath,\n markDone,\n readLedger,\n scanPending,\n} from '../ledger/ledger.js';\nimport {\n adviseWander,\n browseStatus,\n completeWander,\n loadInterests,\n} from '../browse/browse.js';\nimport { ensureRegistered, sendNewMessageHint } from '../notify/notify.js';\nimport {\n loadProfile,\n profileFilePath,\n verifyProfile,\n rebuildProfile,\n} from '../profile/store.js';\nimport { planBurn, executeBurn } from '../vault/burn-list.js';\nimport {\n collectMigrationEntries,\n encryptContainer,\n decryptContainer,\n applyMigrationEntries,\n} from '../vault/migrate.js';\nimport { listWeeklyReports, readWeeklyReport, weeklyDirPath } from '../weekly/report.js';\nimport { writeText } from '../vault/vault.js';\nimport {\n BUNDLED_PRESET_ID,\n conventionalUserPresetRoot,\n describeInstall,\n installBundledPreset,\n presetStatus,\n} from '../core/preset-install.js';\n\nfunction usage(): string {\n return [\n 'usage: heartbeat <command> [args]',\n '',\n 'commands:',\n ' status workspace paths + policy summary',\n ' seeds add <text> [--tag t] [--source s] [--topic x] [--confidence n]',\n ' seeds list [--archived] list active (or archived) seeds',\n ' seeds surface <id> count a surfacing (rule 1 accounting)',\n ' seeds archive <id> deliberate retirement (completed)',\n ' seeds gc run deterministic eviction rules 1-3',\n ' seeds stats pool status JSON',\n ' ledger add <text> register an open item',\n ' ledger list [--pending] list entries',\n ' ledger done <id|substring> mark an entry done',\n ' ledger open open ledger.md in the default editor',\n ' logs cleanup [--dry-run] apply log retention now',\n ' browse status browse state summary (wander)',\n ' browse dry wander adjudication with forced noon window',\n ' browse done <focus> CODE-side registration after a wander visit',\n ' bind list list session bindings (D13)',\n ' bind add <sessionId> [--observe] bind a session (deliver by default)',\n ' bind remove <sessionId> unbind a session',\n ' sessions list enumerate persisted sessions (ids)',\n ' notify check|register|send toast channel (D12: hint only, no body)',\n ' profile list [--all] current valid (or all incl. expired) entries',\n ' profile export decrypted Markdown export to data/exports/',\n ' profile verify journal replay vs disk (report only)',\n ' profile rebuild [--check] rebuild materialized view from journal',\n ' profile snapshot [--force] fold the journal into a snapshot + archive shard',\n ' profile wipe wipe profile data (asks --yes)',\n ' weekly list list generated weekly reports',\n ' weekly show [file] print a report (default: newest)',\n ' migrate export [file] pack memory files into a passphrase container (passphrase: 2 stdin lines)',\n ' migrate import <file> restore a container into this workspace (passphrase: 1 stdin line)',\n ' burn [--yes] [--all] shred runtime data (settings kept unless --all)',\n ' preset status bundled agent preset: target dir + installed?',\n ' preset install [--force] install the bundled preset (never overwrites unless --force)',\n ].join('\\n');\n}\n\nfunction readStdinText(): string {\n try {\n return fs.readFileSync(0, 'utf8').trim();\n } catch {\n return '';\n }\n}\n\nfunction flag(argv: string[], name: string): string | undefined {\n const i = argv.indexOf(name);\n return i > -1 ? argv[i + 1] : undefined;\n}\n\n/** H-42: the seed flags used to be written `flag(rest, '--tag') as never` — the\n * escape hatch threw the type away, so a typo landed in the seed record\n * unchecked. Validate against the known sets instead (the `satisfies` clauses\n * keep these lists in step with the types). */\nconst SEED_TAGS = ['news', 'fandom', 'scene', 'promise'] as const satisfies readonly SeedTag[];\nconst SEED_SOURCES = ['chat', 'screen', 'browse', 'hand', 'profile'] as const satisfies readonly SeedSource[];\n\nfunction flagOneOf<T extends string>(argv: string[], name: string, allowed: readonly T[]): T | undefined {\n const v = flag(argv, name);\n if (v === undefined) return undefined;\n return (allowed as readonly string[]).includes(v) ? (v as T) : undefined;\n}\n\n/** Read N lines from stdin (passphrase prompts for migrate). */\nasync function readLines(prompts: string[]): Promise<string[]> {\n const readline = await import('node:readline/promises');\n const it = readline.createInterface({ input: process.stdin });\n const out: string[] = [];\n for (const p of prompts) out.push((await it.question(p)).trim());\n it.close();\n return out;\n}\n\n/** Everything a verb needs. Built once in {@link main}; `sub`/`rest` are the\n * positional split of argv, `argv` stays available for top-level flags. */\ninterface CliCtx {\n paths: WorkspacePaths;\n guard: PathGuard;\n policy: Policy;\n sub: string | undefined;\n rest: string[];\n argv: string[];\n seedsFile: string;\n ledgerFile: string;\n decisionLog: string;\n seedAudit: (entry: Record<string, unknown>) => void;\n}\n\ntype Verb = (c: CliCtx) => Promise<number>;\n\nasync function verbStatus(c: CliCtx): Promise<number> {\n const { paths, policy } = c;\n console.log(JSON.stringify({\n dataDir: paths.dataDir,\n heartbeatIntervalMin: policy.heartbeat.intervalMin,\n maxDailySend: policy.gate.maxDailySend,\n seedsMaxActive: policy.seeds.maxActive,\n psyEnabled: policy.profile.psyEnabled,\n }, null, 2));\n return 0;\n}\n\nasync function verbSeeds(c: CliCtx): Promise<number> {\n const { guard, policy, sub, rest, seedsFile, seedAudit } = c;\n switch (sub) {\n case 'add': {\n let text = rest[0]?.trim() ?? '';\n if (text === '-') text = readStdinText();\n if (!text) {\n console.error('usage: seeds add <text|-> [--tag t] [--source s] [--topic x] [--confidence n]');\n return 1;\n }\n const result = addSeed(guard, seedsFile, policy, {\n text,\n tag: flagOneOf(rest, '--tag', SEED_TAGS),\n source: flagOneOf(rest, '--source', SEED_SOURCES),\n topic: flag(rest, '--topic'),\n confidence: flag(rest, '--confidence') ? Number(flag(rest, '--confidence')) : undefined,\n }, Date.now(), seedAudit);\n if (result.kind === 'duplicate') {\n console.log(`DUPLICATE: active seed ${result.seed.id} has the same text`);\n return 0;\n }\n console.log(`${result.kind.toUpperCase()} ${result.seed.id}` +\n ` (${activeSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.maxActive})` +\n (result.evicted ? ` [evicted ${result.evicted.id}: ${result.evicted.retireReason}]` : ''));\n return 0;\n }\n case 'list': {\n const db = loadPool(guard, seedsFile);\n const items = rest.includes('--archived') ? archivedSeeds(db) : activeSeeds(db);\n for (const s of items) {\n const ageDays = Math.floor((Date.now() - Date.parse(s.bornAt)) / 86_400_000);\n console.log(`${s.id} [${s.tag}/${s.source}] d${ageDays} used:${s.used}` +\n `${s.protected ? ' *' : ''} ${s.text.slice(0, 60)}`);\n }\n if (items.length === 0) console.log('(empty)');\n return 0;\n }\n case 'surface': {\n const s = surfaceSeed(guard, seedsFile, policy, rest[0] ?? '', Date.now(), seedAudit);\n if (!s) {\n console.log('NOT_FOUND');\n return 1;\n }\n console.log(`SURFACED ${s.id} used:${s.used}${s.status === 'archived' ? ' -> archived (consumed)' : ''}`);\n return 0;\n }\n case 'archive': {\n const s = archiveSeedById(guard, seedsFile, rest[0] ?? '', 'completed', Date.now(), seedAudit);\n if (!s) {\n console.log('NOT_FOUND');\n return 1;\n }\n console.log(`ARCHIVED ${s.id}`);\n return 0;\n }\n case 'gc': {\n const report = gcPool(guard, seedsFile, policy, Date.now(), seedAudit);\n console.log(`GC consumed:${report.consumed} expired:${report.expired} cold_bench:${report.coldBench}` +\n ` archive_trimmed:${report.archiveTrimmed} -> active ${report.activeAfter}/${policy.seeds.maxActive}` +\n ` archive ${archivedSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.archiveCap}`);\n return 0;\n }\n case 'stats': {\n const db = loadPool(guard, seedsFile);\n console.log(JSON.stringify({\n active: activeSeeds(db).length,\n archived: archivedSeeds(db).length,\n cap: policy.seeds.maxActive,\n seq: db.seq,\n }, null, 2));\n return 0;\n }\n default:\n console.error(`unknown seeds subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbLedger(c: CliCtx): Promise<number> {\n const { guard, sub, rest, ledgerFile } = c;\n switch (sub) {\n case 'add': {\n let text = rest.join(' ').trim();\n if (text === '-') text = readStdinText();\n if (!text) {\n console.error('usage: ledger add <text|->');\n return 1;\n }\n const e = appendEntry(guard, ledgerFile, text);\n console.log(`OPEN #${e.id} ${e.text.slice(0, 60)}`);\n return 0;\n }\n case 'list': {\n const { entries } = readLedger(guard, ledgerFile);\n const items = rest.includes('--pending') ? scanPending(guard, ledgerFile) : entries;\n for (const e of items) console.log(`${e.status === 'open' ? ' ' : 'x'} #${e.id} ${e.date} ${e.text.slice(0, 60)}`);\n if (items.length === 0) console.log('(empty)');\n return 0;\n }\n case 'done': {\n const key = rest.join(' ').trim();\n if (!key) {\n console.error('usage: ledger done <id|substring>');\n return 1;\n }\n const e = markDone(guard, ledgerFile, key);\n if (!e) {\n console.log('NOT_FOUND');\n return 1;\n }\n console.log(`DONE #${e.id}`);\n return 0;\n }\n case 'open': {\n const f = ledgerFile;\n if (!fs.existsSync(f)) {\n console.log(`(ledger will be created at ${f})`);\n }\n spawn('cmd', ['/c', 'start', '', f], { detached: true, stdio: 'ignore' }).unref();\n console.log(`opened ${f}`);\n return 0;\n }\n default:\n console.error(`unknown ledger subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbWeekly(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n const reports = listWeeklyReports(guard, paths.dataDir);\n if (sub === 'list' || sub === undefined) {\n if (reports.length === 0) console.log('(no reports yet — heartbeat generates one every 7 days)');\n for (const r of reports) console.log(`${r.file} ${r.start.slice(0, 10)} ~ ${r.end.slice(0, 10)} [${r.source}]`);\n return 0;\n }\n if (sub === 'show') {\n const file = rest[0] ?? reports[0]?.file;\n if (!file) {\n console.log('(no reports yet)');\n return 1;\n }\n const rep = readWeeklyReport(guard, paths.dataDir, file);\n if (!rep) {\n console.error(`no such report: ${file} (see: weekly list; files live in ${weeklyDirPath(paths.dataDir)})`);\n return 1;\n }\n console.log(`# ${rep.start.slice(0, 10)} ~ ${rep.end.slice(0, 10)} [${rep.source}]`);\n console.log(rep.text);\n return 0;\n }\n console.error(`unknown weekly subcommand: ${sub}`);\n return 1;\n}\n\nasync function verbLogs(c: CliCtx): Promise<number> {\n const { paths, policy, sub, rest, decisionLog } = c;\n if (sub !== 'cleanup') {\n console.error('usage: logs cleanup [--dry-run]');\n return 1;\n }\n const dry = rest.includes('--dry-run');\n const envPulse = path.join(paths.logsDir, 'envpulse.jsonl');\n const cut1 = policy.retention.envPulseHours * 3600_000;\n const cut2 = policy.retention.decisionLogDays * 86_400_000;\n if (dry) {\n console.log(`(dry-run) would prune ${envPulse} to ${policy.retention.envPulseHours}h` +\n ` and ${decisionLog} to ${policy.retention.decisionLogDays}d`);\n return 0;\n }\n const a = pruneAuditFile(envPulse, cut1);\n const b = pruneAuditFile(decisionLog, cut2);\n appendAuditLine(decisionLog, { event: 'retention', pruned_envpulse: a, pruned_decision: b });\n console.log(`PRUNED envpulse:${a} decision:${b}`);\n return 0;\n}\n\nasync function verbBrowse(c: CliCtx): Promise<number> {\n const { guard, paths, policy, sub, rest } = c;\n switch (sub) {\n case 'status': {\n const st = browseStatus(guard, paths);\n const interests = loadInterests(paths);\n console.log(JSON.stringify({\n lastWanderAt: st.wander.last_wander_at ? new Date(st.wander.last_wander_at).toISOString() : null,\n focusCount: st.wander.focusCount,\n interestCount: interests.interests?.length ?? 0,\n }, null, 2));\n return 0;\n }\n case 'dry': {\n const forced = new Date();\n forced.setHours(12, 0, 0, 0);\n const advice = adviseWander(guard, paths, policy, forced);\n console.log(JSON.stringify(advice, null, 2));\n return 0;\n }\n case 'done': {\n const focus = rest.join(' ').trim();\n if (!focus) {\n console.error('usage: browse done <focus>');\n return 1;\n }\n const r = completeWander(guard, paths, focus);\n console.log(JSON.stringify(r));\n return 0;\n }\n default:\n console.error(`unknown browse subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbNotify(c: CliCtx): Promise<number> {\n const { paths, sub } = c;\n switch (sub) {\n case 'check':\n console.log(await ensureRegistered(paths) ? 'REGISTERED: yes' : 'REGISTERED: no');\n return 0;\n case 'register':\n await ensureRegistered(paths);\n console.log('register attempted');\n return 0;\n case 'send':\n console.log(await sendNewMessageHint(paths) ? 'SENT' : 'FAILED');\n return 0;\n default:\n console.error('usage: notify check|register|send');\n return 1;\n }\n}\n\nasync function verbProfile(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n const doc = loadProfile(guard, profileFilePath(paths.dataDir));\n const all = rest.includes('--all');\n switch (sub) {\n case 'list': {\n let n = 0;\n for (const p of ['interest', 'projects', 'comm', 'psy'] as const) {\n for (const e of doc.partitions[p]!.entries) {\n if (!all && e.validTo !== null) continue;\n n += 1;\n console.log(`${e.id} [${e.partition}/${e.topic}/${e.subTopic}] ${e.temporal}` +\n ` conf=${e.confidence.toFixed(2)}${e.lowActivity ? ' 久未验证' : ''}${e.validTo ? ' [失效]' : ''}: ${e.content.slice(0, 60)}`);\n }\n }\n if (n === 0) console.log('(empty)');\n return 0;\n }\n case 'export': {\n const lines = [`# 画像导出 ${new Date().toISOString()}`, ''];\n for (const p of ['interest', 'projects', 'comm', 'psy'] as const) {\n lines.push(`## ${p}`);\n for (const e of doc.partitions[p]!.entries) {\n if (e.validTo !== null) continue;\n lines.push(`- [${e.topic}/${e.subTopic}] ${e.content} (conf ${e.confidence.toFixed(2)}, ${e.temporal})`);\n }\n }\n const out = path.join(paths.exportsDir, `profile-export-${Date.now()}.md`);\n writeText(guard, out, lines.join('\\n') + '\\n');\n console.log(`EXPORTED: ${out}`);\n return 0;\n }\n case 'verify': {\n const r = verifyProfile(guard, paths.dataDir);\n console.log(JSON.stringify(r, null, 2));\n return r.ok ? 0 : 1;\n }\n case 'rebuild': {\n const r = rebuildProfile(guard, paths.dataDir, { check: rest.includes('--check') });\n console.log(JSON.stringify(r, null, 2));\n return r.ok ? 0 : 1;\n }\n case 'wipe': {\n if (!rest.includes('--yes')) {\n console.log('REFUSED: add --yes to wipe profile data (profile.json/inbox/journal)');\n return 1;\n }\n for (const f of ['profile.json', 'profile_inbox.jsonl', 'profile_journal.jsonl']) {\n const abs = guard.assert(path.join(paths.dataDir, f));\n if (fs.existsSync(abs)) fs.rmSync(abs, { force: true });\n }\n console.log('WIPED (settings preserved; use burn for full shredding)');\n return 0;\n }\n case 'snapshot': {\n const { snapshotDue, snapshotProfile, SNAPSHOT_THRESHOLD } = await import('../profile/snapshot.js');\n const { needed, lines } = snapshotDue(guard, paths.dataDir);\n console.log(`journal ${lines} 条(阈值 ${SNAPSHOT_THRESHOLD})`);\n if (!needed && !rest.includes('--force')) {\n console.log('未到阈值,未执行(加 --force 强制折叠)。');\n return 0;\n }\n const report = snapshotProfile(guard, paths.dataDir);\n if (!report.ok) {\n console.log(`未执行:${report.reason}`);\n return 0;\n }\n console.log(`已折叠 ${report.folded} 条 → 快照(baseline ${report.baselineTs}),归档 ${report.archiveFile},live journal 已清空。`);\n console.log('verify/rebuild 自动从快照基点重放,无需额外操作。');\n return 0;\n }\n default:\n console.error(`unknown profile subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbBurn(c: CliCtx): Promise<number> {\n const { guard, paths, rest } = c;\n const yes = rest.includes('--yes');\n const all = rest.includes('--all');\n const plan = planBurn(guard, paths, all);\n console.log('预演(不会执行):');\n for (const p of plan) console.log(` [${p.exists ? '存在' : '无 '}] ${p.target.file ?? p.target.dir} ${p.target.note}`);\n if (!yes) {\n console.log('\\n此操作会让心跳 agent 失忆。确认执行请加 --yes(' + (all ? '含 --all 连用户设定' : '用户设定保留') + ')');\n return 0;\n }\n const result = executeBurn(guard, paths, { all });\n console.log(`BURNED: ${result.burned.length} 项;MISSING: ${result.missing.length} 项`);\n return 0;\n}\n\nasync function verbMigrate(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n if (sub === 'export') {\n const defaultOut = path.join(paths.exportsDir, `heartbeat-memory-${new Date().toISOString().slice(0, 10)}.hbmig`);\n const outfile = rest[0] ?? defaultOut;\n let outCanon: string;\n try {\n outCanon = guard.assert(outfile);\n } catch {\n console.error(`导出文件必须落在插件 data/ 目录内(路径守卫拒绝):${outfile}\\n默认位置:${defaultOut}`);\n return 1;\n }\n const [pw1, pw2] = await readLines(['设置口令(用于加密迁移包): ', '再输入一次确认: ']);\n if (!pw1 || pw1 !== pw2) {\n console.error('两次输入不一致或为空,已取消。');\n return 1;\n }\n const { entries, progress } = collectMigrationEntries(guard, paths);\n if (entries.length === 0) {\n console.log('没有可打包的记忆文件(data/ 是空的)。');\n return 1;\n }\n fs.mkdirSync(path.dirname(outCanon), { recursive: true });\n atomicWriteFileSync(outCanon, encryptContainer(entries, pw1));\n console.log(`已打包 ${entries.length} 个文件 → ${outCanon}`);\n console.log(` 打包:${progress.packed.join(', ')}`);\n if (progress.missing.length > 0) console.log(` 跳过(不存在):${progress.missing.join(', ')}`);\n console.log('容器是口令加密的,可安全拷贝到新机器;DPAPI 会在导入时用新机器重新加密。');\n return 0;\n }\n if (sub === 'import') {\n const infile = rest[0];\n if (!infile) {\n console.error('usage: migrate import <container-file>');\n return 1;\n }\n const [pw] = await readLines(['输入迁移包口令: ']);\n let text: string;\n try {\n text = fs.readFileSync(infile, 'utf8');\n } catch {\n console.error(`读不到迁移包:${infile}`);\n return 1;\n }\n let files;\n try {\n files = decryptContainer(text, pw ?? '');\n } catch (e) {\n console.error(String(e instanceof Error ? e.message : e));\n return 1;\n }\n const result = applyMigrationEntries(guard, paths, files.files);\n console.log(`恢复 ${result.restored.length} 个文件;备份 ${result.backedUp.length} 个被覆盖文件;跳过 ${result.skipped.length} 个。`);\n for (const r of result.restored) console.log(` 恢复: ${r}`);\n for (const b of result.backedUp) console.log(` 备份: ${b}.bak-migrate-*`);\n if (result.skipped.length > 0) console.log(` 跳过: ${result.skipped.join(', ')}`);\n if (result.error) console.log(` 失败并已回滚:${result.error}`);\n console.log('重启 DSH 后生效。');\n return result.error ? 1 : 0;\n }\n console.error(`unknown migrate subcommand: ${sub}`);\n return 1;\n}\n\nasync function verbSessions(c: CliCtx): Promise<number> {\n const { guard, paths } = c;\n // Read-only enumeration of the host's persisted sessions (ids from the\n // on-disk store; titles live server-side and are not listed here).\n const root = path.join(os.homedir(), '.dsh', 'sessions');\n let found = 0;\n if (fs.existsSync(root)) {\n const { homeSessionId } = await import('../core/orchestrator.js');\n const own = homeSessionId(guard, paths);\n for (const slug of fs.readdirSync(root)) {\n for (const id of fs.readdirSync(path.join(root, slug))) {\n found += 1;\n const mark = id === own ? ' ← 心跳正身' : '';\n console.log(`${id} [${slug}]${mark}`);\n }\n }\n }\n if (found === 0) console.log('(no persisted sessions found)');\n return 0;\n}\n\nasync function verbBind(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n const { loadBindings, addBinding, removeBinding } = await import('../core/bindings.js');\n switch (sub) {\n case 'list': {\n const data = loadBindings(guard, paths.settingsDir);\n const { homeSessionId } = await import('../core/orchestrator.js');\n const own = homeSessionId(guard, paths);\n if (own) console.log(`心跳正身: ${own}(决策轮次发生地;bind remove 它 = 重置正身)`);\n for (const b of data.bindings) {\n console.log(`${b.sessionId} deliver:${b.deliver ? '√' : '×'} observe:${b.observe ? '√' : '×'}`);\n }\n if (data.bindings.length === 0 && !own) console.log('(no bindings — expressions stay in the dedicated heartbeat session)');\n return 0;\n }\n case 'add': {\n const id = rest[0];\n if (!id || !id.startsWith('session-')) {\n console.error('usage: bind add <sessionId> [--observe] [--no-deliver] (see: sessions list)');\n return 1;\n }\n const observe = rest.includes('--observe') || rest.includes('--observe-only');\n const deliver = !rest.includes('--no-deliver') && !rest.includes('--observe-only');\n const b = addBinding(guard, paths.settingsDir, id, { deliver, observe });\n console.log(`BOUND ${b.sessionId} deliver:${b.deliver} observe:${b.observe}`);\n return 0;\n }\n case 'remove': {\n const id = rest[0];\n if (!id) {\n console.error('usage: bind remove <sessionId>');\n return 1;\n }\n console.log(removeBinding(guard, paths.settingsDir, id) ? `UNBOUND ${id}` : 'NOT_FOUND');\n // Unbinding the HOME session resets it: the next beat creates a fresh\n // dedicated session (the old one goes quiet after restart). H-09/H-45:\n // the shared path also drops the orchestrator's cached agent — the\n // inline delete here used to leave it pointing at the released session.\n const { resetHomeSession } = await import('../core/orchestrator.js');\n if (resetHomeSession(guard, paths, id)) {\n console.log('注意:这是心跳正身会话。已重置——下次心跳将创建新的正身会话(旧会话不再有心跳)');\n }\n return 0;\n }\n default:\n console.error('usage: bind list | add <sessionId> | remove <sessionId>');\n return 1;\n }\n}\n\nasync function verbPreset(c: CliCtx): Promise<number> {\n const { sub, rest, argv } = c;\n const id = flag(argv, '--id') ?? BUNDLED_PRESET_ID;\n // The plugin installs into the roster's own user root; the CLI has no\n // roster, so it reports/uses the conventional one ($DSH_HOME or ~/.dsh).\n const root = conventionalUserPresetRoot();\n const status = presetStatus(import.meta.url, id, root);\n if (sub === undefined || sub === 'status') {\n console.log(`preset id : ${status.id}`);\n console.log(`installed : ${status.installed ? 'yes' : 'no'}`);\n console.log(`target dir : ${status.dir}`);\n console.log(`bundled at : ${status.bundledDir ?? '(not found next to the plugin)'}`);\n if (status.installed) {\n console.log(\n `composition : ${status.compositionMatches ? 'matches the bundled template' : 'differs from the bundled template (hand-edited, or an older version)'}`,\n );\n }\n if (!status.installed) {\n console.log('hint: the plugin installs this itself on the next DSH start (installPreset=true, default);');\n console.log(' or run `preset install` now.');\n }\n return status.installed ? 0 : 1;\n }\n if (sub === 'install') {\n const result = installBundledPreset({\n moduleUrl: import.meta.url,\n id,\n root,\n force: rest.includes('--force'),\n });\n console.log(describeInstall(result));\n return result.action === 'error' ? 1 : 0;\n }\n console.error('usage: preset status | install [--force] [--id <presetId>]');\n return 1;\n}\n\n/** Command table. Adding a verb = one function above plus one line here. */\nconst VERBS: Record<string, Verb> = {\n status: verbStatus,\n seeds: verbSeeds,\n ledger: verbLedger,\n weekly: verbWeekly,\n logs: verbLogs,\n browse: verbBrowse,\n notify: verbNotify,\n profile: verbProfile,\n burn: verbBurn,\n migrate: verbMigrate,\n sessions: verbSessions,\n bind: verbBind,\n preset: verbPreset,\n};\n\nexport async function main(argv: string[]): Promise<number> {\n const [cmd, sub, ...rest] = argv;\n if (!cmd || cmd === 'help' || cmd === '--help') {\n console.log(usage());\n return 0;\n }\n const verb = VERBS[cmd];\n if (!verb) {\n console.error(`unknown command: ${cmd}`);\n console.log(usage());\n return 1;\n }\n const paths = initWorkspace();\n const guard = createPathGuard(paths.dataDir);\n const policy = loadPolicy(guard, paths.configDir, paths.settingsDir);\n const decisionLog = path.join(paths.logsDir, 'heartbeat.jsonl');\n /** Seed lifecycle audit (v1.9.0): CLI edits land in the same log as the beat's. */\n const seedAudit = (entry: Record<string, unknown>): void => {\n try {\n appendAuditLine(decisionLog, entry);\n } catch { /* audit must never break the command */ }\n };\n return verb({\n paths,\n guard,\n policy,\n sub,\n rest,\n argv,\n seedsFile: seedsFilePath(paths.dataDir),\n ledgerFile: ledgerFilePath(paths.dataDir),\n decisionLog,\n seedAudit,\n });\n}\n\nif (process.argv[1] && import.meta.url === new URL(`file:///${process.argv[1].replace(/\\\\/g, '/')}`).href) {\n // Direct execution guard; the CLI is also importable for tests.\n void main(process.argv.slice(2))\n .then((code) => { process.exitCode = code; })\n .catch((e: unknown) => {\n // H-44: no unhandled rejection — print and exit non-zero.\n console.error(e);\n process.exitCode = 1;\n });\n}\n","// Burn list - SINGLE SOURCE OF TRUTH (design doc §10.10; v1 shipped two\n// hardcoded lists and one drifted). burn and uninstall --purge both consume\n// this list. Settings (data/settings/) are NOT burned by default: losing\n// memory != losing the charter/gates the user configured (--all overrides).\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { PathGuard } from '../core/path-guard.js';\nimport type { WorkspacePaths } from '../core/paths.js';\nimport { shredFileSync } from '../core/atomic-fs.js';\nimport { appendAuditLine } from '../core/audit-log.js';\n\nexport interface BurnTarget {\n /** Relative to dataDir. Files are shredded (overwrite x3) then deleted. */\n file?: string;\n /** Every dataDir file whose name starts with this prefix — a family of\n * timestamped files a bare `file` cannot name (journal archive shards).\n * Shredded like files. */\n prefix?: string;\n /** Directories are removed recursively (no shred - content is files). */\n dir?: string;\n note: string;\n}\n\nexport const BURN_LIST: BurnTarget[] = [\n { file: 'profile.json', note: '画像物化视图' },\n { file: 'profile_inbox.jsonl', note: '观察收件箱' },\n { file: 'profile_journal.jsonl', note: '画像操作流水' },\n // H-19: the rotation shards hold the whole profile history and are written\n // in PLAINTEXT — a burn that leaves them behind leaves the memory readable.\n { prefix: 'profile_journal.archive-', note: '画像流水归档分片(明文;journal 轮转产物)' },\n { file: 'profile_rhythm.json', note: '作息聚合(纯统计)' },\n { file: 'screen.json', note: '屏幕快照(加密)' },\n { file: 'screen.jpg', note: '截图(加密)' },\n { file: 'sent.json', note: '表达记录(加密)' },\n { file: 'browse.json', note: '浏览流状态(加密)' },\n { file: 'seeds.jsonl', note: '素材池(加密)' },\n { file: 'ledger.md', note: '账本(明文,人可读是设计目标)' },\n { file: 'envpulse.json', note: '环境温度计快照' },\n { file: 'gate.json', note: '闸门运行时计数' },\n // v1.9.0 completion (H-19): these carry memory just as much as the rows\n // above — the persona report log records WHAT SHE SAID (spoken / seed_ids /\n // profile_ids / reason), so a burn that skips it is a burn that lies.\n { file: 'profile_snapshot.json', note: '画像快照(journal 轮转基线)' },\n { file: 'cursors.json', note: '观察游标(会话事件位点)' },\n { file: 'seed_report.jsonl', note: '投递报账日志(加密)' },\n { file: 'preference.json', note: '话题偏好统计' },\n { dir: 'weekly', note: '周报(画像与素材散文)' },\n { dir: 'tmp', note: '解锁临时文件(崩溃残留的解密明文是最该烧的东西)' },\n { dir: 'logs', note: '审计与决策日志' },\n { dir: 'exports', note: '导出产物' },\n];\n\nconst SETTINGS_DIR = 'settings'; // excluded unless --all\n\n/** dataDir files matching a prefix target, name-sorted for stable output. */\nfunction prefixMatches(guard: PathGuard, paths: WorkspacePaths, prefix: string): string[] {\n try {\n return fs.readdirSync(guard.assert(paths.dataDir))\n .filter((f) => f.startsWith(prefix))\n .sort();\n } catch {\n return [];\n }\n}\n\nexport interface BurnPlanItem {\n target: BurnTarget;\n exists: boolean;\n}\n\n/** Preview: what exists, what would be removed. No writes. */\nexport function planBurn(guard: PathGuard, paths: WorkspacePaths, all = false): BurnPlanItem[] {\n const plan: BurnPlanItem[] = [];\n for (const t of BURN_LIST) {\n const rel = t.file ?? t.dir ?? t.prefix ?? '';\n if (!all && rel === SETTINGS_DIR) continue;\n let exists = false;\n if (t.prefix) {\n exists = prefixMatches(guard, paths, t.prefix).length > 0;\n } else {\n try {\n exists = fs.existsSync(guard.assert(path.join(paths.dataDir, rel)));\n } catch {\n exists = false;\n }\n }\n plan.push({ target: t, exists });\n }\n if (all) {\n plan.push({ target: { dir: SETTINGS_DIR, note: '用户设定(隐私宪章/闸门参数)——仅 --all 连带' }, exists: fs.existsSync(paths.settingsDir) });\n }\n return plan;\n}\n\n/**\n * Execute the burn: overwrite x3 + delete files, remove dirs. Irreversible.\n * Returns per-target results and writes a BURN_EVENT into the (recreated)\n * journal afterwards - the only trace, on purpose (transparency).\n */\nexport function executeBurn(\n guard: PathGuard,\n paths: WorkspacePaths,\n opts: { all?: boolean; shredPasses?: number } = {},\n): { burned: string[]; missing: string[] } {\n const burned: string[] = [];\n const missing: string[] = [];\n const passes = opts.shredPasses ?? 3;\n for (const t of BURN_LIST) {\n // H-19: a prefix target is a family of files (timestamped archive shards);\n // shred every match individually.\n if (t.prefix) {\n const matches = prefixMatches(guard, paths, t.prefix);\n if (matches.length === 0) {\n missing.push(`${t.prefix}*`);\n continue;\n }\n for (const f of matches) {\n try {\n shredFileSync(guard.assert(path.join(paths.dataDir, f)), passes);\n burned.push(f);\n } catch { /* vanished between listing and shredding: not fatal */ }\n }\n continue;\n }\n const rel = t.file ?? t.dir!;\n const abs = path.join(paths.dataDir, rel);\n let absCanon: string;\n try {\n absCanon = guard.assert(abs);\n } catch {\n missing.push(rel);\n continue;\n }\n if (t.file) {\n if (fs.existsSync(absCanon)) {\n shredFileSync(absCanon, passes);\n burned.push(rel);\n } else {\n missing.push(rel);\n }\n } else if (fs.existsSync(absCanon)) {\n fs.rmSync(absCanon, { recursive: true, force: true });\n burned.push(rel);\n } else {\n missing.push(rel);\n }\n }\n if (opts.all) {\n fs.rmSync(paths.settingsDir, { recursive: true, force: true });\n burned.push(SETTINGS_DIR);\n }\n // BURN_EVENT: recreate the journal; data dir is now re-initializable.\n appendAuditLine(guard.assert(path.join(paths.dataDir, 'profile_journal.jsonl')), {\n event: 'BURN_EVENT',\n burned: burned.length,\n missing: missing.length,\n settingsPreserved: !opts.all,\n });\n return { burned, missing };\n}\n"],"mappings":"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","names":["fs","path","fs","path"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/vault/burn-list.ts"],"sourcesContent":["// Operational CLI (design doc §14). Commands grow per module.\n// Run: node dist/cli/index.js <command> [args] (or via tsx in dev)\n//\n// H-42: `main` used to be a 530-line function with a switch holding every\n// verb's body. The verbs are module-level functions now (see CliCtx below) and\n// `main` only builds the shared context and dispatches — one place to add a\n// command, and each verb is readable on its own.\n\nimport { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { initWorkspace } from '../core/paths.js';\nimport type { WorkspacePaths } from '../core/paths.js';\nimport { createPathGuard } from '../core/path-guard.js';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { appendAuditLine, pruneAuditFile } from '../core/audit-log.js';\nimport { atomicWriteFileSync } from '../core/atomic-fs.js';\nimport { loadPolicy } from '../config/load.js';\nimport type { Policy } from '../config/schema.js';\nimport {\n addSeed,\n archiveSeedById,\n gcPool,\n loadPool,\n activeSeeds,\n archivedSeeds,\n seedsFilePath,\n surfaceSeed,\n} from '../seeds/pool.js';\nimport type { SeedSource, SeedTag } from '../seeds/types.js';\nimport {\n appendEntry,\n ledgerFilePath,\n markDone,\n readLedger,\n scanPending,\n} from '../ledger/ledger.js';\nimport {\n adviseWander,\n browseStatus,\n completeWander,\n loadInterests,\n} from '../browse/browse.js';\nimport { ensureRegistered, sendNewMessageHint } from '../notify/notify.js';\nimport {\n loadProfile,\n profileFilePath,\n verifyProfile,\n rebuildProfile,\n} from '../profile/store.js';\nimport { planBurn, executeBurn } from '../vault/burn-list.js';\nimport {\n collectMigrationEntries,\n encryptContainer,\n decryptContainer,\n applyMigrationEntries,\n} from '../vault/migrate.js';\nimport { listWeeklyReports, readWeeklyReport, weeklyDirPath } from '../weekly/report.js';\nimport { writeText } from '../vault/vault.js';\nimport {\n BUNDLED_PRESET_ID,\n conventionalUserPresetRoot,\n describeInstall,\n installBundledPreset,\n presetStatus,\n} from '../core/preset-install.js';\n\nfunction usage(): string {\n return [\n 'usage: heartbeat <command> [args]',\n '',\n 'commands:',\n ' status workspace paths + policy summary',\n ' seeds add <text> [--tag t] [--source s] [--topic x] [--confidence n]',\n ' seeds list [--archived] list active (or archived) seeds',\n ' seeds surface <id> count a surfacing (rule 1 accounting)',\n ' seeds archive <id> deliberate retirement (completed)',\n ' seeds gc run deterministic eviction rules 1-3',\n ' seeds stats pool status JSON',\n ' ledger add <text> register an open item',\n ' ledger list [--pending] list entries',\n ' ledger done <id|substring> mark an entry done',\n ' ledger open open ledger.md in the default editor',\n ' logs cleanup [--dry-run] apply log retention now',\n ' browse status browse state summary (wander)',\n ' browse dry wander adjudication with forced noon window',\n ' browse done <focus> CODE-side registration after a wander visit',\n ' bind list list session bindings (D13)',\n ' bind add <sessionId> [--observe] bind a session (deliver by default)',\n ' bind remove <sessionId> unbind a session',\n ' sessions list enumerate persisted sessions (ids)',\n ' notify check|register|send toast channel (D12: hint only, no body)',\n ' profile list [--all] current valid (or all incl. expired) entries',\n ' profile export decrypted Markdown export to data/exports/',\n ' profile verify journal replay vs disk (report only)',\n ' profile rebuild [--check] rebuild materialized view from journal',\n ' profile snapshot [--force] fold the journal into a snapshot + archive shard',\n ' profile wipe wipe profile data (asks --yes)',\n ' weekly list list generated weekly reports',\n ' weekly show [file] print a report (default: newest)',\n ' migrate export [file] pack memory files into a passphrase container (passphrase: 2 stdin lines)',\n ' migrate import <file> restore a container into this workspace (passphrase: 1 stdin line)',\n ' burn [--yes] [--all] shred runtime data (settings kept unless --all)',\n ' preset status bundled agent preset: target dir + installed?',\n ' preset install [--force] install the bundled preset (never overwrites unless --force)',\n ].join('\\n');\n}\n\nfunction readStdinText(): string {\n try {\n return fs.readFileSync(0, 'utf8').trim();\n } catch {\n return '';\n }\n}\n\nfunction flag(argv: string[], name: string): string | undefined {\n const i = argv.indexOf(name);\n return i > -1 ? argv[i + 1] : undefined;\n}\n\n/** H-42: the seed flags used to be written `flag(rest, '--tag') as never` — the\n * escape hatch threw the type away, so a typo landed in the seed record\n * unchecked. Validate against the known sets instead (the `satisfies` clauses\n * keep these lists in step with the types). */\nconst SEED_TAGS = ['news', 'fandom', 'scene', 'promise'] as const satisfies readonly SeedTag[];\nconst SEED_SOURCES = ['chat', 'screen', 'browse', 'hand', 'profile'] as const satisfies readonly SeedSource[];\n\nfunction flagOneOf<T extends string>(argv: string[], name: string, allowed: readonly T[]): T | undefined {\n const v = flag(argv, name);\n if (v === undefined) return undefined;\n return (allowed as readonly string[]).includes(v) ? (v as T) : undefined;\n}\n\n/** Read N lines from stdin (passphrase prompts for migrate). */\nasync function readLines(prompts: string[]): Promise<string[]> {\n const readline = await import('node:readline/promises');\n const it = readline.createInterface({ input: process.stdin });\n const out: string[] = [];\n for (const p of prompts) out.push((await it.question(p)).trim());\n it.close();\n return out;\n}\n\n/** Everything a verb needs. Built once in {@link main}; `sub`/`rest` are the\n * positional split of argv, `argv` stays available for top-level flags. */\ninterface CliCtx {\n paths: WorkspacePaths;\n guard: PathGuard;\n policy: Policy;\n sub: string | undefined;\n rest: string[];\n argv: string[];\n seedsFile: string;\n ledgerFile: string;\n decisionLog: string;\n seedAudit: (entry: Record<string, unknown>) => void;\n}\n\ntype Verb = (c: CliCtx) => Promise<number>;\n\nasync function verbStatus(c: CliCtx): Promise<number> {\n const { paths, policy } = c;\n console.log(JSON.stringify({\n dataDir: paths.dataDir,\n heartbeatIntervalMin: policy.heartbeat.intervalMin,\n maxDailySend: policy.gate.maxDailySend,\n seedsMaxActive: policy.seeds.maxActive,\n psyEnabled: policy.profile.psyEnabled,\n }, null, 2));\n return 0;\n}\n\nasync function verbSeeds(c: CliCtx): Promise<number> {\n const { guard, policy, sub, rest, seedsFile, seedAudit } = c;\n switch (sub) {\n case 'add': {\n let text = rest[0]?.trim() ?? '';\n if (text === '-') text = readStdinText();\n if (!text) {\n console.error('usage: seeds add <text|-> [--tag t] [--source s] [--topic x] [--confidence n]');\n return 1;\n }\n const result = addSeed(guard, seedsFile, policy, {\n text,\n tag: flagOneOf(rest, '--tag', SEED_TAGS),\n source: flagOneOf(rest, '--source', SEED_SOURCES),\n topic: flag(rest, '--topic'),\n confidence: flag(rest, '--confidence') ? Number(flag(rest, '--confidence')) : undefined,\n }, Date.now(), seedAudit);\n if (result.kind === 'duplicate') {\n console.log(`DUPLICATE: active seed ${result.seed.id} has the same text`);\n return 0;\n }\n console.log(`${result.kind.toUpperCase()} ${result.seed.id}` +\n ` (${activeSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.maxActive})` +\n (result.evicted ? ` [evicted ${result.evicted.id}: ${result.evicted.retireReason}]` : ''));\n return 0;\n }\n case 'list': {\n const db = loadPool(guard, seedsFile);\n const items = rest.includes('--archived') ? archivedSeeds(db) : activeSeeds(db);\n for (const s of items) {\n const ageDays = Math.floor((Date.now() - Date.parse(s.bornAt)) / 86_400_000);\n console.log(`${s.id} [${s.tag}/${s.source}] d${ageDays} used:${s.used}` +\n `${s.protected ? ' *' : ''} ${s.text.slice(0, 60)}`);\n }\n if (items.length === 0) console.log('(empty)');\n return 0;\n }\n case 'surface': {\n const s = surfaceSeed(guard, seedsFile, policy, rest[0] ?? '', Date.now(), seedAudit);\n if (!s) {\n console.log('NOT_FOUND');\n return 1;\n }\n console.log(`SURFACED ${s.id} used:${s.used}${s.status === 'archived' ? ' -> archived (consumed)' : ''}`);\n return 0;\n }\n case 'archive': {\n const s = archiveSeedById(guard, seedsFile, rest[0] ?? '', 'completed', Date.now(), seedAudit);\n if (!s) {\n console.log('NOT_FOUND');\n return 1;\n }\n console.log(`ARCHIVED ${s.id}`);\n return 0;\n }\n case 'gc': {\n const report = gcPool(guard, seedsFile, policy, Date.now(), seedAudit);\n console.log(`GC consumed:${report.consumed} expired:${report.expired} cold_bench:${report.coldBench}` +\n ` archive_trimmed:${report.archiveTrimmed} -> active ${report.activeAfter}/${policy.seeds.maxActive}` +\n ` archive ${archivedSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.archiveCap}`);\n return 0;\n }\n case 'stats': {\n const db = loadPool(guard, seedsFile);\n console.log(JSON.stringify({\n active: activeSeeds(db).length,\n archived: archivedSeeds(db).length,\n cap: policy.seeds.maxActive,\n seq: db.seq,\n }, null, 2));\n return 0;\n }\n default:\n console.error(`unknown seeds subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbLedger(c: CliCtx): Promise<number> {\n const { guard, sub, rest, ledgerFile } = c;\n switch (sub) {\n case 'add': {\n let text = rest.join(' ').trim();\n if (text === '-') text = readStdinText();\n if (!text) {\n console.error('usage: ledger add <text|->');\n return 1;\n }\n const e = appendEntry(guard, ledgerFile, text);\n console.log(`OPEN #${e.id} ${e.text.slice(0, 60)}`);\n return 0;\n }\n case 'list': {\n const { entries } = readLedger(guard, ledgerFile);\n const items = rest.includes('--pending') ? scanPending(guard, ledgerFile) : entries;\n for (const e of items) console.log(`${e.status === 'open' ? ' ' : 'x'} #${e.id} ${e.date} ${e.text.slice(0, 60)}`);\n if (items.length === 0) console.log('(empty)');\n return 0;\n }\n case 'done': {\n const key = rest.join(' ').trim();\n if (!key) {\n console.error('usage: ledger done <id|substring>');\n return 1;\n }\n const e = markDone(guard, ledgerFile, key);\n if (!e) {\n console.log('NOT_FOUND');\n return 1;\n }\n console.log(`DONE #${e.id}`);\n return 0;\n }\n case 'open': {\n const f = ledgerFile;\n if (!fs.existsSync(f)) {\n console.log(`(ledger will be created at ${f})`);\n }\n spawn('cmd', ['/c', 'start', '', f], { detached: true, stdio: 'ignore' }).unref();\n console.log(`opened ${f}`);\n return 0;\n }\n default:\n console.error(`unknown ledger subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbWeekly(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n const reports = listWeeklyReports(guard, paths.dataDir);\n if (sub === 'list' || sub === undefined) {\n if (reports.length === 0) console.log('(no reports yet — heartbeat generates one every 7 days)');\n for (const r of reports) console.log(`${r.file} ${r.start.slice(0, 10)} ~ ${r.end.slice(0, 10)} [${r.source}]`);\n return 0;\n }\n if (sub === 'show') {\n const file = rest[0] ?? reports[0]?.file;\n if (!file) {\n console.log('(no reports yet)');\n return 1;\n }\n const rep = readWeeklyReport(guard, paths.dataDir, file);\n if (!rep) {\n console.error(`no such report: ${file} (see: weekly list; files live in ${weeklyDirPath(paths.dataDir)})`);\n return 1;\n }\n console.log(`# ${rep.start.slice(0, 10)} ~ ${rep.end.slice(0, 10)} [${rep.source}]`);\n console.log(rep.text);\n return 0;\n }\n console.error(`unknown weekly subcommand: ${sub}`);\n return 1;\n}\n\nasync function verbLogs(c: CliCtx): Promise<number> {\n const { paths, policy, sub, rest, decisionLog } = c;\n if (sub !== 'cleanup') {\n console.error('usage: logs cleanup [--dry-run]');\n return 1;\n }\n const dry = rest.includes('--dry-run');\n const envPulse = path.join(paths.logsDir, 'envpulse.jsonl');\n const cut1 = policy.retention.envPulseHours * 3600_000;\n const cut2 = policy.retention.decisionLogDays * 86_400_000;\n if (dry) {\n console.log(`(dry-run) would prune ${envPulse} to ${policy.retention.envPulseHours}h` +\n ` and ${decisionLog} to ${policy.retention.decisionLogDays}d`);\n return 0;\n }\n const a = pruneAuditFile(envPulse, cut1);\n const b = pruneAuditFile(decisionLog, cut2);\n appendAuditLine(decisionLog, { event: 'retention', pruned_envpulse: a, pruned_decision: b });\n console.log(`PRUNED envpulse:${a} decision:${b}`);\n return 0;\n}\n\nasync function verbBrowse(c: CliCtx): Promise<number> {\n const { guard, paths, policy, sub, rest } = c;\n switch (sub) {\n case 'status': {\n const st = browseStatus(guard, paths);\n const interests = loadInterests(paths);\n console.log(JSON.stringify({\n lastWanderAt: st.wander.last_wander_at ? new Date(st.wander.last_wander_at).toISOString() : null,\n focusCount: st.wander.focusCount,\n interestCount: interests.interests?.length ?? 0,\n }, null, 2));\n return 0;\n }\n case 'dry': {\n const forced = new Date();\n forced.setHours(12, 0, 0, 0);\n const advice = adviseWander(guard, paths, policy, forced);\n console.log(JSON.stringify(advice, null, 2));\n return 0;\n }\n case 'done': {\n const focus = rest.join(' ').trim();\n if (!focus) {\n console.error('usage: browse done <focus>');\n return 1;\n }\n const r = completeWander(guard, paths, focus);\n console.log(JSON.stringify(r));\n return 0;\n }\n default:\n console.error(`unknown browse subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbNotify(c: CliCtx): Promise<number> {\n const { paths, sub } = c;\n switch (sub) {\n case 'check':\n console.log(await ensureRegistered(paths) ? 'REGISTERED: yes' : 'REGISTERED: no');\n return 0;\n case 'register':\n await ensureRegistered(paths);\n console.log('register attempted');\n return 0;\n case 'send':\n console.log(await sendNewMessageHint(paths) ? 'SENT' : 'FAILED');\n return 0;\n default:\n console.error('usage: notify check|register|send');\n return 1;\n }\n}\n\nasync function verbProfile(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n const doc = loadProfile(guard, profileFilePath(paths.dataDir));\n const all = rest.includes('--all');\n switch (sub) {\n case 'list': {\n let n = 0;\n for (const p of ['interest', 'projects', 'comm', 'psy'] as const) {\n for (const e of doc.partitions[p]!.entries) {\n if (!all && e.validTo !== null) continue;\n n += 1;\n console.log(`${e.id} [${e.partition}/${e.topic}/${e.subTopic}] ${e.temporal}` +\n ` conf=${e.confidence.toFixed(2)}${e.lowActivity ? ' 久未验证' : ''}${e.validTo ? ' [失效]' : ''}: ${e.content.slice(0, 60)}`);\n }\n }\n if (n === 0) console.log('(empty)');\n return 0;\n }\n case 'export': {\n const lines = [`# 画像导出 ${new Date().toISOString()}`, ''];\n for (const p of ['interest', 'projects', 'comm', 'psy'] as const) {\n lines.push(`## ${p}`);\n for (const e of doc.partitions[p]!.entries) {\n if (e.validTo !== null) continue;\n lines.push(`- [${e.topic}/${e.subTopic}] ${e.content} (conf ${e.confidence.toFixed(2)}, ${e.temporal})`);\n }\n }\n const out = path.join(paths.exportsDir, `profile-export-${Date.now()}.md`);\n writeText(guard, out, lines.join('\\n') + '\\n');\n console.log(`EXPORTED: ${out}`);\n return 0;\n }\n case 'verify': {\n const r = verifyProfile(guard, paths.dataDir);\n console.log(JSON.stringify(r, null, 2));\n return r.ok ? 0 : 1;\n }\n case 'rebuild': {\n const r = rebuildProfile(guard, paths.dataDir, { check: rest.includes('--check') });\n console.log(JSON.stringify(r, null, 2));\n return r.ok ? 0 : 1;\n }\n case 'wipe': {\n if (!rest.includes('--yes')) {\n console.log('REFUSED: add --yes to wipe profile data (profile.json/inbox/journal)');\n return 1;\n }\n for (const f of ['profile.json', 'profile_inbox.jsonl', 'profile_journal.jsonl']) {\n const abs = guard.assert(path.join(paths.dataDir, f));\n if (fs.existsSync(abs)) fs.rmSync(abs, { force: true });\n }\n console.log('WIPED (settings preserved; use burn for full shredding)');\n return 0;\n }\n case 'snapshot': {\n const { snapshotDue, snapshotProfile, SNAPSHOT_THRESHOLD } = await import('../profile/snapshot.js');\n const { needed, lines } = snapshotDue(guard, paths.dataDir);\n console.log(`journal ${lines} 条(阈值 ${SNAPSHOT_THRESHOLD})`);\n if (!needed && !rest.includes('--force')) {\n console.log('未到阈值,未执行(加 --force 强制折叠)。');\n return 0;\n }\n const report = snapshotProfile(guard, paths.dataDir);\n if (!report.ok) {\n console.log(`未执行:${report.reason}`);\n return 0;\n }\n console.log(`已折叠 ${report.folded} 条 → 快照(baseline ${report.baselineTs}),归档 ${report.archiveFile},live journal 已清空。`);\n console.log('verify/rebuild 自动从快照基点重放,无需额外操作。');\n return 0;\n }\n default:\n console.error(`unknown profile subcommand: ${sub}`);\n return 1;\n }\n}\n\nasync function verbBurn(c: CliCtx): Promise<number> {\n const { guard, paths, rest } = c;\n const yes = rest.includes('--yes');\n const all = rest.includes('--all');\n const plan = planBurn(guard, paths, all);\n console.log('预演(不会执行):');\n for (const p of plan) console.log(` [${p.exists ? '存在' : '无 '}] ${p.target.file ?? p.target.dir} ${p.target.note}`);\n if (!yes) {\n console.log('\\n此操作会让心跳 agent 失忆。确认执行请加 --yes(' + (all ? '含 --all 连用户设定' : '用户设定保留') + ')');\n return 0;\n }\n const result = executeBurn(guard, paths, { all });\n console.log(`BURNED: ${result.burned.length} 项;MISSING: ${result.missing.length} 项`);\n return 0;\n}\n\nasync function verbMigrate(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n if (sub === 'export') {\n const defaultOut = path.join(paths.exportsDir, `heartbeat-memory-${new Date().toISOString().slice(0, 10)}.hbmig`);\n const outfile = rest[0] ?? defaultOut;\n let outCanon: string;\n try {\n outCanon = guard.assert(outfile);\n } catch {\n console.error(`导出文件必须落在插件 data/ 目录内(路径守卫拒绝):${outfile}\\n默认位置:${defaultOut}`);\n return 1;\n }\n const [pw1, pw2] = await readLines(['设置口令(用于加密迁移包): ', '再输入一次确认: ']);\n if (!pw1 || pw1 !== pw2) {\n console.error('两次输入不一致或为空,已取消。');\n return 1;\n }\n const { entries, progress } = collectMigrationEntries(guard, paths);\n if (entries.length === 0) {\n console.log('没有可打包的记忆文件(data/ 是空的)。');\n return 1;\n }\n fs.mkdirSync(path.dirname(outCanon), { recursive: true });\n atomicWriteFileSync(outCanon, encryptContainer(entries, pw1));\n console.log(`已打包 ${entries.length} 个文件 → ${outCanon}`);\n console.log(` 打包:${progress.packed.join(', ')}`);\n if (progress.missing.length > 0) console.log(` 跳过(不存在):${progress.missing.join(', ')}`);\n console.log('容器是口令加密的,可安全拷贝到新机器;DPAPI 会在导入时用新机器重新加密。');\n return 0;\n }\n if (sub === 'import') {\n const infile = rest[0];\n if (!infile) {\n console.error('usage: migrate import <container-file>');\n return 1;\n }\n const [pw] = await readLines(['输入迁移包口令: ']);\n let text: string;\n try {\n text = fs.readFileSync(infile, 'utf8');\n } catch {\n console.error(`读不到迁移包:${infile}`);\n return 1;\n }\n let files;\n try {\n files = decryptContainer(text, pw ?? '');\n } catch (e) {\n console.error(String(e instanceof Error ? e.message : e));\n return 1;\n }\n const result = applyMigrationEntries(guard, paths, files.files);\n console.log(`恢复 ${result.restored.length} 个文件;备份 ${result.backedUp.length} 个被覆盖文件;跳过 ${result.skipped.length} 个。`);\n for (const r of result.restored) console.log(` 恢复: ${r}`);\n for (const b of result.backedUp) console.log(` 备份: ${b}.bak-migrate-*`);\n if (result.skipped.length > 0) console.log(` 跳过: ${result.skipped.join(', ')}`);\n if (result.error) console.log(` 失败并已回滚:${result.error}`);\n console.log('重启 DSH 后生效。');\n return result.error ? 1 : 0;\n }\n console.error(`unknown migrate subcommand: ${sub}`);\n return 1;\n}\n\nasync function verbSessions(c: CliCtx): Promise<number> {\n const { guard, paths } = c;\n // Read-only enumeration of the host's persisted sessions (ids from the\n // on-disk store; titles live server-side and are not listed here).\n const root = path.join(os.homedir(), '.dsh', 'sessions');\n let found = 0;\n if (fs.existsSync(root)) {\n const { homeSessionId } = await import('../core/orchestrator.js');\n const own = homeSessionId(guard, paths);\n for (const slug of fs.readdirSync(root)) {\n for (const id of fs.readdirSync(path.join(root, slug))) {\n found += 1;\n const mark = id === own ? ' ← 心跳正身' : '';\n console.log(`${id} [${slug}]${mark}`);\n }\n }\n }\n if (found === 0) console.log('(no persisted sessions found)');\n return 0;\n}\n\nasync function verbBind(c: CliCtx): Promise<number> {\n const { guard, paths, sub, rest } = c;\n const { loadBindings, addBinding, removeBinding } = await import('../core/bindings.js');\n switch (sub) {\n case 'list': {\n const data = loadBindings(guard, paths.settingsDir);\n const { homeSessionId } = await import('../core/orchestrator.js');\n const own = homeSessionId(guard, paths);\n if (own) console.log(`心跳正身: ${own}(决策轮次发生地;bind remove 它 = 重置正身)`);\n for (const b of data.bindings) {\n console.log(`${b.sessionId} deliver:${b.deliver ? '√' : '×'} observe:${b.observe ? '√' : '×'}`);\n }\n if (data.bindings.length === 0 && !own) console.log('(no bindings — expressions stay in the dedicated heartbeat session)');\n return 0;\n }\n case 'add': {\n const id = rest[0];\n if (!id || !id.startsWith('session-')) {\n console.error('usage: bind add <sessionId> [--observe] [--no-deliver] (see: sessions list)');\n return 1;\n }\n const observe = rest.includes('--observe') || rest.includes('--observe-only');\n const deliver = !rest.includes('--no-deliver') && !rest.includes('--observe-only');\n const b = addBinding(guard, paths.settingsDir, id, { deliver, observe });\n console.log(`BOUND ${b.sessionId} deliver:${b.deliver} observe:${b.observe}`);\n return 0;\n }\n case 'remove': {\n const id = rest[0];\n if (!id) {\n console.error('usage: bind remove <sessionId>');\n return 1;\n }\n console.log(removeBinding(guard, paths.settingsDir, id) ? `UNBOUND ${id}` : 'NOT_FOUND');\n // Unbinding the HOME session resets it: the next beat creates a fresh\n // dedicated session (the old one goes quiet after restart). H-09/H-45:\n // the shared path also drops the orchestrator's cached agent — the\n // inline delete here used to leave it pointing at the released session.\n const { resetHomeSession } = await import('../core/orchestrator.js');\n if (resetHomeSession(guard, paths, id)) {\n console.log('注意:这是心跳正身会话。已重置——下次心跳将创建新的正身会话(旧会话不再有心跳)');\n }\n return 0;\n }\n default:\n console.error('usage: bind list | add <sessionId> | remove <sessionId>');\n return 1;\n }\n}\n\nasync function verbPreset(c: CliCtx): Promise<number> {\n const { sub, rest, argv } = c;\n const id = flag(argv, '--id') ?? BUNDLED_PRESET_ID;\n // The plugin installs into the roster's own user root; the CLI has no\n // roster, so it reports/uses the conventional one ($DSH_HOME or ~/.dsh).\n const root = conventionalUserPresetRoot();\n const status = presetStatus(import.meta.url, id, root);\n if (sub === undefined || sub === 'status') {\n console.log(`preset id : ${status.id}`);\n console.log(`installed : ${status.installed ? 'yes' : 'no'}`);\n console.log(`target dir : ${status.dir}`);\n console.log(`bundled at : ${status.bundledDir ?? '(not found next to the plugin)'}`);\n if (status.installed) {\n console.log(\n `composition : ${status.compositionMatches ? 'matches the bundled template' : 'differs from the bundled template (hand-edited, or an older version)'}`,\n );\n }\n if (!status.installed) {\n console.log('hint: the plugin installs this itself on the next DSH start (installPreset=true, default);');\n console.log(' or run `preset install` now.');\n }\n return status.installed ? 0 : 1;\n }\n if (sub === 'install') {\n const result = installBundledPreset({\n moduleUrl: import.meta.url,\n id,\n root,\n force: rest.includes('--force'),\n });\n console.log(describeInstall(result));\n return result.action === 'error' ? 1 : 0;\n }\n console.error('usage: preset status | install [--force] [--id <presetId>]');\n return 1;\n}\n\n/** Command table. Adding a verb = one function above plus one line here. */\nconst VERBS: Record<string, Verb> = {\n status: verbStatus,\n seeds: verbSeeds,\n ledger: verbLedger,\n weekly: verbWeekly,\n logs: verbLogs,\n browse: verbBrowse,\n notify: verbNotify,\n profile: verbProfile,\n burn: verbBurn,\n migrate: verbMigrate,\n sessions: verbSessions,\n bind: verbBind,\n preset: verbPreset,\n};\n\nexport async function main(argv: string[]): Promise<number> {\n const [cmd, sub, ...rest] = argv;\n if (!cmd || cmd === 'help' || cmd === '--help') {\n console.log(usage());\n return 0;\n }\n const verb = VERBS[cmd];\n if (!verb) {\n console.error(`unknown command: ${cmd}`);\n console.log(usage());\n return 1;\n }\n const paths = initWorkspace();\n const guard = createPathGuard(paths.dataDir);\n const policy = loadPolicy(guard, paths.configDir, paths.settingsDir);\n const decisionLog = path.join(paths.logsDir, 'heartbeat.jsonl');\n /** Seed lifecycle audit (v1.9.0): CLI edits land in the same log as the beat's. */\n const seedAudit = (entry: Record<string, unknown>): void => {\n try {\n appendAuditLine(decisionLog, entry);\n } catch { /* audit must never break the command */ }\n };\n return verb({\n paths,\n guard,\n policy,\n sub,\n rest,\n argv,\n seedsFile: seedsFilePath(paths.dataDir),\n ledgerFile: ledgerFilePath(paths.dataDir),\n decisionLog,\n seedAudit,\n });\n}\n\nif (process.argv[1] && import.meta.url === new URL(`file:///${process.argv[1].replace(/\\\\/g, '/')}`).href) {\n // Direct execution guard; the CLI is also importable for tests.\n void main(process.argv.slice(2))\n .then((code) => { process.exitCode = code; })\n .catch((e: unknown) => {\n // H-44: no unhandled rejection — print and exit non-zero.\n console.error(e);\n process.exitCode = 1;\n });\n}\n","// Burn list - SINGLE SOURCE OF TRUTH (design doc §10.10; v1 shipped two\r\n// hardcoded lists and one drifted). burn and uninstall --purge both consume\r\n// this list. Settings (data/settings/) are NOT burned by default: losing\r\n// memory != losing the charter/gates the user configured (--all overrides).\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { PathGuard } from '../core/path-guard.js';\r\nimport type { WorkspacePaths } from '../core/paths.js';\r\nimport { shredFileSync } from '../core/atomic-fs.js';\r\nimport { appendAuditLine } from '../core/audit-log.js';\r\n\r\nexport interface BurnTarget {\r\n /** Relative to dataDir. Files are shredded (overwrite x3) then deleted. */\r\n file?: string;\r\n /** Every dataDir file whose name starts with this prefix — a family of\r\n * timestamped files a bare `file` cannot name (journal archive shards).\r\n * Shredded like files. */\r\n prefix?: string;\r\n /** Directories are removed recursively (no shred - content is files). */\r\n dir?: string;\r\n note: string;\r\n}\r\n\r\nexport const BURN_LIST: BurnTarget[] = [\r\n { file: 'profile.json', note: '画像物化视图' },\r\n { file: 'profile_inbox.jsonl', note: '观察收件箱' },\r\n { file: 'profile_journal.jsonl', note: '画像操作流水' },\r\n // H-19: the rotation shards hold the whole profile history and are written\r\n // in PLAINTEXT — a burn that leaves them behind leaves the memory readable.\r\n { prefix: 'profile_journal.archive-', note: '画像流水归档分片(明文;journal 轮转产物)' },\r\n { file: 'profile_rhythm.json', note: '作息聚合(纯统计)' },\r\n { file: 'screen.json', note: '屏幕快照(加密)' },\r\n { file: 'screen.jpg', note: '截图(加密)' },\r\n { file: 'sent.json', note: '表达记录(加密)' },\r\n { file: 'browse.json', note: '浏览流状态(加密)' },\r\n { file: 'seeds.jsonl', note: '素材池(加密)' },\r\n { file: 'ledger.md', note: '账本(明文,人可读是设计目标)' },\r\n { file: 'envpulse.json', note: '环境温度计快照' },\r\n { file: 'gate.json', note: '闸门运行时计数' },\r\n // v1.9.0 completion (H-19): these carry memory just as much as the rows\r\n // above — the persona report log records WHAT SHE SAID (spoken / seed_ids /\r\n // profile_ids / reason), so a burn that skips it is a burn that lies.\r\n { file: 'profile_snapshot.json', note: '画像快照(journal 轮转基线)' },\r\n { file: 'cursors.json', note: '观察游标(会话事件位点)' },\r\n { file: 'seed_report.jsonl', note: '投递报账日志(加密)' },\r\n { file: 'preference.json', note: '话题偏好统计' },\r\n { dir: 'weekly', note: '周报(画像与素材散文)' },\r\n { dir: 'tmp', note: '解锁临时文件(崩溃残留的解密明文是最该烧的东西)' },\r\n { dir: 'logs', note: '审计与决策日志' },\r\n { dir: 'exports', note: '导出产物' },\r\n];\r\n\r\nconst SETTINGS_DIR = 'settings'; // excluded unless --all\r\n\r\n/** dataDir files matching a prefix target, name-sorted for stable output. */\r\nfunction prefixMatches(guard: PathGuard, paths: WorkspacePaths, prefix: string): string[] {\r\n try {\r\n return fs.readdirSync(guard.assert(paths.dataDir))\r\n .filter((f) => f.startsWith(prefix))\r\n .sort();\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\nexport interface BurnPlanItem {\r\n target: BurnTarget;\r\n exists: boolean;\r\n}\r\n\r\n/** Preview: what exists, what would be removed. No writes. */\r\nexport function planBurn(guard: PathGuard, paths: WorkspacePaths, all = false): BurnPlanItem[] {\r\n const plan: BurnPlanItem[] = [];\r\n for (const t of BURN_LIST) {\r\n const rel = t.file ?? t.dir ?? t.prefix ?? '';\r\n if (!all && rel === SETTINGS_DIR) continue;\r\n let exists = false;\r\n if (t.prefix) {\r\n exists = prefixMatches(guard, paths, t.prefix).length > 0;\r\n } else {\r\n try {\r\n exists = fs.existsSync(guard.assert(path.join(paths.dataDir, rel)));\r\n } catch {\r\n exists = false;\r\n }\r\n }\r\n plan.push({ target: t, exists });\r\n }\r\n if (all) {\r\n plan.push({ target: { dir: SETTINGS_DIR, note: '用户设定(隐私宪章/闸门参数)——仅 --all 连带' }, exists: fs.existsSync(paths.settingsDir) });\r\n }\r\n return plan;\r\n}\r\n\r\n/**\r\n * Execute the burn: overwrite x3 + delete files, remove dirs. Irreversible.\r\n * Returns per-target results and writes a BURN_EVENT into the (recreated)\r\n * journal afterwards - the only trace, on purpose (transparency).\r\n */\r\nexport function executeBurn(\r\n guard: PathGuard,\r\n paths: WorkspacePaths,\r\n opts: { all?: boolean; shredPasses?: number } = {},\r\n): { burned: string[]; missing: string[] } {\r\n const burned: string[] = [];\r\n const missing: string[] = [];\r\n const passes = opts.shredPasses ?? 3;\r\n for (const t of BURN_LIST) {\r\n // H-19: a prefix target is a family of files (timestamped archive shards);\r\n // shred every match individually.\r\n if (t.prefix) {\r\n const matches = prefixMatches(guard, paths, t.prefix);\r\n if (matches.length === 0) {\r\n missing.push(`${t.prefix}*`);\r\n continue;\r\n }\r\n for (const f of matches) {\r\n try {\r\n shredFileSync(guard.assert(path.join(paths.dataDir, f)), passes);\r\n burned.push(f);\r\n } catch { /* vanished between listing and shredding: not fatal */ }\r\n }\r\n continue;\r\n }\r\n const rel = t.file ?? t.dir!;\r\n const abs = path.join(paths.dataDir, rel);\r\n let absCanon: string;\r\n try {\r\n absCanon = guard.assert(abs);\r\n } catch {\r\n missing.push(rel);\r\n continue;\r\n }\r\n if (t.file) {\r\n if (fs.existsSync(absCanon)) {\r\n shredFileSync(absCanon, passes);\r\n burned.push(rel);\r\n } else {\r\n missing.push(rel);\r\n }\r\n } else if (fs.existsSync(absCanon)) {\r\n fs.rmSync(absCanon, { recursive: true, force: true });\r\n burned.push(rel);\r\n } else {\r\n missing.push(rel);\r\n }\r\n }\r\n if (opts.all) {\r\n fs.rmSync(paths.settingsDir, { recursive: true, force: true });\r\n burned.push(SETTINGS_DIR);\r\n }\r\n // BURN_EVENT: recreate the journal; data dir is now re-initializable.\r\n appendAuditLine(guard.assert(path.join(paths.dataDir, 'profile_journal.jsonl')), {\r\n event: 'BURN_EVENT',\r\n burned: burned.length,\r\n missing: missing.length,\r\n settingsPreserved: !opts.all,\r\n });\r\n return { burned, missing };\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,SAAS,aAAa;AACtB,OAAOA,SAAQ;AACf,OAAO,QAAQ;AACf,OAAOC,WAAU;;;ACNjB,OAAO,QAAQ;AACf,OAAO,UAAU;AAkBV,IAAM,YAA0B;AAAA,EACrC,EAAE,MAAM,gBAAgB,MAAM,uCAAS;AAAA,EACvC,EAAE,MAAM,uBAAuB,MAAM,iCAAQ;AAAA,EAC7C,EAAE,MAAM,yBAAyB,MAAM,uCAAS;AAAA;AAAA;AAAA,EAGhD,EAAE,QAAQ,4BAA4B,MAAM,iHAA4B;AAAA,EACxE,EAAE,MAAM,uBAAuB,MAAM,yDAAY;AAAA,EACjD,EAAE,MAAM,eAAe,MAAM,mDAAW;AAAA,EACxC,EAAE,MAAM,cAAc,MAAM,uCAAS;AAAA,EACrC,EAAE,MAAM,aAAa,MAAM,mDAAW;AAAA,EACtC,EAAE,MAAM,eAAe,MAAM,yDAAY;AAAA,EACzC,EAAE,MAAM,eAAe,MAAM,6CAAU;AAAA,EACvC,EAAE,MAAM,aAAa,MAAM,6FAAkB;AAAA,EAC7C,EAAE,MAAM,iBAAiB,MAAM,6CAAU;AAAA,EACzC,EAAE,MAAM,aAAa,MAAM,6CAAU;AAAA;AAAA;AAAA;AAAA,EAIrC,EAAE,MAAM,yBAAyB,MAAM,uEAAqB;AAAA,EAC5D,EAAE,MAAM,gBAAgB,MAAM,2EAAe;AAAA,EAC7C,EAAE,MAAM,qBAAqB,MAAM,+DAAa;AAAA,EAChD,EAAE,MAAM,mBAAmB,MAAM,uCAAS;AAAA,EAC1C,EAAE,KAAK,UAAU,MAAM,qEAAc;AAAA,EACrC,EAAE,KAAK,OAAO,MAAM,mJAA2B;AAAA,EAC/C,EAAE,KAAK,QAAQ,MAAM,6CAAU;AAAA,EAC/B,EAAE,KAAK,WAAW,MAAM,2BAAO;AACjC;AAEA,IAAM,eAAe;AAGrB,SAAS,cAAc,OAAkB,OAAuB,QAA0B;AACxF,MAAI;AACF,WAAO,GAAG,YAAY,MAAM,OAAO,MAAM,OAAO,CAAC,EAC9C,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,CAAC,EAClC,KAAK;AAAA,EACV,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAQO,SAAS,SAAS,OAAkB,OAAuB,MAAM,OAAuB;AAC7F,QAAM,OAAuB,CAAC;AAC9B,aAAW,KAAK,WAAW;AACzB,UAAM,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU;AAC3C,QAAI,CAAC,OAAO,QAAQ,aAAc;AAClC,QAAI,SAAS;AACb,QAAI,EAAE,QAAQ;AACZ,eAAS,cAAc,OAAO,OAAO,EAAE,MAAM,EAAE,SAAS;AAAA,IAC1D,OAAO;AACL,UAAI;AACF,iBAAS,GAAG,WAAW,MAAM,OAAO,KAAK,KAAK,MAAM,SAAS,GAAG,CAAC,CAAC;AAAA,MACpE,QAAQ;AACN,iBAAS;AAAA,MACX;AAAA,IACF;AACA,SAAK,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC;AAAA,EACjC;AACA,MAAI,KAAK;AACP,SAAK,KAAK,EAAE,QAAQ,EAAE,KAAK,cAAc,MAAM,6HAA8B,GAAG,QAAQ,GAAG,WAAW,MAAM,WAAW,EAAE,CAAC;AAAA,EAC5H;AACA,SAAO;AACT;AAOO,SAAS,YACd,OACA,OACA,OAAgD,CAAC,GACR;AACzC,QAAM,SAAmB,CAAC;AAC1B,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAS,KAAK,eAAe;AACnC,aAAW,KAAK,WAAW;AAGzB,QAAI,EAAE,QAAQ;AACZ,YAAM,UAAU,cAAc,OAAO,OAAO,EAAE,MAAM;AACpD,UAAI,QAAQ,WAAW,GAAG;AACxB,gBAAQ,KAAK,GAAG,EAAE,MAAM,GAAG;AAC3B;AAAA,MACF;AACA,iBAAW,KAAK,SAAS;AACvB,YAAI;AACF,wBAAc,MAAM,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC,CAAC,GAAG,MAAM;AAC/D,iBAAO,KAAK,CAAC;AAAA,QACf,QAAQ;AAAA,QAA0D;AAAA,MACpE;AACA;AAAA,IACF;AACA,UAAM,MAAM,EAAE,QAAQ,EAAE;AACxB,UAAM,MAAM,KAAK,KAAK,MAAM,SAAS,GAAG;AACxC,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,OAAO,GAAG;AAAA,IAC7B,QAAQ;AACN,cAAQ,KAAK,GAAG;AAChB;AAAA,IACF;AACA,QAAI,EAAE,MAAM;AACV,UAAI,GAAG,WAAW,QAAQ,GAAG;AAC3B,sBAAc,UAAU,MAAM;AAC9B,eAAO,KAAK,GAAG;AAAA,MACjB,OAAO;AACL,gBAAQ,KAAK,GAAG;AAAA,MAClB;AAAA,IACF,WAAW,GAAG,WAAW,QAAQ,GAAG;AAClC,SAAG,OAAO,UAAU,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACpD,aAAO,KAAK,GAAG;AAAA,IACjB,OAAO;AACL,cAAQ,KAAK,GAAG;AAAA,IAClB;AAAA,EACF;AACA,MAAI,KAAK,KAAK;AACZ,OAAG,OAAO,MAAM,aAAa,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC7D,WAAO,KAAK,YAAY;AAAA,EAC1B;AAEA,kBAAgB,MAAM,OAAO,KAAK,KAAK,MAAM,SAAS,uBAAuB,CAAC,GAAG;AAAA,IAC/E,OAAO;AAAA,IACP,QAAQ,OAAO;AAAA,IACf,SAAS,QAAQ;AAAA,IACjB,mBAAmB,CAAC,KAAK;AAAA,EAC3B,CAAC;AACD,SAAO,EAAE,QAAQ,QAAQ;AAC3B;;;AD5FA,SAAS,QAAgB;AACvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,gBAAwB;AAC/B,MAAI;AACF,WAAOC,IAAG,aAAa,GAAG,MAAM,EAAE,KAAK;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,KAAK,MAAgB,MAAkC;AAC9D,QAAM,IAAI,KAAK,QAAQ,IAAI;AAC3B,SAAO,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI;AAChC;AAMA,IAAM,YAAY,CAAC,QAAQ,UAAU,SAAS,SAAS;AACvD,IAAM,eAAe,CAAC,QAAQ,UAAU,UAAU,QAAQ,SAAS;AAEnE,SAAS,UAA4B,MAAgB,MAAc,SAAsC;AACvG,QAAM,IAAI,KAAK,MAAM,IAAI;AACzB,MAAI,MAAM,OAAW,QAAO;AAC5B,SAAQ,QAA8B,SAAS,CAAC,IAAK,IAAU;AACjE;AAGA,eAAe,UAAU,SAAsC;AAC7D,QAAM,WAAW,MAAM,OAAO,mBAAwB;AACtD,QAAM,KAAK,SAAS,gBAAgB,EAAE,OAAO,QAAQ,MAAM,CAAC;AAC5D,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,QAAS,KAAI,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC;AAC/D,KAAG,MAAM;AACT,SAAO;AACT;AAmBA,eAAe,WAAW,GAA4B;AACpD,QAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,UAAQ,IAAI,KAAK,UAAU;AAAA,IACzB,SAAS,MAAM;AAAA,IACf,sBAAsB,OAAO,UAAU;AAAA,IACvC,cAAc,OAAO,KAAK;AAAA,IAC1B,gBAAgB,OAAO,MAAM;AAAA,IAC7B,YAAY,OAAO,QAAQ;AAAA,EAC7B,GAAG,MAAM,CAAC,CAAC;AACX,SAAO;AACT;AAEA,eAAe,UAAU,GAA4B;AACnD,QAAM,EAAE,OAAO,QAAQ,KAAK,MAAM,WAAW,UAAU,IAAI;AAC3D,UAAQ,KAAK;AAAA,IACX,KAAK,OAAO;AACV,UAAI,OAAO,KAAK,CAAC,GAAG,KAAK,KAAK;AAC9B,UAAI,SAAS,IAAK,QAAO,cAAc;AACvC,UAAI,CAAC,MAAM;AACT,gBAAQ,MAAM,+EAA+E;AAC7F,eAAO;AAAA,MACT;AACA,YAAM,SAAS,QAAQ,OAAO,WAAW,QAAQ;AAAA,QAC/C;AAAA,QACA,KAAK,UAAU,MAAM,SAAS,SAAS;AAAA,QACvC,QAAQ,UAAU,MAAM,YAAY,YAAY;AAAA,QAChD,OAAO,KAAK,MAAM,SAAS;AAAA,QAC3B,YAAY,KAAK,MAAM,cAAc,IAAI,OAAO,KAAK,MAAM,cAAc,CAAC,IAAI;AAAA,MAChF,GAAG,KAAK,IAAI,GAAG,SAAS;AACxB,UAAI,OAAO,SAAS,aAAa;AAC/B,gBAAQ,IAAI,0BAA0B,OAAO,KAAK,EAAE,oBAAoB;AACxE,eAAO;AAAA,MACT;AACA,cAAQ,IAAI,GAAG,OAAO,KAAK,YAAY,CAAC,IAAI,OAAO,KAAK,EAAE,KACnD,YAAY,SAAS,OAAO,SAAS,CAAC,EAAE,MAAM,IAAI,OAAO,MAAM,SAAS,OAC5E,OAAO,UAAU,aAAa,OAAO,QAAQ,EAAE,KAAK,OAAO,QAAQ,YAAY,MAAM,GAAG;AAC3F,aAAO;AAAA,IACT;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,KAAK,SAAS,OAAO,SAAS;AACpC,YAAM,QAAQ,KAAK,SAAS,YAAY,IAAI,cAAc,EAAE,IAAI,YAAY,EAAE;AAC9E,iBAAW,KAAK,OAAO;AACrB,cAAM,UAAU,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,MAAM,EAAE,MAAM,KAAK,KAAU;AAC3E,gBAAQ,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,MAAM,MAAM,OAAO,SAAS,EAAE,IAAI,GAChE,EAAE,YAAY,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE;AAAA,MACvD;AACA,UAAI,MAAM,WAAW,EAAG,SAAQ,IAAI,SAAS;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,KAAK,WAAW;AACd,YAAM,IAAI,YAAY,OAAO,WAAW,QAAQ,KAAK,CAAC,KAAK,IAAI,KAAK,IAAI,GAAG,SAAS;AACpF,UAAI,CAAC,GAAG;AACN,gBAAQ,IAAI,WAAW;AACvB,eAAO;AAAA,MACT;AACA,cAAQ,IAAI,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,WAAW,aAAa,4BAA4B,EAAE,EAAE;AACxG,aAAO;AAAA,IACT;AAAA,IACA,KAAK,WAAW;AACd,YAAM,IAAI,gBAAgB,OAAO,WAAW,KAAK,CAAC,KAAK,IAAI,aAAa,KAAK,IAAI,GAAG,SAAS;AAC7F,UAAI,CAAC,GAAG;AACN,gBAAQ,IAAI,WAAW;AACvB,eAAO;AAAA,MACT;AACA,cAAQ,IAAI,YAAY,EAAE,EAAE,EAAE;AAC9B,aAAO;AAAA,IACT;AAAA,IACA,KAAK,MAAM;AACT,YAAM,SAAS,OAAO,OAAO,WAAW,QAAQ,KAAK,IAAI,GAAG,SAAS;AACrE,cAAQ,IAAI,eAAe,OAAO,QAAQ,YAAY,OAAO,OAAO,eAAe,OAAO,SAAS,oBAC7E,OAAO,cAAc,cAAc,OAAO,WAAW,IAAI,OAAO,MAAM,SAAS,YACvF,cAAc,SAAS,OAAO,SAAS,CAAC,EAAE,MAAM,IAAI,OAAO,MAAM,UAAU,EAAE;AAC3F,aAAO;AAAA,IACT;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,KAAK,SAAS,OAAO,SAAS;AACpC,cAAQ,IAAI,KAAK,UAAU;AAAA,QACzB,QAAQ,YAAY,EAAE,EAAE;AAAA,QACxB,UAAU,cAAc,EAAE,EAAE;AAAA,QAC5B,KAAK,OAAO,MAAM;AAAA,QAClB,KAAK,GAAG;AAAA,MACV,GAAG,MAAM,CAAC,CAAC;AACX,aAAO;AAAA,IACT;AAAA,IACA;AACE,cAAQ,MAAM,6BAA6B,GAAG,EAAE;AAChD,aAAO;AAAA,EACX;AACF;AAEA,eAAe,WAAW,GAA4B;AACpD,QAAM,EAAE,OAAO,KAAK,MAAM,WAAW,IAAI;AACzC,UAAQ,KAAK;AAAA,IACX,KAAK,OAAO;AACV,UAAI,OAAO,KAAK,KAAK,GAAG,EAAE,KAAK;AAC/B,UAAI,SAAS,IAAK,QAAO,cAAc;AACvC,UAAI,CAAC,MAAM;AACT,gBAAQ,MAAM,4BAA4B;AAC1C,eAAO;AAAA,MACT;AACA,YAAM,IAAI,YAAY,OAAO,YAAY,IAAI;AAC7C,cAAQ,IAAI,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE;AAClD,aAAO;AAAA,IACT;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,EAAE,QAAQ,IAAI,WAAW,OAAO,UAAU;AAChD,YAAM,QAAQ,KAAK,SAAS,WAAW,IAAI,YAAY,OAAO,UAAU,IAAI;AAC5E,iBAAW,KAAK,MAAO,SAAQ,IAAI,GAAG,EAAE,WAAW,SAAS,MAAM,GAAG,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE;AACjH,UAAI,MAAM,WAAW,EAAG,SAAQ,IAAI,SAAS;AAC7C,aAAO;AAAA,IACT;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,MAAM,KAAK,KAAK,GAAG,EAAE,KAAK;AAChC,UAAI,CAAC,KAAK;AACR,gBAAQ,MAAM,mCAAmC;AACjD,eAAO;AAAA,MACT;AACA,YAAM,IAAI,SAAS,OAAO,YAAY,GAAG;AACzC,UAAI,CAAC,GAAG;AACN,gBAAQ,IAAI,WAAW;AACvB,eAAO;AAAA,MACT;AACA,cAAQ,IAAI,SAAS,EAAE,EAAE,EAAE;AAC3B,aAAO;AAAA,IACT;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,IAAI;AACV,UAAI,CAACA,IAAG,WAAW,CAAC,GAAG;AACrB,gBAAQ,IAAI,8BAA8B,CAAC,GAAG;AAAA,MAChD;AACA,YAAM,OAAO,CAAC,MAAM,SAAS,IAAI,CAAC,GAAG,EAAE,UAAU,MAAM,OAAO,SAAS,CAAC,EAAE,MAAM;AAChF,cAAQ,IAAI,UAAU,CAAC,EAAE;AACzB,aAAO;AAAA,IACT;AAAA,IACA;AACE,cAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,aAAO;AAAA,EACX;AACF;AAEA,eAAe,WAAW,GAA4B;AACpD,QAAM,EAAE,OAAO,OAAO,KAAK,KAAK,IAAI;AACpC,QAAM,UAAU,kBAAkB,OAAO,MAAM,OAAO;AACtD,MAAI,QAAQ,UAAU,QAAQ,QAAW;AACvC,QAAI,QAAQ,WAAW,EAAG,SAAQ,IAAI,8DAAyD;AAC/F,eAAW,KAAK,QAAS,SAAQ,IAAI,GAAG,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,EAAE,IAAI,MAAM,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG;AAChH,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,QAAQ;AAClB,UAAM,OAAO,KAAK,CAAC,KAAK,QAAQ,CAAC,GAAG;AACpC,QAAI,CAAC,MAAM;AACT,cAAQ,IAAI,kBAAkB;AAC9B,aAAO;AAAA,IACT;AACA,UAAM,MAAM,iBAAiB,OAAO,MAAM,SAAS,IAAI;AACvD,QAAI,CAAC,KAAK;AACR,cAAQ,MAAM,mBAAmB,IAAI,qCAAqC,cAAc,MAAM,OAAO,CAAC,GAAG;AACzG,aAAO;AAAA,IACT;AACA,YAAQ,IAAI,KAAK,IAAI,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,MAAM,IAAI,MAAM,GAAG;AACpF,YAAQ,IAAI,IAAI,IAAI;AACpB,WAAO;AAAA,EACT;AACA,UAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,SAAO;AACT;AAEA,eAAe,SAAS,GAA4B;AAClD,QAAM,EAAE,OAAO,QAAQ,KAAK,MAAM,YAAY,IAAI;AAClD,MAAI,QAAQ,WAAW;AACrB,YAAQ,MAAM,iCAAiC;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,MAAM,KAAK,SAAS,WAAW;AACrC,QAAM,WAAWC,MAAK,KAAK,MAAM,SAAS,gBAAgB;AAC1D,QAAM,OAAO,OAAO,UAAU,gBAAgB;AAC9C,QAAM,OAAO,OAAO,UAAU,kBAAkB;AAChD,MAAI,KAAK;AACP,YAAQ,IAAI,yBAAyB,QAAQ,OAAO,OAAO,UAAU,aAAa,SACxE,WAAW,OAAO,OAAO,UAAU,eAAe,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,QAAM,IAAI,eAAe,UAAU,IAAI;AACvC,QAAM,IAAI,eAAe,aAAa,IAAI;AAC1C,kBAAgB,aAAa,EAAE,OAAO,aAAa,iBAAiB,GAAG,iBAAiB,EAAE,CAAC;AAC3F,UAAQ,IAAI,mBAAmB,CAAC,aAAa,CAAC,EAAE;AAChD,SAAO;AACT;AAEA,eAAe,WAAW,GAA4B;AACpD,QAAM,EAAE,OAAO,OAAO,QAAQ,KAAK,KAAK,IAAI;AAC5C,UAAQ,KAAK;AAAA,IACX,KAAK,UAAU;AACb,YAAM,KAAK,aAAa,OAAO,KAAK;AACpC,YAAM,YAAY,cAAc,KAAK;AACrC,cAAQ,IAAI,KAAK,UAAU;AAAA,QACzB,cAAc,GAAG,OAAO,iBAAiB,IAAI,KAAK,GAAG,OAAO,cAAc,EAAE,YAAY,IAAI;AAAA,QAC5F,YAAY,GAAG,OAAO;AAAA,QACtB,eAAe,UAAU,WAAW,UAAU;AAAA,MAChD,GAAG,MAAM,CAAC,CAAC;AACX,aAAO;AAAA,IACT;AAAA,IACA,KAAK,OAAO;AACV,YAAM,SAAS,oBAAI,KAAK;AACxB,aAAO,SAAS,IAAI,GAAG,GAAG,CAAC;AAC3B,YAAM,SAAS,aAAa,OAAO,OAAO,QAAQ,MAAM;AACxD,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C,aAAO;AAAA,IACT;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,QAAQ,KAAK,KAAK,GAAG,EAAE,KAAK;AAClC,UAAI,CAAC,OAAO;AACV,gBAAQ,MAAM,4BAA4B;AAC1C,eAAO;AAAA,MACT;AACA,YAAM,IAAI,eAAe,OAAO,OAAO,KAAK;AAC5C,cAAQ,IAAI,KAAK,UAAU,CAAC,CAAC;AAC7B,aAAO;AAAA,IACT;AAAA,IACA;AACE,cAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,aAAO;AAAA,EACX;AACF;AAEA,eAAe,WAAW,GAA4B;AACpD,QAAM,EAAE,OAAO,IAAI,IAAI;AACvB,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,cAAQ,IAAI,MAAM,iBAAiB,KAAK,IAAI,oBAAoB,gBAAgB;AAChF,aAAO;AAAA,IACT,KAAK;AACH,YAAM,iBAAiB,KAAK;AAC5B,cAAQ,IAAI,oBAAoB;AAChC,aAAO;AAAA,IACT,KAAK;AACH,cAAQ,IAAI,MAAM,mBAAmB,KAAK,IAAI,SAAS,QAAQ;AAC/D,aAAO;AAAA,IACT;AACE,cAAQ,MAAM,mCAAmC;AACjD,aAAO;AAAA,EACX;AACF;AAEA,eAAe,YAAY,GAA4B;AACrD,QAAM,EAAE,OAAO,OAAO,KAAK,KAAK,IAAI;AACpC,QAAM,MAAM,YAAY,OAAO,gBAAgB,MAAM,OAAO,CAAC;AAC7D,QAAM,MAAM,KAAK,SAAS,OAAO;AACjC,UAAQ,KAAK;AAAA,IACX,KAAK,QAAQ;AACX,UAAI,IAAI;AACR,iBAAW,KAAK,CAAC,YAAY,YAAY,QAAQ,KAAK,GAAY;AAChE,mBAAW,KAAK,IAAI,WAAW,CAAC,EAAG,SAAS;AAC1C,cAAI,CAAC,OAAO,EAAE,YAAY,KAAM;AAChC,eAAK;AACL,kBAAQ,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,QAAQ,SAChE,EAAE,WAAW,QAAQ,CAAC,CAAC,GAAG,EAAE,cAAc,8BAAU,EAAE,GAAG,EAAE,UAAU,oBAAU,EAAE,KAAK,EAAE,QAAQ,MAAM,GAAG,EAAE,CAAC,EAAE;AAAA,QAC3H;AAAA,MACF;AACA,UAAI,MAAM,EAAG,SAAQ,IAAI,SAAS;AAClC,aAAO;AAAA,IACT;AAAA,IACA,KAAK,UAAU;AACb,YAAM,QAAQ,CAAC,+BAAU,oBAAI,KAAK,GAAE,YAAY,CAAC,IAAI,EAAE;AACvD,iBAAW,KAAK,CAAC,YAAY,YAAY,QAAQ,KAAK,GAAY;AAChE,cAAM,KAAK,MAAM,CAAC,EAAE;AACpB,mBAAW,KAAK,IAAI,WAAW,CAAC,EAAG,SAAS;AAC1C,cAAI,EAAE,YAAY,KAAM;AACxB,gBAAM,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,OAAO,UAAU,EAAE,WAAW,QAAQ,CAAC,CAAC,KAAK,EAAE,QAAQ,GAAG;AAAA,QACzG;AAAA,MACF;AACA,YAAM,MAAMA,MAAK,KAAK,MAAM,YAAY,kBAAkB,KAAK,IAAI,CAAC,KAAK;AACzE,gBAAU,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI;AAC7C,cAAQ,IAAI,aAAa,GAAG,EAAE;AAC9B,aAAO;AAAA,IACT;AAAA,IACA,KAAK,UAAU;AACb,YAAM,IAAI,cAAc,OAAO,MAAM,OAAO;AAC5C,cAAQ,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AACtC,aAAO,EAAE,KAAK,IAAI;AAAA,IACpB;AAAA,IACA,KAAK,WAAW;AACd,YAAM,IAAI,eAAe,OAAO,MAAM,SAAS,EAAE,OAAO,KAAK,SAAS,SAAS,EAAE,CAAC;AAClF,cAAQ,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AACtC,aAAO,EAAE,KAAK,IAAI;AAAA,IACpB;AAAA,IACA,KAAK,QAAQ;AACX,UAAI,CAAC,KAAK,SAAS,OAAO,GAAG;AAC3B,gBAAQ,IAAI,sEAAsE;AAClF,eAAO;AAAA,MACT;AACA,iBAAW,KAAK,CAAC,gBAAgB,uBAAuB,uBAAuB,GAAG;AAChF,cAAM,MAAM,MAAM,OAAOA,MAAK,KAAK,MAAM,SAAS,CAAC,CAAC;AACpD,YAAID,IAAG,WAAW,GAAG,EAAG,CAAAA,IAAG,OAAO,KAAK,EAAE,OAAO,KAAK,CAAC;AAAA,MACxD;AACA,cAAQ,IAAI,yDAAyD;AACrE,aAAO;AAAA,IACT;AAAA,IACA,KAAK,YAAY;AACf,YAAM,EAAE,aAAa,iBAAiB,mBAAmB,IAAI,MAAM,OAAO,yBAAwB;AAClG,YAAM,EAAE,QAAQ,MAAM,IAAI,YAAY,OAAO,MAAM,OAAO;AAC1D,cAAQ,IAAI,WAAW,KAAK,6BAAS,kBAAkB,QAAG;AAC1D,UAAI,CAAC,UAAU,CAAC,KAAK,SAAS,SAAS,GAAG;AACxC,gBAAQ,IAAI,2GAA2B;AACvC,eAAO;AAAA,MACT;AACA,YAAM,SAAS,gBAAgB,OAAO,MAAM,OAAO;AACnD,UAAI,CAAC,OAAO,IAAI;AACd,gBAAQ,IAAI,2BAAO,OAAO,MAAM,EAAE;AAClC,eAAO;AAAA,MACT;AACA,cAAQ,IAAI,sBAAO,OAAO,MAAM,6CAAoB,OAAO,UAAU,4BAAQ,OAAO,WAAW,6CAAoB;AACnH,cAAQ,IAAI,uHAAkC;AAC9C,aAAO;AAAA,IACT;AAAA,IACA;AACE,cAAQ,MAAM,+BAA+B,GAAG,EAAE;AAClD,aAAO;AAAA,EACX;AACF;AAEA,eAAe,SAAS,GAA4B;AAClD,QAAM,EAAE,OAAO,OAAO,KAAK,IAAI;AAC/B,QAAM,MAAM,KAAK,SAAS,OAAO;AACjC,QAAM,MAAM,KAAK,SAAS,OAAO;AACjC,QAAM,OAAO,SAAS,OAAO,OAAO,GAAG;AACvC,UAAQ,IAAI,mDAAW;AACvB,aAAW,KAAK,KAAM,SAAQ,IAAI,MAAM,EAAE,SAAS,iBAAO,UAAK,KAAK,EAAE,OAAO,QAAQ,EAAE,OAAO,GAAG,KAAK,EAAE,OAAO,IAAI,EAAE;AACrH,MAAI,CAAC,KAAK;AACR,YAAQ,IAAI,2HAAsC,MAAM,gDAAkB,0CAAY,QAAG;AACzF,WAAO;AAAA,EACT;AACA,QAAM,SAAS,YAAY,OAAO,OAAO,EAAE,IAAI,CAAC;AAChD,UAAQ,IAAI,WAAW,OAAO,OAAO,MAAM,yBAAe,OAAO,QAAQ,MAAM,SAAI;AACnF,SAAO;AACT;AAEA,eAAe,YAAY,GAA4B;AACrD,QAAM,EAAE,OAAO,OAAO,KAAK,KAAK,IAAI;AACpC,MAAI,QAAQ,UAAU;AACpB,UAAM,aAAaC,MAAK,KAAK,MAAM,YAAY,qBAAoB,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ;AAChH,UAAM,UAAU,KAAK,CAAC,KAAK;AAC3B,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,OAAO,OAAO;AAAA,IACjC,QAAQ;AACN,cAAQ,MAAM,8IAAgC,OAAO;AAAA,gCAAU,UAAU,EAAE;AAC3E,aAAO;AAAA,IACT;AACA,UAAM,CAAC,KAAK,GAAG,IAAI,MAAM,UAAU,CAAC,oFAAmB,8CAAW,CAAC;AACnE,QAAI,CAAC,OAAO,QAAQ,KAAK;AACvB,cAAQ,MAAM,4FAAiB;AAC/B,aAAO;AAAA,IACT;AACA,UAAM,EAAE,SAAS,SAAS,IAAI,wBAAwB,OAAO,KAAK;AAClE,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI,wGAAwB;AACpC,aAAO;AAAA,IACT;AACA,IAAAD,IAAG,UAAUC,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,wBAAoB,UAAU,iBAAiB,SAAS,GAAG,CAAC;AAC5D,YAAQ,IAAI,sBAAO,QAAQ,MAAM,8BAAU,QAAQ,EAAE;AACrD,YAAQ,IAAI,uBAAQ,SAAS,OAAO,KAAK,IAAI,CAAC,EAAE;AAChD,QAAI,SAAS,QAAQ,SAAS,EAAG,SAAQ,IAAI,qDAAa,SAAS,QAAQ,KAAK,IAAI,CAAC,EAAE;AACvF,YAAQ,IAAI,8MAAyC;AACrD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,UAAU;AACpB,UAAM,SAAS,KAAK,CAAC;AACrB,QAAI,CAAC,QAAQ;AACX,cAAQ,MAAM,wCAAwC;AACtD,aAAO;AAAA,IACT;AACA,UAAM,CAAC,EAAE,IAAI,MAAM,UAAU,CAAC,8CAAW,CAAC;AAC1C,QAAI;AACJ,QAAI;AACF,aAAOD,IAAG,aAAa,QAAQ,MAAM;AAAA,IACvC,QAAQ;AACN,cAAQ,MAAM,6CAAU,MAAM,EAAE;AAChC,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI;AACF,cAAQ,iBAAiB,MAAM,MAAM,EAAE;AAAA,IACzC,SAAS,GAAG;AACV,cAAQ,MAAM,OAAO,aAAa,QAAQ,EAAE,UAAU,CAAC,CAAC;AACxD,aAAO;AAAA,IACT;AACA,UAAM,SAAS,sBAAsB,OAAO,OAAO,MAAM,KAAK;AAC9D,YAAQ,IAAI,gBAAM,OAAO,SAAS,MAAM,yCAAW,OAAO,SAAS,MAAM,2DAAc,OAAO,QAAQ,MAAM,eAAK;AACjH,eAAW,KAAK,OAAO,SAAU,SAAQ,IAAI,mBAAS,CAAC,EAAE;AACzD,eAAW,KAAK,OAAO,SAAU,SAAQ,IAAI,mBAAS,CAAC,gBAAgB;AACvE,QAAI,OAAO,QAAQ,SAAS,EAAG,SAAQ,IAAI,mBAAS,OAAO,QAAQ,KAAK,IAAI,CAAC,EAAE;AAC/E,QAAI,OAAO,MAAO,SAAQ,IAAI,+CAAY,OAAO,KAAK,EAAE;AACxD,YAAQ,IAAI,2CAAa;AACzB,WAAO,OAAO,QAAQ,IAAI;AAAA,EAC5B;AACA,UAAQ,MAAM,+BAA+B,GAAG,EAAE;AAClD,SAAO;AACT;AAEA,eAAe,aAAa,GAA4B;AACtD,QAAM,EAAE,OAAO,MAAM,IAAI;AAGzB,QAAM,OAAOC,MAAK,KAAK,GAAG,QAAQ,GAAG,QAAQ,UAAU;AACvD,MAAI,QAAQ;AACZ,MAAID,IAAG,WAAW,IAAI,GAAG;AACvB,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,6BAAyB;AAChE,UAAM,MAAM,cAAc,OAAO,KAAK;AACtC,eAAW,QAAQA,IAAG,YAAY,IAAI,GAAG;AACvC,iBAAW,MAAMA,IAAG,YAAYC,MAAK,KAAK,MAAM,IAAI,CAAC,GAAG;AACtD,iBAAS;AACT,cAAM,OAAO,OAAO,MAAM,sCAAa;AACvC,gBAAQ,IAAI,GAAG,EAAE,MAAM,IAAI,IAAI,IAAI,EAAE;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,EAAG,SAAQ,IAAI,+BAA+B;AAC5D,SAAO;AACT;AAEA,eAAe,SAAS,GAA4B;AAClD,QAAM,EAAE,OAAO,OAAO,KAAK,KAAK,IAAI;AACpC,QAAM,EAAE,cAAc,YAAY,cAAc,IAAI,MAAM,OAAO,yBAAqB;AACtF,UAAQ,KAAK;AAAA,IACX,KAAK,QAAQ;AACX,YAAM,OAAO,aAAa,OAAO,MAAM,WAAW;AAClD,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,6BAAyB;AAChE,YAAM,MAAM,cAAc,OAAO,KAAK;AACtC,UAAI,IAAK,SAAQ,IAAI,6BAAS,GAAG,2GAAgC;AACjE,iBAAW,KAAK,KAAK,UAAU;AAC7B,gBAAQ,IAAI,GAAG,EAAE,SAAS,aAAa,EAAE,UAAU,WAAM,MAAG,YAAY,EAAE,UAAU,WAAM,MAAG,EAAE;AAAA,MACjG;AACA,UAAI,KAAK,SAAS,WAAW,KAAK,CAAC,IAAK,SAAQ,IAAI,0EAAqE;AACzH,aAAO;AAAA,IACT;AAAA,IACA,KAAK,OAAO;AACV,YAAM,KAAK,KAAK,CAAC;AACjB,UAAI,CAAC,MAAM,CAAC,GAAG,WAAW,UAAU,GAAG;AACrC,gBAAQ,MAAM,8EAA8E;AAC5F,eAAO;AAAA,MACT;AACA,YAAM,UAAU,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,gBAAgB;AAC5E,YAAM,UAAU,CAAC,KAAK,SAAS,cAAc,KAAK,CAAC,KAAK,SAAS,gBAAgB;AACjF,YAAM,IAAI,WAAW,OAAO,MAAM,aAAa,IAAI,EAAE,SAAS,QAAQ,CAAC;AACvE,cAAQ,IAAI,SAAS,EAAE,SAAS,YAAY,EAAE,OAAO,YAAY,EAAE,OAAO,EAAE;AAC5E,aAAO;AAAA,IACT;AAAA,IACA,KAAK,UAAU;AACb,YAAM,KAAK,KAAK,CAAC;AACjB,UAAI,CAAC,IAAI;AACP,gBAAQ,MAAM,gCAAgC;AAC9C,eAAO;AAAA,MACT;AACA,cAAQ,IAAI,cAAc,OAAO,MAAM,aAAa,EAAE,IAAI,WAAW,EAAE,KAAK,WAAW;AAKvF,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,6BAAyB;AACnE,UAAI,iBAAiB,OAAO,OAAO,EAAE,GAAG;AACtC,gBAAQ,IAAI,kPAA0C;AAAA,MACxD;AACA,aAAO;AAAA,IACT;AAAA,IACA;AACE,cAAQ,MAAM,yDAAyD;AACvE,aAAO;AAAA,EACX;AACF;AAEA,eAAe,WAAW,GAA4B;AACpD,QAAM,EAAE,KAAK,MAAM,KAAK,IAAI;AAC5B,QAAM,KAAK,KAAK,MAAM,MAAM,KAAK;AAGjC,QAAM,OAAO,2BAA2B;AACxC,QAAM,SAAS,aAAa,YAAY,KAAK,IAAI,IAAI;AACrD,MAAI,QAAQ,UAAa,QAAQ,UAAU;AACzC,YAAQ,IAAI,iBAAiB,OAAO,EAAE,EAAE;AACxC,YAAQ,IAAI,iBAAiB,OAAO,YAAY,QAAQ,IAAI,EAAE;AAC9D,YAAQ,IAAI,iBAAiB,OAAO,GAAG,EAAE;AACzC,YAAQ,IAAI,iBAAiB,OAAO,cAAc,gCAAgC,EAAE;AACpF,QAAI,OAAO,WAAW;AACpB,cAAQ;AAAA,QACN,iBAAiB,OAAO,qBAAqB,iCAAiC,sEAAsE;AAAA,MACtJ;AAAA,IACF;AACA,QAAI,CAAC,OAAO,WAAW;AACrB,cAAQ,IAAI,4FAA4F;AACxG,cAAQ,IAAI,oCAAoC;AAAA,IAClD;AACA,WAAO,OAAO,YAAY,IAAI;AAAA,EAChC;AACA,MAAI,QAAQ,WAAW;AACrB,UAAM,SAAS,qBAAqB;AAAA,MAClC,WAAW,YAAY;AAAA,MACvB;AAAA,MACA;AAAA,MACA,OAAO,KAAK,SAAS,SAAS;AAAA,IAChC,CAAC;AACD,YAAQ,IAAI,gBAAgB,MAAM,CAAC;AACnC,WAAO,OAAO,WAAW,UAAU,IAAI;AAAA,EACzC;AACA,UAAQ,MAAM,4DAA4D;AAC1E,SAAO;AACT;AAGA,IAAM,QAA8B;AAAA,EAClC,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AACV;AAEA,eAAsB,KAAK,MAAiC;AAC1D,QAAM,CAAC,KAAK,KAAK,GAAG,IAAI,IAAI;AAC5B,MAAI,CAAC,OAAO,QAAQ,UAAU,QAAQ,UAAU;AAC9C,YAAQ,IAAI,MAAM,CAAC;AACnB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,GAAG;AACtB,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,oBAAoB,GAAG,EAAE;AACvC,YAAQ,IAAI,MAAM,CAAC;AACnB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,cAAc;AAC5B,QAAM,QAAQ,gBAAgB,MAAM,OAAO;AAC3C,QAAM,SAAS,WAAW,OAAO,MAAM,WAAW,MAAM,WAAW;AACnE,QAAM,cAAcA,MAAK,KAAK,MAAM,SAAS,iBAAiB;AAE9D,QAAM,YAAY,CAAC,UAAyC;AAC1D,QAAI;AACF,sBAAgB,aAAa,KAAK;AAAA,IACpC,QAAQ;AAAA,IAA2C;AAAA,EACrD;AACA,SAAO,KAAK;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,cAAc,MAAM,OAAO;AAAA,IACtC,YAAY,eAAe,MAAM,OAAO;AAAA,IACxC;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEA,IAAI,QAAQ,KAAK,CAAC,KAAK,YAAY,QAAQ,IAAI,IAAI,WAAW,QAAQ,KAAK,CAAC,EAAE,QAAQ,OAAO,GAAG,CAAC,EAAE,EAAE,MAAM;AAEzG,OAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,EAC5B,KAAK,CAAC,SAAS;AAAE,YAAQ,WAAW;AAAA,EAAM,CAAC,EAC3C,MAAM,CAAC,MAAe;AAErB,YAAQ,MAAM,CAAC;AACf,YAAQ,WAAW;AAAA,EACrB,CAAC;AACL;","names":["fs","path","fs","path"]}