@ai-ecoverse/slicc-agent 5.8.3 → 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -11
- package/bin/agent +3 -1
- package/dist/gelatiere/index.js +16 -19
- package/dist/gelatiere/index.js.map +2 -2
- package/dist/kernel/agent-users.js +11 -0
- package/dist/kernel/agent-users.js.map +2 -2
- package/dist/licks/boot.js +10 -1
- package/dist/licks/boot.js.map +2 -2
- package/dist/licks/config.js +0 -37
- package/dist/licks/config.js.map +3 -3
- package/dist/licks/crontab.js +141 -0
- package/dist/licks/crontab.js.map +7 -0
- package/dist/licks/extension.js +4 -69
- package/dist/licks/extension.js.map +2 -2
- package/dist/licks/index.js +3 -5
- package/dist/licks/index.js.map +2 -2
- package/dist/licks/sources.js +42 -14
- package/dist/licks/sources.js.map +2 -2
- package/dist/main.js +1 -1
- package/dist/main.js.map +2 -2
- package/dist/scoops/cli.js +32 -0
- package/dist/scoops/cli.js.map +2 -2
- package/dist/scoops/index.js +7 -5
- package/dist/scoops/index.js.map +2 -2
- package/dist/scoops/requests.js +22 -7
- package/dist/scoops/requests.js.map +2 -2
- package/dist/spectrum/monitor-port.js +0 -1
- package/dist/spectrum/monitor-port.js.map +2 -2
- package/dist/types/gelatiere/index.d.ts +2 -1
- package/dist/types/kernel/agent-users.d.ts +2 -0
- package/dist/types/licks/boot.d.ts +12 -1
- package/dist/types/licks/config.d.ts +0 -7
- package/dist/types/licks/crontab.d.ts +37 -0
- package/dist/types/licks/extension.d.ts +1 -17
- package/dist/types/licks/index.d.ts +1 -1
- package/dist/types/licks/sources.d.ts +1 -3
- package/dist/types/scoops/cli.d.ts +2 -0
- package/dist/types/scoops/requests.d.ts +5 -1
- package/package.json +5 -5
- package/packages/vfs-root/skills/gelatiere/SKILL.md +2 -2
- package/packages/vfs-root/skills/licks/SKILL.md +16 -12
- package/dist/licks/cron.js +0 -120
- package/dist/licks/cron.js.map +0 -7
- package/dist/licks/schedules.js +0 -50
- package/dist/licks/schedules.js.map +0 -7
- package/dist/types/licks/cron.d.ts +0 -16
- package/dist/types/licks/schedules.d.ts +0 -25
package/README.md
CHANGED
|
@@ -100,9 +100,9 @@ With users, each cone and each scoop is a kernel user, and works as that user:
|
|
|
100
100
|
| skills for every agent | `/usr/share/skills/` (root, `0755`), as without users |
|
|
101
101
|
| an agent's own skills | `~/.pi/agent/skills` and `~/.agents/skills` in its home; only that agent sees them |
|
|
102
102
|
| memory | `/root/.pi/agent/memory/` and `/root/.pi/agent/agent-memory/` (root; agents use `memory_write` and `memory`) |
|
|
103
|
-
| licks | each cone's `~/.slicc`, whose
|
|
103
|
+
| licks | each cone's `~/.slicc`, whose `cone` target is that cone; root's `/root/.slicc`, whose `cone` is the active cone. Schedules are each user's kernel crontab: a cone's, a scoop's own, and root's |
|
|
104
104
|
| sprinkles | each cone's `~/sprinkles`, every cone's in the rail, with the id `<cone>/<name>` (the cone's id, such as `cone-2`) and the cone as `agentId`; `panel open sprinkle:<cone>/<name>` opens one, and spectrum scopes the rest by cone. welcome and suggestions keep their plain ids. spectrum ≥ 1.56.0 groups the rail by cone, with the selected cone first; an older spectrum lists them ungrouped. A sprinkle's files are read as its owner, inside the owner's home |
|
|
105
|
-
| the gelatiere | its procedure
|
|
105
|
+
| the gelatiere | its procedure in the first cone's home, written as that cone, and its nightly line in the gelatiere scoop's own crontab; `gelatiere install` and `suggest` run as the caller |
|
|
106
106
|
| scoop reports | the cone's `~/.slicc/reports/<handle>/` |
|
|
107
107
|
| sync transcripts | `/tmp/agent-sessions/<user>/` (`0700`), or `~/sessions/` with `--persist-session` |
|
|
108
108
|
| the suggestions store and catalog cache, the welcome flag and profile | `/var/lib/slicc/agent/shared/` (root, `0700`). The built-in welcome and suggestions sprinkles read it as `/shared/…`, through the worker and only inside that folder; a user's sprinkle reads `/shared/…` as its owner's home |
|
|
@@ -201,21 +201,28 @@ gone /home/notes/old.md
|
|
|
201
201
|
|
|
202
202
|
**Stop.** pi's `Conversation.abort()` withdraws every queued steer and follow-up, so a lick that wasn't placed yet is withdrawn with them, and the events merged behind it are dropped too: Stop leaves the cone quiet. Events that arrive after Stop are delivered as usual, and an idle cone starts a run for them.
|
|
203
203
|
|
|
204
|
-
**Configuration** lives in files under `~/.slicc` (`/home/.slicc`), which the user and the agent edit with any tool. The worker watches the folder and reconciles on every change. Each malformed line or file becomes one lick naming the file, the line and the problem, once per distinct problem, with `severity="error"`; the entry is ignored until it's fixed. Names are 1–64 of `a-z 0-9 . _ -`, starting with a letter or digit. A target is `cone` (the active cone) or `scoop:<handle>`; a lick for a scoop that is stopped or gone goes to the cone, and webhooks always go to the cone. Files starting with `.` or ending in `~` are skipped; any other file that isn't `<name>.json` in `watches/` or `webhooks/` is an error.
|
|
204
|
+
**Configuration** of watches and webhooks lives in files under `~/.slicc` (`/home/.slicc`), which the user and the agent edit with any tool. The worker watches the folder and reconciles on every change. Each malformed line or file becomes one lick naming the file, the line and the problem, once per distinct problem, with `severity="error"`; the entry is ignored until it's fixed. Names are 1–64 of `a-z 0-9 . _ -`, starting with a letter or digit. A target is `cone` (the active cone) or `scoop:<handle>`; a lick for a scoop that is stopped or gone goes to the cone, and webhooks always go to the cone. Files starting with `.` or ending in `~` are skipped; any other file that isn't `<name>.json` in `watches/` or `webhooks/` is an error.
|
|
205
205
|
|
|
206
|
-
- `~/.slicc/crontab`, one job per line: `<schedule> <name> [<target>] [<message>]`. The schedule is five fields (minute, hour, day of month, month, day of week, with `*`, ranges, steps, lists and `jan`–`dec`/`sun`–`sat` names; 7 is Sunday, and a job whose day of month and day of week are both set runs on either) or `@yearly`, `@monthly`, `@weekly`, `@daily`, `@hourly`. Times are the browser's local time. Blank lines and `#` comments are ignored.
|
|
207
|
-
```
|
|
208
|
-
*/15 9-17 * * mon-fri standup Summarize what changed in ~/notes since the last standup
|
|
209
|
-
@daily backup scoop:ops
|
|
210
|
-
```
|
|
211
206
|
- `~/.slicc/watches/<name>.json`: `{ "path": "~/notes", "glob": "**/*.md", "target": "cone", "message": "…", "debounce": 500 }`. `path` is absolute or starts with `~/`; `glob` (default `**`) is matched against paths relative to it, with `*`, `**`, `?`, `[…]` and `{a,b}`. `debounce` is 100–60000 ms. `node_modules` and `.git` are never scanned.
|
|
212
207
|
- `~/.slicc/webhooks/<name>.json`: `{ "target": "cone", "message": "…" }`. The URL comes from the tray hub (milestone E); until then, `AgentControl.webhook(name, { id, headers, body })` is how a delivery arrives.
|
|
213
208
|
|
|
209
|
+
**Schedules** are the kernel's crontab (slicc-kernel ≥ 1.110.0), not the agent's: each user's table in `/var/spool/cron/crontabs/<user>`, installed with `crontab -` and listed with `crontab -l`. The kernel runs each due job as its user, with `$SHELL -c` in `$HOME`, and a job that runs `agent send` comes back as a cron lick:
|
|
210
|
+
```
|
|
211
|
+
# name: standup
|
|
212
|
+
*/15 9-17 * * mon-fri agent send cone "Summarize what changed in ~/notes since the last standup"
|
|
213
|
+
# name: backup
|
|
214
|
+
@daily agent send scoop:ops
|
|
215
|
+
```
|
|
216
|
+
- **`agent send` under `SLICC_CRON=1`** (the kernel sets it for every job, with `SLICC_CRON_JOB`, `SLICC_CRON_DUE` and `SLICC_CRON_MISSED`) delivers `<lick channel="cron">` instead of a message: source and title are the job's `# name:` (the kernel's hash of the line without one), the text says when it was due (`standup fired at 2026-10-17 11:00`), the message is the body, and `count` is 1 plus the missed runs (`standup was due at 2026-10-17 14:00; 2 more fires were missed while seven was closed`). Its id is `<job>@<due>`, so a run is delivered once. The `agent` script passes the four variables in its request (protocol `slicc-agent/2`; `slicc-agent/1` requests still parse).
|
|
217
|
+
- **Which agent:** a job's environment is a login's, so `SLICC_AGENT` and the page's variables don't reach it. The `agent` script needs only `$USER`: it writes to `/var/lib/slicc/agent/requests/$USER/`, the folder of that kernel user, so the job's user decides who it is. In a cone's crontab, `agent send cone` wakes that cone and `agent send <handle>` one of its scoops (a scoop the cone doesn't have falls back to the cone); in root's, `cone` is the active cone; a scoop's own crontab can only wake the scoop itself. A frozen cone's job is skipped with the usual `warn` lick.
|
|
218
|
+
- **Missed runs, overlap, several tabs and logs** are the kernel's: missed runs fold into one, at most once per due time, one tab per storage root fires, and each run's output goes to `/var/log/cron/<user>/<name>.log`.
|
|
219
|
+
- **Moving `~/.slicc/crontab`:** the agent no longer schedules anything. At start and whenever the file changes, each `~/.slicc/crontab` (`<schedule> <name> [<target>] [<message>]` per line) is moved into the kernel crontab: its jobs become `# name: <name>` and `<schedule> agent send <target> '<message>'`, replacing jobs of the same names and keeping the rest of the table. A line aimed at a live scoop goes into that scoop's own user's crontab; the others go into the crontab of the file's owner (the cone's user, or root's for `/root/.slicc` and without kernel users). Then the file is kept as `~/.slicc/crontab.migrated` and removed. A line that doesn't parse, or a table `crontab` refuses (it exits 1 and keeps the old table), becomes a `cron` lick with `severity="error"` naming the line and the problem, and the file stays until it's fixed. Durable `slicc.cron` tasks from earlier versions are aborted at start.
|
|
220
|
+
|
|
214
221
|
**Triage.** A watch or webhook file can add `"triage": "<which events the agent should see>"`, a sentence of 1–1000 characters such as `"pushes to main that touch src/"`. Then each of its events (a debounced batch of changes, or a webhook request) first goes to Clef Flash (`adobe/clef-flash`, see Classifiers) with the channel, source, title, text, body (cut at 4000 characters) and up to 50 paths. One `choice` question asks whether the event matches that sentence: `act` or `ignore`. When the answer is `ignore` with a probability of at least 0.75 (`TRIAGE_SKIP`), the lick isn't delivered and starts no turn. Instead, a passive `slicc.lick-triage` entry goes into the target's transcript. It has no model message, so neither the model nor the chat sees it, and it records the would-be lick with the triage sentence, the decision, Clef's confidence and probabilities, the model and the cost. An event whose delivery id was already seen isn't triaged again. A skipped one is remembered only after its record is written, under a `requestId` taken from the delivery id, so a crash in between neither loses the record nor writes it twice. Triage fails open: without an Adobe sign-in, or when the proxy errors, times out (15 s) or returns any other answer, the lick is delivered as usual. Watch notices (stopped, overflow), config errors, frozen notices, crontab jobs and boot licks are never triaged. A small webhook event is about 220 input tokens, which is $0.00002 against the weekly budget; a 4000-character body is about $0.0001.
|
|
215
222
|
|
|
216
223
|
**Sources.**
|
|
217
224
|
- `fswatch`: the kernel environment's `watch()` reports changed paths; those matching the glob are collected for `debounce` ms and delivered as one lick with `changed` or `gone` per path. A watch that overflows or stops says so in a lick (a stopped one with `severity="error"`, an overflow with `severity="warn"`), and saving its file restarts it. Changes made while no worker runs aren't reported. **The agent's own changes are dropped**, so it never licks itself: the kernel environment records every path its file tools write, edit, move or remove, and the time its `bash` commands run; a change to such a path within 2 s, or any change while an agent command runs or within 2 s after it, counts as the agent's. A change the user makes while an agent command runs is therefore dropped too. Background processes the agent started (`&`) aren't tracked, so their changes do lick.
|
|
218
|
-
- `cron`:
|
|
225
|
+
- `cron`: `agent send` from a kernel cron job (see Schedules).
|
|
219
226
|
- `webhook`: each request is its own lick, with the headers and the body; a delivery `id` makes retries idempotent.
|
|
220
227
|
- `upgrade`: when the worker starts with a different slicc-agent version than the session recorded, with the release notes link. Every change of version is its own lick, also back and forth.
|
|
221
228
|
- `session-reload`: when the kernel's boot time (`btime` in `/proc/stat`) differs from the last worker start and the cone ran `bash` since then. The page reloaded, so the kernel's processes are gone; the lick lists background commands (`&`, `nohup`, `setsid`, `disown`) that are no longer running. Durable itself resumes an interrupted run.
|
|
@@ -378,7 +385,7 @@ The `memory` section, read before every request, holds the first 200 lines (at m
|
|
|
378
385
|
**Extraction.** Durable compacts a long conversation into a summary; before it does, a `beforeCompact` hook (which never declines or replaces the summary) schedules a `slicc.memory-extract` task for the cone. A cone that has been idle for `memory.idleMinutes` (30 by default, `0` turns the idle trigger off) gets the same task. The task runs only when there is something new since the last extraction (a cursor per conversation in the session document `slicc.memory-extract`), makes one model call, with the cone's model unless `memory.extractModel` names a cheaper one (`provider/model`), and writes the proposed entries through `memory_write`'s writer into the cone's memory file, within its 16 KB. The transcript it sends is redacted first, its prompt forbids secrets, and the writer redacts again. `"memory": { "extract": false }` in `~/.pi/agent/settings.json` turns extraction off. v6's curator scoop, three-way merge, curation ledger and dreamer aren't ported.
|
|
379
386
|
|
|
380
387
|
**The gelatiere.** SLICC's resident advisor, as in v6, is a scoop with the built-in `gelatiere` role. That role has its own memory (`memory: { scope: user, path: gelatiere }`), so it keeps its notes there.
|
|
381
|
-
- `gelatiere init` starts the scoop in the first cone, writes `~/.pi/agent/GELATIERE.md` (its procedure, which the user can edit) unless it exists, and
|
|
388
|
+
- `gelatiere init` starts the scoop in the first cone, writes `~/.pi/agent/GELATIERE.md` (its procedure, which the user can edit) unless it exists, and installs one job `# name: gelatiere` / `0 3 * * * agent send scoop:<handle> 'Nightly pass: …'` in the scoop's own crontab (root's without kernel users), replacing an older one. `gelatiere status` shows its schedule from there.
|
|
382
389
|
- **Procedure updates:** at every boot, an existing GELATIERE.md that is a shipped version (its sha-256 is in `SHIPPED`, or matches `GELATIERE.md.sha256`, written whenever the agent writes the file) is replaced by the built-in one. A file with the user's edits is kept, the new built-in one goes to `~/.pi/agent/GELATIERE.new.md`, and `gelatiere status` says so.
|
|
383
390
|
- On each pass it reads memory, the installed skills, the roles and the lick files, and writes at most three suggestions as JSON. `gelatiere suggest <file>` validates them and folds them into `/home/.gelatiere/suggestions.json` by id:
|
|
384
391
|
- kinds `skill`, `use-case`, `tip`, `skill-idea` and `issue`;
|
|
@@ -700,7 +707,7 @@ The agent never holds a secret's value. Values live in the vault (`vault.sliccy.
|
|
|
700
707
|
|
|
701
708
|
## The kernel
|
|
702
709
|
|
|
703
|
-
`@ai-ecoverse/slicc-kernel` is a peer dependency (`>=1.
|
|
710
|
+
`@ai-ecoverse/slicc-kernel` is a peer dependency (`>=1.110.0 <2`; until 3.31.0 the floor was 1.26.3, then 1.47.1, then 1.81.0, then 1.90.0): the agent only calls `attachKernel`, the client end of the page kernel's port, so it must match the kernel the page runs. Seven chooses that version: slicc-bios pins the certified kernel next to the agent in `src/packages/agent/package.json`, and pnpm fills the peer from that pin. The floor is 1.110.0, the first kernel with its own cron and `crontab`, which the agent's schedules need; it is ahead of the certified kernel, so slicc-bios must pin 1.110.0 or later with this agent. The kernel and the `wasm-*` commands are devDependencies, pinned to certified versions except the kernel (1.110.0, for its cron), and CI runs the unit and integration tests on them in one leg. If the port protocol ever breaks within 1.x, a patch release narrows the upper bound. Renovate opens PRs for the kernel, `wasi-pnpm`, the `wasm-*` packages and `slicc-cdp` but never merges them, because they move only after the homescoop coordinator certifies them; such a PR is merged once the cert lane has certified its version.
|
|
704
711
|
|
|
705
712
|
## Patched dependencies
|
|
706
713
|
|
package/bin/agent
CHANGED
|
@@ -6,13 +6,15 @@ case "$as" in subagent|memory|gelatiere|freezer|models|panel) ;; *) as=agent ;;
|
|
|
6
6
|
[ -d "$dir/in" ] && [ -d "$dir/out" ] || mkdir -p "$dir/in" "$dir/out" 2>/dev/null
|
|
7
7
|
who="${SLICC_AGENT:-}"
|
|
8
8
|
if [ -z "$who" ] && [ -n "${SLICC_TAB:-}" ]; then who="tab:$SLICC_TAB"; fi
|
|
9
|
+
cron=""
|
|
10
|
+
if [ "${SLICC_CRON:-}" = 1 ]; then cron="${SLICC_CRON_JOB:-} ${SLICC_CRON_DUE:-} ${SLICC_CRON_MISSED:-0}"; fi
|
|
9
11
|
|
|
10
12
|
submit() {
|
|
11
13
|
local id="$1"
|
|
12
14
|
shift
|
|
13
15
|
local tmp="$dir/in/.tmp-$id"
|
|
14
16
|
{
|
|
15
|
-
printf '%s\0' "slicc-agent/
|
|
17
|
+
printf '%s\0' "slicc-agent/2" "$as" "$who" "$PWD" "$$" "$cron" "$#"
|
|
16
18
|
if [ $# -gt 0 ]; then printf '%s\0' "$@"; fi
|
|
17
19
|
if [ "$stdin" = 1 ]; then cat; fi
|
|
18
20
|
} > "$tmp" && mv "$tmp" "$dir/in/$id"
|
package/dist/gelatiere/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
2
2
|
import { live } from "../agents.js";
|
|
3
|
+
import { ROOT_USER } from "../layout.js";
|
|
4
|
+
import { installJobs, readTable, scheduleOf, sendCommand } from "../licks/crontab.js";
|
|
3
5
|
import { skillFromText } from "../skills/skills.js";
|
|
4
6
|
import {
|
|
5
7
|
CATALOG_BASE,
|
|
@@ -40,13 +42,7 @@ const USAGE = `usage:
|
|
|
40
42
|
|
|
41
43
|
The store is ${STORE}; the suggestions sprinkle shows it.
|
|
42
44
|
`;
|
|
43
|
-
|
|
44
|
-
return `${SCHEDULE} ${ROLE} scoop:${handle} Nightly pass: read ~/.pi/agent/GELATIERE.md and follow it.`;
|
|
45
|
-
}
|
|
46
|
-
const JOB = new RegExp(`^((?:\\S+\\s+){4}\\S+|@\\w+)\\s+${ROLE}(?:\\s|$)`);
|
|
47
|
-
function scheduleOf(entry) {
|
|
48
|
-
return JOB.exec(entry.trim())?.[1]?.replace(/\s+/g, " ");
|
|
49
|
-
}
|
|
45
|
+
const PASS = "Nightly pass: read ~/.pi/agent/GELATIERE.md and follow it.";
|
|
50
46
|
function handleOf(agents) {
|
|
51
47
|
const found = Object.entries(agents.state().scoops).find(
|
|
52
48
|
([, record]) => record.role === ROLE && live(record)
|
|
@@ -62,14 +58,15 @@ async function callerOf(g, context) {
|
|
|
62
58
|
}
|
|
63
59
|
const fail = (out) => ({ code: 1, out: `gelatiere: ${out}
|
|
64
60
|
` });
|
|
61
|
+
async function cronUserOf(g, handle) {
|
|
62
|
+
return await g.options.cronUser?.(handle) ?? ROOT_USER;
|
|
63
|
+
}
|
|
65
64
|
async function schedule(g, handle, context) {
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
await env.writeFile(g.crontab, `${[...kept, line(handle)].join("\n")}
|
|
72
|
-
`, context);
|
|
65
|
+
const command = sendCommand(`scoop:${handle}`, PASS);
|
|
66
|
+
const user = await cronUserOf(g, handle);
|
|
67
|
+
const job = { name: ROLE, schedule: SCHEDULE, command };
|
|
68
|
+
const problems = await installJobs(g.options.env, user, [job], context);
|
|
69
|
+
return problems.length ? problems.map((problem) => problem.error).join("; ") : void 0;
|
|
73
70
|
}
|
|
74
71
|
const SHIPPED = [
|
|
75
72
|
"53bc19daa486dec24116d8cf0558a498d8476dc4e18874f8a3e9ccb9defb15bd",
|
|
@@ -132,7 +129,8 @@ async function init(g, context) {
|
|
|
132
129
|
if (spawned.code !== 0) return { code: 1, out: `gelatiere: ${spawned.out}` };
|
|
133
130
|
handle = spawned.out.trim();
|
|
134
131
|
}
|
|
135
|
-
await schedule(g, handle, context);
|
|
132
|
+
const refused = await schedule(g, handle, context);
|
|
133
|
+
if (refused) return fail(`couldn't install the crontab line of scoop ${handle}: ${refused}`);
|
|
136
134
|
return {
|
|
137
135
|
code: 0,
|
|
138
136
|
out: `the gelatiere is scoop ${handle}; it runs at ${SCHEDULE} (crontab) and with gelatiere run, following ${g.procedure}
|
|
@@ -374,8 +372,8 @@ async function status(g, context) {
|
|
|
374
372
|
const { env, agents } = g.options;
|
|
375
373
|
const store = await readStore(env, context, g.store);
|
|
376
374
|
const handle = handleOf(agents);
|
|
377
|
-
const
|
|
378
|
-
const scheduled =
|
|
375
|
+
const table = handle ? await readTable(env, await cronUserOf(g, handle), context) : "";
|
|
376
|
+
const scheduled = typeof table === "string" ? scheduleOf(table, ROLE) : void 0;
|
|
379
377
|
const counts = store ? `${open(store).length} open, ${store.filter((item) => item.takenAt).length} taken, ${store.filter((item) => item.dismissedAt).length} dismissed` : "none yet";
|
|
380
378
|
const pending = await g.files.readTextFile(g.pending, context);
|
|
381
379
|
const lines = [
|
|
@@ -419,7 +417,6 @@ function attachGelatiere(options) {
|
|
|
419
417
|
serial,
|
|
420
418
|
options,
|
|
421
419
|
now,
|
|
422
|
-
crontab: `${home}/.slicc/crontab`,
|
|
423
420
|
procedure: `${home}/.pi/agent/GELATIERE.md`,
|
|
424
421
|
pending: `${home}/.pi/agent/GELATIERE.new.md`,
|
|
425
422
|
files: options.files ?? env,
|
|
@@ -475,6 +472,7 @@ function lateGelatiere() {
|
|
|
475
472
|
}
|
|
476
473
|
export {
|
|
477
474
|
INSTALLER,
|
|
475
|
+
PASS,
|
|
478
476
|
PROCEDURE,
|
|
479
477
|
ROLE,
|
|
480
478
|
SCHEDULE,
|
|
@@ -483,7 +481,6 @@ export {
|
|
|
483
481
|
USAGE,
|
|
484
482
|
attachGelatiere,
|
|
485
483
|
lateGelatiere,
|
|
486
|
-
scheduleOf,
|
|
487
484
|
upskillScript
|
|
488
485
|
};
|
|
489
486
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/gelatiere/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport type { Roles } from '../roles/roles.ts';\nimport type { Scoops } from '../scoops/service.ts';\nimport { skillFromText } from '../skills/skills.ts';\nimport {\n CATALOG_BASE,\n CATALOG_DIR,\n type Fetcher,\n merge,\n PROFILE,\n type Profile,\n parseSkills,\n parseUseCases,\n profileOf,\n type Sheet,\n score,\n sheet,\n slugifyCompany,\n} from './catalog.ts';\nimport {\n fold,\n open,\n readStore,\n STORE,\n type Suggestion,\n storeOf,\n validate,\n writeStore,\n} from './store.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport const ROLE = 'gelatiere';\nexport const SPRINKLE = 'suggestions';\nexport const PROCEDURE = 'packages/vfs-root/gelatiere/GELATIERE.md';\nexport const SCHEDULE = '0 3 * * *';\n\nexport interface GelatiereAttach {\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n scoops: Scoops;\n roles: (context: Context) => Promise<Roles>;\n assets?: (path: string) => Promise<string>;\n now?: () => number;\n fetch?: Fetcher;\n catalog?: string;\n skills?: () => readonly { name: string }[];\n files?: ExecutionEnv;\n profile?: string;\n shared?: string;\n as?: (caller: string | null, context: Context) => Promise<Caller | undefined>;\n}\n\nexport interface Caller {\n env: ExecutionEnv;\n home: string;\n}\n\nexport interface GelatiereRuntime {\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n intercept(action: string, data: unknown, context: Context): Promise<boolean>;\n readonly seeded: Promise<Seeded>;\n}\n\nexport const USAGE = `usage:\n gelatiere init start the gelatiere scoop and its nightly crontab line\n gelatiere run ask the gelatiere for a pass now\n gelatiere suggest <file> fold a JSON array of suggestions into the store\n gelatiere deliver lick each cone with the suggestions that are new for it\n gelatiere catalog [--refresh] [--all] [--json]\n rank the www.sliccy.com skill and use-case catalog for this user\n gelatiere install <id> install a suggested skill with upskill into ~/.pi/agent/skills\n gelatiere list [--all] [--json]\n gelatiere dismiss <id> mark a suggestion as not wanted\n gelatiere status\n\nThe store is ${STORE}; the suggestions sprinkle shows it.\n`;\n\nfunction line(handle: string): string {\n return `${SCHEDULE} ${ROLE} scoop:${handle} Nightly pass: read ~/.pi/agent/GELATIERE.md and follow it.`;\n}\n\nconst JOB = new RegExp(`^((?:\\\\S+\\\\s+){4}\\\\S+|@\\\\w+)\\\\s+${ROLE}(?:\\\\s|$)`);\n\nexport function scheduleOf(entry: string): string | undefined {\n return JOB.exec(entry.trim())?.[1]?.replace(/\\s+/g, ' ');\n}\n\nfunction handleOf(agents: Agents): string | undefined {\n const found = Object.entries(agents.state().scoops).find(\n ([, record]) => record.role === ROLE && live(record)\n );\n return found?.[1].folder;\n}\n\nfunction row(item: Suggestion): string {\n const state = item.takenAt ? 'taken' : item.dismissedAt ? 'dismissed' : 'open';\n return `${item.id}\\t${item.kind}\\t${state}\\t${item.title}`;\n}\n\ntype G = {\n serial: <T>(work: () => Promise<T>) => Promise<T>;\n options: GelatiereAttach;\n now: () => number;\n crontab: string;\n procedure: string;\n pending: string;\n load: (context: Context) => Promise<Suggestion[]>;\n mark: (id: string, field: 'dismissedAt' | 'takenAt', context: Context) => Promise<boolean>;\n files: ExecutionEnv;\n caller: string | null;\n store: string;\n catalog: string;\n};\n\nasync function callerOf(g: G, context: Context): Promise<Caller> {\n return (await g.options.as?.(g.caller, context)) ?? { env: g.options.env, home: g.options.home };\n}\n\nconst fail = (out: string): Answer => ({ code: 1, out: `gelatiere: ${out}\\n` });\n\nasync function schedule(g: G, handle: string, context: Context): Promise<void> {\n const { home } = g.options;\n const env = g.files;\n const read = await env.readTextFile(g.crontab, context);\n const kept = (read.ok ? read.value : '')\n .split('\\n')\n .filter((entry) => entry.trim() && !scheduleOf(entry));\n await env.createDir(`${home}/.slicc`, { recursive: true }, context);\n await env.writeFile(g.crontab, `${[...kept, line(handle)].join('\\n')}\\n`, context);\n}\n\nexport const SHIPPED = [\n '53bc19daa486dec24116d8cf0558a498d8476dc4e18874f8a3e9ccb9defb15bd',\n '1c16654b93bc01179a71ec907919f45132b2e9f1a9ac331b213655c66387b4e5',\n '3bdd8ae18831c37996413cb0277df7141fcd0dff2d056c7b7053c34f0a3beaac',\n '23a063c8156fda9fb0ffdea20172a7f64c1576d1c009a28456c095e2866fc0e9',\n];\n\nexport type Seeded = 'written' | 'updated' | 'current' | 'kept' | 'none';\n\nasync function sha256(text: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));\n return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nasync function seed(g: G, context: Context, create: boolean): Promise<Seeded> {\n const { assets } = g.options;\n const env = g.files;\n const text = assets ? await assets(PROCEDURE).catch(() => undefined) : undefined;\n if (text === undefined) return 'none';\n const bundled = await sha256(text);\n const marker = `${g.procedure}.sha256`;\n const must = (result: { ok: boolean; error?: { message: string } }) => {\n if (!result.ok) throw new Error(result.error?.message ?? 'write failed');\n };\n const write = async (state: Seeded): Promise<Seeded> => {\n must(await env.writeFile(g.procedure, text, context));\n must(await env.writeFile(marker, `${bundled}\\n`, context));\n must(await env.remove(g.pending, { force: true }, context));\n return state;\n };\n const read = await env.readTextFile(g.procedure, context);\n if (!read.ok) return create ? write('written') : 'none';\n const mine = await sha256(read.value);\n if (mine === bundled) return write('current');\n const last = await env.readTextFile(marker, context);\n if ((last.ok && last.value.trim() === mine) || SHIPPED.includes(mine)) return write('updated');\n must(await env.writeFile(g.pending, text, context));\n return 'kept';\n}\n\nasync function init(g: G, context: Context): Promise<Answer> {\n const { options } = g;\n await seed(g, context, true);\n let handle = handleOf(options.agents);\n if (!handle) {\n const roles = await options.roles(context);\n const role = roles.roles.find((entry) => entry.name === ROLE);\n if (!role) return fail('the gelatiere role is missing');\n const spawned = await options.scoops.spawn(\n {\n cone: 'cone',\n kind: 'async',\n name: ROLE,\n role,\n prompts: [],\n target: null,\n fromAgent: false,\n request: `gelatiere-init-${g.now()}`,\n limits: roles.limits,\n },\n context\n );\n if (spawned.code !== 0) return { code: 1, out: `gelatiere: ${spawned.out}` };\n handle = spawned.out.trim();\n }\n await schedule(g, handle, context);\n return {\n code: 0,\n out: `the gelatiere is scoop ${handle}; it runs at ${SCHEDULE} (crontab) and with gelatiere run, following ${g.procedure}\\n`,\n };\n}\n\nasync function run(g: G, context: Context): Promise<Answer> {\n const handle = handleOf(g.options.agents);\n if (!handle) return fail('there is no gelatiere yet; run gelatiere init');\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: 'sprinkle:gelatiere',\n title: 'Gelatiere',\n text: 'run',\n body: `Run a pass now: read ${g.procedure} and follow it.`,\n target: `scoop:${handle}`,\n },\n context\n );\n return { code: 0, out: `asked scoop ${handle} for a pass\\n` };\n}\n\nasync function suggest(g: G, path: string | undefined, context: Context): Promise<Answer> {\n if (!path) return { code: 2, out: USAGE };\n const read = await (await callerOf(g, context)).env.readTextFile(path, context);\n if (!read.ok) return fail(`can't read ${path}`);\n let parsed: unknown;\n try {\n parsed = JSON.parse(read.value);\n } catch (error) {\n return fail(`${path} is not JSON (${(error as Error).message})`);\n }\n const folded = fold(await g.load(context), parsed, g.now());\n await writeStore(g.options.env, folded.store, context, g.store);\n const rejected = folded.rejected.map((reason) => `rejected ${reason}\\n`).join('');\n return {\n code: 0,\n out: `added ${folded.added}, updated ${folded.updated}, rejected ${folded.rejected.length}\\n${rejected}`,\n };\n}\n\nasync function deliver(g: G, context: Context): Promise<Answer> {\n const store = await g.load(context);\n const fresh = open(store).filter((item) => !item.deliveredAt);\n const cones = Object.keys(g.options.agents.state().cones);\n const byCone = new Map<string, Suggestion[]>();\n for (const item of fresh) {\n const named = (item.cones ?? []).filter((cone) => cones.includes(cone));\n for (const cone of named.length ? named : [g.options.agents.activeCone()])\n byCone.set(cone, [...(byCone.get(cone) ?? []), item]);\n }\n for (const [cone, items] of byCone) {\n const body = {\n added: items.length,\n open: open(store).length,\n suggestions: items.map(({ id, kind, title, body: text }) => ({\n id,\n kind,\n title,\n body: text,\n })),\n path: g.store,\n };\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${SPRINKLE}`,\n title: 'Suggestions',\n text: 'gelatiere-suggestions',\n body: JSON.stringify(body, null, 2),\n target: `cone:${cone}`,\n },\n context\n );\n }\n for (const item of fresh) item.deliveredAt = g.now();\n if (fresh.length) await writeStore(g.options.env, store, context, g.store);\n return {\n code: 0,\n out: `delivered ${fresh.length} to ${[...byCone.keys()].join(', ') || 'nobody'}\\n`,\n };\n}\n\nexport const INSTALLER = 'https://raw.githubusercontent.com/ai-ecoverse/gh-upskill/main/install.sh';\n\nexport function upskillScript(\n repo: string,\n skill: string | undefined,\n where: { path?: string; all?: boolean } = {}\n): string {\n const from = where.path ? ` --path '${where.path}'` : '';\n const which = where.all ? ' --all' : ` --skill '${skill}'`;\n return [\n 'bin=\"${PNPM_HOME:-$HOME/.local/share/pnpm}/bin\"',\n `{ command -v upskill >/dev/null 2>&1 || curl -fsSL ${INSTALLER} | bash -s -- --bin-dir \"$bin\"; }`,\n `upskill '${repo}'${from}${which} --dest-path \"$HOME/.pi/agent/skills\"`,\n ].join(' && ');\n}\n\nasync function skillsIn(\n env: ExecutionEnv,\n home: string,\n context: Context\n): Promise<Map<string, string>> {\n const root = `${home}/.pi/agent/skills`;\n const found = new Map<string, string>();\n const listed = await env.listDir(root, context);\n for (const info of listed.ok ? listed.value : []) {\n const path = `${root}/${info.name}/SKILL.md`;\n const read = await env.readTextFile(path, context);\n const name = read.ok ? skillFromText(read.value, path, 'user').skills[0]?.name : undefined;\n if (name) found.set(name, path);\n }\n return found;\n}\n\nasync function install(g: G, id: string, context: Context): Promise<Answer> {\n const { env, home } = await callerOf(g, context);\n const item = (await g.load(context)).find((entry) => entry.id === id && entry.kind === 'skill');\n const checked = item ? validate(item) : `there is no skill suggestion ${id}`;\n if (typeof checked === 'string') return fail(checked);\n const { skill, repo, path, all } = checked as Suggestion & { repo: string };\n const what = all ? `the skills${path ? ` in ${path}` : ''}` : (skill as string);\n const before = await skillsIn(env, home, context);\n const existing = skill ? before.get(skill) : undefined;\n if (!all && existing) return fail(`${skill} is already installed in ${existing}`);\n const lines: string[] = [];\n const ran = await env.exec(\n upskillScript(repo, skill, { ...(path ? { path } : {}), ...(all ? { all } : {}) }),\n { cwd: home, onOutput: (text) => void lines.push(text) },\n context\n );\n const output = lines.join('').trim().split('\\n').slice(-8).join('\\n');\n const after = await skillsIn(env, home, context);\n const added = all\n ? [...after].filter(([name, at]) => before.get(name) !== at)\n : [...after].filter(([name]) => name === skill);\n if (!ran.ok || ran.value.exitCode !== 0 || !added.length)\n return fail(\n `upskill couldn't install ${what} from github.com/${repo}${output ? `:\\n${output}` : ''}`\n );\n await g.mark(id, 'takenAt', context);\n const names = added.map(([name]) => name).sort();\n return {\n code: 0,\n out: all\n ? `installed ${names.join(', ')} in ${home}/.pi/agent/skills; ${names.map((name) => `/skill:${name}`).join(', ')} once the skills reload\\n`\n : `installed ${skill} in ${added[0]?.[1]}; it is /skill:${skill} once the skills reload\\n`,\n };\n}\n\ninterface Candidate {\n id: string;\n kind: 'skill' | 'use-case';\n title: string;\n body: string;\n evidence: string;\n score: number;\n skill?: string;\n repo?: string;\n path?: string;\n all?: boolean;\n prompt?: string;\n skills?: string[];\n}\n\nasync function profileFor(g: G, context: Context): Promise<Profile | undefined> {\n const read = await g.options.env.readTextFile(\n `${g.options.profile ?? g.options.home}/${PROFILE}`,\n context\n );\n if (!read.ok) return undefined;\n try {\n return profileOf(JSON.parse(read.value));\n } catch {\n return undefined;\n }\n}\n\nfunction sourceLine(label: string, found: Sheet): string {\n const at = found.fetchedAt ? `, fetched ${new Date(found.fetchedAt).toISOString()}` : '';\n return `${label}: ${found.state} (${found.url}${at})`;\n}\n\nasync function catalog(g: G, argv: readonly string[], context: Context): Promise<Answer> {\n const { env } = g.options;\n const home = g.options.profile ?? g.options.home;\n const mine = await callerOf(g, context);\n const base = g.options.catalog ?? CATALOG_BASE;\n const fetcher: Fetcher = g.options.fetch ?? ((url, init) => globalThis.fetch(url, init));\n const options = { env, fetcher, now: g.now(), refresh: argv.includes('--refresh') };\n const profile = await profileFor(g, context);\n const slug = slugifyCompany(profile?.company);\n const company = slug && !['catalog', 'use-cases'].includes(slug) ? slug : null;\n const [global, own, cases] = await Promise.all([\n sheet(options, `${base}catalog.json`, `${g.catalog}/catalog.json`, context),\n company\n ? sheet(options, `${base}${company}.json`, `${g.catalog}/company-${company}.json`, context)\n : undefined,\n sheet(options, `${base}use-cases.json`, `${g.catalog}/use-cases.json`, context),\n ]);\n const store = await g.load(context);\n const closed = new Set(store.filter((item) => item.dismissedAt || item.takenAt).map((i) => i.id));\n const have = new Set([\n ...(await skillsIn(mine.env, mine.home, context)).keys(),\n ...(g.options.skills?.() ?? []).map((entry) => entry.name),\n ]);\n const evidence = (reasons: string[]) =>\n `www.sliccy.com catalog: ${reasons.length ? reasons.join(', ') : 'featured'}`;\n const skills: Candidate[] = merge(parseSkills(global.rows), parseSkills(own?.rows ?? []))\n .filter((entry) => !entry.skill || !have.has(entry.skill))\n .map((entry) => {\n const ranked = score(entry, profile);\n return {\n id: `catalog-${entry.name}`,\n kind: 'skill',\n title: entry.title,\n body: entry.description || entry.title,\n evidence: evidence(ranked.reasons),\n score: ranked.score,\n ...(entry.skill ? { skill: entry.skill } : {}),\n repo: entry.repo,\n ...(entry.path ? { path: entry.path } : {}),\n ...(entry.all ? { all: true } : {}),\n };\n });\n const uses: Candidate[] = parseUseCases(cases.rows).map((entry) => {\n const ranked = score(entry, profile);\n return {\n id: `catalog-use-${entry.name}`,\n kind: 'use-case',\n title: entry.title,\n body: entry.description || entry.title,\n evidence: evidence(ranked.reasons),\n score: ranked.score,\n prompt: entry.prompt,\n ...(entry.skills.length ? { skills: entry.skills } : {}),\n };\n });\n const all = argv.includes('--all');\n const ranked = [...skills, ...uses]\n .filter((item) => !closed.has(item.id) && (all || item.score > 0))\n .sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));\n const sources = [\n sourceLine('skills', global),\n ...(own ? [sourceLine(`company ${company}`, own)] : []),\n sourceLine('use cases', cases),\n ];\n const who = profile\n ? `profile: ${home}/${PROFILE} (${[profile.role, profile.purpose].filter(Boolean).join(', ') || 'empty'})`\n : `profile: none (${home}/${PROFILE}); ranked by boost only`;\n if (argv.includes('--json'))\n return {\n code: 0,\n out: `${JSON.stringify({ sources, profile: Boolean(profile), candidates: ranked }, null, 2)}\\n`,\n };\n const rows = ranked.map(\n (item) => `${item.score}\\t${item.id}\\t${item.kind}\\t${item.title}\\t${item.evidence}`\n );\n return {\n code: 0,\n out: `${[...sources, who, ...(rows.length ? rows : ['no catalog entries match'])].join('\\n')}\\n`,\n };\n}\n\nasync function list(g: G, argv: readonly string[], context: Context): Promise<Answer> {\n const store = await g.load(context);\n const shown = argv.includes('--all') ? store : open(store);\n if (argv.includes('--json')) return { code: 0, out: `${JSON.stringify(shown, null, 2)}\\n` };\n return { code: 0, out: shown.length ? `${shown.map(row).join('\\n')}\\n` : 'no suggestions\\n' };\n}\n\nasync function status(g: G, context: Context): Promise<Answer> {\n const { env, agents } = g.options;\n const store = await readStore(env, context, g.store);\n const handle = handleOf(agents);\n const read = await g.files.readTextFile(g.crontab, context);\n const scheduled = (read.ok ? read.value : '').split('\\n').map(scheduleOf).find(Boolean);\n const counts = store\n ? `${open(store).length} open, ${store.filter((item) => item.takenAt).length} taken, ${store.filter((item) => item.dismissedAt).length} dismissed`\n : 'none yet';\n const pending = await g.files.readTextFile(g.pending, context);\n const lines = [\n `scoop: ${handle ?? 'none (gelatiere init)'}`,\n `schedule: ${scheduled ?? 'none'}`,\n `store: ${counts}`,\n ...(pending.ok\n ? [\n `procedure: ${g.procedure} has your edits, so it was kept; the new built-in one is ${g.pending}`,\n ]\n : []),\n ];\n return { code: 0, out: `${lines.join('\\n')}\\n` };\n}\n\nasync function dismiss(g: G, id: string, context: Context): Promise<Answer> {\n return (await g.mark(id, 'dismissedAt', context))\n ? { code: 0, out: `dismissed ${id}\\n` }\n : fail(`there is no suggestion ${id}`);\n}\n\nconst VERBS: Record<string, (g: G, argv: readonly string[], context: Context) => Promise<Answer>> =\n {\n init: (g, _argv, context) => init(g, context),\n run: (g, _argv, context) => run(g, context),\n suggest: (g, argv, context) => g.serial(() => suggest(g, argv[1], context)),\n deliver: (g, _argv, context) => g.serial(() => deliver(g, context)),\n install: (g, argv, context) => install(g, argv[1] ?? '', context),\n catalog: (g, argv, context) => catalog(g, argv, context),\n list: (g, argv, context) => list(g, argv, context),\n status: (g, _argv, context) => status(g, context),\n dismiss: (g, argv, context) => dismiss(g, argv[1] ?? '', context),\n };\n\nexport function attachGelatiere(options: GelatiereAttach): GelatiereRuntime {\n const { env, home } = options;\n const now = options.now ?? Date.now;\n const storeFile = options.shared ? storeOf(options.shared) : STORE;\n const load = async (context: Context) => (await readStore(env, context, storeFile)) ?? [];\n let chain: Promise<unknown> = Promise.resolve();\n const serial = <T>(work: () => Promise<T>): Promise<T> => {\n const next = chain.catch(() => undefined).then(work);\n chain = next;\n return next;\n };\n const g: G = {\n serial,\n options,\n now,\n crontab: `${home}/.slicc/crontab`,\n procedure: `${home}/.pi/agent/GELATIERE.md`,\n pending: `${home}/.pi/agent/GELATIERE.new.md`,\n files: options.files ?? env,\n caller: null,\n store: storeFile,\n catalog: options.shared ? `${options.shared}/.gelatiere/catalog` : CATALOG_DIR,\n load,\n mark: (id, field, context) =>\n serial(async () => {\n const store = await load(context);\n const item = store.find((entry) => entry.id === id);\n if (!item) return false;\n item[field] ??= now();\n await writeStore(env, store, context, storeFile);\n return true;\n }),\n };\n const seeded = seed(g, BACKGROUND_CONTEXT, false).catch((): Seeded => 'none');\n return {\n seeded,\n async command(argv, caller, context) {\n const verb = argv[0] ?? 'help';\n const handler = VERBS[verb];\n await seeded;\n if (handler) return handler({ ...g, caller }, argv, context);\n return { code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE };\n },\n async intercept(action, data, context) {\n const id = String((data as { id?: unknown } | null)?.id ?? '');\n if (action === 'gelatiere-dismiss') {\n await g.mark(id, 'dismissedAt', context);\n return true;\n }\n if (action === 'gelatiere-install' || action === 'gelatiere-try')\n await g.mark(id, 'takenAt', context);\n return false;\n },\n };\n}\n\nexport function lateGelatiere() {\n let runtime: GelatiereRuntime | undefined;\n return {\n async intercept(\n id: string,\n payload: { action: string; data: unknown },\n context: Context\n ): Promise<boolean> {\n return id === SPRINKLE && runtime !== undefined\n ? runtime.intercept(payload.action, payload.data, context)\n : false;\n },\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer> {\n return runtime\n ? runtime.command(argv, caller, context)\n : Promise.resolve({ code: 1, out: 'gelatiere: not ready yet\\n' });\n },\n attach(options: GelatiereAttach): GelatiereRuntime {\n runtime = attachGelatiere(options);\n return runtime;\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,0BAA0B;AAEnC,SAAsB,YAAY;AAIlC,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIA,MAAM,OAAO;AACb,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,WAAW;AA+BjB,MAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAYN,KAAK;AAAA;AAGpB,SAAS,KAAK,QAAwB;AACpC,SAAO,GAAG,QAAQ,IAAI,IAAI,UAAU,MAAM;AAC5C;AAEA,MAAM,MAAM,IAAI,OAAO,mCAAmC,IAAI,WAAW;AAElE,SAAS,WAAW,OAAmC;AAC5D,SAAO,IAAI,KAAK,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,QAAQ,GAAG;AACzD;AAEA,SAAS,SAAS,QAAoC;AACpD,QAAM,QAAQ,OAAO,QAAQ,OAAO,MAAM,EAAE,MAAM,EAAE;AAAA,IAClD,CAAC,CAAC,EAAE,MAAM,MAAM,OAAO,SAAS,QAAQ,KAAK,MAAM;AAAA,EACrD;AACA,SAAO,QAAQ,CAAC,EAAE;AACpB;AAEA,SAAS,IAAI,MAA0B;AACrC,QAAM,QAAQ,KAAK,UAAU,UAAU,KAAK,cAAc,cAAc;AACxE,SAAO,GAAG,KAAK,EAAE,IAAK,KAAK,IAAI,IAAK,KAAK,IAAK,KAAK,KAAK;AAC1D;AAiBA,eAAe,SAAS,GAAM,SAAmC;AAC/D,SAAQ,MAAM,EAAE,QAAQ,KAAK,EAAE,QAAQ,OAAO,KAAM,EAAE,KAAK,EAAE,QAAQ,KAAK,MAAM,EAAE,QAAQ,KAAK;AACjG;AAEA,MAAM,OAAO,CAAC,SAAyB,EAAE,MAAM,GAAG,KAAK,cAAc,GAAG;AAAA,EAAK;AAE7E,eAAe,SAAS,GAAM,QAAgB,SAAiC;AAC7E,QAAM,EAAE,KAAK,IAAI,EAAE;AACnB,QAAM,MAAM,EAAE;AACd,QAAM,OAAO,MAAM,IAAI,aAAa,EAAE,SAAS,OAAO;AACtD,QAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,IAClC,MAAM,IAAI,EACV,OAAO,CAAC,UAAU,MAAM,KAAK,KAAK,CAAC,WAAW,KAAK,CAAC;AACvD,QAAM,IAAI,UAAU,GAAG,IAAI,WAAW,EAAE,WAAW,KAAK,GAAG,OAAO;AAClE,QAAM,IAAI,UAAU,EAAE,SAAS,GAAG,CAAC,GAAG,MAAM,KAAK,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,GAAM,OAAO;AACnF;AAEO,MAAM,UAAU;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,eAAe,OAAO,MAA+B;AACnD,QAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AACnF,SAAO,CAAC,GAAG,IAAI,WAAW,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC9F;AAEA,eAAe,KAAK,GAAM,SAAkB,QAAkC;AAC5E,QAAM,EAAE,OAAO,IAAI,EAAE;AACrB,QAAM,MAAM,EAAE;AACd,QAAM,OAAO,SAAS,MAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS,IAAI;AACvE,MAAI,SAAS,OAAW,QAAO;AAC/B,QAAM,UAAU,MAAM,OAAO,IAAI;AACjC,QAAM,SAAS,GAAG,EAAE,SAAS;AAC7B,QAAM,OAAO,CAAC,WAAyD;AACrE,QAAI,CAAC,OAAO,GAAI,OAAM,IAAI,MAAM,OAAO,OAAO,WAAW,cAAc;AAAA,EACzE;AACA,QAAM,QAAQ,OAAO,UAAmC;AACtD,SAAK,MAAM,IAAI,UAAU,EAAE,WAAW,MAAM,OAAO,CAAC;AACpD,SAAK,MAAM,IAAI,UAAU,QAAQ,GAAG,OAAO;AAAA,GAAM,OAAO,CAAC;AACzD,SAAK,MAAM,IAAI,OAAO,EAAE,SAAS,EAAE,OAAO,KAAK,GAAG,OAAO,CAAC;AAC1D,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,IAAI,aAAa,EAAE,WAAW,OAAO;AACxD,MAAI,CAAC,KAAK,GAAI,QAAO,SAAS,MAAM,SAAS,IAAI;AACjD,QAAM,OAAO,MAAM,OAAO,KAAK,KAAK;AACpC,MAAI,SAAS,QAAS,QAAO,MAAM,SAAS;AAC5C,QAAM,OAAO,MAAM,IAAI,aAAa,QAAQ,OAAO;AACnD,MAAK,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,QAAS,QAAQ,SAAS,IAAI,EAAG,QAAO,MAAM,SAAS;AAC7F,OAAK,MAAM,IAAI,UAAU,EAAE,SAAS,MAAM,OAAO,CAAC;AAClD,SAAO;AACT;AAEA,eAAe,KAAK,GAAM,SAAmC;AAC3D,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,KAAK,GAAG,SAAS,IAAI;AAC3B,MAAI,SAAS,SAAS,QAAQ,MAAM;AACpC,MAAI,CAAC,QAAQ;AACX,UAAM,QAAQ,MAAM,QAAQ,MAAM,OAAO;AACzC,UAAM,OAAO,MAAM,MAAM,KAAK,CAAC,UAAU,MAAM,SAAS,IAAI;AAC5D,QAAI,CAAC,KAAM,QAAO,KAAK,+BAA+B;AACtD,UAAM,UAAU,MAAM,QAAQ,OAAO;AAAA,MACnC;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,SAAS,CAAC;AAAA,QACV,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,SAAS,kBAAkB,EAAE,IAAI,CAAC;AAAA,QAClC,QAAQ,MAAM;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,EAAG,QAAO,EAAE,MAAM,GAAG,KAAK,cAAc,QAAQ,GAAG,GAAG;AAC3E,aAAS,QAAQ,IAAI,KAAK;AAAA,EAC5B;AACA,QAAM,SAAS,GAAG,QAAQ,OAAO;AACjC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK,0BAA0B,MAAM,gBAAgB,QAAQ,gDAAgD,EAAE,SAAS;AAAA;AAAA,EAC1H;AACF;AAEA,eAAe,IAAI,GAAM,SAAmC;AAC1D,QAAM,SAAS,SAAS,EAAE,QAAQ,MAAM;AACxC,MAAI,CAAC,OAAQ,QAAO,KAAK,+CAA+C;AACxE,QAAM,EAAE,QAAQ,MAAM;AAAA,IACpB;AAAA,MACE,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM,wBAAwB,EAAE,SAAS;AAAA,MACzC,QAAQ,SAAS,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACA,SAAO,EAAE,MAAM,GAAG,KAAK,eAAe,MAAM;AAAA,EAAgB;AAC9D;AAEA,eAAe,QAAQ,GAAM,MAA0B,SAAmC;AACxF,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,GAAG,KAAK,MAAM;AACxC,QAAM,OAAO,OAAO,MAAM,SAAS,GAAG,OAAO,GAAG,IAAI,aAAa,MAAM,OAAO;AAC9E,MAAI,CAAC,KAAK,GAAI,QAAO,KAAK,cAAc,IAAI,EAAE;AAC9C,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK,KAAK;AAAA,EAChC,SAAS,OAAO;AACd,WAAO,KAAK,GAAG,IAAI,iBAAkB,MAAgB,OAAO,GAAG;AAAA,EACjE;AACA,QAAM,SAAS,KAAK,MAAM,EAAE,KAAK,OAAO,GAAG,QAAQ,EAAE,IAAI,CAAC;AAC1D,QAAM,WAAW,EAAE,QAAQ,KAAK,OAAO,OAAO,SAAS,EAAE,KAAK;AAC9D,QAAM,WAAW,OAAO,SAAS,IAAI,CAAC,WAAW,YAAY,MAAM;AAAA,CAAI,EAAE,KAAK,EAAE;AAChF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK,SAAS,OAAO,KAAK,aAAa,OAAO,OAAO,cAAc,OAAO,SAAS,MAAM;AAAA,EAAK,QAAQ;AAAA,EACxG;AACF;AAEA,eAAe,QAAQ,GAAM,SAAmC;AAC9D,QAAM,QAAQ,MAAM,EAAE,KAAK,OAAO;AAClC,QAAM,QAAQ,KAAK,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW;AAC5D,QAAM,QAAQ,OAAO,KAAK,EAAE,QAAQ,OAAO,MAAM,EAAE,KAAK;AACxD,QAAM,SAAS,oBAAI,IAA0B;AAC7C,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,KAAK,SAAS,CAAC,GAAG,OAAO,CAAC,SAAS,MAAM,SAAS,IAAI,CAAC;AACtE,eAAW,QAAQ,MAAM,SAAS,QAAQ,CAAC,EAAE,QAAQ,OAAO,WAAW,CAAC;AACtE,aAAO,IAAI,MAAM,CAAC,GAAI,OAAO,IAAI,IAAI,KAAK,CAAC,GAAI,IAAI,CAAC;AAAA,EACxD;AACA,aAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,UAAM,OAAO;AAAA,MACX,OAAO,MAAM;AAAA,MACb,MAAM,KAAK,KAAK,EAAE;AAAA,MAClB,aAAa,MAAM,IAAI,CAAC,EAAE,IAAI,MAAM,OAAO,MAAM,KAAK,OAAO;AAAA,QAC3D;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,MACR,EAAE;AAAA,MACF,MAAM,EAAE;AAAA,IACV;AACA,UAAM,EAAE,QAAQ,MAAM;AAAA,MACpB;AAAA,QACE,SAAS;AAAA,QACT,QAAQ,YAAY,QAAQ;AAAA,QAC5B,OAAO;AAAA,QACP,MAAM;AAAA,QACN,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,QAClC,QAAQ,QAAQ,IAAI;AAAA,MACtB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,aAAW,QAAQ,MAAO,MAAK,cAAc,EAAE,IAAI;AACnD,MAAI,MAAM,OAAQ,OAAM,WAAW,EAAE,QAAQ,KAAK,OAAO,SAAS,EAAE,KAAK;AACzE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK,aAAa,MAAM,MAAM,OAAO,CAAC,GAAG,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI,KAAK,QAAQ;AAAA;AAAA,EAChF;AACF;AAEO,MAAM,YAAY;AAElB,SAAS,cACd,MACA,OACA,QAA0C,CAAC,GACnC;AACR,QAAM,OAAO,MAAM,OAAO,YAAY,MAAM,IAAI,MAAM;AACtD,QAAM,QAAQ,MAAM,MAAM,WAAW,aAAa,KAAK;AACvD,SAAO;AAAA,IACL;AAAA,IACA,sDAAsD,SAAS;AAAA,IAC/D,YAAY,IAAI,IAAI,IAAI,GAAG,KAAK;AAAA,EAClC,EAAE,KAAK,MAAM;AACf;AAEA,eAAe,SACb,KACA,MACA,SAC8B;AAC9B,QAAM,OAAO,GAAG,IAAI;AACpB,QAAM,QAAQ,oBAAI,IAAoB;AACtC,QAAM,SAAS,MAAM,IAAI,QAAQ,MAAM,OAAO;AAC9C,aAAW,QAAQ,OAAO,KAAK,OAAO,QAAQ,CAAC,GAAG;AAChD,UAAM,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI;AACjC,UAAM,OAAO,MAAM,IAAI,aAAa,MAAM,OAAO;AACjD,UAAM,OAAO,KAAK,KAAK,cAAc,KAAK,OAAO,MAAM,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;AACjF,QAAI,KAAM,OAAM,IAAI,MAAM,IAAI;AAAA,EAChC;AACA,SAAO;AACT;AAEA,eAAe,QAAQ,GAAM,IAAY,SAAmC;AAC1E,QAAM,EAAE,KAAK,KAAK,IAAI,MAAM,SAAS,GAAG,OAAO;AAC/C,QAAM,QAAQ,MAAM,EAAE,KAAK,OAAO,GAAG,KAAK,CAAC,UAAU,MAAM,OAAO,MAAM,MAAM,SAAS,OAAO;AAC9F,QAAM,UAAU,OAAO,SAAS,IAAI,IAAI,gCAAgC,EAAE;AAC1E,MAAI,OAAO,YAAY,SAAU,QAAO,KAAK,OAAO;AACpD,QAAM,EAAE,OAAO,MAAM,MAAM,IAAI,IAAI;AACnC,QAAM,OAAO,MAAM,aAAa,OAAO,OAAO,IAAI,KAAK,EAAE,KAAM;AAC/D,QAAM,SAAS,MAAM,SAAS,KAAK,MAAM,OAAO;AAChD,QAAM,WAAW,QAAQ,OAAO,IAAI,KAAK,IAAI;AAC7C,MAAI,CAAC,OAAO,SAAU,QAAO,KAAK,GAAG,KAAK,4BAA4B,QAAQ,EAAE;AAChF,QAAM,QAAkB,CAAC;AACzB,QAAM,MAAM,MAAM,IAAI;AAAA,IACpB,cAAc,MAAM,OAAO,EAAE,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC,GAAI,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC,EAAG,CAAC;AAAA,IACjF,EAAE,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,MAAM,KAAK,IAAI,EAAE;AAAA,IACvD;AAAA,EACF;AACA,QAAM,SAAS,MAAM,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,IAAI;AACpE,QAAM,QAAQ,MAAM,SAAS,KAAK,MAAM,OAAO;AAC/C,QAAM,QAAQ,MACV,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,OAAO,IAAI,IAAI,MAAM,EAAE,IACzD,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,MAAM,SAAS,KAAK;AAChD,MAAI,CAAC,IAAI,MAAM,IAAI,MAAM,aAAa,KAAK,CAAC,MAAM;AAChD,WAAO;AAAA,MACL,4BAA4B,IAAI,oBAAoB,IAAI,GAAG,SAAS;AAAA,EAAM,MAAM,KAAK,EAAE;AAAA,IACzF;AACF,QAAM,EAAE,KAAK,IAAI,WAAW,OAAO;AACnC,QAAM,QAAQ,MAAM,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI,EAAE,KAAK;AAC/C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK,MACD,aAAa,MAAM,KAAK,IAAI,CAAC,OAAO,IAAI,sBAAsB,MAAM,IAAI,CAAC,SAAS,UAAU,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,IAC9G,aAAa,KAAK,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,kBAAkB,KAAK;AAAA;AAAA,EACnE;AACF;AAiBA,eAAe,WAAW,GAAM,SAAgD;AAC9E,QAAM,OAAO,MAAM,EAAE,QAAQ,IAAI;AAAA,IAC/B,GAAG,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAI,IAAI,OAAO;AAAA,IACjD;AAAA,EACF;AACA,MAAI,CAAC,KAAK,GAAI,QAAO;AACrB,MAAI;AACF,WAAO,UAAU,KAAK,MAAM,KAAK,KAAK,CAAC;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,OAAe,OAAsB;AACvD,QAAM,KAAK,MAAM,YAAY,aAAa,IAAI,KAAK,MAAM,SAAS,EAAE,YAAY,CAAC,KAAK;AACtF,SAAO,GAAG,KAAK,KAAK,MAAM,KAAK,KAAK,MAAM,GAAG,GAAG,EAAE;AACpD;AAEA,eAAe,QAAQ,GAAM,MAAyB,SAAmC;AACvF,QAAM,EAAE,IAAI,IAAI,EAAE;AAClB,QAAM,OAAO,EAAE,QAAQ,WAAW,EAAE,QAAQ;AAC5C,QAAM,OAAO,MAAM,SAAS,GAAG,OAAO;AACtC,QAAM,OAAO,EAAE,QAAQ,WAAW;AAClC,QAAM,UAAmB,EAAE,QAAQ,UAAU,CAAC,KAAKA,UAAS,WAAW,MAAM,KAAKA,KAAI;AACtF,QAAM,UAAU,EAAE,KAAK,SAAS,KAAK,EAAE,IAAI,GAAG,SAAS,KAAK,SAAS,WAAW,EAAE;AAClF,QAAM,UAAU,MAAM,WAAW,GAAG,OAAO;AAC3C,QAAM,OAAO,eAAe,SAAS,OAAO;AAC5C,QAAM,UAAU,QAAQ,CAAC,CAAC,WAAW,WAAW,EAAE,SAAS,IAAI,IAAI,OAAO;AAC1E,QAAM,CAAC,QAAQ,KAAK,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC7C,MAAM,SAAS,GAAG,IAAI,gBAAgB,GAAG,EAAE,OAAO,iBAAiB,OAAO;AAAA,IAC1E,UACI,MAAM,SAAS,GAAG,IAAI,GAAG,OAAO,SAAS,GAAG,EAAE,OAAO,YAAY,OAAO,SAAS,OAAO,IACxF;AAAA,IACJ,MAAM,SAAS,GAAG,IAAI,kBAAkB,GAAG,EAAE,OAAO,mBAAmB,OAAO;AAAA,EAChF,CAAC;AACD,QAAM,QAAQ,MAAM,EAAE,KAAK,OAAO;AAClC,QAAM,SAAS,IAAI,IAAI,MAAM,OAAO,CAAC,SAAS,KAAK,eAAe,KAAK,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAChG,QAAM,OAAO,oBAAI,IAAI;AAAA,IACnB,IAAI,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM,OAAO,GAAG,KAAK;AAAA,IACvD,IAAI,EAAE,QAAQ,SAAS,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,MAAM,IAAI;AAAA,EAC3D,CAAC;AACD,QAAM,WAAW,CAAC,YAChB,2BAA2B,QAAQ,SAAS,QAAQ,KAAK,IAAI,IAAI,UAAU;AAC7E,QAAM,SAAsB,MAAM,YAAY,OAAO,IAAI,GAAG,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,EACrF,OAAO,CAAC,UAAU,CAAC,MAAM,SAAS,CAAC,KAAK,IAAI,MAAM,KAAK,CAAC,EACxD,IAAI,CAAC,UAAU;AACd,UAAMC,UAAS,MAAM,OAAO,OAAO;AACnC,WAAO;AAAA,MACL,IAAI,WAAW,MAAM,IAAI;AAAA,MACzB,MAAM;AAAA,MACN,OAAO,MAAM;AAAA,MACb,MAAM,MAAM,eAAe,MAAM;AAAA,MACjC,UAAU,SAASA,QAAO,OAAO;AAAA,MACjC,OAAOA,QAAO;AAAA,MACd,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,MAC5C,MAAM,MAAM;AAAA,MACZ,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,MACzC,GAAI,MAAM,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;AAAA,IACnC;AAAA,EACF,CAAC;AACH,QAAM,OAAoB,cAAc,MAAM,IAAI,EAAE,IAAI,CAAC,UAAU;AACjE,UAAMA,UAAS,MAAM,OAAO,OAAO;AACnC,WAAO;AAAA,MACL,IAAI,eAAe,MAAM,IAAI;AAAA,MAC7B,MAAM;AAAA,MACN,OAAO,MAAM;AAAA,MACb,MAAM,MAAM,eAAe,MAAM;AAAA,MACjC,UAAU,SAASA,QAAO,OAAO;AAAA,MACjC,OAAOA,QAAO;AAAA,MACd,QAAQ,MAAM;AAAA,MACd,GAAI,MAAM,OAAO,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AACD,QAAM,MAAM,KAAK,SAAS,OAAO;AACjC,QAAM,SAAS,CAAC,GAAG,QAAQ,GAAG,IAAI,EAC/B,OAAO,CAAC,SAAS,CAAC,OAAO,IAAI,KAAK,EAAE,MAAM,OAAO,KAAK,QAAQ,EAAE,EAChE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAC/D,QAAM,UAAU;AAAA,IACd,WAAW,UAAU,MAAM;AAAA,IAC3B,GAAI,MAAM,CAAC,WAAW,WAAW,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC;AAAA,IACrD,WAAW,aAAa,KAAK;AAAA,EAC/B;AACA,QAAM,MAAM,UACR,YAAY,IAAI,IAAI,OAAO,KAAK,CAAC,QAAQ,MAAM,QAAQ,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,KAAK,OAAO,MACrG,kBAAkB,IAAI,IAAI,OAAO;AACrC,MAAI,KAAK,SAAS,QAAQ;AACxB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,GAAG,KAAK,UAAU,EAAE,SAAS,SAAS,QAAQ,OAAO,GAAG,YAAY,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA;AAAA,IAC7F;AACF,QAAM,OAAO,OAAO;AAAA,IAClB,CAAC,SAAS,GAAG,KAAK,KAAK,IAAK,KAAK,EAAE,IAAK,KAAK,IAAI,IAAK,KAAK,KAAK,IAAK,KAAK,QAAQ;AAAA,EACpF;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK,GAAG,CAAC,GAAG,SAAS,KAAK,GAAI,KAAK,SAAS,OAAO,CAAC,0BAA0B,CAAE,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,EAC9F;AACF;AAEA,eAAe,KAAK,GAAM,MAAyB,SAAmC;AACpF,QAAM,QAAQ,MAAM,EAAE,KAAK,OAAO;AAClC,QAAM,QAAQ,KAAK,SAAS,OAAO,IAAI,QAAQ,KAAK,KAAK;AACzD,MAAI,KAAK,SAAS,QAAQ,EAAG,QAAO,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,EAAK;AAC1F,SAAO,EAAE,MAAM,GAAG,KAAK,MAAM,SAAS,GAAG,MAAM,IAAI,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA,IAAO,mBAAmB;AAC9F;AAEA,eAAe,OAAO,GAAM,SAAmC;AAC7D,QAAM,EAAE,KAAK,OAAO,IAAI,EAAE;AAC1B,QAAM,QAAQ,MAAM,UAAU,KAAK,SAAS,EAAE,KAAK;AACnD,QAAM,SAAS,SAAS,MAAM;AAC9B,QAAM,OAAO,MAAM,EAAE,MAAM,aAAa,EAAE,SAAS,OAAO;AAC1D,QAAM,aAAa,KAAK,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,EAAE,IAAI,UAAU,EAAE,KAAK,OAAO;AACtF,QAAM,SAAS,QACX,GAAG,KAAK,KAAK,EAAE,MAAM,UAAU,MAAM,OAAO,CAAC,SAAS,KAAK,OAAO,EAAE,MAAM,WAAW,MAAM,OAAO,CAAC,SAAS,KAAK,WAAW,EAAE,MAAM,eACpI;AACJ,QAAM,UAAU,MAAM,EAAE,MAAM,aAAa,EAAE,SAAS,OAAO;AAC7D,QAAM,QAAQ;AAAA,IACZ,UAAU,UAAU,uBAAuB;AAAA,IAC3C,aAAa,aAAa,MAAM;AAAA,IAChC,UAAU,MAAM;AAAA,IAChB,GAAI,QAAQ,KACR;AAAA,MACE,cAAc,EAAE,SAAS,4DAA4D,EAAE,OAAO;AAAA,IAChG,IACA,CAAC;AAAA,EACP;AACA,SAAO,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA,EAAK;AACjD;AAEA,eAAe,QAAQ,GAAM,IAAY,SAAmC;AAC1E,SAAQ,MAAM,EAAE,KAAK,IAAI,eAAe,OAAO,IAC3C,EAAE,MAAM,GAAG,KAAK,aAAa,EAAE;AAAA,EAAK,IACpC,KAAK,0BAA0B,EAAE,EAAE;AACzC;AAEA,MAAM,QACJ;AAAA,EACE,MAAM,CAAC,GAAG,OAAO,YAAY,KAAK,GAAG,OAAO;AAAA,EAC5C,KAAK,CAAC,GAAG,OAAO,YAAY,IAAI,GAAG,OAAO;AAAA,EAC1C,SAAS,CAAC,GAAG,MAAM,YAAY,EAAE,OAAO,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC;AAAA,EAC1E,SAAS,CAAC,GAAG,OAAO,YAAY,EAAE,OAAO,MAAM,QAAQ,GAAG,OAAO,CAAC;AAAA,EAClE,SAAS,CAAC,GAAG,MAAM,YAAY,QAAQ,GAAG,KAAK,CAAC,KAAK,IAAI,OAAO;AAAA,EAChE,SAAS,CAAC,GAAG,MAAM,YAAY,QAAQ,GAAG,MAAM,OAAO;AAAA,EACvD,MAAM,CAAC,GAAG,MAAM,YAAY,KAAK,GAAG,MAAM,OAAO;AAAA,EACjD,QAAQ,CAAC,GAAG,OAAO,YAAY,OAAO,GAAG,OAAO;AAAA,EAChD,SAAS,CAAC,GAAG,MAAM,YAAY,QAAQ,GAAG,KAAK,CAAC,KAAK,IAAI,OAAO;AAClE;AAEK,SAAS,gBAAgB,SAA4C;AAC1E,QAAM,EAAE,KAAK,KAAK,IAAI;AACtB,QAAM,MAAM,QAAQ,OAAO,KAAK;AAChC,QAAM,YAAY,QAAQ,SAAS,QAAQ,QAAQ,MAAM,IAAI;AAC7D,QAAM,OAAO,OAAO,YAAsB,MAAM,UAAU,KAAK,SAAS,SAAS,KAAM,CAAC;AACxF,MAAI,QAA0B,QAAQ,QAAQ;AAC9C,QAAM,SAAS,CAAI,SAAuC;AACxD,UAAM,OAAO,MAAM,MAAM,MAAM,MAAS,EAAE,KAAK,IAAI;AACnD,YAAQ;AACR,WAAO;AAAA,EACT;AACA,QAAM,IAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,GAAG,IAAI;AAAA,IAChB,WAAW,GAAG,IAAI;AAAA,IAClB,SAAS,GAAG,IAAI;AAAA,IAChB,OAAO,QAAQ,SAAS;AAAA,IACxB,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS,QAAQ,SAAS,GAAG,QAAQ,MAAM,wBAAwB;AAAA,IACnE;AAAA,IACA,MAAM,CAAC,IAAI,OAAO,YAChB,OAAO,YAAY;AACjB,YAAM,QAAQ,MAAM,KAAK,OAAO;AAChC,YAAM,OAAO,MAAM,KAAK,CAAC,UAAU,MAAM,OAAO,EAAE;AAClD,UAAI,CAAC,KAAM,QAAO;AAClB,WAAK,KAAK,MAAM,IAAI;AACpB,YAAM,WAAW,KAAK,OAAO,SAAS,SAAS;AAC/C,aAAO;AAAA,IACT,CAAC;AAAA,EACL;AACA,QAAM,SAAS,KAAK,GAAG,oBAAoB,KAAK,EAAE,MAAM,MAAc,MAAM;AAC5E,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAQ,MAAM,QAAQ,SAAS;AACnC,YAAM,OAAO,KAAK,CAAC,KAAK;AACxB,YAAM,UAAU,MAAM,IAAI;AAC1B,YAAM;AACN,UAAI,QAAS,QAAO,QAAQ,EAAE,GAAG,GAAG,OAAO,GAAG,MAAM,OAAO;AAC3D,aAAO,EAAE,MAAM,SAAS,UAAU,SAAS,WAAW,IAAI,GAAG,KAAK,MAAM;AAAA,IAC1E;AAAA,IACA,MAAM,UAAU,QAAQ,MAAM,SAAS;AACrC,YAAM,KAAK,OAAQ,MAAkC,MAAM,EAAE;AAC7D,UAAI,WAAW,qBAAqB;AAClC,cAAM,EAAE,KAAK,IAAI,eAAe,OAAO;AACvC,eAAO;AAAA,MACT;AACA,UAAI,WAAW,uBAAuB,WAAW;AAC/C,cAAM,EAAE,KAAK,IAAI,WAAW,OAAO;AACrC,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,gBAAgB;AAC9B,MAAI;AACJ,SAAO;AAAA,IACL,MAAM,UACJ,IACA,SACA,SACkB;AAClB,aAAO,OAAO,YAAY,YAAY,SAClC,QAAQ,UAAU,QAAQ,QAAQ,QAAQ,MAAM,OAAO,IACvD;AAAA,IACN;AAAA,IACA,QAAQ,MAAyB,QAAuB,SAAmC;AACzF,aAAO,UACH,QAAQ,QAAQ,MAAM,QAAQ,OAAO,IACrC,QAAQ,QAAQ,EAAE,MAAM,GAAG,KAAK,6BAA6B,CAAC;AAAA,IACpE;AAAA,IACA,OAAO,SAA4C;AACjD,gBAAU,gBAAgB,OAAO;AACjC,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport { ROOT_USER } from '../layout.ts';\nimport { installJobs, readTable, scheduleOf, sendCommand } from '../licks/crontab.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport type { Roles } from '../roles/roles.ts';\nimport type { Scoops } from '../scoops/service.ts';\nimport { skillFromText } from '../skills/skills.ts';\nimport {\n CATALOG_BASE,\n CATALOG_DIR,\n type Fetcher,\n merge,\n PROFILE,\n type Profile,\n parseSkills,\n parseUseCases,\n profileOf,\n type Sheet,\n score,\n sheet,\n slugifyCompany,\n} from './catalog.ts';\nimport {\n fold,\n open,\n readStore,\n STORE,\n type Suggestion,\n storeOf,\n validate,\n writeStore,\n} from './store.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport const ROLE = 'gelatiere';\nexport const SPRINKLE = 'suggestions';\nexport const PROCEDURE = 'packages/vfs-root/gelatiere/GELATIERE.md';\nexport const SCHEDULE = '0 3 * * *';\n\nexport interface GelatiereAttach {\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n scoops: Scoops;\n roles: (context: Context) => Promise<Roles>;\n assets?: (path: string) => Promise<string>;\n now?: () => number;\n fetch?: Fetcher;\n catalog?: string;\n skills?: () => readonly { name: string }[];\n files?: ExecutionEnv;\n profile?: string;\n shared?: string;\n as?: (caller: string | null, context: Context) => Promise<Caller | undefined>;\n cronUser?: (folder: string) => Promise<string>;\n}\n\nexport interface Caller {\n env: ExecutionEnv;\n home: string;\n}\n\nexport interface GelatiereRuntime {\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n intercept(action: string, data: unknown, context: Context): Promise<boolean>;\n readonly seeded: Promise<Seeded>;\n}\n\nexport const USAGE = `usage:\n gelatiere init start the gelatiere scoop and its nightly crontab line\n gelatiere run ask the gelatiere for a pass now\n gelatiere suggest <file> fold a JSON array of suggestions into the store\n gelatiere deliver lick each cone with the suggestions that are new for it\n gelatiere catalog [--refresh] [--all] [--json]\n rank the www.sliccy.com skill and use-case catalog for this user\n gelatiere install <id> install a suggested skill with upskill into ~/.pi/agent/skills\n gelatiere list [--all] [--json]\n gelatiere dismiss <id> mark a suggestion as not wanted\n gelatiere status\n\nThe store is ${STORE}; the suggestions sprinkle shows it.\n`;\n\nexport const PASS = 'Nightly pass: read ~/.pi/agent/GELATIERE.md and follow it.';\n\nfunction handleOf(agents: Agents): string | undefined {\n const found = Object.entries(agents.state().scoops).find(\n ([, record]) => record.role === ROLE && live(record)\n );\n return found?.[1].folder;\n}\n\nfunction row(item: Suggestion): string {\n const state = item.takenAt ? 'taken' : item.dismissedAt ? 'dismissed' : 'open';\n return `${item.id}\\t${item.kind}\\t${state}\\t${item.title}`;\n}\n\ntype G = {\n serial: <T>(work: () => Promise<T>) => Promise<T>;\n options: GelatiereAttach;\n now: () => number;\n procedure: string;\n pending: string;\n load: (context: Context) => Promise<Suggestion[]>;\n mark: (id: string, field: 'dismissedAt' | 'takenAt', context: Context) => Promise<boolean>;\n files: ExecutionEnv;\n caller: string | null;\n store: string;\n catalog: string;\n};\n\nasync function callerOf(g: G, context: Context): Promise<Caller> {\n return (await g.options.as?.(g.caller, context)) ?? { env: g.options.env, home: g.options.home };\n}\n\nconst fail = (out: string): Answer => ({ code: 1, out: `gelatiere: ${out}\\n` });\n\nasync function cronUserOf(g: G, handle: string): Promise<string> {\n return (await g.options.cronUser?.(handle)) ?? ROOT_USER;\n}\n\nasync function schedule(g: G, handle: string, context: Context): Promise<string | undefined> {\n const command = sendCommand(`scoop:${handle}`, PASS);\n const user = await cronUserOf(g, handle);\n const job = { name: ROLE, schedule: SCHEDULE, command };\n const problems = await installJobs(g.options.env, user, [job], context);\n return problems.length ? problems.map((problem) => problem.error).join('; ') : undefined;\n}\n\nexport const SHIPPED = [\n '53bc19daa486dec24116d8cf0558a498d8476dc4e18874f8a3e9ccb9defb15bd',\n '1c16654b93bc01179a71ec907919f45132b2e9f1a9ac331b213655c66387b4e5',\n '3bdd8ae18831c37996413cb0277df7141fcd0dff2d056c7b7053c34f0a3beaac',\n '23a063c8156fda9fb0ffdea20172a7f64c1576d1c009a28456c095e2866fc0e9',\n];\n\nexport type Seeded = 'written' | 'updated' | 'current' | 'kept' | 'none';\n\nasync function sha256(text: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));\n return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nasync function seed(g: G, context: Context, create: boolean): Promise<Seeded> {\n const { assets } = g.options;\n const env = g.files;\n const text = assets ? await assets(PROCEDURE).catch(() => undefined) : undefined;\n if (text === undefined) return 'none';\n const bundled = await sha256(text);\n const marker = `${g.procedure}.sha256`;\n const must = (result: { ok: boolean; error?: { message: string } }) => {\n if (!result.ok) throw new Error(result.error?.message ?? 'write failed');\n };\n const write = async (state: Seeded): Promise<Seeded> => {\n must(await env.writeFile(g.procedure, text, context));\n must(await env.writeFile(marker, `${bundled}\\n`, context));\n must(await env.remove(g.pending, { force: true }, context));\n return state;\n };\n const read = await env.readTextFile(g.procedure, context);\n if (!read.ok) return create ? write('written') : 'none';\n const mine = await sha256(read.value);\n if (mine === bundled) return write('current');\n const last = await env.readTextFile(marker, context);\n if ((last.ok && last.value.trim() === mine) || SHIPPED.includes(mine)) return write('updated');\n must(await env.writeFile(g.pending, text, context));\n return 'kept';\n}\n\nasync function init(g: G, context: Context): Promise<Answer> {\n const { options } = g;\n await seed(g, context, true);\n let handle = handleOf(options.agents);\n if (!handle) {\n const roles = await options.roles(context);\n const role = roles.roles.find((entry) => entry.name === ROLE);\n if (!role) return fail('the gelatiere role is missing');\n const spawned = await options.scoops.spawn(\n {\n cone: 'cone',\n kind: 'async',\n name: ROLE,\n role,\n prompts: [],\n target: null,\n fromAgent: false,\n request: `gelatiere-init-${g.now()}`,\n limits: roles.limits,\n },\n context\n );\n if (spawned.code !== 0) return { code: 1, out: `gelatiere: ${spawned.out}` };\n handle = spawned.out.trim();\n }\n const refused = await schedule(g, handle, context);\n if (refused) return fail(`couldn't install the crontab line of scoop ${handle}: ${refused}`);\n return {\n code: 0,\n out: `the gelatiere is scoop ${handle}; it runs at ${SCHEDULE} (crontab) and with gelatiere run, following ${g.procedure}\\n`,\n };\n}\n\nasync function run(g: G, context: Context): Promise<Answer> {\n const handle = handleOf(g.options.agents);\n if (!handle) return fail('there is no gelatiere yet; run gelatiere init');\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: 'sprinkle:gelatiere',\n title: 'Gelatiere',\n text: 'run',\n body: `Run a pass now: read ${g.procedure} and follow it.`,\n target: `scoop:${handle}`,\n },\n context\n );\n return { code: 0, out: `asked scoop ${handle} for a pass\\n` };\n}\n\nasync function suggest(g: G, path: string | undefined, context: Context): Promise<Answer> {\n if (!path) return { code: 2, out: USAGE };\n const read = await (await callerOf(g, context)).env.readTextFile(path, context);\n if (!read.ok) return fail(`can't read ${path}`);\n let parsed: unknown;\n try {\n parsed = JSON.parse(read.value);\n } catch (error) {\n return fail(`${path} is not JSON (${(error as Error).message})`);\n }\n const folded = fold(await g.load(context), parsed, g.now());\n await writeStore(g.options.env, folded.store, context, g.store);\n const rejected = folded.rejected.map((reason) => `rejected ${reason}\\n`).join('');\n return {\n code: 0,\n out: `added ${folded.added}, updated ${folded.updated}, rejected ${folded.rejected.length}\\n${rejected}`,\n };\n}\n\nasync function deliver(g: G, context: Context): Promise<Answer> {\n const store = await g.load(context);\n const fresh = open(store).filter((item) => !item.deliveredAt);\n const cones = Object.keys(g.options.agents.state().cones);\n const byCone = new Map<string, Suggestion[]>();\n for (const item of fresh) {\n const named = (item.cones ?? []).filter((cone) => cones.includes(cone));\n for (const cone of named.length ? named : [g.options.agents.activeCone()])\n byCone.set(cone, [...(byCone.get(cone) ?? []), item]);\n }\n for (const [cone, items] of byCone) {\n const body = {\n added: items.length,\n open: open(store).length,\n suggestions: items.map(({ id, kind, title, body: text }) => ({\n id,\n kind,\n title,\n body: text,\n })),\n path: g.store,\n };\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${SPRINKLE}`,\n title: 'Suggestions',\n text: 'gelatiere-suggestions',\n body: JSON.stringify(body, null, 2),\n target: `cone:${cone}`,\n },\n context\n );\n }\n for (const item of fresh) item.deliveredAt = g.now();\n if (fresh.length) await writeStore(g.options.env, store, context, g.store);\n return {\n code: 0,\n out: `delivered ${fresh.length} to ${[...byCone.keys()].join(', ') || 'nobody'}\\n`,\n };\n}\n\nexport const INSTALLER = 'https://raw.githubusercontent.com/ai-ecoverse/gh-upskill/main/install.sh';\n\nexport function upskillScript(\n repo: string,\n skill: string | undefined,\n where: { path?: string; all?: boolean } = {}\n): string {\n const from = where.path ? ` --path '${where.path}'` : '';\n const which = where.all ? ' --all' : ` --skill '${skill}'`;\n return [\n 'bin=\"${PNPM_HOME:-$HOME/.local/share/pnpm}/bin\"',\n `{ command -v upskill >/dev/null 2>&1 || curl -fsSL ${INSTALLER} | bash -s -- --bin-dir \"$bin\"; }`,\n `upskill '${repo}'${from}${which} --dest-path \"$HOME/.pi/agent/skills\"`,\n ].join(' && ');\n}\n\nasync function skillsIn(\n env: ExecutionEnv,\n home: string,\n context: Context\n): Promise<Map<string, string>> {\n const root = `${home}/.pi/agent/skills`;\n const found = new Map<string, string>();\n const listed = await env.listDir(root, context);\n for (const info of listed.ok ? listed.value : []) {\n const path = `${root}/${info.name}/SKILL.md`;\n const read = await env.readTextFile(path, context);\n const name = read.ok ? skillFromText(read.value, path, 'user').skills[0]?.name : undefined;\n if (name) found.set(name, path);\n }\n return found;\n}\n\nasync function install(g: G, id: string, context: Context): Promise<Answer> {\n const { env, home } = await callerOf(g, context);\n const item = (await g.load(context)).find((entry) => entry.id === id && entry.kind === 'skill');\n const checked = item ? validate(item) : `there is no skill suggestion ${id}`;\n if (typeof checked === 'string') return fail(checked);\n const { skill, repo, path, all } = checked as Suggestion & { repo: string };\n const what = all ? `the skills${path ? ` in ${path}` : ''}` : (skill as string);\n const before = await skillsIn(env, home, context);\n const existing = skill ? before.get(skill) : undefined;\n if (!all && existing) return fail(`${skill} is already installed in ${existing}`);\n const lines: string[] = [];\n const ran = await env.exec(\n upskillScript(repo, skill, { ...(path ? { path } : {}), ...(all ? { all } : {}) }),\n { cwd: home, onOutput: (text) => void lines.push(text) },\n context\n );\n const output = lines.join('').trim().split('\\n').slice(-8).join('\\n');\n const after = await skillsIn(env, home, context);\n const added = all\n ? [...after].filter(([name, at]) => before.get(name) !== at)\n : [...after].filter(([name]) => name === skill);\n if (!ran.ok || ran.value.exitCode !== 0 || !added.length)\n return fail(\n `upskill couldn't install ${what} from github.com/${repo}${output ? `:\\n${output}` : ''}`\n );\n await g.mark(id, 'takenAt', context);\n const names = added.map(([name]) => name).sort();\n return {\n code: 0,\n out: all\n ? `installed ${names.join(', ')} in ${home}/.pi/agent/skills; ${names.map((name) => `/skill:${name}`).join(', ')} once the skills reload\\n`\n : `installed ${skill} in ${added[0]?.[1]}; it is /skill:${skill} once the skills reload\\n`,\n };\n}\n\ninterface Candidate {\n id: string;\n kind: 'skill' | 'use-case';\n title: string;\n body: string;\n evidence: string;\n score: number;\n skill?: string;\n repo?: string;\n path?: string;\n all?: boolean;\n prompt?: string;\n skills?: string[];\n}\n\nasync function profileFor(g: G, context: Context): Promise<Profile | undefined> {\n const read = await g.options.env.readTextFile(\n `${g.options.profile ?? g.options.home}/${PROFILE}`,\n context\n );\n if (!read.ok) return undefined;\n try {\n return profileOf(JSON.parse(read.value));\n } catch {\n return undefined;\n }\n}\n\nfunction sourceLine(label: string, found: Sheet): string {\n const at = found.fetchedAt ? `, fetched ${new Date(found.fetchedAt).toISOString()}` : '';\n return `${label}: ${found.state} (${found.url}${at})`;\n}\n\nasync function catalog(g: G, argv: readonly string[], context: Context): Promise<Answer> {\n const { env } = g.options;\n const home = g.options.profile ?? g.options.home;\n const mine = await callerOf(g, context);\n const base = g.options.catalog ?? CATALOG_BASE;\n const fetcher: Fetcher = g.options.fetch ?? ((url, init) => globalThis.fetch(url, init));\n const options = { env, fetcher, now: g.now(), refresh: argv.includes('--refresh') };\n const profile = await profileFor(g, context);\n const slug = slugifyCompany(profile?.company);\n const company = slug && !['catalog', 'use-cases'].includes(slug) ? slug : null;\n const [global, own, cases] = await Promise.all([\n sheet(options, `${base}catalog.json`, `${g.catalog}/catalog.json`, context),\n company\n ? sheet(options, `${base}${company}.json`, `${g.catalog}/company-${company}.json`, context)\n : undefined,\n sheet(options, `${base}use-cases.json`, `${g.catalog}/use-cases.json`, context),\n ]);\n const store = await g.load(context);\n const closed = new Set(store.filter((item) => item.dismissedAt || item.takenAt).map((i) => i.id));\n const have = new Set([\n ...(await skillsIn(mine.env, mine.home, context)).keys(),\n ...(g.options.skills?.() ?? []).map((entry) => entry.name),\n ]);\n const evidence = (reasons: string[]) =>\n `www.sliccy.com catalog: ${reasons.length ? reasons.join(', ') : 'featured'}`;\n const skills: Candidate[] = merge(parseSkills(global.rows), parseSkills(own?.rows ?? []))\n .filter((entry) => !entry.skill || !have.has(entry.skill))\n .map((entry) => {\n const ranked = score(entry, profile);\n return {\n id: `catalog-${entry.name}`,\n kind: 'skill',\n title: entry.title,\n body: entry.description || entry.title,\n evidence: evidence(ranked.reasons),\n score: ranked.score,\n ...(entry.skill ? { skill: entry.skill } : {}),\n repo: entry.repo,\n ...(entry.path ? { path: entry.path } : {}),\n ...(entry.all ? { all: true } : {}),\n };\n });\n const uses: Candidate[] = parseUseCases(cases.rows).map((entry) => {\n const ranked = score(entry, profile);\n return {\n id: `catalog-use-${entry.name}`,\n kind: 'use-case',\n title: entry.title,\n body: entry.description || entry.title,\n evidence: evidence(ranked.reasons),\n score: ranked.score,\n prompt: entry.prompt,\n ...(entry.skills.length ? { skills: entry.skills } : {}),\n };\n });\n const all = argv.includes('--all');\n const ranked = [...skills, ...uses]\n .filter((item) => !closed.has(item.id) && (all || item.score > 0))\n .sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));\n const sources = [\n sourceLine('skills', global),\n ...(own ? [sourceLine(`company ${company}`, own)] : []),\n sourceLine('use cases', cases),\n ];\n const who = profile\n ? `profile: ${home}/${PROFILE} (${[profile.role, profile.purpose].filter(Boolean).join(', ') || 'empty'})`\n : `profile: none (${home}/${PROFILE}); ranked by boost only`;\n if (argv.includes('--json'))\n return {\n code: 0,\n out: `${JSON.stringify({ sources, profile: Boolean(profile), candidates: ranked }, null, 2)}\\n`,\n };\n const rows = ranked.map(\n (item) => `${item.score}\\t${item.id}\\t${item.kind}\\t${item.title}\\t${item.evidence}`\n );\n return {\n code: 0,\n out: `${[...sources, who, ...(rows.length ? rows : ['no catalog entries match'])].join('\\n')}\\n`,\n };\n}\n\nasync function list(g: G, argv: readonly string[], context: Context): Promise<Answer> {\n const store = await g.load(context);\n const shown = argv.includes('--all') ? store : open(store);\n if (argv.includes('--json')) return { code: 0, out: `${JSON.stringify(shown, null, 2)}\\n` };\n return { code: 0, out: shown.length ? `${shown.map(row).join('\\n')}\\n` : 'no suggestions\\n' };\n}\n\nasync function status(g: G, context: Context): Promise<Answer> {\n const { env, agents } = g.options;\n const store = await readStore(env, context, g.store);\n const handle = handleOf(agents);\n const table = handle ? await readTable(env, await cronUserOf(g, handle), context) : '';\n const scheduled = typeof table === 'string' ? scheduleOf(table, ROLE) : undefined;\n const counts = store\n ? `${open(store).length} open, ${store.filter((item) => item.takenAt).length} taken, ${store.filter((item) => item.dismissedAt).length} dismissed`\n : 'none yet';\n const pending = await g.files.readTextFile(g.pending, context);\n const lines = [\n `scoop: ${handle ?? 'none (gelatiere init)'}`,\n `schedule: ${scheduled ?? 'none'}`,\n `store: ${counts}`,\n ...(pending.ok\n ? [\n `procedure: ${g.procedure} has your edits, so it was kept; the new built-in one is ${g.pending}`,\n ]\n : []),\n ];\n return { code: 0, out: `${lines.join('\\n')}\\n` };\n}\n\nasync function dismiss(g: G, id: string, context: Context): Promise<Answer> {\n return (await g.mark(id, 'dismissedAt', context))\n ? { code: 0, out: `dismissed ${id}\\n` }\n : fail(`there is no suggestion ${id}`);\n}\n\nconst VERBS: Record<string, (g: G, argv: readonly string[], context: Context) => Promise<Answer>> =\n {\n init: (g, _argv, context) => init(g, context),\n run: (g, _argv, context) => run(g, context),\n suggest: (g, argv, context) => g.serial(() => suggest(g, argv[1], context)),\n deliver: (g, _argv, context) => g.serial(() => deliver(g, context)),\n install: (g, argv, context) => install(g, argv[1] ?? '', context),\n catalog: (g, argv, context) => catalog(g, argv, context),\n list: (g, argv, context) => list(g, argv, context),\n status: (g, _argv, context) => status(g, context),\n dismiss: (g, argv, context) => dismiss(g, argv[1] ?? '', context),\n };\n\nexport function attachGelatiere(options: GelatiereAttach): GelatiereRuntime {\n const { env, home } = options;\n const now = options.now ?? Date.now;\n const storeFile = options.shared ? storeOf(options.shared) : STORE;\n const load = async (context: Context) => (await readStore(env, context, storeFile)) ?? [];\n let chain: Promise<unknown> = Promise.resolve();\n const serial = <T>(work: () => Promise<T>): Promise<T> => {\n const next = chain.catch(() => undefined).then(work);\n chain = next;\n return next;\n };\n const g: G = {\n serial,\n options,\n now,\n procedure: `${home}/.pi/agent/GELATIERE.md`,\n pending: `${home}/.pi/agent/GELATIERE.new.md`,\n files: options.files ?? env,\n caller: null,\n store: storeFile,\n catalog: options.shared ? `${options.shared}/.gelatiere/catalog` : CATALOG_DIR,\n load,\n mark: (id, field, context) =>\n serial(async () => {\n const store = await load(context);\n const item = store.find((entry) => entry.id === id);\n if (!item) return false;\n item[field] ??= now();\n await writeStore(env, store, context, storeFile);\n return true;\n }),\n };\n const seeded = seed(g, BACKGROUND_CONTEXT, false).catch((): Seeded => 'none');\n return {\n seeded,\n async command(argv, caller, context) {\n const verb = argv[0] ?? 'help';\n const handler = VERBS[verb];\n await seeded;\n if (handler) return handler({ ...g, caller }, argv, context);\n return { code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE };\n },\n async intercept(action, data, context) {\n const id = String((data as { id?: unknown } | null)?.id ?? '');\n if (action === 'gelatiere-dismiss') {\n await g.mark(id, 'dismissedAt', context);\n return true;\n }\n if (action === 'gelatiere-install' || action === 'gelatiere-try')\n await g.mark(id, 'takenAt', context);\n return false;\n },\n };\n}\n\nexport function lateGelatiere() {\n let runtime: GelatiereRuntime | undefined;\n return {\n async intercept(\n id: string,\n payload: { action: string; data: unknown },\n context: Context\n ): Promise<boolean> {\n return id === SPRINKLE && runtime !== undefined\n ? runtime.intercept(payload.action, payload.data, context)\n : false;\n },\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer> {\n return runtime\n ? runtime.command(argv, caller, context)\n : Promise.resolve({ code: 1, out: 'gelatiere: not ready yet\\n' });\n },\n attach(options: GelatiereAttach): GelatiereRuntime {\n runtime = attachGelatiere(options);\n return runtime;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["init", "ranked"]
|
|
7
7
|
}
|
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
SHARED_DIR,
|
|
10
10
|
usersLayout
|
|
11
11
|
} from "../layout.js";
|
|
12
|
+
import { findScoop } from "../scoops/service.js";
|
|
12
13
|
import {
|
|
13
14
|
CONE_PREFIX,
|
|
14
15
|
createAccounts,
|
|
15
16
|
homeOf,
|
|
17
|
+
scoopUser,
|
|
16
18
|
wanted
|
|
17
19
|
} from "./accounts.js";
|
|
18
20
|
import { HOME, quote, SliccKernelEnv } from "./env.js";
|
|
@@ -120,6 +122,10 @@ function agentUsers(pool, client, root, agents) {
|
|
|
120
122
|
const env = caller ? await this.envOfAgent(caller) : void 0;
|
|
121
123
|
const home = caller ? accounts.home(caller) : void 0;
|
|
122
124
|
return env && home ? { env, home } : { env: root2, home: ROOT_HOME };
|
|
125
|
+
},
|
|
126
|
+
cronUser: async (folder) => {
|
|
127
|
+
await this.envOfAgent(`scoop:${folder}`);
|
|
128
|
+
return scoopUser(folder);
|
|
123
129
|
}
|
|
124
130
|
};
|
|
125
131
|
},
|
|
@@ -133,6 +139,11 @@ function agentUsers(pool, client, root, agents) {
|
|
|
133
139
|
}
|
|
134
140
|
return out;
|
|
135
141
|
},
|
|
142
|
+
cronUser(target, place) {
|
|
143
|
+
if (!target.startsWith("scoop:")) return void 0;
|
|
144
|
+
const found = findScoop(state(), target, place.cone);
|
|
145
|
+
return found ? scoopUser(found[1].folder) : void 0;
|
|
146
|
+
},
|
|
136
147
|
follow(runtimes) {
|
|
137
148
|
listeners.add(() => {
|
|
138
149
|
for (const runtime of runtimes)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/kernel/agent-users.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Agents, AgentsState } from '../agents.ts';\nimport { FIRST_CONE } from '../agents.ts';\nimport type { GelatiereAttach } from '../gelatiere/index.ts';\nimport {\n AGENT_DIR,\n type Layout,\n LEGACY,\n ROOT_HOME,\n ROOT_USER,\n SESSIONS,\n SHARED_DIR,\n usersLayout,\n} from '../layout.ts';\nimport type { LickDir } from '../licks/sources.ts';\nimport type { Spools } from '../scoops/index.ts';\nimport type { SkillPeople } from '../skills/index.ts';\nimport type { SprinklePlace, SprinklesAttach } from '../sprinkles/index.ts';\nimport {\n type Accounts,\n CONE_PREFIX,\n createAccounts,\n homeOf,\n type UsersMode,\n wanted,\n} from './accounts.ts';\nimport type { KernelClient } from './client.ts';\nimport { type EnvRoute, type EnvTargetLike, HOME, quote, SliccKernelEnv } from './env.ts';\nimport type { UserClients } from './users.ts';\n\nexport interface AgentUsers {\n readonly accounts: Accounts;\n readonly layout: Layout;\n readonly spools: Spools;\n readonly enforced: boolean;\n readonly people: SkillPeople;\n follow(\n runtimes: readonly {\n reload?(context: Context): Promise<void>;\n reconcile?(context: Context): Promise<void>;\n }[]\n ): void;\n lickDirs(): Promise<LickDir[]>;\n sprinklePlaces(): SprinklePlace[];\n sprinkles(): Partial<SprinklesAttach>;\n envOfAgent(agentId: string): Promise<SliccKernelEnv | undefined>;\n gelatiere(root: SliccKernelEnv): Promise<Partial<GelatiereAttach>>;\n route(target: EnvTargetLike): Promise<EnvRoute | undefined>;\n start(agents: Agents): Promise<void>;\n close(): void;\n}\n\nexport function modeOf(client: KernelClient): UsersMode {\n const mode = (client as { users?: unknown }).users;\n return mode === 'report' || mode === 'enforce' ? mode : 'off';\n}\n\nexport async function missingSupport(\n pool: UserClients | undefined,\n client: KernelClient,\n requested: boolean | undefined\n): Promise<string[]> {\n if (!pool) return requested === true ? ['admin (the page passed no KernelSource.users)'] : [];\n const missing: string[] = [];\n if (\n !(await pool.users.list().then(\n () => true,\n () => false\n ))\n )\n missing.push('users');\n if ((client as { users?: unknown }).users === undefined)\n missing.push('the users mode (kernel 1.57)');\n if (\n !(await pool.groups.list().then(\n () => true,\n () => false\n ))\n )\n missing.push('groups and homeMode (kernel 1.60 and KernelSource.groups)');\n return missing;\n}\n\nexport async function warnSupport(\n pool: UserClients | undefined,\n client: KernelClient,\n requested: boolean | undefined,\n report: (message: string) => void = (message) => console.error(message)\n): Promise<void> {\n const missing = await missingSupport(pool, client, requested);\n if (missing.length)\n report(\n `slicc-agent: kernel users were requested, but this kernel or page lacks ${missing.join(', ')}`\n );\n}\n\nexport function legacyCwd(state: Readonly<AgentsState>, agentId: string, cwd: string): boolean {\n if (cwd === HOME) return true;\n const record = state.scoops[agentId];\n return record !== undefined && cwd === LEGACY.workspace(record.folder);\n}\n\nexport function spoolScript(user: string, dir: string): string {\n const at = quote(dir);\n const made = `mkdir -p ${at}/in ${at}/out`;\n if (user === ROOT_USER) return made;\n return `${made} && chown -R ${quote(`${user}:${user}`)} ${at} && chmod 700 ${at} ${at}/in ${at}/out`;\n}\n\nexport function agentUsers(\n pool: UserClients,\n client: KernelClient,\n root: SliccKernelEnv,\n agents: () => Agents | undefined\n): AgentUsers {\n const mode = modeOf(client);\n const accounts = createAccounts(pool, agents, mode, pool.groups);\n const state = () => agents()?.state() ?? { cones: {}, active: 'cone', next: 2, scoops: {} };\n const envOf = async (agentId: string, home: string) =>\n new SliccKernelEnv(await accounts.client(agentId), { cwd: home });\n const layout = usersLayout(state, envOf, root);\n let ready: () => void = () => undefined;\n const started = new Promise<void>((resolve) => {\n ready = resolve;\n });\n let off: () => void = () => undefined;\n const homes = () => wanted(state()).map((entry) => homeOf(entry.spec.name));\n const listeners = new Set<() => void>();\n let known = '';\n const changed = () => {\n const key = homes().join('\\n');\n if (key === known) return;\n known = key;\n for (const listener of listeners) listener();\n };\n return {\n accounts,\n layout,\n enforced: mode === 'enforce',\n people: {\n homes,\n homeOfAgent(agentId) {\n return accounts.home(agentId) ?? accounts.home(`${CONE_PREFIX}${agentId}`);\n },\n homeOf(conversationId) {\n const agentId = accounts.agentOf(conversationId);\n return agentId ? accounts.home(agentId) : undefined;\n },\n async envOf(at) {\n const user = wanted(state()).find((entry) => homeOf(entry.spec.name) === at);\n return user ? envOf(user.agentId, at).catch(() => undefined) : undefined;\n },\n },\n sprinklePlaces() {\n return wanted(state())\n .filter((entry) => !entry.cone)\n .map((entry) => ({ dir: `${homeOf(entry.spec.name)}/sprinkles`, cone: entry.spec.name }));\n },\n sprinkles() {\n return {\n places: () => this.sprinklePlaces(),\n envOf: (agentId: string) => this.envOfAgent(agentId),\n homeOf: (agentId: string) => accounts.home(agentId),\n shared: SHARED_DIR,\n };\n },\n async envOfAgent(agentId) {\n const home = accounts.home(agentId);\n return home ? envOf(agentId, home).catch(() => undefined) : undefined;\n },\n async gelatiere(root) {\n const first = `${CONE_PREFIX}${FIRST_CONE}`;\n const files = await this.envOfAgent(first);\n return {\n home: homeOf(FIRST_CONE),\n profile: SHARED_DIR,\n shared: SHARED_DIR,\n ...(files ? { files } : {}),\n as: async (caller) => {\n const env = caller ? await this.envOfAgent(caller) : undefined;\n const home = caller ? accounts.home(caller) : undefined;\n return env && home ? { env, home } : { env: root, home: ROOT_HOME };\n },\n };\n },\n async lickDirs() {\n const cones = wanted(state()).filter((entry) => !entry.cone);\n const out: LickDir[] = [{ dir: `${ROOT_HOME}/.slicc`, home: ROOT_HOME, cone: null }];\n for (const entry of cones) {\n const home = homeOf(entry.spec.name);\n const env = await envOf(entry.agentId, home).catch(() => undefined);\n out.push({ dir: `${home}/.slicc`, home, cone: entry.spec.name, ...(env ? { env } : {}) });\n }\n return out;\n },\n follow(runtimes) {\n listeners.add(() => {\n for (const runtime of runtimes)\n void (runtime.reconcile ?? runtime.reload)\n ?.call(runtime, BACKGROUND_CONTEXT)\n .catch(() => undefined);\n });\n },\n spools: {\n users: () => [ROOT_USER, ...wanted(state()).map((entry) => entry.spec.name)],\n async prepare(user, dir, context) {\n await root.exec(spoolScript(user, dir), undefined, context);\n },\n },\n async route(target) {\n await started;\n const agentId = accounts.agentOf(target.conversationId);\n const home = agentId ? accounts.home(agentId) : undefined;\n if (!agentId || !home) return undefined;\n const legacy = target.cwd !== undefined && legacyCwd(state(), agentId, target.cwd);\n return { client: await accounts.client(agentId), home, ...(legacy ? { cwd: home } : {}) };\n },\n async start(opened) {\n await root.exec(\n `mkdir -p ${SESSIONS.tmp} ${AGENT_DIR} ${SHARED_DIR} && chmod 1777 ${SESSIONS.tmp} && chmod 755 ${AGENT_DIR} && chmod 700 ${SHARED_DIR}`,\n undefined,\n BACKGROUND_CONTEXT\n );\n await accounts.boot();\n known = homes().join('\\n');\n off = opened.onChange(() => {\n void accounts.reconcile();\n changed();\n });\n ready();\n },\n close() {\n off();\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,0BAA0B;AAEnC,SAAS,kBAAkB;AAE3B;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Agents, AgentsState } from '../agents.ts';\nimport { FIRST_CONE } from '../agents.ts';\nimport type { GelatiereAttach } from '../gelatiere/index.ts';\nimport {\n AGENT_DIR,\n type Layout,\n LEGACY,\n ROOT_HOME,\n ROOT_USER,\n SESSIONS,\n SHARED_DIR,\n usersLayout,\n} from '../layout.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport type { LickDir } from '../licks/sources.ts';\nimport type { Spools } from '../scoops/index.ts';\nimport { findScoop } from '../scoops/service.ts';\nimport type { SkillPeople } from '../skills/index.ts';\nimport type { SprinklePlace, SprinklesAttach } from '../sprinkles/index.ts';\nimport {\n type Accounts,\n CONE_PREFIX,\n createAccounts,\n homeOf,\n scoopUser,\n type UsersMode,\n wanted,\n} from './accounts.ts';\nimport type { KernelClient } from './client.ts';\nimport { type EnvRoute, type EnvTargetLike, HOME, quote, SliccKernelEnv } from './env.ts';\nimport type { UserClients } from './users.ts';\n\nexport interface AgentUsers {\n readonly accounts: Accounts;\n readonly layout: Layout;\n readonly spools: Spools;\n readonly enforced: boolean;\n readonly people: SkillPeople;\n follow(\n runtimes: readonly {\n reload?(context: Context): Promise<void>;\n reconcile?(context: Context): Promise<void>;\n }[]\n ): void;\n lickDirs(): Promise<LickDir[]>;\n cronUser(target: LickTarget, place: LickDir): string | undefined;\n sprinklePlaces(): SprinklePlace[];\n sprinkles(): Partial<SprinklesAttach>;\n envOfAgent(agentId: string): Promise<SliccKernelEnv | undefined>;\n gelatiere(root: SliccKernelEnv): Promise<Partial<GelatiereAttach>>;\n route(target: EnvTargetLike): Promise<EnvRoute | undefined>;\n start(agents: Agents): Promise<void>;\n close(): void;\n}\n\nexport function modeOf(client: KernelClient): UsersMode {\n const mode = (client as { users?: unknown }).users;\n return mode === 'report' || mode === 'enforce' ? mode : 'off';\n}\n\nexport async function missingSupport(\n pool: UserClients | undefined,\n client: KernelClient,\n requested: boolean | undefined\n): Promise<string[]> {\n if (!pool) return requested === true ? ['admin (the page passed no KernelSource.users)'] : [];\n const missing: string[] = [];\n if (\n !(await pool.users.list().then(\n () => true,\n () => false\n ))\n )\n missing.push('users');\n if ((client as { users?: unknown }).users === undefined)\n missing.push('the users mode (kernel 1.57)');\n if (\n !(await pool.groups.list().then(\n () => true,\n () => false\n ))\n )\n missing.push('groups and homeMode (kernel 1.60 and KernelSource.groups)');\n return missing;\n}\n\nexport async function warnSupport(\n pool: UserClients | undefined,\n client: KernelClient,\n requested: boolean | undefined,\n report: (message: string) => void = (message) => console.error(message)\n): Promise<void> {\n const missing = await missingSupport(pool, client, requested);\n if (missing.length)\n report(\n `slicc-agent: kernel users were requested, but this kernel or page lacks ${missing.join(', ')}`\n );\n}\n\nexport function legacyCwd(state: Readonly<AgentsState>, agentId: string, cwd: string): boolean {\n if (cwd === HOME) return true;\n const record = state.scoops[agentId];\n return record !== undefined && cwd === LEGACY.workspace(record.folder);\n}\n\nexport function spoolScript(user: string, dir: string): string {\n const at = quote(dir);\n const made = `mkdir -p ${at}/in ${at}/out`;\n if (user === ROOT_USER) return made;\n return `${made} && chown -R ${quote(`${user}:${user}`)} ${at} && chmod 700 ${at} ${at}/in ${at}/out`;\n}\n\nexport function agentUsers(\n pool: UserClients,\n client: KernelClient,\n root: SliccKernelEnv,\n agents: () => Agents | undefined\n): AgentUsers {\n const mode = modeOf(client);\n const accounts = createAccounts(pool, agents, mode, pool.groups);\n const state = () => agents()?.state() ?? { cones: {}, active: 'cone', next: 2, scoops: {} };\n const envOf = async (agentId: string, home: string) =>\n new SliccKernelEnv(await accounts.client(agentId), { cwd: home });\n const layout = usersLayout(state, envOf, root);\n let ready: () => void = () => undefined;\n const started = new Promise<void>((resolve) => {\n ready = resolve;\n });\n let off: () => void = () => undefined;\n const homes = () => wanted(state()).map((entry) => homeOf(entry.spec.name));\n const listeners = new Set<() => void>();\n let known = '';\n const changed = () => {\n const key = homes().join('\\n');\n if (key === known) return;\n known = key;\n for (const listener of listeners) listener();\n };\n return {\n accounts,\n layout,\n enforced: mode === 'enforce',\n people: {\n homes,\n homeOfAgent(agentId) {\n return accounts.home(agentId) ?? accounts.home(`${CONE_PREFIX}${agentId}`);\n },\n homeOf(conversationId) {\n const agentId = accounts.agentOf(conversationId);\n return agentId ? accounts.home(agentId) : undefined;\n },\n async envOf(at) {\n const user = wanted(state()).find((entry) => homeOf(entry.spec.name) === at);\n return user ? envOf(user.agentId, at).catch(() => undefined) : undefined;\n },\n },\n sprinklePlaces() {\n return wanted(state())\n .filter((entry) => !entry.cone)\n .map((entry) => ({ dir: `${homeOf(entry.spec.name)}/sprinkles`, cone: entry.spec.name }));\n },\n sprinkles() {\n return {\n places: () => this.sprinklePlaces(),\n envOf: (agentId: string) => this.envOfAgent(agentId),\n homeOf: (agentId: string) => accounts.home(agentId),\n shared: SHARED_DIR,\n };\n },\n async envOfAgent(agentId) {\n const home = accounts.home(agentId);\n return home ? envOf(agentId, home).catch(() => undefined) : undefined;\n },\n async gelatiere(root) {\n const first = `${CONE_PREFIX}${FIRST_CONE}`;\n const files = await this.envOfAgent(first);\n return {\n home: homeOf(FIRST_CONE),\n profile: SHARED_DIR,\n shared: SHARED_DIR,\n ...(files ? { files } : {}),\n as: async (caller) => {\n const env = caller ? await this.envOfAgent(caller) : undefined;\n const home = caller ? accounts.home(caller) : undefined;\n return env && home ? { env, home } : { env: root, home: ROOT_HOME };\n },\n cronUser: async (folder) => {\n await this.envOfAgent(`scoop:${folder}`);\n return scoopUser(folder);\n },\n };\n },\n async lickDirs() {\n const cones = wanted(state()).filter((entry) => !entry.cone);\n const out: LickDir[] = [{ dir: `${ROOT_HOME}/.slicc`, home: ROOT_HOME, cone: null }];\n for (const entry of cones) {\n const home = homeOf(entry.spec.name);\n const env = await envOf(entry.agentId, home).catch(() => undefined);\n out.push({ dir: `${home}/.slicc`, home, cone: entry.spec.name, ...(env ? { env } : {}) });\n }\n return out;\n },\n cronUser(target, place) {\n if (!target.startsWith('scoop:')) return undefined;\n const found = findScoop(state(), target, place.cone);\n return found ? scoopUser(found[1].folder) : undefined;\n },\n follow(runtimes) {\n listeners.add(() => {\n for (const runtime of runtimes)\n void (runtime.reconcile ?? runtime.reload)\n ?.call(runtime, BACKGROUND_CONTEXT)\n .catch(() => undefined);\n });\n },\n spools: {\n users: () => [ROOT_USER, ...wanted(state()).map((entry) => entry.spec.name)],\n async prepare(user, dir, context) {\n await root.exec(spoolScript(user, dir), undefined, context);\n },\n },\n async route(target) {\n await started;\n const agentId = accounts.agentOf(target.conversationId);\n const home = agentId ? accounts.home(agentId) : undefined;\n if (!agentId || !home) return undefined;\n const legacy = target.cwd !== undefined && legacyCwd(state(), agentId, target.cwd);\n return { client: await accounts.client(agentId), home, ...(legacy ? { cwd: home } : {}) };\n },\n async start(opened) {\n await root.exec(\n `mkdir -p ${SESSIONS.tmp} ${AGENT_DIR} ${SHARED_DIR} && chmod 1777 ${SESSIONS.tmp} && chmod 755 ${AGENT_DIR} && chmod 700 ${SHARED_DIR}`,\n undefined,\n BACKGROUND_CONTEXT\n );\n await accounts.boot();\n known = homes().join('\\n');\n off = opened.onChange(() => {\n void accounts.reconcile();\n changed();\n });\n ready();\n },\n close() {\n off();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["root"]
|
|
7
7
|
}
|
package/dist/licks/boot.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
defineDoc
|
|
3
|
+
} from "@earendil-works/pi-durable";
|
|
4
|
+
const LicksHostDoc = defineDoc({
|
|
5
|
+
kind: "slicc.licks.host",
|
|
6
|
+
version: 1,
|
|
7
|
+
scope: "session",
|
|
8
|
+
initial: () => ({ cron: {}, agent: null, boot: null, mark: 0, upgrades: 0 })
|
|
9
|
+
});
|
|
2
10
|
const BACKGROUND = /(?:&\s*$|&\s*disown\b|\bnohup\b|\bsetsid\b)/m;
|
|
3
11
|
function bashCommands(entries, after) {
|
|
4
12
|
const commands = [];
|
|
@@ -60,6 +68,7 @@ async function bootLicks(harness, cone, deliver, facts, context) {
|
|
|
60
68
|
}, context);
|
|
61
69
|
}
|
|
62
70
|
export {
|
|
71
|
+
LicksHostDoc,
|
|
63
72
|
bashCommands,
|
|
64
73
|
bootLicks
|
|
65
74
|
};
|
package/dist/licks/boot.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/licks/boot.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type EntryRecord,\n type Harness,\n} from '@earendil-works/pi-durable';\nimport type { Deliver } from './fswatch.ts';\n\ntype HostState = {\n cron: Record<string, { task: number }>;\n agent: string | null;\n boot: string | null;\n mark: number;\n upgrades: number;\n};\n\nexport const LicksHostDoc = defineDoc<HostState>({\n kind: 'slicc.licks.host',\n version: 1,\n scope: 'session',\n initial: () => ({ cron: {}, agent: null, boot: null, mark: 0, upgrades: 0 }),\n});\n\nexport interface BootFacts {\n version: string;\n boot: string | null;\n}\n\nconst BACKGROUND = /(?:&\\s*$|&\\s*disown\\b|\\bnohup\\b|\\bsetsid\\b)/m;\n\ninterface Block {\n type?: string;\n name?: string;\n arguments?: { command?: unknown };\n}\n\nexport function bashCommands(entries: readonly EntryRecord[], after: number): string[] {\n const commands: string[] = [];\n for (const entry of entries) {\n if (entry.kind !== 'pi.assistant' || Number(entry.id) <= after) continue;\n const content = (entry.model?.[0] as { content?: unknown } | undefined)?.content;\n for (const block of Array.isArray(content) ? (content as Block[]) : []) {\n const command = block.arguments?.command;\n if (block.type === 'toolCall' && block.name === 'bash' && typeof command === 'string')\n commands.push(command);\n }\n }\n return commands;\n}\n\nfunction upgraded(from: string, to: string, upgrades: number) {\n return {\n channel: 'upgrade',\n source: 'slicc-agent',\n title: `slicc-agent ${from} \u2192 ${to}`,\n text: `The agent was updated from ${from} to ${to}.`,\n body: `Release notes: https://github.com/ai-ecoverse/slicc-agent/releases/tag/v${to}`,\n target: 'cone',\n eventId: `${from}->${to}#${upgrades}`,\n } as const;\n}\n\nfunction restarted(boot: string, commands: readonly string[]) {\n const background = commands.filter((command) => BACKGROUND.test(command)).slice(-5);\n return {\n channel: 'session-reload',\n source: 'kernel',\n title: 'The kernel restarted',\n text: 'The page reloaded, so the kernel started fresh: processes from before the reload are gone.',\n body: background.length\n ? `Background commands that are no longer running:\\n${background.join('\\n')}`\n : 'Files are unchanged. Start again whatever you still need running.',\n target: 'cone',\n eventId: `boot:${boot}`,\n } as const;\n}\n\nexport async function bootLicks(\n harness: Harness,\n cone: Conversation,\n deliver: Deliver,\n facts: BootFacts,\n context: Context\n): Promise<void> {\n const state = await harness.snapshot(LicksHostDoc, context);\n const previous = state?.agent ?? null;\n if (previous !== null && previous !== facts.version)\n await deliver(upgraded(previous, facts.version, state?.upgrades ?? 0), context);\n const { entries } = await cone.context(context);\n if (facts.boot !== null && state?.boot && facts.boot !== state.boot) {\n const commands = bashCommands(entries, state.mark);\n if (commands.length) await deliver(restarted(facts.boot, commands), context);\n }\n const { entries: after } = await cone.context(context);\n const mark = Number(after.at(-1)?.id ?? 0);\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksHostDoc);\n if (doc.agent !== null && doc.agent !== facts.version) doc.upgrades += 1;\n doc.agent = facts.version;\n if (facts.boot !== null && doc.boot !== facts.boot) {\n doc.boot = facts.boot;\n doc.mark = mark;\n }\n }, context);\n}\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EAEE;AAAA,OAGK;AAWA,MAAM,eAAe,UAAqB;AAAA,EAC/C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,MAAM,MAAM,MAAM,MAAM,GAAG,UAAU,EAAE;AAC5E,CAAC;AAOD,MAAM,aAAa;AAQZ,SAAS,aAAa,SAAiC,OAAyB;AACrF,QAAM,WAAqB,CAAC;AAC5B,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,SAAS,kBAAkB,OAAO,MAAM,EAAE,KAAK,MAAO;AAChE,UAAM,UAAW,MAAM,QAAQ,CAAC,GAAyC;AACzE,eAAW,SAAS,MAAM,QAAQ,OAAO,IAAK,UAAsB,CAAC,GAAG;AACtE,YAAM,UAAU,MAAM,WAAW;AACjC,UAAI,MAAM,SAAS,cAAc,MAAM,SAAS,UAAU,OAAO,YAAY;AAC3E,iBAAS,KAAK,OAAO;AAAA,IACzB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,MAAc,IAAY,UAAkB;AAC5D,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO,eAAe,IAAI,WAAM,EAAE;AAAA,IAClC,MAAM,8BAA8B,IAAI,OAAO,EAAE;AAAA,IACjD,MAAM,2EAA2E,EAAE;AAAA,IACnF,QAAQ;AAAA,IACR,SAAS,GAAG,IAAI,KAAK,EAAE,IAAI,QAAQ;AAAA,EACrC;AACF;AAEA,SAAS,UAAU,MAAc,UAA6B;AAC5D,QAAM,aAAa,SAAS,OAAO,CAAC,YAAY,WAAW,KAAK,OAAO,CAAC,EAAE,MAAM,EAAE;AAClF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM,WAAW,SACb;AAAA,EAAoD,WAAW,KAAK,IAAI,CAAC,KACzE;AAAA,IACJ,QAAQ;AAAA,IACR,SAAS,QAAQ,IAAI;AAAA,EACvB;AACF;AAEA,eAAsB,UACpB,SACA,MACA,SACA,OACA,SACe;AACf,QAAM,QAAQ,MAAM,QAAQ,SAAS,cAAc,OAAO;AAC1D,QAAM,WAAW,OAAO,SAAS;AACjC,MAAI,aAAa,QAAQ,aAAa,MAAM;AAC1C,UAAM,QAAQ,SAAS,UAAU,MAAM,SAAS,OAAO,YAAY,CAAC,GAAG,OAAO;AAChF,QAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,QAAQ,OAAO;AAC9C,MAAI,MAAM,SAAS,QAAQ,OAAO,QAAQ,MAAM,SAAS,MAAM,MAAM;AACnE,UAAM,WAAW,aAAa,SAAS,MAAM,IAAI;AACjD,QAAI,SAAS,OAAQ,OAAM,QAAQ,UAAU,MAAM,MAAM,QAAQ,GAAG,OAAO;AAAA,EAC7E;AACA,QAAM,EAAE,SAAS,MAAM,IAAI,MAAM,KAAK,QAAQ,OAAO;AACrD,QAAM,OAAO,OAAO,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC;AACzC,QAAM,QAAQ,OAAO,OAAO,OAAO;AACjC,UAAM,MAAM,MAAM,GAAG,IAAI,YAAY;AACrC,QAAI,IAAI,UAAU,QAAQ,IAAI,UAAU,MAAM,QAAS,KAAI,YAAY;AACvE,QAAI,QAAQ,MAAM;AAClB,QAAI,MAAM,SAAS,QAAQ,IAAI,SAAS,MAAM,MAAM;AAClD,UAAI,OAAO,MAAM;AACjB,UAAI,OAAO;AAAA,IACb;AAAA,EACF,GAAG,OAAO;AACZ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/licks/config.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { parseSchedule } from "./cron.js";
|
|
2
1
|
const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
3
2
|
const TARGET = /^(?:cone|scoop:[a-z0-9][a-z0-9._-]{0,63})$/;
|
|
4
3
|
const DEBOUNCE = { min: 100, max: 6e4, default: 500 };
|
|
@@ -9,41 +8,6 @@ function isName(value) {
|
|
|
9
8
|
function isTarget(value) {
|
|
10
9
|
return TARGET.test(value);
|
|
11
10
|
}
|
|
12
|
-
function scheduleOf(tokens) {
|
|
13
|
-
if (tokens[0]?.startsWith("@")) return [tokens[0], tokens.slice(1)];
|
|
14
|
-
return [tokens.slice(0, 5).join(" "), tokens.slice(5)];
|
|
15
|
-
}
|
|
16
|
-
function cronLine(text2) {
|
|
17
|
-
const [schedule, rest] = scheduleOf(text2.trim().split(/\s+/));
|
|
18
|
-
parseSchedule(schedule);
|
|
19
|
-
const [name, maybeTarget, ...words] = rest;
|
|
20
|
-
if (name === void 0) throw new Error("a name must follow the schedule");
|
|
21
|
-
if (!isName(name))
|
|
22
|
-
throw new Error(
|
|
23
|
-
`name "${name}" must be 1\u201364 of a-z, 0-9, ".", "_" or "-", starting with a letter or digit`
|
|
24
|
-
);
|
|
25
|
-
const target2 = maybeTarget !== void 0 && isTarget(maybeTarget) ? maybeTarget : "cone";
|
|
26
|
-
const message = (target2 === maybeTarget ? words : [maybeTarget, ...words]).filter(Boolean).join(" ");
|
|
27
|
-
return { name, schedule, target: target2, message };
|
|
28
|
-
}
|
|
29
|
-
function parseCrontab(file, text2) {
|
|
30
|
-
const entries = [];
|
|
31
|
-
const errors = [];
|
|
32
|
-
const seen = /* @__PURE__ */ new Set();
|
|
33
|
-
text2.split("\n").forEach((raw, index) => {
|
|
34
|
-
const line = raw.trim();
|
|
35
|
-
if (line === "" || line.startsWith("#")) return;
|
|
36
|
-
try {
|
|
37
|
-
const entry = cronLine(line);
|
|
38
|
-
if (seen.has(entry.name)) throw new Error(`name "${entry.name}" is used twice`);
|
|
39
|
-
seen.add(entry.name);
|
|
40
|
-
entries.push(entry);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
errors.push({ file, line: index + 1, error: error.message });
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
return { entries, errors };
|
|
46
|
-
}
|
|
47
11
|
function object(text2, keys) {
|
|
48
12
|
let value;
|
|
49
13
|
try {
|
|
@@ -135,7 +99,6 @@ export {
|
|
|
135
99
|
isName,
|
|
136
100
|
isTarget,
|
|
137
101
|
nameOf,
|
|
138
|
-
parseCrontab,
|
|
139
102
|
parseWatch,
|
|
140
103
|
parseWebhook
|
|
141
104
|
};
|