@ai-ecoverse/slicc-agent 6.2.2 → 6.3.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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/freezer/v6-schema.ts"],
4
+ "sourcesContent": ["import { ImportError } from './zip.ts';\n\ntype Json = Record<string, unknown>;\n\nexport type Block =\n | { type: 'text'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'attachment-ref'; attachmentId: string };\n\nexport type V6Message = {\n id: string;\n sequence: number;\n role: 'user' | 'assistant' | 'tool-result';\n timestamp: string;\n content: Block[];\n toolCallId?: string;\n isError?: boolean;\n model?: { provider: string; id: string; api?: string };\n usage?: Json;\n stopReason?: string;\n};\n\nexport type V6Conversation = {\n id: string;\n kind: 'cone' | 'scoop';\n name: string;\n folder?: string;\n messages: V6Message[];\n};\n\nexport type V6Attachment = {\n id: string;\n path: string;\n originalName: string;\n sha256: string;\n byteLength: number;\n present: boolean;\n};\n\nexport type Transcript = {\n session: { id: string; title: string; updatedAt?: string; frozenAt?: string; createdAt?: string };\n conversations: V6Conversation[];\n attachments: V6Attachment[];\n};\n\nexport type ManifestScoop = {\n key: string;\n folder: string;\n name: string;\n conversation: string | null;\n};\n\nexport type ManifestCone = ManifestScoop & {\n transcript: string | null;\n memory: string | null;\n scoops: ManifestScoop[];\n};\n\nexport type Manifest = {\n exportId: string;\n producer: { slicc: string; origin: string };\n createdAt: string;\n part?: { index: number; of: number };\n cones: ManifestCone[];\n frozen: { id: string; title: string; frozenAt: string | null; transcript: string }[];\n skills: { name: string; path: string }[];\n sprinkles: { name: string; path: string }[];\n licks: string | null;\n gitconfig?: string | null;\n files: { path: string; size: number; sha256: string }[];\n skipped: { path: string; reason: string }[];\n flagged: { path: string; size: number; rule: string }[];\n providers: string[];\n mcp?: { name: string; url: string }[];\n secrets?: string[];\n};\n\nexport type Licks = {\n crontasks: { name: string; cron: string; targetFolder?: string | null; filter?: string }[];\n tasks: { groupFolder: string; prompt: string; scheduleType: string; scheduleValue: string }[];\n webhooks: { name: string; targetFolder?: string | null; filter?: string }[];\n};\n\nexport const FOLDER = /^[a-z0-9][a-z0-9._-]{0,63}$/;\nconst SHA = /^[0-9a-fA-F]{64}$/;\nconst REASONS = [\n 'canonical-agent-history-unavailable',\n 'tool-data-may-be-truncated',\n 'model-metadata-unavailable',\n 'scoop-history-unavailable',\n 'attachment-file-missing',\n 'attachment-association-unavailable',\n 'complete-snapshot-unavailable',\n];\nconst RULES = ['env', 'pem', 'git-credentials', 'npmrc', 'slicc-oauth'];\n\nconst obj = (value: unknown): value is Json =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\nconst str = (value: unknown): value is string => typeof value === 'string';\nconst full = (value: unknown): value is string => str(value) && value !== '';\nconst count = (value: unknown) => Number.isInteger(value) && (value as number) >= 0;\nconst amount = (value: unknown) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0;\nconst iso = (value: unknown) =>\n str(value) &&\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(value) &&\n !Number.isNaN(Date.parse(value));\nconst maybe = (value: unknown, check: (value: unknown) => boolean) =>\n value === undefined || value === null || check(value);\n\nfunction need(ok: boolean, error: string): asserts ok {\n if (!ok) throw new ImportError(error);\n}\n\nfunction oneOf(value: unknown, allowed: readonly string[], label: string) {\n need(str(value) && allowed.includes(value), `${label} must be one of ${allowed.join(', ')}`);\n}\n\nfunction list(value: unknown, label: string, check: (item: Json, path: string) => void) {\n need(Array.isArray(value), `${label} must be an array`);\n value.forEach((item, at) => {\n need(obj(item), `${label}[${at}] must be an object`);\n check(item, `${label}[${at}]`);\n });\n}\n\nfunction block(item: Json, path: string) {\n oneOf(item.type, ['text', 'tool-call', 'attachment-ref'], `${path}.type`);\n if (item.type === 'text') need(str(item.text), `${path}.text must be a string`);\n else if (item.type === 'tool-call')\n need(\n full(item.id) && full(item.name) && 'input' in item,\n `${path} needs an id, a name and an input`\n );\n else need(full(item.attachmentId), `${path}.attachmentId must be a non-empty string`);\n}\n\nconst TOKENS = ['input', 'output', 'cacheRead', 'cacheWrite', 'totalTokens'];\nconst COSTS = ['input', 'output', 'cacheRead', 'cacheWrite', 'total'];\n\nfunction message(item: Json, path: string) {\n oneOf(item.role, ['user', 'assistant', 'tool-result'], `${path}.role`);\n if (item.role === 'tool-result') need(full(item.toolCallId), `${path}.toolCallId must be set`);\n need(str(item.id), `${path}.id must be a string`);\n need(\n Number.isInteger(item.sequence) && (item.sequence as number) >= 1,\n `${path}.sequence must be a positive integer`\n );\n need(iso(item.timestamp), `${path}.timestamp must be an ISO 8601 date-time`);\n list(item.content, `${path}.content`, block);\n const usage = item.usage;\n if (usage === undefined) return;\n need(\n obj(usage) &&\n TOKENS.every((key) => count(usage[key])) &&\n obj(usage.cost) &&\n COSTS.every((key) => amount((usage.cost as Json)[key])),\n `${path}.usage is malformed`\n );\n}\n\nfunction attachment(item: Json, path: string) {\n const fields = [\n 'id',\n 'path',\n 'originalName',\n 'mimeType',\n 'sha256',\n 'sourceConversationId',\n 'sourceMessageId',\n ];\n need(\n fields.every((key) => str(item[key])),\n `${path} needs ${fields.join(', ')}`\n );\n need(\n typeof item.byteLength === 'number' && typeof item.present === 'boolean',\n `${path} needs byteLength and present`\n );\n oneOf(item.handling, ['text-redacted', 'binary-unchanged'], `${path}.handling`);\n if (item.present)\n need(\n item.path !== '' &&\n SHA.test(item.sha256 as string) &&\n count(item.byteLength) &&\n item.missingReason === undefined,\n `${path} is present but malformed`\n );\n else {\n need(\n item.path === '' && item.sha256 === '' && item.byteLength === 0,\n `${path} is missing but has a file`\n );\n oneOf(item.missingReason, REASONS.slice(4, 6), `${path}.missingReason`);\n }\n}\n\nfunction redaction(item: Json, path: string) {\n need(str(item.id) && str(item.category) && obj(item.target), `${path} is malformed`);\n oneOf(\n item.detector,\n ['known-secret', 'credential-pattern', 'pre-obfuscated'],\n `${path}.detector`\n );\n const target = item.target as Json;\n oneOf(target.kind, ['json', 'attachment'], `${path}.target.kind`);\n if (target.kind === 'json')\n need(\n str(target.pointer) && (target.pointer === '' || target.pointer.startsWith('/')),\n `${path}.target.pointer is not a JSON pointer`\n );\n else need(full(target.attachmentId), `${path}.target.attachmentId must be set`);\n}\n\nfunction sections(doc: Json) {\n need(doc.schemaVersion === 1, 'schemaVersion must equal 1');\n const exported = doc.export;\n need(\n obj(exported) &&\n str(exported.id) &&\n str(exported.generatedAt) &&\n exported.format === 'slicc-transcript' &&\n obj(exported.producer) &&\n exported.producer.application === 'slicc' &&\n str(exported.producer.version),\n 'export is malformed'\n );\n const session = doc.session;\n need(\n obj(session) && str(session.id) && str(session.title) && obj(session.completeness),\n 'session is malformed'\n );\n oneOf(session.state, ['active', 'frozen'], 'session.state');\n const completeness = session.completeness as Json;\n oneOf(completeness.status, ['complete', 'partial'], 'session.completeness.status');\n need(\n Array.isArray(completeness.missing) &&\n completeness.missing.every((reason) => REASONS.includes(reason)),\n 'session.completeness.missing is malformed'\n );\n const privacy = doc.privacy;\n need(\n obj(privacy) &&\n privacy.reasoningExcluded === true &&\n count(privacy.excludedReasoningBlocks) &&\n privacy.binaryAttachments === 'included-unchanged' &&\n obj(privacy.redactionCounts),\n 'privacy is malformed'\n );\n list(privacy.redactions, 'privacy.redactions', redaction);\n list(doc.conversations, 'conversations', (item, path) => {\n need(str(item.id) && str(item.name), `${path} needs an id and a name`);\n oneOf(item.kind, ['cone', 'scoop'], `${path}.kind`);\n list(item.messages, `${path}.messages`, message);\n });\n list(doc.delegations, 'delegations', (item, path) =>\n need(str(item.sourceConversationId) && str(item.targetConversationId), `${path} is malformed`)\n );\n list(doc.attachments, 'attachments', attachment);\n}\n\nfunction relations(doc: Json) {\n const conversations = new Map<string, { messages: Set<string>; calls: Set<string> }>();\n const messages = new Set<string>();\n const calls = new Set<string>();\n const results: string[] = [];\n const refs: string[] = [];\n for (const conversation of doc.conversations as Json[]) {\n const id = conversation.id as string;\n need(!conversations.has(id), `duplicate conversation id \"${id}\"`);\n const own = { messages: new Set<string>(), calls: new Set<string>() };\n conversations.set(id, own);\n let previous = 0;\n for (const item of conversation.messages as V6Message[]) {\n need(!messages.has(item.id), `duplicate message id \"${item.id}\"`);\n need(\n item.sequence > previous,\n `sequences in conversation \"${id}\" must be strictly increasing`\n );\n previous = item.sequence;\n messages.add(item.id);\n own.messages.add(item.id);\n if (item.role === 'tool-result') results.push(item.toolCallId as string);\n for (const part of item.content)\n if (part.type === 'tool-call') {\n calls.add(part.id);\n own.calls.add(part.id);\n } else if (part.type === 'attachment-ref') refs.push(part.attachmentId);\n }\n }\n for (const id of results)\n need(calls.has(id), `tool-result message references unknown toolCallId \"${id}\"`);\n const attached = new Set((doc.attachments as Json[]).map((item) => item.id as string));\n for (const id of refs)\n need(attached.has(id), `attachment-ref references unknown attachmentId \"${id}\"`);\n for (const item of doc.attachments as Json[])\n need(\n conversations\n .get(item.sourceConversationId as string)\n ?.messages.has(item.sourceMessageId as string) === true,\n `attachment ${item.id as string} comes from an unknown message`\n );\n for (const item of doc.delegations as Json[]) {\n const source = conversations.get(item.sourceConversationId as string);\n need(\n source !== undefined && conversations.has(item.targetConversationId as string),\n 'a delegation names an unknown conversation'\n );\n need(\n !full(item.toolCallId) || source.calls.has(item.toolCallId),\n 'a delegation names an unknown tool call'\n );\n }\n for (const item of (doc.privacy as Json).redactions as Json[]) {\n const target = item.target as Json;\n need(\n target.kind !== 'attachment' || attached.has(target.attachmentId as string),\n 'a redaction names an unknown attachment'\n );\n }\n}\n\nexport function checkTranscript(value: unknown): Transcript {\n need(obj(value), 'document must be a non-null object');\n sections(value);\n relations(value);\n return value as unknown as Transcript;\n}\n\nfunction named(item: Json, path: string) {\n need(\n str(item.key) && str(item.name) && FOLDER.test(String(item.folder)),\n `${path} needs a key, a name and a folder`\n );\n need(maybe(item.conversation, str), `${path}.conversation must be a string`);\n}\n\nfunction manifestLists(value: Json) {\n list(value.cones, 'cones', (cone, path) => {\n named(cone, path);\n need(maybe(cone.transcript, str) && maybe(cone.memory, str), `${path} is malformed`);\n list(cone.scoops, `${path}.scoops`, named);\n });\n list(value.frozen, 'frozen', (item, path) =>\n need(\n str(item.id) && str(item.title) && str(item.transcript) && maybe(item.frozenAt, iso),\n `${path} is malformed`\n )\n );\n for (const key of ['skills', 'sprinkles'])\n list(value[key], key, (item, path) =>\n need(str(item.name) && str(item.path), `${path} is malformed`)\n );\n list(value.files, 'files', (item, path) =>\n need(\n str(item.path) && count(item.size) && SHA.test(String(item.sha256)),\n `${path} is malformed`\n )\n );\n list(value.skipped, 'skipped', (item, path) =>\n need(str(item.path) && str(item.reason), `${path} is malformed`)\n );\n list(value.flagged, 'flagged', (item, path) => {\n need(str(item.path) && count(item.size), `${path} is malformed`);\n oneOf(item.rule, RULES, `${path}.rule`);\n });\n list(value.mcp ?? [], 'mcp', (item, path) =>\n need(str(item.name) && str(item.url), `${path} is malformed`)\n );\n for (const key of ['providers', 'secrets'])\n need(\n Array.isArray(value[key] ?? []) && ((value[key] as unknown[] | undefined) ?? []).every(str),\n `${key} must be strings`\n );\n}\n\nexport function checkManifest(value: unknown): Manifest {\n need(obj(value) && value.format === 'slicc-profile', 'this is not a SLICC 6 profile export');\n need(\n !(typeof value.version === 'number' && value.version > 1),\n 'This export is from a newer SLICC 6; update seven.'\n );\n need(value.version === 1, 'the export version must be 1');\n need(\n str(value.exportId) && /^[A-Za-z0-9-]{1,64}$/.test(value.exportId),\n 'exportId must be a UUID'\n );\n need(\n obj(value.producer) && str(value.producer.slicc) && str(value.producer.origin),\n 'producer is malformed'\n );\n need(\n iso(value.createdAt) && maybe(value.licks, str) && maybe(value.gitconfig, str),\n 'createdAt, licks or gitconfig is malformed'\n );\n const part = value.part as Json | undefined;\n need(\n part === undefined ||\n (obj(part) &&\n count(part.index) &&\n (part.index as number) >= 1 &&\n (part.index as number) <= (part.of as number)),\n 'part is malformed'\n );\n manifestLists(value);\n const folders = (value.cones as Manifest['cones']).flatMap((cone) => [\n `cone:${cone.folder}`,\n ...cone.scoops.map((scoop) => `scoop:${scoop.folder}`),\n ]);\n need(new Set(folders).size === folders.length, 'two cones or two scoops share a folder');\n return value as unknown as Manifest;\n}\n\nconst optional = (value: unknown) => value === undefined || str(value);\nconst target = (value: unknown) => maybe(value, (folder) => FOLDER.test(String(folder)));\n\nexport function checkLicks(value: unknown): Licks {\n need(obj(value), 'licks.json must be an object');\n list(value.crontasks, 'crontasks', (item, path) =>\n need(\n str(item.name) && str(item.cron) && target(item.targetFolder) && optional(item.filter),\n `${path} is malformed`\n )\n );\n list(value.tasks, 'tasks', (item, path) => {\n need(\n str(item.groupFolder) && str(item.prompt) && str(item.scheduleValue),\n `${path} is malformed`\n );\n oneOf(item.scheduleType, ['cron', 'interval', 'once'], `${path}.scheduleType`);\n });\n list(value.webhooks, 'webhooks', (item, path) =>\n need(\n str(item.name) && target(item.targetFolder) && optional(item.filter),\n `${path} is malformed`\n )\n );\n return value as unknown as Licks;\n}\n\nexport function lickName(name: string, fallback: string): string {\n const clean = name\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/^[^a-z0-9]+/, '')\n .slice(0, 64);\n return clean || fallback;\n}\n\nexport function skillName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/-{2,}/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64)\n .replace(/-+$/, '');\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["target"]
7
+ }
@@ -0,0 +1,249 @@
1
+ import {
2
+ checkLicks,
3
+ checkManifest,
4
+ checkTranscript
5
+ } from "./v6-schema.js";
6
+ import { ImportError, isLink, readEntry, zipEntries } from "./zip.js";
7
+ const MiB = 1024 ** 2;
8
+ const LIMITS = {
9
+ archive: 2048 * MiB,
10
+ entries: 6e4,
11
+ file: 256 * MiB,
12
+ transcript: 64 * MiB,
13
+ total: 4096 * MiB,
14
+ ratio: 200
15
+ };
16
+ const day = (at) => new Date(at).toISOString().slice(0, 10);
17
+ const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
18
+ const decoder = new TextDecoder("utf-8", { fatal: true });
19
+ function textOf(bytes, name) {
20
+ try {
21
+ return decoder.decode(bytes);
22
+ } catch {
23
+ throw new ImportError(`${name} is not UTF-8 text`);
24
+ }
25
+ }
26
+ function json(bytes, name) {
27
+ const text = textOf(bytes, name);
28
+ try {
29
+ return JSON.parse(text);
30
+ } catch {
31
+ throw new ImportError(`${name} is not JSON`);
32
+ }
33
+ }
34
+ const safe = (name) => !/^\/|^[A-Za-z]:|\\|\0/.test(name) && name.split("/").every((part) => part !== "" && part !== "." && part !== "..");
35
+ const HOOKS = /(^|\/)\.git\/hooks\//;
36
+ function whereOf(manifest, name) {
37
+ const where = placeOf(manifest, name);
38
+ const tree = where.kind === "cone" || where.kind === "scoop" || where.kind === "home-user";
39
+ return tree && HOOKS.test(name) ? { kind: "skip" } : where;
40
+ }
41
+ function placeOf(manifest, name) {
42
+ const [top = "", second = "", ...more] = name.split("/");
43
+ const rest = more.join("/");
44
+ if (["transcripts", "frozen", "memory"].includes(top) || [manifest.licks, manifest.gitconfig].includes(name))
45
+ return { kind: "data" };
46
+ if (top === "attachments" && /^[0-9a-f]{64}$/.test(second) && !rest)
47
+ return { kind: "attachment" };
48
+ if (top === "files" && second === "home-user" && rest)
49
+ return { kind: "home-user", folder: "", rest };
50
+ const folder = more[0];
51
+ const inner = more.slice(1).join("/");
52
+ if (top === "files" && second === "cones" && inner && manifest.cones.some((cone) => cone.folder === folder))
53
+ return { kind: "cone", folder, rest: inner };
54
+ const scoops = manifest.cones.flatMap((cone) => cone.scoops);
55
+ if (top === "files" && second === "scoops" && inner && scoops.some((scoop) => scoop.folder === folder))
56
+ return { kind: "scoop", folder, rest: inner };
57
+ const listed = top === "skills" ? manifest.skills : top === "sprinkles" ? manifest.sprinkles : [];
58
+ if (rest && listed.some((item) => item.name === second))
59
+ return { kind: top === "skills" ? "skill" : "sprinkle", folder: second, rest };
60
+ throw new ImportError(`${name} has no place in a SLICC 6 profile`);
61
+ }
62
+ function scan(list, archive) {
63
+ const entries = /* @__PURE__ */ new Map();
64
+ let total = 0;
65
+ for (const entry of list) {
66
+ if (!safe(entry.name))
67
+ throw new ImportError(`refused the entry name ${JSON.stringify(entry.name)}`);
68
+ if (isLink(entry)) throw new ImportError(`${entry.name} is a symlink`);
69
+ if (entries.has(entry.name)) throw new ImportError(`${entry.name} is in the zip twice`);
70
+ const big = entry === list[0] || /^(transcripts|frozen)\//.test(entry.name);
71
+ const cap = big ? LIMITS.transcript : LIMITS.file;
72
+ if (entry.size > cap) throw new ImportError(`${entry.name} is over ${cap / MiB} MiB`);
73
+ total += entry.size;
74
+ entries.set(entry.name, entry);
75
+ }
76
+ if (total > LIMITS.total) throw new ImportError("the files add up to over 4 GiB");
77
+ if (total > LIMITS.ratio * archive)
78
+ throw new ImportError("the zip expands over 200:1, like a zip bomb");
79
+ return entries;
80
+ }
81
+ async function openProfile(bytes) {
82
+ if (bytes.length > LIMITS.archive) throw new ImportError("the zip is over 2 GiB");
83
+ const list = zipEntries(bytes);
84
+ if (list.length > LIMITS.entries)
85
+ throw new ImportError(`the zip has over ${LIMITS.entries} entries`);
86
+ if (list[0]?.name !== "manifest.json")
87
+ throw new ImportError("manifest.json must be the first entry");
88
+ const entries = scan(list, bytes.length);
89
+ const manifest = checkManifest(json(await readEntry(bytes, list[0]), "manifest.json"));
90
+ const listed = /* @__PURE__ */ new Set();
91
+ for (const file of manifest.files) {
92
+ if (entries.get(file.path)?.size !== file.size || file.path === "manifest.json")
93
+ throw new ImportError(
94
+ `${file.path} is in the manifest but not in the zip, or not at that size`
95
+ );
96
+ listed.add(file.path);
97
+ }
98
+ for (const name of entries.keys()) {
99
+ if (name === "manifest.json") continue;
100
+ if (!listed.has(name)) throw new ImportError(`${name} is not listed in the manifest`);
101
+ whereOf(manifest, name);
102
+ }
103
+ const { exportId, part } = manifest;
104
+ return { manifest, entries, bytes, key: part ? `${exportId}#${part.index}` : exportId };
105
+ }
106
+ const noLicks = { crontasks: [], tasks: [], webhooks: [] };
107
+ async function licksIn(opened, data) {
108
+ const name = opened.manifest.licks;
109
+ if (!name) return noLicks;
110
+ const entry = opened.entries.get(name);
111
+ if (!entry) throw new ImportError(`${name} is named in the manifest but not in the zip`);
112
+ return checkLicks(json(data?.get(name) ?? await readEntry(opened.bytes, entry), name));
113
+ }
114
+ const UNITS = [
115
+ [1e9, "GB"],
116
+ [1e6, "MB"],
117
+ [1e3, "KB"]
118
+ ];
119
+ async function previewOf(opened) {
120
+ const { manifest } = opened;
121
+ const licks = await licksIn(opened);
122
+ const bytes = manifest.files.filter((file) => file.path.startsWith("files/")).reduce((sum, file) => sum + file.size, 0);
123
+ const scoops = manifest.cones.reduce((sum, cone) => sum + cone.scoops.length, 0);
124
+ const mounts = manifest.skipped.filter((item) => item.reason === "mount").map((item) => item.path.replace(/^\/mnt\//, ""));
125
+ const crons = licks.crontasks.length + licks.tasks.filter((task) => task.scheduleType === "cron").length;
126
+ const host = URL.canParse(manifest.producer.origin) ? new URL(manifest.producer.origin).host : manifest.producer.origin;
127
+ const [at, unit] = UNITS.find(([at2]) => bytes >= at2) ?? UNITS[2];
128
+ const size = `${(bytes / at).toFixed(1)} ${unit}`;
129
+ const parts = [
130
+ plural(manifest.cones.length, "cone"),
131
+ plural(scoops, "scoop"),
132
+ plural(manifest.frozen.length, "frozen session"),
133
+ `${size} of files`,
134
+ `${plural(manifest.skills.length, "skill")} (for every agent)`,
135
+ `${plural(crons, "cron")} (imported off)`,
136
+ `${plural(licks.webhooks.length, "webhook")} (new URLs needed)`,
137
+ `${plural(manifest.flagged.length, "credential-like file")} to review`
138
+ ];
139
+ return {
140
+ exportId: manifest.exportId,
141
+ origin: manifest.producer.origin,
142
+ createdAt: manifest.createdAt,
143
+ cones: manifest.cones.length,
144
+ scoops,
145
+ frozen: manifest.frozen.length,
146
+ bytes,
147
+ skills: manifest.skills.map((skill) => skill.name),
148
+ crons,
149
+ webhooks: licks.webhooks.length,
150
+ flagged: manifest.flagged.length,
151
+ providers: manifest.providers,
152
+ mounts,
153
+ text: `SLICC 6 profile from ${host}, exported ${day(manifest.createdAt)}: ${parts.join(", ")}. Re-enter in Settings: ${manifest.providers.join(", ") || "nothing"}. Not imported: ${plural(mounts.length, "mount")}.`
154
+ };
155
+ }
156
+ async function sha256(bytes) {
157
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
158
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
159
+ }
160
+ async function verify(opened) {
161
+ const data = /* @__PURE__ */ new Map();
162
+ for (const file of opened.manifest.files) {
163
+ const bytes = await readEntry(opened.bytes, opened.entries.get(file.path));
164
+ if (await sha256(bytes) !== file.sha256.toLowerCase())
165
+ throw new ImportError(`${file.path} doesn't match its sha256`);
166
+ if (whereOf(opened.manifest, file.path).kind === "data") data.set(file.path, bytes);
167
+ }
168
+ return data;
169
+ }
170
+ function transcriptIn(data, name) {
171
+ if (!name) return void 0;
172
+ const bytes = data.get(name);
173
+ if (!bytes) throw new ImportError(`${name} is named in the manifest but not in the zip`);
174
+ const value = json(bytes, name);
175
+ try {
176
+ return checkTranscript(value);
177
+ } catch (error) {
178
+ throw new ImportError(`${name}: ${error.message}`);
179
+ }
180
+ }
181
+ function present(data, name) {
182
+ if (name && !data.has(name))
183
+ throw new ImportError(`${name} is named in the manifest but not in the zip`);
184
+ }
185
+ function attached(manifest, name, doc) {
186
+ const files = new Map(manifest.files.map((file) => [file.path, file]));
187
+ for (const item of doc.attachments) {
188
+ const file = files.get(item.path);
189
+ const same = file !== void 0 && file.size === item.byteLength && file.sha256.toLowerCase() === item.sha256.toLowerCase();
190
+ if (item.present && !(item.path.startsWith("attachments/") && same))
191
+ throw new ImportError(`${name}: attachment ${item.id} doesn't match ${item.path}`);
192
+ }
193
+ }
194
+ function related(manifest, data) {
195
+ const transcripts = [
196
+ ...manifest.cones.map((cone) => cone.transcript),
197
+ ...manifest.frozen.map((item) => item.transcript)
198
+ ].filter((name) => name !== null);
199
+ const twice = transcripts.find((name, at) => transcripts.indexOf(name) !== at);
200
+ if (twice) throw new ImportError(`${twice} is named twice in the manifest`);
201
+ for (const name of [...manifest.cones.map((cone) => cone.memory), manifest.gitconfig])
202
+ present(data, name);
203
+ for (const [kind, items] of [
204
+ ["skills", manifest.skills],
205
+ ["sprinkles", manifest.sprinkles]
206
+ ])
207
+ for (const item of items)
208
+ if (item.path !== `${kind}/${item.name}/` || !manifest.files.some((file) => file.path.startsWith(item.path)))
209
+ throw new ImportError(`${item.path} is named in the manifest but has no files`);
210
+ for (const cone of manifest.cones) {
211
+ const doc = transcriptIn(data, cone.transcript);
212
+ const ids = new Set(doc?.conversations.map((item) => item.id));
213
+ for (const id of [cone.conversation, ...cone.scoops.map((scoop) => scoop.conversation)])
214
+ if (id !== null && !ids.has(id))
215
+ throw new ImportError(`${cone.transcript ?? cone.folder} has no conversation ${id}`);
216
+ }
217
+ for (const name of transcripts) attached(manifest, name, transcriptIn(data, name));
218
+ }
219
+ async function checked(opened, data) {
220
+ for (const [name, bytes] of data)
221
+ if (name.startsWith("memory/") || name === opened.manifest.gitconfig) textOf(bytes, name);
222
+ related(opened.manifest, data);
223
+ await licksIn(opened, data);
224
+ }
225
+ async function bytesOf(env, source, context) {
226
+ if ("bytes" in source) return source.bytes;
227
+ const info = await env.fileInfo(source.path, context);
228
+ if (!info.ok) throw new ImportError(`can't read ${source.path}: ${info.error.message}`);
229
+ if (info.value.size > LIMITS.archive) throw new ImportError("the zip is over 2 GiB");
230
+ const read = await env.readBinaryFile(source.path, context);
231
+ if (!read.ok) throw new ImportError(`can't read ${source.path}: ${read.error.message}`);
232
+ return read.value;
233
+ }
234
+ async function checkProfile(env, source, context) {
235
+ const opened = await openProfile(await bytesOf(env, source, context));
236
+ const preview = await previewOf(opened);
237
+ await checked(opened, await verify(opened));
238
+ return preview;
239
+ }
240
+ export {
241
+ LIMITS,
242
+ checkProfile,
243
+ day,
244
+ openProfile,
245
+ previewOf,
246
+ textOf,
247
+ whereOf
248
+ };
249
+ //# sourceMappingURL=v6.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/freezer/v6.ts"],
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport {\n checkLicks,\n checkManifest,\n checkTranscript,\n type Licks,\n type Manifest,\n type Transcript,\n} from './v6-schema.ts';\nimport { ImportError, isLink, readEntry, type ZipEntry, zipEntries } from './zip.ts';\n\nconst MiB = 1024 ** 2;\nexport const LIMITS = {\n archive: 2048 * MiB,\n entries: 60_000,\n file: 256 * MiB,\n transcript: 64 * MiB,\n total: 4096 * MiB,\n ratio: 200,\n};\n\nexport interface ImportPreview {\n exportId: string;\n origin: string;\n createdAt: string;\n cones: number;\n scoops: number;\n frozen: number;\n bytes: number;\n skills: string[];\n crons: number;\n webhooks: number;\n flagged: number;\n providers: string[];\n mounts: string[];\n text: string;\n}\n\nexport type ImportSource = { path: string } | { bytes: Uint8Array };\n\nexport type Opened = {\n manifest: Manifest;\n entries: Map<string, ZipEntry>;\n bytes: Uint8Array;\n key: string;\n};\n\nexport type Where =\n | { kind: 'data' | 'skip' | 'attachment' }\n | { kind: 'cone' | 'scoop' | 'home-user' | 'skill' | 'sprinkle'; folder: string; rest: string };\n\nexport const day = (at: number | string) => new Date(at).toISOString().slice(0, 10);\nconst plural = (count: number, word: string) => `${count} ${word}${count === 1 ? '' : 's'}`;\nconst decoder = new TextDecoder('utf-8', { fatal: true });\n\nexport function textOf(bytes: Uint8Array, name: string): string {\n try {\n return decoder.decode(bytes);\n } catch {\n throw new ImportError(`${name} is not UTF-8 text`);\n }\n}\n\nfunction json(bytes: Uint8Array, name: string): unknown {\n const text = textOf(bytes, name);\n try {\n return JSON.parse(text);\n } catch {\n throw new ImportError(`${name} is not JSON`);\n }\n}\n\nconst safe = (name: string) =>\n !/^\\/|^[A-Za-z]:|\\\\|\\0/.test(name) &&\n name.split('/').every((part) => part !== '' && part !== '.' && part !== '..');\n\nconst HOOKS = /(^|\\/)\\.git\\/hooks\\//;\n\nexport function whereOf(manifest: Manifest, name: string): Where {\n const where = placeOf(manifest, name);\n const tree = where.kind === 'cone' || where.kind === 'scoop' || where.kind === 'home-user';\n return tree && HOOKS.test(name) ? { kind: 'skip' } : where;\n}\n\nfunction placeOf(manifest: Manifest, name: string): Where {\n const [top = '', second = '', ...more] = name.split('/');\n const rest = more.join('/');\n if (\n ['transcripts', 'frozen', 'memory'].includes(top) ||\n [manifest.licks, manifest.gitconfig].includes(name)\n )\n return { kind: 'data' };\n if (top === 'attachments' && /^[0-9a-f]{64}$/.test(second) && !rest)\n return { kind: 'attachment' };\n if (top === 'files' && second === 'home-user' && rest)\n return { kind: 'home-user', folder: '', rest };\n const folder = more[0] as string;\n const inner = more.slice(1).join('/');\n if (\n top === 'files' &&\n second === 'cones' &&\n inner &&\n manifest.cones.some((cone) => cone.folder === folder)\n )\n return { kind: 'cone', folder, rest: inner };\n const scoops = manifest.cones.flatMap((cone) => cone.scoops);\n if (\n top === 'files' &&\n second === 'scoops' &&\n inner &&\n scoops.some((scoop) => scoop.folder === folder)\n )\n return { kind: 'scoop', folder, rest: inner };\n const listed = top === 'skills' ? manifest.skills : top === 'sprinkles' ? manifest.sprinkles : [];\n if (rest && listed.some((item) => item.name === second))\n return { kind: top === 'skills' ? 'skill' : 'sprinkle', folder: second, rest };\n throw new ImportError(`${name} has no place in a SLICC 6 profile`);\n}\n\nfunction scan(list: readonly ZipEntry[], archive: number): Map<string, ZipEntry> {\n const entries = new Map<string, ZipEntry>();\n let total = 0;\n for (const entry of list) {\n if (!safe(entry.name))\n throw new ImportError(`refused the entry name ${JSON.stringify(entry.name)}`);\n if (isLink(entry)) throw new ImportError(`${entry.name} is a symlink`);\n if (entries.has(entry.name)) throw new ImportError(`${entry.name} is in the zip twice`);\n const big = entry === list[0] || /^(transcripts|frozen)\\//.test(entry.name);\n const cap = big ? LIMITS.transcript : LIMITS.file;\n if (entry.size > cap) throw new ImportError(`${entry.name} is over ${cap / MiB} MiB`);\n total += entry.size;\n entries.set(entry.name, entry);\n }\n if (total > LIMITS.total) throw new ImportError('the files add up to over 4 GiB');\n if (total > LIMITS.ratio * archive)\n throw new ImportError('the zip expands over 200:1, like a zip bomb');\n return entries;\n}\n\nexport async function openProfile(bytes: Uint8Array): Promise<Opened> {\n if (bytes.length > LIMITS.archive) throw new ImportError('the zip is over 2 GiB');\n const list = zipEntries(bytes);\n if (list.length > LIMITS.entries)\n throw new ImportError(`the zip has over ${LIMITS.entries} entries`);\n if (list[0]?.name !== 'manifest.json')\n throw new ImportError('manifest.json must be the first entry');\n const entries = scan(list, bytes.length);\n const manifest = checkManifest(json(await readEntry(bytes, list[0]), 'manifest.json'));\n const listed = new Set<string>();\n for (const file of manifest.files) {\n if (entries.get(file.path)?.size !== file.size || file.path === 'manifest.json')\n throw new ImportError(\n `${file.path} is in the manifest but not in the zip, or not at that size`\n );\n listed.add(file.path);\n }\n for (const name of entries.keys()) {\n if (name === 'manifest.json') continue;\n if (!listed.has(name)) throw new ImportError(`${name} is not listed in the manifest`);\n whereOf(manifest, name);\n }\n const { exportId, part } = manifest;\n return { manifest, entries, bytes, key: part ? `${exportId}#${part.index}` : exportId };\n}\n\nconst noLicks: Licks = { crontasks: [], tasks: [], webhooks: [] };\n\nasync function licksIn(opened: Opened, data?: Map<string, Uint8Array>): Promise<Licks> {\n const name = opened.manifest.licks;\n if (!name) return noLicks;\n const entry = opened.entries.get(name);\n if (!entry) throw new ImportError(`${name} is named in the manifest but not in the zip`);\n return checkLicks(json(data?.get(name) ?? (await readEntry(opened.bytes, entry)), name));\n}\n\nconst UNITS = [\n [1e9, 'GB'],\n [1e6, 'MB'],\n [1e3, 'KB'],\n] as const;\n\nexport async function previewOf(opened: Opened): Promise<ImportPreview> {\n const { manifest } = opened;\n const licks = await licksIn(opened);\n const bytes = manifest.files\n .filter((file) => file.path.startsWith('files/'))\n .reduce((sum, file) => sum + file.size, 0);\n const scoops = manifest.cones.reduce((sum, cone) => sum + cone.scoops.length, 0);\n const mounts = manifest.skipped\n .filter((item) => item.reason === 'mount')\n .map((item) => item.path.replace(/^\\/mnt\\//, ''));\n const crons =\n licks.crontasks.length + licks.tasks.filter((task) => task.scheduleType === 'cron').length;\n const host = URL.canParse(manifest.producer.origin)\n ? new URL(manifest.producer.origin).host\n : manifest.producer.origin;\n const [at, unit] = UNITS.find(([at]) => bytes >= at) ?? (UNITS[2] as (typeof UNITS)[2]);\n const size = `${(bytes / at).toFixed(1)} ${unit}`;\n const parts = [\n plural(manifest.cones.length, 'cone'),\n plural(scoops, 'scoop'),\n plural(manifest.frozen.length, 'frozen session'),\n `${size} of files`,\n `${plural(manifest.skills.length, 'skill')} (for every agent)`,\n `${plural(crons, 'cron')} (imported off)`,\n `${plural(licks.webhooks.length, 'webhook')} (new URLs needed)`,\n `${plural(manifest.flagged.length, 'credential-like file')} to review`,\n ];\n return {\n exportId: manifest.exportId,\n origin: manifest.producer.origin,\n createdAt: manifest.createdAt,\n cones: manifest.cones.length,\n scoops,\n frozen: manifest.frozen.length,\n bytes,\n skills: manifest.skills.map((skill) => skill.name),\n crons,\n webhooks: licks.webhooks.length,\n flagged: manifest.flagged.length,\n providers: manifest.providers,\n mounts,\n text: `SLICC 6 profile from ${host}, exported ${day(manifest.createdAt)}: ${parts.join(', ')}. Re-enter in Settings: ${manifest.providers.join(', ') || 'nothing'}. Not imported: ${plural(mounts.length, 'mount')}.`,\n };\n}\n\nasync function sha256(bytes: Uint8Array): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', bytes as Uint8Array<ArrayBuffer>);\n return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nasync function verify(opened: Opened): Promise<Map<string, Uint8Array>> {\n const data = new Map<string, Uint8Array>();\n for (const file of opened.manifest.files) {\n const bytes = await readEntry(opened.bytes, opened.entries.get(file.path) as ZipEntry);\n if ((await sha256(bytes)) !== file.sha256.toLowerCase())\n throw new ImportError(`${file.path} doesn't match its sha256`);\n if (whereOf(opened.manifest, file.path).kind === 'data') data.set(file.path, bytes);\n }\n return data;\n}\n\nfunction transcriptIn(data: Map<string, Uint8Array>, name: string | null): Transcript | undefined {\n if (!name) return undefined;\n const bytes = data.get(name);\n if (!bytes) throw new ImportError(`${name} is named in the manifest but not in the zip`);\n const value = json(bytes, name);\n try {\n return checkTranscript(value);\n } catch (error) {\n throw new ImportError(`${name}: ${(error as Error).message}`);\n }\n}\n\nfunction present(data: Map<string, Uint8Array>, name: string | null | undefined) {\n if (name && !data.has(name))\n throw new ImportError(`${name} is named in the manifest but not in the zip`);\n}\n\nfunction attached(manifest: Manifest, name: string, doc: Transcript) {\n const files = new Map(manifest.files.map((file) => [file.path, file]));\n for (const item of doc.attachments) {\n const file = files.get(item.path);\n const same =\n file !== undefined &&\n file.size === item.byteLength &&\n file.sha256.toLowerCase() === item.sha256.toLowerCase();\n if (item.present && !(item.path.startsWith('attachments/') && same))\n throw new ImportError(`${name}: attachment ${item.id} doesn't match ${item.path}`);\n }\n}\n\nfunction related(manifest: Manifest, data: Map<string, Uint8Array>) {\n const transcripts = [\n ...manifest.cones.map((cone) => cone.transcript),\n ...manifest.frozen.map((item) => item.transcript),\n ].filter((name): name is string => name !== null);\n const twice = transcripts.find((name, at) => transcripts.indexOf(name) !== at);\n if (twice) throw new ImportError(`${twice} is named twice in the manifest`);\n for (const name of [...manifest.cones.map((cone) => cone.memory), manifest.gitconfig])\n present(data, name);\n for (const [kind, items] of [\n ['skills', manifest.skills],\n ['sprinkles', manifest.sprinkles],\n ] as const)\n for (const item of items)\n if (\n item.path !== `${kind}/${item.name}/` ||\n !manifest.files.some((file) => file.path.startsWith(item.path))\n )\n throw new ImportError(`${item.path} is named in the manifest but has no files`);\n for (const cone of manifest.cones) {\n const doc = transcriptIn(data, cone.transcript);\n const ids = new Set(doc?.conversations.map((item) => item.id));\n for (const id of [cone.conversation, ...cone.scoops.map((scoop) => scoop.conversation)])\n if (id !== null && !ids.has(id))\n throw new ImportError(`${cone.transcript ?? cone.folder} has no conversation ${id}`);\n }\n for (const name of transcripts) attached(manifest, name, transcriptIn(data, name) as Transcript);\n}\n\nasync function checked(opened: Opened, data: Map<string, Uint8Array>): Promise<void> {\n for (const [name, bytes] of data)\n if (name.startsWith('memory/') || name === opened.manifest.gitconfig) textOf(bytes, name);\n related(opened.manifest, data);\n await licksIn(opened, data);\n}\n\nasync function bytesOf(\n env: ExecutionEnv,\n source: ImportSource,\n context: Context\n): Promise<Uint8Array> {\n if ('bytes' in source) return source.bytes;\n const info = await env.fileInfo(source.path, context);\n if (!info.ok) throw new ImportError(`can't read ${source.path}: ${info.error.message}`);\n if (info.value.size > LIMITS.archive) throw new ImportError('the zip is over 2 GiB');\n const read = await env.readBinaryFile(source.path, context);\n if (!read.ok) throw new ImportError(`can't read ${source.path}: ${read.error.message}`);\n return read.value;\n}\n\nexport async function checkProfile(\n env: ExecutionEnv,\n source: ImportSource,\n context: Context\n): Promise<ImportPreview> {\n const opened = await openProfile(await bytesOf(env, source, context));\n const preview = await previewOf(opened);\n await checked(opened, await verify(opened));\n return preview;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["at"]
7
+ }
@@ -0,0 +1,89 @@
1
+ class ImportError extends Error {
2
+ }
3
+ const END = 101010256;
4
+ const CENTRAL = 33639248;
5
+ const LOCAL = 67324752;
6
+ const WIDE = 4294967295;
7
+ const UNIX = 3;
8
+ const damaged = (what) => new ImportError(`the zip is damaged: ${what}`);
9
+ function viewOf(bytes) {
10
+ return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
11
+ }
12
+ function endOf(bytes, view) {
13
+ for (let at = bytes.length - 22; at >= Math.max(0, bytes.length - 22 - 65535); at--)
14
+ if (view.getUint32(at, true) === END) return at;
15
+ throw new ImportError("this is not a zip file");
16
+ }
17
+ function zipEntries(bytes) {
18
+ const view = viewOf(bytes);
19
+ const end = endOf(bytes, view);
20
+ const count = view.getUint16(end + 10, true);
21
+ let at = view.getUint32(end + 16, true);
22
+ if (count === 65535 || at === WIDE) throw new ImportError("ZIP64 archives are not supported");
23
+ const names = new TextDecoder("utf-8", { fatal: true });
24
+ const out = [];
25
+ for (let n = 0; n < count; n++) {
26
+ if (at + 46 > end || view.getUint32(at, true) !== CENTRAL) throw damaged("a bad directory");
27
+ const length = view.getUint16(at + 28, true);
28
+ const skip = length + view.getUint16(at + 30, true) + view.getUint16(at + 32, true);
29
+ const entry = {
30
+ name: names.decode(bytes.subarray(at + 46, at + 46 + length)),
31
+ flags: view.getUint16(at + 8, true),
32
+ method: view.getUint16(at + 10, true),
33
+ compressed: view.getUint32(at + 20, true),
34
+ size: view.getUint32(at + 24, true),
35
+ offset: view.getUint32(at + 42, true),
36
+ mode: view.getUint16(at + 4, true) >> 8 === UNIX ? view.getUint32(at + 38, true) >>> 16 : 0
37
+ };
38
+ if (entry.size === WIDE || entry.compressed === WIDE || entry.offset === WIDE)
39
+ throw new ImportError("ZIP64 archives are not supported");
40
+ out.push(entry);
41
+ at += 46 + skip;
42
+ }
43
+ return out;
44
+ }
45
+ function isLink(entry) {
46
+ return (entry.mode & 61440) === 40960;
47
+ }
48
+ async function inflate(data, size, name) {
49
+ const stream = new Blob([data]).stream().pipeThrough(new DecompressionStream("deflate-raw"));
50
+ const reader = stream.getReader();
51
+ const out = new Uint8Array(size);
52
+ let filled = 0;
53
+ for (; ; ) {
54
+ const { done, value } = await reader.read().catch(() => {
55
+ throw damaged(`${name} does not inflate`);
56
+ });
57
+ if (done) break;
58
+ if (filled + value.length > size) {
59
+ await reader.cancel();
60
+ throw damaged(`${name} is bigger than it says`);
61
+ }
62
+ out.set(value, filled);
63
+ filled += value.length;
64
+ }
65
+ if (filled !== size) throw damaged(`${name} is smaller than it says`);
66
+ return out;
67
+ }
68
+ async function readEntry(bytes, entry) {
69
+ const view = viewOf(bytes);
70
+ if (entry.flags & 1) throw new ImportError(`${entry.name} is encrypted`);
71
+ if (entry.offset + 30 > bytes.length || view.getUint32(entry.offset, true) !== LOCAL)
72
+ throw damaged(`no local header for ${entry.name}`);
73
+ const start = entry.offset + 30 + view.getUint16(entry.offset + 26, true) + view.getUint16(entry.offset + 28, true);
74
+ if (start + entry.compressed > bytes.length) throw damaged(`${entry.name} is cut off`);
75
+ const data = bytes.subarray(start, start + entry.compressed);
76
+ if (entry.method === 0) {
77
+ if (entry.compressed !== entry.size) throw damaged(`${entry.name} has two sizes`);
78
+ return data;
79
+ }
80
+ if (entry.method !== 8) throw new ImportError(`${entry.name} uses compression ${entry.method}`);
81
+ return inflate(data, entry.size, entry.name);
82
+ }
83
+ export {
84
+ ImportError,
85
+ isLink,
86
+ readEntry,
87
+ zipEntries
88
+ };
89
+ //# sourceMappingURL=zip.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/freezer/zip.ts"],
4
+ "sourcesContent": ["export class ImportError extends Error {}\n\nexport type ZipEntry = {\n name: string;\n method: number;\n flags: number;\n compressed: number;\n size: number;\n offset: number;\n mode: number;\n};\n\nconst END = 0x06054b50;\nconst CENTRAL = 0x02014b50;\nconst LOCAL = 0x04034b50;\nconst WIDE = 0xffffffff;\nconst UNIX = 3;\n\nconst damaged = (what: string) => new ImportError(`the zip is damaged: ${what}`);\n\nfunction viewOf(bytes: Uint8Array): DataView {\n return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n}\n\nfunction endOf(bytes: Uint8Array, view: DataView): number {\n for (let at = bytes.length - 22; at >= Math.max(0, bytes.length - 22 - 0xffff); at--)\n if (view.getUint32(at, true) === END) return at;\n throw new ImportError('this is not a zip file');\n}\n\nexport function zipEntries(bytes: Uint8Array): ZipEntry[] {\n const view = viewOf(bytes);\n const end = endOf(bytes, view);\n const count = view.getUint16(end + 10, true);\n let at = view.getUint32(end + 16, true);\n if (count === 0xffff || at === WIDE) throw new ImportError('ZIP64 archives are not supported');\n const names = new TextDecoder('utf-8', { fatal: true });\n const out: ZipEntry[] = [];\n for (let n = 0; n < count; n++) {\n if (at + 46 > end || view.getUint32(at, true) !== CENTRAL) throw damaged('a bad directory');\n const length = view.getUint16(at + 28, true);\n const skip = length + view.getUint16(at + 30, true) + view.getUint16(at + 32, true);\n const entry = {\n name: names.decode(bytes.subarray(at + 46, at + 46 + length)),\n flags: view.getUint16(at + 8, true),\n method: view.getUint16(at + 10, true),\n compressed: view.getUint32(at + 20, true),\n size: view.getUint32(at + 24, true),\n offset: view.getUint32(at + 42, true),\n mode: view.getUint16(at + 4, true) >> 8 === UNIX ? view.getUint32(at + 38, true) >>> 16 : 0,\n };\n if (entry.size === WIDE || entry.compressed === WIDE || entry.offset === WIDE)\n throw new ImportError('ZIP64 archives are not supported');\n out.push(entry);\n at += 46 + skip;\n }\n return out;\n}\n\nexport function isLink(entry: ZipEntry): boolean {\n return (entry.mode & 0o170000) === 0o120000;\n}\n\nasync function inflate(data: Uint8Array, size: number, name: string): Promise<Uint8Array> {\n const stream = new Blob([data as Uint8Array<ArrayBuffer>])\n .stream()\n .pipeThrough(new DecompressionStream('deflate-raw'));\n const reader = stream.getReader();\n const out = new Uint8Array(size);\n let filled = 0;\n for (;;) {\n const { done, value } = await reader.read().catch(() => {\n throw damaged(`${name} does not inflate`);\n });\n if (done) break;\n if (filled + value.length > size) {\n await reader.cancel();\n throw damaged(`${name} is bigger than it says`);\n }\n out.set(value, filled);\n filled += value.length;\n }\n if (filled !== size) throw damaged(`${name} is smaller than it says`);\n return out;\n}\n\nexport async function readEntry(bytes: Uint8Array, entry: ZipEntry): Promise<Uint8Array> {\n const view = viewOf(bytes);\n if (entry.flags & 1) throw new ImportError(`${entry.name} is encrypted`);\n if (entry.offset + 30 > bytes.length || view.getUint32(entry.offset, true) !== LOCAL)\n throw damaged(`no local header for ${entry.name}`);\n const start =\n entry.offset +\n 30 +\n view.getUint16(entry.offset + 26, true) +\n view.getUint16(entry.offset + 28, true);\n if (start + entry.compressed > bytes.length) throw damaged(`${entry.name} is cut off`);\n const data = bytes.subarray(start, start + entry.compressed);\n if (entry.method === 0) {\n if (entry.compressed !== entry.size) throw damaged(`${entry.name} has two sizes`);\n return data;\n }\n if (entry.method !== 8) throw new ImportError(`${entry.name} uses compression ${entry.method}`);\n return inflate(data, entry.size, entry.name);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/main.js CHANGED
@@ -287,6 +287,19 @@ function setupTitles(registry, models, home, at, opened) {
287
287
  registry.install(titles.extension);
288
288
  return { titleFn, cancel: titles.cancel, label: labeler(models, settings) };
289
289
  }
290
+ function freezerOf(agent, parts) {
291
+ const { home, memory, titles, people } = parts;
292
+ return attachFreezer({
293
+ harness: agent.harness,
294
+ agents: agent.agents,
295
+ env: home,
296
+ extract: memory.extract,
297
+ title: titles.titleFn,
298
+ cancelTitle: titles.cancel,
299
+ ...people ? { layout: people.layout } : {},
300
+ imports: { env: home }
301
+ });
302
+ }
290
303
  async function start(scope, ports, options, vault) {
291
304
  const { client, users, home, at, wiped } = await attached(ports, options);
292
305
  const ready = await prepare(scope, { client, home, vault }, options);
@@ -379,15 +392,7 @@ async function start(scope, ports, options, vault) {
379
392
  },
380
393
  BACKGROUND_CONTEXT
381
394
  );
382
- const freezer = attachFreezer({
383
- harness: agent.harness,
384
- agents: agent.agents,
385
- env: home,
386
- extract: memory.extract,
387
- title: titles.titleFn,
388
- cancelTitle: titles.cancel,
389
- ...people ? { layout: people.layout } : {}
390
- });
395
+ const freezer = freezerOf(agent, { home, memory, titles, people });
391
396
  licks.licks.guard(freezer.frozenTarget);
392
397
  const settings = await settingsOf(ready, mcpRuntime, { agent, vault, wiped });
393
398
  const pages = createPages({ claim: sprinkles.claim });