@ai-ecoverse/slicc-agent 6.18.12 → 6.18.13

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.
@@ -26,6 +26,54 @@ function parseStatus(out) {
26
26
  }
27
27
  return found;
28
28
  }
29
+ const STATUS = [
30
+ "git",
31
+ "--no-optional-locks",
32
+ "status",
33
+ "--porcelain=v1",
34
+ "-z",
35
+ "--untracked-files=all",
36
+ "--ignore-submodules=all"
37
+ ];
38
+ const BATCH_BYTES = 4 * MAX_TEXT;
39
+ const CHECKED = /^([0-9a-f]+) (\S+) (\d+)$/;
40
+ function parseCheck(out, paths) {
41
+ const lines = out.split("\n");
42
+ if (lines.length !== paths.length + 1 || lines.at(-1) !== "") return null;
43
+ const found = [];
44
+ for (const [i, path] of paths.entries()) {
45
+ const line = lines[i];
46
+ const match = CHECKED.exec(line);
47
+ if (match?.[2] === "blob")
48
+ found.push({ path, oid: match[1], size: Number(match[3]) });
49
+ else if (!match && !line.endsWith(" missing") && !line.endsWith(" ambiguous")) return null;
50
+ }
51
+ return found;
52
+ }
53
+ const NEWLINE = 10;
54
+ const HEADER = /^[0-9a-f]+ (\S+) (\d+)$/;
55
+ function parseBatch(bytes, count) {
56
+ const decoder = new TextDecoder();
57
+ const out = [];
58
+ let at = 0;
59
+ while (out.length < count) {
60
+ const end = bytes.indexOf(NEWLINE, at);
61
+ if (end < 0) return null;
62
+ const header = decoder.decode(bytes.subarray(at, end));
63
+ at = end + 1;
64
+ if (header.endsWith(" missing") || header.endsWith(" ambiguous")) {
65
+ out.push(null);
66
+ continue;
67
+ }
68
+ const match = HEADER.exec(header);
69
+ if (!match) return null;
70
+ const size = Number(match[2]);
71
+ if (bytes[at + size] !== NEWLINE) return null;
72
+ out.push(match[1] === "blob" ? bytes.slice(at, at + size) : null);
73
+ at += size + 1;
74
+ }
75
+ return at === bytes.length ? out : null;
76
+ }
29
77
  function textOf(bytes) {
30
78
  if (bytes.length > MAX_TEXT || bytes.includes(0)) return null;
31
79
  try {
@@ -70,6 +118,7 @@ function failure(ran, what) {
70
118
  return new Error(ran.err.trim() || ran.out.trim() || `${what} failed`);
71
119
  }
72
120
  export {
121
+ BATCH_BYTES,
73
122
  CLONE_COMMAND,
74
123
  MAX_FILES,
75
124
  MAX_TEXT,
@@ -77,8 +126,11 @@ export {
77
126
  NO_REPO,
78
127
  SCAN_DEPTH,
79
128
  SCAN_ENTRIES,
129
+ STATUS,
80
130
  failure,
81
131
  joined,
132
+ parseBatch,
133
+ parseCheck,
82
134
  parseStatus,
83
135
  quote,
84
136
  relative,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/changes/git.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\n\nexport const MAX_TEXT = 1024 * 1024;\nexport const MAX_FILES = 200;\nexport const SCAN_DEPTH = 3;\nexport const SCAN_ENTRIES = 200;\nexport const CLONE_COMMAND = 'curl -fsSL https://get.sliccy.ai/slicc | sh';\nexport const NO_GIT =\n \"Changes needs git, and git isn't installed. Install it with `pnpm add -g @ai-ecoverse/wasm-git`, then run `git init` in a folder under /home. To clone from the internet, link slicc or the SLICC extension.\";\nexport const NO_REPO =\n 'Changes lists the changes in git repositories, and there are none here yet. Run `git init` in a folder under /home. To clone from the internet, link slicc or the SLICC extension.';\n\nexport type ChangeStatus = 'added' | 'modified' | 'deleted';\nexport type ChangeKind = 'tracked' | 'untracked' | 'conflict';\n\nconst UNMERGED = ['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU'];\n\nexport interface FileChange {\n path: string;\n repo: string;\n status: ChangeStatus;\n kind: ChangeKind;\n before: string | null;\n after: string | null;\n}\n\nexport interface ChangesView {\n unavailable: string | null;\n changes: FileChange[];\n}\n\nexport interface Pending {\n path: string;\n status: ChangeStatus;\n kind: ChangeKind;\n}\n\nexport function parseStatus(out: string): Pending[] {\n const parts = out.split('\\0');\n const found: Pending[] = [];\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i] as string;\n if (part.length < 4) continue;\n const index = part[0] as string;\n const tree = part[1] as string;\n const path = part.slice(3);\n if (index === 'R' || index === 'C') i++;\n if (UNMERGED.includes(index + tree)) found.push({ path, status: 'modified', kind: 'conflict' });\n else if (index === '?' && tree === '?')\n found.push({ path, status: 'added', kind: 'untracked' });\n else if (tree === 'D') found.push({ path, status: 'deleted', kind: 'tracked' });\n else if (tree === 'A') found.push({ path, status: 'added', kind: 'tracked' });\n else if (tree !== ' ' && tree !== '!')\n found.push({ path, status: 'modified', kind: 'tracked' });\n }\n return found;\n}\n\nexport function textOf(bytes: Uint8Array): string | null {\n if (bytes.length > MAX_TEXT || bytes.includes(0)) return null;\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n } catch {\n return null;\n }\n}\n\nexport function shown(text: string): string | null {\n return text.length > MAX_TEXT || text.includes('\\0') || text.includes('\uFFFD') ? null : text;\n}\n\nexport function scanned(name: string): boolean {\n return name !== 'node_modules' && !name.startsWith('.');\n}\n\nexport function within(path: string, root: string): boolean {\n return root === '/' || path === root || path.startsWith(`${root}/`);\n}\n\nexport function relative(path: string, root: string): string {\n return root === '/' ? path.slice(1) : path.slice(root.length + 1);\n}\n\nexport function joined(root: string, rel: string): string {\n return root === '/' ? `/${rel}` : `${root}/${rel}`;\n}\n\nexport type Ran = { code: number; out: string; err: string };\n\nexport const quote = (word: string): string => `'${word.replaceAll(\"'\", \"'\\\\''\")}'`;\n\nexport async function run(\n env: Pick<ExecutionEnv, 'exec'>,\n command: string | readonly string[],\n cwd: string,\n context: Context\n): Promise<Ran> {\n let out = '';\n let err = '';\n const ran = await env.exec(\n typeof command === 'string' ? command : command.map(quote).join(' '),\n {\n cwd,\n onOutput: (text, _context, info) => {\n if (info.stream === 'stdout') out += text;\n else err += text;\n },\n },\n context\n );\n return ran.ok\n ? { code: ran.value.exitCode, out, err }\n : { code: -1, out, err: ran.error.message };\n}\n\nexport function failure(ran: Ran, what: string): Error {\n return new Error(ran.err.trim() || ran.out.trim() || `${what} failed`);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\n\nexport const MAX_TEXT = 1024 * 1024;\nexport const MAX_FILES = 200;\nexport const SCAN_DEPTH = 3;\nexport const SCAN_ENTRIES = 200;\nexport const CLONE_COMMAND = 'curl -fsSL https://get.sliccy.ai/slicc | sh';\nexport const NO_GIT =\n \"Changes needs git, and git isn't installed. Install it with `pnpm add -g @ai-ecoverse/wasm-git`, then run `git init` in a folder under /home. To clone from the internet, link slicc or the SLICC extension.\";\nexport const NO_REPO =\n 'Changes lists the changes in git repositories, and there are none here yet. Run `git init` in a folder under /home. To clone from the internet, link slicc or the SLICC extension.';\n\nexport type ChangeStatus = 'added' | 'modified' | 'deleted';\nexport type ChangeKind = 'tracked' | 'untracked' | 'conflict';\n\nconst UNMERGED = ['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU'];\n\nexport interface FileChange {\n path: string;\n repo: string;\n status: ChangeStatus;\n kind: ChangeKind;\n before: string | null;\n after: string | null;\n}\n\nexport interface ChangesView {\n unavailable: string | null;\n changes: FileChange[];\n}\n\nexport interface Pending {\n path: string;\n status: ChangeStatus;\n kind: ChangeKind;\n}\n\nexport function parseStatus(out: string): Pending[] {\n const parts = out.split('\\0');\n const found: Pending[] = [];\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i] as string;\n if (part.length < 4) continue;\n const index = part[0] as string;\n const tree = part[1] as string;\n const path = part.slice(3);\n if (index === 'R' || index === 'C') i++;\n if (UNMERGED.includes(index + tree)) found.push({ path, status: 'modified', kind: 'conflict' });\n else if (index === '?' && tree === '?')\n found.push({ path, status: 'added', kind: 'untracked' });\n else if (tree === 'D') found.push({ path, status: 'deleted', kind: 'tracked' });\n else if (tree === 'A') found.push({ path, status: 'added', kind: 'tracked' });\n else if (tree !== ' ' && tree !== '!')\n found.push({ path, status: 'modified', kind: 'tracked' });\n }\n return found;\n}\n\nexport const STATUS = [\n 'git',\n '--no-optional-locks',\n 'status',\n '--porcelain=v1',\n '-z',\n '--untracked-files=all',\n '--ignore-submodules=all',\n];\n\nexport const BATCH_BYTES = 4 * MAX_TEXT;\n\nexport interface Stored {\n path: string;\n oid: string;\n size: number;\n}\n\nconst CHECKED = /^([0-9a-f]+) (\\S+) (\\d+)$/;\n\nexport function parseCheck(out: string, paths: readonly string[]): Stored[] | null {\n const lines = out.split('\\n');\n if (lines.length !== paths.length + 1 || lines.at(-1) !== '') return null;\n const found: Stored[] = [];\n for (const [i, path] of paths.entries()) {\n const line = lines[i] as string;\n const match = CHECKED.exec(line);\n if (match?.[2] === 'blob')\n found.push({ path, oid: match[1] as string, size: Number(match[3]) });\n else if (!match && !line.endsWith(' missing') && !line.endsWith(' ambiguous')) return null;\n }\n return found;\n}\n\nconst NEWLINE = 10;\nconst HEADER = /^[0-9a-f]+ (\\S+) (\\d+)$/;\n\nexport function parseBatch(bytes: Uint8Array, count: number): (Uint8Array | null)[] | null {\n const decoder = new TextDecoder();\n const out: (Uint8Array | null)[] = [];\n let at = 0;\n while (out.length < count) {\n const end = bytes.indexOf(NEWLINE, at);\n if (end < 0) return null;\n const header = decoder.decode(bytes.subarray(at, end));\n at = end + 1;\n if (header.endsWith(' missing') || header.endsWith(' ambiguous')) {\n out.push(null);\n continue;\n }\n const match = HEADER.exec(header);\n if (!match) return null;\n const size = Number(match[2]);\n if (bytes[at + size] !== NEWLINE) return null;\n out.push(match[1] === 'blob' ? bytes.slice(at, at + size) : null);\n at += size + 1;\n }\n return at === bytes.length ? out : null;\n}\n\nexport function textOf(bytes: Uint8Array): string | null {\n if (bytes.length > MAX_TEXT || bytes.includes(0)) return null;\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n } catch {\n return null;\n }\n}\n\nexport function shown(text: string): string | null {\n return text.length > MAX_TEXT || text.includes('\\0') || text.includes('\uFFFD') ? null : text;\n}\n\nexport function scanned(name: string): boolean {\n return name !== 'node_modules' && !name.startsWith('.');\n}\n\nexport function within(path: string, root: string): boolean {\n return root === '/' || path === root || path.startsWith(`${root}/`);\n}\n\nexport function relative(path: string, root: string): string {\n return root === '/' ? path.slice(1) : path.slice(root.length + 1);\n}\n\nexport function joined(root: string, rel: string): string {\n return root === '/' ? `/${rel}` : `${root}/${rel}`;\n}\n\nexport type Ran = { code: number; out: string; err: string };\n\nexport const quote = (word: string): string => `'${word.replaceAll(\"'\", \"'\\\\''\")}'`;\n\nexport async function run(\n env: Pick<ExecutionEnv, 'exec'>,\n command: string | readonly string[],\n cwd: string,\n context: Context\n): Promise<Ran> {\n let out = '';\n let err = '';\n const ran = await env.exec(\n typeof command === 'string' ? command : command.map(quote).join(' '),\n {\n cwd,\n onOutput: (text, _context, info) => {\n if (info.stream === 'stdout') out += text;\n else err += text;\n },\n },\n context\n );\n return ran.ok\n ? { code: ran.value.exitCode, out, err }\n : { code: -1, out, err: ran.error.message };\n}\n\nexport function failure(ran: Ran, what: string): Error {\n return new Error(ran.err.trim() || ran.out.trim() || `${what} failed`);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,16 +1,22 @@
1
1
  import { replicatedState } from "@earendil-works/chord";
2
2
  import { HOME } from "../kernel/env.js";
3
3
  import {
4
+ BATCH_BYTES,
4
5
  failure,
5
6
  joined,
6
7
  MAX_FILES,
8
+ MAX_TEXT,
7
9
  NO_GIT,
8
10
  NO_REPO,
11
+ parseBatch,
12
+ parseCheck,
9
13
  parseStatus,
14
+ quote,
10
15
  relative,
11
16
  run,
12
17
  SCAN_DEPTH,
13
18
  SCAN_ENTRIES,
19
+ STATUS,
14
20
  scanned,
15
21
  shown,
16
22
  textOf,
@@ -63,36 +69,81 @@ async function discover(options, context) {
63
69
  (root) => !EXCLUDED.some((excluded) => within(root, excluded))
64
70
  );
65
71
  }
66
- async function sideOf(env, repo, pending, context) {
67
- const path = joined(repo, pending.path);
68
- const before = pending.status === "added" ? null : await run(env, ["git", "show", `:${pending.path}`], repo, context).then(
69
- (ran) => ran.code === 0 ? shown(ran.out) : null
72
+ const piped = (words, command) => `printf '%s\\n' ${words.map(quote).join(" ")} | ${command}`;
73
+ async function shownOne(env, repo, path, context) {
74
+ const ran = await run(env, ["git", "show", `:${path}`], repo, context);
75
+ return ran.code === 0 ? shown(ran.out) : null;
76
+ }
77
+ function chunked(items) {
78
+ const out = [];
79
+ let bytes = Infinity;
80
+ for (const item of items) {
81
+ if (bytes + item.size > BATCH_BYTES) {
82
+ out.push([]);
83
+ bytes = 0;
84
+ }
85
+ out.at(-1)?.push(item);
86
+ bytes += item.size;
87
+ }
88
+ return out;
89
+ }
90
+ async function befores(env, repo, pending, context) {
91
+ const paths = pending.filter((item) => item.status !== "added").map((item) => item.path);
92
+ const out = /* @__PURE__ */ new Map();
93
+ if (!paths.length) return out;
94
+ const listing = paths.some((path) => path.includes("\n")) ? null : await run(
95
+ env,
96
+ piped(
97
+ paths.map((path) => `:${path}`),
98
+ "git cat-file --batch-check"
99
+ ),
100
+ repo,
101
+ context
70
102
  );
103
+ const checked = listing?.code === 0 ? parseCheck(listing.out, paths) : null;
104
+ if (!checked) {
105
+ for (const path of paths) out.set(path, await shownOne(env, repo, path, context));
106
+ return out;
107
+ }
108
+ for (const path of paths) out.set(path, null);
109
+ const small = checked.filter((item) => item.size <= MAX_TEXT);
110
+ for (const chunk of chunked(small)) {
111
+ const batch = await run(
112
+ env,
113
+ piped(
114
+ chunk.map((item) => item.oid),
115
+ "git cat-file --batch"
116
+ ),
117
+ repo,
118
+ context
119
+ );
120
+ const parsed = batch.code === 0 && !batch.out.includes("\uFFFD") ? parseBatch(new TextEncoder().encode(batch.out), chunk.length) : null;
121
+ for (const [i, item] of chunk.entries()) {
122
+ const bytes = parsed?.[i];
123
+ out.set(
124
+ item.path,
125
+ parsed ? bytes ? textOf(bytes) : null : await shownOne(env, repo, item.path, context)
126
+ );
127
+ }
128
+ }
129
+ return out;
130
+ }
131
+ async function sideOf(env, repo, pending, before, context) {
132
+ const path = joined(repo, pending.path);
71
133
  const read = pending.status === "deleted" ? null : await env.readBinaryFile(path, context);
72
134
  const after = read?.ok ? textOf(read.value) : null;
73
135
  const opaque = pending.status !== "added" && before === null || pending.status !== "deleted" && after === null;
74
136
  return { path, before: opaque ? null : before, after: opaque ? null : after };
75
137
  }
76
138
  async function statusOf(env, repo, context) {
77
- const ran = await run(
78
- env,
79
- [
80
- "git",
81
- "--no-optional-locks",
82
- "status",
83
- "--porcelain=v1",
84
- "-z",
85
- "--untracked-files=all",
86
- "--ignore-submodules=all"
87
- ],
88
- repo,
89
- context
90
- );
139
+ const ran = await run(env, STATUS, repo, context);
91
140
  if (ran.code !== 0) return null;
141
+ const pending = parseStatus(ran.out).slice(0, MAX_FILES);
142
+ const before = await befores(env, repo, pending, context);
92
143
  const out = [];
93
- for (const pending of parseStatus(ran.out).slice(0, MAX_FILES)) {
94
- const side = await sideOf(env, repo, pending, context);
95
- out.push({ ...side, repo, status: pending.status, kind: pending.kind });
144
+ for (const item of pending) {
145
+ const side = await sideOf(env, repo, item, before.get(item.path) ?? null, context);
146
+ out.push({ ...side, repo, status: item.status, kind: item.kind });
96
147
  }
97
148
  return out.sort((a, b) => a.path.localeCompare(b.path));
98
149
  }
@@ -126,18 +177,22 @@ function attachChanges(options) {
126
177
  lock = next.catch(() => void 0);
127
178
  return next;
128
179
  };
180
+ const hasGit = async (context) => (await run(env, "command -v git", HOME, context)).code === 0;
181
+ const missing = (context) => {
182
+ repos = null;
183
+ view.replace(context, { unavailable: NO_GIT, changes: [] });
184
+ };
129
185
  const scan = async (context) => {
130
- const git = await run(env, "command -v git", HOME, context);
131
- if (git.code !== 0) {
132
- view.replace(context, { unavailable: NO_GIT, changes: [] });
133
- return;
134
- }
186
+ if (!repos?.length && !await hasGit(context)) return missing(context);
135
187
  repos ??= await discover(options, context);
136
188
  const changes = [];
137
189
  const live = [];
190
+ let checked = false;
138
191
  for (const repo of repos) {
139
192
  const found = await statusOf(env, repo, context);
140
193
  if (!found) {
194
+ if (!checked && !await hasGit(context)) return missing(context);
195
+ checked = true;
141
196
  const still = await env.exists(joined(repo, ".git"), context);
142
197
  if (!still.ok || !still.value) continue;
143
198
  }
@@ -147,6 +202,19 @@ function attachChanges(options) {
147
202
  repos = live;
148
203
  view.replace(context, { unavailable: live.length ? null : NO_REPO, changes });
149
204
  };
205
+ const pendingOf = async (path, context) => {
206
+ const listed = () => view.value.changes.find((item) => item.path === path);
207
+ if (!listed()) await scan(context);
208
+ const repo = listed()?.repo;
209
+ if (!repo) return null;
210
+ const rel = relative(path, repo);
211
+ const ran = await run(env, [...STATUS, "--", `:(literal)${rel}`], repo, context);
212
+ if (ran.code !== 0) throw failure(ran, `git status ${rel}`);
213
+ const fresh = parseStatus(ran.out).find((item) => item.path === rel);
214
+ if (fresh) return { path, repo, kind: fresh.kind };
215
+ await scan(context);
216
+ return null;
217
+ };
150
218
  const refresh = (context) => serial(() => scan(context));
151
219
  let running = false;
152
220
  let again = false;
@@ -167,8 +235,7 @@ function attachChanges(options) {
167
235
  timer = setTimeout(() => void kick(context), options.reloadMs);
168
236
  };
169
237
  const change = (path, context, act) => serial(async () => {
170
- await scan(context);
171
- const found = view.value.changes.find((item) => item.path === path);
238
+ const found = await pendingOf(path, context);
172
239
  if (!found) throw new Error(`${path} has no pending change`);
173
240
  await act(found);
174
241
  await scan(context);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/changes/index.ts"],
4
- "sourcesContent": ["import { type Context, type MutableReplicatedState, replicatedState } from '@earendil-works/chord';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Agents } from '../agents.ts';\nimport { HOME } from '../kernel/env.ts';\nimport {\n type ChangesView,\n type FileChange,\n failure,\n joined,\n MAX_FILES,\n NO_GIT,\n NO_REPO,\n type Pending,\n parseStatus,\n relative,\n run,\n SCAN_DEPTH,\n SCAN_ENTRIES,\n scanned,\n shown,\n textOf,\n within,\n} from './git.ts';\n\nexport interface ChangesRuntime {\n readonly view: MutableReplicatedState<ChangesView>;\n open(context: Context): Promise<void>;\n accept(path: string, context: Context): Promise<void>;\n revert(path: string, context: Context): Promise<void>;\n refresh(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport const CHANGES_MS = 500;\n\nexport interface ChangesOptions {\n env: ExecutionEnv;\n agents: Agents;\n reloadMs: number;\n}\n\nconst WATCHED = [HOME, '/mnt', '/scoops'];\nconst EXCLUDED = ['/tmp', '/var/lib/slicc'];\n\ntype Env = ChangesOptions['env'];\n\nasync function walk(env: Env, dir: string, depth: number, out: Set<string>, context: Context) {\n const listed = await env.listDir(dir, context);\n if (!listed.ok) return;\n if (listed.value.some((entry) => entry.name === '.git')) {\n out.add(dir);\n return;\n }\n if (depth >= SCAN_DEPTH) return;\n const dirs = listed.value\n .filter((entry) => entry.kind === 'directory' && scanned(entry.name))\n .slice(0, SCAN_ENTRIES);\n for (const entry of dirs) await walk(env, entry.path, depth + 1, out, context);\n}\n\nasync function cwds(agents: Agents, context: Context): Promise<string[]> {\n const state = agents.state();\n const ids = [\n state.active,\n ...Object.keys(state.cones).filter((id) => id !== state.active),\n ...Object.entries(state.scoops)\n .filter(([, scoop]) => !scoop.gone && !scoop.dropped && !scoop.frozen)\n .map(([id]) => id),\n ];\n const out: string[] = [];\n for (const id of ids) {\n const conversation = await agents.conversation(id, context);\n if (conversation) out.push((await conversation.agent(context)).cwd ?? HOME);\n }\n return out;\n}\n\nasync function discover(options: ChangesOptions, context: Context): Promise<string[]> {\n const { env } = options;\n const found: string[] = [];\n for (const cwd of await cwds(options.agents, context)) {\n const top = await run(env, ['git', 'rev-parse', '--show-toplevel'], cwd, context);\n if (top.code === 0 && top.out.trim()) found.push(top.out.trim());\n }\n const walked = new Set<string>();\n await walk(env, HOME, 0, walked, context);\n const mounts = await env.listDir('/mnt', context);\n if (mounts.ok)\n for (const mount of mounts.value)\n if (mount.kind === 'directory') await walk(env, mount.path, 0, walked, context);\n const ordered = [...found, ...[...walked].sort()];\n return [...new Set(ordered)].filter(\n (root) => !EXCLUDED.some((excluded) => within(root, excluded))\n );\n}\n\nasync function sideOf(env: Env, repo: string, pending: Pending, context: Context) {\n const path = joined(repo, pending.path);\n const before =\n pending.status === 'added'\n ? null\n : await run(env, ['git', 'show', `:${pending.path}`], repo, context).then((ran) =>\n ran.code === 0 ? shown(ran.out) : null\n );\n const read = pending.status === 'deleted' ? null : await env.readBinaryFile(path, context);\n const after = read?.ok ? textOf(read.value) : null;\n const opaque =\n (pending.status !== 'added' && before === null) ||\n (pending.status !== 'deleted' && after === null);\n return { path, before: opaque ? null : before, after: opaque ? null : after };\n}\n\nasync function statusOf(env: Env, repo: string, context: Context): Promise<FileChange[] | null> {\n const ran = await run(\n env,\n [\n 'git',\n '--no-optional-locks',\n 'status',\n '--porcelain=v1',\n '-z',\n '--untracked-files=all',\n '--ignore-submodules=all',\n ],\n repo,\n context\n );\n if (ran.code !== 0) return null;\n const out: FileChange[] = [];\n for (const pending of parseStatus(ran.out).slice(0, MAX_FILES)) {\n const side = await sideOf(env, repo, pending, context);\n out.push({ ...side, repo, status: pending.status, kind: pending.kind });\n }\n return out.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nconst STATE = ['index', 'HEAD', 'refs'];\n\nfunction noise(path: string, repos: readonly string[]): boolean {\n return repos.some((repo) => {\n const git = joined(repo, '.git');\n return (\n within(path, git) &&\n path !== git &&\n !STATE.includes(relative(path, git).split('/')[0] as string)\n );\n });\n}\n\nfunction rediscovers(paths: readonly string[], repos: readonly string[]): boolean {\n return paths.some((path) => {\n const parts = path.split('/');\n const git = parts.indexOf('.git');\n if (git > 0) return !repos.includes(parts.slice(0, git).join('/') || '/');\n if (parts.some((part) => part && !scanned(part))) return false;\n return !repos.some((repo) => within(path, repo));\n });\n}\n\nexport function attachChanges(options: ChangesOptions): ChangesRuntime {\n const { env } = options;\n const view = replicatedState<ChangesView>({ unavailable: null, changes: [] });\n let repos: string[] | null = null;\n let opened: Promise<void> | null = null;\n let watcher: FileWatcher | undefined;\n let stop = () => undefined as void;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(work: () => Promise<T>): Promise<T> => {\n const next = lock.then(work, work);\n lock = next.catch(() => undefined);\n return next;\n };\n const scan = async (context: Context) => {\n const git = await run(env, 'command -v git', HOME, context);\n if (git.code !== 0) {\n view.replace(context, { unavailable: NO_GIT, changes: [] });\n return;\n }\n repos ??= await discover(options, context);\n const changes: FileChange[] = [];\n const live: string[] = [];\n for (const repo of repos) {\n const found = await statusOf(env, repo, context);\n if (!found) {\n const still = await env.exists(joined(repo, '.git'), context);\n if (!still.ok || !still.value) continue;\n }\n live.push(repo);\n changes.push(...(found ?? view.value.changes.filter((change) => change.repo === repo)));\n }\n repos = live;\n view.replace(context, { unavailable: live.length ? null : NO_REPO, changes });\n };\n const refresh = (context: Context) => serial(() => scan(context));\n let running = false;\n let again = false;\n const kick = async (context: Context) => {\n if (running) {\n again = true;\n return;\n }\n running = true;\n do {\n again = false;\n await refresh(context).catch(() => undefined);\n } while (again);\n running = false;\n };\n const later = (context: Context) => {\n clearTimeout(timer);\n timer = setTimeout(() => void kick(context), options.reloadMs);\n };\n const change = (path: string, context: Context, act: (found: FileChange) => Promise<void>) =>\n serial(async () => {\n await scan(context);\n const found = view.value.changes.find((item) => item.path === path);\n if (!found) throw new Error(`${path} has no pending change`);\n await act(found);\n await scan(context);\n });\n const start = async (context: Context) => {\n await refresh(context);\n const watched = await env.watch(\n WATCHED.map((path) => ({ path, recursive: true, exclude: { names: ['node_modules'] } })),\n (change) => {\n const known = repos ?? [];\n const paths = 'paths' in change ? change.paths.filter((path) => !noise(path, known)) : null;\n if (paths?.length === 0) return;\n if (!paths || rediscovers(paths, known)) repos = null;\n later(context);\n },\n context\n );\n watcher = watched.ok ? watched.value : undefined;\n stop = options.agents.onChange(() => {\n repos = null;\n later(context);\n });\n };\n return {\n view,\n open(context) {\n opened ??= start(context);\n return opened;\n },\n accept: (path, context) =>\n change(path, context, async (found) => {\n const rel = relative(found.path, found.repo);\n const ran = await run(env, ['git', 'add', '-A', '--', rel], found.repo, context);\n if (ran.code !== 0) throw failure(ran, `git add ${rel}`);\n }),\n revert: (path, context) =>\n change(path, context, async (found) => {\n const rel = relative(found.path, found.repo);\n if (found.kind === 'conflict')\n throw new Error(`${found.path} has a merge conflict; resolve it with git first.`);\n if (found.kind === 'untracked') {\n const removed = await env.remove(found.path, { force: true }, context);\n if (!removed.ok) throw removed.error;\n } else {\n const ran = await run(env, ['git', 'restore', '--', rel], found.repo, context);\n if (ran.code !== 0) throw failure(ran, `git restore ${rel}`);\n }\n }),\n refresh,\n async close(context) {\n clearTimeout(timer);\n stop();\n await watcher?.close(context);\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { type Context, type MutableReplicatedState, replicatedState } from '@earendil-works/chord';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Agents } from '../agents.ts';\nimport { HOME } from '../kernel/env.ts';\nimport {\n BATCH_BYTES,\n type ChangesView,\n type FileChange,\n failure,\n joined,\n MAX_FILES,\n MAX_TEXT,\n NO_GIT,\n NO_REPO,\n type Pending,\n parseBatch,\n parseCheck,\n parseStatus,\n quote,\n relative,\n run,\n SCAN_DEPTH,\n SCAN_ENTRIES,\n STATUS,\n type Stored,\n scanned,\n shown,\n textOf,\n within,\n} from './git.ts';\n\nexport interface ChangesRuntime {\n readonly view: MutableReplicatedState<ChangesView>;\n open(context: Context): Promise<void>;\n accept(path: string, context: Context): Promise<void>;\n revert(path: string, context: Context): Promise<void>;\n refresh(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport const CHANGES_MS = 500;\n\nexport interface ChangesOptions {\n env: ExecutionEnv;\n agents: Agents;\n reloadMs: number;\n}\n\nconst WATCHED = [HOME, '/mnt', '/scoops'];\nconst EXCLUDED = ['/tmp', '/var/lib/slicc'];\n\ntype Env = ChangesOptions['env'];\n\nasync function walk(env: Env, dir: string, depth: number, out: Set<string>, context: Context) {\n const listed = await env.listDir(dir, context);\n if (!listed.ok) return;\n if (listed.value.some((entry) => entry.name === '.git')) {\n out.add(dir);\n return;\n }\n if (depth >= SCAN_DEPTH) return;\n const dirs = listed.value\n .filter((entry) => entry.kind === 'directory' && scanned(entry.name))\n .slice(0, SCAN_ENTRIES);\n for (const entry of dirs) await walk(env, entry.path, depth + 1, out, context);\n}\n\nasync function cwds(agents: Agents, context: Context): Promise<string[]> {\n const state = agents.state();\n const ids = [\n state.active,\n ...Object.keys(state.cones).filter((id) => id !== state.active),\n ...Object.entries(state.scoops)\n .filter(([, scoop]) => !scoop.gone && !scoop.dropped && !scoop.frozen)\n .map(([id]) => id),\n ];\n const out: string[] = [];\n for (const id of ids) {\n const conversation = await agents.conversation(id, context);\n if (conversation) out.push((await conversation.agent(context)).cwd ?? HOME);\n }\n return out;\n}\n\nasync function discover(options: ChangesOptions, context: Context): Promise<string[]> {\n const { env } = options;\n const found: string[] = [];\n for (const cwd of await cwds(options.agents, context)) {\n const top = await run(env, ['git', 'rev-parse', '--show-toplevel'], cwd, context);\n if (top.code === 0 && top.out.trim()) found.push(top.out.trim());\n }\n const walked = new Set<string>();\n await walk(env, HOME, 0, walked, context);\n const mounts = await env.listDir('/mnt', context);\n if (mounts.ok)\n for (const mount of mounts.value)\n if (mount.kind === 'directory') await walk(env, mount.path, 0, walked, context);\n const ordered = [...found, ...[...walked].sort()];\n return [...new Set(ordered)].filter(\n (root) => !EXCLUDED.some((excluded) => within(root, excluded))\n );\n}\n\nconst piped = (words: readonly string[], command: string) =>\n `printf '%s\\\\n' ${words.map(quote).join(' ')} | ${command}`;\n\nasync function shownOne(env: Env, repo: string, path: string, context: Context) {\n const ran = await run(env, ['git', 'show', `:${path}`], repo, context);\n return ran.code === 0 ? shown(ran.out) : null;\n}\n\nfunction chunked(items: readonly Stored[]): Stored[][] {\n const out: Stored[][] = [];\n let bytes = Infinity;\n for (const item of items) {\n if (bytes + item.size > BATCH_BYTES) {\n out.push([]);\n bytes = 0;\n }\n out.at(-1)?.push(item);\n bytes += item.size;\n }\n return out;\n}\n\nasync function befores(\n env: Env,\n repo: string,\n pending: readonly Pending[],\n context: Context\n): Promise<Map<string, string | null>> {\n const paths = pending.filter((item) => item.status !== 'added').map((item) => item.path);\n const out = new Map<string, string | null>();\n if (!paths.length) return out;\n const listing = paths.some((path) => path.includes('\\n'))\n ? null\n : await run(\n env,\n piped(\n paths.map((path) => `:${path}`),\n 'git cat-file --batch-check'\n ),\n repo,\n context\n );\n const checked = listing?.code === 0 ? parseCheck(listing.out, paths) : null;\n if (!checked) {\n for (const path of paths) out.set(path, await shownOne(env, repo, path, context));\n return out;\n }\n for (const path of paths) out.set(path, null);\n const small = checked.filter((item) => item.size <= MAX_TEXT);\n for (const chunk of chunked(small)) {\n const batch = await run(\n env,\n piped(\n chunk.map((item) => item.oid),\n 'git cat-file --batch'\n ),\n repo,\n context\n );\n const parsed =\n batch.code === 0 && !batch.out.includes('\\uFFFD')\n ? parseBatch(new TextEncoder().encode(batch.out), chunk.length)\n : null;\n for (const [i, item] of chunk.entries()) {\n const bytes = parsed?.[i];\n out.set(\n item.path,\n parsed ? (bytes ? textOf(bytes) : null) : await shownOne(env, repo, item.path, context)\n );\n }\n }\n return out;\n}\n\nasync function sideOf(\n env: Env,\n repo: string,\n pending: Pending,\n before: string | null,\n context: Context\n) {\n const path = joined(repo, pending.path);\n const read = pending.status === 'deleted' ? null : await env.readBinaryFile(path, context);\n const after = read?.ok ? textOf(read.value) : null;\n const opaque =\n (pending.status !== 'added' && before === null) ||\n (pending.status !== 'deleted' && after === null);\n return { path, before: opaque ? null : before, after: opaque ? null : after };\n}\n\nasync function statusOf(env: Env, repo: string, context: Context): Promise<FileChange[] | null> {\n const ran = await run(env, STATUS, repo, context);\n if (ran.code !== 0) return null;\n const pending = parseStatus(ran.out).slice(0, MAX_FILES);\n const before = await befores(env, repo, pending, context);\n const out: FileChange[] = [];\n for (const item of pending) {\n const side = await sideOf(env, repo, item, before.get(item.path) ?? null, context);\n out.push({ ...side, repo, status: item.status, kind: item.kind });\n }\n return out.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nconst STATE = ['index', 'HEAD', 'refs'];\n\ntype Target = Pick<FileChange, 'path' | 'repo' | 'kind'>;\n\nfunction noise(path: string, repos: readonly string[]): boolean {\n return repos.some((repo) => {\n const git = joined(repo, '.git');\n return (\n within(path, git) &&\n path !== git &&\n !STATE.includes(relative(path, git).split('/')[0] as string)\n );\n });\n}\n\nfunction rediscovers(paths: readonly string[], repos: readonly string[]): boolean {\n return paths.some((path) => {\n const parts = path.split('/');\n const git = parts.indexOf('.git');\n if (git > 0) return !repos.includes(parts.slice(0, git).join('/') || '/');\n if (parts.some((part) => part && !scanned(part))) return false;\n return !repos.some((repo) => within(path, repo));\n });\n}\n\nexport function attachChanges(options: ChangesOptions): ChangesRuntime {\n const { env } = options;\n const view = replicatedState<ChangesView>({ unavailable: null, changes: [] });\n let repos: string[] | null = null;\n let opened: Promise<void> | null = null;\n let watcher: FileWatcher | undefined;\n let stop = () => undefined as void;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(work: () => Promise<T>): Promise<T> => {\n const next = lock.then(work, work);\n lock = next.catch(() => undefined);\n return next;\n };\n const hasGit = async (context: Context) =>\n (await run(env, 'command -v git', HOME, context)).code === 0;\n const missing = (context: Context) => {\n repos = null;\n view.replace(context, { unavailable: NO_GIT, changes: [] });\n };\n const scan = async (context: Context) => {\n if (!repos?.length && !(await hasGit(context))) return missing(context);\n repos ??= await discover(options, context);\n const changes: FileChange[] = [];\n const live: string[] = [];\n let checked = false;\n for (const repo of repos) {\n const found = await statusOf(env, repo, context);\n if (!found) {\n if (!checked && !(await hasGit(context))) return missing(context);\n checked = true;\n const still = await env.exists(joined(repo, '.git'), context);\n if (!still.ok || !still.value) continue;\n }\n live.push(repo);\n changes.push(...(found ?? view.value.changes.filter((change) => change.repo === repo)));\n }\n repos = live;\n view.replace(context, { unavailable: live.length ? null : NO_REPO, changes });\n };\n const pendingOf = async (path: string, context: Context): Promise<Target | null> => {\n const listed = () => view.value.changes.find((item) => item.path === path);\n if (!listed()) await scan(context);\n const repo = listed()?.repo;\n if (!repo) return null;\n const rel = relative(path, repo);\n const ran = await run(env, [...STATUS, '--', `:(literal)${rel}`], repo, context);\n if (ran.code !== 0) throw failure(ran, `git status ${rel}`);\n const fresh = parseStatus(ran.out).find((item) => item.path === rel);\n if (fresh) return { path, repo, kind: fresh.kind };\n await scan(context);\n return null;\n };\n const refresh = (context: Context) => serial(() => scan(context));\n let running = false;\n let again = false;\n const kick = async (context: Context) => {\n if (running) {\n again = true;\n return;\n }\n running = true;\n do {\n again = false;\n await refresh(context).catch(() => undefined);\n } while (again);\n running = false;\n };\n const later = (context: Context) => {\n clearTimeout(timer);\n timer = setTimeout(() => void kick(context), options.reloadMs);\n };\n const change = (path: string, context: Context, act: (found: Target) => Promise<void>) =>\n serial(async () => {\n const found = await pendingOf(path, context);\n if (!found) throw new Error(`${path} has no pending change`);\n await act(found);\n await scan(context);\n });\n const start = async (context: Context) => {\n await refresh(context);\n const watched = await env.watch(\n WATCHED.map((path) => ({ path, recursive: true, exclude: { names: ['node_modules'] } })),\n (change) => {\n const known = repos ?? [];\n const paths = 'paths' in change ? change.paths.filter((path) => !noise(path, known)) : null;\n if (paths?.length === 0) return;\n if (!paths || rediscovers(paths, known)) repos = null;\n later(context);\n },\n context\n );\n watcher = watched.ok ? watched.value : undefined;\n stop = options.agents.onChange(() => {\n repos = null;\n later(context);\n });\n };\n return {\n view,\n open(context) {\n opened ??= start(context);\n return opened;\n },\n accept: (path, context) =>\n change(path, context, async (found) => {\n const rel = relative(found.path, found.repo);\n const ran = await run(env, ['git', 'add', '-A', '--', rel], found.repo, context);\n if (ran.code !== 0) throw failure(ran, `git add ${rel}`);\n }),\n revert: (path, context) =>\n change(path, context, async (found) => {\n const rel = relative(found.path, found.repo);\n if (found.kind === 'conflict')\n throw new Error(`${found.path} has a merge conflict; resolve it with git first.`);\n if (found.kind === 'untracked') {\n const removed = await env.remove(found.path, { force: true }, context);\n if (!removed.ok) throw removed.error;\n } else {\n const ran = await run(env, ['git', 'restore', '--', rel], found.repo, context);\n if (ran.code !== 0) throw failure(ran, `git restore ${rel}`);\n }\n }),\n refresh,\n async close(context) {\n clearTimeout(timer);\n stop();\n await watcher?.close(context);\n },\n };\n}\n"],
5
+ "mappings": "AAAA,SAAoD,uBAAuB;AAG3E,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAWA,MAAM,aAAa;AAQ1B,MAAM,UAAU,CAAC,MAAM,QAAQ,SAAS;AACxC,MAAM,WAAW,CAAC,QAAQ,gBAAgB;AAI1C,eAAe,KAAK,KAAU,KAAa,OAAe,KAAkB,SAAkB;AAC5F,QAAM,SAAS,MAAM,IAAI,QAAQ,KAAK,OAAO;AAC7C,MAAI,CAAC,OAAO,GAAI;AAChB,MAAI,OAAO,MAAM,KAAK,CAAC,UAAU,MAAM,SAAS,MAAM,GAAG;AACvD,QAAI,IAAI,GAAG;AACX;AAAA,EACF;AACA,MAAI,SAAS,WAAY;AACzB,QAAM,OAAO,OAAO,MACjB,OAAO,CAAC,UAAU,MAAM,SAAS,eAAe,QAAQ,MAAM,IAAI,CAAC,EACnE,MAAM,GAAG,YAAY;AACxB,aAAW,SAAS,KAAM,OAAM,KAAK,KAAK,MAAM,MAAM,QAAQ,GAAG,KAAK,OAAO;AAC/E;AAEA,eAAe,KAAK,QAAgB,SAAqC;AACvE,QAAM,QAAQ,OAAO,MAAM;AAC3B,QAAM,MAAM;AAAA,IACV,MAAM;AAAA,IACN,GAAG,OAAO,KAAK,MAAM,KAAK,EAAE,OAAO,CAAC,OAAO,OAAO,MAAM,MAAM;AAAA,IAC9D,GAAG,OAAO,QAAQ,MAAM,MAAM,EAC3B,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,MAAM,QAAQ,CAAC,MAAM,WAAW,CAAC,MAAM,MAAM,EACpE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE;AAAA,EACrB;AACA,QAAM,MAAgB,CAAC;AACvB,aAAW,MAAM,KAAK;AACpB,UAAM,eAAe,MAAM,OAAO,aAAa,IAAI,OAAO;AAC1D,QAAI,aAAc,KAAI,MAAM,MAAM,aAAa,MAAM,OAAO,GAAG,OAAO,IAAI;AAAA,EAC5E;AACA,SAAO;AACT;AAEA,eAAe,SAAS,SAAyB,SAAqC;AACpF,QAAM,EAAE,IAAI,IAAI;AAChB,QAAM,QAAkB,CAAC;AACzB,aAAW,OAAO,MAAM,KAAK,QAAQ,QAAQ,OAAO,GAAG;AACrD,UAAM,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,aAAa,iBAAiB,GAAG,KAAK,OAAO;AAChF,QAAI,IAAI,SAAS,KAAK,IAAI,IAAI,KAAK,EAAG,OAAM,KAAK,IAAI,IAAI,KAAK,CAAC;AAAA,EACjE;AACA,QAAM,SAAS,oBAAI,IAAY;AAC/B,QAAM,KAAK,KAAK,MAAM,GAAG,QAAQ,OAAO;AACxC,QAAM,SAAS,MAAM,IAAI,QAAQ,QAAQ,OAAO;AAChD,MAAI,OAAO;AACT,eAAW,SAAS,OAAO;AACzB,UAAI,MAAM,SAAS,YAAa,OAAM,KAAK,KAAK,MAAM,MAAM,GAAG,QAAQ,OAAO;AAAA;AAClF,QAAM,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC;AAChD,SAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,EAAE;AAAA,IAC3B,CAAC,SAAS,CAAC,SAAS,KAAK,CAAC,aAAa,OAAO,MAAM,QAAQ,CAAC;AAAA,EAC/D;AACF;AAEA,MAAM,QAAQ,CAAC,OAA0B,YACvC,kBAAkB,MAAM,IAAI,KAAK,EAAE,KAAK,GAAG,CAAC,MAAM,OAAO;AAE3D,eAAe,SAAS,KAAU,MAAc,MAAc,SAAkB;AAC9E,QAAM,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,QAAQ,IAAI,IAAI,EAAE,GAAG,MAAM,OAAO;AACrE,SAAO,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG,IAAI;AAC3C;AAEA,SAAS,QAAQ,OAAsC;AACrD,QAAM,MAAkB,CAAC;AACzB,MAAI,QAAQ;AACZ,aAAW,QAAQ,OAAO;AACxB,QAAI,QAAQ,KAAK,OAAO,aAAa;AACnC,UAAI,KAAK,CAAC,CAAC;AACX,cAAQ;AAAA,IACV;AACA,QAAI,GAAG,EAAE,GAAG,KAAK,IAAI;AACrB,aAAS,KAAK;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAe,QACb,KACA,MACA,SACA,SACqC;AACrC,QAAM,QAAQ,QAAQ,OAAO,CAAC,SAAS,KAAK,WAAW,OAAO,EAAE,IAAI,CAAC,SAAS,KAAK,IAAI;AACvF,QAAM,MAAM,oBAAI,IAA2B;AAC3C,MAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,QAAM,UAAU,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,IACpD,OACA,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM,IAAI,CAAC,SAAS,IAAI,IAAI,EAAE;AAAA,MAC9B;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACJ,QAAM,UAAU,SAAS,SAAS,IAAI,WAAW,QAAQ,KAAK,KAAK,IAAI;AACvE,MAAI,CAAC,SAAS;AACZ,eAAW,QAAQ,MAAO,KAAI,IAAI,MAAM,MAAM,SAAS,KAAK,MAAM,MAAM,OAAO,CAAC;AAChF,WAAO;AAAA,EACT;AACA,aAAW,QAAQ,MAAO,KAAI,IAAI,MAAM,IAAI;AAC5C,QAAM,QAAQ,QAAQ,OAAO,CAAC,SAAS,KAAK,QAAQ,QAAQ;AAC5D,aAAW,SAAS,QAAQ,KAAK,GAAG;AAClC,UAAM,QAAQ,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,QACE,MAAM,IAAI,CAAC,SAAS,KAAK,GAAG;AAAA,QAC5B;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,UAAM,SACJ,MAAM,SAAS,KAAK,CAAC,MAAM,IAAI,SAAS,QAAQ,IAC5C,WAAW,IAAI,YAAY,EAAE,OAAO,MAAM,GAAG,GAAG,MAAM,MAAM,IAC5D;AACN,eAAW,CAAC,GAAG,IAAI,KAAK,MAAM,QAAQ,GAAG;AACvC,YAAM,QAAQ,SAAS,CAAC;AACxB,UAAI;AAAA,QACF,KAAK;AAAA,QACL,SAAU,QAAQ,OAAO,KAAK,IAAI,OAAQ,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,OAAO;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,OACb,KACA,MACA,SACA,QACA,SACA;AACA,QAAM,OAAO,OAAO,MAAM,QAAQ,IAAI;AACtC,QAAM,OAAO,QAAQ,WAAW,YAAY,OAAO,MAAM,IAAI,eAAe,MAAM,OAAO;AACzF,QAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,KAAK,IAAI;AAC9C,QAAM,SACH,QAAQ,WAAW,WAAW,WAAW,QACzC,QAAQ,WAAW,aAAa,UAAU;AAC7C,SAAO,EAAE,MAAM,QAAQ,SAAS,OAAO,QAAQ,OAAO,SAAS,OAAO,MAAM;AAC9E;AAEA,eAAe,SAAS,KAAU,MAAc,SAAgD;AAC9F,QAAM,MAAM,MAAM,IAAI,KAAK,QAAQ,MAAM,OAAO;AAChD,MAAI,IAAI,SAAS,EAAG,QAAO;AAC3B,QAAM,UAAU,YAAY,IAAI,GAAG,EAAE,MAAM,GAAG,SAAS;AACvD,QAAM,SAAS,MAAM,QAAQ,KAAK,MAAM,SAAS,OAAO;AACxD,QAAM,MAAoB,CAAC;AAC3B,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAO,MAAM,OAAO,KAAK,MAAM,MAAM,OAAO,IAAI,KAAK,IAAI,KAAK,MAAM,OAAO;AACjF,QAAI,KAAK,EAAE,GAAG,MAAM,MAAM,QAAQ,KAAK,QAAQ,MAAM,KAAK,KAAK,CAAC;AAAA,EAClE;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACxD;AAEA,MAAM,QAAQ,CAAC,SAAS,QAAQ,MAAM;AAItC,SAAS,MAAM,MAAc,OAAmC;AAC9D,SAAO,MAAM,KAAK,CAAC,SAAS;AAC1B,UAAM,MAAM,OAAO,MAAM,MAAM;AAC/B,WACE,OAAO,MAAM,GAAG,KAChB,SAAS,OACT,CAAC,MAAM,SAAS,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,CAAW;AAAA,EAE/D,CAAC;AACH;AAEA,SAAS,YAAY,OAA0B,OAAmC;AAChF,SAAO,MAAM,KAAK,CAAC,SAAS;AAC1B,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,UAAM,MAAM,MAAM,QAAQ,MAAM;AAChC,QAAI,MAAM,EAAG,QAAO,CAAC,MAAM,SAAS,MAAM,MAAM,GAAG,GAAG,EAAE,KAAK,GAAG,KAAK,GAAG;AACxE,QAAI,MAAM,KAAK,CAAC,SAAS,QAAQ,CAAC,QAAQ,IAAI,CAAC,EAAG,QAAO;AACzD,WAAO,CAAC,MAAM,KAAK,CAAC,SAAS,OAAO,MAAM,IAAI,CAAC;AAAA,EACjD,CAAC;AACH;AAEO,SAAS,cAAc,SAAyC;AACrE,QAAM,EAAE,IAAI,IAAI;AAChB,QAAM,OAAO,gBAA6B,EAAE,aAAa,MAAM,SAAS,CAAC,EAAE,CAAC;AAC5E,MAAI,QAAyB;AAC7B,MAAI,SAA+B;AACnC,MAAI;AACJ,MAAI,OAAO,MAAM;AACjB,MAAI;AACJ,MAAI,OAAyB,QAAQ,QAAQ;AAC7C,QAAM,SAAS,CAAI,SAAuC;AACxD,UAAM,OAAO,KAAK,KAAK,MAAM,IAAI;AACjC,WAAO,KAAK,MAAM,MAAM,MAAS;AACjC,WAAO;AAAA,EACT;AACA,QAAM,SAAS,OAAO,aACnB,MAAM,IAAI,KAAK,kBAAkB,MAAM,OAAO,GAAG,SAAS;AAC7D,QAAM,UAAU,CAAC,YAAqB;AACpC,YAAQ;AACR,SAAK,QAAQ,SAAS,EAAE,aAAa,QAAQ,SAAS,CAAC,EAAE,CAAC;AAAA,EAC5D;AACA,QAAM,OAAO,OAAO,YAAqB;AACvC,QAAI,CAAC,OAAO,UAAU,CAAE,MAAM,OAAO,OAAO,EAAI,QAAO,QAAQ,OAAO;AACtE,cAAU,MAAM,SAAS,SAAS,OAAO;AACzC,UAAM,UAAwB,CAAC;AAC/B,UAAM,OAAiB,CAAC;AACxB,QAAI,UAAU;AACd,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,MAAM,SAAS,KAAK,MAAM,OAAO;AAC/C,UAAI,CAAC,OAAO;AACV,YAAI,CAAC,WAAW,CAAE,MAAM,OAAO,OAAO,EAAI,QAAO,QAAQ,OAAO;AAChE,kBAAU;AACV,cAAM,QAAQ,MAAM,IAAI,OAAO,OAAO,MAAM,MAAM,GAAG,OAAO;AAC5D,YAAI,CAAC,MAAM,MAAM,CAAC,MAAM,MAAO;AAAA,MACjC;AACA,WAAK,KAAK,IAAI;AACd,cAAQ,KAAK,GAAI,SAAS,KAAK,MAAM,QAAQ,OAAO,CAACA,YAAWA,QAAO,SAAS,IAAI,CAAE;AAAA,IACxF;AACA,YAAQ;AACR,SAAK,QAAQ,SAAS,EAAE,aAAa,KAAK,SAAS,OAAO,SAAS,QAAQ,CAAC;AAAA,EAC9E;AACA,QAAM,YAAY,OAAO,MAAc,YAA6C;AAClF,UAAM,SAAS,MAAM,KAAK,MAAM,QAAQ,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI;AACzE,QAAI,CAAC,OAAO,EAAG,OAAM,KAAK,OAAO;AACjC,UAAM,OAAO,OAAO,GAAG;AACvB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,MAAM,SAAS,MAAM,IAAI;AAC/B,UAAM,MAAM,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,MAAM,aAAa,GAAG,EAAE,GAAG,MAAM,OAAO;AAC/E,QAAI,IAAI,SAAS,EAAG,OAAM,QAAQ,KAAK,cAAc,GAAG,EAAE;AAC1D,UAAM,QAAQ,YAAY,IAAI,GAAG,EAAE,KAAK,CAAC,SAAS,KAAK,SAAS,GAAG;AACnE,QAAI,MAAO,QAAO,EAAE,MAAM,MAAM,MAAM,MAAM,KAAK;AACjD,UAAM,KAAK,OAAO;AAClB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,CAAC,YAAqB,OAAO,MAAM,KAAK,OAAO,CAAC;AAChE,MAAI,UAAU;AACd,MAAI,QAAQ;AACZ,QAAM,OAAO,OAAO,YAAqB;AACvC,QAAI,SAAS;AACX,cAAQ;AACR;AAAA,IACF;AACA,cAAU;AACV,OAAG;AACD,cAAQ;AACR,YAAM,QAAQ,OAAO,EAAE,MAAM,MAAM,MAAS;AAAA,IAC9C,SAAS;AACT,cAAU;AAAA,EACZ;AACA,QAAM,QAAQ,CAAC,YAAqB;AAClC,iBAAa,KAAK;AAClB,YAAQ,WAAW,MAAM,KAAK,KAAK,OAAO,GAAG,QAAQ,QAAQ;AAAA,EAC/D;AACA,QAAM,SAAS,CAAC,MAAc,SAAkB,QAC9C,OAAO,YAAY;AACjB,UAAM,QAAQ,MAAM,UAAU,MAAM,OAAO;AAC3C,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,GAAG,IAAI,wBAAwB;AAC3D,UAAM,IAAI,KAAK;AACf,UAAM,KAAK,OAAO;AAAA,EACpB,CAAC;AACH,QAAM,QAAQ,OAAO,YAAqB;AACxC,UAAM,QAAQ,OAAO;AACrB,UAAM,UAAU,MAAM,IAAI;AAAA,MACxB,QAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,WAAW,MAAM,SAAS,EAAE,OAAO,CAAC,cAAc,EAAE,EAAE,EAAE;AAAA,MACvF,CAACA,YAAW;AACV,cAAM,QAAQ,SAAS,CAAC;AACxB,cAAM,QAAQ,WAAWA,UAASA,QAAO,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,MAAM,KAAK,CAAC,IAAI;AACvF,YAAI,OAAO,WAAW,EAAG;AACzB,YAAI,CAAC,SAAS,YAAY,OAAO,KAAK,EAAG,SAAQ;AACjD,cAAM,OAAO;AAAA,MACf;AAAA,MACA;AAAA,IACF;AACA,cAAU,QAAQ,KAAK,QAAQ,QAAQ;AACvC,WAAO,QAAQ,OAAO,SAAS,MAAM;AACnC,cAAQ;AACR,YAAM,OAAO;AAAA,IACf,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL;AAAA,IACA,KAAK,SAAS;AACZ,iBAAW,MAAM,OAAO;AACxB,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,CAAC,MAAM,YACb,OAAO,MAAM,SAAS,OAAO,UAAU;AACrC,YAAM,MAAM,SAAS,MAAM,MAAM,MAAM,IAAI;AAC3C,YAAM,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,OAAO,MAAM,MAAM,GAAG,GAAG,MAAM,MAAM,OAAO;AAC/E,UAAI,IAAI,SAAS,EAAG,OAAM,QAAQ,KAAK,WAAW,GAAG,EAAE;AAAA,IACzD,CAAC;AAAA,IACH,QAAQ,CAAC,MAAM,YACb,OAAO,MAAM,SAAS,OAAO,UAAU;AACrC,YAAM,MAAM,SAAS,MAAM,MAAM,MAAM,IAAI;AAC3C,UAAI,MAAM,SAAS;AACjB,cAAM,IAAI,MAAM,GAAG,MAAM,IAAI,mDAAmD;AAClF,UAAI,MAAM,SAAS,aAAa;AAC9B,cAAM,UAAU,MAAM,IAAI,OAAO,MAAM,MAAM,EAAE,OAAO,KAAK,GAAG,OAAO;AACrE,YAAI,CAAC,QAAQ,GAAI,OAAM,QAAQ;AAAA,MACjC,OAAO;AACL,cAAM,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,WAAW,MAAM,GAAG,GAAG,MAAM,MAAM,OAAO;AAC7E,YAAI,IAAI,SAAS,EAAG,OAAM,QAAQ,KAAK,eAAe,GAAG,EAAE;AAAA,MAC7D;AAAA,IACF,CAAC;AAAA,IACH;AAAA,IACA,MAAM,MAAM,SAAS;AACnB,mBAAa,KAAK;AAClB,WAAK;AACL,YAAM,SAAS,MAAM,OAAO;AAAA,IAC9B;AAAA,EACF;AACF;",
6
6
  "names": ["change"]
7
7
  }
@@ -27,6 +27,15 @@ export interface Pending {
27
27
  kind: ChangeKind;
28
28
  }
29
29
  export declare function parseStatus(out: string): Pending[];
30
+ export declare const STATUS: string[];
31
+ export declare const BATCH_BYTES: number;
32
+ export interface Stored {
33
+ path: string;
34
+ oid: string;
35
+ size: number;
36
+ }
37
+ export declare function parseCheck(out: string, paths: readonly string[]): Stored[] | null;
38
+ export declare function parseBatch(bytes: Uint8Array, count: number): (Uint8Array | null)[] | null;
30
39
  export declare function textOf(bytes: Uint8Array): string | null;
31
40
  export declare function shown(text: string): string | null;
32
41
  export declare function scanned(name: string): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "6.18.12",
3
+ "version": "6.18.13",
4
4
  "description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",