@codeworksh/cli 0.0.1-dev.20260918040208

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.
@@ -0,0 +1,220 @@
1
+ import { A as Service$2, C as create$1, D as resolve, E as register, F as Service$4, H as Prompt, I as SessionNotFoundError, L as Service$1, O as stop, R as SubscriptionOverflowError, S as relink, T as list, W as ID, _ as Core, b as create, g as Connected, h as Api, j as Service$3, k as layer$3, r as renderError, u as writeError, v as encode, w as drivers, x as get, y as attach } from "./error-Cr8LvfEq.mjs";
2
+ import { i as Cmd, t as handler } from "./runtime-DuL-GGrx.mjs";
3
+ import { t as harnessOptions } from "./harness-hle7yBVs.mjs";
4
+ import { Cause, Context, Effect, Layer, Option, Queue, Stream } from "effect";
5
+ import { NodeHttpServer } from "@effect/platform-node";
6
+ import { RpcSerialization, RpcServer } from "effect/unstable/rpc";
7
+ import { HttpRouter, HttpServer } from "effect/unstable/http";
8
+ import * as NetAddress from "effect/unstable/net/NetAddress";
9
+ import { createServer } from "node:http";
10
+ var Service = class extends Context.Service()("@codeworksh/cli/server/feed/Service") {};
11
+ /**
12
+ * Fan-out for `event.subscribe`. One listener on the event bus encodes each
13
+ * event once and hands the same envelope to every client, rather than every
14
+ * client re-encoding the same payload.
15
+ *
16
+ * Failure is per-cause, not per-feed: a client that cannot keep up fails alone
17
+ * with its own overflow, while an event that cannot be encoded is a server bug
18
+ * that disconnects everyone currently attached -- the feed stays usable for
19
+ * whoever connects next. Types outside the server's registry are dropped
20
+ * before they can consume a client's capacity.
21
+ */
22
+ const make = Effect.fn("EventFeed.make")(function* (listen, options) {
23
+ const capacity = options?.capacity ?? 4096;
24
+ const registry = options?.registry ?? Core;
25
+ const subscribers = /* @__PURE__ */ new Set();
26
+ const failAll = (error) => Effect.sync(() => {
27
+ const current = Array.from(subscribers);
28
+ subscribers.clear();
29
+ for (const subscriber of current) Queue.failCauseUnsafe(subscriber, Cause.fail(error));
30
+ });
31
+ const deliver = Effect.fnUntraced(function* (event) {
32
+ if (subscribers.size === 0) return;
33
+ if (registry(event.type) === void 0) return;
34
+ const envelope = yield* encode(event, registry);
35
+ for (const subscriber of subscribers) {
36
+ if (Queue.offerUnsafe(subscriber, envelope)) continue;
37
+ subscribers.delete(subscriber);
38
+ Queue.failCauseUnsafe(subscriber, Cause.fail(new SubscriptionOverflowError({ capacity })));
39
+ }
40
+ });
41
+ const publish = (event) => deliver(event).pipe(Effect.catchTags({
42
+ EventEncodingError: (error) => Effect.logError("Failed to encode public event", {
43
+ type: error.type,
44
+ cause: error.cause
45
+ }).pipe(Effect.andThen(failAll(error))),
46
+ UnknownEventTypeError: Effect.die
47
+ }));
48
+ const unsubscribe = yield* listen(publish);
49
+ yield* Effect.addFinalizer(() => unsubscribe);
50
+ return Service.of({ subscribe: Effect.acquireRelease(Queue.dropping(capacity).pipe(Effect.tap((queue) => Effect.sync(() => subscribers.add(queue)))), (queue) => Effect.sync(() => subscribers.delete(queue)).pipe(Effect.andThen(Queue.shutdown(queue)))).pipe(Effect.map(Stream.fromQueue)) });
51
+ });
52
+ const layer$2 = Layer.effect(Service, Effect.gen(function* () {
53
+ const events = yield* Service$1;
54
+ const registered = yield* Service$2;
55
+ return yield* make(events.listen, { registry: registered.get });
56
+ }));
57
+ //#endregion
58
+ //#region src/server/handlers.ts
59
+ const toSandboxInfo = (info) => ({
60
+ id: info.id,
61
+ driver: info.driver,
62
+ kind: info.kind,
63
+ providerResourceId: info.providerResourceId,
64
+ ownership: info.ownership,
65
+ status: info.status,
66
+ usage: info.usage,
67
+ refCount: info.refCount,
68
+ createdAt: info.createdAt,
69
+ updatedAt: info.updatedAt
70
+ });
71
+ const toSessionInfo = (info) => ({
72
+ id: info.id,
73
+ title: info.title,
74
+ directory: info.directory,
75
+ ...info.sandbox === void 0 ? {} : { sandbox: toSandboxInfo(info.sandbox) }
76
+ });
77
+ const runtimeInput = (runtime) => ({
78
+ ...runtime?.model === void 0 ? {} : { model: runtime.model },
79
+ ...runtime?.thinkingLevel === void 0 ? {} : { thinkingLevel: runtime.thinkingLevel }
80
+ });
81
+ const sessionInfo = Effect.fn("Server.sessionInfo")(function* (sessionId) {
82
+ const found = yield* get(sessionId);
83
+ if (Option.isNone(found)) return yield* new SessionNotFoundError({ sessionId });
84
+ return toSessionInfo(yield* found.value.info);
85
+ });
86
+ const releaseOnFailure = (selection) => selection?.created && selection.info !== void 0 ? stop(selection.info.id).pipe(Effect.catchCause(Effect.logError)) : Effect.void;
87
+ const layer$1 = Api.toLayer(Effect.gen(function* () {
88
+ const control = yield* Service$3;
89
+ const feed = yield* Service;
90
+ const sessions = yield* Service$4;
91
+ return Api.of({
92
+ "session.create": Effect.fnUntraced(function* ({ title, directory, sandbox, runtime }) {
93
+ const selection = sandbox === void 0 ? void 0 : yield* resolve(sandbox);
94
+ const selected = selection?.info;
95
+ const handle = yield* create({
96
+ ...title === void 0 ? {} : { title },
97
+ ...directory === void 0 ? {} : { directory },
98
+ ...selected === void 0 ? {} : { sandbox: selected },
99
+ ...runtimeInput(runtime)
100
+ }).pipe(Effect.onError(() => releaseOnFailure(selection)));
101
+ return toSessionInfo(yield* handle.info);
102
+ }),
103
+ "session.list": Effect.fnUntraced(function* () {
104
+ const rows = yield* sessions.list();
105
+ return yield* Effect.forEach(rows, (row) => sessionInfo(row.id).pipe(Effect.orDie));
106
+ }),
107
+ "session.configure": Effect.fnUntraced(function* ({ sessionId, runtime }) {
108
+ const handle = yield* attach({
109
+ sessionId,
110
+ ...runtimeInput(runtime)
111
+ });
112
+ return toSessionInfo(yield* handle.info);
113
+ }),
114
+ "session.info": Effect.fnUntraced(function* ({ sessionId }) {
115
+ return yield* sessionInfo(sessionId);
116
+ }),
117
+ "session.relink": Effect.fnUntraced(function* ({ sessionId, sandbox, directory }) {
118
+ const selection = sandbox === void 0 ? void 0 : yield* resolve(sandbox);
119
+ const selected = selection?.info;
120
+ const handle = yield* relink({
121
+ sessionId,
122
+ ...selected === void 0 ? {} : { sandbox: selected },
123
+ ...directory === void 0 ? {} : { directory }
124
+ }).pipe(Effect.onError(() => releaseOnFailure(selection)));
125
+ return toSessionInfo(yield* handle.info);
126
+ }),
127
+ "session.prompt": Effect.fnUntraced(function* ({ sessionId, text, delivery, id }) {
128
+ yield* control.prompt({
129
+ sessionId,
130
+ prompt: Prompt.make({ text }),
131
+ ...delivery === void 0 ? {} : { delivery },
132
+ ...id === void 0 ? {} : { id }
133
+ });
134
+ }),
135
+ "session.interrupt": Effect.fnUntraced(function* ({ sessionId }) {
136
+ return { interrupted: yield* control.interrupt(sessionId, { reason: "user" }) };
137
+ }),
138
+ "session.message": Effect.fnUntraced(function* ({ sessionId, messageId }) {
139
+ const found = yield* sessions.entry(messageId);
140
+ return { found: Option.isSome(found) && found.value.entry.sessionId === sessionId };
141
+ }),
142
+ "session.wait": Effect.fnUntraced(function* ({ sessionId }) {
143
+ yield* control.wait(sessionId);
144
+ }),
145
+ "sandbox.drivers": Effect.fnUntraced(function* () {
146
+ return (yield* drivers()).map(({ name, kind }) => ({
147
+ name,
148
+ kind
149
+ }));
150
+ }),
151
+ "sandbox.list": Effect.fnUntraced(function* () {
152
+ return (yield* list()).map(toSandboxInfo);
153
+ }),
154
+ "sandbox.create": Effect.fnUntraced(function* ({ driver }) {
155
+ return toSandboxInfo(yield* create$1({ driver }));
156
+ }),
157
+ "sandbox.register": Effect.fnUntraced(function* ({ driver, providerResourceId }) {
158
+ return toSandboxInfo(yield* register({
159
+ driver,
160
+ providerResourceId
161
+ }));
162
+ }),
163
+ "sandbox.stop": Effect.fnUntraced(function* ({ sandboxId }) {
164
+ yield* stop(sandboxId);
165
+ }),
166
+ "event.subscribe": () => Stream.unwrap(feed.subscribe.pipe(Effect.map((events) => Stream.make({
167
+ id: ID.create(),
168
+ type: Connected.type,
169
+ data: {}
170
+ }).pipe(Stream.concat(events)))))
171
+ });
172
+ }));
173
+ //#endregion
174
+ //#region src/server/server.ts
175
+ const WsProtocol = RpcServer.layerProtocolWebsocket({ path: "/rpc" }).pipe(Layer.provide(HttpRouter.layer));
176
+ const managedShutdown = Layer.unwrap(Effect.gen(function* () {
177
+ const control = yield* Service$3;
178
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
179
+ yield* Effect.forEach(yield* control.active, (id) => control.interrupt(id, {
180
+ reason: "shutdown",
181
+ awaitSettlement: true
182
+ }).pipe(Effect.timeout("10 seconds"), Effect.ignore), {
183
+ concurrency: "unbounded",
184
+ discard: true
185
+ });
186
+ const infos = yield* list();
187
+ yield* Effect.forEach(infos.filter((info) => info.ownership === "managed"), (info) => stop(info.id).pipe(Effect.timeout("10 seconds"), Effect.catchCause((cause) => Effect.logError("Failed to stop managed sandbox during shutdown", cause).pipe(Effect.annotateLogs({ sandboxInstanceId: info.id })))), {
188
+ concurrency: "unbounded",
189
+ discard: true
190
+ });
191
+ }));
192
+ return Layer.empty;
193
+ }));
194
+ const listenLog = Layer.unwrap(Effect.gen(function* () {
195
+ const server = yield* HttpServer.HttpServer;
196
+ yield* Effect.log(`codework serve listening on ${NetAddress.formatUrlUnsafe(server.address, "ws")}/rpc`);
197
+ return Layer.empty;
198
+ }));
199
+ const layer = (options) => {
200
+ return RpcServer.layer(Api).pipe(Layer.provide(layer$1.pipe(Layer.provide(layer$2)))).pipe(Layer.provideMerge(WsProtocol), Layer.provide(HttpRouter.serve(WsProtocol, { disableListenLog: true })), Layer.provideMerge(managedShutdown)).pipe(Layer.provideMerge(listenLog), Layer.provideMerge(NodeHttpServer.layer(() => createServer(), {
201
+ host: options.host,
202
+ port: options.port
203
+ })), Layer.provide(RpcSerialization.layerNdjson), Layer.provide(layer$3(options.harness)));
204
+ };
205
+ //#endregion
206
+ //#region src/cli/cmd/handlers/serve.ts
207
+ var serve_default = handler(Cmd.commands.serve, Effect.fn("CLI.serve")(function* ({ host, port }) {
208
+ const shared = yield* Cmd.spec;
209
+ yield* Layer.launch(layer({
210
+ host,
211
+ port,
212
+ harness: harnessOptions(shared)
213
+ })).pipe(Effect.catch((error) => writeError(renderError(error)).pipe(Effect.andThen(Effect.sync(() => {
214
+ process.exitCode = 1;
215
+ })))));
216
+ }));
217
+ //#endregion
218
+ export { serve_default as default };
219
+
220
+ //# sourceMappingURL=serve-CRUpKDkt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-CRUpKDkt.mjs","names":["Envelope.Core","Envelope.encode","Event.SubscriptionOverflowError","layer","Event.Service","EventRegistry.Service","Session.get","SessionStore.SessionNotFoundError","Sandbox.stop","layer","Control.Service","EventFeed.Service","SessionStore.Service","Sandbox.resolve","Session.create","Session.attach","Session.relink","Sandbox.drivers","Sandbox.list","Sandbox.create","Sandbox.register","Control.Service","Sandbox.list","Sandbox.stop","Contract.Api","EventFeed.layer","Harness.layer","Runtime.handler","Server.layer"],"sources":["../../src/server/feed.ts","../../src/server/handlers.ts","../../src/server/server.ts","../../src/cli/cmd/handlers/serve.ts"],"sourcesContent":["import { Event, EventRegistry, EventSchema } from \"@codeworksh/harness/effect\";\nimport { Cause, Context, Effect, Layer, Queue, Scope, Stream } from \"effect\";\nimport { EncodingError, type EventEnvelope, Envelope } from \"./envelope.ts\";\n\nexport const SubscriberCapacity = 4096;\n\nexport type Error = Event.SubscriptionOverflowError | EncodingError;\n\nexport interface Interface {\n\t/** A live envelope stream for one client; releasing the scope unregisters it. */\n\treadonly subscribe: Effect.Effect<Stream.Stream<EventEnvelope, Error>, never, Scope.Scope>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@codeworksh/cli/server/feed/Service\") {}\n\n/**\n * Fan-out for `event.subscribe`. One listener on the event bus encodes each\n * event once and hands the same envelope to every client, rather than every\n * client re-encoding the same payload.\n *\n * Failure is per-cause, not per-feed: a client that cannot keep up fails alone\n * with its own overflow, while an event that cannot be encoded is a server bug\n * that disconnects everyone currently attached -- the feed stays usable for\n * whoever connects next. Types outside the server's registry are dropped\n * before they can consume a client's capacity.\n */\nexport const make = Effect.fn(\"EventFeed.make\")(function* (\n\tlisten: (subscriber: Event.Subscriber) => Effect.Effect<Event.Unsubscribe>,\n\toptions?: { readonly capacity?: number; readonly registry?: Envelope.Registry },\n) {\n\tconst capacity = options?.capacity ?? SubscriberCapacity;\n\t// Whatever this server knows how to describe, plugin registrations included.\n\tconst registry = options?.registry ?? Envelope.Core;\n\tconst subscribers = new Set<Queue.Queue<EventEnvelope, Error>>();\n\n\tconst failAll = (error: Error) =>\n\t\tEffect.sync(() => {\n\t\t\tconst current = Array.from(subscribers);\n\t\t\tsubscribers.clear();\n\t\t\tfor (const subscriber of current) Queue.failCauseUnsafe(subscriber, Cause.fail(error));\n\t\t});\n\n\tconst deliver = Effect.fnUntraced(function* (event: EventSchema.Payload) {\n\t\tif (subscribers.size === 0) return;\n\t\t// Dropped here rather than after the queue: a burst of events this server\n\t\t// cannot describe must not spend a client's capacity on its way to nowhere.\n\t\tif (registry(event.type) === undefined) return;\n\t\tconst envelope = yield* Envelope.encode(event, registry);\n\t\tfor (const subscriber of subscribers) {\n\t\t\tif (Queue.offerUnsafe(subscriber, envelope)) continue;\n\t\t\tsubscribers.delete(subscriber);\n\t\t\tQueue.failCauseUnsafe(subscriber, Cause.fail(new Event.SubscriptionOverflowError({ capacity })));\n\t\t}\n\t});\n\n\tconst publish = (event: EventSchema.Payload) =>\n\t\tdeliver(event).pipe(\n\t\t\tEffect.catchTags({\n\t\t\t\tEventEncodingError: (error) =>\n\t\t\t\t\tEffect.logError(\"Failed to encode public event\", { type: error.type, cause: error.cause }).pipe(\n\t\t\t\t\t\tEffect.andThen(failAll(error)),\n\t\t\t\t\t),\n\t\t\t\t// The registry guard in `deliver` already returned for unknown types.\n\t\t\t\tUnknownEventTypeError: Effect.die,\n\t\t\t}),\n\t\t);\n\n\tconst unsubscribe = yield* listen(publish);\n\tyield* Effect.addFinalizer(() => unsubscribe);\n\n\treturn Service.of({\n\t\tsubscribe: Effect.acquireRelease(\n\t\t\tQueue.dropping<EventEnvelope, Error>(capacity).pipe(\n\t\t\t\tEffect.tap((queue) => Effect.sync(() => subscribers.add(queue))),\n\t\t\t),\n\t\t\t(queue) => Effect.sync(() => subscribers.delete(queue)).pipe(Effect.andThen(Queue.shutdown(queue))),\n\t\t).pipe(Effect.map(Stream.fromQueue)),\n\t});\n});\n\nexport const layer = Layer.effect(\n\tService,\n\tEffect.gen(function* () {\n\t\tconst events = yield* Event.Service;\n\t\tconst registered = yield* EventRegistry.Service;\n\t\treturn yield* make(events.listen, { registry: registered.get });\n\t}),\n);\n\nexport * as EventFeed from \"./feed.ts\";\n","import { Control, EventSchema, PromptSchema, Sandbox, Session, SessionStore } from \"@codeworksh/harness/effect\";\nimport { Effect, Option, Stream } from \"effect\";\nimport { Contract, type RuntimeConfig, type SandboxInfo, type SessionInfo } from \"./contract.ts\";\nimport { Envelope } from \"./envelope.ts\";\nimport { EventFeed } from \"./feed.ts\";\n\nconst toSandboxInfo = (info: Sandbox.Info): SandboxInfo => ({\n\tid: info.id,\n\tdriver: info.driver,\n\tkind: info.kind,\n\tproviderResourceId: info.providerResourceId,\n\townership: info.ownership,\n\tstatus: info.status,\n\tusage: info.usage,\n\trefCount: info.refCount,\n\tcreatedAt: info.createdAt,\n\tupdatedAt: info.updatedAt,\n});\n\nconst toSessionInfo = (info: Session.Info): SessionInfo => ({\n\tid: info.id,\n\ttitle: info.title,\n\tdirectory: info.directory,\n\t...(info.sandbox === undefined ? {} : { sandbox: toSandboxInfo(info.sandbox) }),\n});\n\n// Wire configs name only the keys they set, so `undefined` never reaches the\n// harness inputs under exactOptionalPropertyTypes.\nconst runtimeInput = (runtime: RuntimeConfig | undefined): Session.RuntimeInput => ({\n\t...(runtime?.model === undefined ? {} : { model: runtime.model }),\n\t...(runtime?.thinkingLevel === undefined ? {} : { thinkingLevel: runtime.thinkingLevel }),\n});\n\nconst sessionInfo = Effect.fn(\"Server.sessionInfo\")(function* (sessionId: Session.SessionSchema.ID) {\n\tconst found = yield* Session.get(sessionId);\n\tif (Option.isNone(found)) return yield* new SessionStore.SessionNotFoundError({ sessionId });\n\treturn toSessionInfo(yield* found.value.info);\n});\n\nconst releaseOnFailure = (selection: Effect.Success<ReturnType<typeof Sandbox.resolve>> | undefined) =>\n\tselection?.created && selection.info !== undefined\n\t\t? Sandbox.stop(selection.info.id).pipe(Effect.catchCause(Effect.logError))\n\t\t: Effect.void;\n\nexport const layer = Contract.Api.toLayer(\n\tEffect.gen(function* () {\n\t\tconst control = yield* Control.Service;\n\t\tconst feed = yield* EventFeed.Service;\n\t\tconst sessions = yield* SessionStore.Service;\n\n\t\treturn Contract.Api.of({\n\t\t\t\"session.create\": Effect.fnUntraced(function* ({ title, directory, sandbox, runtime }) {\n\t\t\t\tconst selection = sandbox === undefined ? undefined : yield* Sandbox.resolve(sandbox);\n\t\t\t\tconst selected = selection?.info;\n\t\t\t\tconst handle = yield* Session.create({\n\t\t\t\t\t...(title === undefined ? {} : { title }),\n\t\t\t\t\t...(directory === undefined ? {} : { directory }),\n\t\t\t\t\t...(selected === undefined ? {} : { sandbox: selected }),\n\t\t\t\t\t...runtimeInput(runtime),\n\t\t\t\t}).pipe(Effect.onError(() => releaseOnFailure(selection)));\n\t\t\t\treturn toSessionInfo(yield* handle.info);\n\t\t\t}),\n\t\t\t\"session.list\": Effect.fnUntraced(function* () {\n\t\t\t\tconst rows = yield* sessions.list();\n\t\t\t\treturn yield* Effect.forEach(rows, (row) => sessionInfo(row.id).pipe(Effect.orDie));\n\t\t\t}),\n\t\t\t\"session.configure\": Effect.fnUntraced(function* ({ sessionId, runtime }) {\n\t\t\t\tconst handle = yield* Session.attach({ sessionId, ...runtimeInput(runtime) });\n\t\t\t\treturn toSessionInfo(yield* handle.info);\n\t\t\t}),\n\t\t\t\"session.info\": Effect.fnUntraced(function* ({ sessionId }) {\n\t\t\t\treturn yield* sessionInfo(sessionId);\n\t\t\t}),\n\t\t\t\"session.relink\": Effect.fnUntraced(function* ({ sessionId, sandbox, directory }) {\n\t\t\t\tconst selection = sandbox === undefined ? undefined : yield* Sandbox.resolve(sandbox);\n\t\t\t\tconst selected = selection?.info;\n\t\t\t\tconst handle = yield* Session.relink({\n\t\t\t\t\tsessionId,\n\t\t\t\t\t...(selected === undefined ? {} : { sandbox: selected }),\n\t\t\t\t\t...(directory === undefined ? {} : { directory }),\n\t\t\t\t}).pipe(Effect.onError(() => releaseOnFailure(selection)));\n\t\t\t\treturn toSessionInfo(yield* handle.info);\n\t\t\t}),\n\t\t\t\"session.prompt\": Effect.fnUntraced(function* ({ sessionId, text, delivery, id }) {\n\t\t\t\tyield* control.prompt({\n\t\t\t\t\tsessionId,\n\t\t\t\t\tprompt: PromptSchema.Prompt.make({ text }),\n\t\t\t\t\t...(delivery === undefined ? {} : { delivery }),\n\t\t\t\t\t...(id === undefined ? {} : { id }),\n\t\t\t\t});\n\t\t\t}),\n\t\t\t\"session.interrupt\": Effect.fnUntraced(function* ({ sessionId }) {\n\t\t\t\t// Ack only: the wire answers \"was there work to stop\", and the caller\n\t\t\t\t// uses `session.wait` when it needs cleanup to have finished.\n\t\t\t\treturn { interrupted: yield* control.interrupt(sessionId, { reason: \"user\" }) };\n\t\t\t}),\n\t\t\t\"session.message\": Effect.fnUntraced(function* ({ sessionId, messageId }) {\n\t\t\t\tconst found = yield* sessions.entry(messageId);\n\t\t\t\t// Scoped to the session the caller named: an entry id is global, and a\n\t\t\t\t// client should not learn about conversations it did not ask for.\n\t\t\t\treturn { found: Option.isSome(found) && found.value.entry.sessionId === sessionId };\n\t\t\t}),\n\t\t\t\"session.wait\": Effect.fnUntraced(function* ({ sessionId }) {\n\t\t\t\tyield* control.wait(sessionId);\n\t\t\t}),\n\t\t\t\"sandbox.drivers\": Effect.fnUntraced(function* () {\n\t\t\t\tconst drivers = yield* Sandbox.drivers();\n\t\t\t\treturn drivers.map(({ name, kind }) => ({ name, kind }));\n\t\t\t}),\n\t\t\t\"sandbox.list\": Effect.fnUntraced(function* () {\n\t\t\t\treturn (yield* Sandbox.list()).map(toSandboxInfo);\n\t\t\t}),\n\t\t\t\"sandbox.create\": Effect.fnUntraced(function* ({ driver }) {\n\t\t\t\treturn toSandboxInfo(yield* Sandbox.create({ driver }));\n\t\t\t}),\n\t\t\t\"sandbox.register\": Effect.fnUntraced(function* ({ driver, providerResourceId }) {\n\t\t\t\treturn toSandboxInfo(yield* Sandbox.register({ driver, providerResourceId }));\n\t\t\t}),\n\t\t\t\"sandbox.stop\": Effect.fnUntraced(function* ({ sandboxId }) {\n\t\t\t\tyield* Sandbox.stop(sandboxId);\n\t\t\t}),\n\t\t\t// Acquiring the feed subscription is the readiness barrier: `server.connected`\n\t\t\t// is only emitted once this client is registered, so a prompt admitted after\n\t\t\t// it cannot land in the gap.\n\t\t\t\"event.subscribe\": () =>\n\t\t\t\tStream.unwrap(\n\t\t\t\t\tfeed.subscribe.pipe(\n\t\t\t\t\t\tEffect.map((events) =>\n\t\t\t\t\t\t\tStream.make({ id: EventSchema.ID.create(), type: Envelope.Connected.type, data: {} }).pipe(\n\t\t\t\t\t\t\t\tStream.concat(events),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t});\n\t}),\n);\n\nexport * as Handlers from \"./handlers.ts\";\n","import { Control, Harness, Sandbox } from \"@codeworksh/harness/effect\";\nimport { NodeHttpServer } from \"@effect/platform-node\";\nimport { Effect, Layer } from \"effect\";\nimport { HttpRouter, HttpServer } from \"effect/unstable/http\";\nimport * as NetAddress from \"effect/unstable/net/NetAddress\";\nimport { RpcSerialization, RpcServer } from \"effect/unstable/rpc\";\n/* @effect-diagnostics nodeBuiltinImport:off -- the RPC listener needs a raw node server. */\nimport { createServer } from \"node:http\";\nimport { Contract } from \"./contract.ts\";\nimport { EventFeed } from \"./feed.ts\";\nimport { Handlers } from \"./handlers.ts\";\n\nconst WsProtocol = RpcServer.layerProtocolWebsocket({ path: \"/rpc\" }).pipe(Layer.provide(HttpRouter.layer));\n\n// Managed instances outlive any single RPC; the serve scope is what stops them.\nconst managedShutdown = Layer.unwrap(\n\tEffect.gen(function* () {\n\t\tconst control = yield* Control.Service;\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\t// Release drain leases before stopping the server's managed sandboxes.\n\t\t\t\t// Both phases are best effort and bounded so a provider cannot prevent\n\t\t\t\t// process shutdown; an interrupted stop is persisted as faulted and can\n\t\t\t\t// recover the next time that sandbox is mounted.\n\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\tyield* control.active,\n\t\t\t\t\t(id) =>\n\t\t\t\t\t\tcontrol\n\t\t\t\t\t\t\t.interrupt(id, { reason: \"shutdown\", awaitSettlement: true })\n\t\t\t\t\t\t\t.pipe(Effect.timeout(\"10 seconds\"), Effect.ignore),\n\t\t\t\t\t{ concurrency: \"unbounded\", discard: true },\n\t\t\t\t);\n\t\t\t\tconst infos = yield* Sandbox.list();\n\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\tinfos.filter((info) => info.ownership === \"managed\"),\n\t\t\t\t\t(info) =>\n\t\t\t\t\t\tSandbox.stop(info.id).pipe(\n\t\t\t\t\t\t\tEffect.timeout(\"10 seconds\"),\n\t\t\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\t\t\tEffect.logError(\"Failed to stop managed sandbox during shutdown\", cause).pipe(\n\t\t\t\t\t\t\t\t\tEffect.annotateLogs({ sandboxInstanceId: info.id }),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t{ concurrency: \"unbounded\", discard: true },\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t\treturn Layer.empty;\n\t}),\n);\n\nconst listenLog = Layer.unwrap(\n\tEffect.gen(function* () {\n\t\tconst server = yield* HttpServer.HttpServer;\n\t\tyield* Effect.log(`codework serve listening on ${NetAddress.formatUrlUnsafe(server.address, \"ws\")}/rpc`);\n\t\treturn Layer.empty;\n\t}),\n);\n\nexport const layer = (options: { host: string; port: number; harness: Harness.Options }) => {\n\tconst rpc = RpcServer.layer(Contract.Api).pipe(Layer.provide(Handlers.layer.pipe(Layer.provide(EventFeed.layer))));\n\tconst app = rpc.pipe(\n\t\tLayer.provideMerge(WsProtocol),\n\t\tLayer.provide(HttpRouter.serve(WsProtocol, { disableListenLog: true })),\n\t\tLayer.provideMerge(managedShutdown),\n\t);\n\treturn app.pipe(\n\t\tLayer.provideMerge(listenLog),\n\t\tLayer.provideMerge(NodeHttpServer.layer(() => createServer(), { host: options.host, port: options.port })),\n\t\tLayer.provide(RpcSerialization.layerNdjson),\n\t\tLayer.provide(Harness.layer(options.harness)),\n\t);\n};\n\nexport * as Server from \"./server.ts\";\n","import { Effect, Layer } from \"effect\";\nimport { Runtime } from \"../../../framework/runtime.ts\";\nimport { Server } from \"../../../server/server.ts\";\nimport { renderError } from \"../../error.ts\";\nimport { harnessOptions } from \"../../harness.ts\";\nimport { writeError } from \"../../output.ts\";\nimport { Cmd } from \"../cmd.ts\";\n\nexport default Runtime.handler(\n\tCmd.commands.serve,\n\tEffect.fn(\"CLI.serve\")(function* ({ host, port }) {\n\t\tconst shared = yield* Cmd.spec;\n\t\tyield* Layer.launch(Server.layer({ host, port, harness: harnessOptions(shared) })).pipe(\n\t\t\tEffect.catch((error) =>\n\t\t\t\twriteError(renderError(error)).pipe(\n\t\t\t\t\tEffect.andThen(\n\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\tprocess.exitCode = 1;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}),\n);\n"],"mappings":"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"}