@ai-ecoverse/slicc-agent 6.18.7 → 6.18.8

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,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/freezer/v6-place.ts"],
4
- "sourcesContent": ["import { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { coneSpec } from '../kernel/accounts.ts';\nimport { ROOT_HOME } from '../layout.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport { memoryRoot } from '../memory/store.ts';\nimport { GLOBAL_DIR } from '../skills/index.ts';\nimport { validateName } from '../skills/skills.ts';\nimport type { ImportDeps } from './v6.ts';\nimport {\n accountsSplit,\n type Opened,\n type Parsed,\n textOf,\n type Unit,\n type Where,\n whereOf,\n} from './v6.ts';\nimport { lickName, skillName } from './v6-schema.ts';\nimport {\n type CronLine,\n installCrons,\n ownTree,\n remember,\n same,\n userSection,\n type Writer,\n} from './v6-write.ts';\nimport { ImportError } from './zip.ts';\n\nexport const V6_ROOT = `${ROOT_HOME}/v6`;\n\nexport type Notes = {\n lines: string[];\n skipped: string[];\n shadowed: string[];\n tasks: string[];\n sprinkles: Map<string, Set<string>>;\n crons: string[];\n webhooks: string[];\n placed: Map<string, string>;\n};\n\nexport type Run = {\n deps: ImportDeps;\n opened: Opened;\n parsed: Parsed;\n writer: Writer;\n stage: string;\n date: string;\n notes: Notes;\n show: (phase: 'writing', done: number, total: number) => void;\n fresh: Map<string, boolean>;\n ready: Set<Unit>;\n at: Map<string, string>;\n};\n\ntype Source = Uint8Array | { zip: string };\n\nasync function staged(run: Run, zip: string): Promise<Uint8Array> {\n const read = await run.writer.env.readBinaryFile(\n run.at.get(zip) ?? `${run.stage}/${zip}`,\n run.writer.context\n );\n if (!read.ok) throw new ImportError(`the staged copy of ${zip} is gone`);\n return read.value;\n}\n\nasync function prepare(run: Run, unit: Unit): Promise<void> {\n if (run.ready.has(unit)) return;\n run.ready.add(unit);\n const { users, layout, env } = run.deps;\n if (unit.fresh)\n run.writer.later(async () => {\n await users?.remove(unit.cone).catch(() => false);\n await env.remove(unit.home, { recursive: true, force: true }, BACKGROUND_CONTEXT);\n });\n if (layout.users && users) await users.add(coneSpec(unit.cone));\n}\n\nasync function envOf(run: Run, unit: Unit | null): Promise<ExecutionEnv | undefined> {\n if (!unit || unit.fresh || !run.deps.layout.users) return undefined;\n const env = await run.deps.envOf?.(unit.cone);\n if (!env) throw new ImportError(`there is no kernel user ${unit.cone} to write its files as`);\n return env;\n}\n\nasync function putIn(run: Run, unit: Unit | null, root: string, rest: string, source: Source) {\n if (unit) await prepare(run, unit);\n const env = await envOf(run, unit);\n let fresh = run.fresh.get(root);\n if (fresh === undefined) {\n fresh = unit?.fresh || (await run.writer.tree(root, env));\n run.fresh.set(root, fresh);\n }\n if (unit?.fresh) unit.roots.add(root);\n const path = `${root}/${rest}`;\n const from = 'zip' in source && !env ? run.at.get(source.zip) : undefined;\n if (from && 'zip' in source) {\n await run.writer.move(from, path, fresh);\n run.at.set(source.zip, path);\n } else\n await run.writer.put(\n path,\n 'zip' in source ? await staged(run, source.zip) : source,\n fresh,\n env\n );\n if ('zip' in source && !run.notes.placed.has(source.zip)) run.notes.placed.set(source.zip, path);\n}\n\nconst clean = (name: string) => name.replace(/[^A-Za-z0-9._-]+/g, '_').slice(-80) || 'file';\n\ntype Maps = { byFolder: Map<string, Unit>; owners: Map<string, Unit>; primary?: Unit };\n\nfunction targets(run: Run, maps: Maps, where: Where, zip: string): [Unit, string, string][] {\n if (where.kind === 'cone' || where.kind === 'scoop') {\n const unit = (where.kind === 'cone' ? maps.byFolder : maps.owners).get(where.folder) as Unit;\n const root =\n where.kind === 'cone'\n ? `${unit.home}/workspace${unit.suffix}`\n : `${unit.home}/scoops${unit.suffix}/${where.folder}`;\n return [[unit, root, where.rest]];\n }\n if (where.kind === 'sprinkle') {\n if (!maps.primary)\n run.notes.skipped.push(`the sprinkle ${where.folder}: there is no cone to put it in`);\n return maps.primary\n ? [\n [\n maps.primary,\n `${maps.primary.home}/sprinkles/${lickName(where.folder, 'sprinkle')}`,\n where.rest,\n ],\n ]\n : [];\n }\n if (where.kind !== 'attachment') return [];\n return run.parsed.units.flatMap((unit) =>\n (unit.doc?.attachments ?? [])\n .filter((item) => item.present && item.path === zip)\n .map((item): [Unit, string, string] => {\n const rest = `${item.sha256.slice(0, 12)}-${clean(item.originalName)}`;\n unit.files.set(item.id, `${unit.home}/v6-attachments/${rest}`);\n return [unit, `${unit.home}/v6-attachments`, rest];\n })\n );\n}\n\nfunction sprinkleCalls(run: Run, name: string, bytes: Uint8Array) {\n const found = [...new TextDecoder().decode(bytes).matchAll(/\\bslicc\\.([A-Za-z_$][\\w$]*)/g)]\n .map((match) => `slicc.${match[1]}`)\n .filter((call) => call !== 'slicc.lick');\n if (!found.length) return;\n const known = run.notes.sprinkles.get(name) ?? new Set<string>();\n for (const call of found) known.add(call);\n run.notes.sprinkles.set(name, known);\n}\n\nasync function alike(\n run: Run,\n dir: string,\n six: string,\n rests: readonly string[]\n): Promise<boolean> {\n for (const rest of rests) {\n const old = await run.writer.env.readBinaryFile(`${dir}/${rest}`, run.writer.context);\n if (!old.ok || !same(old.value, await staged(run, `skills/${six}/${rest}`))) return false;\n }\n return true;\n}\n\nasync function placeSkills(run: Run, skills: Map<string, string[]>, primary: Unit | undefined) {\n const seen = new Set<string>();\n for (const [six, rests] of skills) {\n const name = skillName(six);\n if (!name || validateName(name).length || seen.has(name)) {\n run.notes.skipped.push(\n `the skill ${six}: its name doesn't fit seven's rule (a-z, 0-9 and -)`\n );\n continue;\n }\n seen.add(name);\n const global = `${GLOBAL_DIR}/${name}`;\n const exists = await run.writer.exists(global);\n if (exists && (await alike(run, global, six, rests))) {\n run.notes.lines.push(`the skill ${name}: already in ${GLOBAL_DIR}`);\n continue;\n }\n const own = primary && exists ? `${primary.home}/.pi/agent/skills/${name}` : undefined;\n if (exists && !own) {\n run.notes.skipped.push(`the skill ${name}: ${GLOBAL_DIR} has another skill of that name`);\n continue;\n }\n for (const rest of rests)\n await putIn(run, own ? (primary as Unit) : null, own ?? global, rest, {\n zip: `skills/${six}/${rest}`,\n });\n if (own)\n run.notes.shadowed.push(\n `the skill ${name}: ${GLOBAL_DIR} has another skill of that name, so SLICC 6's copy is in ${own}, for ${(primary as Unit).cone} only`\n );\n else run.notes.lines.push(`the skill ${name}, for every agent, in ${global}`);\n }\n}\n\nasync function placeMemory(run: Run) {\n const root = memoryRoot(run.deps.memoryHome);\n const { data, units } = run.parsed;\n const places = [\n { name: 'memory/global.md', file: `${root}/MEMORY.md`, rest: `${V6_ROOT}/memory/global.md` },\n ...units\n .filter((unit) => unit.memory)\n .map((unit) => ({\n name: unit.memory as string,\n file: `${root}/${unit.cone}/MEMORY.md`,\n rest: `${V6_ROOT}/memory/${unit.cone}.md`,\n })),\n ];\n for (const place of places) {\n const bytes = data.get(place.name);\n const done = bytes\n ? await remember(run.writer, place.file, textOf(bytes, place.name), {\n file: place.rest,\n date: run.date,\n })\n : 'none';\n if (done === 'merged')\n run.notes.lines.push(`memory, under \"Imported from SLICC 6 (${run.date})\", in ${place.file}`);\n if (done === 'aside')\n run.notes.lines.push(`memory, in ${place.rest}: ${place.file} has no room left for it`);\n }\n}\n\nfunction earlier(run: Run, unit: Unit, name: string): LickTarget | undefined {\n const found = Object.entries(run.deps.agents.state().scoops).filter(\n ([, record]) => record.cone === unit.cone && record.name === name && !record.removed\n );\n return found.at(-1)?.[0] as LickTarget | undefined;\n}\n\nfunction router(run: Run, maps: Maps) {\n return (folder: string | null | undefined): [Unit, LickTarget] | undefined => {\n const cone = folder ? maps.byFolder.get(folder) : undefined;\n if (cone) return [cone, 'cone'];\n const owner = folder ? maps.owners.get(folder) : undefined;\n const scoop = owner?.scoops.find((item) => item.six === folder);\n const target =\n owner && scoop\n ? owner.record\n ? (`scoop:${scoop.folder}` as LickTarget)\n : earlier(run, owner, scoop.name)\n : undefined;\n if (owner && target) return [owner, target];\n const unit = owner ?? maps.primary;\n return unit ? [unit, 'cone'] : undefined;\n };\n}\n\nconst FIELD = '[0-9A-Za-z*,/-]+';\nconst SCHEDULE = new RegExp(\n `^(@(yearly|annually|monthly|weekly|daily|midnight|hourly|reboot)|${FIELD}( ${FIELD}){4})$`\n);\n\nconst flat = (text: string) => text.replace(/\\s+/g, ' ').trim();\n\nasync function placeLicks(run: Run, maps: Maps) {\n const { licks } = run.parsed;\n const route = router(run, maps);\n const crons = new Map<Unit, CronLine[]>();\n const add = (found: [Unit, LickTarget] | undefined, line: Omit<CronLine, 'target'>) => {\n const why = !SCHEDULE.test(line.schedule)\n ? `\"${line.schedule}\" is not a cron schedule`\n : !found && 'there is no cone to send it to';\n if (why) run.notes.skipped.push(`the cron ${line.name}: ${why}`);\n else if (found)\n crons.set(found[0], [...(crons.get(found[0]) ?? []), { ...line, target: found[1] }]);\n };\n for (const [at, task] of licks.crontasks.entries())\n add(route(task.targetFolder), {\n name: lickName(task.name, `v6-cron-${at + 1}`),\n schedule: flat(task.cron),\n message: '',\n ...(task.filter ? { filter: task.filter } : {}),\n });\n for (const [at, task] of licks.tasks.entries()) {\n if (task.scheduleType === 'cron')\n add(route(task.groupFolder), {\n name: `v6-task-${at + 1}`,\n schedule: flat(task.scheduleValue),\n message: flat(task.prompt),\n });\n else\n run.notes.tasks.push(\n `${task.scheduleType} ${task.scheduleValue}: ${flat(task.prompt).slice(0, 80)}`\n );\n }\n for (const [unit, list] of crons) {\n await prepare(run, unit);\n const user = run.deps.layout.users ? unit.cone : 'root';\n const count = await installCrons(run.writer, user, list, run.date);\n if (count) run.notes.crons.push(`${count} in crontab -u ${user}`);\n }\n for (const [at, hook] of licks.webhooks.entries()) {\n const found = route(hook.targetFolder);\n const name = lickName(hook.name, `v6-webhook-${at + 1}`);\n if (!found) run.notes.skipped.push(`the webhook ${name}: there is no cone to send it to`);\n if (!found) continue;\n const root = `${found[0].home}/.slicc/webhooks`;\n await putIn(\n run,\n found[0],\n root,\n `${name}.json.v6`,\n new TextEncoder().encode(`${JSON.stringify({ target: found[1] }, null, 2)}\\n`)\n );\n run.notes.webhooks.push(`${root}/${name}.json.v6`);\n }\n}\n\nasync function placeGit(run: Run) {\n const name = run.opened.manifest.gitconfig;\n const bytes = name ? run.parsed.data.get(name) : undefined;\n const section = bytes ? userSection(textOf(bytes, name as string)) : '';\n if (!section) return;\n const file = `${ROOT_HOME}/.gitconfig`;\n const read = await run.writer.env.readTextFile(file, run.writer.context);\n const before = read.ok ? read.value.trimEnd() : '';\n if (/^\\s*\\[user\\]/im.test(before)) {\n run.notes.skipped.push(`SLICC 6's git [user]: ${file} already has one`);\n return;\n }\n await run.writer.put(file, `${before ? `${before}\\n` : ''}${section}`, false);\n run.notes.lines.push(`your git name and email, in ${file}`);\n}\n\nexport async function place(run: Run): Promise<void> {\n const { manifest } = run.opened;\n const units = run.parsed.units;\n const byFolder = new Map(\n units.filter((unit) => unit.six).map((unit) => [unit.six as string, unit])\n );\n const owners = new Map(\n units.flatMap((unit) =>\n unit.scoops.filter((scoop) => scoop.six).map((scoop) => [scoop.six as string, unit])\n )\n );\n const maps: Maps = {\n byFolder,\n owners,\n primary: byFolder.get('cone') ?? units.find((unit) => unit.six !== null),\n };\n const skills = new Map<string, string[]>();\n const later: [string, string][] = [];\n for (const [at, file] of manifest.files.entries()) {\n const where = whereOf(manifest, file.path);\n if (where.kind === 'skill')\n skills.set(where.folder, [...(skills.get(where.folder) ?? []), where.rest]);\n if (where.kind === 'home-user') later.push([file.path, where.rest]);\n if (where.kind === 'sprinkle') sprinkleCalls(run, where.folder, await staged(run, file.path));\n for (const [unit, root, rest] of targets(run, maps, where, file.path))\n await putIn(run, unit, root, rest, { zip: file.path });\n run.show('writing', at + 1, manifest.files.length);\n }\n await placeSkills(run, skills, maps.primary);\n await placeMemory(run);\n await placeLicks(run, maps);\n await placeGit(run);\n for (const [zip, rest] of later) await putIn(run, null, `${V6_ROOT}/home-user`, rest, { zip });\n if (run.deps.layout.users)\n for (const unit of units) await ownTree(run.writer, unit.cone, unit.home, unit.roots);\n}\n\nconst WHY: Record<string, string> = {\n env: 'environment file, often holds keys',\n pem: 'PEM file, may be a private key',\n 'git-credentials': 'git credentials in plain text',\n npmrc: 'npm config, may hold an auth token',\n 'slicc-oauth': 'SLICC 6 OAuth state',\n};\n\nconst NEVER: Record<string, string> = {\n 'secrets-env': '~/.slicc/secrets.env, which stays on the host',\n 'github-token': 'the GitHub token',\n 'mcp-tokens': 'MCP tokens',\n 'chrome-storage': \"the extension's chrome.storage secrets\",\n};\n\nconst bullets = (items: readonly string[], none: string) =>\n (items.length ? items : [none]).map((item) => `- ${item}`);\n\nfunction nextSteps(run: Run, mounts: readonly string[]): string[] {\n const { manifest } = run.opened;\n const { notes } = run;\n const sprinkles = [...notes.sprinkles].map(\n ([name, calls]) => `${name} (${[...calls].join(', ')})`\n );\n return [\n 'Re-enter your keys and logins in Settings.',\n ...(mounts.length ? [`Mount these folders again: ${mounts.join(', ')}.`] : []),\n ...(manifest.mcp.length\n ? [\n `Add these MCP servers again: ${manifest.mcp.map((item) => `${item.name} (${item.url})`).join(', ')}.`,\n ]\n : []),\n ...(notes.crons.length\n ? [\n `Turn crons on with sudo crontab -u <user> -e: remove the leading \"# \" from a job's two lines.`,\n ]\n : []),\n ...(notes.webhooks.length\n ? [\n \"Turn a webhook on by renaming <name>.json.v6 to <name>.json, then point its sender at seven's webhook URL.\",\n ]\n : []),\n ...(sprinkles.length\n ? [\n `These sprinkles call SLICC APIs other than slicc.lick and may need edits: ${sprinkles.join(', ')}.`,\n ]\n : []),\n ];\n}\n\nexport async function reportOf(run: Run, made: readonly string[]): Promise<string> {\n const { manifest } = run.opened;\n const { notes, date, parsed } = run;\n const recorded = parsed.units.filter((unit) => unit.record);\n const imported = [\n ...recorded.map(\n (unit, at) =>\n `${unit.title}: frozen cone ${made[at]}, home ${unit.home}${unit.suffix ? `, new files in ${unit.home}/workspace${unit.suffix}` : ''}${unit.scoops.length ? `, with ${unit.scoops.map((scoop) => scoop.name).join(', ')}` : ''}`\n ),\n ...notes.lines,\n ...notes.crons.map((line) => `crons, imported off: ${line}`),\n ...notes.webhooks.map((path) => `a webhook, imported off: ${path}`),\n ];\n const mounts = manifest.skipped\n .filter((item) => item.reason === 'mount')\n .map((item) => item.path);\n const skipped = [\n ...manifest.skipped.map((item) => `${item.path}: ${item.reason}`),\n ...parsed.units\n .filter((unit) => !unit.record)\n .map((unit) => `${unit.title}: unchanged since the last import`),\n ...notes.skipped,\n ...notes.shadowed,\n ...notes.tasks.map((task) => `a SLICC 6 task that cron can't express: ${task}`),\n ];\n const flagged = manifest.flagged.map(\n (item) => `| ${notes.placed.get(item.path) ?? item.path} | ${item.size} B | ${WHY[item.rule]} |`\n );\n const split = accountsSplit(manifest.providers, run.deps.providers);\n const providers = [\n ...split.again.map((id) => `${split.nameOf(id)}: re-enter in Settings`),\n ...split.missing.map((id) => `${split.nameOf(id)}: SLICC 7 has no such provider`),\n ];\n const never = manifest.secrets.map((name) => NEVER[name] ?? name);\n const part = manifest.part ? `, part ${manifest.part.index} of ${manifest.part.of}` : '';\n const text = [\n `# SLICC 6 import, ${date}`,\n '',\n `From ${manifest.producer.origin} (SLICC ${manifest.producer.slicc}), exported ${manifest.createdAt}, export ${manifest.exportId}${part}.`,\n '',\n '## Imported',\n '',\n ...bullets(imported, 'nothing new'),\n '',\n '## Skipped',\n '',\n ...bullets(skipped, 'nothing'),\n '',\n ...(flagged.length\n ? [\n '## Review these files',\n '',\n '| Path | Size | Why |',\n '| --- | --- | --- |',\n ...flagged,\n '',\n \"Delete or move these if you don't need them here.\",\n '',\n ]\n : []),\n '## Re-enter in Settings',\n '',\n ...bullets(providers, 'no providers'),\n ...(never.length ? ['', `These never move: ${never.join(', ')}.`] : []),\n '',\n '## Next steps',\n '',\n ...nextSteps(run, mounts).map((step) => `- ${step}`),\n '',\n ].join('\\n');\n let file = `${ROOT_HOME}/v6-import-${date}.md`;\n for (let n = 2; await run.writer.exists(file); n++)\n file = `${ROOT_HOME}/v6-import-${date}-${n}.md`;\n const written = await run.writer.env.writeFile(file, text, BACKGROUND_CONTEXT);\n if (!written.ok)\n throw new ImportError(`couldn't write the report ${file}: ${written.error.message}`);\n return file;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { coneSpec } from '../kernel/accounts.ts';\nimport { ROOT_HOME } from '../layout.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport { memoryRoot } from '../memory/store.ts';\nimport { GLOBAL_DIR } from '../skills/index.ts';\nimport { validateName } from '../skills/skills.ts';\nimport type { ImportDeps } from './v6.ts';\nimport {\n accountsSplit,\n type Opened,\n type Parsed,\n textOf,\n type Unit,\n type Where,\n whereOf,\n} from './v6.ts';\nimport { lickName, skillName } from './v6-schema.ts';\nimport {\n type CronLine,\n installCrons,\n ownTree,\n remember,\n same,\n userSection,\n type Writer,\n} from './v6-write.ts';\nimport { ImportError } from './zip.ts';\n\nexport const V6_ROOT = `${ROOT_HOME}/v6`;\n\nexport type Notes = {\n lines: string[];\n skipped: string[];\n shadowed: string[];\n tasks: string[];\n sprinkles: Map<string, Set<string>>;\n crons: string[];\n webhooks: string[];\n placed: Map<string, string>;\n};\n\nexport type Run = {\n deps: ImportDeps;\n opened: Opened;\n parsed: Parsed;\n writer: Writer;\n stage: string;\n date: string;\n notes: Notes;\n show: (phase: 'writing', done: number, total: number) => void;\n fresh: Map<string, boolean>;\n ready: Set<Unit>;\n at: Map<string, string>;\n};\n\ntype Source = Uint8Array | { zip: string };\n\nasync function staged(run: Run, zip: string): Promise<Uint8Array> {\n const read = await run.writer.env.readBinaryFile(\n run.at.get(zip) ?? `${run.stage}/${zip}`,\n run.writer.context\n );\n if (!read.ok) throw new ImportError(`the staged copy of ${zip} is gone`);\n return read.value;\n}\n\nasync function prepare(run: Run, unit: Unit): Promise<void> {\n if (run.ready.has(unit)) return;\n run.ready.add(unit);\n const { users, layout } = run.deps;\n if (layout.users && users) await users.add(coneSpec(unit.cone));\n}\n\nasync function envOf(run: Run, unit: Unit | null): Promise<ExecutionEnv | undefined> {\n if (!unit || unit.fresh || !run.deps.layout.users) return undefined;\n const env = await run.deps.envOf?.(unit.cone);\n if (!env) throw new ImportError(`there is no kernel user ${unit.cone} to write its files as`);\n return env;\n}\n\nasync function putIn(run: Run, unit: Unit | null, root: string, rest: string, source: Source) {\n if (unit) await prepare(run, unit);\n const env = await envOf(run, unit);\n let fresh = run.fresh.get(root);\n if (fresh === undefined) {\n fresh = unit?.fresh || (await run.writer.tree(root, env));\n run.fresh.set(root, fresh);\n }\n if (unit?.fresh) unit.roots.add(root);\n const path = `${root}/${rest}`;\n const from = 'zip' in source && !env ? run.at.get(source.zip) : undefined;\n if (from && 'zip' in source) {\n await run.writer.move(from, path, fresh);\n run.at.set(source.zip, path);\n } else\n await run.writer.put(\n path,\n 'zip' in source ? await staged(run, source.zip) : source,\n fresh,\n env\n );\n if ('zip' in source && !run.notes.placed.has(source.zip)) run.notes.placed.set(source.zip, path);\n}\n\nconst clean = (name: string) => name.replace(/[^A-Za-z0-9._-]+/g, '_').slice(-80) || 'file';\n\ntype Maps = { byFolder: Map<string, Unit>; owners: Map<string, Unit>; primary?: Unit };\n\nfunction targets(run: Run, maps: Maps, where: Where, zip: string): [Unit, string, string][] {\n if (where.kind === 'cone' || where.kind === 'scoop') {\n const unit = (where.kind === 'cone' ? maps.byFolder : maps.owners).get(where.folder) as Unit;\n const root =\n where.kind === 'cone'\n ? `${unit.home}/workspace${unit.suffix}`\n : `${unit.home}/scoops${unit.suffix}/${where.folder}`;\n return [[unit, root, where.rest]];\n }\n if (where.kind === 'sprinkle') {\n if (!maps.primary)\n run.notes.skipped.push(`the sprinkle ${where.folder}: there is no cone to put it in`);\n return maps.primary\n ? [\n [\n maps.primary,\n `${maps.primary.home}/sprinkles/${lickName(where.folder, 'sprinkle')}`,\n where.rest,\n ],\n ]\n : [];\n }\n if (where.kind !== 'attachment') return [];\n return run.parsed.units.flatMap((unit) =>\n (unit.doc?.attachments ?? [])\n .filter((item) => item.present && item.path === zip)\n .map((item): [Unit, string, string] => {\n const rest = `${item.sha256.slice(0, 12)}-${clean(item.originalName)}`;\n unit.files.set(item.id, `${unit.home}/v6-attachments/${rest}`);\n return [unit, `${unit.home}/v6-attachments`, rest];\n })\n );\n}\n\nfunction sprinkleCalls(run: Run, name: string, bytes: Uint8Array) {\n const found = [...new TextDecoder().decode(bytes).matchAll(/\\bslicc\\.([A-Za-z_$][\\w$]*)/g)]\n .map((match) => `slicc.${match[1]}`)\n .filter((call) => call !== 'slicc.lick');\n if (!found.length) return;\n const known = run.notes.sprinkles.get(name) ?? new Set<string>();\n for (const call of found) known.add(call);\n run.notes.sprinkles.set(name, known);\n}\n\nasync function alike(\n run: Run,\n dir: string,\n six: string,\n rests: readonly string[]\n): Promise<boolean> {\n for (const rest of rests) {\n const old = await run.writer.env.readBinaryFile(`${dir}/${rest}`, run.writer.context);\n if (!old.ok || !same(old.value, await staged(run, `skills/${six}/${rest}`))) return false;\n }\n return true;\n}\n\nasync function placeSkills(run: Run, skills: Map<string, string[]>, primary: Unit | undefined) {\n const seen = new Set<string>();\n for (const [six, rests] of skills) {\n const name = skillName(six);\n if (!name || validateName(name).length || seen.has(name)) {\n run.notes.skipped.push(\n `the skill ${six}: its name doesn't fit seven's rule (a-z, 0-9 and -)`\n );\n continue;\n }\n seen.add(name);\n const global = `${GLOBAL_DIR}/${name}`;\n const exists = await run.writer.exists(global);\n if (exists && (await alike(run, global, six, rests))) {\n run.notes.lines.push(`the skill ${name}: already in ${GLOBAL_DIR}`);\n continue;\n }\n const own = primary && exists ? `${primary.home}/.pi/agent/skills/${name}` : undefined;\n if (exists && !own) {\n run.notes.skipped.push(`the skill ${name}: ${GLOBAL_DIR} has another skill of that name`);\n continue;\n }\n for (const rest of rests)\n await putIn(run, own ? (primary as Unit) : null, own ?? global, rest, {\n zip: `skills/${six}/${rest}`,\n });\n if (own)\n run.notes.shadowed.push(\n `the skill ${name}: ${GLOBAL_DIR} has another skill of that name, so SLICC 6's copy is in ${own}, for ${(primary as Unit).cone} only`\n );\n else run.notes.lines.push(`the skill ${name}, for every agent, in ${global}`);\n }\n}\n\nasync function placeMemory(run: Run) {\n const root = memoryRoot(run.deps.memoryHome);\n const { data, units } = run.parsed;\n const places = [\n { name: 'memory/global.md', file: `${root}/MEMORY.md`, rest: `${V6_ROOT}/memory/global.md` },\n ...units\n .filter((unit) => unit.memory)\n .map((unit) => ({\n name: unit.memory as string,\n file: `${root}/${unit.cone}/MEMORY.md`,\n rest: `${V6_ROOT}/memory/${unit.cone}.md`,\n })),\n ];\n for (const place of places) {\n const bytes = data.get(place.name);\n const done = bytes\n ? await remember(run.writer, place.file, textOf(bytes, place.name), {\n file: place.rest,\n date: run.date,\n })\n : 'none';\n if (done === 'merged')\n run.notes.lines.push(`memory, under \"Imported from SLICC 6 (${run.date})\", in ${place.file}`);\n if (done === 'aside')\n run.notes.lines.push(`memory, in ${place.rest}: ${place.file} has no room left for it`);\n }\n}\n\nfunction earlier(run: Run, unit: Unit, name: string): LickTarget | undefined {\n const found = Object.entries(run.deps.agents.state().scoops).filter(\n ([, record]) => record.cone === unit.cone && record.name === name && !record.removed\n );\n return found.at(-1)?.[0] as LickTarget | undefined;\n}\n\nfunction router(run: Run, maps: Maps) {\n return (folder: string | null | undefined): [Unit, LickTarget] | undefined => {\n const cone = folder ? maps.byFolder.get(folder) : undefined;\n if (cone) return [cone, 'cone'];\n const owner = folder ? maps.owners.get(folder) : undefined;\n const scoop = owner?.scoops.find((item) => item.six === folder);\n const target =\n owner && scoop\n ? owner.record\n ? (`scoop:${scoop.folder}` as LickTarget)\n : earlier(run, owner, scoop.name)\n : undefined;\n if (owner && target) return [owner, target];\n const unit = owner ?? maps.primary;\n return unit ? [unit, 'cone'] : undefined;\n };\n}\n\nconst FIELD = '[0-9A-Za-z*,/-]+';\nconst SCHEDULE = new RegExp(\n `^(@(yearly|annually|monthly|weekly|daily|midnight|hourly|reboot)|${FIELD}( ${FIELD}){4})$`\n);\n\nconst flat = (text: string) => text.replace(/\\s+/g, ' ').trim();\n\nasync function placeLicks(run: Run, maps: Maps) {\n const { licks } = run.parsed;\n const route = router(run, maps);\n const crons = new Map<Unit, CronLine[]>();\n const add = (found: [Unit, LickTarget] | undefined, line: Omit<CronLine, 'target'>) => {\n const why = !SCHEDULE.test(line.schedule)\n ? `\"${line.schedule}\" is not a cron schedule`\n : !found && 'there is no cone to send it to';\n if (why) run.notes.skipped.push(`the cron ${line.name}: ${why}`);\n else if (found)\n crons.set(found[0], [...(crons.get(found[0]) ?? []), { ...line, target: found[1] }]);\n };\n for (const [at, task] of licks.crontasks.entries())\n add(route(task.targetFolder), {\n name: lickName(task.name, `v6-cron-${at + 1}`),\n schedule: flat(task.cron),\n message: '',\n ...(task.filter ? { filter: task.filter } : {}),\n });\n for (const [at, task] of licks.tasks.entries()) {\n if (task.scheduleType === 'cron')\n add(route(task.groupFolder), {\n name: `v6-task-${at + 1}`,\n schedule: flat(task.scheduleValue),\n message: flat(task.prompt),\n });\n else\n run.notes.tasks.push(\n `${task.scheduleType} ${task.scheduleValue}: ${flat(task.prompt).slice(0, 80)}`\n );\n }\n for (const [unit, list] of crons) {\n await prepare(run, unit);\n const user = run.deps.layout.users ? unit.cone : 'root';\n const count = await installCrons(run.writer, user, list, run.date);\n if (count) run.notes.crons.push(`${count} in crontab -u ${user}`);\n }\n for (const [at, hook] of licks.webhooks.entries()) {\n const found = route(hook.targetFolder);\n const name = lickName(hook.name, `v6-webhook-${at + 1}`);\n if (!found) run.notes.skipped.push(`the webhook ${name}: there is no cone to send it to`);\n if (!found) continue;\n const root = `${found[0].home}/.slicc/webhooks`;\n await putIn(\n run,\n found[0],\n root,\n `${name}.json.v6`,\n new TextEncoder().encode(`${JSON.stringify({ target: found[1] }, null, 2)}\\n`)\n );\n run.notes.webhooks.push(`${root}/${name}.json.v6`);\n }\n}\n\nasync function placeGit(run: Run) {\n const name = run.opened.manifest.gitconfig;\n const bytes = name ? run.parsed.data.get(name) : undefined;\n const section = bytes ? userSection(textOf(bytes, name as string)) : '';\n if (!section) return;\n const file = `${ROOT_HOME}/.gitconfig`;\n const read = await run.writer.env.readTextFile(file, run.writer.context);\n const before = read.ok ? read.value.trimEnd() : '';\n if (/^\\s*\\[user\\]/im.test(before)) {\n run.notes.skipped.push(`SLICC 6's git [user]: ${file} already has one`);\n return;\n }\n await run.writer.put(file, `${before ? `${before}\\n` : ''}${section}`, false);\n run.notes.lines.push(`your git name and email, in ${file}`);\n}\n\nexport async function place(run: Run): Promise<void> {\n const { manifest } = run.opened;\n const units = run.parsed.units;\n const byFolder = new Map(\n units.filter((unit) => unit.six).map((unit) => [unit.six as string, unit])\n );\n const owners = new Map(\n units.flatMap((unit) =>\n unit.scoops.filter((scoop) => scoop.six).map((scoop) => [scoop.six as string, unit])\n )\n );\n const maps: Maps = {\n byFolder,\n owners,\n primary: byFolder.get('cone') ?? units.find((unit) => unit.six !== null),\n };\n const skills = new Map<string, string[]>();\n const later: [string, string][] = [];\n for (const [at, file] of manifest.files.entries()) {\n const where = whereOf(manifest, file.path);\n if (where.kind === 'skill')\n skills.set(where.folder, [...(skills.get(where.folder) ?? []), where.rest]);\n if (where.kind === 'home-user') later.push([file.path, where.rest]);\n if (where.kind === 'sprinkle') sprinkleCalls(run, where.folder, await staged(run, file.path));\n for (const [unit, root, rest] of targets(run, maps, where, file.path))\n await putIn(run, unit, root, rest, { zip: file.path });\n run.show('writing', at + 1, manifest.files.length);\n }\n await placeSkills(run, skills, maps.primary);\n await placeMemory(run);\n await placeLicks(run, maps);\n await placeGit(run);\n for (const [zip, rest] of later) await putIn(run, null, `${V6_ROOT}/home-user`, rest, { zip });\n if (run.deps.layout.users)\n for (const unit of units) await ownTree(run.writer, unit.cone, unit.home, unit.roots);\n}\n\nconst WHY: Record<string, string> = {\n env: 'environment file, often holds keys',\n pem: 'PEM file, may be a private key',\n 'git-credentials': 'git credentials in plain text',\n npmrc: 'npm config, may hold an auth token',\n 'slicc-oauth': 'SLICC 6 OAuth state',\n};\n\nconst NEVER: Record<string, string> = {\n 'secrets-env': '~/.slicc/secrets.env, which stays on the host',\n 'github-token': 'the GitHub token',\n 'mcp-tokens': 'MCP tokens',\n 'chrome-storage': \"the extension's chrome.storage secrets\",\n};\n\nconst bullets = (items: readonly string[], none: string) =>\n (items.length ? items : [none]).map((item) => `- ${item}`);\n\nfunction nextSteps(run: Run, mounts: readonly string[]): string[] {\n const { manifest } = run.opened;\n const { notes } = run;\n const sprinkles = [...notes.sprinkles].map(\n ([name, calls]) => `${name} (${[...calls].join(', ')})`\n );\n return [\n 'Re-enter your keys and logins in Settings.',\n ...(mounts.length ? [`Mount these folders again: ${mounts.join(', ')}.`] : []),\n ...(manifest.mcp.length\n ? [\n `Add these MCP servers again: ${manifest.mcp.map((item) => `${item.name} (${item.url})`).join(', ')}.`,\n ]\n : []),\n ...(notes.crons.length\n ? [\n `Turn crons on with sudo crontab -u <user> -e: remove the leading \"# \" from a job's two lines.`,\n ]\n : []),\n ...(notes.webhooks.length\n ? [\n \"Turn a webhook on by renaming <name>.json.v6 to <name>.json, then point its sender at seven's webhook URL.\",\n ]\n : []),\n ...(sprinkles.length\n ? [\n `These sprinkles call SLICC APIs other than slicc.lick and may need edits: ${sprinkles.join(', ')}.`,\n ]\n : []),\n ];\n}\n\nexport async function reportOf(run: Run, made: readonly string[]): Promise<string> {\n const { manifest } = run.opened;\n const { notes, date, parsed } = run;\n const recorded = parsed.units.filter((unit) => unit.record);\n const imported = [\n ...recorded.map(\n (unit, at) =>\n `${unit.title}: frozen cone ${made[at]}, home ${unit.home}${unit.suffix ? `, new files in ${unit.home}/workspace${unit.suffix}` : ''}${unit.scoops.length ? `, with ${unit.scoops.map((scoop) => scoop.name).join(', ')}` : ''}`\n ),\n ...notes.lines,\n ...notes.crons.map((line) => `crons, imported off: ${line}`),\n ...notes.webhooks.map((path) => `a webhook, imported off: ${path}`),\n ];\n const mounts = manifest.skipped\n .filter((item) => item.reason === 'mount')\n .map((item) => item.path);\n const skipped = [\n ...manifest.skipped.map((item) => `${item.path}: ${item.reason}`),\n ...parsed.units\n .filter((unit) => !unit.record)\n .map((unit) => `${unit.title}: unchanged since the last import`),\n ...notes.skipped,\n ...notes.shadowed,\n ...notes.tasks.map((task) => `a SLICC 6 task that cron can't express: ${task}`),\n ];\n const flagged = manifest.flagged.map(\n (item) => `| ${notes.placed.get(item.path) ?? item.path} | ${item.size} B | ${WHY[item.rule]} |`\n );\n const split = accountsSplit(manifest.providers, run.deps.providers);\n const providers = [\n ...split.again.map((id) => `${split.nameOf(id)}: re-enter in Settings`),\n ...split.missing.map((id) => `${split.nameOf(id)}: SLICC 7 has no such provider`),\n ];\n const never = manifest.secrets.map((name) => NEVER[name] ?? name);\n const part = manifest.part ? `, part ${manifest.part.index} of ${manifest.part.of}` : '';\n const text = [\n `# SLICC 6 import, ${date}`,\n '',\n `From ${manifest.producer.origin} (SLICC ${manifest.producer.slicc}), exported ${manifest.createdAt}, export ${manifest.exportId}${part}.`,\n '',\n '## Imported',\n '',\n ...bullets(imported, 'nothing new'),\n '',\n '## Skipped',\n '',\n ...bullets(skipped, 'nothing'),\n '',\n ...(flagged.length\n ? [\n '## Review these files',\n '',\n '| Path | Size | Why |',\n '| --- | --- | --- |',\n ...flagged,\n '',\n \"Delete or move these if you don't need them here.\",\n '',\n ]\n : []),\n '## Re-enter in Settings',\n '',\n ...bullets(providers, 'no providers'),\n ...(never.length ? ['', `These never move: ${never.join(', ')}.`] : []),\n '',\n '## Next steps',\n '',\n ...nextSteps(run, mounts).map((step) => `- ${step}`),\n '',\n ].join('\\n');\n let file = `${ROOT_HOME}/v6-import-${date}.md`;\n for (let n = 2; await run.writer.exists(file); n++)\n file = `${ROOT_HOME}/v6-import-${date}-${n}.md`;\n const written = await run.writer.env.writeFile(file, text, BACKGROUND_CONTEXT);\n if (!written.ok)\n throw new ImportError(`couldn't write the report ${file}: ${written.error.message}`);\n return file;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["place"]
7
7
  }
@@ -1,4 +1,3 @@
1
- import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
2
1
  import { quote } from "../kernel/env.js";
3
2
  import { dirname } from "../kernel/paths.js";
4
3
  import { readTable, sendCommand, writeTable } from "../licks/crontab.js";
@@ -19,23 +18,20 @@ function chunks(items) {
19
18
  return out;
20
19
  }
21
20
  class Writer {
22
- #undo = [];
23
21
  #modes = /* @__PURE__ */ new Map();
24
- #moved = [];
25
22
  env;
26
23
  context;
27
24
  signal;
28
- constructor(env, context, signal) {
25
+ journal;
26
+ constructor(env, context, signal, journal) {
29
27
  this.env = env;
30
28
  this.context = context;
31
29
  this.signal = signal;
30
+ this.journal = journal;
32
31
  }
33
32
  check() {
34
33
  if (this.signal.aborted) throw new ImportError("the import was cancelled");
35
34
  }
36
- later(step) {
37
- this.#undo.push(step);
38
- }
39
35
  async exists(path, env = this.env) {
40
36
  const found = await env.exists(path, this.context);
41
37
  return found.ok && found.value;
@@ -45,8 +41,7 @@ class Writer {
45
41
  for (let dir = root; dir !== "/" && !await this.exists(dir, env); dir = dirname(dir))
46
42
  top = dir;
47
43
  if (top === void 0) return false;
48
- const gone = top;
49
- this.later(() => env.remove(gone, { recursive: true, force: true }, BACKGROUND_CONTEXT));
44
+ await this.journal.log({ made: top }, this.context);
50
45
  return true;
51
46
  }
52
47
  #real = /* @__PURE__ */ new Set(["/"]);
@@ -67,9 +62,7 @@ class Writer {
67
62
  if (!fresh && !await this.tree(dirname(path), env)) {
68
63
  const old = await env.readBinaryFile(path, this.context);
69
64
  if (old.ok && same(old.value, bytes)) return false;
70
- this.later(
71
- () => old.ok ? env.writeFile(path, old.value, BACKGROUND_CONTEXT) : env.remove(path, { force: true }, BACKGROUND_CONTEXT)
72
- );
65
+ await this.#keep(path, old.ok ? old.value : null);
73
66
  }
74
67
  done(await env.writeFile(path, bytes, this.context), path);
75
68
  return true;
@@ -83,38 +76,20 @@ class Writer {
83
76
  }
84
77
  }
85
78
  }
86
- async #restore() {
87
- const groups = /* @__PURE__ */ new Map();
88
- for (const path of this.#moved) {
89
- const mode = this.#modes.get(path);
90
- if (mode) groups.set(mode, [...groups.get(mode) ?? [], path]);
91
- }
92
- for (const [both, paths] of groups) {
93
- const [owner, mode] = both.split(" ");
94
- for (const part of chunks(paths)) {
95
- const list = part.map(quote).join(" ");
96
- await this.env.exec(
97
- `chown ${quote(owner)} -- ${list} && chmod ${quote(mode)} -- ${list}`,
98
- void 0,
99
- BACKGROUND_CONTEXT
100
- );
101
- }
102
- }
79
+ async #keep(path, old) {
80
+ const backup = old ? await this.journal.backup(old, this.context) : null;
81
+ await this.journal.log({ file: path, backup }, this.context);
103
82
  }
104
83
  async move(from, to, fresh) {
105
84
  this.check();
106
- if (!this.#moved.length) this.later(() => this.#restore());
107
85
  await this.direct(to);
108
86
  if (!fresh && !await this.tree(dirname(to))) {
109
87
  const old = await this.env.readBinaryFile(to, this.context);
110
- this.later(
111
- () => old.ok ? this.env.writeFile(to, old.value, BACKGROUND_CONTEXT) : this.env.remove(to, { force: true }, BACKGROUND_CONTEXT)
112
- );
88
+ await this.#keep(to, old.ok ? old.value : null);
113
89
  }
114
90
  done(await this.env.createDir(dirname(to), { recursive: true }, this.context), dirname(to));
91
+ await this.journal.log({ moved: from, to, mode: this.#modes.get(from) ?? null }, this.context);
115
92
  done(await this.env.renameFile(from, to, this.context), to);
116
- this.#moved.push(from);
117
- this.later(() => this.env.renameFile(to, from, BACKGROUND_CONTEXT));
118
93
  }
119
94
  async out(command) {
120
95
  let out = "";
@@ -148,8 +123,8 @@ class Writer {
148
123
  const failed = ran.ok ? ran.value.exitCode === 0 ? "" : err.trim() || "it failed" : ran.error.message;
149
124
  if (failed) throw new ImportError(`${command.split(" ")[0]}: ${failed}`);
150
125
  }
151
- async rollback() {
152
- for (const step of this.#undo.reverse()) await step().catch(() => void 0);
126
+ rollback() {
127
+ return this.journal.rollback();
153
128
  }
154
129
  }
155
130
  async function ownTree(writer, user, home, roots) {
@@ -206,9 +181,8 @@ async function installCrons(writer, user, crons, date) {
206
181
  const kept = withoutImported(before, new Set(crons.map((cron) => cron.name)));
207
182
  const table = `${[...kept, head, ...notes, ...blocks].join("\n")}
208
183
  `;
209
- writer.later(
210
- async () => before ? writeTable(writer.env, user, before, BACKGROUND_CONTEXT) : writer.env.exec(`crontab -u ${quote(user)} -r`, void 0, BACKGROUND_CONTEXT)
211
- );
184
+ const backup = before ? await writer.journal.backup(before, writer.context) : null;
185
+ await writer.journal.log({ cron: user, backup }, writer.context);
212
186
  const ran = await writeTable(writer.env, user, table, writer.context);
213
187
  if (ran.code !== 0) throw new ImportError(`crontab -u ${user}: ${ran.err.trim()}`);
214
188
  return changed;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/freezer/v6-write.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ExecutionEnv, Result } from '@earendil-works/pi-durable/env';\nimport { quote } from '../kernel/env.ts';\nimport { dirname } from '../kernel/paths.ts';\nimport { readTable, sendCommand, writeTable } from '../licks/crontab.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport { byteLength, MAX_BYTES, MAX_LINES } from '../memory/format.ts';\nimport { ImportError } from './zip.ts';\n\nconst encoder = new TextEncoder();\n\nexport function same(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length && a.every((byte, at) => byte === b[at]);\n}\n\nfunction done<T>(result: Result<T, { message: string }>, what: string): T {\n if (!result.ok) throw new ImportError(`couldn't write ${what}: ${result.error.message}`);\n return result.value;\n}\n\nconst CHUNK = 200;\n\nfunction chunks<T>(items: readonly T[]): T[][] {\n const out: T[][] = [];\n for (let at = 0; at < items.length; at += CHUNK) out.push(items.slice(at, at + CHUNK));\n return out;\n}\n\nexport class Writer {\n readonly #undo: (() => Promise<unknown>)[] = [];\n readonly #modes = new Map<string, string>();\n readonly #moved: string[] = [];\n readonly env: ExecutionEnv;\n readonly context: Context;\n readonly signal: AbortSignal;\n\n constructor(env: ExecutionEnv, context: Context, signal: AbortSignal) {\n this.env = env;\n this.context = context;\n this.signal = signal;\n }\n\n check(): void {\n if (this.signal.aborted) throw new ImportError('the import was cancelled');\n }\n\n later(step: () => Promise<unknown>): void {\n this.#undo.push(step);\n }\n\n async exists(path: string, env = this.env): Promise<boolean> {\n const found = await env.exists(path, this.context);\n return found.ok && found.value;\n }\n\n async tree(root: string, env = this.env): Promise<boolean> {\n let top: string | undefined;\n for (let dir = root; dir !== '/' && !(await this.exists(dir, env)); dir = dirname(dir))\n top = dir;\n if (top === undefined) return false;\n const gone = top;\n this.later(() => env.remove(gone, { recursive: true, force: true }, BACKGROUND_CONTEXT));\n return true;\n }\n\n readonly #real = new Set<string>(['/']);\n\n async direct(path: string): Promise<void> {\n for (let at = dirname(path); !this.#real.has(at); at = dirname(at)) {\n const real = await this.env.canonicalPath(at, this.context);\n if (!real.ok) continue;\n if (real.value !== at)\n throw new ImportError(`${at} leads through a symlink, and the import won't write there`);\n this.#real.add(at);\n break;\n }\n }\n\n async put(path: string, content: Uint8Array | string, fresh: boolean, env = this.env) {\n this.check();\n if (env === this.env) await this.direct(path);\n const bytes = typeof content === 'string' ? encoder.encode(content) : content;\n if (!fresh && !(await this.tree(dirname(path), env))) {\n const old = await env.readBinaryFile(path, this.context);\n if (old.ok && same(old.value, bytes)) return false;\n this.later(() =>\n old.ok\n ? env.writeFile(path, old.value, BACKGROUND_CONTEXT)\n : env.remove(path, { force: true }, BACKGROUND_CONTEXT)\n );\n }\n done(await env.writeFile(path, bytes, this.context), path);\n return true;\n }\n\n async remember(paths: readonly string[]): Promise<void> {\n for (const part of chunks(paths)) {\n const out = await this.out(`stat -c '%u:%g %a %n' -- ${part.map(quote).join(' ')}`);\n for (const line of out.split('\\n').filter(Boolean)) {\n const [owner = '', mode = '', ...name] = line.split(' ');\n this.#modes.set(name.join(' '), `${owner} ${mode}`);\n }\n }\n }\n\n async #restore(): Promise<void> {\n const groups = new Map<string, string[]>();\n for (const path of this.#moved) {\n const mode = this.#modes.get(path);\n if (mode) groups.set(mode, [...(groups.get(mode) ?? []), path]);\n }\n for (const [both, paths] of groups) {\n const [owner, mode] = both.split(' ') as [string, string];\n for (const part of chunks(paths)) {\n const list = part.map(quote).join(' ');\n await this.env.exec(\n `chown ${quote(owner)} -- ${list} && chmod ${quote(mode)} -- ${list}`,\n undefined,\n BACKGROUND_CONTEXT\n );\n }\n }\n }\n\n async move(from: string, to: string, fresh: boolean): Promise<void> {\n this.check();\n if (!this.#moved.length) this.later(() => this.#restore());\n await this.direct(to);\n if (!fresh && !(await this.tree(dirname(to)))) {\n const old = await this.env.readBinaryFile(to, this.context);\n this.later(() =>\n old.ok\n ? this.env.writeFile(to, old.value, BACKGROUND_CONTEXT)\n : this.env.remove(to, { force: true }, BACKGROUND_CONTEXT)\n );\n }\n done(await this.env.createDir(dirname(to), { recursive: true }, this.context), dirname(to));\n done(await this.env.renameFile(from, to, this.context), to);\n this.#moved.push(from);\n this.later(() => this.env.renameFile(to, from, BACKGROUND_CONTEXT));\n }\n\n async out(command: string): Promise<string> {\n let out = '';\n let err = '';\n const ran = await this.env.exec(\n command,\n {\n onOutput: (text, _context, info) => {\n if (info.stream === 'stderr') err += text;\n else out += text;\n },\n },\n this.context\n );\n if (!ran.ok) throw new ImportError(`${command.split(' ')[0]}: ${ran.error.message}`);\n if (ran.value.exitCode !== 0)\n throw new ImportError(`${command.split(' ')[0]}: ${err.trim() || 'it failed'}`);\n return out;\n }\n\n async sh(command: string): Promise<void> {\n let err = '';\n const ran = await this.env.exec(\n command,\n {\n onOutput: (text, _context, info) => {\n if (info.stream === 'stderr') err += text;\n },\n },\n this.context\n );\n const failed = ran.ok\n ? ran.value.exitCode === 0\n ? ''\n : err.trim() || 'it failed'\n : ran.error.message;\n if (failed) throw new ImportError(`${command.split(' ')[0]}: ${failed}`);\n }\n\n async rollback(): Promise<void> {\n for (const step of this.#undo.reverse()) await step().catch(() => undefined);\n }\n}\n\nexport async function ownTree(writer: Writer, user: string, home: string, roots: Iterable<string>) {\n for (const root of roots) {\n const parents: string[] = [];\n for (let at = root.lastIndexOf('/'); at > home.length; at = root.lastIndexOf('/', at - 1))\n parents.push(quote(root.slice(0, at)));\n const owner = quote(`${user}:${user}`);\n const lift = parents.length\n ? `chown ${owner} ${parents.join(' ')} && chmod 700 ${parents.join(' ')} && `\n : '';\n await writer.sh(`${lift}chown -R ${owner} ${quote(root)} && chmod -R u=rwX,go= ${quote(root)}`);\n }\n}\n\nconst fits = (text: string) =>\n byteLength(text) <= MAX_BYTES && text.split('\\n').length <= MAX_LINES;\n\nexport async function remember(\n writer: Writer,\n file: string,\n text: string,\n rest: { file: string; date: string }\n): Promise<'merged' | 'aside' | 'none'> {\n const body = text.replace(/^#{1,2}(\\s)/gm, '###$1').trim();\n if (!body) return 'none';\n await writer.direct(file);\n const real = await writer.env.canonicalPath(file, writer.context);\n if (real.ok && real.value !== file)\n throw new ImportError(`${file} is a symlink, and the import won't write memory through it`);\n const read = await writer.env.readTextFile(file, writer.context);\n const before = read.ok ? read.value.trimEnd() : '';\n if (before.includes(body)) return 'none';\n const joined = (section: string) => `${before}${before ? '\\n\\n' : ''}${section}\\n`;\n const heading = `## Imported from SLICC 6 (${rest.date})\\n\\n`;\n const whole = joined(`${heading}${body}`);\n if (fits(whole)) return (await writer.put(file, whole, false)) ? 'merged' : 'none';\n await writer.put(rest.file, `${text.trimEnd()}\\n`, false);\n const lines = body.split('\\n');\n const tail = `The rest is in ${rest.file}.`;\n const cut = () => joined(`${heading}${[...lines, '', tail].join('\\n')}`);\n while (lines.length && !fits(cut())) lines.pop();\n if (!lines.length) return 'aside';\n await writer.put(file, cut(), false);\n return 'merged';\n}\n\nexport type CronLine = {\n name: string;\n schedule: string;\n target: LickTarget;\n message: string;\n filter?: string;\n};\n\nexport async function installCrons(\n writer: Writer,\n user: string,\n crons: readonly CronLine[],\n date: string\n) {\n const before = await readTable(writer.env, user, writer.context);\n if (typeof before !== 'string') throw new ImportError(`crontab -u ${user}: ${before.error}`);\n const blocks = crons.map(\n (cron) => `# # name: ${cron.name}\\n# ${cron.schedule} ${sendCommand(cron.target, cron.message)}`\n );\n const changed = blocks.filter((block) => !before.includes(block)).length;\n if (!changed) return 0;\n const head = `# SLICC 6 crons, imported off on ${date}. Remove \"# \" from a job's two lines to turn it on.`;\n const notes = crons\n .filter((cron) => cron.filter)\n .map(\n (cron) =>\n `# ${cron.name} had a SLICC 6 filter, not imported: ${(cron.filter as string).replace(/\\s+/g, ' ')}`\n );\n const kept = withoutImported(before, new Set(crons.map((cron) => cron.name)));\n const table = `${[...kept, head, ...notes, ...blocks].join('\\n')}\\n`;\n writer.later(async () =>\n before\n ? writeTable(writer.env, user, before, BACKGROUND_CONTEXT)\n : writer.env.exec(`crontab -u ${quote(user)} -r`, undefined, BACKGROUND_CONTEXT)\n );\n const ran = await writeTable(writer.env, user, table, writer.context);\n if (ran.code !== 0) throw new ImportError(`crontab -u ${user}: ${ran.err.trim()}`);\n return changed;\n}\n\nexport function withoutImported(table: string, names: ReadonlySet<string>): string[] {\n const lines = table.split('\\n');\n if (lines.at(-1) === '') lines.pop();\n const out: string[] = [];\n for (let at = 0; at < lines.length; at++) {\n const line = lines[at] as string;\n const named = /^# # name: (\\S+)$/.exec(line);\n if (named && names.has(named[1] as string)) at++;\n else if (\n !line.startsWith('# SLICC 6 crons, imported off on ') &&\n !/^# (\\S+) had a SLICC 6 filter/.test(line)\n )\n out.push(line);\n }\n return out;\n}\n\nexport function userSection(text: string): string {\n const out: string[] = [];\n let inside = false;\n for (const line of text.split('\\n')) {\n if (/^\\s*\\[/.test(line)) inside = /^\\s*\\[user\\]\\s*$/i.test(line);\n if (inside && /^\\s*(\\[user\\]|(name|email)\\s*=)/i.test(line)) out.push(line.trim());\n }\n return out.length > 1\n ? `${out[0]}\\n${out\n .slice(1)\n .map((line) => `\\t${line}`)\n .join('\\n')}\\n`\n : '';\n}\n"],
5
- "mappings": "AACA,SAAS,0BAA0B;AAEnC,SAAS,aAAa;AACtB,SAAS,eAAe;AACxB,SAAS,WAAW,aAAa,kBAAkB;AAEnD,SAAS,YAAY,WAAW,iBAAiB;AACjD,SAAS,mBAAmB;AAE5B,MAAM,UAAU,IAAI,YAAY;AAEzB,SAAS,KAAK,GAAe,GAAwB;AAC1D,SAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,OAAO,SAAS,EAAE,EAAE,CAAC;AACtE;AAEA,SAAS,KAAQ,QAAwC,MAAiB;AACxE,MAAI,CAAC,OAAO,GAAI,OAAM,IAAI,YAAY,kBAAkB,IAAI,KAAK,OAAO,MAAM,OAAO,EAAE;AACvF,SAAO,OAAO;AAChB;AAEA,MAAM,QAAQ;AAEd,SAAS,OAAU,OAA4B;AAC7C,QAAM,MAAa,CAAC;AACpB,WAAS,KAAK,GAAG,KAAK,MAAM,QAAQ,MAAM,MAAO,KAAI,KAAK,MAAM,MAAM,IAAI,KAAK,KAAK,CAAC;AACrF,SAAO;AACT;AAEO,MAAM,OAAO;AAAA,EACT,QAAoC,CAAC;AAAA,EACrC,SAAS,oBAAI,IAAoB;AAAA,EACjC,SAAmB,CAAC;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,KAAmB,SAAkB,QAAqB;AACpE,SAAK,MAAM;AACX,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,OAAO,QAAS,OAAM,IAAI,YAAY,0BAA0B;AAAA,EAC3E;AAAA,EAEA,MAAM,MAAoC;AACxC,SAAK,MAAM,KAAK,IAAI;AAAA,EACtB;AAAA,EAEA,MAAM,OAAO,MAAc,MAAM,KAAK,KAAuB;AAC3D,UAAM,QAAQ,MAAM,IAAI,OAAO,MAAM,KAAK,OAAO;AACjD,WAAO,MAAM,MAAM,MAAM;AAAA,EAC3B;AAAA,EAEA,MAAM,KAAK,MAAc,MAAM,KAAK,KAAuB;AACzD,QAAI;AACJ,aAAS,MAAM,MAAM,QAAQ,OAAO,CAAE,MAAM,KAAK,OAAO,KAAK,GAAG,GAAI,MAAM,QAAQ,GAAG;AACnF,YAAM;AACR,QAAI,QAAQ,OAAW,QAAO;AAC9B,UAAM,OAAO;AACb,SAAK,MAAM,MAAM,IAAI,OAAO,MAAM,EAAE,WAAW,MAAM,OAAO,KAAK,GAAG,kBAAkB,CAAC;AACvF,WAAO;AAAA,EACT;AAAA,EAES,QAAQ,oBAAI,IAAY,CAAC,GAAG,CAAC;AAAA,EAEtC,MAAM,OAAO,MAA6B;AACxC,aAAS,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,MAAM,IAAI,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG;AAClE,YAAM,OAAO,MAAM,KAAK,IAAI,cAAc,IAAI,KAAK,OAAO;AAC1D,UAAI,CAAC,KAAK,GAAI;AACd,UAAI,KAAK,UAAU;AACjB,cAAM,IAAI,YAAY,GAAG,EAAE,4DAA4D;AACzF,WAAK,MAAM,IAAI,EAAE;AACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,IAAI,MAAc,SAA8B,OAAgB,MAAM,KAAK,KAAK;AACpF,SAAK,MAAM;AACX,QAAI,QAAQ,KAAK,IAAK,OAAM,KAAK,OAAO,IAAI;AAC5C,UAAM,QAAQ,OAAO,YAAY,WAAW,QAAQ,OAAO,OAAO,IAAI;AACtE,QAAI,CAAC,SAAS,CAAE,MAAM,KAAK,KAAK,QAAQ,IAAI,GAAG,GAAG,GAAI;AACpD,YAAM,MAAM,MAAM,IAAI,eAAe,MAAM,KAAK,OAAO;AACvD,UAAI,IAAI,MAAM,KAAK,IAAI,OAAO,KAAK,EAAG,QAAO;AAC7C,WAAK;AAAA,QAAM,MACT,IAAI,KACA,IAAI,UAAU,MAAM,IAAI,OAAO,kBAAkB,IACjD,IAAI,OAAO,MAAM,EAAE,OAAO,KAAK,GAAG,kBAAkB;AAAA,MAC1D;AAAA,IACF;AACA,SAAK,MAAM,IAAI,UAAU,MAAM,OAAO,KAAK,OAAO,GAAG,IAAI;AACzD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,OAAyC;AACtD,eAAW,QAAQ,OAAO,KAAK,GAAG;AAChC,YAAM,MAAM,MAAM,KAAK,IAAI,4BAA4B,KAAK,IAAI,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE;AAClF,iBAAW,QAAQ,IAAI,MAAM,IAAI,EAAE,OAAO,OAAO,GAAG;AAClD,cAAM,CAAC,QAAQ,IAAI,OAAO,IAAI,GAAG,IAAI,IAAI,KAAK,MAAM,GAAG;AACvD,aAAK,OAAO,IAAI,KAAK,KAAK,GAAG,GAAG,GAAG,KAAK,IAAI,IAAI,EAAE;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,UAAM,SAAS,oBAAI,IAAsB;AACzC,eAAW,QAAQ,KAAK,QAAQ;AAC9B,YAAM,OAAO,KAAK,OAAO,IAAI,IAAI;AACjC,UAAI,KAAM,QAAO,IAAI,MAAM,CAAC,GAAI,OAAO,IAAI,IAAI,KAAK,CAAC,GAAI,IAAI,CAAC;AAAA,IAChE;AACA,eAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,YAAM,CAAC,OAAO,IAAI,IAAI,KAAK,MAAM,GAAG;AACpC,iBAAW,QAAQ,OAAO,KAAK,GAAG;AAChC,cAAM,OAAO,KAAK,IAAI,KAAK,EAAE,KAAK,GAAG;AACrC,cAAM,KAAK,IAAI;AAAA,UACb,SAAS,MAAM,KAAK,CAAC,OAAO,IAAI,aAAa,MAAM,IAAI,CAAC,OAAO,IAAI;AAAA,UACnE;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,MAAc,IAAY,OAA+B;AAClE,SAAK,MAAM;AACX,QAAI,CAAC,KAAK,OAAO,OAAQ,MAAK,MAAM,MAAM,KAAK,SAAS,CAAC;AACzD,UAAM,KAAK,OAAO,EAAE;AACpB,QAAI,CAAC,SAAS,CAAE,MAAM,KAAK,KAAK,QAAQ,EAAE,CAAC,GAAI;AAC7C,YAAM,MAAM,MAAM,KAAK,IAAI,eAAe,IAAI,KAAK,OAAO;AAC1D,WAAK;AAAA,QAAM,MACT,IAAI,KACA,KAAK,IAAI,UAAU,IAAI,IAAI,OAAO,kBAAkB,IACpD,KAAK,IAAI,OAAO,IAAI,EAAE,OAAO,KAAK,GAAG,kBAAkB;AAAA,MAC7D;AAAA,IACF;AACA,SAAK,MAAM,KAAK,IAAI,UAAU,QAAQ,EAAE,GAAG,EAAE,WAAW,KAAK,GAAG,KAAK,OAAO,GAAG,QAAQ,EAAE,CAAC;AAC1F,SAAK,MAAM,KAAK,IAAI,WAAW,MAAM,IAAI,KAAK,OAAO,GAAG,EAAE;AAC1D,SAAK,OAAO,KAAK,IAAI;AACrB,SAAK,MAAM,MAAM,KAAK,IAAI,WAAW,IAAI,MAAM,kBAAkB,CAAC;AAAA,EACpE;AAAA,EAEA,MAAM,IAAI,SAAkC;AAC1C,QAAI,MAAM;AACV,QAAI,MAAM;AACV,UAAM,MAAM,MAAM,KAAK,IAAI;AAAA,MACzB;AAAA,MACA;AAAA,QACE,UAAU,CAAC,MAAM,UAAU,SAAS;AAClC,cAAI,KAAK,WAAW,SAAU,QAAO;AAAA,cAChC,QAAO;AAAA,QACd;AAAA,MACF;AAAA,MACA,KAAK;AAAA,IACP;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,YAAY,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,MAAM,OAAO,EAAE;AACnF,QAAI,IAAI,MAAM,aAAa;AACzB,YAAM,IAAI,YAAY,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK,KAAK,WAAW,EAAE;AAChF,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,GAAG,SAAgC;AACvC,QAAI,MAAM;AACV,UAAM,MAAM,MAAM,KAAK,IAAI;AAAA,MACzB;AAAA,MACA;AAAA,QACE,UAAU,CAAC,MAAM,UAAU,SAAS;AAClC,cAAI,KAAK,WAAW,SAAU,QAAO;AAAA,QACvC;AAAA,MACF;AAAA,MACA,KAAK;AAAA,IACP;AACA,UAAM,SAAS,IAAI,KACf,IAAI,MAAM,aAAa,IACrB,KACA,IAAI,KAAK,KAAK,cAChB,IAAI,MAAM;AACd,QAAI,OAAQ,OAAM,IAAI,YAAY,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,MAAM,EAAE;AAAA,EACzE;AAAA,EAEA,MAAM,WAA0B;AAC9B,eAAW,QAAQ,KAAK,MAAM,QAAQ,EAAG,OAAM,KAAK,EAAE,MAAM,MAAM,MAAS;AAAA,EAC7E;AACF;AAEA,eAAsB,QAAQ,QAAgB,MAAc,MAAc,OAAyB;AACjG,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAoB,CAAC;AAC3B,aAAS,KAAK,KAAK,YAAY,GAAG,GAAG,KAAK,KAAK,QAAQ,KAAK,KAAK,YAAY,KAAK,KAAK,CAAC;AACtF,cAAQ,KAAK,MAAM,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AACvC,UAAM,QAAQ,MAAM,GAAG,IAAI,IAAI,IAAI,EAAE;AACrC,UAAM,OAAO,QAAQ,SACjB,SAAS,KAAK,IAAI,QAAQ,KAAK,GAAG,CAAC,iBAAiB,QAAQ,KAAK,GAAG,CAAC,SACrE;AACJ,UAAM,OAAO,GAAG,GAAG,IAAI,YAAY,KAAK,IAAI,MAAM,IAAI,CAAC,0BAA0B,MAAM,IAAI,CAAC,EAAE;AAAA,EAChG;AACF;AAEA,MAAM,OAAO,CAAC,SACZ,WAAW,IAAI,KAAK,aAAa,KAAK,MAAM,IAAI,EAAE,UAAU;AAE9D,eAAsB,SACpB,QACA,MACA,MACA,MACsC;AACtC,QAAM,OAAO,KAAK,QAAQ,iBAAiB,OAAO,EAAE,KAAK;AACzD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,OAAO,IAAI;AACxB,QAAM,OAAO,MAAM,OAAO,IAAI,cAAc,MAAM,OAAO,OAAO;AAChE,MAAI,KAAK,MAAM,KAAK,UAAU;AAC5B,UAAM,IAAI,YAAY,GAAG,IAAI,6DAA6D;AAC5F,QAAM,OAAO,MAAM,OAAO,IAAI,aAAa,MAAM,OAAO,OAAO;AAC/D,QAAM,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,IAAI;AAChD,MAAI,OAAO,SAAS,IAAI,EAAG,QAAO;AAClC,QAAM,SAAS,CAAC,YAAoB,GAAG,MAAM,GAAG,SAAS,SAAS,EAAE,GAAG,OAAO;AAAA;AAC9E,QAAM,UAAU,6BAA6B,KAAK,IAAI;AAAA;AAAA;AACtD,QAAM,QAAQ,OAAO,GAAG,OAAO,GAAG,IAAI,EAAE;AACxC,MAAI,KAAK,KAAK,EAAG,QAAQ,MAAM,OAAO,IAAI,MAAM,OAAO,KAAK,IAAK,WAAW;AAC5E,QAAM,OAAO,IAAI,KAAK,MAAM,GAAG,KAAK,QAAQ,CAAC;AAAA,GAAM,KAAK;AACxD,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,OAAO,kBAAkB,KAAK,IAAI;AACxC,QAAM,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AACvE,SAAO,MAAM,UAAU,CAAC,KAAK,IAAI,CAAC,EAAG,OAAM,IAAI;AAC/C,MAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,QAAM,OAAO,IAAI,MAAM,IAAI,GAAG,KAAK;AACnC,SAAO;AACT;AAUA,eAAsB,aACpB,QACA,MACA,OACA,MACA;AACA,QAAM,SAAS,MAAM,UAAU,OAAO,KAAK,MAAM,OAAO,OAAO;AAC/D,MAAI,OAAO,WAAW,SAAU,OAAM,IAAI,YAAY,cAAc,IAAI,KAAK,OAAO,KAAK,EAAE;AAC3F,QAAM,SAAS,MAAM;AAAA,IACnB,CAAC,SAAS,aAAa,KAAK,IAAI;AAAA,IAAO,KAAK,QAAQ,IAAI,YAAY,KAAK,QAAQ,KAAK,OAAO,CAAC;AAAA,EAChG;AACA,QAAM,UAAU,OAAO,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,KAAK,CAAC,EAAE;AAClE,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,OAAO,oCAAoC,IAAI;AACrD,QAAM,QAAQ,MACX,OAAO,CAAC,SAAS,KAAK,MAAM,EAC5B;AAAA,IACC,CAAC,SACC,KAAK,KAAK,IAAI,wCAAyC,KAAK,OAAkB,QAAQ,QAAQ,GAAG,CAAC;AAAA,EACtG;AACF,QAAM,OAAO,gBAAgB,QAAQ,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC;AAC5E,QAAM,QAAQ,GAAG,CAAC,GAAG,MAAM,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA;AAChE,SAAO;AAAA,IAAM,YACX,SACI,WAAW,OAAO,KAAK,MAAM,QAAQ,kBAAkB,IACvD,OAAO,IAAI,KAAK,cAAc,MAAM,IAAI,CAAC,OAAO,QAAW,kBAAkB;AAAA,EACnF;AACA,QAAM,MAAM,MAAM,WAAW,OAAO,KAAK,MAAM,OAAO,OAAO,OAAO;AACpE,MAAI,IAAI,SAAS,EAAG,OAAM,IAAI,YAAY,cAAc,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,EAAE;AACjF,SAAO;AACT;AAEO,SAAS,gBAAgB,OAAe,OAAsC;AACnF,QAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,MAAI,MAAM,GAAG,EAAE,MAAM,GAAI,OAAM,IAAI;AACnC,QAAM,MAAgB,CAAC;AACvB,WAAS,KAAK,GAAG,KAAK,MAAM,QAAQ,MAAM;AACxC,UAAM,OAAO,MAAM,EAAE;AACrB,UAAM,QAAQ,oBAAoB,KAAK,IAAI;AAC3C,QAAI,SAAS,MAAM,IAAI,MAAM,CAAC,CAAW,EAAG;AAAA,aAE1C,CAAC,KAAK,WAAW,mCAAmC,KACpD,CAAC,gCAAgC,KAAK,IAAI;AAE1C,UAAI,KAAK,IAAI;AAAA,EACjB;AACA,SAAO;AACT;AAEO,SAAS,YAAY,MAAsB;AAChD,QAAM,MAAgB,CAAC;AACvB,MAAI,SAAS;AACb,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,SAAS,KAAK,IAAI,EAAG,UAAS,oBAAoB,KAAK,IAAI;AAC/D,QAAI,UAAU,mCAAmC,KAAK,IAAI,EAAG,KAAI,KAAK,KAAK,KAAK,CAAC;AAAA,EACnF;AACA,SAAO,IAAI,SAAS,IAChB,GAAG,IAAI,CAAC,CAAC;AAAA,EAAK,IACX,MAAM,CAAC,EACP,IAAI,CAAC,SAAS,IAAK,IAAI,EAAE,EACzB,KAAK,IAAI,CAAC;AAAA,IACb;AACN;",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv, Result } from '@earendil-works/pi-durable/env';\nimport { quote } from '../kernel/env.ts';\nimport { dirname } from '../kernel/paths.ts';\nimport { readTable, sendCommand, writeTable } from '../licks/crontab.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport { byteLength, MAX_BYTES, MAX_LINES } from '../memory/format.ts';\nimport type { Journal } from './v6-journal.ts';\nimport { ImportError } from './zip.ts';\n\nconst encoder = new TextEncoder();\n\nexport function same(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length && a.every((byte, at) => byte === b[at]);\n}\n\nfunction done<T>(result: Result<T, { message: string }>, what: string): T {\n if (!result.ok) throw new ImportError(`couldn't write ${what}: ${result.error.message}`);\n return result.value;\n}\n\nconst CHUNK = 200;\n\nfunction chunks<T>(items: readonly T[]): T[][] {\n const out: T[][] = [];\n for (let at = 0; at < items.length; at += CHUNK) out.push(items.slice(at, at + CHUNK));\n return out;\n}\n\nexport class Writer {\n readonly #modes = new Map<string, string>();\n readonly env: ExecutionEnv;\n readonly context: Context;\n readonly signal: AbortSignal;\n readonly journal: Journal;\n\n constructor(env: ExecutionEnv, context: Context, signal: AbortSignal, journal: Journal) {\n this.env = env;\n this.context = context;\n this.signal = signal;\n this.journal = journal;\n }\n\n check(): void {\n if (this.signal.aborted) throw new ImportError('the import was cancelled');\n }\n\n async exists(path: string, env = this.env): Promise<boolean> {\n const found = await env.exists(path, this.context);\n return found.ok && found.value;\n }\n\n async tree(root: string, env = this.env): Promise<boolean> {\n let top: string | undefined;\n for (let dir = root; dir !== '/' && !(await this.exists(dir, env)); dir = dirname(dir))\n top = dir;\n if (top === undefined) return false;\n await this.journal.log({ made: top }, this.context);\n return true;\n }\n\n readonly #real = new Set<string>(['/']);\n\n async direct(path: string): Promise<void> {\n for (let at = dirname(path); !this.#real.has(at); at = dirname(at)) {\n const real = await this.env.canonicalPath(at, this.context);\n if (!real.ok) continue;\n if (real.value !== at)\n throw new ImportError(`${at} leads through a symlink, and the import won't write there`);\n this.#real.add(at);\n break;\n }\n }\n\n async put(path: string, content: Uint8Array | string, fresh: boolean, env = this.env) {\n this.check();\n if (env === this.env) await this.direct(path);\n const bytes = typeof content === 'string' ? encoder.encode(content) : content;\n if (!fresh && !(await this.tree(dirname(path), env))) {\n const old = await env.readBinaryFile(path, this.context);\n if (old.ok && same(old.value, bytes)) return false;\n await this.#keep(path, old.ok ? old.value : null);\n }\n done(await env.writeFile(path, bytes, this.context), path);\n return true;\n }\n\n async remember(paths: readonly string[]): Promise<void> {\n for (const part of chunks(paths)) {\n const out = await this.out(`stat -c '%u:%g %a %n' -- ${part.map(quote).join(' ')}`);\n for (const line of out.split('\\n').filter(Boolean)) {\n const [owner = '', mode = '', ...name] = line.split(' ');\n this.#modes.set(name.join(' '), `${owner} ${mode}`);\n }\n }\n }\n\n async #keep(path: string, old: Uint8Array | null): Promise<void> {\n const backup = old ? await this.journal.backup(old, this.context) : null;\n await this.journal.log({ file: path, backup }, this.context);\n }\n\n async move(from: string, to: string, fresh: boolean): Promise<void> {\n this.check();\n await this.direct(to);\n if (!fresh && !(await this.tree(dirname(to)))) {\n const old = await this.env.readBinaryFile(to, this.context);\n await this.#keep(to, old.ok ? old.value : null);\n }\n done(await this.env.createDir(dirname(to), { recursive: true }, this.context), dirname(to));\n await this.journal.log({ moved: from, to, mode: this.#modes.get(from) ?? null }, this.context);\n done(await this.env.renameFile(from, to, this.context), to);\n }\n\n async out(command: string): Promise<string> {\n let out = '';\n let err = '';\n const ran = await this.env.exec(\n command,\n {\n onOutput: (text, _context, info) => {\n if (info.stream === 'stderr') err += text;\n else out += text;\n },\n },\n this.context\n );\n if (!ran.ok) throw new ImportError(`${command.split(' ')[0]}: ${ran.error.message}`);\n if (ran.value.exitCode !== 0)\n throw new ImportError(`${command.split(' ')[0]}: ${err.trim() || 'it failed'}`);\n return out;\n }\n\n async sh(command: string): Promise<void> {\n let err = '';\n const ran = await this.env.exec(\n command,\n {\n onOutput: (text, _context, info) => {\n if (info.stream === 'stderr') err += text;\n },\n },\n this.context\n );\n const failed = ran.ok\n ? ran.value.exitCode === 0\n ? ''\n : err.trim() || 'it failed'\n : ran.error.message;\n if (failed) throw new ImportError(`${command.split(' ')[0]}: ${failed}`);\n }\n\n rollback(): Promise<boolean> {\n return this.journal.rollback();\n }\n}\n\nexport async function ownTree(writer: Writer, user: string, home: string, roots: Iterable<string>) {\n for (const root of roots) {\n const parents: string[] = [];\n for (let at = root.lastIndexOf('/'); at > home.length; at = root.lastIndexOf('/', at - 1))\n parents.push(quote(root.slice(0, at)));\n const owner = quote(`${user}:${user}`);\n const lift = parents.length\n ? `chown ${owner} ${parents.join(' ')} && chmod 700 ${parents.join(' ')} && `\n : '';\n await writer.sh(`${lift}chown -R ${owner} ${quote(root)} && chmod -R u=rwX,go= ${quote(root)}`);\n }\n}\n\nconst fits = (text: string) =>\n byteLength(text) <= MAX_BYTES && text.split('\\n').length <= MAX_LINES;\n\nexport async function remember(\n writer: Writer,\n file: string,\n text: string,\n rest: { file: string; date: string }\n): Promise<'merged' | 'aside' | 'none'> {\n const body = text.replace(/^#{1,2}(\\s)/gm, '###$1').trim();\n if (!body) return 'none';\n await writer.direct(file);\n const real = await writer.env.canonicalPath(file, writer.context);\n if (real.ok && real.value !== file)\n throw new ImportError(`${file} is a symlink, and the import won't write memory through it`);\n const read = await writer.env.readTextFile(file, writer.context);\n const before = read.ok ? read.value.trimEnd() : '';\n if (before.includes(body)) return 'none';\n const joined = (section: string) => `${before}${before ? '\\n\\n' : ''}${section}\\n`;\n const heading = `## Imported from SLICC 6 (${rest.date})\\n\\n`;\n const whole = joined(`${heading}${body}`);\n if (fits(whole)) return (await writer.put(file, whole, false)) ? 'merged' : 'none';\n await writer.put(rest.file, `${text.trimEnd()}\\n`, false);\n const lines = body.split('\\n');\n const tail = `The rest is in ${rest.file}.`;\n const cut = () => joined(`${heading}${[...lines, '', tail].join('\\n')}`);\n while (lines.length && !fits(cut())) lines.pop();\n if (!lines.length) return 'aside';\n await writer.put(file, cut(), false);\n return 'merged';\n}\n\nexport type CronLine = {\n name: string;\n schedule: string;\n target: LickTarget;\n message: string;\n filter?: string;\n};\n\nexport async function installCrons(\n writer: Writer,\n user: string,\n crons: readonly CronLine[],\n date: string\n) {\n const before = await readTable(writer.env, user, writer.context);\n if (typeof before !== 'string') throw new ImportError(`crontab -u ${user}: ${before.error}`);\n const blocks = crons.map(\n (cron) => `# # name: ${cron.name}\\n# ${cron.schedule} ${sendCommand(cron.target, cron.message)}`\n );\n const changed = blocks.filter((block) => !before.includes(block)).length;\n if (!changed) return 0;\n const head = `# SLICC 6 crons, imported off on ${date}. Remove \"# \" from a job's two lines to turn it on.`;\n const notes = crons\n .filter((cron) => cron.filter)\n .map(\n (cron) =>\n `# ${cron.name} had a SLICC 6 filter, not imported: ${(cron.filter as string).replace(/\\s+/g, ' ')}`\n );\n const kept = withoutImported(before, new Set(crons.map((cron) => cron.name)));\n const table = `${[...kept, head, ...notes, ...blocks].join('\\n')}\\n`;\n const backup = before ? await writer.journal.backup(before, writer.context) : null;\n await writer.journal.log({ cron: user, backup }, writer.context);\n const ran = await writeTable(writer.env, user, table, writer.context);\n if (ran.code !== 0) throw new ImportError(`crontab -u ${user}: ${ran.err.trim()}`);\n return changed;\n}\n\nexport function withoutImported(table: string, names: ReadonlySet<string>): string[] {\n const lines = table.split('\\n');\n if (lines.at(-1) === '') lines.pop();\n const out: string[] = [];\n for (let at = 0; at < lines.length; at++) {\n const line = lines[at] as string;\n const named = /^# # name: (\\S+)$/.exec(line);\n if (named && names.has(named[1] as string)) at++;\n else if (\n !line.startsWith('# SLICC 6 crons, imported off on ') &&\n !/^# (\\S+) had a SLICC 6 filter/.test(line)\n )\n out.push(line);\n }\n return out;\n}\n\nexport function userSection(text: string): string {\n const out: string[] = [];\n let inside = false;\n for (const line of text.split('\\n')) {\n if (/^\\s*\\[/.test(line)) inside = /^\\s*\\[user\\]\\s*$/i.test(line);\n if (inside && /^\\s*(\\[user\\]|(name|email)\\s*=)/i.test(line)) out.push(line.trim());\n }\n return out.length > 1\n ? `${out[0]}\\n${out\n .slice(1)\n .map((line) => `\\t${line}`)\n .join('\\n')}\\n`\n : '';\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,6 +7,7 @@ import { quote } from "../kernel/env.js";
7
7
  import { opfsFile } from "../opfs-pool.js";
8
8
  import { TITLE_TURNS } from "../titles/extension.js";
9
9
  import { IMPORTS_DIR } from "./paths.js";
10
+ import { Journal, sweep } from "./v6-journal.js";
10
11
  import { place, reportOf } from "./v6-place.js";
11
12
  import {
12
13
  checkLicks,
@@ -664,21 +665,25 @@ async function storageRoom() {
664
665
  async function importOpened(deps, opened, options, context) {
665
666
  const now = (deps.now ?? Date.now)();
666
667
  const date = day(now);
667
- const writer = new Writer(deps.env, context, options.signal);
668
668
  const { dir } = opened;
669
- const stage = dir ?? `${IMPORTS_DIR}/${opened.key.replace("#", "-part")}`;
669
+ const own = `${IMPORTS_DIR}/${opened.key.replace("#", "-part")}`;
670
+ const stage = dir ?? own;
671
+ const journal = new Journal(undoing(deps), own);
672
+ const writer = new Writer(deps.env, context, options.signal, journal);
670
673
  const total = opened.manifest.files.filter((file) => !opened.sources.has(file.path)).reduce((sum, file) => sum + file.size, 0);
671
674
  const room = await (deps.room ?? storageRoom)();
672
675
  if (room !== void 0 && room < (dir ? 1 : 2) * total)
673
676
  throw new ImportError("there is not enough free storage for this import");
677
+ let keep = false;
674
678
  try {
675
- if (!dir)
676
- await writer.sh(
677
- `mkdir -p ${IMPORTS_DIR} && chmod 700 ${IMPORTS_DIR} && rm -rf ${quote(stage)} && mkdir -p ${quote(stage)}`
678
- );
679
+ await writer.sh(
680
+ `mkdir -p ${IMPORTS_DIR} && chmod 700 ${IMPORTS_DIR} && rm -rf ${quote(own)} && mkdir -p ${quote(own)}`
681
+ );
679
682
  const data = await verify(opened, writer, stage, options.show);
680
683
  const parsed = await parse(opened, data);
681
684
  await assign(deps, opened, parsed.units, options.again, date, context);
685
+ const cones = parsed.units.filter((unit) => unit.fresh).map((unit) => unit.cone);
686
+ await journal.open({ key: opened.key, at: now, cones, source: dir }, context);
682
687
  const notes = {
683
688
  lines: [],
684
689
  skipped: [],
@@ -709,11 +714,12 @@ async function importOpened(deps, opened, options, context) {
709
714
  await settled(opened, run.at, writer);
710
715
  made = await commit(deps, opened, parsed.units, now, context);
711
716
  } catch (error) {
712
- await writer.rollback();
717
+ keep = !await writer.rollback();
713
718
  throw error;
714
719
  }
720
+ keep = dir !== null && !(await deps.env.remove(dir, { recursive: true, force: true }, BACKGROUND_CONTEXT)).ok;
721
+ if (!keep) await journal.close().catch(() => void 0);
715
722
  const report = await reportOf(run, made).catch((error) => `nowhere (${error.message})`);
716
- if (dir) await deps.env.remove(dir, { recursive: true, force: true }, BACKGROUND_CONTEXT);
717
723
  const split = accountsSplit(opened.manifest.providers, deps.providers);
718
724
  const { origin } = opened.manifest.producer;
719
725
  const settings = said(split.settings, split.lacking);
@@ -730,7 +736,7 @@ ${settings ? `${settings}
730
736
  error: null
731
737
  };
732
738
  } finally {
733
- if (!dir) await deps.env.remove(stage, { recursive: true, force: true }, BACKGROUND_CONTEXT);
739
+ if (!keep) await deps.env.remove(own, { recursive: true, force: true }, BACKGROUND_CONTEXT);
734
740
  }
735
741
  }
736
742
  async function sourceOf(deps, source, context) {
@@ -748,9 +754,14 @@ async function sourceOf(deps, source, context) {
748
754
  if (!read.ok) throw new ImportError(`can't read ${source.path}: ${read.error.message}`);
749
755
  return bytesSource(read.value);
750
756
  }
757
+ function undoing(deps) {
758
+ return { env: deps.env, users: deps.users, state: () => deps.agents.state() };
759
+ }
751
760
  const IDLE = { phase: "idle", done: 0, total: 0, text: "" };
752
761
  function createImporter(deps) {
753
762
  const progress = replicatedState(IDLE);
763
+ let sweeping = sweep(undoing(deps)).catch(() => [IMPORTS_DIR]);
764
+ const swept = sweeping.then(() => void 0);
754
765
  let running;
755
766
  const result = (fields) => ({
756
767
  done: false,
@@ -764,11 +775,14 @@ function createImporter(deps) {
764
775
  });
765
776
  return {
766
777
  progress,
778
+ swept,
767
779
  cancel: () => running?.abort(),
768
780
  async run(source, options, context) {
769
781
  if (running) return result({ error: "an import is already running" });
770
782
  const controller = new AbortController();
771
783
  running = controller;
784
+ sweeping = sweeping.then(() => sweep(undoing(deps))).catch(() => [IMPORTS_DIR]);
785
+ const kept = await sweeping;
772
786
  const moving = "inPlace" in source;
773
787
  const writing = moving ? "Moving" : "Importing";
774
788
  const show = (phase, done, total) => progress.replace(BACKGROUND_CONTEXT, {
@@ -783,7 +797,8 @@ function createImporter(deps) {
783
797
  const opened = moving ? await openDirectory(deps, source.inPlace, context) : await openProfile(await sourceOf(deps, upload ?? source, context));
784
798
  const preview = await previewOf(opened, deps.agents.state(), deps.providers);
785
799
  if (options.preview) {
786
- const writer = new Writer(deps.env, context, controller.signal);
800
+ const journal = new Journal(undoing(deps), IMPORTS_DIR);
801
+ const writer = new Writer(deps.env, context, controller.signal, journal);
787
802
  await parse(opened, await verify(opened, writer, null, show));
788
803
  progress.replace(BACKGROUND_CONTEXT, IDLE);
789
804
  return result({ preview, text: preview.text, line: `${preview.line}
@@ -796,6 +811,10 @@ function createImporter(deps) {
796
811
  line: `Already imported on ${day(preview.already)}; freezer import --again makes fresh copies.
797
812
  `
798
813
  });
814
+ if (kept.length)
815
+ throw new ImportError(
816
+ `an earlier import couldn't be rolled back completely; its journal is in ${kept.join(", ")}`
817
+ );
799
818
  const done = await importOpened(
800
819
  deps,
801
820
  opened,