@agimon-ai/doompi 0.0.1-alpha.53 → 0.0.1-alpha.54

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.
@@ -1,2 +1,2 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:crypto");n=e.__toESM(n,1);let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);const a=`.git`,o=`repo`;function s(e){let n=t.default.resolve(e);try{return r.default.realpathSync.native(n)}catch{return n}}function c(e){let n=t.default.join(e,a);try{let i=r.default.statSync(n);if(i.isDirectory())return s(n);if(!i.isFile())return;let a=/^gitdir:\s*(.+)$/imu.exec(r.default.readFileSync(n,`utf8`).trim());return a?.[1]?s(t.default.resolve(e,a[1].trim())):void 0}catch{return}}function l(e){let n=c(e);if(n)try{let e=r.default.readFileSync(t.default.join(n,`commondir`),`utf8`).trim();return e?s(t.default.resolve(n,e)):n}catch{return n}}function u(e){return n.default.createHash(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,32)}function d(e,t){let n=e.normalize(`NFKD`).replace(/[\u0300-\u036f]/gu,``).replace(/[^A-Za-z0-9._-]+/gu,`-`).replace(/^[._-]+|[._-]+$/gu,``).slice(0,48).replace(/[._-]+$/gu,``);return n===``||n===`.`||n===`..`?t:n}function f(e,n){if(!e)return d(t.default.basename(n),o);let r=t.default.basename(e);return d(r===a?t.default.basename(t.default.dirname(e)):r,o)}function p(e,n=i.default.homedir()){let r=s(e),a=l(r),o=a?`git:${a}`:`root:${r}`,c={repositoryId:u(o),worktreeId:u(`${o}\0worktree:${r}`)},p=f(a,r),m=d(t.default.basename(r),`worktree`),h=t.default.join(t.default.resolve(n),`.pi`,`.doom`,`sync`),g=t.default.join(h,`registrations`),_=t.default.join(h,`${p}--${c.repositoryId}`),v=t.default.join(_,`worktrees`,`${m}--${c.worktreeId}`),y=t.default.join(r,`.pi`,`doom`);return{root:r,identity:c,repositoryLabel:p,worktreeLabel:m,generatedRoot:h,registrationsDirectory:g,registrationPath:t.default.join(g,c.repositoryId,`${c.worktreeId}.json`),repositoryDirectory:_,sharedCacheDirectory:t.default.join(_,`shared-cache`),directory:v,generationsDirectory:t.default.join(v,`generations`),statePath:t.default.join(v,`state.json`),legacyDirectory:y,legacyStatePath:t.default.join(y,`state.json`)}}const m=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;function h(e,n){if(!m.test(n)||n===`.`||n===`..`)throw Error(`Invalid Doom sync generation id: ${n}`);return t.default.join(e.generationsDirectory,n)}function g(e){try{if(r.default.lstatSync(e).isSymbolicLink())throw Error(`Doom sync namespace must not contain a symbolic link: ${e}`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}}function _(e){try{let t=JSON.parse(r.default.readFileSync(e,`utf8`));return typeof t.pid==`number`&&typeof t.token==`string`?{pid:t.pid,token:t.token}:void 0}catch{return}}function v(e){try{return process.kill(e,0),!0}catch(e){return e instanceof Error&&`code`in e&&e.code===`EPERM`}}function y(e){try{let t=r.default.statSync(e);if(Date.now()-t.mtimeMs<=3e5)return;let n=_(e);if(n&&v(n.pid))return;r.default.rmSync(e,{force:!0})}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e}}async function b(e){x(e),await r.default.promises.mkdir(e.directory,{mode:448,recursive:!0}),await r.default.promises.chmod(e.directory,448);let i=t.default.join(e.directory,`.sync.lock`);y(i);let a={pid:process.pid,token:n.default.randomUUID()},o;try{o=await r.default.promises.open(i,`wx`,384)}catch(t){throw t instanceof Error&&`code`in t&&t.code===`EEXIST`?Error(`Another Doom sync is already publishing ${e.directory}`):t}await o.writeFile(`${JSON.stringify(a)}\n`);let s=setInterval(()=>{if(_(i)?.token!==a.token)return;let e=new Date;r.default.utimesSync(i,e,e)},1e5);return s.unref(),async()=>{clearInterval(s),await o.close(),_(i)?.token===a.token&&await r.default.promises.rm(i,{force:!0})}}function x(e){let n=t.default.dirname(e.repositoryDirectory),r=t.default.dirname(n),i=t.default.dirname(r),a=t.default.dirname(e.directory);for(let t of[i,r,n,e.repositoryDirectory,a,e.directory])g(t)}exports.acquireSyncLocationLock=b,exports.assertSyncLocationSafe=x,exports.resolveSyncLocation=p,exports.sanitizeSyncLabel=d,exports.syncGenerationDirectory=h;
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:crypto");n=e.__toESM(n,1);let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);const a=`.git`,o=`repo`;function s(e){let n=t.default.resolve(e);try{return r.default.realpathSync.native(n)}catch{return n}}function c(e){let n=t.default.join(e,a);try{let i=r.default.statSync(n);if(i.isDirectory())return s(n);if(!i.isFile())return;let a=/^gitdir:\s*(.+)$/imu.exec(r.default.readFileSync(n,`utf8`).trim());return a?.[1]?s(t.default.resolve(e,a[1].trim())):void 0}catch{return}}function l(e){let n=c(e);if(n)try{let e=r.default.readFileSync(t.default.join(n,`commondir`),`utf8`).trim();return e?s(t.default.resolve(n,e)):n}catch{return n}}function u(e){return n.default.createHash(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,32)}function d(e,t){let n=e.normalize(`NFKD`).replace(/[\u0300-\u036f]/gu,``).replace(/[^A-Za-z0-9._-]+/gu,`-`).replace(/^[._-]+|[._-]+$/gu,``).slice(0,48).replace(/[._-]+$/gu,``);return n===``||n===`.`||n===`..`?t:n}function f(e,n){if(!e)return d(t.default.basename(n),o);let r=t.default.basename(e);return d(r===a?t.default.basename(t.default.dirname(e)):r,o)}function p(e,n=i.default.homedir()){let r=s(e),a=l(r),o=a?`git:${a}`:`root:${r}`,c={repositoryId:u(o),worktreeId:u(`${o}\0worktree:${r}`)},p=f(a,r),m=d(t.default.basename(r),`worktree`),h=t.default.join(t.default.resolve(n),`.pi`,`.doom`,`sync`),g=t.default.join(h,`registrations`),_=t.default.join(h,`${p}--${c.repositoryId}`),v=t.default.join(_,`worktrees`,`${m}--${c.worktreeId}`),y=t.default.join(r,`.pi`,`doom`);return{root:r,identity:c,repositoryLabel:p,worktreeLabel:m,generatedRoot:h,registrationsDirectory:g,registrationPath:t.default.join(g,c.repositoryId,`${c.worktreeId}.json`),repositoryDirectory:_,sharedCacheDirectory:t.default.join(_,`shared-cache`),directory:v,generationsDirectory:t.default.join(v,`generations`),statePath:t.default.join(v,`state.json`),legacyDirectory:y,legacyStatePath:t.default.join(y,`state.json`)}}const m=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;function h(e,n){if(!m.test(n)||n===`.`||n===`..`)throw Error(`Invalid Doom sync generation id: ${n}`);return t.default.join(e.generationsDirectory,n)}function g(e){try{if(r.default.lstatSync(e).isSymbolicLink())throw Error(`Doom sync namespace must not contain a symbolic link: ${e}`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}}function _(e){try{let t=JSON.parse(r.default.readFileSync(e,`utf8`));return typeof t.pid==`number`&&typeof t.token==`string`?{pid:t.pid,token:t.token}:void 0}catch{return}}function v(e){try{return process.kill(e,0),!0}catch(e){return e instanceof Error&&`code`in e&&e.code===`EPERM`}}function y(e){try{let t=r.default.statSync(e),n=_(e);if(n){if(v(n.pid))return}else if(Date.now()-t.mtimeMs<=3e5)return;r.default.rmSync(e,{force:!0})}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e}}async function b(e){x(e),await r.default.promises.mkdir(e.directory,{mode:448,recursive:!0}),await r.default.promises.chmod(e.directory,448);let i=t.default.join(e.directory,`.sync.lock`),a={pid:process.pid,token:n.default.randomUUID()},o;for(;;){y(i);try{o=await r.default.promises.open(i,`wx`,384);break}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`EEXIST`))throw e;await new Promise(e=>setTimeout(e,100))}}await o.writeFile(`${JSON.stringify(a)}\n`);let s=setInterval(()=>{if(_(i)?.token!==a.token)return;let e=new Date;r.default.utimesSync(i,e,e)},1e5);return s.unref(),async()=>{clearInterval(s),await o.close(),_(i)?.token===a.token&&await r.default.promises.rm(i,{force:!0})}}function x(e){let n=t.default.dirname(e.repositoryDirectory),r=t.default.dirname(n),i=t.default.dirname(r),a=t.default.dirname(e.directory);for(let t of[i,r,n,e.repositoryDirectory,a,e.directory])g(t)}exports.acquireSyncLocationLock=b,exports.assertSyncLocationSafe=x,exports.resolveSyncLocation=p,exports.sanitizeSyncLabel=d,exports.syncGenerationDirectory=h;
2
2
  //# sourceMappingURL=syncLocation.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncLocation.cjs","names":["path","fs","crypto","os"],"sources":["../../../src/adapters/syncLocation.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst PI_DIRECTORY = '.pi';\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst SYNC_DIRECTORY = 'sync';\nconst WORKTREES_DIRECTORY = 'worktrees';\nconst GENERATIONS_DIRECTORY = 'generations';\nconst REGISTRATIONS_DIRECTORY = 'registrations';\nconst SHARED_CACHE_DIRECTORY = 'shared-cache';\nconst LEGACY_DOOM_DIRECTORY = 'doom';\nconst GIT_DIRECTORY = '.git';\nconst COMMON_DIRECTORY_FILE = 'commondir';\nconst IDENTITY_HEX_LENGTH = 32;\nconst LABEL_MAX_LENGTH = 48;\nconst DEFAULT_REPOSITORY_LABEL = 'repo';\nconst DEFAULT_WORKTREE_LABEL = 'worktree';\nconst LOCK_FILE = '.sync.lock';\nconst STALE_LOCK_MS = 5 * 60 * 1000;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface SyncIdentity {\n repositoryId: string;\n worktreeId: string;\n}\n\nexport interface SyncLocation {\n root: string;\n identity: SyncIdentity;\n repositoryLabel: string;\n worktreeLabel: string;\n generatedRoot: string;\n registrationsDirectory: string;\n registrationPath: string;\n repositoryDirectory: string;\n sharedCacheDirectory: string;\n directory: string;\n generationsDirectory: string;\n statePath: string;\n legacyDirectory: string;\n legacyStatePath: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction readGitDirectory(repositoryRoot: string): string | undefined {\n const gitPath = path.join(repositoryRoot, GIT_DIRECTORY);\n try {\n const stat = fs.statSync(gitPath);\n if (stat.isDirectory()) return canonicalPath(gitPath);\n if (!stat.isFile()) return undefined;\n const match = /^gitdir:\\s*(.+)$/imu.exec(fs.readFileSync(gitPath, 'utf8').trim());\n return match?.[1] ? canonicalPath(path.resolve(repositoryRoot, match[1].trim())) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction gitCommonDirectory(repositoryRoot: string): string | undefined {\n const gitDirectory = readGitDirectory(repositoryRoot);\n if (!gitDirectory) return undefined;\n try {\n const relative = fs.readFileSync(path.join(gitDirectory, COMMON_DIRECTORY_FILE), 'utf8').trim();\n return relative ? canonicalPath(path.resolve(gitDirectory, relative)) : gitDirectory;\n } catch {\n return gitDirectory;\n }\n}\n\nfunction identityHash(value: string): string {\n return crypto.createHash('sha256').update(value, 'utf8').digest('hex').slice(0, IDENTITY_HEX_LENGTH);\n}\n\nexport function sanitizeSyncLabel(value: string, fallback: string): string {\n const normalized = value\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/gu, '')\n .replace(/[^A-Za-z0-9._-]+/gu, '-')\n .replace(/^[._-]+|[._-]+$/gu, '')\n .slice(0, LABEL_MAX_LENGTH)\n .replace(/[._-]+$/gu, '');\n return normalized === '' || normalized === '.' || normalized === '..' ? fallback : normalized;\n}\n\nfunction repositoryLabel(commonDirectory: string | undefined, repositoryRoot: string): string {\n if (!commonDirectory) return sanitizeSyncLabel(path.basename(repositoryRoot), DEFAULT_REPOSITORY_LABEL);\n const base = path.basename(commonDirectory);\n const source = base === GIT_DIRECTORY ? path.basename(path.dirname(commonDirectory)) : base;\n return sanitizeSyncLabel(source, DEFAULT_REPOSITORY_LABEL);\n}\n\nexport function resolveSyncLocation(repositoryRoot: string, homeDirectory: string = os.homedir()): SyncLocation {\n const root = canonicalPath(repositoryRoot);\n const commonDirectory = gitCommonDirectory(root);\n const repositoryToken = commonDirectory ? `git:${commonDirectory}` : `root:${root}`;\n const identity: SyncIdentity = {\n repositoryId: identityHash(repositoryToken),\n worktreeId: identityHash(`${repositoryToken}\\0worktree:${root}`),\n };\n const repoLabel = repositoryLabel(commonDirectory, root);\n const worktreeLabel = sanitizeSyncLabel(path.basename(root), DEFAULT_WORKTREE_LABEL);\n const generatedRoot = path.join(path.resolve(homeDirectory), PI_DIRECTORY, DOOM_CONFIG_DIRECTORY, SYNC_DIRECTORY);\n const registrationsDirectory = path.join(generatedRoot, REGISTRATIONS_DIRECTORY);\n const repositoryDirectory = path.join(generatedRoot, `${repoLabel}--${identity.repositoryId}`);\n const directory = path.join(repositoryDirectory, WORKTREES_DIRECTORY, `${worktreeLabel}--${identity.worktreeId}`);\n const legacyDirectory = path.join(root, PI_DIRECTORY, LEGACY_DOOM_DIRECTORY);\n return {\n root,\n identity,\n repositoryLabel: repoLabel,\n worktreeLabel,\n generatedRoot,\n registrationsDirectory,\n registrationPath: path.join(registrationsDirectory, identity.repositoryId, `${identity.worktreeId}.json`),\n repositoryDirectory,\n sharedCacheDirectory: path.join(repositoryDirectory, SHARED_CACHE_DIRECTORY),\n directory,\n generationsDirectory: path.join(directory, GENERATIONS_DIRECTORY),\n statePath: path.join(directory, 'state.json'),\n legacyDirectory,\n legacyStatePath: path.join(legacyDirectory, 'state.json'),\n };\n}\n\nconst GENERATION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;\n\n/** Resolves one immutable generation below its worktree namespace. */\nexport function syncGenerationDirectory(location: SyncLocation, generation: string): string {\n if (!GENERATION_ID.test(generation) || generation === '.' || generation === '..') {\n throw new Error(`Invalid Doom sync generation id: ${generation}`);\n }\n return path.join(location.generationsDirectory, generation);\n}\n\nfunction rejectSymlink(target: string): void {\n try {\n if (fs.lstatSync(target).isSymbolicLink()) {\n throw new Error(`Doom sync namespace must not contain a symbolic link: ${target}`);\n }\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return;\n throw error;\n }\n}\n\ninterface SyncLockRecord {\n pid: number;\n token: string;\n}\n\nfunction readLockRecord(lockPath: string): SyncLockRecord | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf8')) as { pid?: unknown; token?: unknown };\n return typeof parsed.pid === 'number' && typeof parsed.token === 'string'\n ? { pid: parsed.pid, token: parsed.token }\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction processIsAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch (error) {\n return error instanceof Error && 'code' in error && error.code === 'EPERM';\n }\n}\n\nfunction removeStaleLock(lockPath: string): void {\n try {\n const stat = fs.statSync(lockPath);\n if (Date.now() - stat.mtimeMs <= STALE_LOCK_MS) return;\n const record = readLockRecord(lockPath);\n if (record && processIsAlive(record.pid)) return;\n fs.rmSync(lockPath, { force: true });\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) throw error;\n }\n}\n\n/** Acquires the exclusive publisher lock for one worktree namespace. */\nexport async function acquireSyncLocationLock(location: SyncLocation): Promise<() => Promise<void>> {\n assertSyncLocationSafe(location);\n await fs.promises.mkdir(location.directory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n await fs.promises.chmod(location.directory, PRIVATE_DIRECTORY_MODE);\n const lockPath = path.join(location.directory, LOCK_FILE);\n removeStaleLock(lockPath);\n const record: SyncLockRecord = { pid: process.pid, token: crypto.randomUUID() };\n let handle: fs.promises.FileHandle;\n try {\n handle = await fs.promises.open(lockPath, 'wx', PRIVATE_FILE_MODE);\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'EEXIST') {\n throw new Error(`Another Doom sync is already publishing ${location.directory}`);\n }\n throw error;\n }\n await handle.writeFile(`${JSON.stringify(record)}\\n`);\n const heartbeat = setInterval(\n () => {\n if (readLockRecord(lockPath)?.token !== record.token) return;\n const now = new Date();\n fs.utimesSync(lockPath, now, now);\n },\n Math.floor(STALE_LOCK_MS / 3),\n );\n heartbeat.unref();\n return async () => {\n clearInterval(heartbeat);\n await handle.close();\n if (readLockRecord(lockPath)?.token === record.token) {\n await fs.promises.rm(lockPath, { force: true });\n }\n };\n}\n\n/** Refuses generated namespace redirection before a writer creates or prunes it. */\nexport function assertSyncLocationSafe(location: SyncLocation): void {\n const syncRoot = path.dirname(location.repositoryDirectory);\n const doomRoot = path.dirname(syncRoot);\n const piRoot = path.dirname(doomRoot);\n const worktreesRoot = path.dirname(location.directory);\n for (const target of [piRoot, doomRoot, syncRoot, location.repositoryDirectory, worktreesRoot, location.directory]) {\n rejectSymlink(target);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncLocation.cjs","names":["path","fs","crypto","os"],"sources":["../../../src/adapters/syncLocation.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst PI_DIRECTORY = '.pi';\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst SYNC_DIRECTORY = 'sync';\nconst WORKTREES_DIRECTORY = 'worktrees';\nconst GENERATIONS_DIRECTORY = 'generations';\nconst REGISTRATIONS_DIRECTORY = 'registrations';\nconst SHARED_CACHE_DIRECTORY = 'shared-cache';\nconst LEGACY_DOOM_DIRECTORY = 'doom';\nconst GIT_DIRECTORY = '.git';\nconst COMMON_DIRECTORY_FILE = 'commondir';\nconst IDENTITY_HEX_LENGTH = 32;\nconst LABEL_MAX_LENGTH = 48;\nconst DEFAULT_REPOSITORY_LABEL = 'repo';\nconst DEFAULT_WORKTREE_LABEL = 'worktree';\nconst LOCK_FILE = '.sync.lock';\nconst STALE_LOCK_MS = 5 * 60 * 1000;\nconst LOCK_RETRY_INTERVAL_MS = 100;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface SyncIdentity {\n repositoryId: string;\n worktreeId: string;\n}\n\nexport interface SyncLocation {\n root: string;\n identity: SyncIdentity;\n repositoryLabel: string;\n worktreeLabel: string;\n generatedRoot: string;\n registrationsDirectory: string;\n registrationPath: string;\n repositoryDirectory: string;\n sharedCacheDirectory: string;\n directory: string;\n generationsDirectory: string;\n statePath: string;\n legacyDirectory: string;\n legacyStatePath: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction readGitDirectory(repositoryRoot: string): string | undefined {\n const gitPath = path.join(repositoryRoot, GIT_DIRECTORY);\n try {\n const stat = fs.statSync(gitPath);\n if (stat.isDirectory()) return canonicalPath(gitPath);\n if (!stat.isFile()) return undefined;\n const match = /^gitdir:\\s*(.+)$/imu.exec(fs.readFileSync(gitPath, 'utf8').trim());\n return match?.[1] ? canonicalPath(path.resolve(repositoryRoot, match[1].trim())) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction gitCommonDirectory(repositoryRoot: string): string | undefined {\n const gitDirectory = readGitDirectory(repositoryRoot);\n if (!gitDirectory) return undefined;\n try {\n const relative = fs.readFileSync(path.join(gitDirectory, COMMON_DIRECTORY_FILE), 'utf8').trim();\n return relative ? canonicalPath(path.resolve(gitDirectory, relative)) : gitDirectory;\n } catch {\n return gitDirectory;\n }\n}\n\nfunction identityHash(value: string): string {\n return crypto.createHash('sha256').update(value, 'utf8').digest('hex').slice(0, IDENTITY_HEX_LENGTH);\n}\n\nexport function sanitizeSyncLabel(value: string, fallback: string): string {\n const normalized = value\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/gu, '')\n .replace(/[^A-Za-z0-9._-]+/gu, '-')\n .replace(/^[._-]+|[._-]+$/gu, '')\n .slice(0, LABEL_MAX_LENGTH)\n .replace(/[._-]+$/gu, '');\n return normalized === '' || normalized === '.' || normalized === '..' ? fallback : normalized;\n}\n\nfunction repositoryLabel(commonDirectory: string | undefined, repositoryRoot: string): string {\n if (!commonDirectory) return sanitizeSyncLabel(path.basename(repositoryRoot), DEFAULT_REPOSITORY_LABEL);\n const base = path.basename(commonDirectory);\n const source = base === GIT_DIRECTORY ? path.basename(path.dirname(commonDirectory)) : base;\n return sanitizeSyncLabel(source, DEFAULT_REPOSITORY_LABEL);\n}\n\nexport function resolveSyncLocation(repositoryRoot: string, homeDirectory: string = os.homedir()): SyncLocation {\n const root = canonicalPath(repositoryRoot);\n const commonDirectory = gitCommonDirectory(root);\n const repositoryToken = commonDirectory ? `git:${commonDirectory}` : `root:${root}`;\n const identity: SyncIdentity = {\n repositoryId: identityHash(repositoryToken),\n worktreeId: identityHash(`${repositoryToken}\\0worktree:${root}`),\n };\n const repoLabel = repositoryLabel(commonDirectory, root);\n const worktreeLabel = sanitizeSyncLabel(path.basename(root), DEFAULT_WORKTREE_LABEL);\n const generatedRoot = path.join(path.resolve(homeDirectory), PI_DIRECTORY, DOOM_CONFIG_DIRECTORY, SYNC_DIRECTORY);\n const registrationsDirectory = path.join(generatedRoot, REGISTRATIONS_DIRECTORY);\n const repositoryDirectory = path.join(generatedRoot, `${repoLabel}--${identity.repositoryId}`);\n const directory = path.join(repositoryDirectory, WORKTREES_DIRECTORY, `${worktreeLabel}--${identity.worktreeId}`);\n const legacyDirectory = path.join(root, PI_DIRECTORY, LEGACY_DOOM_DIRECTORY);\n return {\n root,\n identity,\n repositoryLabel: repoLabel,\n worktreeLabel,\n generatedRoot,\n registrationsDirectory,\n registrationPath: path.join(registrationsDirectory, identity.repositoryId, `${identity.worktreeId}.json`),\n repositoryDirectory,\n sharedCacheDirectory: path.join(repositoryDirectory, SHARED_CACHE_DIRECTORY),\n directory,\n generationsDirectory: path.join(directory, GENERATIONS_DIRECTORY),\n statePath: path.join(directory, 'state.json'),\n legacyDirectory,\n legacyStatePath: path.join(legacyDirectory, 'state.json'),\n };\n}\n\nconst GENERATION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;\n\n/** Resolves one immutable generation below its worktree namespace. */\nexport function syncGenerationDirectory(location: SyncLocation, generation: string): string {\n if (!GENERATION_ID.test(generation) || generation === '.' || generation === '..') {\n throw new Error(`Invalid Doom sync generation id: ${generation}`);\n }\n return path.join(location.generationsDirectory, generation);\n}\n\nfunction rejectSymlink(target: string): void {\n try {\n if (fs.lstatSync(target).isSymbolicLink()) {\n throw new Error(`Doom sync namespace must not contain a symbolic link: ${target}`);\n }\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return;\n throw error;\n }\n}\n\ninterface SyncLockRecord {\n pid: number;\n token: string;\n}\n\nfunction readLockRecord(lockPath: string): SyncLockRecord | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf8')) as { pid?: unknown; token?: unknown };\n return typeof parsed.pid === 'number' && typeof parsed.token === 'string'\n ? { pid: parsed.pid, token: parsed.token }\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction processIsAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch (error) {\n return error instanceof Error && 'code' in error && error.code === 'EPERM';\n }\n}\n\nfunction removeStaleLock(lockPath: string): void {\n try {\n const stat = fs.statSync(lockPath);\n const record = readLockRecord(lockPath);\n if (record) {\n if (processIsAlive(record.pid)) return;\n } else if (Date.now() - stat.mtimeMs <= STALE_LOCK_MS) {\n // A publisher may have created the file but not written its record yet.\n return;\n }\n fs.rmSync(lockPath, { force: true });\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) throw error;\n }\n}\n\n/** Waits for and acquires the exclusive publisher lock for one worktree namespace. */\nexport async function acquireSyncLocationLock(location: SyncLocation): Promise<() => Promise<void>> {\n assertSyncLocationSafe(location);\n await fs.promises.mkdir(location.directory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n await fs.promises.chmod(location.directory, PRIVATE_DIRECTORY_MODE);\n const lockPath = path.join(location.directory, LOCK_FILE);\n const record: SyncLockRecord = { pid: process.pid, token: crypto.randomUUID() };\n let handle: fs.promises.FileHandle;\n for (;;) {\n removeStaleLock(lockPath);\n try {\n handle = await fs.promises.open(lockPath, 'wx', PRIVATE_FILE_MODE);\n break;\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST')) throw error;\n await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));\n }\n }\n await handle.writeFile(`${JSON.stringify(record)}\\n`);\n const heartbeat = setInterval(\n () => {\n if (readLockRecord(lockPath)?.token !== record.token) return;\n const now = new Date();\n fs.utimesSync(lockPath, now, now);\n },\n Math.floor(STALE_LOCK_MS / 3),\n );\n heartbeat.unref();\n return async () => {\n clearInterval(heartbeat);\n await handle.close();\n if (readLockRecord(lockPath)?.token === record.token) {\n await fs.promises.rm(lockPath, { force: true });\n }\n };\n}\n\n/** Refuses generated namespace redirection before a writer creates or prunes it. */\nexport function assertSyncLocationSafe(location: SyncLocation): void {\n const syncRoot = path.dirname(location.repositoryDirectory);\n const doomRoot = path.dirname(syncRoot);\n const piRoot = path.dirname(doomRoot);\n const worktreesRoot = path.dirname(location.directory);\n for (const target of [piRoot, doomRoot, syncRoot, location.repositoryDirectory, worktreesRoot, location.directory]) {\n rejectSymlink(target);\n }\n}\n"],"mappings":"sOAKA,MAQM,EAAgB,OAIhB,EAA2B,OA8BjC,SAAS,EAAc,EAAwB,CAC7C,IAAM,EAAWA,EAAAA,QAAK,QAAQ,CAAM,EACpC,GAAI,CACF,OAAOC,EAAAA,QAAG,aAAa,OAAO,CAAQ,CACxC,MAAQ,CACN,OAAO,CACT,CACF,CAEA,SAAS,EAAiB,EAA4C,CACpE,IAAM,EAAUD,EAAAA,QAAK,KAAK,EAAgB,CAAa,EACvD,GAAI,CACF,IAAM,EAAOC,EAAAA,QAAG,SAAS,CAAO,EAChC,GAAI,EAAK,YAAY,EAAG,OAAO,EAAc,CAAO,EACpD,GAAI,CAAC,EAAK,OAAO,EAAG,OACpB,IAAM,EAAQ,sBAAsB,KAAKA,EAAAA,QAAG,aAAa,EAAS,MAAM,CAAC,CAAC,KAAK,CAAC,EAChF,OAAO,IAAQ,GAAK,EAAcD,EAAAA,QAAK,QAAQ,EAAgB,EAAM,EAAE,CAAC,KAAK,CAAC,CAAC,EAAI,IAAA,EACrF,MAAQ,CACN,MACF,CACF,CAEA,SAAS,EAAmB,EAA4C,CACtE,IAAM,EAAe,EAAiB,CAAc,EAC/C,KACL,GAAI,CACF,IAAM,EAAWC,EAAAA,QAAG,aAAaD,EAAAA,QAAK,KAAK,EAAc,WAAqB,EAAG,MAAM,CAAC,CAAC,KAAK,EAC9F,OAAO,EAAW,EAAcA,EAAAA,QAAK,QAAQ,EAAc,CAAQ,CAAC,EAAI,CAC1E,MAAQ,CACN,OAAO,CACT,CACF,CAEA,SAAS,EAAa,EAAuB,CAC3C,OAAOE,EAAAA,QAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,EAAO,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,EAAmB,CACrG,CAEA,SAAgB,EAAkB,EAAe,EAA0B,CACzE,IAAM,EAAa,EAChB,UAAU,MAAM,CAAC,CACjB,QAAQ,oBAAqB,EAAE,CAAC,CAChC,QAAQ,qBAAsB,GAAG,CAAC,CAClC,QAAQ,oBAAqB,EAAE,CAAC,CAChC,MAAM,EAAG,EAAgB,CAAC,CAC1B,QAAQ,YAAa,EAAE,EAC1B,OAAO,IAAe,IAAM,IAAe,KAAO,IAAe,KAAO,EAAW,CACrF,CAEA,SAAS,EAAgB,EAAqC,EAAgC,CAC5F,GAAI,CAAC,EAAiB,OAAO,EAAkBF,EAAAA,QAAK,SAAS,CAAc,EAAG,CAAwB,EACtG,IAAM,EAAOA,EAAAA,QAAK,SAAS,CAAe,EAE1C,OAAO,EADQ,IAAS,EAAgBA,EAAAA,QAAK,SAASA,EAAAA,QAAK,QAAQ,CAAe,CAAC,EAAI,EACtD,CAAwB,CAC3D,CAEA,SAAgB,EAAoB,EAAwB,EAAwBG,EAAAA,QAAG,QAAQ,EAAiB,CAC9G,IAAM,EAAO,EAAc,CAAc,EACnC,EAAkB,EAAmB,CAAI,EACzC,EAAkB,EAAkB,OAAO,IAAoB,QAAQ,IACvE,EAAyB,CAC7B,aAAc,EAAa,CAAe,EAC1C,WAAY,EAAa,GAAG,EAAgB,aAAa,GAAM,CACjE,EACM,EAAY,EAAgB,EAAiB,CAAI,EACjD,EAAgB,EAAkBH,EAAAA,QAAK,SAAS,CAAI,EAAG,UAAsB,EAC7E,EAAgBA,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,CAAa,EAAG,MAAc,QAAuB,MAAc,EAC1G,EAAyBA,EAAAA,QAAK,KAAK,EAAe,eAAuB,EACzE,EAAsBA,EAAAA,QAAK,KAAK,EAAe,GAAG,EAAU,IAAI,EAAS,cAAc,EACvF,EAAYA,EAAAA,QAAK,KAAK,EAAqB,YAAqB,GAAG,EAAc,IAAI,EAAS,YAAY,EAC1G,EAAkBA,EAAAA,QAAK,KAAK,EAAM,MAAc,MAAqB,EAC3E,MAAO,CACL,OACA,WACA,gBAAiB,EACjB,gBACA,gBACA,yBACA,iBAAkBA,EAAAA,QAAK,KAAK,EAAwB,EAAS,aAAc,GAAG,EAAS,WAAW,MAAM,EACxG,sBACA,qBAAsBA,EAAAA,QAAK,KAAK,EAAqB,cAAsB,EAC3E,YACA,qBAAsBA,EAAAA,QAAK,KAAK,EAAW,aAAqB,EAChE,UAAWA,EAAAA,QAAK,KAAK,EAAW,YAAY,EAC5C,kBACA,gBAAiBA,EAAAA,QAAK,KAAK,EAAiB,YAAY,CAC1D,CACF,CAEA,MAAM,EAAgB,sCAGtB,SAAgB,EAAwB,EAAwB,EAA4B,CAC1F,GAAI,CAAC,EAAc,KAAK,CAAU,GAAK,IAAe,KAAO,IAAe,KAC1E,MAAU,MAAM,oCAAoC,GAAY,EAElE,OAAOA,EAAAA,QAAK,KAAK,EAAS,qBAAsB,CAAU,CAC5D,CAEA,SAAS,EAAc,EAAsB,CAC3C,GAAI,CACF,GAAIC,EAAAA,QAAG,UAAU,CAAM,CAAC,CAAC,eAAe,EACtC,MAAU,MAAM,yDAAyD,GAAQ,CAErF,OAAS,EAAO,CACd,GAAI,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,SAAU,OAC1E,MAAM,CACR,CACF,CAOA,SAAS,EAAe,EAA8C,CACpE,GAAI,CACF,IAAM,EAAS,KAAK,MAAMA,EAAAA,QAAG,aAAa,EAAU,MAAM,CAAC,EAC3D,OAAO,OAAO,EAAO,KAAQ,UAAY,OAAO,EAAO,OAAU,SAC7D,CAAE,IAAK,EAAO,IAAK,MAAO,EAAO,KAAM,EACvC,IAAA,EACN,MAAQ,CACN,MACF,CACF,CAEA,SAAS,EAAe,EAA4B,CAClD,GAAI,CAEF,OADA,QAAQ,KAAK,EAAW,CAAC,EAClB,EACT,OAAS,EAAO,CACd,OAAO,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,OACrE,CACF,CAEA,SAAS,EAAgB,EAAwB,CAC/C,GAAI,CACF,IAAM,EAAOA,EAAAA,QAAG,SAAS,CAAQ,EAC3B,EAAS,EAAe,CAAQ,EACtC,GAAI,EACE,IAAA,EAAe,EAAO,GAAG,EAAG,MAAA,MAC3B,GAAI,KAAK,IAAI,EAAI,EAAK,SAAW,IAEtC,OAEF,EAAA,QAAG,OAAO,EAAU,CAAE,MAAO,EAAK,CAAC,CACrC,OAAS,EAAO,CACd,GAAI,EAAE,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,UAAW,MAAM,CACrF,CACF,CAGA,eAAsB,EAAwB,EAAsD,CAClG,EAAuB,CAAQ,EAC/B,MAAMA,EAAAA,QAAG,SAAS,MAAM,EAAS,UAAW,CAAE,KAAM,IAAwB,UAAW,EAAK,CAAC,EAC7F,MAAMA,EAAAA,QAAG,SAAS,MAAM,EAAS,UAAW,GAAsB,EAClE,IAAM,EAAWD,EAAAA,QAAK,KAAK,EAAS,UAAW,YAAS,EAClD,EAAyB,CAAE,IAAK,QAAQ,IAAK,MAAOE,EAAAA,QAAO,WAAW,CAAE,EAC1E,EACJ,OAAS,CACP,EAAgB,CAAQ,EACxB,GAAI,CACF,EAAS,MAAMD,EAAAA,QAAG,SAAS,KAAK,EAAU,KAAM,GAAiB,EACjE,KACF,OAAS,EAAO,CACd,GAAI,EAAE,aAAiB,OAAS,SAAU,GAAS,EAAM,OAAS,UAAW,MAAM,EACnF,MAAM,IAAI,QAAS,GAAY,WAAW,EAAS,GAAsB,CAAC,CAC5E,CACF,CACA,MAAM,EAAO,UAAU,GAAG,KAAK,UAAU,CAAM,EAAE,GAAG,EACpD,IAAM,EAAY,gBACV,CACJ,GAAI,EAAe,CAAQ,CAAC,EAAE,QAAU,EAAO,MAAO,OACtD,IAAM,EAAM,IAAI,KAChB,EAAA,QAAG,WAAW,EAAU,EAAK,CAAG,CAClC,EACA,GACF,EAEA,OADA,EAAU,MAAM,EACT,SAAY,CACjB,cAAc,CAAS,EACvB,MAAM,EAAO,MAAM,EACf,EAAe,CAAQ,CAAC,EAAE,QAAU,EAAO,OAC7C,MAAMA,EAAAA,QAAG,SAAS,GAAG,EAAU,CAAE,MAAO,EAAK,CAAC,CAElD,CACF,CAGA,SAAgB,EAAuB,EAA8B,CACnE,IAAM,EAAWD,EAAAA,QAAK,QAAQ,EAAS,mBAAmB,EACpD,EAAWA,EAAAA,QAAK,QAAQ,CAAQ,EAChC,EAASA,EAAAA,QAAK,QAAQ,CAAQ,EAC9B,EAAgBA,EAAAA,QAAK,QAAQ,EAAS,SAAS,EACrD,IAAK,IAAM,IAAU,CAAC,EAAQ,EAAU,EAAU,EAAS,oBAAqB,EAAe,EAAS,SAAS,EAC/G,EAAc,CAAM,CAExB"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncLocation.d.cts","names":[],"sources":["../../../src/adapters/syncLocation.ts"],"mappings":";UAwBiB;EACf;EACA;;UAGe;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA"}
1
+ {"version":3,"file":"syncLocation.d.cts","names":[],"sources":["../../../src/adapters/syncLocation.ts"],"mappings":";UAyBiB;EACf;EACA;;UAGe;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncLocation.d.mts","names":[],"sources":["../../../src/adapters/syncLocation.ts"],"mappings":";UAwBiB;EACf;EACA;;UAGe;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA"}
1
+ {"version":3,"file":"syncLocation.d.mts","names":[],"sources":["../../../src/adapters/syncLocation.ts"],"mappings":";UAyBiB;EACf;EACA;;UAGe;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA"}
@@ -1,2 +1,2 @@
1
- import e from"node:path";import t from"node:crypto";import n from"node:fs";import r from"node:os";const i=`.git`,a=`repo`;function o(t){let r=e.resolve(t);try{return n.realpathSync.native(r)}catch{return r}}function s(t){let r=e.join(t,i);try{let i=n.statSync(r);if(i.isDirectory())return o(r);if(!i.isFile())return;let a=/^gitdir:\s*(.+)$/imu.exec(n.readFileSync(r,`utf8`).trim());return a?.[1]?o(e.resolve(t,a[1].trim())):void 0}catch{return}}function c(t){let r=s(t);if(r)try{let t=n.readFileSync(e.join(r,`commondir`),`utf8`).trim();return t?o(e.resolve(r,t)):r}catch{return r}}function l(e){return t.createHash(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,32)}function u(e,t){let n=e.normalize(`NFKD`).replace(/[\u0300-\u036f]/gu,``).replace(/[^A-Za-z0-9._-]+/gu,`-`).replace(/^[._-]+|[._-]+$/gu,``).slice(0,48).replace(/[._-]+$/gu,``);return n===``||n===`.`||n===`..`?t:n}function d(t,n){if(!t)return u(e.basename(n),a);let r=e.basename(t);return u(r===i?e.basename(e.dirname(t)):r,a)}function f(t,n=r.homedir()){let i=o(t),a=c(i),s=a?`git:${a}`:`root:${i}`,f={repositoryId:l(s),worktreeId:l(`${s}\0worktree:${i}`)},p=d(a,i),m=u(e.basename(i),`worktree`),h=e.join(e.resolve(n),`.pi`,`.doom`,`sync`),g=e.join(h,`registrations`),_=e.join(h,`${p}--${f.repositoryId}`),v=e.join(_,`worktrees`,`${m}--${f.worktreeId}`),y=e.join(i,`.pi`,`doom`);return{root:i,identity:f,repositoryLabel:p,worktreeLabel:m,generatedRoot:h,registrationsDirectory:g,registrationPath:e.join(g,f.repositoryId,`${f.worktreeId}.json`),repositoryDirectory:_,sharedCacheDirectory:e.join(_,`shared-cache`),directory:v,generationsDirectory:e.join(v,`generations`),statePath:e.join(v,`state.json`),legacyDirectory:y,legacyStatePath:e.join(y,`state.json`)}}const p=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;function m(t,n){if(!p.test(n)||n===`.`||n===`..`)throw Error(`Invalid Doom sync generation id: ${n}`);return e.join(t.generationsDirectory,n)}function h(e){try{if(n.lstatSync(e).isSymbolicLink())throw Error(`Doom sync namespace must not contain a symbolic link: ${e}`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}}function g(e){try{let t=JSON.parse(n.readFileSync(e,`utf8`));return typeof t.pid==`number`&&typeof t.token==`string`?{pid:t.pid,token:t.token}:void 0}catch{return}}function _(e){try{return process.kill(e,0),!0}catch(e){return e instanceof Error&&`code`in e&&e.code===`EPERM`}}function v(e){try{let t=n.statSync(e);if(Date.now()-t.mtimeMs<=3e5)return;let r=g(e);if(r&&_(r.pid))return;n.rmSync(e,{force:!0})}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e}}async function y(r){b(r),await n.promises.mkdir(r.directory,{mode:448,recursive:!0}),await n.promises.chmod(r.directory,448);let i=e.join(r.directory,`.sync.lock`);v(i);let a={pid:process.pid,token:t.randomUUID()},o;try{o=await n.promises.open(i,`wx`,384)}catch(e){throw e instanceof Error&&`code`in e&&e.code===`EEXIST`?Error(`Another Doom sync is already publishing ${r.directory}`):e}await o.writeFile(`${JSON.stringify(a)}\n`);let s=setInterval(()=>{if(g(i)?.token!==a.token)return;let e=new Date;n.utimesSync(i,e,e)},1e5);return s.unref(),async()=>{clearInterval(s),await o.close(),g(i)?.token===a.token&&await n.promises.rm(i,{force:!0})}}function b(t){let n=e.dirname(t.repositoryDirectory),r=e.dirname(n),i=e.dirname(r),a=e.dirname(t.directory);for(let e of[i,r,n,t.repositoryDirectory,a,t.directory])h(e)}export{y as acquireSyncLocationLock,b as assertSyncLocationSafe,f as resolveSyncLocation,u as sanitizeSyncLabel,m as syncGenerationDirectory};
1
+ import e from"node:path";import t from"node:crypto";import n from"node:fs";import r from"node:os";const i=`.git`,a=`repo`;function o(t){let r=e.resolve(t);try{return n.realpathSync.native(r)}catch{return r}}function s(t){let r=e.join(t,i);try{let i=n.statSync(r);if(i.isDirectory())return o(r);if(!i.isFile())return;let a=/^gitdir:\s*(.+)$/imu.exec(n.readFileSync(r,`utf8`).trim());return a?.[1]?o(e.resolve(t,a[1].trim())):void 0}catch{return}}function c(t){let r=s(t);if(r)try{let t=n.readFileSync(e.join(r,`commondir`),`utf8`).trim();return t?o(e.resolve(r,t)):r}catch{return r}}function l(e){return t.createHash(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,32)}function u(e,t){let n=e.normalize(`NFKD`).replace(/[\u0300-\u036f]/gu,``).replace(/[^A-Za-z0-9._-]+/gu,`-`).replace(/^[._-]+|[._-]+$/gu,``).slice(0,48).replace(/[._-]+$/gu,``);return n===``||n===`.`||n===`..`?t:n}function d(t,n){if(!t)return u(e.basename(n),a);let r=e.basename(t);return u(r===i?e.basename(e.dirname(t)):r,a)}function f(t,n=r.homedir()){let i=o(t),a=c(i),s=a?`git:${a}`:`root:${i}`,f={repositoryId:l(s),worktreeId:l(`${s}\0worktree:${i}`)},p=d(a,i),m=u(e.basename(i),`worktree`),h=e.join(e.resolve(n),`.pi`,`.doom`,`sync`),g=e.join(h,`registrations`),_=e.join(h,`${p}--${f.repositoryId}`),v=e.join(_,`worktrees`,`${m}--${f.worktreeId}`),y=e.join(i,`.pi`,`doom`);return{root:i,identity:f,repositoryLabel:p,worktreeLabel:m,generatedRoot:h,registrationsDirectory:g,registrationPath:e.join(g,f.repositoryId,`${f.worktreeId}.json`),repositoryDirectory:_,sharedCacheDirectory:e.join(_,`shared-cache`),directory:v,generationsDirectory:e.join(v,`generations`),statePath:e.join(v,`state.json`),legacyDirectory:y,legacyStatePath:e.join(y,`state.json`)}}const p=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;function m(t,n){if(!p.test(n)||n===`.`||n===`..`)throw Error(`Invalid Doom sync generation id: ${n}`);return e.join(t.generationsDirectory,n)}function h(e){try{if(n.lstatSync(e).isSymbolicLink())throw Error(`Doom sync namespace must not contain a symbolic link: ${e}`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}}function g(e){try{let t=JSON.parse(n.readFileSync(e,`utf8`));return typeof t.pid==`number`&&typeof t.token==`string`?{pid:t.pid,token:t.token}:void 0}catch{return}}function _(e){try{return process.kill(e,0),!0}catch(e){return e instanceof Error&&`code`in e&&e.code===`EPERM`}}function v(e){try{let t=n.statSync(e),r=g(e);if(r){if(_(r.pid))return}else if(Date.now()-t.mtimeMs<=3e5)return;n.rmSync(e,{force:!0})}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e}}async function y(r){b(r),await n.promises.mkdir(r.directory,{mode:448,recursive:!0}),await n.promises.chmod(r.directory,448);let i=e.join(r.directory,`.sync.lock`),a={pid:process.pid,token:t.randomUUID()},o;for(;;){v(i);try{o=await n.promises.open(i,`wx`,384);break}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`EEXIST`))throw e;await new Promise(e=>setTimeout(e,100))}}await o.writeFile(`${JSON.stringify(a)}\n`);let s=setInterval(()=>{if(g(i)?.token!==a.token)return;let e=new Date;n.utimesSync(i,e,e)},1e5);return s.unref(),async()=>{clearInterval(s),await o.close(),g(i)?.token===a.token&&await n.promises.rm(i,{force:!0})}}function b(t){let n=e.dirname(t.repositoryDirectory),r=e.dirname(n),i=e.dirname(r),a=e.dirname(t.directory);for(let e of[i,r,n,t.repositoryDirectory,a,t.directory])h(e)}export{y as acquireSyncLocationLock,b as assertSyncLocationSafe,f as resolveSyncLocation,u as sanitizeSyncLabel,m as syncGenerationDirectory};
2
2
  //# sourceMappingURL=syncLocation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncLocation.mjs","names":[],"sources":["../../../src/adapters/syncLocation.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst PI_DIRECTORY = '.pi';\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst SYNC_DIRECTORY = 'sync';\nconst WORKTREES_DIRECTORY = 'worktrees';\nconst GENERATIONS_DIRECTORY = 'generations';\nconst REGISTRATIONS_DIRECTORY = 'registrations';\nconst SHARED_CACHE_DIRECTORY = 'shared-cache';\nconst LEGACY_DOOM_DIRECTORY = 'doom';\nconst GIT_DIRECTORY = '.git';\nconst COMMON_DIRECTORY_FILE = 'commondir';\nconst IDENTITY_HEX_LENGTH = 32;\nconst LABEL_MAX_LENGTH = 48;\nconst DEFAULT_REPOSITORY_LABEL = 'repo';\nconst DEFAULT_WORKTREE_LABEL = 'worktree';\nconst LOCK_FILE = '.sync.lock';\nconst STALE_LOCK_MS = 5 * 60 * 1000;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface SyncIdentity {\n repositoryId: string;\n worktreeId: string;\n}\n\nexport interface SyncLocation {\n root: string;\n identity: SyncIdentity;\n repositoryLabel: string;\n worktreeLabel: string;\n generatedRoot: string;\n registrationsDirectory: string;\n registrationPath: string;\n repositoryDirectory: string;\n sharedCacheDirectory: string;\n directory: string;\n generationsDirectory: string;\n statePath: string;\n legacyDirectory: string;\n legacyStatePath: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction readGitDirectory(repositoryRoot: string): string | undefined {\n const gitPath = path.join(repositoryRoot, GIT_DIRECTORY);\n try {\n const stat = fs.statSync(gitPath);\n if (stat.isDirectory()) return canonicalPath(gitPath);\n if (!stat.isFile()) return undefined;\n const match = /^gitdir:\\s*(.+)$/imu.exec(fs.readFileSync(gitPath, 'utf8').trim());\n return match?.[1] ? canonicalPath(path.resolve(repositoryRoot, match[1].trim())) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction gitCommonDirectory(repositoryRoot: string): string | undefined {\n const gitDirectory = readGitDirectory(repositoryRoot);\n if (!gitDirectory) return undefined;\n try {\n const relative = fs.readFileSync(path.join(gitDirectory, COMMON_DIRECTORY_FILE), 'utf8').trim();\n return relative ? canonicalPath(path.resolve(gitDirectory, relative)) : gitDirectory;\n } catch {\n return gitDirectory;\n }\n}\n\nfunction identityHash(value: string): string {\n return crypto.createHash('sha256').update(value, 'utf8').digest('hex').slice(0, IDENTITY_HEX_LENGTH);\n}\n\nexport function sanitizeSyncLabel(value: string, fallback: string): string {\n const normalized = value\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/gu, '')\n .replace(/[^A-Za-z0-9._-]+/gu, '-')\n .replace(/^[._-]+|[._-]+$/gu, '')\n .slice(0, LABEL_MAX_LENGTH)\n .replace(/[._-]+$/gu, '');\n return normalized === '' || normalized === '.' || normalized === '..' ? fallback : normalized;\n}\n\nfunction repositoryLabel(commonDirectory: string | undefined, repositoryRoot: string): string {\n if (!commonDirectory) return sanitizeSyncLabel(path.basename(repositoryRoot), DEFAULT_REPOSITORY_LABEL);\n const base = path.basename(commonDirectory);\n const source = base === GIT_DIRECTORY ? path.basename(path.dirname(commonDirectory)) : base;\n return sanitizeSyncLabel(source, DEFAULT_REPOSITORY_LABEL);\n}\n\nexport function resolveSyncLocation(repositoryRoot: string, homeDirectory: string = os.homedir()): SyncLocation {\n const root = canonicalPath(repositoryRoot);\n const commonDirectory = gitCommonDirectory(root);\n const repositoryToken = commonDirectory ? `git:${commonDirectory}` : `root:${root}`;\n const identity: SyncIdentity = {\n repositoryId: identityHash(repositoryToken),\n worktreeId: identityHash(`${repositoryToken}\\0worktree:${root}`),\n };\n const repoLabel = repositoryLabel(commonDirectory, root);\n const worktreeLabel = sanitizeSyncLabel(path.basename(root), DEFAULT_WORKTREE_LABEL);\n const generatedRoot = path.join(path.resolve(homeDirectory), PI_DIRECTORY, DOOM_CONFIG_DIRECTORY, SYNC_DIRECTORY);\n const registrationsDirectory = path.join(generatedRoot, REGISTRATIONS_DIRECTORY);\n const repositoryDirectory = path.join(generatedRoot, `${repoLabel}--${identity.repositoryId}`);\n const directory = path.join(repositoryDirectory, WORKTREES_DIRECTORY, `${worktreeLabel}--${identity.worktreeId}`);\n const legacyDirectory = path.join(root, PI_DIRECTORY, LEGACY_DOOM_DIRECTORY);\n return {\n root,\n identity,\n repositoryLabel: repoLabel,\n worktreeLabel,\n generatedRoot,\n registrationsDirectory,\n registrationPath: path.join(registrationsDirectory, identity.repositoryId, `${identity.worktreeId}.json`),\n repositoryDirectory,\n sharedCacheDirectory: path.join(repositoryDirectory, SHARED_CACHE_DIRECTORY),\n directory,\n generationsDirectory: path.join(directory, GENERATIONS_DIRECTORY),\n statePath: path.join(directory, 'state.json'),\n legacyDirectory,\n legacyStatePath: path.join(legacyDirectory, 'state.json'),\n };\n}\n\nconst GENERATION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;\n\n/** Resolves one immutable generation below its worktree namespace. */\nexport function syncGenerationDirectory(location: SyncLocation, generation: string): string {\n if (!GENERATION_ID.test(generation) || generation === '.' || generation === '..') {\n throw new Error(`Invalid Doom sync generation id: ${generation}`);\n }\n return path.join(location.generationsDirectory, generation);\n}\n\nfunction rejectSymlink(target: string): void {\n try {\n if (fs.lstatSync(target).isSymbolicLink()) {\n throw new Error(`Doom sync namespace must not contain a symbolic link: ${target}`);\n }\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return;\n throw error;\n }\n}\n\ninterface SyncLockRecord {\n pid: number;\n token: string;\n}\n\nfunction readLockRecord(lockPath: string): SyncLockRecord | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf8')) as { pid?: unknown; token?: unknown };\n return typeof parsed.pid === 'number' && typeof parsed.token === 'string'\n ? { pid: parsed.pid, token: parsed.token }\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction processIsAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch (error) {\n return error instanceof Error && 'code' in error && error.code === 'EPERM';\n }\n}\n\nfunction removeStaleLock(lockPath: string): void {\n try {\n const stat = fs.statSync(lockPath);\n if (Date.now() - stat.mtimeMs <= STALE_LOCK_MS) return;\n const record = readLockRecord(lockPath);\n if (record && processIsAlive(record.pid)) return;\n fs.rmSync(lockPath, { force: true });\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) throw error;\n }\n}\n\n/** Acquires the exclusive publisher lock for one worktree namespace. */\nexport async function acquireSyncLocationLock(location: SyncLocation): Promise<() => Promise<void>> {\n assertSyncLocationSafe(location);\n await fs.promises.mkdir(location.directory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n await fs.promises.chmod(location.directory, PRIVATE_DIRECTORY_MODE);\n const lockPath = path.join(location.directory, LOCK_FILE);\n removeStaleLock(lockPath);\n const record: SyncLockRecord = { pid: process.pid, token: crypto.randomUUID() };\n let handle: fs.promises.FileHandle;\n try {\n handle = await fs.promises.open(lockPath, 'wx', PRIVATE_FILE_MODE);\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'EEXIST') {\n throw new Error(`Another Doom sync is already publishing ${location.directory}`);\n }\n throw error;\n }\n await handle.writeFile(`${JSON.stringify(record)}\\n`);\n const heartbeat = setInterval(\n () => {\n if (readLockRecord(lockPath)?.token !== record.token) return;\n const now = new Date();\n fs.utimesSync(lockPath, now, now);\n },\n Math.floor(STALE_LOCK_MS / 3),\n );\n heartbeat.unref();\n return async () => {\n clearInterval(heartbeat);\n await handle.close();\n if (readLockRecord(lockPath)?.token === record.token) {\n await fs.promises.rm(lockPath, { force: true });\n }\n };\n}\n\n/** Refuses generated namespace redirection before a writer creates or prunes it. */\nexport function assertSyncLocationSafe(location: SyncLocation): void {\n const syncRoot = path.dirname(location.repositoryDirectory);\n const doomRoot = path.dirname(syncRoot);\n const piRoot = path.dirname(doomRoot);\n const worktreesRoot = path.dirname(location.directory);\n for (const target of [piRoot, doomRoot, syncRoot, location.repositoryDirectory, worktreesRoot, location.directory]) {\n rejectSymlink(target);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncLocation.mjs","names":[],"sources":["../../../src/adapters/syncLocation.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst PI_DIRECTORY = '.pi';\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst SYNC_DIRECTORY = 'sync';\nconst WORKTREES_DIRECTORY = 'worktrees';\nconst GENERATIONS_DIRECTORY = 'generations';\nconst REGISTRATIONS_DIRECTORY = 'registrations';\nconst SHARED_CACHE_DIRECTORY = 'shared-cache';\nconst LEGACY_DOOM_DIRECTORY = 'doom';\nconst GIT_DIRECTORY = '.git';\nconst COMMON_DIRECTORY_FILE = 'commondir';\nconst IDENTITY_HEX_LENGTH = 32;\nconst LABEL_MAX_LENGTH = 48;\nconst DEFAULT_REPOSITORY_LABEL = 'repo';\nconst DEFAULT_WORKTREE_LABEL = 'worktree';\nconst LOCK_FILE = '.sync.lock';\nconst STALE_LOCK_MS = 5 * 60 * 1000;\nconst LOCK_RETRY_INTERVAL_MS = 100;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface SyncIdentity {\n repositoryId: string;\n worktreeId: string;\n}\n\nexport interface SyncLocation {\n root: string;\n identity: SyncIdentity;\n repositoryLabel: string;\n worktreeLabel: string;\n generatedRoot: string;\n registrationsDirectory: string;\n registrationPath: string;\n repositoryDirectory: string;\n sharedCacheDirectory: string;\n directory: string;\n generationsDirectory: string;\n statePath: string;\n legacyDirectory: string;\n legacyStatePath: string;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync.native(absolute);\n } catch {\n return absolute;\n }\n}\n\nfunction readGitDirectory(repositoryRoot: string): string | undefined {\n const gitPath = path.join(repositoryRoot, GIT_DIRECTORY);\n try {\n const stat = fs.statSync(gitPath);\n if (stat.isDirectory()) return canonicalPath(gitPath);\n if (!stat.isFile()) return undefined;\n const match = /^gitdir:\\s*(.+)$/imu.exec(fs.readFileSync(gitPath, 'utf8').trim());\n return match?.[1] ? canonicalPath(path.resolve(repositoryRoot, match[1].trim())) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction gitCommonDirectory(repositoryRoot: string): string | undefined {\n const gitDirectory = readGitDirectory(repositoryRoot);\n if (!gitDirectory) return undefined;\n try {\n const relative = fs.readFileSync(path.join(gitDirectory, COMMON_DIRECTORY_FILE), 'utf8').trim();\n return relative ? canonicalPath(path.resolve(gitDirectory, relative)) : gitDirectory;\n } catch {\n return gitDirectory;\n }\n}\n\nfunction identityHash(value: string): string {\n return crypto.createHash('sha256').update(value, 'utf8').digest('hex').slice(0, IDENTITY_HEX_LENGTH);\n}\n\nexport function sanitizeSyncLabel(value: string, fallback: string): string {\n const normalized = value\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/gu, '')\n .replace(/[^A-Za-z0-9._-]+/gu, '-')\n .replace(/^[._-]+|[._-]+$/gu, '')\n .slice(0, LABEL_MAX_LENGTH)\n .replace(/[._-]+$/gu, '');\n return normalized === '' || normalized === '.' || normalized === '..' ? fallback : normalized;\n}\n\nfunction repositoryLabel(commonDirectory: string | undefined, repositoryRoot: string): string {\n if (!commonDirectory) return sanitizeSyncLabel(path.basename(repositoryRoot), DEFAULT_REPOSITORY_LABEL);\n const base = path.basename(commonDirectory);\n const source = base === GIT_DIRECTORY ? path.basename(path.dirname(commonDirectory)) : base;\n return sanitizeSyncLabel(source, DEFAULT_REPOSITORY_LABEL);\n}\n\nexport function resolveSyncLocation(repositoryRoot: string, homeDirectory: string = os.homedir()): SyncLocation {\n const root = canonicalPath(repositoryRoot);\n const commonDirectory = gitCommonDirectory(root);\n const repositoryToken = commonDirectory ? `git:${commonDirectory}` : `root:${root}`;\n const identity: SyncIdentity = {\n repositoryId: identityHash(repositoryToken),\n worktreeId: identityHash(`${repositoryToken}\\0worktree:${root}`),\n };\n const repoLabel = repositoryLabel(commonDirectory, root);\n const worktreeLabel = sanitizeSyncLabel(path.basename(root), DEFAULT_WORKTREE_LABEL);\n const generatedRoot = path.join(path.resolve(homeDirectory), PI_DIRECTORY, DOOM_CONFIG_DIRECTORY, SYNC_DIRECTORY);\n const registrationsDirectory = path.join(generatedRoot, REGISTRATIONS_DIRECTORY);\n const repositoryDirectory = path.join(generatedRoot, `${repoLabel}--${identity.repositoryId}`);\n const directory = path.join(repositoryDirectory, WORKTREES_DIRECTORY, `${worktreeLabel}--${identity.worktreeId}`);\n const legacyDirectory = path.join(root, PI_DIRECTORY, LEGACY_DOOM_DIRECTORY);\n return {\n root,\n identity,\n repositoryLabel: repoLabel,\n worktreeLabel,\n generatedRoot,\n registrationsDirectory,\n registrationPath: path.join(registrationsDirectory, identity.repositoryId, `${identity.worktreeId}.json`),\n repositoryDirectory,\n sharedCacheDirectory: path.join(repositoryDirectory, SHARED_CACHE_DIRECTORY),\n directory,\n generationsDirectory: path.join(directory, GENERATIONS_DIRECTORY),\n statePath: path.join(directory, 'state.json'),\n legacyDirectory,\n legacyStatePath: path.join(legacyDirectory, 'state.json'),\n };\n}\n\nconst GENERATION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;\n\n/** Resolves one immutable generation below its worktree namespace. */\nexport function syncGenerationDirectory(location: SyncLocation, generation: string): string {\n if (!GENERATION_ID.test(generation) || generation === '.' || generation === '..') {\n throw new Error(`Invalid Doom sync generation id: ${generation}`);\n }\n return path.join(location.generationsDirectory, generation);\n}\n\nfunction rejectSymlink(target: string): void {\n try {\n if (fs.lstatSync(target).isSymbolicLink()) {\n throw new Error(`Doom sync namespace must not contain a symbolic link: ${target}`);\n }\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return;\n throw error;\n }\n}\n\ninterface SyncLockRecord {\n pid: number;\n token: string;\n}\n\nfunction readLockRecord(lockPath: string): SyncLockRecord | undefined {\n try {\n const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf8')) as { pid?: unknown; token?: unknown };\n return typeof parsed.pid === 'number' && typeof parsed.token === 'string'\n ? { pid: parsed.pid, token: parsed.token }\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction processIsAlive(processId: number): boolean {\n try {\n process.kill(processId, 0);\n return true;\n } catch (error) {\n return error instanceof Error && 'code' in error && error.code === 'EPERM';\n }\n}\n\nfunction removeStaleLock(lockPath: string): void {\n try {\n const stat = fs.statSync(lockPath);\n const record = readLockRecord(lockPath);\n if (record) {\n if (processIsAlive(record.pid)) return;\n } else if (Date.now() - stat.mtimeMs <= STALE_LOCK_MS) {\n // A publisher may have created the file but not written its record yet.\n return;\n }\n fs.rmSync(lockPath, { force: true });\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) throw error;\n }\n}\n\n/** Waits for and acquires the exclusive publisher lock for one worktree namespace. */\nexport async function acquireSyncLocationLock(location: SyncLocation): Promise<() => Promise<void>> {\n assertSyncLocationSafe(location);\n await fs.promises.mkdir(location.directory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n await fs.promises.chmod(location.directory, PRIVATE_DIRECTORY_MODE);\n const lockPath = path.join(location.directory, LOCK_FILE);\n const record: SyncLockRecord = { pid: process.pid, token: crypto.randomUUID() };\n let handle: fs.promises.FileHandle;\n for (;;) {\n removeStaleLock(lockPath);\n try {\n handle = await fs.promises.open(lockPath, 'wx', PRIVATE_FILE_MODE);\n break;\n } catch (error) {\n if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST')) throw error;\n await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));\n }\n }\n await handle.writeFile(`${JSON.stringify(record)}\\n`);\n const heartbeat = setInterval(\n () => {\n if (readLockRecord(lockPath)?.token !== record.token) return;\n const now = new Date();\n fs.utimesSync(lockPath, now, now);\n },\n Math.floor(STALE_LOCK_MS / 3),\n );\n heartbeat.unref();\n return async () => {\n clearInterval(heartbeat);\n await handle.close();\n if (readLockRecord(lockPath)?.token === record.token) {\n await fs.promises.rm(lockPath, { force: true });\n }\n };\n}\n\n/** Refuses generated namespace redirection before a writer creates or prunes it. */\nexport function assertSyncLocationSafe(location: SyncLocation): void {\n const syncRoot = path.dirname(location.repositoryDirectory);\n const doomRoot = path.dirname(syncRoot);\n const piRoot = path.dirname(doomRoot);\n const worktreesRoot = path.dirname(location.directory);\n for (const target of [piRoot, doomRoot, syncRoot, location.repositoryDirectory, worktreesRoot, location.directory]) {\n rejectSymlink(target);\n }\n}\n"],"mappings":"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"}
Binary file
@@ -1 +1 @@
1
- {"version":3,"file":"syncState.cjs","names":["os","resolveSyncLocation","readSyncRegistration","path","fs","crypto","loadDomains","resolvePluginEntries","loadProfiles","PERSONA_FILES","isRecord","isDoomMcpProjection","LAYER_RESOLVERS","PERSONA_ENTRY","OLLAMA_PRESET"],"sources":["../../../src/adapters/syncState.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { loadDomains, resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport type { LayerResolvers, MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { loadProfiles, PERSONA_FILES } from '@agimon-ai/doompi-config/profiles';\nimport type { HarnessState } from '@agimon-ai/doompi-config/types';\nimport { isDoomMcpProjection, type DoomMcpProjection } from '@agimon-ai/doompi-extension-contracts/mcp-projection';\nimport {\n assembleChildExtensions,\n assembleExtensions,\n type ExtensionLayerResolvers,\n LAYER_RESOLVERS,\n OLLAMA_PRESET,\n PERSONA_ENTRY,\n} from '../services/extensionAssembler.ts';\nimport { isRecord } from './serialization/json';\nimport type { JsonObject } from './serialization/json';\nimport { assertSyncLocationSafe, resolveSyncLocation, type SyncIdentity, type SyncLocation } from './syncLocation.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\nimport { PRECOMPILE_STATE_VERSION, SYNC_STATE_VERSION } from './syncStateContract.ts';\n\nexport { SYNC_STATE_VERSION } from './syncStateContract.ts';\n\n/**\n * The file `doom-pi sync` writes and the doom-pi Pi extension reads back.\n *\n * This is the whole contract between the two halves of the synced path. Sync\n * runs in plain Node, where module resolution and the repository config are\n * available; the extension runs inside a Pi session, where neither can be\n * relied on. Everything the extension needs is therefore resolved once here,\n * and everything in this file has to survive JSON.\n */\n\nconst PI_DIRECTORY = '.pi';\n/** The `.doom` config directory name, under both the repo root and `~/.pi`. */\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst RUN_DIRECTORY = 'run';\nconst DEFAULT_PRESET = 'default';\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst OWN_PREFIX = 'own:';\nconst PACKAGE_PREFIX = 'pkg:';\nconst LOCAL_PREFIX = 'local:';\nconst LOCAL_PACKAGE_NAME_PREFIX = 'local-package-name:';\nconst ENTRY_INDEX_SEPARATOR = ':entry:';\nconst COMPOSITION_FINGERPRINT = /^[a-f0-9]{64}$/u;\nconst PLUGIN_MCP_CONFIG_FILES = ['.mcp.json', 'mcp.json'] as const;\nconst PACKAGE_MANIFEST_FILE = 'package.json';\n/** Folder the cockpit bundler compiles straight from each installed package. */\nconst WEB_PLUGIN_DIRECTORY = 'web';\n\n/** Config that feeds a sync, in the order the hash reads it. */\nconst HASHED_CONFIG_FILES = ['config.yaml', 'modes.yaml', 'domains.yaml', 'profiles.yaml', 'hooks.yaml'] as const;\n\nexport interface SyncSelection {\n majorMode: string;\n domains: string[];\n profile?: string;\n preset: string;\n}\n\n/** Paths to the resources sync staged, before any live switch replaces them. */\nexport interface SyncBaseline {\n mcpConfigPath?: string;\n personaFile?: string;\n themePath: string;\n themeName: string;\n}\n\nexport interface SyncPrecompileState {\n /** Version of the lightweight freshness contract used by package startup. */\n version: number;\n strategy: 'bundle';\n /** Package entry the generated bootstrap bundles. */\n bootstrapEntry: string;\n /** Compiler manifest whose output is the graph-bundled package bootstrap. */\n bootstrapManifest: string;\n /** Compiler manifest for each graph-bundled composition. */\n bundleManifests: Record<string, string>;\n}\n\nexport interface SyncState {\n version: number;\n root: string;\n /** Canonical repository and worktree namespace that owns this state. */\n identity: SyncIdentity;\n /** Hash of the config that produced this state, for the staleness warning. */\n inputsHash: string;\n /**\n * Hash of the cockpit sources and package versions this state was built from.\n *\n * Absent in a state written before cockpit sources were tracked, which reads\n * as \"cannot be compared\" rather than as a change, so an old state does not\n * force a resync on sight.\n */\n webSourcesHash?: string;\n /** Canonical composition selected when this state was recorded. */\n compositionFingerprint: string;\n selection: SyncSelection;\n /** Harness environment the extension applies when the values are unset. */\n env: Record<string, string>;\n /**\n * State the environment does not carry, so it has to be recorded on its own.\n *\n * Plugin hooks, the profile's environment defaults, and the neutral MCP\n * projection live in the session state file rather than in variables.\n */\n fileState: Pick<HarnessState, 'profileEnvironment' | 'pluginHooks'> & {\n mcpProjection: DoomMcpProjection;\n };\n /** Entry name to absolute path, covering every layer, not just the selected mode's. */\n resolved: Record<string, string>;\n /**\n * The same entries after TypeScript ones were bundled to plain ESM.\n *\n * Kept apart from `resolved` so the staleness check still compares resolution\n * against resolution; a state written before this existed simply has none and\n * falls back to loading the source.\n */\n compiled?: Record<string, string>;\n /** Aggregate entry by canonical composition fingerprint. */\n bundles?: Record<string, string>;\n /** Graph-compiled synced bootstrap dynamically loaded by the package entry. */\n bootstrap?: string;\n /** Inputs used to validate generated artifacts without loading the compiler. */\n precompile?: SyncPrecompileState;\n baseline: SyncBaseline;\n}\n\nexport function syncDirectory(repoRoot: string, homeDirectory: string = os.homedir()): string {\n return resolveSyncLocation(repoRoot, homeDirectory).directory;\n}\n\nexport function syncStatePath(repoRoot: string, homeDirectory: string = os.homedir()): string {\n return (\n readSyncRegistration(repoRoot, homeDirectory)?.statePath ?? resolveSyncLocation(repoRoot, homeDirectory).statePath\n );\n}\n\nexport function legacySyncDirectory(repoRoot: string): string {\n return resolveSyncLocation(repoRoot).legacyDirectory;\n}\n\nexport function legacySyncStatePath(repoRoot: string): string {\n return resolveSyncLocation(repoRoot).legacyStatePath;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync(absolute);\n } catch {\n const parent = path.dirname(absolute);\n if (parent === absolute) return absolute;\n return path.join(canonicalPath(parent), path.basename(absolute));\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(canonicalPath(directory), canonicalPath(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\n/** Whether generated state belongs to the repository that is reading it. */\nexport function syncStateRootMatches(repoRoot: string, stateRoot: string): boolean {\n return canonicalPath(repoRoot) === canonicalPath(stateRoot);\n}\n\n/**\n * Per-process scratch directory for live `/domains` and `/profile` switches.\n *\n * Switching rewrites the MCP config and the persona prompt. Writing those back\n * into the synced directory would both mutate the pinned baseline and let two\n * Pi sessions in one repository overwrite each other, which the launcher avoids\n * by giving every run its own temporary directory.\n */\nexport function runDirectory(\n repoRoot: string,\n processId: number = process.pid,\n homeDirectory: string = os.homedir(),\n): string {\n return path.join(syncDirectory(repoRoot, homeDirectory), RUN_DIRECTORY, String(processId));\n}\n\n/**\n * How a Pi project settings file should refer to a generated path.\n *\n * Relative to `.pi` when the target lives in the repository, which keeps the\n * committed settings file identical on every machine. A doom-pi installed\n * outside the repository stays absolute: Pi accepts both, and a relative path\n * climbing out of the project would be neither portable nor readable.\n */\nexport function settingsRelativePath(repoRoot: string, target: string): string {\n const relative = path.relative(repoRoot, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) return target;\n return path.relative(path.join(repoRoot, PI_DIRECTORY), target).split(path.sep).join('/');\n}\n\nfunction readIfPresent(filePath: string): string {\n return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';\n}\n\nfunction updateFramedHash(hash: ReturnType<typeof crypto.createHash>, label: string, value: string | Buffer): void {\n const bytes = typeof value === 'string' ? Buffer.from(value) : value;\n hash.update(`${String(label.length)}:${label}:${String(bytes.byteLength)}:`);\n hash.update(bytes);\n}\n\n/** Hashes path, existence, and bytes without making absent and empty files aliases. */\nfunction updateFileInputHash(hash: ReturnType<typeof crypto.createHash>, filePath: string): void {\n const absolutePath = path.resolve(filePath);\n updateFramedHash(hash, 'path', absolutePath);\n if (!fs.existsSync(absolutePath)) {\n updateFramedHash(hash, 'state', 'missing');\n return;\n }\n updateFramedHash(hash, 'state', 'present');\n updateFramedHash(hash, 'contents', fs.readFileSync(absolutePath));\n}\n\n/** Package root owning a resolved entry, or undefined when it sits outside one. */\nfunction owningPackageRoot(entryPath: string): string | undefined {\n let directory = path.dirname(path.resolve(entryPath));\n for (;;) {\n if (fs.existsSync(path.join(directory, PACKAGE_MANIFEST_FILE))) return directory;\n const parent = path.dirname(directory);\n if (parent === directory) return undefined;\n directory = parent;\n }\n}\n\n/** Every regular file under a directory, as absolute paths in a stable order. */\nfunction filesUnder(directory: string): string[] {\n if (!fs.existsSync(directory)) return [];\n const found: string[] = [];\n const walk = (current: string): void => {\n for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {\n const full = path.join(current, entry.name);\n if (entry.isDirectory()) walk(full);\n else if (entry.isFile()) found.push(full);\n }\n };\n walk(directory);\n return found;\n}\n\n/**\n * Hashes the cockpit sources and package versions a sync compiled.\n *\n * The cockpit bundle is built from each installed package's `web/` folder by\n * `syncWebBundle`, straight from source, with no compiler manifest recording\n * what went in. Nothing else in the drift check reads those files, so editing a\n * plugin surface and rebuilding left the hub serving the previously bundled\n * cockpit with nothing reporting a reason. Compiled entries are covered\n * elsewhere, by the content digests in their compiler manifests, so only the\n * entry identity and its package version are folded in here.\n *\n * Bounded on purpose: the web sources of a full composition are a few hundred\n * small files, which is cheap enough for the hub to poll.\n */\nexport function computeWebSourcesHash(resolved: Record<string, string>): string {\n const hash = crypto.createHash('sha256');\n const roots = [...new Set(Object.values(resolved).flatMap((entry) => owningPackageRoot(entry) ?? []))].sort();\n for (const root of roots) {\n updateFramedHash(hash, 'package', root);\n const manifestPath = path.join(root, PACKAGE_MANIFEST_FILE);\n let manifest: { name?: unknown; version?: unknown } = {};\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { name?: unknown; version?: unknown };\n } catch {\n // An unreadable manifest still contributes its path above, so the package\n // is not silently dropped from the hash; the version simply reads empty\n // and any later repair shows up as a change.\n manifest = {};\n }\n updateFramedHash(hash, 'name', typeof manifest.name === 'string' ? manifest.name : '');\n updateFramedHash(hash, 'version', typeof manifest.version === 'string' ? manifest.version : '');\n for (const file of filesUnder(path.join(root, WEB_PLUGIN_DIRECTORY))) updateFileInputHash(hash, file);\n }\n for (const [name, entry] of Object.entries(resolved).sort(([left], [right]) => left.localeCompare(right))) {\n updateFramedHash(hash, 'entry', `${name}\u0000${entry}`);\n }\n return hash.digest('hex');\n}\n\n/**\n * Hashes the configuration a sync consumed.\n *\n * Deliberately cheap: the extension recomputes this on every session start, so\n * it reads config, marketplace, local plugin manifests, and profile persona text\n * without downloading plugins or re-resolving packages. A dependency upgrade that\n * moves a package path therefore does not show up here; `doom-pi sync --check`\n * re-resolves and is the check that catches that.\n */\nexport function computeInputsHash(\n repoRoot: string,\n selection: SyncSelection,\n homeDirectory: string = os.homedir(),\n): string {\n const hash = crypto.createHash('sha256');\n hash.update(JSON.stringify({ ...selection, domains: [...selection.domains].sort() }));\n // Every .doom document layers the global copy under the repository one, so an\n // edit in either location is a real change.\n for (const directory of [\n path.join(homeDirectory, PI_DIRECTORY, DOOM_CONFIG_DIRECTORY),\n path.join(repoRoot, DOOM_CONFIG_DIRECTORY),\n ]) {\n for (const fileName of HASHED_CONFIG_FILES) hash.update(readIfPresent(path.join(directory, fileName)));\n }\n updateFileInputHash(hash, path.join(repoRoot, '.mcp.json'));\n\n const catalog = loadDomains(repoRoot, homeDirectory).plugins;\n for (const root of catalog.roots) hash.update(root);\n for (const diagnostic of catalog.diagnostics) hash.update(diagnostic);\n for (const marketplace of catalog.marketplaces) {\n hash.update(marketplace);\n hash.update(readIfPresent(marketplace));\n }\n for (const [name, plugin] of Object.entries(catalog.entries).sort(([left], [right]) => left.localeCompare(right))) {\n hash.update(name);\n hash.update(JSON.stringify(plugin.source));\n if (plugin.manifest) {\n hash.update(plugin.manifest.path);\n hash.update(readIfPresent(plugin.manifest.path));\n }\n }\n\n let selectedPluginDirectories: string[] = [];\n try {\n selectedPluginDirectories = [\n ...new Set(\n resolvePluginEntries(repoRoot, selection.domains, [], homeDirectory)\n .filter((plugin) => plugin.mcp !== false)\n .map((plugin) => path.resolve(plugin.directory)),\n ),\n ].sort();\n } catch {\n // The surrounding .doom inputs already make an invalid or removed domain\n // selection stale. Keep this lightweight check readable for old state\n // instead of turning that expected staleness into a startup exception.\n }\n for (const directory of selectedPluginDirectories) {\n for (const fileName of PLUGIN_MCP_CONFIG_FILES) {\n updateFileInputHash(hash, path.join(directory, fileName));\n }\n }\n\n for (const profile of loadProfiles(repoRoot, homeDirectory)) {\n hash.update(profile.name);\n hash.update(profile.personaRoot);\n hash.update(profile.persona);\n hash.update(JSON.stringify(profile.env));\n const personaDirectory = path.resolve(profile.personaRoot, profile.persona);\n for (const fileName of PERSONA_FILES) {\n const filePath = path.join(personaDirectory, fileName);\n hash.update(filePath);\n hash.update(readIfPresent(filePath));\n }\n }\n return hash.digest('hex');\n}\n\nfunction stringRecord(value: unknown, field: string, statePath: string): Record<string, string> {\n if (!isRecord(value)) throw new Error(`Doom sync state at ${statePath} requires ${field} to be an object`);\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction parsePrecompileState(value: unknown, statePath: string): SyncPrecompileState {\n if (\n !isRecord(value) ||\n value.version !== PRECOMPILE_STATE_VERSION ||\n value.strategy !== 'bundle' ||\n typeof value.bootstrapEntry !== 'string' ||\n typeof value.bootstrapManifest !== 'string' ||\n !isRecord(value.bundleManifests) ||\n Object.entries(value.bundleManifests).some(\n ([fingerprint, manifest]) => !COMPOSITION_FINGERPRINT.test(fingerprint) || typeof manifest !== 'string',\n )\n ) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n return {\n version: value.version,\n strategy: value.strategy,\n bootstrapEntry: value.bootstrapEntry,\n bootstrapManifest: value.bootstrapManifest,\n bundleManifests: value.bundleManifests as Record<string, string>,\n };\n}\n\nfunction precompileMatchesBundles(\n precompile: SyncPrecompileState,\n bundles: Record<string, string> | undefined,\n): boolean {\n const bundleFingerprints = Object.keys(bundles ?? {}).sort();\n const manifestFingerprints = Object.keys(precompile.bundleManifests).sort();\n return (\n bundleFingerprints.length === manifestFingerprints.length &&\n bundleFingerprints.every((fingerprint, index) => fingerprint === manifestFingerprints[index])\n );\n}\n\ninterface ParseSyncStateOptions {\n repoRoot: string;\n location: SyncLocation;\n legacy: boolean;\n}\n\nfunction identityMatches(left: SyncIdentity, right: SyncIdentity): boolean {\n return left.repositoryId === right.repositoryId && left.worktreeId === right.worktreeId;\n}\n\nfunction parseSyncIdentity(value: unknown, statePath: string): SyncIdentity {\n if (!isRecord(value) || typeof value.repositoryId !== 'string' || typeof value.worktreeId !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires repository and worktree identity`);\n }\n return { repositoryId: value.repositoryId, worktreeId: value.worktreeId };\n}\n\nfunction parseSyncState(source: string, statePath: string, options: ParseSyncStateOptions): SyncState {\n let parsed: unknown;\n try {\n parsed = JSON.parse(source);\n } catch (error) {\n throw new Error(`Doom sync state at ${statePath} is not valid JSON`, { cause: error });\n }\n if (!isRecord(parsed)) throw new Error(`Doom sync state at ${statePath} must be an object`);\n if (typeof parsed.version !== 'number' || parsed.version !== SYNC_STATE_VERSION) {\n throw new Error(\n `Doom sync state at ${statePath} has version ${String(parsed.version)}, expected ${SYNC_STATE_VERSION}. Run doompi sync.`,\n );\n }\n if (typeof parsed.root !== 'string' || typeof parsed.inputsHash !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires string root and inputsHash`);\n }\n if (\n typeof parsed.compositionFingerprint !== 'string' ||\n !COMPOSITION_FINGERPRINT.test(parsed.compositionFingerprint)\n ) {\n throw new Error(`Doom sync state at ${statePath} requires a canonical compositionFingerprint`);\n }\n if (parsed.nativeGraphs !== undefined) {\n throw new Error(`Doom sync state at ${statePath} contains removed native graph state. Run doompi sync.`);\n }\n if (!syncStateRootMatches(options.repoRoot, parsed.root)) {\n throw new Error(`Doom sync state at ${statePath} belongs to a different repository: ${parsed.root}`);\n }\n const identity = parseSyncIdentity(parsed.identity, statePath);\n if (!identityMatches(identity, options.location.identity)) {\n throw new Error(`Doom sync state at ${statePath} belongs to a different repository or worktree identity`);\n }\n if (!isRecord(parsed.selection) || typeof parsed.selection.majorMode !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a selection with a majorMode`);\n }\n if (!isRecord(parsed.baseline) || typeof parsed.baseline.themePath !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a baseline with a themePath`);\n }\n const { majorMode, domains, profile, preset } = parsed.selection;\n const fileState = isRecord(parsed.fileState) ? parsed.fileState : {};\n if (!isDoomMcpProjection(fileState.mcpProjection)) {\n throw new Error(`Doom sync state at ${statePath} requires a valid MCP projection. Run doompi sync.`);\n }\n if (!syncStateRootMatches(parsed.root, fileState.mcpProjection.repoRoot)) {\n throw new Error(`Doom sync state at ${statePath} contains an MCP projection for a different repository`);\n }\n const generatedDirectory = options.legacy ? options.location.legacyDirectory : options.location.directory;\n const compiled = parsed.compiled === undefined ? undefined : stringRecord(parsed.compiled, 'compiled', statePath);\n const bundles = parsed.bundles === undefined ? undefined : stringRecord(parsed.bundles, 'bundles', statePath);\n const precompile = parsed.precompile === undefined ? undefined : parsePrecompileState(parsed.precompile, statePath);\n if (precompile && !precompileMatchesBundles(precompile, bundles)) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n const bootstrap = typeof parsed.bootstrap === 'string' ? parsed.bootstrap : undefined;\n const generatedPaths = [bootstrap, ...Object.values(compiled ?? {}), ...Object.values(bundles ?? {})].filter(\n (entry): entry is string => entry !== undefined,\n );\n if (generatedPaths.some((entry) => !isInside(generatedDirectory, entry))) {\n throw new Error(`Doom sync state at ${statePath} references generated material outside ${generatedDirectory}`);\n }\n return {\n version: parsed.version,\n root: parsed.root,\n identity,\n inputsHash: parsed.inputsHash,\n webSourcesHash: typeof parsed.webSourcesHash === 'string' ? parsed.webSourcesHash : undefined,\n compositionFingerprint: parsed.compositionFingerprint,\n selection: {\n majorMode,\n domains: Array.isArray(domains) ? domains.filter((name): name is string => typeof name === 'string') : [],\n profile: typeof profile === 'string' ? profile : undefined,\n preset: typeof preset === 'string' ? preset : DEFAULT_PRESET,\n },\n env: stringRecord(parsed.env, 'env', statePath),\n fileState: {\n profileEnvironment: isRecord(fileState.profileEnvironment)\n ? (fileState.profileEnvironment as Record<string, string>)\n : {},\n pluginHooks: Array.isArray(fileState.pluginHooks) ? (fileState.pluginHooks as HarnessState['pluginHooks']) : [],\n mcpProjection: fileState.mcpProjection,\n },\n resolved: stringRecord(parsed.resolved, 'resolved', statePath),\n compiled,\n bundles,\n bootstrap,\n precompile,\n baseline: parsed.baseline as unknown as SyncBaseline,\n };\n}\n\nexport interface LocatedSyncState {\n state: SyncState;\n location: SyncLocation;\n layout: 'global';\n}\n\n/** Reads only state reached through this repository/worktree's validated registration. */\nexport function readLocatedSyncState(\n repoRoot: string,\n homeDirectory: string = os.homedir(),\n): LocatedSyncState | undefined {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n if (!registration) return undefined;\n return {\n state: parseSyncState(fs.readFileSync(registration.statePath, 'utf8'), registration.statePath, {\n repoRoot,\n location,\n legacy: false,\n }),\n location,\n layout: 'global',\n };\n}\n\n/** Reads the synced state, or undefined when the repository was never synced. */\nexport function readSyncState(repoRoot: string, homeDirectory: string = os.homedir()): SyncState | undefined {\n return readLocatedSyncState(repoRoot, homeDirectory)?.state;\n}\n\nexport function serializeSyncState(state: SyncState): string {\n return `${JSON.stringify(state, null, 2)}\\n`;\n}\n\nexport async function writeSyncState(\n repoRoot: string,\n state: SyncState,\n homeDirectory: string = os.homedir(),\n targetPath?: string,\n): Promise<string> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const statePath = targetPath ?? location.statePath;\n if (state.version !== SYNC_STATE_VERSION) {\n throw new Error(`Doom sync state writes require version ${SYNC_STATE_VERSION}`);\n }\n if (!COMPOSITION_FINGERPRINT.test(state.compositionFingerprint)) {\n throw new Error('Doom sync state writes require a canonical composition fingerprint');\n }\n if (!isDoomMcpProjection(state.fileState.mcpProjection)) {\n throw new Error('Doom sync state writes require a valid MCP projection');\n }\n if (!syncStateRootMatches(state.root, state.fileState.mcpProjection.repoRoot)) {\n throw new Error('Refusing to write a Doom sync state whose MCP projection belongs to another repository');\n }\n if (!syncStateRootMatches(repoRoot, state.root) || !identityMatches(state.identity, location.identity)) {\n throw new Error('Refusing to write Doom sync state for a different repository or worktree');\n }\n if (state.precompile) {\n const precompile = parsePrecompileState(state.precompile, statePath);\n if (!precompileMatchesBundles(precompile, state.bundles)) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n }\n if (!isInside(location.directory, statePath)) {\n throw new Error(`Refusing to write Doom sync state outside ${location.directory}`);\n }\n assertSyncLocationSafe(location);\n await fs.promises.mkdir(path.dirname(statePath), { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const temporaryPath = `${statePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n await fs.promises.writeFile(temporaryPath, serializeSyncState(state), { mode: PRIVATE_FILE_MODE });\n await fs.promises.rename(temporaryPath, statePath);\n } catch (error) {\n await fs.promises.rm(temporaryPath, { force: true });\n throw error;\n }\n return statePath;\n}\n\nexport function ownKey(name: string): string {\n return `${OWN_PREFIX}${name}`;\n}\n\nexport function packageKey(specifier: string): string {\n return `${PACKAGE_PREFIX}${specifier}`;\n}\n\nexport function packageEntryKey(specifier: string, index: number): string {\n return index === 0 ? packageKey(specifier) : `${packageKey(specifier)}${ENTRY_INDEX_SEPARATOR}${index}`;\n}\n\n/**\n * Keyed by the absolute target, because the same relative specifier means\n * different files under the repository and the global config directory.\n */\nexport function localKey(specifier: string, baseDirectory: string): string {\n return `${LOCAL_PREFIX}${path.resolve(baseDirectory, specifier)}`;\n}\n\nexport function localEntryKey(specifier: string, baseDirectory: string, index: number): string {\n return index === 0\n ? localKey(specifier, baseDirectory)\n : `${localKey(specifier, baseDirectory)}${ENTRY_INDEX_SEPARATOR}${index}`;\n}\n\nexport function localPackageNameKey(specifier: string, baseDirectory: string): string {\n return `${LOCAL_PACKAGE_NAME_PREFIX}${path.resolve(baseDirectory, specifier)}`;\n}\n\nexport interface RecordingResolvers extends ExtensionLayerResolvers {\n /** Every name this resolver was asked for, keyed for the state file. */\n readonly resolved: Record<string, string>;\n packageEntries(name: string): string[];\n optionalPackageEntries(name: string): string[] | undefined;\n localEntries(specifier: string, baseDirectory: string): string[] | undefined;\n}\n\n/**\n * Resolvers that record what they resolve.\n *\n * Sync composes once with every layer selected and hands the recording to the\n * state file, so the extension never has to resolve a specifier itself.\n */\nexport function createRecordingResolvers(base: LayerResolvers = LAYER_RESOLVERS): RecordingResolvers {\n const resolved: Record<string, string> = {};\n const extensionBase = base as ExtensionLayerResolvers;\n const recordEntries = (entries: string[], keyFor: (index: number) => string): string[] => {\n entries.forEach((entry, index) => {\n resolved[keyFor(index)] = entry;\n });\n return entries;\n };\n return {\n resolved,\n ownEntry(name) {\n const entry = base.ownEntry(name);\n resolved[ownKey(name)] = entry;\n return entry;\n },\n packageEntry(name) {\n const entry = base.packageEntry(name);\n resolved[packageKey(name)] = entry;\n return entry;\n },\n optionalPackageEntry(name) {\n const entry = base.optionalPackageEntry(name);\n // A miss is recorded by omission, which is what later makes the extension\n // treat it as absent instead of failing the whole composition.\n if (entry) resolved[packageKey(name)] = entry;\n return entry;\n },\n packageEntries(name) {\n const entries = extensionBase.packageEntries?.(name) ?? [base.packageEntry(name)];\n return recordEntries(entries, (index) => packageEntryKey(name, index));\n },\n optionalPackageEntries(name) {\n let entries = extensionBase.optionalPackageEntries?.(name);\n if (entries === undefined) {\n const entry = base.optionalPackageEntry(name);\n entries = entry ? [entry] : undefined;\n }\n return entries ? recordEntries(entries, (index) => packageEntryKey(name, index)) : undefined;\n },\n localEntry(specifier, baseDirectory) {\n const entry = base.localEntry(specifier, baseDirectory);\n if (entry) resolved[localKey(specifier, baseDirectory)] = entry;\n return entry;\n },\n localEntries(specifier, baseDirectory) {\n let entries = extensionBase.localEntries?.(specifier, baseDirectory);\n if (entries === undefined) {\n const entry = base.localEntry(specifier, baseDirectory);\n entries = entry ? [entry] : undefined;\n }\n return entries ? recordEntries(entries, (index) => localEntryKey(specifier, baseDirectory, index)) : undefined;\n },\n localPackageName(specifier, baseDirectory) {\n const name = extensionBase.localPackageName?.(specifier, baseDirectory);\n if (name) resolved[localPackageNameKey(specifier, baseDirectory)] = name;\n return name;\n },\n };\n}\n\n/**\n * Resolves every entry any layer could contribute, in one pass.\n *\n * Composing with every layer selected and the widest options is what makes the\n * map complete: a later `/mode` switch inside a Pi session can then pick a\n * different set without resolving anything itself. The persona entry is asked\n * for separately because it remains a fixed host entry rather than a layer.\n */\nexport function recordResolvedEntries(\n majorModesConfig: MajorModesConfig,\n base: LayerResolvers = LAYER_RESOLVERS,\n): Record<string, string> {\n const recording = createRecordingResolvers(base);\n recording.packageEntry(PERSONA_ENTRY);\n assembleExtensions({\n agents: true,\n autoStop: true,\n mute: false,\n // The one preset that contributes an extension of its own.\n preset: OLLAMA_PRESET,\n layers: Object.keys(majorModesConfig.layers),\n majorModesConfig,\n resolvers: recording,\n });\n assembleChildExtensions({\n agents: false,\n autoStop: false,\n mute: true,\n preset: OLLAMA_PRESET,\n layers: Object.keys(majorModesConfig.layers),\n majorModesConfig,\n resolvers: recording,\n });\n return recording.resolved;\n}\n\n/** Resolvers backed by a synced map, for composing inside a Pi session. */\nexport function createMapResolvers(\n resolved: Record<string, string>,\n compiled: Record<string, string> = {},\n): ExtensionLayerResolvers {\n // The compiled bundle when this entry has one, the source otherwise.\n const entryFor = (key: string): string | undefined => compiled[key] ?? resolved[key];\n const lookup = (key: string, name: string): string => {\n const entry = entryFor(key);\n if (!entry) throw new Error(`${name} is missing from the doompi sync state. Run doompi sync.`);\n return entry;\n };\n const indexedEntries = (keyFor: (index: number) => string): string[] | undefined => {\n const first = entryFor(keyFor(0));\n if (!first) return undefined;\n const entries = [first];\n for (let index = 1; ; index += 1) {\n const entry = entryFor(keyFor(index));\n if (!entry) break;\n entries.push(entry);\n }\n return entries;\n };\n return {\n ownEntry: (name) => lookup(ownKey(name), name),\n packageEntry: (name) => lookup(packageKey(name), name),\n optionalPackageEntry: (name) => entryFor(packageKey(name)),\n packageEntries: (name) => {\n const entries = indexedEntries((index) => packageEntryKey(name, index));\n if (!entries) throw new Error(`${name} is missing from the doompi sync state. Run doompi sync.`);\n return entries;\n },\n optionalPackageEntries: (name) => indexedEntries((index) => packageEntryKey(name, index)),\n localEntry: (specifier, baseDirectory) => entryFor(localKey(specifier, baseDirectory)),\n localEntries: (specifier, baseDirectory) =>\n indexedEntries((index) => localEntryKey(specifier, baseDirectory, index)),\n localPackageName: (specifier, baseDirectory) => resolved[localPackageNameKey(specifier, baseDirectory)],\n };\n}\n\n/** Reads the resolved MCP servers back out of a generated config. */\nexport function readMcpServerNames(configPath: string): string[] {\n if (!fs.existsSync(configPath)) return [];\n const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as JsonObject;\n return isRecord(config.mcpServers) ? Object.keys(config.mcpServers) : [];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncState.cjs","names":["os","resolveSyncLocation","readSyncRegistration","path","fs","crypto","loadDomains","resolvePluginEntries","loadProfiles","PERSONA_FILES","isRecord","isDoomMcpProjection","LAYER_RESOLVERS","PERSONA_ENTRY","OLLAMA_PRESET"],"sources":["../../../src/adapters/syncState.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { loadDomains, resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport type { LayerResolvers, MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { loadProfiles, PERSONA_FILES } from '@agimon-ai/doompi-config/profiles';\nimport type { HarnessState } from '@agimon-ai/doompi-config/types';\nimport { isDoomMcpProjection, type DoomMcpProjection } from '@agimon-ai/doompi-extension-contracts/mcp-projection';\nimport {\n assembleChildExtensions,\n assembleExtensions,\n type ExtensionLayerResolvers,\n LAYER_RESOLVERS,\n OLLAMA_PRESET,\n PERSONA_ENTRY,\n} from '../services/extensionAssembler.ts';\nimport { isRecord } from './serialization/json';\nimport type { JsonObject } from './serialization/json';\nimport { assertSyncLocationSafe, resolveSyncLocation, type SyncIdentity, type SyncLocation } from './syncLocation.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\nimport { PRECOMPILE_STATE_VERSION, SYNC_STATE_VERSION } from './syncStateContract.ts';\n\nexport { SYNC_STATE_VERSION } from './syncStateContract.ts';\n\n/**\n * The file `doom-pi sync` writes and the doom-pi Pi extension reads back.\n *\n * This is the whole contract between the two halves of the synced path. Sync\n * runs in plain Node, where module resolution and the repository config are\n * available; the extension runs inside a Pi session, where neither can be\n * relied on. Everything the extension needs is therefore resolved once here,\n * and everything in this file has to survive JSON.\n */\n\nconst PI_DIRECTORY = '.pi';\n/** The `.doom` config directory name, under both the repo root and `~/.pi`. */\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst RUN_DIRECTORY = 'run';\nconst DEFAULT_PRESET = 'default';\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst OWN_PREFIX = 'own:';\nconst PACKAGE_PREFIX = 'pkg:';\nconst LOCAL_PREFIX = 'local:';\nconst LOCAL_PACKAGE_NAME_PREFIX = 'local-package-name:';\nconst ENTRY_INDEX_SEPARATOR = ':entry:';\nconst COMPOSITION_FINGERPRINT = /^[a-f0-9]{64}$/u;\nconst PLUGIN_MCP_CONFIG_FILES = ['.mcp.json', 'mcp.json'] as const;\nconst PACKAGE_MANIFEST_FILE = 'package.json';\n/** Folder the cockpit bundler compiles straight from each installed package. */\nconst WEB_PLUGIN_DIRECTORY = 'web';\n\n/** Config that feeds a sync, in the order the hash reads it. */\nconst HASHED_CONFIG_FILES = ['config.yaml', 'modes.yaml', 'domains.yaml', 'profiles.yaml', 'hooks.yaml'] as const;\n\nexport interface SyncSelection {\n majorMode: string;\n domains: string[];\n profile?: string;\n preset: string;\n}\n\n/** Paths to the resources sync staged, before any live switch replaces them. */\nexport interface SyncBaseline {\n mcpConfigPath?: string;\n personaFile?: string;\n themePath: string;\n themeName: string;\n}\n\nexport interface SyncPrecompileState {\n /** Version of the lightweight freshness contract used by package startup. */\n version: number;\n strategy: 'bundle';\n /** Package entry the generated bootstrap bundles. */\n bootstrapEntry: string;\n /** Compiler manifest whose output is the graph-bundled package bootstrap. */\n bootstrapManifest: string;\n /** Compiler manifest for each graph-bundled composition. */\n bundleManifests: Record<string, string>;\n}\n\nexport interface SyncState {\n version: number;\n root: string;\n /** Canonical repository and worktree namespace that owns this state. */\n identity: SyncIdentity;\n /** Hash of the config that produced this state, for the staleness warning. */\n inputsHash: string;\n /**\n * Hash of the cockpit sources and package versions this state was built from.\n *\n * Absent in a state written before cockpit sources were tracked, which reads\n * as \"cannot be compared\" rather than as a change, so an old state does not\n * force a resync on sight.\n */\n webSourcesHash?: string;\n /** Canonical composition selected when this state was recorded. */\n compositionFingerprint: string;\n selection: SyncSelection;\n /** Harness environment the extension applies when the values are unset. */\n env: Record<string, string>;\n /**\n * State the environment does not carry, so it has to be recorded on its own.\n *\n * Plugin hooks, the profile's environment defaults, and the neutral MCP\n * projection live in the session state file rather than in variables.\n */\n fileState: Pick<HarnessState, 'profileEnvironment' | 'pluginHooks'> & {\n mcpProjection: DoomMcpProjection;\n };\n /** Entry name to absolute path, covering every layer, not just the selected mode's. */\n resolved: Record<string, string>;\n /**\n * The same entries after TypeScript ones were bundled to plain ESM.\n *\n * Kept apart from `resolved` so the staleness check still compares resolution\n * against resolution; a state written before this existed simply has none and\n * falls back to loading the source.\n */\n compiled?: Record<string, string>;\n /** Aggregate entry by canonical composition fingerprint. */\n bundles?: Record<string, string>;\n /** Graph-compiled synced bootstrap dynamically loaded by the package entry. */\n bootstrap?: string;\n /** Inputs used to validate generated artifacts without loading the compiler. */\n precompile?: SyncPrecompileState;\n baseline: SyncBaseline;\n}\n\nexport function syncDirectory(repoRoot: string, homeDirectory: string = os.homedir()): string {\n return resolveSyncLocation(repoRoot, homeDirectory).directory;\n}\n\nexport function syncStatePath(repoRoot: string, homeDirectory: string = os.homedir()): string {\n return (\n readSyncRegistration(repoRoot, homeDirectory)?.statePath ?? resolveSyncLocation(repoRoot, homeDirectory).statePath\n );\n}\n\nexport function legacySyncDirectory(repoRoot: string): string {\n return resolveSyncLocation(repoRoot).legacyDirectory;\n}\n\nexport function legacySyncStatePath(repoRoot: string): string {\n return resolveSyncLocation(repoRoot).legacyStatePath;\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync(absolute);\n } catch {\n const parent = path.dirname(absolute);\n if (parent === absolute) return absolute;\n return path.join(canonicalPath(parent), path.basename(absolute));\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(canonicalPath(directory), canonicalPath(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\n/** Whether generated state belongs to the repository that is reading it. */\nexport function syncStateRootMatches(repoRoot: string, stateRoot: string): boolean {\n return canonicalPath(repoRoot) === canonicalPath(stateRoot);\n}\n\n/**\n * Per-process scratch directory for live `/domains` and `/profile` switches.\n *\n * Switching rewrites the MCP config and the persona prompt. Writing those back\n * into the synced directory would both mutate the pinned baseline and let two\n * Pi sessions in one repository overwrite each other, which the launcher avoids\n * by giving every run its own temporary directory.\n */\nexport function runDirectory(\n repoRoot: string,\n processId: number = process.pid,\n homeDirectory: string = os.homedir(),\n): string {\n return path.join(syncDirectory(repoRoot, homeDirectory), RUN_DIRECTORY, String(processId));\n}\n\n/**\n * How a Pi project settings file should refer to a generated path.\n *\n * Relative to `.pi` when the target lives in the repository, which keeps the\n * committed settings file identical on every machine. A doom-pi installed\n * outside the repository stays absolute: Pi accepts both, and a relative path\n * climbing out of the project would be neither portable nor readable.\n */\nexport function settingsRelativePath(repoRoot: string, target: string): string {\n const relative = path.relative(repoRoot, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) return target;\n return path.relative(path.join(repoRoot, PI_DIRECTORY), target).split(path.sep).join('/');\n}\n\nfunction readIfPresent(filePath: string): string {\n return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';\n}\n\nfunction updateFramedHash(hash: ReturnType<typeof crypto.createHash>, label: string, value: string | Buffer): void {\n const bytes = typeof value === 'string' ? Buffer.from(value) : value;\n hash.update(`${String(label.length)}:${label}:${String(bytes.byteLength)}:`);\n hash.update(bytes);\n}\n\n/** Hashes path, existence, and bytes without making absent and empty files aliases. */\nfunction updateFileInputHash(hash: ReturnType<typeof crypto.createHash>, filePath: string): void {\n const absolutePath = path.resolve(filePath);\n updateFramedHash(hash, 'path', absolutePath);\n if (!fs.existsSync(absolutePath)) {\n updateFramedHash(hash, 'state', 'missing');\n return;\n }\n updateFramedHash(hash, 'state', 'present');\n updateFramedHash(hash, 'contents', fs.readFileSync(absolutePath));\n}\n\n/** Package root owning a resolved entry, or undefined when it sits outside one. */\nfunction owningPackageRoot(entryPath: string): string | undefined {\n // The resolved map also carries package-name metadata. Relative metadata must\n // not become a filesystem path whose meaning changes with process.cwd().\n if (!path.isAbsolute(entryPath)) return undefined;\n let directory = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(directory, PACKAGE_MANIFEST_FILE))) return directory;\n const parent = path.dirname(directory);\n if (parent === directory) return undefined;\n directory = parent;\n }\n}\n\n/** Every regular file under a directory, as absolute paths in a stable order. */\nfunction filesUnder(directory: string): string[] {\n if (!fs.existsSync(directory)) return [];\n const found: string[] = [];\n const walk = (current: string): void => {\n for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {\n const full = path.join(current, entry.name);\n if (entry.isDirectory()) walk(full);\n else if (entry.isFile()) found.push(full);\n }\n };\n walk(directory);\n return found;\n}\n\n/**\n * Hashes the cockpit sources and package versions a sync compiled.\n *\n * The cockpit bundle is built from each installed package's `web/` folder by\n * `syncWebBundle`, straight from source, with no compiler manifest recording\n * what went in. Nothing else in the drift check reads those files, so editing a\n * plugin surface and rebuilding left the hub serving the previously bundled\n * cockpit with nothing reporting a reason. Compiled entries are covered\n * elsewhere, by the content digests in their compiler manifests, so only the\n * entry identity and its package version are folded in here.\n *\n * Bounded on purpose: the web sources of a full composition are a few hundred\n * small files, which is cheap enough for the hub to poll.\n */\nexport function computeWebSourcesHash(resolved: Record<string, string>): string {\n const hash = crypto.createHash('sha256');\n const roots = [...new Set(Object.values(resolved).flatMap((entry) => owningPackageRoot(entry) ?? []))].sort();\n for (const root of roots) {\n updateFramedHash(hash, 'package', root);\n const manifestPath = path.join(root, PACKAGE_MANIFEST_FILE);\n let manifest: { name?: unknown; version?: unknown } = {};\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { name?: unknown; version?: unknown };\n } catch {\n // An unreadable manifest still contributes its path above, so the package\n // is not silently dropped from the hash; the version simply reads empty\n // and any later repair shows up as a change.\n manifest = {};\n }\n updateFramedHash(hash, 'name', typeof manifest.name === 'string' ? manifest.name : '');\n updateFramedHash(hash, 'version', typeof manifest.version === 'string' ? manifest.version : '');\n for (const file of filesUnder(path.join(root, WEB_PLUGIN_DIRECTORY))) updateFileInputHash(hash, file);\n }\n for (const [name, entry] of Object.entries(resolved).sort(([left], [right]) => left.localeCompare(right))) {\n updateFramedHash(hash, 'entry', `${name}\u0000${entry}`);\n }\n return hash.digest('hex');\n}\n\n/**\n * Hashes the configuration a sync consumed.\n *\n * Deliberately cheap: the extension recomputes this on every session start, so\n * it reads config, marketplace, local plugin manifests, and profile persona text\n * without downloading plugins or re-resolving packages. A dependency upgrade that\n * moves a package path therefore does not show up here; `doom-pi sync --check`\n * re-resolves and is the check that catches that.\n */\nexport function computeInputsHash(\n repoRoot: string,\n selection: SyncSelection,\n homeDirectory: string = os.homedir(),\n): string {\n const hash = crypto.createHash('sha256');\n hash.update(JSON.stringify({ ...selection, domains: [...selection.domains].sort() }));\n // Every .doom document layers the global copy under the repository one, so an\n // edit in either location is a real change.\n for (const directory of [\n path.join(homeDirectory, PI_DIRECTORY, DOOM_CONFIG_DIRECTORY),\n path.join(repoRoot, DOOM_CONFIG_DIRECTORY),\n ]) {\n for (const fileName of HASHED_CONFIG_FILES) hash.update(readIfPresent(path.join(directory, fileName)));\n }\n updateFileInputHash(hash, path.join(repoRoot, '.mcp.json'));\n\n const catalog = loadDomains(repoRoot, homeDirectory).plugins;\n for (const root of catalog.roots) hash.update(root);\n for (const diagnostic of catalog.diagnostics) hash.update(diagnostic);\n for (const marketplace of catalog.marketplaces) {\n hash.update(marketplace);\n hash.update(readIfPresent(marketplace));\n }\n for (const [name, plugin] of Object.entries(catalog.entries).sort(([left], [right]) => left.localeCompare(right))) {\n hash.update(name);\n hash.update(JSON.stringify(plugin.source));\n if (plugin.manifest) {\n hash.update(plugin.manifest.path);\n hash.update(readIfPresent(plugin.manifest.path));\n }\n }\n\n let selectedPluginDirectories: string[] = [];\n try {\n selectedPluginDirectories = [\n ...new Set(\n resolvePluginEntries(repoRoot, selection.domains, [], homeDirectory)\n .filter((plugin) => plugin.mcp !== false)\n .map((plugin) => path.resolve(plugin.directory)),\n ),\n ].sort();\n } catch {\n // The surrounding .doom inputs already make an invalid or removed domain\n // selection stale. Keep this lightweight check readable for old state\n // instead of turning that expected staleness into a startup exception.\n }\n for (const directory of selectedPluginDirectories) {\n for (const fileName of PLUGIN_MCP_CONFIG_FILES) {\n updateFileInputHash(hash, path.join(directory, fileName));\n }\n }\n\n for (const profile of loadProfiles(repoRoot, homeDirectory)) {\n hash.update(profile.name);\n hash.update(profile.personaRoot);\n hash.update(profile.persona);\n hash.update(JSON.stringify(profile.env));\n const personaDirectory = path.resolve(profile.personaRoot, profile.persona);\n for (const fileName of PERSONA_FILES) {\n const filePath = path.join(personaDirectory, fileName);\n hash.update(filePath);\n hash.update(readIfPresent(filePath));\n }\n }\n return hash.digest('hex');\n}\n\nfunction stringRecord(value: unknown, field: string, statePath: string): Record<string, string> {\n if (!isRecord(value)) throw new Error(`Doom sync state at ${statePath} requires ${field} to be an object`);\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction parsePrecompileState(value: unknown, statePath: string): SyncPrecompileState {\n if (\n !isRecord(value) ||\n value.version !== PRECOMPILE_STATE_VERSION ||\n value.strategy !== 'bundle' ||\n typeof value.bootstrapEntry !== 'string' ||\n typeof value.bootstrapManifest !== 'string' ||\n !isRecord(value.bundleManifests) ||\n Object.entries(value.bundleManifests).some(\n ([fingerprint, manifest]) => !COMPOSITION_FINGERPRINT.test(fingerprint) || typeof manifest !== 'string',\n )\n ) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n return {\n version: value.version,\n strategy: value.strategy,\n bootstrapEntry: value.bootstrapEntry,\n bootstrapManifest: value.bootstrapManifest,\n bundleManifests: value.bundleManifests as Record<string, string>,\n };\n}\n\nfunction precompileMatchesBundles(\n precompile: SyncPrecompileState,\n bundles: Record<string, string> | undefined,\n): boolean {\n const bundleFingerprints = Object.keys(bundles ?? {}).sort();\n const manifestFingerprints = Object.keys(precompile.bundleManifests).sort();\n return (\n bundleFingerprints.length === manifestFingerprints.length &&\n bundleFingerprints.every((fingerprint, index) => fingerprint === manifestFingerprints[index])\n );\n}\n\ninterface ParseSyncStateOptions {\n repoRoot: string;\n location: SyncLocation;\n legacy: boolean;\n}\n\nfunction identityMatches(left: SyncIdentity, right: SyncIdentity): boolean {\n return left.repositoryId === right.repositoryId && left.worktreeId === right.worktreeId;\n}\n\nfunction parseSyncIdentity(value: unknown, statePath: string): SyncIdentity {\n if (!isRecord(value) || typeof value.repositoryId !== 'string' || typeof value.worktreeId !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires repository and worktree identity`);\n }\n return { repositoryId: value.repositoryId, worktreeId: value.worktreeId };\n}\n\nfunction parseSyncState(source: string, statePath: string, options: ParseSyncStateOptions): SyncState {\n let parsed: unknown;\n try {\n parsed = JSON.parse(source);\n } catch (error) {\n throw new Error(`Doom sync state at ${statePath} is not valid JSON`, { cause: error });\n }\n if (!isRecord(parsed)) throw new Error(`Doom sync state at ${statePath} must be an object`);\n if (typeof parsed.version !== 'number' || parsed.version !== SYNC_STATE_VERSION) {\n throw new Error(\n `Doom sync state at ${statePath} has version ${String(parsed.version)}, expected ${SYNC_STATE_VERSION}. Run doompi sync.`,\n );\n }\n if (typeof parsed.root !== 'string' || typeof parsed.inputsHash !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires string root and inputsHash`);\n }\n if (\n typeof parsed.compositionFingerprint !== 'string' ||\n !COMPOSITION_FINGERPRINT.test(parsed.compositionFingerprint)\n ) {\n throw new Error(`Doom sync state at ${statePath} requires a canonical compositionFingerprint`);\n }\n if (parsed.nativeGraphs !== undefined) {\n throw new Error(`Doom sync state at ${statePath} contains removed native graph state. Run doompi sync.`);\n }\n if (!syncStateRootMatches(options.repoRoot, parsed.root)) {\n throw new Error(`Doom sync state at ${statePath} belongs to a different repository: ${parsed.root}`);\n }\n const identity = parseSyncIdentity(parsed.identity, statePath);\n if (!identityMatches(identity, options.location.identity)) {\n throw new Error(`Doom sync state at ${statePath} belongs to a different repository or worktree identity`);\n }\n if (!isRecord(parsed.selection) || typeof parsed.selection.majorMode !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a selection with a majorMode`);\n }\n if (!isRecord(parsed.baseline) || typeof parsed.baseline.themePath !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a baseline with a themePath`);\n }\n const { majorMode, domains, profile, preset } = parsed.selection;\n const fileState = isRecord(parsed.fileState) ? parsed.fileState : {};\n if (!isDoomMcpProjection(fileState.mcpProjection)) {\n throw new Error(`Doom sync state at ${statePath} requires a valid MCP projection. Run doompi sync.`);\n }\n if (!syncStateRootMatches(parsed.root, fileState.mcpProjection.repoRoot)) {\n throw new Error(`Doom sync state at ${statePath} contains an MCP projection for a different repository`);\n }\n const generatedDirectory = options.legacy ? options.location.legacyDirectory : options.location.directory;\n const compiled = parsed.compiled === undefined ? undefined : stringRecord(parsed.compiled, 'compiled', statePath);\n const bundles = parsed.bundles === undefined ? undefined : stringRecord(parsed.bundles, 'bundles', statePath);\n const precompile = parsed.precompile === undefined ? undefined : parsePrecompileState(parsed.precompile, statePath);\n if (precompile && !precompileMatchesBundles(precompile, bundles)) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n const bootstrap = typeof parsed.bootstrap === 'string' ? parsed.bootstrap : undefined;\n const generatedPaths = [bootstrap, ...Object.values(compiled ?? {}), ...Object.values(bundles ?? {})].filter(\n (entry): entry is string => entry !== undefined,\n );\n if (generatedPaths.some((entry) => !isInside(generatedDirectory, entry))) {\n throw new Error(`Doom sync state at ${statePath} references generated material outside ${generatedDirectory}`);\n }\n return {\n version: parsed.version,\n root: parsed.root,\n identity,\n inputsHash: parsed.inputsHash,\n webSourcesHash: typeof parsed.webSourcesHash === 'string' ? parsed.webSourcesHash : undefined,\n compositionFingerprint: parsed.compositionFingerprint,\n selection: {\n majorMode,\n domains: Array.isArray(domains) ? domains.filter((name): name is string => typeof name === 'string') : [],\n profile: typeof profile === 'string' ? profile : undefined,\n preset: typeof preset === 'string' ? preset : DEFAULT_PRESET,\n },\n env: stringRecord(parsed.env, 'env', statePath),\n fileState: {\n profileEnvironment: isRecord(fileState.profileEnvironment)\n ? (fileState.profileEnvironment as Record<string, string>)\n : {},\n pluginHooks: Array.isArray(fileState.pluginHooks) ? (fileState.pluginHooks as HarnessState['pluginHooks']) : [],\n mcpProjection: fileState.mcpProjection,\n },\n resolved: stringRecord(parsed.resolved, 'resolved', statePath),\n compiled,\n bundles,\n bootstrap,\n precompile,\n baseline: parsed.baseline as unknown as SyncBaseline,\n };\n}\n\nexport interface LocatedSyncState {\n state: SyncState;\n location: SyncLocation;\n layout: 'global';\n}\n\n/** Reads only state reached through this repository/worktree's validated registration. */\nexport function readLocatedSyncState(\n repoRoot: string,\n homeDirectory: string = os.homedir(),\n): LocatedSyncState | undefined {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n if (!registration) return undefined;\n return {\n state: parseSyncState(fs.readFileSync(registration.statePath, 'utf8'), registration.statePath, {\n repoRoot,\n location,\n legacy: false,\n }),\n location,\n layout: 'global',\n };\n}\n\n/** Reads the synced state, or undefined when the repository was never synced. */\nexport function readSyncState(repoRoot: string, homeDirectory: string = os.homedir()): SyncState | undefined {\n return readLocatedSyncState(repoRoot, homeDirectory)?.state;\n}\n\nexport function serializeSyncState(state: SyncState): string {\n return `${JSON.stringify(state, null, 2)}\\n`;\n}\n\nexport async function writeSyncState(\n repoRoot: string,\n state: SyncState,\n homeDirectory: string = os.homedir(),\n targetPath?: string,\n): Promise<string> {\n const location = resolveSyncLocation(repoRoot, homeDirectory);\n const statePath = targetPath ?? location.statePath;\n if (state.version !== SYNC_STATE_VERSION) {\n throw new Error(`Doom sync state writes require version ${SYNC_STATE_VERSION}`);\n }\n if (!COMPOSITION_FINGERPRINT.test(state.compositionFingerprint)) {\n throw new Error('Doom sync state writes require a canonical composition fingerprint');\n }\n if (!isDoomMcpProjection(state.fileState.mcpProjection)) {\n throw new Error('Doom sync state writes require a valid MCP projection');\n }\n if (!syncStateRootMatches(state.root, state.fileState.mcpProjection.repoRoot)) {\n throw new Error('Refusing to write a Doom sync state whose MCP projection belongs to another repository');\n }\n if (!syncStateRootMatches(repoRoot, state.root) || !identityMatches(state.identity, location.identity)) {\n throw new Error('Refusing to write Doom sync state for a different repository or worktree');\n }\n if (state.precompile) {\n const precompile = parsePrecompileState(state.precompile, statePath);\n if (!precompileMatchesBundles(precompile, state.bundles)) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n }\n if (!isInside(location.directory, statePath)) {\n throw new Error(`Refusing to write Doom sync state outside ${location.directory}`);\n }\n assertSyncLocationSafe(location);\n await fs.promises.mkdir(path.dirname(statePath), { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const temporaryPath = `${statePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n await fs.promises.writeFile(temporaryPath, serializeSyncState(state), { mode: PRIVATE_FILE_MODE });\n await fs.promises.rename(temporaryPath, statePath);\n } catch (error) {\n await fs.promises.rm(temporaryPath, { force: true });\n throw error;\n }\n return statePath;\n}\n\nexport function ownKey(name: string): string {\n return `${OWN_PREFIX}${name}`;\n}\n\nexport function packageKey(specifier: string): string {\n return `${PACKAGE_PREFIX}${specifier}`;\n}\n\nexport function packageEntryKey(specifier: string, index: number): string {\n return index === 0 ? packageKey(specifier) : `${packageKey(specifier)}${ENTRY_INDEX_SEPARATOR}${index}`;\n}\n\n/**\n * Keyed by the absolute target, because the same relative specifier means\n * different files under the repository and the global config directory.\n */\nexport function localKey(specifier: string, baseDirectory: string): string {\n return `${LOCAL_PREFIX}${path.resolve(baseDirectory, specifier)}`;\n}\n\nexport function localEntryKey(specifier: string, baseDirectory: string, index: number): string {\n return index === 0\n ? localKey(specifier, baseDirectory)\n : `${localKey(specifier, baseDirectory)}${ENTRY_INDEX_SEPARATOR}${index}`;\n}\n\nexport function localPackageNameKey(specifier: string, baseDirectory: string): string {\n return `${LOCAL_PACKAGE_NAME_PREFIX}${path.resolve(baseDirectory, specifier)}`;\n}\n\nexport interface RecordingResolvers extends ExtensionLayerResolvers {\n /** Every name this resolver was asked for, keyed for the state file. */\n readonly resolved: Record<string, string>;\n packageEntries(name: string): string[];\n optionalPackageEntries(name: string): string[] | undefined;\n localEntries(specifier: string, baseDirectory: string): string[] | undefined;\n}\n\n/**\n * Resolvers that record what they resolve.\n *\n * Sync composes once with every layer selected and hands the recording to the\n * state file, so the extension never has to resolve a specifier itself.\n */\nexport function createRecordingResolvers(base: LayerResolvers = LAYER_RESOLVERS): RecordingResolvers {\n const resolved: Record<string, string> = {};\n const extensionBase = base as ExtensionLayerResolvers;\n const recordEntries = (entries: string[], keyFor: (index: number) => string): string[] => {\n entries.forEach((entry, index) => {\n resolved[keyFor(index)] = entry;\n });\n return entries;\n };\n return {\n resolved,\n ownEntry(name) {\n const entry = base.ownEntry(name);\n resolved[ownKey(name)] = entry;\n return entry;\n },\n packageEntry(name) {\n const entry = base.packageEntry(name);\n resolved[packageKey(name)] = entry;\n return entry;\n },\n optionalPackageEntry(name) {\n const entry = base.optionalPackageEntry(name);\n // A miss is recorded by omission, which is what later makes the extension\n // treat it as absent instead of failing the whole composition.\n if (entry) resolved[packageKey(name)] = entry;\n return entry;\n },\n packageEntries(name) {\n const entries = extensionBase.packageEntries?.(name) ?? [base.packageEntry(name)];\n return recordEntries(entries, (index) => packageEntryKey(name, index));\n },\n optionalPackageEntries(name) {\n let entries = extensionBase.optionalPackageEntries?.(name);\n if (entries === undefined) {\n const entry = base.optionalPackageEntry(name);\n entries = entry ? [entry] : undefined;\n }\n return entries ? recordEntries(entries, (index) => packageEntryKey(name, index)) : undefined;\n },\n localEntry(specifier, baseDirectory) {\n const entry = base.localEntry(specifier, baseDirectory);\n if (entry) resolved[localKey(specifier, baseDirectory)] = entry;\n return entry;\n },\n localEntries(specifier, baseDirectory) {\n let entries = extensionBase.localEntries?.(specifier, baseDirectory);\n if (entries === undefined) {\n const entry = base.localEntry(specifier, baseDirectory);\n entries = entry ? [entry] : undefined;\n }\n return entries ? recordEntries(entries, (index) => localEntryKey(specifier, baseDirectory, index)) : undefined;\n },\n localPackageName(specifier, baseDirectory) {\n const name = extensionBase.localPackageName?.(specifier, baseDirectory);\n if (name) resolved[localPackageNameKey(specifier, baseDirectory)] = name;\n return name;\n },\n };\n}\n\n/**\n * Resolves every entry any layer could contribute, in one pass.\n *\n * Composing with every layer selected and the widest options is what makes the\n * map complete: a later `/mode` switch inside a Pi session can then pick a\n * different set without resolving anything itself. The persona entry is asked\n * for separately because it remains a fixed host entry rather than a layer.\n */\nexport function recordResolvedEntries(\n majorModesConfig: MajorModesConfig,\n base: LayerResolvers = LAYER_RESOLVERS,\n): Record<string, string> {\n const recording = createRecordingResolvers(base);\n recording.packageEntry(PERSONA_ENTRY);\n assembleExtensions({\n agents: true,\n autoStop: true,\n mute: false,\n // The one preset that contributes an extension of its own.\n preset: OLLAMA_PRESET,\n layers: Object.keys(majorModesConfig.layers),\n majorModesConfig,\n resolvers: recording,\n });\n assembleChildExtensions({\n agents: false,\n autoStop: false,\n mute: true,\n preset: OLLAMA_PRESET,\n layers: Object.keys(majorModesConfig.layers),\n majorModesConfig,\n resolvers: recording,\n });\n return recording.resolved;\n}\n\n/** Resolvers backed by a synced map, for composing inside a Pi session. */\nexport function createMapResolvers(\n resolved: Record<string, string>,\n compiled: Record<string, string> = {},\n): ExtensionLayerResolvers {\n // The compiled bundle when this entry has one, the source otherwise.\n const entryFor = (key: string): string | undefined => compiled[key] ?? resolved[key];\n const lookup = (key: string, name: string): string => {\n const entry = entryFor(key);\n if (!entry) throw new Error(`${name} is missing from the doompi sync state. Run doompi sync.`);\n return entry;\n };\n const indexedEntries = (keyFor: (index: number) => string): string[] | undefined => {\n const first = entryFor(keyFor(0));\n if (!first) return undefined;\n const entries = [first];\n for (let index = 1; ; index += 1) {\n const entry = entryFor(keyFor(index));\n if (!entry) break;\n entries.push(entry);\n }\n return entries;\n };\n return {\n ownEntry: (name) => lookup(ownKey(name), name),\n packageEntry: (name) => lookup(packageKey(name), name),\n optionalPackageEntry: (name) => entryFor(packageKey(name)),\n packageEntries: (name) => {\n const entries = indexedEntries((index) => packageEntryKey(name, index));\n if (!entries) throw new Error(`${name} is missing from the doompi sync state. Run doompi sync.`);\n return entries;\n },\n optionalPackageEntries: (name) => indexedEntries((index) => packageEntryKey(name, index)),\n localEntry: (specifier, baseDirectory) => entryFor(localKey(specifier, baseDirectory)),\n localEntries: (specifier, baseDirectory) =>\n indexedEntries((index) => localEntryKey(specifier, baseDirectory, index)),\n localPackageName: (specifier, baseDirectory) => resolved[localPackageNameKey(specifier, baseDirectory)],\n };\n}\n\n/** Reads the resolved MCP servers back out of a generated config. */\nexport function readMcpServerNames(configPath: string): string[] {\n if (!fs.existsSync(configPath)) return [];\n const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as JsonObject;\n return isRecord(config.mcpServers) ? Object.keys(config.mcpServers) : [];\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncState.d.cts","names":[],"sources":["../../../src/adapters/syncState.ts"],"mappings":";;;;;;;UAwDiB;EACf;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;;EAEf;EACA;;EAEA;;EAEA;;EAEA,iBAAiB;;UAGF;EACf;EACA;;EAEA,UAAU;;EAEV;;;;;;;;EAQA;;EAEA;EACA,WAAW;;EAEX,KAAK;;;;;;;EAOL,WAAW,KAAK;IACd,eAAe;;;EAGjB,UAAU;;;;;;;;EAQV,WAAW;;EAEX,UAAU;;EAEV;;EAEA,aAAa;EACb,UAAU;;iBAGI,cAAc,kBAAkB;iBAIhC,cAAc,kBAAkB;iBAMhC,oBAAoB;iBAIpB,oBAAoB;;iBAqBpB,qBAAqB,kBAAkB;;;;;;;;;iBAYvC,aACd,kBACA,oBACA;;;;;;;;;iBAac,qBAAqB,kBAAkB;;;;;;;;;;;;;;;iBAoEvC,sBAAsB,UAAU;;;;;;;;;;iBAkChC,kBACd,kBACA,WAAW,eACX;UAsNe;EACf,OAAO;EACP,UAAU;EACV;;;iBAIc,qBACd,kBACA,yBACC;;iBAgBa,cAAc,kBAAkB,yBAAuC;iBAIvE,mBAAmB,OAAO;iBAIpB,eACpB,kBACA,OAAO,WACP,wBACA,sBACC;iBAwCa,OAAO;iBAIP,WAAW;iBAIX,gBAAgB,mBAAmB;;;;;iBAQnC,SAAS,mBAAmB;iBAI5B,cAAc,mBAAmB,uBAAuB;iBAMxD,oBAAoB,mBAAmB;UAItC,2BAA2B;;WAEjC,UAAU;EACnB,eAAe;EACf,uBAAuB;EACvB,aAAa,mBAAmB;;;;;;;;iBASlB,yBAAyB,OAAM,iBAAmC;;;;;;;;;iBAqElE,sBACd,kBAAkB,kBAClB,OAAM,iBACL;;iBA0Ba,mBACd,UAAU,wBACV,WAAU,yBACT;;iBAqCa,mBAAmB"}
1
+ {"version":3,"file":"syncState.d.cts","names":[],"sources":["../../../src/adapters/syncState.ts"],"mappings":";;;;;;;UAwDiB;EACf;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;;EAEf;EACA;;EAEA;;EAEA;;EAEA,iBAAiB;;UAGF;EACf;EACA;;EAEA,UAAU;;EAEV;;;;;;;;EAQA;;EAEA;EACA,WAAW;;EAEX,KAAK;;;;;;;EAOL,WAAW,KAAK;IACd,eAAe;;;EAGjB,UAAU;;;;;;;;EAQV,WAAW;;EAEX,UAAU;;EAEV;;EAEA,aAAa;EACb,UAAU;;iBAGI,cAAc,kBAAkB;iBAIhC,cAAc,kBAAkB;iBAMhC,oBAAoB;iBAIpB,oBAAoB;;iBAqBpB,qBAAqB,kBAAkB;;;;;;;;;iBAYvC,aACd,kBACA,oBACA;;;;;;;;;iBAac,qBAAqB,kBAAkB;;;;;;;;;;;;;;;iBAuEvC,sBAAsB,UAAU;;;;;;;;;;iBAkChC,kBACd,kBACA,WAAW,eACX;UAsNe;EACf,OAAO;EACP,UAAU;EACV;;;iBAIc,qBACd,kBACA,yBACC;;iBAgBa,cAAc,kBAAkB,yBAAuC;iBAIvE,mBAAmB,OAAO;iBAIpB,eACpB,kBACA,OAAO,WACP,wBACA,sBACC;iBAwCa,OAAO;iBAIP,WAAW;iBAIX,gBAAgB,mBAAmB;;;;;iBAQnC,SAAS,mBAAmB;iBAI5B,cAAc,mBAAmB,uBAAuB;iBAMxD,oBAAoB,mBAAmB;UAItC,2BAA2B;;WAEjC,UAAU;EACnB,eAAe;EACf,uBAAuB;EACvB,aAAa,mBAAmB;;;;;;;;iBASlB,yBAAyB,OAAM,iBAAmC;;;;;;;;;iBAqElE,sBACd,kBAAkB,kBAClB,OAAM,iBACL;;iBA0Ba,mBACd,UAAU,wBACV,WAAU,yBACT;;iBAqCa,mBAAmB"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncState.d.mts","names":[],"sources":["../../../src/adapters/syncState.ts"],"mappings":";;;;;;;UAwDiB;EACf;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;;EAEf;EACA;;EAEA;;EAEA;;EAEA,iBAAiB;;UAGF;EACf;EACA;;EAEA,UAAU;;EAEV;;;;;;;;EAQA;;EAEA;EACA,WAAW;;EAEX,KAAK;;;;;;;EAOL,WAAW,KAAK;IACd,eAAe;;;EAGjB,UAAU;;;;;;;;EAQV,WAAW;;EAEX,UAAU;;EAEV;;EAEA,aAAa;EACb,UAAU;;iBAGI,cAAc,kBAAkB;iBAIhC,cAAc,kBAAkB;iBAMhC,oBAAoB;iBAIpB,oBAAoB;;iBAqBpB,qBAAqB,kBAAkB;;;;;;;;;iBAYvC,aACd,kBACA,oBACA;;;;;;;;;iBAac,qBAAqB,kBAAkB;;;;;;;;;;;;;;;iBAoEvC,sBAAsB,UAAU;;;;;;;;;;iBAkChC,kBACd,kBACA,WAAW,eACX;UAsNe;EACf,OAAO;EACP,UAAU;EACV;;;iBAIc,qBACd,kBACA,yBACC;;iBAgBa,cAAc,kBAAkB,yBAAuC;iBAIvE,mBAAmB,OAAO;iBAIpB,eACpB,kBACA,OAAO,WACP,wBACA,sBACC;iBAwCa,OAAO;iBAIP,WAAW;iBAIX,gBAAgB,mBAAmB;;;;;iBAQnC,SAAS,mBAAmB;iBAI5B,cAAc,mBAAmB,uBAAuB;iBAMxD,oBAAoB,mBAAmB;UAItC,2BAA2B;;WAEjC,UAAU;EACnB,eAAe;EACf,uBAAuB;EACvB,aAAa,mBAAmB;;;;;;;;iBASlB,yBAAyB,OAAM,iBAAmC;;;;;;;;;iBAqElE,sBACd,kBAAkB,kBAClB,OAAM,iBACL;;iBA0Ba,mBACd,UAAU,wBACV,WAAU,yBACT;;iBAqCa,mBAAmB"}
1
+ {"version":3,"file":"syncState.d.mts","names":[],"sources":["../../../src/adapters/syncState.ts"],"mappings":";;;;;;;UAwDiB;EACf;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;;EAEf;EACA;;EAEA;;EAEA;;EAEA,iBAAiB;;UAGF;EACf;EACA;;EAEA,UAAU;;EAEV;;;;;;;;EAQA;;EAEA;EACA,WAAW;;EAEX,KAAK;;;;;;;EAOL,WAAW,KAAK;IACd,eAAe;;;EAGjB,UAAU;;;;;;;;EAQV,WAAW;;EAEX,UAAU;;EAEV;;EAEA,aAAa;EACb,UAAU;;iBAGI,cAAc,kBAAkB;iBAIhC,cAAc,kBAAkB;iBAMhC,oBAAoB;iBAIpB,oBAAoB;;iBAqBpB,qBAAqB,kBAAkB;;;;;;;;;iBAYvC,aACd,kBACA,oBACA;;;;;;;;;iBAac,qBAAqB,kBAAkB;;;;;;;;;;;;;;;iBAuEvC,sBAAsB,UAAU;;;;;;;;;;iBAkChC,kBACd,kBACA,WAAW,eACX;UAsNe;EACf,OAAO;EACP,UAAU;EACV;;;iBAIc,qBACd,kBACA,yBACC;;iBAgBa,cAAc,kBAAkB,yBAAuC;iBAIvE,mBAAmB,OAAO;iBAIpB,eACpB,kBACA,OAAO,WACP,wBACA,sBACC;iBAwCa,OAAO;iBAIP,WAAW;iBAIX,gBAAgB,mBAAmB;;;;;iBAQnC,SAAS,mBAAmB;iBAI5B,cAAc,mBAAmB,uBAAuB;iBAMxD,oBAAoB,mBAAmB;UAItC,2BAA2B;;WAEjC,UAAU;EACnB,eAAe;EACf,uBAAuB;EACvB,aAAa,mBAAmB;;;;;;;;iBASlB,yBAAyB,OAAM,iBAAmC;;;;;;;;;iBAqElE,sBACd,kBAAkB,kBAClB,OAAM,iBACL;;iBA0Ba,mBACd,UAAU,wBACV,WAAU,yBACT;;iBAqCa,mBAAmB"}
Binary file