From b35ca01f3e240749b8219267eafa2197f71e0950 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:48:38 +0000 Subject: [PATCH 01/15] WIP --- .gitignore | 1 + src/durable-streams/storage.ts | 120 ++++++++++++ src/durable-streams/stream-manager.ts | 100 ++++++++++ src/durable-streams/stream.test.ts | 259 ++++++++++++++++++++++++++ src/durable-streams/stream.ts | 139 ++++++++++++++ src/durable-streams/types.ts | 53 ++++++ 6 files changed, 672 insertions(+) create mode 100644 src/durable-streams/storage.ts create mode 100644 src/durable-streams/stream-manager.ts create mode 100644 src/durable-streams/stream.test.ts create mode 100644 src/durable-streams/stream.ts create mode 100644 src/durable-streams/types.ts diff --git a/.gitignore b/.gitignore index d21dcbe..aad0d00 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Agent runtime data (contexts, logs) .mini-agent/ +opensrc/ diff --git a/src/durable-streams/storage.ts b/src/durable-streams/storage.ts new file mode 100644 index 0000000..54ac18c --- /dev/null +++ b/src/durable-streams/storage.ts @@ -0,0 +1,120 @@ +/** + * Storage abstraction for durable streams + */ +import { Effect, Layer } from "effect" +import { isStartOffset, makeOffset, type Offset, type StorageError, StreamEvent, type StreamName } from "./types.ts" + +/** Stored event shape (internal) */ +interface StoredEvent { + readonly offset: Offset + readonly data: unknown + readonly timestamp: number +} + +/** Storage service interface - all methods use object params */ +export class Storage extends Effect.Service()("@durable-streams/Storage", { + succeed: { + /** Append events to a stream, returns created events with offsets */ + append: (_opts: { + name: StreamName + events: ReadonlyArray<{ data: unknown }> + }): Effect.Effect, StorageError> => Effect.succeed([]), + + /** Get events from offset (inclusive). Offset -1 means from start */ + getFrom: (_opts: { + name: StreamName + offset: Offset + limit?: number + }): Effect.Effect, StorageError> => Effect.succeed([]), + + /** Get all events for a stream */ + getAll: (_opts: { + name: StreamName + }): Effect.Effect, StorageError> => Effect.succeed([]), + + /** Check if stream exists */ + exists: (_opts: { + name: StreamName + }): Effect.Effect => Effect.succeed(false), + + /** Create stream (idempotent) */ + create: (_opts: { + name: StreamName + }): Effect.Effect => Effect.void, + + /** Delete stream */ + delete: (_opts: { + name: StreamName + }): Effect.Effect => Effect.void, + + /** List all stream names */ + list: (): Effect.Effect, StorageError> => Effect.succeed([]) + }, + accessors: true +}) { + /** In-memory storage implementation. Uses mutable Map for simplicity in tests. */ + static readonly InMemory: Layer.Layer = Layer.sync(Storage, () => { + const store = new Map>() + + return { + append: (opts: { name: StreamName; events: ReadonlyArray<{ data: unknown }> }) => + Effect.sync(() => { + if (!store.has(opts.name)) { + store.set(opts.name, []) + } + const events = store.get(opts.name)! + const now = Date.now() + const newEvents: Array = opts.events.map((e, i) => + new StreamEvent({ + offset: makeOffset(events.length + i), + data: e.data, + timestamp: now + }) + ) + store.set(opts.name, [ + ...events, + ...newEvents.map((e) => ({ + offset: e.offset, + data: e.data, + timestamp: e.timestamp + })) + ]) + return newEvents + }), + + getFrom: (opts: { name: StreamName; offset: Offset; limit?: number }) => + Effect.sync(() => { + const events = store.get(opts.name) ?? [] + if (isStartOffset(opts.offset)) { + const limited = opts.limit ? events.slice(0, opts.limit) : events + return limited.map((e) => new StreamEvent(e)) + } + const filtered = events.filter((e) => e.offset >= opts.offset) + const limited = opts.limit ? filtered.slice(0, opts.limit) : filtered + return limited.map((e) => new StreamEvent(e)) + }), + + getAll: (opts: { name: StreamName }) => + Effect.sync(() => { + const events = store.get(opts.name) ?? [] + return events.map((e) => new StreamEvent(e)) + }), + + exists: (opts: { name: StreamName }) => Effect.sync(() => store.has(opts.name)), + + create: (opts: { name: StreamName }) => + Effect.sync(() => { + if (!store.has(opts.name)) { + store.set(opts.name, []) + } + }), + + delete: (opts: { name: StreamName }) => + Effect.sync(() => { + store.delete(opts.name) + }), + + list: () => Effect.sync(() => Array.from(store.keys())) + } as unknown as Storage + }) +} diff --git a/src/durable-streams/stream-manager.ts b/src/durable-streams/stream-manager.ts new file mode 100644 index 0000000..3c4a0fc --- /dev/null +++ b/src/durable-streams/stream-manager.ts @@ -0,0 +1,100 @@ +/** + * StreamManager - Layer 1 + * + * Manages multiple named DurableStreams with lazy initialization. + * Each stream is created on first access and cached. + */ +import type { Scope } from "effect" +import { Effect, HashMap, Layer, Ref, Stream } from "effect" +import { Storage } from "./storage.ts" +import { type DurableStream, makeDurableStream } from "./stream.ts" +import type { InvalidOffsetError, Offset, StorageError, StreamEvent, StreamName } from "./types.ts" + +/** StreamManager service interface */ +export interface StreamManager { + /** Get or create a stream by name */ + getStream(opts: { name: StreamName }): Effect.Effect + + /** Append to a stream (creates if not exists) */ + append(opts: { name: StreamName; data: unknown }): Effect.Effect + + /** Subscribe to a stream (creates if not exists) */ + subscribe(opts: { + name: StreamName + offset?: Offset + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> + + /** List all stream names */ + list(): Effect.Effect, StorageError> + + /** Delete a stream */ + delete(opts: { name: StreamName }): Effect.Effect +} + +/** Create StreamManager instance */ +export const makeStreamManager = Effect.gen(function*() { + const storage = yield* Storage + + // Cache of initialized streams (StreamName -> DurableStream) + const streamsRef = yield* Ref.make(HashMap.empty()) + + const getStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + const streams = yield* Ref.get(streamsRef) + const existing = HashMap.get(streams, opts.name) + + if (existing._tag === "Some") { + return existing.value + } + + // Create new stream + const stream = yield* makeDurableStream({ name: opts.name }) + yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) + return stream + }) + + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.append({ data: opts.data }) + }) + + const subscribe = (opts: { + name: StreamName + offset?: Offset + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.subscribe({ offset: opts.offset }) + }) + + const list = (): Effect.Effect, StorageError> => storage.list() + + const deleteStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + yield* storage.delete(opts) + yield* Ref.update(streamsRef, HashMap.remove(opts.name)) + }) + + return { + getStream, + append, + subscribe, + list, + delete: deleteStream + } satisfies StreamManager +}) + +/** StreamManager service tag */ +export class StreamManagerService extends Effect.Service()( + "@durable-streams/StreamManager", + { + effect: makeStreamManager, + dependencies: [Storage.Default] + } +) { + static readonly InMemory: Layer.Layer = Layer.effect( + StreamManagerService, + makeStreamManager + ).pipe(Layer.provide(Storage.InMemory)) +} diff --git a/src/durable-streams/stream.test.ts b/src/durable-streams/stream.test.ts new file mode 100644 index 0000000..fde7f6d --- /dev/null +++ b/src/durable-streams/stream.test.ts @@ -0,0 +1,259 @@ +/** + * Tests for DurableStream (Layer 0) + */ +import { describe, expect, it } from "@effect/vitest" +import { Effect, Fiber, Queue, Stream } from "effect" +import { Storage } from "./storage.ts" +import { makeDurableStream } from "./stream.ts" +import { makeOffset, type Offset, OFFSET_START, type StreamName } from "./types.ts" + +const testStreamName = "test-stream" as StreamName + +describe("DurableStream", () => { + describe("append", () => { + it.effect("assigns sequential offsets", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + const e1 = yield* stream.append({ data: { msg: "first" } }) + const e2 = yield* stream.append({ data: { msg: "second" } }) + + expect(e1.offset).toBe(makeOffset(0)) + expect(e2.offset).toBe(makeOffset(1)) + }).pipe(Effect.provide(Storage.InMemory))) + + it.effect("stores data correctly", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + const event = yield* stream.append({ data: { key: "value" } }) + + expect(event.data).toEqual({ key: "value" }) + expect(typeof event.timestamp).toBe("number") + expect(event.timestamp).toBeGreaterThan(0) + }).pipe(Effect.provide(Storage.InMemory))) + + it.effect("increments count", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + expect(yield* stream.count).toBe(0) + + yield* stream.append({ data: { n: 1 } }) + expect(yield* stream.count).toBe(1) + + yield* stream.append({ data: { n: 2 } }) + expect(yield* stream.count).toBe(2) + }).pipe(Effect.provide(Storage.InMemory))) + }) + + describe("subscribe", () => { + it.effect("returns historical events", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + yield* stream.append({ data: { msg: "first" } }) + yield* stream.append({ data: { msg: "second" } }) + + const eventStream = yield* stream.subscribe() + const events = yield* eventStream.pipe( + Stream.take(2), + Stream.runCollect, + Effect.map((chunk) => Array.from(chunk)) + ) + + expect(events).toHaveLength(2) + expect(events[0]?.data).toEqual({ msg: "first" }) + expect(events[1]?.data).toEqual({ msg: "second" }) + }).pipe(Effect.scoped, Effect.provide(Storage.InMemory))) + + it.effect("receives live events after subscribing", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + const collected = yield* Queue.unbounded() + + // Subscribe first (no historical events) + // PubSub.subscribe guarantees subscription IS established when this completes + const eventStream = yield* stream.subscribe() + + // Fork consumer + const fiber = yield* eventStream.pipe( + Stream.take(2), + Stream.runForEach((e) => Queue.offer(collected, e.data)), + Effect.fork + ) + + // Append live events immediately - subscription is already established + yield* stream.append({ data: { msg: "live1" } }) + yield* stream.append({ data: { msg: "live2" } }) + + // Wait for consumer with timeout + yield* Fiber.join(fiber).pipe(Effect.timeout("5 seconds"), Effect.orDie) + + // Check results + const results: Array = [] + let item = yield* Queue.poll(collected) + while (item._tag === "Some") { + results.push(item.value) + item = yield* Queue.poll(collected) + } + + expect(results).toEqual([{ msg: "live1" }, { msg: "live2" }]) + }).pipe(Effect.scoped, Effect.provide(Storage.InMemory))) + + it.effect("returns both historical and live events", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + const collected = yield* Queue.unbounded() + + // Add historical + yield* stream.append({ data: { type: "historical" } }) + + // Subscribe - subscription is established when this completes + const eventStream = yield* stream.subscribe() + + // Fork consumer for 3 events + const fiber = yield* eventStream.pipe( + Stream.take(3), + Stream.runForEach((e) => Queue.offer(collected, e.data)), + Effect.fork + ) + + // Add live immediately + yield* stream.append({ data: { type: "live1" } }) + yield* stream.append({ data: { type: "live2" } }) + + yield* Fiber.join(fiber).pipe(Effect.timeout("5 seconds"), Effect.orDie) + + const results: Array = [] + let item = yield* Queue.poll(collected) + while (item._tag === "Some") { + results.push(item.value) + item = yield* Queue.poll(collected) + } + + expect(results).toEqual([ + { type: "historical" }, + { type: "live1" }, + { type: "live2" } + ]) + }).pipe(Effect.scoped, Effect.provide(Storage.InMemory))) + }) + + describe("subscribe with offset", () => { + it.effect("returns events from specified offset", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + yield* stream.append({ data: { n: 0 } }) + yield* stream.append({ data: { n: 1 } }) + yield* stream.append({ data: { n: 2 } }) + + // Subscribe from offset 1 (should get n:1 and n:2) + const eventStream = yield* stream.subscribe({ offset: makeOffset(1) }) + const events = yield* eventStream.pipe( + Stream.take(2), + Stream.runCollect, + Effect.map((chunk) => Array.from(chunk)) + ) + + expect(events).toHaveLength(2) + expect(events[0]?.data).toEqual({ n: 1 }) + expect(events[1]?.data).toEqual({ n: 2 }) + }).pipe(Effect.scoped, Effect.provide(Storage.InMemory))) + + it.effect("handles -1 offset (start from beginning)", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + yield* stream.append({ data: { n: 0 } }) + yield* stream.append({ data: { n: 1 } }) + + const eventStream = yield* stream.subscribe({ offset: OFFSET_START }) + const events = yield* eventStream.pipe( + Stream.take(2), + Stream.runCollect, + Effect.map((chunk) => Array.from(chunk)) + ) + + expect(events).toHaveLength(2) + expect(events[0]?.data).toEqual({ n: 0 }) + }).pipe(Effect.scoped, Effect.provide(Storage.InMemory))) + + it.effect("fails with invalid offset", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + const result = yield* stream + .subscribe({ offset: "invalid" as Offset }) + .pipe(Effect.either) + + expect(result._tag).toBe("Left") + if (result._tag === "Left") { + expect(result.left._tag).toBe("InvalidOffsetError") + } + }).pipe(Effect.scoped, Effect.provide(Storage.InMemory))) + }) + + describe("multiple subscribers", () => { + it.effect("all subscribers receive the same events", () => + Effect.gen(function*() { + const stream = yield* makeDurableStream({ name: testStreamName }) + + const q1 = yield* Queue.unbounded() + const q2 = yield* Queue.unbounded() + const q3 = yield* Queue.unbounded() + + // Create 3 subscribers - all established when this completes + const s1 = yield* stream.subscribe() + const s2 = yield* stream.subscribe() + const s3 = yield* stream.subscribe() + + // Fork all consumers + const f1 = yield* s1.pipe( + Stream.take(2), + Stream.runForEach((e) => Queue.offer(q1, e.data)), + Effect.fork + ) + const f2 = yield* s2.pipe( + Stream.take(2), + Stream.runForEach((e) => Queue.offer(q2, e.data)), + Effect.fork + ) + const f3 = yield* s3.pipe( + Stream.take(2), + Stream.runForEach((e) => Queue.offer(q3, e.data)), + Effect.fork + ) + + // Publish events immediately + yield* stream.append({ data: { msg: "a" } }) + yield* stream.append({ data: { msg: "b" } }) + + // Wait for all with timeout + yield* Fiber.join(f1).pipe(Effect.timeout("5 seconds"), Effect.orDie) + yield* Fiber.join(f2).pipe(Effect.timeout("5 seconds"), Effect.orDie) + yield* Fiber.join(f3).pipe(Effect.timeout("5 seconds"), Effect.orDie) + + // Collect results + const collect = (q: Queue.Queue) => + Effect.gen(function*() { + const results: Array = [] + let item = yield* Queue.poll(q) + while (item._tag === "Some") { + results.push(item.value) + item = yield* Queue.poll(q) + } + return results + }) + + const r1 = yield* collect(q1) + const r2 = yield* collect(q2) + const r3 = yield* collect(q3) + + expect(r1).toEqual([{ msg: "a" }, { msg: "b" }]) + expect(r2).toEqual([{ msg: "a" }, { msg: "b" }]) + expect(r3).toEqual([{ msg: "a" }, { msg: "b" }]) + }).pipe(Effect.scoped, Effect.provide(Storage.InMemory))) + }) +}) diff --git a/src/durable-streams/stream.ts b/src/durable-streams/stream.ts new file mode 100644 index 0000000..749bc95 --- /dev/null +++ b/src/durable-streams/stream.ts @@ -0,0 +1,139 @@ +/** + * DurableStream - Layer 0 core primitive + * + * A single named stream with append and subscribe operations. + * Uses PubSub for live subscriptions with historical catch-up. + */ +import type { Scope } from "effect" +import { Effect, PubSub, Ref, Stream } from "effect" +import { Storage } from "./storage.ts" +import { + InvalidOffsetError, + isStartOffset, + makeOffset, + OFFSET_START, + parseOffset, + StorageError, + StreamEvent, + type Offset, + type StreamName +} from "./types.ts" + +/** DurableStream interface - all methods use object params */ +export interface DurableStream { + readonly name: StreamName + + /** Append data to stream, returns event with assigned offset */ + append(opts: { data: unknown }): Effect.Effect + + /** Subscribe to events. Returns historical + live. Use offset to start from position. + * Requires Scope - subscription stays active while scope is open. */ + subscribe(opts?: { offset?: Offset }): Effect.Effect< + Stream.Stream, + InvalidOffsetError | StorageError, + Scope.Scope + > + + /** Current event count */ + readonly count: Effect.Effect +} + +/** Create a DurableStream instance for a given name */ +export const makeDurableStream = (opts: { + name: StreamName +}): Effect.Effect => + Effect.gen(function*() { + const storage = yield* Storage + const { name } = opts + + // Ensure stream exists in storage + yield* storage.create({ name }) + + // PubSub for live event fan-out + const pubsub = yield* PubSub.unbounded() + + // Sync offset counter with storage + const events = yield* storage.getAll({ name }) + const nextOffsetRef = yield* Ref.make(events.length) + + const append = (appendOpts: { data: unknown }): Effect.Effect => + Effect.gen(function*() { + const counter = yield* Ref.getAndUpdate(nextOffsetRef, (n) => n + 1) + const offset = makeOffset(counter) + const timestamp = Date.now() + + const event = new StreamEvent({ + offset, + data: appendOpts.data, + timestamp + }) + + // Store the event + yield* storage.append({ name, events: [{ data: appendOpts.data }] }) + + // Broadcast to live subscribers + yield* PubSub.publish(pubsub, event) + + return event + }) + + const subscribe = ( + subscribeOpts?: { offset?: Offset } + ): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + Effect.gen(function*() { + const offset = subscribeOpts?.offset ?? OFFSET_START + + // Validate offset format (unless it's the start sentinel) + if (!isStartOffset(offset)) { + const parsed = parseOffset(offset) + if (isNaN(parsed) || parsed < 0) { + return yield* Effect.fail( + new InvalidOffsetError({ + offset, + message: "Offset must be a non-negative integer string or -1" + }) + ) + } + } + + // Subscribe to PubSub FIRST (before fetching historical) + // PubSub.subscribe guarantees subscription is established when this completes + // Scope keeps subscription alive - caller must provide scope + const dequeue = yield* PubSub.subscribe(pubsub) + + // Get historical events (subscription already established, no gap) + const historical = yield* storage.getFrom({ name, offset }) + + // Track last historical offset to avoid duplicates in live + const lastHistoricalOffset = historical.length > 0 + ? historical[historical.length - 1]!.offset + : null + + // Create historical stream + const historicalStream = Stream.fromIterable(historical) + + // Create live stream from PubSub, filtering out duplicates + const liveStream = Stream.fromQueue(dequeue).pipe( + Stream.filter((e) => + lastHistoricalOffset === null + ? (isStartOffset(offset) || e.offset >= offset) + : e.offset > lastHistoricalOffset + ) + ) + + // Concat: historical first, then live + return Stream.concat(historicalStream, liveStream) + }) + + const count: Effect.Effect = Effect.gen(function*() { + const events = yield* storage.getAll({ name }) + return events.length + }) + + return { + name, + append, + subscribe, + count + } + }) diff --git a/src/durable-streams/types.ts b/src/durable-streams/types.ts new file mode 100644 index 0000000..7197a92 --- /dev/null +++ b/src/durable-streams/types.ts @@ -0,0 +1,53 @@ +/** + * Core types for durable streams + */ +import { Schema } from "effect" + +// Branded types +export const StreamName = Schema.String.pipe( + Schema.nonEmptyString(), + Schema.brand("StreamName") +) +export type StreamName = typeof StreamName.Type + +export const Offset = Schema.String.pipe(Schema.brand("Offset")) +export type Offset = typeof Offset.Type + +/** Zero-padded 16-char offset for lexicographic sorting */ +export const makeOffset = (n: number): Offset => String(n).padStart(16, "0") as Offset + +export const parseOffset = (offset: Offset): number => parseInt(offset, 10) + +/** Special offset meaning "start from beginning" per durable-streams spec */ +export const OFFSET_START = "-1" as Offset + +export const isStartOffset = (offset: Offset): boolean => offset === OFFSET_START + +// Event schema +export class StreamEvent extends Schema.Class("StreamEvent")({ + offset: Offset, + data: Schema.Unknown, + timestamp: Schema.Number +}) {} + +// Errors +export class StreamNotFoundError extends Schema.TaggedError()( + "StreamNotFoundError", + { name: StreamName } +) {} + +export class StorageError extends Schema.TaggedError()( + "StorageError", + { + message: Schema.String, + cause: Schema.optional(Schema.Unknown) + } +) {} + +export class InvalidOffsetError extends Schema.TaggedError()( + "InvalidOffsetError", + { + offset: Schema.String, + message: Schema.String + } +) {} From 4cd5884f028b4da5c2dad10495cc6143e78665c6 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:15:14 +0000 Subject: [PATCH 02/15] Implement durable-streams HTTP server with Node.js runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete durable-streams implementation with three layers: - Stream (persistence, append/subscribe) - StreamManager (multi-stream coordination) - HTTP routes (REST + SSE endpoints) Migrate from Bun to Node.js: - Use @effect/platform-node for HTTP server - Use tsx for TypeScript execution - Add native fetch e2e tests alongside Effect tests 38 tests passing (stream, manager, Effect HTTP, native fetch e2e). πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- CLAUDE.md | 39 ++- bun.lock | 132 +++++++--- package.json | 14 +- src/durable-streams/cli.ts | 60 +++++ src/durable-streams/http-routes.e2e.test.ts | 264 ++++++++++++++++++++ src/durable-streams/http-routes.test.ts | 202 +++++++++++++++ src/durable-streams/http-routes.ts | 149 +++++++++++ src/durable-streams/index.ts | 35 +++ src/durable-streams/main.ts | 18 ++ src/durable-streams/stream-manager.test.ts | 203 +++++++++++++++ src/durable-streams/stream-manager.ts | 170 ++++++++----- src/durable-streams/stream.ts | 3 +- 12 files changed, 1187 insertions(+), 102 deletions(-) create mode 100644 src/durable-streams/cli.ts create mode 100644 src/durable-streams/http-routes.e2e.test.ts create mode 100644 src/durable-streams/http-routes.test.ts create mode 100644 src/durable-streams/http-routes.ts create mode 100644 src/durable-streams/index.ts create mode 100644 src/durable-streams/main.ts create mode 100644 src/durable-streams/stream-manager.test.ts diff --git a/CLAUDE.md b/CLAUDE.md index bcb04e0..f9bc1df 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -333,7 +333,7 @@ describe("MyService", () => { static readonly testLayer = Layer.sync(MyService, () => { // Mutable state is fine in tests - JS is single-threaded const store = new Map() - + return MyService.of({ get: (key) => Effect.succeed(store.get(key)), set: (key, value) => Effect.sync(() => void store.set(key, value)) @@ -341,6 +341,43 @@ static readonly testLayer = Layer.sync(MyService, () => { }) ``` +## TestClock in vitest + +`@effect/vitest` uses `TestClock` by defaultβ€”time doesn't advance automatically. Use `TestClock.adjust` to advance time in tests with `Effect.sleep`: + +```typescript +import { Effect, Fiber, TestClock } from "effect" +import { it } from "@effect/vitest" + +it.effect("handles sleep with TestClock", () => + Effect.gen(function*() { + const fiber = yield* Effect.fork( + Effect.gen(function*() { + yield* Effect.sleep("1 second") + return "done" + }) + ) + + yield* TestClock.adjust("1 second") // Advance virtual time + + const result = yield* Fiber.join(fiber) + expect(result).toBe("done") + }) +) +``` + +For real time instead of test clock, use `it.live`: + +```typescript +it.live("uses real clock", () => + Effect.gen(function*() { + yield* Effect.sleep("10 millis") // Actually waits + }) +) +``` + +**Alternative:** For PubSub subscription tests, `PubSub.subscribe` guarantees subscription is established when effect completesβ€”often you can publish immediately without sleep. + ## Layer Memoization Layers are memoized by reference. Functions returning layers defeat memoizationβ€”each call creates a new object, causing duplicate construction, resource leaks, and inconsistent state. diff --git a/bun.lock b/bun.lock index df96ba0..6ac1490 100644 --- a/bun.lock +++ b/bun.lock @@ -13,6 +13,7 @@ "@effect/opentelemetry": "^0.59.1", "@effect/platform": "^0.93.5", "@effect/platform-bun": "^0.85.0", + "@effect/platform-node": "^0.104.0", "@effect/rpc": "^0.72.2", "@effect/rpc-http": "^0.52.4", "@opentelemetry/otlp-transformer": "^0.208.0", @@ -31,7 +32,7 @@ "@eslint/compat": "^1.1.1", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.10.0", - "@types/bun": "latest", + "@types/node": "^22", "@types/react": "19", "@typescript-eslint/eslint-plugin": "^8.4.0", "@typescript-eslint/parser": "^8.4.0", @@ -41,6 +42,7 @@ "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-sort-destructure-keys": "^2.0.0", "semver": "^7.7.3", + "tsx": "^4.19.0", "tuistory": "^0.0.2", "vitest": "^3.2.0", }, @@ -82,6 +84,8 @@ "@effect/platform-bun": ["@effect/platform-bun@0.85.0", "", { "dependencies": { "@effect/platform-node-shared": "^0.55.0", "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/cluster": "^0.54.0", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "effect": "^3.19.8" } }, "sha512-olIFcq2tNow4jRhLNQuuTgcts7qFICvL4z7kI0YoFzvHvXwH+R87b+iTa/5jR7h6KuckapqgIyf1LX5IN2Jj6w=="], + "@effect/platform-node": ["@effect/platform-node@0.104.0", "", { "dependencies": { "@effect/platform-node-shared": "^0.57.0", "mime": "^3.0.0", "undici": "^7.10.0", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.56.0", "@effect/platform": "^0.94.0", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "effect": "^3.19.13" } }, "sha512-2ZkUDDTxLD95ARdYIKBx4tdIIgqA3cwb3jlnVVBxmHUf0Pg5N2HdMuD0Q+CXQ7Q94FDwnLW3ZvaSfxDh6FvrNw=="], + "@effect/platform-node-shared": ["@effect/platform-node-shared@0.55.0", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "multipasta": "^0.2.7", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.54.0", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "effect": "^3.19.8" } }, "sha512-rmeb3bnt6CKIC+QSyDY0DBa46XQMWRzY3RAsr9MUbxGbPmk1fY8kX0R9rZ8wd1ymi7RlAYXfwE8KvJG2WI2NXA=="], "@effect/printer": ["@effect/printer@0.47.0", "", { "peerDependencies": { "@effect/typeclass": "^0.38.0", "effect": "^3.19.0" } }, "sha512-VgR8e+YWWhMEAh9qFOjwiZ3OXluAbcVLIOtvp2S5di1nSrPOZxj78g8LE77JSvyfp5y5bS2gmFW+G7xD5uU+2Q=="], @@ -106,57 +110,57 @@ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], @@ -400,8 +404,6 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="], - "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], @@ -412,7 +414,7 @@ "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], - "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], + "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], @@ -542,8 +544,6 @@ "bun-pty": ["bun-pty@0.4.2", "", {}, "sha512-sHImDz6pJDsHAroYpC9ouKVgOyqZ7FP3N+stX5IdMddHve3rf9LIZBDomQcXrACQ7sQDNuwZQHG8BKR7w8krkQ=="], - "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="], - "bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="], "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eDgLN9teKTfmvrCqgwwmWNsNszxYs7IZdCqk0S1DCarvMhr4wcajoSBlA/nQA0/owwLduPTS8xxCnQp4/N/gDg=="], @@ -622,7 +622,7 @@ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], @@ -1068,6 +1068,8 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + "tuistory": ["tuistory@0.0.2", "", { "dependencies": { "ghostty-opentui": "^1.3.3" }, "optionalDependencies": { "bun-pty": "*", "node-pty": "^1.0.0" } }, "sha512-14FfFhL+s3Ai+XybzuYeygw7NgBhxk01S7DCfYHtMqy3Si5lkvJLNZdJEFVuGnbtBZDXpfxeGaE9HzJaAjITEg=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], @@ -1084,7 +1086,9 @@ "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], @@ -1134,6 +1138,8 @@ "@anthropic-ai/tokenizer/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], + "@effect/platform-node/@effect/platform-node-shared": ["@effect/platform-node-shared@0.57.0", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "multipasta": "^0.2.7", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.56.0", "@effect/platform": "^0.94.0", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "effect": "^3.19.13" } }, "sha512-QXuvmLNlABCQLcTl+lN1YPhKosR6KqArPYjC2reU0fb5lroCo3YRb/aGpXIgLthHzQL8cLU5XMGA3Cu5hKY2Tw=="], + "@effect/rpc-http/@effect/rpc": ["@effect/rpc@0.54.4", "", { "peerDependencies": { "@effect/platform": "^0.79.4", "effect": "^3.13.12" } }, "sha512-iu3TGWCt4OMH8iKL1ATeROhAxrMF+HdF3NbR5lWls9yWJwBgVU+cps3ZzRbNQhFPWXDGqVuYgmYNY1GKbZgMaw=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], @@ -1172,8 +1178,12 @@ "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="], + "protobufjs/@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], + "react-reconciler/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "@anthropic-ai/tokenizer/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@opentelemetry/sdk-logs/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], @@ -1185,5 +1195,59 @@ "@opentelemetry/sdk-metrics/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "protobufjs/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], } } diff --git a/package.json b/package.json index 1664ef1..3d98fb9 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,14 @@ "private": true, "scripts": { "prepare": "effect-language-service patch", - "mini-agent": "doppler run -- bun src/cli/main.ts", - "start": "bun src/cli/main.ts", - "dev": "bun --watch src/cli/main.ts", + "mini-agent": "doppler run -- npx tsx src/cli/main.ts", + "start": "npx tsx src/cli/main.ts", + "dev": "npx tsx --watch src/cli/main.ts", "typecheck": "tsc --noEmit", "lint": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\"", "lint:fix": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\" --fix", - "check": "bun run typecheck && bun run lint", - "check:fix": "bun run typecheck && bun run lint:fix", + "check": "npm run typecheck && npm run lint", + "check:fix": "npm run typecheck && npm run lint:fix", "test": "doppler run -- vitest run", "test:watch": "doppler run -- vitest" }, @@ -23,8 +23,9 @@ "@eslint/compat": "^1.1.1", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.10.0", - "@types/bun": "latest", + "@types/node": "^22", "@types/react": "19", + "tsx": "^4.19.0", "@typescript-eslint/eslint-plugin": "^8.4.0", "@typescript-eslint/parser": "^8.4.0", "eslint": "^9.10.0", @@ -48,6 +49,7 @@ "@effect/opentelemetry": "^0.59.1", "@effect/platform": "^0.93.5", "@effect/platform-bun": "^0.85.0", + "@effect/platform-node": "^0.104.0", "@effect/rpc": "^0.72.2", "@effect/rpc-http": "^0.52.4", "@opentelemetry/otlp-transformer": "^0.208.0", diff --git a/src/durable-streams/cli.ts b/src/durable-streams/cli.ts new file mode 100644 index 0000000..2a230ac --- /dev/null +++ b/src/durable-streams/cli.ts @@ -0,0 +1,60 @@ +/** + * Durable Streams CLI + * + * Commands: + * - start: Start HTTP server for durable streams + */ +import { Command, Options } from "@effect/cli" +import { HttpServer } from "@effect/platform" +import { NodeHttpServer } from "@effect/platform-node" +import { Console, Effect, Layer } from "effect" +import { createServer } from "node:http" +import { durableStreamsRouter } from "./http-routes.ts" +import { StreamManagerService } from "./stream-manager.ts" + +/** Port option */ +const portOption = Options.integer("port").pipe( + Options.withAlias("p"), + Options.withDescription("Port to listen on"), + Options.withDefault(3000) +) + +/** Host option */ +const hostOption = Options.text("host").pipe( + Options.withAlias("h"), + Options.withDescription("Host to bind to"), + Options.withDefault("0.0.0.0") +) + +/** Start command - launches HTTP server */ +const startCommand = Command.make( + "start", + { host: hostOption, port: portOption }, + ({ host, port }) => + Effect.gen(function*() { + yield* Console.log(`Starting durable-streams server on ${host}:${port}`) + + // Build service layers + const serviceLayer = StreamManagerService.InMemory + + // HTTP server layer + const serverLayer = HttpServer.serve(durableStreamsRouter).pipe( + Layer.provide(NodeHttpServer.layer(createServer, { port })), + Layer.provide(serviceLayer) + ) + + return yield* Layer.launch(serverLayer) + }) +) + +/** Main CLI */ +export const cli = Command.make("durable-streams").pipe( + Command.withSubcommands([startCommand]) +) + +/** Run CLI with args */ +export const run = (args: ReadonlyArray) => + Command.run(cli, { + name: "durable-streams", + version: "0.1.0" + })(args) diff --git a/src/durable-streams/http-routes.e2e.test.ts b/src/durable-streams/http-routes.e2e.test.ts new file mode 100644 index 0000000..d8925de --- /dev/null +++ b/src/durable-streams/http-routes.e2e.test.ts @@ -0,0 +1,264 @@ +/** + * E2E tests using native fetch against a real HTTP server. + * + * Starts the server on a random port, tests with standard fetch API. + */ +import { HttpServer } from "@effect/platform" +import { NodeHttpServer } from "@effect/platform-node" +import { Deferred, Effect, Fiber, Layer } from "effect" +import { createServer } from "node:http" +import { afterAll, beforeAll, describe, expect, test } from "vitest" +import { durableStreamsRouter } from "./http-routes.ts" +import { StreamManagerService } from "./stream-manager.ts" +import type { StreamEvent } from "./types.ts" + +let baseUrl: string +let serverFiber: Fiber.RuntimeFiber + +beforeAll(async () => { + // Build server layer with random port (port 0) + const serverLayer = Layer.mergeAll( + NodeHttpServer.layer(createServer, { port: 0 }), + StreamManagerService.InMemory + ).pipe(Layer.provideMerge(HttpServer.layerContext)) + + // Create a deferred to signal when address is available + const fiberId = Effect.runSync(Effect.fiberId) + const addressDeferred = Deferred.unsafeMake(fiberId) + + // Server effect that publishes address then serves forever + const serverEffect = Effect.gen(function*() { + const server = yield* HttpServer.HttpServer + yield* Deferred.succeed(addressDeferred, server.address) + yield* HttpServer.serveEffect(durableStreamsRouter) + return yield* Effect.never + }).pipe( + Effect.scoped, + Effect.provide(serverLayer) + ) + + // Fork server in background + serverFiber = Effect.runFork(serverEffect) + + // Wait for address + const address = await Effect.runPromise(Deferred.await(addressDeferred)) + + if (address._tag === "TcpAddress") { + baseUrl = `http://127.0.0.1:${address.port}` + } else { + throw new Error("Expected TCP address") + } +}) + +afterAll(async () => { + await Effect.runPromise(Fiber.interrupt(serverFiber)) +}) + +describe("Native Fetch E2E", () => { + describe("GET /streams", () => { + test("returns empty list initially", async () => { + const response = await fetch(`${baseUrl}/streams`) + + expect(response.status).toBe(200) + expect(response.headers.get("content-type")).toContain("application/json") + + const body = await response.json() + expect(body).toEqual({ streams: [] }) + }) + }) + + describe("POST /streams/:name", () => { + test("appends event and returns 201", async () => { + const response = await fetch(`${baseUrl}/streams/fetch-test`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: { message: "hello from fetch" } }) + }) + + expect(response.status).toBe(201) + + const event = await response.json() as StreamEvent + expect(event.offset).toBe("0000000000000000") + expect(event.data).toEqual({ message: "hello from fetch" }) + expect(typeof event.timestamp).toBe("number") + }) + + test("assigns sequential offsets", async () => { + const res1 = await fetch(`${baseUrl}/streams/fetch-sequential`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "event1" }) + }) + const event1 = await res1.json() as StreamEvent + + const res2 = await fetch(`${baseUrl}/streams/fetch-sequential`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "event2" }) + }) + const event2 = await res2.json() as StreamEvent + + expect(event1.offset).toBe("0000000000000000") + expect(event2.offset).toBe("0000000000000001") + }) + + test("returns 400 for invalid JSON", async () => { + const response = await fetch(`${baseUrl}/streams/fetch-invalid`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "not valid json" + }) + + expect(response.status).toBe(400) + }) + }) + + describe("GET /streams/:name (SSE)", () => { + test("returns historical events as SSE", async () => { + // Add some events first + await fetch(`${baseUrl}/streams/fetch-sse`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "sse-event1" }) + }) + await fetch(`${baseUrl}/streams/fetch-sse`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "sse-event2" }) + }) + + // Subscribe to SSE stream + const response = await fetch(`${baseUrl}/streams/fetch-sse`) + + expect(response.status).toBe(200) + expect(response.headers.get("content-type")).toBe("text/event-stream") + + // Read SSE events from stream + const reader = response.body!.getReader() + const decoder = new TextDecoder() + const events: Array = [] + let buffer = "" + + while (events.length < 2) { + const { done, value } = await reader.read() + if (done) break + + buffer += decoder.decode(value, { stream: true }) + + // Parse SSE events from buffer + const lines = buffer.split("\n\n") + buffer = lines.pop() || "" // Keep incomplete chunk + + for (const chunk of lines) { + if (chunk.startsWith("data: ")) { + const json = chunk.slice(6) + events.push(JSON.parse(json)) + } + } + } + + reader.cancel() + + expect(events.length).toBe(2) + expect(events[0]!.data).toBe("sse-event1") + expect(events[1]!.data).toBe("sse-event2") + }) + + test("supports offset query parameter", async () => { + // Add 3 events + await fetch(`${baseUrl}/streams/fetch-offset`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "offset-event0" }) + }) + + const res1 = await fetch(`${baseUrl}/streams/fetch-offset`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "offset-event1" }) + }) + const event1 = await res1.json() as StreamEvent + + await fetch(`${baseUrl}/streams/fetch-offset`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "offset-event2" }) + }) + + // Subscribe starting from event1's offset + const response = await fetch(`${baseUrl}/streams/fetch-offset?offset=${event1.offset}`) + + expect(response.status).toBe(200) + + const reader = response.body!.getReader() + const decoder = new TextDecoder() + const events: Array = [] + let buffer = "" + + while (events.length < 2) { + const { done, value } = await reader.read() + if (done) break + + buffer += decoder.decode(value, { stream: true }) + const lines = buffer.split("\n\n") + buffer = lines.pop() || "" + + for (const chunk of lines) { + if (chunk.startsWith("data: ")) { + events.push(JSON.parse(chunk.slice(6))) + } + } + } + + reader.cancel() + + // Should start from event1, not event0 + expect(events[0]!.data).toBe("offset-event1") + expect(events[1]!.data).toBe("offset-event2") + }) + }) + + describe("DELETE /streams/:name", () => { + test("deletes stream and returns 204", async () => { + // Create stream + await fetch(`${baseUrl}/streams/fetch-delete`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "to be deleted" }) + }) + + // Verify it exists + const listBefore = await fetch(`${baseUrl}/streams`) + const bodyBefore = await listBefore.json() as { streams: Array } + expect(bodyBefore.streams).toContain("fetch-delete") + + // Delete it + const deleteRes = await fetch(`${baseUrl}/streams/fetch-delete`, { + method: "DELETE" + }) + expect(deleteRes.status).toBe(204) + + // Verify it's gone + const listAfter = await fetch(`${baseUrl}/streams`) + const bodyAfter = await listAfter.json() as { streams: Array } + expect(bodyAfter.streams).not.toContain("fetch-delete") + }) + }) + + describe("GET /streams (listing)", () => { + test("lists streams that were created", async () => { + // Create a unique stream for this test + const streamName = `list-test-${Date.now()}` + await fetch(`${baseUrl}/streams/${streamName}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "for listing" }) + }) + + const response = await fetch(`${baseUrl}/streams`) + const body = await response.json() as { streams: Array } + + expect(body.streams).toContain(streamName) + }) + }) +}) diff --git a/src/durable-streams/http-routes.test.ts b/src/durable-streams/http-routes.test.ts new file mode 100644 index 0000000..1ed13a1 --- /dev/null +++ b/src/durable-streams/http-routes.test.ts @@ -0,0 +1,202 @@ +/** + * E2E tests for durable-streams HTTP routes + * + * Uses @effect/platform-node's layerTest for automatic server setup. + * HttpClient is pre-configured with the server's base URL. + */ +import { HttpClient, HttpClientRequest, HttpServer } from "@effect/platform" +import { NodeHttpServer } from "@effect/platform-node" +import { describe, expect, it } from "@effect/vitest" +import { Effect, Layer, Stream } from "effect" +import { durableStreamsRouter } from "./http-routes.ts" +import { StreamManagerService } from "./stream-manager.ts" +import type { StreamEvent } from "./types.ts" + +// Test layer: Node HTTP server + StreamManager + serve the router +const testLayer = Layer.mergeAll( + NodeHttpServer.layerTest, + StreamManagerService.InMemory +).pipe( + Layer.provideMerge(HttpServer.layerContext) +) + +describe("HTTP Routes E2E", () => { + describe("GET /streams", () => { + it.effect("returns empty list initially", () => + Effect.gen(function*() { + yield* HttpServer.serveEffect(durableStreamsRouter) + const client = yield* HttpClient.HttpClient + + const response = yield* client.get("/streams") + expect(response.status).toBe(200) + + const body = yield* response.json + expect(body).toEqual({ streams: [] }) + }).pipe(Effect.scoped, Effect.provide(testLayer))) + }) + + describe("POST /streams/:name", () => { + it.effect("appends event and returns 201", () => + Effect.gen(function*() { + yield* HttpServer.serveEffect(durableStreamsRouter) + const client = yield* HttpClient.HttpClient + + const request = yield* HttpClientRequest.post("/streams/test-stream").pipe( + HttpClientRequest.bodyJson({ data: { message: "hello world" } }) + ) + const response = yield* client.execute(request) + + expect(response.status).toBe(201) + + const event = (yield* response.json) as StreamEvent + expect(event.offset).toBe("0000000000000000") + expect(event.data).toEqual({ message: "hello world" }) + expect(typeof event.timestamp).toBe("number") + }).pipe(Effect.scoped, Effect.provide(testLayer))) + + it.effect("assigns sequential offsets", () => + Effect.gen(function*() { + yield* HttpServer.serveEffect(durableStreamsRouter) + const client = yield* HttpClient.HttpClient + + const req1 = yield* HttpClientRequest.post("/streams/sequential-test").pipe( + HttpClientRequest.bodyJson({ data: "event1" }) + ) + const res1 = yield* client.execute(req1) + const event1 = (yield* res1.json) as StreamEvent + + const req2 = yield* HttpClientRequest.post("/streams/sequential-test").pipe( + HttpClientRequest.bodyJson({ data: "event2" }) + ) + const res2 = yield* client.execute(req2) + const event2 = (yield* res2.json) as StreamEvent + + expect(event1.offset).toBe("0000000000000000") + expect(event2.offset).toBe("0000000000000001") + }).pipe(Effect.scoped, Effect.provide(testLayer))) + + it.effect("returns 400 for invalid JSON", () => + Effect.gen(function*() { + yield* HttpServer.serveEffect(durableStreamsRouter) + const client = yield* HttpClient.HttpClient + + const request = HttpClientRequest.post("/streams/invalid-json-test").pipe( + HttpClientRequest.bodyText("not valid json", "application/json") + ) + const response = yield* client.execute(request) + + expect(response.status).toBe(400) + }).pipe(Effect.scoped, Effect.provide(testLayer))) + }) + + describe("GET /streams/:name (SSE subscription)", () => { + it.effect("returns historical events as SSE", () => + Effect.gen(function*() { + yield* HttpServer.serveEffect(durableStreamsRouter) + const client = yield* HttpClient.HttpClient + + // Add some events first + const req1 = yield* HttpClientRequest.post("/streams/sse-test").pipe( + HttpClientRequest.bodyJson({ data: "event1" }) + ) + yield* client.execute(req1) + + const req2 = yield* HttpClientRequest.post("/streams/sse-test").pipe( + HttpClientRequest.bodyJson({ data: "event2" }) + ) + yield* client.execute(req2) + + // Subscribe and get the stream + const response = yield* client.get("/streams/sse-test") + + expect(response.status).toBe(200) + expect(response.headers["content-type"]).toBe("text/event-stream") + + // Read first 2 events from the SSE stream + const events: Array = [] + + yield* response.stream.pipe( + Stream.decodeText(), + Stream.mapConcat((chunk) => chunk.split("\n\n")), + Stream.filter((line) => line.startsWith("data: ")), + Stream.map((line) => JSON.parse(line.slice(6)) as StreamEvent), + Stream.take(2), + Stream.runForEach((event) => Effect.sync(() => events.push(event))) + ) + + expect(events.length).toBe(2) + expect(events[0]!.data).toBe("event1") + expect(events[1]!.data).toBe("event2") + }).pipe(Effect.scoped, Effect.provide(testLayer))) + + it.effect("supports offset query parameter", () => + Effect.gen(function*() { + yield* HttpServer.serveEffect(durableStreamsRouter) + const client = yield* HttpClient.HttpClient + + // Add 3 events + const req0 = yield* HttpClientRequest.post("/streams/offset-test").pipe( + HttpClientRequest.bodyJson({ data: "event0" }) + ) + yield* client.execute(req0) + + const req1 = yield* HttpClientRequest.post("/streams/offset-test").pipe( + HttpClientRequest.bodyJson({ data: "event1" }) + ) + const res1 = yield* client.execute(req1) + const event1 = (yield* res1.json) as StreamEvent + + const req2 = yield* HttpClientRequest.post("/streams/offset-test").pipe( + HttpClientRequest.bodyJson({ data: "event2" }) + ) + yield* client.execute(req2) + + // Subscribe starting from event1's offset + const response = yield* client.get(`/streams/offset-test?offset=${event1.offset}`) + + expect(response.status).toBe(200) + + const events: Array = [] + yield* response.stream.pipe( + Stream.decodeText(), + Stream.mapConcat((chunk) => chunk.split("\n\n")), + Stream.filter((line) => line.startsWith("data: ")), + Stream.map((line) => JSON.parse(line.slice(6)) as StreamEvent), + Stream.take(2), + Stream.runForEach((event) => Effect.sync(() => events.push(event))) + ) + + // Should start from event1, not event0 + expect(events[0]!.data).toBe("event1") + expect(events[1]!.data).toBe("event2") + }).pipe(Effect.scoped, Effect.provide(testLayer))) + }) + + describe("DELETE /streams/:name", () => { + it.effect("deletes stream and returns 204", () => + Effect.gen(function*() { + yield* HttpServer.serveEffect(durableStreamsRouter) + const client = yield* HttpClient.HttpClient + + // Create stream + const createReq = yield* HttpClientRequest.post("/streams/delete-test").pipe( + HttpClientRequest.bodyJson({ data: "to be deleted" }) + ) + yield* client.execute(createReq) + + // Verify it exists + const listBefore = yield* client.get("/streams") + const bodyBefore = (yield* listBefore.json) as { streams: Array } + expect(bodyBefore.streams).toContain("delete-test") + + // Delete it + const deleteRes = yield* client.del("/streams/delete-test") + expect(deleteRes.status).toBe(204) + + // Verify it's gone + const listAfter = yield* client.get("/streams") + const bodyAfter = (yield* listAfter.json) as { streams: Array } + expect(bodyAfter.streams).not.toContain("delete-test") + }).pipe(Effect.scoped, Effect.provide(testLayer))) + }) +}) diff --git a/src/durable-streams/http-routes.ts b/src/durable-streams/http-routes.ts new file mode 100644 index 0000000..39548e6 --- /dev/null +++ b/src/durable-streams/http-routes.ts @@ -0,0 +1,149 @@ +/** + * HTTP Routes for durable-streams + * + * Endpoints: + * - POST /streams/:name - Append event to stream (JSON body: { data: any }) + * - GET /streams/:name - Subscribe to stream (SSE). Query params: offset + * - GET /streams - List all streams + * - DELETE /streams/:name - Delete stream + */ +import { HttpRouter, HttpServerRequest, HttpServerResponse } from "@effect/platform" +import { Effect, Schema, Stream } from "effect" +import { StreamManagerService } from "./stream-manager.ts" +import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" + +/** Encode a StreamEvent as SSE data line */ +const encodeSSE = (event: StreamEvent): Uint8Array => { + const encoded = Schema.encodeSync(StreamEvent)(event) + return new TextEncoder().encode(`data: ${JSON.stringify(encoded)}\n\n`) +} + +/** Input schema for append */ +const AppendInput = Schema.Struct({ + data: Schema.Unknown +}) + +/** Parse JSON body for append */ +const parseAppendBody = (body: string) => + Effect.gen(function*() { + const json = yield* Effect.try({ + try: () => JSON.parse(body) as unknown, + catch: (e) => new Error(`Invalid JSON: ${e instanceof Error ? e.message : String(e)}`) + }) + return yield* Schema.decodeUnknown(AppendInput)(json) + }) + +/** POST /streams/:name - Append to stream */ +const appendHandler = Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + const manager = yield* StreamManagerService + const params = yield* HttpRouter.params + + const name = params.name + if (!name) { + return HttpServerResponse.text("Missing stream name", { status: 400 }) + } + + const body = yield* request.text + + if (body.trim() === "") { + return HttpServerResponse.text("Empty request body", { status: 400 }) + } + + const parseResult = yield* parseAppendBody(body).pipe(Effect.either) + + if (parseResult._tag === "Left") { + return HttpServerResponse.text(parseResult.left.message, { status: 400 }) + } + + const { data } = parseResult.right + + const event = yield* manager.append({ name: name as StreamName, data }).pipe( + Effect.mapError((e) => new Error(e.message)) + ) + + const encoded = Schema.encodeSync(StreamEvent)(event) + return yield* HttpServerResponse.json(encoded, { status: 201 }) +}) + +/** GET /streams/:name - Subscribe to stream (SSE) */ +const subscribeHandler = Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + const manager = yield* StreamManagerService + const params = yield* HttpRouter.params + + const name = params.name + if (!name) { + return HttpServerResponse.text("Missing stream name", { status: 400 }) + } + + // Parse offset from query string + const url = new URL(request.url, "http://localhost") + const offsetParam = url.searchParams.get("offset") + const offset: Offset | undefined = offsetParam === null + ? undefined + : offsetParam === "-1" + ? OFFSET_START + : offsetParam as Offset + + const eventStreamResult = yield* manager.subscribe({ + name: name as StreamName, + offset + }).pipe(Effect.either) + + if (eventStreamResult._tag === "Left") { + const err = eventStreamResult.left + if (err._tag === "InvalidOffsetError") { + return HttpServerResponse.text(err.message, { status: 400 }) + } + return HttpServerResponse.text(err.message, { status: 500 }) + } + + const eventStream = eventStreamResult.right + + const sseStream = eventStream.pipe(Stream.map(encodeSSE)) + + return HttpServerResponse.stream(sseStream, { + contentType: "text/event-stream", + headers: { + "Cache-Control": "no-cache", + "Connection": "keep-alive" + } + }) +}) + +/** GET /streams - List all streams */ +const listHandler = Effect.gen(function*() { + const manager = yield* StreamManagerService + + const names = yield* manager.list().pipe( + Effect.mapError((e) => new Error(e.message)) + ) + + return yield* HttpServerResponse.json({ streams: names }) +}) + +/** DELETE /streams/:name - Delete stream */ +const deleteHandler = Effect.gen(function*() { + const manager = yield* StreamManagerService + const params = yield* HttpRouter.params + + const name = params.name + if (!name) { + return HttpServerResponse.text("Missing stream name", { status: 400 }) + } + + yield* manager.delete({ name: name as StreamName }).pipe( + Effect.mapError((e) => new Error(e.message)) + ) + + return HttpServerResponse.empty({ status: 204 }) +}) + +/** Durable streams router */ +export const durableStreamsRouter = HttpRouter.empty.pipe( + HttpRouter.post("/streams/:name", appendHandler), + HttpRouter.get("/streams/:name", subscribeHandler), + HttpRouter.get("/streams", listHandler), + HttpRouter.del("/streams/:name", deleteHandler) +) diff --git a/src/durable-streams/index.ts b/src/durable-streams/index.ts new file mode 100644 index 0000000..4432cd8 --- /dev/null +++ b/src/durable-streams/index.ts @@ -0,0 +1,35 @@ +/** + * Durable Streams - Event sourcing primitives with Effect-TS + * + * @module durable-streams + */ + +// Types +export { + InvalidOffsetError, + isStartOffset, + makeOffset, + Offset, + OFFSET_START, + parseOffset, + StorageError, + StreamEvent, + StreamName, + StreamNotFoundError +} from "./types.ts" +export type { Offset as OffsetType, StreamName as StreamNameType } from "./types.ts" + +// Storage +export { Storage } from "./storage.ts" + +// Stream (Layer 0) +export { type DurableStream, makeDurableStream } from "./stream.ts" + +// StreamManager (Layer 1) +export { type StreamManager, StreamManagerService } from "./stream-manager.ts" + +// HTTP Routes (Layer 2) +export { durableStreamsRouter } from "./http-routes.ts" + +// CLI +export { cli, run } from "./cli.ts" diff --git a/src/durable-streams/main.ts b/src/durable-streams/main.ts new file mode 100644 index 0000000..d98ed9e --- /dev/null +++ b/src/durable-streams/main.ts @@ -0,0 +1,18 @@ +/** + * Durable Streams CLI Entry Point + * + * Usage: npx tsx src/durable-streams/main.ts start [--port 3000] + */ +import { NodeContext, NodeRuntime } from "@effect/platform-node" +import { Cause, Effect, Layer, Logger, LogLevel } from "effect" +import { run } from "./cli.ts" + +const loggingLayer = Logger.minimumLogLevel(LogLevel.Info) + +const mainLayer = Layer.mergeAll(loggingLayer, NodeContext.layer) + +run(process.argv).pipe( + Effect.provide(mainLayer), + Effect.catchAllCause((cause) => Cause.isInterruptedOnly(cause) ? Effect.void : Effect.failCause(cause)), + NodeRuntime.runMain +) diff --git a/src/durable-streams/stream-manager.test.ts b/src/durable-streams/stream-manager.test.ts new file mode 100644 index 0000000..b7bdb10 --- /dev/null +++ b/src/durable-streams/stream-manager.test.ts @@ -0,0 +1,203 @@ +/** + * StreamManager tests + */ +import { describe, expect, it } from "@effect/vitest" +import { Effect, Fiber, Stream } from "effect" +import { StreamManagerService } from "./stream-manager.ts" +import { OFFSET_START, type StreamName } from "./types.ts" + +const testStreamName = "test-stream" as StreamName + +describe("StreamManagerService", () => { + describe("getStream", () => { + it.effect("creates stream on first access", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + const stream = yield* manager.getStream({ name: testStreamName }) + + expect(stream.name).toBe(testStreamName) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + + it.effect("returns cached stream on subsequent access", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + const stream1 = yield* manager.getStream({ name: testStreamName }) + const stream2 = yield* manager.getStream({ name: testStreamName }) + + // Should be exact same instance + expect(stream1).toBe(stream2) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + + it.effect("creates separate streams for different names", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + const stream1 = yield* manager.getStream({ name: "stream-a" as StreamName }) + const stream2 = yield* manager.getStream({ name: "stream-b" as StreamName }) + + expect(stream1.name).toBe("stream-a") + expect(stream2.name).toBe("stream-b") + expect(stream1).not.toBe(stream2) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + }) + + describe("append", () => { + it.effect("appends to new stream", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + const event = yield* manager.append({ name: testStreamName, data: { msg: "hello" } }) + + expect(event.data).toEqual({ msg: "hello" }) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + + it.effect("appends to existing stream", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + const event1 = yield* manager.append({ name: testStreamName, data: "first" }) + const event2 = yield* manager.append({ name: testStreamName, data: "second" }) + + expect(event1.offset < event2.offset).toBe(true) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + + it.effect("appends to multiple streams independently", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + yield* manager.append({ name: "stream-a" as StreamName, data: "a1" }) + yield* manager.append({ name: "stream-a" as StreamName, data: "a2" }) + yield* manager.append({ name: "stream-b" as StreamName, data: "b1" }) + + const streamA = yield* manager.getStream({ name: "stream-a" as StreamName }) + const streamB = yield* manager.getStream({ name: "stream-b" as StreamName }) + + const countA = yield* streamA.count + const countB = yield* streamB.count + + expect(countA).toBe(2) + expect(countB).toBe(1) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + }) + + describe("subscribe", () => { + it.effect("subscribes to new stream", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + const eventStream = yield* manager.subscribe({ name: testStreamName }) + const fiber = yield* eventStream.pipe( + Stream.take(2), + Stream.runCollect, + Effect.fork + ) + + yield* manager.append({ name: testStreamName, data: "event1" }) + yield* manager.append({ name: testStreamName, data: "event2" }) + + const events = yield* Fiber.join(fiber).pipe( + Effect.timeout("5 seconds"), + Effect.orDie + ) + + expect(events.length).toBe(2) + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) + + it.effect("subscribes with offset", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + yield* manager.append({ name: testStreamName, data: "event0" }) + const event1 = yield* manager.append({ name: testStreamName, data: "event1" }) + yield* manager.append({ name: testStreamName, data: "event2" }) + + // Subscribe starting from event1's offset + const eventStream = yield* manager.subscribe({ + name: testStreamName, + offset: event1.offset + }) + + // Take 2 from historical (event1 + event2) + const events = yield* eventStream.pipe( + Stream.take(2), + Stream.runCollect + ) + + // events is a Chunk, convert to array for assertions + const arr = Array.from(events) + expect(arr.length).toBe(2) + expect(arr[0]!.data).toBe("event1") + expect(arr[1]!.data).toBe("event2") + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) + + it.effect("subscribes with OFFSET_START", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + yield* manager.append({ name: testStreamName, data: "event0" }) + yield* manager.append({ name: testStreamName, data: "event1" }) + + const eventStream = yield* manager.subscribe({ + name: testStreamName, + offset: OFFSET_START + }) + + const events = yield* eventStream.pipe( + Stream.take(2), + Stream.runCollect + ) + + const arr = Array.from(events) + expect(arr[0]!.data).toBe("event0") + expect(arr[1]!.data).toBe("event1") + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) + }) + + describe("list", () => { + it.effect("returns empty list initially", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + const names = yield* manager.list() + + expect(names).toEqual([]) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + + it.effect("returns stream names after creation", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + yield* manager.append({ name: "stream-a" as StreamName, data: "a" }) + yield* manager.append({ name: "stream-b" as StreamName, data: "b" }) + + const names = yield* manager.list() + + expect(names.length).toBe(2) + expect(names).toContain("stream-a") + expect(names).toContain("stream-b") + }).pipe(Effect.provide(StreamManagerService.InMemory))) + }) + + describe("delete", () => { + it.effect("deletes stream", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + yield* manager.append({ name: testStreamName, data: "event" }) + yield* manager.delete({ name: testStreamName }) + + const names = yield* manager.list() + expect(names).not.toContain(testStreamName) + }).pipe(Effect.provide(StreamManagerService.InMemory))) + + it.effect("creates fresh stream after delete", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + yield* manager.append({ name: testStreamName, data: "before-delete" }) + yield* manager.delete({ name: testStreamName }) + + // New append should create fresh stream + const event = yield* manager.append({ name: testStreamName, data: "after-delete" }) + + // Offset should be 0 (fresh start) + expect(event.offset).toBe("0000000000000000") + }).pipe(Effect.provide(StreamManagerService.InMemory))) + }) +}) diff --git a/src/durable-streams/stream-manager.ts b/src/durable-streams/stream-manager.ts index 3c4a0fc..e4aff9f 100644 --- a/src/durable-streams/stream-manager.ts +++ b/src/durable-streams/stream-manager.ts @@ -4,8 +4,8 @@ * Manages multiple named DurableStreams with lazy initialization. * Each stream is created on first access and cached. */ -import type { Scope } from "effect" -import { Effect, HashMap, Layer, Ref, Stream } from "effect" +import type { Scope, Stream } from "effect" +import { Effect, HashMap, Layer, Ref } from "effect" import { Storage } from "./storage.ts" import { type DurableStream, makeDurableStream } from "./stream.ts" import type { InvalidOffsetError, Offset, StorageError, StreamEvent, StreamName } from "./types.ts" @@ -21,7 +21,7 @@ export interface StreamManager { /** Subscribe to a stream (creates if not exists) */ subscribe(opts: { name: StreamName - offset?: Offset + offset?: Offset | undefined }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> /** List all stream names */ @@ -31,70 +31,120 @@ export interface StreamManager { delete(opts: { name: StreamName }): Effect.Effect } -/** Create StreamManager instance */ -export const makeStreamManager = Effect.gen(function*() { - const storage = yield* Storage - - // Cache of initialized streams (StreamName -> DurableStream) - const streamsRef = yield* Ref.make(HashMap.empty()) - - const getStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - const streams = yield* Ref.get(streamsRef) - const existing = HashMap.get(streams, opts.name) - - if (existing._tag === "Some") { - return existing.value - } - - // Create new stream - const stream = yield* makeDurableStream({ name: opts.name }) - yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) - return stream - }) - - const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.append({ data: opts.data }) - }) - - const subscribe = (opts: { - name: StreamName - offset?: Offset - }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.subscribe({ offset: opts.offset }) - }) - - const list = (): Effect.Effect, StorageError> => storage.list() - - const deleteStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - yield* storage.delete(opts) - yield* Ref.update(streamsRef, HashMap.remove(opts.name)) - }) - - return { - getStream, - append, - subscribe, - list, - delete: deleteStream - } satisfies StreamManager -}) - -/** StreamManager service tag */ +/** StreamManager service tag and layer */ export class StreamManagerService extends Effect.Service()( "@durable-streams/StreamManager", { - effect: makeStreamManager, + effect: Effect.gen(function*() { + const storage = yield* Storage + + // Cache of initialized streams + const streamsRef = yield* Ref.make(HashMap.empty()) + + const getStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + const streams = yield* Ref.get(streamsRef) + const existing = HashMap.get(streams, opts.name) + + if (existing._tag === "Some") { + return existing.value + } + + // Create new stream - provide Storage from closure + const stream = yield* makeDurableStream({ name: opts.name }).pipe( + Effect.provideService(Storage, storage) + ) + yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) + return stream + }) + + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.append({ data: opts.data }) + }) + + const subscribe = (opts: { + name: StreamName + offset?: Offset | undefined + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) + }) + + const list = (): Effect.Effect, StorageError> => storage.list() + + const deleteStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + yield* storage.delete(opts) + yield* Ref.update(streamsRef, HashMap.remove(opts.name)) + }) + + return { + getStream, + append, + subscribe, + list, + delete: deleteStream + } satisfies StreamManager + }), dependencies: [Storage.Default] } ) { static readonly InMemory: Layer.Layer = Layer.effect( StreamManagerService, - makeStreamManager + Effect.gen(function*() { + const storage = yield* Storage + + const streamsRef = yield* Ref.make(HashMap.empty()) + + const getStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + const streams = yield* Ref.get(streamsRef) + const existing = HashMap.get(streams, opts.name) + + if (existing._tag === "Some") { + return existing.value + } + + const stream = yield* makeDurableStream({ name: opts.name }).pipe( + Effect.provideService(Storage, storage) + ) + yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) + return stream + }) + + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.append({ data: opts.data }) + }) + + const subscribe = (opts: { + name: StreamName + offset?: Offset | undefined + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) + }) + + const list = (): Effect.Effect, StorageError> => storage.list() + + const deleteStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + yield* storage.delete(opts) + yield* Ref.update(streamsRef, HashMap.remove(opts.name)) + }) + + return { + getStream, + append, + subscribe, + list, + delete: deleteStream + } as unknown as StreamManagerService + }) ).pipe(Layer.provide(Storage.InMemory)) } diff --git a/src/durable-streams/stream.ts b/src/durable-streams/stream.ts index 749bc95..4274c39 100644 --- a/src/durable-streams/stream.ts +++ b/src/durable-streams/stream.ts @@ -7,15 +7,16 @@ import type { Scope } from "effect" import { Effect, PubSub, Ref, Stream } from "effect" import { Storage } from "./storage.ts" +// eslint-disable-next-line @typescript-eslint/consistent-type-imports import { InvalidOffsetError, isStartOffset, makeOffset, + type Offset, OFFSET_START, parseOffset, StorageError, StreamEvent, - type Offset, type StreamName } from "./types.ts" From 46fd3347cb176d0d650a585596bf1f2d7e782226 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:17:37 +0000 Subject: [PATCH 03/15] Switch from Bun to pnpm/Node.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove bun.lock, add pnpm-lock.yaml - Update scripts to use pnpm instead of npm/bun - Remove @effect/platform-bun dependency - Update CLAUDE.md docs with pnpm commands πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 18 +- bun.lock | 1253 ------------ package.json | 5 +- pnpm-lock.yaml | 5330 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 5341 insertions(+), 1265 deletions(-) delete mode 100644 bun.lock create mode 100644 pnpm-lock.yaml diff --git a/CLAUDE.md b/CLAUDE.md index f9bc1df..04c85fa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,8 +15,8 @@ See README.md for context # Typescript -- Use bun as runtime and package manager -- Run CLI using `bun run mini-agent` (includes doppler for env vars) +- Use Node.js as runtime, pnpm as package manager +- Run CLI using `pnpm mini-agent` (includes doppler for env vars) - kebab-case filenames - tests using vitest; colocate test files with .test.ts - import using .ts extension; no .js @@ -27,18 +27,18 @@ See README.md for context ## Scripts -- `bun run typecheck` β€” tsc only -- `bun run lint` / `bun run lint:fix` β€” eslint only -- `bun run check` β€” typecheck + lint -- `bun run check:fix` β€” typecheck + lint:fix -- `doppler run -- bun run test` β€” vitest (requires Doppler for API keys) -- `doppler run -- bun run test:watch` β€” vitest watch mode +- `pnpm typecheck` β€” tsc only +- `pnpm lint` / `pnpm lint:fix` β€” eslint only +- `pnpm check` β€” typecheck + lint +- `pnpm check:fix` β€” typecheck + lint:fix +- `pnpm test` β€” vitest (requires Doppler for API keys) +- `pnpm test:watch` β€” vitest watch mode ## Pull Requests Before committing and pushing code, you must run: ```bash -bun run check:fix +pnpm check:fix ``` This runs typecheck + linter with auto-fix. Commit any resulting changes before pushing. diff --git a/bun.lock b/bun.lock deleted file mode 100644 index 6ac1490..0000000 --- a/bun.lock +++ /dev/null @@ -1,1253 +0,0 @@ -{ - "lockfileVersion": 1, - "configVersion": 1, - "workspaces": { - "": { - "name": "2025-11-30-effect-cli", - "dependencies": { - "@effect/ai": "^0.32.1", - "@effect/ai-anthropic": "^0.22.0", - "@effect/ai-google": "^0.11.0", - "@effect/ai-openai": "^0.35.0", - "@effect/cli": "^0.72.1", - "@effect/opentelemetry": "^0.59.1", - "@effect/platform": "^0.93.5", - "@effect/platform-bun": "^0.85.0", - "@effect/platform-node": "^0.104.0", - "@effect/rpc": "^0.72.2", - "@effect/rpc-http": "^0.52.4", - "@opentelemetry/otlp-transformer": "^0.208.0", - "@opentelemetry/sdk-trace-base": "^2.2.0", - "@opentui/core": "^0.1.55", - "@opentui/react": "^0.1.55", - "effect": "^3.19.8", - "react": "19", - "react-dom": "19", - "yaml": "^2.7.0", - }, - "devDependencies": { - "@effect/eslint-plugin": "^0.3.2", - "@effect/language-service": "^0.57.1", - "@effect/vitest": "^0.25.1", - "@eslint/compat": "^1.1.1", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.10.0", - "@types/node": "^22", - "@types/react": "19", - "@typescript-eslint/eslint-plugin": "^8.4.0", - "@typescript-eslint/parser": "^8.4.0", - "eslint": "^9.10.0", - "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-import": "^2.30.0", - "eslint-plugin-simple-import-sort": "^12.1.1", - "eslint-plugin-sort-destructure-keys": "^2.0.0", - "semver": "^7.7.3", - "tsx": "^4.19.0", - "tuistory": "^0.0.2", - "vitest": "^3.2.0", - }, - "peerDependencies": { - "typescript": "^5", - }, - }, - }, - "packages": { - "@anthropic-ai/tokenizer": ["@anthropic-ai/tokenizer@0.0.4", "", { "dependencies": { "@types/node": "^18.11.18", "tiktoken": "^1.0.10" } }, "sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g=="], - - "@dimforge/rapier2d-simd-compat": ["@dimforge/rapier2d-simd-compat@0.17.3", "", {}, "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg=="], - - "@dprint/formatter": ["@dprint/formatter@0.4.1", "", {}, "sha512-IB/GXdlMOvi0UhQQ9mcY15Fxcrc2JPadmo6tqefCNV0bptFq7YBpggzpqYXldBXDa04CbKJ+rDwO2eNRPE2+/g=="], - - "@dprint/typescript": ["@dprint/typescript@0.91.8", "", {}, "sha512-tuKn4leCPItox1O4uunHcQF0QllDCvPWklnNQIh2PiWWVtRAGltJJnM4Cwj5AciplosD1Hiz7vAY3ew3crLb3A=="], - - "@effect/ai": ["@effect/ai@0.32.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6" }, "peerDependencies": { "@effect/experimental": "^0.57.0", "@effect/platform": "^0.93.0", "@effect/rpc": "^0.72.1", "effect": "^3.19.3" } }, "sha512-6qedQhnHigkRjNsUGaXKqvsbwhvQH3Ez3aePeROBRJi0c8q9tnR1E8qhQrTSjVPWcrv9AFtP0NRTr6iAHujGWQ=="], - - "@effect/ai-anthropic": ["@effect/ai-anthropic@0.22.0", "", { "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4" }, "peerDependencies": { "@effect/ai": "^0.32.0", "@effect/experimental": "^0.57.0", "@effect/platform": "^0.93.0", "effect": "^3.19.0" } }, "sha512-uzgXeC8iAMSVtyFi/HxYPQGBcCBxzbgXKt/wxeyB1FlQGx4Uxn1tBy1dhBc2RvbVRYlT8MIPOCxLcUj1/995zg=="], - - "@effect/ai-google": ["@effect/ai-google@0.11.0", "", { "peerDependencies": { "@effect/ai": "^0.32.0", "@effect/experimental": "^0.57.0", "@effect/platform": "^0.93.0", "effect": "^3.19.0" } }, "sha512-OhUXYj0xQvW+NUJje9rBgbyzlNaqU+2XRQHY6FQMAz6nv+SLjGUysFZYN54B1SOHVDsaezqCL3HDQGx5+k8c9Q=="], - - "@effect/ai-openai": ["@effect/ai-openai@0.35.0", "", { "dependencies": { "gpt-tokenizer": "^2.9.0" }, "peerDependencies": { "@effect/ai": "^0.32.0", "@effect/experimental": "^0.57.0", "@effect/platform": "^0.93.0", "effect": "^3.19.0" } }, "sha512-cB5cXITMsO+bfesIDqJRCiAGH8r/P5ts32HwZ5HbAvUscrqFgZBQDIsypiOCHL2teWFaBbXLdiqbF/lL4Ct8wg=="], - - "@effect/cli": ["@effect/cli@0.72.1", "", { "dependencies": { "ini": "^4.1.3", "toml": "^3.0.0", "yaml": "^2.5.0" }, "peerDependencies": { "@effect/platform": "^0.93.0", "@effect/printer": "^0.47.0", "@effect/printer-ansi": "^0.47.0", "effect": "^3.19.3" } }, "sha512-HGDMGD23TxFW9tCSX6g+M2u0robikMA0mP0SqeJMj7FWXTdcQ+cQsJE99bxi9iu+5YID7MIrVJMs8TUwXUV2sg=="], - - "@effect/cluster": ["@effect/cluster@0.54.0", "", { "dependencies": { "kubernetes-types": "^1.30.0" }, "peerDependencies": { "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "@effect/workflow": "^0.14.0", "effect": "^3.19.8" } }, "sha512-Hpxn+z1NaDIN+USM3ml7lp3AQtQHN/VRUfiTzyRJp6nVdUVZRylz8PggEq+O9E1TIeG+TXHtv7/Jq7ME2Ahbdg=="], - - "@effect/eslint-plugin": ["@effect/eslint-plugin@0.3.2", "", { "dependencies": { "@dprint/formatter": "^0.4.1", "@dprint/typescript": "^0.91.3", "prettier-linter-helpers": "^1.0.0" } }, "sha512-c4Vs9t3r54A4Zpl+wo8+PGzZz3JWYsip41H+UrebRLjQ2Hk/ap63IeCgN/HWcYtxtyhRopjp7gW9nOQ2Snbl+g=="], - - "@effect/experimental": ["@effect/experimental@0.57.8", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.93.5", "effect": "^3.19.8", "ioredis": "^5", "lmdb": "^3" }, "optionalPeers": ["ioredis", "lmdb"] }, "sha512-3NYIH+ujSR+aBiQ5KqG8RviIdSWORjQUVQbLq8KSTT/DExRgsa8Sw+8IWiGeaaneVQd/PBpNnNqvncxhv+JGHw=="], - - "@effect/language-service": ["@effect/language-service@0.57.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-uWzYN+aHl4KfZHGmDxa3+OjV/mk9gMUycIyB8SYvyDiny3lXBtmRFdRVnw6TYL1P5EBfg4N09+lO/0ECRREVXQ=="], - - "@effect/opentelemetry": ["@effect/opentelemetry@0.59.1", "", { "peerDependencies": { "@effect/platform": "^0.93.1", "@opentelemetry/api": "^1.9", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": "^0.203.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^3.19.3" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-mG+duN2KLJR6lDwuxEyoZqe5/4aSVnK50MoY87AC/u3hQGiSHxP7tZIORUghKd6JEEODJb4zP8eo7wmW7EGTuw=="], - - "@effect/platform": ["@effect/platform@0.93.5", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.8" } }, "sha512-31ikgJRAG8py26SEIjyrgaPlEJ+JYqOsGP7g4WHFRIYxGFwFX/OrlBrW0+mRISCfeDR1BUztaFyIZyRPfBRvcw=="], - - "@effect/platform-bun": ["@effect/platform-bun@0.85.0", "", { "dependencies": { "@effect/platform-node-shared": "^0.55.0", "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/cluster": "^0.54.0", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "effect": "^3.19.8" } }, "sha512-olIFcq2tNow4jRhLNQuuTgcts7qFICvL4z7kI0YoFzvHvXwH+R87b+iTa/5jR7h6KuckapqgIyf1LX5IN2Jj6w=="], - - "@effect/platform-node": ["@effect/platform-node@0.104.0", "", { "dependencies": { "@effect/platform-node-shared": "^0.57.0", "mime": "^3.0.0", "undici": "^7.10.0", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.56.0", "@effect/platform": "^0.94.0", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "effect": "^3.19.13" } }, "sha512-2ZkUDDTxLD95ARdYIKBx4tdIIgqA3cwb3jlnVVBxmHUf0Pg5N2HdMuD0Q+CXQ7Q94FDwnLW3ZvaSfxDh6FvrNw=="], - - "@effect/platform-node-shared": ["@effect/platform-node-shared@0.55.0", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "multipasta": "^0.2.7", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.54.0", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "effect": "^3.19.8" } }, "sha512-rmeb3bnt6CKIC+QSyDY0DBa46XQMWRzY3RAsr9MUbxGbPmk1fY8kX0R9rZ8wd1ymi7RlAYXfwE8KvJG2WI2NXA=="], - - "@effect/printer": ["@effect/printer@0.47.0", "", { "peerDependencies": { "@effect/typeclass": "^0.38.0", "effect": "^3.19.0" } }, "sha512-VgR8e+YWWhMEAh9qFOjwiZ3OXluAbcVLIOtvp2S5di1nSrPOZxj78g8LE77JSvyfp5y5bS2gmFW+G7xD5uU+2Q=="], - - "@effect/printer-ansi": ["@effect/printer-ansi@0.47.0", "", { "dependencies": { "@effect/printer": "^0.47.0" }, "peerDependencies": { "@effect/typeclass": "^0.38.0", "effect": "^3.19.0" } }, "sha512-tDEQ9XJpXDNYoWMQJHFRMxKGmEOu6z32x3Kb8YLOV5nkauEKnKmWNs7NBp8iio/pqoJbaSwqDwUg9jXVquxfWQ=="], - - "@effect/rpc": ["@effect/rpc@0.72.2", "", { "dependencies": { "msgpackr": "^1.11.4" }, "peerDependencies": { "@effect/platform": "^0.93.3", "effect": "^3.19.5" } }, "sha512-BmTXybXCOq96D2r9mvSW/YdiTQs5CStnd4II+lfVKrMr3pMNERKLZ2LG37Tfm4Sy3Q8ire6IVVKO/CN+VR0uQQ=="], - - "@effect/rpc-http": ["@effect/rpc-http@0.52.4", "", { "dependencies": { "@effect/rpc": "^0.54.4" }, "peerDependencies": { "@effect/platform": "^0.79.4", "effect": "^3.13.12" } }, "sha512-QpIaTlqo5zYO+ujFqMMv3g/9y4CByuez7taMFCL+2mihlDCKt9s+vIEbxQYzn1r0/ppDYfZ/d5vgEJBpddcgxg=="], - - "@effect/sql": ["@effect/sql@0.48.5", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.57.7", "@effect/platform": "^0.93.5", "effect": "^3.19.8" } }, "sha512-OhmqA4zaajZc5F4M30W/5UjnjnhW+Z3MIbdTn6KihYOywXG+uSD2RVDhW4HiMWlysD4jv2mapbYSzi+IowYqOg=="], - - "@effect/typeclass": ["@effect/typeclass@0.38.0", "", { "peerDependencies": { "effect": "^3.19.0" } }, "sha512-lMUcJTRtG8KXhXoczapZDxbLK5os7M6rn0zkvOgncJW++A0UyelZfMVMKdT5R+fgpZcsAU/1diaqw3uqLJwGxA=="], - - "@effect/vitest": ["@effect/vitest@0.25.1", "", { "peerDependencies": { "effect": "^3.17.7", "vitest": "^3.2.0" } }, "sha512-OMYvOU8iGed8GZXxgVBXlYtjG+jwWj5cJxFk0hOHOfTbCHXtdCMEWlXNba5zxbE7dBnW4srbnSYrP/NGGTC3qQ=="], - - "@effect/workflow": ["@effect/workflow@0.14.0", "", { "peerDependencies": { "@effect/experimental": "^0.57.7", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "effect": "^3.19.8" } }, "sha512-NPJqScwqe0szAl9IOYNIYnTR7cN00UiMWgOlLD6500ejnjgsaXYnZIWSQZHK54zkOMfsAVVyfuZ0wgVq/K2zMw=="], - - "@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], - - "@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], - - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="], - - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="], - - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="], - - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], - - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], - - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], - - "@eslint/compat": ["@eslint/compat@1.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0" }, "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w=="], - - "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], - - "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], - - "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], - - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], - - "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="], - - "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], - - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], - - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], - - "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], - - "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], - - "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - - "@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", "file-type": "^16.0.0", "mime": "3" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="], - - "@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "^5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="], - - "@jimp/file-ops": ["@jimp/file-ops@1.6.0", "", {}, "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ=="], - - "@jimp/js-bmp": ["@jimp/js-bmp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "bmp-ts": "^1.0.9" } }, "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw=="], - - "@jimp/js-gif": ["@jimp/js-gif@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "gifwrap": "^0.10.1", "omggif": "^1.0.10" } }, "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g=="], - - "@jimp/js-jpeg": ["@jimp/js-jpeg@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "jpeg-js": "^0.4.4" } }, "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA=="], - - "@jimp/js-png": ["@jimp/js-png@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "pngjs": "^7.0.0" } }, "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg=="], - - "@jimp/js-tiff": ["@jimp/js-tiff@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "utif2": "^4.1.0" } }, "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw=="], - - "@jimp/plugin-blit": ["@jimp/plugin-blit@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA=="], - - "@jimp/plugin-blur": ["@jimp/plugin-blur@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw=="], - - "@jimp/plugin-circle": ["@jimp/plugin-circle@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw=="], - - "@jimp/plugin-color": ["@jimp/plugin-color@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "tinycolor2": "^1.6.0", "zod": "^3.23.8" } }, "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA=="], - - "@jimp/plugin-contain": ["@jimp/plugin-contain@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ=="], - - "@jimp/plugin-cover": ["@jimp/plugin-cover@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA=="], - - "@jimp/plugin-crop": ["@jimp/plugin-crop@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang=="], - - "@jimp/plugin-displace": ["@jimp/plugin-displace@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q=="], - - "@jimp/plugin-dither": ["@jimp/plugin-dither@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0" } }, "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ=="], - - "@jimp/plugin-fisheye": ["@jimp/plugin-fisheye@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA=="], - - "@jimp/plugin-flip": ["@jimp/plugin-flip@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg=="], - - "@jimp/plugin-hash": ["@jimp/plugin-hash@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "any-base": "^1.1.0" } }, "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q=="], - - "@jimp/plugin-mask": ["@jimp/plugin-mask@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA=="], - - "@jimp/plugin-print": ["@jimp/plugin-print@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/types": "1.6.0", "parse-bmfont-ascii": "^1.0.6", "parse-bmfont-binary": "^1.0.6", "parse-bmfont-xml": "^1.1.6", "simple-xml-to-json": "^1.2.2", "zod": "^3.23.8" } }, "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A=="], - - "@jimp/plugin-quantize": ["@jimp/plugin-quantize@1.6.0", "", { "dependencies": { "image-q": "^4.0.0", "zod": "^3.23.8" } }, "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg=="], - - "@jimp/plugin-resize": ["@jimp/plugin-resize@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA=="], - - "@jimp/plugin-rotate": ["@jimp/plugin-rotate@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw=="], - - "@jimp/plugin-threshold": ["@jimp/plugin-threshold@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w=="], - - "@jimp/types": ["@jimp/types@1.6.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg=="], - - "@jimp/utils": ["@jimp/utils@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "tinycolor2": "^1.6.0" } }, "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - - "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], - - "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], - - "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="], - - "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="], - - "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="], - - "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="], - - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], - - "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], - - "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - - "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg=="], - - "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.2.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ=="], - - "@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="], - - "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="], - - "@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], - - "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.57.2", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.2", "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg=="], - - "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog=="], - - "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="], - - "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.2.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.2.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-+OaRja3f0IqGG2kptVeYsrZQK9nKRSpfFrKtRBq4uh6nIB8bTBgaGvYQrQoRrQWQMA5dK5yLhDMDc0dvYvCOIQ=="], - - "@opentelemetry/sdk-trace-web": ["@opentelemetry/sdk-trace-web@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-x/LHsDBO3kfqaFx5qSzBljJ5QHsRXrvS4MybBDy1k7Svidb8ZyIPudWVzj3s5LpPkYZIgi9e+7tdsNCnptoelw=="], - - "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.38.0", "", {}, "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg=="], - - "@opentui/core": ["@opentui/core@0.1.57", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.57", "@opentui/core-darwin-x64": "0.1.57", "@opentui/core-linux-arm64": "0.1.57", "@opentui/core-linux-x64": "0.1.57", "@opentui/core-win32-arm64": "0.1.57", "@opentui/core-win32-x64": "0.1.57", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-VKfCRo8CICXlM5nsJvf+IFFubLX/cQGDnSPTVKsZUy58eVw3K1swY3B8dqOX1OssG4Fv80QrJGcBTzJCuq9dtQ=="], - - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.57", "", { "os": "darwin", "cpu": "arm64" }, "sha512-by+Pvh5aKw13zSuNbwQKAthrlCpdI7eU8HuIEN/PPQdHuQivtgxkMn9jgLEwIMkOnrvZ8SqdFb392zZRgSCNDg=="], - - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.57", "", { "os": "darwin", "cpu": "x64" }, "sha512-Upcem+gU4I2/znG7IoR3l7VklZTmhxD2E4QV98sY/FAz4blD+ng/n4M3fCj+M03ZnSkoulELkPzWXH0AwIkcyw=="], - - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.57", "", { "os": "linux", "cpu": "arm64" }, "sha512-0YAqzuKNLEm+NBQVSni1/pd8960Ybf8LMw5Ead5m4BNtPzYQ5QkdUEAIhsdViQFnFlCPo9AI12oRBzurGMgSmw=="], - - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.57", "", { "os": "linux", "cpu": "x64" }, "sha512-2JTGolyVbuKY+DcE7V15DQwjOLygCHFgFzYwrsLCGxoaEgnEAcocWxAF0CdSlvx01/eY4u8uspzqOT94XTUJRQ=="], - - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.57", "", { "os": "win32", "cpu": "arm64" }, "sha512-HPT9fPzOPaLZ2P35Ed6N1icWDgLHpzxnqKsBfDG3bZq5ClhfHGuXm00JmekxauIhR6zTTYd2WSfMCB2qpCufrw=="], - - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.57", "", { "os": "win32", "cpu": "x64" }, "sha512-Q7PcfqQjlz3idOBns3KbKMrTm15Zx4SMtReNX8BTbVP1UsLDAtIif+1JqjPx1YFRq5WCCsuRAggJaUEmQp5Nqw=="], - - "@opentui/react": ["@opentui/react@0.1.57", "", { "dependencies": { "@opentui/core": "0.1.57", "react-reconciler": "^0.32.0" }, "peerDependencies": { "react": ">=19.0.0" } }, "sha512-8tu2drY/2aRYxPFTDRVKq0x2TKs7uvdjAgi5xXrYn8xk/+j5wmcVr1bF8+VanLMHkTu8zHAHuxcrFh5yD+KSng=="], - - "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], - - "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], - - "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="], - - "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="], - - "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="], - - "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="], - - "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="], - - "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="], - - "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="], - - "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="], - - "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="], - - "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="], - - "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="], - - "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], - - "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], - - "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], - - "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], - - "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], - - "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], - - "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], - - "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], - - "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], - - "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], - - "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], - - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="], - - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="], - - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="], - - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="], - - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="], - - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="], - - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="], - - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="], - - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="], - - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="], - - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="], - - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="], - - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="], - - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="], - - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="], - - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="], - - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="], - - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="], - - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="], - - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="], - - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="], - - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="], - - "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], - - "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], - - "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], - - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - - "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], - - "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], - - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - - "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - - "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], - - "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], - - "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], - - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.48.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/type-utils": "8.48.1", "@typescript-eslint/utils": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.48.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA=="], - - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.48.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA=="], - - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.48.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.48.1", "@typescript-eslint/types": "^8.48.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w=="], - - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.48.1", "", { "dependencies": { "@typescript-eslint/types": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1" } }, "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w=="], - - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.48.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw=="], - - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.48.1", "", { "dependencies": { "@typescript-eslint/types": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1", "@typescript-eslint/utils": "8.48.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg=="], - - "@typescript-eslint/types": ["@typescript-eslint/types@8.48.1", "", {}, "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q=="], - - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.48.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.48.1", "@typescript-eslint/tsconfig-utils": "8.48.1", "@typescript-eslint/types": "8.48.1", "@typescript-eslint/visitor-keys": "8.48.1", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg=="], - - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.48.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", "@typescript-eslint/typescript-estree": "8.48.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA=="], - - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.48.1", "", { "dependencies": { "@typescript-eslint/types": "8.48.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q=="], - - "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], - - "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], - - "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="], - - "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="], - - "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="], - - "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="], - - "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="], - - "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="], - - "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="], - - "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="], - - "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="], - - "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="], - - "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="], - - "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="], - - "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="], - - "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="], - - "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="], - - "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="], - - "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], - - "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], - - "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], - - "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@webgpu/types": ["@webgpu/types@0.1.67", "", {}, "sha512-uk53+2ECGUkWoDFez/hymwpRfdgdIn6y1ref70fEecGMe5607f4sozNFgBk0oxlr7j2CRGWBEc3IBYMmFdGGTQ=="], - - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], - - "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "any-base": ["any-base@1.1.0", "", {}, "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="], - - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], - - "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], - - "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], - - "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], - - "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], - - "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], - - "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], - - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - - "await-to-js": ["await-to-js@3.0.0", "", {}, "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - - "bmp-ts": ["bmp-ts@1.0.9", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="], - - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - - "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - - "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="], - - "bun-pty": ["bun-pty@0.4.2", "", {}, "sha512-sHImDz6pJDsHAroYpC9ouKVgOyqZ7FP3N+stX5IdMddHve3rf9LIZBDomQcXrACQ7sQDNuwZQHG8BKR7w8krkQ=="], - - "bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="], - - "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eDgLN9teKTfmvrCqgwwmWNsNszxYs7IZdCqk0S1DCarvMhr4wcajoSBlA/nQA0/owwLduPTS8xxCnQp4/N/gDg=="], - - "bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-X+PjwJUWenUmdQBP8EtdItMyieQ6Nlpn+BH518oaouDiSnWj5+b0Y7DNDZJq7Ezom4EaxmqL/uGYZK3aCQ7CXg=="], - - "bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-zMLs2YIGB+/jxrYFXaFhVKX/GBt05UTF45lc9srcHc9JXGjEj+12CIo1CHLTAWatXMTqt0Jsu6ukWEoWVT/ayA=="], - - "bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-Z5yAK28xrcm8Wb5k7TZ8FJKpOI/r+aVCRdlHYAqI2SDJFN3nD4mJs900X6kNVmG/xFzb5yOuKVYWGg+6ZXWbyA=="], - - "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - - "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - - "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], - - "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], - - "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], - - "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], - - "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], - - "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], - - "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], - - "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], - - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - - "effect": ["effect@3.19.8", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-OmLw8EfH02vdmyU2fO4uY9He/wepwKI5E/JNpE2pseaWWUbaYOK9UlxIiKP20ZEqQr+S/jSqRDGmpiqD/2DeCQ=="], - - "es-abstract": ["es-abstract@1.24.0", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg=="], - - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], - - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - - "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - - "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], - - "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - - "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], - - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - - "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="], - - "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], - - "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], - - "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="], - - "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], - - "eslint-plugin-simple-import-sort": ["eslint-plugin-simple-import-sort@12.1.1", "", { "peerDependencies": { "eslint": ">=5.0.0" } }, "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA=="], - - "eslint-plugin-sort-destructure-keys": ["eslint-plugin-sort-destructure-keys@2.0.0", "", { "dependencies": { "natural-compare-lite": "^1.4.0" }, "peerDependencies": { "eslint": "5 - 9" } }, "sha512-4w1UQCa3o/YdfWaLr9jY8LfGowwjwjmwClyFLxIsToiyIdZMq3x9Ti44nDn34DtTPP7PWg96tUONKVmATKhYGQ=="], - - "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], - - "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], - - "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], - - "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], - - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], - - "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - - "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], - - "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - - "exif-parser": ["exif-parser@0.1.12", "", {}, "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="], - - "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], - - "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], - - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - - "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - - "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], - - "file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="], - - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - - "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="], - - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], - - "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - - "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - - "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], - - "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], - - "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], - - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - - "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], - - "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], - - "ghostty-opentui": ["ghostty-opentui@1.3.3", "", { "dependencies": { "strip-ansi": "^7.1.2" }, "peerDependencies": { "@opentui/core": "*" }, "optionalPeers": ["@opentui/core"] }, "sha512-j8LfHbUhCGxiw2YEFhPQ1IZzXisPgIwsm6/fzmXBkoSo3g9dszMoCXYfOdIJqxEVkcZ/7KVkaUTBkcga2qBkOw=="], - - "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="], - - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - - "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], - - "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], - - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - - "gpt-tokenizer": ["gpt-tokenizer@2.9.0", "", {}, "sha512-YSpexBL/k4bfliAzMrRqn3M6+it02LutVyhVpDeMKrC/O9+pCe/5s8U2hYKa2vFLD5/vHhsKc8sOn/qGqII8Kg=="], - - "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], - - "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], - - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], - - "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - - "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - - "image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="], - - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - - "ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="], - - "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], - - "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], - - "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], - - "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], - - "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], - - "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], - - "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], - - "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], - - "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], - - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - - "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], - - "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], - - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - - "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], - - "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], - - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - - "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], - - "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], - - "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], - - "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], - - "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], - - "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], - - "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], - - "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], - - "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], - - "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], - - "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "jimp": ["jimp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/diff": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-gif": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-blur": "1.6.0", "@jimp/plugin-circle": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-contain": "1.6.0", "@jimp/plugin-cover": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-displace": "1.6.0", "@jimp/plugin-dither": "1.6.0", "@jimp/plugin-fisheye": "1.6.0", "@jimp/plugin-flip": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/plugin-mask": "1.6.0", "@jimp/plugin-print": "1.6.0", "@jimp/plugin-quantize": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/plugin-rotate": "1.6.0", "@jimp/plugin-threshold": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg=="], - - "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="], - - "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - - "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], - - "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - - "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - - "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], - - "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - - "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], - - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - - "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], - - "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - - "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], - - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "msgpackr": ["msgpackr@1.11.5", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA=="], - - "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="], - - "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], - - "nan": ["nan@2.24.0", "", {}, "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg=="], - - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], - - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - - "natural-compare-lite": ["natural-compare-lite@1.4.0", "", {}, "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g=="], - - "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], - - "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], - - "node-pty": ["node-pty@1.0.0", "", { "dependencies": { "nan": "^2.17.0" } }, "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA=="], - - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - - "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], - - "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], - - "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], - - "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], - - "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], - - "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="], - - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - - "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], - - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - - "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], - - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - - "parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="], - - "parse-bmfont-binary": ["parse-bmfont-binary@1.0.6", "", {}, "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="], - - "parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "^1.0.0", "xml2js": "^0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="], - - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - - "peek-readable": ["peek-readable@4.1.0", "", {}, "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "^6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="], - - "planck": ["planck@1.4.2", "", { "peerDependencies": { "stage-js": "^1.0.0-alpha.12" } }, "sha512-mNbhnV3g8X2rwGxzcesjmN8BDA6qfXgQxXVMkWau9MCRlQY0RLNEkyHlVp6yFy/X6qrzAXyNONCnZ1cGDLrNew=="], - - "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="], - - "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - - "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], - - "prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="], - - "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], - - "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], - - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - - "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], - - "react": ["react@19.2.1", "", {}, "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw=="], - - "react-dom": ["react-dom@19.2.1", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.1" } }, "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg=="], - - "react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="], - - "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], - - "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "^4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="], - - "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], - - "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], - - "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - - "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], - - "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], - - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], - - "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], - - "sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], - - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - - "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - - "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], - - "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], - - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], - - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], - - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - - "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], - - "simple-xml-to-json": ["simple-xml-to-json@1.2.3", "", {}, "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], - - "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], - - "stage-js": ["stage-js@1.0.0-alpha.17", "", {}, "sha512-AzlMO+t51v6cFvKZ+Oe9DJnL1OXEH5s9bEy6di5aOrUpcP7PCzI/wIeXF0u3zg0L89gwnceoKxrLId0ZpYnNXw=="], - - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], - - "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], - - "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], - - "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], - - "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], - - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - - "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - - "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], - - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - - "three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="], - - "tiktoken": ["tiktoken@1.0.22", "", {}, "sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA=="], - - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - - "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="], - - "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - - "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - - "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - - "token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="], - - "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], - - "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], - - "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], - - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], - - "tuistory": ["tuistory@0.0.2", "", { "dependencies": { "ghostty-opentui": "^1.3.3" }, "optionalDependencies": { "bun-pty": "*", "node-pty": "^1.0.0" } }, "sha512-14FfFhL+s3Ai+XybzuYeygw7NgBhxk01S7DCfYHtMqy3Si5lkvJLNZdJEFVuGnbtBZDXpfxeGaE9HzJaAjITEg=="], - - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - - "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], - - "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], - - "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], - - "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], - - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - - "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], - - "undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="], - - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], - - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - - "utif2": ["utif2@4.1.0", "", { "dependencies": { "pako": "^1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="], - - "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], - - "vite": ["vite@7.2.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ=="], - - "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - - "web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="], - - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], - - "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], - - "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], - - "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], - - "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], - - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - - "xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="], - - "xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="], - - "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], - - "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], - - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - - "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - - "@anthropic-ai/tokenizer/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], - - "@effect/platform-node/@effect/platform-node-shared": ["@effect/platform-node-shared@0.57.0", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "multipasta": "^0.2.7", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.56.0", "@effect/platform": "^0.94.0", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "effect": "^3.19.13" } }, "sha512-QXuvmLNlABCQLcTl+lN1YPhKosR6KqArPYjC2reU0fb5lroCo3YRb/aGpXIgLthHzQL8cLU5XMGA3Cu5hKY2Tw=="], - - "@effect/rpc-http/@effect/rpc": ["@effect/rpc@0.54.4", "", { "peerDependencies": { "@effect/platform": "^0.79.4", "effect": "^3.13.12" } }, "sha512-iu3TGWCt4OMH8iKL1ATeROhAxrMF+HdF3NbR5lWls9yWJwBgVU+cps3ZzRbNQhFPWXDGqVuYgmYNY1GKbZgMaw=="], - - "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - - "@opentelemetry/otlp-transformer/@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="], - - "@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw=="], - - "@opentelemetry/sdk-logs/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.57.2", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A=="], - - "@opentelemetry/sdk-logs/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], - - "@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], - - "@opentelemetry/sdk-metrics/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], - - "@opentelemetry/sdk-metrics/@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], - - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], - - "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], - - "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], - - "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="], - - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - - "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="], - - "protobufjs/@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], - - "react-reconciler/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], - - "vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - - "@anthropic-ai/tokenizer/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - - "@opentelemetry/sdk-logs/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - - "@opentelemetry/sdk-logs/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - - "@opentelemetry/sdk-metrics/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - - "@opentelemetry/sdk-metrics/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - - "protobufjs/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - - "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - - "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - - "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - - "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - - "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - - "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - - "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - - "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - - "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - - "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - - "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - - "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - - "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - - "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - - "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - - "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - - "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - - "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - - "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - - "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - - "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - - "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - - "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - - "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - - "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - - "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - } -} diff --git a/package.json b/package.json index 3d98fb9..c4394c0 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "typecheck": "tsc --noEmit", "lint": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\"", "lint:fix": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\" --fix", - "check": "npm run typecheck && npm run lint", - "check:fix": "npm run typecheck && npm run lint:fix", + "check": "pnpm typecheck && pnpm lint", + "check:fix": "pnpm typecheck && pnpm lint:fix", "test": "doppler run -- vitest run", "test:watch": "doppler run -- vitest" }, @@ -48,7 +48,6 @@ "@effect/cli": "^0.72.1", "@effect/opentelemetry": "^0.59.1", "@effect/platform": "^0.93.5", - "@effect/platform-bun": "^0.85.0", "@effect/platform-node": "^0.104.0", "@effect/rpc": "^0.72.2", "@effect/rpc-http": "^0.52.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a081c8c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5330 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@effect/ai': + specifier: ^0.32.1 + version: 0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/ai-anthropic': + specifier: ^0.22.0 + version: 0.22.0(@effect/ai@0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/ai-google': + specifier: ^0.11.0 + version: 0.11.1(@effect/ai@0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/ai-openai': + specifier: ^0.35.0 + version: 0.35.0(@effect/ai@0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/cli': + specifier: ^0.72.1 + version: 0.72.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/printer-ansi@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14))(@effect/printer@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/opentelemetry': + specifier: ^0.59.1 + version: 0.59.3(@effect/platform@0.93.8(effect@3.19.14))(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)(effect@3.19.14) + '@effect/platform': + specifier: ^0.93.5 + version: 0.93.8(effect@3.19.14) + '@effect/platform-node': + specifier: ^0.104.0 + version: 0.104.0(@effect/cluster@0.56.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/rpc': + specifier: ^0.72.2 + version: 0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/rpc-http': + specifier: ^0.52.4 + version: 0.52.4(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@opentelemetry/otlp-transformer': + specifier: ^0.208.0 + version: 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': + specifier: ^2.2.0 + version: 2.2.0(@opentelemetry/api@1.9.0) + '@opentui/core': + specifier: ^0.1.55 + version: 0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10) + '@opentui/react': + specifier: ^0.1.55 + version: 0.1.69(react-devtools-core@7.0.1)(react@19.2.3)(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)(ws@8.19.0) + effect: + specifier: ^3.19.8 + version: 3.19.14 + react: + specifier: '19' + version: 19.2.3 + react-dom: + specifier: '19' + version: 19.2.3(react@19.2.3) + typescript: + specifier: ^5 + version: 5.9.3 + yaml: + specifier: ^2.7.0 + version: 2.8.2 + devDependencies: + '@effect/eslint-plugin': + specifier: ^0.3.2 + version: 0.3.2 + '@effect/language-service': + specifier: ^0.57.1 + version: 0.57.1 + '@effect/vitest': + specifier: ^0.25.1 + version: 0.25.1(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2)) + '@eslint/compat': + specifier: ^1.1.1 + version: 1.4.1(eslint@9.39.2) + '@eslint/eslintrc': + specifier: ^3.1.0 + version: 3.3.3 + '@eslint/js': + specifier: ^9.10.0 + version: 9.39.2 + '@types/node': + specifier: ^22 + version: 22.19.3 + '@types/react': + specifier: '19' + version: 19.2.7 + '@typescript-eslint/eslint-plugin': + specifier: ^8.4.0 + version: 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.4.0 + version: 8.52.0(eslint@9.39.2)(typescript@5.9.3) + eslint: + specifier: ^9.10.0 + version: 9.39.2 + eslint-import-resolver-typescript: + specifier: ^3.6.3 + version: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) + eslint-plugin-import: + specifier: ^2.30.0 + version: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-plugin-simple-import-sort: + specifier: ^12.1.1 + version: 12.1.1(eslint@9.39.2) + eslint-plugin-sort-destructure-keys: + specifier: ^2.0.0 + version: 2.0.0(eslint@9.39.2) + semver: + specifier: ^7.7.3 + version: 7.7.3 + tsx: + specifier: ^4.19.0 + version: 4.21.0 + tuistory: + specifier: ^0.0.2 + version: 0.0.2(@opentui/core@0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)) + vitest: + specifier: ^3.2.0 + version: 3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + +packages: + + '@anthropic-ai/tokenizer@0.0.4': + resolution: {integrity: sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g==} + + '@dimforge/rapier2d-simd-compat@0.17.3': + resolution: {integrity: sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg==} + + '@dprint/formatter@0.4.1': + resolution: {integrity: sha512-IB/GXdlMOvi0UhQQ9mcY15Fxcrc2JPadmo6tqefCNV0bptFq7YBpggzpqYXldBXDa04CbKJ+rDwO2eNRPE2+/g==} + + '@dprint/typescript@0.91.8': + resolution: {integrity: sha512-tuKn4leCPItox1O4uunHcQF0QllDCvPWklnNQIh2PiWWVtRAGltJJnM4Cwj5AciplosD1Hiz7vAY3ew3crLb3A==} + + '@effect/ai-anthropic@0.22.0': + resolution: {integrity: sha512-uzgXeC8iAMSVtyFi/HxYPQGBcCBxzbgXKt/wxeyB1FlQGx4Uxn1tBy1dhBc2RvbVRYlT8MIPOCxLcUj1/995zg==} + peerDependencies: + '@effect/ai': ^0.32.0 + '@effect/experimental': ^0.57.0 + '@effect/platform': ^0.93.0 + effect: ^3.19.0 + + '@effect/ai-google@0.11.1': + resolution: {integrity: sha512-mW8409EBFHSqbNLio/eMlGJdi0UzBJfRfShA5guJHCkWIF7Xoudh0LdRuikt8dIobZlBl1/tdeCyWLaRBeHYyg==} + peerDependencies: + '@effect/ai': ^0.32.1 + '@effect/experimental': ^0.57.11 + '@effect/platform': ^0.93.7 + effect: ^3.19.11 + + '@effect/ai-openai@0.35.0': + resolution: {integrity: sha512-cB5cXITMsO+bfesIDqJRCiAGH8r/P5ts32HwZ5HbAvUscrqFgZBQDIsypiOCHL2teWFaBbXLdiqbF/lL4Ct8wg==} + peerDependencies: + '@effect/ai': ^0.32.0 + '@effect/experimental': ^0.57.0 + '@effect/platform': ^0.93.0 + effect: ^3.19.0 + + '@effect/ai@0.32.1': + resolution: {integrity: sha512-6qedQhnHigkRjNsUGaXKqvsbwhvQH3Ez3aePeROBRJi0c8q9tnR1E8qhQrTSjVPWcrv9AFtP0NRTr6iAHujGWQ==} + peerDependencies: + '@effect/experimental': ^0.57.0 + '@effect/platform': ^0.93.0 + '@effect/rpc': ^0.72.1 + effect: ^3.19.3 + + '@effect/cli@0.72.1': + resolution: {integrity: sha512-HGDMGD23TxFW9tCSX6g+M2u0robikMA0mP0SqeJMj7FWXTdcQ+cQsJE99bxi9iu+5YID7MIrVJMs8TUwXUV2sg==} + peerDependencies: + '@effect/platform': ^0.93.0 + '@effect/printer': ^0.47.0 + '@effect/printer-ansi': ^0.47.0 + effect: ^3.19.3 + + '@effect/cluster@0.56.1': + resolution: {integrity: sha512-gnrsH6kfrUjn+82j/bw1IR4yFqJqV8tc7xZvrbJPRgzANycc6K1hu3LMg548uYbUkTzD8YYyqrSatMO1mkQpzw==} + peerDependencies: + '@effect/platform': ^0.94.1 + '@effect/rpc': ^0.73.0 + '@effect/sql': ^0.49.0 + '@effect/workflow': ^0.16.0 + effect: ^3.19.14 + + '@effect/eslint-plugin@0.3.2': + resolution: {integrity: sha512-c4Vs9t3r54A4Zpl+wo8+PGzZz3JWYsip41H+UrebRLjQ2Hk/ap63IeCgN/HWcYtxtyhRopjp7gW9nOQ2Snbl+g==} + + '@effect/experimental@0.57.11': + resolution: {integrity: sha512-M5uug3Drs/gyTHLfA+XzcIZQGUEV/Jn5yi1POki4oZswhpzNmsVTHl4THpxAordRKwa5lFvTSlsRP684YH7pSw==} + peerDependencies: + '@effect/platform': ^0.93.6 + effect: ^3.19.9 + ioredis: ^5 + lmdb: ^3 + peerDependenciesMeta: + ioredis: + optional: true + lmdb: + optional: true + + '@effect/language-service@0.57.1': + resolution: {integrity: sha512-uWzYN+aHl4KfZHGmDxa3+OjV/mk9gMUycIyB8SYvyDiny3lXBtmRFdRVnw6TYL1P5EBfg4N09+lO/0ECRREVXQ==} + hasBin: true + + '@effect/opentelemetry@0.59.3': + resolution: {integrity: sha512-0Ozqxc1AMMnOu18Iqqcf8rokyrdvHCYaJzUEDQpi5pt7z+C5zhv5GjFMI9OxMgGRBiGxsY63ZUth+Q6w8F8KhQ==} + peerDependencies: + '@effect/platform': ^0.93.8 + '@opentelemetry/api': ^1.9 + '@opentelemetry/resources': ^2.0.0 + '@opentelemetry/sdk-logs': '>=0.203.0 <0.300.0' + '@opentelemetry/sdk-metrics': ^2.0.0 + '@opentelemetry/sdk-trace-base': ^2.0.0 + '@opentelemetry/sdk-trace-node': ^2.0.0 + '@opentelemetry/sdk-trace-web': ^2.0.0 + '@opentelemetry/semantic-conventions': ^1.33.0 + effect: ^3.19.12 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/resources': + optional: true + '@opentelemetry/sdk-logs': + optional: true + '@opentelemetry/sdk-metrics': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + '@opentelemetry/sdk-trace-node': + optional: true + '@opentelemetry/sdk-trace-web': + optional: true + + '@effect/platform-node-shared@0.57.0': + resolution: {integrity: sha512-QXuvmLNlABCQLcTl+lN1YPhKosR6KqArPYjC2reU0fb5lroCo3YRb/aGpXIgLthHzQL8cLU5XMGA3Cu5hKY2Tw==} + peerDependencies: + '@effect/cluster': ^0.56.0 + '@effect/platform': ^0.94.0 + '@effect/rpc': ^0.73.0 + '@effect/sql': ^0.49.0 + effect: ^3.19.13 + + '@effect/platform-node@0.104.0': + resolution: {integrity: sha512-2ZkUDDTxLD95ARdYIKBx4tdIIgqA3cwb3jlnVVBxmHUf0Pg5N2HdMuD0Q+CXQ7Q94FDwnLW3ZvaSfxDh6FvrNw==} + peerDependencies: + '@effect/cluster': ^0.56.0 + '@effect/platform': ^0.94.0 + '@effect/rpc': ^0.73.0 + '@effect/sql': ^0.49.0 + effect: ^3.19.13 + + '@effect/platform@0.93.8': + resolution: {integrity: sha512-xTEy6fyTy4ijmFC3afKgtvYtn/JyPoIov4ZSUWJZUv3VeOcUPNGrrqG6IJlWkXs3NhvSywKv7wc1kw3epCQVZw==} + peerDependencies: + effect: ^3.19.12 + + '@effect/printer-ansi@0.47.0': + resolution: {integrity: sha512-tDEQ9XJpXDNYoWMQJHFRMxKGmEOu6z32x3Kb8YLOV5nkauEKnKmWNs7NBp8iio/pqoJbaSwqDwUg9jXVquxfWQ==} + peerDependencies: + '@effect/typeclass': ^0.38.0 + effect: ^3.19.0 + + '@effect/printer@0.47.0': + resolution: {integrity: sha512-VgR8e+YWWhMEAh9qFOjwiZ3OXluAbcVLIOtvp2S5di1nSrPOZxj78g8LE77JSvyfp5y5bS2gmFW+G7xD5uU+2Q==} + peerDependencies: + '@effect/typeclass': ^0.38.0 + effect: ^3.19.0 + + '@effect/rpc-http@0.52.4': + resolution: {integrity: sha512-QpIaTlqo5zYO+ujFqMMv3g/9y4CByuez7taMFCL+2mihlDCKt9s+vIEbxQYzn1r0/ppDYfZ/d5vgEJBpddcgxg==} + peerDependencies: + '@effect/platform': ^0.79.4 + effect: ^3.13.12 + + '@effect/rpc@0.54.4': + resolution: {integrity: sha512-iu3TGWCt4OMH8iKL1ATeROhAxrMF+HdF3NbR5lWls9yWJwBgVU+cps3ZzRbNQhFPWXDGqVuYgmYNY1GKbZgMaw==} + peerDependencies: + '@effect/platform': ^0.79.4 + effect: ^3.13.12 + + '@effect/rpc@0.72.2': + resolution: {integrity: sha512-BmTXybXCOq96D2r9mvSW/YdiTQs5CStnd4II+lfVKrMr3pMNERKLZ2LG37Tfm4Sy3Q8ire6IVVKO/CN+VR0uQQ==} + peerDependencies: + '@effect/platform': ^0.93.3 + effect: ^3.19.5 + + '@effect/sql@0.49.0': + resolution: {integrity: sha512-9UEKR+z+MrI/qMAmSvb/RiD9KlgIazjZUCDSpwNgm0lEK9/Q6ExEyfziiYFVCPiptp52cBw8uBHRic8hHnwqXA==} + peerDependencies: + '@effect/experimental': ^0.58.0 + '@effect/platform': ^0.94.0 + effect: ^3.19.13 + + '@effect/typeclass@0.38.0': + resolution: {integrity: sha512-lMUcJTRtG8KXhXoczapZDxbLK5os7M6rn0zkvOgncJW++A0UyelZfMVMKdT5R+fgpZcsAU/1diaqw3uqLJwGxA==} + peerDependencies: + effect: ^3.19.0 + + '@effect/vitest@0.25.1': + resolution: {integrity: sha512-OMYvOU8iGed8GZXxgVBXlYtjG+jwWj5cJxFk0hOHOfTbCHXtdCMEWlXNba5zxbE7dBnW4srbnSYrP/NGGTC3qQ==} + peerDependencies: + effect: ^3.17.7 + vitest: ^3.2.0 + + '@effect/workflow@0.16.0': + resolution: {integrity: sha512-MiAdlxx3TixkgHdbw+Yf1Z3tHAAE0rOQga12kIydJqj05Fnod+W/I+kQGRMY/XWRg+QUsVxhmh1qTr7Ype6lrw==} + peerDependencies: + '@effect/experimental': ^0.58.0 + '@effect/platform': ^0.94.0 + '@effect/rpc': ^0.73.0 + effect: ^3.19.13 + + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@1.4.1': + resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jimp/core@1.6.0': + resolution: {integrity: sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==} + engines: {node: '>=18'} + + '@jimp/diff@1.6.0': + resolution: {integrity: sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==} + engines: {node: '>=18'} + + '@jimp/file-ops@1.6.0': + resolution: {integrity: sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==} + engines: {node: '>=18'} + + '@jimp/js-bmp@1.6.0': + resolution: {integrity: sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==} + engines: {node: '>=18'} + + '@jimp/js-gif@1.6.0': + resolution: {integrity: sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==} + engines: {node: '>=18'} + + '@jimp/js-jpeg@1.6.0': + resolution: {integrity: sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==} + engines: {node: '>=18'} + + '@jimp/js-png@1.6.0': + resolution: {integrity: sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==} + engines: {node: '>=18'} + + '@jimp/js-tiff@1.6.0': + resolution: {integrity: sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==} + engines: {node: '>=18'} + + '@jimp/plugin-blit@1.6.0': + resolution: {integrity: sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==} + engines: {node: '>=18'} + + '@jimp/plugin-blur@1.6.0': + resolution: {integrity: sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==} + engines: {node: '>=18'} + + '@jimp/plugin-circle@1.6.0': + resolution: {integrity: sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==} + engines: {node: '>=18'} + + '@jimp/plugin-color@1.6.0': + resolution: {integrity: sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==} + engines: {node: '>=18'} + + '@jimp/plugin-contain@1.6.0': + resolution: {integrity: sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==} + engines: {node: '>=18'} + + '@jimp/plugin-cover@1.6.0': + resolution: {integrity: sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==} + engines: {node: '>=18'} + + '@jimp/plugin-crop@1.6.0': + resolution: {integrity: sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==} + engines: {node: '>=18'} + + '@jimp/plugin-displace@1.6.0': + resolution: {integrity: sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==} + engines: {node: '>=18'} + + '@jimp/plugin-dither@1.6.0': + resolution: {integrity: sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==} + engines: {node: '>=18'} + + '@jimp/plugin-fisheye@1.6.0': + resolution: {integrity: sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==} + engines: {node: '>=18'} + + '@jimp/plugin-flip@1.6.0': + resolution: {integrity: sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==} + engines: {node: '>=18'} + + '@jimp/plugin-hash@1.6.0': + resolution: {integrity: sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==} + engines: {node: '>=18'} + + '@jimp/plugin-mask@1.6.0': + resolution: {integrity: sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==} + engines: {node: '>=18'} + + '@jimp/plugin-print@1.6.0': + resolution: {integrity: sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==} + engines: {node: '>=18'} + + '@jimp/plugin-quantize@1.6.0': + resolution: {integrity: sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==} + engines: {node: '>=18'} + + '@jimp/plugin-resize@1.6.0': + resolution: {integrity: sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==} + engines: {node: '>=18'} + + '@jimp/plugin-rotate@1.6.0': + resolution: {integrity: sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==} + engines: {node: '>=18'} + + '@jimp/plugin-threshold@1.6.0': + resolution: {integrity: sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==} + engines: {node: '>=18'} + + '@jimp/types@1.6.0': + resolution: {integrity: sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==} + engines: {node: '>=18'} + + '@jimp/utils@1.6.0': + resolution: {integrity: sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==} + engines: {node: '>=18'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@opentelemetry/api-logs@0.208.0': + resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/core@2.2.0': + resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/otlp-transformer@0.208.0': + resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.2.0': + resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.208.0': + resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.2.0': + resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.2.0': + resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.38.0': + resolution: {integrity: sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==} + engines: {node: '>=14'} + + '@opentui/core-darwin-arm64@0.1.69': + resolution: {integrity: sha512-d9RPAh84O2XIyMw+7+X0fEyi+4KH5sPk9AxLze8GHRBGOzkRunqagFCLBrN5VFs2e2nbhIYtjMszo7gcpWyh7g==} + cpu: [arm64] + os: [darwin] + + '@opentui/core-darwin-x64@0.1.69': + resolution: {integrity: sha512-41K9zkL2IG0ahL+8Gd+e9ulMrnJF6lArPzG7grjWzo+FWEZwvw0WLCO1/Gn5K85G8Yx7gQXkZOUaw1BmHjxoRw==} + cpu: [x64] + os: [darwin] + + '@opentui/core-linux-arm64@0.1.69': + resolution: {integrity: sha512-IcUjwjuIpX3BBG1a9kjMqWrHYCFHAVfjh5nIRozWZZoqaczLzJb3nJeF2eg8aDeIoGhXvERWB1r1gmqPW8u3vQ==} + cpu: [arm64] + os: [linux] + + '@opentui/core-linux-x64@0.1.69': + resolution: {integrity: sha512-5S9vqEIq7q+MEdp4cT0HLegBWu0pWLcletHZL80bsLbJt9OT8en3sQmL5bvas9sIuyeBFru9bfCmrQ/gnVTTiA==} + cpu: [x64] + os: [linux] + + '@opentui/core-win32-arm64@0.1.69': + resolution: {integrity: sha512-eSKcGwbcnJJPtrTFJI7STZ7inSYeedHS0swwjZhh9SADAruEz08intamunOslffv5+mnlvRp7UBGK35cMjbv/w==} + cpu: [arm64] + os: [win32] + + '@opentui/core-win32-x64@0.1.69': + resolution: {integrity: sha512-OjG/0jqYXURqbbUwNgSPrBA6yuKF3OOFh8JSG7VvzoYHJFJRmwVWY0fztWv/hgGHe354ti37c7JDJBQ44HOCdA==} + cpu: [x64] + os: [win32] + + '@opentui/core@0.1.69': + resolution: {integrity: sha512-BcEFnAuMq4vgfb+zxOP/l+NO1AS3fVHkYjn+E8Wpmaxr0AzWNTi2NPAMtQf+Wqufxo0NYh0gY4c9B6n8OxTjGw==} + peerDependencies: + web-tree-sitter: 0.25.10 + + '@opentui/react@0.1.69': + resolution: {integrity: sha512-E+kcCnGI6lONJZC90m4T8n0tWZ2dUz2Nx+lkNqgnXt+pvxN0HIdW3MAsOxntkYFw5e5BgGv8E3hRuBhoyCatgA==} + peerDependencies: + react: '>=19.0.0' + react-devtools-core: ^7.0.1 + ws: ^8.18.0 + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@16.9.1': + resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} + + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + + '@types/node@22.19.3': + resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + + '@typescript-eslint/eslint-plugin@8.52.0': + resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.52.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.52.0': + resolution: {integrity: sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.52.0': + resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.52.0': + resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.52.0': + resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.52.0': + resolution: {integrity: sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.52.0': + resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.52.0': + resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.52.0': + resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.52.0': + resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@webgpu/types@0.1.68': + resolution: {integrity: sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + any-base@1.1.0: + resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + await-to-js@3.0.0: + resolution: {integrity: sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==} + engines: {node: '>=6.0.0'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bmp-ts@1.0.9: + resolution: {integrity: sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bun-ffi-structs@0.1.2: + resolution: {integrity: sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w==} + peerDependencies: + typescript: ^5 + + bun-pty@0.4.6: + resolution: {integrity: sha512-sMAZgcxuCe4A9iPdPNJFiWu3ZVqkxQbVlEXu9llxZoEll6cLVXx0rXUiFrM4WWgQsmacM+5xNvyzgLAH0NAA3A==} + engines: {bun: '>=1.0.0'} + + bun-webgpu-darwin-arm64@0.1.4: + resolution: {integrity: sha512-eDgLN9teKTfmvrCqgwwmWNsNszxYs7IZdCqk0S1DCarvMhr4wcajoSBlA/nQA0/owwLduPTS8xxCnQp4/N/gDg==} + cpu: [arm64] + os: [darwin] + + bun-webgpu-darwin-x64@0.1.4: + resolution: {integrity: sha512-X+PjwJUWenUmdQBP8EtdItMyieQ6Nlpn+BH518oaouDiSnWj5+b0Y7DNDZJq7Ezom4EaxmqL/uGYZK3aCQ7CXg==} + cpu: [x64] + os: [darwin] + + bun-webgpu-linux-x64@0.1.4: + resolution: {integrity: sha512-zMLs2YIGB+/jxrYFXaFhVKX/GBt05UTF45lc9srcHc9JXGjEj+12CIo1CHLTAWatXMTqt0Jsu6ukWEoWVT/ayA==} + cpu: [x64] + os: [linux] + + bun-webgpu-win32-x64@0.1.4: + resolution: {integrity: sha512-Z5yAK28xrcm8Wb5k7TZ8FJKpOI/r+aVCRdlHYAqI2SDJFN3nD4mJs900X6kNVmG/xFzb5yOuKVYWGg+6ZXWbyA==} + cpu: [x64] + os: [win32] + + bun-webgpu@0.1.4: + resolution: {integrity: sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + effect@3.19.14: + resolution: {integrity: sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA==} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-simple-import-sort@12.1.1: + resolution: {integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==} + peerDependencies: + eslint: '>=5.0.0' + + eslint-plugin-sort-destructure-keys@2.0.0: + resolution: {integrity: sha512-4w1UQCa3o/YdfWaLr9jY8LfGowwjwjmwClyFLxIsToiyIdZMq3x9Ti44nDn34DtTPP7PWg96tUONKVmATKhYGQ==} + engines: {node: '>=12'} + peerDependencies: + eslint: 5 - 9 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exif-parser@0.1.12: + resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-type@16.5.4: + resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} + engines: {node: '>=10'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-my-way-ts@0.1.6: + resolution: {integrity: sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + ghostty-opentui@1.3.11: + resolution: {integrity: sha512-taKOhQD65dip/GBi2eDicyS6Z+m7T6CAWyUcFUVP3nX+JKTCiOwfncGqhnqtSa8VE3mG6VHaPzIimDVN7pdD6w==} + peerDependencies: + '@opentui/core': '*' + peerDependenciesMeta: + '@opentui/core': + optional: true + + gifwrap@0.10.1: + resolution: {integrity: sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + gpt-tokenizer@2.9.0: + resolution: {integrity: sha512-YSpexBL/k4bfliAzMrRqn3M6+it02LutVyhVpDeMKrC/O9+pCe/5s8U2hYKa2vFLD5/vHhsKc8sOn/qGqII8Kg==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + image-q@4.0.0: + resolution: {integrity: sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jimp@1.6.0: + resolution: {integrity: sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==} + engines: {node: '>=18'} + + jpeg-js@0.4.4: + resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kubernetes-types@1.30.0: + resolution: {integrity: sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.8: + resolution: {integrity: sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==} + + multipasta@0.2.7: + resolution: {integrity: sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-pty@1.1.0: + resolution: {integrity: sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + omggif@1.0.10: + resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-bmfont-ascii@1.0.6: + resolution: {integrity: sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==} + + parse-bmfont-binary@1.0.6: + resolution: {integrity: sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==} + + parse-bmfont-xml@1.1.6: + resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + peek-readable@4.1.0: + resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pixelmatch@5.3.0: + resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==} + hasBin: true + + planck@1.4.2: + resolution: {integrity: sha512-mNbhnV3g8X2rwGxzcesjmN8BDA6qfXgQxXVMkWau9MCRlQY0RLNEkyHlVp6yFy/X6qrzAXyNONCnZ1cGDLrNew==} + engines: {node: '>=14.0'} + peerDependencies: + stage-js: ^1.0.0-alpha.12 + + pngjs@6.0.0: + resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} + engines: {node: '>=12.13.0'} + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} + engines: {node: '>=6.0.0'} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + react-devtools-core@7.0.1: + resolution: {integrity: sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw==} + + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + + react-reconciler@0.32.0: + resolution: {integrity: sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^19.1.0 + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readable-web-to-node-stream@3.0.4: + resolution: {integrity: sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==} + engines: {node: '>=8'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + sax@1.4.3: + resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + simple-xml-to-json@1.2.3: + resolution: {integrity: sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==} + engines: {node: '>=20.12.2'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + stage-js@1.0.0-alpha.17: + resolution: {integrity: sha512-AzlMO+t51v6cFvKZ+Oe9DJnL1OXEH5s9bEy6di5aOrUpcP7PCzI/wIeXF0u3zg0L89gwnceoKxrLId0ZpYnNXw==} + engines: {node: '>=18.0'} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + strtok3@6.3.0: + resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} + engines: {node: '>=10'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + three@0.177.0: + resolution: {integrity: sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==} + + tiktoken@1.0.22: + resolution: {integrity: sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + token-types@4.2.1: + resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} + engines: {node: '>=10'} + + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + tuistory@0.0.2: + resolution: {integrity: sha512-14FfFhL+s3Ai+XybzuYeygw7NgBhxk01S7DCfYHtMqy3Si5lkvJLNZdJEFVuGnbtBZDXpfxeGaE9HzJaAjITEg==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@7.18.2: + resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} + engines: {node: '>=20.18.1'} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + utif2@4.1.0: + resolution: {integrity: sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==} + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + web-tree-sitter@0.25.10: + resolution: {integrity: sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA==} + peerDependencies: + '@types/emscripten': ^1.40.0 + peerDependenciesMeta: + '@types/emscripten': + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-parse-from-string@1.0.1: + resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==} + + xml2js@0.5.0: + resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@anthropic-ai/tokenizer@0.0.4': + dependencies: + '@types/node': 18.19.130 + tiktoken: 1.0.22 + + '@dimforge/rapier2d-simd-compat@0.17.3': + optional: true + + '@dprint/formatter@0.4.1': {} + + '@dprint/typescript@0.91.8': {} + + '@effect/ai-anthropic@0.22.0(@effect/ai@0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@anthropic-ai/tokenizer': 0.0.4 + '@effect/ai': 0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + effect: 3.19.14 + + '@effect/ai-google@0.11.1(@effect/ai@0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/ai': 0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + effect: 3.19.14 + + '@effect/ai-openai@0.35.0(@effect/ai@0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/ai': 0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + effect: 3.19.14 + gpt-tokenizer: 2.9.0 + + '@effect/ai@0.32.1(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + effect: 3.19.14 + find-my-way-ts: 0.1.6 + + '@effect/cli@0.72.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/printer-ansi@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14))(@effect/printer@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.14) + '@effect/printer': 0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14) + '@effect/printer-ansi': 0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14) + effect: 3.19.14 + ini: 4.1.3 + toml: 3.0.0 + yaml: 2.8.2 + + '@effect/cluster@0.56.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.14) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/sql': 0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/workflow': 0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + effect: 3.19.14 + kubernetes-types: 1.30.0 + + '@effect/eslint-plugin@0.3.2': + dependencies: + '@dprint/formatter': 0.4.1 + '@dprint/typescript': 0.91.8 + prettier-linter-helpers: 1.0.1 + + '@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.14) + effect: 3.19.14 + uuid: 11.1.0 + + '@effect/language-service@0.57.1': {} + + '@effect/opentelemetry@0.59.3(@effect/platform@0.93.8(effect@3.19.14))(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)(effect@3.19.14)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.14) + '@opentelemetry/semantic-conventions': 1.38.0 + effect: 3.19.14 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) + + '@effect/platform-node-shared@0.57.0(@effect/cluster@0.56.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/cluster': 0.56.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/sql': 0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@parcel/watcher': 2.5.1 + effect: 3.19.14 + multipasta: 0.2.7 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@effect/platform-node@0.104.0(@effect/cluster@0.56.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/cluster': 0.56.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + '@effect/platform-node-shared': 0.57.0(@effect/cluster@0.56.1(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/sql': 0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + effect: 3.19.14 + mime: 3.0.0 + undici: 7.18.2 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@effect/platform@0.93.8(effect@3.19.14)': + dependencies: + effect: 3.19.14 + find-my-way-ts: 0.1.6 + msgpackr: 1.11.8 + multipasta: 0.2.7 + + '@effect/printer-ansi@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/printer': 0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14) + '@effect/typeclass': 0.38.0(effect@3.19.14) + effect: 3.19.14 + + '@effect/printer@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/typeclass': 0.38.0(effect@3.19.14) + effect: 3.19.14 + + '@effect/rpc-http@0.52.4(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.14) + '@effect/rpc': 0.54.4(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + effect: 3.19.14 + + '@effect/rpc@0.54.4(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.14) + effect: 3.19.14 + + '@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.14) + effect: 3.19.14 + msgpackr: 1.11.8 + + '@effect/sql@0.49.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + effect: 3.19.14 + uuid: 11.1.0 + + '@effect/typeclass@0.38.0(effect@3.19.14)': + dependencies: + effect: 3.19.14 + + '@effect/vitest@0.25.1(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + effect: 3.19.14 + vitest: 3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + + '@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': + dependencies: + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@effect/platform': 0.93.8(effect@3.19.14) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + effect: 3.19.14 + + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + dependencies: + eslint: 9.39.2 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/compat@1.4.1(eslint@9.39.2)': + dependencies: + '@eslint/core': 0.17.0 + optionalDependencies: + eslint: 9.39.2 + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jimp/core@1.6.0': + dependencies: + '@jimp/file-ops': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + await-to-js: 3.0.0 + exif-parser: 0.1.12 + file-type: 16.5.4 + mime: 3.0.0 + + '@jimp/diff@1.6.0': + dependencies: + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + pixelmatch: 5.3.0 + + '@jimp/file-ops@1.6.0': {} + + '@jimp/js-bmp@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + bmp-ts: 1.0.9 + + '@jimp/js-gif@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + gifwrap: 0.10.1 + omggif: 1.0.10 + + '@jimp/js-jpeg@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + jpeg-js: 0.4.4 + + '@jimp/js-png@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + pngjs: 7.0.0 + + '@jimp/js-tiff@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + utif2: 4.1.0 + + '@jimp/plugin-blit@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-blur@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/utils': 1.6.0 + + '@jimp/plugin-circle@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-color@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + tinycolor2: 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-contain@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-blit': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-cover@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-crop': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-crop@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-displace@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-dither@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + + '@jimp/plugin-fisheye@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-flip@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-hash@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/js-bmp': 1.6.0 + '@jimp/js-jpeg': 1.6.0 + '@jimp/js-png': 1.6.0 + '@jimp/js-tiff': 1.6.0 + '@jimp/plugin-color': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + any-base: 1.1.0 + + '@jimp/plugin-mask@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-print@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/js-jpeg': 1.6.0 + '@jimp/js-png': 1.6.0 + '@jimp/plugin-blit': 1.6.0 + '@jimp/types': 1.6.0 + parse-bmfont-ascii: 1.0.6 + parse-bmfont-binary: 1.0.6 + parse-bmfont-xml: 1.1.6 + simple-xml-to-json: 1.2.3 + zod: 3.25.76 + + '@jimp/plugin-quantize@1.6.0': + dependencies: + image-q: 4.0.0 + zod: 3.25.76 + + '@jimp/plugin-resize@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-rotate@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-crop': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-threshold@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-color': 1.6.0 + '@jimp/plugin-hash': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/types@1.6.0': + dependencies: + zod: 3.25.76 + + '@jimp/utils@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + tinycolor2: 1.6.0 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@nolyfill/is-core-module@1.0.39': {} + + '@opentelemetry/api-logs@0.208.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + + '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/semantic-conventions@1.38.0': {} + + '@opentui/core-darwin-arm64@0.1.69': + optional: true + + '@opentui/core-darwin-x64@0.1.69': + optional: true + + '@opentui/core-linux-arm64@0.1.69': + optional: true + + '@opentui/core-linux-x64@0.1.69': + optional: true + + '@opentui/core-win32-arm64@0.1.69': + optional: true + + '@opentui/core-win32-x64@0.1.69': + optional: true + + '@opentui/core@0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)': + dependencies: + bun-ffi-structs: 0.1.2(typescript@5.9.3) + diff: 8.0.2 + jimp: 1.6.0 + web-tree-sitter: 0.25.10 + yoga-layout: 3.2.1 + optionalDependencies: + '@dimforge/rapier2d-simd-compat': 0.17.3 + '@opentui/core-darwin-arm64': 0.1.69 + '@opentui/core-darwin-x64': 0.1.69 + '@opentui/core-linux-arm64': 0.1.69 + '@opentui/core-linux-x64': 0.1.69 + '@opentui/core-win32-arm64': 0.1.69 + '@opentui/core-win32-x64': 0.1.69 + bun-webgpu: 0.1.4 + planck: 1.4.2(stage-js@1.0.0-alpha.17) + three: 0.177.0 + transitivePeerDependencies: + - stage-js + - typescript + + '@opentui/react@0.1.69(react-devtools-core@7.0.1)(react@19.2.3)(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)(ws@8.19.0)': + dependencies: + '@opentui/core': 0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10) + react: 19.2.3 + react-devtools-core: 7.0.1 + react-reconciler: 0.32.0(react@19.2.3) + ws: 8.19.0 + transitivePeerDependencies: + - stage-js + - typescript + - web-tree-sitter + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@rollup/rollup-android-arm-eabi@4.55.1': + optional: true + + '@rollup/rollup-android-arm64@4.55.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.55.1': + optional: true + + '@rollup/rollup-darwin-x64@4.55.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.55.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.55.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.55.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.55.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.55.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.55.1': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@tokenizer/token@0.3.0': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@16.9.1': {} + + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.19.3': + dependencies: + undici-types: 6.21.0 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 + eslint: 9.39.2 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 + debug: 4.4.3 + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.52.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.52.0': + dependencies: + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 + + '@typescript-eslint/tsconfig-utils@8.52.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.52.0': {} + + '@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.52.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.52.0': + dependencies: + '@typescript-eslint/types': 8.52.0 + eslint-visitor-keys: 4.2.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@webgpu/types@0.1.68': + optional: true + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + any-base@1.1.0: {} + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + await-to-js@3.0.0: {} + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + bmp-ts@1.0.9: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bun-ffi-structs@0.1.2(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + bun-pty@0.4.6: + optional: true + + bun-webgpu-darwin-arm64@0.1.4: + optional: true + + bun-webgpu-darwin-x64@0.1.4: + optional: true + + bun-webgpu-linux-x64@0.1.4: + optional: true + + bun-webgpu-win32-x64@0.1.4: + optional: true + + bun-webgpu@0.1.4: + dependencies: + '@webgpu/types': 0.1.68 + optionalDependencies: + bun-webgpu-darwin-arm64: 0.1.4 + bun-webgpu-darwin-x64: 0.1.4 + bun-webgpu-linux-x64: 0.1.4 + bun-webgpu-win32-x64: 0.1.4 + optional: true + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@2.1.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@1.0.3: {} + + detect-libc@2.1.2: + optional: true + + diff@8.0.2: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + effect@3.19.14: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escape-string-regexp@4.0.0: {} + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.2 + get-tsconfig: 4.13.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.2): + dependencies: + eslint: 9.39.2 + + eslint-plugin-sort-destructure-keys@2.0.0(eslint@9.39.2): + dependencies: + eslint: 9.39.2 + natural-compare-lite: 1.4.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.2: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + event-target-shim@5.0.1: {} + + events@3.3.0: {} + + exif-parser@0.1.12: {} + + expect-type@1.3.0: {} + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-type@16.5.4: + dependencies: + readable-web-to-node-stream: 3.0.4 + strtok3: 6.3.0 + token-types: 4.2.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-my-way-ts@0.1.6: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + ghostty-opentui@1.3.11(@opentui/core@0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)): + dependencies: + strip-ansi: 7.1.2 + optionalDependencies: + '@opentui/core': 0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10) + + gifwrap@0.10.1: + dependencies: + image-q: 4.0.0 + omggif: 1.0.10 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + gpt-tokenizer@2.9.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + image-q@4.0.0: + dependencies: + '@types/node': 16.9.1 + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + ini@4.1.3: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.7.3 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + jimp@1.6.0: + dependencies: + '@jimp/core': 1.6.0 + '@jimp/diff': 1.6.0 + '@jimp/js-bmp': 1.6.0 + '@jimp/js-gif': 1.6.0 + '@jimp/js-jpeg': 1.6.0 + '@jimp/js-png': 1.6.0 + '@jimp/js-tiff': 1.6.0 + '@jimp/plugin-blit': 1.6.0 + '@jimp/plugin-blur': 1.6.0 + '@jimp/plugin-circle': 1.6.0 + '@jimp/plugin-color': 1.6.0 + '@jimp/plugin-contain': 1.6.0 + '@jimp/plugin-cover': 1.6.0 + '@jimp/plugin-crop': 1.6.0 + '@jimp/plugin-displace': 1.6.0 + '@jimp/plugin-dither': 1.6.0 + '@jimp/plugin-fisheye': 1.6.0 + '@jimp/plugin-flip': 1.6.0 + '@jimp/plugin-hash': 1.6.0 + '@jimp/plugin-mask': 1.6.0 + '@jimp/plugin-print': 1.6.0 + '@jimp/plugin-quantize': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/plugin-rotate': 1.6.0 + '@jimp/plugin-threshold': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + + jpeg-js@0.4.4: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kubernetes-types@1.30.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + long@5.3.2: {} + + loupe@3.2.1: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime@3.0.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.8: + optionalDependencies: + msgpackr-extract: 3.0.3 + + multipasta@0.2.7: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.3.4: {} + + natural-compare-lite@1.4.0: {} + + natural-compare@1.4.0: {} + + node-addon-api@7.1.1: {} + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.1.2 + optional: true + + node-pty@1.1.0: + dependencies: + node-addon-api: 7.1.1 + optional: true + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + omggif@1.0.10: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + pako@1.0.11: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-bmfont-ascii@1.0.6: {} + + parse-bmfont-binary@1.0.6: {} + + parse-bmfont-xml@1.1.6: + dependencies: + xml-parse-from-string: 1.0.1 + xml2js: 0.5.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + peek-readable@4.1.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pixelmatch@5.3.0: + dependencies: + pngjs: 6.0.0 + + planck@1.4.2(stage-js@1.0.0-alpha.17): + dependencies: + stage-js: 1.0.0-alpha.17 + optional: true + + pngjs@6.0.0: {} + + pngjs@7.0.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.1: + dependencies: + fast-diff: 1.3.0 + + process@0.11.10: {} + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.19.3 + long: 5.3.2 + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + react-devtools-core@7.0.1: + dependencies: + shell-quote: 1.8.3 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-reconciler@0.32.0(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.26.0 + + react@19.2.3: {} + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readable-web-to-node-stream@3.0.4: + dependencies: + readable-stream: 4.7.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.55.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 + fsevents: 2.3.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + sax@1.4.3: {} + + scheduler@0.26.0: {} + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.7.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + simple-xml-to-json@1.2.3: {} + + source-map-js@1.2.1: {} + + stable-hash@0.0.5: {} + + stackback@0.0.2: {} + + stage-js@1.0.0-alpha.17: + optional: true + + std-env@3.10.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + strtok3@6.3.0: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 4.1.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + three@0.177.0: + optional: true + + tiktoken@1.0.22: {} + + tinybench@2.9.0: {} + + tinycolor2@1.6.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + token-types@4.2.1: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + toml@3.0.0: {} + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: + optional: true + + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + tuistory@0.0.2(@opentui/core@0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)): + dependencies: + ghostty-opentui: 1.3.11(@opentui/core@0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)) + optionalDependencies: + bun-pty: 0.4.6 + node-pty: 1.1.0 + transitivePeerDependencies: + - '@opentui/core' + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + undici@7.18.2: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + utif2@4.1.0: + dependencies: + pako: 1.0.11 + + uuid@11.1.0: {} + + vite-node@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.55.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.3 + fsevents: 2.3.3 + tsx: 4.21.0 + yaml: 2.8.2 + + vitest@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.3 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + web-tree-sitter@0.25.10: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + ws@7.5.10: {} + + ws@8.19.0: {} + + xml-parse-from-string@1.0.1: {} + + xml2js@0.5.0: + dependencies: + sax: 1.4.3 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + yaml@2.8.2: {} + + yocto-queue@0.1.0: {} + + yoga-layout@3.2.1: {} + + zod@3.25.76: {} From 8b6049b074b5ae24f47fe71352eeb0c539c70b6c Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:52:37 +0000 Subject: [PATCH 04/15] Add CLI redesign with daemon support and stream commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New daemon.ts: DaemonService for process management (start/stop/restart/status) - New client.ts: HTTP client with SSE parsing and auto-daemon support - New README.md: Design docs, architecture, CLI reference, tmux testing guide - Rewrite cli.ts with server and stream subcommand groups - Update main.ts with proper layer composition CLI structure: server run/start/stop/restart/status stream subscribe/append/list/delete πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/durable-streams/README.md | 219 +++++++++++++++++++++++++++ src/durable-streams/cli.ts | 270 +++++++++++++++++++++++++++++++--- src/durable-streams/client.ts | 262 +++++++++++++++++++++++++++++++++ src/durable-streams/daemon.ts | 222 ++++++++++++++++++++++++++++ src/durable-streams/main.ts | 23 ++- 5 files changed, 973 insertions(+), 23 deletions(-) create mode 100644 src/durable-streams/README.md create mode 100644 src/durable-streams/client.ts create mode 100644 src/durable-streams/daemon.ts diff --git a/src/durable-streams/README.md b/src/durable-streams/README.md new file mode 100644 index 0000000..f3dfda5 --- /dev/null +++ b/src/durable-streams/README.md @@ -0,0 +1,219 @@ +# Durable Streams + +Pure event streams with append/subscribe semantics. No LLM logic, no agent behavior - just ordered, persistent event logs. + +## Design Philosophy + +**Streams are named, ordered event logs.** Each stream: +- Has a unique name +- Contains events with monotonically increasing offsets +- Supports append (write) and subscribe (read) +- Provides fan-out to multiple subscribers + +**CLI wraps HTTP.** The CLI doesn't talk to streams directly - it talks to an HTTP server. This decouples client from storage and enables remote operation. + +**Auto-daemon for local use.** Stream commands auto-start a local server if none running. No manual server management needed for simple use cases. + +## Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ CLI Layer β”‚ +β”‚ stream subscribe/append β†’ HTTP client β†’ server β”‚ +β”‚ server run/start/stop β†’ daemon management β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ HTTP Layer β”‚ +β”‚ POST /streams/:name β†’ append event β”‚ +β”‚ GET /streams/:name β†’ subscribe (SSE) β”‚ +β”‚ GET /streams β†’ list streams β”‚ +β”‚ DELETE /streams/:name β†’ delete stream β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ StreamManager (Layer 1) β”‚ +β”‚ getStream() β†’ lazy init + cache β”‚ +β”‚ append/subscribe/list/delete β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ DurableStream (Layer 0) β”‚ +β”‚ Per-stream state: offset counter, PubSub β”‚ +β”‚ append() β†’ increment offset, store, broadcast β”‚ +β”‚ subscribe() β†’ historical catchup + live PubSub β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Storage β”‚ +β”‚ InMemory (default) | FileSystem (future) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## CLI Reference + +### Server Commands + +```bash +# Run server in foreground (blocks) +npx tsx src/durable-streams/main.ts server run --port 3000 + +# Start daemonized server (returns immediately) +npx tsx src/durable-streams/main.ts server start --port 3000 + +# Stop daemon +npx tsx src/durable-streams/main.ts server stop + +# Restart daemon +npx tsx src/durable-streams/main.ts server restart --port 3000 + +# Check daemon status +npx tsx src/durable-streams/main.ts server status +``` + +### Stream Commands + +```bash +# Subscribe to stream (outputs JSON lines) +npx tsx src/durable-streams/main.ts stream subscribe my-stream + +# Subscribe from beginning (offset -1) +npx tsx src/durable-streams/main.ts stream subscribe my-stream --offset -1 + +# Append message (auto-wraps as {type:"message",text:"..."}) +npx tsx src/durable-streams/main.ts stream append my-stream -m "hello world" + +# Append raw JSON +npx tsx src/durable-streams/main.ts stream append my-stream -e '{"custom":"data"}' + +# List all streams +npx tsx src/durable-streams/main.ts stream list + +# Delete stream +npx tsx src/durable-streams/main.ts stream delete my-stream +``` + +### Environment Variables + +- `DURABLE_STREAMS_URL` - Server URL (skips auto-daemon) + +### Files Created + +When using daemon mode: +- `daemon.pid` - Process ID of running daemon +- `daemon.port` - Port the daemon is listening on +- `daemon.log` - Server stdout/stderr + +## Testing with tmux + +Multi-window setup for interactive testing: + +```bash +# Create tmux session with 3 panes +tmux new-session -d -s ds +tmux split-window -h -t ds +tmux split-window -v -t ds:0.1 + +# Pane 0: Server +tmux send-keys -t ds:0.0 'npx tsx src/durable-streams/main.ts server run' Enter + +# Pane 1: Subscriber (wait for server to start) +tmux send-keys -t ds:0.1 'sleep 1 && npx tsx src/durable-streams/main.ts stream subscribe test' Enter + +# Pane 2: Publisher +tmux send-keys -t ds:0.2 'sleep 2' Enter + +# Attach to session +tmux attach -t ds +``` + +Then in pane 2, send messages: +```bash +npx tsx src/durable-streams/main.ts stream append test -m "first message" +npx tsx src/durable-streams/main.ts stream append test -m "second message" +npx tsx src/durable-streams/main.ts stream append test -e '{"type":"custom","payload":123}' +``` + +Watch them appear in pane 1 (subscriber). + +## HTTP API Examples + +### Append Event + +```bash +curl -X POST http://localhost:3000/streams/my-stream \ + -H "Content-Type: application/json" \ + -d '{"data": {"type": "message", "text": "hello"}}' +``` + +Response: +```json +{"offset":"0000000000000000","data":{"type":"message","text":"hello"},"timestamp":1704672000000} +``` + +### Subscribe (SSE) + +```bash +# Subscribe from current position +curl -N http://localhost:3000/streams/my-stream + +# Subscribe from beginning +curl -N http://localhost:3000/streams/my-stream?offset=-1 + +# Subscribe from specific offset +curl -N http://localhost:3000/streams/my-stream?offset=0000000000000005 +``` + +Output (SSE format): +``` +data: {"offset":"0000000000000000","data":{"type":"message","text":"hello"},"timestamp":1704672000000} + +data: {"offset":"0000000000000001","data":{"type":"message","text":"world"},"timestamp":1704672001000} +``` + +### List Streams + +```bash +curl http://localhost:3000/streams +``` + +Response: +```json +{"streams":["my-stream","another-stream"]} +``` + +### Delete Stream + +```bash +curl -X DELETE http://localhost:3000/streams/my-stream +``` + +## Event Structure + +```typescript +interface StreamEvent { + offset: string // Zero-padded 16-char number ("0000000000000042") + data: unknown // Your payload + timestamp: number // Unix millis +} +``` + +Offsets are lexicographically sortable strings. Special offset `-1` means "start from beginning". + +## Key Files + +| File | Purpose | +|------|---------| +| `cli.ts` | CLI command definitions | +| `main.ts` | Entry point | +| `daemon.ts` | Daemon management (start/stop/status) | +| `client.ts` | HTTP client with auto-daemon | +| `http-routes.ts` | HTTP route handlers | +| `stream-manager.ts` | Stream lifecycle management | +| `stream.ts` | Core DurableStream implementation | +| `storage.ts` | Storage backend interface | +| `types.ts` | Type definitions | diff --git a/src/durable-streams/cli.ts b/src/durable-streams/cli.ts index 2a230ac..64e2b0a 100644 --- a/src/durable-streams/cli.ts +++ b/src/durable-streams/cli.ts @@ -2,42 +2,65 @@ * Durable Streams CLI * * Commands: - * - start: Start HTTP server for durable streams + * + * server run [--port] [--host] Run server in foreground + * server start [--port] Start daemonized server + * server stop Stop daemon + * server restart [--port] Restart daemon + * server status Check daemon status + * + * stream subscribe [--offset] Subscribe to stream events + * stream append -m|-e Append event to stream */ -import { Command, Options } from "@effect/cli" +import { Args, Command, Options } from "@effect/cli" import { HttpServer } from "@effect/platform" import { NodeHttpServer } from "@effect/platform-node" -import { Console, Effect, Layer } from "effect" +import { Console, Effect, Layer, Option, Schema, Stream } from "effect" import { createServer } from "node:http" +import { StreamClientService } from "./client.ts" +import { DaemonService } from "./daemon.ts" import { durableStreamsRouter } from "./http-routes.ts" import { StreamManagerService } from "./stream-manager.ts" +import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" + +// ─── Shared Options ───────────────────────────────────────────────────────── -/** Port option */ const portOption = Options.integer("port").pipe( Options.withAlias("p"), Options.withDescription("Port to listen on"), Options.withDefault(3000) ) -/** Host option */ const hostOption = Options.text("host").pipe( - Options.withAlias("h"), Options.withDescription("Host to bind to"), Options.withDefault("0.0.0.0") ) -/** Start command - launches HTTP server */ -const startCommand = Command.make( - "start", +const serverUrlOption = Options.text("server").pipe( + Options.withAlias("s"), + Options.withDescription("Server URL (overrides auto-daemon behavior)"), + Options.optional +) + +// ─── Server Commands ──────────────────────────────────────────────────────── + +/** server run - run server in foreground */ +const serverRunCommand = Command.make( + "run", { host: hostOption, port: portOption }, ({ host, port }) => Effect.gen(function*() { yield* Console.log(`Starting durable-streams server on ${host}:${port}`) + yield* Console.log("") + yield* Console.log("Endpoints:") + yield* Console.log(" POST /streams/:name Append event") + yield* Console.log(" GET /streams/:name Subscribe (SSE)") + yield* Console.log(" GET /streams List streams") + yield* Console.log(" DELETE /streams/:name Delete stream") + yield* Console.log("") - // Build service layers const serviceLayer = StreamManagerService.InMemory - // HTTP server layer const serverLayer = HttpServer.serve(durableStreamsRouter).pipe( Layer.provide(NodeHttpServer.layer(createServer, { port })), Layer.provide(serviceLayer) @@ -45,16 +68,223 @@ const startCommand = Command.make( return yield* Layer.launch(serverLayer) }) +).pipe(Command.withDescription("Run server in foreground")) + +/** server start - start daemon */ +const serverStartCommand = Command.make( + "start", + { port: portOption }, + ({ port }) => + Effect.gen(function*() { + const daemon = yield* DaemonService + const pid = yield* daemon.start({ port }) + yield* Console.log(`Daemon started (PID ${pid}) on port ${port}`) + yield* Console.log(`Logs: daemon.log`) + }).pipe( + Effect.catchTag("DaemonError", (e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Start daemonized server")) + +/** server stop - stop daemon */ +const serverStopCommand = Command.make( + "stop", + {}, + () => + Effect.gen(function*() { + const daemon = yield* DaemonService + yield* daemon.stop() + yield* Console.log("Daemon stopped") + }).pipe( + Effect.catchTag("DaemonError", (e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Stop daemon")) + +/** server restart - restart daemon */ +const serverRestartCommand = Command.make( + "restart", + { port: portOption }, + ({ port }) => + Effect.gen(function*() { + const daemon = yield* DaemonService + const pid = yield* daemon.restart({ port }) + yield* Console.log(`Daemon restarted (PID ${pid}) on port ${port}`) + }).pipe( + Effect.catchTag("DaemonError", (e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Restart daemon")) + +/** server status - check daemon status */ +const serverStatusCommand = Command.make( + "status", + {}, + () => + Effect.gen(function*() { + const daemon = yield* DaemonService + const status = yield* daemon.status() + if (Option.isSome(status)) { + const url = yield* daemon.getServerUrl() + yield* Console.log(`Running (PID ${status.value})`) + if (Option.isSome(url)) { + yield* Console.log(`URL: ${url.value}`) + } + } else { + yield* Console.log("Not running") + } + }) +).pipe(Command.withDescription("Check daemon status")) + +/** server command group */ +const serverCommand = Command.make("server").pipe( + Command.withSubcommands([ + serverRunCommand, + serverStartCommand, + serverStopCommand, + serverRestartCommand, + serverStatusCommand + ]), + Command.withDescription("Server management commands") +) + +// ─── Stream Commands ──────────────────────────────────────────────────────── + +const streamNameArg = Args.text({ name: "name" }).pipe( + Args.withDescription("Stream name") +) + +const offsetOption = Options.text("offset").pipe( + Options.withAlias("o"), + Options.withDescription("Start offset (-1 for beginning, or specific offset)"), + Options.optional ) -/** Main CLI */ -export const cli = Command.make("durable-streams").pipe( - Command.withSubcommands([startCommand]) +const messageOption = Options.text("message").pipe( + Options.withAlias("m"), + Options.withDescription("Message text (creates {type:'message',text:'...'})"), + Options.optional ) -/** Run CLI with args */ -export const run = (args: ReadonlyArray) => - Command.run(cli, { - name: "durable-streams", - version: "0.1.0" - })(args) +const eventOption = Options.text("event").pipe( + Options.withAlias("e"), + Options.withDescription("Raw JSON event data"), + Options.optional +) + +/** stream subscribe - subscribe to stream events */ +const streamSubscribeCommand = Command.make( + "subscribe", + { name: streamNameArg, offset: offsetOption, server: serverUrlOption }, + ({ name, offset, server: _server }) => + Effect.gen(function*() { + const client = yield* StreamClientService + + // Parse offset - build opts object conditionally + const subscribeOpts: { name: StreamName; offset?: Offset } = { name: name as StreamName } + if (Option.isSome(offset)) { + subscribeOpts.offset = offset.value === "-1" ? OFFSET_START : offset.value as Offset + } + + const eventStream = yield* client.subscribe(subscribeOpts) + + // Output events as JSON lines + yield* eventStream.pipe( + Stream.runForEach((event) => { + const encoded = Schema.encodeSync(StreamEvent)(event) + return Console.log(JSON.stringify(encoded)) + }) + ) + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Subscribe to stream events (outputs JSON lines)")) + +/** stream append - append event to stream */ +const streamAppendCommand = Command.make( + "append", + { name: streamNameArg, message: messageOption, event: eventOption, server: serverUrlOption }, + ({ event, message, name, server: _server }) => + Effect.gen(function*() { + const client = yield* StreamClientService + + // Determine data from -m or -e + let data: unknown + if (Option.isSome(message)) { + data = { type: "message", text: message.value } + } else if (Option.isSome(event)) { + try { + data = JSON.parse(event.value) + } catch (e) { + return yield* Console.error(`Invalid JSON: ${e}`) + } + } else { + return yield* Console.error("Either -m (message) or -e (event JSON) is required") + } + + const result = yield* client.append({ name: name as StreamName, data }) + const encoded = Schema.encodeSync(StreamEvent)(result) + yield* Console.log(JSON.stringify(encoded)) + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Append event to stream")) + +/** stream list - list all streams */ +const streamListCommand = Command.make( + "list", + { server: serverUrlOption }, + () => + Effect.gen(function*() { + const client = yield* StreamClientService + const streams = yield* client.list() + + if (streams.length === 0) { + yield* Console.log("No streams") + } else { + for (const name of streams) { + yield* Console.log(name) + } + } + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("List all streams")) + +/** stream delete - delete a stream */ +const streamDeleteCommand = Command.make( + "delete", + { name: streamNameArg, server: serverUrlOption }, + ({ name }) => + Effect.gen(function*() { + const client = yield* StreamClientService + yield* client.delete({ name: name as StreamName }) + yield* Console.log(`Deleted stream: ${name}`) + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Delete a stream")) + +/** stream command group */ +const streamCommand = Command.make("stream").pipe( + Command.withSubcommands([ + streamSubscribeCommand, + streamAppendCommand, + streamListCommand, + streamDeleteCommand + ]), + Command.withDescription("Stream operations") +) + +// ─── Root Command ─────────────────────────────────────────────────────────── + +const rootCommand = Command.make("durable-streams").pipe( + Command.withSubcommands([serverCommand, streamCommand]), + Command.withDescription("Durable event streams with daemon support") +) + +/** Main CLI definition */ +export const cli = Command.run(rootCommand, { + name: "durable-streams", + version: "0.1.0" +}) + +/** Run CLI with provided layers */ +export const run = (args: ReadonlyArray) => cli(args) diff --git a/src/durable-streams/client.ts b/src/durable-streams/client.ts new file mode 100644 index 0000000..8c91693 --- /dev/null +++ b/src/durable-streams/client.ts @@ -0,0 +1,262 @@ +/** + * HTTP Client for Durable Streams + * + * Provides typed client operations for stream append and subscribe. + * Handles SSE parsing for subscriptions. + */ +import * as Sse from "@effect/experimental/Sse" +import * as HttpBody from "@effect/platform/HttpBody" +import * as HttpClient from "@effect/platform/HttpClient" +import * as HttpClientRequest from "@effect/platform/HttpClientRequest" +import { Effect, Layer, Option, Schema, Stream } from "effect" +import { DaemonService, defaultDaemonConfig } from "./daemon.ts" +import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" + +/** Client configuration */ +export interface ClientConfig { + /** Server URL (e.g., "http://localhost:3000") */ + readonly serverUrl: string +} + +/** Error for client operations */ +export class ClientError extends Error { + readonly _tag = "ClientError" + constructor( + message: string, + readonly statusCode?: number + ) { + super(message) + this.name = "ClientError" + } +} + +/** Stream client service interface */ +export interface StreamClient { + /** Append data to a stream, returns the created event */ + readonly append: (opts: { + name: StreamName + data: unknown + }) => Effect.Effect + + /** Subscribe to a stream, returns event stream */ + readonly subscribe: (opts: { + name: StreamName + offset?: Offset + }) => Effect.Effect, ClientError> + + /** List all streams */ + readonly list: () => Effect.Effect, ClientError> + + /** Delete a stream */ + readonly delete: (opts: { name: StreamName }) => Effect.Effect +} + +/** Create client with explicit server URL */ +export const makeStreamClient = ( + config: ClientConfig +): Effect.Effect => + Effect.gen(function*() { + const baseClient = yield* HttpClient.HttpClient + const client = baseClient.pipe( + HttpClient.mapRequest((request) => + request.pipe( + HttpClientRequest.prependUrl(config.serverUrl) + ) + ) + ) + const clientOk = HttpClient.filterStatusOk(client) + + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => + Effect.gen(function*() { + const request = HttpClientRequest.post(`/streams/${opts.name}`, { + body: HttpBody.unsafeJson({ data: opts.data }) + }) + + const response = yield* clientOk.execute(request).pipe( + Effect.flatMap((r) => r.json), + Effect.scoped, + Effect.catchTags({ + RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + ResponseError: (error) => + Effect.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) + }) + ) + + return yield* Schema.decodeUnknown(StreamEvent)(response).pipe( + Effect.mapError((e) => new ClientError(`Invalid event: ${e}`)) + ) + }) + + const subscribe = (opts: { + name: StreamName + offset?: Offset + }): Effect.Effect, ClientError> => { + const offsetParam = opts.offset ? `?offset=${opts.offset === OFFSET_START ? "-1" : opts.offset}` : "" + const url = `/streams/${opts.name}${offsetParam}` + + const request = HttpClientRequest.get(url) + + // Return a stream that handles the SSE connection + const eventStream: Stream.Stream = clientOk.execute(request).pipe( + Effect.map((r) => r.stream), + Stream.unwrapScoped, + Stream.decodeText(), + Stream.pipeThroughChannel(Sse.makeChannel()), + Stream.mapEffect((event) => + Schema.decode(Schema.parseJson(StreamEvent))(event.data).pipe( + Effect.mapError((e) => new ClientError(`Parse error: ${e}`)) + ) + ), + Stream.catchTags({ + RequestError: (error) => Stream.fail(new ClientError(`Request failed: ${error.message}`)), + ResponseError: (error) => Stream.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) + }) + ) + + return Effect.succeed(eventStream) + } + + const list = (): Effect.Effect, ClientError> => + Effect.gen(function*() { + const request = HttpClientRequest.get(`/streams`) + + const response = yield* clientOk.execute(request).pipe( + Effect.flatMap((r) => r.json), + Effect.scoped, + Effect.catchTags({ + RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + ResponseError: (error) => + Effect.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) + }) + ) as Effect.Effect<{ streams: ReadonlyArray }, ClientError> + + return response.streams + }) + + const deleteStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + const request = HttpClientRequest.del(`/streams/${opts.name}`) + + yield* client.execute(request).pipe( + Effect.scoped, + Effect.asVoid, + Effect.catchTags({ + RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + ResponseError: (error) => { + // 204 is success for delete + if (error.response.status === 204) return Effect.void + return Effect.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) + } + }) + ) + }) + + return { + append, + subscribe, + list, + delete: deleteStream + } satisfies StreamClient + }) + +/** Stream client service with auto-daemon support */ +export class StreamClientService extends Effect.Service()( + "@durable-streams/StreamClient", + { + effect: Effect.gen(function*() { + const daemon = yield* DaemonService + const httpClient = yield* HttpClient.HttpClient + + /** Resolve server URL from env, flag, or daemon */ + const resolveServerUrl: Effect.Effect = Effect.gen(function*() { + // Check env var first + const envUrl = process.env.DURABLE_STREAMS_URL + if (envUrl) return envUrl + + // Check if daemon is running + const daemonUrl = yield* daemon.getServerUrl().pipe( + Effect.catchAll(() => Effect.succeed(Option.none())) + ) + if (Option.isSome(daemonUrl)) return daemonUrl.value + + // Auto-start daemon + yield* Effect.log("No server found, starting daemon...") + const pid = yield* daemon.start().pipe( + Effect.mapError((e) => new ClientError(`Failed to start daemon: ${e.message}`)) + ) + yield* Effect.log(`Daemon started (PID ${pid})`) + + // Wait a moment for server to be ready + yield* Effect.sleep("500 millis") + + return `http://localhost:${defaultDaemonConfig.port}` + }) + + const withClient = ( + fn: (client: StreamClient) => Effect.Effect + ): Effect.Effect => + Effect.gen(function*() { + const serverUrl = yield* resolveServerUrl + const client = yield* makeStreamClient({ serverUrl }).pipe( + Effect.provideService(HttpClient.HttpClient, httpClient) + ) + return yield* fn(client) + }) + + return { + append: (opts: { name: StreamName; data: unknown }) => withClient((c) => c.append(opts)), + subscribe: (opts: { name: StreamName; offset?: Offset }) => withClient((c) => c.subscribe(opts)), + list: () => withClient((c) => c.list()), + delete: (opts: { name: StreamName }) => withClient((c) => c.delete(opts)) + } satisfies StreamClient + }), + dependencies: [] + } +) {} + +/** Layer for StreamClient with all dependencies */ +export const StreamClientLive: Layer.Layer = Layer + .effect( + StreamClientService, + Effect.gen(function*() { + const daemon = yield* DaemonService + const httpClient = yield* HttpClient.HttpClient + + const resolveServerUrl: Effect.Effect = Effect.gen(function*() { + const envUrl = process.env.DURABLE_STREAMS_URL + if (envUrl) return envUrl + + const daemonUrl = yield* daemon.getServerUrl().pipe( + Effect.catchAll(() => Effect.succeed(Option.none())) + ) + if (Option.isSome(daemonUrl)) return daemonUrl.value + + yield* Effect.log("No server found, starting daemon...") + const pid = yield* daemon.start().pipe( + Effect.mapError((e) => new ClientError(`Failed to start daemon: ${e.message}`)) + ) + yield* Effect.log(`Daemon started (PID ${pid})`) + yield* Effect.sleep("500 millis") + + return `http://localhost:${defaultDaemonConfig.port}` + }) + + const withClient = ( + fn: (client: StreamClient) => Effect.Effect + ): Effect.Effect => + Effect.gen(function*() { + const serverUrl = yield* resolveServerUrl + const client = yield* makeStreamClient({ serverUrl }).pipe( + Effect.provideService(HttpClient.HttpClient, httpClient) + ) + return yield* fn(client) + }) + + return { + append: (opts: { name: StreamName; data: unknown }) => withClient((c) => c.append(opts)), + subscribe: (opts: { name: StreamName; offset?: Offset }) => withClient((c) => c.subscribe(opts)), + list: () => withClient((c) => c.list()), + delete: (opts: { name: StreamName }) => withClient((c) => c.delete(opts)) + } as unknown as StreamClientService + }) + ) diff --git a/src/durable-streams/daemon.ts b/src/durable-streams/daemon.ts new file mode 100644 index 0000000..780b2e8 --- /dev/null +++ b/src/durable-streams/daemon.ts @@ -0,0 +1,222 @@ +/** + * Daemon Service + * + * Manages a daemonized server process with PID file tracking. + * Uses Node.js spawn with detached mode wrapped in Effect. + */ +import { FileSystem, Path } from "@effect/platform" +import { Effect, Layer, Option, Schema } from "effect" +import { spawn } from "node:child_process" +import { openSync } from "node:fs" + +/** Daemon configuration */ +export interface DaemonConfig { + readonly pidFile: string + readonly logFile: string + readonly port: number +} + +/** Default config - files in PWD */ +export const defaultDaemonConfig: DaemonConfig = { + pidFile: "daemon.pid", + logFile: "daemon.log", + port: 3000 +} + +/** Error for daemon operations */ +export class DaemonError extends Schema.TaggedError()("DaemonError", { + message: Schema.String +}) {} + +/** Daemon service interface */ +export interface Daemon { + /** Start daemon, returns PID. Fails if already running. */ + readonly start: (config?: Partial) => Effect.Effect + + /** Stop daemon gracefully (SIGTERM β†’ SIGKILL). Fails if not running. */ + readonly stop: () => Effect.Effect + + /** Restart daemon, returns new PID */ + readonly restart: (config?: Partial) => Effect.Effect + + /** Check if daemon is running, returns PID if running */ + readonly status: () => Effect.Effect> + + /** Get the server URL for the running daemon */ + readonly getServerUrl: () => Effect.Effect> +} + +/** Create daemon service implementation */ +const makeDaemonImpl = ( + fs: FileSystem.FileSystem, + path: Path.Path +): Daemon => { + const cwd = process.cwd() + + const resolvePath = (file: string) => path.join(cwd, file) + + /** Read PID from file, returns None if not exists or invalid */ + const readPid = (pidFile: string) => + Effect.gen(function*() { + const pidPath = resolvePath(pidFile) + const exists = yield* fs.exists(pidPath) + if (!exists) return Option.none() + + const content = yield* fs.readFileString(pidPath) + const pid = parseInt(content.trim(), 10) + if (isNaN(pid)) return Option.none() + return Option.some(pid) + }).pipe(Effect.orElseSucceed(() => Option.none())) + + /** Check if a process is running */ + const isRunning = (pid: number) => + Effect.sync(() => { + try { + process.kill(pid, 0) + return true + } catch { + return false + } + }) + + /** Send signal to process */ + const kill = (pid: number, signal: NodeJS.Signals) => + Effect.try({ + try: () => process.kill(pid, signal), + catch: () => new DaemonError({ message: `Failed to send ${signal} to PID ${pid}` }) + }) + + /** Spawn daemonized server process */ + const spawnDaemon = (config: DaemonConfig) => + Effect.sync(() => { + const logPath = resolvePath(config.logFile) + const out = openSync(logPath, "a") + + // Spawn the main.ts with "server run" args + const mainScript = path.join(cwd, "src/durable-streams/main.ts") + const child = spawn( + "npx", + ["tsx", mainScript, "server", "run", "--port", String(config.port)], + { + detached: true, + stdio: ["ignore", out, out], + cwd, + env: { ...process.env } + } + ) + child.unref() + return child.pid! + }) + + /** Stop process gracefully */ + const stopProcess = (pidFile: string): Effect.Effect => + Effect.gen(function*() { + const maybePid = yield* readPid(pidFile) + if (Option.isNone(maybePid)) { + return yield* Effect.fail(new DaemonError({ message: "No daemon running" })) + } + + const pid = maybePid.value + + // Try graceful shutdown first + yield* kill(pid, "SIGTERM") + yield* Effect.sleep("2 seconds") + + // Force kill if still running + const stillRunning = yield* isRunning(pid) + if (stillRunning) { + yield* kill(pid, "SIGKILL") + } + + // Clean up PID file + yield* fs.remove(resolvePath(pidFile)).pipe(Effect.ignore) + }) + + const start = (config?: Partial): Effect.Effect => + Effect.gen(function*() { + const cfg = { ...defaultDaemonConfig, ...config } + const pidPath = resolvePath(cfg.pidFile) + + // Check if already running + const existing = yield* readPid(cfg.pidFile) + if (Option.isSome(existing)) { + const running = yield* isRunning(existing.value) + if (running) { + return yield* Effect.fail( + new DaemonError({ message: `Daemon already running (PID ${existing.value})` }) + ) + } + } + + // Spawn daemon + const pid = yield* spawnDaemon(cfg) + yield* fs.writeFileString(pidPath, String(pid)).pipe(Effect.ignore) + + // Write port to a separate file for client discovery + const portPath = resolvePath("daemon.port") + yield* fs.writeFileString(portPath, String(cfg.port)).pipe(Effect.ignore) + + return pid + }) + + const stop = (): Effect.Effect => stopProcess(defaultDaemonConfig.pidFile) + + const restart = (config?: Partial): Effect.Effect => + Effect.gen(function*() { + yield* stopProcess(defaultDaemonConfig.pidFile).pipe(Effect.ignore) + return yield* start(config) + }) + + const status = (): Effect.Effect> => + Effect.gen(function*() { + const maybePid = yield* readPid(defaultDaemonConfig.pidFile) + if (Option.isNone(maybePid)) return Option.none() + + const running = yield* isRunning(maybePid.value) + return running ? maybePid : Option.none() + }) + + const getServerUrl = (): Effect.Effect> => + Effect.gen(function*() { + const maybePid = yield* status() + if (Option.isNone(maybePid)) return Option.none() + + // Read port from file + const portPath = resolvePath("daemon.port") + const exists = yield* fs.exists(portPath).pipe(Effect.orElseSucceed(() => false)) + if (!exists) return Option.some(`http://localhost:${defaultDaemonConfig.port}`) + + const portStr = yield* fs.readFileString(portPath).pipe( + Effect.orElseSucceed(() => String(defaultDaemonConfig.port)) + ) + const port = parseInt(portStr.trim(), 10) + return Option.some(`http://localhost:${isNaN(port) ? defaultDaemonConfig.port : port}`) + }) + + return { + start, + stop, + restart, + status, + getServerUrl + } +} + +/** Daemon service tag and implementation */ +export class DaemonService extends Effect.Service()("@durable-streams/Daemon", { + effect: Effect.gen(function*() { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + return makeDaemonImpl(fs, path) + }), + dependencies: [] +}) { + static readonly Live: Layer.Layer = Layer.effect( + DaemonService, + Effect.gen(function*() { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + return makeDaemonImpl(fs, path) as unknown as DaemonService + }) + ) +} diff --git a/src/durable-streams/main.ts b/src/durable-streams/main.ts index d98ed9e..e26264a 100644 --- a/src/durable-streams/main.ts +++ b/src/durable-streams/main.ts @@ -1,15 +1,32 @@ /** * Durable Streams CLI Entry Point * - * Usage: npx tsx src/durable-streams/main.ts start [--port 3000] + * Usage: + * npx tsx src/durable-streams/main.ts server run [--port 3000] + * npx tsx src/durable-streams/main.ts server start [--port 3000] + * npx tsx src/durable-streams/main.ts server stop + * npx tsx src/durable-streams/main.ts server status + * npx tsx src/durable-streams/main.ts stream subscribe + * npx tsx src/durable-streams/main.ts stream append -m "hello" */ -import { NodeContext, NodeRuntime } from "@effect/platform-node" +import { NodeContext, NodeHttpClient, NodeRuntime } from "@effect/platform-node" import { Cause, Effect, Layer, Logger, LogLevel } from "effect" import { run } from "./cli.ts" +import { StreamClientLive } from "./client.ts" +import { DaemonService } from "./daemon.ts" const loggingLayer = Logger.minimumLogLevel(LogLevel.Info) -const mainLayer = Layer.mergeAll(loggingLayer, NodeContext.layer) +// Build the layer stack with proper dependencies +// DaemonService.Live needs FileSystem + Path (from NodeContext) +// StreamClientLive needs HttpClient + DaemonService +const servicesLayer = StreamClientLive.pipe( + Layer.provideMerge(DaemonService.Live), + Layer.provideMerge(NodeHttpClient.layer), + Layer.provideMerge(NodeContext.layer) +) + +const mainLayer = Layer.mergeAll(loggingLayer, servicesLayer) run(process.argv).pipe( Effect.provide(mainLayer), From c25456883540af7f04f40942f33ffc0b06bea55f Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:26:27 +0000 Subject: [PATCH 05/15] Add FileSystem persistence and stream get command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Storage.FileSystem adapter for persistent event storage - Events stored in .iterate/streams/*.json - Each stream gets its own JSON file - Update daemon to use .iterate/ for all runtime files - PID, port, and log files now in .iterate/ - Add stream get CLI command for fetching historic events - One-shot fetch (vs subscribe which waits for live events) - Supports --offset and --limit options - Add GET /streams/:name/events HTTP endpoint - Returns historic events without SSE streaming - Supports offset and limit query params - Add StreamManagerService.Live layer - Properly injects Storage dependency - FileSystem storage used in production server - Write e2e test proving persistence across server restarts - Events survive server stop/start - Offsets continue monotonically after restart - Update README with new commands and file locations πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .gitignore | 3 + bun.lock | 1170 +++++++++++++++++++ src/durable-streams/README.md | 44 +- src/durable-streams/cli.ts | 66 +- src/durable-streams/client.ts | 43 + src/durable-streams/daemon.ts | 21 +- src/durable-streams/http-routes.ts | 46 + src/durable-streams/persistence.e2e.test.ts | 224 ++++ src/durable-streams/storage.ts | 188 ++- src/durable-streams/stream-manager.ts | 115 +- src/durable-streams/stream.ts | 29 + 11 files changed, 1921 insertions(+), 28 deletions(-) create mode 100644 bun.lock create mode 100644 src/durable-streams/persistence.e2e.test.ts diff --git a/.gitignore b/.gitignore index aad0d00..d71a818 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Agent runtime data (contexts, logs) .mini-agent/ opensrc/ + +# durable-streams runtime data +.iterate/ diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..7cc1453 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1170 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "effect-tasks", + "dependencies": { + "@effect/ai": "^0.32.1", + "@effect/ai-anthropic": "^0.22.0", + "@effect/ai-google": "^0.11.0", + "@effect/ai-openai": "^0.35.0", + "@effect/cli": "^0.72.1", + "@effect/opentelemetry": "^0.59.1", + "@effect/platform": "^0.93.5", + "@effect/platform-node": "^0.104.0", + "@effect/rpc": "^0.72.2", + "@effect/rpc-http": "^0.52.4", + "@opentelemetry/otlp-transformer": "^0.208.0", + "@opentelemetry/sdk-trace-base": "^2.2.0", + "@opentui/core": "^0.1.55", + "@opentui/react": "^0.1.55", + "effect": "^3.19.8", + "react": "19", + "react-dom": "19", + "yaml": "^2.7.0", + }, + "devDependencies": { + "@effect/eslint-plugin": "^0.3.2", + "@effect/language-service": "^0.57.1", + "@effect/vitest": "^0.25.1", + "@eslint/compat": "^1.1.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.10.0", + "@types/node": "^22", + "@types/react": "19", + "@typescript-eslint/eslint-plugin": "^8.4.0", + "@typescript-eslint/parser": "^8.4.0", + "eslint": "^9.10.0", + "eslint-import-resolver-typescript": "^3.6.3", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-sort-destructure-keys": "^2.0.0", + "semver": "^7.7.3", + "tsx": "^4.19.0", + "tuistory": "^0.0.2", + "vitest": "^3.2.0", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@anthropic-ai/tokenizer": ["@anthropic-ai/tokenizer@0.0.4", "", { "dependencies": { "@types/node": "18.19.130", "tiktoken": "1.0.22" } }, "sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g=="], + + "@dimforge/rapier2d-simd-compat": ["@dimforge/rapier2d-simd-compat@0.17.3", "", {}, "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg=="], + + "@dprint/formatter": ["@dprint/formatter@0.4.1", "", {}, "sha512-IB/GXdlMOvi0UhQQ9mcY15Fxcrc2JPadmo6tqefCNV0bptFq7YBpggzpqYXldBXDa04CbKJ+rDwO2eNRPE2+/g=="], + + "@dprint/typescript": ["@dprint/typescript@0.91.8", "", {}, "sha512-tuKn4leCPItox1O4uunHcQF0QllDCvPWklnNQIh2PiWWVtRAGltJJnM4Cwj5AciplosD1Hiz7vAY3ew3crLb3A=="], + + "@effect/ai": ["@effect/ai@0.32.1", "", { "dependencies": { "find-my-way-ts": "0.1.6" }, "peerDependencies": { "@effect/experimental": "0.57.11", "@effect/platform": "0.93.8", "@effect/rpc": "0.72.2", "effect": "3.19.14" } }, "sha512-6qedQhnHigkRjNsUGaXKqvsbwhvQH3Ez3aePeROBRJi0c8q9tnR1E8qhQrTSjVPWcrv9AFtP0NRTr6iAHujGWQ=="], + + "@effect/ai-anthropic": ["@effect/ai-anthropic@0.22.0", "", { "dependencies": { "@anthropic-ai/tokenizer": "0.0.4" }, "peerDependencies": { "@effect/ai": "0.32.1", "@effect/experimental": "0.57.11", "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-uzgXeC8iAMSVtyFi/HxYPQGBcCBxzbgXKt/wxeyB1FlQGx4Uxn1tBy1dhBc2RvbVRYlT8MIPOCxLcUj1/995zg=="], + + "@effect/ai-google": ["@effect/ai-google@0.11.1", "", { "peerDependencies": { "@effect/ai": "0.32.1", "@effect/experimental": "0.57.11", "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-mW8409EBFHSqbNLio/eMlGJdi0UzBJfRfShA5guJHCkWIF7Xoudh0LdRuikt8dIobZlBl1/tdeCyWLaRBeHYyg=="], + + "@effect/ai-openai": ["@effect/ai-openai@0.35.0", "", { "dependencies": { "gpt-tokenizer": "2.9.0" }, "peerDependencies": { "@effect/ai": "0.32.1", "@effect/experimental": "0.57.11", "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-cB5cXITMsO+bfesIDqJRCiAGH8r/P5ts32HwZ5HbAvUscrqFgZBQDIsypiOCHL2teWFaBbXLdiqbF/lL4Ct8wg=="], + + "@effect/cli": ["@effect/cli@0.72.1", "", { "dependencies": { "ini": "4.1.3", "toml": "3.0.0", "yaml": "2.8.2" }, "peerDependencies": { "@effect/platform": "0.93.8", "@effect/printer": "0.47.0", "@effect/printer-ansi": "0.47.0", "effect": "3.19.14" } }, "sha512-HGDMGD23TxFW9tCSX6g+M2u0robikMA0mP0SqeJMj7FWXTdcQ+cQsJE99bxi9iu+5YID7MIrVJMs8TUwXUV2sg=="], + + "@effect/cluster": ["@effect/cluster@0.56.1", "", { "dependencies": { "kubernetes-types": "1.30.0" }, "peerDependencies": { "@effect/platform": "0.93.8", "@effect/rpc": "0.72.2", "@effect/sql": "0.49.0", "@effect/workflow": "0.16.0", "effect": "3.19.14" } }, "sha512-gnrsH6kfrUjn+82j/bw1IR4yFqJqV8tc7xZvrbJPRgzANycc6K1hu3LMg548uYbUkTzD8YYyqrSatMO1mkQpzw=="], + + "@effect/eslint-plugin": ["@effect/eslint-plugin@0.3.2", "", { "dependencies": { "@dprint/formatter": "0.4.1", "@dprint/typescript": "0.91.8", "prettier-linter-helpers": "1.0.1" } }, "sha512-c4Vs9t3r54A4Zpl+wo8+PGzZz3JWYsip41H+UrebRLjQ2Hk/ap63IeCgN/HWcYtxtyhRopjp7gW9nOQ2Snbl+g=="], + + "@effect/experimental": ["@effect/experimental@0.57.11", "", { "dependencies": { "uuid": "11.1.0" }, "peerDependencies": { "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-M5uug3Drs/gyTHLfA+XzcIZQGUEV/Jn5yi1POki4oZswhpzNmsVTHl4THpxAordRKwa5lFvTSlsRP684YH7pSw=="], + + "@effect/language-service": ["@effect/language-service@0.57.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-uWzYN+aHl4KfZHGmDxa3+OjV/mk9gMUycIyB8SYvyDiny3lXBtmRFdRVnw6TYL1P5EBfg4N09+lO/0ECRREVXQ=="], + + "@effect/opentelemetry": ["@effect/opentelemetry@0.59.3", "", { "optionalDependencies": { "@opentelemetry/api": "1.9.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@effect/platform": "0.93.8", "@opentelemetry/semantic-conventions": "1.38.0", "effect": "3.19.14" } }, "sha512-0Ozqxc1AMMnOu18Iqqcf8rokyrdvHCYaJzUEDQpi5pt7z+C5zhv5GjFMI9OxMgGRBiGxsY63ZUth+Q6w8F8KhQ=="], + + "@effect/platform": ["@effect/platform@0.93.8", "", { "dependencies": { "find-my-way-ts": "0.1.6", "msgpackr": "1.11.8", "multipasta": "0.2.7" }, "peerDependencies": { "effect": "3.19.14" } }, "sha512-xTEy6fyTy4ijmFC3afKgtvYtn/JyPoIov4ZSUWJZUv3VeOcUPNGrrqG6IJlWkXs3NhvSywKv7wc1kw3epCQVZw=="], + + "@effect/platform-node": ["@effect/platform-node@0.104.0", "", { "dependencies": { "@effect/platform-node-shared": "0.57.0", "mime": "3.0.0", "undici": "7.18.2", "ws": "8.19.0" }, "peerDependencies": { "@effect/cluster": "0.56.1", "@effect/platform": "0.93.8", "@effect/rpc": "0.72.2", "@effect/sql": "0.49.0", "effect": "3.19.14" } }, "sha512-2ZkUDDTxLD95ARdYIKBx4tdIIgqA3cwb3jlnVVBxmHUf0Pg5N2HdMuD0Q+CXQ7Q94FDwnLW3ZvaSfxDh6FvrNw=="], + + "@effect/platform-node-shared": ["@effect/platform-node-shared@0.57.0", "", { "dependencies": { "@parcel/watcher": "2.5.1", "multipasta": "0.2.7", "ws": "8.19.0" }, "peerDependencies": { "@effect/cluster": "0.56.1", "@effect/platform": "0.93.8", "@effect/rpc": "0.72.2", "@effect/sql": "0.49.0", "effect": "3.19.14" } }, "sha512-QXuvmLNlABCQLcTl+lN1YPhKosR6KqArPYjC2reU0fb5lroCo3YRb/aGpXIgLthHzQL8cLU5XMGA3Cu5hKY2Tw=="], + + "@effect/printer": ["@effect/printer@0.47.0", "", { "peerDependencies": { "@effect/typeclass": "0.38.0", "effect": "3.19.14" } }, "sha512-VgR8e+YWWhMEAh9qFOjwiZ3OXluAbcVLIOtvp2S5di1nSrPOZxj78g8LE77JSvyfp5y5bS2gmFW+G7xD5uU+2Q=="], + + "@effect/printer-ansi": ["@effect/printer-ansi@0.47.0", "", { "dependencies": { "@effect/printer": "0.47.0" }, "peerDependencies": { "@effect/typeclass": "0.38.0", "effect": "3.19.14" } }, "sha512-tDEQ9XJpXDNYoWMQJHFRMxKGmEOu6z32x3Kb8YLOV5nkauEKnKmWNs7NBp8iio/pqoJbaSwqDwUg9jXVquxfWQ=="], + + "@effect/rpc": ["@effect/rpc@0.72.2", "", { "dependencies": { "msgpackr": "1.11.8" }, "peerDependencies": { "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-BmTXybXCOq96D2r9mvSW/YdiTQs5CStnd4II+lfVKrMr3pMNERKLZ2LG37Tfm4Sy3Q8ire6IVVKO/CN+VR0uQQ=="], + + "@effect/rpc-http": ["@effect/rpc-http@0.52.4", "", { "dependencies": { "@effect/rpc": "0.54.4" }, "peerDependencies": { "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-QpIaTlqo5zYO+ujFqMMv3g/9y4CByuez7taMFCL+2mihlDCKt9s+vIEbxQYzn1r0/ppDYfZ/d5vgEJBpddcgxg=="], + + "@effect/sql": ["@effect/sql@0.49.0", "", { "dependencies": { "uuid": "11.1.0" }, "peerDependencies": { "@effect/experimental": "0.57.11", "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-9UEKR+z+MrI/qMAmSvb/RiD9KlgIazjZUCDSpwNgm0lEK9/Q6ExEyfziiYFVCPiptp52cBw8uBHRic8hHnwqXA=="], + + "@effect/typeclass": ["@effect/typeclass@0.38.0", "", { "peerDependencies": { "effect": "3.19.14" } }, "sha512-lMUcJTRtG8KXhXoczapZDxbLK5os7M6rn0zkvOgncJW++A0UyelZfMVMKdT5R+fgpZcsAU/1diaqw3uqLJwGxA=="], + + "@effect/vitest": ["@effect/vitest@0.25.1", "", { "peerDependencies": { "effect": "3.19.14", "vitest": "3.2.4" } }, "sha512-OMYvOU8iGed8GZXxgVBXlYtjG+jwWj5cJxFk0hOHOfTbCHXtdCMEWlXNba5zxbE7dBnW4srbnSYrP/NGGTC3qQ=="], + + "@effect/workflow": ["@effect/workflow@0.16.0", "", { "peerDependencies": { "@effect/experimental": "0.57.11", "@effect/platform": "0.93.8", "@effect/rpc": "0.72.2", "effect": "3.19.14" } }, "sha512-MiAdlxx3TixkgHdbw+Yf1Z3tHAAE0rOQga12kIydJqj05Fnod+W/I+kQGRMY/XWRg+QUsVxhmh1qTr7Ype6lrw=="], + + "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "2.8.1" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "3.4.3" }, "peerDependencies": { "eslint": "9.39.2" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/compat": ["@eslint/compat@1.4.1", "", { "dependencies": { "@eslint/core": "0.17.0" }, "optionalDependencies": { "eslint": "9.39.2" } }, "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "2.1.7", "debug": "4.4.3", "minimatch": "3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "6.12.6", "debug": "4.4.3", "espree": "10.4.0", "globals": "14.0.0", "ignore": "5.3.2", "import-fresh": "3.3.1", "js-yaml": "4.1.1", "minimatch": "3.1.2", "strip-json-comments": "3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], + + "@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "0.17.0", "levn": "0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "0.19.1", "@humanwhocodes/retry": "0.4.3" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "3.0.0", "exif-parser": "0.1.12", "file-type": "16.5.4", "mime": "3.0.0" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="], + + "@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="], + + "@jimp/file-ops": ["@jimp/file-ops@1.6.0", "", {}, "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ=="], + + "@jimp/js-bmp": ["@jimp/js-bmp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "bmp-ts": "1.0.9" } }, "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw=="], + + "@jimp/js-gif": ["@jimp/js-gif@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "gifwrap": "0.10.1", "omggif": "1.0.10" } }, "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g=="], + + "@jimp/js-jpeg": ["@jimp/js-jpeg@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "jpeg-js": "0.4.4" } }, "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA=="], + + "@jimp/js-png": ["@jimp/js-png@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "pngjs": "7.0.0" } }, "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg=="], + + "@jimp/js-tiff": ["@jimp/js-tiff@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "utif2": "4.1.0" } }, "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw=="], + + "@jimp/plugin-blit": ["@jimp/plugin-blit@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "3.25.76" } }, "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA=="], + + "@jimp/plugin-blur": ["@jimp/plugin-blur@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw=="], + + "@jimp/plugin-circle": ["@jimp/plugin-circle@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "3.25.76" } }, "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw=="], + + "@jimp/plugin-color": ["@jimp/plugin-color@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "tinycolor2": "1.6.0", "zod": "3.25.76" } }, "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA=="], + + "@jimp/plugin-contain": ["@jimp/plugin-contain@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "3.25.76" } }, "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ=="], + + "@jimp/plugin-cover": ["@jimp/plugin-cover@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "zod": "3.25.76" } }, "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA=="], + + "@jimp/plugin-crop": ["@jimp/plugin-crop@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "3.25.76" } }, "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang=="], + + "@jimp/plugin-displace": ["@jimp/plugin-displace@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "3.25.76" } }, "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q=="], + + "@jimp/plugin-dither": ["@jimp/plugin-dither@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0" } }, "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ=="], + + "@jimp/plugin-fisheye": ["@jimp/plugin-fisheye@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "3.25.76" } }, "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA=="], + + "@jimp/plugin-flip": ["@jimp/plugin-flip@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "3.25.76" } }, "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg=="], + + "@jimp/plugin-hash": ["@jimp/plugin-hash@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "any-base": "1.1.0" } }, "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q=="], + + "@jimp/plugin-mask": ["@jimp/plugin-mask@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "3.25.76" } }, "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA=="], + + "@jimp/plugin-print": ["@jimp/plugin-print@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/types": "1.6.0", "parse-bmfont-ascii": "1.0.6", "parse-bmfont-binary": "1.0.6", "parse-bmfont-xml": "1.1.6", "simple-xml-to-json": "1.2.3", "zod": "3.25.76" } }, "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A=="], + + "@jimp/plugin-quantize": ["@jimp/plugin-quantize@1.6.0", "", { "dependencies": { "image-q": "4.0.0", "zod": "3.25.76" } }, "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg=="], + + "@jimp/plugin-resize": ["@jimp/plugin-resize@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "zod": "3.25.76" } }, "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA=="], + + "@jimp/plugin-rotate": ["@jimp/plugin-rotate@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "3.25.76" } }, "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw=="], + + "@jimp/plugin-threshold": ["@jimp/plugin-threshold@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "3.25.76" } }, "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w=="], + + "@jimp/types": ["@jimp/types@1.6.0", "", { "dependencies": { "zod": "3.25.76" } }, "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg=="], + + "@jimp/utils": ["@jimp/utils@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "tinycolor2": "1.6.0" } }, "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], + + "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], + + "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="], + + "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="], + + "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="], + + "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "1.8.1", "@emnapi/runtime": "1.8.1", "@tybys/wasm-util": "0.10.1" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api": "1.9.0" } }, "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg=="], + + "@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.38.0" }, "peerDependencies": { "@opentelemetry/api": "1.9.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="], + + "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "7.5.4" }, "peerDependencies": { "@opentelemetry/api": "1.9.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="], + + "@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "1.38.0" }, "peerDependencies": { "@opentelemetry/api": "1.9.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], + + "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "1.9.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="], + + "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "1.9.0" } }, "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw=="], + + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "1.38.0" }, "peerDependencies": { "@opentelemetry/api": "1.9.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="], + + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.38.0", "", {}, "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg=="], + + "@opentui/core": ["@opentui/core@0.1.69", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "0.17.3", "@opentui/core-darwin-arm64": "0.1.69", "@opentui/core-darwin-x64": "0.1.69", "@opentui/core-linux-arm64": "0.1.69", "@opentui/core-linux-x64": "0.1.69", "@opentui/core-win32-arm64": "0.1.69", "@opentui/core-win32-x64": "0.1.69", "bun-webgpu": "0.1.4", "planck": "1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-BcEFnAuMq4vgfb+zxOP/l+NO1AS3fVHkYjn+E8Wpmaxr0AzWNTi2NPAMtQf+Wqufxo0NYh0gY4c9B6n8OxTjGw=="], + + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.69", "", { "os": "darwin", "cpu": "arm64" }, "sha512-d9RPAh84O2XIyMw+7+X0fEyi+4KH5sPk9AxLze8GHRBGOzkRunqagFCLBrN5VFs2e2nbhIYtjMszo7gcpWyh7g=="], + + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.69", "", { "os": "darwin", "cpu": "x64" }, "sha512-41K9zkL2IG0ahL+8Gd+e9ulMrnJF6lArPzG7grjWzo+FWEZwvw0WLCO1/Gn5K85G8Yx7gQXkZOUaw1BmHjxoRw=="], + + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.69", "", { "os": "linux", "cpu": "arm64" }, "sha512-IcUjwjuIpX3BBG1a9kjMqWrHYCFHAVfjh5nIRozWZZoqaczLzJb3nJeF2eg8aDeIoGhXvERWB1r1gmqPW8u3vQ=="], + + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.69", "", { "os": "linux", "cpu": "x64" }, "sha512-5S9vqEIq7q+MEdp4cT0HLegBWu0pWLcletHZL80bsLbJt9OT8en3sQmL5bvas9sIuyeBFru9bfCmrQ/gnVTTiA=="], + + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.69", "", { "os": "win32", "cpu": "arm64" }, "sha512-eSKcGwbcnJJPtrTFJI7STZ7inSYeedHS0swwjZhh9SADAruEz08intamunOslffv5+mnlvRp7UBGK35cMjbv/w=="], + + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.69", "", { "os": "win32", "cpu": "x64" }, "sha512-OjG/0jqYXURqbbUwNgSPrBA6yuKF3OOFh8JSG7VvzoYHJFJRmwVWY0fztWv/hgGHe354ti37c7JDJBQ44HOCdA=="], + + "@opentui/react": ["@opentui/react@0.1.69", "", { "dependencies": { "@opentui/core": "0.1.69", "react-reconciler": "0.32.0" }, "peerDependencies": { "react": "19.2.3", "react-devtools-core": "7.0.1", "ws": "8.19.0" } }, "sha512-E+kcCnGI6lONJZC90m4T8n0tWZ2dUz2Nx+lkNqgnXt+pvxN0HIdW3MAsOxntkYFw5e5BgGv8E3hRuBhoyCatgA=="], + + "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "1.0.3", "is-glob": "4.0.3", "micromatch": "4.0.8", "node-addon-api": "7.1.1" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], + + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], + + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="], + + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="], + + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="], + + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="], + + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="], + + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="], + + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="], + + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="], + + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="], + + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="], + + "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="], + + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "1.1.2", "@protobufjs/inquire": "1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "4.0.2", "assertion-error": "2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], + + "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "3.2.3" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.52.0", "", { "dependencies": { "@eslint-community/regexpp": "4.12.2", "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/type-utils": "8.52.0", "@typescript-eslint/utils": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0", "ignore": "7.0.5", "natural-compare": "1.4.0", "ts-api-utils": "2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "8.52.0", "eslint": "9.39.2", "typescript": "5.9.3" } }, "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.52.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/types": "8.52.0", "@typescript-eslint/typescript-estree": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0", "debug": "4.4.3" }, "peerDependencies": { "eslint": "9.39.2", "typescript": "5.9.3" } }, "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.52.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "8.52.0", "@typescript-eslint/types": "8.52.0", "debug": "4.4.3" }, "peerDependencies": { "typescript": "5.9.3" } }, "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.52.0", "", { "dependencies": { "@typescript-eslint/types": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0" } }, "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.52.0", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.52.0", "", { "dependencies": { "@typescript-eslint/types": "8.52.0", "@typescript-eslint/typescript-estree": "8.52.0", "@typescript-eslint/utils": "8.52.0", "debug": "4.4.3", "ts-api-utils": "2.4.0" }, "peerDependencies": { "eslint": "9.39.2", "typescript": "5.9.3" } }, "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.52.0", "", {}, "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.52.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.52.0", "@typescript-eslint/tsconfig-utils": "8.52.0", "@typescript-eslint/types": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0", "debug": "4.4.3", "minimatch": "9.0.5", "semver": "7.7.3", "tinyglobby": "0.2.15", "ts-api-utils": "2.4.0" }, "peerDependencies": { "typescript": "5.9.3" } }, "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.52.0", "", { "dependencies": { "@eslint-community/eslint-utils": "4.9.1", "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/types": "8.52.0", "@typescript-eslint/typescript-estree": "8.52.0" }, "peerDependencies": { "eslint": "9.39.2", "typescript": "5.9.3" } }, "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.52.0", "", { "dependencies": { "@typescript-eslint/types": "8.52.0", "eslint-visitor-keys": "4.2.1" } }, "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ=="], + + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], + + "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="], + + "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="], + + "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "0.2.12" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], + + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "5.2.3", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "5.3.3", "tinyrainbow": "2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "3.0.3", "magic-string": "0.30.21" }, "optionalDependencies": { "vite": "7.3.1" } }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "2.0.3", "strip-literal": "3.1.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + + "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "0.30.21", "pathe": "2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "4.0.4" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "3.2.1", "tinyrainbow": "2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + + "@webgpu/types": ["@webgpu/types@0.1.68", "", {}, "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA=="], + + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "5.0.1" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "8.15.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "3.1.3", "fast-json-stable-stringify": "2.1.0", "json-schema-traverse": "0.4.1", "uri-js": "4.4.1" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "any-base": ["any-base@1.1.0", "", {}, "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "is-array-buffer": "3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-object-atoms": "1.1.1", "get-intrinsic": "1.3.0", "is-string": "1.1.1", "math-intrinsics": "1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "es-shim-unscopables": "1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-shim-unscopables": "1.1.0" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-shim-unscopables": "1.1.0" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "1.0.2", "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "is-array-buffer": "3.0.5" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "1.1.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "await-to-js": ["await-to-js@3.0.0", "", {}, "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bmp-ts": ["bmp-ts@1.0.9", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "1.0.2", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "1.5.1", "ieee754": "1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="], + + "bun-pty": ["bun-pty@0.4.6", "", {}, "sha512-sMAZgcxuCe4A9iPdPNJFiWu3ZVqkxQbVlEXu9llxZoEll6cLVXx0rXUiFrM4WWgQsmacM+5xNvyzgLAH0NAA3A=="], + + "bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "0.1.68" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "0.1.4", "bun-webgpu-darwin-x64": "0.1.4", "bun-webgpu-linux-x64": "0.1.4", "bun-webgpu-win32-x64": "0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="], + + "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eDgLN9teKTfmvrCqgwwmWNsNszxYs7IZdCqk0S1DCarvMhr4wcajoSBlA/nQA0/owwLduPTS8xxCnQp4/N/gDg=="], + + "bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-X+PjwJUWenUmdQBP8EtdItMyieQ6Nlpn+BH518oaouDiSnWj5+b0Y7DNDZJq7Ezom4EaxmqL/uGYZK3aCQ7CXg=="], + + "bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-zMLs2YIGB+/jxrYFXaFhVKX/GBt05UTF45lc9srcHc9JXGjEj+12CIo1CHLTAWatXMTqt0Jsu6ukWEoWVT/ayA=="], + + "bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-Z5yAK28xrcm8Wb5k7TZ8FJKpOI/r+aVCRdlHYAqI2SDJFN3nD4mJs900X6kNVmG/xFzb5yOuKVYWGg+6ZXWbyA=="], + + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-define-property": "1.0.1", "get-intrinsic": "1.3.0", "set-function-length": "1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "1.3.0", "function-bind": "1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "get-intrinsic": "1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "2.0.1", "check-error": "2.1.3", "deep-eql": "5.0.2", "loupe": "3.2.1", "pathval": "2.0.1" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "4.3.0", "supports-color": "7.2.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "3.1.1", "shebang-command": "2.0.0", "which": "2.0.2" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "1.0.1", "es-errors": "1.3.0", "gopd": "1.2.0" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "1.1.4", "has-property-descriptors": "1.0.2", "object-keys": "1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], + + "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "2.0.3" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-errors": "1.3.0", "gopd": "1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "effect": ["effect@3.19.14", "", { "dependencies": { "@standard-schema/spec": "1.1.0", "fast-check": "3.23.2" } }, "sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA=="], + + "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "1.0.2", "arraybuffer.prototype.slice": "1.0.4", "available-typed-arrays": "1.0.7", "call-bind": "1.0.8", "call-bound": "1.0.4", "data-view-buffer": "1.0.2", "data-view-byte-length": "1.0.2", "data-view-byte-offset": "1.0.1", "es-define-property": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "es-set-tostringtag": "2.1.0", "es-to-primitive": "1.3.0", "function.prototype.name": "1.1.8", "get-intrinsic": "1.3.0", "get-proto": "1.0.1", "get-symbol-description": "1.1.0", "globalthis": "1.0.4", "gopd": "1.2.0", "has-property-descriptors": "1.0.2", "has-proto": "1.2.0", "has-symbols": "1.1.0", "hasown": "2.0.2", "internal-slot": "1.1.0", "is-array-buffer": "3.0.5", "is-callable": "1.2.7", "is-data-view": "1.0.2", "is-negative-zero": "2.0.3", "is-regex": "1.2.1", "is-set": "2.0.3", "is-shared-array-buffer": "1.0.4", "is-string": "1.1.1", "is-typed-array": "1.1.15", "is-weakref": "1.1.1", "math-intrinsics": "1.1.0", "object-inspect": "1.13.4", "object-keys": "1.1.1", "object.assign": "4.1.7", "own-keys": "1.0.1", "regexp.prototype.flags": "1.5.4", "safe-array-concat": "1.1.3", "safe-push-apply": "1.0.0", "safe-regex-test": "1.1.0", "set-proto": "1.0.0", "stop-iteration-iterator": "1.1.0", "string.prototype.trim": "1.2.10", "string.prototype.trimend": "1.0.9", "string.prototype.trimstart": "1.0.8", "typed-array-buffer": "1.0.3", "typed-array-byte-length": "1.0.3", "typed-array-byte-offset": "1.0.4", "typed-array-length": "1.0.7", "unbox-primitive": "1.1.0", "which-typed-array": "1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "has-tostringtag": "1.0.2", "hasown": "2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "1.2.7", "is-date-object": "1.1.0", "is-symbol": "1.1.1" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "4.9.1", "@eslint-community/regexpp": "4.12.2", "@eslint/config-array": "0.21.1", "@eslint/config-helpers": "0.4.2", "@eslint/core": "0.17.0", "@eslint/eslintrc": "3.3.3", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "0.4.1", "@humanfs/node": "0.16.7", "@humanwhocodes/module-importer": "1.0.1", "@humanwhocodes/retry": "0.4.3", "@types/estree": "1.0.8", "ajv": "6.12.6", "chalk": "4.1.2", "cross-spawn": "7.0.6", "debug": "4.4.3", "escape-string-regexp": "4.0.0", "eslint-scope": "8.4.0", "eslint-visitor-keys": "4.2.1", "espree": "10.4.0", "esquery": "1.7.0", "esutils": "2.0.3", "fast-deep-equal": "3.1.3", "file-entry-cache": "8.0.0", "find-up": "5.0.0", "glob-parent": "6.0.2", "ignore": "5.3.2", "imurmurhash": "0.1.4", "is-glob": "4.0.3", "json-stable-stringify-without-jsonify": "1.0.1", "lodash.merge": "4.6.2", "minimatch": "3.1.2", "natural-compare": "1.4.0", "optionator": "0.9.4" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "3.2.7", "is-core-module": "2.16.1", "resolve": "1.22.11" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "4.4.3", "get-tsconfig": "4.13.0", "is-bun-module": "2.0.0", "stable-hash": "0.0.5", "tinyglobby": "0.2.15", "unrs-resolver": "1.11.1" }, "optionalDependencies": { "eslint-plugin-import": "2.32.0" }, "peerDependencies": { "eslint": "9.39.2" } }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + + "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "3.2.7" }, "optionalDependencies": { "@typescript-eslint/parser": "8.52.0", "eslint": "9.39.2", "eslint-import-resolver-node": "0.3.9", "eslint-import-resolver-typescript": "3.10.1" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "1.1.0", "array-includes": "3.1.9", "array.prototype.findlastindex": "1.2.6", "array.prototype.flat": "1.3.3", "array.prototype.flatmap": "1.3.3", "debug": "3.2.7", "doctrine": "2.1.0", "eslint-import-resolver-node": "0.3.9", "eslint-module-utils": "2.12.1", "hasown": "2.0.2", "is-core-module": "2.16.1", "is-glob": "4.0.3", "minimatch": "3.1.2", "object.fromentries": "2.0.8", "object.groupby": "1.0.3", "object.values": "1.2.1", "semver": "6.3.1", "string.prototype.trimend": "1.0.9", "tsconfig-paths": "3.15.0" }, "optionalDependencies": { "@typescript-eslint/parser": "8.52.0" }, "peerDependencies": { "eslint": "9.39.2" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], + + "eslint-plugin-simple-import-sort": ["eslint-plugin-simple-import-sort@12.1.1", "", { "peerDependencies": { "eslint": "9.39.2" } }, "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA=="], + + "eslint-plugin-sort-destructure-keys": ["eslint-plugin-sort-destructure-keys@2.0.0", "", { "dependencies": { "natural-compare-lite": "1.4.0" }, "peerDependencies": { "eslint": "9.39.2" } }, "sha512-4w1UQCa3o/YdfWaLr9jY8LfGowwjwjmwClyFLxIsToiyIdZMq3x9Ti44nDn34DtTPP7PWg96tUONKVmATKhYGQ=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "4.3.0", "estraverse": "5.3.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "8.15.0", "acorn-jsx": "5.3.2", "eslint-visitor-keys": "4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "5.3.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "5.3.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "1.0.8" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + + "exif-parser": ["exif-parser@0.1.12", "", {}, "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="], + + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + + "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fdir": ["fdir@6.5.0", "", { "optionalDependencies": { "picomatch": "4.0.3" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "4.0.1" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "3.0.4", "strtok3": "6.3.0", "token-types": "4.2.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "6.0.0", "path-exists": "4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "3.3.3", "keyv": "4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "functions-have-names": "1.2.3", "hasown": "2.0.2", "is-callable": "1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-define-property": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "function-bind": "1.1.2", "get-proto": "1.0.1", "gopd": "1.2.0", "has-symbols": "1.1.0", "hasown": "2.0.2", "math-intrinsics": "1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "1.0.1", "es-object-atoms": "1.1.1" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], + + "ghostty-opentui": ["ghostty-opentui@1.3.11", "", { "dependencies": { "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core": "0.1.69" } }, "sha512-taKOhQD65dip/GBi2eDicyS6Z+m7T6CAWyUcFUVP3nX+JKTCiOwfncGqhnqtSa8VE3mG6VHaPzIimDVN7pdD6w=="], + + "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "4.0.0", "omggif": "1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "1.2.1", "gopd": "1.2.0" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "gpt-tokenizer": ["gpt-tokenizer@2.9.0", "", {}, "sha512-YSpexBL/k4bfliAzMrRqn3M6+it02LutVyhVpDeMKrC/O9+pCe/5s8U2hYKa2vFLD5/vHhsKc8sOn/qGqII8Kg=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "1.0.1" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "1.0.1" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "1.1.0" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "1.0.1", "resolve-from": "4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "hasown": "2.0.2", "side-channel": "1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "get-intrinsic": "1.3.0" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "1.0.0", "call-bound": "1.0.4", "get-proto": "1.0.1", "has-tostringtag": "1.0.2", "safe-regex-test": "1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "1.1.0" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "7.7.3" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "get-intrinsic": "1.3.0", "is-typed-array": "1.1.15" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "1.0.4" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "1.0.4", "generator-function": "2.0.1", "get-proto": "1.0.1", "has-tostringtag": "1.0.2", "safe-regex-test": "1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "1.0.4", "gopd": "1.2.0", "has-tostringtag": "1.0.2", "hasown": "2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "1.0.4" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "1.0.4", "has-symbols": "1.1.0", "safe-regex-test": "1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "1.1.19" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "1.0.4" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "1.0.4", "get-intrinsic": "1.3.0" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jimp": ["jimp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/diff": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-gif": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-blur": "1.6.0", "@jimp/plugin-circle": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-contain": "1.6.0", "@jimp/plugin-cover": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-displace": "1.6.0", "@jimp/plugin-dither": "1.6.0", "@jimp/plugin-fisheye": "1.6.0", "@jimp/plugin-flip": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/plugin-mask": "1.6.0", "@jimp/plugin-print": "1.6.0", "@jimp/plugin-quantize": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/plugin-rotate": "1.6.0", "@jimp/plugin-threshold": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg=="], + + "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="], + + "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "1.2.8" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "1.2.1", "type-check": "0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "3.0.3", "picomatch": "2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "1.1.12" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "msgpackr": ["msgpackr@1.11.8", "", { "optionalDependencies": { "msgpackr-extract": "3.0.3" } }, "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA=="], + + "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="], + + "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "natural-compare-lite": ["natural-compare-lite@1.4.0", "", {}, "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g=="], + + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "2.1.2" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], + + "node-pty": ["node-pty@1.1.0", "", { "dependencies": { "node-addon-api": "7.1.1" } }, "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1", "has-symbols": "1.1.0", "object-keys": "1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-object-atoms": "1.1.1" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.24.1" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "0.1.4", "fast-levenshtein": "2.0.6", "levn": "0.4.1", "prelude-ls": "1.2.1", "type-check": "0.4.0", "word-wrap": "1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "1.3.0", "object-keys": "1.1.1", "safe-push-apply": "1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "3.1.0" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "3.1.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="], + + "parse-bmfont-binary": ["parse-bmfont-binary@1.0.6", "", {}, "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="], + + "parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "1.0.1", "xml2js": "0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + + "peek-readable": ["peek-readable@4.1.0", "", {}, "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="], + + "planck": ["planck@1.4.2", "", { "peerDependencies": { "stage-js": "1.0.0-alpha.17" } }, "sha512-mNbhnV3g8X2rwGxzcesjmN8BDA6qfXgQxXVMkWau9MCRlQY0RLNEkyHlVp6yFy/X6qrzAXyNONCnZ1cGDLrNew=="], + + "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier-linter-helpers": ["prettier-linter-helpers@1.0.1", "", { "dependencies": { "fast-diff": "1.3.0" } }, "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg=="], + + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "1.1.2", "@protobufjs/base64": "1.1.2", "@protobufjs/codegen": "2.0.4", "@protobufjs/eventemitter": "1.1.0", "@protobufjs/fetch": "1.1.0", "@protobufjs/float": "1.0.2", "@protobufjs/inquire": "1.1.0", "@protobufjs/path": "1.1.2", "@protobufjs/pool": "1.1.0", "@protobufjs/utf8": "1.1.0", "@types/node": "22.19.3", "long": "5.3.2" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + + "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + + "react-devtools-core": ["react-devtools-core@7.0.1", "", { "dependencies": { "shell-quote": "1.8.3", "ws": "7.5.10" } }, "sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw=="], + + "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "0.27.0" }, "peerDependencies": { "react": "19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], + + "react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "0.26.0" }, "peerDependencies": { "react": "19.2.3" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="], + + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "3.0.0", "buffer": "6.0.3", "events": "3.3.0", "process": "0.11.10", "string_decoder": "1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + + "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "get-intrinsic": "1.3.0", "get-proto": "1.0.1", "which-builtin-type": "1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-errors": "1.3.0", "get-proto": "1.0.1", "gopd": "1.2.0", "set-function-name": "2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "2.3.3" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="], + + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "get-intrinsic": "1.3.0", "has-symbols": "1.1.0", "isarray": "2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "1.3.0", "isarray": "2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-regex": "1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "1.1.4", "es-errors": "1.3.0", "function-bind": "1.1.2", "get-intrinsic": "1.3.0", "gopd": "1.2.0", "has-property-descriptors": "1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "1.1.4", "es-errors": "1.3.0", "functions-have-names": "1.2.3", "has-property-descriptors": "1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "object-inspect": "1.13.4", "side-channel-list": "1.0.0", "side-channel-map": "1.0.1", "side-channel-weakmap": "1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "1.3.0", "object-inspect": "1.13.4" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "object-inspect": "1.13.4" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "object-inspect": "1.13.4", "side-channel-map": "1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "simple-xml-to-json": ["simple-xml-to-json@1.2.3", "", {}, "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "stage-js": ["stage-js@1.0.0-alpha.17", "", {}, "sha512-AzlMO+t51v6cFvKZ+Oe9DJnL1OXEH5s9bEy6di5aOrUpcP7PCzI/wIeXF0u3zg0L89gwnceoKxrLId0ZpYnNXw=="], + + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "internal-slot": "1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-data-property": "1.1.4", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-object-atoms": "1.1.1", "has-property-descriptors": "1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "6.2.2" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], + + "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "0.3.0", "peek-readable": "4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="], + + "tiktoken": ["tiktoken@1.0.22", "", {}, "sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "6.5.0", "picomatch": "4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "0.3.0", "ieee754": "1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="], + + "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], + + "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "0.0.29", "json5": "1.0.2", "minimist": "1.2.8", "strip-bom": "3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "0.27.2", "get-tsconfig": "4.13.0" }, "optionalDependencies": { "fsevents": "2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + + "tuistory": ["tuistory@0.0.2", "", { "dependencies": { "ghostty-opentui": "1.3.11" }, "optionalDependencies": { "bun-pty": "0.4.6", "node-pty": "1.1.0" } }, "sha512-14FfFhL+s3Ai+XybzuYeygw7NgBhxk01S7DCfYHtMqy3Si5lkvJLNZdJEFVuGnbtBZDXpfxeGaE9HzJaAjITEg=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-typed-array": "1.1.15" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "1.0.8", "for-each": "0.3.5", "gopd": "1.2.0", "has-proto": "1.2.0", "is-typed-array": "1.1.15" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "1.0.7", "call-bind": "1.0.8", "for-each": "0.3.5", "gopd": "1.2.0", "has-proto": "1.2.0", "is-typed-array": "1.1.15", "reflect.getprototypeof": "1.0.10" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "1.0.8", "for-each": "0.3.5", "gopd": "1.2.0", "is-typed-array": "1.1.15", "possible-typed-array-names": "1.1.0", "reflect.getprototypeof": "1.0.10" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "has-bigints": "1.1.0", "has-symbols": "1.1.0", "which-boxed-primitive": "1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "0.3.4" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "2.3.1" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "utif2": ["utif2@4.1.0", "", { "dependencies": { "pako": "1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="], + + "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + + "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "0.27.2", "fdir": "6.5.0", "picomatch": "4.0.3", "postcss": "8.5.6", "rollup": "4.55.1", "tinyglobby": "0.2.15" }, "optionalDependencies": { "@types/node": "22.19.3", "fsevents": "2.3.3", "tsx": "4.21.0", "yaml": "2.8.2" }, "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], + + "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "6.7.14", "debug": "4.4.3", "es-module-lexer": "1.7.0", "pathe": "2.0.3", "vite": "7.3.1" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + + "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "5.2.3", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "5.3.3", "debug": "4.4.3", "expect-type": "1.3.0", "magic-string": "0.30.21", "pathe": "2.0.3", "picomatch": "4.0.3", "std-env": "3.10.0", "tinybench": "2.9.0", "tinyexec": "0.3.2", "tinyglobby": "0.2.15", "tinypool": "1.1.1", "tinyrainbow": "2.0.0", "vite": "7.3.1", "vite-node": "3.2.4", "why-is-node-running": "2.3.0" }, "optionalDependencies": { "@types/node": "22.19.3" }, "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + + "web-tree-sitter": ["web-tree-sitter@0.25.10", "", {}, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "1.1.0", "is-boolean-object": "1.2.2", "is-number-object": "1.1.1", "is-string": "1.1.1", "is-symbol": "1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "1.0.4", "function.prototype.name": "1.1.8", "has-tostringtag": "1.0.2", "is-async-function": "2.1.1", "is-date-object": "1.1.0", "is-finalizationregistry": "1.1.1", "is-generator-function": "1.1.2", "is-regex": "1.2.1", "is-weakref": "1.1.1", "isarray": "2.0.5", "which-boxed-primitive": "1.1.1", "which-collection": "1.0.2", "which-typed-array": "1.1.19" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "2.0.3", "is-set": "2.0.3", "is-weakmap": "2.0.2", "is-weakset": "2.0.4" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "1.0.7", "call-bind": "1.0.8", "call-bound": "1.0.4", "for-each": "0.3.5", "get-proto": "1.0.1", "gopd": "1.2.0", "has-tostringtag": "1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "ws": ["ws@8.19.0", "", {}, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], + + "xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="], + + "xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": "1.4.3", "xmlbuilder": "11.0.1" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="], + + "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], + + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@anthropic-ai/tokenizer/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "5.26.5" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], + + "@effect/rpc-http/@effect/rpc": ["@effect/rpc@0.54.4", "", { "peerDependencies": { "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-iu3TGWCt4OMH8iKL1ATeROhAxrMF+HdF3NbR5lWls9yWJwBgVU+cps3ZzRbNQhFPWXDGqVuYgmYNY1GKbZgMaw=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "2.0.2" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="], + + "react-devtools-core/ws": ["ws@7.5.10", "", {}, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "react-reconciler/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + + "@anthropic-ai/tokenizer/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "1.0.2" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + } +} diff --git a/src/durable-streams/README.md b/src/durable-streams/README.md index f3dfda5..d0bbbaf 100644 --- a/src/durable-streams/README.md +++ b/src/durable-streams/README.md @@ -26,10 +26,11 @@ Pure event streams with append/subscribe semantics. No LLM logic, no agent behav β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HTTP Layer β”‚ -β”‚ POST /streams/:name β†’ append event β”‚ -β”‚ GET /streams/:name β†’ subscribe (SSE) β”‚ -β”‚ GET /streams β†’ list streams β”‚ -β”‚ DELETE /streams/:name β†’ delete stream β”‚ +β”‚ POST /streams/:name β†’ append event β”‚ +β”‚ GET /streams/:name β†’ subscribe (SSE) β”‚ +β”‚ GET /streams/:name/events β†’ get historic events β”‚ +β”‚ GET /streams β†’ list streams β”‚ +β”‚ DELETE /streams/:name β†’ delete stream β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό @@ -50,7 +51,8 @@ Pure event streams with append/subscribe semantics. No LLM logic, no agent behav β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Storage β”‚ -β”‚ InMemory (default) | FileSystem (future) β”‚ +β”‚ InMemory (tests) | FileSystem (production) β”‚ +β”‚ Data persisted in .iterate/streams/*.json β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` @@ -78,12 +80,18 @@ npx tsx src/durable-streams/main.ts server status ### Stream Commands ```bash -# Subscribe to stream (outputs JSON lines) +# Subscribe to stream (outputs JSON lines - waits for live events) npx tsx src/durable-streams/main.ts stream subscribe my-stream # Subscribe from beginning (offset -1) npx tsx src/durable-streams/main.ts stream subscribe my-stream --offset -1 +# Get historic events (one-shot, exits after fetching) +npx tsx src/durable-streams/main.ts stream get my-stream + +# Get events with offset and limit +npx tsx src/durable-streams/main.ts stream get my-stream --offset 0000000000000005 --limit 10 + # Append message (auto-wraps as {type:"message",text:"..."}) npx tsx src/durable-streams/main.ts stream append my-stream -m "hello world" @@ -103,10 +111,11 @@ npx tsx src/durable-streams/main.ts stream delete my-stream ### Files Created -When using daemon mode: -- `daemon.pid` - Process ID of running daemon -- `daemon.port` - Port the daemon is listening on -- `daemon.log` - Server stdout/stderr +Data files are stored in `.iterate/` in the working directory: +- `.iterate/daemon.pid` - Process ID of running daemon +- `.iterate/daemon.port` - Port the daemon is listening on +- `.iterate/daemon.log` - Server stdout/stderr +- `.iterate/streams/*.json` - Persisted event streams ## Testing with tmux @@ -175,6 +184,21 @@ data: {"offset":"0000000000000000","data":{"type":"message","text":"hello"},"tim data: {"offset":"0000000000000001","data":{"type":"message","text":"world"},"timestamp":1704672001000} ``` +### Get Historic Events + +```bash +# Get all events from stream +curl http://localhost:3000/streams/my-stream/events + +# Get events with offset and limit +curl http://localhost:3000/streams/my-stream/events?offset=0000000000000005&limit=10 +``` + +Response: +```json +{"events":[{"offset":"0000000000000005","data":{"type":"message","text":"hello"},"timestamp":1704672000000}]} +``` + ### List Streams ```bash diff --git a/src/durable-streams/cli.ts b/src/durable-streams/cli.ts index 64e2b0a..03e592e 100644 --- a/src/durable-streams/cli.ts +++ b/src/durable-streams/cli.ts @@ -13,13 +13,14 @@ * stream append -m|-e Append event to stream */ import { Args, Command, Options } from "@effect/cli" -import { HttpServer } from "@effect/platform" -import { NodeHttpServer } from "@effect/platform-node" +import { FileSystem, HttpServer, Path } from "@effect/platform" +import { NodeContext, NodeHttpServer } from "@effect/platform-node" import { Console, Effect, Layer, Option, Schema, Stream } from "effect" import { createServer } from "node:http" import { StreamClientService } from "./client.ts" -import { DaemonService } from "./daemon.ts" +import { DaemonService, DATA_DIR } from "./daemon.ts" import { durableStreamsRouter } from "./http-routes.ts" +import { Storage } from "./storage.ts" import { StreamManagerService } from "./stream-manager.ts" import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" @@ -50,16 +51,31 @@ const serverRunCommand = Command.make( { host: hostOption, port: portOption }, ({ host, port }) => Effect.gen(function*() { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + + // Ensure data directory exists + const dataDirPath = path.join(process.cwd(), DATA_DIR) + yield* fs.makeDirectory(dataDirPath, { recursive: true }).pipe(Effect.ignore) + yield* Console.log(`Starting durable-streams server on ${host}:${port}`) + yield* Console.log(`Data directory: ${dataDirPath}`) yield* Console.log("") yield* Console.log("Endpoints:") - yield* Console.log(" POST /streams/:name Append event") - yield* Console.log(" GET /streams/:name Subscribe (SSE)") - yield* Console.log(" GET /streams List streams") - yield* Console.log(" DELETE /streams/:name Delete stream") + yield* Console.log(" POST /streams/:name Append event") + yield* Console.log(" GET /streams/:name Subscribe (SSE)") + yield* Console.log(" GET /streams/:name/events Get historic events") + yield* Console.log(" GET /streams List streams") + yield* Console.log(" DELETE /streams/:name Delete stream") yield* Console.log("") - const serviceLayer = StreamManagerService.InMemory + // Use FileSystem storage for persistence + const storageLayer = Storage.FileSystem({ dataDir: dataDirPath }).pipe( + Layer.provide(NodeContext.layer) + ) + const serviceLayer = StreamManagerService.Live.pipe( + Layer.provide(storageLayer) + ) const serverLayer = HttpServer.serve(durableStreamsRouter).pipe( Layer.provide(NodeHttpServer.layer(createServer, { port })), @@ -169,6 +185,12 @@ const eventOption = Options.text("event").pipe( Options.optional ) +const limitOption = Options.integer("limit").pipe( + Options.withAlias("n"), + Options.withDescription("Maximum number of events to return"), + Options.optional +) + /** stream subscribe - subscribe to stream events */ const streamSubscribeCommand = Command.make( "subscribe", @@ -227,6 +249,33 @@ const streamAppendCommand = Command.make( ) ).pipe(Command.withDescription("Append event to stream")) +/** stream get - get historic events (one-shot) */ +const streamGetCommand = Command.make( + "get", + { name: streamNameArg, offset: offsetOption, limit: limitOption, server: serverUrlOption }, + ({ limit, name, offset, server: _server }) => + Effect.gen(function*() { + const client = yield* StreamClientService + + const getOpts: { name: StreamName; offset?: Offset; limit?: number } = { name: name as StreamName } + if (Option.isSome(offset)) { + getOpts.offset = offset.value === "-1" ? OFFSET_START : offset.value as Offset + } + if (Option.isSome(limit)) { + getOpts.limit = limit.value + } + + const events = yield* client.get(getOpts) + + for (const event of events) { + const encoded = Schema.encodeSync(StreamEvent)(event) + yield* Console.log(JSON.stringify(encoded)) + } + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Get historic events (one-shot, no live subscription)")) + /** stream list - list all streams */ const streamListCommand = Command.make( "list", @@ -267,6 +316,7 @@ const streamCommand = Command.make("stream").pipe( Command.withSubcommands([ streamSubscribeCommand, streamAppendCommand, + streamGetCommand, streamListCommand, streamDeleteCommand ]), diff --git a/src/durable-streams/client.ts b/src/durable-streams/client.ts index 8c91693..0a471ad 100644 --- a/src/durable-streams/client.ts +++ b/src/durable-streams/client.ts @@ -44,6 +44,13 @@ export interface StreamClient { offset?: Offset }) => Effect.Effect, ClientError> + /** Get historic events from a stream (one-shot, no live subscription) */ + readonly get: (opts: { + name: StreamName + offset?: Offset + limit?: number + }) => Effect.Effect, ClientError> + /** List all streams */ readonly list: () => Effect.Effect, ClientError> @@ -116,6 +123,39 @@ export const makeStreamClient = ( return Effect.succeed(eventStream) } + const get = (opts: { + name: StreamName + offset?: Offset + limit?: number + }): Effect.Effect, ClientError> => + Effect.gen(function*() { + const params = new URLSearchParams() + if (opts.offset) params.set("offset", opts.offset === OFFSET_START ? "-1" : opts.offset) + if (opts.limit) params.set("limit", String(opts.limit)) + const queryString = params.toString() + const url = `/streams/${opts.name}/events${queryString ? `?${queryString}` : ""}` + + const request = HttpClientRequest.get(url) + + const response = yield* clientOk.execute(request).pipe( + Effect.flatMap((r) => r.json), + Effect.scoped, + Effect.catchTags({ + RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + ResponseError: (error) => + Effect.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) + }) + ) as Effect.Effect<{ events: ReadonlyArray }, ClientError> + + return yield* Effect.all( + response.events.map((e) => + Schema.decodeUnknown(StreamEvent)(e).pipe( + Effect.mapError((err) => new ClientError(`Invalid event: ${err}`)) + ) + ) + ) + }) + const list = (): Effect.Effect, ClientError> => Effect.gen(function*() { const request = HttpClientRequest.get(`/streams`) @@ -154,6 +194,7 @@ export const makeStreamClient = ( return { append, subscribe, + get, list, delete: deleteStream } satisfies StreamClient @@ -206,6 +247,7 @@ export class StreamClientService extends Effect.Service()( return { append: (opts: { name: StreamName; data: unknown }) => withClient((c) => c.append(opts)), subscribe: (opts: { name: StreamName; offset?: Offset }) => withClient((c) => c.subscribe(opts)), + get: (opts: { name: StreamName; offset?: Offset; limit?: number }) => withClient((c) => c.get(opts)), list: () => withClient((c) => c.list()), delete: (opts: { name: StreamName }) => withClient((c) => c.delete(opts)) } satisfies StreamClient @@ -255,6 +297,7 @@ export const StreamClientLive: Layer.Layer withClient((c) => c.append(opts)), subscribe: (opts: { name: StreamName; offset?: Offset }) => withClient((c) => c.subscribe(opts)), + get: (opts: { name: StreamName; offset?: Offset; limit?: number }) => withClient((c) => c.get(opts)), list: () => withClient((c) => c.list()), delete: (opts: { name: StreamName }) => withClient((c) => c.delete(opts)) } as unknown as StreamClientService diff --git a/src/durable-streams/daemon.ts b/src/durable-streams/daemon.ts index 780b2e8..087fd7e 100644 --- a/src/durable-streams/daemon.ts +++ b/src/durable-streams/daemon.ts @@ -16,10 +16,13 @@ export interface DaemonConfig { readonly port: number } -/** Default config - files in PWD */ +/** Data directory for all durable-streams files */ +export const DATA_DIR = ".iterate" + +/** Default config - files in .iterate/ */ export const defaultDaemonConfig: DaemonConfig = { - pidFile: "daemon.pid", - logFile: "daemon.log", + pidFile: `${DATA_DIR}/daemon.pid`, + logFile: `${DATA_DIR}/daemon.log`, port: 3000 } @@ -88,7 +91,11 @@ const makeDaemonImpl = ( /** Spawn daemonized server process */ const spawnDaemon = (config: DaemonConfig) => - Effect.sync(() => { + Effect.gen(function*() { + // Ensure .iterate/ directory exists + const dataDirPath = resolvePath(DATA_DIR) + yield* fs.makeDirectory(dataDirPath, { recursive: true }).pipe(Effect.ignore) + const logPath = resolvePath(config.logFile) const out = openSync(logPath, "a") @@ -106,7 +113,7 @@ const makeDaemonImpl = ( ) child.unref() return child.pid! - }) + }).pipe(Effect.orDie) /** Stop process gracefully */ const stopProcess = (pidFile: string): Effect.Effect => @@ -153,7 +160,7 @@ const makeDaemonImpl = ( yield* fs.writeFileString(pidPath, String(pid)).pipe(Effect.ignore) // Write port to a separate file for client discovery - const portPath = resolvePath("daemon.port") + const portPath = resolvePath(`${DATA_DIR}/daemon.port`) yield* fs.writeFileString(portPath, String(cfg.port)).pipe(Effect.ignore) return pid @@ -182,7 +189,7 @@ const makeDaemonImpl = ( if (Option.isNone(maybePid)) return Option.none() // Read port from file - const portPath = resolvePath("daemon.port") + const portPath = resolvePath(`${DATA_DIR}/daemon.port`) const exists = yield* fs.exists(portPath).pipe(Effect.orElseSucceed(() => false)) if (!exists) return Option.some(`http://localhost:${defaultDaemonConfig.port}`) diff --git a/src/durable-streams/http-routes.ts b/src/durable-streams/http-routes.ts index 39548e6..5fab7d0 100644 --- a/src/durable-streams/http-routes.ts +++ b/src/durable-streams/http-routes.ts @@ -140,9 +140,55 @@ const deleteHandler = Effect.gen(function*() { return HttpServerResponse.empty({ status: 204 }) }) +/** GET /streams/:name/events - Get historic events (one-shot, no SSE) */ +const getEventsHandler = Effect.gen(function*() { + const request = yield* HttpServerRequest.HttpServerRequest + const manager = yield* StreamManagerService + const params = yield* HttpRouter.params + + const name = params.name + if (!name) { + return HttpServerResponse.text("Missing stream name", { status: 400 }) + } + + // Parse offset and limit from query string + const url = new URL(request.url, "http://localhost") + const offsetParam = url.searchParams.get("offset") + const limitParam = url.searchParams.get("limit") + + const offset: Offset | undefined = offsetParam === null + ? undefined + : offsetParam === "-1" + ? OFFSET_START + : offsetParam as Offset + + const limit = limitParam ? parseInt(limitParam, 10) : undefined + + const getFromOpts: { name: StreamName; offset?: Offset; limit?: number } = { name: name as StreamName } + if (offset !== undefined) { + getFromOpts.offset = offset + } + if (limit !== undefined && !isNaN(limit)) { + getFromOpts.limit = limit + } + const eventsResult = yield* manager.getFrom(getFromOpts).pipe(Effect.either) + + if (eventsResult._tag === "Left") { + const err = eventsResult.left + if (err._tag === "InvalidOffsetError") { + return HttpServerResponse.text(err.message, { status: 400 }) + } + return HttpServerResponse.text(err.message, { status: 500 }) + } + + const events = eventsResult.right.map((e) => Schema.encodeSync(StreamEvent)(e)) + return yield* HttpServerResponse.json({ events }) +}) + /** Durable streams router */ export const durableStreamsRouter = HttpRouter.empty.pipe( HttpRouter.post("/streams/:name", appendHandler), + HttpRouter.get("/streams/:name/events", getEventsHandler), HttpRouter.get("/streams/:name", subscribeHandler), HttpRouter.get("/streams", listHandler), HttpRouter.del("/streams/:name", deleteHandler) diff --git a/src/durable-streams/persistence.e2e.test.ts b/src/durable-streams/persistence.e2e.test.ts new file mode 100644 index 0000000..656c637 --- /dev/null +++ b/src/durable-streams/persistence.e2e.test.ts @@ -0,0 +1,224 @@ +/** + * E2E tests for persistence across server restarts. + * + * Tests that events stored in FileSystem storage survive server restarts. + * Tests run sequentially to avoid port conflicts and shared state issues. + */ +import { HttpServer } from "@effect/platform" +import { NodeContext, NodeHttpServer } from "@effect/platform-node" +import { Deferred, Effect, Fiber, Layer } from "effect" +import { mkdtemp, rm } from "node:fs/promises" +import { createServer } from "node:http" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { describe, expect, test } from "vitest" +import { durableStreamsRouter } from "./http-routes.ts" +import { Storage } from "./storage.ts" +import { StreamManagerService } from "./stream-manager.ts" +import type { StreamEvent } from "./types.ts" + +/** Helper to start a server with FileSystem storage */ +const startServer = async (dataDir: string): Promise<{ + baseUrl: string + fiber: Fiber.RuntimeFiber +}> => { + const storageLayer = Storage.FileSystem({ dataDir }).pipe( + Layer.provide(NodeContext.layer) + ) + const serviceLayer = StreamManagerService.Live.pipe( + Layer.provide(storageLayer) + ) + + const serverLayer = Layer.mergeAll( + NodeHttpServer.layer(createServer, { port: 0 }), + serviceLayer + ).pipe(Layer.provideMerge(HttpServer.layerContext)) + + const fiberId = Effect.runSync(Effect.fiberId) + const addressDeferred = Deferred.unsafeMake(fiberId) + + const serverEffect = Effect.gen(function*() { + const server = yield* HttpServer.HttpServer + yield* Deferred.succeed(addressDeferred, server.address) + yield* HttpServer.serveEffect(durableStreamsRouter) + return yield* Effect.never + }).pipe( + Effect.scoped, + Effect.provide(serverLayer) + ) + + const fiber = Effect.runFork(serverEffect) + const address = await Effect.runPromise(Deferred.await(addressDeferred)) + + if (address._tag !== "TcpAddress") { + throw new Error("Expected TCP address") + } + + return { + baseUrl: `http://127.0.0.1:${address.port}`, + fiber + } +} + +/** Helper to stop server */ +const stopServer = async (fiber: Fiber.RuntimeFiber) => { + await Effect.runPromise(Fiber.interrupt(fiber)) +} + +describe("Persistence E2E", () => { + test("events persist across server restarts", async () => { + const testDir = await mkdtemp(join(tmpdir(), "persist-test-")) + console.log(`Test dir: ${testDir}`) + + try { + // Start first server + let server = await startServer(testDir) + const streamName = "persist-test" + + // 1. Append events to stream + const res1 = await fetch(`${server.baseUrl}/streams/${streamName}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: { message: "first event" } }) + }) + expect(res1.status).toBe(201) + const event1 = await res1.json() as StreamEvent + + const res2 = await fetch(`${server.baseUrl}/streams/${streamName}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: { message: "second event" } }) + }) + expect(res2.status).toBe(201) + const event2 = await res2.json() as StreamEvent + + // Verify offsets + expect(event1.offset).toBe("0000000000000000") + expect(event2.offset).toBe("0000000000000001") + + // 2. Get events before restart (verify they exist) + const beforeRes = await fetch(`${server.baseUrl}/streams/${streamName}/events`) + expect(beforeRes.status).toBe(200) + const beforeBody = await beforeRes.json() as { events: Array } + expect(beforeBody.events).toHaveLength(2) + + // 3. Stop server + await stopServer(server.fiber) + await new Promise((resolve) => setTimeout(resolve, 100)) + + // 4. Start server again (same data directory) + server = await startServer(testDir) + + // 5. Get events after restart - should still be there + const afterRes = await fetch(`${server.baseUrl}/streams/${streamName}/events`) + expect(afterRes.status).toBe(200) + const afterBody = await afterRes.json() as { events: Array } + + expect(afterBody.events).toHaveLength(2) + expect(afterBody.events[0]!.offset).toBe("0000000000000000") + expect(afterBody.events[0]!.data).toEqual({ message: "first event" }) + expect(afterBody.events[1]!.offset).toBe("0000000000000001") + expect(afterBody.events[1]!.data).toEqual({ message: "second event" }) + + // 6. Append more events after restart - offsets should continue + const res3 = await fetch(`${server.baseUrl}/streams/${streamName}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: { message: "third event after restart" } }) + }) + expect(res3.status).toBe(201) + const event3 = await res3.json() as StreamEvent + + // Offset should continue from where we left off + expect(event3.offset).toBe("0000000000000002") + + await stopServer(server.fiber) + } finally { + await rm(testDir, { recursive: true, force: true }) + } + }) + + test("stream list survives restart", async () => { + const testDir = await mkdtemp(join(tmpdir(), "list-persist-")) + console.log(`Test dir: ${testDir}`) + + try { + let server = await startServer(testDir) + const streamName = `list-persist-${Date.now()}` + + // Create a stream + const createRes = await fetch(`${server.baseUrl}/streams/${streamName}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "test" }) + }) + expect(createRes.status).toBe(201) + + // Verify stream appears in list + const listBefore = await fetch(`${server.baseUrl}/streams`) + const bodyBefore = await listBefore.json() as { streams: Array } + expect(bodyBefore.streams).toContain(streamName) + + // Restart server + await stopServer(server.fiber) + await new Promise((resolve) => setTimeout(resolve, 100)) + server = await startServer(testDir) + + // Verify stream still in list after restart + const listAfter = await fetch(`${server.baseUrl}/streams`) + const bodyAfter = await listAfter.json() as { streams: Array } + expect(bodyAfter.streams).toContain(streamName) + + await stopServer(server.fiber) + } finally { + await rm(testDir, { recursive: true, force: true }) + } + }) + + test("get events with offset and limit", async () => { + const testDir = await mkdtemp(join(tmpdir(), "pagination-")) + console.log(`Test dir: ${testDir}`) + + try { + const server = await startServer(testDir) + const streamName = "pagination-test" + + // Add 5 events + for (let i = 0; i < 5; i++) { + await fetch(`${server.baseUrl}/streams/${streamName}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: { index: i } }) + }) + } + + // Get all events + const allRes = await fetch(`${server.baseUrl}/streams/${streamName}/events`) + const allBody = await allRes.json() as { events: Array } + expect(allBody.events).toHaveLength(5) + + // Get with limit + const limitRes = await fetch(`${server.baseUrl}/streams/${streamName}/events?limit=2`) + const limitBody = await limitRes.json() as { events: Array } + expect(limitBody.events).toHaveLength(2) + expect((limitBody.events[0]!.data as { index: number }).index).toBe(0) + + // Get with offset (start from event 2) + const offset = allBody.events[2]!.offset + const offsetRes = await fetch(`${server.baseUrl}/streams/${streamName}/events?offset=${offset}`) + const offsetBody = await offsetRes.json() as { events: Array } + expect(offsetBody.events).toHaveLength(3) // events 2, 3, 4 + expect((offsetBody.events[0]!.data as { index: number }).index).toBe(2) + + // Get with offset and limit + const combinedRes = await fetch(`${server.baseUrl}/streams/${streamName}/events?offset=${offset}&limit=1`) + const combinedBody = await combinedRes.json() as { events: Array } + expect(combinedBody.events).toHaveLength(1) + expect((combinedBody.events[0]!.data as { index: number }).index).toBe(2) + + await stopServer(server.fiber) + } finally { + await rm(testDir, { recursive: true, force: true }) + } + }) +}) diff --git a/src/durable-streams/storage.ts b/src/durable-streams/storage.ts index 54ac18c..e271e1d 100644 --- a/src/durable-streams/storage.ts +++ b/src/durable-streams/storage.ts @@ -1,8 +1,13 @@ /** * Storage abstraction for durable streams + * + * Implementations: + * - InMemory: Fast, ephemeral (for tests) + * - FileSystem: Persistent JSON files (for production) */ -import { Effect, Layer } from "effect" -import { isStartOffset, makeOffset, type Offset, type StorageError, StreamEvent, type StreamName } from "./types.ts" +import { FileSystem, Path } from "@effect/platform" +import { Effect, Layer, Schema } from "effect" +import { isStartOffset, makeOffset, type Offset, StorageError, StreamEvent, type StreamName } from "./types.ts" /** Stored event shape (internal) */ interface StoredEvent { @@ -117,4 +122,183 @@ export class Storage extends Effect.Service()("@durable-streams/Storage list: () => Effect.sync(() => Array.from(store.keys())) } as unknown as Storage }) + + /** + * FileSystem storage implementation. + * + * Storage structure: + * - {dataDir}/streams/{streamName}.json + * - Each file contains: { events: [...] } + * + * Requires dataDir to be provided (typically .iterate/) + */ + static FileSystem(opts: { + dataDir: string + }): Layer.Layer { + return Layer.effect( + Storage, + Effect.gen(function*() { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + + const streamsDir = path.join(opts.dataDir, "streams") + + const getStreamPath = (name: StreamName) => path.join(streamsDir, `${name}.json`) + + // Schema for file contents + const FileSchema = Schema.Struct({ + events: Schema.Array(Schema.Struct({ + offset: Schema.String, + data: Schema.Unknown, + timestamp: Schema.Number + })) + }) + + const readStreamFile = (name: StreamName): Effect.Effect, StorageError> => + Effect.gen(function*() { + const filePath = getStreamPath(name) + const exists = yield* fs.exists(filePath).pipe( + Effect.catchAll(() => Effect.succeed(false)) + ) + + if (!exists) { + return [] + } + + const content = yield* fs.readFileString(filePath).pipe( + Effect.mapError((e) => new StorageError({ message: `Failed to read stream file: ${e}` })) + ) + + const parsed = yield* Effect.try({ + try: () => JSON.parse(content) as unknown, + catch: (e) => new StorageError({ message: `Invalid JSON in stream file: ${e}` }) + }) + + const decoded = yield* Schema.decodeUnknown(FileSchema)(parsed).pipe( + Effect.mapError((e) => new StorageError({ message: `Invalid stream file schema: ${e}` })) + ) + + return decoded.events.map((e) => ({ + offset: e.offset as Offset, + data: e.data, + timestamp: e.timestamp + })) + }) + + const writeStreamFile = ( + name: StreamName, + events: Array + ): Effect.Effect => + Effect.gen(function*() { + // Ensure directory exists + yield* fs.makeDirectory(streamsDir, { recursive: true }).pipe( + Effect.catchAll(() => Effect.void) + ) + + const filePath = getStreamPath(name) + const content = JSON.stringify( + { events: events.map((e) => ({ offset: e.offset, data: e.data, timestamp: e.timestamp })) }, + null, + 2 + ) + + yield* fs.writeFileString(filePath, content).pipe( + Effect.mapError((e) => new StorageError({ message: `Failed to write stream file: ${e}` })) + ) + }) + + return { + append: (appendOpts: { name: StreamName; events: ReadonlyArray<{ data: unknown }> }) => + Effect.gen(function*() { + const existing = yield* readStreamFile(appendOpts.name) + const now = Date.now() + + const newEvents: Array = appendOpts.events.map((e, i) => + new StreamEvent({ + offset: makeOffset(existing.length + i), + data: e.data, + timestamp: now + }) + ) + + const combined = [ + ...existing, + ...newEvents.map((e) => ({ + offset: e.offset, + data: e.data, + timestamp: e.timestamp + })) + ] + + yield* writeStreamFile(appendOpts.name, combined) + + return newEvents + }), + + getFrom: (getFromOpts: { name: StreamName; offset: Offset; limit?: number }) => + Effect.gen(function*() { + const events = yield* readStreamFile(getFromOpts.name) + + let filtered: Array + if (isStartOffset(getFromOpts.offset)) { + filtered = events + } else { + filtered = events.filter((e) => e.offset >= getFromOpts.offset) + } + + const limited = getFromOpts.limit ? filtered.slice(0, getFromOpts.limit) : filtered + return limited.map((e) => new StreamEvent(e)) + }), + + getAll: (getAllOpts: { name: StreamName }) => + Effect.gen(function*() { + const events = yield* readStreamFile(getAllOpts.name) + return events.map((e) => new StreamEvent(e)) + }), + + exists: (existsOpts: { name: StreamName }) => + fs.exists(getStreamPath(existsOpts.name)).pipe( + Effect.catchAll(() => Effect.succeed(false)) + ), + + create: (createOpts: { name: StreamName }) => + Effect.gen(function*() { + const filePath = getStreamPath(createOpts.name) + const exists = yield* fs.exists(filePath).pipe( + Effect.catchAll(() => Effect.succeed(false)) + ) + + if (!exists) { + yield* writeStreamFile(createOpts.name, []) + } + }), + + delete: (deleteOpts: { name: StreamName }) => + fs.remove(getStreamPath(deleteOpts.name)).pipe( + Effect.catchAll(() => Effect.void), + Effect.asVoid + ), + + list: () => + Effect.gen(function*() { + const dirExists = yield* fs.exists(streamsDir).pipe( + Effect.catchAll(() => Effect.succeed(false)) + ) + + if (!dirExists) { + return [] as ReadonlyArray + } + + const entries = yield* fs.readDirectory(streamsDir).pipe( + Effect.catchAll(() => Effect.succeed([] as ReadonlyArray)) + ) + + return entries + .filter((name) => name.endsWith(".json")) + .map((name) => name.slice(0, -5) as StreamName) + }) + } as unknown as Storage + }) + ) + } } diff --git a/src/durable-streams/stream-manager.ts b/src/durable-streams/stream-manager.ts index e4aff9f..4d4e74f 100644 --- a/src/durable-streams/stream-manager.ts +++ b/src/durable-streams/stream-manager.ts @@ -8,7 +8,14 @@ import type { Scope, Stream } from "effect" import { Effect, HashMap, Layer, Ref } from "effect" import { Storage } from "./storage.ts" import { type DurableStream, makeDurableStream } from "./stream.ts" -import type { InvalidOffsetError, Offset, StorageError, StreamEvent, StreamName } from "./types.ts" +import { + type InvalidOffsetError, + type Offset, + OFFSET_START, + type StorageError, + type StreamEvent, + type StreamName +} from "./types.ts" /** StreamManager service interface */ export interface StreamManager { @@ -24,6 +31,13 @@ export interface StreamManager { offset?: Offset | undefined }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> + /** Get events from a stream (one-shot, no live subscription) */ + getFrom(opts: { + name: StreamName + offset?: Offset | undefined + limit?: number + }): Effect.Effect, InvalidOffsetError | StorageError> + /** List all stream names */ list(): Effect.Effect, StorageError> @@ -73,6 +87,19 @@ export class StreamManagerService extends Effect.Service() return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) }) + const getFrom = (opts: { + name: StreamName + offset?: Offset | undefined + limit?: number + }): Effect.Effect, InvalidOffsetError | StorageError> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + const offset = opts.offset ?? OFFSET_START + return yield* stream.getFrom( + opts.limit !== undefined ? { offset, limit: opts.limit } : { offset } + ) + }) + const list = (): Effect.Effect, StorageError> => storage.list() const deleteStream = (opts: { name: StreamName }): Effect.Effect => @@ -85,6 +112,7 @@ export class StreamManagerService extends Effect.Service() getStream, append, subscribe, + getFrom, list, delete: deleteStream } satisfies StreamManager @@ -92,6 +120,77 @@ export class StreamManagerService extends Effect.Service() dependencies: [Storage.Default] } ) { + /** Create a StreamManagerService layer with custom storage layer */ + static readonly Live: Layer.Layer = Layer.effect( + StreamManagerService, + Effect.gen(function*() { + const storage = yield* Storage + + const streamsRef = yield* Ref.make(HashMap.empty()) + + const getStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + const streams = yield* Ref.get(streamsRef) + const existing = HashMap.get(streams, opts.name) + + if (existing._tag === "Some") { + return existing.value + } + + const stream = yield* makeDurableStream({ name: opts.name }).pipe( + Effect.provideService(Storage, storage) + ) + yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) + return stream + }) + + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.append({ data: opts.data }) + }) + + const subscribe = (opts: { + name: StreamName + offset?: Offset | undefined + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) + }) + + const getFrom = (opts: { + name: StreamName + offset?: Offset | undefined + limit?: number + }): Effect.Effect, InvalidOffsetError | StorageError> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + const offset = opts.offset ?? OFFSET_START + return yield* stream.getFrom( + opts.limit !== undefined ? { offset, limit: opts.limit } : { offset } + ) + }) + + const list = (): Effect.Effect, StorageError> => storage.list() + + const deleteStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + yield* storage.delete(opts) + yield* Ref.update(streamsRef, HashMap.remove(opts.name)) + }) + + return { + getStream, + append, + subscribe, + getFrom, + list, + delete: deleteStream + } as unknown as StreamManagerService + }) + ) + static readonly InMemory: Layer.Layer = Layer.effect( StreamManagerService, Effect.gen(function*() { @@ -130,6 +229,19 @@ export class StreamManagerService extends Effect.Service() return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) }) + const getFrom = (opts: { + name: StreamName + offset?: Offset | undefined + limit?: number + }): Effect.Effect, InvalidOffsetError | StorageError> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + const offset = opts.offset ?? OFFSET_START + return yield* stream.getFrom( + opts.limit !== undefined ? { offset, limit: opts.limit } : { offset } + ) + }) + const list = (): Effect.Effect, StorageError> => storage.list() const deleteStream = (opts: { name: StreamName }): Effect.Effect => @@ -142,6 +254,7 @@ export class StreamManagerService extends Effect.Service() getStream, append, subscribe, + getFrom, list, delete: deleteStream } as unknown as StreamManagerService diff --git a/src/durable-streams/stream.ts b/src/durable-streams/stream.ts index 4274c39..2fc9372 100644 --- a/src/durable-streams/stream.ts +++ b/src/durable-streams/stream.ts @@ -35,6 +35,12 @@ export interface DurableStream { Scope.Scope > + /** Get events from offset (for historic reads without live subscription) */ + getFrom(opts: { offset: Offset; limit?: number }): Effect.Effect< + ReadonlyArray, + InvalidOffsetError | StorageError + > + /** Current event count */ readonly count: Effect.Effect } @@ -126,6 +132,28 @@ export const makeDurableStream = (opts: { return Stream.concat(historicalStream, liveStream) }) + const getFrom = ( + getFromOpts: { offset: Offset; limit?: number } + ): Effect.Effect, InvalidOffsetError | StorageError> => + Effect.gen(function*() { + const { limit, offset } = getFromOpts + + // Validate offset format (unless it's the start sentinel) + if (!isStartOffset(offset)) { + const parsed = parseOffset(offset) + if (isNaN(parsed) || parsed < 0) { + return yield* Effect.fail( + new InvalidOffsetError({ + offset, + message: "Offset must be a non-negative integer string or -1" + }) + ) + } + } + + return yield* storage.getFrom(limit !== undefined ? { name, offset, limit } : { name, offset }) + }) + const count: Effect.Effect = Effect.gen(function*() { const events = yield* storage.getAll({ name }) return events.length @@ -135,6 +163,7 @@ export const makeDurableStream = (opts: { name, append, subscribe, + getFrom, count } }) From 65c40cf1a54001f9e75e485afe1b3dd9e33ba6b4 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:35:48 +0000 Subject: [PATCH 06/15] Fix CLI examples in README: options before args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @effect/cli requires options to come before positional arguments. Updated all CLI examples in Stream Commands and tmux sections. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/durable-streams/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/durable-streams/README.md b/src/durable-streams/README.md index d0bbbaf..4c9b9d5 100644 --- a/src/durable-streams/README.md +++ b/src/durable-streams/README.md @@ -84,19 +84,19 @@ npx tsx src/durable-streams/main.ts server status npx tsx src/durable-streams/main.ts stream subscribe my-stream # Subscribe from beginning (offset -1) -npx tsx src/durable-streams/main.ts stream subscribe my-stream --offset -1 +npx tsx src/durable-streams/main.ts stream subscribe --offset -1 my-stream # Get historic events (one-shot, exits after fetching) npx tsx src/durable-streams/main.ts stream get my-stream # Get events with offset and limit -npx tsx src/durable-streams/main.ts stream get my-stream --offset 0000000000000005 --limit 10 +npx tsx src/durable-streams/main.ts stream get --offset 0000000000000005 --limit 10 my-stream # Append message (auto-wraps as {type:"message",text:"..."}) -npx tsx src/durable-streams/main.ts stream append my-stream -m "hello world" +npx tsx src/durable-streams/main.ts stream append -m "hello world" my-stream # Append raw JSON -npx tsx src/durable-streams/main.ts stream append my-stream -e '{"custom":"data"}' +npx tsx src/durable-streams/main.ts stream append -e '{"custom":"data"}' my-stream # List all streams npx tsx src/durable-streams/main.ts stream list @@ -131,7 +131,7 @@ tmux split-window -v -t ds:0.1 tmux send-keys -t ds:0.0 'npx tsx src/durable-streams/main.ts server run' Enter # Pane 1: Subscriber (wait for server to start) -tmux send-keys -t ds:0.1 'sleep 1 && npx tsx src/durable-streams/main.ts stream subscribe test' Enter +tmux send-keys -t ds:0.1 'sleep 1 && npx tsx src/durable-streams/main.ts stream subscribe --offset -1 test' Enter # Pane 2: Publisher tmux send-keys -t ds:0.2 'sleep 2' Enter @@ -142,9 +142,9 @@ tmux attach -t ds Then in pane 2, send messages: ```bash -npx tsx src/durable-streams/main.ts stream append test -m "first message" -npx tsx src/durable-streams/main.ts stream append test -m "second message" -npx tsx src/durable-streams/main.ts stream append test -e '{"type":"custom","payload":123}' +npx tsx src/durable-streams/main.ts stream append -m "first message" test +npx tsx src/durable-streams/main.ts stream append -m "second message" test +npx tsx src/durable-streams/main.ts stream append -e '{"type":"custom","payload":123}' test ``` Watch them appear in pane 1 (subscriber). From d29ddd32d86abf4581f4c468f15ac08cd3525c3d Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:39:17 +0000 Subject: [PATCH 07/15] Add --storage CLI option for memory/fs backend selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server commands now accept --storage [memory|fs] (default: fs): - memory: Volatile in-memory storage for testing - fs: Persistent file-based storage in .iterate/streams/ πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/durable-streams/README.md | 10 +++++++ src/durable-streams/cli.ts | 50 ++++++++++++++++++++++------------- src/durable-streams/daemon.ts | 9 +++++-- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/durable-streams/README.md b/src/durable-streams/README.md index 4c9b9d5..d1bfe9e 100644 --- a/src/durable-streams/README.md +++ b/src/durable-streams/README.md @@ -64,9 +64,15 @@ Pure event streams with append/subscribe semantics. No LLM logic, no agent behav # Run server in foreground (blocks) npx tsx src/durable-streams/main.ts server run --port 3000 +# Run with in-memory storage (no persistence) +npx tsx src/durable-streams/main.ts server run --storage memory + # Start daemonized server (returns immediately) npx tsx src/durable-streams/main.ts server start --port 3000 +# Start daemon with in-memory storage +npx tsx src/durable-streams/main.ts server start --storage memory + # Stop daemon npx tsx src/durable-streams/main.ts server stop @@ -77,6 +83,10 @@ npx tsx src/durable-streams/main.ts server restart --port 3000 npx tsx src/durable-streams/main.ts server status ``` +**Storage options:** +- `--storage fs` (default) - Persistent file-based storage in `.iterate/streams/` +- `--storage memory` - Volatile in-memory storage (data lost on restart) + ### Stream Commands ```bash diff --git a/src/durable-streams/cli.ts b/src/durable-streams/cli.ts index 03e592e..8d4a0d6 100644 --- a/src/durable-streams/cli.ts +++ b/src/durable-streams/cli.ts @@ -43,23 +43,39 @@ const serverUrlOption = Options.text("server").pipe( Options.optional ) +const storageOption = Options.choice("storage", ["memory", "fs"]).pipe( + Options.withDescription("Storage backend: memory (volatile) or fs (persistent)"), + Options.withDefault("fs" as const) +) + // ─── Server Commands ──────────────────────────────────────────────────────── /** server run - run server in foreground */ const serverRunCommand = Command.make( "run", - { host: hostOption, port: portOption }, - ({ host, port }) => + { host: hostOption, port: portOption, storage: storageOption }, + ({ host, port, storage }) => Effect.gen(function*() { const fs = yield* FileSystem.FileSystem const path = yield* Path.Path - // Ensure data directory exists - const dataDirPath = path.join(process.cwd(), DATA_DIR) - yield* fs.makeDirectory(dataDirPath, { recursive: true }).pipe(Effect.ignore) - yield* Console.log(`Starting durable-streams server on ${host}:${port}`) - yield* Console.log(`Data directory: ${dataDirPath}`) + yield* Console.log(`Storage: ${storage}`) + + // Build storage layer based on option + let storageLayer: Layer.Layer + if (storage === "memory") { + yield* Console.log("Warning: Using in-memory storage - data will be lost on restart") + storageLayer = Storage.InMemory + } else { + const dataDirPath = path.join(process.cwd(), DATA_DIR) + yield* fs.makeDirectory(dataDirPath, { recursive: true }).pipe(Effect.ignore) + yield* Console.log(`Data directory: ${dataDirPath}`) + storageLayer = Storage.FileSystem({ dataDir: dataDirPath }).pipe( + Layer.provide(NodeContext.layer) + ) + } + yield* Console.log("") yield* Console.log("Endpoints:") yield* Console.log(" POST /streams/:name Append event") @@ -69,10 +85,6 @@ const serverRunCommand = Command.make( yield* Console.log(" DELETE /streams/:name Delete stream") yield* Console.log("") - // Use FileSystem storage for persistence - const storageLayer = Storage.FileSystem({ dataDir: dataDirPath }).pipe( - Layer.provide(NodeContext.layer) - ) const serviceLayer = StreamManagerService.Live.pipe( Layer.provide(storageLayer) ) @@ -89,13 +101,14 @@ const serverRunCommand = Command.make( /** server start - start daemon */ const serverStartCommand = Command.make( "start", - { port: portOption }, - ({ port }) => + { port: portOption, storage: storageOption }, + ({ port, storage }) => Effect.gen(function*() { const daemon = yield* DaemonService - const pid = yield* daemon.start({ port }) + const pid = yield* daemon.start({ port, storage }) yield* Console.log(`Daemon started (PID ${pid}) on port ${port}`) - yield* Console.log(`Logs: daemon.log`) + yield* Console.log(`Storage: ${storage}`) + yield* Console.log(`Logs: ${DATA_DIR}/daemon.log`) }).pipe( Effect.catchTag("DaemonError", (e) => Console.error(`Error: ${e.message}`)) ) @@ -118,12 +131,13 @@ const serverStopCommand = Command.make( /** server restart - restart daemon */ const serverRestartCommand = Command.make( "restart", - { port: portOption }, - ({ port }) => + { port: portOption, storage: storageOption }, + ({ port, storage }) => Effect.gen(function*() { const daemon = yield* DaemonService - const pid = yield* daemon.restart({ port }) + const pid = yield* daemon.restart({ port, storage }) yield* Console.log(`Daemon restarted (PID ${pid}) on port ${port}`) + yield* Console.log(`Storage: ${storage}`) }).pipe( Effect.catchTag("DaemonError", (e) => Console.error(`Error: ${e.message}`)) ) diff --git a/src/durable-streams/daemon.ts b/src/durable-streams/daemon.ts index 087fd7e..1b3dacd 100644 --- a/src/durable-streams/daemon.ts +++ b/src/durable-streams/daemon.ts @@ -9,11 +9,15 @@ import { Effect, Layer, Option, Schema } from "effect" import { spawn } from "node:child_process" import { openSync } from "node:fs" +/** Storage backend type */ +export type StorageBackend = "memory" | "fs" + /** Daemon configuration */ export interface DaemonConfig { readonly pidFile: string readonly logFile: string readonly port: number + readonly storage: StorageBackend } /** Data directory for all durable-streams files */ @@ -23,7 +27,8 @@ export const DATA_DIR = ".iterate" export const defaultDaemonConfig: DaemonConfig = { pidFile: `${DATA_DIR}/daemon.pid`, logFile: `${DATA_DIR}/daemon.log`, - port: 3000 + port: 3000, + storage: "fs" } /** Error for daemon operations */ @@ -103,7 +108,7 @@ const makeDaemonImpl = ( const mainScript = path.join(cwd, "src/durable-streams/main.ts") const child = spawn( "npx", - ["tsx", mainScript, "server", "run", "--port", String(config.port)], + ["tsx", mainScript, "server", "run", "--port", String(config.port), "--storage", config.storage], { detached: true, stdio: ["ignore", out, out], From 675c745931b93917e3cfc91e9442a09be449bfe3 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:42:25 +0000 Subject: [PATCH 08/15] Change stream name from positional arg to -n/--name option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All stream commands now use -n/--name for stream name: stream append -n mystream -m "hello" stream get -n mystream stream subscribe -n mystream stream delete -n mystream Also changed limit option from -n to -l to avoid conflict. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/durable-streams/README.md | 22 +++++++++++----------- src/durable-streams/cli.ts | 34 +++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/durable-streams/README.md b/src/durable-streams/README.md index d1bfe9e..1039057 100644 --- a/src/durable-streams/README.md +++ b/src/durable-streams/README.md @@ -91,28 +91,28 @@ npx tsx src/durable-streams/main.ts server status ```bash # Subscribe to stream (outputs JSON lines - waits for live events) -npx tsx src/durable-streams/main.ts stream subscribe my-stream +npx tsx src/durable-streams/main.ts stream subscribe -n my-stream # Subscribe from beginning (offset -1) -npx tsx src/durable-streams/main.ts stream subscribe --offset -1 my-stream +npx tsx src/durable-streams/main.ts stream subscribe -n my-stream --offset -1 # Get historic events (one-shot, exits after fetching) -npx tsx src/durable-streams/main.ts stream get my-stream +npx tsx src/durable-streams/main.ts stream get -n my-stream # Get events with offset and limit -npx tsx src/durable-streams/main.ts stream get --offset 0000000000000005 --limit 10 my-stream +npx tsx src/durable-streams/main.ts stream get -n my-stream --offset 0000000000000005 -l 10 # Append message (auto-wraps as {type:"message",text:"..."}) -npx tsx src/durable-streams/main.ts stream append -m "hello world" my-stream +npx tsx src/durable-streams/main.ts stream append -n my-stream -m "hello world" # Append raw JSON -npx tsx src/durable-streams/main.ts stream append -e '{"custom":"data"}' my-stream +npx tsx src/durable-streams/main.ts stream append -n my-stream -e '{"custom":"data"}' # List all streams npx tsx src/durable-streams/main.ts stream list # Delete stream -npx tsx src/durable-streams/main.ts stream delete my-stream +npx tsx src/durable-streams/main.ts stream delete -n my-stream ``` ### Environment Variables @@ -141,7 +141,7 @@ tmux split-window -v -t ds:0.1 tmux send-keys -t ds:0.0 'npx tsx src/durable-streams/main.ts server run' Enter # Pane 1: Subscriber (wait for server to start) -tmux send-keys -t ds:0.1 'sleep 1 && npx tsx src/durable-streams/main.ts stream subscribe --offset -1 test' Enter +tmux send-keys -t ds:0.1 'sleep 1 && npx tsx src/durable-streams/main.ts stream subscribe -n test --offset -1' Enter # Pane 2: Publisher tmux send-keys -t ds:0.2 'sleep 2' Enter @@ -152,9 +152,9 @@ tmux attach -t ds Then in pane 2, send messages: ```bash -npx tsx src/durable-streams/main.ts stream append -m "first message" test -npx tsx src/durable-streams/main.ts stream append -m "second message" test -npx tsx src/durable-streams/main.ts stream append -e '{"type":"custom","payload":123}' test +npx tsx src/durable-streams/main.ts stream append -n test -m "first message" +npx tsx src/durable-streams/main.ts stream append -n test -m "second message" +npx tsx src/durable-streams/main.ts stream append -n test -e '{"type":"custom","payload":123}' ``` Watch them appear in pane 1 (subscriber). diff --git a/src/durable-streams/cli.ts b/src/durable-streams/cli.ts index 8d4a0d6..a725144 100644 --- a/src/durable-streams/cli.ts +++ b/src/durable-streams/cli.ts @@ -3,16 +3,19 @@ * * Commands: * - * server run [--port] [--host] Run server in foreground - * server start [--port] Start daemonized server - * server stop Stop daemon - * server restart [--port] Restart daemon - * server status Check daemon status + * server run [--port] [--host] [--storage] Run server in foreground + * server start [--port] [--storage] Start daemonized server + * server stop Stop daemon + * server restart [--port] [--storage] Restart daemon + * server status Check daemon status * - * stream subscribe [--offset] Subscribe to stream events - * stream append -m|-e Append event to stream + * stream subscribe -n [--offset] Subscribe to stream events + * stream append -n -m|-e Append event to stream + * stream get -n [--offset] [--limit] Get historic events + * stream list List all streams + * stream delete -n Delete a stream */ -import { Args, Command, Options } from "@effect/cli" +import { Command, Options } from "@effect/cli" import { FileSystem, HttpServer, Path } from "@effect/platform" import { NodeContext, NodeHttpServer } from "@effect/platform-node" import { Console, Effect, Layer, Option, Schema, Stream } from "effect" @@ -177,8 +180,9 @@ const serverCommand = Command.make("server").pipe( // ─── Stream Commands ──────────────────────────────────────────────────────── -const streamNameArg = Args.text({ name: "name" }).pipe( - Args.withDescription("Stream name") +const streamNameOption = Options.text("name").pipe( + Options.withAlias("n"), + Options.withDescription("Stream name") ) const offsetOption = Options.text("offset").pipe( @@ -200,7 +204,7 @@ const eventOption = Options.text("event").pipe( ) const limitOption = Options.integer("limit").pipe( - Options.withAlias("n"), + Options.withAlias("l"), Options.withDescription("Maximum number of events to return"), Options.optional ) @@ -208,7 +212,7 @@ const limitOption = Options.integer("limit").pipe( /** stream subscribe - subscribe to stream events */ const streamSubscribeCommand = Command.make( "subscribe", - { name: streamNameArg, offset: offsetOption, server: serverUrlOption }, + { name: streamNameOption, offset: offsetOption, server: serverUrlOption }, ({ name, offset, server: _server }) => Effect.gen(function*() { const client = yield* StreamClientService @@ -236,7 +240,7 @@ const streamSubscribeCommand = Command.make( /** stream append - append event to stream */ const streamAppendCommand = Command.make( "append", - { name: streamNameArg, message: messageOption, event: eventOption, server: serverUrlOption }, + { name: streamNameOption, message: messageOption, event: eventOption, server: serverUrlOption }, ({ event, message, name, server: _server }) => Effect.gen(function*() { const client = yield* StreamClientService @@ -266,7 +270,7 @@ const streamAppendCommand = Command.make( /** stream get - get historic events (one-shot) */ const streamGetCommand = Command.make( "get", - { name: streamNameArg, offset: offsetOption, limit: limitOption, server: serverUrlOption }, + { name: streamNameOption, offset: offsetOption, limit: limitOption, server: serverUrlOption }, ({ limit, name, offset, server: _server }) => Effect.gen(function*() { const client = yield* StreamClientService @@ -314,7 +318,7 @@ const streamListCommand = Command.make( /** stream delete - delete a stream */ const streamDeleteCommand = Command.make( "delete", - { name: streamNameArg, server: serverUrlOption }, + { name: streamNameOption, server: serverUrlOption }, ({ name }) => Effect.gen(function*() { const client = yield* StreamClientService From dcdffa85cc355b94c7d3fb8e3628ba1853186e86 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:56:10 +0000 Subject: [PATCH 09/15] Migrate from @effect/platform-bun to @effect/platform-node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes CI failures by replacing all Bun-specific platform imports with Node.js equivalents: - BunContext β†’ NodeContext - BunRuntime β†’ NodeRuntime - BunHttpServer β†’ NodeHttpServer - BunStream β†’ native Node.js stdin handling Test files updated to use `npx tsx` instead of `bun` for CLI execution. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/cli/commands.ts | 43 ++++++++++++++++++++++++++--------------- src/cli/main.ts | 8 ++++---- src/layercode/cli.ts | 7 ++++--- src/logging.ts | 4 ++-- src/server.ts | 12 ++++++------ test/cli.e2e.test.ts | 6 +++--- test/fixtures.ts | 6 +++--- test/server.e2e.test.ts | 16 +++++++-------- 8 files changed, 57 insertions(+), 45 deletions(-) diff --git a/src/cli/commands.ts b/src/cli/commands.ts index 842228f..c1258d8 100644 --- a/src/cli/commands.ts +++ b/src/cli/commands.ts @@ -6,8 +6,9 @@ import { type Prompt, Telemetry } from "@effect/ai" import { Command, Options, Prompt as CliPrompt } from "@effect/cli" import { type Error as PlatformError, FileSystem, HttpServer, type Terminal } from "@effect/platform" -import { BunHttpServer, BunStream } from "@effect/platform-bun" -import { Chunk, Console, DateTime, Effect, Fiber, Layer, Option, Schema, Stream } from "effect" +import { NodeHttpServer } from "@effect/platform-node" +import { Console, DateTime, Effect, Fiber, Layer, Option, Schema, Stream } from "effect" +import { createServer } from "node:http" import { AgentRegistry } from "../agent-registry.ts" import { AppConfig, resolveBaseDir } from "../config.ts" import { @@ -198,13 +199,13 @@ const determineMode = (options: { return "pipe" } -const utf8Decoder = new TextDecoder("utf-8") - -const readAllStdin: Effect.Effect = BunStream.stdin.pipe( - Stream.mapChunks(Chunk.map((bytes) => utf8Decoder.decode(bytes))), - Stream.runCollect, - Effect.map((chunks) => Chunk.join(chunks, "").trim()) -) +const readAllStdin: Effect.Effect = Effect.promise(async () => { + const chunks: Array = [] + for await (const chunk of process.stdin) { + chunks.push(chunk as Buffer) + } + return Buffer.concat(chunks).toString("utf-8").trim() +}) /** Simple input message schema - accepts minimal fields */ const SimpleInputMessage = Schema.Struct({ @@ -218,10 +219,21 @@ const SimpleInputMessage = Schema.Struct({ }) type SimpleInputMessage = typeof SimpleInputMessage.Type -const stdinEvents = BunStream.stdin.pipe( - Stream.mapChunks(Chunk.map((bytes) => utf8Decoder.decode(bytes))), - Stream.splitLines, - Stream.filter((line) => line.trim() !== ""), +const stdinEvents = Stream.fromAsyncIterable( + (async function*() { + let buffer = "" + for await (const chunk of process.stdin) { + buffer += (chunk as Buffer).toString("utf-8") + const lines = buffer.split("\n") + buffer = lines.pop() ?? "" + for (const line of lines) { + if (line.trim() !== "") yield line + } + } + if (buffer.trim() !== "") yield buffer + })(), + (error) => new Error(String(error)) +).pipe( Stream.mapEffect((line) => Effect.try(() => JSON.parse(line) as unknown).pipe( Effect.flatMap((json) => Schema.decodeUnknown(SimpleInputMessage)(json)) @@ -592,9 +604,8 @@ export const serveCommand = Command.make( yield* Console.log(` -d '{"_tag":"UserMessageEvent","content":"hello"}'`) yield* Console.log("") - // Create server layer with configured port/host - // Set idleTimeout high for SSE streaming - Bun defaults to 10s which kills long-running streams - const serverLayer = BunHttpServer.layer({ port: actualPort, hostname: actualHost, idleTimeout: 120 }) + // Create Node.js HTTP server layer + const serverLayer = NodeHttpServer.layer(createServer, { port: actualPort, host: actualHost }) // Use Layer.launch to keep the server running return yield* Layer.launch( diff --git a/src/cli/main.ts b/src/cli/main.ts index 3f7363a..7c78928 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -5,7 +5,7 @@ import { AnthropicClient, AnthropicLanguageModel } from "@effect/ai-anthropic" import { GoogleClient, GoogleLanguageModel } from "@effect/ai-google" import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai" import { FetchHttpClient } from "@effect/platform" -import { BunContext, BunRuntime } from "@effect/platform-bun" +import { NodeContext, NodeRuntime } from "@effect/platform-node" import { Cause, Effect, Layer } from "effect" import { AgentRegistry } from "../agent-registry.ts" import { @@ -110,13 +110,13 @@ const makeMainLayer = (args: ReadonlyArray) => Layer.provideMerge(appConfigLayer), Layer.provideMerge(configProviderLayer), Layer.provideMerge(loggingLayer), - Layer.provideMerge(BunContext.layer) + Layer.provideMerge(NodeContext.layer) ) }) return Layer.unwrapEffect(buildLayers.pipe(Effect.provide(loggingLayer))) }).pipe( - Effect.provide(BunContext.layer) + Effect.provide(NodeContext.layer) ) ) @@ -125,5 +125,5 @@ const args = process.argv.slice(2) cli(process.argv).pipe( Effect.provide(makeMainLayer(args)), Effect.catchAllCause((cause) => Cause.isInterruptedOnly(cause) ? Effect.void : Effect.failCause(cause)), - (effect) => BunRuntime.runMain(effect, { disablePrettyLogger: true }) + (effect) => NodeRuntime.runMain(effect, { disablePrettyLogger: true }) ) diff --git a/src/layercode/cli.ts b/src/layercode/cli.ts index 5eeb644..f7f5b1f 100644 --- a/src/layercode/cli.ts +++ b/src/layercode/cli.ts @@ -6,8 +6,9 @@ import { Command as CliCommand, Options } from "@effect/cli" import type { CommandExecutor } from "@effect/platform" import { Command as PlatformCommand, HttpRouter, HttpServer } from "@effect/platform" -import { BunHttpServer } from "@effect/platform-bun" +import { NodeHttpServer } from "@effect/platform-node" import { Console, Effect, Layer, Option, Stream } from "effect" +import { createServer } from "node:http" import { AppConfig } from "../config.ts" import { makeRouter } from "../http-routes.ts" import { makeLayerCodeRouter } from "./layercode.adapter.ts" @@ -143,8 +144,8 @@ const layercodeServeCommand = CliCommand.make( makeLayerCodeRouter(welcomeMessage) ) - // Set a high idleTimeout for SSE streaming - Bun defaults to 10s which kills long-running streams - const serverLayer = BunHttpServer.layer({ port: actualPort, hostname: actualHost, idleTimeout: 120 }) + // Create Node.js HTTP server layer + const serverLayer = NodeHttpServer.layer(createServer, { port: actualPort, host: actualHost }) // Start the tunnel if enabled (fork it to run concurrently with server) if (tunnelEnabled && Option.isSome(agentId)) { diff --git a/src/logging.ts b/src/logging.ts index 42c6002..c8879c0 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -5,7 +5,7 @@ * Uses PlatformLogger.toFile for proper resource management. */ import { FileSystem, PlatformLogger } from "@effect/platform" -import { BunContext } from "@effect/platform-bun" +import { NodeContext } from "@effect/platform-node" import { Effect, Layer, Logger, LogLevel } from "effect" import * as YamlLogger from "./yaml-logger.ts" @@ -101,7 +101,7 @@ export const createLoggingLayer = (config: LoggingConfig): Layer.Layer => // 2. Add file logger (scoped, cleaned up properly without breaking console) const consoleLayer = Logger.replace(Logger.defaultLogger, consoleLogger) const fileLayer = Logger.addScoped(fileLoggerEffect).pipe( - Layer.provide(BunContext.layer), + Layer.provide(NodeContext.layer), Layer.catchAll(() => Layer.empty) ) diff --git a/src/server.ts b/src/server.ts index a51ac33..47a6ed1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,8 +8,9 @@ import { AnthropicClient, AnthropicLanguageModel } from "@effect/ai-anthropic" import { GoogleClient, GoogleLanguageModel } from "@effect/ai-google" import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai" import { FetchHttpClient, HttpServer } from "@effect/platform" -import { BunContext, BunHttpServer, BunRuntime } from "@effect/platform-bun" +import { NodeContext, NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { ConfigProvider, Effect, Layer, LogLevel, Option } from "effect" +import { createServer } from "node:http" import { AgentRegistry } from "./agent-registry.ts" import { AppConfig, type MiniAgentConfig } from "./config.ts" import { EventReducer } from "./event-reducer.ts" @@ -104,13 +105,12 @@ const program = Effect.gen(function*() { Layer.provide(EventStoreFileSystem), Layer.provide(EventReducer.Default), Layer.provide(appConfigLayer), - Layer.provide(BunContext.layer) + Layer.provide(NodeContext.layer) ) // HTTP server layer - // Set idleTimeout high for SSE streaming - Bun defaults to 10s which kills long-running streams const serverLayer = HttpServer.serve(makeRouter).pipe( - Layer.provide(BunHttpServer.layer({ port, idleTimeout: 120 })), + Layer.provide(NodeHttpServer.layer(createServer, { port })), Layer.provide(serviceLayer) ) @@ -123,9 +123,9 @@ const loggingLayer = createLoggingLayer({ baseDir: ".mini-agent" }) -const mainLayer = Layer.mergeAll(loggingLayer, BunContext.layer) +const mainLayer = Layer.mergeAll(loggingLayer, NodeContext.layer) program.pipe( Effect.provide(mainLayer), - BunRuntime.runMain + NodeRuntime.runMain ) diff --git a/test/cli.e2e.test.ts b/test/cli.e2e.test.ts index 579a59b..8be29fa 100644 --- a/test/cli.e2e.test.ts +++ b/test/cli.e2e.test.ts @@ -6,7 +6,7 @@ * By default uses mock LLM server. Set USE_REAL_LLM=1 to use real APIs. */ import { Command } from "@effect/platform" -import { BunContext } from "@effect/platform-bun" +import { NodeContext } from "@effect/platform-node" import { Effect, Stream } from "effect" import * as fs from "node:fs" import * as path from "node:path" @@ -26,11 +26,11 @@ const runCliWithStdin = (cwd: string, llmEnv: LlmEnv, input: string, ...args: Ar ...llmEnv } - return Command.make("bun", CLI_PATH, ...cwdArgs, ...args).pipe( + return Command.make("npx", "tsx", CLI_PATH, ...cwdArgs, ...args).pipe( Command.stdin(Stream.make(Buffer.from(input, "utf-8"))), Command.env(env), Command.string, - Effect.provide(BunContext.layer) + Effect.provide(NodeContext.layer) ) } diff --git a/test/fixtures.ts b/test/fixtures.ts index da00c11..e60068d 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -11,7 +11,7 @@ * - Example: USE_REAL_LLM=1 doppler run -- bun vitest run */ import { Command } from "@effect/platform" -import { BunContext } from "@effect/platform-bun" +import { NodeContext } from "@effect/platform-node" import type { PlatformError } from "@effect/platform/Error" import { Effect, Layer, LogLevel, Option, Stream } from "effect" import { mkdir, mkdtemp, realpath } from "node:fs/promises" @@ -52,7 +52,7 @@ export const runCli = ( ...options.env } - let cmd = Command.make("bun", CLI_PATH, ...cwdArgs, ...args) + let cmd = Command.make("npx", "tsx", CLI_PATH, ...cwdArgs, ...args) if (options.cwd) cmd = Command.workingDirectory(cmd, options.cwd) cmd = Command.env(cmd, env) @@ -68,7 +68,7 @@ export const runCli = ( return { stdout, stderr, exitCode } }) - ).pipe(Effect.provide(BunContext.layer)) + ).pipe(Effect.provide(NodeContext.layer)) } /** Run CLI with custom environment variables (for multi-LLM testing) */ diff --git a/test/server.e2e.test.ts b/test/server.e2e.test.ts index 231ac38..41d793f 100644 --- a/test/server.e2e.test.ts +++ b/test/server.e2e.test.ts @@ -9,7 +9,7 @@ * By default uses mock LLM server. Set USE_REAL_LLM=1 to use real APIs. */ import { Command } from "@effect/platform" -import { BunContext } from "@effect/platform-bun" +import { NodeContext } from "@effect/platform-node" import { spawn } from "child_process" import { Effect } from "effect" import { describe } from "vitest" @@ -51,10 +51,10 @@ const startServer = async ( for (let attempt = 0; attempt < maxRetries; attempt++) { const port = getRandomPort() const args = subcommand === "serve" - ? [CLI_PATH, "--cwd", cwd, "serve", "--port", String(port)] - : [CLI_PATH, "--cwd", cwd, "layercode", "serve", "--port", String(port), "--no-tunnel"] + ? ["tsx", CLI_PATH, "--cwd", cwd, "serve", "--port", String(port)] + : ["tsx", CLI_PATH, "--cwd", cwd, "layercode", "serve", "--port", String(port), "--no-tunnel"] - const proc = spawn("bun", args, { + const proc = spawn("npx", args, { cwd, env: { ...process.env, @@ -115,9 +115,9 @@ describe("HTTP Server", () => { describe("serve command", () => { test("shows help with --help", async () => { const result = await Effect.runPromise( - Command.make("bun", CLI_PATH, "serve", "--help").pipe( + Command.make("npx", "tsx", CLI_PATH, "serve", "--help").pipe( Command.string, - Effect.provide(BunContext.layer) + Effect.provide(NodeContext.layer) ) ) @@ -308,9 +308,9 @@ describe("HTTP Server", () => { describe("layercode command", () => { test("shows help with --help", async () => { const result = await Effect.runPromise( - Command.make("bun", CLI_PATH, "layercode", "serve", "--help").pipe( + Command.make("npx", "tsx", CLI_PATH, "layercode", "serve", "--help").pipe( Command.string, - Effect.provide(BunContext.layer) + Effect.provide(NodeContext.layer) ) ) From 62333c3a4d517d0bf4807726092f60e5d1bee30a Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Thu, 8 Jan 2026 08:04:21 +0000 Subject: [PATCH 10/15] Fix tracing.ts body type error with exactOptionalPropertyTypes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The body property can be undefined, but with exactOptionalPropertyTypes enabled, we can't pass undefined to fetch()'s body. Fix by conditionally spreading the body property only when defined. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/tracing.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tracing.ts b/src/tracing.ts index ad3c9ac..f9983b5 100644 --- a/src/tracing.ts +++ b/src/tracing.ts @@ -45,14 +45,14 @@ const executeFetch = ( url: URL, signal: AbortSignal ): Effect.Effect => { - const send = (body: unknown) => + const send = (body: NonNullable | undefined) => Effect.tryPromise({ try: () => globalThis.fetch(url, { method: request.method, headers: request.headers, - body: body as RequestInit["body"], - signal + signal, + ...(body !== undefined ? { body } : {}) }), catch: (cause) => new HttpClientError.RequestError({ @@ -62,10 +62,11 @@ const executeFetch = ( }) }).pipe(Effect.map((response) => HttpClientResponse.fromWeb(request, response))) + type Body = NonNullable switch (request.body._tag) { case "Raw": case "Uint8Array": - return send(request.body.body) + return send(request.body.body as Body) case "FormData": return send(request.body.formData) case "Stream": From be97d670869f7e37829475c5ab12d1b7dd1950a8 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:08:47 +0000 Subject: [PATCH 11/15] Improve durable-streams daemon reliability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add proper server ready polling with timeout instead of fixed sleep - Fix script path resolution using import.meta.url for daemon spawn - Better error messages for connection failures πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/durable-streams/client.ts | 75 ++++++++++++++++++++++++++++------- src/durable-streams/daemon.ts | 8 +++- 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/durable-streams/client.ts b/src/durable-streams/client.ts index 0a471ad..e86a83c 100644 --- a/src/durable-streams/client.ts +++ b/src/durable-streams/client.ts @@ -7,11 +7,17 @@ import * as Sse from "@effect/experimental/Sse" import * as HttpBody from "@effect/platform/HttpBody" import * as HttpClient from "@effect/platform/HttpClient" +import type * as HttpClientError from "@effect/platform/HttpClientError" import * as HttpClientRequest from "@effect/platform/HttpClientRequest" -import { Effect, Layer, Option, Schema, Stream } from "effect" +import { Duration, Effect, Layer, Option, Schedule, Schema, Stream } from "effect" import { DaemonService, defaultDaemonConfig } from "./daemon.ts" import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" +/** Max time to wait for daemon to become ready */ +const DAEMON_READY_TIMEOUT = Duration.seconds(10) +/** Interval between health check attempts */ +const HEALTH_CHECK_INTERVAL = Duration.millis(100) + /** Client configuration */ export interface ClientConfig { /** Server URL (e.g., "http://localhost:3000") */ @@ -58,6 +64,35 @@ export interface StreamClient { readonly delete: (opts: { name: StreamName }) => Effect.Effect } +/** Poll server until it responds (or timeout) */ +const waitForServerReady = ( + serverUrl: string, + httpClient: HttpClient.HttpClient +): Effect.Effect => { + const healthCheck = HttpClientRequest.get(`${serverUrl}/streams`).pipe( + httpClient.execute, + Effect.scoped, + Effect.asVoid, + Effect.mapError(() => new ClientError("Server not ready")) + ) + + return healthCheck.pipe( + Effect.retry( + Schedule.spaced(HEALTH_CHECK_INTERVAL).pipe( + Schedule.compose(Schedule.elapsed), + Schedule.whileOutput(Duration.lessThan(DAEMON_READY_TIMEOUT)) + ) + ), + Effect.mapError(() => + new ClientError( + `Daemon failed to become ready within ${ + Duration.toSeconds(DAEMON_READY_TIMEOUT) + }s. Check ${defaultDaemonConfig.logFile} for errors.` + ) + ) + ) +} + /** Create client with explicit server URL */ export const makeStreamClient = ( config: ClientConfig @@ -73,6 +108,14 @@ export const makeStreamClient = ( ) const clientOk = HttpClient.filterStatusOk(client) + /** Map HTTP errors to ClientError with helpful messages */ + const mapRequestError = (error: HttpClientError.RequestError): ClientError => { + if (error.reason === "Transport") { + return new ClientError(`Cannot connect to ${config.serverUrl} - server not reachable`) + } + return new ClientError(`Request failed: ${error.message}`) + } + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => Effect.gen(function*() { const request = HttpClientRequest.post(`/streams/${opts.name}`, { @@ -83,7 +126,7 @@ export const makeStreamClient = ( Effect.flatMap((r) => r.json), Effect.scoped, Effect.catchTags({ - RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + RequestError: (error) => Effect.fail(mapRequestError(error)), ResponseError: (error) => Effect.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) }) @@ -115,7 +158,7 @@ export const makeStreamClient = ( ) ), Stream.catchTags({ - RequestError: (error) => Stream.fail(new ClientError(`Request failed: ${error.message}`)), + RequestError: (error) => Stream.fail(mapRequestError(error)), ResponseError: (error) => Stream.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) }) ) @@ -141,7 +184,7 @@ export const makeStreamClient = ( Effect.flatMap((r) => r.json), Effect.scoped, Effect.catchTags({ - RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + RequestError: (error) => Effect.fail(mapRequestError(error)), ResponseError: (error) => Effect.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) }) @@ -164,7 +207,7 @@ export const makeStreamClient = ( Effect.flatMap((r) => r.json), Effect.scoped, Effect.catchTags({ - RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + RequestError: (error) => Effect.fail(mapRequestError(error)), ResponseError: (error) => Effect.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) }) @@ -181,7 +224,7 @@ export const makeStreamClient = ( Effect.scoped, Effect.asVoid, Effect.catchTags({ - RequestError: (error) => Effect.fail(new ClientError(`Request failed: ${error.message}`)), + RequestError: (error) => Effect.fail(mapRequestError(error)), ResponseError: (error) => { // 204 is success for delete if (error.response.status === 204) return Effect.void @@ -208,7 +251,7 @@ export class StreamClientService extends Effect.Service()( const daemon = yield* DaemonService const httpClient = yield* HttpClient.HttpClient - /** Resolve server URL from env, flag, or daemon */ + /** Resolve server URL from env, flag, or daemon. Starts daemon if needed and waits for ready. */ const resolveServerUrl: Effect.Effect = Effect.gen(function*() { // Check env var first const envUrl = process.env.DURABLE_STREAMS_URL @@ -225,12 +268,13 @@ export class StreamClientService extends Effect.Service()( const pid = yield* daemon.start().pipe( Effect.mapError((e) => new ClientError(`Failed to start daemon: ${e.message}`)) ) - yield* Effect.log(`Daemon started (PID ${pid})`) + yield* Effect.log(`Daemon started (PID ${pid}), waiting for ready...`) - // Wait a moment for server to be ready - yield* Effect.sleep("500 millis") + const serverUrl = `http://localhost:${defaultDaemonConfig.port}` + yield* waitForServerReady(serverUrl, httpClient) + yield* Effect.log("Daemon ready") - return `http://localhost:${defaultDaemonConfig.port}` + return serverUrl }) const withClient = ( @@ -277,10 +321,13 @@ export const StreamClientLive: Layer.Layer new ClientError(`Failed to start daemon: ${e.message}`)) ) - yield* Effect.log(`Daemon started (PID ${pid})`) - yield* Effect.sleep("500 millis") + yield* Effect.log(`Daemon started (PID ${pid}), waiting for ready...`) + + const serverUrl = `http://localhost:${defaultDaemonConfig.port}` + yield* waitForServerReady(serverUrl, httpClient) + yield* Effect.log("Daemon ready") - return `http://localhost:${defaultDaemonConfig.port}` + return serverUrl }) const withClient = ( diff --git a/src/durable-streams/daemon.ts b/src/durable-streams/daemon.ts index 1b3dacd..c509e8a 100644 --- a/src/durable-streams/daemon.ts +++ b/src/durable-streams/daemon.ts @@ -8,6 +8,10 @@ import { FileSystem, Path } from "@effect/platform" import { Effect, Layer, Option, Schema } from "effect" import { spawn } from "node:child_process" import { openSync } from "node:fs" +import { dirname, join } from "node:path" +import { fileURLToPath } from "node:url" + +const __dirname = dirname(fileURLToPath(import.meta.url)) /** Storage backend type */ export type StorageBackend = "memory" | "fs" @@ -104,8 +108,8 @@ const makeDaemonImpl = ( const logPath = resolvePath(config.logFile) const out = openSync(logPath, "a") - // Spawn the main.ts with "server run" args - const mainScript = path.join(cwd, "src/durable-streams/main.ts") + // Use import.meta.url to locate main.ts reliably regardless of cwd + const mainScript = join(__dirname, "main.ts") const child = spawn( "npx", ["tsx", mainScript, "server", "run", "--port", String(config.port), "--storage", config.storage], From c4755acf71f305fe26652b320bf0eff03ce1f112 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:00:44 +0000 Subject: [PATCH 12/15] Add architecture sketch for Iterate agent orchestration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive architecture-sketch.md documenting: - Event design with source-based namespacing - Harness bridge architecture (hooks-based, not translation) - Concrete bridge implementations for OpenCode, Claude Code, Pi - 24 open questions with multiple solutions per domain - Recommendations summary table - Add hooks system to durable-streams: - BeforeAppendHook and AfterAppendHook interfaces - withHooks() wrapper for stream composition - StreamFactory for creating hooked streams - Tests for hook behavior Bridge implementations cover: - OpenCode: HTTP/SSE server, SDK client - Claude Code: CLI subprocess, SDK async generator, global hooks - Pi: stdin/stdout RPC, JSONL sessions Open questions organized into: - Session management & concurrency - Event streaming & format - Process lifecycle & supervision - TUI compatibility & handoff - Tool injection & routing - Error recovery & state - Storage architecture πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/architecture-sketch.md | 1685 +++++++++++++++++++ src/durable-streams/README.md | 103 +- src/durable-streams/cli.ts | 2 + src/durable-streams/hooks.e2e.test.ts | 273 +++ src/durable-streams/hooks.ts | 40 + src/durable-streams/http-routes.ts | 15 +- src/durable-streams/index.ts | 21 +- src/durable-streams/persistence.e2e.test.ts | 2 + src/durable-streams/stream-factory.ts | 122 ++ src/durable-streams/stream-manager.ts | 279 +-- src/durable-streams/with-hooks.test.ts | 250 +++ src/durable-streams/with-hooks.ts | 82 + 12 files changed, 2656 insertions(+), 218 deletions(-) create mode 100644 src/architecture-sketch.md create mode 100644 src/durable-streams/hooks.e2e.test.ts create mode 100644 src/durable-streams/hooks.ts create mode 100644 src/durable-streams/stream-factory.ts create mode 100644 src/durable-streams/with-hooks.test.ts create mode 100644 src/durable-streams/with-hooks.ts diff --git a/src/architecture-sketch.md b/src/architecture-sketch.md new file mode 100644 index 0000000..f6f8c78 --- /dev/null +++ b/src/architecture-sketch.md @@ -0,0 +1,1685 @@ +# Iterate: Agent Harness Bridge Architecture + +## Overview + +Iterate orchestrates AI coding agents through **durable event streams**. Rather than building a monolithic agent, we create **bridges** that adapt existing agent harnesses (OpenCode, Claude Code, Pi, as well as our own Iterate agent in the future) to our event-driven architecture. + +## Terminology + +| Term | Definition | +|------|------------| +| **Agent Harness** | A standalone agent runtime (OpenCode, Claude Code, Pi, our own Iterate agent). Handles LLM calls, tool execution, conversation state. | +| **Harness Bridge** | Adapter that connects an agent harness to Iterate's event stream. Reacts to Iterate control events via hooks, wraps harness events with namespace prefix. | +| **Durable Stream** | Append-only event log with offset-based resumption. The source of truth for all agent interactions. | + +## Design Principles + +### 1. Our Stream is the Superset + +Each agent harness (OpenCode, Claude, Pi) has its own internal event format. Our durable stream is a **wrapped superset**: +- Harness events are wrapped verbatim into `iterate:agent:harness:` events +- Control events (`iterate:agent:control:*`) are our own, reacted to by bridge hooks +- External events use source-based naming (`slack:webhook`, `github:pr_comment`) - not wrapped with iterate prefix +- We store everything on our end, even if OpenCode also stores in SQLite + +### 2. No Unified Abstraction + +We do NOT try to normalize harness events into a common format. The UI must know how to render: +- Claude tool calls in Claude's format +- OpenCode tool calls in OpenCode's format +- Pi events in Pi's format + +This avoids lossy translation and keeps full fidelity. + +### 3. Native TUI Must Work + +Users can SSH into the sandbox and use the native terminal UI (OpenCode, ClaudeCode, Pi). When they do: +- From the agent's perspective, everything is normal +- The bridge captures these events and they appear on our SSE stream +- Our web UI and any other consumers see the same events + +### 4. Iterate Tools Available to All Harnesses + +We have server-side tools that all agent harnesses should access. The bridge is responsible for injecting/registering these tools with each harness using harness-specific mechanisms. + +## Event Design + +This section covers event naming, envelope structure, and versioning. These decisions are hard to change later, so we want to get them right. + +### Industry Standards & Philosophies + +#### CloudEvents (CNCF Standard) + +[CloudEvents](https://cloudevents.io/) is the CNCF-graduated standard for describing events. Key concepts: + +- **Reverse-DNS type naming**: `com.example.object.deleted` +- **Standard envelope fields**: `type`, `source`, `id`, `time`, `data` +- **Lowercase alphanumeric** attribute names only +- **`source` + `id`** must be unique per event + +```json +{ + "specversion": "1.0", + "type": "com.example.order.created", + "source": "/orders/service", + "id": "abc-123", + "time": "2024-01-15T12:00:00Z", + "data": { "orderId": "12345" } +} +``` + +**Pros:** Interoperability with other systems, well-documented, wide tooling support. +**Cons:** Verbose, reverse-DNS feels enterprisey, may be overkill for internal system. + +#### Domain-Driven Design (Event Sourcing) + +DDD events use **past tense** and **domain language**: + +- `OrderPlaced`, `CustomerRegistered`, `PaymentFailed` +- Pattern: `[Noun][PastTenseVerb]` +- Natural language: "Stock was depleted" not "StockDepleted" + +**Pros:** Business-readable, self-documenting. +**Cons:** Assumes single domain, doesn't address namespacing across systems. + +#### Kafka Topic Naming + +[Kafka conventions](https://www.confluent.io/learn/kafka-topic-naming-convention/) use hierarchical dot or underscore: + +- `domain.entity.action` or `domain_entity_action` +- Components: domain, classification, description, version +- Example: `sales.orders.created.v1` + +**Pros:** Well-suited for broker filtering, explicit versioning. +**Cons:** Topic-centric (not event-type-centric), verbose. + +### Separator Trade-offs + +| Separator | Example | Pros | Cons | +|-----------|---------|------|------| +| **Colon (:)** | `iterate:agent:opencode` | URL-safe, common in URIs, clear hierarchy | Less common than dots | +| **Dot (.)** | `iterate.agent.opencode` | Most common (Java packages, domains), natural hierarchy | Conflicts with JSON path syntax (`event.type` ambiguous) | +| **Slash (/)** | `iterate/agent/opencode` | Path-like, very clear hierarchy | Needs URL encoding in some contexts | +| **Underscore (_)** | `iterate_agent_opencode` | No special meaning, flat | Hard to distinguish hierarchy levels | + +**Recommendation:** **Colon (`:`)** - Clear hierarchy without JSON path conflicts. Widely used in URIs and message protocols. Easy to filter: `type.startsWith("iterate:")`. + +### Namespace Structure Options + +Given the requirement for 50-200+ event sources, and future routing/broker needs: + +#### Option A: `iterate:` prefix for internal, flat for external + +```typescript +// Internal events - all prefixed with iterate: +iterate:agent:harness:opencode +iterate:agent:harness:claude +iterate:agent:control:create +iterate:agent:system:ready + +// External events - no prefix (raw from outside world) +slack_webhook +github_webhook +``` + +**Pros:** Clear internal/external boundary. +**Cons:** Mixed conventions (colons vs underscores). + +#### Option B: Full hierarchy with `iterate:` everywhere + +```typescript +// All iterate-owned events +iterate:agent:harness:opencode +iterate:agent:control:create +iterate:external:slack:webhook +iterate:external:github:webhook +iterate:trigger:cron +``` + +**Pros:** Consistent structure, easy to filter by any level. +**Cons:** Verbose for external events that we don't "own". + +#### Option C: Source-based namespacing (Recommended) + +```typescript +// External events - named by source +slack:webhook // Slack sent this +github:webhook // GitHub sent this +github:pr_comment // GitHub PR comment + +// Iterate-owned events - prefixed with iterate: +iterate:agent:harness:opencode // Wrapped OpenCode events +iterate:agent:control:create // Create agent command +iterate:trigger:cron // Scheduled trigger +``` + +**Pros:** Clear provenance - external events are from external sources, not "owned" by iterate. +**Cons:** Two namespacing conventions (but they're semantically different). + +#### Option D: Flat with type categories + +```typescript +// Flat but categorized +agent_harness_opencode +agent_control_create +webhook_slack +webhook_github +trigger_cron +``` + +**Pros:** Simple, no hierarchy parsing. +**Cons:** Harder to do prefix filtering, long names. + +### Recommendation: Option C (Source-based) + +External events should be named by their source, not wrapped with `iterate:`: + +``` +// External events (from outside world) +slack:webhook - Slack webhook payload +slack:command - Slack slash command +github:webhook - GitHub webhook payload +github:pr_comment - GitHub PR comment + +// Iterate-owned events +iterate:agent:harness:opencode - Wrapped OpenCode events +iterate:agent:harness:claude - Wrapped Claude events +iterate:agent:harness:pi - Wrapped Pi events +iterate:agent:control:create - Create agent command +iterate:agent:control:destroy - Destroy agent command +iterate:agent:control:message - Send message command +iterate:agent:system:ready - Agent ready notification +iterate:agent:system:error - Agent error notification +iterate:trigger:cron - Cron trigger +``` + +**Why:** +- External events are genuinely from external sources - `slack:webhook` is clearer than `iterate:external:slack:webhook` +- `iterate:` prefix reserved for things we actually own/create +- Still easy to filter: `type.startsWith("iterate:")` for our events, or by source prefix + +### Envelope Structure + +Durable Streams has no opinion on event structure - it just stores bytes with offsets. We define our own envelope. + +**Goals:** +1. Single flat envelope (no nested wrappers) +2. Preserve verbatim external payloads +3. Clear separation: our fields vs their payload +4. Both timestamps: when we received it, when they created it + +### Flat vs Nested: Type-Specific Fields + +A key design question: should type-specific fields like `agentId` be at the root level or nested? + +#### Option: Flat (type-specific fields at root) + +```typescript +{ + type: "iterate:agent:harness:opencode", + version: 1, + timestamp: 1705312800000, + agentId: "agent-123", // At root + payload: { ... } +} +``` + +#### Option: Nested (type-specific fields inside data) + +```typescript +{ + type: "iterate:agent:harness:opencode", + version: 1, + timestamp: 1705312800000, + data: { + agentId: "agent-123", // Nested + payload: { ... } + } +} +``` + +#### Comparison + +| Aspect | Flat | Nested | +|--------|------|--------| +| **Access pattern** | `event.agentId` | `event.data.agentId` | +| **Querying/filtering** | Easy - top-level fields | Requires path traversal | +| **Schema clarity** | Mixed concerns at one level | Clear envelope vs content separation | +| **Naming collisions** | Possible (if payload has `type` field) | Impossible - separate namespaces | +| **Database indexing** | Most DBs index top-level better | May need special JSON path indexing | +| **Nesting risk** | None | Can lead to `data.payload.data.x` | + +#### Modern Systems Using Flat Structures + +- **CloudEvents**: Metadata flat at root, only `data` is nested +- **Stripe webhooks**: Flat structure with `type`, `id`, `created`, then type-specific fields +- **AWS EventBridge**: `source`, `detail-type`, `time` at root, `detail` for payload +- **Segment events**: `type`, `timestamp`, `userId` at root + +**Common pattern**: Envelope fields + routing fields at root, opaque payload in one nested field. + +#### Recommendation: Flat with Reserved Envelope Fields + +Use flat structure but reserve a small set of envelope field names: + +```typescript +// Reserved envelope fields (always present or optional on all events) +type: string // Required +version: number // Required +timestamp: number // Required +payload?: unknown // For external data (mutually exclusive with data) +data?: Record<...> // For our structured data (mutually exclusive with payload) +metadata?: Record<...> // Optional debug info + +// Type-specific fields at root (varies by event type) +agentId?: string // For agent events +channel?: string // For slack events +// etc. +``` + +**Why flat:** +- Simpler access patterns +- Better database indexing +- Avoids deep nesting (`event.data.agentId` vs `event.agentId`) +- Matches how most modern event systems work + +**Collision avoidance:** +- We control our field names +- `payload` is opaque - we never reach into it for routing +- If external payload has a `type` field, it's inside `payload.type`, not `event.type` + +#### Envelope Schema + +```typescript +interface IterateEvent { + // === Durable Streams fields (protocol-level) === + offset: Offset // Assigned by durable-streams + + // === Iterate envelope fields (always present) === + type: string // e.g. "iterate:agent:harness:opencode" + version: number // Envelope schema version (start at 1) + timestamp: number // Our timestamp (when we received/created) + + // === Type-specific fields at root === + agentId?: string // For agent events + // ... other type-specific fields as needed + + // === Payload (mutually exclusive) === + payload?: unknown // Verbatim external data (harness event, webhook body) + data?: Record // Our structured data (for control/system events) + + // === Optional === + metadata?: Record // Debug info, correlation IDs, etc. +} +``` + +#### Two Timestamps + +When wrapping external events (harness or webhook): + +```typescript +{ + type: "iterate:agent:harness:opencode", + version: 1, + timestamp: 1705312800000, // When we received it + agentId: "agent-123", + payload: { + // Verbatim OpenCode event - may have its own timestamp + type: "Session.Message.Created", + timestamp: 1705312799500, // When OpenCode created it + sessionId: "sess-456", + message: { ... } + } +} +``` + +- `timestamp`: When Iterate received/processed the event +- `payload.timestamp` (or similar): When the source created it + +Both are useful - `timestamp` for ordering in our stream, `payload.timestamp` for understanding actual event time. + +### Versioning Strategies + +Since this is an append-only log, we can't update old events. Versioning options: + +#### Strategy 1: Version in Type Name + +```typescript +iterate:agent:harness:opencode:v1 +iterate:agent:harness:opencode:v2 +``` + +**Pros:** Explicit, easy to filter by version. +**Cons:** Type proliferation, harder to query "all opencode events". + +#### Strategy 2: Version Field in Envelope + +```typescript +{ + type: "iterate:agent:harness:opencode", + version: 1, // Envelope version + ... +} +``` + +**Pros:** Type stays stable, consumers check `version` field. +**Cons:** Need to handle multiple versions at runtime. + +#### Strategy 3: Semantic Versioning with Breaking Change Policy + +- Minor changes (adding optional fields): No version bump +- Breaking changes: Bump `version`, document migration + +#### Strategy 4: Never Version, Only Evolve + +- Only add optional fields +- Never remove or change field semantics +- If breaking change needed, create new event type entirely + +### Recommendation: Strategy 2 + 3 + +1. **`version` field in envelope** - start at `1` +2. **Minor changes**: Add optional fields, don't bump +3. **Breaking changes**: Bump `version`, emit both versions during transition +4. **Consumer rule**: Ignore unknown fields, fail on unknown `version` > supported + +```typescript +// Consumer code +function handleEvent(event: IterateEvent) { + if (event.version > SUPPORTED_VERSION) { + log.warn(`Unknown event version ${event.version}, skipping`) + return + } + // Process based on type and version +} +``` + +### Event Type Summary + +| Category | Type | Description | +|----------|------|-------------| +| **Agent harness** | `iterate:agent:harness:opencode` | Wrapped OpenCode event | +| | `iterate:agent:harness:claude` | Wrapped Claude event | +| | `iterate:agent:harness:pi` | Wrapped Pi event | +| | `iterate:agent:harness:iterate` | Our own agent (future) | +| **Agent control** | `iterate:agent:control:create` | Create agent command | +| | `iterate:agent:control:destroy` | Destroy agent command | +| | `iterate:agent:control:message` | Send message to agent | +| **Agent system** | `iterate:agent:system:ready` | Agent ready | +| | `iterate:agent:system:stopped` | Agent stopped | +| | `iterate:agent:system:error` | Agent error | +| **External** | `slack:webhook` | Slack webhook | +| | `slack:command` | Slack slash command | +| | `github:webhook` | GitHub webhook | +| | `github:pr_comment` | GitHub PR comment | +| **Triggers** | `iterate:trigger:cron` | Scheduled trigger | +| | `iterate:trigger:manual` | Manual trigger | + +### Example Events + +```typescript +// Agent harness event (verbatim payload) +{ + type: "iterate:agent:harness:opencode", + version: 1, + timestamp: 1705312800000, + agentId: "agent-123", + payload: { + type: "Session.Message.Created", + timestamp: 1705312799500, + sessionId: "sess-456", + message: { role: "assistant", parts: [...] } + } +} + +// Agent control event (our data) +{ + type: "iterate:agent:control:create", + version: 1, + timestamp: 1705312700000, + data: { + agentId: "agent-123", + harness: "opencode", + config: { model: "claude-3", workingDirectory: "/workspace" } + } +} + +// External webhook (verbatim payload, source-based naming) +{ + type: "slack:webhook", + version: 1, + timestamp: 1705312900000, + payload: { + type: "message", + channel: "C123", + user: "U456", + text: "Hello agent", + ts: "1705312899.000100" // Slack's timestamp format + } +} +``` + +## Agent Lifecycle + +Agents don't magically exist. The stream starts empty, and agents are created via control events. + +### Creating an Agent + +```typescript +{ + type: "iterate:agent:control:create", + version: 1, + timestamp: 1705312700000, + data: { + agentId: "agent-123", + harness: "opencode" | "claude" | "pi" | "iterate", + config: { + model?: string, + workingDirectory: string, + tools?: string[], // Iterate tools to inject + } + } +} +``` + +### Agent Ready + +```typescript +{ + type: "iterate:agent:system:ready", + version: 1, + timestamp: 1705312750000, + agentId: "agent-123", + data: { + harness: "opencode", + pid?: number, + endpoint?: string, // For server-based harnesses + } +} +``` + +### Example: Full Agent Creation Flow + +```typescript +// 1. Control event requests agent creation +{ type: "iterate:agent:control:create", version: 1, timestamp: ..., data: { agentId: "agent-123", harness: "opencode", config: {...} } } + +// 2. System event confirms agent is ready +{ type: "iterate:agent:system:ready", version: 1, timestamp: ..., agentId: "agent-123", data: { harness: "opencode", pid: 12345 } } + +// 3. Control event sends a message +{ type: "iterate:agent:control:message", version: 1, timestamp: ..., agentId: "agent-123", data: { content: "Hello agent" } } + +// 4. Agent harness emits events (verbatim OpenCode payload) +{ type: "iterate:agent:harness:opencode", version: 1, timestamp: ..., agentId: "agent-123", payload: { type: "Session.Message.Created", ... } } +{ type: "iterate:agent:harness:opencode", version: 1, timestamp: ..., agentId: "agent-123", payload: { type: "Session.Message.Updated", ... } } + +// 5. Eventually agent completes or is destroyed +{ type: "iterate:agent:control:destroy", version: 1, timestamp: ..., agentId: "agent-123", data: {} } +{ type: "iterate:agent:system:stopped", version: 1, timestamp: ..., agentId: "agent-123", data: { reason: "requested" } } +``` + +## Harness Bridge Architecture + +The bridge is **hooks-based**, not translation-based. It reacts to control events by calling harness-specific imperative APIs, and wraps harness output into our event format. + +```typescript +interface HarnessBridge { + readonly harness: "opencode" | "claude" | "pi" + + /** React to agent control events */ + hooks: { + /** Called on iterate:agent:control:create for this harness type */ + onCreateAgent(agentId: string, config: AgentConfig): Effect + + /** Called on iterate:agent:control:message targeting this agent */ + onMessage(agentId: string, content: string): Effect + + /** Called on iterate:agent:control:destroy */ + onDestroyAgent(agentId: string): Effect + } + + /** Subscribe to harness's native event stream for an agent */ + subscribeToHarness(agentId: string): Stream // unknown = verbatim harness event + + /** Wrap harness event into our format */ + wrapEvent(agentId: string, nativeEvent: unknown): IterateEvent + // Returns: { type: "iterate:agent:harness:opencode", version: 1, timestamp: ..., agentId, payload: nativeEvent } + + /** Inject Iterate tools into the harness */ + injectTools(agentId: string, tools: IterateTool[]): Effect +} +``` + +### Event Flow + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Durable Stream β”‚ +β”‚ [iterate:agent:control:*] [iterate:agent:harness:*] [slack:*] [github:*] β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ OpenCode β”‚ β”‚ Claude Code β”‚ β”‚ Pi Bridge β”‚ + β”‚ Bridge β”‚ β”‚ Bridge β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ hooks.on*() β”‚ β”‚ hooks.on*() β”‚ β”‚ hooks.on*() β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ OpenCode β”‚ β”‚ Claude Code β”‚ β”‚ Pi Process β”‚ + β”‚ Server β”‚ β”‚ CLI/SDK β”‚ β”‚ (RPC) β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ (SQLite, β”‚ β”‚ (stream-json) β”‚ β”‚ (stdin/stdout)β”‚ + β”‚ Event Bus) β”‚ β”‚ β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Control Event β†’ Imperative API + +When `iterate:agent:control:message` arrives: + +1. Bridge hook `onMessage` fires for the target agent +2. Hook calls harness-specific imperative API: + - **OpenCode**: POST to event bus / SDK call + - **Claude**: Spawn CLI with `-p` flag or SDK call + - **Pi**: Write JSON to stdin `{ command: "prompt", message: "..." }` +3. Harness processes and emits its own events +4. Bridge captures and wraps into `iterate:agent:harness:` event + +### Harness Event β†’ Wrapped in Stream + +When harness emits an event: + +1. Bridge `subscribeToHarness()` receives native event (verbatim) +2. Bridge `wrapEvent()` wraps it with envelope fields +3. Appended to durable stream +4. SSE consumers (web UI, etc.) receive it + +```typescript +// OpenCode native event (verbatim, we don't touch it) +{ + type: "Session.Message.Created", + timestamp: 1705312799500, + sessionId: "sess-456", + message: { role: "assistant", parts: [...] } +} + +// Wrapped in our stream +{ + type: "iterate:agent:harness:opencode", + version: 1, + timestamp: 1705312800000, // Our timestamp + agentId: "agent-123", + payload: { + type: "Session.Message.Created", + timestamp: 1705312799500, // Their timestamp (preserved) + sessionId: "sess-456", + message: { role: "assistant", parts: [...] } + } +} +``` + +The `payload` is byte-for-byte identical to what the harness emitted. UI must understand OpenCode's native event format to render it. + +## Process Management + +### Open Question: Who Manages Agent Processes? + +Different harnesses have different process models: + +| Harness | Process Model | Management | +|---------|---------------|------------| +| **OpenCode** | Long-running server, multiple sessions | One server per sandbox, sessions multiplexed | +| **Claude Code** | CLI invocation or SDK | Per-request CLI or persistent SDK connection | +| **Pi** | Per-agent RPC process | One process per agent, stdin/stdout | + +**Questions to resolve:** +1. Does Iterate spawn/supervise these processes, or assume they're running? +2. For OpenCode server model: one server shared by multiple "agents" (sessions)? +3. For Pi: one process per agent, managed by Iterate? +4. What happens on crash/restart? + +### Sketch: Process Supervisor + +```typescript +interface ProcessSupervisor { + /** Ensure harness process is running */ + ensureRunning(harness: HarnessType, agentId: string): Effect + + /** Stop a specific agent's process */ + stop(agentId: string): Effect + + /** Handle process crashes */ + onCrash(agentId: string, reason: unknown): Effect +} +``` + +### OpenCode: Server Model + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ OpenCode Server β”‚ +β”‚ (one per sandbox) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚Session 1β”‚ β”‚Session 2β”‚ β”‚Session 3β”‚ β”‚ +β”‚ β”‚(Agent A)β”‚ β”‚(Agent B)β”‚ β”‚(Agent C)β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ SQLite DB Event Bus β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + OpenCode Bridge + (subscribes to event bus, + reacts to iterate:agent:control:* by + creating sessions / sending messages, + emits iterate:agent:harness:opencode events) +``` + +### Pi: Per-Agent Process Model + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Pi Process β”‚ β”‚ Pi Process β”‚ β”‚ Pi Process β”‚ +β”‚ (Agent A) β”‚ β”‚ (Agent B) β”‚ β”‚ (Agent C) β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ stdin ◀────────│ β”‚ stdin ◀────────│ β”‚ stdin ◀────────│ +β”‚ stdout ────────▢ β”‚ stdout ────────▢ β”‚ stdout ────────▢ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ JSONL session β”‚ β”‚ JSONL session β”‚ β”‚ JSONL session β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β–Ό + Pi Bridge + (manages process lifecycle, + emits iterate:agent:harness:pi events) +``` + +## Tool Injection + +Iterate has server-side tools that should be available to all agents. Each harness has different mechanisms: + +| Harness | Tool Registration | +|---------|-------------------| +| **OpenCode** | Agent config in markdown, or runtime injection via SDK | +| **Claude Code** | `--allowedTools` flag, MCP servers, custom tool definitions | +| **Pi** | TypeBox tool schemas passed to agent runtime | + +The bridge must: +1. Convert Iterate tool definitions to harness-specific format +2. Register/inject at agent creation time +3. Handle tool calls that route back to Iterate server + +## Consumer Clients + +Multiple clients subscribe to the durable stream via SSE: + +``` + Durable Stream + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Web UI β”‚ β”‚ Terminal β”‚ β”‚ Slack β”‚ + β”‚ β”‚ β”‚ UI β”‚ β”‚ Bot β”‚ + β”‚ Renders: β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ iterate: β”‚ β”‚ Renders: β”‚ β”‚ Posts β”‚ + β”‚ agent: β”‚ β”‚ all β”‚ β”‚ replies β”‚ + β”‚ harness: β”‚ β”‚ events β”‚ β”‚ β”‚ + β”‚ * β”‚ β”‚ β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +Each client must understand the native event formats inside `payload` - no translation layer. + +### Example: Web UI Event Handler + +```typescript +function handleStreamEvent(event: IterateEvent) { + // Check version compatibility + if (event.version > SUPPORTED_VERSION) { + console.warn(`Unknown event version ${event.version}`) + return + } + + // Route by type prefix + if (event.type.startsWith("iterate:agent:harness:")) { + const harness = event.type.split(":")[3] // opencode, claude, pi + switch (harness) { + case "opencode": + renderOpenCodeEvent(event.payload) + break + case "claude": + renderClaudeEvent(event.payload) + break + case "pi": + renderPiEvent(event.payload) + break + } + } else if (event.type.startsWith("iterate:agent:system:")) { + renderSystemEvent(event) + } else if (event.type.startsWith("slack:") || event.type.startsWith("github:")) { + // External webhook - show notification or route to handler + renderExternalEvent(event) + } +} +``` + +## Agent Harness Comparison + +| Feature | OpenCode | Claude Code | Pi | +|---------|----------|-------------|-----| +| **Process Model** | Server (multi-session) | CLI or SDK | Per-agent process | +| **Event Format** | BusEvent + MessageV2 | stream-json NDJSON | JSON RPC events | +| **State Storage** | SQLite | Per-session ID | JSONL tree | +| **Tool Definition** | Markdown agent config | MCP / allowedTools | TypeBox schemas | +| **Programmatic Control** | Event Bus / SDK | `-p` flag / SDK | stdin JSON commands | +| **Native TUI** | Yes (Bubble Tea) | Yes | Yes | + +### Message Format Comparison + +**User Input:** +| Harness | Format | +|---------|--------| +| **OpenCode** | `{ role: "user", parts: [{ type: "text", text }] }` | +| **Claude** | CLI arg, stdin, or SDK Message object | +| **Pi** | `{ command: "prompt", message: string }` | + +**Assistant Output:** +| Harness | Format | +|---------|--------| +| **OpenCode** | `{ role: "assistant", parts: [...], tokens, cost, finish }` | +| **Claude** | NDJSON with `type: "assistant"` | +| **Pi** | `message_update` / `turn_end` events | + +**Tool Calls:** +| Harness | Format | +|---------|--------| +| **OpenCode** | `{ type: "toolCall", name, args }` in parts array | +| **Claude** | `{ type: "tool_use", name, input }` | +| **Pi** | `tool_execution_start` event with toolName, args | + +## Concrete Bridge Implementations + +Three dominant patterns for programmatically controlling AI coding agents: **HTTP/SSE servers** (OpenCode), **CLI subprocess with streaming JSON** (Claude Code), and **stdin/stdout RPC** (Pi). All preserve TUI compatibility through session file persistence. + +### OpenCode Bridge + +OpenCode implements a **client-server architecture** where the TUI communicates with an internal HTTP server. + +#### Starting the Server + +```bash +opencode serve --port 4096 +``` + +Exposes an OpenAPI 3.1-compliant REST API at `http://localhost:4096/doc`. + +#### Key Endpoints + +| Endpoint | Method | Purpose | +|----------|--------|---------| +| `/session` | GET/POST | List or create sessions | +| `/session/:id/prompt` | POST | Send message, wait for response | +| `/session/:id/prompt_async` | POST | Send message, stream via SSE | +| `/session/:id/abort` | POST | Cancel running operation | +| `/global/health` | GET | Health check | +| `/event` | GET | SSE event stream | + +#### TypeScript Bridge + +```typescript +import { createOpencodeClient } from "@opencode-ai/sdk" + +class OpenCodeBridge { + private client: ReturnType + + constructor(baseUrl = "http://localhost:4096") { + this.client = createOpencodeClient({ baseUrl }) + } + + async *subscribeToEvents() { + const events = await this.client.event.subscribe() + for await (const event of events.stream) { + yield { + type: `iterate:agent:harness:opencode`, + version: 1, + timestamp: Date.now(), + agentId: event.sessionId, + payload: event + } + } + } + + async sendMessage(sessionId: string, text: string) { + return this.client.session.prompt({ + path: { id: sessionId }, + body: { parts: [{ type: "text", text }] } + }) + } + + async createSession() { + return this.client.session.create({}) + } + + async abort(sessionId: string) { + return this.client.session.abort({ path: { id: sessionId } }) + } +} +``` + +#### TUI Compatibility + +Multiple TUI clients can connect to same server simultaneously: + +```bash +opencode attach --hostname localhost --port 4096 +``` + +#### Session Storage + +Sessions persist as JSON files in `~/.local/share/opencode/project//storage/session/`. + +### Claude Code Bridge + +Claude Code uses a **CLI-per-invocation** model. The TypeScript SDK (`@anthropic-ai/claude-agent-sdk`) spawns the CLI binary as a subprocess internally, so SDK and CLI sessions are identical and fully interoperable. + +#### Architecture + +``` +Your Daemon + β”‚ + β”œβ”€β”€β”€ SDK query() ───► Spawns: claude --output-format stream-json ... + β”‚ β”‚ + β”‚ β”œβ”€β–Ί Writes to ~/.claude/projects//.jsonl + β”‚ β”‚ + β”‚ └─► Triggers global hooks (if configured) + β”‚ + └─── Receives events via: + β€’ SDK async generator (from spawned process) + β€’ Global hooks (from any Claude process, including user CLI) +``` + +#### TypeScript Bridge + +```typescript +import { query } from '@anthropic-ai/claude-agent-sdk' + +class ClaudeCodeBridge extends EventEmitter { + private sessionId: string | null = null + private abortController: AbortController | null = null + + async startSession(prompt: string, options: { + resume?: string + cwd?: string + allowedTools?: string[] + } = {}): Promise { + this.abortController = new AbortController() + + const response = query({ + prompt, + options: { + model: 'claude-sonnet-4-5', + cwd: options.cwd || process.cwd(), + resume: options.resume, + allowedTools: options.allowedTools || ['Read', 'Write', 'Edit', 'Bash'], + permissionMode: 'acceptEdits', + abortController: this.abortController, + settingSources: ['project'], + } + }) + + for await (const message of response) { + if (message.session_id) this.sessionId = message.session_id + + this.emit('event', { + type: `iterate:agent:harness:claude`, + version: 1, + timestamp: Date.now(), + agentId: this.sessionId, + payload: message + }) + } + + return this.sessionId! + } + + interrupt(): void { + this.abortController?.abort() + } +} +``` + +#### Performance Note + +SDK spawns a fresh CLI process for each `query()` call: **~12 second overhead** per query. Mitigations: +- Batch work into fewer, longer sessions +- Use raw Anthropic Messages API for simple queries +- Accept startup cost for agentic workflows + +#### Global Hooks for CLI Sessions + +The SDK captures events for sessions *you* initiate. For events from user CLI sessions (SSH TUI), configure global hooks in `~/.claude/settings.json`: + +```json +{ + "hooks": { + "SessionStart": [{ + "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts SessionStart" }] + }], + "PreToolUse": [{ + "matcher": "*", + "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts PreToolUse" }] + }], + "PostToolUse": [{ + "matcher": "*", + "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts PostToolUse" }] + }], + "Stop": [{ + "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts Stop" }] + }] + } +} +``` + +**Critical limitation**: Hooks cannot capture streaming text chunks - only discrete lifecycle events. + +#### Hook Events Reference + +| Hook | When it Fires | Can Block? | +|------|---------------|------------| +| `SessionStart` | Session begins | No | +| `SessionEnd` | Session ends | No | +| `UserPromptSubmit` | User sends message | No | +| `PreToolUse` | Before tool executes | Yes (exit 2) | +| `PostToolUse` | After tool completes | No | +| `Stop` | Agent turn complete | No | + +#### Concurrent Access Warning + +**No file locking on session files.** Running multiple processes on same session causes corruption. Must: +1. Track which sessions are "active" +2. Reject concurrent access attempts +3. Allow resumption only after previous query completes + +If user SSHes in and runs `claude --resume` on active SDK session: **corruption will occur**. + +#### TUI Compatibility + +```bash +claude --resume # Resume specific session +claude --continue # Resume most recent in current directory +``` + +#### Session Storage + +Sessions: `~/.claude/projects//.jsonl` +Path encoding: `/home/user/myproject` β†’ `-home-user-myproject` + +### Pi Bridge + +Pi uses the simplest model: a **long-running subprocess** with JSON commands on stdin and events on stdout. + +#### TypeScript Bridge + +```typescript +import { spawn, ChildProcess } from 'child_process' +import { createInterface } from 'readline' + +class PiBridge extends EventEmitter { + private process: ChildProcess + private sessionPath: string | null + + constructor(options: { sessionPath?: string } = {}) { + super() + + const args = ['--mode', 'rpc'] + if (options.sessionPath) { + args.push('--session', options.sessionPath) + this.sessionPath = options.sessionPath + } + + this.process = spawn('pi', args, { + stdio: ['pipe', 'pipe', 'inherit'] + }) + + const rl = createInterface({ input: this.process.stdout! }) + rl.on('line', (line) => { + const event = JSON.parse(line) + this.emit('event', { + type: `iterate:agent:harness:pi`, + version: 1, + timestamp: Date.now(), + agentId: this.sessionPath, + payload: event + }) + }) + } + + async prompt(message: string): Promise { + this.process.stdin!.write(JSON.stringify({ type: 'prompt', message }) + '\n') + } + + async abort(): Promise { + this.process.stdin!.write(JSON.stringify({ type: 'abort' }) + '\n') + } + + kill(): void { + this.process.kill() + } +} +``` + +#### RPC Commands Reference + +| Command | Purpose | Response | +|---------|---------|----------| +| `prompt` | Send user message | Streams events, ends with `turn_end` | +| `abort` | Cancel current operation | Confirmation | +| `get_state` | Query session state | State object | +| `get_messages` | Get message history | Message array | +| `branch` | Fork from message index | New branch created | +| `switch_session` | Load different session | Confirmation | + +#### TUI Compatibility + +```bash +pi --continue # Resume most recent session +pi --resume # Interactive session picker +pi --session /path/to.jsonl # Load specific session file +``` + +#### Session Storage + +Sessions: `~/.pi/agent/sessions/` as JSONL with tree structure (`id`/`parentId` for branching). + +### Bridge Comparison Summary + +| Aspect | OpenCode | Claude Code | Pi | +|--------|----------|-------------|-----| +| **Architecture** | HTTP/SSE server | CLI per query + SDK | stdin/stdout RPC | +| **Event source** | SSE endpoint | SDK async generator + hooks | stdout NDJSON | +| **Session ID format** | API-assigned | UUID from `session_id` | File path | +| **TUI attach** | `opencode attach` | `claude --resume` | `pi --session` | +| **Concurrent safe** | Yes (server) | No (file-based) | No (file-based) | +| **Interruption** | `abort()` API | `AbortController` | `abort` command | +| **Startup overhead** | Server must be running | ~12s per query | Process spawn | +| **CLI event capture** | Built-in (same server) | Global hooks needed | Same process | + +### TUI Compatibility Guarantee + +All three harnesses preserve TUI compatibility through their native session storage: + +- **OpenCode**: Multiple TUIs can attach to same server simultaneously +- **Claude Code**: Sessions in `~/.claude/projects/` accessible via `claude --resume` +- **Pi**: Sessions in `~/.pi/agent/sessions/` accessible via `pi --session` + +Bridge design principles: +1. Let harness manage its own session storage (don't intercept or modify) +2. Capture session identifiers and expose them for TUI resumption +3. Prevent concurrent programmatic access to same session +4. Allow users to "take over" in TUI by terminating programmatic access first + +When something goes wrong in web UI, users can SSH in, run native TUI command with session ID, and continue exactly where things left off. + +## Open Questions + +Comprehensive list of implementation decisions with multiple solutions. Each question includes recommended approach. + +--- + +### Session Management & Concurrency + +#### 1. How to prevent file corruption for Claude Code/Pi sessions without built-in locking? + +Claude Code SDK/CLI uses file-based sessions (JSONL) with NO built-in file locking. Concurrent access from SDK and TUI (`claude --resume`) causes corruption. + +**Solution A: Advisory Lock File (`.lock` suffix)** ⭐ Recommended +- Bridge creates `.lock` file before read/write +- Use `flock` (POSIX) with timeout +- Release lock after operation completes +- Requires Claude CLI to also implement locking (may need upstream patch) + +**Solution B: Single Owner with Event Queue** +- Bridge maintains single `SessionManager` process per session +- All access routed through manager via IPC queue +- Manager serializes all reads/writes +- More complex architecture but guaranteed serialization + +**Solution C: Copy-on-Write Sessions** +- SDK operates on `.sdk` copy, TUI on `.tui` copy +- Bridge periodically merges into canonical file +- No blocking, but complex merge logic and storage overhead (3x) + +**Solution D: Event-Sourced Writes** +- SDK only reads session files, never writes +- All mutations go through durable stream as control events +- Bridge applies events atomically +- Major architectural change + +#### 2. Session locking strategy - lease-based vs ownership transfer? + +User SSHes in and runs `claude --resume` on session actively used by SDK. Who "owns" the session? + +**Solution A: Lease-Based with Heartbeat** +- Session lock acquired as timed lease (30s) +- Lock holder sends heartbeat every 10s to renew +- Missed heartbeat = lock expires +- Automatic recovery from crashed processes + +**Solution B: Explicit Handoff Protocol** ⭐ Recommended +- SDK holds session, user types `claude --resume` +- TUI sends `iterate:agent:control:request_ownership` event +- Bridge gracefully pauses SDK, releases lock +- Emits `iterate:agent:system:ownership_transferred` +- Clean handoff, no race conditions, audit trail + +**Solution C: Read-Only Observer Mode** +- TUI launches in "observer" mode if session locked by SDK +- User sees live updates but cannot send messages +- User types `/takeover` command to trigger handoff + +#### 3. How to amortize 12-second Claude Code SDK startup overhead? + +**Solution A: Long-Lived SDK Daemon per Session** +- Bridge spawns SDK process once, keeps alive for session lifetime +- Queries sent via stdin/IPC +- Near-instant response time after first query +- Memory overhead (one daemon per active session) + +**Solution B: Connection Pool with Session Affinity** +- Maintain pool of N warm SDK processes +- Incoming query assigned to idle process from pool +- LRU eviction when pool full +- Bounded resource usage + +**Solution C: Lazy Initialization with Prompt Queue** +- User sends message β†’ immediately returns "Initializing agent..." +- SDK starts in background (12s) +- Subsequent messages queued during startup +- Poor UX for first message + +**Solution D: Pre-Warmed SDK on Agent Create** ⭐ Recommended +- `iterate:agent:control:create` triggers immediate SDK spawn +- SDK starts loading context before any user messages +- By the time user types first message, SDK likely ready +- Best UX, explicit lifecycle management + +--- + +### Event Streaming & Format + +#### 4. Event deduplication between SDK and hook events? + +When using both SDK async generators AND global hooks for same harness, events may appear twice. Both get wrapped into `iterate:agent:harness:claude` and appended. + +**Solution A: Hash-Based Deduplication** ⭐ Recommended +- Generate deterministic IDs from content hash +- Deduplicate at append time via before-append hook +- TTL cache of recent event IDs (last 1000 events) +- Works for any event source, idempotent appends + +**Solution B: Event Source Tagging** +- Tag events with source (`sdk` vs `hook`) +- Suppress hook events if SDK is active for session +- Requires per-harness coordination logic + +**Solution C: Offset-Based Windowed Deduplication** +- Store recent event content (last N offsets) +- Check for duplicates within window +- Bounded memory, transparent to sources + +#### 5. Timestamp reconciliation - our time vs harness time vs LLM time? + +Three different timestamp sources with different semantics. + +**Solution A: Multi-Timestamp Envelope** ⭐ Recommended +- Store all three timestamps in event +- Use Iterate time (`timestamp`) for ordering +- Expose harness/LLM times in `metadata` for debugging +- Can compute latencies: `harnessToIterate`, `llmToHarness` + +```typescript +interface IterateEvent { + timestamp: number // PRIMARY - for ordering + payload: { + timestamp?: number // Harness emit time + } + metadata?: { + llmTimestamp?: number + latencies?: { harnessToIterate: number } + } +} +``` + +**Solution B: Iterate Time Only** +- Only `timestamp` at envelope level +- Harness/LLM times stay in payload as opaque data +- Simpler but less debuggable + +#### 6. Capturing streaming text from Claude Code when user is in native TUI? + +SDK sessions yield streaming chunks, but CLI hooks can't capture streaming text. When user uses TUI directly, streaming invisible to hooks. + +**Solution A: PTY Wrapper Interception** +- Wrap CLI in pseudo-terminal +- Parse ANSI output to reconstruct streaming chunks +- No harness changes needed +- Brittle, high overhead, false positives risk + +**Solution B: Patch Claude Code for Streaming Hooks** +- Fork or patch Claude Code with `onStreamingChunk` hook +- Clean first-class streaming support +- Requires maintaining fork or upstreaming + +**Solution C: Dual-Path Hybrid** ⭐ Recommended +- SDK for Iterate-initiated sessions (full streaming) +- Lifecycle-only capture for user CLI sessions +- Document limitation: "Streaming text visible in web UI for SDK sessions; CLI sessions show lifecycle events only" +- Pragmatic, no brittle parsing + +--- + +### Process Lifecycle & Supervision + +#### 7. OpenCode server ownership - who starts and manages the server process? + +**Solution A: User-Managed Server (Assume Running)** +- Iterate connects to existing server via config +- User controls lifecycle (systemd, Docker, manual) +- Simple for Iterate, poor developer experience + +**Solution B: Iterate Spawns and Supervises Server** +- Process supervisor launches OpenCode server on first agent creation +- Keeps alive, restarts on crashes +- Better UX but Iterate must manage external binary + +**Solution C: Hybrid Auto-Daemon Pattern** ⭐ Recommended +- Attempt to connect to existing server +- Spawn local daemon if none found +- User can override with `OPENCODE_SERVER_URL` env var +- Matches durable-streams pattern, best of both worlds + +#### 8. Pi process supervision strategy? + +**Solution A: Basic Spawn with Manual Restart** +- Spawn Pi process when agent created, kill on destroy +- If Pi crashes, agent unavailable until user recreates +- Simplest but poor reliability + +**Solution B: Supervised with Automatic Restart** ⭐ Recommended +- Monitor Pi process health via heartbeat/stderr +- Automatically restart on crash, emit system events +- Implement backoff/retry, circuit breaker (max restarts) +- Resilient to transient failures + +**Solution C: Supervised with Manual Restart** +- Detect and notify on crashes +- Require explicit restart command +- Good for debugging but higher user burden + +#### 9. Health monitoring mechanism for agent processes? + +**Solution A: Passive - Monitor Process Exit Events Only** +- Listen to `process.on('exit')` events +- Zero overhead but doesn't detect hangs/zombies + +**Solution B: Active Heartbeat via RPC Ping** +- Periodic ping to RPC processes +- Expect pong within timeout +- Detects hangs, measures latency +- Overhead from periodic messages + +**Solution C: Hybrid** ⭐ Recommended +- Passive exit monitoring (immediate crash detection) +- Plus 60s health checks (hang detection) +- Emit structured events with crash reason + +--- + +### TUI Compatibility & Handoff + +#### 10. Handoff protocol when user transitions from web UI to native TUI? + +**Solution A: Passive Handoff (No Coordination)** +- No explicit handoff, both clients see all events via SSE +- Race conditions, confusing UX + +**Solution B: Explicit Handoff with Lock File** +- `.iterate/active-client` file indicates which client has control +- Web UI checks before sending, warns if TUI active +- Stale lock file risk if TUI crashes + +**Solution C: Graceful Takeover with Warning** ⭐ Recommended +- TUI launches β†’ emit `iterate:agent:system:client-attached` event +- Web UI shows takeover banner, remains functional (read-only or warnings) +- Explicit signal without hard locks + +```typescript +{ + type: "iterate:agent:system:client-attached", + agentId: "agent-123", + data: { + clientType: "tui", + clientId: "opencode-tty", + pid: 12345 + } +} +``` + +#### 11. Should handoff be graceful (coordinate) or forced (take over immediately)? + +**Solution A: Forced Takeover** +- TUI just works, no handoff protocol +- Race conditions, confusing UX when both sending + +**Solution B: Graceful Handoff with Pause** ⭐ Recommended +- When TUI attaches, web UI enters "paused" state +- Shows banner, disables input until TUI detaches +- Clear state machine: web-active β†’ tui-active β†’ transitioning + +**Solution C: Collaborative Mode with Input Tagging** +- Both clients remain active +- All inputs tagged with `clientId` +- UI shows who sent what +- Good for pair debugging + +#### 12. How does programmatic control resume after user exits TUI? + +**Solution A: Automatic Reactivation** ⭐ Recommended +- TUI process exits β†’ bridge emits `client-detached` event +- Web UI automatically resumes +- Seamless, no manual intervention + +**Solution B: Explicit Resume Command** +- User must run `iterate resume ` or click "Resume Control" +- More explicit but extra step + +**Solution C: Session Timeout with Auto-Resume** +- If no TUI activity for N minutes, auto-resume web +- Handles crashes gracefully but timeout tuning tricky + +--- + +### Tool Injection & Routing + +#### 13. Tool definition format conversion across harnesses? + +Each harness has different tool registration formats. + +**Solution A: Schema-First with Format Adapters** ⭐ Recommended for MVP +- Define tools using `@effect/ai/Tool` (Effect Schema) +- Adapt to each format via converter functions +- Single source of truth, type-safe + +```typescript +interface ToolFormatAdapter { + toOpenCode(tool: IterateTool): OpenCodeToolDef + toClaudeCode(tool: IterateTool): ClaudeToolDef + toPi(tool: IterateTool): TypeBoxSchema + toMcp(tool: IterateTool): McpToolDef +} +``` + +**Solution B: MCP-Native with Converters** ⭐ Recommended long-term +- Use MCP tool schema as canonical format +- Convert to harness-specific on demand +- MCP is emerging standard (Anthropic-backed) +- Future-proof as harnesses adopt MCP natively + +#### 14. MCP server vs native tool injection? + +**Solution A: MCP Server (Harnesses Connect to Iterate)** +- Run Iterate as MCP server +- Harnesses that support MCP connect via protocol +- Works immediately with Claude Code +- Not all harnesses support MCP yet + +**Solution B: Native Injection via Harness APIs** +- Inject tools using each harness's native mechanism +- Works with all harnesses +- N implementations (one per harness) + +**Solution C: Hybrid (MCP + Native Fallback)** ⭐ Recommended +- Use MCP where supported (Claude Code) +- Fall back to native injection otherwise (Pi, OpenCode) +- Best of both worlds, future-proof + +#### 15. Tool call interception and routing to Iterate server? + +Some tools need Iterate state (event stream access). + +**Solution A: Bridge Subscribes to Harness Events, Intercepts** +- Bridge listens to harness event stream +- Intercepts tool call events, executes on Iterate side, injects result back +- Race conditions if harness executes locally first + +**Solution B: Tool Handlers are HTTP Endpoints** ⭐ Recommended for native injection +- Register tools where handler is HTTP call to Iterate +- Harness executes HTTP call directly +- Simple request/response model + +**Solution C: MCP Protocol Routing** ⭐ Recommended for MCP path +- Using MCP, routing is built-in +- Harness calls tool via MCP protocol β†’ routes to server +- Zero custom routing logic + +--- + +### Error Recovery & State + +#### 16. State recovery after Iterate daemon restarts? + +**Solution A: PID File Registry with Process Liveness Check** +- Maintain `.iterate/agents/{agentId}.pid` files +- On startup, scan PID files, verify processes alive +- Reconnect to survivors, clean up stale PIDs +- PID reuse risk, race conditions + +**Solution B: State File + Offset-Based Resume** ⭐ Recommended +- Store agent state file: `.iterate/agents/{agentId}.json` +- On restart, replay stream from last offset to reconstruct lifecycle +- Stream is source of truth, handles partial operations naturally + +```json +{ + "agentId": "agent-123", + "harness": "opencode", + "status": "running", + "lastEventOffset": "0000000000000042", + "processHint": { "pid": 12345, "endpoint": "http://localhost:8080" } +} +``` + +**Solution C: Ephemeral Agents + Explicit Restart** +- Daemon restart = all agents considered stopped +- Clients must explicitly re-create +- Simple but user-facing disruption + +#### 17. Handling partial operations after crashes? + +Agent turn interrupted mid-operation (tool started but no result). + +**Solution A: Idempotent Event Replay** +- Every operation has start+end events +- Replay stream, detect incomplete operations +- Emit synthetic completion events with status="cancelled" +- No special cleanup logic needed + +**Solution B: Compensation Events** ⭐ Recommended +- If operation incomplete after timeout, emit compensation event +- `tool_execution_start` with no `tool_execution_end` β†’ emit `tool_execution_cancelled` +- Explicit failure handling in stream +- Use operation-specific timeouts (tool: 5min, LLM request: 2min) + +#### 18. Checkpoint strategy for fast startup? + +For long-running agents with thousands of events, replaying from offset 0 is slow. + +**Solution A: Periodic State Snapshots** ⭐ Recommended +- Every N events (1000), serialize reducedContext to `.iterate/snapshots/{agentId}-{offset}.json` +- On startup, load latest snapshot + replay events since that offset +- Fast startup, standard event sourcing pattern + +```json +{ + "agentId": "agent-123", + "offset": "0000000000001000", + "reducedContext": { "messages": [...], "config": {...} }, + "version": 1 +} +``` + +**Solution B: Last-Event-Offset Tracking Only** +- Store only last processed offset +- Replay from that offset (no full state) +- Minimal storage but doesn't help if replay slow + +--- + +### Storage Architecture + +#### 19. Storage duplication - do we need harness storage if we have our stream? + +**Solution A: Full Duplication** ⭐ Recommended (current architecture) +- Both systems maintain independent storage +- Native TUI works unchanged +- Simple bridge implementation +- 2x storage space but avoids complexity + +**Solution B: Single Source of Truth (Iterate Only)** +- Configure harnesses to skip persistence +- Breaking change to native TUI +- Not viable if harnesses don't support memory-only mode + +**Solution C: Harness as Primary, Iterate as Index** +- Iterate maintains lightweight index pointing to harness storage +- No duplication but complex query path + +#### 20. Session ID mapping - Iterate agentId ↔ Harness sessionId? + +**Solution A: Deterministic Derivation** +- `harnessSessionId = iterate-${harness}-${agentId}` +- Stateless, no storage needed +- Naming collision risk + +**Solution B: Mapping Events in Stream** ⭐ Recommended +- Store mapping as `iterate:agent:system:session_mapped` events +- Auditable, flexible, supports one-to-many + +```typescript +{ + type: "iterate:agent:system:session_mapped", + agentId: "agent-123", + data: { harness: "opencode", harnessSessionId: "sess-abc-xyz" } +} +``` + +**Solution C: Separate Mapping Storage** +- Store in `{dataDir}/session-mappings.json` +- Fast lookup without stream replay +- Consistency risk with stream + +#### 21. Query patterns - when to hit our stream vs harness storage? + +| Query | Iterate Stream | Harness Storage | Recommended | +|-------|---------------|-----------------|-------------| +| Get latest agent state | Replay from offset | Query harness API | Harness (cached state) | +| Stream new events (SSE) | PubSub subscription | Poll harness | Iterate (real-time) | +| Search across all agents | Scan all streams | N/A | Iterate (global view) | +| Full conversation history | Read stream file | Query harness DB | Iterate (source of truth) | + +**Recommendation:** Read-through cache pattern - check Iterate in-memory cache first, fall back to stream read. + +#### 22. Storage cleanup - when to GC old sessions? + +**Solution A: Manual Cleanup Only** +- CLI command: `iterate cleanup --older-than 30d` +- User control, no accidental loss +- Storage can grow unchecked + +**Solution B: Automatic Time-Based Expiry** +- Background job deletes sessions older than 30 days +- No intervention needed +- Data loss risk if user wants old sessions + +**Solution C: Lifecycle with Archival** ⭐ Recommended +- Active (30 days) β†’ Archived (compressed) β†’ Deleted (90 days) +- User can restore archived sessions +- Clear lifecycle stages + +--- + +### Future Considerations + +#### 23. Subscription filtering for consumers? + +Consumers need to subscribe to event subsets (web UI: agent X only, Slack bot: messages only). + +**Solution A: Stream-per-Agent** ⭐ Recommended for MVP +- Create separate durable stream per agent +- Perfect isolation, no server-side filtering +- Stream proliferation concern (1000 agents = 1000 streams) + +**Solution B: Single Stream with Server-Side Filtering** +- Add `filter` parameter to SSE subscribe endpoint +- Flexible but CPU overhead + +**Solution C: Event Type-Based Topic Routing** +- Subscribe to event type prefixes +- Efficient but can't filter by non-type fields + +#### 24. Multi-agent coordination? + +Can agents communicate via the shared stream? + +**Options to explore:** +- Agent A emits event, bridge for Agent B can react via hook +- Explicit `iterate:agent:control:forward` event +- Cross-agent event subscriptions: "notify agent X when `slack:webhook` arrives" +- The naming scheme supports this, routing logic TBD + +--- + +### Recommendations Summary + +| Domain | Question | Recommended Solution | +|--------|----------|---------------------| +| **Session Management** | File corruption prevention | Advisory lock files | +| **Session Management** | Session ownership | Explicit handoff protocol | +| **Session Management** | SDK startup overhead | Pre-warmed SDK on create | +| **Event Streaming** | Deduplication | Hash-based with source tagging | +| **Event Streaming** | Timestamp reconciliation | Multi-timestamp envelope | +| **Event Streaming** | CLI streaming capture | Dual-path hybrid (accept limitation) | +| **Process Lifecycle** | OpenCode server | Hybrid auto-daemon | +| **Process Lifecycle** | Pi supervision | Supervised with auto-restart | +| **Process Lifecycle** | Health monitoring | Hybrid exit + health checks | +| **TUI Handoff** | Handoff protocol | Graceful takeover with warning | +| **TUI Handoff** | Graceful vs forced | Graceful handoff with pause | +| **TUI Handoff** | Resume after TUI | Automatic reactivation | +| **Tool Injection** | Format conversion | Schema-first adapters β†’ MCP long-term | +| **Tool Injection** | MCP vs native | Hybrid (MCP + native fallback) | +| **Tool Injection** | Call routing | MCP routing + HTTP endpoints | +| **Error Recovery** | State recovery | State file + offset replay | +| **Error Recovery** | Partial operations | Compensation events | +| **Error Recovery** | Checkpointing | Periodic state snapshots | +| **Storage** | Duplication strategy | Full duplication | +| **Storage** | Session mapping | Mapping events in stream | +| **Storage** | Cleanup | Lifecycle with archival | + +## Next Steps + +1. **Review event naming** - Confirm Option C (source-based namespacing) with team +2. **Prototype OpenCode bridge** - Connect to event bus, emit `iterate:agent:harness:opencode` events +3. **Prototype Pi bridge** - Process management, stdin/stdout RPC, emit `iterate:agent:harness:pi` events +4. **Tool injection POC** - One Iterate tool registered in each harness +5. **SSE consumer** - Simple web UI that renders native event formats +6. **Versioning test** - Verify `version` field handling with schema evolution diff --git a/src/durable-streams/README.md b/src/durable-streams/README.md index 1039057..9ecd216 100644 --- a/src/durable-streams/README.md +++ b/src/durable-streams/README.md @@ -16,6 +16,8 @@ Pure event streams with append/subscribe semantics. No LLM logic, no agent behav ## Architecture +Each layer is expressible entirely in terms of the layer below (onion model). + ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CLI Layer β”‚ @@ -25,7 +27,7 @@ Pure event streams with append/subscribe semantics. No LLM logic, no agent behav β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ HTTP Layer β”‚ +β”‚ HTTP Routes (Layer 5) β”‚ β”‚ POST /streams/:name β†’ append event β”‚ β”‚ GET /streams/:name β†’ subscribe (SSE) β”‚ β”‚ GET /streams/:name/events β†’ get historic events β”‚ @@ -35,27 +37,107 @@ Pure event streams with append/subscribe semantics. No LLM logic, no agent behav β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ StreamManager (Layer 1) β”‚ -β”‚ getStream() β†’ lazy init + cache β”‚ +β”‚ StreamManager (Layer 4) β”‚ +β”‚ getStream() β†’ lazy init + cache via factory β”‚ β”‚ append/subscribe/list/delete β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ DurableStream (Layer 0) β”‚ +β”‚ DurableStreamFactory (Layer 3) β”‚ +β”‚ Plain β†’ returns base DurableStream β”‚ +β”‚ WithHooks β†’ wraps with before/after hooks β”‚ +β”‚ ActiveFactory β†’ change one line to swap implementations β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ withHooks (Layer 2) β”‚ +β”‚ Pure wrapper function for hook composition β”‚ +β”‚ Before hooks: veto append on failure (HookError) β”‚ +β”‚ After hooks: log errors but don't fail β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ DurableStream (Layer 1) β”‚ β”‚ Per-stream state: offset counter, PubSub β”‚ β”‚ append() β†’ increment offset, store, broadcast β”‚ β”‚ subscribe() β†’ historical catchup + live PubSub β”‚ +β”‚ Pure stream primitive - no hook concept β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Storage β”‚ +β”‚ Storage (Layer 0) β”‚ β”‚ InMemory (tests) | FileSystem (production) β”‚ β”‚ Data persisted in .iterate/streams/*.json β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` +## Hooks System + +Hooks allow intercepting stream operations without modifying the base `DurableStream` implementation. + +### Hook Types + +**Before hooks** run before append. Failure vetoes the operation: +```typescript +const validateType: BeforeAppendHook = { + id: "validate-type", + run: ({ data }) => { + const obj = data as Record + if (typeof obj._type !== "string") { + return Effect.fail(new HookError({ + hookId: "validate-type", + message: "Data must have _type field" + })) + } + return Effect.void + } +} +``` + +**After hooks** run after successful append. Errors are logged but don't fail: +```typescript +const auditLog: AfterAppendHook = { + id: "audit-log", + run: ({ name, event }) => + Effect.log("Event appended", { stream: name, offset: event.offset }) +} +``` + +### Using withHooks + +Wrap any `DurableStream` with hooks: +```typescript +import { withHooks } from "./with-hooks.ts" + +const hooked = withHooks(baseStream, { + beforeAppend: [validateType, rateLimit], + afterAppend: [auditLog, notifyDownstream] +}) +``` + +### Swapping Implementations + +Edit `stream-factory.ts` to change `ActiveFactory`: +```typescript +// Default: plain streams (no hooks) +export const ActiveFactory = PlainFactory + +// To test validation hooks: +export const ActiveFactory = ValidatedFactory + +// To test agent event hooks: +export const ActiveFactory = EmbryonicAgentFactory +``` + +Pre-configured variants in `stream-factory.ts`: +- `PlainFactory` - base DurableStream, no hooks +- `ValidatedFactory` - requires `_type` field on all events +- `EmbryonicAgentFactory` - validates agent events (`_type` starts with `agent:`) + logging + ## CLI Reference ### Server Commands @@ -246,8 +328,11 @@ Offsets are lexicographically sortable strings. Special offset `-1` means "start | `main.ts` | Entry point | | `daemon.ts` | Daemon management (start/stop/status) | | `client.ts` | HTTP client with auto-daemon | -| `http-routes.ts` | HTTP route handlers | -| `stream-manager.ts` | Stream lifecycle management | -| `stream.ts` | Core DurableStream implementation | -| `storage.ts` | Storage backend interface | +| `http-routes.ts` | HTTP route handlers (Layer 5) | +| `stream-manager.ts` | Stream lifecycle management (Layer 4) | +| `stream-factory.ts` | Factory service + ActiveFactory (Layer 3) | +| `with-hooks.ts` | Hook wrapper function (Layer 2) | +| `hooks.ts` | Hook types and HookError | +| `stream.ts` | Core DurableStream implementation (Layer 1) | +| `storage.ts` | Storage backend interface (Layer 0) | | `types.ts` | Type definitions | diff --git a/src/durable-streams/cli.ts b/src/durable-streams/cli.ts index a725144..3ff1ad4 100644 --- a/src/durable-streams/cli.ts +++ b/src/durable-streams/cli.ts @@ -24,6 +24,7 @@ import { StreamClientService } from "./client.ts" import { DaemonService, DATA_DIR } from "./daemon.ts" import { durableStreamsRouter } from "./http-routes.ts" import { Storage } from "./storage.ts" +import { ActiveFactory } from "./stream-factory.ts" import { StreamManagerService } from "./stream-manager.ts" import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" @@ -89,6 +90,7 @@ const serverRunCommand = Command.make( yield* Console.log("") const serviceLayer = StreamManagerService.Live.pipe( + Layer.provide(ActiveFactory), Layer.provide(storageLayer) ) diff --git a/src/durable-streams/hooks.e2e.test.ts b/src/durable-streams/hooks.e2e.test.ts new file mode 100644 index 0000000..02c3b07 --- /dev/null +++ b/src/durable-streams/hooks.e2e.test.ts @@ -0,0 +1,273 @@ +/** + * E2E tests proving hooks are executed. + * + * Tests ValidatedFactory which requires _type field on all events. + */ +import { HttpServer } from "@effect/platform" +import { NodeHttpServer } from "@effect/platform-node" +import { Deferred, Effect, Fiber, Layer, Ref } from "effect" +import { createServer } from "node:http" +import { afterAll, beforeAll, describe, expect, test } from "vitest" +import type { StreamHooks } from "./hooks.ts" +import { HookError } from "./hooks.ts" +import { durableStreamsRouter } from "./http-routes.ts" +import { Storage } from "./storage.ts" +import { DurableStreamFactory } from "./stream-factory.ts" +import { StreamManagerService } from "./stream-manager.ts" +import type { StreamEvent } from "./types.ts" + +describe("Hooks E2E", () => { + describe("ValidatedFactory (before hook)", () => { + let baseUrl: string + let serverFiber: Fiber.RuntimeFiber + + // Before hook that requires _type field + const validatedHooks: StreamHooks = { + beforeAppend: [ + { + id: "require-type-field", + run: ({ data }) => { + const obj = data as Record + if (typeof obj._type !== "string") { + return Effect.fail( + new HookError({ + hookId: "require-type-field", + message: "Data must have _type string field" + }) + ) + } + return Effect.void + } + } + ] + } + + beforeAll(async () => { + // Build server with ValidatedFactory + const serverLayer = Layer.mergeAll( + NodeHttpServer.layer(createServer, { port: 0 }), + StreamManagerService.Live.pipe( + Layer.provide(DurableStreamFactory.WithHooks(validatedHooks)), + Layer.provide(Storage.InMemory) + ) + ).pipe(Layer.provideMerge(HttpServer.layerContext)) + + const fiberId = Effect.runSync(Effect.fiberId) + const addressDeferred = Deferred.unsafeMake(fiberId) + + const serverEffect = Effect.gen(function*() { + const server = yield* HttpServer.HttpServer + yield* Deferred.succeed(addressDeferred, server.address) + yield* HttpServer.serveEffect(durableStreamsRouter) + return yield* Effect.never + }).pipe( + Effect.scoped, + Effect.provide(serverLayer) + ) + + serverFiber = Effect.runFork(serverEffect) + + const address = await Effect.runPromise(Deferred.await(addressDeferred)) + if (address._tag === "TcpAddress") { + baseUrl = `http://127.0.0.1:${address.port}` + } else { + throw new Error("Expected TCP address") + } + }) + + afterAll(async () => { + await Effect.runPromise(Fiber.interrupt(serverFiber)) + }) + + test("rejects events without _type field (before hook vetoes)", async () => { + const response = await fetch(`${baseUrl}/streams/validated-test`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: { message: "no type field" } }) + }) + + // Should fail because _type is missing - HookError returns 400 + expect(response.status).toBe(400) + const body = await response.json() as { error: string; hookId: string } + expect(body.error).toBe("Data must have _type string field") + expect(body.hookId).toBe("require-type-field") + }) + + test("accepts events with _type field (before hook passes)", async () => { + const response = await fetch(`${baseUrl}/streams/validated-test`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: { _type: "message", text: "hello" } }) + }) + + expect(response.status).toBe(201) + + const event = await response.json() as StreamEvent + expect(event.offset).toBe("0000000000000000") + expect(event.data).toEqual({ _type: "message", text: "hello" }) + }) + }) + + describe("Custom hooks (after hook)", () => { + let baseUrl: string + let serverFiber: Fiber.RuntimeFiber + let afterHookCallCount: Ref.Ref + + beforeAll(async () => { + // Create a ref to track after hook calls + afterHookCallCount = Effect.runSync(Ref.make(0)) + + // After hook that increments counter + const trackingHooks: StreamHooks = { + afterAppend: [ + { + id: "track-append", + run: () => Ref.update(afterHookCallCount, (n) => n + 1) + } + ] + } + + const serverLayer = Layer.mergeAll( + NodeHttpServer.layer(createServer, { port: 0 }), + StreamManagerService.Live.pipe( + Layer.provide(DurableStreamFactory.WithHooks(trackingHooks)), + Layer.provide(Storage.InMemory) + ) + ).pipe(Layer.provideMerge(HttpServer.layerContext)) + + const fiberId = Effect.runSync(Effect.fiberId) + const addressDeferred = Deferred.unsafeMake(fiberId) + + const serverEffect = Effect.gen(function*() { + const server = yield* HttpServer.HttpServer + yield* Deferred.succeed(addressDeferred, server.address) + yield* HttpServer.serveEffect(durableStreamsRouter) + return yield* Effect.never + }).pipe( + Effect.scoped, + Effect.provide(serverLayer) + ) + + serverFiber = Effect.runFork(serverEffect) + + const address = await Effect.runPromise(Deferred.await(addressDeferred)) + if (address._tag === "TcpAddress") { + baseUrl = `http://127.0.0.1:${address.port}` + } else { + throw new Error("Expected TCP address") + } + }) + + afterAll(async () => { + await Effect.runPromise(Fiber.interrupt(serverFiber)) + }) + + test("after hook is called on successful append", async () => { + // Get initial count + const initialCount = Effect.runSync(Ref.get(afterHookCallCount)) + + // Append 3 events + await fetch(`${baseUrl}/streams/after-hook-test`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "event1" }) + }) + await fetch(`${baseUrl}/streams/after-hook-test`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "event2" }) + }) + await fetch(`${baseUrl}/streams/after-hook-test`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "event3" }) + }) + + // Check after hook was called 3 times + const finalCount = Effect.runSync(Ref.get(afterHookCallCount)) + expect(finalCount - initialCount).toBe(3) + }) + }) + + describe("Both hooks", () => { + let baseUrl: string + let serverFiber: Fiber.RuntimeFiber + let hookOrder: Ref.Ref> + + beforeAll(async () => { + // Track hook execution order + hookOrder = Effect.runSync(Ref.make>([])) + + const orderTrackingHooks: StreamHooks = { + beforeAppend: [ + { + id: "before-1", + run: () => Ref.update(hookOrder, (arr) => [...arr, "before-1"]) + }, + { + id: "before-2", + run: () => Ref.update(hookOrder, (arr) => [...arr, "before-2"]) + } + ], + afterAppend: [ + { + id: "after-1", + run: () => Ref.update(hookOrder, (arr) => [...arr, "after-1"]) + }, + { + id: "after-2", + run: () => Ref.update(hookOrder, (arr) => [...arr, "after-2"]) + } + ] + } + + const serverLayer = Layer.mergeAll( + NodeHttpServer.layer(createServer, { port: 0 }), + StreamManagerService.Live.pipe( + Layer.provide(DurableStreamFactory.WithHooks(orderTrackingHooks)), + Layer.provide(Storage.InMemory) + ) + ).pipe(Layer.provideMerge(HttpServer.layerContext)) + + const fiberId = Effect.runSync(Effect.fiberId) + const addressDeferred = Deferred.unsafeMake(fiberId) + + const serverEffect = Effect.gen(function*() { + const server = yield* HttpServer.HttpServer + yield* Deferred.succeed(addressDeferred, server.address) + yield* HttpServer.serveEffect(durableStreamsRouter) + return yield* Effect.never + }).pipe( + Effect.scoped, + Effect.provide(serverLayer) + ) + + serverFiber = Effect.runFork(serverEffect) + + const address = await Effect.runPromise(Deferred.await(addressDeferred)) + if (address._tag === "TcpAddress") { + baseUrl = `http://127.0.0.1:${address.port}` + } else { + throw new Error("Expected TCP address") + } + }) + + afterAll(async () => { + await Effect.runPromise(Fiber.interrupt(serverFiber)) + }) + + test("hooks run in correct order: before-1, before-2, after-1, after-2", async () => { + // Reset order + Effect.runSync(Ref.set(hookOrder, [])) + + await fetch(`${baseUrl}/streams/order-test`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ data: "test" }) + }) + + const order = Effect.runSync(Ref.get(hookOrder)) + expect(order).toEqual(["before-1", "before-2", "after-1", "after-2"]) + }) + }) +}) diff --git a/src/durable-streams/hooks.ts b/src/durable-streams/hooks.ts new file mode 100644 index 0000000..1f25d10 --- /dev/null +++ b/src/durable-streams/hooks.ts @@ -0,0 +1,40 @@ +/** + * Hook types for DurableStream composition (Layer 2) + * + * Hooks have IDs for debugging/logging and future dependency-based execution. + * Before-hooks can veto operations; after-hooks are fire-and-forget. + */ +import type { Effect } from "effect" +import { Schema } from "effect" +import type { StreamEvent, StreamName } from "./types.ts" + +/** Error when a before-hook vetoes an operation */ +export class HookError extends Schema.TaggedError()("HookError", { + hookId: Schema.String, + message: Schema.String, + cause: Schema.optional(Schema.Unknown) +}) {} + +/** Before-hook: runs before append, can veto by failing with HookError */ +export interface BeforeAppendHook { + readonly id: string + readonly run: (opts: { + name: StreamName + data: unknown + }) => Effect.Effect +} + +/** After-hook: runs after successful append, errors logged but don't fail */ +export interface AfterAppendHook { + readonly id: string + readonly run: (opts: { + name: StreamName + event: StreamEvent + }) => Effect.Effect +} + +/** Hook configuration for a DurableStream */ +export interface StreamHooks { + readonly beforeAppend?: ReadonlyArray + readonly afterAppend?: ReadonlyArray +} diff --git a/src/durable-streams/http-routes.ts b/src/durable-streams/http-routes.ts index 5fab7d0..6c0da34 100644 --- a/src/durable-streams/http-routes.ts +++ b/src/durable-streams/http-routes.ts @@ -58,10 +58,19 @@ const appendHandler = Effect.gen(function*() { const { data } = parseResult.right - const event = yield* manager.append({ name: name as StreamName, data }).pipe( - Effect.mapError((e) => new Error(e.message)) - ) + const appendResult = yield* manager.append({ name: name as StreamName, data }).pipe(Effect.either) + + if (appendResult._tag === "Left") { + const err = appendResult.left as { _tag: string; message: string; hookId?: string } + // HookError from validation hooks (runtime check - factory may inject hooked streams) + if (err._tag === "HookError" && err.hookId) { + return yield* HttpServerResponse.json({ error: err.message, hookId: err.hookId }, { status: 400 }) + } + // StorageError + return yield* HttpServerResponse.json({ error: err.message }, { status: 500 }) + } + const event = appendResult.right const encoded = Schema.encodeSync(StreamEvent)(event) return yield* HttpServerResponse.json(encoded, { status: 201 }) }) diff --git a/src/durable-streams/index.ts b/src/durable-streams/index.ts index 4432cd8..ec5a054 100644 --- a/src/durable-streams/index.ts +++ b/src/durable-streams/index.ts @@ -19,16 +19,29 @@ export { } from "./types.ts" export type { Offset as OffsetType, StreamName as StreamNameType } from "./types.ts" -// Storage +// Storage (Layer 0) export { Storage } from "./storage.ts" -// Stream (Layer 0) +// Stream (Layer 1) export { type DurableStream, makeDurableStream } from "./stream.ts" -// StreamManager (Layer 1) +// Hooks (Layer 2) +export { type AfterAppendHook, type BeforeAppendHook, HookError, type StreamHooks } from "./hooks.ts" +export { withHooks } from "./with-hooks.ts" + +// Stream Factory (Layer 3) +export { + ActiveFactory, + DurableStreamFactory, + EmbryonicAgentFactory, + PlainFactory, + ValidatedFactory +} from "./stream-factory.ts" + +// StreamManager (Layer 4) export { type StreamManager, StreamManagerService } from "./stream-manager.ts" -// HTTP Routes (Layer 2) +// HTTP Routes (Layer 5) export { durableStreamsRouter } from "./http-routes.ts" // CLI diff --git a/src/durable-streams/persistence.e2e.test.ts b/src/durable-streams/persistence.e2e.test.ts index 656c637..82d39aa 100644 --- a/src/durable-streams/persistence.e2e.test.ts +++ b/src/durable-streams/persistence.e2e.test.ts @@ -14,6 +14,7 @@ import { join } from "node:path" import { describe, expect, test } from "vitest" import { durableStreamsRouter } from "./http-routes.ts" import { Storage } from "./storage.ts" +import { PlainFactory } from "./stream-factory.ts" import { StreamManagerService } from "./stream-manager.ts" import type { StreamEvent } from "./types.ts" @@ -26,6 +27,7 @@ const startServer = async (dataDir: string): Promise<{ Layer.provide(NodeContext.layer) ) const serviceLayer = StreamManagerService.Live.pipe( + Layer.provide(PlainFactory), Layer.provide(storageLayer) ) diff --git a/src/durable-streams/stream-factory.ts b/src/durable-streams/stream-factory.ts new file mode 100644 index 0000000..59723b0 --- /dev/null +++ b/src/durable-streams/stream-factory.ts @@ -0,0 +1,122 @@ +/** + * DurableStreamFactory - Service that creates DurableStream instances + * + * Different factory implementations provide different behaviors: + * - Plain: returns base DurableStream unchanged + * - WithHooks: wraps streams with before/after hooks + */ +import { Effect, Layer } from "effect" +import { HookError, type StreamHooks } from "./hooks.ts" +import { Storage } from "./storage.ts" +import { type DurableStream, makeDurableStream } from "./stream.ts" +import type { StorageError, StreamName } from "./types.ts" +import { withHooks } from "./with-hooks.ts" + +/** Factory interface - creates DurableStream instances (Storage already provided) */ +export class DurableStreamFactory extends Effect.Service()( + "@durable-streams/DurableStreamFactory", + { + succeed: { + // Note: Layers provide Storage closure, so make() returns Effect without Storage requirement + make: (_opts: { name: StreamName }): Effect.Effect => + Effect.die("DurableStreamFactory.Default not usable - use Plain or WithHooks layer") + } + } +) { + /** Plain factory - returns base DurableStream unchanged */ + static readonly Plain: Layer.Layer = Layer.effect( + DurableStreamFactory, + Effect.gen(function*() { + const storage = yield* Storage + return { + make: (opts: { name: StreamName }) => makeDurableStream(opts).pipe(Effect.provideService(Storage, storage)) + } as DurableStreamFactory + }) + ) + + /** Factory that wraps streams with hooks */ + static WithHooks(hooks: StreamHooks): Layer.Layer { + return Layer.effect( + DurableStreamFactory, + Effect.gen(function*() { + const storage = yield* Storage + return { + make: (opts: { name: StreamName }) => + makeDurableStream(opts).pipe( + Effect.provideService(Storage, storage), + // withHooks returns HookedDurableStream which is compatible with DurableStream + // (HookError is added to append error channel) + Effect.map((base) => withHooks(base, hooks) as unknown as DurableStream) + ) + } as DurableStreamFactory + }) + ) + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// PLAYGROUND - Define variant configurations here, change ActiveFactory to swap +// ═══════════════════════════════════════════════════════════════════════════════ + +/** Validated streams - require _type field */ +const validatedHooks: StreamHooks = { + beforeAppend: [ + { + id: "require-type-field", + run: ({ data }) => { + const obj = data as Record + if (typeof obj._type !== "string") { + return Effect.fail( + new HookError({ + hookId: "require-type-field", + message: "Data must have _type string field" + }) + ) + } + return Effect.void + } + } + ] +} + +/** Embryonic agent streams - agent event validation + logging */ +const embryonicAgentHooks: StreamHooks = { + beforeAppend: [ + { + id: "validate-agent-event", + run: ({ data }) => { + const obj = data as Record + if (typeof obj._type !== "string" || !obj._type.startsWith("agent:")) { + return Effect.fail( + new HookError({ + hookId: "validate-agent-event", + message: "Agent events must have _type starting with 'agent:'" + }) + ) + } + return Effect.void + } + } + ], + afterAppend: [ + { + id: "log-agent-event", + run: ({ event, name }) => + Effect.log("Agent event", { + stream: name, + offset: event.offset, + type: (event.data as Record)._type + }) + } + ] +} + +// Variant layers +export const PlainFactory = DurableStreamFactory.Plain +export const ValidatedFactory = DurableStreamFactory.WithHooks(validatedHooks) +export const EmbryonicAgentFactory = DurableStreamFactory.WithHooks(embryonicAgentHooks) + +// ═══════════════════════════════════════════════════════════════════════════════ +// CHANGE THIS LINE TO SWAP IMPLEMENTATION +// ═══════════════════════════════════════════════════════════════════════════════ +export const ActiveFactory = PlainFactory diff --git a/src/durable-streams/stream-manager.ts b/src/durable-streams/stream-manager.ts index 4d4e74f..d908e05 100644 --- a/src/durable-streams/stream-manager.ts +++ b/src/durable-streams/stream-manager.ts @@ -1,13 +1,15 @@ /** - * StreamManager - Layer 1 + * StreamManager - Layer 4 * * Manages multiple named DurableStreams with lazy initialization. * Each stream is created on first access and cached. + * Uses DurableStreamFactory for stream creation (enables hook composition). */ import type { Scope, Stream } from "effect" import { Effect, HashMap, Layer, Ref } from "effect" import { Storage } from "./storage.ts" -import { type DurableStream, makeDurableStream } from "./stream.ts" +import { DurableStreamFactory, PlainFactory } from "./stream-factory.ts" +import type { DurableStream } from "./stream.ts" import { type InvalidOffsetError, type Offset, @@ -45,219 +47,92 @@ export interface StreamManager { delete(opts: { name: StreamName }): Effect.Effect } -/** StreamManager service tag and layer */ -export class StreamManagerService extends Effect.Service()( - "@durable-streams/StreamManager", - { - effect: Effect.gen(function*() { - const storage = yield* Storage - - // Cache of initialized streams - const streamsRef = yield* Ref.make(HashMap.empty()) - - const getStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - const streams = yield* Ref.get(streamsRef) - const existing = HashMap.get(streams, opts.name) - - if (existing._tag === "Some") { - return existing.value - } +/** Helper to create StreamManager implementation given factory and storage */ +const makeStreamManager = Effect.gen(function*() { + const factory = yield* DurableStreamFactory + const storage = yield* Storage - // Create new stream - provide Storage from closure - const stream = yield* makeDurableStream({ name: opts.name }).pipe( - Effect.provideService(Storage, storage) - ) - yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) - return stream - }) + // Cache of initialized streams + const streamsRef = yield* Ref.make(HashMap.empty()) - const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.append({ data: opts.data }) - }) - - const subscribe = (opts: { - name: StreamName - offset?: Offset | undefined - }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) - }) - - const getFrom = (opts: { - name: StreamName - offset?: Offset | undefined - limit?: number - }): Effect.Effect, InvalidOffsetError | StorageError> => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - const offset = opts.offset ?? OFFSET_START - return yield* stream.getFrom( - opts.limit !== undefined ? { offset, limit: opts.limit } : { offset } - ) - }) - - const list = (): Effect.Effect, StorageError> => storage.list() - - const deleteStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - yield* storage.delete(opts) - yield* Ref.update(streamsRef, HashMap.remove(opts.name)) - }) - - return { - getStream, - append, - subscribe, - getFrom, - list, - delete: deleteStream - } satisfies StreamManager - }), - dependencies: [Storage.Default] - } -) { - /** Create a StreamManagerService layer with custom storage layer */ - static readonly Live: Layer.Layer = Layer.effect( - StreamManagerService, + const getStream = (opts: { name: StreamName }): Effect.Effect => Effect.gen(function*() { - const storage = yield* Storage - - const streamsRef = yield* Ref.make(HashMap.empty()) - - const getStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - const streams = yield* Ref.get(streamsRef) - const existing = HashMap.get(streams, opts.name) - - if (existing._tag === "Some") { - return existing.value - } - - const stream = yield* makeDurableStream({ name: opts.name }).pipe( - Effect.provideService(Storage, storage) - ) - yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) - return stream - }) + const streams = yield* Ref.get(streamsRef) + const existing = HashMap.get(streams, opts.name) - const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.append({ data: opts.data }) - }) + if (existing._tag === "Some") { + return existing.value + } - const subscribe = (opts: { - name: StreamName - offset?: Offset | undefined - }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) - }) - - const getFrom = (opts: { - name: StreamName - offset?: Offset | undefined - limit?: number - }): Effect.Effect, InvalidOffsetError | StorageError> => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - const offset = opts.offset ?? OFFSET_START - return yield* stream.getFrom( - opts.limit !== undefined ? { offset, limit: opts.limit } : { offset } - ) - }) - - const list = (): Effect.Effect, StorageError> => storage.list() - - const deleteStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - yield* storage.delete(opts) - yield* Ref.update(streamsRef, HashMap.remove(opts.name)) - }) - - return { - getStream, - append, - subscribe, - getFrom, - list, - delete: deleteStream - } as unknown as StreamManagerService + // Create new stream via factory + const stream = yield* factory.make(opts) + yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) + return stream }) - ) - static readonly InMemory: Layer.Layer = Layer.effect( - StreamManagerService, + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => Effect.gen(function*() { - const storage = yield* Storage - - const streamsRef = yield* Ref.make(HashMap.empty()) - - const getStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - const streams = yield* Ref.get(streamsRef) - const existing = HashMap.get(streams, opts.name) - - if (existing._tag === "Some") { - return existing.value - } + const stream = yield* getStream({ name: opts.name }) + return yield* stream.append({ data: opts.data }) + }) - const stream = yield* makeDurableStream({ name: opts.name }).pipe( - Effect.provideService(Storage, storage) - ) - yield* Ref.update(streamsRef, HashMap.set(opts.name, stream)) - return stream - }) + const subscribe = (opts: { + name: StreamName + offset?: Offset | undefined + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) + }) - const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.append({ data: opts.data }) - }) + const getFrom = (opts: { + name: StreamName + offset?: Offset | undefined + limit?: number + }): Effect.Effect, InvalidOffsetError | StorageError> => + Effect.gen(function*() { + const stream = yield* getStream({ name: opts.name }) + const offset = opts.offset ?? OFFSET_START + return yield* stream.getFrom( + opts.limit !== undefined ? { offset, limit: opts.limit } : { offset } + ) + }) - const subscribe = (opts: { - name: StreamName - offset?: Offset | undefined - }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) - }) + const list = (): Effect.Effect, StorageError> => storage.list() - const getFrom = (opts: { - name: StreamName - offset?: Offset | undefined - limit?: number - }): Effect.Effect, InvalidOffsetError | StorageError> => - Effect.gen(function*() { - const stream = yield* getStream({ name: opts.name }) - const offset = opts.offset ?? OFFSET_START - return yield* stream.getFrom( - opts.limit !== undefined ? { offset, limit: opts.limit } : { offset } - ) - }) + const deleteStream = (opts: { name: StreamName }): Effect.Effect => + Effect.gen(function*() { + yield* storage.delete(opts) + yield* Ref.update(streamsRef, HashMap.remove(opts.name)) + }) - const list = (): Effect.Effect, StorageError> => storage.list() + return { + getStream, + append, + subscribe, + getFrom, + list, + delete: deleteStream + } satisfies StreamManager +}) - const deleteStream = (opts: { name: StreamName }): Effect.Effect => - Effect.gen(function*() { - yield* storage.delete(opts) - yield* Ref.update(streamsRef, HashMap.remove(opts.name)) - }) +/** StreamManager service tag and layer */ +export class StreamManagerService extends Effect.Service()( + "@durable-streams/StreamManager", + { + effect: makeStreamManager, + dependencies: [Storage.Default, DurableStreamFactory.Default] + } +) { + /** Create a StreamManagerService layer - requires Storage and DurableStreamFactory */ + static readonly Live: Layer.Layer = Layer.effect( + StreamManagerService, + makeStreamManager as Effect.Effect + ) - return { - getStream, - append, - subscribe, - getFrom, - list, - delete: deleteStream - } as unknown as StreamManagerService - }) - ).pipe(Layer.provide(Storage.InMemory)) + /** In-memory layer with plain factory (for tests) */ + static readonly InMemory: Layer.Layer = StreamManagerService.Live.pipe( + Layer.provide(PlainFactory), + Layer.provide(Storage.InMemory) + ) } diff --git a/src/durable-streams/with-hooks.test.ts b/src/durable-streams/with-hooks.test.ts new file mode 100644 index 0000000..289f110 --- /dev/null +++ b/src/durable-streams/with-hooks.test.ts @@ -0,0 +1,250 @@ +/** + * Tests for withHooks() wrapper + */ +import { describe, expect, it } from "@effect/vitest" +import { Effect, Ref } from "effect" +import { HookError, type StreamHooks } from "./hooks.ts" +import { Storage } from "./storage.ts" +import { makeDurableStream } from "./stream.ts" +import type { StreamName } from "./types.ts" +import { withHooks } from "./with-hooks.ts" + +describe("withHooks", () => { + const testStreamName = "test-stream" as StreamName + + const makeTestStream = Effect.gen(function*() { + return yield* makeDurableStream({ name: testStreamName }) + }).pipe(Effect.provide(Storage.InMemory)) + + describe("beforeAppend hooks", () => { + it.effect("runs before hooks before append", () => + Effect.gen(function*() { + const callOrder = yield* Ref.make>([]) + + const hooks: StreamHooks = { + beforeAppend: [ + { + id: "track-before", + run: () => Ref.update(callOrder, (arr) => [...arr, "before"]) + } + ], + afterAppend: [ + { + id: "track-after", + run: () => Ref.update(callOrder, (arr) => [...arr, "after"]) + } + ] + } + + const base = yield* makeTestStream + const wrapped = withHooks(base, hooks) + + yield* wrapped.append({ data: { test: true } }) + + const order = yield* Ref.get(callOrder) + expect(order).toEqual(["before", "after"]) + })) + + it.effect("vetoes append when before hook fails", () => + Effect.gen(function*() { + const appendCalled = yield* Ref.make(false) + + const hooks: StreamHooks = { + beforeAppend: [ + { + id: "veto-hook", + run: () => + Effect.fail( + new HookError({ + hookId: "veto-hook", + message: "Vetoed!" + }) + ) + } + ] + } + + const base = yield* makeTestStream + // Track if base append is called + const trackedBase = { + ...base, + append: (opts: { data: unknown }) => + Effect.gen(function*() { + yield* Ref.set(appendCalled, true) + return yield* base.append(opts) + }) + } + const wrapped = withHooks(trackedBase, hooks) + + const result = yield* wrapped.append({ data: {} }).pipe(Effect.either) + + expect(result._tag).toBe("Left") + if (result._tag === "Left") { + expect(result.left._tag).toBe("HookError") + } + + // Base append should NOT have been called + expect(yield* Ref.get(appendCalled)).toBe(false) + })) + + it.effect("runs multiple before hooks in order", () => + Effect.gen(function*() { + const callOrder = yield* Ref.make>([]) + + const hooks: StreamHooks = { + beforeAppend: [ + { + id: "first", + run: () => Ref.update(callOrder, (arr) => [...arr, "first"]) + }, + { + id: "second", + run: () => Ref.update(callOrder, (arr) => [...arr, "second"]) + }, + { + id: "third", + run: () => Ref.update(callOrder, (arr) => [...arr, "third"]) + } + ] + } + + const base = yield* makeTestStream + const wrapped = withHooks(base, hooks) + + yield* wrapped.append({ data: {} }) + + const order = yield* Ref.get(callOrder) + expect(order).toEqual(["first", "second", "third"]) + })) + + it.effect("stops at first failing before hook", () => + Effect.gen(function*() { + const callOrder = yield* Ref.make>([]) + + const hooks: StreamHooks = { + beforeAppend: [ + { + id: "first", + run: () => Ref.update(callOrder, (arr) => [...arr, "first"]) + }, + { + id: "fail", + run: () => + Ref.update(callOrder, (arr) => [...arr, "fail"]).pipe( + Effect.flatMap(() => Effect.fail(new HookError({ hookId: "fail", message: "stop" }))) + ) + }, + { + id: "never-reached", + run: () => Ref.update(callOrder, (arr) => [...arr, "never"]) + } + ] + } + + const base = yield* makeTestStream + const wrapped = withHooks(base, hooks) + + yield* wrapped.append({ data: {} }).pipe(Effect.ignore) + + const order = yield* Ref.get(callOrder) + expect(order).toEqual(["first", "fail"]) + })) + }) + + describe("afterAppend hooks", () => { + it.effect("receives the created event", () => + Effect.gen(function*() { + const receivedEvent = yield* Ref.make(null) + + const hooks: StreamHooks = { + afterAppend: [ + { + id: "capture-event", + run: ({ event }) => Ref.set(receivedEvent, event) + } + ] + } + + const base = yield* makeTestStream + const wrapped = withHooks(base, hooks) + + const event = yield* wrapped.append({ data: { msg: "hello" } }) + + const captured = yield* Ref.get(receivedEvent) + expect(captured).toEqual(event) + })) + + it.effect("logs error but succeeds when after hook fails", () => + Effect.gen(function*() { + // After hooks have type Effect, so we just make it succeed + // The actual error handling is tested by checking the result succeeds + const hookCalled = yield* Ref.make(false) + + const hooks: StreamHooks = { + afterAppend: [ + { + id: "after-hook", + run: () => Ref.set(hookCalled, true) + } + ] + } + + const base = yield* makeTestStream + const wrapped = withHooks(base, hooks) + + // Should succeed + const result = yield* wrapped.append({ data: {} }).pipe(Effect.either) + expect(result._tag).toBe("Right") + expect(yield* Ref.get(hookCalled)).toBe(true) + })) + + it.effect("runs multiple after hooks", () => + Effect.gen(function*() { + const callCount = yield* Ref.make(0) + + const hooks: StreamHooks = { + afterAppend: [ + { id: "a", run: () => Ref.update(callCount, (n) => n + 1) }, + { id: "b", run: () => Ref.update(callCount, (n) => n + 1) }, + { id: "c", run: () => Ref.update(callCount, (n) => n + 1) } + ] + } + + const base = yield* makeTestStream + const wrapped = withHooks(base, hooks) + + yield* wrapped.append({ data: {} }) + + expect(yield* Ref.get(callCount)).toBe(3) + })) + }) + + describe("passthrough methods", () => { + it.scoped("subscribe passes through unchanged", () => + Effect.gen(function*() { + const base = yield* makeTestStream + const wrapped = withHooks(base, {}) + + // Append via base + yield* base.append({ data: { from: "base" } }) + + // Subscribe via wrapped - should get the stream + const stream = yield* wrapped.subscribe() + + // Just verify we can call subscribe and it returns a stream + expect(stream).toBeDefined() + })) + + it.effect("count passes through unchanged", () => + Effect.gen(function*() { + const base = yield* makeTestStream + const wrapped = withHooks(base, {}) + + yield* base.append({ data: {} }) + yield* base.append({ data: {} }) + + const count = yield* wrapped.count + expect(count).toBe(2) + })) + }) +}) diff --git a/src/durable-streams/with-hooks.ts b/src/durable-streams/with-hooks.ts new file mode 100644 index 0000000..927d257 --- /dev/null +++ b/src/durable-streams/with-hooks.ts @@ -0,0 +1,82 @@ +/** + * withHooks - Layer 2 wrapper for DurableStream + * + * Pure function that wraps a DurableStream with before/after hooks. + * No services, no layers - just composition. + */ +import type { Scope, Stream } from "effect" +import { Effect } from "effect" +import type { HookError, StreamHooks } from "./hooks.ts" +import type { InvalidOffsetError, Offset, StorageError, StreamEvent, StreamName } from "./types.ts" + +/** DurableStream with hooks has HookError in its append error channel */ +export interface HookedDurableStream { + readonly name: StreamName + append(opts: { data: unknown }): Effect.Effect + subscribe: (opts?: { offset?: Offset }) => Effect.Effect< + Stream.Stream, + InvalidOffsetError | StorageError, + Scope.Scope + > + getFrom: (opts: { + offset: Offset + limit?: number + }) => Effect.Effect, InvalidOffsetError | StorageError> + readonly count: Effect.Effect +} + +/** + * Wrap a DurableStream with before/after hooks. + * + * - Before hooks run sequentially in array order; any failure vetoes append + * - After hooks run sequentially after successful append; errors logged, don't fail + * - subscribe/getFrom/count pass through unchanged + * + * Note: Returns HookedDurableStream which has HookError in the append error channel. + */ +export const withHooks = ( + base: { + readonly name: StreamName + append(opts: { data: unknown }): Effect.Effect + subscribe: HookedDurableStream["subscribe"] + getFrom: HookedDurableStream["getFrom"] + readonly count: Effect.Effect + }, + hooks: StreamHooks +): HookedDurableStream => { + const { afterAppend = [], beforeAppend = [] } = hooks + + const append = (opts: { data: unknown }): Effect.Effect< + StreamEvent, + StorageError | HookError + > => + Effect.gen(function*() { + // Run before hooks - failure vetoes append + for (const hook of beforeAppend) { + yield* hook.run({ name: base.name, data: opts.data }).pipe( + Effect.annotateLogs({ hookId: hook.id }) + ) + } + + // Delegate to base + const event = yield* base.append(opts) + + // Run after hooks - errors logged but don't fail + for (const hook of afterAppend) { + yield* hook.run({ name: base.name, event }).pipe( + Effect.annotateLogs({ hookId: hook.id }), + Effect.catchAll((e) => Effect.logWarning("After-hook failed", { hookId: hook.id, error: e })) + ) + } + + return event + }) + + return { + name: base.name, + append, + subscribe: base.subscribe, + getFrom: base.getFrom, + count: base.count + } +} From beff1947372d516442c617144669541361b5140c Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:52:40 +0000 Subject: [PATCH 13/15] boop --- src/architecture-sketch.md | 1855 +++++++----------------------------- src/diagram-scratch.md | 80 ++ 2 files changed, 416 insertions(+), 1519 deletions(-) create mode 100644 src/diagram-scratch.md diff --git a/src/architecture-sketch.md b/src/architecture-sketch.md index f6f8c78..2b6605d 100644 --- a/src/architecture-sketch.md +++ b/src/architecture-sketch.md @@ -1,1685 +1,502 @@ -# Iterate: Agent Harness Bridge Architecture - -## Overview - -Iterate orchestrates AI coding agents through **durable event streams**. Rather than building a monolithic agent, we create **bridges** that adapt existing agent harnesses (OpenCode, Claude Code, Pi, as well as our own Iterate agent in the future) to our event-driven architecture. +# Iterate Agent Architecture + +## What We're Building + +Iterate orchestrates AI coding agents (OpenCode, Claude Code, Pi, and our own Iterate Agent) through **durable event streams**. Each agent has its own stream. Harness implementations are **event subscribers** that react to stream events and call harness APIs. + +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Agent Stream (per agent) β”‚ + β”‚ [external] [actions] [harness events] β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ OpenCode β”‚ β”‚ Claude β”‚ β”‚ Pi β”‚ β”‚ Iterate β”‚ β”‚ +β”‚ Sub- β”‚ β”‚ Code β”‚ β”‚ Sub- β”‚ β”‚ Agent β”‚ β”‚ +β”‚ scriber β”‚ β”‚ Sub- β”‚ β”‚ scriber β”‚ β”‚ Sub- β”‚ β”‚ +β”‚ (global) β”‚ β”‚ scriber β”‚ β”‚ (global) β”‚ β”‚ scriber β”‚ β”‚ +β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ OpenCode β”‚ β”‚ Claude β”‚ β”‚ Pi β”‚ β”‚ mini- β”‚ β”‚ +β”‚ Server β”‚ β”‚ SDK/CLI β”‚ β”‚ Process β”‚ β”‚ agent β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β”‚ β”‚ + β–Ό β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ + β”‚ Renderers β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ (Web UI, β”‚ (bidirectional) + β”‚ Slack bot, β”‚ + β”‚ CLI) β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**Key properties:** +- One durable stream per agent instance +- Harness subscribers are global (one per harness type, subscribes to all agent streams) +- Action events are first-class stream events (Redux-like pattern) +- Harness events wrapped verbatim in `payload` (no lossy normalization) +- Renderers are bidirectional (can read and append events) +- Native TUIs remain fully functional (SSH in, use OpenCode/Claude/Pi directly) ## Terminology | Term | Definition | |------|------------| -| **Agent Harness** | A standalone agent runtime (OpenCode, Claude Code, Pi, our own Iterate agent). Handles LLM calls, tool execution, conversation state. | -| **Harness Bridge** | Adapter that connects an agent harness to Iterate's event stream. Reacts to Iterate control events via hooks, wraps harness events with namespace prefix. | -| **Durable Stream** | Append-only event log with offset-based resumption. The source of truth for all agent interactions. | - -## Design Principles - -### 1. Our Stream is the Superset - -Each agent harness (OpenCode, Claude, Pi) has its own internal event format. Our durable stream is a **wrapped superset**: -- Harness events are wrapped verbatim into `iterate:agent:harness:` events -- Control events (`iterate:agent:control:*`) are our own, reacted to by bridge hooks -- External events use source-based naming (`slack:webhook`, `github:pr_comment`) - not wrapped with iterate prefix -- We store everything on our end, even if OpenCode also stores in SQLite - -### 2. No Unified Abstraction - -We do NOT try to normalize harness events into a common format. The UI must know how to render: -- Claude tool calls in Claude's format -- OpenCode tool calls in OpenCode's format -- Pi events in Pi's format - -This avoids lossy translation and keeps full fidelity. - -### 3. Native TUI Must Work - -Users can SSH into the sandbox and use the native terminal UI (OpenCode, ClaudeCode, Pi). When they do: -- From the agent's perspective, everything is normal -- The bridge captures these events and they appear on our SSE stream -- Our web UI and any other consumers see the same events - -### 4. Iterate Tools Available to All Harnesses - -We have server-side tools that all agent harnesses should access. The bridge is responsible for injecting/registering these tools with each harness using harness-specific mechanisms. - -## Event Design - -This section covers event naming, envelope structure, and versioning. These decisions are hard to change later, so we want to get them right. +| **Agent Harness** | Standalone agent runtime (OpenCode, Claude Code, Pi, Iterate Agent). Handles LLM calls, tool execution, conversation state. | +| **Harness Subscriber** | Event subscriber that reacts to stream events and calls harness APIs. One global subscriber per harness type. | +| **Durable Stream** | Append-only event log with offset-based resumption. One stream per agent instance. | +| **Action Event** | Event requesting a side effect (e.g., `prompt-requested`). Past-tense "-requested" suffix. | +| **Renderer** | Bidirectional stream client (Web UI, Slack bot). Can read events and append new ones. | -### Industry Standards & Philosophies +## Event Architecture -#### CloudEvents (CNCF Standard) +### Event Types -[CloudEvents](https://cloudevents.io/) is the CNCF-graduated standard for describing events. Key concepts: - -- **Reverse-DNS type naming**: `com.example.object.deleted` -- **Standard envelope fields**: `type`, `source`, `id`, `time`, `data` -- **Lowercase alphanumeric** attribute names only -- **`source` + `id`** must be unique per event - -```json -{ - "specversion": "1.0", - "type": "com.example.order.created", - "source": "/orders/service", - "id": "abc-123", - "time": "2024-01-15T12:00:00Z", - "data": { "orderId": "12345" } -} ``` +External events (from outside world): + slack:message-received + github:webhook-received -**Pros:** Interoperability with other systems, well-documented, wide tooling support. -**Cons:** Verbose, reverse-DNS feels enterprisey, may be overkill for internal system. - -#### Domain-Driven Design (Event Sourcing) - -DDD events use **past tense** and **domain language**: - -- `OrderPlaced`, `CustomerRegistered`, `PaymentFailed` -- Pattern: `[Noun][PastTenseVerb]` -- Natural language: "Stock was depleted" not "StockDepleted" - -**Pros:** Business-readable, self-documenting. -**Cons:** Assumes single domain, doesn't address namespacing across systems. - -#### Kafka Topic Naming - -[Kafka conventions](https://www.confluent.io/learn/kafka-topic-naming-convention/) use hierarchical dot or underscore: - -- `domain.entity.action` or `domain_entity_action` -- Components: domain, classification, description, version -- Example: `sales.orders.created.v1` - -**Pros:** Well-suited for broker filtering, explicit versioning. -**Cons:** Topic-centric (not event-type-centric), verbose. - -### Separator Trade-offs +Action events (requesting side effects): + iterate:agent:harness:opencode:action:session-create-requested + iterate:agent:harness:opencode:action:prompt-requested + iterate:agent:harness:claude:action:prompt-requested + iterate:agent:harness:pi:action:prompt-requested + iterate:agent:harness:iterate:action:prompt-requested -| Separator | Example | Pros | Cons | -|-----------|---------|------|------| -| **Colon (:)** | `iterate:agent:opencode` | URL-safe, common in URIs, clear hierarchy | Less common than dots | -| **Dot (.)** | `iterate.agent.opencode` | Most common (Java packages, domains), natural hierarchy | Conflicts with JSON path syntax (`event.type` ambiguous) | -| **Slash (/)** | `iterate/agent/opencode` | Path-like, very clear hierarchy | Needs URL encoding in some contexts | -| **Underscore (_)** | `iterate_agent_opencode` | No special meaning, flat | Hard to distinguish hierarchy levels | +Wrapped harness events (verbatim payload): + iterate:agent:harness:opencode:event-received + iterate:agent:harness:claude:event-received + iterate:agent:harness:pi:event-received + iterate:agent:harness:iterate:event-received -**Recommendation:** **Colon (`:`)** - Clear hierarchy without JSON path conflicts. Widely used in URIs and message protocols. Easy to filter: `type.startsWith("iterate:")`. - -### Namespace Structure Options - -Given the requirement for 50-200+ event sources, and future routing/broker needs: - -#### Option A: `iterate:` prefix for internal, flat for external - -```typescript -// Internal events - all prefixed with iterate: -iterate:agent:harness:opencode -iterate:agent:harness:claude -iterate:agent:control:create -iterate:agent:system:ready - -// External events - no prefix (raw from outside world) -slack_webhook -github_webhook -``` - -**Pros:** Clear internal/external boundary. -**Cons:** Mixed conventions (colons vs underscores). - -#### Option B: Full hierarchy with `iterate:` everywhere - -```typescript -// All iterate-owned events -iterate:agent:harness:opencode -iterate:agent:control:create -iterate:external:slack:webhook -iterate:external:github:webhook -iterate:trigger:cron -``` - -**Pros:** Consistent structure, easy to filter by any level. -**Cons:** Verbose for external events that we don't "own". - -#### Option C: Source-based namespacing (Recommended) - -```typescript -// External events - named by source -slack:webhook // Slack sent this -github:webhook // GitHub sent this -github:pr_comment // GitHub PR comment - -// Iterate-owned events - prefixed with iterate: -iterate:agent:harness:opencode // Wrapped OpenCode events -iterate:agent:control:create // Create agent command -iterate:trigger:cron // Scheduled trigger +System events: + iterate:agent:system:ready + iterate:agent:system:stopped + iterate:agent:system:error-occurred ``` -**Pros:** Clear provenance - external events are from external sources, not "owned" by iterate. -**Cons:** Two namespacing conventions (but they're semantically different). +**Naming conventions:** +- External events: source-based (`slack:*`, `github:*`), past-tense verbs +- Action events: `-requested` suffix (things we want to happen) +- Wrapped harness: generic `event-received` type, native format in `payload` +- System events: past-tense verbs (`-occurred`, `-stopped`) +- Colon separator: URL-safe, clear hierarchy -#### Option D: Flat with type categories - -```typescript -// Flat but categorized -agent_harness_opencode -agent_control_create -webhook_slack -webhook_github -trigger_cron -``` - -**Pros:** Simple, no hierarchy parsing. -**Cons:** Harder to do prefix filtering, long names. - -### Recommendation: Option C (Source-based) - -External events should be named by their source, not wrapped with `iterate:`: - -``` -// External events (from outside world) -slack:webhook - Slack webhook payload -slack:command - Slack slash command -github:webhook - GitHub webhook payload -github:pr_comment - GitHub PR comment - -// Iterate-owned events -iterate:agent:harness:opencode - Wrapped OpenCode events -iterate:agent:harness:claude - Wrapped Claude events -iterate:agent:harness:pi - Wrapped Pi events -iterate:agent:control:create - Create agent command -iterate:agent:control:destroy - Destroy agent command -iterate:agent:control:message - Send message command -iterate:agent:system:ready - Agent ready notification -iterate:agent:system:error - Agent error notification -iterate:trigger:cron - Cron trigger -``` - -**Why:** -- External events are genuinely from external sources - `slack:webhook` is clearer than `iterate:external:slack:webhook` -- `iterate:` prefix reserved for things we actually own/create -- Still easy to filter: `type.startsWith("iterate:")` for our events, or by source prefix - -### Envelope Structure - -Durable Streams has no opinion on event structure - it just stores bytes with offsets. We define our own envelope. - -**Goals:** -1. Single flat envelope (no nested wrappers) -2. Preserve verbatim external payloads -3. Clear separation: our fields vs their payload -4. Both timestamps: when we received it, when they created it - -### Flat vs Nested: Type-Specific Fields - -A key design question: should type-specific fields like `agentId` be at the root level or nested? - -#### Option: Flat (type-specific fields at root) - -```typescript -{ - type: "iterate:agent:harness:opencode", - version: 1, - timestamp: 1705312800000, - agentId: "agent-123", // At root - payload: { ... } -} -``` - -#### Option: Nested (type-specific fields inside data) - -```typescript -{ - type: "iterate:agent:harness:opencode", - version: 1, - timestamp: 1705312800000, - data: { - agentId: "agent-123", // Nested - payload: { ... } - } -} -``` - -#### Comparison - -| Aspect | Flat | Nested | -|--------|------|--------| -| **Access pattern** | `event.agentId` | `event.data.agentId` | -| **Querying/filtering** | Easy - top-level fields | Requires path traversal | -| **Schema clarity** | Mixed concerns at one level | Clear envelope vs content separation | -| **Naming collisions** | Possible (if payload has `type` field) | Impossible - separate namespaces | -| **Database indexing** | Most DBs index top-level better | May need special JSON path indexing | -| **Nesting risk** | None | Can lead to `data.payload.data.x` | - -#### Modern Systems Using Flat Structures - -- **CloudEvents**: Metadata flat at root, only `data` is nested -- **Stripe webhooks**: Flat structure with `type`, `id`, `created`, then type-specific fields -- **AWS EventBridge**: `source`, `detail-type`, `time` at root, `detail` for payload -- **Segment events**: `type`, `timestamp`, `userId` at root - -**Common pattern**: Envelope fields + routing fields at root, opaque payload in one nested field. - -#### Recommendation: Flat with Reserved Envelope Fields - -Use flat structure but reserve a small set of envelope field names: - -```typescript -// Reserved envelope fields (always present or optional on all events) -type: string // Required -version: number // Required -timestamp: number // Required -payload?: unknown // For external data (mutually exclusive with data) -data?: Record<...> // For our structured data (mutually exclusive with payload) -metadata?: Record<...> // Optional debug info - -// Type-specific fields at root (varies by event type) -agentId?: string // For agent events -channel?: string // For slack events -// etc. -``` - -**Why flat:** -- Simpler access patterns -- Better database indexing -- Avoids deep nesting (`event.data.agentId` vs `event.agentId`) -- Matches how most modern event systems work - -**Collision avoidance:** -- We control our field names -- `payload` is opaque - we never reach into it for routing -- If external payload has a `type` field, it's inside `payload.type`, not `event.type` - -#### Envelope Schema +### Event Envelope ```typescript interface IterateEvent { - // === Durable Streams fields (protocol-level) === + // Protocol fields offset: Offset // Assigned by durable-streams - // === Iterate envelope fields (always present) === - type: string // e.g. "iterate:agent:harness:opencode" - version: number // Envelope schema version (start at 1) - timestamp: number // Our timestamp (when we received/created) + // Envelope fields (always present) + type: string // e.g. "iterate:agent:harness:opencode:event-received" + version: number // Schema version (start at 1) + timestamp: number // When Iterate received/created - // === Type-specific fields at root === + // Type-specific fields at root (varies by event) agentId?: string // For agent events - // ... other type-specific fields as needed - - // === Payload (mutually exclusive) === - payload?: unknown // Verbatim external data (harness event, webhook body) - data?: Record // Our structured data (for control/system events) - - // === Optional === - metadata?: Record // Debug info, correlation IDs, etc. -} -``` + harness?: string // "opencode" | "claude" | "pi" | "iterate" -#### Two Timestamps + // Payload (mutually exclusive) + payload?: unknown // Verbatim external data (harness/webhook) + data?: Record // Iterate's structured data -When wrapping external events (harness or webhook): - -```typescript -{ - type: "iterate:agent:harness:opencode", - version: 1, - timestamp: 1705312800000, // When we received it - agentId: "agent-123", - payload: { - // Verbatim OpenCode event - may have its own timestamp - type: "Session.Message.Created", - timestamp: 1705312799500, // When OpenCode created it - sessionId: "sess-456", - message: { ... } - } + // Optional + metadata?: Record // Debug info, correlation IDs } ``` -- `timestamp`: When Iterate received/processed the event -- `payload.timestamp` (or similar): When the source created it - -Both are useful - `timestamp` for ordering in our stream, `payload.timestamp` for understanding actual event time. - -### Versioning Strategies - -Since this is an append-only log, we can't update old events. Versioning options: - -#### Strategy 1: Version in Type Name - -```typescript -iterate:agent:harness:opencode:v1 -iterate:agent:harness:opencode:v2 -``` - -**Pros:** Explicit, easy to filter by version. -**Cons:** Type proliferation, harder to query "all opencode events". - -#### Strategy 2: Version Field in Envelope - -```typescript -{ - type: "iterate:agent:harness:opencode", - version: 1, // Envelope version - ... -} -``` - -**Pros:** Type stays stable, consumers check `version` field. -**Cons:** Need to handle multiple versions at runtime. - -#### Strategy 3: Semantic Versioning with Breaking Change Policy - -- Minor changes (adding optional fields): No version bump -- Breaking changes: Bump `version`, document migration - -#### Strategy 4: Never Version, Only Evolve - -- Only add optional fields -- Never remove or change field semantics -- If breaking change needed, create new event type entirely - -### Recommendation: Strategy 2 + 3 - -1. **`version` field in envelope** - start at `1` -2. **Minor changes**: Add optional fields, don't bump -3. **Breaking changes**: Bump `version`, emit both versions during transition -4. **Consumer rule**: Ignore unknown fields, fail on unknown `version` > supported - -```typescript -// Consumer code -function handleEvent(event: IterateEvent) { - if (event.version > SUPPORTED_VERSION) { - log.warn(`Unknown event version ${event.version}, skipping`) - return - } - // Process based on type and version -} -``` - -### Event Type Summary - -| Category | Type | Description | -|----------|------|-------------| -| **Agent harness** | `iterate:agent:harness:opencode` | Wrapped OpenCode event | -| | `iterate:agent:harness:claude` | Wrapped Claude event | -| | `iterate:agent:harness:pi` | Wrapped Pi event | -| | `iterate:agent:harness:iterate` | Our own agent (future) | -| **Agent control** | `iterate:agent:control:create` | Create agent command | -| | `iterate:agent:control:destroy` | Destroy agent command | -| | `iterate:agent:control:message` | Send message to agent | -| **Agent system** | `iterate:agent:system:ready` | Agent ready | -| | `iterate:agent:system:stopped` | Agent stopped | -| | `iterate:agent:system:error` | Agent error | -| **External** | `slack:webhook` | Slack webhook | -| | `slack:command` | Slack slash command | -| | `github:webhook` | GitHub webhook | -| | `github:pr_comment` | GitHub PR comment | -| **Triggers** | `iterate:trigger:cron` | Scheduled trigger | -| | `iterate:trigger:manual` | Manual trigger | +Flat structure at root for easy filtering/indexing. External payloads preserved byte-for-byte in `payload` field. ### Example Events ```typescript -// Agent harness event (verbatim payload) -{ - type: "iterate:agent:harness:opencode", - version: 1, - timestamp: 1705312800000, - agentId: "agent-123", - payload: { - type: "Session.Message.Created", - timestamp: 1705312799500, - sessionId: "sess-456", - message: { role: "assistant", parts: [...] } - } -} - -// Agent control event (our data) -{ - type: "iterate:agent:control:create", - version: 1, - timestamp: 1705312700000, - data: { - agentId: "agent-123", - harness: "opencode", - config: { model: "claude-3", workingDirectory: "/workspace" } - } -} - -// External webhook (verbatim payload, source-based naming) +// 1. External event arrives { - type: "slack:webhook", + type: "slack:message-received", version: 1, timestamp: 1705312900000, payload: { type: "message", channel: "C123", user: "U456", - text: "Hello agent", - ts: "1705312899.000100" // Slack's timestamp format + text: "Hello agent" } } -``` - -## Agent Lifecycle - -Agents don't magically exist. The stream starts empty, and agents are created via control events. -### Creating an Agent - -```typescript +// 2. Harness subscriber transforms to action event { - type: "iterate:agent:control:create", + type: "iterate:agent:harness:opencode:action:prompt-requested", version: 1, - timestamp: 1705312700000, + timestamp: 1705312900100, + agentId: "agent-123", + harness: "opencode", data: { - agentId: "agent-123", - harness: "opencode" | "claude" | "pi" | "iterate", - config: { - model?: string, - workingDirectory: string, - tools?: string[], // Iterate tools to inject - } + content: "Hello agent", + source: "slack:message-received" } } -``` - -### Agent Ready -```typescript +// 3. Harness subscriber executes action, wraps response { - type: "iterate:agent:system:ready", + type: "iterate:agent:harness:opencode:event-received", version: 1, - timestamp: 1705312750000, + timestamp: 1705312800000, agentId: "agent-123", - data: { - harness: "opencode", - pid?: number, - endpoint?: string, // For server-based harnesses - } -} -``` - -### Example: Full Agent Creation Flow - -```typescript -// 1. Control event requests agent creation -{ type: "iterate:agent:control:create", version: 1, timestamp: ..., data: { agentId: "agent-123", harness: "opencode", config: {...} } } - -// 2. System event confirms agent is ready -{ type: "iterate:agent:system:ready", version: 1, timestamp: ..., agentId: "agent-123", data: { harness: "opencode", pid: 12345 } } - -// 3. Control event sends a message -{ type: "iterate:agent:control:message", version: 1, timestamp: ..., agentId: "agent-123", data: { content: "Hello agent" } } - -// 4. Agent harness emits events (verbatim OpenCode payload) -{ type: "iterate:agent:harness:opencode", version: 1, timestamp: ..., agentId: "agent-123", payload: { type: "Session.Message.Created", ... } } -{ type: "iterate:agent:harness:opencode", version: 1, timestamp: ..., agentId: "agent-123", payload: { type: "Session.Message.Updated", ... } } - -// 5. Eventually agent completes or is destroyed -{ type: "iterate:agent:control:destroy", version: 1, timestamp: ..., agentId: "agent-123", data: {} } -{ type: "iterate:agent:system:stopped", version: 1, timestamp: ..., agentId: "agent-123", data: { reason: "requested" } } -``` - -## Harness Bridge Architecture - -The bridge is **hooks-based**, not translation-based. It reacts to control events by calling harness-specific imperative APIs, and wraps harness output into our event format. - -```typescript -interface HarnessBridge { - readonly harness: "opencode" | "claude" | "pi" - - /** React to agent control events */ - hooks: { - /** Called on iterate:agent:control:create for this harness type */ - onCreateAgent(agentId: string, config: AgentConfig): Effect - - /** Called on iterate:agent:control:message targeting this agent */ - onMessage(agentId: string, content: string): Effect - - /** Called on iterate:agent:control:destroy */ - onDestroyAgent(agentId: string): Effect + harness: "opencode", + payload: { + type: "Session.Message.Created", // OpenCode's native format + timestamp: 1705312799500, // OpenCode's timestamp + sessionId: "sess-456", + message: { role: "assistant", parts: [...] } } - - /** Subscribe to harness's native event stream for an agent */ - subscribeToHarness(agentId: string): Stream // unknown = verbatim harness event - - /** Wrap harness event into our format */ - wrapEvent(agentId: string, nativeEvent: unknown): IterateEvent - // Returns: { type: "iterate:agent:harness:opencode", version: 1, timestamp: ..., agentId, payload: nativeEvent } - - /** Inject Iterate tools into the harness */ - injectTools(agentId: string, tools: IterateTool[]): Effect -} -``` - -### Event Flow - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Durable Stream β”‚ -β”‚ [iterate:agent:control:*] [iterate:agent:harness:*] [slack:*] [github:*] β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ OpenCode β”‚ β”‚ Claude Code β”‚ β”‚ Pi Bridge β”‚ - β”‚ Bridge β”‚ β”‚ Bridge β”‚ β”‚ β”‚ - β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - β”‚ hooks.on*() β”‚ β”‚ hooks.on*() β”‚ β”‚ hooks.on*() β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ OpenCode β”‚ β”‚ Claude Code β”‚ β”‚ Pi Process β”‚ - β”‚ Server β”‚ β”‚ CLI/SDK β”‚ β”‚ (RPC) β”‚ - β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - β”‚ (SQLite, β”‚ β”‚ (stream-json) β”‚ β”‚ (stdin/stdout)β”‚ - β”‚ Event Bus) β”‚ β”‚ β”‚ β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -### Control Event β†’ Imperative API - -When `iterate:agent:control:message` arrives: - -1. Bridge hook `onMessage` fires for the target agent -2. Hook calls harness-specific imperative API: - - **OpenCode**: POST to event bus / SDK call - - **Claude**: Spawn CLI with `-p` flag or SDK call - - **Pi**: Write JSON to stdin `{ command: "prompt", message: "..." }` -3. Harness processes and emits its own events -4. Bridge captures and wraps into `iterate:agent:harness:` event - -### Harness Event β†’ Wrapped in Stream - -When harness emits an event: - -1. Bridge `subscribeToHarness()` receives native event (verbatim) -2. Bridge `wrapEvent()` wraps it with envelope fields -3. Appended to durable stream -4. SSE consumers (web UI, etc.) receive it - -```typescript -// OpenCode native event (verbatim, we don't touch it) -{ - type: "Session.Message.Created", - timestamp: 1705312799500, - sessionId: "sess-456", - message: { role: "assistant", parts: [...] } } -// Wrapped in our stream +// Action that failed (at-most-once, fail fast) { - type: "iterate:agent:harness:opencode", + type: "iterate:agent:system:error-occurred", version: 1, - timestamp: 1705312800000, // Our timestamp + timestamp: 1705312900200, agentId: "agent-123", - payload: { - type: "Session.Message.Created", - timestamp: 1705312799500, // Their timestamp (preserved) - sessionId: "sess-456", - message: { role: "assistant", parts: [...] } + data: { + action: "iterate:agent:harness:opencode:action:prompt-requested", + error: "OpenCode server unreachable", + code: "HARNESS_UNAVAILABLE" } } ``` -The `payload` is byte-for-byte identical to what the harness emitted. UI must understand OpenCode's native event format to render it. - -## Process Management - -### Open Question: Who Manages Agent Processes? +### Versioning Strategy -Different harnesses have different process models: +- `version` field in envelope, starting at 1 +- Adding optional fields: no version bump +- Breaking changes: bump version, emit both during transition +- Consumer rule: ignore unknown fields, warn on unsupported version -| Harness | Process Model | Management | -|---------|---------------|------------| -| **OpenCode** | Long-running server, multiple sessions | One server per sandbox, sessions multiplexed | -| **Claude Code** | CLI invocation or SDK | Per-request CLI or persistent SDK connection | -| **Pi** | Per-agent RPC process | One process per agent, stdin/stdout | - -**Questions to resolve:** -1. Does Iterate spawn/supervise these processes, or assume they're running? -2. For OpenCode server model: one server shared by multiple "agents" (sessions)? -3. For Pi: one process per agent, managed by Iterate? -4. What happens on crash/restart? - -### Sketch: Process Supervisor +## Agent Lifecycle ```typescript -interface ProcessSupervisor { - /** Ensure harness process is running */ - ensureRunning(harness: HarnessType, agentId: string): Effect - - /** Stop a specific agent's process */ - stop(agentId: string): Effect - - /** Handle process crashes */ - onCrash(agentId: string, reason: unknown): Effect -} -``` - -### OpenCode: Server Model - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ OpenCode Server β”‚ -β”‚ (one per sandbox) β”‚ -β”‚ β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚Session 1β”‚ β”‚Session 2β”‚ β”‚Session 3β”‚ β”‚ -β”‚ β”‚(Agent A)β”‚ β”‚(Agent B)β”‚ β”‚(Agent C)β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β”‚ β”‚ -β”‚ SQLite DB Event Bus β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β–Ό - OpenCode Bridge - (subscribes to event bus, - reacts to iterate:agent:control:* by - creating sessions / sending messages, - emits iterate:agent:harness:opencode events) -``` - -### Pi: Per-Agent Process Model - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Pi Process β”‚ β”‚ Pi Process β”‚ β”‚ Pi Process β”‚ -β”‚ (Agent A) β”‚ β”‚ (Agent B) β”‚ β”‚ (Agent C) β”‚ -β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ -β”‚ stdin ◀────────│ β”‚ stdin ◀────────│ β”‚ stdin ◀────────│ -β”‚ stdout ────────▢ β”‚ stdout ────────▢ β”‚ stdout ────────▢ -β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ -β”‚ JSONL session β”‚ β”‚ JSONL session β”‚ β”‚ JSONL session β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β–Ό - Pi Bridge - (manages process lifecycle, - emits iterate:agent:harness:pi events) -``` - -## Tool Injection +// 1. Request agent creation (action event) +{ type: "iterate:agent:harness:opencode:action:session-create-requested", agentId: "agent-123", harness: "opencode", data: { config: {...} } } -Iterate has server-side tools that should be available to all agents. Each harness has different mechanisms: +// 2. Agent ready (system event) +{ type: "iterate:agent:system:ready", agentId: "agent-123", data: { harness: "opencode", pid: 12345 } } -| Harness | Tool Registration | -|---------|-------------------| -| **OpenCode** | Agent config in markdown, or runtime injection via SDK | -| **Claude Code** | `--allowedTools` flag, MCP servers, custom tool definitions | -| **Pi** | TypeBox tool schemas passed to agent runtime | +// 3. External event arrives +{ type: "slack:message-received", payload: { channel: "C123", text: "Hello" } } -The bridge must: -1. Convert Iterate tool definitions to harness-specific format -2. Register/inject at agent creation time -3. Handle tool calls that route back to Iterate server +// 4. Subscriber transforms to action +{ type: "iterate:agent:harness:opencode:action:prompt-requested", agentId: "agent-123", harness: "opencode", data: { content: "Hello" } } -## Consumer Clients +// 5. Harness events flow (verbatim wrapped) +{ type: "iterate:agent:harness:opencode:event-received", agentId: "agent-123", harness: "opencode", payload: { type: "Session.Message.Created", ... } } +{ type: "iterate:agent:harness:opencode:event-received", agentId: "agent-123", harness: "opencode", payload: { type: "Session.Message.Updated", ... } } -Multiple clients subscribe to the durable stream via SSE: - -``` - Durable Stream - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Web UI β”‚ β”‚ Terminal β”‚ β”‚ Slack β”‚ - β”‚ β”‚ β”‚ UI β”‚ β”‚ Bot β”‚ - β”‚ Renders: β”‚ β”‚ β”‚ β”‚ β”‚ - β”‚ iterate: β”‚ β”‚ Renders: β”‚ β”‚ Posts β”‚ - β”‚ agent: β”‚ β”‚ all β”‚ β”‚ replies β”‚ - β”‚ harness: β”‚ β”‚ events β”‚ β”‚ β”‚ - β”‚ * β”‚ β”‚ β”‚ β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +// 6. Destroy +{ type: "iterate:agent:harness:opencode:action:session-destroy-requested", agentId: "agent-123" } +{ type: "iterate:agent:system:stopped", agentId: "agent-123", data: { reason: "requested" } } ``` -Each client must understand the native event formats inside `payload` - no translation layer. +## Harness Subscriber Architecture -### Example: Web UI Event Handler +Harness implementations are **event subscribers**. One global subscriber per harness type subscribes to all agent streams and: +1. Transforms external events β†’ action events +2. Executes action events β†’ calls harness API +3. Wraps harness output β†’ appends to stream ```typescript -function handleStreamEvent(event: IterateEvent) { - // Check version compatibility - if (event.version > SUPPORTED_VERSION) { - console.warn(`Unknown event version ${event.version}`) - return - } - - // Route by type prefix - if (event.type.startsWith("iterate:agent:harness:")) { - const harness = event.type.split(":")[3] // opencode, claude, pi - switch (harness) { - case "opencode": - renderOpenCodeEvent(event.payload) - break - case "claude": - renderClaudeEvent(event.payload) - break - case "pi": - renderPiEvent(event.payload) - break - } - } else if (event.type.startsWith("iterate:agent:system:")) { - renderSystemEvent(event) - } else if (event.type.startsWith("slack:") || event.type.startsWith("github:")) { - // External webhook - show notification or route to handler - renderExternalEvent(event) - } +interface HarnessSubscriber { + readonly harness: "opencode" | "claude" | "pi" | "iterate" + + // Called for every event on every agent stream using this harness + handleEvent( + agentId: string, + event: IterateEvent + ): Effect, HarnessError> } -``` - -## Agent Harness Comparison - -| Feature | OpenCode | Claude Code | Pi | -|---------|----------|-------------|-----| -| **Process Model** | Server (multi-session) | CLI or SDK | Per-agent process | -| **Event Format** | BusEvent + MessageV2 | stream-json NDJSON | JSON RPC events | -| **State Storage** | SQLite | Per-session ID | JSONL tree | -| **Tool Definition** | Markdown agent config | MCP / allowedTools | TypeBox schemas | -| **Programmatic Control** | Event Bus / SDK | `-p` flag / SDK | stdin JSON commands | -| **Native TUI** | Yes (Bubble Tea) | Yes | Yes | - -### Message Format Comparison - -**User Input:** -| Harness | Format | -|---------|--------| -| **OpenCode** | `{ role: "user", parts: [{ type: "text", text }] }` | -| **Claude** | CLI arg, stdin, or SDK Message object | -| **Pi** | `{ command: "prompt", message: string }` | - -**Assistant Output:** -| Harness | Format | -|---------|--------| -| **OpenCode** | `{ role: "assistant", parts: [...], tokens, cost, finish }` | -| **Claude** | NDJSON with `type: "assistant"` | -| **Pi** | `message_update` / `turn_end` events | - -**Tool Calls:** -| Harness | Format | -|---------|--------| -| **OpenCode** | `{ type: "toolCall", name, args }` in parts array | -| **Claude** | `{ type: "tool_use", name, input }` | -| **Pi** | `tool_execution_start` event with toolName, args | - -## Concrete Bridge Implementations - -Three dominant patterns for programmatically controlling AI coding agents: **HTTP/SSE servers** (OpenCode), **CLI subprocess with streaming JSON** (Claude Code), and **stdin/stdout RPC** (Pi). All preserve TUI compatibility through session file persistence. -### OpenCode Bridge - -OpenCode implements a **client-server architecture** where the TUI communicates with an internal HTTP server. - -#### Starting the Server - -```bash -opencode serve --port 4096 -``` - -Exposes an OpenAPI 3.1-compliant REST API at `http://localhost:4096/doc`. - -#### Key Endpoints - -| Endpoint | Method | Purpose | -|----------|--------|---------| -| `/session` | GET/POST | List or create sessions | -| `/session/:id/prompt` | POST | Send message, wait for response | -| `/session/:id/prompt_async` | POST | Send message, stream via SSE | -| `/session/:id/abort` | POST | Cancel running operation | -| `/global/health` | GET | Health check | -| `/event` | GET | SSE event stream | - -#### TypeScript Bridge - -```typescript -import { createOpencodeClient } from "@opencode-ai/sdk" - -class OpenCodeBridge { - private client: ReturnType - - constructor(baseUrl = "http://localhost:4096") { - this.client = createOpencodeClient({ baseUrl }) - } - - async *subscribeToEvents() { - const events = await this.client.event.subscribe() - for await (const event of events.stream) { - yield { - type: `iterate:agent:harness:opencode`, - version: 1, - timestamp: Date.now(), - agentId: event.sessionId, - payload: event - } +// Example: OpenCode subscriber +const openCodeSubscriber: HarnessSubscriber = { + harness: "opencode", + + handleEvent: (agentId, event) => Effect.gen(function*() { + switch (event.type) { + // Transform external events to actions + case "slack:message-received": + return [{ + type: "iterate:agent:harness:opencode:action:prompt-requested", + agentId, + harness: "opencode", + data: { content: event.payload.text } + }] + + // Execute actions by calling harness API + case "iterate:agent:harness:opencode:action:prompt-requested": + yield* openCodeClient.sendPrompt(agentId, event.data.content) + return [] // Harness events come via SSE subscription + + case "iterate:agent:harness:opencode:action:session-create-requested": + yield* openCodeClient.createSession(agentId, event.data.config) + return [] + + default: + return [] // Ignore events we don't handle } - } - - async sendMessage(sessionId: string, text: string) { - return this.client.session.prompt({ - path: { id: sessionId }, - body: { parts: [{ type: "text", text }] } - }) - } - - async createSession() { - return this.client.session.create({}) - } - - async abort(sessionId: string) { - return this.client.session.abort({ path: { id: sessionId } }) - } + }) } ``` -#### TUI Compatibility +### Event Flow (Slack β†’ OpenCode Example) -Multiple TUI clients can connect to same server simultaneously: - -```bash -opencode attach --hostname localhost --port 4096 ``` - -#### Session Storage - -Sessions persist as JSON files in `~/.local/share/opencode/project//storage/session/`. - -### Claude Code Bridge - -Claude Code uses a **CLI-per-invocation** model. The TypeScript SDK (`@anthropic-ai/claude-agent-sdk`) spawns the CLI binary as a subprocess internally, so SDK and CLI sessions are identical and fully interoperable. - -#### Architecture - -``` -Your Daemon +slack:message-received arrives on agent-123 stream + β”‚ + β–Ό +OpenCode subscriber sees event, returns action: + iterate:agent:harness:opencode:action:prompt-requested β”‚ - β”œβ”€β”€β”€ SDK query() ───► Spawns: claude --output-format stream-json ... - β”‚ β”‚ - β”‚ β”œβ”€β–Ί Writes to ~/.claude/projects//.jsonl - β”‚ β”‚ - β”‚ └─► Triggers global hooks (if configured) + β–Ό +Action appended to stream β”‚ - └─── Receives events via: - β€’ SDK async generator (from spawned process) - β€’ Global hooks (from any Claude process, including user CLI) + β–Ό +OpenCode subscriber sees action, calls: + POST /session/:id/prompt_async + β”‚ + β–Ό +OpenCode SSE subscription receives native events + β”‚ + β–Ό +Wrapped as iterate:agent:harness:opencode:event-received + β”‚ + β–Ό +Appended to stream β†’ Renderers see it ``` -#### TypeScript Bridge - -```typescript -import { query } from '@anthropic-ai/claude-agent-sdk' - -class ClaudeCodeBridge extends EventEmitter { - private sessionId: string | null = null - private abortController: AbortController | null = null - - async startSession(prompt: string, options: { - resume?: string - cwd?: string - allowedTools?: string[] - } = {}): Promise { - this.abortController = new AbortController() - - const response = query({ - prompt, - options: { - model: 'claude-sonnet-4-5', - cwd: options.cwd || process.cwd(), - resume: options.resume, - allowedTools: options.allowedTools || ['Read', 'Write', 'Edit', 'Bash'], - permissionMode: 'acceptEdits', - abortController: this.abortController, - settingSources: ['project'], - } - }) - - for await (const message of response) { - if (message.session_id) this.sessionId = message.session_id - - this.emit('event', { - type: `iterate:agent:harness:claude`, - version: 1, - timestamp: Date.now(), - agentId: this.sessionId, - payload: message - }) - } +### Offset Tracking (Replay Safety) - return this.sessionId! - } +Each subscriber tracks its last-processed offset per stream in a simple file: - interrupt(): void { - this.abortController?.abort() - } -} ``` - -#### Performance Note - -SDK spawns a fresh CLI process for each `query()` call: **~12 second overhead** per query. Mitigations: -- Batch work into fewer, longer sessions -- Use raw Anthropic Messages API for simple queries -- Accept startup cost for agentic workflows - -#### Global Hooks for CLI Sessions - -The SDK captures events for sessions *you* initiate. For events from user CLI sessions (SSH TUI), configure global hooks in `~/.claude/settings.json`: - -```json +~/.iterate/subscriber-offsets/opencode.json { - "hooks": { - "SessionStart": [{ - "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts SessionStart" }] - }], - "PreToolUse": [{ - "matcher": "*", - "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts PreToolUse" }] - }], - "PostToolUse": [{ - "matcher": "*", - "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts PostToolUse" }] - }], - "Stop": [{ - "hooks": [{ "type": "command", "command": "/path/to/hook-forwarder.ts Stop" }] - }] - } + "agent-123": 42, + "agent-456": 17 } ``` -**Critical limitation**: Hooks cannot capture streaming text chunks - only discrete lifecycle events. - -#### Hook Events Reference - -| Hook | When it Fires | Can Block? | -|------|---------------|------------| -| `SessionStart` | Session begins | No | -| `SessionEnd` | Session ends | No | -| `UserPromptSubmit` | User sends message | No | -| `PreToolUse` | Before tool executes | Yes (exit 2) | -| `PostToolUse` | After tool completes | No | -| `Stop` | Agent turn complete | No | +On daemon restart, subscriber resumes from stored offset, skipping already-handled events. At-most-once semantics: if action execution fails, emit error event, don't retry. -#### Concurrent Access Warning +## Harness Implementations -**No file locking on session files.** Running multiple processes on same session causes corruption. Must: -1. Track which sessions are "active" -2. Reject concurrent access attempts -3. Allow resumption only after previous query completes +### OpenCode Subscriber -If user SSHes in and runs `claude --resume` on active SDK session: **corruption will occur**. - -#### TUI Compatibility +HTTP/SSE server architecture. One server per sandbox, multiple sessions multiplexed. ```bash -claude --resume # Resume specific session -claude --continue # Resume most recent in current directory +opencode serve --port 4096 ``` -#### Session Storage +| Endpoint | Purpose | +|----------|---------| +| `/session` | List/create sessions | +| `/session/:id/prompt` | Send message (sync) | +| `/session/:id/prompt_async` | Send message (SSE stream) | +| `/session/:id/abort` | Cancel operation | +| `/event` | SSE event stream | -Sessions: `~/.claude/projects//.jsonl` -Path encoding: `/home/user/myproject` β†’ `-home-user-myproject` +**Action handlers:** +- `session-create-requested` β†’ `POST /session` +- `prompt-requested` β†’ `POST /session/:id/prompt_async` +- `abort-requested` β†’ `POST /session/:id/abort` -### Pi Bridge +**Event wrapping:** Subscribe to `/event` SSE, wrap each native event as `iterate:agent:harness:opencode:event-received`. -Pi uses the simplest model: a **long-running subprocess** with JSON commands on stdin and events on stdout. +TUI attach: `opencode attach --hostname localhost --port 4096` -#### TypeScript Bridge - -```typescript -import { spawn, ChildProcess } from 'child_process' -import { createInterface } from 'readline' +### Claude Code Subscriber -class PiBridge extends EventEmitter { - private process: ChildProcess - private sessionPath: string | null - - constructor(options: { sessionPath?: string } = {}) { - super() - - const args = ['--mode', 'rpc'] - if (options.sessionPath) { - args.push('--session', options.sessionPath) - this.sessionPath = options.sessionPath - } +CLI-per-invocation via SDK. SDK spawns CLI binary internally. - this.process = spawn('pi', args, { - stdio: ['pipe', 'pipe', 'inherit'] - }) - - const rl = createInterface({ input: this.process.stdout! }) - rl.on('line', (line) => { - const event = JSON.parse(line) - this.emit('event', { - type: `iterate:agent:harness:pi`, - version: 1, - timestamp: Date.now(), - agentId: this.sessionPath, - payload: event - }) - }) - } - - async prompt(message: string): Promise { - this.process.stdin!.write(JSON.stringify({ type: 'prompt', message }) + '\n') - } +```typescript +import { query } from '@anthropic-ai/claude-agent-sdk' - async abort(): Promise { - this.process.stdin!.write(JSON.stringify({ type: 'abort' }) + '\n') +const response = query({ + prompt: "Hello", + options: { + model: 'claude-sonnet-4-5', + cwd: process.cwd(), + resume: sessionId, + allowedTools: ['Read', 'Write', 'Edit', 'Bash'], + permissionMode: 'acceptEdits', + abortController, } +}) - kill(): void { - this.process.kill() - } +for await (const message of response) { + // Wrap as iterate:agent:harness:claude:event-received } ``` -#### RPC Commands Reference +**Startup overhead**: ~12 seconds per query (SDK spawns fresh CLI process). -| Command | Purpose | Response | -|---------|---------|----------| -| `prompt` | Send user message | Streams events, ends with `turn_end` | -| `abort` | Cancel current operation | Confirmation | -| `get_state` | Query session state | State object | -| `get_messages` | Get message history | Message array | -| `branch` | Fork from message index | New branch created | -| `switch_session` | Load different session | Confirmation | +**Global hooks** for CLI sessions (user SSH): Configure in `~/.claude/settings.json` to forward lifecycle events. -#### TUI Compatibility +**Concurrency warning**: No file locking on sessions. Concurrent SDK + CLI access causes corruption. -```bash -pi --continue # Resume most recent session -pi --resume # Interactive session picker -pi --session /path/to.jsonl # Load specific session file -``` +TUI resume: `claude --resume ` -#### Session Storage +### Pi Subscriber -Sessions: `~/.pi/agent/sessions/` as JSONL with tree structure (`id`/`parentId` for branching). +Long-running subprocess with JSON RPC on stdin/stdout. -### Bridge Comparison Summary - -| Aspect | OpenCode | Claude Code | Pi | -|--------|----------|-------------|-----| -| **Architecture** | HTTP/SSE server | CLI per query + SDK | stdin/stdout RPC | -| **Event source** | SSE endpoint | SDK async generator + hooks | stdout NDJSON | -| **Session ID format** | API-assigned | UUID from `session_id` | File path | -| **TUI attach** | `opencode attach` | `claude --resume` | `pi --session` | -| **Concurrent safe** | Yes (server) | No (file-based) | No (file-based) | -| **Interruption** | `abort()` API | `AbortController` | `abort` command | -| **Startup overhead** | Server must be running | ~12s per query | Process spawn | -| **CLI event capture** | Built-in (same server) | Global hooks needed | Same process | +```typescript +const process = spawn('pi', ['--mode', 'rpc', '--session', sessionPath]) -### TUI Compatibility Guarantee +// Send command +process.stdin.write(JSON.stringify({ type: 'prompt', message: "Hello" }) + '\n') -All three harnesses preserve TUI compatibility through their native session storage: +// Receive events on stdout (NDJSON), wrap as iterate:agent:harness:pi:event-received +``` -- **OpenCode**: Multiple TUIs can attach to same server simultaneously -- **Claude Code**: Sessions in `~/.claude/projects/` accessible via `claude --resume` -- **Pi**: Sessions in `~/.pi/agent/sessions/` accessible via `pi --session` +| Command | Purpose | +|---------|---------| +| `prompt` | Send user message | +| `abort` | Cancel operation | +| `get_state` | Query session state | +| `branch` | Fork from message index | -Bridge design principles: -1. Let harness manage its own session storage (don't intercept or modify) -2. Capture session identifiers and expose them for TUI resumption -3. Prevent concurrent programmatic access to same session -4. Allow users to "take over" in TUI by terminating programmatic access first +TUI resume: `pi --session /path/to/session.jsonl` -When something goes wrong in web UI, users can SSH in, run native TUI command with session ID, and continue exactly where things left off. +### Iterate Agent Subscriber -## Open Questions +Our own agent implementation. Details TBDβ€”will use mini-agent infrastructure with Effect-based LLM adapter. -Comprehensive list of implementation decisions with multiple solutions. Each question includes recommended approach. +### Comparison ---- +| Aspect | OpenCode | Claude Code | Pi | Iterate Agent | +|--------|----------|-------------|-----|---------------| +| Architecture | HTTP/SSE server | CLI per query | stdin/stdout RPC | TBD | +| Concurrent safe | Yes (server) | No (file-based) | No (file-based) | TBD | +| Startup overhead | Server must run | ~12s per query | Process spawn | TBD | +| CLI capture | Built-in (same server) | Global hooks needed | Same process | N/A | -### Session Management & Concurrency - -#### 1. How to prevent file corruption for Claude Code/Pi sessions without built-in locking? - -Claude Code SDK/CLI uses file-based sessions (JSONL) with NO built-in file locking. Concurrent access from SDK and TUI (`claude --resume`) causes corruption. - -**Solution A: Advisory Lock File (`.lock` suffix)** ⭐ Recommended -- Bridge creates `.lock` file before read/write -- Use `flock` (POSIX) with timeout -- Release lock after operation completes -- Requires Claude CLI to also implement locking (may need upstream patch) - -**Solution B: Single Owner with Event Queue** -- Bridge maintains single `SessionManager` process per session -- All access routed through manager via IPC queue -- Manager serializes all reads/writes -- More complex architecture but guaranteed serialization - -**Solution C: Copy-on-Write Sessions** -- SDK operates on `.sdk` copy, TUI on `.tui` copy -- Bridge periodically merges into canonical file -- No blocking, but complex merge logic and storage overhead (3x) - -**Solution D: Event-Sourced Writes** -- SDK only reads session files, never writes -- All mutations go through durable stream as control events -- Bridge applies events atomically -- Major architectural change - -#### 2. Session locking strategy - lease-based vs ownership transfer? - -User SSHes in and runs `claude --resume` on session actively used by SDK. Who "owns" the session? - -**Solution A: Lease-Based with Heartbeat** -- Session lock acquired as timed lease (30s) -- Lock holder sends heartbeat every 10s to renew -- Missed heartbeat = lock expires -- Automatic recovery from crashed processes - -**Solution B: Explicit Handoff Protocol** ⭐ Recommended -- SDK holds session, user types `claude --resume` -- TUI sends `iterate:agent:control:request_ownership` event -- Bridge gracefully pauses SDK, releases lock -- Emits `iterate:agent:system:ownership_transferred` -- Clean handoff, no race conditions, audit trail - -**Solution C: Read-Only Observer Mode** -- TUI launches in "observer" mode if session locked by SDK -- User sees live updates but cannot send messages -- User types `/takeover` command to trigger handoff - -#### 3. How to amortize 12-second Claude Code SDK startup overhead? - -**Solution A: Long-Lived SDK Daemon per Session** -- Bridge spawns SDK process once, keeps alive for session lifetime -- Queries sent via stdin/IPC -- Near-instant response time after first query -- Memory overhead (one daemon per active session) - -**Solution B: Connection Pool with Session Affinity** -- Maintain pool of N warm SDK processes -- Incoming query assigned to idle process from pool -- LRU eviction when pool full -- Bounded resource usage - -**Solution C: Lazy Initialization with Prompt Queue** -- User sends message β†’ immediately returns "Initializing agent..." -- SDK starts in background (12s) -- Subsequent messages queued during startup -- Poor UX for first message - -**Solution D: Pre-Warmed SDK on Agent Create** ⭐ Recommended -- `iterate:agent:control:create` triggers immediate SDK spawn -- SDK starts loading context before any user messages -- By the time user types first message, SDK likely ready -- Best UX, explicit lifecycle management - ---- +## Tool Injection -### Event Streaming & Format +Iterate tools registered in each harness using harness-specific mechanisms: -#### 4. Event deduplication between SDK and hook events? +| Harness | Registration | +|---------|-------------| +| OpenCode | Agent config or runtime SDK | +| Claude Code | MCP servers, `--allowedTools` | +| Pi | TypeBox schemas | +| Iterate Agent | Effect Schema directly | -When using both SDK async generators AND global hooks for same harness, events may appear twice. Both get wrapped into `iterate:agent:harness:claude` and appended. +**Approach**: Define tools using Effect Schema, convert to harness-specific format. Long-term: MCP as canonical format where supported. -**Solution A: Hash-Based Deduplication** ⭐ Recommended -- Generate deterministic IDs from content hash -- Deduplicate at append time via before-append hook -- TTL cache of recent event IDs (last 1000 events) -- Works for any event source, idempotent appends +## TUI Compatibility -**Solution B: Event Source Tagging** -- Tag events with source (`sdk` vs `hook`) -- Suppress hook events if SDK is active for session -- Requires per-harness coordination logic +Users can SSH into sandbox and use native TUI. When they do: +- From agent's perspective, everything is normal +- Subscriber captures events β†’ appear on SSE stream +- Web UI sees same events -**Solution C: Offset-Based Windowed Deduplication** -- Store recent event content (last N offsets) -- Check for duplicates within window -- Bounded memory, transparent to sources +**Handoff protocol**: When TUI attaches, emit `iterate:agent:system:client-attached`. Web UI shows takeover banner, optionally enters read-only mode. On TUI exit, emit `iterate:agent:system:client-detached`, web UI resumes. -#### 5. Timestamp reconciliation - our time vs harness time vs LLM time? +## Renderers -Three different timestamp sources with different semantics. +Renderers are **bidirectional** stream clients. They can: +1. Subscribe to events via SSE (read) +2. Append new events to the stream (write) -**Solution A: Multi-Timestamp Envelope** ⭐ Recommended -- Store all three timestamps in event -- Use Iterate time (`timestamp`) for ordering -- Expose harness/LLM times in `metadata` for debugging -- Can compute latencies: `harnessToIterate`, `llmToHarness` +Examples: Web UI, Slack bot, CLI. ```typescript -interface IterateEvent { - timestamp: number // PRIMARY - for ordering - payload: { - timestamp?: number // Harness emit time +// Reading events +function handleStreamEvent(event: IterateEvent) { + if (event.type === "iterate:agent:harness:opencode:event-received") { + return renderOpenCodeEvent(event.payload) } - metadata?: { - llmTimestamp?: number - latencies?: { harnessToIterate: number } + if (event.type === "iterate:agent:harness:claude:event-received") { + return renderClaudeEvent(event.payload) } -} -``` - -**Solution B: Iterate Time Only** -- Only `timestamp` at envelope level -- Harness/LLM times stay in payload as opaque data -- Simpler but less debuggable - -#### 6. Capturing streaming text from Claude Code when user is in native TUI? - -SDK sessions yield streaming chunks, but CLI hooks can't capture streaming text. When user uses TUI directly, streaming invisible to hooks. - -**Solution A: PTY Wrapper Interception** -- Wrap CLI in pseudo-terminal -- Parse ANSI output to reconstruct streaming chunks -- No harness changes needed -- Brittle, high overhead, false positives risk - -**Solution B: Patch Claude Code for Streaming Hooks** -- Fork or patch Claude Code with `onStreamingChunk` hook -- Clean first-class streaming support -- Requires maintaining fork or upstreaming - -**Solution C: Dual-Path Hybrid** ⭐ Recommended -- SDK for Iterate-initiated sessions (full streaming) -- Lifecycle-only capture for user CLI sessions -- Document limitation: "Streaming text visible in web UI for SDK sessions; CLI sessions show lifecycle events only" -- Pragmatic, no brittle parsing - ---- - -### Process Lifecycle & Supervision - -#### 7. OpenCode server ownership - who starts and manages the server process? - -**Solution A: User-Managed Server (Assume Running)** -- Iterate connects to existing server via config -- User controls lifecycle (systemd, Docker, manual) -- Simple for Iterate, poor developer experience - -**Solution B: Iterate Spawns and Supervises Server** -- Process supervisor launches OpenCode server on first agent creation -- Keeps alive, restarts on crashes -- Better UX but Iterate must manage external binary - -**Solution C: Hybrid Auto-Daemon Pattern** ⭐ Recommended -- Attempt to connect to existing server -- Spawn local daemon if none found -- User can override with `OPENCODE_SERVER_URL` env var -- Matches durable-streams pattern, best of both worlds - -#### 8. Pi process supervision strategy? - -**Solution A: Basic Spawn with Manual Restart** -- Spawn Pi process when agent created, kill on destroy -- If Pi crashes, agent unavailable until user recreates -- Simplest but poor reliability - -**Solution B: Supervised with Automatic Restart** ⭐ Recommended -- Monitor Pi process health via heartbeat/stderr -- Automatically restart on crash, emit system events -- Implement backoff/retry, circuit breaker (max restarts) -- Resilient to transient failures - -**Solution C: Supervised with Manual Restart** -- Detect and notify on crashes -- Require explicit restart command -- Good for debugging but higher user burden - -#### 9. Health monitoring mechanism for agent processes? - -**Solution A: Passive - Monitor Process Exit Events Only** -- Listen to `process.on('exit')` events -- Zero overhead but doesn't detect hangs/zombies - -**Solution B: Active Heartbeat via RPC Ping** -- Periodic ping to RPC processes -- Expect pong within timeout -- Detects hangs, measures latency -- Overhead from periodic messages - -**Solution C: Hybrid** ⭐ Recommended -- Passive exit monitoring (immediate crash detection) -- Plus 60s health checks (hang detection) -- Emit structured events with crash reason - ---- - -### TUI Compatibility & Handoff - -#### 10. Handoff protocol when user transitions from web UI to native TUI? - -**Solution A: Passive Handoff (No Coordination)** -- No explicit handoff, both clients see all events via SSE -- Race conditions, confusing UX - -**Solution B: Explicit Handoff with Lock File** -- `.iterate/active-client` file indicates which client has control -- Web UI checks before sending, warns if TUI active -- Stale lock file risk if TUI crashes - -**Solution C: Graceful Takeover with Warning** ⭐ Recommended -- TUI launches β†’ emit `iterate:agent:system:client-attached` event -- Web UI shows takeover banner, remains functional (read-only or warnings) -- Explicit signal without hard locks - -```typescript -{ - type: "iterate:agent:system:client-attached", - agentId: "agent-123", - data: { - clientType: "tui", - clientId: "opencode-tty", - pid: 12345 + if (event.type === "iterate:agent:harness:pi:event-received") { + return renderPiEvent(event.payload) + } + if (event.type === "iterate:agent:harness:iterate:event-received") { + return renderIterateEvent(event.payload) } + // Show raw event for unknown types + return renderRawEvent(event) } -``` - -#### 11. Should handoff be graceful (coordinate) or forced (take over immediately)? - -**Solution A: Forced Takeover** -- TUI just works, no handoff protocol -- Race conditions, confusing UX when both sending -**Solution B: Graceful Handoff with Pause** ⭐ Recommended -- When TUI attaches, web UI enters "paused" state -- Shows banner, disables input until TUI detaches -- Clear state machine: web-active β†’ tui-active β†’ transitioning - -**Solution C: Collaborative Mode with Input Tagging** -- Both clients remain active -- All inputs tagged with `clientId` -- UI shows who sent what -- Good for pair debugging - -#### 12. How does programmatic control resume after user exits TUI? - -**Solution A: Automatic Reactivation** ⭐ Recommended -- TUI process exits β†’ bridge emits `client-detached` event -- Web UI automatically resumes -- Seamless, no manual intervention - -**Solution B: Explicit Resume Command** -- User must run `iterate resume ` or click "Resume Control" -- More explicit but extra step - -**Solution C: Session Timeout with Auto-Resume** -- If no TUI activity for N minutes, auto-resume web -- Handles crashes gracefully but timeout tuning tricky - ---- - -### Tool Injection & Routing - -#### 13. Tool definition format conversion across harnesses? - -Each harness has different tool registration formats. - -**Solution A: Schema-First with Format Adapters** ⭐ Recommended for MVP -- Define tools using `@effect/ai/Tool` (Effect Schema) -- Adapt to each format via converter functions -- Single source of truth, type-safe - -```typescript -interface ToolFormatAdapter { - toOpenCode(tool: IterateTool): OpenCodeToolDef - toClaudeCode(tool: IterateTool): ClaudeToolDef - toPi(tool: IterateTool): TypeBoxSchema - toMcp(tool: IterateTool): McpToolDef +// Writing events (user sends message from Web UI) +async function onUserSubmit(agentId: string, text: string) { + await stream.append({ + type: "iterate:agent:harness:opencode:action:prompt-requested", + agentId, + harness: "opencode", + data: { content: text, source: "web-ui" } + }) } ``` -**Solution B: MCP-Native with Converters** ⭐ Recommended long-term -- Use MCP tool schema as canonical format -- Convert to harness-specific on demand -- MCP is emerging standard (Anthropic-backed) -- Future-proof as harnesses adopt MCP natively - -#### 14. MCP server vs native tool injection? - -**Solution A: MCP Server (Harnesses Connect to Iterate)** -- Run Iterate as MCP server -- Harnesses that support MCP connect via protocol -- Works immediately with Claude Code -- Not all harnesses support MCP yet - -**Solution B: Native Injection via Harness APIs** -- Inject tools using each harness's native mechanism -- Works with all harnesses -- N implementations (one per harness) - -**Solution C: Hybrid (MCP + Native Fallback)** ⭐ Recommended -- Use MCP where supported (Claude Code) -- Fall back to native injection otherwise (Pi, OpenCode) -- Best of both worlds, future-proof - -#### 15. Tool call interception and routing to Iterate server? - -Some tools need Iterate state (event stream access). - -**Solution A: Bridge Subscribes to Harness Events, Intercepts** -- Bridge listens to harness event stream -- Intercepts tool call events, executes on Iterate side, injects result back -- Race conditions if harness executes locally first - -**Solution B: Tool Handlers are HTTP Endpoints** ⭐ Recommended for native injection -- Register tools where handler is HTTP call to Iterate -- Harness executes HTTP call directly -- Simple request/response model - -**Solution C: MCP Protocol Routing** ⭐ Recommended for MCP path -- Using MCP, routing is built-in -- Harness calls tool via MCP protocol β†’ routes to server -- Zero custom routing logic +Initial implementation: Show all events raw in a feed. Later: Rich rendering for user/assistant messages per harness. --- -### Error Recovery & State - -#### 16. State recovery after Iterate daemon restarts? - -**Solution A: PID File Registry with Process Liveness Check** -- Maintain `.iterate/agents/{agentId}.pid` files -- On startup, scan PID files, verify processes alive -- Reconnect to survivors, clean up stale PIDs -- PID reuse risk, race conditions - -**Solution B: State File + Offset-Based Resume** ⭐ Recommended -- Store agent state file: `.iterate/agents/{agentId}.json` -- On restart, replay stream from last offset to reconstruct lifecycle -- Stream is source of truth, handles partial operations naturally - -```json -{ - "agentId": "agent-123", - "harness": "opencode", - "status": "running", - "lastEventOffset": "0000000000000042", - "processHint": { "pid": 12345, "endpoint": "http://localhost:8080" } -} -``` - -**Solution C: Ephemeral Agents + Explicit Restart** -- Daemon restart = all agents considered stopped -- Clients must explicitly re-create -- Simple but user-facing disruption - -#### 17. Handling partial operations after crashes? - -Agent turn interrupted mid-operation (tool started but no result). - -**Solution A: Idempotent Event Replay** -- Every operation has start+end events -- Replay stream, detect incomplete operations -- Emit synthetic completion events with status="cancelled" -- No special cleanup logic needed - -**Solution B: Compensation Events** ⭐ Recommended -- If operation incomplete after timeout, emit compensation event -- `tool_execution_start` with no `tool_execution_end` β†’ emit `tool_execution_cancelled` -- Explicit failure handling in stream -- Use operation-specific timeouts (tool: 5min, LLM request: 2min) - -#### 18. Checkpoint strategy for fast startup? - -For long-running agents with thousands of events, replaying from offset 0 is slow. - -**Solution A: Periodic State Snapshots** ⭐ Recommended -- Every N events (1000), serialize reducedContext to `.iterate/snapshots/{agentId}-{offset}.json` -- On startup, load latest snapshot + replay events since that offset -- Fast startup, standard event sourcing pattern - -```json -{ - "agentId": "agent-123", - "offset": "0000000000001000", - "reducedContext": { "messages": [...], "config": {...} }, - "version": 1 -} -``` +## Decisions Made -**Solution B: Last-Event-Offset Tracking Only** -- Store only last processed offset -- Replay from that offset (no full state) -- Minimal storage but doesn't help if replay slow +| Decision | Choice | +|----------|--------| +| Action event persistence | First-class stream events | +| Subscriber cardinality | One global subscriber per harness type | +| Stream topology | One stream per agent instance | +| External event routing | Hardcoded rules in harness subscriber | +| Action namespace | `iterate:agent:harness:{harness}:action:{verb}-requested` | +| Wrapped event naming | Generic `event-received` type, native in payload | +| Renderer capabilities | Bidirectional (read + write) | +| Replay safety | Offset file per subscriber | +| Action execution | At-most-once (fail fast, emit error event) | --- -### Storage Architecture - -#### 19. Storage duplication - do we need harness storage if we have our stream? - -**Solution A: Full Duplication** ⭐ Recommended (current architecture) -- Both systems maintain independent storage -- Native TUI works unchanged -- Simple bridge implementation -- 2x storage space but avoids complexity - -**Solution B: Single Source of Truth (Iterate Only)** -- Configure harnesses to skip persistence -- Breaking change to native TUI -- Not viable if harnesses don't support memory-only mode - -**Solution C: Harness as Primary, Iterate as Index** -- Iterate maintains lightweight index pointing to harness storage -- No duplication but complex query path - -#### 20. Session ID mapping - Iterate agentId ↔ Harness sessionId? - -**Solution A: Deterministic Derivation** -- `harnessSessionId = iterate-${harness}-${agentId}` -- Stateless, no storage needed -- Naming collision risk - -**Solution B: Mapping Events in Stream** ⭐ Recommended -- Store mapping as `iterate:agent:system:session_mapped` events -- Auditable, flexible, supports one-to-many - -```typescript -{ - type: "iterate:agent:system:session_mapped", - agentId: "agent-123", - data: { harness: "opencode", harnessSessionId: "sess-abc-xyz" } -} -``` - -**Solution C: Separate Mapping Storage** -- Store in `{dataDir}/session-mappings.json` -- Fast lookup without stream replay -- Consistency risk with stream - -#### 21. Query patterns - when to hit our stream vs harness storage? - -| Query | Iterate Stream | Harness Storage | Recommended | -|-------|---------------|-----------------|-------------| -| Get latest agent state | Replay from offset | Query harness API | Harness (cached state) | -| Stream new events (SSE) | PubSub subscription | Poll harness | Iterate (real-time) | -| Search across all agents | Scan all streams | N/A | Iterate (global view) | -| Full conversation history | Read stream file | Query harness DB | Iterate (source of truth) | - -**Recommendation:** Read-through cache pattern - check Iterate in-memory cache first, fall back to stream read. - -#### 22. Storage cleanup - when to GC old sessions? - -**Solution A: Manual Cleanup Only** -- CLI command: `iterate cleanup --older-than 30d` -- User control, no accidental loss -- Storage can grow unchecked - -**Solution B: Automatic Time-Based Expiry** -- Background job deletes sessions older than 30 days -- No intervention needed -- Data loss risk if user wants old sessions +## Open Questions -**Solution C: Lifecycle with Archival** ⭐ Recommended -- Active (30 days) β†’ Archived (compressed) β†’ Deleted (90 days) -- User can restore archived sessions -- Clear lifecycle stages +### Session Concurrency ---- +**How to prevent file corruption for Claude Code/Pi?** -### Future Considerations +Ignore for now -#### 23. Subscription filtering for consumers? +### Event Deduplication -Consumers need to subscribe to event subsets (web UI: agent X only, Slack bot: messages only). +Ignore for now -**Solution A: Stream-per-Agent** ⭐ Recommended for MVP -- Create separate durable stream per agent -- Perfect isolation, no server-side filtering -- Stream proliferation concern (1000 agents = 1000 streams) +### Process Supervision -**Solution B: Single Stream with Server-Side Filtering** -- Add `filter` parameter to SSE subscribe endpoint -- Flexible but CPU overhead +**Who manages harness processes?** -**Solution C: Event Type-Based Topic Routing** -- Subscribe to event type prefixes -- Efficient but can't filter by non-type fields +- OpenCode: Hybrid auto-daemon (connect to existing, spawn if needed) +- Claude: SDK manages per-query +- Pi: Supervised with auto-restart on crash +- Iterate Agent: TBD -#### 24. Multi-agent coordination? +**Health monitoring?** +- Passive exit monitoring + periodic health checks +- Emit structured crash events (`iterate:agent:system:error-occurred`) -Can agents communicate via the shared stream? +### Storage -**Options to explore:** -- Agent A emits event, bridge for Agent B can react via hook -- Explicit `iterate:agent:control:forward` event -- Cross-agent event subscriptions: "notify agent X when `slack:webhook` arrives" -- The naming scheme supports this, routing logic TBD +**Duplication strategy**: We store the full wrapped harness events ourselves, separately to how the harness themselves does it. So technically harness events are stored twice - once by opencode/pi/claude and once wrapped by us. ---- +**Session ID mapping**: Mapping events in stream (`iterate:agent:system:session-mapped`). Need to elaborate on this -### Recommendations Summary - -| Domain | Question | Recommended Solution | -|--------|----------|---------------------| -| **Session Management** | File corruption prevention | Advisory lock files | -| **Session Management** | Session ownership | Explicit handoff protocol | -| **Session Management** | SDK startup overhead | Pre-warmed SDK on create | -| **Event Streaming** | Deduplication | Hash-based with source tagging | -| **Event Streaming** | Timestamp reconciliation | Multi-timestamp envelope | -| **Event Streaming** | CLI streaming capture | Dual-path hybrid (accept limitation) | -| **Process Lifecycle** | OpenCode server | Hybrid auto-daemon | -| **Process Lifecycle** | Pi supervision | Supervised with auto-restart | -| **Process Lifecycle** | Health monitoring | Hybrid exit + health checks | -| **TUI Handoff** | Handoff protocol | Graceful takeover with warning | -| **TUI Handoff** | Graceful vs forced | Graceful handoff with pause | -| **TUI Handoff** | Resume after TUI | Automatic reactivation | -| **Tool Injection** | Format conversion | Schema-first adapters β†’ MCP long-term | -| **Tool Injection** | MCP vs native | Hybrid (MCP + native fallback) | -| **Tool Injection** | Call routing | MCP routing + HTTP endpoints | -| **Error Recovery** | State recovery | State file + offset replay | -| **Error Recovery** | Partial operations | Compensation events | -| **Error Recovery** | Checkpointing | Periodic state snapshots | -| **Storage** | Duplication strategy | Full duplication | -| **Storage** | Session mapping | Mapping events in stream | -| **Storage** | Cleanup | Lifecycle with archival | - -## Next Steps - -1. **Review event naming** - Confirm Option C (source-based namespacing) with team -2. **Prototype OpenCode bridge** - Connect to event bus, emit `iterate:agent:harness:opencode` events -3. **Prototype Pi bridge** - Process management, stdin/stdout RPC, emit `iterate:agent:harness:pi` events -4. **Tool injection POC** - One Iterate tool registered in each harness -5. **SSE consumer** - Simple web UI that renders native event formats -6. **Versioning test** - Verify `version` field handling with schema evolution diff --git a/src/diagram-scratch.md b/src/diagram-scratch.md new file mode 100644 index 0000000..b3036a9 --- /dev/null +++ b/src/diagram-scratch.md @@ -0,0 +1,80 @@ + + +## Attempt 6 - Cleaner, shows harness implementation pattern + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ +β”‚ AGENT STREAM (per agent) β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ + slack:webhook-received iterate:agent:harness: iterate:agent:harness: iterate:agent:harness: + github:webhook-received opencode:action: opencode:action: opencode:event-received + session-create-requested prompt-requested opencode:event-received + opencode:event-received + β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ + from external from harness from Web/TUI from harness + (Slack, GitHub) subscribers (user sends message) runtimes + (tool-call, assistant-msg, + streaming-chunk) + β”‚ + β”‚ subscribe + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ HARNESS SUBSCRIBERS β”‚ + β”‚ β”‚ + β”‚ OpenCode Claude Pi Iterate β”‚ + β”‚ β”‚ + β”‚ A harness subscriber: β”‚ + β”‚ β€’ Sees events on the stream β”‚ + β”‚ β€’ Emits action events (e.g. prompt-requested) to control the harness β”‚ + β”‚ β€’ Calls harness APIs when it sees action events β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ call harness API + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ HARNESS RUNTIMES β”‚ + β”‚ β”‚ + β”‚ OpenCode Server Claude SDK/CLI Pi Process mini-agent β”‚ + β”‚ β”‚ + β”‚ Harness runtimes emit native events. β”‚ + β”‚ Subscribers wrap these as iterate:agent:harness:{name}:event-received β”‚ + β”‚ and append back to stream. β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ wrapped harness events + β”‚ (append to stream) + β”‚ + └──────────────────────────────────────────────────────►┐ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ + β”‚ β”‚ + β”‚ RENDERERS β”‚ + β”‚ β”‚ + β”‚ CLI / TUI Web UI β”‚ + β”‚ β”‚ + β”‚ Renderers subscribe to the stream and display events. β”‚ + β”‚ They can also append action events (e.g. user types a message). β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ control events (user sends message) + β”‚ iterate:agent:harness:opencode:action:prompt-requested + β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”˜ + (append to stream) +``` + +**Key insight**: A harness implementation is just a subscriber that: +1. Reacts to events by emitting action events (control the harness) +2. Calls harness APIs when it sees action events +3. Wraps harness output and appends back to stream + From d2559436259d0b0fc3b35a8f46becc97629b7468 Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Thu, 8 Jan 2026 22:24:30 +0000 Subject: [PATCH 14/15] WIP --- src/architecture-sketch.md | 495 +++++++++++--------- src/diagram-scratch.md | 128 +++-- src/durable-streams/README.md | 17 +- src/durable-streams/cli.ts | 46 +- src/durable-streams/client.ts | 59 ++- src/durable-streams/daemon.ts | 4 +- src/durable-streams/hooks.e2e.test.ts | 20 +- src/durable-streams/hooks.ts | 8 +- src/durable-streams/http-routes.e2e.test.ts | 20 +- src/durable-streams/http-routes.test.ts | 38 +- src/durable-streams/http-routes.ts | 44 +- src/durable-streams/index.ts | 17 +- src/durable-streams/persistence.e2e.test.ts | 24 +- src/durable-streams/storage.ts | 74 ++- src/durable-streams/stream-factory.ts | 44 +- src/durable-streams/stream-manager.ts | 131 +++++- src/durable-streams/stream.test.ts | 30 +- src/durable-streams/stream.ts | 36 +- src/durable-streams/subscribe-all.test.ts | 198 ++++++++ src/durable-streams/types.ts | 15 +- src/durable-streams/with-hooks.test.ts | 4 +- src/durable-streams/with-hooks.ts | 30 +- 22 files changed, 965 insertions(+), 517 deletions(-) create mode 100644 src/durable-streams/subscribe-all.test.ts diff --git a/src/architecture-sketch.md b/src/architecture-sketch.md index 2b6605d..d969830 100644 --- a/src/architecture-sketch.md +++ b/src/architecture-sketch.md @@ -2,45 +2,67 @@ ## What We're Building -Iterate orchestrates AI coding agents (OpenCode, Claude Code, Pi, and our own Iterate Agent) through **durable event streams**. Each agent has its own stream. Harness implementations are **event subscribers** that react to stream events and call harness APIs. +Iterate orchestrates AI coding agents (OpenCode, Claude Code, Pi, and our own Iterate Agent) through **durable event streams**. Each agent has its own stream. Harness adapters react to stream events and call harness APIs. ``` - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Agent Stream (per agent) β”‚ - β”‚ [external] [actions] [harness events] β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό β–Ό β”‚ -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ OpenCode β”‚ β”‚ Claude β”‚ β”‚ Pi β”‚ β”‚ Iterate β”‚ β”‚ -β”‚ Sub- β”‚ β”‚ Code β”‚ β”‚ Sub- β”‚ β”‚ Agent β”‚ β”‚ -β”‚ scriber β”‚ β”‚ Sub- β”‚ β”‚ scriber β”‚ β”‚ Sub- β”‚ β”‚ -β”‚ (global) β”‚ β”‚ scriber β”‚ β”‚ (global) β”‚ β”‚ scriber β”‚ β”‚ -β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ - β”‚ β”‚ β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό β–Ό β”‚ -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ OpenCode β”‚ β”‚ Claude β”‚ β”‚ Pi β”‚ β”‚ mini- β”‚ β”‚ -β”‚ Server β”‚ β”‚ SDK/CLI β”‚ β”‚ Process β”‚ β”‚ agent β”‚ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ - β”‚ β”‚ β”‚ β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ - β”‚ β”‚ - β–Ό β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ - β”‚ Renderers β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ (Web UI, β”‚ (bidirectional) - β”‚ Slack bot, β”‚ - β”‚ CLI) β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +External events + slack:webhook-received + github:webhook-received + ... + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ +β”‚ AGENT STREAM (per agent) β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β–² β”‚ β–² + β”‚ β”‚ β”‚ β”‚ +subscribe β”‚ β”‚ append subscribe β”‚ β”‚ append + β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ iterate:agent:harness:opencode:action:prompt:called | | + β”‚ β”‚ iterate:agent:action:send-user-message:called | | + β”‚ β”‚ iterate:agent:harness:opencode:action:session-create:called β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ iterate:agent:harness:opencode:event-received β”‚ β”‚ + β”‚ β”‚ iterate:agent:harness:pi:event-received β”‚ β”‚ + β”‚ β”‚ iterate:agent:harness:claude:event-received β”‚ β”‚ + β”‚ β”‚ iterate:agent:harness:iterate:event-received β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ + β–Ό β”‚ β–Ό β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ β”‚ β”‚ +β”‚ HARNESS ADAPTERS β”‚ β”‚ RENDERERS β”‚ +β”‚ (our code) β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ Web UI CLI/TUI β”‚ +β”‚ OpenCode Claude Pi Iterate β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ Subscribe to stream, display β”‚ +β”‚ Subscribe to stream, append action events, β”‚ β”‚ events. User types message β†’ β”‚ +β”‚ call harness APIs, wrap harness output β”‚ β”‚ append control event. β”‚ +β”‚ β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β–² + β”‚ β”‚ + call API β”‚ β”‚ subscribe to + β”‚ β”‚ harness events + β”‚ β”‚ + β–Ό β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ +β”‚ HARNESS RUNTIMES β”‚ +β”‚ (their code) β”‚ +β”‚ β”‚ +β”‚ OpenCode Server Claude SDK Pi SDK Iterate Agent β”‚ +β”‚ β”‚ +β”‚ Native agent runtimes. Emit their own events. β”‚ +β”‚ We subscribe to these and wrap them. β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` **Key properties:** -- One durable stream per agent instance -- Harness subscribers are global (one per harness type, subscribes to all agent streams) -- Action events are first-class stream events (Redux-like pattern) +- One durable stream per agent instance / session (identified by `eventStreamId`) +- Action events are first-class stream events (Redux-like pattern) - adapters observe the actions and enact side effects (e.g. sending a prompt to opencode) - Harness events wrapped verbatim in `payload` (no lossy normalization) - Renderers are bidirectional (can read and append events) - Native TUIs remain fully functional (SSH in, use OpenCode/Claude/Pi directly) @@ -50,44 +72,57 @@ Iterate orchestrates AI coding agents (OpenCode, Claude Code, Pi, and our own It | Term | Definition | |------|------------| | **Agent Harness** | Standalone agent runtime (OpenCode, Claude Code, Pi, Iterate Agent). Handles LLM calls, tool execution, conversation state. | -| **Harness Subscriber** | Event subscriber that reacts to stream events and calls harness APIs. One global subscriber per harness type. | +| **Harness Adapter** | Code that subscribes to a stream, appends events, and interacts with a harness SDK/CLI. | | **Durable Stream** | Append-only event log with offset-based resumption. One stream per agent instance. | -| **Action Event** | Event requesting a side effect (e.g., `prompt-requested`). Past-tense "-requested" suffix. | +| **Event Stream ID** | Unique identifier for a stream (e.g., `"stream-abc123"`). Used everywhere instead of "agent ID". | +| **Action Event** | Event requesting a side effect (e.g., `action:prompt:called`). Uses `action:*:called` naming. | | **Renderer** | Bidirectional stream client (Web UI, Slack bot). Can read events and append new ones. | +## The Core Pattern: Durable Streams + +**A durable stream has exactly three operations:** + +1. **Subscribe** β€” get events as they arrive (with offset-based resumption) +2. **Get history** β€” read past events from a given offset +3. **Append** β€” add new events to the stream + +That's it. If you want to support a new agent harness, you need to: +1. Subscribe to the stream +2. Append events to the stream +3. Interact with the harness SDK/CLI + +Nothing else. No special interfaces, no framework abstractions. Just subscribe, append, and call the SDK. + ## Event Architecture -### Event Types +### Event Types (examples) ``` External events (from outside world): - slack:message-received + slack:webhook-received github:webhook-received + ... Action events (requesting side effects): - iterate:agent:harness:opencode:action:session-create-requested - iterate:agent:harness:opencode:action:prompt-requested - iterate:agent:harness:claude:action:prompt-requested - iterate:agent:harness:pi:action:prompt-requested - iterate:agent:harness:iterate:action:prompt-requested + iterate:agent:harness:opencode:action:session-create:called + iterate:agent:harness:opencode:action:prompt:called + iterate:agent:harness:claude:action:prompt:called + iterate:agent:harness:pi:action:prompt:called + iterate:agent:harness:iterate:action:prompt:called + ... Wrapped harness events (verbatim payload): iterate:agent:harness:opencode:event-received iterate:agent:harness:claude:event-received iterate:agent:harness:pi:event-received iterate:agent:harness:iterate:event-received - -System events: - iterate:agent:system:ready - iterate:agent:system:stopped - iterate:agent:system:error-occurred + ... ``` **Naming conventions:** - External events: source-based (`slack:*`, `github:*`), past-tense verbs -- Action events: `-requested` suffix (things we want to happen) +- Action events: `action:*:called` suffix (things we want to happen) - Wrapped harness: generic `event-received` type, native format in `payload` -- System events: past-tense verbs (`-occurred`, `-stopped`) - Colon separator: URL-safe, clear hierarchy ### Event Envelope @@ -95,20 +130,16 @@ System events: ```typescript interface IterateEvent { // Protocol fields - offset: Offset // Assigned by durable-streams + offset: string // Assigned by durable-streams // Envelope fields (always present) type: string // e.g. "iterate:agent:harness:opencode:event-received" version: number // Schema version (start at 1) - timestamp: number // When Iterate received/created - - // Type-specific fields at root (varies by event) - agentId?: string // For agent events - harness?: string // "opencode" | "claude" | "pi" | "iterate" + createdAt: string // ISO 8601 (e.g. "2024-01-15T10:15:00.000Z") + eventStreamId: string // Stream this event belongs to - // Payload (mutually exclusive) - payload?: unknown // Verbatim external data (harness/webhook) - data?: Record // Iterate's structured data + // Type-specific fields + payload?: Record // Optional metadata?: Record // Debug info, correlation IDs @@ -122,9 +153,10 @@ Flat structure at root for easy filtering/indexing. External payloads preserved ```typescript // 1. External event arrives { - type: "slack:message-received", + type: "slack:webhook-received", version: 1, - timestamp: 1705312900000, + createdAt: "2024-01-15T10:15:00.000Z", + eventStreamId: "stream-abc123", payload: { type: "message", channel: "C123", @@ -133,46 +165,30 @@ Flat structure at root for easy filtering/indexing. External payloads preserved } } -// 2. Harness subscriber transforms to action event +// 2. Harness adapter transforms to action event { - type: "iterate:agent:harness:opencode:action:prompt-requested", + type: "iterate:agent:harness:opencode:action:prompt:called", version: 1, - timestamp: 1705312900100, - agentId: "agent-123", - harness: "opencode", - data: { - content: "Hello agent", - source: "slack:message-received" + createdAt: "2024-01-15T10:15:00.100Z", + eventStreamId: "stream-abc123", + payload: { + content: "Hello agent" } } -// 3. Harness subscriber executes action, wraps response +// 3. Harness adapter executes action, wraps response { type: "iterate:agent:harness:opencode:event-received", version: 1, - timestamp: 1705312800000, - agentId: "agent-123", - harness: "opencode", + createdAt: "2024-01-15T10:15:01.500Z", + eventStreamId: "stream-abc123", payload: { type: "Session.Message.Created", // OpenCode's native format - timestamp: 1705312799500, // OpenCode's timestamp + timestamp: 1705312799500, // OpenCode's timestamp (their format) sessionId: "sess-456", message: { role: "assistant", parts: [...] } } } - -// Action that failed (at-most-once, fail fast) -{ - type: "iterate:agent:system:error-occurred", - version: 1, - timestamp: 1705312900200, - agentId: "agent-123", - data: { - action: "iterate:agent:harness:opencode:action:prompt-requested", - error: "OpenCode server unreachable", - code: "HARNESS_UNAVAILABLE" - } -} ``` ### Versioning Strategy @@ -180,95 +196,91 @@ Flat structure at root for easy filtering/indexing. External payloads preserved - `version` field in envelope, starting at 1 - Adding optional fields: no version bump - Breaking changes: bump version, emit both during transition -- Consumer rule: ignore unknown fields, warn on unsupported version ## Agent Lifecycle ```typescript // 1. Request agent creation (action event) -{ type: "iterate:agent:harness:opencode:action:session-create-requested", agentId: "agent-123", harness: "opencode", data: { config: {...} } } +{ type: "iterate:agent:harness:opencode:action:session-create:called", version: 1, createdAt: "2024-01-15T10:15:00.000Z", eventStreamId: "stream-abc123", payload: { config: {...} } } -// 2. Agent ready (system event) -{ type: "iterate:agent:system:ready", agentId: "agent-123", data: { harness: "opencode", pid: 12345 } } +// 2. External event arrives +{ type: "slack:webhook-received", version: 1, createdAt: "2024-01-15T10:15:01.000Z", eventStreamId: "stream-abc123", payload: { channel: "C123", text: "Hello" } } -// 3. External event arrives -{ type: "slack:message-received", payload: { channel: "C123", text: "Hello" } } +// 3. Adapter transforms to action +{ type: "iterate:agent:harness:opencode:action:prompt:called", version: 1, createdAt: "2024-01-15T10:15:01.100Z", eventStreamId: "stream-abc123", payload: { content: "Hello" } } -// 4. Subscriber transforms to action -{ type: "iterate:agent:harness:opencode:action:prompt-requested", agentId: "agent-123", harness: "opencode", data: { content: "Hello" } } +// 4. Harness events flow (verbatim wrapped) +{ type: "iterate:agent:harness:opencode:event-received", version: 1, createdAt: "2024-01-15T10:15:02.000Z", eventStreamId: "stream-abc123", payload: { type: "Session.Message.Created", ... } } +{ type: "iterate:agent:harness:opencode:event-received", version: 1, createdAt: "2024-01-15T10:15:02.500Z", eventStreamId: "stream-abc123", payload: { type: "Session.Message.Updated", ... } } -// 5. Harness events flow (verbatim wrapped) -{ type: "iterate:agent:harness:opencode:event-received", agentId: "agent-123", harness: "opencode", payload: { type: "Session.Message.Created", ... } } -{ type: "iterate:agent:harness:opencode:event-received", agentId: "agent-123", harness: "opencode", payload: { type: "Session.Message.Updated", ... } } - -// 6. Destroy -{ type: "iterate:agent:harness:opencode:action:session-destroy-requested", agentId: "agent-123" } -{ type: "iterate:agent:system:stopped", agentId: "agent-123", data: { reason: "requested" } } +// 5. Destroy +{ type: "iterate:agent:harness:opencode:action:session-destroy:called", version: 1, createdAt: "2024-01-15T10:20:00.000Z", eventStreamId: "stream-abc123" } ``` -## Harness Subscriber Architecture +## Harness Adapters: The Pattern -Harness implementations are **event subscribers**. One global subscriber per harness type subscribes to all agent streams and: -1. Transforms external events β†’ action events -2. Executes action events β†’ calls harness API -3. Wraps harness output β†’ appends to stream +A harness adapter is just code that: +1. Subscribes to a durable stream +2. Appends events to the stream +3. Interacts with a harness SDK/CLI -```typescript -interface HarnessSubscriber { - readonly harness: "opencode" | "claude" | "pi" | "iterate" - - // Called for every event on every agent stream using this harness - handleEvent( - agentId: string, - event: IterateEvent - ): Effect, HarnessError> -} +No interfaces. No abstractions. Just the three stream operations + SDK calls. -// Example: OpenCode subscriber -const openCodeSubscriber: HarnessSubscriber = { - harness: "opencode", - - handleEvent: (agentId, event) => Effect.gen(function*() { - switch (event.type) { - // Transform external events to actions - case "slack:message-received": - return [{ - type: "iterate:agent:harness:opencode:action:prompt-requested", - agentId, - harness: "opencode", - data: { content: event.payload.text } - }] - - // Execute actions by calling harness API - case "iterate:agent:harness:opencode:action:prompt-requested": - yield* openCodeClient.sendPrompt(agentId, event.data.content) - return [] // Harness events come via SSE subscription - - case "iterate:agent:harness:opencode:action:session-create-requested": - yield* openCodeClient.createSession(agentId, event.data.config) - return [] - - default: - return [] // Ignore events we don't handle - } - }) -} +### OpenCode Adapter (Complete Example) + +```typescript +// OpenCode adapter - the entire implementation pattern +const runOpenCodeAdapter = (eventStreamId: string) => Effect.gen(function*() { + const stream = yield* DurableStream + const opencode = yield* OpenCodeClient + + // 1. Subscribe to stream events + yield* stream.subscribe(eventStreamId, { fromOffset: "latest" }).pipe( + Stream.runForEach((event) => Effect.gen(function*() { + // Handle action events by calling OpenCode API + if (event.type === "iterate:agent:harness:opencode:action:prompt:called") { + yield* opencode.sendPrompt(event.payload.sessionId, event.payload.content) + } + if (event.type === "iterate:agent:harness:opencode:action:session-create:called") { + yield* opencode.createSession(event.payload.config) + } + // Transform external events to action events + if (event.type === "slack:webhook-received") { + yield* stream.append(eventStreamId, { + type: "iterate:agent:harness:opencode:action:prompt:called", + eventStreamId, + payload: { content: event.payload.text } + }) + } + })) + ) + + // 2. Subscribe to OpenCode's native events, wrap and append + yield* opencode.subscribeEvents().pipe( + Stream.runForEach((nativeEvent) => + stream.append(eventStreamId, { + type: "iterate:agent:harness:opencode:event-received", + eventStreamId, + payload: nativeEvent // Verbatim, no transformation + }) + ) + ) +}) ``` -### Event Flow (Slack β†’ OpenCode Example) +**That's the whole pattern.** Subscribe to stream. Append to stream. Call SDK. Done. + +### Event Flow (Slack β†’ OpenCode) ``` -slack:message-received arrives on agent-123 stream +slack:webhook-received arrives on stream β”‚ β–Ό -OpenCode subscriber sees event, returns action: - iterate:agent:harness:opencode:action:prompt-requested +Adapter sees event, appends action: + iterate:agent:harness:opencode:action:prompt:called β”‚ β–Ό -Action appended to stream - β”‚ - β–Ό -OpenCode subscriber sees action, calls: +Adapter sees action event, calls: POST /session/:id/prompt_async β”‚ β–Ό @@ -283,21 +295,21 @@ Appended to stream β†’ Renderers see it ### Offset Tracking (Replay Safety) -Each subscriber tracks its last-processed offset per stream in a simple file: +Each adapter tracks its last-processed offset per stream in a simple file: ``` -~/.iterate/subscriber-offsets/opencode.json +.iterate/adapter-offsets/opencode.json { - "agent-123": 42, - "agent-456": 17 + "stream-abc123": 42, + "stream-def456": 17 } ``` -On daemon restart, subscriber resumes from stored offset, skipping already-handled events. At-most-once semantics: if action execution fails, emit error event, don't retry. +On restart, adapter resumes from stored offset, skipping already-handled events. At-most-once semantics: if action execution fails, log and continue (don't retry). ## Harness Implementations -### OpenCode Subscriber +### OpenCode HTTP/SSE server architecture. One server per sandbox, multiple sessions multiplexed. @@ -314,15 +326,15 @@ opencode serve --port 4096 | `/event` | SSE event stream | **Action handlers:** -- `session-create-requested` β†’ `POST /session` -- `prompt-requested` β†’ `POST /session/:id/prompt_async` -- `abort-requested` β†’ `POST /session/:id/abort` +- `action:session-create:called` β†’ `POST /session` +- `action:prompt:called` β†’ `POST /session/:id/prompt_async` +- `action:abort:called` β†’ `POST /session/:id/abort` **Event wrapping:** Subscribe to `/event` SSE, wrap each native event as `iterate:agent:harness:opencode:event-received`. TUI attach: `opencode attach --hostname localhost --port 4096` -### Claude Code Subscriber +### Claude Code CLI-per-invocation via SDK. SDK spawns CLI binary internally. @@ -354,40 +366,122 @@ for await (const message of response) { TUI resume: `claude --resume ` -### Pi Subscriber +### Pi + +Direct programmatic usage via `@mariozechner/pi-coding-agent` SDK. Eliminates subprocess overhead while maintaining CLI compatibility. + +**Design philosophy**: "omit to discover, provide to override" β€” omit config options to use CLI-compatible auto-discovery, or provide explicit values. -Long-running subprocess with JSON RPC on stdin/stdout. +**Primary exports:** +- `createAgentSession(options)` β€” Main factory returning `{ session }` for agent interaction +- `SessionManager` β€” Session file persistence (JSONL format) with static factory methods +- `discoverAuthStorage()` β€” Discovers credentials from `~/.pi/agent/auth.json` and env vars +- `discoverModels(authStorage)` β€” Discovers available models (built-in + custom `~/.pi/agent/models.json`) + +**Session creation (mirrors CLI):** ```typescript -const process = spawn('pi', ['--mode', 'rpc', '--session', sessionPath]) +SessionManager.create() // New session (auto-saves to ~/.pi/agent/sessions/) +SessionManager.open(path) // Open specific file (--session /path/to/file.jsonl) +SessionManager.open("a8ec1c2a") // Resume by partial UUID (--session a8ec1c2a) +SessionManager.continueRecent() // Most recent session (-c or --continue) +SessionManager.inMemory() // Ephemeral (--no-session) +SessionManager.list() // List available sessions (-r or --resume) +``` + +**Event types (match RPC protocol):** -// Send command -process.stdin.write(JSON.stringify({ type: 'prompt', message: "Hello" }) + '\n') +| Event Type | Description | +|------------|-------------| +| `agent_start` | Agent processing begins | +| `message_update` | Streaming text/thinking deltas; contains `assistantMessageEvent` | +| `turn_start` / `turn_end` | Turn boundaries (turns repeat while LLM calls tools) | +| `tool_call` | Tool about to execute | +| `tool_result` | Tool execution completed | +| `agent_end` | Agent processing complete | +| `error` | Error occurred | -// Receive events on stdout (NDJSON), wrap as iterate:agent:harness:pi:event-received +**Complete adapter example:** + +```typescript +import { + createAgentSession, + discoverAuthStorage, + discoverModels, + SessionManager, +} from "@mariozechner/pi-coding-agent" + +// CLI-compatible configuration discovery +const authStorage = discoverAuthStorage() +const modelRegistry = discoverModels(authStorage) + +// Create session with file persistence +const sessionManager = SessionManager.create() + +const { session } = await createAgentSession({ + sessionManager, + authStorage, + modelRegistry, + // Optional overrides: cwd, model, thinkingLevel, systemPrompt, tools, extensions +}) + +// Session file path for CLI interop (pi --session ) +console.log("Session file:", sessionManager.sessionFile) + +// Subscribe to events +const unsubscribe = session.subscribe((event) => { + switch (event.type) { + case "message_update": + if (event.assistantMessageEvent?.type === "text_delta") { + // Stream text output, wrap as iterate:agent:harness:pi:event-received + process.stdout.write(event.assistantMessageEvent.delta ?? "") + } + break + case "tool_call": + console.log(`[Tool: ${event.toolName}]`) + break + case "agent_end": + console.log("[Agent finished]") + break + } +}) + +// Send prompts (async, returns when agent finishes) +await session.prompt("List TypeScript files") +await session.prompt("What patterns do you see?") + +// Session methods +session.abort() // Cancel current processing +await session.waitForIdle() +await session.branch() // Fork conversation (like /branch) +await session.reset() // Reset session (like /new) + +unsubscribe() ``` -| Command | Purpose | -|---------|---------| -| `prompt` | Send user message | -| `abort` | Cancel operation | -| `get_state` | Query session state | -| `branch` | Fork from message index | +**Resuming existing session:** + +```typescript +const sessionManager = SessionManager.open(sessionPath) +const { session } = await createAgentSession({ sessionManager, authStorage, modelRegistry }) +// History loaded from JSONL file, continue conversation +await session.prompt("Continue where we left off") +``` TUI resume: `pi --session /path/to/session.jsonl` -### Iterate Agent Subscriber +### Iterate Agent -Our own agent implementation. Details TBDβ€”will use mini-agent infrastructure with Effect-based LLM adapter. +Our own agent implementation. Details TBDβ€”Effect-based LLM adapter. ### Comparison | Aspect | OpenCode | Claude Code | Pi | Iterate Agent | |--------|----------|-------------|-----|---------------| -| Architecture | HTTP/SSE server | CLI per query | stdin/stdout RPC | TBD | +| Architecture | HTTP/SSE server | CLI per query | Direct SDK | TBD | | Concurrent safe | Yes (server) | No (file-based) | No (file-based) | TBD | -| Startup overhead | Server must run | ~12s per query | Process spawn | TBD | -| CLI capture | Built-in (same server) | Global hooks needed | Same process | N/A | +| Startup overhead | Server must run | ~12s per query | None (in-process) | TBD | +| CLI capture | Built-in (same server) | Global hooks needed | Same session file | N/A | ## Tool Injection @@ -397,7 +491,7 @@ Iterate tools registered in each harness using harness-specific mechanisms: |---------|-------------| | OpenCode | Agent config or runtime SDK | | Claude Code | MCP servers, `--allowedTools` | -| Pi | TypeBox schemas | +| Pi | `extensions` option in `createAgentSession()` | | Iterate Agent | Effect Schema directly | **Approach**: Define tools using Effect Schema, convert to harness-specific format. Long-term: MCP as canonical format where supported. @@ -406,10 +500,10 @@ Iterate tools registered in each harness using harness-specific mechanisms: Users can SSH into sandbox and use native TUI. When they do: - From agent's perspective, everything is normal -- Subscriber captures events β†’ appear on SSE stream +- Adapter captures events β†’ appear on SSE stream - Web UI sees same events -**Handoff protocol**: When TUI attaches, emit `iterate:agent:system:client-attached`. Web UI shows takeover banner, optionally enters read-only mode. On TUI exit, emit `iterate:agent:system:client-detached`, web UI resumes. +**Handoff**: When TUI attaches, adapter detects and web UI shows takeover banner (optionally enters read-only mode). On TUI exit, web UI resumes. ## Renderers @@ -439,12 +533,11 @@ function handleStreamEvent(event: IterateEvent) { } // Writing events (user sends message from Web UI) -async function onUserSubmit(agentId: string, text: string) { - await stream.append({ - type: "iterate:agent:harness:opencode:action:prompt-requested", - agentId, - harness: "opencode", - data: { content: text, source: "web-ui" } +async function onUserSubmit(eventStreamId: string, text: string) { + await stream.append(eventStreamId, { + type: "iterate:agent:harness:opencode:action:prompt:called", + eventStreamId, + payload: { content: text } }) } ``` @@ -453,22 +546,6 @@ Initial implementation: Show all events raw in a feed. Later: Rich rendering for --- -## Decisions Made - -| Decision | Choice | -|----------|--------| -| Action event persistence | First-class stream events | -| Subscriber cardinality | One global subscriber per harness type | -| Stream topology | One stream per agent instance | -| External event routing | Hardcoded rules in harness subscriber | -| Action namespace | `iterate:agent:harness:{harness}:action:{verb}-requested` | -| Wrapped event naming | Generic `event-received` type, native in payload | -| Renderer capabilities | Bidirectional (read + write) | -| Replay safety | Offset file per subscriber | -| Action execution | At-most-once (fail fast, emit error event) | - ---- - ## Open Questions ### Session Concurrency @@ -487,16 +564,12 @@ Ignore for now - OpenCode: Hybrid auto-daemon (connect to existing, spawn if needed) - Claude: SDK manages per-query -- Pi: Supervised with auto-restart on crash +- Pi: In-process SDK (no process to manage) - Iterate Agent: TBD **Health monitoring?** - Passive exit monitoring + periodic health checks -- Emit structured crash events (`iterate:agent:system:error-occurred`) ### Storage **Duplication strategy**: We store the full wrapped harness events ourselves, separately to how the harness themselves does it. So technically harness events are stored twice - once by opencode/pi/claude and once wrapped by us. - -**Session ID mapping**: Mapping events in stream (`iterate:agent:system:session-mapped`). Need to elaborate on this - diff --git a/src/diagram-scratch.md b/src/diagram-scratch.md index b3036a9..41fce6d 100644 --- a/src/diagram-scratch.md +++ b/src/diagram-scratch.md @@ -1,80 +1,58 @@ +# Architecture Diagram Scratch Pad - -## Attempt 6 - Cleaner, shows harness implementation pattern +## Attempt 7 - Simplified bidirectional ``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ β”‚ -β”‚ AGENT STREAM (per agent) β”‚ -β”‚ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ β”‚ β”‚ - β”‚ β”‚ β”‚ β”‚ - slack:webhook-received iterate:agent:harness: iterate:agent:harness: iterate:agent:harness: - github:webhook-received opencode:action: opencode:action: opencode:event-received - session-create-requested prompt-requested opencode:event-received - opencode:event-received - β”‚ β”‚ β”‚ β”‚ - β”‚ β”‚ β”‚ β”‚ - from external from harness from Web/TUI from harness - (Slack, GitHub) subscribers (user sends message) runtimes - (tool-call, assistant-msg, - streaming-chunk) - β”‚ - β”‚ subscribe - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ - β”‚ HARNESS SUBSCRIBERS β”‚ - β”‚ β”‚ - β”‚ OpenCode Claude Pi Iterate β”‚ - β”‚ β”‚ - β”‚ A harness subscriber: β”‚ - β”‚ β€’ Sees events on the stream β”‚ - β”‚ β€’ Emits action events (e.g. prompt-requested) to control the harness β”‚ - β”‚ β€’ Calls harness APIs when it sees action events β”‚ - β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”‚ call harness API - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ - β”‚ HARNESS RUNTIMES β”‚ - β”‚ β”‚ - β”‚ OpenCode Server Claude SDK/CLI Pi Process mini-agent β”‚ - β”‚ β”‚ - β”‚ Harness runtimes emit native events. β”‚ - β”‚ Subscribers wrap these as iterate:agent:harness:{name}:event-received β”‚ - β”‚ and append back to stream. β”‚ - β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”‚ wrapped harness events - β”‚ (append to stream) - β”‚ - └──────────────────────────────────────────────────────►┐ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ - β”‚ β”‚ - β”‚ RENDERERS β”‚ - β”‚ β”‚ - β”‚ CLI / TUI Web UI β”‚ - β”‚ β”‚ - β”‚ Renderers subscribe to the stream and display events. β”‚ - β”‚ They can also append action events (e.g. user types a message). β”‚ - β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”‚ control events (user sends message) - β”‚ iterate:agent:harness:opencode:action:prompt-requested - β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”˜ - (append to stream) + External events + (slack:webhook-received, github:webhook-received) + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ +β”‚ AGENT STREAM (per agent) β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β–² β”‚ β–² + β”‚ β”‚ β”‚ β”‚ + subscribeβ”‚ β”‚publish subscribeβ”‚ β”‚publish + β”‚ β”‚action events β”‚ β”‚control events + β”‚ β”‚(prompt-requested, β”‚ β”‚(prompt-requested) + β”‚ β”‚ session-create-requested) β”‚ β”‚ + β”‚ β”‚wrapped harness events β”‚ β”‚ + β”‚ β”‚(opencode:event-received) β”‚ β”‚ + β–Ό β”‚ β–Ό β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ β”‚ β”‚ +β”‚ HARNESS SUBSCRIBERS β”‚ β”‚ RENDERERS β”‚ +β”‚ (our code) β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ Web UI CLI/TUI β”‚ +β”‚ OpenCode Claude Pi Iterate β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ Subscribe to stream, display β”‚ +β”‚ Subscribe to stream, emit action events, β”‚ β”‚ events. User types message β†’ β”‚ +β”‚ call harness APIs, wrap harness output β”‚ β”‚ publish control event. β”‚ +β”‚ β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β–² + β”‚ β”‚ + call APIβ”‚ β”‚subscribe to + β”‚ β”‚harness events + β–Ό β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ +β”‚ HARNESS RUNTIMES β”‚ +β”‚ (their code) β”‚ +β”‚ β”‚ +β”‚ OpenCode Server Claude SDK Pi Process mini-agent +β”‚ β”‚ +β”‚ Native agent runtimes. Emit their own events. β”‚ +β”‚ We subscribe to these and wrap them. β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` -**Key insight**: A harness implementation is just a subscriber that: -1. Reacts to events by emitting action events (control the harness) -2. Calls harness APIs when it sees action events -3. Wraps harness output and appends back to stream - +**The pattern:** +- External events β†’ Agent Stream (only external input) +- Harness Subscribers ←→ Agent Stream (subscribe + publish action events + wrapped harness events) +- Renderers ←→ Agent Stream (subscribe + publish control events) +- Harness Subscribers ←→ Harness Runtimes (call API + subscribe to their events) +- Harness Runtimes do NOT touch Agent Stream directly diff --git a/src/durable-streams/README.md b/src/durable-streams/README.md index 9ecd216..6b7bc2c 100644 --- a/src/durable-streams/README.md +++ b/src/durable-streams/README.md @@ -253,7 +253,7 @@ curl -X POST http://localhost:3000/streams/my-stream \ Response: ```json -{"offset":"0000000000000000","data":{"type":"message","text":"hello"},"timestamp":1704672000000} +{"offset":"0000000000000000","eventStreamId":"my-stream","data":{"type":"message","text":"hello"},"createdAt":"2024-01-08T00:00:00.000Z"} ``` ### Subscribe (SSE) @@ -271,9 +271,9 @@ curl -N http://localhost:3000/streams/my-stream?offset=0000000000000005 Output (SSE format): ``` -data: {"offset":"0000000000000000","data":{"type":"message","text":"hello"},"timestamp":1704672000000} +data: {"offset":"0000000000000000","eventStreamId":"my-stream","data":{"type":"message","text":"hello"},"createdAt":"2024-01-08T00:00:00.000Z"} -data: {"offset":"0000000000000001","data":{"type":"message","text":"world"},"timestamp":1704672001000} +data: {"offset":"0000000000000001","eventStreamId":"my-stream","data":{"type":"message","text":"world"},"createdAt":"2024-01-08T00:00:01.000Z"} ``` ### Get Historic Events @@ -288,7 +288,7 @@ curl http://localhost:3000/streams/my-stream/events?offset=0000000000000005&limi Response: ```json -{"events":[{"offset":"0000000000000005","data":{"type":"message","text":"hello"},"timestamp":1704672000000}]} +{"events":[{"offset":"0000000000000005","eventStreamId":"my-stream","data":{"type":"message","text":"hello"},"createdAt":"2024-01-08T00:00:00.000Z"}]} ``` ### List Streams @@ -311,10 +311,11 @@ curl -X DELETE http://localhost:3000/streams/my-stream ## Event Structure ```typescript -interface StreamEvent { - offset: string // Zero-padded 16-char number ("0000000000000042") - data: unknown // Your payload - timestamp: number // Unix millis +interface Event { + offset: string // Zero-padded 16-char number ("0000000000000042") + eventStreamId: string // Name of the stream this event belongs to + data: unknown // Your payload + createdAt: string // ISO 8601 timestamp ("2024-01-08T00:00:00.000Z") } ``` diff --git a/src/durable-streams/cli.ts b/src/durable-streams/cli.ts index 3ff1ad4..0ee870e 100644 --- a/src/durable-streams/cli.ts +++ b/src/durable-streams/cli.ts @@ -1,5 +1,5 @@ /** - * Durable Streams CLI + * Event Stream CLI * * Commands: * @@ -10,6 +10,7 @@ * server status Check daemon status * * stream subscribe -n [--offset] Subscribe to stream events + * stream subscribe-all Subscribe to all streams (live only) * stream append -n -m|-e Append event to stream * stream get -n [--offset] [--limit] Get historic events * stream list List all streams @@ -22,11 +23,11 @@ import { Console, Effect, Layer, Option, Schema, Stream } from "effect" import { createServer } from "node:http" import { StreamClientService } from "./client.ts" import { DaemonService, DATA_DIR } from "./daemon.ts" -import { durableStreamsRouter } from "./http-routes.ts" +import { eventStreamRouter } from "./http-routes.ts" import { Storage } from "./storage.ts" import { ActiveFactory } from "./stream-factory.ts" import { StreamManagerService } from "./stream-manager.ts" -import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" +import { Event, type Offset, OFFSET_START, type StreamName } from "./types.ts" // ─── Shared Options ───────────────────────────────────────────────────────── @@ -63,7 +64,7 @@ const serverRunCommand = Command.make( const fs = yield* FileSystem.FileSystem const path = yield* Path.Path - yield* Console.log(`Starting durable-streams server on ${host}:${port}`) + yield* Console.log(`Starting event-stream server on ${host}:${port}`) yield* Console.log(`Storage: ${storage}`) // Build storage layer based on option @@ -94,7 +95,7 @@ const serverRunCommand = Command.make( Layer.provide(storageLayer) ) - const serverLayer = HttpServer.serve(durableStreamsRouter).pipe( + const serverLayer = HttpServer.serve(eventStreamRouter).pipe( Layer.provide(NodeHttpServer.layer(createServer, { port })), Layer.provide(serviceLayer) ) @@ -230,7 +231,7 @@ const streamSubscribeCommand = Command.make( // Output events as JSON lines yield* eventStream.pipe( Stream.runForEach((event) => { - const encoded = Schema.encodeSync(StreamEvent)(event) + const encoded = Schema.encodeSync(Event)(event) return Console.log(JSON.stringify(encoded)) }) ) @@ -239,6 +240,28 @@ const streamSubscribeCommand = Command.make( ) ).pipe(Command.withDescription("Subscribe to stream events (outputs JSON lines)")) +/** stream subscribe-all - subscribe to all streams (live events only) */ +const streamSubscribeAllCommand = Command.make( + "subscribe-all", + { server: serverUrlOption }, + () => + Effect.gen(function*() { + const client = yield* StreamClientService + + const eventStream = yield* client.subscribeAll() + + // Output events as JSON lines + yield* eventStream.pipe( + Stream.runForEach((event) => { + const encoded = Schema.encodeSync(Event)(event) + return Console.log(JSON.stringify(encoded)) + }) + ) + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e.message}`)) + ) +).pipe(Command.withDescription("Subscribe to all streams (live events only, outputs JSON lines)")) + /** stream append - append event to stream */ const streamAppendCommand = Command.make( "append", @@ -262,7 +285,7 @@ const streamAppendCommand = Command.make( } const result = yield* client.append({ name: name as StreamName, data }) - const encoded = Schema.encodeSync(StreamEvent)(result) + const encoded = Schema.encodeSync(Event)(result) yield* Console.log(JSON.stringify(encoded)) }).pipe( Effect.catchAll((e) => Console.error(`Error: ${e.message}`)) @@ -288,7 +311,7 @@ const streamGetCommand = Command.make( const events = yield* client.get(getOpts) for (const event of events) { - const encoded = Schema.encodeSync(StreamEvent)(event) + const encoded = Schema.encodeSync(Event)(event) yield* Console.log(JSON.stringify(encoded)) } }).pipe( @@ -335,6 +358,7 @@ const streamDeleteCommand = Command.make( const streamCommand = Command.make("stream").pipe( Command.withSubcommands([ streamSubscribeCommand, + streamSubscribeAllCommand, streamAppendCommand, streamGetCommand, streamListCommand, @@ -345,14 +369,14 @@ const streamCommand = Command.make("stream").pipe( // ─── Root Command ─────────────────────────────────────────────────────────── -const rootCommand = Command.make("durable-streams").pipe( +const rootCommand = Command.make("event-stream").pipe( Command.withSubcommands([serverCommand, streamCommand]), - Command.withDescription("Durable event streams with daemon support") + Command.withDescription("Event streams with daemon support") ) /** Main CLI definition */ export const cli = Command.run(rootCommand, { - name: "durable-streams", + name: "event-stream", version: "0.1.0" }) diff --git a/src/durable-streams/client.ts b/src/durable-streams/client.ts index e86a83c..779e684 100644 --- a/src/durable-streams/client.ts +++ b/src/durable-streams/client.ts @@ -1,5 +1,5 @@ /** - * HTTP Client for Durable Streams + * HTTP Client for Event Streams * * Provides typed client operations for stream append and subscribe. * Handles SSE parsing for subscriptions. @@ -11,7 +11,7 @@ import type * as HttpClientError from "@effect/platform/HttpClientError" import * as HttpClientRequest from "@effect/platform/HttpClientRequest" import { Duration, Effect, Layer, Option, Schedule, Schema, Stream } from "effect" import { DaemonService, defaultDaemonConfig } from "./daemon.ts" -import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" +import { Event, type Offset, OFFSET_START, type StreamName } from "./types.ts" /** Max time to wait for daemon to become ready */ const DAEMON_READY_TIMEOUT = Duration.seconds(10) @@ -42,20 +42,23 @@ export interface StreamClient { readonly append: (opts: { name: StreamName data: unknown - }) => Effect.Effect + }) => Effect.Effect /** Subscribe to a stream, returns event stream */ readonly subscribe: (opts: { name: StreamName offset?: Offset - }) => Effect.Effect, ClientError> + }) => Effect.Effect, ClientError> + + /** Subscribe to all streams (live events only, no history) */ + readonly subscribeAll: () => Effect.Effect, ClientError> /** Get historic events from a stream (one-shot, no live subscription) */ readonly get: (opts: { name: StreamName offset?: Offset limit?: number - }) => Effect.Effect, ClientError> + }) => Effect.Effect, ClientError> /** List all streams */ readonly list: () => Effect.Effect, ClientError> @@ -116,7 +119,7 @@ export const makeStreamClient = ( return new ClientError(`Request failed: ${error.message}`) } - const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => Effect.gen(function*() { const request = HttpClientRequest.post(`/streams/${opts.name}`, { body: HttpBody.unsafeJson({ data: opts.data }) @@ -132,7 +135,7 @@ export const makeStreamClient = ( }) ) - return yield* Schema.decodeUnknown(StreamEvent)(response).pipe( + return yield* Schema.decodeUnknown(Event)(response).pipe( Effect.mapError((e) => new ClientError(`Invalid event: ${e}`)) ) }) @@ -140,20 +143,43 @@ export const makeStreamClient = ( const subscribe = (opts: { name: StreamName offset?: Offset - }): Effect.Effect, ClientError> => { + }): Effect.Effect, ClientError> => { const offsetParam = opts.offset ? `?offset=${opts.offset === OFFSET_START ? "-1" : opts.offset}` : "" const url = `/streams/${opts.name}${offsetParam}` const request = HttpClientRequest.get(url) // Return a stream that handles the SSE connection - const eventStream: Stream.Stream = clientOk.execute(request).pipe( + const eventStream: Stream.Stream = clientOk.execute(request).pipe( + Effect.map((r) => r.stream), + Stream.unwrapScoped, + Stream.decodeText(), + Stream.pipeThroughChannel(Sse.makeChannel()), + Stream.mapEffect((event) => + Schema.decode(Schema.parseJson(Event))(event.data).pipe( + Effect.mapError((e) => new ClientError(`Parse error: ${e}`)) + ) + ), + Stream.catchTags({ + RequestError: (error) => Stream.fail(mapRequestError(error)), + ResponseError: (error) => Stream.fail(new ClientError(`HTTP ${error.response.status}`, error.response.status)) + }) + ) + + return Effect.succeed(eventStream) + } + + const subscribeAll = (): Effect.Effect, ClientError> => { + const request = HttpClientRequest.get("/streams/all") + + // Return a stream that handles the SSE connection + const eventStream: Stream.Stream = clientOk.execute(request).pipe( Effect.map((r) => r.stream), Stream.unwrapScoped, Stream.decodeText(), Stream.pipeThroughChannel(Sse.makeChannel()), Stream.mapEffect((event) => - Schema.decode(Schema.parseJson(StreamEvent))(event.data).pipe( + Schema.decode(Schema.parseJson(Event))(event.data).pipe( Effect.mapError((e) => new ClientError(`Parse error: ${e}`)) ) ), @@ -170,7 +196,7 @@ export const makeStreamClient = ( name: StreamName offset?: Offset limit?: number - }): Effect.Effect, ClientError> => + }): Effect.Effect, ClientError> => Effect.gen(function*() { const params = new URLSearchParams() if (opts.offset) params.set("offset", opts.offset === OFFSET_START ? "-1" : opts.offset) @@ -192,7 +218,7 @@ export const makeStreamClient = ( return yield* Effect.all( response.events.map((e) => - Schema.decodeUnknown(StreamEvent)(e).pipe( + Schema.decodeUnknown(Event)(e).pipe( Effect.mapError((err) => new ClientError(`Invalid event: ${err}`)) ) ) @@ -237,6 +263,7 @@ export const makeStreamClient = ( return { append, subscribe, + subscribeAll, get, list, delete: deleteStream @@ -245,7 +272,7 @@ export const makeStreamClient = ( /** Stream client service with auto-daemon support */ export class StreamClientService extends Effect.Service()( - "@durable-streams/StreamClient", + "@event-stream/StreamClient", { effect: Effect.gen(function*() { const daemon = yield* DaemonService @@ -254,7 +281,7 @@ export class StreamClientService extends Effect.Service()( /** Resolve server URL from env, flag, or daemon. Starts daemon if needed and waits for ready. */ const resolveServerUrl: Effect.Effect = Effect.gen(function*() { // Check env var first - const envUrl = process.env.DURABLE_STREAMS_URL + const envUrl = process.env.EVENT_STREAM_URL if (envUrl) return envUrl // Check if daemon is running @@ -291,6 +318,7 @@ export class StreamClientService extends Effect.Service()( return { append: (opts: { name: StreamName; data: unknown }) => withClient((c) => c.append(opts)), subscribe: (opts: { name: StreamName; offset?: Offset }) => withClient((c) => c.subscribe(opts)), + subscribeAll: () => withClient((c) => c.subscribeAll()), get: (opts: { name: StreamName; offset?: Offset; limit?: number }) => withClient((c) => c.get(opts)), list: () => withClient((c) => c.list()), delete: (opts: { name: StreamName }) => withClient((c) => c.delete(opts)) @@ -309,7 +337,7 @@ export const StreamClientLive: Layer.Layer = Effect.gen(function*() { - const envUrl = process.env.DURABLE_STREAMS_URL + const envUrl = process.env.EVENT_STREAM_URL if (envUrl) return envUrl const daemonUrl = yield* daemon.getServerUrl().pipe( @@ -344,6 +372,7 @@ export const StreamClientLive: Layer.Layer withClient((c) => c.append(opts)), subscribe: (opts: { name: StreamName; offset?: Offset }) => withClient((c) => c.subscribe(opts)), + subscribeAll: () => withClient((c) => c.subscribeAll()), get: (opts: { name: StreamName; offset?: Offset; limit?: number }) => withClient((c) => c.get(opts)), list: () => withClient((c) => c.list()), delete: (opts: { name: StreamName }) => withClient((c) => c.delete(opts)) diff --git a/src/durable-streams/daemon.ts b/src/durable-streams/daemon.ts index c509e8a..a2a472e 100644 --- a/src/durable-streams/daemon.ts +++ b/src/durable-streams/daemon.ts @@ -24,7 +24,7 @@ export interface DaemonConfig { readonly storage: StorageBackend } -/** Data directory for all durable-streams files */ +/** Data directory for all event-stream files */ export const DATA_DIR = ".iterate" /** Default config - files in .iterate/ */ @@ -219,7 +219,7 @@ const makeDaemonImpl = ( } /** Daemon service tag and implementation */ -export class DaemonService extends Effect.Service()("@durable-streams/Daemon", { +export class DaemonService extends Effect.Service()("@event-stream/Daemon", { effect: Effect.gen(function*() { const fs = yield* FileSystem.FileSystem const path = yield* Path.Path diff --git a/src/durable-streams/hooks.e2e.test.ts b/src/durable-streams/hooks.e2e.test.ts index 02c3b07..7a1c35d 100644 --- a/src/durable-streams/hooks.e2e.test.ts +++ b/src/durable-streams/hooks.e2e.test.ts @@ -10,11 +10,11 @@ import { createServer } from "node:http" import { afterAll, beforeAll, describe, expect, test } from "vitest" import type { StreamHooks } from "./hooks.ts" import { HookError } from "./hooks.ts" -import { durableStreamsRouter } from "./http-routes.ts" +import { eventStreamRouter } from "./http-routes.ts" import { Storage } from "./storage.ts" -import { DurableStreamFactory } from "./stream-factory.ts" +import { EventStreamFactory } from "./stream-factory.ts" import { StreamManagerService } from "./stream-manager.ts" -import type { StreamEvent } from "./types.ts" +import type { Event } from "./types.ts" describe("Hooks E2E", () => { describe("ValidatedFactory (before hook)", () => { @@ -47,7 +47,7 @@ describe("Hooks E2E", () => { const serverLayer = Layer.mergeAll( NodeHttpServer.layer(createServer, { port: 0 }), StreamManagerService.Live.pipe( - Layer.provide(DurableStreamFactory.WithHooks(validatedHooks)), + Layer.provide(EventStreamFactory.WithHooks(validatedHooks)), Layer.provide(Storage.InMemory) ) ).pipe(Layer.provideMerge(HttpServer.layerContext)) @@ -58,7 +58,7 @@ describe("Hooks E2E", () => { const serverEffect = Effect.gen(function*() { const server = yield* HttpServer.HttpServer yield* Deferred.succeed(addressDeferred, server.address) - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) return yield* Effect.never }).pipe( Effect.scoped, @@ -102,7 +102,7 @@ describe("Hooks E2E", () => { expect(response.status).toBe(201) - const event = await response.json() as StreamEvent + const event = await response.json() as Event expect(event.offset).toBe("0000000000000000") expect(event.data).toEqual({ _type: "message", text: "hello" }) }) @@ -130,7 +130,7 @@ describe("Hooks E2E", () => { const serverLayer = Layer.mergeAll( NodeHttpServer.layer(createServer, { port: 0 }), StreamManagerService.Live.pipe( - Layer.provide(DurableStreamFactory.WithHooks(trackingHooks)), + Layer.provide(EventStreamFactory.WithHooks(trackingHooks)), Layer.provide(Storage.InMemory) ) ).pipe(Layer.provideMerge(HttpServer.layerContext)) @@ -141,7 +141,7 @@ describe("Hooks E2E", () => { const serverEffect = Effect.gen(function*() { const server = yield* HttpServer.HttpServer yield* Deferred.succeed(addressDeferred, server.address) - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) return yield* Effect.never }).pipe( Effect.scoped, @@ -224,7 +224,7 @@ describe("Hooks E2E", () => { const serverLayer = Layer.mergeAll( NodeHttpServer.layer(createServer, { port: 0 }), StreamManagerService.Live.pipe( - Layer.provide(DurableStreamFactory.WithHooks(orderTrackingHooks)), + Layer.provide(EventStreamFactory.WithHooks(orderTrackingHooks)), Layer.provide(Storage.InMemory) ) ).pipe(Layer.provideMerge(HttpServer.layerContext)) @@ -235,7 +235,7 @@ describe("Hooks E2E", () => { const serverEffect = Effect.gen(function*() { const server = yield* HttpServer.HttpServer yield* Deferred.succeed(addressDeferred, server.address) - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) return yield* Effect.never }).pipe( Effect.scoped, diff --git a/src/durable-streams/hooks.ts b/src/durable-streams/hooks.ts index 1f25d10..6fa742f 100644 --- a/src/durable-streams/hooks.ts +++ b/src/durable-streams/hooks.ts @@ -1,12 +1,12 @@ /** - * Hook types for DurableStream composition (Layer 2) + * Hook types for EventStream composition (Layer 2) * * Hooks have IDs for debugging/logging and future dependency-based execution. * Before-hooks can veto operations; after-hooks are fire-and-forget. */ import type { Effect } from "effect" import { Schema } from "effect" -import type { StreamEvent, StreamName } from "./types.ts" +import type { Event, StreamName } from "./types.ts" /** Error when a before-hook vetoes an operation */ export class HookError extends Schema.TaggedError()("HookError", { @@ -29,11 +29,11 @@ export interface AfterAppendHook { readonly id: string readonly run: (opts: { name: StreamName - event: StreamEvent + event: Event }) => Effect.Effect } -/** Hook configuration for a DurableStream */ +/** Hook configuration for an EventStream */ export interface StreamHooks { readonly beforeAppend?: ReadonlyArray readonly afterAppend?: ReadonlyArray diff --git a/src/durable-streams/http-routes.e2e.test.ts b/src/durable-streams/http-routes.e2e.test.ts index d8925de..1c964ef 100644 --- a/src/durable-streams/http-routes.e2e.test.ts +++ b/src/durable-streams/http-routes.e2e.test.ts @@ -8,9 +8,9 @@ import { NodeHttpServer } from "@effect/platform-node" import { Deferred, Effect, Fiber, Layer } from "effect" import { createServer } from "node:http" import { afterAll, beforeAll, describe, expect, test } from "vitest" -import { durableStreamsRouter } from "./http-routes.ts" +import { eventStreamRouter } from "./http-routes.ts" import { StreamManagerService } from "./stream-manager.ts" -import type { StreamEvent } from "./types.ts" +import type { Event } from "./types.ts" let baseUrl: string let serverFiber: Fiber.RuntimeFiber @@ -30,7 +30,7 @@ beforeAll(async () => { const serverEffect = Effect.gen(function*() { const server = yield* HttpServer.HttpServer yield* Deferred.succeed(addressDeferred, server.address) - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) return yield* Effect.never }).pipe( Effect.scoped, @@ -77,10 +77,10 @@ describe("Native Fetch E2E", () => { expect(response.status).toBe(201) - const event = await response.json() as StreamEvent + const event = await response.json() as Event expect(event.offset).toBe("0000000000000000") expect(event.data).toEqual({ message: "hello from fetch" }) - expect(typeof event.timestamp).toBe("number") + expect(typeof event.createdAt).toBe("string") }) test("assigns sequential offsets", async () => { @@ -89,14 +89,14 @@ describe("Native Fetch E2E", () => { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: "event1" }) }) - const event1 = await res1.json() as StreamEvent + const event1 = await res1.json() as Event const res2 = await fetch(`${baseUrl}/streams/fetch-sequential`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: "event2" }) }) - const event2 = await res2.json() as StreamEvent + const event2 = await res2.json() as Event expect(event1.offset).toBe("0000000000000000") expect(event2.offset).toBe("0000000000000001") @@ -136,7 +136,7 @@ describe("Native Fetch E2E", () => { // Read SSE events from stream const reader = response.body!.getReader() const decoder = new TextDecoder() - const events: Array = [] + const events: Array = [] let buffer = "" while (events.length < 2) { @@ -177,7 +177,7 @@ describe("Native Fetch E2E", () => { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: "offset-event1" }) }) - const event1 = await res1.json() as StreamEvent + const event1 = await res1.json() as Event await fetch(`${baseUrl}/streams/fetch-offset`, { method: "POST", @@ -192,7 +192,7 @@ describe("Native Fetch E2E", () => { const reader = response.body!.getReader() const decoder = new TextDecoder() - const events: Array = [] + const events: Array = [] let buffer = "" while (events.length < 2) { diff --git a/src/durable-streams/http-routes.test.ts b/src/durable-streams/http-routes.test.ts index 1ed13a1..38417a1 100644 --- a/src/durable-streams/http-routes.test.ts +++ b/src/durable-streams/http-routes.test.ts @@ -1,5 +1,5 @@ /** - * E2E tests for durable-streams HTTP routes + * E2E tests for event-stream HTTP routes * * Uses @effect/platform-node's layerTest for automatic server setup. * HttpClient is pre-configured with the server's base URL. @@ -8,9 +8,9 @@ import { HttpClient, HttpClientRequest, HttpServer } from "@effect/platform" import { NodeHttpServer } from "@effect/platform-node" import { describe, expect, it } from "@effect/vitest" import { Effect, Layer, Stream } from "effect" -import { durableStreamsRouter } from "./http-routes.ts" +import { eventStreamRouter } from "./http-routes.ts" import { StreamManagerService } from "./stream-manager.ts" -import type { StreamEvent } from "./types.ts" +import type { Event } from "./types.ts" // Test layer: Node HTTP server + StreamManager + serve the router const testLayer = Layer.mergeAll( @@ -24,7 +24,7 @@ describe("HTTP Routes E2E", () => { describe("GET /streams", () => { it.effect("returns empty list initially", () => Effect.gen(function*() { - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) const client = yield* HttpClient.HttpClient const response = yield* client.get("/streams") @@ -38,7 +38,7 @@ describe("HTTP Routes E2E", () => { describe("POST /streams/:name", () => { it.effect("appends event and returns 201", () => Effect.gen(function*() { - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) const client = yield* HttpClient.HttpClient const request = yield* HttpClientRequest.post("/streams/test-stream").pipe( @@ -48,28 +48,28 @@ describe("HTTP Routes E2E", () => { expect(response.status).toBe(201) - const event = (yield* response.json) as StreamEvent + const event = (yield* response.json) as Event expect(event.offset).toBe("0000000000000000") expect(event.data).toEqual({ message: "hello world" }) - expect(typeof event.timestamp).toBe("number") + expect(typeof event.createdAt).toBe("string") }).pipe(Effect.scoped, Effect.provide(testLayer))) it.effect("assigns sequential offsets", () => Effect.gen(function*() { - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) const client = yield* HttpClient.HttpClient const req1 = yield* HttpClientRequest.post("/streams/sequential-test").pipe( HttpClientRequest.bodyJson({ data: "event1" }) ) const res1 = yield* client.execute(req1) - const event1 = (yield* res1.json) as StreamEvent + const event1 = (yield* res1.json) as Event const req2 = yield* HttpClientRequest.post("/streams/sequential-test").pipe( HttpClientRequest.bodyJson({ data: "event2" }) ) const res2 = yield* client.execute(req2) - const event2 = (yield* res2.json) as StreamEvent + const event2 = (yield* res2.json) as Event expect(event1.offset).toBe("0000000000000000") expect(event2.offset).toBe("0000000000000001") @@ -77,7 +77,7 @@ describe("HTTP Routes E2E", () => { it.effect("returns 400 for invalid JSON", () => Effect.gen(function*() { - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) const client = yield* HttpClient.HttpClient const request = HttpClientRequest.post("/streams/invalid-json-test").pipe( @@ -92,7 +92,7 @@ describe("HTTP Routes E2E", () => { describe("GET /streams/:name (SSE subscription)", () => { it.effect("returns historical events as SSE", () => Effect.gen(function*() { - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) const client = yield* HttpClient.HttpClient // Add some events first @@ -113,13 +113,13 @@ describe("HTTP Routes E2E", () => { expect(response.headers["content-type"]).toBe("text/event-stream") // Read first 2 events from the SSE stream - const events: Array = [] + const events: Array = [] yield* response.stream.pipe( Stream.decodeText(), Stream.mapConcat((chunk) => chunk.split("\n\n")), Stream.filter((line) => line.startsWith("data: ")), - Stream.map((line) => JSON.parse(line.slice(6)) as StreamEvent), + Stream.map((line) => JSON.parse(line.slice(6)) as Event), Stream.take(2), Stream.runForEach((event) => Effect.sync(() => events.push(event))) ) @@ -131,7 +131,7 @@ describe("HTTP Routes E2E", () => { it.effect("supports offset query parameter", () => Effect.gen(function*() { - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) const client = yield* HttpClient.HttpClient // Add 3 events @@ -144,7 +144,7 @@ describe("HTTP Routes E2E", () => { HttpClientRequest.bodyJson({ data: "event1" }) ) const res1 = yield* client.execute(req1) - const event1 = (yield* res1.json) as StreamEvent + const event1 = (yield* res1.json) as Event const req2 = yield* HttpClientRequest.post("/streams/offset-test").pipe( HttpClientRequest.bodyJson({ data: "event2" }) @@ -156,12 +156,12 @@ describe("HTTP Routes E2E", () => { expect(response.status).toBe(200) - const events: Array = [] + const events: Array = [] yield* response.stream.pipe( Stream.decodeText(), Stream.mapConcat((chunk) => chunk.split("\n\n")), Stream.filter((line) => line.startsWith("data: ")), - Stream.map((line) => JSON.parse(line.slice(6)) as StreamEvent), + Stream.map((line) => JSON.parse(line.slice(6)) as Event), Stream.take(2), Stream.runForEach((event) => Effect.sync(() => events.push(event))) ) @@ -175,7 +175,7 @@ describe("HTTP Routes E2E", () => { describe("DELETE /streams/:name", () => { it.effect("deletes stream and returns 204", () => Effect.gen(function*() { - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) const client = yield* HttpClient.HttpClient // Create stream diff --git a/src/durable-streams/http-routes.ts b/src/durable-streams/http-routes.ts index 6c0da34..be6b96f 100644 --- a/src/durable-streams/http-routes.ts +++ b/src/durable-streams/http-routes.ts @@ -1,20 +1,21 @@ /** - * HTTP Routes for durable-streams + * HTTP Routes for event-stream * * Endpoints: * - POST /streams/:name - Append event to stream (JSON body: { data: any }) * - GET /streams/:name - Subscribe to stream (SSE). Query params: offset + * - GET /streams/all - Subscribe to all streams (SSE, live events only) * - GET /streams - List all streams * - DELETE /streams/:name - Delete stream */ import { HttpRouter, HttpServerRequest, HttpServerResponse } from "@effect/platform" import { Effect, Schema, Stream } from "effect" import { StreamManagerService } from "./stream-manager.ts" -import { type Offset, OFFSET_START, StreamEvent, type StreamName } from "./types.ts" +import { Event, type Offset, OFFSET_START, type StreamName } from "./types.ts" -/** Encode a StreamEvent as SSE data line */ -const encodeSSE = (event: StreamEvent): Uint8Array => { - const encoded = Schema.encodeSync(StreamEvent)(event) +/** Encode an Event as SSE data line */ +const encodeSSE = (event: Event): Uint8Array => { + const encoded = Schema.encodeSync(Event)(event) return new TextEncoder().encode(`data: ${JSON.stringify(encoded)}\n\n`) } @@ -71,7 +72,7 @@ const appendHandler = Effect.gen(function*() { } const event = appendResult.right - const encoded = Schema.encodeSync(StreamEvent)(event) + const encoded = Schema.encodeSync(Event)(event) return yield* HttpServerResponse.json(encoded, { status: 201 }) }) @@ -149,6 +150,30 @@ const deleteHandler = Effect.gen(function*() { return HttpServerResponse.empty({ status: 204 }) }) +/** GET /streams/all - Subscribe to all streams (SSE, live events only) */ +const subscribeAllHandler = Effect.gen(function*() { + const manager = yield* StreamManagerService + + const eventStreamResult = yield* manager.subscribeAll().pipe(Effect.either) + + if (eventStreamResult._tag === "Left") { + const err = eventStreamResult.left + return HttpServerResponse.text(err.message, { status: 500 }) + } + + const eventStream = eventStreamResult.right + + const sseStream = eventStream.pipe(Stream.map(encodeSSE)) + + return HttpServerResponse.stream(sseStream, { + contentType: "text/event-stream", + headers: { + "Cache-Control": "no-cache", + "Connection": "keep-alive" + } + }) +}) + /** GET /streams/:name/events - Get historic events (one-shot, no SSE) */ const getEventsHandler = Effect.gen(function*() { const request = yield* HttpServerRequest.HttpServerRequest @@ -190,14 +215,15 @@ const getEventsHandler = Effect.gen(function*() { return HttpServerResponse.text(err.message, { status: 500 }) } - const events = eventsResult.right.map((e) => Schema.encodeSync(StreamEvent)(e)) + const events = eventsResult.right.map((e) => Schema.encodeSync(Event)(e)) return yield* HttpServerResponse.json({ events }) }) -/** Durable streams router */ -export const durableStreamsRouter = HttpRouter.empty.pipe( +/** Event stream router */ +export const eventStreamRouter = HttpRouter.empty.pipe( HttpRouter.post("/streams/:name", appendHandler), HttpRouter.get("/streams/:name/events", getEventsHandler), + HttpRouter.get("/streams/all", subscribeAllHandler), // Must be before :name to avoid "all" being matched HttpRouter.get("/streams/:name", subscribeHandler), HttpRouter.get("/streams", listHandler), HttpRouter.del("/streams/:name", deleteHandler) diff --git a/src/durable-streams/index.ts b/src/durable-streams/index.ts index ec5a054..8956b9a 100644 --- a/src/durable-streams/index.ts +++ b/src/durable-streams/index.ts @@ -1,11 +1,13 @@ /** - * Durable Streams - Event sourcing primitives with Effect-TS + * Event Streams - Event sourcing primitives with Effect-TS * - * @module durable-streams + * @module event-stream */ // Types export { + Event, + EventStreamId, InvalidOffsetError, isStartOffset, makeOffset, @@ -13,27 +15,26 @@ export { OFFSET_START, parseOffset, StorageError, - StreamEvent, StreamName, StreamNotFoundError } from "./types.ts" -export type { Offset as OffsetType, StreamName as StreamNameType } from "./types.ts" +export type { EventStreamId as EventStreamIdType, Offset as OffsetType, StreamName as StreamNameType } from "./types.ts" // Storage (Layer 0) export { Storage } from "./storage.ts" // Stream (Layer 1) -export { type DurableStream, makeDurableStream } from "./stream.ts" +export { type EventStream, makeEventStream } from "./stream.ts" // Hooks (Layer 2) export { type AfterAppendHook, type BeforeAppendHook, HookError, type StreamHooks } from "./hooks.ts" -export { withHooks } from "./with-hooks.ts" +export { type HookedEventStream, withHooks } from "./with-hooks.ts" // Stream Factory (Layer 3) export { ActiveFactory, - DurableStreamFactory, EmbryonicAgentFactory, + EventStreamFactory, PlainFactory, ValidatedFactory } from "./stream-factory.ts" @@ -42,7 +43,7 @@ export { export { type StreamManager, StreamManagerService } from "./stream-manager.ts" // HTTP Routes (Layer 5) -export { durableStreamsRouter } from "./http-routes.ts" +export { eventStreamRouter } from "./http-routes.ts" // CLI export { cli, run } from "./cli.ts" diff --git a/src/durable-streams/persistence.e2e.test.ts b/src/durable-streams/persistence.e2e.test.ts index 82d39aa..71a8d4d 100644 --- a/src/durable-streams/persistence.e2e.test.ts +++ b/src/durable-streams/persistence.e2e.test.ts @@ -12,11 +12,11 @@ import { createServer } from "node:http" import { tmpdir } from "node:os" import { join } from "node:path" import { describe, expect, test } from "vitest" -import { durableStreamsRouter } from "./http-routes.ts" +import { eventStreamRouter } from "./http-routes.ts" import { Storage } from "./storage.ts" import { PlainFactory } from "./stream-factory.ts" import { StreamManagerService } from "./stream-manager.ts" -import type { StreamEvent } from "./types.ts" +import type { Event } from "./types.ts" /** Helper to start a server with FileSystem storage */ const startServer = async (dataDir: string): Promise<{ @@ -42,7 +42,7 @@ const startServer = async (dataDir: string): Promise<{ const serverEffect = Effect.gen(function*() { const server = yield* HttpServer.HttpServer yield* Deferred.succeed(addressDeferred, server.address) - yield* HttpServer.serveEffect(durableStreamsRouter) + yield* HttpServer.serveEffect(eventStreamRouter) return yield* Effect.never }).pipe( Effect.scoped, @@ -84,7 +84,7 @@ describe("Persistence E2E", () => { body: JSON.stringify({ data: { message: "first event" } }) }) expect(res1.status).toBe(201) - const event1 = await res1.json() as StreamEvent + const event1 = await res1.json() as Event const res2 = await fetch(`${server.baseUrl}/streams/${streamName}`, { method: "POST", @@ -92,7 +92,7 @@ describe("Persistence E2E", () => { body: JSON.stringify({ data: { message: "second event" } }) }) expect(res2.status).toBe(201) - const event2 = await res2.json() as StreamEvent + const event2 = await res2.json() as Event // Verify offsets expect(event1.offset).toBe("0000000000000000") @@ -101,7 +101,7 @@ describe("Persistence E2E", () => { // 2. Get events before restart (verify they exist) const beforeRes = await fetch(`${server.baseUrl}/streams/${streamName}/events`) expect(beforeRes.status).toBe(200) - const beforeBody = await beforeRes.json() as { events: Array } + const beforeBody = await beforeRes.json() as { events: Array } expect(beforeBody.events).toHaveLength(2) // 3. Stop server @@ -114,7 +114,7 @@ describe("Persistence E2E", () => { // 5. Get events after restart - should still be there const afterRes = await fetch(`${server.baseUrl}/streams/${streamName}/events`) expect(afterRes.status).toBe(200) - const afterBody = await afterRes.json() as { events: Array } + const afterBody = await afterRes.json() as { events: Array } expect(afterBody.events).toHaveLength(2) expect(afterBody.events[0]!.offset).toBe("0000000000000000") @@ -129,7 +129,7 @@ describe("Persistence E2E", () => { body: JSON.stringify({ data: { message: "third event after restart" } }) }) expect(res3.status).toBe(201) - const event3 = await res3.json() as StreamEvent + const event3 = await res3.json() as Event // Offset should continue from where we left off expect(event3.offset).toBe("0000000000000002") @@ -196,25 +196,25 @@ describe("Persistence E2E", () => { // Get all events const allRes = await fetch(`${server.baseUrl}/streams/${streamName}/events`) - const allBody = await allRes.json() as { events: Array } + const allBody = await allRes.json() as { events: Array } expect(allBody.events).toHaveLength(5) // Get with limit const limitRes = await fetch(`${server.baseUrl}/streams/${streamName}/events?limit=2`) - const limitBody = await limitRes.json() as { events: Array } + const limitBody = await limitRes.json() as { events: Array } expect(limitBody.events).toHaveLength(2) expect((limitBody.events[0]!.data as { index: number }).index).toBe(0) // Get with offset (start from event 2) const offset = allBody.events[2]!.offset const offsetRes = await fetch(`${server.baseUrl}/streams/${streamName}/events?offset=${offset}`) - const offsetBody = await offsetRes.json() as { events: Array } + const offsetBody = await offsetRes.json() as { events: Array } expect(offsetBody.events).toHaveLength(3) // events 2, 3, 4 expect((offsetBody.events[0]!.data as { index: number }).index).toBe(2) // Get with offset and limit const combinedRes = await fetch(`${server.baseUrl}/streams/${streamName}/events?offset=${offset}&limit=1`) - const combinedBody = await combinedRes.json() as { events: Array } + const combinedBody = await combinedRes.json() as { events: Array } expect(combinedBody.events).toHaveLength(1) expect((combinedBody.events[0]!.data as { index: number }).index).toBe(2) diff --git a/src/durable-streams/storage.ts b/src/durable-streams/storage.ts index e271e1d..7adb8d4 100644 --- a/src/durable-streams/storage.ts +++ b/src/durable-streams/storage.ts @@ -1,5 +1,5 @@ /** - * Storage abstraction for durable streams + * Storage abstraction for event streams * * Implementations: * - InMemory: Fast, ephemeral (for tests) @@ -7,35 +7,44 @@ */ import { FileSystem, Path } from "@effect/platform" import { Effect, Layer, Schema } from "effect" -import { isStartOffset, makeOffset, type Offset, StorageError, StreamEvent, type StreamName } from "./types.ts" +import { + Event, + type EventStreamId, + isStartOffset, + makeOffset, + type Offset, + StorageError, + type StreamName +} from "./types.ts" /** Stored event shape (internal) */ interface StoredEvent { readonly offset: Offset + readonly eventStreamId: EventStreamId readonly data: unknown - readonly timestamp: number + readonly createdAt: string } /** Storage service interface - all methods use object params */ -export class Storage extends Effect.Service()("@durable-streams/Storage", { +export class Storage extends Effect.Service()("@event-stream/Storage", { succeed: { /** Append events to a stream, returns created events with offsets */ append: (_opts: { name: StreamName events: ReadonlyArray<{ data: unknown }> - }): Effect.Effect, StorageError> => Effect.succeed([]), + }): Effect.Effect, StorageError> => Effect.succeed([]), /** Get events from offset (inclusive). Offset -1 means from start */ getFrom: (_opts: { name: StreamName offset: Offset limit?: number - }): Effect.Effect, StorageError> => Effect.succeed([]), + }): Effect.Effect, StorageError> => Effect.succeed([]), /** Get all events for a stream */ getAll: (_opts: { name: StreamName - }): Effect.Effect, StorageError> => Effect.succeed([]), + }): Effect.Effect, StorageError> => Effect.succeed([]), /** Check if stream exists */ exists: (_opts: { @@ -68,20 +77,23 @@ export class Storage extends Effect.Service()("@durable-streams/Storage store.set(opts.name, []) } const events = store.get(opts.name)! - const now = Date.now() - const newEvents: Array = opts.events.map((e, i) => - new StreamEvent({ + const createdAt = new Date().toISOString() + const eventStreamId = opts.name as unknown as EventStreamId + const newEvents: Array = opts.events.map((e, i) => + new Event({ offset: makeOffset(events.length + i), + eventStreamId, data: e.data, - timestamp: now + createdAt }) ) store.set(opts.name, [ ...events, ...newEvents.map((e) => ({ offset: e.offset, + eventStreamId: e.eventStreamId, data: e.data, - timestamp: e.timestamp + createdAt: e.createdAt })) ]) return newEvents @@ -92,17 +104,17 @@ export class Storage extends Effect.Service()("@durable-streams/Storage const events = store.get(opts.name) ?? [] if (isStartOffset(opts.offset)) { const limited = opts.limit ? events.slice(0, opts.limit) : events - return limited.map((e) => new StreamEvent(e)) + return limited.map((e) => new Event(e)) } const filtered = events.filter((e) => e.offset >= opts.offset) const limited = opts.limit ? filtered.slice(0, opts.limit) : filtered - return limited.map((e) => new StreamEvent(e)) + return limited.map((e) => new Event(e)) }), getAll: (opts: { name: StreamName }) => Effect.sync(() => { const events = store.get(opts.name) ?? [] - return events.map((e) => new StreamEvent(e)) + return events.map((e) => new Event(e)) }), exists: (opts: { name: StreamName }) => Effect.sync(() => store.has(opts.name)), @@ -149,8 +161,9 @@ export class Storage extends Effect.Service()("@durable-streams/Storage const FileSchema = Schema.Struct({ events: Schema.Array(Schema.Struct({ offset: Schema.String, + eventStreamId: Schema.String, data: Schema.Unknown, - timestamp: Schema.Number + createdAt: Schema.String })) }) @@ -180,8 +193,9 @@ export class Storage extends Effect.Service()("@durable-streams/Storage return decoded.events.map((e) => ({ offset: e.offset as Offset, + eventStreamId: e.eventStreamId as EventStreamId, data: e.data, - timestamp: e.timestamp + createdAt: e.createdAt })) }) @@ -197,7 +211,14 @@ export class Storage extends Effect.Service()("@durable-streams/Storage const filePath = getStreamPath(name) const content = JSON.stringify( - { events: events.map((e) => ({ offset: e.offset, data: e.data, timestamp: e.timestamp })) }, + { + events: events.map((e) => ({ + offset: e.offset, + eventStreamId: e.eventStreamId, + data: e.data, + createdAt: e.createdAt + })) + }, null, 2 ) @@ -211,13 +232,15 @@ export class Storage extends Effect.Service()("@durable-streams/Storage append: (appendOpts: { name: StreamName; events: ReadonlyArray<{ data: unknown }> }) => Effect.gen(function*() { const existing = yield* readStreamFile(appendOpts.name) - const now = Date.now() + const createdAt = new Date().toISOString() + const eventStreamId = appendOpts.name as unknown as EventStreamId - const newEvents: Array = appendOpts.events.map((e, i) => - new StreamEvent({ + const newEvents: Array = appendOpts.events.map((e, i) => + new Event({ offset: makeOffset(existing.length + i), + eventStreamId, data: e.data, - timestamp: now + createdAt }) ) @@ -225,8 +248,9 @@ export class Storage extends Effect.Service()("@durable-streams/Storage ...existing, ...newEvents.map((e) => ({ offset: e.offset, + eventStreamId: e.eventStreamId, data: e.data, - timestamp: e.timestamp + createdAt: e.createdAt })) ] @@ -247,13 +271,13 @@ export class Storage extends Effect.Service()("@durable-streams/Storage } const limited = getFromOpts.limit ? filtered.slice(0, getFromOpts.limit) : filtered - return limited.map((e) => new StreamEvent(e)) + return limited.map((e) => new Event(e)) }), getAll: (getAllOpts: { name: StreamName }) => Effect.gen(function*() { const events = yield* readStreamFile(getAllOpts.name) - return events.map((e) => new StreamEvent(e)) + return events.map((e) => new Event(e)) }), exists: (existsOpts: { name: StreamName }) => diff --git a/src/durable-streams/stream-factory.ts b/src/durable-streams/stream-factory.ts index 59723b0..ec94b57 100644 --- a/src/durable-streams/stream-factory.ts +++ b/src/durable-streams/stream-factory.ts @@ -1,54 +1,54 @@ /** - * DurableStreamFactory - Service that creates DurableStream instances + * EventStreamFactory - Service that creates EventStream instances * * Different factory implementations provide different behaviors: - * - Plain: returns base DurableStream unchanged + * - Plain: returns base EventStream unchanged * - WithHooks: wraps streams with before/after hooks */ import { Effect, Layer } from "effect" import { HookError, type StreamHooks } from "./hooks.ts" import { Storage } from "./storage.ts" -import { type DurableStream, makeDurableStream } from "./stream.ts" +import { type EventStream, makeEventStream } from "./stream.ts" import type { StorageError, StreamName } from "./types.ts" import { withHooks } from "./with-hooks.ts" -/** Factory interface - creates DurableStream instances (Storage already provided) */ -export class DurableStreamFactory extends Effect.Service()( - "@durable-streams/DurableStreamFactory", +/** Factory interface - creates EventStream instances (Storage already provided) */ +export class EventStreamFactory extends Effect.Service()( + "@event-stream/EventStreamFactory", { succeed: { // Note: Layers provide Storage closure, so make() returns Effect without Storage requirement - make: (_opts: { name: StreamName }): Effect.Effect => - Effect.die("DurableStreamFactory.Default not usable - use Plain or WithHooks layer") + make: (_opts: { name: StreamName }): Effect.Effect => + Effect.die("EventStreamFactory.Default not usable - use Plain or WithHooks layer") } } ) { - /** Plain factory - returns base DurableStream unchanged */ - static readonly Plain: Layer.Layer = Layer.effect( - DurableStreamFactory, + /** Plain factory - returns base EventStream unchanged */ + static readonly Plain: Layer.Layer = Layer.effect( + EventStreamFactory, Effect.gen(function*() { const storage = yield* Storage return { - make: (opts: { name: StreamName }) => makeDurableStream(opts).pipe(Effect.provideService(Storage, storage)) - } as DurableStreamFactory + make: (opts: { name: StreamName }) => makeEventStream(opts).pipe(Effect.provideService(Storage, storage)) + } as EventStreamFactory }) ) /** Factory that wraps streams with hooks */ - static WithHooks(hooks: StreamHooks): Layer.Layer { + static WithHooks(hooks: StreamHooks): Layer.Layer { return Layer.effect( - DurableStreamFactory, + EventStreamFactory, Effect.gen(function*() { const storage = yield* Storage return { make: (opts: { name: StreamName }) => - makeDurableStream(opts).pipe( + makeEventStream(opts).pipe( Effect.provideService(Storage, storage), - // withHooks returns HookedDurableStream which is compatible with DurableStream + // withHooks returns HookedEventStream which is compatible with EventStream // (HookError is added to append error channel) - Effect.map((base) => withHooks(base, hooks) as unknown as DurableStream) + Effect.map((base) => withHooks(base, hooks) as unknown as EventStream) ) - } as DurableStreamFactory + } as EventStreamFactory }) ) } @@ -112,9 +112,9 @@ const embryonicAgentHooks: StreamHooks = { } // Variant layers -export const PlainFactory = DurableStreamFactory.Plain -export const ValidatedFactory = DurableStreamFactory.WithHooks(validatedHooks) -export const EmbryonicAgentFactory = DurableStreamFactory.WithHooks(embryonicAgentHooks) +export const PlainFactory = EventStreamFactory.Plain +export const ValidatedFactory = EventStreamFactory.WithHooks(validatedHooks) +export const EmbryonicAgentFactory = EventStreamFactory.WithHooks(embryonicAgentHooks) // ═══════════════════════════════════════════════════════════════════════════════ // CHANGE THIS LINE TO SWAP IMPLEMENTATION diff --git a/src/durable-streams/stream-manager.ts b/src/durable-streams/stream-manager.ts index d908e05..3cd28d5 100644 --- a/src/durable-streams/stream-manager.ts +++ b/src/durable-streams/stream-manager.ts @@ -1,61 +1,66 @@ /** * StreamManager - Layer 4 * - * Manages multiple named DurableStreams with lazy initialization. + * Manages multiple named EventStreams with lazy initialization. * Each stream is created on first access and cached. - * Uses DurableStreamFactory for stream creation (enables hook composition). + * Uses EventStreamFactory for stream creation (enables hook composition). */ -import type { Scope, Stream } from "effect" -import { Effect, HashMap, Layer, Ref } from "effect" +import type { Scope } from "effect" +import { Duration, Effect, Fiber, HashMap, HashSet, Layer, PubSub, Ref, Stream } from "effect" import { Storage } from "./storage.ts" -import { DurableStreamFactory, PlainFactory } from "./stream-factory.ts" -import type { DurableStream } from "./stream.ts" +import { EventStreamFactory, PlainFactory } from "./stream-factory.ts" +import type { EventStream } from "./stream.ts" import { + type Event, type InvalidOffsetError, + makeOffset, type Offset, OFFSET_START, - type StorageError, - type StreamEvent, + StorageError, type StreamName } from "./types.ts" /** StreamManager service interface */ export interface StreamManager { /** Get or create a stream by name */ - getStream(opts: { name: StreamName }): Effect.Effect + getStream(opts: { name: StreamName }): Effect.Effect /** Append to a stream (creates if not exists) */ - append(opts: { name: StreamName; data: unknown }): Effect.Effect + append(opts: { name: StreamName; data: unknown }): Effect.Effect /** Subscribe to a stream (creates if not exists) */ subscribe(opts: { name: StreamName offset?: Offset | undefined - }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> /** Get events from a stream (one-shot, no live subscription) */ getFrom(opts: { name: StreamName offset?: Offset | undefined limit?: number - }): Effect.Effect, InvalidOffsetError | StorageError> + }): Effect.Effect, InvalidOffsetError | StorageError> /** List all stream names */ list(): Effect.Effect, StorageError> /** Delete a stream */ delete(opts: { name: StreamName }): Effect.Effect + + /** Subscribe to ALL streams (live events only, no history). + * Dynamically discovers new streams created after subscription starts. */ + subscribeAll(): Effect.Effect, StorageError, Scope.Scope> } /** Helper to create StreamManager implementation given factory and storage */ const makeStreamManager = Effect.gen(function*() { - const factory = yield* DurableStreamFactory + const factory = yield* EventStreamFactory const storage = yield* Storage // Cache of initialized streams - const streamsRef = yield* Ref.make(HashMap.empty()) + const streamsRef = yield* Ref.make(HashMap.empty()) - const getStream = (opts: { name: StreamName }): Effect.Effect => + const getStream = (opts: { name: StreamName }): Effect.Effect => Effect.gen(function*() { const streams = yield* Ref.get(streamsRef) const existing = HashMap.get(streams, opts.name) @@ -70,7 +75,7 @@ const makeStreamManager = Effect.gen(function*() { return stream }) - const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => + const append = (opts: { name: StreamName; data: unknown }): Effect.Effect => Effect.gen(function*() { const stream = yield* getStream({ name: opts.name }) return yield* stream.append({ data: opts.data }) @@ -79,7 +84,7 @@ const makeStreamManager = Effect.gen(function*() { const subscribe = (opts: { name: StreamName offset?: Offset | undefined - }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + }): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => Effect.gen(function*() { const stream = yield* getStream({ name: opts.name }) return yield* stream.subscribe(opts.offset !== undefined ? { offset: opts.offset } : {}) @@ -89,7 +94,7 @@ const makeStreamManager = Effect.gen(function*() { name: StreamName offset?: Offset | undefined limit?: number - }): Effect.Effect, InvalidOffsetError | StorageError> => + }): Effect.Effect, InvalidOffsetError | StorageError> => Effect.gen(function*() { const stream = yield* getStream({ name: opts.name }) const offset = opts.offset ?? OFFSET_START @@ -106,26 +111,106 @@ const makeStreamManager = Effect.gen(function*() { yield* Ref.update(streamsRef, HashMap.remove(opts.name)) }) + /** Interval between stream discovery polls */ + const DISCOVERY_INTERVAL = Duration.seconds(1) + + const subscribeAll = (): Effect.Effect, StorageError, Scope.Scope> => + Effect.gen(function*() { + // Global PubSub for merged events from all streams + const mergedPubSub = yield* PubSub.unbounded() + + // Track which streams we've already subscribed to + const subscribedStreamsRef = yield* Ref.make(HashSet.empty()) + + // Subscribe to a single stream, forwarding live events to merged PubSub + // For existingStream=true: start from end (skip history) + // For existingStream=false: start from beginning (newly discovered, catch all events) + const subscribeToStream = ( + name: StreamName, + existingStream: boolean + ): Effect.Effect => + Effect.gen(function*() { + const alreadySubscribed = yield* Ref.get(subscribedStreamsRef).pipe( + Effect.map(HashSet.has(name)) + ) + if (alreadySubscribed) return + + yield* Ref.update(subscribedStreamsRef, HashSet.add(name)) + + const stream = yield* getStream({ name }) + + // For existing streams: start from end (no history replay) + // For newly discovered streams: start from beginning (they're all "live" to us) + const startOffset = existingStream + ? makeOffset(yield* stream.count) + : OFFSET_START + + const eventStream = yield* stream.subscribe({ offset: startOffset }).pipe( + Effect.catchTag("InvalidOffsetError", () => Effect.fail(new StorageError({ message: "Invalid offset" }))) + ) + + // Fork a fiber to forward events to merged PubSub + yield* eventStream.pipe( + Stream.runForEach((event) => PubSub.publish(mergedPubSub, event)), + Effect.fork + ) + + // Yield to allow forked fiber to start consuming from stream's PubSub + yield* Effect.yieldNow() + }) + + // Subscribe to all existing streams (existing=true to skip their history) + const existingNames = yield* list() + yield* Effect.forEach(existingNames, (name) => subscribeToStream(name, true), { discard: true }) + + // Background fiber that polls for new streams + const discoveryFiber = yield* Effect.gen(function*() { + while (true) { + yield* Effect.sleep(DISCOVERY_INTERVAL) + const currentNames = yield* list() + const subscribed = yield* Ref.get(subscribedStreamsRef) + + for (const name of currentNames) { + if (!HashSet.has(subscribed, name)) { + // New stream discovered after subscribeAll started - read from beginning + yield* subscribeToStream(name, false) + } + } + } + }).pipe( + Effect.catchAll(() => Effect.void), // Ignore errors in discovery loop + Effect.fork + ) + + // Register finalizer to interrupt discovery fiber when scope closes + yield* Effect.addFinalizer(() => Fiber.interrupt(discoveryFiber)) + + // Return stream from merged PubSub + const dequeue = yield* PubSub.subscribe(mergedPubSub) + return Stream.fromQueue(dequeue) + }) + return { getStream, append, subscribe, getFrom, list, - delete: deleteStream + delete: deleteStream, + subscribeAll } satisfies StreamManager }) /** StreamManager service tag and layer */ export class StreamManagerService extends Effect.Service()( - "@durable-streams/StreamManager", + "@event-stream/StreamManager", { effect: makeStreamManager, - dependencies: [Storage.Default, DurableStreamFactory.Default] + dependencies: [Storage.Default, EventStreamFactory.Default] } ) { - /** Create a StreamManagerService layer - requires Storage and DurableStreamFactory */ - static readonly Live: Layer.Layer = Layer.effect( + /** Create a StreamManagerService layer - requires Storage and EventStreamFactory */ + static readonly Live: Layer.Layer = Layer.effect( StreamManagerService, makeStreamManager as Effect.Effect ) diff --git a/src/durable-streams/stream.test.ts b/src/durable-streams/stream.test.ts index fde7f6d..77b6ec6 100644 --- a/src/durable-streams/stream.test.ts +++ b/src/durable-streams/stream.test.ts @@ -1,19 +1,19 @@ /** - * Tests for DurableStream (Layer 0) + * Tests for EventStream (Layer 0) */ import { describe, expect, it } from "@effect/vitest" import { Effect, Fiber, Queue, Stream } from "effect" import { Storage } from "./storage.ts" -import { makeDurableStream } from "./stream.ts" +import { makeEventStream } from "./stream.ts" import { makeOffset, type Offset, OFFSET_START, type StreamName } from "./types.ts" const testStreamName = "test-stream" as StreamName -describe("DurableStream", () => { +describe("EventStream", () => { describe("append", () => { it.effect("assigns sequential offsets", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) const e1 = yield* stream.append({ data: { msg: "first" } }) const e2 = yield* stream.append({ data: { msg: "second" } }) @@ -24,18 +24,18 @@ describe("DurableStream", () => { it.effect("stores data correctly", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) const event = yield* stream.append({ data: { key: "value" } }) expect(event.data).toEqual({ key: "value" }) - expect(typeof event.timestamp).toBe("number") - expect(event.timestamp).toBeGreaterThan(0) + expect(typeof event.createdAt).toBe("string") + expect(new Date(event.createdAt).toISOString()).toBe(event.createdAt) }).pipe(Effect.provide(Storage.InMemory))) it.effect("increments count", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) expect(yield* stream.count).toBe(0) @@ -50,7 +50,7 @@ describe("DurableStream", () => { describe("subscribe", () => { it.effect("returns historical events", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) yield* stream.append({ data: { msg: "first" } }) yield* stream.append({ data: { msg: "second" } }) @@ -69,7 +69,7 @@ describe("DurableStream", () => { it.effect("receives live events after subscribing", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) const collected = yield* Queue.unbounded() // Subscribe first (no historical events) @@ -103,7 +103,7 @@ describe("DurableStream", () => { it.effect("returns both historical and live events", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) const collected = yield* Queue.unbounded() // Add historical @@ -143,7 +143,7 @@ describe("DurableStream", () => { describe("subscribe with offset", () => { it.effect("returns events from specified offset", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) yield* stream.append({ data: { n: 0 } }) yield* stream.append({ data: { n: 1 } }) @@ -164,7 +164,7 @@ describe("DurableStream", () => { it.effect("handles -1 offset (start from beginning)", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) yield* stream.append({ data: { n: 0 } }) yield* stream.append({ data: { n: 1 } }) @@ -182,7 +182,7 @@ describe("DurableStream", () => { it.effect("fails with invalid offset", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) const result = yield* stream .subscribe({ offset: "invalid" as Offset }) @@ -198,7 +198,7 @@ describe("DurableStream", () => { describe("multiple subscribers", () => { it.effect("all subscribers receive the same events", () => Effect.gen(function*() { - const stream = yield* makeDurableStream({ name: testStreamName }) + const stream = yield* makeEventStream({ name: testStreamName }) const q1 = yield* Queue.unbounded() const q2 = yield* Queue.unbounded() diff --git a/src/durable-streams/stream.ts b/src/durable-streams/stream.ts index 2fc9372..d55f0b0 100644 --- a/src/durable-streams/stream.ts +++ b/src/durable-streams/stream.ts @@ -1,5 +1,5 @@ /** - * DurableStream - Layer 0 core primitive + * EventStream - Layer 0 core primitive * * A single named stream with append and subscribe operations. * Uses PubSub for live subscriptions with historical catch-up. @@ -9,6 +9,8 @@ import { Effect, PubSub, Ref, Stream } from "effect" import { Storage } from "./storage.ts" // eslint-disable-next-line @typescript-eslint/consistent-type-imports import { + Event, + type EventStreamId, InvalidOffsetError, isStartOffset, makeOffset, @@ -16,28 +18,27 @@ import { OFFSET_START, parseOffset, StorageError, - StreamEvent, type StreamName } from "./types.ts" -/** DurableStream interface - all methods use object params */ -export interface DurableStream { +/** EventStream interface - all methods use object params */ +export interface EventStream { readonly name: StreamName /** Append data to stream, returns event with assigned offset */ - append(opts: { data: unknown }): Effect.Effect + append(opts: { data: unknown }): Effect.Effect /** Subscribe to events. Returns historical + live. Use offset to start from position. * Requires Scope - subscription stays active while scope is open. */ subscribe(opts?: { offset?: Offset }): Effect.Effect< - Stream.Stream, + Stream.Stream, InvalidOffsetError | StorageError, Scope.Scope > /** Get events from offset (for historic reads without live subscription) */ getFrom(opts: { offset: Offset; limit?: number }): Effect.Effect< - ReadonlyArray, + ReadonlyArray, InvalidOffsetError | StorageError > @@ -45,10 +46,10 @@ export interface DurableStream { readonly count: Effect.Effect } -/** Create a DurableStream instance for a given name */ -export const makeDurableStream = (opts: { +/** Create an EventStream instance for a given name */ +export const makeEventStream = (opts: { name: StreamName -}): Effect.Effect => +}): Effect.Effect => Effect.gen(function*() { const storage = yield* Storage const { name } = opts @@ -57,22 +58,23 @@ export const makeDurableStream = (opts: { yield* storage.create({ name }) // PubSub for live event fan-out - const pubsub = yield* PubSub.unbounded() + const pubsub = yield* PubSub.unbounded() // Sync offset counter with storage const events = yield* storage.getAll({ name }) const nextOffsetRef = yield* Ref.make(events.length) - const append = (appendOpts: { data: unknown }): Effect.Effect => + const append = (appendOpts: { data: unknown }): Effect.Effect => Effect.gen(function*() { const counter = yield* Ref.getAndUpdate(nextOffsetRef, (n) => n + 1) const offset = makeOffset(counter) - const timestamp = Date.now() + const createdAt = new Date().toISOString() - const event = new StreamEvent({ + const event = new Event({ offset, + eventStreamId: name as unknown as EventStreamId, data: appendOpts.data, - timestamp + createdAt }) // Store the event @@ -86,7 +88,7 @@ export const makeDurableStream = (opts: { const subscribe = ( subscribeOpts?: { offset?: Offset } - ): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => + ): Effect.Effect, InvalidOffsetError | StorageError, Scope.Scope> => Effect.gen(function*() { const offset = subscribeOpts?.offset ?? OFFSET_START @@ -134,7 +136,7 @@ export const makeDurableStream = (opts: { const getFrom = ( getFromOpts: { offset: Offset; limit?: number } - ): Effect.Effect, InvalidOffsetError | StorageError> => + ): Effect.Effect, InvalidOffsetError | StorageError> => Effect.gen(function*() { const { limit, offset } = getFromOpts diff --git a/src/durable-streams/subscribe-all.test.ts b/src/durable-streams/subscribe-all.test.ts new file mode 100644 index 0000000..d29a8ab --- /dev/null +++ b/src/durable-streams/subscribe-all.test.ts @@ -0,0 +1,198 @@ +/** + * E2E tests for subscribeAll functionality + * + * Tests: + * - Receives events from existing streams + * - Receives events from multiple streams + * - Discovers new streams dynamically + * - eventStreamId identifies source stream + * - Only live events (no historical replay) + */ +import { describe, expect, it } from "@effect/vitest" +import { Effect, Fiber, Stream } from "effect" +import { StreamManagerService } from "./stream-manager.ts" +import type { StreamName } from "./types.ts" + +describe("subscribeAll", () => { + // Use it.live for real time tests (subscription and fiber coordination) + it.live("receives events from existing streams", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + // Create a stream first + yield* manager.append({ name: "stream-a" as StreamName, data: "setup" }) + + // Subscribe to all + const allEvents = yield* manager.subscribeAll() + + // Fork consumer + const fiber = yield* allEvents.pipe( + Stream.take(1), + Stream.runCollect, + Effect.fork + ) + + // Small delay to ensure consumer is ready to receive from merged PubSub + yield* Effect.sleep("100 millis") + + // Append to existing stream + yield* manager.append({ name: "stream-a" as StreamName, data: "live-event" }) + + // Get result + const events = yield* Fiber.join(fiber).pipe( + Effect.timeout("5 seconds"), + Effect.orDie + ) + + const eventsArray = Array.from(events) + expect(eventsArray.length).toBe(1) + expect(eventsArray[0]!.data).toBe("live-event") + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) + + it.live("receives events from multiple streams", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + // Create two streams + yield* manager.append({ name: "multi-a" as StreamName, data: "setup-a" }) + yield* manager.append({ name: "multi-b" as StreamName, data: "setup-b" }) + + // Subscribe to all + const allEvents = yield* manager.subscribeAll() + + // Fork consumer for 2 events + const fiber = yield* allEvents.pipe( + Stream.take(2), + Stream.runCollect, + Effect.fork + ) + + // Small delay to ensure subscription is set up + yield* Effect.sleep("50 millis") + + // Append to both streams + yield* manager.append({ name: "multi-a" as StreamName, data: "event-a" }) + yield* manager.append({ name: "multi-b" as StreamName, data: "event-b" }) + + // Get results + const events = yield* Fiber.join(fiber).pipe( + Effect.timeout("5 seconds"), + Effect.orDie + ) + + expect(events.length).toBe(2) + const dataValues = Array.from(events).map((e) => e.data) + expect(dataValues).toContain("event-a") + expect(dataValues).toContain("event-b") + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) + + it.live("eventStreamId identifies source stream", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + // Create streams + yield* manager.append({ name: "id-test-a" as StreamName, data: "setup" }) + yield* manager.append({ name: "id-test-b" as StreamName, data: "setup" }) + + // Subscribe to all + const allEvents = yield* manager.subscribeAll() + + // Fork consumer + const fiber = yield* allEvents.pipe( + Stream.take(2), + Stream.runCollect, + Effect.fork + ) + + // Small delay to ensure subscription is set up + yield* Effect.sleep("50 millis") + + // Append to both streams + yield* manager.append({ name: "id-test-a" as StreamName, data: "from-a" }) + yield* manager.append({ name: "id-test-b" as StreamName, data: "from-b" }) + + const events = yield* Fiber.join(fiber).pipe( + Effect.timeout("5 seconds"), + Effect.orDie + ) + + // Each event should have eventStreamId matching its source stream + const eventsArray = Array.from(events) + const eventFromA = eventsArray.find((e) => e.data === "from-a") + const eventFromB = eventsArray.find((e) => e.data === "from-b") + + expect(eventFromA).toBeDefined() + expect(eventFromB).toBeDefined() + expect(eventFromA!.eventStreamId).toBe("id-test-a") + expect(eventFromB!.eventStreamId).toBe("id-test-b") + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) + + it.live("only live events - no historical replay", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + // Append historical events BEFORE subscribing + yield* manager.append({ name: "history-test" as StreamName, data: "historical-1" }) + yield* manager.append({ name: "history-test" as StreamName, data: "historical-2" }) + + // Subscribe to all + const allEvents = yield* manager.subscribeAll() + + // Fork consumer + const fiber = yield* allEvents.pipe( + Stream.take(1), + Stream.runCollect, + Effect.fork + ) + + // Small delay to ensure subscription is set up + yield* Effect.sleep("100 millis") + + // Append live event AFTER subscribing + yield* manager.append({ name: "history-test" as StreamName, data: "live-event" }) + + const events = yield* Fiber.join(fiber).pipe( + Effect.timeout("5 seconds"), + Effect.orDie + ) + + // Should only receive the live event, not historical + const eventsArray = Array.from(events) + expect(eventsArray.length).toBe(1) + expect(eventsArray[0]!.data).toBe("live-event") + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) + + it.live("discovers new streams dynamically", () => + Effect.gen(function*() { + const manager = yield* StreamManagerService + + // Subscribe to all BEFORE creating any streams + const allEvents = yield* manager.subscribeAll() + + // Fork consumer + const fiber = yield* allEvents.pipe( + Stream.take(1), + Stream.runCollect, + Effect.fork + ) + + // Wait past discovery interval (1 second) to allow initial discovery loop to run + yield* Effect.sleep("1200 millis") + + // Create a NEW stream after subscription - the discovery loop should pick it up + yield* manager.append({ name: "dynamic-stream" as StreamName, data: "discovered" }) + + // Wait for next discovery cycle to subscribe to new stream and forward event + yield* Effect.sleep("1200 millis") + + const events = yield* Fiber.join(fiber).pipe( + Effect.timeout("10 seconds"), + Effect.orDie + ) + + const eventsArray = Array.from(events) + expect(eventsArray.length).toBe(1) + expect(eventsArray[0]!.data).toBe("discovered") + expect(eventsArray[0]!.eventStreamId).toBe("dynamic-stream") + }).pipe(Effect.scoped, Effect.provide(StreamManagerService.InMemory))) +}) diff --git a/src/durable-streams/types.ts b/src/durable-streams/types.ts index 7197a92..5020e61 100644 --- a/src/durable-streams/types.ts +++ b/src/durable-streams/types.ts @@ -1,5 +1,5 @@ /** - * Core types for durable streams + * Core types for event streams */ import { Schema } from "effect" @@ -10,6 +10,12 @@ export const StreamName = Schema.String.pipe( ) export type StreamName = typeof StreamName.Type +export const EventStreamId = Schema.String.pipe( + Schema.nonEmptyString(), + Schema.brand("EventStreamId") +) +export type EventStreamId = typeof EventStreamId.Type + export const Offset = Schema.String.pipe(Schema.brand("Offset")) export type Offset = typeof Offset.Type @@ -18,16 +24,17 @@ export const makeOffset = (n: number): Offset => String(n).padStart(16, "0") as export const parseOffset = (offset: Offset): number => parseInt(offset, 10) -/** Special offset meaning "start from beginning" per durable-streams spec */ +/** Special offset meaning "start from beginning" per event-stream spec */ export const OFFSET_START = "-1" as Offset export const isStartOffset = (offset: Offset): boolean => offset === OFFSET_START // Event schema -export class StreamEvent extends Schema.Class("StreamEvent")({ +export class Event extends Schema.Class("Event")({ offset: Offset, + eventStreamId: EventStreamId, data: Schema.Unknown, - timestamp: Schema.Number + createdAt: Schema.String }) {} // Errors diff --git a/src/durable-streams/with-hooks.test.ts b/src/durable-streams/with-hooks.test.ts index 289f110..48a2f36 100644 --- a/src/durable-streams/with-hooks.test.ts +++ b/src/durable-streams/with-hooks.test.ts @@ -5,7 +5,7 @@ import { describe, expect, it } from "@effect/vitest" import { Effect, Ref } from "effect" import { HookError, type StreamHooks } from "./hooks.ts" import { Storage } from "./storage.ts" -import { makeDurableStream } from "./stream.ts" +import { makeEventStream } from "./stream.ts" import type { StreamName } from "./types.ts" import { withHooks } from "./with-hooks.ts" @@ -13,7 +13,7 @@ describe("withHooks", () => { const testStreamName = "test-stream" as StreamName const makeTestStream = Effect.gen(function*() { - return yield* makeDurableStream({ name: testStreamName }) + return yield* makeEventStream({ name: testStreamName }) }).pipe(Effect.provide(Storage.InMemory)) describe("beforeAppend hooks", () => { diff --git a/src/durable-streams/with-hooks.ts b/src/durable-streams/with-hooks.ts index 927d257..23749f0 100644 --- a/src/durable-streams/with-hooks.ts +++ b/src/durable-streams/with-hooks.ts @@ -1,53 +1,53 @@ /** - * withHooks - Layer 2 wrapper for DurableStream + * withHooks - Layer 2 wrapper for EventStream * - * Pure function that wraps a DurableStream with before/after hooks. + * Pure function that wraps an EventStream with before/after hooks. * No services, no layers - just composition. */ import type { Scope, Stream } from "effect" import { Effect } from "effect" import type { HookError, StreamHooks } from "./hooks.ts" -import type { InvalidOffsetError, Offset, StorageError, StreamEvent, StreamName } from "./types.ts" +import type { Event, InvalidOffsetError, Offset, StorageError, StreamName } from "./types.ts" -/** DurableStream with hooks has HookError in its append error channel */ -export interface HookedDurableStream { +/** EventStream with hooks has HookError in its append error channel */ +export interface HookedEventStream { readonly name: StreamName - append(opts: { data: unknown }): Effect.Effect + append(opts: { data: unknown }): Effect.Effect subscribe: (opts?: { offset?: Offset }) => Effect.Effect< - Stream.Stream, + Stream.Stream, InvalidOffsetError | StorageError, Scope.Scope > getFrom: (opts: { offset: Offset limit?: number - }) => Effect.Effect, InvalidOffsetError | StorageError> + }) => Effect.Effect, InvalidOffsetError | StorageError> readonly count: Effect.Effect } /** - * Wrap a DurableStream with before/after hooks. + * Wrap an EventStream with before/after hooks. * * - Before hooks run sequentially in array order; any failure vetoes append * - After hooks run sequentially after successful append; errors logged, don't fail * - subscribe/getFrom/count pass through unchanged * - * Note: Returns HookedDurableStream which has HookError in the append error channel. + * Note: Returns HookedEventStream which has HookError in the append error channel. */ export const withHooks = ( base: { readonly name: StreamName - append(opts: { data: unknown }): Effect.Effect - subscribe: HookedDurableStream["subscribe"] - getFrom: HookedDurableStream["getFrom"] + append(opts: { data: unknown }): Effect.Effect + subscribe: HookedEventStream["subscribe"] + getFrom: HookedEventStream["getFrom"] readonly count: Effect.Effect }, hooks: StreamHooks -): HookedDurableStream => { +): HookedEventStream => { const { afterAppend = [], beforeAppend = [] } = hooks const append = (opts: { data: unknown }): Effect.Effect< - StreamEvent, + Event, StorageError | HookError > => Effect.gen(function*() { From 2941a187cb36b19350f95e76284dcddd8b5eee8c Mon Sep 17 00:00:00 2001 From: Jonas Templestein <242550+jonastemplestein@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:07:11 +0000 Subject: [PATCH 15/15] Add agent-wrapper with Pi SDK integration and CLI interop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add agent-wrapper package for wrapping Pi coding agent with durable streams - Pi adapter connects Pi SDK sessions to event streams - Use file-based SessionManager.create() for Pi CLI interop - Sessions stored in ~/.pi/agent/sessions/ (Pi's default location) - Use INIT_CWD env var to preserve shell working directory - Add CLI commands: start, prompt, abort, subscribe, list - Add e2e test for full session flow πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- bun.lock | 326 ++++++- package.json | 3 + pnpm-lock.yaml | 1346 +++++++++++++++++++++++++-- src/agent-wrapper/adapter-runner.ts | 159 ++++ src/agent-wrapper/cli.ts | 241 +++++ src/agent-wrapper/e2e-test.ts | 231 +++++ src/agent-wrapper/index.ts | 32 + src/agent-wrapper/main.ts | 40 + src/agent-wrapper/pi-adapter.ts | 210 +++++ src/agent-wrapper/types.ts | 204 ++++ src/durable-streams/client.ts | 20 +- 11 files changed, 2746 insertions(+), 66 deletions(-) create mode 100644 src/agent-wrapper/adapter-runner.ts create mode 100644 src/agent-wrapper/cli.ts create mode 100644 src/agent-wrapper/e2e-test.ts create mode 100644 src/agent-wrapper/index.ts create mode 100644 src/agent-wrapper/main.ts create mode 100644 src/agent-wrapper/pi-adapter.ts create mode 100644 src/agent-wrapper/types.ts diff --git a/bun.lock b/bun.lock index 7cc1453..ca4ae36 100644 --- a/bun.lock +++ b/bun.lock @@ -15,6 +15,7 @@ "@effect/platform-node": "^0.104.0", "@effect/rpc": "^0.72.2", "@effect/rpc-http": "^0.52.4", + "@mariozechner/pi-coding-agent": "^0.38.0", "@opentelemetry/otlp-transformer": "^0.208.0", "@opentelemetry/sdk-trace-base": "^2.2.0", "@opentui/core": "^0.1.55", @@ -22,6 +23,7 @@ "effect": "^3.19.8", "react": "19", "react-dom": "19", + "uuid": "^11.1.0", "yaml": "^2.7.0", }, "devDependencies": { @@ -51,8 +53,14 @@ }, }, "packages": { + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.71.2", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ=="], + "@anthropic-ai/tokenizer": ["@anthropic-ai/tokenizer@0.0.4", "", { "dependencies": { "@types/node": "18.19.130", "tiktoken": "1.0.22" } }, "sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g=="], + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + + "@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="], + "@dimforge/rapier2d-simd-compat": ["@dimforge/rapier2d-simd-compat@0.17.3", "", {}, "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg=="], "@dprint/formatter": ["@dprint/formatter@0.4.1", "", {}, "sha512-IB/GXdlMOvi0UhQQ9mcY15Fxcrc2JPadmo6tqefCNV0bptFq7YBpggzpqYXldBXDa04CbKJ+rDwO2eNRPE2+/g=="], @@ -179,6 +187,8 @@ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "0.17.0", "levn": "0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + "@google/genai": ["@google/genai@1.34.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.24.0" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-vu53UMPvjmb7PGzlYu6Tzxso8Dfhn+a7eQFaS2uNemVtDZKwzSpJ5+ikqBbXplF7RGB1STcVDqCkPvquiwb2sw=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "0.19.1", "@humanwhocodes/retry": "0.4.3" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], @@ -187,6 +197,62 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], + + "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + "@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "3.0.0", "exif-parser": "0.1.12", "file-type": "16.5.4", "mime": "3.0.0" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="], "@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="], @@ -245,6 +311,36 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + "@mariozechner/clipboard": ["@mariozechner/clipboard@0.3.0", "", { "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.0", "@mariozechner/clipboard-darwin-universal": "0.3.0", "@mariozechner/clipboard-darwin-x64": "0.3.0", "@mariozechner/clipboard-linux-arm64-gnu": "0.3.0", "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.0", "@mariozechner/clipboard-linux-x64-gnu": "0.3.0", "@mariozechner/clipboard-linux-x64-musl": "0.3.0", "@mariozechner/clipboard-win32-arm64-msvc": "0.3.0", "@mariozechner/clipboard-win32-x64-msvc": "0.3.0" } }, "sha512-tQrCRAtr58BLmWcvwCqlJo5GJgqBGb3zwOBFFBKCEKvRgD8y/EawhCyXsfOh9XOOde1NTAYsYuYyVOYw2tLnoQ=="], + + "@mariozechner/clipboard-darwin-arm64": ["@mariozechner/clipboard-darwin-arm64@0.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7i4bitLzRSij0fj6q6tPmmf+JrwHqfBsBmf8mOcLVv0LVexD+4gEsyMait4i92exKYmCfna6uHKVS84G4nqehg=="], + + "@mariozechner/clipboard-darwin-universal": ["@mariozechner/clipboard-darwin-universal@0.3.0", "", { "os": "darwin" }, "sha512-FVZLGdIkmvqtPQjD0GQwKLVheL+zV7DjA6I5NcsHGjBeWpG2nACS6COuelNf8ruMoPxJFw7RoB4fjw6mmjT+Nw=="], + + "@mariozechner/clipboard-darwin-x64": ["@mariozechner/clipboard-darwin-x64@0.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-KuurQYEqRhalvBji3CH5xIq1Ts23IgVRE3rjanhqFDI77luOhCnlNbDtqv3No5OxJhEBLykQNrAzfgjqPsPWdA=="], + + "@mariozechner/clipboard-linux-arm64-gnu": ["@mariozechner/clipboard-linux-arm64-gnu@0.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-nWpGMlk43bch7ztGfnALcSi5ZREVziPYzrFKjoJimbwaiULrfY0fGce0gWBynP9ak0nHgDLp0nSa7b4cCl+cIw=="], + + "@mariozechner/clipboard-linux-riscv64-gnu": ["@mariozechner/clipboard-linux-riscv64-gnu@0.3.0", "", { "os": "linux", "cpu": "none" }, "sha512-4BC08CIaOXSSAGRZLEjqJmQfioED8ohAzwt0k2amZPEbH96YKoBNorq5EdwPf5VT+odS0DeyCwhwtxokRLZIvQ=="], + + "@mariozechner/clipboard-linux-x64-gnu": ["@mariozechner/clipboard-linux-x64-gnu@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-GpNY5Y9nOzr0Vt0Qi5U88qwe6piiIHk44kSMexl8ns90LluN5UTNYmyfi7Xq3/lmPZCpnB2xvBTYbsXCxnopIA=="], + + "@mariozechner/clipboard-linux-x64-musl": ["@mariozechner/clipboard-linux-x64-musl@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+PnR48/x9GMY5Kh8BLjzHMx6trOegMtxAuqTM9X/bhV3QuW6sLLd7nojDHSGj/ZueK6i0tcQxvOrgNLozVtNDA=="], + + "@mariozechner/clipboard-win32-arm64-msvc": ["@mariozechner/clipboard-win32-arm64-msvc@0.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-+dy2vZ1Ph4EYj0cotB+bVUVk/uKl2bh9LOp/zlnFqoCCYDN6sm+L0VyIOPPo3hjoEVdGpHe1MUxp3qG/OLwXgg=="], + + "@mariozechner/clipboard-win32-x64-msvc": ["@mariozechner/clipboard-win32-x64-msvc@0.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dfpHrUpKHl7ad3xVGE1+gIN3cEnjjPZa4I0BIYMuj2OKq07Gf1FKTXMypB41rDFv6XNzcfhYQnY+ZNgIu9FB8A=="], + + "@mariozechner/pi-agent-core": ["@mariozechner/pi-agent-core@0.38.0", "", { "dependencies": { "@mariozechner/pi-ai": "^0.38.0", "@mariozechner/pi-tui": "^0.38.0" } }, "sha512-VtX2j0cSefdZ6X+osUZXLp8BRT2ZB6utxl7IWoebRq0iPpJScUGUNB+K0POUduW90MmraNUvFCrKhEZSWffs+g=="], + + "@mariozechner/pi-ai": ["@mariozechner/pi-ai@0.38.0", "", { "dependencies": { "@anthropic-ai/sdk": "0.71.2", "@google/genai": "1.34.0", "@mistralai/mistralai": "1.10.0", "@sinclair/typebox": "^0.34.41", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "chalk": "^5.6.2", "openai": "6.10.0", "partial-json": "^0.1.7", "zod-to-json-schema": "^3.24.6" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-AOH5LIsC6EgaTiYe0er9trZhuba/lk62xDlTxVNxskrF+wiNhuBWue7MQ9BQIyzWDh8sEVvNhnbXIKBX7LYdbw=="], + + "@mariozechner/pi-coding-agent": ["@mariozechner/pi-coding-agent@0.38.0", "", { "dependencies": { "@mariozechner/clipboard": "^0.3.0", "@mariozechner/pi-agent-core": "^0.38.0", "@mariozechner/pi-ai": "^0.38.0", "@mariozechner/pi-tui": "^0.38.0", "chalk": "^5.5.0", "cli-highlight": "^2.1.11", "diff": "^8.0.2", "file-type": "^21.1.1", "glob": "^11.0.3", "jiti": "^2.6.1", "marked": "^15.0.12", "minimatch": "^10.1.1", "proper-lockfile": "^4.1.2", "sharp": "^0.34.2" }, "bin": { "pi": "dist/cli.js" } }, "sha512-fBCgOUSrca/CpU+LPeEl0PJnOPAHlovbsEf3XbQ+MctreC5zMCvD61mdfdeHnuvu/jBer+WVjnGyNy0j0f0Z0Q=="], + + "@mariozechner/pi-tui": ["@mariozechner/pi-tui@0.38.0", "", { "dependencies": { "@types/mime-types": "^2.1.4", "chalk": "^5.5.0", "get-east-asian-width": "^1.3.0", "marked": "^15.0.12", "mime-types": "^3.0.1" } }, "sha512-gMhvh0dQ40kjj7gOOWTkYaD2CTq/omh2bii0w8SUnrRERg/mIj03dCjay6sViG75WdMpoTuDlvQ4wXlG633rpA=="], + + "@mistralai/mistralai": ["@mistralai/mistralai@1.10.0", "", { "dependencies": { "zod": "^3.20.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg=="], + "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], @@ -323,6 +419,8 @@ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], @@ -395,8 +493,12 @@ "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + "@sinclair/typebox": ["@sinclair/typebox@0.34.47", "", {}, "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], + "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], @@ -411,6 +513,8 @@ "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + "@types/mime-types": ["@types/mime-types@2.1.4", "", {}, "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w=="], + "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "3.2.3" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], @@ -495,14 +599,20 @@ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "8.15.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "3.1.3", "fast-json-stable-stringify": "2.1.0", "json-schema-traverse": "0.4.1", "uri-js": "4.4.1" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "any-base": ["any-base@1.1.0", "", {}, "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="], + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "is-array-buffer": "3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], @@ -529,6 +639,8 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + "bmp-ts": ["bmp-ts@1.0.9", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="], "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "1.0.2", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -537,6 +649,8 @@ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "1.5.1", "ieee754": "1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="], "bun-pty": ["bun-pty@0.4.6", "", {}, "sha512-sMAZgcxuCe4A9iPdPNJFiWu3ZVqkxQbVlEXu9llxZoEll6cLVXx0rXUiFrM4WWgQsmacM+5xNvyzgLAH0NAA3A=="], @@ -567,6 +681,10 @@ "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + "cli-highlight": ["cli-highlight@2.1.11", "", { "dependencies": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", "mz": "^2.4.0", "parse5": "^5.1.1", "parse5-htmlparser2-tree-adapter": "^6.0.0", "yargs": "^16.0.0" }, "bin": { "highlight": "bin/highlight" } }, "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg=="], + + "cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -577,6 +695,8 @@ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], @@ -593,7 +713,7 @@ "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "1.1.4", "has-property-descriptors": "1.0.2", "object-keys": "1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], - "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], @@ -601,8 +721,14 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-errors": "1.3.0", "gopd": "1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + "effect": ["effect@3.19.14", "", { "dependencies": { "@standard-schema/spec": "1.1.0", "fast-check": "3.23.2" } }, "sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "1.0.2", "arraybuffer.prototype.slice": "1.0.4", "available-typed-arrays": "1.0.7", "call-bind": "1.0.8", "call-bound": "1.0.4", "data-view-buffer": "1.0.2", "data-view-byte-length": "1.0.2", "data-view-byte-offset": "1.0.1", "es-define-property": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "es-set-tostringtag": "2.1.0", "es-to-primitive": "1.3.0", "function.prototype.name": "1.1.8", "get-intrinsic": "1.3.0", "get-proto": "1.0.1", "get-symbol-description": "1.1.0", "globalthis": "1.0.4", "gopd": "1.2.0", "has-property-descriptors": "1.0.2", "has-proto": "1.2.0", "has-symbols": "1.1.0", "hasown": "2.0.2", "internal-slot": "1.1.0", "is-array-buffer": "3.0.5", "is-callable": "1.2.7", "is-data-view": "1.0.2", "is-negative-zero": "2.0.3", "is-regex": "1.2.1", "is-set": "2.0.3", "is-shared-array-buffer": "1.0.4", "is-string": "1.1.1", "is-typed-array": "1.1.15", "is-weakref": "1.1.1", "math-intrinsics": "1.1.0", "object-inspect": "1.13.4", "object-keys": "1.1.1", "object.assign": "4.1.7", "own-keys": "1.0.1", "regexp.prototype.flags": "1.5.4", "safe-array-concat": "1.1.3", "safe-push-apply": "1.0.0", "safe-regex-test": "1.1.0", "set-proto": "1.0.0", "stop-iteration-iterator": "1.1.0", "string.prototype.trim": "1.2.10", "string.prototype.trimend": "1.0.9", "string.prototype.trimstart": "1.0.8", "typed-array-buffer": "1.0.3", "typed-array-byte-length": "1.0.3", "typed-array-byte-offset": "1.0.4", "typed-array-length": "1.0.7", "unbox-primitive": "1.1.0", "which-typed-array": "1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], @@ -621,6 +747,8 @@ "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "4.9.1", "@eslint-community/regexpp": "4.12.2", "@eslint/config-array": "0.21.1", "@eslint/config-helpers": "0.4.2", "@eslint/core": "0.17.0", "@eslint/eslintrc": "3.3.3", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "0.4.1", "@humanfs/node": "0.16.7", "@humanwhocodes/module-importer": "1.0.1", "@humanwhocodes/retry": "0.4.3", "@types/estree": "1.0.8", "ajv": "6.12.6", "chalk": "4.1.2", "cross-spawn": "7.0.6", "debug": "4.4.3", "escape-string-regexp": "4.0.0", "eslint-scope": "8.4.0", "eslint-visitor-keys": "4.2.1", "espree": "10.4.0", "esquery": "1.7.0", "esutils": "2.0.3", "fast-deep-equal": "3.1.3", "file-entry-cache": "8.0.0", "find-up": "5.0.0", "glob-parent": "6.0.2", "ignore": "5.3.2", "imurmurhash": "0.1.4", "is-glob": "4.0.3", "json-stable-stringify-without-jsonify": "1.0.1", "lodash.merge": "4.6.2", "minimatch": "3.1.2", "natural-compare": "1.4.0", "optionator": "0.9.4" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], @@ -661,6 +789,8 @@ "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -671,11 +801,15 @@ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fdir": ["fdir@6.5.0", "", { "optionalDependencies": { "picomatch": "4.0.3" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "4.0.1" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], - "file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "3.0.4", "strtok3": "6.3.0", "token-types": "4.2.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="], + "file-type": ["file-type@21.3.0", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], @@ -689,6 +823,10 @@ "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -697,8 +835,16 @@ "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + "gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="], + + "gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="], + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-define-property": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "function-bind": "1.1.2", "get-proto": "1.0.1", "gopd": "1.2.0", "has-symbols": "1.1.0", "hasown": "2.0.2", "math-intrinsics": "1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "1.0.1", "es-object-atoms": "1.1.1" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], @@ -711,16 +857,26 @@ "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "4.0.0", "omggif": "1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="], + "glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "1.2.1", "gopd": "1.2.0" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + "google-auth-library": ["google-auth-library@10.5.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="], + + "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "gpt-tokenizer": ["gpt-tokenizer@2.9.0", "", {}, "sha512-YSpexBL/k4bfliAzMrRqn3M6+it02LutVyhVpDeMKrC/O9+pCe/5s8U2hYKa2vFLD5/vHhsKc8sOn/qGqII8Kg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="], + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -735,6 +891,10 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -771,6 +931,8 @@ "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "1.0.4" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "1.0.4", "generator-function": "2.0.1", "get-proto": "1.0.1", "has-tostringtag": "1.0.2", "safe-regex-test": "1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], @@ -805,22 +967,34 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="], + "jimp": ["jimp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/diff": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-gif": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-blur": "1.6.0", "@jimp/plugin-circle": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-contain": "1.6.0", "@jimp/plugin-cover": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-displace": "1.6.0", "@jimp/plugin-dither": "1.6.0", "@jimp/plugin-fisheye": "1.6.0", "@jimp/plugin-flip": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/plugin-mask": "1.6.0", "@jimp/plugin-print": "1.6.0", "@jimp/plugin-quantize": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/plugin-rotate": "1.6.0", "@jimp/plugin-threshold": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg=="], + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="], "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "1.2.8" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], + + "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], @@ -835,18 +1009,28 @@ "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "3.0.3", "picomatch": "2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "1.1.12" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "msgpackr": ["msgpackr@1.11.8", "", { "optionalDependencies": { "msgpackr-extract": "3.0.3" } }, "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA=="], @@ -855,6 +1039,8 @@ "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], @@ -865,10 +1051,16 @@ "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "2.1.2" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], "node-pty": ["node-pty@1.1.0", "", { "dependencies": { "node-addon-api": "7.1.1" } }, "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], @@ -883,6 +1075,8 @@ "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="], + "openai": ["openai@6.10.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "0.1.4", "fast-levenshtein": "2.0.6", "levn": "0.4.1", "prelude-ls": "1.2.1", "type-check": "0.4.0", "word-wrap": "1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "1.3.0", "object-keys": "1.1.1", "safe-push-apply": "1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], @@ -891,6 +1085,8 @@ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "3.1.0" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "3.1.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], @@ -901,12 +1097,20 @@ "parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "1.0.1", "xml2js": "0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="], + "parse5": ["parse5@5.1.1", "", {}, "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="], + + "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@6.0.1", "", { "dependencies": { "parse5": "^6.0.1" } }, "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA=="], + + "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], @@ -933,6 +1137,8 @@ "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "1.1.2", "@protobufjs/base64": "1.1.2", "@protobufjs/codegen": "2.0.4", "@protobufjs/eventemitter": "1.1.0", "@protobufjs/fetch": "1.1.0", "@protobufjs/float": "1.0.2", "@protobufjs/inquire": "1.1.0", "@protobufjs/path": "1.1.2", "@protobufjs/pool": "1.1.0", "@protobufjs/utf8": "1.1.0", "@types/node": "22.19.3", "long": "5.3.2" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], @@ -955,12 +1161,20 @@ "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-errors": "1.3.0", "get-proto": "1.0.1", "gopd": "1.2.0", "set-function-name": "2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], + "rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "2.3.3" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="], "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "get-intrinsic": "1.3.0", "has-symbols": "1.1.0", "isarray": "2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], @@ -983,6 +1197,8 @@ "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -999,6 +1215,8 @@ "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "simple-xml-to-json": ["simple-xml-to-json@1.2.3", "", {}, "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -1013,6 +1231,10 @@ "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "internal-slot": "1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-data-property": "1.1.4", "define-properties": "1.2.1", "es-abstract": "1.24.1", "es-object-atoms": "1.1.1", "has-property-descriptors": "1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], @@ -1023,18 +1245,24 @@ "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "6.2.2" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "0.3.0", "peek-readable": "4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], + "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], + + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + "three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="], "tiktoken": ["tiktoken@1.0.22", "", {}, "sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA=="], @@ -1055,10 +1283,12 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - "token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "0.3.0", "ieee754": "1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="], + "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], + "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], + "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "0.0.29", "json5": "1.0.2", "minimist": "1.2.8", "strip-bom": "3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], @@ -1081,6 +1311,8 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "has-bigints": "1.1.0", "has-symbols": "1.1.0", "which-boxed-primitive": "1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], "undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="], @@ -1101,6 +1333,8 @@ "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "5.2.3", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "5.3.3", "debug": "4.4.3", "expect-type": "1.3.0", "magic-string": "0.30.21", "pathe": "2.0.3", "picomatch": "4.0.3", "std-env": "3.10.0", "tinybench": "2.9.0", "tinyexec": "0.3.2", "tinyglobby": "0.2.15", "tinypool": "1.1.1", "tinyrainbow": "2.0.0", "vite": "7.3.1", "vite-node": "3.2.4", "why-is-node-running": "2.3.0" }, "optionalDependencies": { "@types/node": "22.19.3" }, "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "web-tree-sitter": ["web-tree-sitter@0.25.10", "", {}, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -1117,6 +1351,10 @@ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "ws": ["ws@8.19.0", "", {}, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="], @@ -1125,24 +1363,54 @@ "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + "yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], + + "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + "@anthropic-ai/tokenizer/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "5.26.5" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], "@effect/rpc-http/@effect/rpc": ["@effect/rpc@0.54.4", "", { "peerDependencies": { "@effect/platform": "0.93.8", "effect": "3.19.14" } }, "sha512-iu3TGWCt4OMH8iKL1ATeROhAxrMF+HdF3NbR5lWls9yWJwBgVU+cps3ZzRbNQhFPWXDGqVuYgmYNY1GKbZgMaw=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@jimp/core/file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "3.0.4", "strtok3": "6.3.0", "token-types": "4.2.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="], + + "@mariozechner/pi-ai/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "@mariozechner/pi-ai/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "@mariozechner/pi-coding-agent/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "@mariozechner/pi-coding-agent/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + + "@mariozechner/pi-tui/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "2.0.2" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], @@ -1151,11 +1419,15 @@ "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + "image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="], @@ -1163,8 +1435,52 @@ "react-reconciler/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + + "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@anthropic-ai/tokenizer/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "@jimp/core/file-type/strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "0.3.0", "peek-readable": "4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], + + "@jimp/core/file-type/token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "0.3.0", "ieee754": "1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="], + + "@mariozechner/pi-ai/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "1.0.2" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "rimraf/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "rimraf/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "2.0.2" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "rimraf/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "1.0.2" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "rimraf/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], } } diff --git a/package.json b/package.json index c4394c0..a1b87fc 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "prepare": "effect-language-service patch", "mini-agent": "doppler run -- npx tsx src/cli/main.ts", + "agent-wrapper": "doppler run -- npx tsx src/agent-wrapper/main.ts", "start": "npx tsx src/cli/main.ts", "dev": "npx tsx --watch src/cli/main.ts", "typecheck": "tsc --noEmit", @@ -51,6 +52,7 @@ "@effect/platform-node": "^0.104.0", "@effect/rpc": "^0.72.2", "@effect/rpc-http": "^0.52.4", + "@mariozechner/pi-coding-agent": "^0.38.0", "@opentelemetry/otlp-transformer": "^0.208.0", "@opentelemetry/sdk-trace-base": "^2.2.0", "@opentui/core": "^0.1.55", @@ -58,6 +60,7 @@ "effect": "^3.19.8", "react": "19", "react-dom": "19", + "uuid": "^11.1.0", "yaml": "^2.7.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a081c8c..6f9b6b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@effect/rpc-http': specifier: ^0.52.4 version: 0.52.4(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14) + '@mariozechner/pi-coding-agent': + specifier: ^0.38.0 + version: 0.38.0(ws@8.19.0)(zod@3.25.76) '@opentelemetry/otlp-transformer': specifier: ^0.208.0 version: 0.208.0(@opentelemetry/api@1.9.0) @@ -62,6 +65,9 @@ importers: typescript: specifier: ^5 version: 5.9.3 + uuid: + specifier: ^11.1.0 + version: 11.1.0 yaml: specifier: ^2.7.0 version: 2.8.2 @@ -74,10 +80,10 @@ importers: version: 0.57.1 '@effect/vitest': specifier: ^0.25.1 - version: 0.25.1(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2)) + version: 0.25.1(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) '@eslint/compat': specifier: ^1.1.1 - version: 1.4.1(eslint@9.39.2) + version: 1.4.1(eslint@9.39.2(jiti@2.6.1)) '@eslint/eslintrc': specifier: ^3.1.0 version: 3.3.3 @@ -92,25 +98,25 @@ importers: version: 19.2.7 '@typescript-eslint/eslint-plugin': specifier: ^8.4.0 - version: 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + version: 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^8.4.0 - version: 8.52.0(eslint@9.39.2)(typescript@5.9.3) + version: 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: specifier: ^9.10.0 - version: 9.39.2 + version: 9.39.2(jiti@2.6.1) eslint-import-resolver-typescript: specifier: ^3.6.3 - version: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) + version: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import: specifier: ^2.30.0 - version: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + version: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-simple-import-sort: specifier: ^12.1.1 - version: 12.1.1(eslint@9.39.2) + version: 12.1.1(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-sort-destructure-keys: specifier: ^2.0.0 - version: 2.0.0(eslint@9.39.2) + version: 2.0.0(eslint@9.39.2(jiti@2.6.1)) semver: specifier: ^7.7.3 version: 7.7.3 @@ -122,13 +128,29 @@ importers: version: 0.0.2(@opentui/core@0.1.69(stage-js@1.0.0-alpha.17)(typescript@5.9.3)(web-tree-sitter@0.25.10)) vitest: specifier: ^3.2.0 - version: 3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) packages: + '@anthropic-ai/sdk@0.71.2': + resolution: {integrity: sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + '@anthropic-ai/tokenizer@0.0.4': resolution: {integrity: sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@borewit/text-codec@0.2.1': + resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} + '@dimforge/rapier2d-simd-compat@0.17.3': resolution: {integrity: sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg==} @@ -527,6 +549,15 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@google/genai@1.34.0': + resolution: {integrity: sha512-vu53UMPvjmb7PGzlYu6Tzxso8Dfhn+a7eQFaS2uNemVtDZKwzSpJ5+ikqBbXplF7RGB1STcVDqCkPvquiwb2sw==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.24.0 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -543,6 +574,155 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jimp/core@1.6.0': resolution: {integrity: sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==} engines: {node: '>=18'} @@ -658,6 +838,84 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@mariozechner/clipboard-darwin-arm64@0.3.0': + resolution: {integrity: sha512-7i4bitLzRSij0fj6q6tPmmf+JrwHqfBsBmf8mOcLVv0LVexD+4gEsyMait4i92exKYmCfna6uHKVS84G4nqehg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@mariozechner/clipboard-darwin-universal@0.3.0': + resolution: {integrity: sha512-FVZLGdIkmvqtPQjD0GQwKLVheL+zV7DjA6I5NcsHGjBeWpG2nACS6COuelNf8ruMoPxJFw7RoB4fjw6mmjT+Nw==} + engines: {node: '>= 10'} + os: [darwin] + + '@mariozechner/clipboard-darwin-x64@0.3.0': + resolution: {integrity: sha512-KuurQYEqRhalvBji3CH5xIq1Ts23IgVRE3rjanhqFDI77luOhCnlNbDtqv3No5OxJhEBLykQNrAzfgjqPsPWdA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.0': + resolution: {integrity: sha512-nWpGMlk43bch7ztGfnALcSi5ZREVziPYzrFKjoJimbwaiULrfY0fGce0gWBynP9ak0nHgDLp0nSa7b4cCl+cIw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.0': + resolution: {integrity: sha512-4BC08CIaOXSSAGRZLEjqJmQfioED8ohAzwt0k2amZPEbH96YKoBNorq5EdwPf5VT+odS0DeyCwhwtxokRLZIvQ==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@mariozechner/clipboard-linux-x64-gnu@0.3.0': + resolution: {integrity: sha512-GpNY5Y9nOzr0Vt0Qi5U88qwe6piiIHk44kSMexl8ns90LluN5UTNYmyfi7Xq3/lmPZCpnB2xvBTYbsXCxnopIA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@mariozechner/clipboard-linux-x64-musl@0.3.0': + resolution: {integrity: sha512-+PnR48/x9GMY5Kh8BLjzHMx6trOegMtxAuqTM9X/bhV3QuW6sLLd7nojDHSGj/ZueK6i0tcQxvOrgNLozVtNDA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.0': + resolution: {integrity: sha512-+dy2vZ1Ph4EYj0cotB+bVUVk/uKl2bh9LOp/zlnFqoCCYDN6sm+L0VyIOPPo3hjoEVdGpHe1MUxp3qG/OLwXgg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@mariozechner/clipboard-win32-x64-msvc@0.3.0': + resolution: {integrity: sha512-dfpHrUpKHl7ad3xVGE1+gIN3cEnjjPZa4I0BIYMuj2OKq07Gf1FKTXMypB41rDFv6XNzcfhYQnY+ZNgIu9FB8A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@mariozechner/clipboard@0.3.0': + resolution: {integrity: sha512-tQrCRAtr58BLmWcvwCqlJo5GJgqBGb3zwOBFFBKCEKvRgD8y/EawhCyXsfOh9XOOde1NTAYsYuYyVOYw2tLnoQ==} + engines: {node: '>= 10'} + + '@mariozechner/pi-agent-core@0.38.0': + resolution: {integrity: sha512-VtX2j0cSefdZ6X+osUZXLp8BRT2ZB6utxl7IWoebRq0iPpJScUGUNB+K0POUduW90MmraNUvFCrKhEZSWffs+g==} + engines: {node: '>=20.0.0'} + + '@mariozechner/pi-ai@0.38.0': + resolution: {integrity: sha512-AOH5LIsC6EgaTiYe0er9trZhuba/lk62xDlTxVNxskrF+wiNhuBWue7MQ9BQIyzWDh8sEVvNhnbXIKBX7LYdbw==} + engines: {node: '>=20.0.0'} + hasBin: true + + '@mariozechner/pi-coding-agent@0.38.0': + resolution: {integrity: sha512-fBCgOUSrca/CpU+LPeEl0PJnOPAHlovbsEf3XbQ+MctreC5zMCvD61mdfdeHnuvu/jBer+WVjnGyNy0j0f0Z0Q==} + engines: {node: '>=20.0.0'} + hasBin: true + + '@mariozechner/pi-tui@0.38.0': + resolution: {integrity: sha512-gMhvh0dQ40kjj7gOOWTkYaD2CTq/omh2bii0w8SUnrRERg/mIj03dCjay6sViG75WdMpoTuDlvQ4wXlG633rpA==} + engines: {node: '>=20.0.0'} + + '@mistralai/mistralai@1.10.0': + resolution: {integrity: sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} cpu: [arm64] @@ -867,6 +1125,10 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -1025,9 +1287,16 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@sinclair/typebox@0.34.47': + resolution: {integrity: sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tokenizer/inflate@0.4.1': + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} + engines: {node: '>=18'} + '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} @@ -1049,6 +1318,9 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/mime-types@2.1.4': + resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + '@types/node@16.9.1': resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} @@ -1261,9 +1533,28 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} @@ -1272,9 +1563,16 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + any-base@1.1.0: resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1324,6 +1622,9 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + bmp-ts@1.0.9: resolution: {integrity: sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==} @@ -1337,6 +1638,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -1400,10 +1704,22 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + check-error@2.1.3: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} + cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1421,6 +1737,10 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1486,9 +1806,21 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + effect@3.19.14: resolution: {integrity: sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + es-abstract@1.24.1: resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} @@ -1525,6 +1857,10 @@ packages: engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1647,6 +1983,9 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-check@3.23.2: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} @@ -1663,6 +2002,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1672,6 +2014,10 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1680,6 +2026,10 @@ packages: resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} engines: {node: '>=10'} + file-type@21.3.0: + resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} + engines: {node: '>=20'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1702,6 +2052,14 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1717,10 +2075,26 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + gaxios@7.1.3: + resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} + engines: {node: '>=18'} + + gcp-metadata@8.1.2: + resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} + engines: {node: '>=18'} + generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1751,6 +2125,15 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + hasBin: true + + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + hasBin: true + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1759,6 +2142,14 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + google-auth-library@10.5.0: + resolution: {integrity: sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==} + engines: {node: '>=18'} + + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} + engines: {node: '>=14'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -1766,6 +2157,13 @@ packages: gpt-tokenizer@2.9.0: resolution: {integrity: sha512-YSpexBL/k4bfliAzMrRqn3M6+it02LutVyhVpDeMKrC/O9+pCe/5s8U2hYKa2vFLD5/vHhsKc8sOn/qGqII8Kg==} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + gtoken@8.0.0: + resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} + engines: {node: '>=18'} + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -1793,6 +2191,13 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1866,6 +2271,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -1932,10 +2341,21 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + jimp@1.6.0: resolution: {integrity: sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==} engines: {node: '>=18'} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + jpeg-js@0.4.4: resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} @@ -1946,12 +2366,22 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1959,6 +2389,12 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1982,9 +2418,21 @@ packages: loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -1993,11 +2441,23 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} hasBin: true + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2008,6 +2468,10 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2021,6 +2485,9 @@ packages: multipasta@0.2.7: resolution: {integrity: sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2040,6 +2507,15 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-gyp-build-optional-packages@5.2.2: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true @@ -2047,6 +2523,10 @@ packages: node-pty@1.1.0: resolution: {integrity: sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -2074,6 +2554,18 @@ packages: omggif@1.0.10: resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + openai@6.10.0: + resolution: {integrity: sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2090,6 +2582,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -2106,6 +2601,18 @@ packages: parse-bmfont-xml@1.1.6: resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==} + parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + + parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2117,6 +2624,14 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -2177,6 +2692,9 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + protobufjs@7.5.4: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} @@ -2222,6 +2740,14 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2234,6 +2760,14 @@ packages: engines: {node: '>= 0.4'} hasBin: true + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + rollup@4.55.1: resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2284,6 +2818,10 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2315,6 +2853,13 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-xml-to-json@1.2.3: resolution: {integrity: sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==} engines: {node: '>=20.12.2'} @@ -2340,6 +2885,14 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string.prototype.trim@1.2.10: resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} @@ -2355,6 +2908,10 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + strip-ansi@7.1.2: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} @@ -2370,6 +2927,10 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + strtok3@10.3.4: + resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} + engines: {node: '>=18'} + strtok3@6.3.0: resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} engines: {node: '>=10'} @@ -2382,6 +2943,13 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + three@0.177.0: resolution: {integrity: sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==} @@ -2421,9 +2989,16 @@ packages: resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} engines: {node: '>=10'} + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} + toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -2469,6 +3044,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -2569,6 +3148,10 @@ packages: jsdom: optional: true + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + web-tree-sitter@0.25.10: resolution: {integrity: sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA==} peerDependencies: @@ -2607,6 +3190,14 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} @@ -2642,11 +3233,23 @@ packages: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2654,16 +3257,31 @@ packages: yoga-layout@3.2.1: resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + zod-to-json-schema@3.25.1: + resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + peerDependencies: + zod: ^3.25 || ^4 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: + '@anthropic-ai/sdk@0.71.2(zod@3.25.76)': + dependencies: + json-schema-to-ts: 3.1.1 + optionalDependencies: + zod: 3.25.76 + '@anthropic-ai/tokenizer@0.0.4': dependencies: '@types/node': 18.19.130 tiktoken: 1.0.22 + '@babel/runtime@7.28.4': {} + + '@borewit/text-codec@0.2.1': {} + '@dimforge/rapier2d-simd-compat@0.17.3': optional: true @@ -2822,10 +3440,10 @@ snapshots: dependencies: effect: 3.19.14 - '@effect/vitest@0.25.1(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2))': + '@effect/vitest@0.25.1(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: effect: 3.19.14 - vitest: 3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) '@effect/workflow@0.16.0(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.93.8(effect@3.19.14))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': dependencies: @@ -2928,18 +3546,18 @@ snapshots: '@esbuild/win32-x64@0.27.2': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': dependencies: - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@1.4.1(eslint@9.39.2)': + '@eslint/compat@1.4.1(eslint@9.39.2(jiti@2.6.1))': dependencies: '@eslint/core': 0.17.0 optionalDependencies: - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) '@eslint/config-array@0.21.1': dependencies: @@ -2980,6 +3598,15 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@google/genai@1.34.0': + dependencies: + google-auth-library: 10.5.0 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2991,6 +3618,117 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@img/colour@1.0.0': {} + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.8.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jimp/core@1.6.0': dependencies: '@jimp/file-ops': 1.6.0 @@ -3182,6 +3920,114 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@mariozechner/clipboard-darwin-arm64@0.3.0': + optional: true + + '@mariozechner/clipboard-darwin-universal@0.3.0': + optional: true + + '@mariozechner/clipboard-darwin-x64@0.3.0': + optional: true + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.0': + optional: true + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.0': + optional: true + + '@mariozechner/clipboard-linux-x64-gnu@0.3.0': + optional: true + + '@mariozechner/clipboard-linux-x64-musl@0.3.0': + optional: true + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.0': + optional: true + + '@mariozechner/clipboard-win32-x64-msvc@0.3.0': + optional: true + + '@mariozechner/clipboard@0.3.0': + optionalDependencies: + '@mariozechner/clipboard-darwin-arm64': 0.3.0 + '@mariozechner/clipboard-darwin-universal': 0.3.0 + '@mariozechner/clipboard-darwin-x64': 0.3.0 + '@mariozechner/clipboard-linux-arm64-gnu': 0.3.0 + '@mariozechner/clipboard-linux-riscv64-gnu': 0.3.0 + '@mariozechner/clipboard-linux-x64-gnu': 0.3.0 + '@mariozechner/clipboard-linux-x64-musl': 0.3.0 + '@mariozechner/clipboard-win32-arm64-msvc': 0.3.0 + '@mariozechner/clipboard-win32-x64-msvc': 0.3.0 + + '@mariozechner/pi-agent-core@0.38.0(ws@8.19.0)(zod@3.25.76)': + dependencies: + '@mariozechner/pi-ai': 0.38.0(ws@8.19.0)(zod@3.25.76) + '@mariozechner/pi-tui': 0.38.0 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@mariozechner/pi-ai@0.38.0(ws@8.19.0)(zod@3.25.76)': + dependencies: + '@anthropic-ai/sdk': 0.71.2(zod@3.25.76) + '@google/genai': 1.34.0 + '@mistralai/mistralai': 1.10.0 + '@sinclair/typebox': 0.34.47 + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + chalk: 5.6.2 + openai: 6.10.0(ws@8.19.0)(zod@3.25.76) + partial-json: 0.1.7 + zod-to-json-schema: 3.25.1(zod@3.25.76) + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@mariozechner/pi-coding-agent@0.38.0(ws@8.19.0)(zod@3.25.76)': + dependencies: + '@mariozechner/clipboard': 0.3.0 + '@mariozechner/pi-agent-core': 0.38.0(ws@8.19.0)(zod@3.25.76) + '@mariozechner/pi-ai': 0.38.0(ws@8.19.0)(zod@3.25.76) + '@mariozechner/pi-tui': 0.38.0 + chalk: 5.6.2 + cli-highlight: 2.1.11 + diff: 8.0.2 + file-type: 21.3.0 + glob: 11.1.0 + jiti: 2.6.1 + marked: 15.0.12 + minimatch: 10.1.1 + proper-lockfile: 4.1.2 + sharp: 0.34.5 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@mariozechner/pi-tui@0.38.0': + dependencies: + '@types/mime-types': 2.1.4 + chalk: 5.6.2 + get-east-asian-width: 1.4.0 + marked: 15.0.12 + mime-types: 3.0.2 + + '@mistralai/mistralai@1.10.0': + dependencies: + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': optional: true @@ -3371,6 +4217,9 @@ snapshots: '@parcel/watcher-win32-ia32': 2.5.1 '@parcel/watcher-win32-x64': 2.5.1 + '@pkgjs/parseargs@0.11.0': + optional: true + '@protobufjs/aspromise@1.1.2': {} '@protobufjs/base64@1.1.2': {} @@ -3471,8 +4320,17 @@ snapshots: '@rtsao/scc@1.1.0': {} + '@sinclair/typebox@0.34.47': {} + '@standard-schema/spec@1.1.0': {} + '@tokenizer/inflate@0.4.1': + dependencies: + debug: 4.4.3 + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color + '@tokenizer/token@0.3.0': {} '@tybys/wasm-util@0.10.1': @@ -3493,6 +4351,8 @@ snapshots: '@types/json5@0.0.29': {} + '@types/mime-types@2.1.4': {} + '@types/node@16.9.1': {} '@types/node@18.19.130': @@ -3507,15 +4367,15 @@ snapshots: dependencies: csstype: 3.2.3 - '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.52.0 - '@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.52.0 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -3523,14 +4383,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.52.0 '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.52.0 debug: 4.4.3 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3553,13 +4413,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -3582,13 +4442,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.52.0 '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3665,13 +4525,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@3.2.4': dependencies: @@ -3712,6 +4572,12 @@ snapshots: acorn@8.15.0: {} + agent-base@7.1.4: {} + + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3719,14 +4585,27 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.3: {} + any-base@1.1.0: {} + any-promise@1.3.0: {} + argparse@2.0.1: {} array-buffer-byte-length@1.0.2: @@ -3793,6 +4672,8 @@ snapshots: base64-js@1.5.1: {} + bignumber.js@9.3.1: {} + bmp-ts@1.0.9: {} brace-expansion@1.1.12: @@ -3808,6 +4689,8 @@ snapshots: dependencies: fill-range: 7.1.1 + buffer-equal-constant-time@1.0.1: {} + buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -3876,8 +4759,25 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + check-error@2.1.3: {} + cli-highlight@2.1.11: + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3894,6 +4794,8 @@ snapshots: csstype@3.2.3: {} + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -3938,8 +4840,7 @@ snapshots: detect-libc@1.0.3: {} - detect-libc@2.1.2: - optional: true + detect-libc@2.1.2: {} diff@8.0.2: {} @@ -3953,11 +4854,21 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + effect@3.19.14: dependencies: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 @@ -4071,6 +4982,8 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 + escalade@3.2.0: {} + escape-string-regexp@4.0.0: {} eslint-import-resolver-node@0.3.9: @@ -4081,33 +4994,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) get-tsconfig: 4.13.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) - eslint: 9.39.2 + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -4116,9 +5029,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -4130,19 +5043,19 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.2): + eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.2(jiti@2.6.1)): dependencies: - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-sort-destructure-keys@2.0.0(eslint@9.39.2): + eslint-plugin-sort-destructure-keys@2.0.0(eslint@9.39.2(jiti@2.6.1)): dependencies: - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) natural-compare-lite: 1.4.0 eslint-scope@8.4.0: @@ -4154,9 +5067,9 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.2: + eslint@9.39.2(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 @@ -4190,6 +5103,8 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -4223,6 +5138,8 @@ snapshots: expect-type@1.3.0: {} + extend@3.0.2: {} + fast-check@3.23.2: dependencies: pure-rand: 6.1.0 @@ -4235,10 +5152,17 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4249,6 +5173,15 @@ snapshots: strtok3: 6.3.0 token-types: 4.2.1 + file-type@21.3.0: + dependencies: + '@tokenizer/inflate': 0.4.1 + strtok3: 10.3.4 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4271,6 +5204,15 @@ snapshots: dependencies: is-callable: 1.2.7 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fsevents@2.3.3: optional: true @@ -4287,8 +5229,29 @@ snapshots: functions-have-names@1.2.3: {} + gaxios@7.1.3: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + rimraf: 5.0.10 + transitivePeerDependencies: + - supports-color + + gcp-metadata@8.1.2: + dependencies: + gaxios: 7.1.3 + google-logging-utils: 1.1.3 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + generator-function@2.0.1: {} + get-caller-file@2.0.5: {} + + get-east-asian-width@1.4.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4332,6 +5295,24 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.1.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.1 + globals@14.0.0: {} globalthis@1.0.4: @@ -4339,10 +5320,33 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 + google-auth-library@10.5.0: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 7.1.3 + gcp-metadata: 8.1.2 + google-logging-utils: 1.1.3 + gtoken: 8.0.0 + jws: 4.0.1 + transitivePeerDependencies: + - supports-color + + google-logging-utils@1.1.3: {} + gopd@1.2.0: {} gpt-tokenizer@2.9.0: {} + graceful-fs@4.2.11: {} + + gtoken@8.0.0: + dependencies: + gaxios: 7.1.3 + jws: 4.0.1 + transitivePeerDependencies: + - supports-color + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -4365,6 +5369,15 @@ snapshots: dependencies: function-bind: 1.1.2 + highlight.js@10.7.3: {} + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -4440,6 +5453,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -4506,6 +5521,16 @@ snapshots: isexe@2.0.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + jimp@1.6.0: dependencies: '@jimp/core': 1.6.0 @@ -4536,6 +5561,8 @@ snapshots: '@jimp/types': 1.6.0 '@jimp/utils': 1.6.0 + jiti@2.6.1: {} + jpeg-js@0.4.4: {} js-tokens@9.0.1: {} @@ -4544,16 +5571,38 @@ snapshots: dependencies: argparse: 2.0.1 + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + json-buffer@3.0.1: {} + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.28.4 + ts-algebra: 2.0.0 + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: dependencies: minimist: 1.2.8 + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -4575,10 +5624,16 @@ snapshots: loupe@3.2.1: {} + lru-cache@10.4.3: {} + + lru-cache@11.2.4: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + marked@15.0.12: {} + math-intrinsics@1.1.0: {} micromatch@4.0.8: @@ -4586,8 +5641,18 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mime@3.0.0: {} + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -4598,6 +5663,8 @@ snapshots: minimist@1.2.8: {} + minipass@7.1.2: {} + ms@2.1.3: {} msgpackr-extract@3.0.3: @@ -4618,6 +5685,12 @@ snapshots: multipasta@0.2.7: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} napi-postinstall@0.3.4: {} @@ -4628,6 +5701,14 @@ snapshots: node-addon-api@7.1.1: {} + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc: 2.1.2 @@ -4638,6 +5719,8 @@ snapshots: node-addon-api: 7.1.1 optional: true + object-assign@4.1.1: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -4673,6 +5756,11 @@ snapshots: omggif@1.0.10: {} + openai@6.10.0(ws@8.19.0)(zod@3.25.76): + optionalDependencies: + ws: 8.19.0 + zod: 3.25.76 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -4696,6 +5784,8 @@ snapshots: dependencies: p-limit: 3.1.0 + package-json-from-dist@1.0.1: {} + pako@1.0.11: {} parent-module@1.0.1: @@ -4711,12 +5801,32 @@ snapshots: xml-parse-from-string: 1.0.1 xml2js: 0.5.0 + parse5-htmlparser2-tree-adapter@6.0.1: + dependencies: + parse5: 6.0.1 + + parse5@5.1.1: {} + + parse5@6.0.1: {} + + partial-json@0.1.7: {} + path-exists@4.0.0: {} path-key@3.1.1: {} path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-scurry@2.0.1: + dependencies: + lru-cache: 11.2.4 + minipass: 7.1.2 + pathe@2.0.3: {} pathval@2.0.1: {} @@ -4758,6 +5868,12 @@ snapshots: process@0.11.10: {} + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + protobufjs@7.5.4: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -4829,6 +5945,10 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -4839,6 +5959,12 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + retry@0.12.0: {} + + rimraf@5.0.10: + dependencies: + glob: 10.5.0 + rollup@4.55.1: dependencies: '@types/estree': 1.0.8 @@ -4923,6 +6049,37 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + sharp@0.34.5: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -4961,6 +6118,10 @@ snapshots: siginfo@2.0.0: {} + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + simple-xml-to-json@1.2.3: {} source-map-js@1.2.1: {} @@ -4979,6 +6140,18 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 @@ -5006,6 +6179,10 @@ snapshots: dependencies: safe-buffer: 5.2.1 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-ansi@7.1.2: dependencies: ansi-regex: 6.2.2 @@ -5018,6 +6195,10 @@ snapshots: dependencies: js-tokens: 9.0.1 + strtok3@10.3.4: + dependencies: + '@tokenizer/token': 0.3.0 + strtok3@6.3.0: dependencies: '@tokenizer/token': 0.3.0 @@ -5029,6 +6210,14 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + three@0.177.0: optional: true @@ -5060,8 +6249,16 @@ snapshots: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 + token-types@6.1.2: + dependencies: + '@borewit/text-codec': 0.2.1 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + toml@3.0.0: {} + ts-algebra@2.0.0: {} + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -5131,6 +6328,8 @@ snapshots: typescript@5.9.3: {} + uint8array-extras@1.5.0: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -5178,13 +6377,13 @@ snapshots: uuid@11.1.0: {} - vite-node@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -5199,7 +6398,7 @@ snapshots: - tsx - yaml - vite@7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.1(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -5210,14 +6409,15 @@ snapshots: optionalDependencies: '@types/node': 22.19.3 fsevents: 2.3.3 + jiti: 2.6.1 tsx: 4.21.0 yaml: 2.8.2 - vitest@3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -5235,8 +6435,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@22.19.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.3 @@ -5254,6 +6454,8 @@ snapshots: - tsx - yaml + web-streams-polyfill@3.3.3: {} + web-tree-sitter@0.25.10: {} which-boxed-primitive@1.1.1: @@ -5308,6 +6510,18 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + ws@7.5.10: {} ws@8.19.0: {} @@ -5321,10 +6535,28 @@ snapshots: xmlbuilder@11.0.1: {} + y18n@5.0.8: {} + yaml@2.8.2: {} + yargs-parser@20.2.9: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + yocto-queue@0.1.0: {} yoga-layout@3.2.1: {} + zod-to-json-schema@3.25.1(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod@3.25.76: {} diff --git a/src/agent-wrapper/adapter-runner.ts b/src/agent-wrapper/adapter-runner.ts new file mode 100644 index 0000000..1677853 --- /dev/null +++ b/src/agent-wrapper/adapter-runner.ts @@ -0,0 +1,159 @@ +/** + * Adapter Runner + * + * Orchestrates the lifecycle of agent harness adapters. + * - Ensures stream server is running + * - Creates streams for new agent sessions + * - Starts and manages adapters + */ +import { Console, Deferred, Effect, Fiber, Ref } from "effect" +import { v4 as uuidv4 } from "uuid" +import type { ClientError } from "../durable-streams/client.ts" +import { StreamClientService } from "../durable-streams/client.ts" +import type { StreamName } from "../durable-streams/types.ts" +import { runPiAdapter } from "./pi-adapter.ts" +import { type EventStreamId, makeSessionCreateEvent } from "./types.ts" + +/** + * Info about a running adapter + */ +interface AdapterInfo { + streamName: StreamName + eventStreamId: EventStreamId + harness: "pi" | "claude" | "opencode" | "iterate" + fiber: Fiber.RuntimeFiber + createdAt: Date +} + +/** + * AdapterRunner service + * + * Manages the lifecycle of agent adapters connected to durable streams. + */ +export class AdapterRunnerService extends Effect.Service()( + "@agent-wrapper/AdapterRunner", + { + effect: Effect.gen(function*() { + const client = yield* StreamClientService + const adaptersRef = yield* Ref.make>(new Map()) + + /** + * Start a new Pi agent session. + * + * Creates a new stream, starts the Pi adapter, and sends the session-create action. + * Returns the stream name that clients can subscribe to. + * Session files are stored in Pi's default location (~/.pi/agent/sessions/). + * + * @param options.sessionFile - Specific session file to resume + */ + const startPiSession = ( + options?: { cwd?: string; model?: string; thinkingLevel?: string; sessionFile?: string } + ): Effect.Effect<{ streamName: StreamName; eventStreamId: EventStreamId }, ClientError> => + Effect.gen(function*() { + // Generate unique stream name + const sessionId = uuidv4().slice(0, 8) + const streamName = `pi-${sessionId}` as StreamName + const eventStreamId = streamName as unknown as EventStreamId + + yield* Console.log(`[AdapterRunner] Starting Pi session: ${streamName}`) + + // Create a deferred to wait for adapter to be ready + const adapterReady = yield* Deferred.make() + + // Start the adapter (runs in background) + const fiber = yield* Effect.fork( + runPiAdapter(streamName, eventStreamId, adapterReady).pipe( + Effect.provideService(StreamClientService, client) + ) + ) + + // Track the adapter + yield* Ref.update(adaptersRef, (adapters) => { + const newAdapters = new Map(adapters) + newAdapters.set(streamName, { + streamName, + eventStreamId, + harness: "pi", + fiber, + createdAt: new Date() + }) + return newAdapters + }) + + // Wait for adapter to signal it's ready (subscription is active) + yield* Deferred.await(adapterReady) + + const createEvent = makeSessionCreateEvent( + eventStreamId, + options + ) + + yield* client.append({ + name: streamName, + data: createEvent + }) + + yield* Console.log(`[AdapterRunner] Pi session started: ${streamName}`) + + return { streamName, eventStreamId } + }) + + /** + * Stop an adapter and remove it from tracking. + */ + const stopAdapter = (streamName: StreamName): Effect.Effect => + Effect.gen(function*() { + const adapters = yield* Ref.get(adaptersRef) + const adapter = adapters.get(streamName) + + if (!adapter) { + return false + } + + yield* Console.log(`[AdapterRunner] Stopping adapter: ${streamName}`) + + yield* Fiber.interrupt(adapter.fiber) + + yield* Ref.update(adaptersRef, (current) => { + const newAdapters = new Map(current) + newAdapters.delete(streamName) + return newAdapters + }) + + return true + }) + + /** + * List all running adapters. + */ + const listAdapters = (): Effect.Effect>> => + Effect.gen(function*() { + const adapters = yield* Ref.get(adaptersRef) + return Array.from(adapters.values()).map(({ fiber: _, ...info }) => info) + }) + + /** + * Stop all adapters. + */ + const stopAll = (): Effect.Effect => + Effect.gen(function*() { + const adapters = yield* Ref.get(adaptersRef) + + for (const [streamName, adapter] of adapters) { + yield* Console.log(`[AdapterRunner] Stopping adapter: ${streamName}`) + yield* Fiber.interrupt(adapter.fiber) + } + + yield* Ref.set(adaptersRef, new Map()) + }) + + return { + startPiSession, + stopAdapter, + listAdapters, + stopAll + } + }), + dependencies: [StreamClientService.Default] + } +) {} diff --git a/src/agent-wrapper/cli.ts b/src/agent-wrapper/cli.ts new file mode 100644 index 0000000..56cc825 --- /dev/null +++ b/src/agent-wrapper/cli.ts @@ -0,0 +1,241 @@ +/** + * Agent Wrapper CLI + * + * Commands: + * + * start [--port] [--storage] Start server + create Pi session + * prompt Send prompt to agent + * abort Abort current operation + * subscribe Subscribe to stream events + * list List active agent sessions + */ +import { Args, Command, Options } from "@effect/cli" +import { NodeContext, NodeHttpClient } from "@effect/platform-node" +import { Console, Effect, Layer, Schema, Stream } from "effect" +import { StreamClientService } from "../durable-streams/client.ts" +import { DaemonService } from "../durable-streams/daemon.ts" +import { Event, type StreamName } from "../durable-streams/types.ts" +import { AdapterRunnerService } from "./adapter-runner.ts" +import { type EventStreamId, makeAbortEvent, makePromptEvent } from "./types.ts" + +// ─── Shared Options ───────────────────────────────────────────────────────── + +const portOption = Options.integer("port").pipe( + Options.withAlias("p"), + Options.withDescription("Port for stream server"), + Options.withDefault(3000) +) + +const storageOption = Options.choice("storage", ["memory", "fs"]).pipe( + Options.withDescription("Storage backend: memory (volatile) or fs (persistent)"), + Options.withDefault("fs" as const) +) + +const cwdOption = Options.text("cwd").pipe( + Options.withDescription("Working directory for the agent"), + Options.optional +) + +const sessionFileOption = Options.text("session-file").pipe( + Options.withDescription("Specific session file to resume"), + Options.optional +) + +// ─── Start Command ────────────────────────────────────────────────────────── + +/** start - Start server and create Pi session */ +const startCommand = Command.make( + "start", + { port: portOption, storage: storageOption, cwd: cwdOption, sessionFile: sessionFileOption }, + ({ cwd, port, sessionFile, storage }) => + Effect.gen(function*() { + yield* Console.log(`Starting agent wrapper (port=${port}, storage=${storage})...`) + + // Start daemon if not running + const daemon = yield* DaemonService + const status = yield* daemon.status() + + if (status._tag === "None") { + yield* Console.log("Starting stream server daemon...") + yield* daemon.start({ port, storage }) + yield* Console.log("Daemon started") + } else { + yield* Console.log(`Daemon already running (PID ${status.value})`) + } + + // Start Pi session (uses Pi's default session dir: ~/.pi/agent/sessions/) + const runner = yield* AdapterRunnerService + const sessionOptions: { cwd?: string; sessionFile?: string } = {} + if (cwd._tag === "Some") sessionOptions.cwd = cwd.value + if (sessionFile._tag === "Some") sessionOptions.sessionFile = sessionFile.value + const result = yield* runner.startPiSession(Object.keys(sessionOptions).length > 0 ? sessionOptions : undefined) + + yield* Console.log("") + yield* Console.log("Pi session started!") + yield* Console.log(` Stream: ${result.streamName}`) + yield* Console.log("") + yield* Console.log("Listening for events... (Ctrl+C to stop)") + yield* Console.log("") + + // Subscribe to stream and print events + const client = yield* StreamClientService + const eventStream = yield* client.subscribe({ name: result.streamName }) + + yield* eventStream.pipe( + Stream.runForEach((event) => + Effect.gen(function*() { + const encoded = Schema.encodeSync(Event)(event) + yield* Console.log(JSON.stringify(encoded, null, 2)) + }) + ) + ) + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e}`)) + ) +).pipe(Command.withDescription("Start server and create a new Pi agent session")) + +// ─── Prompt Command ───────────────────────────────────────────────────────── + +const streamNameArg = Args.text({ name: "stream-name" }).pipe( + Args.withDescription("Name of the stream (e.g., pi-abc12345)") +) + +const messageArg = Args.text({ name: "message" }).pipe( + Args.withDescription("Message to send to the agent") +) + +/** prompt - Send prompt to agent */ +const promptCommand = Command.make( + "prompt", + { streamName: streamNameArg, message: messageArg }, + ({ message, streamName }) => + Effect.gen(function*() { + const client = yield* StreamClientService + + yield* Console.log(`Sending prompt to ${streamName}...`) + + const event = makePromptEvent( + streamName as unknown as EventStreamId, + message + ) + + yield* client.append({ + name: streamName as StreamName, + data: event + }) + + yield* Console.log("Prompt sent!") + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e}`)) + ) +).pipe(Command.withDescription("Send prompt to agent")) + +// ─── Abort Command ────────────────────────────────────────────────────────── + +/** abort - Abort current operation */ +const abortCommand = Command.make( + "abort", + { streamName: streamNameArg }, + ({ streamName }) => + Effect.gen(function*() { + const client = yield* StreamClientService + + yield* Console.log(`Sending abort to ${streamName}...`) + + const event = makeAbortEvent( + streamName as unknown as EventStreamId + ) + + yield* client.append({ + name: streamName as StreamName, + data: event + }) + + yield* Console.log("Abort sent!") + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e}`)) + ) +).pipe(Command.withDescription("Abort current agent operation")) + +// ─── Subscribe Command ────────────────────────────────────────────────────── + +/** subscribe - Subscribe to stream events */ +const subscribeCommand = Command.make( + "subscribe", + { streamName: streamNameArg }, + ({ streamName }) => + Effect.gen(function*() { + const client = yield* StreamClientService + + yield* Console.log(`Subscribing to ${streamName}...`) + yield* Console.log("") + + const eventStream = yield* client.subscribe({ name: streamName as StreamName }) + + yield* eventStream.pipe( + Stream.runForEach((event) => + Effect.gen(function*() { + const encoded = Schema.encodeSync(Event)(event) + yield* Console.log(JSON.stringify(encoded, null, 2)) + }) + ) + ) + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e}`)) + ) +).pipe(Command.withDescription("Subscribe to stream events")) + +// ─── List Command ─────────────────────────────────────────────────────────── + +/** list - List active sessions */ +const listCommand = Command.make( + "list", + {}, + () => + Effect.gen(function*() { + const runner = yield* AdapterRunnerService + const adapters = yield* runner.listAdapters() + + if (adapters.length === 0) { + yield* Console.log("No active sessions") + } else { + yield* Console.log("Active sessions:") + for (const adapter of adapters) { + yield* Console.log( + ` ${adapter.streamName} (${adapter.harness}) - created ${adapter.createdAt.toISOString()}` + ) + } + } + }).pipe( + Effect.catchAll((e) => Console.error(`Error: ${e}`)) + ) +).pipe(Command.withDescription("List active agent sessions")) + +// ─── Root Command ─────────────────────────────────────────────────────────── + +const rootCommand = Command.make("agent-wrapper").pipe( + Command.withSubcommands([ + startCommand, + promptCommand, + abortCommand, + subscribeCommand, + listCommand + ]), + Command.withDescription("Agent wrapper for Pi coding agent") +) + +/** Main CLI definition */ +export const cli = Command.run(rootCommand, { + name: "agent-wrapper", + version: "0.1.0" +}) + +/** Service layer for CLI */ +export const cliLayer = Layer.mergeAll( + DaemonService.Default, + NodeContext.layer, + NodeHttpClient.layer +) + +/** Run CLI with provided args */ +export const run = (args: ReadonlyArray) => cli(args) diff --git a/src/agent-wrapper/e2e-test.ts b/src/agent-wrapper/e2e-test.ts new file mode 100644 index 0000000..904e2a4 --- /dev/null +++ b/src/agent-wrapper/e2e-test.ts @@ -0,0 +1,231 @@ +#!/usr/bin/env npx tsx +/* eslint-disable no-console, no-empty */ +/** + * E2E test script for agent-wrapper + * + * Tests the full flow: + * 1. Start agent-wrapper (daemon + Pi session) + * 2. Send a prompt + * 3. Verify events are processed correctly + */ +import { spawn } from "node:child_process" +import { existsSync, readFileSync, rmSync } from "node:fs" +import { setTimeout } from "node:timers/promises" + +const DATA_DIR = ".iterate" +const TIMEOUT_MS = 30000 + +interface StreamFile { + events: Array<{ + offset: string + eventStreamId: string + data: { + type: string + payload?: unknown + } + }> +} + +async function cleanup() { + console.log("🧹 Cleaning up...") + + // Kill any processes on port 3000 + try { + const lsof = spawn("lsof", ["-t", "-i", ":3000"]) + const pids: Array = [] + lsof.stdout.on("data", (data) => pids.push(data.toString().trim())) + await new Promise((resolve) => lsof.on("close", resolve)) + for (const pid of pids.filter(Boolean)) { + try { + process.kill(parseInt(pid, 10), "SIGKILL") + } catch {} + } + } catch {} + + // Remove data files + try { + rmSync(`${DATA_DIR}/daemon.pid`, { force: true }) + rmSync(`${DATA_DIR}/daemon.port`, { force: true }) + rmSync(`${DATA_DIR}/daemon.log`, { force: true }) + rmSync(`${DATA_DIR}/streams`, { recursive: true, force: true }) + } catch {} + + await setTimeout(1000) +} + +async function runCommand( + cmd: string, + args: Array, + options?: { timeout?: number } +): Promise<{ stdout: string; stderr: string; code: number | null }> { + return new Promise((resolve) => { + // Run as a single shell command to handle argument quoting properly + const fullCmd = [cmd, ...args].join(" ") + const proc = spawn("sh", ["-c", fullCmd]) + let stdout = "" + let stderr = "" + + proc.stdout.on("data", (data) => { + stdout += data.toString() + }) + proc.stderr.on("data", (data) => { + stderr += data.toString() + }) + + const timeout = options?.timeout ?? 10000 + const timer = globalThis.setTimeout(() => { + proc.kill("SIGKILL") + }, timeout) + + proc.on("close", (code) => { + clearTimeout(timer) + resolve({ stdout, stderr, code }) + }) + }) +} + +function getStreamName(output: string): string | null { + const match = output.match(/Stream: (pi-[a-f0-9]+)/) + return match?.[1] ?? null +} + +function readStreamFile(streamName: string): StreamFile | null { + const path = `${DATA_DIR}/streams/${streamName}.json` + if (!existsSync(path)) return null + try { + return JSON.parse(readFileSync(path, "utf-8")) as StreamFile + } catch { + return null + } +} + +async function main() { + console.log("πŸ§ͺ Agent Wrapper E2E Test\n") + + // Cleanup before test + await cleanup() + + // Start agent-wrapper in background + console.log("πŸ“¦ Starting agent-wrapper...") + const wrapper = spawn("pnpm", ["agent-wrapper", "start"], { + shell: true, + stdio: ["ignore", "pipe", "pipe"] + }) + + let wrapperOutput = "" + wrapper.stdout.on("data", (data) => { + const text = data.toString() + wrapperOutput += text + process.stdout.write(text) + }) + wrapper.stderr.on("data", (data) => { + const text = data.toString() + if (!text.includes("npm warn")) { + wrapperOutput += text + process.stderr.write(text) + } + }) + + // Wait for session to start + const startTime = Date.now() + let streamName: string | null = null + while (Date.now() - startTime < TIMEOUT_MS) { + streamName = getStreamName(wrapperOutput) + if (streamName && wrapperOutput.includes("[Pi Adapter] Session created")) { + break + } + await setTimeout(500) + } + + if (!streamName) { + console.error("\n❌ FAIL: Could not find stream name in output") + wrapper.kill("SIGKILL") + await cleanup() + process.exit(1) + } + + console.log(`\nβœ… Session started: ${streamName}`) + + // Verify session-create event in stream + await setTimeout(1000) + const streamBefore = readStreamFile(streamName) + if (!streamBefore || streamBefore.events.length === 0) { + console.error("❌ FAIL: No events in stream file") + wrapper.kill("SIGKILL") + await cleanup() + process.exit(1) + } + + const sessionCreateEvent = streamBefore.events[0] + if (!sessionCreateEvent?.data.type.includes("session-create")) { + console.error("❌ FAIL: First event is not session-create") + wrapper.kill("SIGKILL") + await cleanup() + process.exit(1) + } + + console.log("βœ… Session-create event stored") + + // Send a prompt + console.log("\nπŸ“€ Sending prompt...") + const promptResult = await runCommand("pnpm", ["agent-wrapper", "prompt", streamName, "'hello world'"], { + timeout: 15000 + }) + + if (promptResult.code !== 0) { + console.error(`❌ FAIL: Prompt command failed with code ${promptResult.code}`) + console.error(promptResult.stderr) + wrapper.kill("SIGKILL") + await cleanup() + process.exit(1) + } + + console.log("βœ… Prompt sent") + + // Wait for events to be processed + await setTimeout(2000) + + // Verify prompt event in stream + const streamAfter = readStreamFile(streamName) + if (!streamAfter) { + console.error("❌ FAIL: Could not read stream file after prompt") + wrapper.kill("SIGKILL") + await cleanup() + process.exit(1) + } + + const promptEvent = streamAfter.events.find((e) => e.data.type.includes("prompt:called")) + if (!promptEvent) { + console.error("❌ FAIL: Prompt event not found in stream") + console.error("Events:", JSON.stringify(streamAfter.events.map((e) => e.data.type), null, 2)) + wrapper.kill("SIGKILL") + await cleanup() + process.exit(1) + } + + console.log("βœ… Prompt event stored") + + // Check if adapter processed the prompt (look for "Sending prompt" in output) + if (wrapperOutput.includes("[Pi Adapter] Sending prompt:")) { + console.log("βœ… Adapter processed prompt") + } else if (wrapperOutput.includes("[Pi Adapter] No session")) { + console.error("❌ FAIL: Adapter has no session - race condition!") + wrapper.kill("SIGKILL") + await cleanup() + process.exit(1) + } else { + console.log("⚠️ Could not verify adapter processed prompt (may be waiting for Pi SDK)") + } + + // Cleanup + console.log("\n🧹 Cleaning up...") + wrapper.kill("SIGKILL") + await cleanup() + + console.log("\nβœ… All E2E tests passed!\n") +} + +main().catch((err) => { + console.error("Fatal error:", err) + process.exit(1) +}) diff --git a/src/agent-wrapper/index.ts b/src/agent-wrapper/index.ts new file mode 100644 index 0000000..e730e88 --- /dev/null +++ b/src/agent-wrapper/index.ts @@ -0,0 +1,32 @@ +/** + * Agent Wrapper public exports + */ + +// Types +export { + AgentActionTypes, + EventStreamId, + IterateEventEnvelope, + makeAbortEvent, + makeIterateEvent, + makePiEventReceivedEvent, + makePromptEvent, + makeSessionCreateEvent, + PiEventReceivedEvent, + PiEventReceivedPayload, + PiEventTypes, + PiIterateEvent, + PromptEvent, + PromptPayload, + SessionCreateEvent, + SessionCreatePayload +} from "./types.ts" + +// Adapter +export { runPiAdapter } from "./pi-adapter.ts" + +// Runner service +export { AdapterRunnerService } from "./adapter-runner.ts" + +// CLI +export { cli, run } from "./cli.ts" diff --git a/src/agent-wrapper/main.ts b/src/agent-wrapper/main.ts new file mode 100644 index 0000000..2770d6e --- /dev/null +++ b/src/agent-wrapper/main.ts @@ -0,0 +1,40 @@ +/** + * Agent Wrapper CLI Entry Point + * + * Usage: + * npx tsx src/agent-wrapper/main.ts start [--port 3000] + * npx tsx src/agent-wrapper/main.ts prompt "message" + * npx tsx src/agent-wrapper/main.ts subscribe + * npx tsx src/agent-wrapper/main.ts list + */ +import { NodeContext, NodeHttpClient, NodeRuntime } from "@effect/platform-node" +import { Cause, Effect, Layer, Logger, LogLevel } from "effect" +import { StreamClientLive } from "../durable-streams/client.ts" +import { DaemonService } from "../durable-streams/daemon.ts" +import { AdapterRunnerService } from "./adapter-runner.ts" +import { run } from "./cli.ts" + +const loggingLayer = Logger.minimumLogLevel(LogLevel.Info) + +// Build the layer stack +// DaemonService needs FileSystem + Path (from NodeContext) +// StreamClientLive needs HttpClient + DaemonService +// AdapterRunnerService needs StreamClientService +const baseLayer = StreamClientLive.pipe( + Layer.provideMerge(DaemonService.Live), + Layer.provideMerge(NodeHttpClient.layer), + Layer.provideMerge(NodeContext.layer) +) + +const servicesLayer = Layer.provideMerge( + AdapterRunnerService.Default, + baseLayer +) + +const mainLayer = Layer.mergeAll(loggingLayer, servicesLayer) + +run(process.argv).pipe( + Effect.provide(mainLayer), + Effect.catchAllCause((cause) => Cause.isInterruptedOnly(cause) ? Effect.void : Effect.failCause(cause)), + NodeRuntime.runMain +) diff --git a/src/agent-wrapper/pi-adapter.ts b/src/agent-wrapper/pi-adapter.ts new file mode 100644 index 0000000..1993a0b --- /dev/null +++ b/src/agent-wrapper/pi-adapter.ts @@ -0,0 +1,210 @@ +/** + * Pi Harness Adapter + * + * Connects a Pi coding agent session to a durable event stream. + * - Subscribes to stream for action events (prompt, abort) + * - Calls Pi SDK methods in response + * - Wraps Pi SDK events and appends to stream + */ +import type { + AgentSession, + AgentSessionEvent, + SessionManager as SessionManagerType +} from "@mariozechner/pi-coding-agent" +import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@mariozechner/pi-coding-agent" +import { Console, Deferred, Effect, Fiber, Queue, Stream } from "effect" +import type { ClientError } from "../durable-streams/client.ts" +import { StreamClientService } from "../durable-streams/client.ts" +import type { StreamName } from "../durable-streams/types.ts" +import { type EventStreamId, makePiEventReceivedEvent, PiEventTypes, type SessionCreatePayload } from "./types.ts" + +/** + * State of the Pi adapter + */ +interface PiAdapterState { + session: AgentSession | null + sessionManager: SessionManagerType | null + eventUnsubscribe: (() => void) | null +} + +/** + * Create and run a Pi adapter for a given stream. + * + * The adapter: + * 1. Subscribes to the stream for action events + * 2. Creates a Pi session when session-create action is received + * 3. Forwards prompts to the Pi session + * 4. Wraps Pi events and appends them to the stream + * + * @param readyDeferred - Optional deferred to signal when subscription is consuming events + */ +export const runPiAdapter = ( + streamName: StreamName, + eventStreamId: EventStreamId, + readyDeferred?: Deferred.Deferred +): Effect.Effect => + Effect.gen(function*() { + const client = yield* StreamClientService + + const state: PiAdapterState = { + session: null, + sessionManager: null, + eventUnsubscribe: null + } + + // Queue for Pi events to append to stream + const piEventQueue = yield* Queue.unbounded() + + // Fiber to process Pi events and append to stream + const appendFiber = yield* Effect.fork( + Stream.fromQueue(piEventQueue).pipe( + Stream.runForEach((piEvent) => + Effect.gen(function*() { + const wrappedEvent = makePiEventReceivedEvent( + eventStreamId, + piEvent.type, + piEvent + ) + yield* client.append({ + name: streamName, + data: wrappedEvent + }) + }) + ) + ) + ) + + /** + * Subscribe to Pi session events and forward to queue + */ + const subscribeToPiEvents = (session: AgentSession): void => { + if (state.eventUnsubscribe) { + state.eventUnsubscribe() + } + + state.eventUnsubscribe = session.subscribe((event) => { + // Fire-and-forget: queue the event for async processing + Effect.runFork(Queue.offer(piEventQueue, event)) + }) + } + + /** + * Handle session create action + */ + const handleSessionCreate = (payload: SessionCreatePayload): Effect.Effect => + Effect.gen(function*() { + yield* Console.log("[Pi Adapter] Creating session...") + + const authStorage = discoverAuthStorage() + const modelRegistry = discoverModels(authStorage) + // Use INIT_CWD (set by pnpm to original shell dir) if available, else process.cwd() + const cwd = payload.cwd ?? process.env.INIT_CWD ?? process.cwd() + + // Use file-based session (same behavior as pi CLI) + const sessionManager = payload.sessionFile + ? SessionManager.open(payload.sessionFile) + : SessionManager.create(cwd) + + const { session } = yield* Effect.promise(() => + createAgentSession({ + sessionManager, + authStorage, + modelRegistry, + cwd + }) + ) + + state.session = session + state.sessionManager = sessionManager + subscribeToPiEvents(session) + + const sessionFile = sessionManager.getSessionFile() + yield* Console.log(`[Pi Adapter] Session created${sessionFile ? ` (file: ${sessionFile})` : " (in-memory)"}`) + }) + + /** + * Handle prompt action + */ + const handlePrompt = (content: string): Effect.Effect => + Effect.gen(function*() { + if (!state.session) { + yield* Console.error("[Pi Adapter] No session - ignoring prompt") + return + } + + yield* Console.log(`[Pi Adapter] Sending prompt: ${content.slice(0, 50)}...`) + + yield* Effect.promise(() => state.session!.prompt(content)) + + yield* Console.log("[Pi Adapter] Prompt completed") + }) + + /** + * Handle abort action + */ + const handleAbort = (): Effect.Effect => + Effect.gen(function*() { + if (!state.session) { + yield* Console.error("[Pi Adapter] No session - ignoring abort") + return + } + + yield* Console.log("[Pi Adapter] Aborting...") + + yield* Effect.promise(() => state.session!.abort()) + + yield* Console.log("[Pi Adapter] Aborted") + }) + + // Subscribe to stream and handle action events + yield* Console.log(`[Pi Adapter] Subscribing to stream: ${streamName}`) + + const eventStream = yield* client.subscribe({ name: streamName }) + + // Process events in a forked fiber + const processFiber = yield* Effect.fork( + eventStream.pipe( + Stream.runForEach((event) => + Effect.gen(function*() { + const data = event.data as { type?: string; payload?: unknown } | null + + if (!data || typeof data.type !== "string") { + return + } + + switch (data.type) { + case PiEventTypes.SESSION_CREATE: + yield* handleSessionCreate(data.payload as SessionCreatePayload) + break + + case PiEventTypes.PROMPT: + yield* handlePrompt((data.payload as { content: string }).content) + break + + case PiEventTypes.ABORT: + yield* handleAbort() + break + } + }) + ) + ) + ) + + // Yield to scheduler to let the forked fiber start executing + yield* Effect.yieldNow() + + // Signal ready after forking - the fiber has started and will begin the HTTP request + if (readyDeferred) { + yield* Deferred.succeed(readyDeferred, undefined) + } + yield* Console.log(`[Pi Adapter] Subscription active, waiting for events...`) + + // Wait for the processing fiber to complete (or be interrupted) + yield* Fiber.join(processFiber) + + // Cleanup + if (state.eventUnsubscribe) { + state.eventUnsubscribe() + } + yield* Fiber.interrupt(appendFiber) + }) diff --git a/src/agent-wrapper/types.ts b/src/agent-wrapper/types.ts new file mode 100644 index 0000000..2eae965 --- /dev/null +++ b/src/agent-wrapper/types.ts @@ -0,0 +1,204 @@ +/** + * Event types for the agent wrapper architecture. + * + * Events follow the Iterate envelope format with verbatim harness payloads. + * See src/architecture-sketch.md for the full design. + */ +import { Schema } from "effect" +import { EventStreamId } from "../durable-streams/types.ts" + +// Re-export for convenience +export { EventStreamId } from "../durable-streams/types.ts" + +/** + * Base envelope for all Iterate events. + * + * Protocol fields (offset) are assigned by durable-streams. + * Envelope fields are always present. Type-specific payload varies. + */ +export class IterateEventEnvelope extends Schema.Class("IterateEventEnvelope")({ + type: Schema.String, + version: Schema.Number, + createdAt: Schema.String, + eventStreamId: EventStreamId, + payload: Schema.optional(Schema.Unknown), + metadata: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown })) +}) {} + +// Event type constants +export const PiEventTypes = { + // Action events (requesting side effects) + SESSION_CREATE: "iterate:agent:harness:pi:action:session-create:called", + PROMPT: "iterate:agent:harness:pi:action:prompt:called", + ABORT: "iterate:agent:harness:pi:action:abort:called", + + // Wrapped harness events (verbatim payload) + EVENT_RECEIVED: "iterate:agent:harness:pi:event-received" +} as const + +// Generic action event for sending user messages (harness-agnostic) +export const AgentActionTypes = { + SEND_USER_MESSAGE: "iterate:agent:action:send-user-message:called" +} as const + +/** + * Payload schemas for action events + */ +export class SessionCreatePayload extends Schema.Class("SessionCreatePayload")({ + cwd: Schema.optional(Schema.String), + model: Schema.optional(Schema.String), + thinkingLevel: Schema.optional(Schema.String), + sessionFile: Schema.optional(Schema.String) +}) {} + +export class PromptPayload extends Schema.Class("PromptPayload")({ + content: Schema.String +}) {} + +export class AbortPayload extends Schema.Class("AbortPayload")({}) {} + +/** + * Wrapped Pi SDK event payload. + * The `piEventType` field captures the original Pi event type. + * The `piEvent` field contains the verbatim Pi SDK event. + */ +export class PiEventReceivedPayload extends Schema.Class("PiEventReceivedPayload")({ + piEventType: Schema.String, + piEvent: Schema.Unknown +}) {} + +/** + * Typed action events + */ +export class SessionCreateEvent extends Schema.Class("SessionCreateEvent")({ + type: Schema.Literal(PiEventTypes.SESSION_CREATE), + version: Schema.Number, + createdAt: Schema.String, + eventStreamId: EventStreamId, + payload: SessionCreatePayload, + metadata: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown })) +}) {} + +export class PromptEvent extends Schema.Class("PromptEvent")({ + type: Schema.Literal(PiEventTypes.PROMPT), + version: Schema.Number, + createdAt: Schema.String, + eventStreamId: EventStreamId, + payload: PromptPayload, + metadata: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown })) +}) {} + +export class AbortEvent extends Schema.Class("AbortEvent")({ + type: Schema.Literal(PiEventTypes.ABORT), + version: Schema.Number, + createdAt: Schema.String, + eventStreamId: EventStreamId, + payload: AbortPayload, + metadata: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown })) +}) {} + +export class PiEventReceivedEvent extends Schema.Class("PiEventReceivedEvent")({ + type: Schema.Literal(PiEventTypes.EVENT_RECEIVED), + version: Schema.Number, + createdAt: Schema.String, + eventStreamId: EventStreamId, + payload: PiEventReceivedPayload, + metadata: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown })) +}) {} + +/** + * Union of all Pi-related events + */ +export const PiIterateEvent = Schema.Union( + SessionCreateEvent, + PromptEvent, + AbortEvent, + PiEventReceivedEvent +) +export type PiIterateEvent = typeof PiIterateEvent.Type + +/** + * Helper to create events with current timestamp + */ +export const makeIterateEvent = ( + eventStreamId: EventStreamId, + type: T["type"], + payload?: unknown, + metadata?: Record +): IterateEventEnvelope => + new IterateEventEnvelope({ + type, + version: 1, + createdAt: new Date().toISOString(), + eventStreamId, + payload, + metadata + }) + +/** + * Helper to create a session create event + */ +export const makeSessionCreateEvent = ( + eventStreamId: EventStreamId, + options?: { cwd?: string; model?: string; thinkingLevel?: string; sessionFile?: string } +): SessionCreateEvent => { + // Build payload without undefined values (Schema.optional doesn't accept undefined) + const payloadFields: { cwd?: string; model?: string; thinkingLevel?: string; sessionFile?: string } = {} + if (options?.cwd !== undefined) payloadFields.cwd = options.cwd + if (options?.model !== undefined) payloadFields.model = options.model + if (options?.thinkingLevel !== undefined) payloadFields.thinkingLevel = options.thinkingLevel + if (options?.sessionFile !== undefined) payloadFields.sessionFile = options.sessionFile + + return new SessionCreateEvent({ + type: PiEventTypes.SESSION_CREATE, + version: 1, + createdAt: new Date().toISOString(), + eventStreamId, + payload: new SessionCreatePayload(payloadFields) + }) +} + +/** + * Helper to create a prompt event + */ +export const makePromptEvent = ( + eventStreamId: EventStreamId, + content: string +): PromptEvent => + new PromptEvent({ + type: PiEventTypes.PROMPT, + version: 1, + createdAt: new Date().toISOString(), + eventStreamId, + payload: new PromptPayload({ content }) + }) + +/** + * Helper to create an abort event + */ +export const makeAbortEvent = ( + eventStreamId: EventStreamId +): AbortEvent => + new AbortEvent({ + type: PiEventTypes.ABORT, + version: 1, + createdAt: new Date().toISOString(), + eventStreamId, + payload: new AbortPayload({}) + }) + +/** + * Helper to wrap a Pi SDK event + */ +export const makePiEventReceivedEvent = ( + eventStreamId: EventStreamId, + piEventType: string, + piEvent: unknown +): PiEventReceivedEvent => + new PiEventReceivedEvent({ + type: PiEventTypes.EVENT_RECEIVED, + version: 1, + createdAt: new Date().toISOString(), + eventStreamId, + payload: new PiEventReceivedPayload({ piEventType, piEvent }) + }) diff --git a/src/durable-streams/client.ts b/src/durable-streams/client.ts index 779e684..413c74d 100644 --- a/src/durable-streams/client.ts +++ b/src/durable-streams/client.ts @@ -282,13 +282,19 @@ export class StreamClientService extends Effect.Service()( const resolveServerUrl: Effect.Effect = Effect.gen(function*() { // Check env var first const envUrl = process.env.EVENT_STREAM_URL - if (envUrl) return envUrl + if (envUrl) { + yield* waitForServerReady(envUrl, httpClient) + return envUrl + } // Check if daemon is running const daemonUrl = yield* daemon.getServerUrl().pipe( Effect.catchAll(() => Effect.succeed(Option.none())) ) - if (Option.isSome(daemonUrl)) return daemonUrl.value + if (Option.isSome(daemonUrl)) { + yield* waitForServerReady(daemonUrl.value, httpClient) + return daemonUrl.value + } // Auto-start daemon yield* Effect.log("No server found, starting daemon...") @@ -338,12 +344,18 @@ export const StreamClientLive: Layer.Layer = Effect.gen(function*() { const envUrl = process.env.EVENT_STREAM_URL - if (envUrl) return envUrl + if (envUrl) { + yield* waitForServerReady(envUrl, httpClient) + return envUrl + } const daemonUrl = yield* daemon.getServerUrl().pipe( Effect.catchAll(() => Effect.succeed(Option.none())) ) - if (Option.isSome(daemonUrl)) return daemonUrl.value + if (Option.isSome(daemonUrl)) { + yield* waitForServerReady(daemonUrl.value, httpClient) + return daemonUrl.value + } yield* Effect.log("No server found, starting daemon...") const pid = yield* daemon.start().pipe(