@ai-ecoverse/slicc-spectrum 1.108.4 → 1.108.6

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.
@@ -12,10 +12,60 @@ import {
12
12
  } from "./chunk-T5NHW4KC.js";
13
13
  import "./chunk-AXQFWXYZ.js";
14
14
 
15
+ // node_modules/@ai-ecoverse/slicc-kernel/dist/meta.js
16
+ var META_DB = "slicc-kernel";
17
+ function dirsChannel(metadata) {
18
+ return `slicc-kernel-dirs:${metadata}`;
19
+ }
20
+ function fsError(code, path) {
21
+ return Object.assign(new Error(`${code}: ${path}`), { code });
22
+ }
23
+ function normalizePath(path) {
24
+ const parts = [];
25
+ for (const part of path.split("/")) {
26
+ if (part === "" || part === ".") continue;
27
+ if (part === "..") parts.pop();
28
+ else parts.push(part);
29
+ }
30
+ return `/${parts.join("/")}`;
31
+ }
32
+ function absolute(list) {
33
+ return (list ?? []).map((path) => {
34
+ if (!String(path).startsWith("/")) throw fsError("EINVAL", `not an absolute path: ${path}`);
35
+ return normalizePath(String(path));
36
+ });
37
+ }
38
+ function postFsNotice(notice, options = {}) {
39
+ const paths = absolute(notice.paths);
40
+ const trees = absolute(notice.trees);
41
+ if (paths.length === 0 && trees.length === 0) return;
42
+ const channel = new BroadcastChannel(dirsChannel(options.metadata ?? META_DB));
43
+ try {
44
+ channel.postMessage({ paths, trees });
45
+ } finally {
46
+ channel.close();
47
+ }
48
+ }
49
+
15
50
  // src/kernel/files.ts
16
51
  function split(path) {
17
52
  return path.split("/").filter(Boolean);
18
53
  }
54
+ function child(dir, name) {
55
+ return dir === "/" ? `/${name}` : `${dir}/${name}`;
56
+ }
57
+ function parentOf(path) {
58
+ return path.slice(0, path.lastIndexOf("/")) || "/";
59
+ }
60
+ async function opened(changed, path, parent, open) {
61
+ try {
62
+ return await open(false);
63
+ } catch {
64
+ const made = await open(true);
65
+ changed.push(path, parent);
66
+ return made;
67
+ }
68
+ }
19
69
  function same(a, b) {
20
70
  return a?.kind === b.kind && a.size === b.size && a.modified === b.modified;
21
71
  }
@@ -24,6 +74,7 @@ var KernelFiles = class extends Emitter {
24
74
  #skip;
25
75
  #hide;
26
76
  #interval;
77
+ #metadata;
27
78
  #entries = /* @__PURE__ */ new Map();
28
79
  #scanned = false;
29
80
  #running = null;
@@ -32,9 +83,10 @@ var KernelFiles = class extends Emitter {
32
83
  #observer = null;
33
84
  #pending = null;
34
85
  #last = Number.NEGATIVE_INFINITY;
35
- constructor(root, { skip = [], hide = [], interval = 2e3 } = {}) {
86
+ constructor(root, { skip = [], hide = [], interval = 2e3, metadata } = {}) {
36
87
  super();
37
88
  this.#root = root;
89
+ this.#metadata = metadata;
38
90
  this.#skip = new Set(skip);
39
91
  this.#hide = new Set(hide);
40
92
  this.#interval = interval;
@@ -146,36 +198,77 @@ var KernelFiles = class extends Emitter {
146
198
  if (!this.#scanned) await this.refresh();
147
199
  return this.#list();
148
200
  }
149
- async #parent(path, create) {
201
+ async #parent(path) {
150
202
  const parts = split(path);
151
203
  const name = parts.pop();
152
204
  if (!name) throw new Error(`EISDIR: ${path}`);
153
205
  let dir = this.#root;
154
- for (const part of parts) dir = await dir.getDirectoryHandle(part, { create });
206
+ for (const part of parts) dir = await dir.getDirectoryHandle(part);
155
207
  return [dir, name];
156
208
  }
209
+ #notice(paths, trees = []) {
210
+ if (paths.length + trees.length === 0) return;
211
+ postFsNotice({ paths, trees }, { metadata: this.#metadata });
212
+ }
157
213
  async read(path) {
158
214
  try {
159
- const [dir, name] = await this.#parent(path, false);
215
+ const [dir, name] = await this.#parent(path);
160
216
  return await (await (await dir.getFileHandle(name)).getFile()).text();
161
217
  } catch {
162
218
  throw new Error(`ENOENT: ${path}`);
163
219
  }
164
220
  }
165
221
  async write(path, text) {
166
- const [dir, name] = await this.#parent(path, true);
167
- const writable = await (await dir.getFileHandle(name, { create: true })).createWritable();
168
- await writable.write(text);
169
- await writable.close();
222
+ const parts = split(path);
223
+ const name = parts.pop();
224
+ if (!name) throw new Error(`EISDIR: ${path}`);
225
+ const changed = [];
226
+ try {
227
+ let dir = this.#root;
228
+ let at = "/";
229
+ for (const part of parts) {
230
+ const parent = dir;
231
+ const next = child(at, part);
232
+ dir = await opened(
233
+ changed,
234
+ next,
235
+ at,
236
+ (create) => parent.getDirectoryHandle(part, { create })
237
+ );
238
+ at = next;
239
+ }
240
+ const file = child(at, name);
241
+ const handle = await opened(
242
+ changed,
243
+ file,
244
+ at,
245
+ (create) => dir.getFileHandle(name, { create })
246
+ );
247
+ const writable = await handle.createWritable();
248
+ await writable.write(text);
249
+ await writable.close();
250
+ changed.push(file);
251
+ } finally {
252
+ this.#notice([...new Set(changed)]);
253
+ }
170
254
  await this.refresh();
171
255
  }
172
256
  async remove(path) {
257
+ let target;
258
+ let tree;
173
259
  try {
174
- const [dir, name] = await this.#parent(path, false);
260
+ const [dir, name] = await this.#parent(path);
261
+ target = `/${split(path).join("/")}`;
262
+ tree = await dir.getFileHandle(name).then(
263
+ () => false,
264
+ () => true
265
+ );
175
266
  await dir.removeEntry(name, { recursive: true });
176
267
  } catch {
177
268
  throw new Error(`ENOENT: ${path}`);
178
269
  }
270
+ if (tree) this.#notice([parentOf(target)], [target]);
271
+ else this.#notice([target, parentOf(target)]);
179
272
  await this.refresh();
180
273
  }
181
274
  changes() {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/kernel/files.ts", "../src/kernel/idle.ts", "../src/kernel/terminals.ts", "../src/kernel/index.ts"],
4
- "sourcesContent": ["import { Emitter } from '../model/emitter.ts';\nimport type { Change, FileEntry, FileEvents, FilePort } from '../model/types.ts';\n\nexport interface KernelFilesOptions {\n skip?: readonly string[];\n hide?: readonly string[];\n interval?: number;\n}\n\ninterface Observer {\n observe(handle: FileSystemHandle, options: { recursive: boolean }): Promise<void>;\n disconnect(): void;\n}\n\ninterface ChangeRecord {\n type: string;\n relativePathComponents: readonly string[];\n relativePathMovedFrom?: readonly string[] | null;\n}\n\ntype ObserverConstructor = new (callback: (records: readonly ChangeRecord[]) => void) => Observer;\n\nfunction split(path: string): string[] {\n return path.split('/').filter(Boolean);\n}\n\nfunction same(a: FileEntry | undefined, b: FileEntry): boolean {\n return a?.kind === b.kind && a.size === b.size && a.modified === b.modified;\n}\n\nexport class KernelFiles extends Emitter<FileEvents> implements FilePort {\n #root: FileSystemDirectoryHandle;\n #skip: Set<string>;\n #hide: Set<string>;\n #interval: number;\n #entries = new Map<string, FileEntry>();\n #scanned = false;\n #running: Promise<void> | null = null;\n #again = false;\n #timer: ReturnType<typeof setInterval> | null = null;\n #observer: Observer | null = null;\n #pending: ReturnType<typeof setTimeout> | null = null;\n #last = Number.NEGATIVE_INFINITY;\n\n constructor(\n root: FileSystemDirectoryHandle,\n { skip = [], hide = [], interval = 2000 }: KernelFilesOptions = {}\n ) {\n super();\n this.#root = root;\n this.#skip = new Set(skip);\n this.#hide = new Set(hide);\n this.#interval = interval;\n }\n\n start(): void {\n if (this.#timer || this.#observer) return;\n const Observer = (globalThis as { FileSystemObserver?: ObserverConstructor })\n .FileSystemObserver;\n if (!Observer) {\n this.#poll();\n return;\n }\n const observer = new Observer((records) => this.#observed(records));\n this.#observer = observer;\n observer.observe(this.#root, { recursive: true }).catch(() => {\n if (this.#observer !== observer) return;\n this.#observer = null;\n this.#poll();\n });\n }\n\n #poll(): void {\n this.#timer = setInterval(() => void this.refresh(), this.#interval);\n }\n\n #observed(records: readonly ChangeRecord[]): void {\n if (records.length > 0 && records.every((record) => this.#unseen(record))) return;\n if (this.#pending) return;\n const wait = Math.max(0, this.#last + this.#interval - performance.now());\n this.#pending = setTimeout(() => {\n this.#pending = null;\n this.#last = performance.now();\n void this.refresh();\n }, wait);\n }\n\n #unseen({ type, relativePathComponents, relativePathMovedFrom }: ChangeRecord): boolean {\n if (type === 'unknown' || type === 'errored') return false;\n return [relativePathComponents, relativePathMovedFrom ?? relativePathComponents].every(\n (parts) => this.#beneath(parts)\n );\n }\n\n #beneath(parts: readonly string[]): boolean {\n let path = '';\n for (const [i, part] of parts.entries()) {\n path += `/${part}`;\n if (this.#hide.has(path)) return true;\n if (this.#skip.has(path) && i < parts.length - 1) return true;\n }\n return false;\n }\n\n stop(): void {\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = null;\n if (this.#pending) clearTimeout(this.#pending);\n this.#pending = null;\n this.#observer?.disconnect();\n this.#observer = null;\n }\n\n async #walk(\n dir: FileSystemDirectoryHandle,\n prefix: string,\n out: Map<string, FileEntry>\n ): Promise<void> {\n const values = (\n dir as FileSystemDirectoryHandle & {\n values(): AsyncIterable<FileSystemDirectoryHandle | FileSystemFileHandle>;\n }\n ).values();\n for await (const handle of values) {\n const path = `${prefix}/${handle.name}`;\n if (this.#hide.has(path)) continue;\n if (handle.kind === 'directory') {\n out.set(path, { path, kind: 'directory', size: 0, modified: 0 });\n if (!this.#skip.has(path)) await this.#walk(handle, path, out).catch(() => {});\n } else {\n const file = await handle.getFile().catch(() => null);\n if (file)\n out.set(path, { path, kind: 'file', size: file.size, modified: file.lastModified });\n }\n }\n }\n\n #list(): FileEntry[] {\n return [...this.#entries.values()].sort((a, b) => a.path.localeCompare(b.path));\n }\n\n async #scan(): Promise<void> {\n const next = new Map<string, FileEntry>();\n await this.#walk(this.#root, '', next);\n const touched = [...next.values()].filter(\n (entry) => !same(this.#entries.get(entry.path), entry)\n );\n const gone = [...this.#entries.values()].filter((entry) => !next.has(entry.path));\n const first = !this.#scanned;\n const previous = this.#entries;\n this.#entries = next;\n this.#scanned = true;\n if (first || touched.length + gone.length === 0) return;\n this.emit('files', this.#list());\n for (const entry of [...touched, ...gone]) {\n if (entry.kind === 'file' || previous.get(entry.path)?.kind === 'file')\n this.emit('file', entry.path);\n }\n }\n\n refresh(): Promise<void> {\n if (this.#running !== null) {\n this.#again = true;\n return this.#running;\n }\n this.#running = (async () => {\n do {\n this.#again = false;\n await this.#scan();\n } while (this.#again);\n })().finally(() => {\n this.#running = null;\n });\n return this.#running;\n }\n\n async list(): Promise<readonly FileEntry[]> {\n if (!this.#scanned) await this.refresh();\n return this.#list();\n }\n\n async #parent(path: string, create: boolean): Promise<[FileSystemDirectoryHandle, string]> {\n const parts = split(path);\n const name = parts.pop();\n if (!name) throw new Error(`EISDIR: ${path}`);\n let dir = this.#root;\n for (const part of parts) dir = await dir.getDirectoryHandle(part, { create });\n return [dir, name];\n }\n\n async read(path: string): Promise<string> {\n try {\n const [dir, name] = await this.#parent(path, false);\n return await (await (await dir.getFileHandle(name)).getFile()).text();\n } catch {\n throw new Error(`ENOENT: ${path}`);\n }\n }\n\n async write(path: string, text: string): Promise<void> {\n const [dir, name] = await this.#parent(path, true);\n const writable = await (await dir.getFileHandle(name, { create: true })).createWritable();\n await writable.write(text);\n await writable.close();\n await this.refresh();\n }\n\n async remove(path: string): Promise<void> {\n try {\n const [dir, name] = await this.#parent(path, false);\n await dir.removeEntry(name, { recursive: true });\n } catch {\n throw new Error(`ENOENT: ${path}`);\n }\n await this.refresh();\n }\n\n changes(): readonly Change[] {\n return [];\n }\n\n accept(): void {}\n\n async revert(): Promise<void> {}\n}\n", "import { Emitter } from '../model/emitter.ts';\nimport type {\n Agent,\n AgentEvents,\n AgentPort,\n BrowserEvents,\n BrowserPort,\n BrowserTab,\n FrozenCone,\n Memory,\n MemoryEvents,\n MemoryPort,\n Message,\n MonitorEvents,\n MonitorPort,\n MonitorSnapshot,\n SlashCommand,\n SprinkleEvents,\n SprinklePort,\n TrayEvents,\n TrayPort,\n TrayStatus,\n UserMessage,\n} from '../model/types.ts';\n\nfunction missing(what: string): Error {\n return new Error(`No ${what} in this SLICC yet`);\n}\n\nexport class IdleAgent extends Emitter<AgentEvents> implements AgentPort {\n list(): readonly Agent[] {\n return [];\n }\n active(): string {\n return '';\n }\n select(): void {}\n messages(): readonly Message[] {\n return [];\n }\n async send(): Promise<void> {\n throw missing('agent');\n }\n stop(): void {}\n busy(): boolean {\n return false;\n }\n queue(): readonly UserMessage[] {\n return [];\n }\n unqueue(): void {}\n suggestion(): string | null {\n return null;\n }\n answer(): void {}\n resolveLick(): void {}\n compact(): void {}\n clear(): void {}\n setModel(): void {}\n setThinking(): void {}\n async older(): Promise<readonly Message[]> {\n return [];\n }\n commands(): readonly SlashCommand[] {\n return [];\n }\n async ready(): Promise<void> {}\n createScoop(): Agent {\n throw missing('agent');\n }\n frozen(): readonly FrozenCone[] {\n return [];\n }\n freeze(): void {}\n thaw(): Agent | null {\n return null;\n }\n discard(): void {}\n}\n\nexport class IdleBrowser extends Emitter<BrowserEvents> implements BrowserPort {\n list(): readonly BrowserTab[] {\n return [];\n }\n active(): string | null {\n return null;\n }\n activate(): void {}\n open(): BrowserTab {\n throw missing('browser');\n }\n navigate(): void {}\n close(): void {}\n async screenshot(): Promise<string> {\n throw missing('browser');\n }\n}\n\nexport class IdleMemory extends Emitter<MemoryEvents> implements MemoryPort {\n list(): readonly Memory[] {\n return [];\n }\n save(): Memory {\n throw missing('memory');\n }\n remove(): void {}\n}\n\nexport class IdleMonitor extends Emitter<MonitorEvents> implements MonitorPort {\n snapshot(): MonitorSnapshot {\n return { vitals: [], alerts: [], sections: [], updatedAt: 0 };\n }\n resync(): void {}\n}\n\nexport class IdleSprinkles extends Emitter<SprinkleEvents> implements SprinklePort {\n list(): readonly [] {\n return [];\n }\n send(): void {}\n}\n\nexport class IdleTray extends Emitter<TrayEvents> implements TrayPort {\n status(): TrayStatus {\n return {\n name: 'slicc',\n kind: 'hosted',\n connection: 'offline',\n role: 'none',\n followers: [],\n spent: 0,\n rate: 0,\n budget: { percent: 0, window: 'daily', resets: '' },\n joinUrl: '',\n };\n }\n reconnect(): void {}\n disconnect(): void {}\n}\n", "import type { TerminalBackend } from '../backend.ts';\nimport { cwdPromptCommand, kernelBackend, type TerminalKernel } from '../kernel-backend.ts';\nimport { Emitter } from '../model/emitter.ts';\nimport type { TerminalEvents, TerminalInfo, TerminalPort } from '../model/types.ts';\n\nexport interface KernelTerminalsOptions {\n argv?: string[];\n cwd?: string;\n user?: string | number;\n env?: Record<string, string>;\n open?: number;\n}\n\nexport class KernelTerminals extends Emitter<TerminalEvents> implements TerminalPort {\n #kernel: TerminalKernel;\n #argv: string[];\n #cwd: string;\n #user: string | number | undefined;\n #env: Record<string, string> | undefined;\n #terminals: TerminalInfo[] = [];\n #backends = new Map<string, TerminalBackend>();\n #next = 1;\n\n constructor(\n kernel: TerminalKernel,\n { argv = ['bash', '-i'], cwd = '/home', user, env, open = 1 }: KernelTerminalsOptions = {}\n ) {\n super();\n this.#kernel = kernel;\n this.#argv = argv;\n this.#cwd = cwd;\n this.#user = user;\n this.#env = env;\n for (let i = 0; i < open; i++) this.open();\n }\n\n list(): readonly TerminalInfo[] {\n return this.#terminals.map((terminal) => ({ ...terminal }));\n }\n\n open({\n cwd = this.#cwd,\n agentId = null,\n }: {\n cwd?: string;\n agentId?: string | null;\n } = {}): TerminalInfo {\n const terminal: TerminalInfo = {\n id: `term-${this.#next}`,\n title: `${this.#argv[0]} ${this.#next}`,\n cwd,\n agentId,\n };\n this.#next += 1;\n this.#terminals.push(terminal);\n this.emit('terminals', this.list());\n return { ...terminal };\n }\n\n close(id: string): void {\n this.#terminals = this.#terminals.filter((terminal) => terminal.id !== id);\n this.#backends.delete(id);\n this.emit('terminals', this.list());\n }\n\n #move(id: string, cwd: string): void {\n const terminal = this.#terminals.find((candidate) => candidate.id === id);\n if (!terminal || terminal.cwd === cwd) return;\n terminal.cwd = cwd;\n this.emit('terminals', this.list());\n }\n\n backend(id: string): TerminalBackend {\n const terminal = this.#terminals.find((candidate) => candidate.id === id);\n if (!terminal) throw new Error(`No terminal ${id}`);\n let backend = this.#backends.get(id);\n if (!backend) {\n backend = kernelBackend(this.#kernel, {\n argv: this.#argv,\n cwd: terminal.cwd,\n user: this.#user,\n env: { PROMPT_COMMAND: cwdPromptCommand, ...this.#env },\n onCwd: (cwd) => this.#move(id, cwd),\n });\n this.#backends.set(id, backend);\n }\n return backend;\n }\n}\n", "import { clock } from '../dummy/clock.ts';\nimport { DummySettings, dummySettingsKey } from '../dummy/settings.ts';\nimport type { TerminalKernel } from '../kernel-backend.ts';\nimport type { Settings, SliccModel } from '../model/types.ts';\nimport { KernelFiles, type KernelFilesOptions } from './files.ts';\nimport {\n IdleAgent,\n IdleBrowser,\n IdleMemory,\n IdleMonitor,\n IdleSprinkles,\n IdleTray,\n} from './idle.ts';\nimport { KernelTerminals, type KernelTerminalsOptions } from './terminals.ts';\n\nexport interface KernelModelOptions {\n kernel: TerminalKernel;\n root: FileSystemDirectoryHandle;\n files?: KernelFilesOptions;\n terminals?: KernelTerminalsOptions;\n storage?: Storage | null;\n}\n\nexport interface KernelModel extends SliccModel {\n files: KernelFiles;\n terminals: KernelTerminals;\n}\n\nexport const settingsKey = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n pushToTalk: false,\n showThinking: false,\n diffStyle: 'unified',\n};\n\nexport function idleModel(storage: Storage | null = null): Omit<SliccModel, 'files' | 'terminals'> {\n return {\n agent: new IdleAgent(),\n browser: new IdleBrowser(),\n settings: new DummySettings(defaults, [], [], storage, clock(0), [\n settingsKey,\n dummySettingsKey,\n ]),\n memory: new IdleMemory(),\n monitor: new IdleMonitor(),\n sprinkles: new IdleSprinkles(),\n tray: new IdleTray(),\n };\n}\n\nexport function createKernelModel({\n kernel,\n root,\n files,\n terminals,\n storage = null,\n}: KernelModelOptions): KernelModel {\n const model: KernelModel = {\n ...idleModel(storage),\n files: new KernelFiles(root, files),\n terminals: new KernelTerminals(kernel, terminals),\n };\n model.files.start();\n return model;\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../node_modules/@ai-ecoverse/slicc-kernel/src/fs/meta-reads.ts", "../node_modules/@ai-ecoverse/slicc-kernel/src/fs/path-map.ts", "../node_modules/@ai-ecoverse/slicc-kernel/src/fs/meta.ts", "../node_modules/@ai-ecoverse/slicc-kernel/src/fs/notice.ts", "../node_modules/@ai-ecoverse/slicc-kernel/src/fs/types.ts", "../node_modules/@ai-ecoverse/slicc-kernel/src/kernel/replacing.ts", "../node_modules/@ai-ecoverse/slicc-kernel/src/meta.ts", "../src/kernel/files.ts", "../src/kernel/idle.ts", "../src/kernel/terminals.ts", "../src/kernel/index.ts"],
4
+ "sourcesContent": ["import type { MetaEntry } from './meta.ts';\n\nexport interface Invalidated {\n paths?: readonly string[];\n trees?: readonly string[];\n}\n\nconst parentOf = (path: string): string => path.slice(0, path.lastIndexOf('/')) || '/';\nconst within = (path: string, root: string): boolean =>\n root === '/' || path === root || path.startsWith(`${root}/`);\n\ninterface Noted {\n at: number;\n paths: readonly string[];\n trees: readonly string[];\n}\n\nexport class Invalidations {\n private seq = 0;\n private readonly log: Noted[] = [];\n private readonly open = new Map<number, number>();\n\n start(): number {\n const at = this.seq;\n this.open.set(at, (this.open.get(at) ?? 0) + 1);\n return at;\n }\n\n end(at: number): void {\n const left = (this.open.get(at) ?? 1) - 1;\n if (left > 0) this.open.set(at, left);\n else this.open.delete(at);\n const oldest = this.open.keys().next();\n if (oldest.done) this.log.length = 0;\n else while (this.log.length > 0 && (this.log[0] as Noted).at <= oldest.value) this.log.shift();\n }\n\n note({ paths = [], trees = [] }: Invalidated): void {\n this.seq++;\n if (this.open.size > 0) this.log.push({ at: this.seq, paths, trees });\n }\n\n private since(at: number, hit: (noted: Noted) => boolean): boolean {\n for (let i = this.log.length - 1; i >= 0; i--) {\n const noted = this.log[i] as Noted;\n if (noted.at <= at) return false;\n if (hit(noted)) return true;\n }\n return false;\n }\n\n entryStale(at: number, path: string): boolean {\n return this.since(\n at,\n ({ paths, trees }) => paths.includes(path) || trees.some((tree) => within(path, tree))\n );\n }\n\n listingStale(at: number, dir: string): boolean {\n return this.since(\n at,\n ({ paths, trees }) =>\n paths.some((path) => path !== '/' && parentOf(path) === dir) ||\n trees.some((tree) => within(dir, tree) || (tree !== '/' && parentOf(tree) === dir))\n );\n }\n}\n\nexport type MetaReadKind = 'entry' | 'links';\n\ninterface Wanted<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (error: unknown) => void;\n at?: number;\n}\n\nexport interface ReadBatch {\n entries: readonly string[];\n links: readonly string[];\n}\n\nexport interface ReadResult {\n entries: Map<string, MetaEntry | null>;\n links: Map<string, MetaEntry[]>;\n}\n\nexport const META_READS_IN_FLIGHT = 4;\n\nexport class BatchedReads {\n private readonly run: (batch: ReadBatch) => Promise<ReadResult>;\n private readonly invalidations: Invalidations;\n private readonly keep: (kind: MetaReadKind, key: string, value: unknown) => void;\n private readonly queued: Record<MetaReadKind, Map<string, Wanted<unknown>>> = {\n entry: new Map(),\n links: new Map(),\n };\n private readonly reading: Record<MetaReadKind, Map<string, Wanted<unknown>>> = {\n entry: new Map(),\n links: new Map(),\n };\n private inFlight = 0;\n\n constructor(\n invalidations: Invalidations,\n run: (batch: ReadBatch) => Promise<ReadResult>,\n keep: (kind: MetaReadKind, key: string, value: unknown) => void\n ) {\n this.invalidations = invalidations;\n this.run = run;\n this.keep = keep;\n }\n\n entries(paths: readonly string[]): Promise<Map<string, MetaEntry | null>> {\n const wanted = paths.map((path) => [path, this.want('entry', path)] as const);\n this.pump();\n return Promise.all(wanted.map(([, w]) => w.promise)).then(\n (found) => new Map(wanted.map(([path], i) => [path, found[i] as MetaEntry | null]))\n );\n }\n\n links(dir: string): Promise<MetaEntry[]> {\n const wanted = this.want('links', dir);\n this.pump();\n return wanted.promise as Promise<MetaEntry[]>;\n }\n\n private stale(kind: MetaReadKind, key: string, at: number): boolean {\n return kind === 'entry'\n ? this.invalidations.entryStale(at, key)\n : this.invalidations.listingStale(at, key);\n }\n\n private want(kind: MetaReadKind, key: string): Wanted<unknown> {\n const reading = this.reading[kind].get(key);\n if (reading && !this.stale(kind, key, reading.at as number)) return reading;\n const queued = this.queued[kind].get(key);\n if (queued) return queued;\n const made = Promise.withResolvers<unknown>();\n made.promise.catch(() => undefined);\n this.queued[kind].set(key, made);\n return made;\n }\n\n private pump(): void {\n while (\n this.inFlight < META_READS_IN_FLIGHT &&\n (this.queued.entry.size > 0 || this.queued.links.size > 0)\n ) {\n this.flush();\n }\n }\n\n private flush(): void {\n const entries = this.queued.entry;\n const links = this.queued.links;\n this.queued.entry = new Map();\n this.queued.links = new Map();\n const at = this.invalidations.start();\n for (const [kind, taken] of [\n ['entry', entries],\n ['links', links],\n ] as const) {\n for (const [key, wanted] of taken) {\n wanted.at = at;\n this.reading[kind].set(key, wanted);\n }\n }\n this.inFlight++;\n const settled = (result: ReadResult | undefined, error?: unknown) => {\n this.inFlight--;\n for (const [kind, taken, found] of [\n ['entry', entries, result?.entries],\n ['links', links, result?.links],\n ] as const) {\n for (const [key, wanted] of taken) {\n if (this.reading[kind].get(key) === wanted) this.reading[kind].delete(key);\n if (!found) {\n wanted.reject(error);\n continue;\n }\n const value = found.get(key) ?? (kind === 'entry' ? null : []);\n if (!this.stale(kind, key, at)) this.keep(kind, key, value);\n wanted.resolve(value);\n }\n }\n this.invalidations.end(at);\n this.pump();\n };\n let reading: Promise<ReadResult>;\n try {\n reading = this.run({ entries: [...entries.keys()], links: [...links.keys()] });\n } catch (error) {\n settled(undefined, error);\n return;\n }\n reading.then(\n (result) => settled(result),\n (error: unknown) => settled(undefined, error)\n );\n }\n}\n", "function parentOf(path: string): string | undefined {\n if (path === '/' || path === '') return undefined;\n const at = path.lastIndexOf('/');\n return at <= 0 ? '/' : path.slice(0, at);\n}\n\nexport class PathMap<V> {\n private readonly values = new Map<string, V>();\n private readonly children = new Map<string, Set<string>>();\n\n get size(): number {\n return this.values.size;\n }\n\n has(path: string): boolean {\n return this.values.has(path);\n }\n\n get(path: string): V | undefined {\n return this.values.get(path);\n }\n\n keys(): IterableIterator<string> {\n return this.values.keys();\n }\n\n set(path: string, value: V): this {\n if (!this.values.has(path)) this.link(path);\n this.values.set(path, value);\n return this;\n }\n\n delete(path: string): boolean {\n if (!this.values.delete(path)) return false;\n this.prune(path);\n return true;\n }\n\n subtree(root: string): string[] {\n const out: string[] = [];\n const stack = [root];\n for (let path = stack.pop(); path !== undefined; path = stack.pop()) {\n if (this.values.has(path)) out.push(path);\n const below = this.children.get(path);\n if (below) for (const child of below) stack.push(child);\n }\n return out;\n }\n\n entries(): IterableIterator<[string, V]> {\n return this.values.entries();\n }\n\n deleteTree(root: string): void {\n const stack = [root];\n for (let path = stack.pop(); path !== undefined; path = stack.pop()) {\n this.values.delete(path);\n const below = this.children.get(path);\n if (below) for (const child of below) stack.push(child);\n this.children.delete(path);\n }\n this.prune(root);\n }\n\n clear(): void {\n this.values.clear();\n this.children.clear();\n }\n\n private link(path: string): void {\n for (let child = path, parent = parentOf(path); parent !== undefined; ) {\n const set = this.children.get(parent);\n if (set?.has(child)) return;\n if (set) set.add(child);\n else this.children.set(parent, new Set([child]));\n child = parent;\n parent = parentOf(parent);\n }\n }\n\n private prune(path: string): void {\n for (let child = path, parent = parentOf(path); parent !== undefined; ) {\n if (this.values.has(child) || this.children.has(child)) return;\n const set = this.children.get(parent);\n set?.delete(child);\n if (set && set.size > 0) return;\n this.children.delete(parent);\n child = parent;\n parent = parentOf(parent);\n }\n }\n}\n", "import { BatchedReads, Invalidations, type ReadBatch, type ReadResult } from './meta-reads.ts';\nimport { PathMap } from './path-map.ts';\nexport interface MetaEntry {\n path: string;\n mode?: number;\n atimeMs?: number;\n mtimeMs?: number;\n mtimeFor?: number;\n timesAt?: number;\n timesBy?: string;\n ctimeMs?: number;\n ino?: number;\n fid?: string;\n link?: string;\n socket?: true;\n fifo?: true;\n dir?: string;\n uid?: number;\n gid?: number;\n heir?: [number, number];\n writes?: number;\n epoch?: number;\n vacated?: true;\n rename?: {\n from: string;\n to: string;\n at: number;\n replace?: true;\n lock?: string;\n root?: { ino: number; uid?: number; gid?: number; mode?: number };\n };\n}\n\nexport type MetaChange = (entry: MetaEntry | undefined) => MetaEntry | undefined;\n\nexport interface MetaUpdate {\n path: string;\n change: MetaChange;\n}\n\nexport interface MetaStore {\n get(paths: readonly string[]): Promise<Array<MetaEntry | undefined>>;\n fresh?(paths: readonly string[]): Promise<Array<MetaEntry | undefined>>;\n update(path: string, change: MetaChange): Promise<void>;\n updateMany?(changes: ReadonlyArray<MetaUpdate>): Promise<void>;\n move(\n from: string,\n to: string,\n root: (entry: MetaEntry | undefined) => MetaEntry,\n left?: MetaEntry\n ): Promise<void>;\n remove(paths: readonly string[], left?: MetaEntry): Promise<void>;\n links(dir: string): Promise<MetaEntry[]>;\n under(path: string): Promise<MetaEntry[]>;\n all(): Promise<MetaEntry[]>;\n absent?(path: string): boolean;\n removeFile?(path: string): Promise<void>;\n forgetTrees?(trees: readonly string[]): void;\n transactions?(): MetaTransactions;\n}\n\nexport interface MetaTransactions {\n reads: number;\n writes: number;\n}\n\nexport const META_DB = 'slicc-kernel';\nexport const META_VERSION = 1;\nexport const META_STORE = 'entries';\n\nfunction parentOf(path: string): string {\n return path.slice(0, path.lastIndexOf('/')) || '/';\n}\n\nfunction moved(entry: MetaEntry, path: string): MetaEntry {\n return entry.link === undefined ? { ...entry, path } : { ...entry, path, dir: parentOf(path) };\n}\n\nfunction under(path: string, root: string): boolean {\n return path === root || path.startsWith(`${root}/`);\n}\n\nexport class MemoryMeta implements MetaStore {\n private readonly entries = new PathMap<MetaEntry>();\n\n async get(paths: readonly string[]): Promise<Array<MetaEntry | undefined>> {\n return paths.map((path) => this.entries.get(path));\n }\n\n async fresh(paths: readonly string[]): Promise<Array<MetaEntry | undefined>> {\n return this.get(paths);\n }\n\n async update(path: string, change: MetaChange): Promise<void> {\n this.set(path, change(this.entries.get(path)));\n }\n\n private set(path: string, entry: MetaEntry | undefined): void {\n if (entry) this.entries.set(path, entry);\n else this.entries.delete(path);\n }\n\n async move(\n from: string,\n to: string,\n root: (entry: MetaEntry | undefined) => MetaEntry,\n left?: MetaEntry\n ) {\n await this.remove([to]);\n const carried = this.entries.subtree(from).map((path) => this.entries.get(path) as MetaEntry);\n for (const entry of carried) this.entries.delete(entry.path);\n for (const entry of carried) {\n const path = to + entry.path.slice(from.length);\n if (path !== to) this.entries.set(path, moved(entry, path));\n }\n this.entries.set(to, moved(root(carried.find((entry) => entry.path === from)), to));\n if (left) this.entries.set(from, left);\n }\n\n async remove(paths: readonly string[], left?: MetaEntry): Promise<void> {\n for (const path of paths) this.entries.deleteTree(path);\n if (left) this.entries.set(left.path, left);\n }\n\n absent(path: string): boolean {\n return !this.entries.has(path);\n }\n\n async links(dir: string): Promise<MetaEntry[]> {\n return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);\n }\n\n async under(path: string): Promise<MetaEntry[]> {\n return [...this.entries.entries()].map(([, e]) => e).filter((entry) => under(entry.path, path));\n }\n\n async all(): Promise<MetaEntry[]> {\n return [...this.entries.entries()].map(([, e]) => e);\n }\n}\n\nfunction descendants(path: string): IDBKeyRange {\n return IDBKeyRange.bound(`${path}/`, `${path}0`, false, true);\n}\n\nfunction settle<T>(transaction: IDBTransaction, value: () => T): Promise<T> {\n return new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve(value());\n transaction.onabort = () =>\n reject(transaction.error ?? new Error('metadata transaction aborted'));\n });\n}\n\ninterface Invalidation {\n paths?: readonly string[];\n trees?: readonly string[];\n moved?: { from: string; to: string };\n}\n\nconst REMOVE_BATCH = 256;\nconst REMOVE_WAIT_MS = 5;\nconst LISTINGS_KEPT = 4096;\n\nexport class IndexedDbMeta implements MetaStore {\n private readonly db: IDBDatabase;\n private readonly cache = new PathMap<MetaEntry | null>();\n private readonly listings = new PathMap<readonly MetaEntry[]>();\n private readonly channel: BroadcastChannel;\n private readonly invalidations = new Invalidations();\n private readonly reads = new BatchedReads(\n this.invalidations,\n (batch) => this.read(batch),\n (kind, key, value) => {\n if (kind === 'entry') this.cache.set(key, value as MetaEntry | null);\n else this.keepListing(key, value as MetaEntry[]);\n }\n );\n private readonly listeners = new Set<(change: Invalidation) => void>();\n\n listen(listener: (change: Invalidation) => void): void {\n this.listeners.add(listener);\n }\n\n constructor(db: IDBDatabase) {\n this.db = db;\n db.onversionchange = () => db.close();\n this.channel = new BroadcastChannel(`slicc-kernel-meta:${db.name}`);\n this.channel.onmessage = ({ data }) => {\n this.drop(data as Invalidation);\n for (const listener of this.listeners) listener(data as Invalidation);\n };\n (this.channel as BroadcastChannel & { unref?: () => void }).unref?.();\n }\n\n forgetTrees(trees: readonly string[]): void {\n this.drop({ trees: [...trees] });\n }\n\n private drop({ paths = [], trees = [] }: Invalidation): void {\n this.invalidations.note({ paths, trees });\n for (const path of paths) {\n this.cache.delete(path);\n if (path !== '/') this.listings.delete(parentOf(path));\n }\n for (const tree of trees) {\n this.cache.deleteTree(tree);\n this.listings.deleteTree(tree);\n if (tree !== '/') this.listings.delete(parentOf(tree));\n }\n }\n\n private keepListing(dir: string, links: MetaEntry[]): void {\n if (this.listings.size >= LISTINGS_KEPT) this.listings.clear();\n this.listings.set(dir, links);\n }\n\n private async read({ entries, links }: ReadBatch): Promise<ReadResult> {\n this.settleRemoves();\n const [transaction, store] = this.transaction('readonly');\n const result: ReadResult = { entries: new Map(), links: new Map() };\n for (const path of entries) {\n const request = store.get(path);\n request.onsuccess = () => void result.entries.set(path, request.result ?? null);\n }\n if (links.length > 0) {\n const index = store.index('dir');\n for (const dir of links) {\n const request = index.getAll(dir);\n request.onsuccess = () => void result.links.set(dir, request.result as MetaEntry[]);\n }\n }\n return settle(transaction, () => result);\n }\n\n private changed(invalidation: Invalidation): void {\n this.drop(invalidation);\n this.channel.postMessage(invalidation);\n }\n\n private readonly counted: MetaTransactions = { reads: 0, writes: 0 };\n\n transactions(): MetaTransactions {\n return { ...this.counted };\n }\n\n private transaction(mode: IDBTransactionMode): [IDBTransaction, IDBObjectStore] {\n if (mode === 'readonly') this.counted.reads++;\n else this.counted.writes++;\n const transaction = this.db.transaction(META_STORE, mode);\n return [transaction, transaction.objectStore(META_STORE)];\n }\n\n async get(paths: readonly string[]): Promise<Array<MetaEntry | undefined>> {\n const missing = paths.filter((path) => !this.cache.has(path));\n if (missing.length > 0) {\n const found = await this.reads.entries(missing);\n return paths.map(\n (path) => (found.has(path) ? found.get(path) : this.cache.get(path)) ?? undefined\n );\n }\n return paths.map((path) => this.cache.get(path) ?? undefined);\n }\n\n async fresh(paths: readonly string[]): Promise<Array<MetaEntry | undefined>> {\n const [transaction, store] = this.transaction('readonly');\n const requests = paths.map((path) => store.get(path));\n return settle(transaction, () =>\n requests.map((request) => (request.result as MetaEntry | undefined) ?? undefined)\n );\n }\n\n async update(path: string, change: MetaChange): Promise<void> {\n this.settleRemoves();\n const [transaction, store] = this.transaction('readwrite');\n const request = store.get(path);\n let next: MetaEntry | undefined;\n request.onsuccess = () => {\n next = change(request.result);\n if (next) store.put(next);\n else store.delete(path);\n };\n await settle(transaction, () => undefined);\n this.changed({ paths: [path] });\n this.cache.set(path, next ?? null);\n }\n\n async updateMany(changes: ReadonlyArray<MetaUpdate>): Promise<void> {\n if (changes.length === 0) return;\n this.settleRemoves();\n const [transaction, store] = this.transaction('readwrite');\n const next = new Map<string, MetaEntry | null>();\n for (const { path, change } of changes) {\n const request = store.get(path);\n request.onsuccess = () => {\n const entry = change(request.result);\n next.set(path, entry ?? null);\n if (entry) store.put(entry);\n else store.delete(path);\n };\n }\n await settle(transaction, () => undefined);\n this.changed({ paths: changes.map(({ path }) => path) });\n for (const [path, entry] of next) this.cache.set(path, entry);\n }\n\n close(): void {\n this.channel.close();\n this.db.close();\n }\n\n async move(\n from: string,\n to: string,\n root: (entry: MetaEntry | undefined) => MetaEntry,\n left?: MetaEntry\n ) {\n this.settleRemoves();\n const [transaction, store] = this.transaction('readwrite');\n store.delete(to);\n store.delete(descendants(to));\n const source = store.get(from);\n const below = store.getAll(descendants(from));\n below.onsuccess = () => {\n store.delete(from);\n store.delete(descendants(from));\n for (const entry of below.result as MetaEntry[]) {\n store.put(moved(entry, to + entry.path.slice(from.length)));\n }\n store.put(moved(root(source.result), to));\n if (left) store.put(left);\n };\n await settle(transaction, () => undefined);\n this.changed({ trees: [from, to], moved: { from, to } });\n }\n\n absent(path: string): boolean {\n return this.cache.has(path) && this.cache.get(path) === null;\n }\n\n private removing = new Map<string, string>();\n private removeTimer: ReturnType<typeof setTimeout> | undefined;\n\n async remove(paths: readonly string[], left?: MetaEntry): Promise<void> {\n this.settleRemoves();\n return this.removeNow(paths, left);\n }\n\n async removeFile(path: string): Promise<void> {\n const known = this.cache.get(path);\n if (!known) return this.remove([path]);\n this.invalidations.note({ paths: [path] });\n if (path !== '/') this.listings.delete(parentOf(path));\n this.cache.set(path, null);\n this.removing.set(path, JSON.stringify(known));\n if (this.removing.size >= REMOVE_BATCH) return this.flushRemoves();\n this.removeTimer ??= setTimeout(\n () => void this.flushRemoves().catch(() => undefined),\n REMOVE_WAIT_MS\n );\n }\n\n private async removeNow(paths: readonly string[], left?: MetaEntry): Promise<void> {\n const [transaction, store] = this.transaction('readwrite');\n for (const path of paths) {\n store.delete(path);\n store.delete(descendants(path));\n }\n if (left) store.put(left);\n await settle(transaction, () => undefined);\n this.changed({ trees: paths });\n if (left) this.cache.set(left.path, left);\n }\n\n private settleRemoves(): void {\n if (this.removing.size > 0) void this.flushRemoves().catch(() => undefined);\n }\n\n private flushRemoves(): Promise<void> {\n clearTimeout(this.removeTimer);\n this.removeTimer = undefined;\n const batch = this.removing;\n if (batch.size === 0) return Promise.resolve();\n this.removing = new Map();\n const [transaction, store] = this.transaction('readwrite');\n for (const [path, seen] of batch) {\n const request = store.get(path);\n request.onsuccess = () => {\n if (request.result && JSON.stringify(request.result) === seen) store.delete(path);\n };\n }\n const paths = [...batch.keys()];\n return settle(transaction, () => void this.channel.postMessage({ paths }));\n }\n\n async links(dir: string): Promise<MetaEntry[]> {\n const known = this.listings.get(dir);\n return [...(known ?? (await this.reads.links(dir)))];\n }\n\n async under(path: string): Promise<MetaEntry[]> {\n this.settleRemoves();\n const [transaction, store] = this.transaction('readonly');\n const self = store.get(path);\n const below = store.getAll(descendants(path));\n return settle(transaction, () =>\n [self.result as MetaEntry | undefined, ...(below.result as MetaEntry[])].filter(\n (entry): entry is MetaEntry => entry !== undefined\n )\n );\n }\n\n async all(): Promise<MetaEntry[]> {\n this.settleRemoves();\n const [transaction, store] = this.transaction('readonly');\n const request = store.getAll();\n return settle(transaction, () => request.result as MetaEntry[]);\n }\n}\n\nexport function openMetaDb(name: string, factory: IDBFactory): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = factory.open(name, META_VERSION);\n request.onupgradeneeded = (event) => {\n if (event.oldVersion < 1) {\n request.result.createObjectStore(META_STORE, { keyPath: 'path' }).createIndex('dir', 'dir');\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nexport async function openMeta(name: string, factory: IDBFactory): Promise<IndexedDbMeta> {\n return new IndexedDbMeta(await openMetaDb(name, factory));\n}\n", "export interface FsNotice {\n paths?: readonly string[];\n trees?: readonly string[];\n}\n\nexport function dirsChannel(metadata: string): string {\n return `slicc-kernel-dirs:${metadata}`;\n}\n\nexport function noticeOf(data: unknown): { paths: string[]; trees: string[]; outside?: true } {\n if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === 'string'), trees: [] };\n const notice = (data ?? {}) as FsNotice;\n const strings = (list: unknown) =>\n Array.isArray(list) ? list.filter((p): p is string => typeof p === 'string') : [];\n return { paths: strings(notice.paths), trees: strings(notice.trees), outside: true };\n}\n", "export interface FsStat {\n isFile: boolean;\n isDirectory: boolean;\n isSymbolicLink: boolean;\n isSocket?: boolean;\n isFifo?: boolean;\n size: number;\n mode: number;\n mtime: Date;\n atime: Date;\n ctime: Date;\n ino: number;\n fid?: string;\n nlink?: number;\n dev?: number;\n readonly?: boolean;\n maxFile?: number;\n ranged?: boolean;\n linkable?: boolean;\n version?: string;\n uid?: number;\n gid?: number;\n}\n\nexport type UsersMode = 'off' | 'report' | 'enforce';\n\nexport const DEFAULT_USERS: UsersMode = 'enforce';\n\nexport interface Owner {\n uid: number;\n gid: number;\n}\n\nexport interface Perm {\n uid: number;\n gid: number;\n mode?: number;\n link?: string;\n}\n\nexport interface NewOwner extends Owner {\n mode?: number;\n parents?: number;\n socket?: boolean;\n fifo?: boolean;\n existing?: true;\n}\n\nexport interface CopyOptions {\n exclusive: boolean;\n owner?: NewOwner;\n}\n\nexport interface OwnerChange {\n uid?: number;\n gid?: number;\n follow?: boolean;\n kind?: 'chown' | 'adopt' | 'new';\n mode?: number;\n}\n\nexport interface HeldEntry {\n writeFile: KernelFs['writeFile'];\n direct: boolean;\n}\n\nexport interface Peers {\n joined(): Promise<unknown>;\n crowded(): boolean;\n watch(listener: (crowded: boolean) => void): void;\n fresh?(): void;\n}\n\nexport interface KernelFs {\n resolvePath(base: string, path: string): string;\n exclusive?<T>(path: string, run: (held: HeldEntry) => Promise<T>, follow?: boolean): Promise<T>;\n readFile(path: string): Promise<string>;\n readFileBuffer(path: string): Promise<Uint8Array>;\n writeFile(path: string, content: Uint8Array | string, owner?: NewOwner): Promise<void>;\n exists(path: string): Promise<boolean>;\n stat(path: string): Promise<FsStat>;\n lstat(path: string): Promise<FsStat>;\n readdir(path: string): Promise<string[]>;\n readdirStat(path: string): Promise<Array<[string, FsStat | null]>>;\n mkdir(path: string, options?: { recursive?: boolean; owner?: NewOwner }): Promise<void>;\n rm(\n path: string,\n options?: { recursive?: boolean; force?: boolean; file?: boolean }\n ): Promise<void>;\n rename(from: string, to: string): Promise<void>;\n link(from: string, to: string, follow?: boolean): Promise<void>;\n links?(path: string): boolean;\n symlink(target: string, path: string, owner?: NewOwner): Promise<void>;\n readlink(path: string): Promise<string>;\n realpath?(path: string, follow: boolean): Promise<string>;\n createExclusive?(path: string, owner?: NewOwner): Promise<void>;\n copyFile?(from: string, to: string, options: CopyOptions): Promise<void>;\n readonly rootName?: string;\n chown?(path: string, change: OwnerChange): Promise<void>;\n trackOwners?(): void;\n onPeer?(settle: () => Promise<void>): void;\n heldBy?(held: (dir: string) => readonly string[]): void;\n fileId?(path: string): Promise<string>;\n bindId?(path: string): Promise<string | undefined>;\n remoteChanges?(\n listener: (change: {\n paths: readonly string[];\n trees?: readonly string[];\n moved?: { from: string; to: string };\n content?: true;\n outside?: true;\n }) => void\n ): () => void;\n refresh?(trees: readonly string[]): void;\n peers?(): Peers | undefined;\n crossings?(mounted: (path: string) => boolean): void;\n ancestry?(path: string): Promise<Array<Perm | undefined>>;\n hasOwner?(path: string): Promise<boolean>;\n chmod(path: string, mode: number): Promise<void>;\n append?(path: string, bytes: Uint8Array, options?: PwriteOptions): Promise<number>;\n utimes(path: string, atime: Date, mtime: Date): Promise<void>;\n lutimes?(path: string, atime: Date, mtime: Date): Promise<void>;\n pread?(path: string, offset: number, length: number, version?: string): Promise<Uint8Array>;\n pwrite?(\n path: string,\n offset: number,\n bytes: Uint8Array,\n transfer?: boolean,\n options?: PwriteOptions\n ): Promise<void>;\n truncate?(path: string, size: number): Promise<void>;\n protect?(prefixes: readonly string[], versions?: boolean): void;\n unguarded?(): KernelFs;\n noLinks?(): KernelFs;\n mayChange?(path: string, follow: boolean, whole?: boolean): Promise<void>;\n commandTarget?(path: string): Promise<string | undefined>;\n readVersioned?(path: string, cached?: boolean): Promise<{ bytes: Uint8Array; version: string }>;\n version?(path: string, cached?: boolean): Promise<string>;\n opfsCalls?(): number;\n renameWaits?(): {\n acks: number;\n busy: number;\n waits: number;\n queries: number;\n ackMs: readonly number[];\n };\n opfsOps?(): number;\n metaTransactions?(): { reads: number; writes: number } | undefined;\n fileIdWrites?(): number;\n announceHolds?(\n add: ReadonlyArray<[string, string | undefined]>,\n drop: readonly string[],\n wait: boolean\n ): Promise<void>;\n holdCounts?(): { mine: number; peers: number; sent: number; held: string[] } | undefined;\n peerHolds?(listener: (paths: string[]) => void): void;\n rehold?(from: string, to: string): void;\n peerKeeps?(): PeerKeeps | undefined;\n servePeerKeeps?(\n instance: string,\n handlers: {\n keeping(tab: string, paths: readonly string[]): Promise<void>;\n kept(\n tab: string,\n items: ReadonlyArray<{\n path: string;\n hidden: string | undefined;\n fid: string | undefined;\n }>,\n paths: readonly string[]\n ): Promise<void>;\n left(tab: string): void;\n }\n ): void;\n}\n\nexport interface PeerKeeps {\n shared(): boolean;\n heldWithin(\n path: string\n ): Array<{ tab: string; path: string; fid: string | undefined; instance: string | undefined }>;\n ask(\n to: string,\n body: {\n keeping?: { paths: string[] };\n kept?: {\n items: Array<{\n path: string;\n hidden: string | undefined;\n fid: string | undefined;\n }>;\n paths?: string[];\n };\n },\n cap: number\n ): Promise<boolean>;\n}\n\nexport interface PwriteOptions {\n create?: false;\n alone?: true;\n}\n\nexport type RangedOps = Required<Pick<KernelFs, 'pread' | 'pwrite' | 'truncate'>>;\n\nexport function rangedOps(fs: Partial<RangedOps>): RangedOps | undefined {\n const { pread, pwrite, truncate } = fs;\n if (!pread || !pwrite || !truncate) return undefined;\n return { pread: pread.bind(fs), pwrite: pwrite.bind(fs), truncate: truncate.bind(fs) };\n}\n\nexport async function lutimesOf(\n fs: KernelFs,\n path: string,\n atime: Date,\n mtime: Date\n): Promise<void> {\n if (fs.lutimes) return fs.lutimes(path, atime, mtime);\n if ((await fs.lstat(path)).isSymbolicLink) throw fsError('EOPNOTSUPP', path);\n return fs.utimes(path, atime, mtime);\n}\n\nexport function mayLink(fs: { links?(path: string): boolean }, path: string): boolean {\n return fs.links?.(path) ?? true;\n}\n\nexport function modeOption(mode: unknown): number {\n if (!Number.isInteger(mode) || (mode as number) < 0 || (mode as number) > 0o7777) {\n throw fsError('EINVAL', `not a mode: ${String(mode)}`);\n }\n return mode as number;\n}\n\nexport function fsError(code: string, path: string): Error & { code: string } {\n return Object.assign(new Error(`${code}: ${path}`), { code });\n}\n\nexport function normalizePath(path: string): string {\n const parts: string[] = [];\n for (const part of path.split('/')) {\n if (part === '' || part === '.') continue;\n if (part === '..') parts.pop();\n else parts.push(part);\n }\n return `/${parts.join('/')}`;\n}\n\nexport function resolvePath(base: string, path: string): string {\n return normalizePath(path.startsWith('/') ? path : `${base}/${path}`);\n}\n\nexport function inodeOf(path: string): number {\n let h1 = 0xdeadbeef;\n let h2 = 0x41c6ce57;\n for (let i = 0; i < path.length; i++) {\n const c = path.charCodeAt(i);\n h1 = Math.imul(h1 ^ c, 2654435761);\n h2 = Math.imul(h2 ^ c, 1597334677);\n }\n h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);\n h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);\n return 4294967296 * (2097151 & h2) + (h1 >>> 0) || 1;\n}\n\nexport interface Redirect {\n of: string;\n to: string;\n}\n\nexport function redirected(of: string, to: string): Error {\n return Object.assign(fsError('EXDEV', `${of} leads to the mount at ${to}`), {\n redirect: { of: normalizePath(of), to: normalizePath(to) },\n });\n}\n\nexport function redirectOf(err: unknown): Redirect | undefined {\n return (err as { redirect?: Redirect } | null)?.redirect;\n}\n", "export const REPLACING_LOCK = 'slicc-kernel-replacing:v1:';\nexport const REPLACED_WAIT_MS = 150;\n\nexport interface ReplacingLocks {\n request(\n name: string,\n options: { mode: 'shared' | 'exclusive'; ifAvailable?: boolean },\n callback: (lock: unknown) => unknown\n ): Promise<unknown>;\n}\n\nexport function replacingLocks(scope: object = globalThis): ReplacingLocks | undefined {\n const locks = (scope as { navigator?: { locks?: ReplacingLocks } }).navigator?.locks;\n return typeof locks?.request === 'function' ? locks : undefined;\n}\n\nexport function replacingLock(metadata: string, root: string): string {\n return `${REPLACING_LOCK}${metadata}:${root}`;\n}\n\nexport function exclusively<T>(\n locks: ReplacingLocks,\n names: readonly string[],\n run: () => Promise<T>\n): Promise<T> {\n const [name, ...rest] = names;\n if (name === undefined) return run();\n return locks.request(name, { mode: 'exclusive' }, () =>\n exclusively(locks, rest, run)\n ) as Promise<T>;\n}\n\nfunction shared<T>(\n locks: ReplacingLocks,\n names: readonly string[],\n run: (waited: boolean) => Promise<T>,\n waited: boolean\n): Promise<T> {\n const [name, ...rest] = names;\n if (name === undefined) return run(waited);\n const next = (after: boolean) => shared(locks, rest, run, after);\n return locks.request(name, { mode: 'shared', ifAvailable: true }, (lock) =>\n lock ? next(waited) : locks.request(name, { mode: 'shared' }, () => next(true))\n ) as Promise<T>;\n}\n\nexport class PackageLocks {\n private readonly locks: ReplacingLocks | undefined;\n private readonly metadata: string;\n\n constructor(metadata: string, locks = replacingLocks()) {\n this.metadata = metadata;\n this.locks = locks;\n }\n\n loading<T>(roots: readonly string[], run: (waited: boolean) => Promise<T>): Promise<T> {\n if (!this.locks || roots.length === 0) return run(false);\n const names = roots.map((root) => replacingLock(this.metadata, root));\n return shared(this.locks, names, run, false);\n }\n\n holding(root: string): Promise<() => void> {\n const { locks } = this;\n if (!locks) return Promise.resolve(() => {});\n const { promise: held, resolve: release } = Promise.withResolvers<void>();\n return new Promise((granted, failed) => {\n locks\n .request(replacingLock(this.metadata, root), { mode: 'exclusive' }, () => {\n granted(() => release());\n return held;\n })\n .catch(failed);\n });\n }\n}\n", "import { META_DB, type MetaEntry, openMeta } from './fs/meta.ts';\nimport { dirsChannel, type FsNotice } from './fs/notice.ts';\nimport { fsError, normalizePath } from './fs/types.ts';\nimport {\n exclusively,\n REPLACED_WAIT_MS,\n replacingLock,\n replacingLocks,\n} from './kernel/replacing.ts';\n\nexport type { FsNotice };\n\nexport interface MetadataEntry {\n path: string;\n mode?: number;\n uid?: number;\n gid?: number;\n}\n\nexport interface WriteMetadataOptions {\n metadata?: string;\n indexedDB?: IDBFactory;\n}\n\nconst MAX_ID = 0xfffffffe;\n\nfunction id(value: unknown, what: string): number {\n if (!Number.isInteger(value) || (value as number) < 0 || (value as number) > MAX_ID) {\n throw fsError('EINVAL', `not a ${what}: ${String(value)}`);\n }\n return value as number;\n}\n\nfunction checked(entry: MetadataEntry): MetadataEntry {\n if (!String(entry.path).startsWith('/'))\n throw fsError('EINVAL', `not an absolute path: ${entry.path}`);\n const path = normalizePath(String(entry.path));\n if ((entry.uid === undefined) !== (entry.gid === undefined)) {\n throw fsError('EINVAL', `${path}: give both uid and gid, or neither`);\n }\n const mode = entry.mode;\n if (mode !== undefined && (!Number.isInteger(mode) || mode < 0 || mode > 0o7777)) {\n throw fsError('EINVAL', `${path}: not a mode: ${String(mode)}`);\n }\n return {\n path,\n ...(mode !== undefined ? { mode } : {}),\n ...(entry.uid !== undefined ? { uid: id(entry.uid, 'uid'), gid: id(entry.gid, 'gid') } : {}),\n };\n}\n\nasync function present(root: FileSystemDirectoryHandle, path: string): Promise<boolean> {\n const parts = path.split('/').filter(Boolean);\n let dir = root;\n for (const [i, name] of parts.entries()) {\n const last = i === parts.length - 1;\n try {\n dir = await dir.getDirectoryHandle(name);\n } catch {\n if (!last) return false;\n return dir.getFileHandle(name).then(\n () => true,\n () => false\n );\n }\n }\n return true;\n}\n\nexport async function writeMetadata(\n root: FileSystemDirectoryHandle,\n entries: readonly MetadataEntry[],\n options: WriteMetadataOptions = {}\n): Promise<void> {\n const merged = new Map<string, MetadataEntry>();\n for (const want of entries.map(checked))\n merged.set(want.path, { ...merged.get(want.path), ...want });\n const wanted = [...merged.values()];\n for (const { path } of wanted) {\n if (!(await present(root, path))) throw fsError('ENOENT', path);\n }\n const factory = options.indexedDB ?? globalThis.indexedDB;\n const store = await openMeta(options.metadata ?? META_DB, factory);\n const ctimeMs = Date.now();\n try {\n await store.updateMany(\n wanted.map((want) => ({\n path: want.path,\n change: (entry: MetaEntry | undefined): MetaEntry => ({\n ...entry,\n path: want.path,\n ...(want.mode !== undefined ? { mode: want.mode } : {}),\n ...(want.uid !== undefined ? { uid: want.uid, gid: want.gid as number } : {}),\n ctimeMs,\n }),\n }))\n );\n } finally {\n store.close();\n }\n}\n\nexport interface PostFsNoticeOptions {\n metadata?: string;\n}\n\nfunction absolute(list: readonly string[] | undefined): string[] {\n return (list ?? []).map((path) => {\n if (!String(path).startsWith('/')) throw fsError('EINVAL', `not an absolute path: ${path}`);\n return normalizePath(String(path));\n });\n}\n\nexport function postFsNotice(notice: FsNotice, options: PostFsNoticeOptions = {}): void {\n const paths = absolute(notice.paths);\n const trees = absolute(notice.trees);\n if (paths.length === 0 && trees.length === 0) return;\n const channel = new BroadcastChannel(dirsChannel(options.metadata ?? META_DB));\n try {\n channel.postMessage({ paths, trees });\n } finally {\n channel.close();\n }\n}\n\nexport interface ReplacingOptions {\n metadata?: string;\n}\n\nconst HELD = Symbol.for('slicc-kernel.replacing.v1');\n\nfunction realmHeld(): Set<string> {\n const scope = globalThis as { [HELD]?: Set<string> };\n scope[HELD] ??= new Set();\n return scope[HELD];\n}\n\nexport async function replacing<T>(\n paths: readonly string[],\n task: () => T | Promise<T>,\n options: ReplacingOptions = {}\n): Promise<T> {\n if (!Array.isArray(paths)) throw new TypeError('replacing: paths must be an array of paths');\n if (typeof task !== 'function') throw new TypeError('replacing: task must be a function');\n const roots = [...new Set(absolute(paths))].sort();\n const metadata = options.metadata ?? META_DB;\n const held = realmHeld();\n const busy = roots.find((root) => held.has(root));\n if (busy !== undefined) {\n throw fsError('EDEADLK', `${busy} is already being replaced in this realm`);\n }\n const locks = replacingLocks();\n const run = async () => {\n try {\n return await task();\n } finally {\n postFsNotice({ trees: roots }, { metadata });\n if (locks && roots.length > 0) {\n await new Promise((resolve) => setTimeout(resolve, REPLACED_WAIT_MS));\n }\n }\n };\n for (const root of roots) held.add(root);\n try {\n if (!locks) return await run();\n const names = roots.map((root) => replacingLock(metadata, root));\n return await exclusively(locks, names, run);\n } finally {\n for (const root of roots) held.delete(root);\n }\n}\n", "import { postFsNotice } from '@ai-ecoverse/slicc-kernel/meta';\nimport { Emitter } from '../model/emitter.ts';\nimport type { Change, FileEntry, FileEvents, FilePort } from '../model/types.ts';\n\nexport interface KernelFilesOptions {\n skip?: readonly string[];\n hide?: readonly string[];\n interval?: number;\n metadata?: string;\n}\n\ninterface Observer {\n observe(handle: FileSystemHandle, options: { recursive: boolean }): Promise<void>;\n disconnect(): void;\n}\n\ninterface ChangeRecord {\n type: string;\n relativePathComponents: readonly string[];\n relativePathMovedFrom?: readonly string[] | null;\n}\n\ntype ObserverConstructor = new (callback: (records: readonly ChangeRecord[]) => void) => Observer;\n\nfunction split(path: string): string[] {\n return path.split('/').filter(Boolean);\n}\n\nfunction child(dir: string, name: string): string {\n return dir === '/' ? `/${name}` : `${dir}/${name}`;\n}\n\nfunction parentOf(path: string): string {\n return path.slice(0, path.lastIndexOf('/')) || '/';\n}\n\nasync function opened<T>(\n changed: string[],\n path: string,\n parent: string,\n open: (create: boolean) => Promise<T>\n): Promise<T> {\n try {\n return await open(false);\n } catch {\n const made = await open(true);\n changed.push(path, parent);\n return made;\n }\n}\n\nfunction same(a: FileEntry | undefined, b: FileEntry): boolean {\n return a?.kind === b.kind && a.size === b.size && a.modified === b.modified;\n}\n\nexport class KernelFiles extends Emitter<FileEvents> implements FilePort {\n #root: FileSystemDirectoryHandle;\n #skip: Set<string>;\n #hide: Set<string>;\n #interval: number;\n #metadata: string | undefined;\n #entries = new Map<string, FileEntry>();\n #scanned = false;\n #running: Promise<void> | null = null;\n #again = false;\n #timer: ReturnType<typeof setInterval> | null = null;\n #observer: Observer | null = null;\n #pending: ReturnType<typeof setTimeout> | null = null;\n #last = Number.NEGATIVE_INFINITY;\n\n constructor(\n root: FileSystemDirectoryHandle,\n { skip = [], hide = [], interval = 2000, metadata }: KernelFilesOptions = {}\n ) {\n super();\n this.#root = root;\n this.#metadata = metadata;\n this.#skip = new Set(skip);\n this.#hide = new Set(hide);\n this.#interval = interval;\n }\n\n start(): void {\n if (this.#timer || this.#observer) return;\n const Observer = (globalThis as { FileSystemObserver?: ObserverConstructor })\n .FileSystemObserver;\n if (!Observer) {\n this.#poll();\n return;\n }\n const observer = new Observer((records) => this.#observed(records));\n this.#observer = observer;\n observer.observe(this.#root, { recursive: true }).catch(() => {\n if (this.#observer !== observer) return;\n this.#observer = null;\n this.#poll();\n });\n }\n\n #poll(): void {\n this.#timer = setInterval(() => void this.refresh(), this.#interval);\n }\n\n #observed(records: readonly ChangeRecord[]): void {\n if (records.length > 0 && records.every((record) => this.#unseen(record))) return;\n if (this.#pending) return;\n const wait = Math.max(0, this.#last + this.#interval - performance.now());\n this.#pending = setTimeout(() => {\n this.#pending = null;\n this.#last = performance.now();\n void this.refresh();\n }, wait);\n }\n\n #unseen({ type, relativePathComponents, relativePathMovedFrom }: ChangeRecord): boolean {\n if (type === 'unknown' || type === 'errored') return false;\n return [relativePathComponents, relativePathMovedFrom ?? relativePathComponents].every(\n (parts) => this.#beneath(parts)\n );\n }\n\n #beneath(parts: readonly string[]): boolean {\n let path = '';\n for (const [i, part] of parts.entries()) {\n path += `/${part}`;\n if (this.#hide.has(path)) return true;\n if (this.#skip.has(path) && i < parts.length - 1) return true;\n }\n return false;\n }\n\n stop(): void {\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = null;\n if (this.#pending) clearTimeout(this.#pending);\n this.#pending = null;\n this.#observer?.disconnect();\n this.#observer = null;\n }\n\n async #walk(\n dir: FileSystemDirectoryHandle,\n prefix: string,\n out: Map<string, FileEntry>\n ): Promise<void> {\n const values = (\n dir as FileSystemDirectoryHandle & {\n values(): AsyncIterable<FileSystemDirectoryHandle | FileSystemFileHandle>;\n }\n ).values();\n for await (const handle of values) {\n const path = `${prefix}/${handle.name}`;\n if (this.#hide.has(path)) continue;\n if (handle.kind === 'directory') {\n out.set(path, { path, kind: 'directory', size: 0, modified: 0 });\n if (!this.#skip.has(path)) await this.#walk(handle, path, out).catch(() => {});\n } else {\n const file = await handle.getFile().catch(() => null);\n if (file)\n out.set(path, { path, kind: 'file', size: file.size, modified: file.lastModified });\n }\n }\n }\n\n #list(): FileEntry[] {\n return [...this.#entries.values()].sort((a, b) => a.path.localeCompare(b.path));\n }\n\n async #scan(): Promise<void> {\n const next = new Map<string, FileEntry>();\n await this.#walk(this.#root, '', next);\n const touched = [...next.values()].filter(\n (entry) => !same(this.#entries.get(entry.path), entry)\n );\n const gone = [...this.#entries.values()].filter((entry) => !next.has(entry.path));\n const first = !this.#scanned;\n const previous = this.#entries;\n this.#entries = next;\n this.#scanned = true;\n if (first || touched.length + gone.length === 0) return;\n this.emit('files', this.#list());\n for (const entry of [...touched, ...gone]) {\n if (entry.kind === 'file' || previous.get(entry.path)?.kind === 'file')\n this.emit('file', entry.path);\n }\n }\n\n refresh(): Promise<void> {\n if (this.#running !== null) {\n this.#again = true;\n return this.#running;\n }\n this.#running = (async () => {\n do {\n this.#again = false;\n await this.#scan();\n } while (this.#again);\n })().finally(() => {\n this.#running = null;\n });\n return this.#running;\n }\n\n async list(): Promise<readonly FileEntry[]> {\n if (!this.#scanned) await this.refresh();\n return this.#list();\n }\n\n async #parent(path: string): Promise<[FileSystemDirectoryHandle, string]> {\n const parts = split(path);\n const name = parts.pop();\n if (!name) throw new Error(`EISDIR: ${path}`);\n let dir = this.#root;\n for (const part of parts) dir = await dir.getDirectoryHandle(part);\n return [dir, name];\n }\n\n #notice(paths: readonly string[], trees: readonly string[] = []): void {\n if (paths.length + trees.length === 0) return;\n postFsNotice({ paths, trees }, { metadata: this.#metadata });\n }\n\n async read(path: string): Promise<string> {\n try {\n const [dir, name] = await this.#parent(path);\n return await (await (await dir.getFileHandle(name)).getFile()).text();\n } catch {\n throw new Error(`ENOENT: ${path}`);\n }\n }\n\n async write(path: string, text: string): Promise<void> {\n const parts = split(path);\n const name = parts.pop();\n if (!name) throw new Error(`EISDIR: ${path}`);\n const changed: string[] = [];\n try {\n let dir = this.#root;\n let at = '/';\n for (const part of parts) {\n const parent = dir;\n const next = child(at, part);\n dir = await opened(changed, next, at, (create) =>\n parent.getDirectoryHandle(part, { create })\n );\n at = next;\n }\n const file = child(at, name);\n const handle = await opened(changed, file, at, (create) =>\n dir.getFileHandle(name, { create })\n );\n const writable = await handle.createWritable();\n await writable.write(text);\n await writable.close();\n changed.push(file);\n } finally {\n this.#notice([...new Set(changed)]);\n }\n await this.refresh();\n }\n\n async remove(path: string): Promise<void> {\n let target: string;\n let tree: boolean;\n try {\n const [dir, name] = await this.#parent(path);\n target = `/${split(path).join('/')}`;\n tree = await dir.getFileHandle(name).then(\n () => false,\n () => true\n );\n await dir.removeEntry(name, { recursive: true });\n } catch {\n throw new Error(`ENOENT: ${path}`);\n }\n if (tree) this.#notice([parentOf(target)], [target]);\n else this.#notice([target, parentOf(target)]);\n await this.refresh();\n }\n\n changes(): readonly Change[] {\n return [];\n }\n\n accept(): void {}\n\n async revert(): Promise<void> {}\n}\n", "import { Emitter } from '../model/emitter.ts';\nimport type {\n Agent,\n AgentEvents,\n AgentPort,\n BrowserEvents,\n BrowserPort,\n BrowserTab,\n FrozenCone,\n Memory,\n MemoryEvents,\n MemoryPort,\n Message,\n MonitorEvents,\n MonitorPort,\n MonitorSnapshot,\n SlashCommand,\n SprinkleEvents,\n SprinklePort,\n TrayEvents,\n TrayPort,\n TrayStatus,\n UserMessage,\n} from '../model/types.ts';\n\nfunction missing(what: string): Error {\n return new Error(`No ${what} in this SLICC yet`);\n}\n\nexport class IdleAgent extends Emitter<AgentEvents> implements AgentPort {\n list(): readonly Agent[] {\n return [];\n }\n active(): string {\n return '';\n }\n select(): void {}\n messages(): readonly Message[] {\n return [];\n }\n async send(): Promise<void> {\n throw missing('agent');\n }\n stop(): void {}\n busy(): boolean {\n return false;\n }\n queue(): readonly UserMessage[] {\n return [];\n }\n unqueue(): void {}\n suggestion(): string | null {\n return null;\n }\n answer(): void {}\n resolveLick(): void {}\n compact(): void {}\n clear(): void {}\n setModel(): void {}\n setThinking(): void {}\n async older(): Promise<readonly Message[]> {\n return [];\n }\n commands(): readonly SlashCommand[] {\n return [];\n }\n async ready(): Promise<void> {}\n createScoop(): Agent {\n throw missing('agent');\n }\n frozen(): readonly FrozenCone[] {\n return [];\n }\n freeze(): void {}\n thaw(): Agent | null {\n return null;\n }\n discard(): void {}\n}\n\nexport class IdleBrowser extends Emitter<BrowserEvents> implements BrowserPort {\n list(): readonly BrowserTab[] {\n return [];\n }\n active(): string | null {\n return null;\n }\n activate(): void {}\n open(): BrowserTab {\n throw missing('browser');\n }\n navigate(): void {}\n close(): void {}\n async screenshot(): Promise<string> {\n throw missing('browser');\n }\n}\n\nexport class IdleMemory extends Emitter<MemoryEvents> implements MemoryPort {\n list(): readonly Memory[] {\n return [];\n }\n save(): Memory {\n throw missing('memory');\n }\n remove(): void {}\n}\n\nexport class IdleMonitor extends Emitter<MonitorEvents> implements MonitorPort {\n snapshot(): MonitorSnapshot {\n return { vitals: [], alerts: [], sections: [], updatedAt: 0 };\n }\n resync(): void {}\n}\n\nexport class IdleSprinkles extends Emitter<SprinkleEvents> implements SprinklePort {\n list(): readonly [] {\n return [];\n }\n send(): void {}\n}\n\nexport class IdleTray extends Emitter<TrayEvents> implements TrayPort {\n status(): TrayStatus {\n return {\n name: 'slicc',\n kind: 'hosted',\n connection: 'offline',\n role: 'none',\n followers: [],\n spent: 0,\n rate: 0,\n budget: { percent: 0, window: 'daily', resets: '' },\n joinUrl: '',\n };\n }\n reconnect(): void {}\n disconnect(): void {}\n}\n", "import type { TerminalBackend } from '../backend.ts';\nimport { cwdPromptCommand, kernelBackend, type TerminalKernel } from '../kernel-backend.ts';\nimport { Emitter } from '../model/emitter.ts';\nimport type { TerminalEvents, TerminalInfo, TerminalPort } from '../model/types.ts';\n\nexport interface KernelTerminalsOptions {\n argv?: string[];\n cwd?: string;\n user?: string | number;\n env?: Record<string, string>;\n open?: number;\n}\n\nexport class KernelTerminals extends Emitter<TerminalEvents> implements TerminalPort {\n #kernel: TerminalKernel;\n #argv: string[];\n #cwd: string;\n #user: string | number | undefined;\n #env: Record<string, string> | undefined;\n #terminals: TerminalInfo[] = [];\n #backends = new Map<string, TerminalBackend>();\n #next = 1;\n\n constructor(\n kernel: TerminalKernel,\n { argv = ['bash', '-i'], cwd = '/home', user, env, open = 1 }: KernelTerminalsOptions = {}\n ) {\n super();\n this.#kernel = kernel;\n this.#argv = argv;\n this.#cwd = cwd;\n this.#user = user;\n this.#env = env;\n for (let i = 0; i < open; i++) this.open();\n }\n\n list(): readonly TerminalInfo[] {\n return this.#terminals.map((terminal) => ({ ...terminal }));\n }\n\n open({\n cwd = this.#cwd,\n agentId = null,\n }: {\n cwd?: string;\n agentId?: string | null;\n } = {}): TerminalInfo {\n const terminal: TerminalInfo = {\n id: `term-${this.#next}`,\n title: `${this.#argv[0]} ${this.#next}`,\n cwd,\n agentId,\n };\n this.#next += 1;\n this.#terminals.push(terminal);\n this.emit('terminals', this.list());\n return { ...terminal };\n }\n\n close(id: string): void {\n this.#terminals = this.#terminals.filter((terminal) => terminal.id !== id);\n this.#backends.delete(id);\n this.emit('terminals', this.list());\n }\n\n #move(id: string, cwd: string): void {\n const terminal = this.#terminals.find((candidate) => candidate.id === id);\n if (!terminal || terminal.cwd === cwd) return;\n terminal.cwd = cwd;\n this.emit('terminals', this.list());\n }\n\n backend(id: string): TerminalBackend {\n const terminal = this.#terminals.find((candidate) => candidate.id === id);\n if (!terminal) throw new Error(`No terminal ${id}`);\n let backend = this.#backends.get(id);\n if (!backend) {\n backend = kernelBackend(this.#kernel, {\n argv: this.#argv,\n cwd: terminal.cwd,\n user: this.#user,\n env: { PROMPT_COMMAND: cwdPromptCommand, ...this.#env },\n onCwd: (cwd) => this.#move(id, cwd),\n });\n this.#backends.set(id, backend);\n }\n return backend;\n }\n}\n", "import { clock } from '../dummy/clock.ts';\nimport { DummySettings, dummySettingsKey } from '../dummy/settings.ts';\nimport type { TerminalKernel } from '../kernel-backend.ts';\nimport type { Settings, SliccModel } from '../model/types.ts';\nimport { KernelFiles, type KernelFilesOptions } from './files.ts';\nimport {\n IdleAgent,\n IdleBrowser,\n IdleMemory,\n IdleMonitor,\n IdleSprinkles,\n IdleTray,\n} from './idle.ts';\nimport { KernelTerminals, type KernelTerminalsOptions } from './terminals.ts';\n\nexport interface KernelModelOptions {\n kernel: TerminalKernel;\n root: FileSystemDirectoryHandle;\n files?: KernelFilesOptions;\n terminals?: KernelTerminalsOptions;\n storage?: Storage | null;\n}\n\nexport interface KernelModel extends SliccModel {\n files: KernelFiles;\n terminals: KernelTerminals;\n}\n\nexport const settingsKey = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n pushToTalk: false,\n showThinking: false,\n diffStyle: 'unified',\n};\n\nexport function idleModel(storage: Storage | null = null): Omit<SliccModel, 'files' | 'terminals'> {\n return {\n agent: new IdleAgent(),\n browser: new IdleBrowser(),\n settings: new DummySettings(defaults, [], [], storage, clock(0), [\n settingsKey,\n dummySettingsKey,\n ]),\n memory: new IdleMemory(),\n monitor: new IdleMonitor(),\n sprinkles: new IdleSprinkles(),\n tray: new IdleTray(),\n };\n}\n\nexport function createKernelModel({\n kernel,\n root,\n files,\n terminals,\n storage = null,\n}: KernelModelOptions): KernelModel {\n const model: KernelModel = {\n ...idleModel(storage),\n files: new KernelFiles(root, files),\n terminals: new KernelTerminals(kernel, terminals),\n };\n model.files.start();\n return model;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;AEkEO,IAAM,UAAU;AC7DhB,SAAS,YAAY,UAA0B;AACpD,SAAO,qBAAqB,QAAQ;AACtC;ACkOO,SAAS,QAAQ,MAAc,MAAwC;AAC5E,SAAO,OAAO,OAAO,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC;AAC9D;AAEO,SAAS,cAAc,MAAsB;AAClD,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,KAAK,MAAM,GAAG,GAAG;AAClC,QAAI,SAAS,MAAM,SAAS,IAAK;AACjC,QAAI,SAAS,KAAM,OAAM,IAAI;QACxB,OAAM,KAAK,IAAI;EACtB;AACA,SAAO,IAAI,MAAM,KAAK,GAAG,CAAC;AAC5B;AE3IA,SAAS,SAAS,MAA+C;AAC/D,UAAQ,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS;AAChC,QAAI,CAAC,OAAO,IAAI,EAAE,WAAW,GAAG,EAAG,OAAM,QAAQ,UAAU,yBAAyB,IAAI,EAAE;AAC1F,WAAO,cAAc,OAAO,IAAI,CAAC;EACnC,CAAC;AACH;AAEO,SAAS,aAAa,QAAkB,UAA+B,CAAC,GAAS;AACtF,QAAM,QAAQ,SAAS,OAAO,KAAK;AACnC,QAAM,QAAQ,SAAS,OAAO,KAAK;AACnC,MAAI,MAAM,WAAW,KAAK,MAAM,WAAW,EAAG;AAC9C,QAAM,UAAU,IAAI,iBAAiB,YAAY,QAAQ,YAAY,OAAO,CAAC;AAC7E,MAAI;AACF,YAAQ,YAAY,EAAE,OAAO,MAAM,CAAC;EACtC,UAAA;AACE,YAAQ,MAAM;EAChB;AACF;;;ACnGA,SAAS,MAAM,MAAwB;AACrC,SAAO,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AACvC;AAEA,SAAS,MAAM,KAAa,MAAsB;AAChD,SAAO,QAAQ,MAAM,IAAI,IAAI,KAAK,GAAG,GAAG,IAAI,IAAI;AAClD;AAEA,SAAS,SAAS,MAAsB;AACtC,SAAO,KAAK,MAAM,GAAG,KAAK,YAAY,GAAG,CAAC,KAAK;AACjD;AAEA,eAAe,OACb,SACA,MACA,QACA,MACY;AACZ,MAAI;AACF,WAAO,MAAM,KAAK,KAAK;AAAA,EACzB,QAAQ;AACN,UAAM,OAAO,MAAM,KAAK,IAAI;AAC5B,YAAQ,KAAK,MAAM,MAAM;AACzB,WAAO;AAAA,EACT;AACF;AAEA,SAAS,KAAK,GAA0B,GAAuB;AAC7D,SAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE;AACrE;AAEO,IAAM,cAAN,cAA0B,QAAwC;AAAA,EACvE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW,oBAAI,IAAuB;AAAA,EACtC,WAAW;AAAA,EACX,WAAiC;AAAA,EACjC,SAAS;AAAA,EACT,SAAgD;AAAA,EAChD,YAA6B;AAAA,EAC7B,WAAiD;AAAA,EACjD,QAAQ,OAAO;AAAA,EAEf,YACE,MACA,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,WAAW,KAAM,SAAS,IAAwB,CAAC,GAC3E;AACA,UAAM;AACN,SAAK,QAAQ;AACb,SAAK,YAAY;AACjB,SAAK,QAAQ,IAAI,IAAI,IAAI;AACzB,SAAK,QAAQ,IAAI,IAAI,IAAI;AACzB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,UAAU,KAAK,UAAW;AACnC,UAAM,WAAY,WACf;AACH,QAAI,CAAC,UAAU;AACb,WAAK,MAAM;AACX;AAAA,IACF;AACA,UAAM,WAAW,IAAI,SAAS,CAAC,YAAY,KAAK,UAAU,OAAO,CAAC;AAClE,SAAK,YAAY;AACjB,aAAS,QAAQ,KAAK,OAAO,EAAE,WAAW,KAAK,CAAC,EAAE,MAAM,MAAM;AAC5D,UAAI,KAAK,cAAc,SAAU;AACjC,WAAK,YAAY;AACjB,WAAK,MAAM;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS,YAAY,MAAM,KAAK,KAAK,QAAQ,GAAG,KAAK,SAAS;AAAA,EACrE;AAAA,EAEA,UAAU,SAAwC;AAChD,QAAI,QAAQ,SAAS,KAAK,QAAQ,MAAM,CAAC,WAAW,KAAK,QAAQ,MAAM,CAAC,EAAG;AAC3E,QAAI,KAAK,SAAU;AACnB,UAAM,OAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK,YAAY,YAAY,IAAI,CAAC;AACxE,SAAK,WAAW,WAAW,MAAM;AAC/B,WAAK,WAAW;AAChB,WAAK,QAAQ,YAAY,IAAI;AAC7B,WAAK,KAAK,QAAQ;AAAA,IACpB,GAAG,IAAI;AAAA,EACT;AAAA,EAEA,QAAQ,EAAE,MAAM,wBAAwB,sBAAsB,GAA0B;AACtF,QAAI,SAAS,aAAa,SAAS,UAAW,QAAO;AACrD,WAAO,CAAC,wBAAwB,yBAAyB,sBAAsB,EAAE;AAAA,MAC/E,CAAC,UAAU,KAAK,SAAS,KAAK;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,SAAS,OAAmC;AAC1C,QAAI,OAAO;AACX,eAAW,CAAC,GAAG,IAAI,KAAK,MAAM,QAAQ,GAAG;AACvC,cAAQ,IAAI,IAAI;AAChB,UAAI,KAAK,MAAM,IAAI,IAAI,EAAG,QAAO;AACjC,UAAI,KAAK,MAAM,IAAI,IAAI,KAAK,IAAI,MAAM,SAAS,EAAG,QAAO;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,OAAQ,eAAc,KAAK,MAAM;AAC1C,SAAK,SAAS;AACd,QAAI,KAAK,SAAU,cAAa,KAAK,QAAQ;AAC7C,SAAK,WAAW;AAChB,SAAK,WAAW,WAAW;AAC3B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,MACJ,KACA,QACA,KACe;AACf,UAAM,SACJ,IAGA,OAAO;AACT,qBAAiB,UAAU,QAAQ;AACjC,YAAM,OAAO,GAAG,MAAM,IAAI,OAAO,IAAI;AACrC,UAAI,KAAK,MAAM,IAAI,IAAI,EAAG;AAC1B,UAAI,OAAO,SAAS,aAAa;AAC/B,YAAI,IAAI,MAAM,EAAE,MAAM,MAAM,aAAa,MAAM,GAAG,UAAU,EAAE,CAAC;AAC/D,YAAI,CAAC,KAAK,MAAM,IAAI,IAAI,EAAG,OAAM,KAAK,MAAM,QAAQ,MAAM,GAAG,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC/E,OAAO;AACL,cAAM,OAAO,MAAM,OAAO,QAAQ,EAAE,MAAM,MAAM,IAAI;AACpD,YAAI;AACF,cAAI,IAAI,MAAM,EAAE,MAAM,MAAM,QAAQ,MAAM,KAAK,MAAM,UAAU,KAAK,aAAa,CAAC;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAqB;AACnB,WAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA,EAChF;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,OAAO,oBAAI,IAAuB;AACxC,UAAM,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI;AACrC,UAAM,UAAU,CAAC,GAAG,KAAK,OAAO,CAAC,EAAE;AAAA,MACjC,CAAC,UAAU,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,IAAI,GAAG,KAAK;AAAA,IACvD;AACA,UAAM,OAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,MAAM,IAAI,CAAC;AAChF,UAAM,QAAQ,CAAC,KAAK;AACpB,UAAM,WAAW,KAAK;AACtB,SAAK,WAAW;AAChB,SAAK,WAAW;AAChB,QAAI,SAAS,QAAQ,SAAS,KAAK,WAAW,EAAG;AACjD,SAAK,KAAK,SAAS,KAAK,MAAM,CAAC;AAC/B,eAAW,SAAS,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG;AACzC,UAAI,MAAM,SAAS,UAAU,SAAS,IAAI,MAAM,IAAI,GAAG,SAAS;AAC9D,aAAK,KAAK,QAAQ,MAAM,IAAI;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,UAAyB;AACvB,QAAI,KAAK,aAAa,MAAM;AAC1B,WAAK,SAAS;AACd,aAAO,KAAK;AAAA,IACd;AACA,SAAK,YAAY,YAAY;AAC3B,SAAG;AACD,aAAK,SAAS;AACd,cAAM,KAAK,MAAM;AAAA,MACnB,SAAS,KAAK;AAAA,IAChB,GAAG,EAAE,QAAQ,MAAM;AACjB,WAAK,WAAW;AAAA,IAClB,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,OAAsC;AAC1C,QAAI,CAAC,KAAK,SAAU,OAAM,KAAK,QAAQ;AACvC,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,MAAM,QAAQ,MAA4D;AACxE,UAAM,QAAQ,MAAM,IAAI;AACxB,UAAM,OAAO,MAAM,IAAI;AACvB,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,WAAW,IAAI,EAAE;AAC5C,QAAI,MAAM,KAAK;AACf,eAAW,QAAQ,MAAO,OAAM,MAAM,IAAI,mBAAmB,IAAI;AACjE,WAAO,CAAC,KAAK,IAAI;AAAA,EACnB;AAAA,EAEA,QAAQ,OAA0B,QAA2B,CAAC,GAAS;AACrE,QAAI,MAAM,SAAS,MAAM,WAAW,EAAG;AACvC,iBAAa,EAAE,OAAO,MAAM,GAAG,EAAE,UAAU,KAAK,UAAU,CAAC;AAAA,EAC7D;AAAA,EAEA,MAAM,KAAK,MAA+B;AACxC,QAAI;AACF,YAAM,CAAC,KAAK,IAAI,IAAI,MAAM,KAAK,QAAQ,IAAI;AAC3C,aAAO,OAAO,OAAO,MAAM,IAAI,cAAc,IAAI,GAAG,QAAQ,GAAG,KAAK;AAAA,IACtE,QAAQ;AACN,YAAM,IAAI,MAAM,WAAW,IAAI,EAAE;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,MAAc,MAA6B;AACrD,UAAM,QAAQ,MAAM,IAAI;AACxB,UAAM,OAAO,MAAM,IAAI;AACvB,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,WAAW,IAAI,EAAE;AAC5C,UAAM,UAAoB,CAAC;AAC3B,QAAI;AACF,UAAI,MAAM,KAAK;AACf,UAAI,KAAK;AACT,iBAAW,QAAQ,OAAO;AACxB,cAAM,SAAS;AACf,cAAM,OAAO,MAAM,IAAI,IAAI;AAC3B,cAAM,MAAM;AAAA,UAAO;AAAA,UAAS;AAAA,UAAM;AAAA,UAAI,CAAC,WACrC,OAAO,mBAAmB,MAAM,EAAE,OAAO,CAAC;AAAA,QAC5C;AACA,aAAK;AAAA,MACP;AACA,YAAM,OAAO,MAAM,IAAI,IAAI;AAC3B,YAAM,SAAS,MAAM;AAAA,QAAO;AAAA,QAAS;AAAA,QAAM;AAAA,QAAI,CAAC,WAC9C,IAAI,cAAc,MAAM,EAAE,OAAO,CAAC;AAAA,MACpC;AACA,YAAM,WAAW,MAAM,OAAO,eAAe;AAC7C,YAAM,SAAS,MAAM,IAAI;AACzB,YAAM,SAAS,MAAM;AACrB,cAAQ,KAAK,IAAI;AAAA,IACnB,UAAE;AACA,WAAK,QAAQ,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC;AAAA,IACpC;AACA,UAAM,KAAK,QAAQ;AAAA,EACrB;AAAA,EAEA,MAAM,OAAO,MAA6B;AACxC,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,YAAM,CAAC,KAAK,IAAI,IAAI,MAAM,KAAK,QAAQ,IAAI;AAC3C,eAAS,IAAI,MAAM,IAAI,EAAE,KAAK,GAAG,CAAC;AAClC,aAAO,MAAM,IAAI,cAAc,IAAI,EAAE;AAAA,QACnC,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AACA,YAAM,IAAI,YAAY,MAAM,EAAE,WAAW,KAAK,CAAC;AAAA,IACjD,QAAQ;AACN,YAAM,IAAI,MAAM,WAAW,IAAI,EAAE;AAAA,IACnC;AACA,QAAI,KAAM,MAAK,QAAQ,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;AAAA,QAC9C,MAAK,QAAQ,CAAC,QAAQ,SAAS,MAAM,CAAC,CAAC;AAC5C,UAAM,KAAK,QAAQ;AAAA,EACrB;AAAA,EAEA,UAA6B;AAC3B,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,SAAe;AAAA,EAAC;AAAA,EAEhB,MAAM,SAAwB;AAAA,EAAC;AACjC;;;ACtQA,SAAS,QAAQ,MAAqB;AACpC,SAAO,IAAI,MAAM,MAAM,IAAI,oBAAoB;AACjD;AAEO,IAAM,YAAN,cAAwB,QAA0C;AAAA,EACvE,OAAyB;AACvB,WAAO,CAAC;AAAA,EACV;AAAA,EACA,SAAiB;AACf,WAAO;AAAA,EACT;AAAA,EACA,SAAe;AAAA,EAAC;AAAA,EAChB,WAA+B;AAC7B,WAAO,CAAC;AAAA,EACV;AAAA,EACA,MAAM,OAAsB;AAC1B,UAAM,QAAQ,OAAO;AAAA,EACvB;AAAA,EACA,OAAa;AAAA,EAAC;AAAA,EACd,OAAgB;AACd,WAAO;AAAA,EACT;AAAA,EACA,QAAgC;AAC9B,WAAO,CAAC;AAAA,EACV;AAAA,EACA,UAAgB;AAAA,EAAC;AAAA,EACjB,aAA4B;AAC1B,WAAO;AAAA,EACT;AAAA,EACA,SAAe;AAAA,EAAC;AAAA,EAChB,cAAoB;AAAA,EAAC;AAAA,EACrB,UAAgB;AAAA,EAAC;AAAA,EACjB,QAAc;AAAA,EAAC;AAAA,EACf,WAAiB;AAAA,EAAC;AAAA,EAClB,cAAoB;AAAA,EAAC;AAAA,EACrB,MAAM,QAAqC;AACzC,WAAO,CAAC;AAAA,EACV;AAAA,EACA,WAAoC;AAClC,WAAO,CAAC;AAAA,EACV;AAAA,EACA,MAAM,QAAuB;AAAA,EAAC;AAAA,EAC9B,cAAqB;AACnB,UAAM,QAAQ,OAAO;AAAA,EACvB;AAAA,EACA,SAAgC;AAC9B,WAAO,CAAC;AAAA,EACV;AAAA,EACA,SAAe;AAAA,EAAC;AAAA,EAChB,OAAqB;AACnB,WAAO;AAAA,EACT;AAAA,EACA,UAAgB;AAAA,EAAC;AACnB;AAEO,IAAM,cAAN,cAA0B,QAA8C;AAAA,EAC7E,OAA8B;AAC5B,WAAO,CAAC;AAAA,EACV;AAAA,EACA,SAAwB;AACtB,WAAO;AAAA,EACT;AAAA,EACA,WAAiB;AAAA,EAAC;AAAA,EAClB,OAAmB;AACjB,UAAM,QAAQ,SAAS;AAAA,EACzB;AAAA,EACA,WAAiB;AAAA,EAAC;AAAA,EAClB,QAAc;AAAA,EAAC;AAAA,EACf,MAAM,aAA8B;AAClC,UAAM,QAAQ,SAAS;AAAA,EACzB;AACF;AAEO,IAAM,aAAN,cAAyB,QAA4C;AAAA,EAC1E,OAA0B;AACxB,WAAO,CAAC;AAAA,EACV;AAAA,EACA,OAAe;AACb,UAAM,QAAQ,QAAQ;AAAA,EACxB;AAAA,EACA,SAAe;AAAA,EAAC;AAClB;AAEO,IAAM,cAAN,cAA0B,QAA8C;AAAA,EAC7E,WAA4B;AAC1B,WAAO,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,WAAW,EAAE;AAAA,EAC9D;AAAA,EACA,SAAe;AAAA,EAAC;AAClB;AAEO,IAAM,gBAAN,cAA4B,QAAgD;AAAA,EACjF,OAAoB;AAClB,WAAO,CAAC;AAAA,EACV;AAAA,EACA,OAAa;AAAA,EAAC;AAChB;AAEO,IAAM,WAAN,cAAuB,QAAwC;AAAA,EACpE,SAAqB;AACnB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW,CAAC;AAAA,MACZ,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ,EAAE,SAAS,GAAG,QAAQ,SAAS,QAAQ,GAAG;AAAA,MAClD,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,YAAkB;AAAA,EAAC;AAAA,EACnB,aAAmB;AAAA,EAAC;AACtB;;;AC7HO,IAAM,kBAAN,cAA8B,QAAgD;AAAA,EACnF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAA6B,CAAC;AAAA,EAC9B,YAAY,oBAAI,IAA6B;AAAA,EAC7C,QAAQ;AAAA,EAER,YACE,QACA,EAAE,OAAO,CAAC,QAAQ,IAAI,GAAG,MAAM,SAAS,MAAM,KAAK,OAAO,EAAE,IAA4B,CAAC,GACzF;AACA,UAAM;AACN,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,SAAK,OAAO;AACZ,aAAS,IAAI,GAAG,IAAI,MAAM,IAAK,MAAK,KAAK;AAAA,EAC3C;AAAA,EAEA,OAAgC;AAC9B,WAAO,KAAK,WAAW,IAAI,CAAC,cAAc,EAAE,GAAG,SAAS,EAAE;AAAA,EAC5D;AAAA,EAEA,KAAK;AAAA,IACH,MAAM,KAAK;AAAA,IACX,UAAU;AAAA,EACZ,IAGI,CAAC,GAAiB;AACpB,UAAM,WAAyB;AAAA,MAC7B,IAAI,QAAQ,KAAK,KAAK;AAAA,MACtB,OAAO,GAAG,KAAK,MAAM,CAAC,CAAC,IAAI,KAAK,KAAK;AAAA,MACrC;AAAA,MACA;AAAA,IACF;AACA,SAAK,SAAS;AACd,SAAK,WAAW,KAAK,QAAQ;AAC7B,SAAK,KAAK,aAAa,KAAK,KAAK,CAAC;AAClC,WAAO,EAAE,GAAG,SAAS;AAAA,EACvB;AAAA,EAEA,MAAM,IAAkB;AACtB,SAAK,aAAa,KAAK,WAAW,OAAO,CAAC,aAAa,SAAS,OAAO,EAAE;AACzE,SAAK,UAAU,OAAO,EAAE;AACxB,SAAK,KAAK,aAAa,KAAK,KAAK,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,IAAY,KAAmB;AACnC,UAAM,WAAW,KAAK,WAAW,KAAK,CAAC,cAAc,UAAU,OAAO,EAAE;AACxE,QAAI,CAAC,YAAY,SAAS,QAAQ,IAAK;AACvC,aAAS,MAAM;AACf,SAAK,KAAK,aAAa,KAAK,KAAK,CAAC;AAAA,EACpC;AAAA,EAEA,QAAQ,IAA6B;AACnC,UAAM,WAAW,KAAK,WAAW,KAAK,CAAC,cAAc,UAAU,OAAO,EAAE;AACxE,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,eAAe,EAAE,EAAE;AAClD,QAAI,UAAU,KAAK,UAAU,IAAI,EAAE;AACnC,QAAI,CAAC,SAAS;AACZ,gBAAU,cAAc,KAAK,SAAS;AAAA,QACpC,MAAM,KAAK;AAAA,QACX,KAAK,SAAS;AAAA,QACd,MAAM,KAAK;AAAA,QACX,KAAK,EAAE,gBAAgB,kBAAkB,GAAG,KAAK,KAAK;AAAA,QACtD,OAAO,CAAC,QAAQ,KAAK,MAAM,IAAI,GAAG;AAAA,MACpC,CAAC;AACD,WAAK,UAAU,IAAI,IAAI,OAAO;AAAA,IAChC;AACA,WAAO;AAAA,EACT;AACF;;;AC5DO,IAAM,cAAc;AAE3B,IAAM,WAAqB;AAAA,EACzB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,WAAW;AACb;AAEO,SAAS,UAAU,UAA0B,MAA+C;AACjG,SAAO;AAAA,IACL,OAAO,IAAI,UAAU;AAAA,IACrB,SAAS,IAAI,YAAY;AAAA,IACzB,UAAU,IAAI,cAAc,UAAU,CAAC,GAAG,CAAC,GAAG,SAAS,MAAM,CAAC,GAAG;AAAA,MAC/D;AAAA,MACA;AAAA,IACF,CAAC;AAAA,IACD,QAAQ,IAAI,WAAW;AAAA,IACvB,SAAS,IAAI,YAAY;AAAA,IACzB,WAAW,IAAI,cAAc;AAAA,IAC7B,MAAM,IAAI,SAAS;AAAA,EACrB;AACF;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAAoC;AAClC,QAAM,QAAqB;AAAA,IACzB,GAAG,UAAU,OAAO;AAAA,IACpB,OAAO,IAAI,YAAY,MAAM,KAAK;AAAA,IAClC,WAAW,IAAI,gBAAgB,QAAQ,SAAS;AAAA,EAClD;AACA,QAAM,MAAM,MAAM;AAClB,SAAO;AACT;",
6
6
  "names": []
7
7
  }
package/dist/slicc-ui.js CHANGED
@@ -22605,9 +22605,9 @@ function cleanupStaleAnnotationItem(item2) {
22605
22605
 
22606
22606
  // node_modules/@pierre/diffs/dist/highlighter/languages/areLanguagesAttached.js
22607
22607
  function areLanguagesAttached(languages, highlighter2) {
22608
- const names2 = Array.isArray(languages) ? languages : [languages];
22609
- if (highlighter2 != null) return highlighter2.hasLoadedLanguages(names2);
22610
- return names2.every((name) => name === "text" || name === "ansi" || AttachedLanguages.has(name));
22608
+ const names3 = Array.isArray(languages) ? languages : [languages];
22609
+ if (highlighter2 != null) return highlighter2.hasLoadedLanguages(names3);
22610
+ return names3.every((name) => name === "text" || name === "ansi" || AttachedLanguages.has(name));
22611
22611
  }
22612
22612
 
22613
22613
  // node_modules/@pierre/diffs/dist/utils/areFileRenderOptionsEqual.js
@@ -47742,7 +47742,7 @@ function withheldText(reason, prefix = "Not offered") {
47742
47742
  }
47743
47743
  var unreachable = (items) => items.some((item2) => item2.registry === "unreachable");
47744
47744
  var packageRunning = /* @__PURE__ */ new Set(["installing", "updating", "removing"]);
47745
- var list5 = (names2) => new Intl.ListFormat("en", { type: "conjunction" }).format(names2);
47745
+ var list5 = (names3) => new Intl.ListFormat("en", { type: "conjunction" }).format(names3);
47746
47746
  function requiresText(item2, all2) {
47747
47747
  const needs = (item2.requires ?? []).map((id) => {
47748
47748
  const found = all2.find((other) => other.id === id);
@@ -49616,6 +49616,34 @@ var SliccApp = class extends ThemedElement {
49616
49616
  }
49617
49617
  };
49618
49618
 
49619
+ // src/app/command-display.ts
49620
+ var controls2 = /[\p{Cc}\p{Cf}\p{Default_Ignorable_Code_Point}\u2028\u2029]/u;
49621
+ var notation = (char) => `U+${char.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`;
49622
+ var names2 = {
49623
+ "\r": ["carriage return", "CR"],
49624
+ "\n": ["line feed", "LF"],
49625
+ " ": ["tab", "TAB"],
49626
+ "\x7F": ["delete", "DEL"]
49627
+ };
49628
+ function displayCommand(command3) {
49629
+ return command3.replace(new RegExp(controls2.source, "gu"), (char) => {
49630
+ const code3 = char.codePointAt(0);
49631
+ if (code3 < 32) return String.fromCodePoint(9216 + code3) + (code3 === 10 ? "\n" : "");
49632
+ if (code3 === 127) return "\u2421";
49633
+ return notation(char);
49634
+ });
49635
+ }
49636
+ function commandText(command3, multiline = true) {
49637
+ return command3.split(new RegExp(`(${controls2.source})`, "gu")).map((part2) => {
49638
+ if (!controls2.test(part2)) return part2;
49639
+ const code3 = notation(part2);
49640
+ const [name, short2] = names2[part2] ?? [code3, ""];
49641
+ const label = short2 ? `${name} (${code3})` : code3;
49642
+ const extra = part2.codePointAt(0) < 32 ? short2 || code3 : short2;
49643
+ return b`<span class="control" role="img" aria-label=${label} title=${label} data-label=${extra}>${displayCommand(part2).trimEnd()}</span>${part2 === "\n" && multiline ? "\n" : ""}`;
49644
+ });
49645
+ }
49646
+
49619
49647
  // src/app/approvals.ts
49620
49648
  function countdown(ms) {
49621
49649
  const seconds = Math.max(0, Math.ceil(ms / 1e3));
@@ -49869,7 +49897,7 @@ function truncated(approval) {
49869
49897
  return approval.kind === "guest-tool" && !approval.summary.trim() && !!approval.argsTruncated;
49870
49898
  }
49871
49899
  function hintOf(approval) {
49872
- return approval.kind === "sudo" ? alwaysHint(approval) : guestAlwaysHint(approval);
49900
+ return approval.kind === "sudo" ? commandText(alwaysHint(approval), false) : guestAlwaysHint(approval);
49873
49901
  }
49874
49902
  var tooLong = "This request is too long to show in full, so it can\u2019t be allowed.";
49875
49903
  function guestWaiting(approval) {
@@ -49970,6 +49998,20 @@ var SliccApprovals = class extends ModelElement {
49970
49998
  white-space: pre-wrap;
49971
49999
  overflow-wrap: anywhere;
49972
50000
  }
50001
+ .control {
50002
+ display: inline-flex;
50003
+ align-items: baseline;
50004
+ gap: var(--swc-spacing-50);
50005
+ padding-inline: var(--swc-spacing-50);
50006
+ border-radius: var(--swc-corner-radius-75);
50007
+ background: var(--swc-gray-300);
50008
+ color: var(--swc-gray-900);
50009
+ font-size: var(--swc-font-size-100);
50010
+ font-weight: var(--swc-bold-font-weight);
50011
+ }
50012
+ .control[data-label]:not([data-label=''])::after {
50013
+ content: attr(data-label);
50014
+ }
49973
50015
  .command .lines:not([data-clamp]) {
49974
50016
  display: block;
49975
50017
  max-block-size: min(16lh, 50dvh);
@@ -50211,12 +50253,12 @@ var SliccApprovals = class extends ModelElement {
50211
50253
  if (!text4) return b`<p class="hint" data-id="no-details">This request has no details.</p>`;
50212
50254
  const full = !pending || this.#full.has(id);
50213
50255
  const toggle = pending && (full || this.#cuts.get(id)) ? fullCommand(full, () => this.#expand(id)) : A;
50214
- return b`<pre class="command" data-command=${approval.kind === "sudo" ? "" : A} data-summary=${approval.kind === "sudo" ? A : ""}><span class="lines" data-clamp=${full ? A : id}>${text4}${pending ? b`<span class="end" data-end=${id}></span>` : A}</span></pre>${toggle}`;
50256
+ return b`<pre class="command" data-command=${approval.kind === "sudo" ? "" : A} data-summary=${approval.kind === "sudo" ? A : ""}><span class="lines" data-clamp=${full ? A : id}>${approval.kind === "sudo" ? commandText(text4) : text4}${pending ? b`<span class="end" data-end=${id}></span>` : A}</span></pre>${toggle}`;
50215
50257
  }
50216
50258
  #request(approval, pending = false) {
50217
50259
  if (approval.kind === "tray") return b`<p id="asking">${trayAsking(approval)}</p>`;
50218
50260
  if (approval.kind === "sudo")
50219
- return b`<p id="asking">${asking(approval)}:</p>
50261
+ return b`<p id="asking">${commandText(asking(approval), false)}:</p>
50220
50262
  ${this.#command(approval, pending)}`;
50221
50263
  if (approval.kind === "guest-tool")
50222
50264
  return b`<p id="asking">${guestAsking(approval)}</p>
@@ -50269,8 +50311,8 @@ var SliccApprovals = class extends ModelElement {
50269
50311
  <h2 id="title" slot="heading">${title(approval)}</h2>
50270
50312
  ${this.#request(approval, true)}
50271
50313
  ${this.#verdict(approval)}
50272
- ${approval.kind === "sudo" && approval.cwd ? b`<p class="cwd">In ${approval.cwd}</p>` : A}
50273
- ${rules ? b`<div class="rules" data-secret-rules=${rules}><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><p>${verdict.certain ? b`<strong>This changes secret rules.</strong> It names <code>${rules}</code>, where SLICC keeps which secrets programs may use and where they may be sent.` : b`<strong>This may change secret rules.</strong> SLICC can't tell where <code>${rules}</code> leads, and it could reach the files where SLICC keeps which secrets programs may use and where they may be sent.`} Allow it only if you asked for this change.</p></div>` : A}
50314
+ ${approval.kind === "sudo" && approval.cwd ? b`<p class="cwd">In ${commandText(approval.cwd, false)}</p>` : A}
50315
+ ${rules ? b`<div class="rules" data-secret-rules=${rules}><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><p>${verdict.certain ? b`<strong>This changes secret rules.</strong> It names <code>${commandText(rules, false)}</code>, where SLICC keeps which secrets programs may use and where they may be sent.` : b`<strong>This may change secret rules.</strong> SLICC can't tell where <code>${commandText(rules, false)}</code> leads, and it could reach the files where SLICC keeps which secrets programs may use and where they may be sent.`} Allow it only if you asked for this change.</p></div>` : A}
50274
50316
  ${hint2 ? b`<p class="hint">${hint2}</p>` : A}
50275
50317
  ${!failure2 && gate ? b`<p class="hint" data-allow-hint>${gate}</p>` : A}
50276
50318
  ${failure2 ? b`<p class="problem" role="alert" data-error><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${failure2}</span></p>` : b`<p class="timer" data-timer>Denied automatically in ${countdown(approval.expiresAt - this.now)}.</p>`}
@@ -52035,10 +52077,10 @@ function svg(name, node2) {
52035
52077
  const children = node2.map(([tag3, child]) => `<${tag3}${attrs(child)}/>`).join("");
52036
52078
  return `<svg${attrs(attributes)}>${children}</svg>`;
52037
52079
  }
52038
- async function icons2(names2) {
52080
+ async function icons2(names3) {
52039
52081
  const lib = await load2();
52040
52082
  const out = {};
52041
- for (const name of names2) {
52083
+ for (const name of names3) {
52042
52084
  if (typeof name !== "string" || !/^[a-z0-9-]+$/.test(name)) continue;
52043
52085
  const node2 = lib[pascal(name)] ?? lib.icons[pascal(name)];
52044
52086
  if (Array.isArray(node2)) out[name] = svg(name, node2);
@@ -55239,10 +55281,10 @@ var SliccSettings = class extends ThemedElement {
55239
55281
  if (account.auth === "api-key" || dual(account))
55240
55282
  return [[recordName(account), recordKind(account)]];
55241
55283
  const server = serverOf(account);
55242
- const names2 = (vault.list()?.secrets ?? []).filter(
55284
+ const names3 = (vault.list()?.secrets ?? []).filter(
55243
55285
  (secret) => secret.kind === "mcp" && secret.server === server && secret.state !== "removed"
55244
55286
  ).map((secret) => secret.name);
55245
- const tokens = names2.length ? names2 : [`${upper(server)}_ACCESS_TOKEN`];
55287
+ const tokens = names3.length ? names3 : [`${upper(server)}_ACCESS_TOKEN`];
55246
55288
  return tokens.map((name) => [name, "mcp"]);
55247
55289
  }
55248
55290
  async #disconnect(account) {
@@ -55452,8 +55494,8 @@ var SliccSettings = class extends ThemedElement {
55452
55494
  }
55453
55495
  #serverRecords(account, vault) {
55454
55496
  const token = account.auth === "api-key" ? [recordName(account)] : [];
55455
- const names2 = /* @__PURE__ */ new Set([...token, ...this.#heldRecords(account, vault)]);
55456
- return [...names2].map((name) => [name, "mcp"]);
55497
+ const names3 = /* @__PURE__ */ new Set([...token, ...this.#heldRecords(account, vault)]);
55498
+ return [...names3].map((name) => [name, "mcp"]);
55457
55499
  }
55458
55500
  #removeServerBody(account) {
55459
55501
  const vault = this.model?.vault;
@@ -70376,6 +70418,8 @@ var EdgeGroupView = class {
70376
70418
  const strip = this._group.element.querySelector(".dv-tabs-and-actions-container");
70377
70419
  if (!strip) return;
70378
70420
  this._tabSizeDisposables.addDisposables(watchElementResize(strip, () => {
70421
+ const { headerPosition } = this._group;
70422
+ if ((headerPosition === "left" || headerPosition === "right") !== (this._orientation === "horizontal")) return;
70379
70423
  this._applyMeasuredTabSize(this._orientation === "vertical" ? strip.offsetHeight : strip.offsetWidth);
70380
70424
  }));
70381
70425
  }
@@ -72361,7 +72405,14 @@ var DockviewComponent = class extends BaseGrid {
72361
72405
  if (options2.autoHide !== void 0) service.setAutoHide(group, options2.autoHide);
72362
72406
  if (options2.autoReveal !== void 0) service.setAutoReveal(group, options2.autoReveal);
72363
72407
  this._onDidAddGroup.fire(group);
72364
- this._shellManager.addEdgeView(position2, options2, group);
72408
+ const host4 = {
72409
+ element: group.element,
72410
+ get headerPosition() {
72411
+ return group.model.headerPosition;
72412
+ },
72413
+ layout: (width, height) => group.layout(width, height)
72414
+ };
72415
+ this._shellManager.addEdgeView(position2, options2, host4);
72365
72416
  return group.api;
72366
72417
  });
72367
72418
  }
@@ -84085,7 +84136,7 @@ lit-html/static.js:
84085
84136
  dockview-core/dist/package/main.esm.mjs:
84086
84137
  (**
84087
84138
  * dockview-core
84088
- * @version 8.7.0
84139
+ * @version 8.7.1
84089
84140
  * @link https://github.com/dockview/dockview
84090
84141
  * @license MIT
84091
84142
  *)