@turri-dev/cli 0.1.8-next.b03162b → 0.1.8-next.bfcb24e

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/dist/extension.js CHANGED
@@ -46,6 +46,7 @@ import { defaultBuildDir } from "./regenerate/paths.js";
46
46
  import { describeTurnIncident, describeUsage, onModelEvent } from "./regenerate/activity.js";
47
47
  import { ledgerWanted, startLedger } from "./regenerate/ledger.js";
48
48
  import { describePastPulse, describeRuns, earlierPulses, ledgerProjects } from "./regenerate/ledger-view.js";
49
+ import { ledgerTool } from "./ledger-tools.js";
49
50
  import { describeSpecConflict } from "./regenerate/stop-report.js";
50
51
  import { inheritProviders, inheritThinkingLevel } from "./pi.js";
51
52
  import { beginCapture, captureConsole, describeCaptured, dropNoise } from "./quiet.js";
@@ -1489,9 +1490,13 @@ function registerJobTools(pi, jobs, cwd) {
1489
1490
  return { content: [{ type: "text", text: `${pastPulseNote}\n\n${past}` }], details: { status: "earlier" } };
1490
1491
  return { content: [{ type: "text", text: `No pulse ${params.job_id} has run in this session, and the ledger has none by that id.` }], details: { status: "unknown" } };
1491
1492
  }
1492
- return { content: [{ type: "text", text: whyFailed(job) }], details: { status: job.status } };
1493
+ // What the ledger recorded of it besides: its rounds, test runs, healers and commits, as facts.
1494
+ const facts = describePastPulse(ledgerProjects(cwd), job.id);
1495
+ const text = facts !== undefined ? `${whyFailed(job)}\n\nWhat the ledger recorded of it:\n${facts}` : whyFailed(job);
1496
+ return { content: [{ type: "text", text }], details: { status: job.status } };
1493
1497
  },
1494
1498
  });
1499
+ pi.registerTool(ledgerTool({ cwd }));
1495
1500
  pi.registerTool({
1496
1501
  name: "pulses",
1497
1502
  label: "Pulses",
@@ -0,0 +1,10 @@
1
+ import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
2
+ type AnyTool = ToolDefinition<any, any, any>;
3
+ /**
4
+ * ledger: what turri did, as the ledger recorded it - in this session and every one before it. One pulse, by its id,
5
+ * as facts; the last runs, a line a pulse; or the events of some kinds, counted and listed. Reads only.
6
+ */
7
+ export declare function ledgerTool(options: {
8
+ cwd: string;
9
+ }): AnyTool;
10
+ export {};
@@ -0,0 +1,64 @@
1
+ import { defineTool } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { describeEvents, describePastPulse, describeRuns, ledgerProjects, runViews, sinceOf } from "./regenerate/ledger-view.js";
4
+ /** How many runs `ledger` lists when it is asked for none. */
5
+ const RUNS_SHOWN = 3;
6
+ /**
7
+ * ledger: what turri did, as the ledger recorded it - in this session and every one before it. One pulse, by its id,
8
+ * as facts; the last runs, a line a pulse; or the events of some kinds, counted and listed. Reads only.
9
+ */
10
+ export function ledgerTool(options) {
11
+ return defineTool({
12
+ name: "ledger",
13
+ label: "Ledger",
14
+ description: "What turri did here, as its ledger recorded it - facts, not transcripts - in this session and every earlier one. " +
15
+ "With pulse: that pulse, this session's or an earlier one's - how it ended, its rounds and test runs (how many failed " +
16
+ "the same way), the judge's and the reviews' verdicts, the healers (the debugger, the diagnosis) and whether they " +
17
+ "failed, the model provider's trouble, what turri refused and stopped, what it committed or rolled back, its last " +
18
+ "stages and its log. With kinds (and module, since): how many events of those kinds there were, and the most recent, " +
19
+ "a line each - healer.failed, provider.error, script.run, gate.round, judge.verdict, guard.refused, history.failed, " +
20
+ "job.finished, ...; a group by its first part, healer for every healer event. Otherwise: the last runs, a line a pulse - " +
21
+ "how each ended, how long, what it cost. Starts nothing and calls no model.",
22
+ promptSnippet: "ledger: what turri did - a pulse as facts, the last runs, or events counted by kind - this session and earlier ones",
23
+ promptGuidelines: [
24
+ "Use ledger for what turri recorded of the work - a pulse's rounds, test runs, verdicts, healers and commits, this session's or an earlier one's; how often something happened; what earlier runs did and cost - rather than reading .turri/ledger with the shell. pulses is what is running and planned now; a pulse's whole transcript is its log, which ledger names.",
25
+ ],
26
+ parameters: Type.Object({
27
+ pulse: Type.Optional(Type.String({ description: "A pulse's id, or the start of it: that pulse, as the ledger has it" })),
28
+ kinds: Type.Optional(Type.Array(Type.String(), { description: "Event kinds to count and list - healer.failed, or healer for the whole group" })),
29
+ module: Type.Optional(Type.String({ description: "With kinds: only the events of this module" })),
30
+ since: Type.Optional(Type.String({ description: "With kinds: only events since then - 24h, 7d, 30m, or a date and time" })),
31
+ runs: Type.Optional(Type.Number({ description: `Without pulse or kinds: how many of the last runs to list (default ${RUNS_SHOWN})` })),
32
+ limit: Type.Optional(Type.Number({ description: "With kinds: how many of the most recent events to list (default 20)" })),
33
+ }),
34
+ async execute(_id, params) {
35
+ const text = (body, details = {}) => ({ content: [{ type: "text", text: body }], details });
36
+ const projects = ledgerProjects(options.cwd);
37
+ if (projects.length === 0)
38
+ return text("The ledger is empty here: nothing has run in this folder, or in a project under it, since it began.", { found: false });
39
+ if (params.pulse !== undefined) {
40
+ const described = describePastPulse(projects, params.pulse.trim());
41
+ return described !== undefined ? text(described, { found: true }) : text(`The ledger has no pulse ${params.pulse}.`, { found: false });
42
+ }
43
+ if (params.kinds !== undefined && params.kinds.length > 0) {
44
+ let since;
45
+ if (params.since !== undefined) {
46
+ since = sinceOf(params.since);
47
+ if (since === undefined)
48
+ return text(`since takes 24h, 7d, 30m or a date and time, not ${JSON.stringify(params.since)}.`, { found: false });
49
+ }
50
+ return text(describeEvents(projects, {
51
+ kinds: params.kinds,
52
+ ...(params.module !== undefined ? { module: params.module } : {}),
53
+ ...(since !== undefined ? { since } : {}),
54
+ ...(params.limit !== undefined ? { limit: params.limit } : {}),
55
+ }), { found: true });
56
+ }
57
+ const count = params.runs !== undefined && Number.isFinite(params.runs) ? Math.max(1, Math.floor(params.runs)) : RUNS_SHOWN;
58
+ const runs = projects.flatMap((project) => runViews(project)).sort((a, b) => (b.started?.ts ?? "").localeCompare(a.started?.ts ?? ""));
59
+ const shown = runs.slice(0, count);
60
+ const more = runs.length - shown.length;
61
+ return text(`${describeRuns(shown)}${more > 0 ? `\n\n${more} earlier run${more === 1 ? "" : "s"} beyond these: ask with runs for more.` : ""}`, { found: true, runs: shown.length });
62
+ },
63
+ });
64
+ }
@@ -70,4 +70,23 @@ export declare function earlierPulses(projects: string[], known: Set<string>, ru
70
70
  };
71
71
  /** A pulse of the ledger's, as the lists show a job: finished as it ended, its time and cost - and nothing to run. */
72
72
  export declare function asJob(view: PulseView): Job;
73
+ /** What to count in the ledger: kinds - whole, or a group by what comes before the dot - a module, since when. */
74
+ export interface EventQuery {
75
+ /** `healer.failed`, or `healer` for every healer event. */
76
+ kinds?: string[];
77
+ module?: string;
78
+ /** Only events at or after this time. */
79
+ since?: Date;
80
+ /** How many of the most recent to list, a line each. */
81
+ limit?: number;
82
+ }
83
+ /** One event in a line: when, its pulse and module, its kind, its own fields - the agent's role when it has one. */
84
+ export declare function eventLine(entry: LedgerEntry): string;
85
+ /**
86
+ * The events of these projects' ledgers that `query` asks for: how many of each kind, and the most recent, a line each -
87
+ * or, when no kind asked for is in the ledger, the kinds it has.
88
+ */
89
+ export declare function describeEvents(projects: string[], query?: EventQuery): string;
90
+ /** `24h`, `7d`, `30m`, or a date and time: the moment it means, or undefined when it means none. */
91
+ export declare function sinceOf(text: string, now?: number): Date | undefined;
73
92
  export {};
@@ -148,18 +148,25 @@ export function describePulseView(view) {
148
148
  lines.push(` tried again (${retry.attempt} of ${retry.of}) after: ${retry.failure}`);
149
149
  if (view.rounds.size > 0)
150
150
  lines.push(` rounds: ${[...view.rounds].map(([gate, { round, of }]) => `${gate} ${round} of ${of}`).join("; ")}`);
151
- if (view.scripts.length > 0) {
152
- const failing = view.scripts.filter((run) => run.exit !== 0);
151
+ // can_it_fail's runs, on code changed to break it, apart: one failing is a test that caught the bug.
152
+ const mutations = view.scripts.filter((run) => run.mutated === true);
153
+ const tests = view.scripts.filter((run) => run.mutated !== true);
154
+ if (tests.length > 0) {
155
+ const failing = tests.filter((run) => run.exit !== 0);
153
156
  const signatures = new Map();
154
157
  for (const run of failing)
155
158
  if (run.signature !== undefined)
156
159
  signatures.set(run.signature, (signatures.get(run.signature) ?? 0) + 1);
157
160
  const most = Math.max(0, ...signatures.values());
158
- const timedOut = view.scripts.filter((run) => run.timedOut).length;
159
- lines.push(` test runs: ${view.scripts.length}, ${failing.length} failing` +
161
+ const timedOut = tests.filter((run) => run.timedOut).length;
162
+ lines.push(` test runs: ${tests.length}, ${failing.length} failing` +
160
163
  (most > 1 ? ` - ${most} of them the same way` : "") +
161
164
  (timedOut > 0 ? `, ${timedOut} stopped at the time limit` : ""));
162
165
  }
166
+ if (mutations.length > 0) {
167
+ const caught = mutations.filter((run) => run.exit !== 0 && !run.timedOut).length;
168
+ lines.push(` can_it_fail: ${mutations.length} run${mutations.length === 1 ? "" : "s"} on code broken on purpose, ${caught} caught by the tests`);
169
+ }
163
170
  if (view.judge.size > 0)
164
171
  lines.push(` the judge blamed: ${[...view.judge].map(([culprit, times]) => `${culprit} ${times}x`).join(", ")}`);
165
172
  if (view.reviews.length > 0)
@@ -284,3 +291,55 @@ export function asJob(view) {
284
291
  earlier: { run: view.run, ...(view.runMode !== undefined ? { runMode: view.runMode } : {}), lines: describePulseView(view).split("\n").slice(1).map((line) => line.replace(/^ /, "")) },
285
292
  };
286
293
  }
294
+ /** The envelope's fields, said apart from an event's own. */
295
+ const ENVELOPE = new Set(["v", "ts", "seq", "run", "job", "module", "agent", "kind"]);
296
+ /** One event in a line: when, its pulse and module, its kind, its own fields - the agent's role when it has one. */
297
+ export function eventLine(entry) {
298
+ const fields = Object.entries(entry)
299
+ .filter(([key]) => !ENVELOPE.has(key))
300
+ .map(([key, value]) => {
301
+ const text = typeof value === "string" ? JSON.stringify(value.length > 160 ? `${value.slice(0, 159)}…` : value) : JSON.stringify(value);
302
+ return `${key}=${text}`;
303
+ });
304
+ const where = [entry.job, entry.module, entry.agent?.role ?? entry.agent?.label].filter((part) => part !== undefined);
305
+ return [entry.ts.slice(0, 19).replace("T", " "), ...where, entry.kind, ...fields].join(" · ");
306
+ }
307
+ /**
308
+ * The events of these projects' ledgers that `query` asks for: how many of each kind, and the most recent, a line each -
309
+ * or, when no kind asked for is in the ledger, the kinds it has.
310
+ */
311
+ export function describeEvents(projects, query = {}) {
312
+ const all = projects.flatMap((project) => readLedger(project, query.since !== undefined ? { since: query.since } : {}));
313
+ const inModule = query.module !== undefined ? all.filter((entry) => entry.module === query.module) : all;
314
+ const wanted = query.kinds?.map((kind) => kind.trim()).filter((kind) => kind !== "");
315
+ const matches = (kind) => wanted === undefined || wanted.length === 0 || wanted.some((asked) => kind === asked || kind.startsWith(`${asked}.`));
316
+ const found = inModule.filter((entry) => matches(entry.kind));
317
+ const scope = [query.module !== undefined ? `in ${query.module}` : "", query.since !== undefined ? `since ${query.since.toISOString().slice(0, 16).replace("T", " ")}` : ""].filter(Boolean).join(", ");
318
+ if (found.length === 0) {
319
+ const kinds = [...new Set(inModule.map((entry) => entry.kind))].sort();
320
+ return `No ${wanted !== undefined && wanted.length > 0 ? wanted.join(", ") : ""} events${scope !== "" ? ` ${scope}` : ""} in the ledger.${kinds.length > 0 ? ` It has: ${kinds.join(", ")}.` : ""}`;
321
+ }
322
+ const counts = new Map();
323
+ for (const entry of found)
324
+ counts.set(entry.kind, (counts.get(entry.kind) ?? 0) + 1);
325
+ const pulses = new Set(found.map((entry) => entry.job).filter((job) => job !== undefined)).size;
326
+ const runs = new Set(found.map((entry) => entry.run)).size;
327
+ const limit = Math.max(1, query.limit ?? 20);
328
+ const recent = found.slice(-limit);
329
+ return [
330
+ `${found.length} event${found.length === 1 ? "" : "s"}${scope !== "" ? ` ${scope}` : ""}, in ${runs} run${runs === 1 ? "" : "s"} and ${pulses} pulse${pulses === 1 ? "" : "s"}: ${[...counts].sort((a, b) => b[1] - a[1]).map(([kind, times]) => `${kind} ${times}`).join(", ")}`,
331
+ "",
332
+ recent.length === found.length ? "Each, oldest first:" : `The last ${recent.length}, oldest first:`,
333
+ ...recent.map(eventLine),
334
+ ].join("\n");
335
+ }
336
+ /** `24h`, `7d`, `30m`, or a date and time: the moment it means, or undefined when it means none. */
337
+ export function sinceOf(text, now = Date.now()) {
338
+ const relative = /^(\d+)\s*([mhd])$/i.exec(text.trim());
339
+ if (relative !== null) {
340
+ const unit = { m: 60_000, h: 3_600_000, d: 86_400_000 }[relative[2].toLowerCase()];
341
+ return new Date(now - Number(relative[1]) * unit);
342
+ }
343
+ const date = new Date(text.trim());
344
+ return Number.isNaN(date.getTime()) ? undefined : date;
345
+ }
@@ -131,6 +131,7 @@ export type LedgerEvent = {
131
131
  ms: number;
132
132
  timedOut: boolean;
133
133
  inPlace: boolean;
134
+ mutated?: boolean;
134
135
  signature?: string;
135
136
  } | {
136
137
  kind: "gate.round";
@@ -378,6 +378,8 @@ async function runScript(cwd, script, args, logDir, options = {}) {
378
378
  ms: run.elapsedMs,
379
379
  timedOut: run.timedOut === true,
380
380
  inPlace: options.named === undefined,
381
+ // On code changed for the run - can_it_fail's bug: failing is what it is for.
382
+ ...(options.mutated === true ? { mutated: true } : {}),
381
383
  ...(run.exitCode !== 0 ? { signature: signatureHash(failureSignature(run)) } : {}),
382
384
  });
383
385
  return run;
@@ -417,10 +419,10 @@ function quote(path) {
417
419
  return `'${path.replace(/'/g, "'\\''")}'`;
418
420
  }
419
421
  /** A testing script run on a fresh copy of the code: the project's script copied fresh into the staging folder, beside it. */
420
- async function runOnCopy(stagingRoot, script, args, logDir, limitMs) {
422
+ async function runOnCopy(stagingRoot, script, args, logDir, limitMs, mutated) {
421
423
  const copy = join(stagingRoot, basename(script));
422
424
  writeFileSync(copy, readFileSync(script));
423
- return runScript(stagingRoot, script, args, logDir, { named: copy, ...(limitMs !== undefined ? { limitMs } : {}) });
425
+ return runScript(stagingRoot, script, args, logDir, { named: copy, ...(limitMs !== undefined ? { limitMs } : {}), ...(mutated === true ? { mutated } : {}) });
424
426
  }
425
427
  /**
426
428
  * The harness's run of the unit testing script on the code as it is now - and run_unit_tests'.
@@ -486,7 +488,7 @@ limitMs) {
486
488
  return run;
487
489
  };
488
490
  if (project.inPlace) {
489
- const inPlace = () => runScript(project.root, script, [resolve(buildDir)], buildDir, limitMs !== undefined ? { limitMs } : {});
491
+ const inPlace = () => runScript(project.root, script, [resolve(buildDir)], buildDir, { ...(limitMs !== undefined ? { limitMs } : {}), ...(change !== undefined ? { mutated: true } : {}) });
490
492
  const started = Date.now();
491
493
  if (change === undefined)
492
494
  return passed(await inPlace(), started);
@@ -501,7 +503,7 @@ limitMs) {
501
503
  if (change !== undefined)
502
504
  writeFileSync(join(staging.build, change.file), change.text);
503
505
  const started = Date.now();
504
- return passed(await runOnCopy(staging.root, script, [staging.build], buildDir, limitMs), started);
506
+ return passed(await runOnCopy(staging.root, script, [staging.build], buildDir, limitMs, change !== undefined), started);
505
507
  }
506
508
  finally {
507
509
  rmSync(staging.root, { recursive: true, force: true });
@@ -57,7 +57,7 @@ The workflow signs in to npm with npm's trusted publishing (OIDC), not a token:
57
57
  | `src/screen-padding.ts` | the blank row above and below the whole screen and the two columns either side, in both of pi's ways of drawing it |
58
58
  | `src/regenerate/spec-map.ts`, `src/regenerate/spec-map-view.ts` | regen: each item's mark, from the records, the `.plain` files and the work loop's plan (`mapPlan`), and the map drawn |
59
59
  | `src/regenerate/debug-log.ts` | each render agent's whole conversation, always streamed to `.turri/debug/` |
60
- | `src/regenerate/ledger.ts`, `src/regenerate/ledger-view.ts`, `src/ledger-cli.ts` | the ledger: what turri did, as typed facts, in `.turri/ledger/` - `record()`, read back for `pulses`, `diagnose` and `turri ledger` - see [the ledger](using.md#the-ledger) |
60
+ | `src/regenerate/ledger.ts`, `src/regenerate/ledger-view.ts`, `src/ledger-tools.ts`, `src/ledger-cli.ts` | the ledger: what turri did, as typed facts, in `.turri/ledger/` - `record()`, read back for the `ledger` tool, `pulses`, `diagnose` and `turri ledger` - see [the ledger](using.md#the-ledger) |
61
61
  | `genome/` | what turri knows and can do: `genome/skills/`, the skill library, and `genome/rules/`, the \*\*\*plain authoring rules |
62
62
 
63
63
  ## Known edges
package/docs/using.md CHANGED
@@ -36,7 +36,8 @@ While a skill runs, a line above the input shows its phases, every one by name -
36
36
  | `engine` | show or choose the model the code is regenerated on - see [below](#choosing-a-model) |
37
37
  | `pulses` | the pulses - running, planned and finished - or one of them |
38
38
  | `halt` | halt a pulse, a module's planned pulses, or what `activate` started |
39
- | `diagnose` | why a pulse did not land, from its transcript: how it ended, the stages that went wrong, the last failing test run, the last review, the tool calls that failed |
39
+ | `diagnose` | why a pulse did not land, from its transcript: how it ended, the stages that went wrong, the last failing test run, the last review, the tool calls that failed - and what the [ledger](#the-ledger) recorded of it |
40
+ | `ledger` | what turri did, from the [ledger](#the-ledger), this session's and every earlier one's: a pulse as facts, by its id - its rounds, test runs, verdicts, healers, refusals and commits; events of some kinds counted and listed, for a module or since a time - `healer.failed`, or `healer` for the group; or the last runs, a line a pulse |
40
41
  | `lineage` | a module's lineage: what each regeneration and conformance run changed in it, and what it was regenerated from |
41
42
  | `cultivate` | run a gated skill, its steps and review gates enforced |
42
43
  | `release`, `activate` | try what is regenerated: release it to `dist/<module>` and activate it on this machine, installing what it needs - see [deploy](deploy.md) |
@@ -182,7 +183,7 @@ It is in `.turri/ledger/` in the project, a JSON line an event, one file a run -
182
183
  | pulses | `job.queued`, `job.started`, every `job.stage`, `job.retried`, `job.finished` - its status, whether it landed, how long it ran, what it cost, how it ended |
183
184
  | the work loop | `loop.halted`, `loop.stopped` (a pulse the spec moved on from), `planner.failed`, `planner.fellBack` |
184
185
  | the model | `model.call` (time and tokens), `model.turn`, `model.compaction`, `provider.retry`, `provider.stall`, `provider.error`, `turn.stopped` (a turn turri ended itself, and why) |
185
- | tools and tests | `tool.call` (the tool, how long, whether it failed - never its arguments or result), `script.run` (the testing script, its exit, how long, whether the time limit stopped it, the failure's fingerprint) |
186
+ | tools and tests | `tool.call` (the tool, how long, whether it failed - never its arguments or result), `script.run` (the testing script, its exit, how long, whether the time limit stopped it, the failure's fingerprint, and `mutated` for a `can_it_fail` run on code broken on purpose) |
186
187
  | the gates | `gate.round`, `review.verdict`, `arbiter.ruling`, `judge.verdict` |
187
188
  | what turri refused | `guard.refused` (a repeated call, a call past its budget, a tool the turn does not have), `command.refused`, `audit.putBack` |
188
189
  | the healers | `healer.started`, `healer.ended` with what it found, `healer.failed` and why - the debugger, the diagnosis of a failed pulse |
@@ -191,7 +192,7 @@ It is in `.turri/ledger/` in the project, a JSON line an event, one file a run -
191
192
 
192
193
  Never in it: prompts, replies, thinking, tool arguments and results, file contents. What an event does carry - a stage, an error's first line - is masked as the logs are.
193
194
 
194
- `turri ledger` prints the last runs, a line a pulse; `turri ledger <pulse>` one pulse - its rounds, its test runs and how many failed the same way, the verdicts, the healers, what turri refused, what its history recorded, its log; `--json` the events themselves, for `jq`. In the chat, `pulses` lists the last three earlier runs under this session's pulses, and `pulses` and `diagnose` tell one of them by its id; the side panel's pulses list has them as **earlier sessions** (see [the side panel](#in-a-conversation)).
195
+ `turri ledger` prints the last runs, a line a pulse; `turri ledger <pulse>` one pulse - its rounds, its test runs and how many failed the same way, the verdicts, the healers, what turri refused, what its history recorded, its log; `--json` the events themselves, for `jq`. In the chat, `ledger` is the way in - a pulse of this session or an earlier one, the events of some kinds counted (how often the debugger failed this week: `kinds: [healer.failed], since: 7d`), the last runs - and `pulses` lists the last three earlier runs under this session's pulses, `pulses` and `diagnose` tell one of them by its id, and `diagnose` adds the ledger's facts to a pulse of this session; the side panel's pulses list has them as **earlier sessions** (see [the side panel](#in-a-conversation)).
195
196
 
196
197
  It is for looking up, never for deciding: what to build comes from the records alone (see [what the code is built from](regenerating.md#what-the-code-is-built-from)), so deleting `.turri/ledger/` loses nothing but what can be looked up. When a run first writes to the project, the files of runs that have ended are gzipped and the oldest pruned past `ledger-keep:` megabytes - 100 unset, `0` for none at all; a run still going is never touched. `.turri` ignores itself, so the ledger is never committed, and nothing of it is sent anywhere. A ledger that cannot be written is said once and given up on, never failing the work.
197
198
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turri-dev/cli",
3
- "version": "0.1.8-next.b03162b",
3
+ "version": "0.1.8-next.bfcb24e",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {