@usine/engine 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -2
- package/dist/cursor/model.d.ts +5 -0
- package/dist/cursor/model.d.ts.map +1 -0
- package/dist/cursor/model.js +4 -0
- package/dist/cursor/model.js.map +1 -0
- package/dist/cursor/store.d.ts +18 -0
- package/dist/cursor/store.d.ts.map +1 -0
- package/dist/cursor/store.js +54 -0
- package/dist/cursor/store.js.map +1 -0
- package/dist/database/database.d.ts +15 -0
- package/dist/database/database.d.ts.map +1 -0
- package/dist/database/database.js +5 -0
- package/dist/database/database.js.map +1 -0
- package/dist/database/local-sqlite.d.ts +17 -0
- package/dist/database/local-sqlite.d.ts.map +1 -0
- package/dist/database/local-sqlite.js +45 -0
- package/dist/database/local-sqlite.js.map +1 -0
- package/dist/database/schema.d.ts +1096 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +171 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/engine.d.ts +39 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +179 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +16 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/item-projection/model.d.ts +21 -0
- package/dist/item-projection/model.d.ts.map +1 -0
- package/dist/item-projection/model.js +16 -0
- package/dist/item-projection/model.js.map +1 -0
- package/dist/item-projection/service.d.ts +25 -0
- package/dist/item-projection/service.d.ts.map +1 -0
- package/dist/item-projection/service.js +98 -0
- package/dist/item-projection/service.js.map +1 -0
- package/dist/loop-control.d.ts +16 -0
- package/dist/loop-control.d.ts.map +1 -0
- package/dist/loop-control.js +69 -0
- package/dist/loop-control.js.map +1 -0
- package/dist/model.d.ts +185 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +174 -0
- package/dist/model.js.map +1 -0
- package/dist/repository-label-projection/model.d.ts +10 -0
- package/dist/repository-label-projection/model.d.ts.map +1 -0
- package/dist/repository-label-projection/model.js +11 -0
- package/dist/repository-label-projection/model.js.map +1 -0
- package/dist/repository-label-projection/service.d.ts +20 -0
- package/dist/repository-label-projection/service.d.ts.map +1 -0
- package/dist/repository-label-projection/service.js +73 -0
- package/dist/repository-label-projection/service.js.map +1 -0
- package/dist/run-reader.d.ts +20 -0
- package/dist/run-reader.d.ts.map +1 -0
- package/dist/run-reader.js +19 -0
- package/dist/run-reader.js.map +1 -0
- package/dist/step-executor.d.ts +21 -0
- package/dist/step-executor.d.ts.map +1 -0
- package/dist/step-executor.js +8 -0
- package/dist/step-executor.js.map +1 -0
- package/dist/store/codec.d.ts +431 -0
- package/dist/store/codec.d.ts.map +1 -0
- package/dist/store/codec.js +140 -0
- package/dist/store/codec.js.map +1 -0
- package/dist/store/queries.d.ts +75 -0
- package/dist/store/queries.d.ts.map +1 -0
- package/dist/store/queries.js +508 -0
- package/dist/store/queries.js.map +1 -0
- package/dist/store/store.d.ts +83 -0
- package/dist/store/store.d.ts.map +1 -0
- package/dist/store/store.js +374 -0
- package/dist/store/store.js.map +1 -0
- package/dist/workflow.d.ts +14 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +105 -0
- package/dist/workflow.js.map +1 -0
- package/drizzle/20260825055549_initial_runtime/migration.sql +73 -0
- package/drizzle/20260825055549_initial_runtime/snapshot.json +592 -0
- package/drizzle/20260830191125_polling_cursors/migration.sql +5 -0
- package/drizzle/20260830191125_polling_cursors/snapshot.json +629 -0
- package/drizzle/20260831073158_attempt_channel/migration.sql +36 -0
- package/drizzle/20260831073158_attempt_channel/snapshot.json +845 -0
- package/drizzle/20260831133534_agent_context/migration.sql +49 -0
- package/drizzle/20260831133534_agent_context/snapshot.json +897 -0
- package/drizzle/20260831134612_attempt_brain/migration.sql +26 -0
- package/drizzle/20260831134612_attempt_brain/snapshot.json +923 -0
- package/drizzle/20260831194605_loop_runtime/migration.sql +4 -0
- package/drizzle/20260831194605_loop_runtime/snapshot.json +954 -0
- package/drizzle/20260901073233_projection_revision/migration.sql +55 -0
- package/drizzle/20260901073233_projection_revision/snapshot.json +991 -0
- package/drizzle/20260901180433_item_projections/migration.sql +22 -0
- package/drizzle/20260901180433_item_projections/snapshot.json +1110 -0
- package/drizzle/20260901185146_repository_label_projections/migration.sql +17 -0
- package/drizzle/20260901185146_repository_label_projections/snapshot.json +1147 -0
- package/package.json +21 -3
package/README.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# @usine/engine
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The durable run engine for [usine](https://github.com/basaltbytes/usine). It turns a synthetic Event into a Run with one named Step and a durable Attempt history.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The default adapter stores usine's Event ledger and Run projections beside Effect's workflow tables in `<instance>/.usine/runtime.sqlite`. Submitting the same dedup key joins the original Run, including after a restart.
|
|
6
|
+
|
|
7
|
+
`CursorStore` keeps typed keys with JSON values in that database. Trigger adapters use it for efficiency-only cursors such as GitHub watermarks and ETags; deleting a cursor causes a safe replay because the Event ledger remains the source of deduplication.
|
|
8
|
+
|
|
9
|
+
`LoopControl` keeps runtime pause state in the same database. Pausing or unpausing a Loop never edits its authored Markdown file. `RunReader.list()` returns lightweight durable Run summaries newest first for observe-and-intervene surfaces, without loading Event payloads or issuing one snapshot read per Run. `RunReader.revision()` lets callers reuse an unchanged projection without rescanning history.
|
|
10
|
+
|
|
11
|
+
`ItemProjectionReader` and `ItemProjectionWriter` store the latest issue and pull-request snapshots already fetched by trigger polling. Board consumers can read that projection after a restart without making a second GitHub query; `ItemProjectionReader.list` returns the newest Items first, bounded by the caller's `limit`. Item writes update the shared projection revision only when the content hash changes.
|
|
12
|
+
|
|
13
|
+
`RepositoryLabelProjectionReader` and `RepositoryLabelProjectionWriter` store the complete label catalog observed during repository polling. Repositories with no Items can still receive proposed Board lanes, and the catalog remains available after a restart. Catalog writes update the projection revision only when the label set changes.
|
|
14
|
+
|
|
15
|
+
## Runtime rules
|
|
16
|
+
|
|
17
|
+
- Approval-gated Runs wait for an approval Event before their first Attempt.
|
|
18
|
+
- Infrastructure failures retry automatically, with a cap of three Attempts. Agent and runaway failures wait for an operator retry.
|
|
19
|
+
- A newer Event supersedes a pending or waiting Run. A running Attempt finishes first, then the engine opens only the newest queued Event for that Item.
|
|
20
|
+
- Revoke stops pending and waiting Runs. Operator cancellation can also stop a running Attempt.
|
|
21
|
+
- The startup sweep rejoins open workflow executions. If the process stopped during an Attempt, the engine records that Attempt as an infrastructure failure and resumes as N+1.
|
|
22
|
+
- A crash inside the `StepExecutor` adapter is recorded as an infrastructure failure like any other. If the engine itself hits an invariant defect, the workflow suspends for inspection; `RunEngine.reconcile` (or a restart) repairs the projection.
|
|
23
|
+
- Attempt channel envelopes are stored in an append-only transcript. Normalized parent and child session events retain their session identity. `RunEngine.transcript` accepts distinct `TranscriptAllQuery`, `TranscriptTailQuery`, and `TranscriptPageQuery` variants, so full diagnostics, bounded live tails, and bounded backward pages cannot be confused. Tail and page queries require a positive row limit. A protocol-v3 `Started` frame writes the effective brain and SDK-selected parent model onto the Attempt.
|
|
24
|
+
- The submitted agent, rendered task, payload snapshot, effective parent model, and per-Attempt child brain survive restarts in the run ledger.
|
|
25
|
+
|
|
26
|
+
## Composition
|
|
27
|
+
|
|
28
|
+
The root entry (`@usine/engine`) is platform-free; the Node local adapter lives behind `@usine/engine/local-sqlite`. Its `layer` supplies local SQLite, Effect `SingleRunner`, workflow registration, and startup reconciliation. Provide a `StepExecutor` implementation at the composition root:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { Effect, Layer } from "effect"
|
|
32
|
+
import { StepResult, Worker } from "@usine/core"
|
|
33
|
+
import { CursorStore, InstanceDirectory, LoopControl, RunEngine, StepExecutor } from "@usine/engine"
|
|
34
|
+
import { layer } from "@usine/engine/local-sqlite"
|
|
35
|
+
|
|
36
|
+
const StepExecutorLive = Layer.succeed(StepExecutor)({
|
|
37
|
+
reserve: () => Effect.succeed({
|
|
38
|
+
worker: Worker.Name.make("local"),
|
|
39
|
+
release: Effect.void,
|
|
40
|
+
}),
|
|
41
|
+
execute: ({ event }) => Effect.succeed(new StepResult.StepResult({
|
|
42
|
+
message: `Handled ${event.id}`,
|
|
43
|
+
facts: [],
|
|
44
|
+
})),
|
|
45
|
+
interrupt: () => Effect.void,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const EngineLive = layer(InstanceDirectory.make("/path/to/instance")).pipe(
|
|
49
|
+
Layer.provide(StepExecutorLive),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const program = Effect.gen(function* () {
|
|
53
|
+
const engine = yield* RunEngine
|
|
54
|
+
const cursors = yield* CursorStore
|
|
55
|
+
const loops = yield* LoopControl
|
|
56
|
+
// submit, approve, retry, cancel, revoke, inspect, reconcile, read a transcript,
|
|
57
|
+
// list runs, pause/unpause loops, or read and write efficiency-only cursors
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`layerWithDatabase` (from the root entry) accepts another SQLite-compatible database layer. That layer must provide both `RunDatabase` and the matching Effect `SqlClient`, preserving atomic transactions and keeping the run projections beside Effect's cluster tables; the deployment also supplies its platform `Crypto` layer, which the cluster uses to hash over-length deduplication keys. Cloudflare D1 is not supported by Drizzle RC.4 for this use yet because its Effect driver cannot execute the store's transactions.
|
|
62
|
+
|
|
63
|
+
The Drizzle schema is the source of truth for usine tables. After changing it, generate and review a migration with:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pnpm --filter @usine/engine db:generate --name <migration_name>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Everything is 0.x, so APIs may change. Supply `effect` at the version pinned by the usine workspace.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/cursor/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,6DAA6D;AAC7D,eAAO,MAAM,SAAS,kDAAwD,CAAC;AAC/E,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/cursor/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option, Schema } from "effect";
|
|
2
|
+
import { RunDatabase } from "../database/database.ts";
|
|
3
|
+
import { EnginePersistenceError } from "../model.ts";
|
|
4
|
+
import { type CursorKey } from "./model.ts";
|
|
5
|
+
declare const CursorStore_base: Context.ServiceClass<CursorStore, "@usine/engine/CursorStore", {
|
|
6
|
+
readonly read: <T, E extends Schema.Json>(key: CursorKey, schema: Schema.Codec<T, E, never, never>) => Effect.Effect<Option.Option<T>, EnginePersistenceError>;
|
|
7
|
+
readonly write: <T, E extends Schema.Json>(key: CursorKey, schema: Schema.Codec<T, E, never, never>, value: T) => Effect.Effect<void, EnginePersistenceError>;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Durable storage for efficiency-only cursors. The module that owns a key also
|
|
11
|
+
* owns its schema and passes it per call; a stored value that no longer decodes
|
|
12
|
+
* is treated as absent — losing a cursor is always safe — and logged.
|
|
13
|
+
*/
|
|
14
|
+
export declare class CursorStore extends CursorStore_base {
|
|
15
|
+
static readonly layer: Layer.Layer<CursorStore, never, RunDatabase>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cursor/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;;mBAkBzB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,EACtC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KACrC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,sBAAsB,CAAC;oBAC5C,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,EACvC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,EACxC,KAAK,EAAE,CAAC,KACL,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,sBAAsB,CAAC;;AAhBpD;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,gBAaD;IAC9B,MAAM,CAAC,QAAQ,CAAC,KAAK,+CAqDnB;CACH"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option, Schema } from "effect";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { RunDatabase } from "../database/database.js";
|
|
4
|
+
import { cursors } from "../database/schema.js";
|
|
5
|
+
import { EnginePersistenceError } from "../model.js";
|
|
6
|
+
import {} from "./model.js";
|
|
7
|
+
const CursorRow = Schema.Struct({ value: Schema.Json });
|
|
8
|
+
const decodeCursorRows = Schema.decodeUnknownEffect(Schema.Array(CursorRow));
|
|
9
|
+
const persistence = (operation) => (effect) => Effect.mapError(effect, (cause) => new EnginePersistenceError({ operation, cause }));
|
|
10
|
+
/**
|
|
11
|
+
* Durable storage for efficiency-only cursors. The module that owns a key also
|
|
12
|
+
* owns its schema and passes it per call; a stored value that no longer decodes
|
|
13
|
+
* is treated as absent — losing a cursor is always safe — and logged.
|
|
14
|
+
*/
|
|
15
|
+
export class CursorStore extends Context.Service()("@usine/engine/CursorStore") {
|
|
16
|
+
static layer = Layer.effect(CursorStore, Effect.gen(function* () {
|
|
17
|
+
const database = yield* RunDatabase;
|
|
18
|
+
return {
|
|
19
|
+
read: Effect.fn("CursorStore.read")(function* (key, schema) {
|
|
20
|
+
const rows = yield* Effect.gen(function* () {
|
|
21
|
+
const selected = yield* database
|
|
22
|
+
.select({ value: cursors.valueJson })
|
|
23
|
+
.from(cursors)
|
|
24
|
+
.where(eq(cursors.cursorKey, key))
|
|
25
|
+
.limit(1)
|
|
26
|
+
.execute();
|
|
27
|
+
return yield* decodeCursorRows(selected);
|
|
28
|
+
}).pipe(persistence("read runtime cursor"));
|
|
29
|
+
const stored = Option.fromNullishOr(rows[0]);
|
|
30
|
+
if (Option.isNone(stored))
|
|
31
|
+
return Option.none();
|
|
32
|
+
return yield* Schema.decodeUnknownEffect(schema)(stored.value.value, {
|
|
33
|
+
onExcessProperty: "error",
|
|
34
|
+
}).pipe(Effect.map(Option.some), Effect.catch((error) => Effect.logWarning("Stored cursor no longer decodes; treating it as absent", {
|
|
35
|
+
key,
|
|
36
|
+
detail: error.message,
|
|
37
|
+
}).pipe(Effect.as(Option.none()))));
|
|
38
|
+
}),
|
|
39
|
+
write: Effect.fn("CursorStore.write")(function* (key, schema, value) {
|
|
40
|
+
const encoded = yield* Schema.encodeEffect(schema)(value).pipe(persistence("encode runtime cursor"));
|
|
41
|
+
yield* database
|
|
42
|
+
.insert(cursors)
|
|
43
|
+
.values({ cursorKey: key, valueJson: encoded })
|
|
44
|
+
.onConflictDoUpdate({
|
|
45
|
+
target: cursors.cursorKey,
|
|
46
|
+
set: { valueJson: encoded },
|
|
47
|
+
})
|
|
48
|
+
.execute()
|
|
49
|
+
.pipe(Effect.asVoid, persistence("write runtime cursor"));
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cursor/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EffectDrizzleQueryError, QueryEffectHKTBase } from "drizzle-orm/effect-core";
|
|
2
|
+
import type { SQLiteEffectDatabase } from "drizzle-orm/sqlite-core/effect";
|
|
3
|
+
import { Context, Layer } from "effect";
|
|
4
|
+
interface RunDatabaseEffect extends QueryEffectHKTBase {
|
|
5
|
+
readonly context: never;
|
|
6
|
+
readonly error: EffectDrizzleQueryError;
|
|
7
|
+
}
|
|
8
|
+
/** Driver-neutral Effect database used by the run projection store. */
|
|
9
|
+
export type RunDatabaseClient = SQLiteEffectDatabase<RunDatabaseEffect, unknown>;
|
|
10
|
+
declare const RunDatabase_base: Context.ServiceClass<RunDatabase, "@usine/engine/internal/RunDatabase", RunDatabaseClient>;
|
|
11
|
+
export declare class RunDatabase extends RunDatabase_base {
|
|
12
|
+
static readonly layer: (database: RunDatabaseClient) => Layer.Layer<RunDatabase, never, never>;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/database/database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAExC,UAAU,iBAAkB,SAAQ,kBAAkB;IACpD,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;CACzC;AAED,uEAAuE;AACvE,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;;AAEjF,qBAAa,WAAY,SAAQ,gBAEhC;IACC,MAAM,CAAC,QAAQ,CAAC,KAAK,aAAc,iBAAiB,4CAA0C;CAC/F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/database/database.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAUxC,MAAM,OAAO,WAAY,SAAQ,OAAO,CAAC,OAAO,EAAkC,CAChF,oCAAoC,CACrC;IACC,MAAM,CAAU,KAAK,GAAG,CAAC,QAA2B,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC;CAC/F"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SqliteClient } from "@effect/sql-sqlite-node";
|
|
2
|
+
import { Layer } from "effect";
|
|
3
|
+
import { CursorStore } from "../cursor/store.ts";
|
|
4
|
+
import { ItemProjectionReader, ItemProjectionWriter } from "../item-projection/service.ts";
|
|
5
|
+
import { RepositoryLabelProjectionReader, RepositoryLabelProjectionWriter } from "../repository-label-projection/service.ts";
|
|
6
|
+
import { LoopControl } from "../loop-control.ts";
|
|
7
|
+
import { type RunEngine } from "../engine.ts";
|
|
8
|
+
import { EnginePersistenceError, type InstanceDirectory, type RunEngineError } from "../model.ts";
|
|
9
|
+
import { RunReader } from "../run-reader.ts";
|
|
10
|
+
import type { StepExecutor } from "../step-executor.ts";
|
|
11
|
+
import { RunDatabase } from "./database.ts";
|
|
12
|
+
export declare const localSqliteLayer: (instanceDirectory: InstanceDirectory) => Layer.Layer<RunDatabase | import("effect/unstable/sql/SqlClient").SqlClient | SqliteClient.SqliteClient, EnginePersistenceError, never>;
|
|
13
|
+
/** Local-first runtime backed by `.usine/runtime.sqlite`. */
|
|
14
|
+
export declare const layer: (instanceDirectory: InstanceDirectory) => Layer.Layer<CursorStore | ItemProjectionReader | ItemProjectionWriter | LoopControl | RepositoryLabelProjectionReader | RepositoryLabelProjectionWriter | RunEngine | RunReader, RunEngineError, StepExecutor>;
|
|
15
|
+
/** Read-only local projection; it never reconciles workflows or starts Attempts. */
|
|
16
|
+
export declare const observationLayer: (instanceDirectory: InstanceDirectory) => Layer.Layer<ItemProjectionReader | LoopControl | RepositoryLabelProjectionReader | RunReader, RunEngineError>;
|
|
17
|
+
//# sourceMappingURL=local-sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-sqlite.d.ts","sourceRoot":"","sources":["../../src/database/local-sqlite.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAKvD,OAAO,EAAsB,KAAK,EAAQ,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC3F,OAAO,EACL,+BAA+B,EAC/B,+BAA+B,EAChC,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAqB5C,eAAO,MAAM,gBAAgB,sBAAuB,iBAAiB,4IAepE,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,KAAK,sBACG,iBAAiB,KACnC,KAAK,CAAC,KAAK,CACV,WAAW,GACX,oBAAoB,GACpB,oBAAoB,GACpB,WAAW,GACX,+BAA+B,GAC/B,+BAA+B,GAC/B,SAAS,GACT,SAAS,EACX,cAAc,EACd,YAAY,CACqF,CAAC;AAEpG,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,sBACR,iBAAiB,KACnC,KAAK,CAAC,KAAK,CACZ,oBAAoB,GAAG,WAAW,GAAG,+BAA+B,GAAG,SAAS,EAChF,cAAc,CASf,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NodeServices } from "@effect/platform-node";
|
|
2
|
+
import { SqliteClient } from "@effect/sql-sqlite-node";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import * as SQLiteDrizzle from "drizzle-orm/effect-sqlite-node";
|
|
5
|
+
import { migrate } from "drizzle-orm/effect-sqlite-node/migrator";
|
|
6
|
+
import { sql } from "drizzle-orm";
|
|
7
|
+
import { Effect, FileSystem, Layer, Path } from "effect";
|
|
8
|
+
import { CursorStore } from "../cursor/store.js";
|
|
9
|
+
import { ItemProjectionReader, ItemProjectionWriter } from "../item-projection/service.js";
|
|
10
|
+
import { RepositoryLabelProjectionReader, RepositoryLabelProjectionWriter, } from "../repository-label-projection/service.js";
|
|
11
|
+
import { LoopControl } from "../loop-control.js";
|
|
12
|
+
import { layerWithDatabase } from "../engine.js";
|
|
13
|
+
import { EnginePersistenceError } from "../model.js";
|
|
14
|
+
import { RunReader } from "../run-reader.js";
|
|
15
|
+
import { RunDatabase } from "./database.js";
|
|
16
|
+
const migrationsFolder = fileURLToPath(new URL("../../drizzle", import.meta.url));
|
|
17
|
+
const startupError = (operation, cause) => new EnginePersistenceError({ operation, cause });
|
|
18
|
+
const sqliteClientLayer = (instanceDirectory) => Layer.unwrap(Effect.gen(function* () {
|
|
19
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
20
|
+
const path = yield* Path.Path;
|
|
21
|
+
const runtimeDirectory = path.join(instanceDirectory, ".usine");
|
|
22
|
+
yield* fileSystem.makeDirectory(runtimeDirectory, { recursive: true });
|
|
23
|
+
return SqliteClient.layer({ filename: path.join(runtimeDirectory, "runtime.sqlite") });
|
|
24
|
+
}).pipe(Effect.mapError((cause) => startupError("prepare local database directory", cause)), Effect.provide(NodeServices.layer)));
|
|
25
|
+
export const localSqliteLayer = (instanceDirectory) => {
|
|
26
|
+
const sqlClient = sqliteClientLayer(instanceDirectory);
|
|
27
|
+
const database = Layer.effect(RunDatabase, Effect.gen(function* () {
|
|
28
|
+
const drizzle = yield* SQLiteDrizzle.makeWithDefaults();
|
|
29
|
+
yield* drizzle.run(sql `PRAGMA foreign_keys = ON`).execute();
|
|
30
|
+
yield* migrate(drizzle, {
|
|
31
|
+
migrationsFolder,
|
|
32
|
+
migrationsTable: "usine_migrations",
|
|
33
|
+
});
|
|
34
|
+
return drizzle;
|
|
35
|
+
}).pipe(Effect.mapError((cause) => startupError("initialize local database", cause))));
|
|
36
|
+
return database.pipe(Layer.provideMerge(sqlClient));
|
|
37
|
+
};
|
|
38
|
+
/** Local-first runtime backed by `.usine/runtime.sqlite`. */
|
|
39
|
+
export const layer = (instanceDirectory) => layerWithDatabase(localSqliteLayer(instanceDirectory)).pipe(Layer.provide(NodeServices.layer));
|
|
40
|
+
/** Read-only local projection; it never reconciles workflows or starts Attempts. */
|
|
41
|
+
export const observationLayer = (instanceDirectory) => {
|
|
42
|
+
const database = localSqliteLayer(instanceDirectory);
|
|
43
|
+
return Layer.mergeAll(ItemProjectionReader.layer, LoopControl.layer, RepositoryLabelProjectionReader.layer, RunReader.layer).pipe(Layer.provide(database));
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=local-sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-sqlite.js","sourceRoot":"","sources":["../../src/database/local-sqlite.ts"],"names":[],"mappings":"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"}
|