@ai-ecoverse/slicc-agent 6.4.3 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -1
- package/dist/client.js +1 -0
- package/dist/client.js.map +2 -2
- package/dist/control.js +16 -2
- package/dist/control.js.map +2 -2
- package/dist/freezer/index.js +8 -4
- package/dist/freezer/index.js.map +2 -2
- package/dist/freezer/paths.js +5 -0
- package/dist/freezer/paths.js.map +7 -0
- package/dist/freezer/v6-place.js +316 -8
- package/dist/freezer/v6-place.js.map +3 -3
- package/dist/freezer/v6-write.js +94 -1
- package/dist/freezer/v6-write.js.map +2 -2
- package/dist/freezer/v6.js +119 -30
- package/dist/freezer/v6.js.map +3 -3
- package/dist/host.js +2 -0
- package/dist/host.js.map +2 -2
- package/dist/kernel/agent-users.js +2 -1
- package/dist/kernel/agent-users.js.map +2 -2
- package/dist/licks/crontab.js +2 -1
- package/dist/licks/crontab.js.map +2 -2
- package/dist/licks/sources.js +1 -0
- package/dist/licks/sources.js.map +2 -2
- package/dist/main.js +5 -2
- package/dist/main.js.map +2 -2
- package/dist/services.js +3 -0
- package/dist/services.js.map +2 -2
- package/dist/spectrum/agent-port.js +15 -3
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/import-port.js +93 -0
- package/dist/spectrum/import-port.js.map +7 -0
- package/dist/types/client.d.ts +2 -0
- package/dist/types/control.d.ts +3 -1
- package/dist/types/freezer/paths.d.ts +1 -0
- package/dist/types/freezer/v6-place.d.ts +12 -0
- package/dist/types/freezer/v6-write.d.ts +16 -0
- package/dist/types/freezer/v6.d.ts +31 -4
- package/dist/types/licks/crontab.d.ts +7 -0
- package/dist/types/services.d.ts +12 -2
- package/dist/types/spectrum/agent-port.d.ts +8 -1
- package/dist/types/spectrum/import-port.d.ts +21 -0
- package/package.json +3 -3
|
@@ -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 { ImportDeps } from './v6.ts';\nimport { type Opened, type Parsed, type Unit, type Where, whereOf } from './v6.ts';\nimport { ownTree, type Writer } from './v6-write.ts';\nimport { ImportError } from './zip.ts';\n\nexport const V6_ROOT = `${ROOT_HOME}/v6`;\n\nexport type Run = {\n deps: ImportDeps;\n opened: Opened;\n parsed: Parsed;\n writer: Writer;\n stage: string;\n date: string;\n show: (phase: 'writing', done: number, total: number) => void;\n fresh: Map<string, boolean>;\n ready: Set<Unit>;\n};\n\nasync function staged(run: Run, zip: string): Promise<Uint8Array> {\n const read = await run.writer.env.readBinaryFile(`${run.stage}/${zip}`, run.writer.context);\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, zip: string) {\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 await run.writer.put(path, await staged(run, zip), fresh, env);\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> };\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 !== '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\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 = { byFolder, owners };\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 === 'home-user') later.push([file.path, where.rest]);\n for (const [unit, root, rest] of targets(run, maps, where, file.path))\n await putIn(run, unit, root, rest, file.path);\n run.show('writing', at + 1, manifest.files.length);\n }\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"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
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};\n\ntype Source = Uint8Array | { zip: string };\n\nasync function staged(run: Run, zip: string): Promise<Uint8Array> {\n const read = await run.writer.env.readBinaryFile(`${run.stage}/${zip}`, run.writer.context);\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 await run.writer.put(path, 'zip' in source ? await staged(run, source.zip) : source, fresh, env);\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
|
+
"names": ["place"]
|
|
7
7
|
}
|
package/dist/freezer/v6-write.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
2
2
|
import { quote } from "../kernel/env.js";
|
|
3
3
|
import { dirname } from "../kernel/paths.js";
|
|
4
|
+
import { readTable, sendCommand, writeTable } from "../licks/crontab.js";
|
|
5
|
+
import { byteLength, MAX_BYTES, MAX_LINES } from "../memory/format.js";
|
|
4
6
|
import { ImportError } from "./zip.js";
|
|
5
7
|
const encoder = new TextEncoder();
|
|
6
8
|
function same(a, b) {
|
|
@@ -39,8 +41,20 @@ class Writer {
|
|
|
39
41
|
this.later(() => env.remove(gone, { recursive: true, force: true }, BACKGROUND_CONTEXT));
|
|
40
42
|
return true;
|
|
41
43
|
}
|
|
44
|
+
#real = /* @__PURE__ */ new Set(["/"]);
|
|
45
|
+
async direct(path) {
|
|
46
|
+
for (let at = dirname(path); !this.#real.has(at); at = dirname(at)) {
|
|
47
|
+
const real = await this.env.canonicalPath(at, this.context);
|
|
48
|
+
if (!real.ok) continue;
|
|
49
|
+
if (real.value !== at)
|
|
50
|
+
throw new ImportError(`${at} leads through a symlink, and the import won't write there`);
|
|
51
|
+
this.#real.add(at);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
42
55
|
async put(path, content, fresh, env = this.env) {
|
|
43
56
|
this.check();
|
|
57
|
+
if (env === this.env) await this.direct(path);
|
|
44
58
|
const bytes = typeof content === "string" ? encoder.encode(content) : content;
|
|
45
59
|
if (!fresh && !await this.tree(dirname(path), env)) {
|
|
46
60
|
const old = await env.readBinaryFile(path, this.context);
|
|
@@ -80,9 +94,88 @@ async function ownTree(writer, user, home, roots) {
|
|
|
80
94
|
await writer.sh(`${lift}chown -R ${owner} ${quote(root)} && chmod -R u=rwX,go= ${quote(root)}`);
|
|
81
95
|
}
|
|
82
96
|
}
|
|
97
|
+
const fits = (text) => byteLength(text) <= MAX_BYTES && text.split("\n").length <= MAX_LINES;
|
|
98
|
+
async function remember(writer, file, text, rest) {
|
|
99
|
+
const body = text.replace(/^#{1,2}(\s)/gm, "###$1").trim();
|
|
100
|
+
if (!body) return "none";
|
|
101
|
+
await writer.direct(file);
|
|
102
|
+
const real = await writer.env.canonicalPath(file, writer.context);
|
|
103
|
+
if (real.ok && real.value !== file)
|
|
104
|
+
throw new ImportError(`${file} is a symlink, and the import won't write memory through it`);
|
|
105
|
+
const read = await writer.env.readTextFile(file, writer.context);
|
|
106
|
+
const before = read.ok ? read.value.trimEnd() : "";
|
|
107
|
+
if (before.includes(body)) return "none";
|
|
108
|
+
const joined = (section) => `${before}${before ? "\n\n" : ""}${section}
|
|
109
|
+
`;
|
|
110
|
+
const heading = `## Imported from SLICC 6 (${rest.date})
|
|
111
|
+
|
|
112
|
+
`;
|
|
113
|
+
const whole = joined(`${heading}${body}`);
|
|
114
|
+
if (fits(whole)) return await writer.put(file, whole, false) ? "merged" : "none";
|
|
115
|
+
await writer.put(rest.file, `${text.trimEnd()}
|
|
116
|
+
`, false);
|
|
117
|
+
const lines = body.split("\n");
|
|
118
|
+
const tail = `The rest is in ${rest.file}.`;
|
|
119
|
+
const cut = () => joined(`${heading}${[...lines, "", tail].join("\n")}`);
|
|
120
|
+
while (lines.length && !fits(cut())) lines.pop();
|
|
121
|
+
if (!lines.length) return "aside";
|
|
122
|
+
await writer.put(file, cut(), false);
|
|
123
|
+
return "merged";
|
|
124
|
+
}
|
|
125
|
+
async function installCrons(writer, user, crons, date) {
|
|
126
|
+
const before = await readTable(writer.env, user, writer.context);
|
|
127
|
+
if (typeof before !== "string") throw new ImportError(`crontab -u ${user}: ${before.error}`);
|
|
128
|
+
const blocks = crons.map(
|
|
129
|
+
(cron) => `# # name: ${cron.name}
|
|
130
|
+
# ${cron.schedule} ${sendCommand(cron.target, cron.message)}`
|
|
131
|
+
);
|
|
132
|
+
const changed = blocks.filter((block) => !before.includes(block)).length;
|
|
133
|
+
if (!changed) return 0;
|
|
134
|
+
const head = `# SLICC 6 crons, imported off on ${date}. Remove "# " from a job's two lines to turn it on.`;
|
|
135
|
+
const notes = crons.filter((cron) => cron.filter).map(
|
|
136
|
+
(cron) => `# ${cron.name} had a SLICC 6 filter, not imported: ${cron.filter.replace(/\s+/g, " ")}`
|
|
137
|
+
);
|
|
138
|
+
const kept = withoutImported(before, new Set(crons.map((cron) => cron.name)));
|
|
139
|
+
const table = `${[...kept, head, ...notes, ...blocks].join("\n")}
|
|
140
|
+
`;
|
|
141
|
+
writer.later(
|
|
142
|
+
async () => before ? writeTable(writer.env, user, before, BACKGROUND_CONTEXT) : writer.env.exec(`crontab -u ${quote(user)} -r`, void 0, BACKGROUND_CONTEXT)
|
|
143
|
+
);
|
|
144
|
+
const ran = await writeTable(writer.env, user, table, writer.context);
|
|
145
|
+
if (ran.code !== 0) throw new ImportError(`crontab -u ${user}: ${ran.err.trim()}`);
|
|
146
|
+
return changed;
|
|
147
|
+
}
|
|
148
|
+
function withoutImported(table, names) {
|
|
149
|
+
const lines = table.split("\n");
|
|
150
|
+
if (lines.at(-1) === "") lines.pop();
|
|
151
|
+
const out = [];
|
|
152
|
+
for (let at = 0; at < lines.length; at++) {
|
|
153
|
+
const line = lines[at];
|
|
154
|
+
const named = /^# # name: (\S+)$/.exec(line);
|
|
155
|
+
if (named && names.has(named[1])) at++;
|
|
156
|
+
else if (!line.startsWith("# SLICC 6 crons, imported off on ") && !/^# (\S+) had a SLICC 6 filter/.test(line))
|
|
157
|
+
out.push(line);
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function userSection(text) {
|
|
162
|
+
const out = [];
|
|
163
|
+
let inside = false;
|
|
164
|
+
for (const line of text.split("\n")) {
|
|
165
|
+
if (/^\s*\[/.test(line)) inside = /^\s*\[user\]\s*$/i.test(line);
|
|
166
|
+
if (inside && /^\s*(\[user\]|(name|email)\s*=)/i.test(line)) out.push(line.trim());
|
|
167
|
+
}
|
|
168
|
+
return out.length > 1 ? `${out[0]}
|
|
169
|
+
${out.slice(1).map((line) => ` ${line}`).join("\n")}
|
|
170
|
+
` : "";
|
|
171
|
+
}
|
|
83
172
|
export {
|
|
84
173
|
Writer,
|
|
174
|
+
installCrons,
|
|
85
175
|
ownTree,
|
|
86
|
-
|
|
176
|
+
remember,
|
|
177
|
+
same,
|
|
178
|
+
userSection,
|
|
179
|
+
withoutImported
|
|
87
180
|
};
|
|
88
181
|
//# sourceMappingURL=v6-write.js.map
|
|
@@ -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 { 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\nexport class Writer {\n readonly #undo: (() => Promise<unknown>)[] = [];\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 async put(path: string, content: Uint8Array | string, fresh: boolean, env = this.env) {\n this.check();\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 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"],
|
|
5
|
-
"mappings": "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",
|
|
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\nexport class Writer {\n readonly #undo: (() => Promise<unknown>)[] = [];\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 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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/freezer/v6.js
CHANGED
|
@@ -6,7 +6,8 @@ import { homeOf } from "../kernel/accounts.js";
|
|
|
6
6
|
import { quote } from "../kernel/env.js";
|
|
7
7
|
import { opfsFile } from "../opfs-pool.js";
|
|
8
8
|
import { TITLE_TURNS } from "../titles/extension.js";
|
|
9
|
-
import {
|
|
9
|
+
import { IMPORTS_DIR } from "./paths.js";
|
|
10
|
+
import { place, reportOf } from "./v6-place.js";
|
|
10
11
|
import {
|
|
11
12
|
checkLicks,
|
|
12
13
|
checkManifest,
|
|
@@ -22,7 +23,6 @@ import {
|
|
|
22
23
|
zipEntries
|
|
23
24
|
} from "./zip.js";
|
|
24
25
|
const V6_KIND = "slicc.v6";
|
|
25
|
-
const IMPORTS_DIR = "/var/lib/slicc/agent/imports";
|
|
26
26
|
const MiB = 1024 ** 2;
|
|
27
27
|
const LIMITS = {
|
|
28
28
|
archive: 2048 * MiB,
|
|
@@ -32,6 +32,13 @@ const LIMITS = {
|
|
|
32
32
|
total: 4096 * MiB,
|
|
33
33
|
ratio: 200
|
|
34
34
|
};
|
|
35
|
+
const UPLOAD = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
36
|
+
const uploadPath = (id) => `${IMPORTS_DIR}/upload-${id}.zip`;
|
|
37
|
+
function uploaded(source) {
|
|
38
|
+
if (!("upload" in source)) return void 0;
|
|
39
|
+
if (!UPLOAD.test(source.upload)) throw new ImportError("that upload isn't one of the agent's");
|
|
40
|
+
return { path: uploadPath(source.upload) };
|
|
41
|
+
}
|
|
35
42
|
const day = (at) => new Date(at).toISOString().slice(0, 10);
|
|
36
43
|
const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
37
44
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
@@ -73,8 +80,8 @@ function placeOf(manifest, name) {
|
|
|
73
80
|
const scoops = manifest.cones.flatMap((cone) => cone.scoops);
|
|
74
81
|
if (top === "files" && second === "scoops" && inner && scoops.some((scoop) => scoop.folder === folder))
|
|
75
82
|
return { kind: "scoop", folder, rest: inner };
|
|
76
|
-
const
|
|
77
|
-
if (rest &&
|
|
83
|
+
const listed2 = top === "skills" ? manifest.skills : top === "sprinkles" ? manifest.sprinkles : [];
|
|
84
|
+
if (rest && listed2.some((item) => item.name === second))
|
|
78
85
|
return { kind: top === "skills" ? "skill" : "sprinkle", folder: second, rest };
|
|
79
86
|
throw new ImportError(`${name} has no place in a SLICC 6 profile`);
|
|
80
87
|
}
|
|
@@ -106,17 +113,17 @@ async function openProfile(source) {
|
|
|
106
113
|
throw new ImportError("manifest.json must be the first entry");
|
|
107
114
|
const entries = scan(list, source.size);
|
|
108
115
|
const manifest = checkManifest(json(await readEntry(source, list[0]), "manifest.json"));
|
|
109
|
-
const
|
|
116
|
+
const listed2 = /* @__PURE__ */ new Set();
|
|
110
117
|
for (const file of manifest.files) {
|
|
111
118
|
if (entries.get(file.path)?.size !== file.size || file.path === "manifest.json")
|
|
112
119
|
throw new ImportError(
|
|
113
120
|
`${file.path} is in the manifest but not in the zip, or not at that size`
|
|
114
121
|
);
|
|
115
|
-
|
|
122
|
+
listed2.add(file.path);
|
|
116
123
|
}
|
|
117
124
|
for (const name of entries.keys()) {
|
|
118
125
|
if (name === "manifest.json") continue;
|
|
119
|
-
if (!
|
|
126
|
+
if (!listed2.has(name)) throw new ImportError(`${name} is not listed in the manifest`);
|
|
120
127
|
whereOf(manifest, name);
|
|
121
128
|
}
|
|
122
129
|
const { exportId, part } = manifest;
|
|
@@ -130,19 +137,66 @@ async function licksIn(opened, data) {
|
|
|
130
137
|
if (!entry) throw new ImportError(`${name} is named in the manifest but not in the zip`);
|
|
131
138
|
return checkLicks(json(data?.get(name) ?? await readEntry(opened.source, entry), name));
|
|
132
139
|
}
|
|
140
|
+
const ACCOUNTS = {
|
|
141
|
+
"bedrock-camp": "amazon-bedrock",
|
|
142
|
+
bedrock: "amazon-bedrock"
|
|
143
|
+
};
|
|
144
|
+
function accountsOf(providers) {
|
|
145
|
+
return [...new Set(providers.map((name) => ACCOUNTS[name] ?? name))];
|
|
146
|
+
}
|
|
133
147
|
const UNITS = [
|
|
134
|
-
[
|
|
135
|
-
[
|
|
136
|
-
[
|
|
148
|
+
[1024 ** 3, "GB"],
|
|
149
|
+
[1024 ** 2, "MB"],
|
|
150
|
+
[1024, "KB"]
|
|
137
151
|
];
|
|
138
|
-
|
|
152
|
+
const NAMES = {
|
|
153
|
+
anthropic: "Anthropic",
|
|
154
|
+
openai: "OpenAI",
|
|
155
|
+
"github-copilot": "GitHub Copilot",
|
|
156
|
+
google: "Google",
|
|
157
|
+
azure: "Azure",
|
|
158
|
+
"azure-openai": "Azure OpenAI",
|
|
159
|
+
"azure-ai-foundry": "Azure AI Foundry",
|
|
160
|
+
"amazon-bedrock": "Amazon Bedrock",
|
|
161
|
+
adobe: "Adobe",
|
|
162
|
+
openrouter: "OpenRouter",
|
|
163
|
+
mistral: "Mistral",
|
|
164
|
+
groq: "Groq",
|
|
165
|
+
xai: "xAI",
|
|
166
|
+
deepseek: "DeepSeek",
|
|
167
|
+
"local-llm": "Local LLM"
|
|
168
|
+
};
|
|
169
|
+
function listed(names) {
|
|
170
|
+
return names.length > 1 ? `${names.slice(0, -1).join(", ")} and ${names.at(-1)}` : names.join("");
|
|
171
|
+
}
|
|
172
|
+
function accountsSplit(providers, known) {
|
|
173
|
+
const accounts = accountsOf(providers);
|
|
174
|
+
const ids = new Map((known ?? []).map((account) => [account.id, account.name]));
|
|
175
|
+
const again = known ? accounts.filter((id) => ids.has(id)) : accounts;
|
|
176
|
+
const missing = accounts.filter((id) => !again.includes(id));
|
|
177
|
+
const nameOf = (id) => ids.get(id) || NAMES[id] || id;
|
|
178
|
+
const lacking = missing.length ? `SLICC 7 has no accounts for ${listed(missing.map(nameOf))}.` : "";
|
|
179
|
+
const settings = again.length ? `Re-enter in Settings: ${listed(again.map(nameOf))}.` : "";
|
|
180
|
+
const signIn = again.length ? `Sign in to ${listed(again.map(nameOf))} again before you thaw them.` : "";
|
|
181
|
+
return { again, missing, nameOf, lacking, settings, signIn };
|
|
182
|
+
}
|
|
183
|
+
const said = (...parts) => parts.filter(Boolean).join(" ");
|
|
184
|
+
const words = (at) => new Date(at).toLocaleDateString("en-GB", {
|
|
185
|
+
day: "numeric",
|
|
186
|
+
month: "short",
|
|
187
|
+
year: "numeric",
|
|
188
|
+
timeZone: "UTC"
|
|
189
|
+
});
|
|
190
|
+
const hostOf = (origin) => URL.canParse(origin) ? new URL(origin).host : origin;
|
|
191
|
+
async function previewOf(opened, state, known) {
|
|
139
192
|
const { manifest } = opened;
|
|
140
193
|
const licks = await licksIn(opened);
|
|
141
194
|
const bytes = manifest.files.filter((file) => file.path.startsWith("files/")).reduce((sum, file) => sum + file.size, 0);
|
|
142
195
|
const scoops = manifest.cones.reduce((sum, cone) => sum + cone.scoops.length, 0);
|
|
143
196
|
const mounts = manifest.skipped.filter((item) => item.reason === "mount").map((item) => item.path.replace(/^\/mnt\//, ""));
|
|
144
197
|
const crons = licks.crontasks.length + licks.tasks.filter((task) => task.scheduleType === "cron").length;
|
|
145
|
-
const host =
|
|
198
|
+
const host = hostOf(manifest.producer.origin);
|
|
199
|
+
const split = accountsSplit(manifest.providers, known);
|
|
146
200
|
const [at, unit] = UNITS.find(([at2]) => bytes >= at2) ?? UNITS[2];
|
|
147
201
|
const size = `${(bytes / at).toFixed(1)} ${unit}`;
|
|
148
202
|
const parts = [
|
|
@@ -166,11 +220,20 @@ async function previewOf(opened, state) {
|
|
|
166
220
|
skills: manifest.skills.map((skill) => skill.name),
|
|
167
221
|
crons,
|
|
168
222
|
webhooks: licks.webhooks.length,
|
|
169
|
-
flagged: manifest.flagged.
|
|
170
|
-
providers:
|
|
223
|
+
flagged: manifest.flagged.map((item) => item.path),
|
|
224
|
+
providers: split.again,
|
|
171
225
|
mounts,
|
|
172
226
|
already: state.imports?.[opened.key]?.at ?? null,
|
|
173
|
-
text:
|
|
227
|
+
text: said(
|
|
228
|
+
`SLICC 6 profile from ${host}, exported ${words(manifest.createdAt)}.`,
|
|
229
|
+
split.lacking
|
|
230
|
+
),
|
|
231
|
+
line: said(
|
|
232
|
+
`SLICC 6 profile from ${host}, exported ${day(manifest.createdAt)}: ${parts.join(", ")}.`,
|
|
233
|
+
split.settings,
|
|
234
|
+
split.lacking,
|
|
235
|
+
`Not imported: ${plural(mounts.length, "mount")}.`
|
|
236
|
+
)
|
|
174
237
|
};
|
|
175
238
|
}
|
|
176
239
|
async function sha256(bytes) {
|
|
@@ -420,9 +483,10 @@ async function conversationIn(tx, conversation, unit, model) {
|
|
|
420
483
|
for (const entry of entriesOf(conversation, unit.files)) await tx.appendEntry(record.id, entry);
|
|
421
484
|
return record.id;
|
|
422
485
|
}
|
|
423
|
-
function modelOf(conversation) {
|
|
486
|
+
function modelOf(deps, conversation) {
|
|
424
487
|
const model = conversation?.messages.findLast((message) => message.model)?.model;
|
|
425
|
-
|
|
488
|
+
if (!model) return "";
|
|
489
|
+
return deps.catalog?.getModel(model.provider, model.id)?.name ?? `${model.provider}/${model.id}`;
|
|
426
490
|
}
|
|
427
491
|
async function commit(deps, opened, units, at, context) {
|
|
428
492
|
const model = (await deps.agents.cone().agent(context)).model;
|
|
@@ -465,7 +529,7 @@ async function commit(deps, opened, units, at, context) {
|
|
|
465
529
|
name: unit.name,
|
|
466
530
|
conversation: await conversationIn(tx, unit.main, unit, model),
|
|
467
531
|
title: unit.title,
|
|
468
|
-
model: modelOf(unit.main),
|
|
532
|
+
model: modelOf(deps, unit.main),
|
|
469
533
|
messages: (unit.main?.messages ?? []).filter((message) => message.role !== "tool-result").length,
|
|
470
534
|
frozenAt: unit.frozenAt,
|
|
471
535
|
scoops,
|
|
@@ -503,6 +567,16 @@ async function importOpened(deps, opened, options, context) {
|
|
|
503
567
|
const data = await verify(opened, writer, stage, options.show);
|
|
504
568
|
const parsed = await parse(opened, data);
|
|
505
569
|
await assign(deps, opened, parsed.units, options.again, date, context);
|
|
570
|
+
const notes = {
|
|
571
|
+
lines: [],
|
|
572
|
+
skipped: [],
|
|
573
|
+
shadowed: [],
|
|
574
|
+
tasks: [],
|
|
575
|
+
sprinkles: /* @__PURE__ */ new Map(),
|
|
576
|
+
crons: [],
|
|
577
|
+
webhooks: [],
|
|
578
|
+
placed: /* @__PURE__ */ new Map()
|
|
579
|
+
};
|
|
506
580
|
const run = {
|
|
507
581
|
deps,
|
|
508
582
|
opened,
|
|
@@ -510,6 +584,7 @@ async function importOpened(deps, opened, options, context) {
|
|
|
510
584
|
writer,
|
|
511
585
|
stage,
|
|
512
586
|
date,
|
|
587
|
+
notes,
|
|
513
588
|
show: options.show,
|
|
514
589
|
fresh: /* @__PURE__ */ new Map(),
|
|
515
590
|
ready: /* @__PURE__ */ new Set()
|
|
@@ -522,15 +597,19 @@ async function importOpened(deps, opened, options, context) {
|
|
|
522
597
|
await writer.rollback();
|
|
523
598
|
throw error;
|
|
524
599
|
}
|
|
525
|
-
const
|
|
526
|
-
const
|
|
600
|
+
const report = await reportOf(run, made).catch((error) => `nowhere (${error.message})`);
|
|
601
|
+
const split = accountsSplit(opened.manifest.providers, deps.providers);
|
|
602
|
+
const { origin } = opened.manifest.producer;
|
|
603
|
+
const settings = said(split.settings, split.lacking);
|
|
527
604
|
return {
|
|
528
605
|
done: true,
|
|
529
606
|
preview: null,
|
|
530
|
-
cones,
|
|
531
|
-
report
|
|
532
|
-
text: `
|
|
533
|
-
|
|
607
|
+
cones: made,
|
|
608
|
+
report,
|
|
609
|
+
text: said(`From ${hostOf(origin)}.`, split.signIn),
|
|
610
|
+
line: `Imported ${plural(made.length, "frozen cone")} from ${origin}${made.length ? `: ${made.join(", ")}` : ""}.
|
|
611
|
+
${settings ? `${settings}
|
|
612
|
+
` : ""}The report is ${report}.
|
|
534
613
|
`,
|
|
535
614
|
error: null
|
|
536
615
|
};
|
|
@@ -563,6 +642,7 @@ function createImporter(deps) {
|
|
|
563
642
|
cones: [],
|
|
564
643
|
report: null,
|
|
565
644
|
text: "",
|
|
645
|
+
line: "",
|
|
566
646
|
error: null,
|
|
567
647
|
...fields
|
|
568
648
|
});
|
|
@@ -577,22 +657,27 @@ function createImporter(deps) {
|
|
|
577
657
|
phase,
|
|
578
658
|
done,
|
|
579
659
|
total,
|
|
580
|
-
text: phase === "checking" ?
|
|
660
|
+
text: `${phase === "checking" ? "Checking" : "Importing"} ${done} of ${total}`
|
|
581
661
|
});
|
|
662
|
+
let upload;
|
|
582
663
|
try {
|
|
583
|
-
|
|
584
|
-
const
|
|
664
|
+
upload = uploaded(source);
|
|
665
|
+
const opened = await openProfile(
|
|
666
|
+
await sourceOf(deps, upload ?? source, context)
|
|
667
|
+
);
|
|
668
|
+
const preview = await previewOf(opened, deps.agents.state(), deps.providers);
|
|
585
669
|
if (options.preview) {
|
|
586
670
|
const writer = new Writer(deps.env, context, controller.signal);
|
|
587
671
|
await parse(opened, await verify(opened, writer, null, show));
|
|
588
672
|
progress.replace(BACKGROUND_CONTEXT, IDLE);
|
|
589
|
-
return result({ preview, text: `${preview.
|
|
673
|
+
return result({ preview, text: preview.text, line: `${preview.line}
|
|
590
674
|
` });
|
|
591
675
|
}
|
|
592
676
|
if (preview.already !== null && !options.again)
|
|
593
677
|
return result({
|
|
594
678
|
preview,
|
|
595
|
-
text: `Already imported on ${day(preview.already)}
|
|
679
|
+
text: `Already imported on ${day(preview.already)}.`,
|
|
680
|
+
line: `Already imported on ${day(preview.already)}; freezer import --again makes fresh copies.
|
|
596
681
|
`
|
|
597
682
|
});
|
|
598
683
|
const done = await importOpened(
|
|
@@ -609,22 +694,26 @@ function createImporter(deps) {
|
|
|
609
694
|
return result({ error: message });
|
|
610
695
|
} finally {
|
|
611
696
|
running = void 0;
|
|
697
|
+
if (upload) await deps.env.remove(upload.path, { force: true }, BACKGROUND_CONTEXT);
|
|
612
698
|
}
|
|
613
699
|
}
|
|
614
700
|
};
|
|
615
701
|
}
|
|
616
702
|
export {
|
|
617
703
|
IDLE,
|
|
618
|
-
IMPORTS_DIR,
|
|
619
704
|
LIMITS,
|
|
620
705
|
V6_KIND,
|
|
706
|
+
accountsOf,
|
|
707
|
+
accountsSplit,
|
|
621
708
|
createImporter,
|
|
622
709
|
day,
|
|
623
710
|
entriesOf,
|
|
711
|
+
listed,
|
|
624
712
|
openProfile,
|
|
625
713
|
previewOf,
|
|
626
714
|
textOf,
|
|
627
715
|
unitsOf,
|
|
716
|
+
uploadPath,
|
|
628
717
|
whereOf
|
|
629
718
|
};
|
|
630
719
|
//# sourceMappingURL=v6.js.map
|