@rikalabs/akter-cli 0.0.0-stage → 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js ADDED
@@ -0,0 +1,3280 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/main.ts
4
+ import { Context as Context2, Effect as Effect23, Layer as Layer5, Stdio as Stdio2 } from "effect";
5
+ import { FetchHttpClient } from "effect/http";
6
+
7
+ // src/cli.ts
8
+ import { Effect as Effect22, Runtime as Runtime2, Schema as Schema29 } from "effect";
9
+ import { CliError, Command as Command19 } from "effect/cli";
10
+ // package.json
11
+ var version = "0.1.0-alpha.4";
12
+
13
+ // src/commands/adopt/run.ts
14
+ import {
15
+ adoptionStatus,
16
+ adoptionWriters,
17
+ backfillAdoption,
18
+ enforceAdoption,
19
+ formatAdoptionPlan,
20
+ formatAdoptionStatus,
21
+ formatBackfill,
22
+ formatEnforce,
23
+ formatObservedWriter,
24
+ observeAdoption,
25
+ planAdoption,
26
+ releaseAdoption
27
+ } from "@rikalabs/akter/runtime";
28
+ import { Clock, Console as Console3, Effect as Effect3, Layer as Layer2, Option } from "effect";
29
+ import { Argument, Command as Command2, Flag as Flag2 } from "effect/cli";
30
+ import { Database as Database2 } from "@rikalabs/akter/runtime";
31
+
32
+ // src/failure.ts
33
+ import { Console, Effect, Runtime, Schema } from "effect";
34
+
35
+ class UsageError extends Schema.TaggedError()("UsageError", {
36
+ message: Schema.String
37
+ }) {
38
+ }
39
+
40
+ class CommandFailed extends Schema.TaggedError()("CommandFailed", {
41
+ exitCode: Schema.Int,
42
+ reason: Schema.String
43
+ }) {
44
+ [Runtime.errorReported] = false;
45
+ get [Runtime.errorExitCode]() {
46
+ return this.exitCode;
47
+ }
48
+ }
49
+ var fail = (failure) => Console.error(failure.message).pipe(Effect.andThen(Effect.fail(CommandFailed.make({ exitCode: failure.exitCode ?? 2, reason: failure.reason }))));
50
+
51
+ // src/platform.ts
52
+ import { createServer } from "node:http";
53
+ var platform = typeof Bun === "undefined" ? await import("@effect/platform-node").then((node) => ({
54
+ crypto: node.NodeCrypto.layer,
55
+ services: node.NodeServices.layer,
56
+ runMain: node.NodeRuntime.runMain,
57
+ httpServer: (options) => node.NodeHttpServer.layer(createServer, { port: options.port, host: options.hostname })
58
+ })) : await import("@effect/platform-bun").then((bun) => ({
59
+ crypto: bun.BunCrypto.layer,
60
+ services: bun.BunServices.layer,
61
+ runMain: bun.BunRuntime.runMain,
62
+ httpServer: (options) => bun.BunHttpServer.layer(options)
63
+ }));
64
+ var PlatformCrypto = { layer: platform.crypto };
65
+ var PlatformServices = { layer: platform.services };
66
+ var PlatformRuntime = { runMain: platform.runMain };
67
+ var PlatformHttpServer = { layer: platform.httpServer };
68
+
69
+ // src/commands/workflows/check.ts
70
+ import { Database, checkWorkflows, formatIncompatibility } from "@rikalabs/akter/runtime";
71
+ import { Console as Console2, Effect as Effect2, Layer, Schema as Schema2 } from "effect";
72
+ import { Command, Flag } from "effect/cli";
73
+ import { pathToFileURL } from "node:url";
74
+ var entryFlags = {
75
+ entry: Flag.File("entry", { mustExist: true }).pipe(Flag.withDescription("The entry module; it exports an `actors` array of actor definitions")),
76
+ databaseUrl: Flag.Redacted("database-url").pipe(Flag.withDescription("The application's Postgres URL")),
77
+ json: Flag.Boolean("json").pipe(Flag.withDefault(false), Flag.withDescription("Print the report as JSON"))
78
+ };
79
+ var entryCommand = ({
80
+ options,
81
+ reading,
82
+ run
83
+ }) => Effect2.gen(function* () {
84
+ const module = yield* loadEntry(options.entry);
85
+ const actors = yield* actorsOf({ module, entry: options.entry });
86
+ const services = yield* Layer.build(Database.postgres({ url: options.databaseUrl }).pipe(Layer.provideMerge(PlatformCrypto.layer)));
87
+ const { output, exitCode } = yield* run(actors).pipe(Effect2.provideContext(services));
88
+ yield* Console2.log(output);
89
+ if (exitCode !== 0)
90
+ return yield* CommandFailed.make({ exitCode, reason: "Refused" });
91
+ }).pipe(Effect2.scoped, Effect2.catchTags({
92
+ SqlError: (error) => fail({ reason: error._tag, message: `Cannot read ${reading}: ${error.message}` }),
93
+ UsageError: (error) => fail({ reason: error._tag, message: error.message })
94
+ }));
95
+ var Entry = Schema2.Struct({
96
+ actors: Schema2.Array(Schema2.Struct({ name: Schema2.String, api: Schema2.Record(Schema2.String, Schema2.Unknown) }))
97
+ });
98
+ var decodeEntry = Schema2.decodeUnknownEffect(Entry);
99
+ var AnySchema = Schema2.declare(Schema2.isSchema);
100
+ var WorkflowDefinition = Schema2.Struct({
101
+ kind: Schema2.Literal("workflow"),
102
+ tag: Schema2.String,
103
+ payload: AnySchema,
104
+ success: AnySchema,
105
+ error: AnySchema,
106
+ versions: Schema2.Record(Schema2.String, Schema2.Struct({ current: Schema2.Finite, min: Schema2.Finite })),
107
+ registry: Schema2.Struct({
108
+ steps: Schema2.declare((u) => u instanceof Map)
109
+ })
110
+ });
111
+ var isWorkflowDefinition = Schema2.is(WorkflowDefinition);
112
+ var isWorkflowKind = Schema2.is(Schema2.Struct({ kind: Schema2.Literal("workflow") }));
113
+ var loadEntry = (entry) => Effect2.tryPromise({
114
+ try: () => import(pathToFileURL(entry).href),
115
+ catch: (cause) => UsageError.make({
116
+ message: `Cannot load ${entry}: ${cause instanceof Error ? cause.message : String(cause)}`
117
+ })
118
+ });
119
+ var actorsOf = ({ module, entry }) => decodeEntry(module).pipe(Effect2.mapError(() => UsageError.make({ message: `${entry} must export an \`actors\` array of actor definitions` })), Effect2.flatMap(({ actors }) => {
120
+ for (const actor of actors)
121
+ for (const [name, member] of Object.entries(actor.api))
122
+ if (isWorkflowKind(member) && !isWorkflowDefinition(member))
123
+ return Effect2.fail(UsageError.make({
124
+ message: `${entry}: ${actor.name}.${name} is not an Actor.workflow definition`
125
+ }));
126
+ return Effect2.succeed(actors);
127
+ }));
128
+ var report = ({
129
+ incompatibilities,
130
+ json
131
+ }) => ({
132
+ incompatibilities,
133
+ output: json ? JSON.stringify({ compatible: incompatibilities.length === 0, incompatibilities }, null, 2) : incompatibilities.length === 0 ? "Workflows are compatible with every open execution" : [
134
+ ...incompatibilities.map(formatIncompatibility),
135
+ `${incompatibilities.length} incompatibilit${incompatibilities.length === 1 ? "y" : "ies"}; deploy refused (exit 1)`
136
+ ].join(`
137
+ `),
138
+ exitCode: incompatibilities.length === 0 ? 0 : 1
139
+ });
140
+ var check = ({
141
+ actors,
142
+ json
143
+ }) => checkWorkflows(actors).pipe(Effect2.map((incompatibilities) => report({ incompatibilities, json })));
144
+ var checkCommand = Command.make("check", entryFlags, (options) => entryCommand({
145
+ options,
146
+ reading: "workflow state",
147
+ run: (actors) => check({ actors, json: options.json })
148
+ })).pipe(Command.withDescription("Compare the entry's workflows with every open execution, read-only; exit 1 when a deploy would be refused"));
149
+
150
+ // src/commands/adopt/run.ts
151
+ var UNITS = { m: 60000, h: 3600000, d: 86400000 };
152
+ var WINDOW = /^([1-9][0-9]*)([mhd])$/;
153
+ var parseWindow = (text) => {
154
+ const match = WINDOW.exec(text);
155
+ return match === null ? Option.none() : Option.some(Number(match[1]) * UNITS[match[2]]);
156
+ };
157
+ var printJson = (value) => JSON.stringify(value, null, 2);
158
+ var adopt = ({
159
+ options,
160
+ actors
161
+ }) => Effect3.gen(function* () {
162
+ if (options.command === "status") {
163
+ const statuses = yield* adoptionStatus;
164
+ return {
165
+ output: options.json ? printJson({ tables: statuses }) : statuses.length === 0 ? "No table is adopted" : statuses.map(formatAdoptionStatus).join(`
166
+ `),
167
+ exitCode: 0
168
+ };
169
+ }
170
+ if (options.command === "enforce") {
171
+ const enforced = yield* enforceAdoption(actors, {
172
+ only: options.table,
173
+ writerRole: options.writerRole,
174
+ allowedRoles: options.allow,
175
+ quietMs: options.quietMs,
176
+ nowMs: yield* Clock.currentTimeMillis
177
+ });
178
+ return {
179
+ output: options.json ? printJson({ enforced }) : enforced.map(formatEnforce).join(`
180
+ `),
181
+ exitCode: 0
182
+ };
183
+ }
184
+ if (options.command === "release") {
185
+ const released = yield* releaseAdoption(actors, { only: options.table });
186
+ return {
187
+ output: options.json ? printJson({ observing: released }) : released.map((table) => `${table} is observing again`).join(`
188
+ `),
189
+ exitCode: 0
190
+ };
191
+ }
192
+ if (options.command === "plan") {
193
+ const plans = yield* planAdoption(actors, options.table);
194
+ return {
195
+ output: options.json ? printJson({ tables: plans }) : plans.map(formatAdoptionPlan).join(`
196
+
197
+ `),
198
+ exitCode: plans.some((plan) => plan.problems.length > 0) ? 1 : 0
199
+ };
200
+ }
201
+ if (options.command === "backfill") {
202
+ const results = yield* backfillAdoption(actors, {
203
+ only: options.table,
204
+ batch: options.batch
205
+ });
206
+ return {
207
+ output: options.json ? printJson({ tables: results }) : results.map(formatBackfill).join(`
208
+ `),
209
+ exitCode: 0
210
+ };
211
+ }
212
+ if (options.report) {
213
+ const writers = yield* adoptionWriters(actors, {
214
+ only: options.table,
215
+ sinceMs: options.sinceMs,
216
+ clear: options.clear
217
+ });
218
+ return {
219
+ output: options.json ? printJson({ writers }) : writers.length === 0 ? "No write was recorded" : writers.map(formatObservedWriter).join(`
220
+ `),
221
+ exitCode: 0
222
+ };
223
+ }
224
+ const observed = yield* observeAdoption(actors, options.table);
225
+ return {
226
+ output: options.json ? printJson({ observing: observed }) : observed.map((table) => `${table} is observing`).join(`
227
+ `),
228
+ exitCode: 0
229
+ };
230
+ }).pipe(Effect3.catchTag("AdoptionRefused", (refused) => Effect3.succeed({ output: refused.message, exitCode: 1 })));
231
+ var run = (options) => Effect3.gen(function* () {
232
+ const actors = options.entry === undefined ? [] : yield* actorsOf({ module: yield* loadEntry(options.entry), entry: options.entry });
233
+ const services = yield* Layer2.build(Database2.postgres({ url: options.databaseUrl }).pipe(Layer2.provideMerge(PlatformCrypto.layer)));
234
+ const { output, exitCode } = yield* adopt({ options, actors }).pipe(Effect3.provideContext(services));
235
+ yield* Console3.log(output);
236
+ if (exitCode !== 0)
237
+ return yield* CommandFailed.make({ exitCode, reason: "Refused" });
238
+ }).pipe(Effect3.scoped, Effect3.catchTags({
239
+ SqlError: (error) => fail({
240
+ reason: error._tag,
241
+ message: `Cannot read adoption state (is the database migrated to 0024_adoption?): ${error.message}`
242
+ }),
243
+ UsageError: (error) => fail({ reason: error._tag, message: error.message })
244
+ }));
245
+ var defaults = {
246
+ entry: undefined,
247
+ table: undefined,
248
+ report: false,
249
+ clear: false,
250
+ sinceMs: undefined,
251
+ batch: undefined,
252
+ writerRole: undefined,
253
+ allow: [],
254
+ quietMs: undefined
255
+ };
256
+ var table = (verb) => Argument.String("table").pipe(Argument.withDescription(`The adopted table to ${verb}`));
257
+ var planCommand = Command2.make("plan", {
258
+ ...entryFlags,
259
+ table: Flag2.String("table").pipe(Flag2.optional, Flag2.withDescription("Plan only this adopted table"))
260
+ }, (options) => run({
261
+ ...defaults,
262
+ ...options,
263
+ command: "plan",
264
+ table: Option.getOrUndefined(options.table)
265
+ })).pipe(Command2.withDescription("Plan adopting the entry's existing tables, with the SQL each needs; exit 1 while a table has a problem"));
266
+ var observeCommand = Command2.make("observe", {
267
+ ...entryFlags,
268
+ table: table("observe"),
269
+ report: Flag2.Boolean("report").pipe(Flag2.withDefault(false), Flag2.withDescription("Report the writes recorded so far instead of starting to observe")),
270
+ since: Flag2.String("since").pipe(Flag2.filterMap(parseWindow, () => "a window such as 30m, 12h, or 7d"), Flag2.optional, Flag2.withDescription("With --report, only writes within this window, such as 7d")),
271
+ clear: Flag2.Boolean("clear").pipe(Flag2.withDefault(false), Flag2.withDescription("With --report, clear the reported writes"))
272
+ }, (options) => Effect3.gen(function* () {
273
+ if ((Option.isSome(options.since) || options.clear) && !options.report)
274
+ return yield* fail({
275
+ reason: "UsageError",
276
+ message: "--since and --clear belong to observe --report"
277
+ });
278
+ const nowMs = yield* Clock.currentTimeMillis;
279
+ return yield* run({
280
+ ...defaults,
281
+ ...options,
282
+ command: "observe",
283
+ sinceMs: Option.getOrUndefined(Option.map(options.since, (window) => nowMs - window))
284
+ });
285
+ })).pipe(Command2.withDescription("Record which writers still write an adopted table, or report them with --report"));
286
+ var backfillCommand = Command2.make("backfill", {
287
+ ...entryFlags,
288
+ table: table("backfill"),
289
+ batch: Flag2.Int("batch").pipe(Flag2.filter((batch) => batch >= 1, () => "a positive integer"), Flag2.optional, Flag2.withDescription("Rows per pass (default 1000)"))
290
+ }, (options) => run({
291
+ ...defaults,
292
+ ...options,
293
+ command: "backfill",
294
+ batch: Option.getOrUndefined(options.batch)
295
+ })).pipe(Command2.withDescription("Fill routing_key on an observed table's rows, in batches"));
296
+ var enforceCommand = Command2.make("enforce", {
297
+ ...entryFlags,
298
+ table: table("enforce"),
299
+ writerRole: Flag2.String("writer-role").pipe(Flag2.withDescription("The database role the runtime writes the table as")),
300
+ allow: Flag2.String("allow").pipe(Flag2.atLeast(0), Flag2.withDescription("Another role still allowed to write the table; repeat for several")),
301
+ quiet: Flag2.String("quiet").pipe(Flag2.filterMap(parseWindow, () => "a window such as 30m, 12h, or 7d"), Flag2.optional, Flag2.withDescription("How long no legacy write may have been recorded before enforcing, such as 7d"))
302
+ }, (options) => run({
303
+ ...defaults,
304
+ ...options,
305
+ command: "enforce",
306
+ quietMs: Option.getOrUndefined(options.quiet)
307
+ })).pipe(Command2.withDescription("Enforce an adopted table: only the runtime's writer role and --allow roles may write it"));
308
+ var releaseCommand = Command2.make("release", {
309
+ ...entryFlags,
310
+ table: table("release"),
311
+ to: Flag2.Literals("to", ["observe"]).pipe(Flag2.withDescription("The mode to return the table to; only observe"))
312
+ }, (options) => run({ ...defaults, ...options, command: "release" })).pipe(Command2.withDescription("Return an enforced table to observing"));
313
+ var statusCommand = Command2.make("status", { databaseUrl: entryFlags.databaseUrl, json: entryFlags.json }, (options) => run({ ...defaults, ...options, command: "status" })).pipe(Command2.withDescription("Show each adopted table's mode and the rows left to backfill"));
314
+
315
+ // src/commands/dead-letters/repair.ts
316
+ import { Argument as Argument3, Command as Command3, Flag as Flag4 } from "effect/cli";
317
+
318
+ // src/commands/operator/request.ts
319
+ import { Config, Console as Console4, Effect as Effect4, Option as Option2, Redacted, Schema as Schema3 } from "effect";
320
+ import { Argument as Argument2, Flag as Flag3 } from "effect/cli";
321
+ import { HttpClient, HttpClientRequest } from "effect/http";
322
+ var TOKEN_ENV = "AKTER_OPERATOR_TOKEN";
323
+
324
+ class RunnerUnreachable extends Schema3.TaggedError()("RunnerUnreachable", { url: Schema3.String, message: Schema3.String }) {
325
+ }
326
+
327
+ class OperatorRefused extends Schema3.TaggedError()("OperatorRefused", {
328
+ status: Schema3.Finite,
329
+ body: Schema3.String
330
+ }) {
331
+ }
332
+ var urls = Flag3.String("url").pipe(Flag3.atLeast(1), Flag3.map((values) => values.map((value) => value.replace(/\/+$/, ""))), Flag3.withDescription("A runner's base URL; repeat to name several, a single-actor command uses the first"));
333
+ var tenant = Flag3.String("tenant").pipe(Flag3.withDescription("The tenant the request acts in"));
334
+ var operatorFlags = {
335
+ urls,
336
+ tokenEnv: Flag3.String("token-env").pipe(Flag3.withDefault(TOKEN_ENV), Flag3.withDescription(`The environment variable holding the operator bearer token (default ${TOKEN_ENV})`)),
337
+ json: Flag3.Boolean("json").pipe(Flag3.withDefault(false), Flag3.withDescription("Print the runner's answer as JSON"))
338
+ };
339
+ var splitActor = (value) => {
340
+ const slash = value.indexOf("/");
341
+ return slash <= 0 || slash === value.length - 1 ? Option2.none() : Option2.some({ actorType: value.slice(0, slash), actorId: value.slice(slash + 1) });
342
+ };
343
+ var notAnActor = () => "an actor named as <Type>/<id>";
344
+ var actorArgument = Argument2.String("actor").pipe(Argument2.filterMap(splitActor, notAnActor), Argument2.withDescription("The actor, as <Type>/<id>"));
345
+ var actorFlag = (flag) => Flag3.String(flag.name).pipe(Flag3.filterMap(splitActor, notAnActor), Flag3.withDescription(flag.description));
346
+ var reason = Flag3.String("reason").pipe(Flag3.filter((value) => value.length > 0 && value.length <= 500, () => "a reason of 1 to 500 characters"), Flag3.withDescription("Why, recorded in the operator audit log (up to 500 characters)"));
347
+ var operatorToken = (name) => Effect4.map(Config.option(Config.Redacted(name)), (token) => Option2.match(token, { onNone: () => {
348
+ return;
349
+ }, onSome: (value) => Redacted.value(value) }));
350
+ var encodeJson = (answer) => Schema3.encodeEffect(Schema3.fromJsonString(Schema3.Json))(answer).pipe(Effect4.orDie);
351
+ var operatorCommand = ({
352
+ options,
353
+ request,
354
+ format
355
+ }) => Effect4.gen(function* () {
356
+ const token = yield* operatorToken(options.tokenEnv);
357
+ const answer = yield* request({ options, token });
358
+ yield* Console4.log(options.json ? yield* encodeJson(answer) : yield* format(answer));
359
+ }).pipe(Effect4.catchTags({
360
+ RunnerUnreachable: (error) => fail({ reason: error._tag, message: `Cannot reach ${error.url}: ${error.message}` }),
361
+ OperatorRefused: (error) => fail({
362
+ reason: error._tag,
363
+ message: `Refused (${error.status}): ${error.body}`,
364
+ exitCode: 1
365
+ }),
366
+ ConfigError: (error) => fail({ reason: error._tag, message: `Cannot read the operator token: ${error.message}` }),
367
+ SchemaError: (error) => fail({ reason: error._tag, message: `Unexpected answer: ${error.message}` }),
368
+ PlatformError: (error) => fail({ reason: error._tag, message: `Cannot write the file: ${error.message}` })
369
+ }));
370
+ var decodeJson = Schema3.decodeUnknownEffect(Schema3.fromJsonString(Schema3.Json));
371
+ var operatorRequest = Effect4.fnUntraced(function* ({
372
+ url,
373
+ path,
374
+ token,
375
+ body
376
+ }) {
377
+ const client = yield* HttpClient.HttpClient;
378
+ const base = body === undefined ? HttpClientRequest.get(`${url}${path}`) : HttpClientRequest.post(`${url}${path}`);
379
+ const request = (body === undefined ? base : HttpClientRequest.bodyJsonUnsafe(base, body)).pipe(token === undefined ? (same) => same : HttpClientRequest.bearerToken(token));
380
+ const response = yield* client.execute(request).pipe(Effect4.mapError((error) => RunnerUnreachable.make({ url, message: error.message })));
381
+ const text = yield* response.text.pipe(Effect4.mapError((error) => RunnerUnreachable.make({ url, message: error.message })));
382
+ if (response.status >= 400)
383
+ return yield* OperatorRefused.make({ status: response.status, body: text });
384
+ return yield* decodeJson(text).pipe(Effect4.mapError(() => RunnerUnreachable.make({ url, message: "the answer is not JSON" })));
385
+ });
386
+
387
+ // src/commands/dead-letters/repair.ts
388
+ var flags = {
389
+ jobId: Argument3.String("jobId").pipe(Argument3.withDescription("The dead-lettered job's id")),
390
+ actor: actorFlag({
391
+ name: "actor",
392
+ description: "The actor that enqueued the job, as <Type>/<id>"
393
+ }),
394
+ tenant,
395
+ reason,
396
+ ...operatorFlags
397
+ };
398
+ var repair = ({
399
+ options,
400
+ token
401
+ }) => operatorRequest({
402
+ url: options.urls[0],
403
+ path: `/operator/dead-letters/${encodeURIComponent(options.jobId)}/${options.action}`,
404
+ token,
405
+ body: options.action === "retry" ? {
406
+ tenant: options.tenant,
407
+ actorType: options.actor.actorType,
408
+ actorId: options.actor.actorId,
409
+ reason: options.reason,
410
+ providerChecked: options.providerChecked
411
+ } : {
412
+ tenant: options.tenant,
413
+ actorType: options.actor.actorType,
414
+ actorId: options.actor.actorId,
415
+ reason: options.reason
416
+ }
417
+ });
418
+ var retryCommand = Command3.make("retry", {
419
+ ...flags,
420
+ providerChecked: Flag4.Boolean("provider-checked").pipe(Flag4.withDefault(false), Flag4.withDescription("Confirm the provider never applied an ambiguous attempt, so running it again is safe"))
421
+ }, (options) => operatorCommand({
422
+ options: { ...options, action: "retry" },
423
+ request: repair,
424
+ format: encodeJson
425
+ })).pipe(Command3.withDescription("Run a dead-lettered job again"));
426
+ var discardCommand = Command3.make("discard", flags, (options) => operatorCommand({
427
+ options: { ...options, action: "discard", providerChecked: false },
428
+ request: repair,
429
+ format: encodeJson
430
+ })).pipe(Command3.withDescription("Settle a dead-lettered job without running it"));
431
+
432
+ // src/commands/defects/list.ts
433
+ import { DefectRecords } from "@rikalabs/akter/runtime";
434
+ import { Clock as Clock2, Console as Console5, DateTime, Duration, Effect as Effect5, Option as Option3, Schema as Schema4 } from "effect";
435
+ import { Command as Command4, Flag as Flag5 } from "effect/cli";
436
+ var units = { s: "seconds", m: "minutes", h: "hours", d: "days" };
437
+ var parseSince = (value) => {
438
+ const compact = /^(\d+)([smhd])$/.exec(value);
439
+ return Duration.fromInput(compact === null ? value : `${compact[1]} ${units[compact[2]]}`);
440
+ };
441
+ var decodeDefects = Schema4.decodeUnknownEffect(DefectRecords);
442
+ var flags2 = {
443
+ ...operatorFlags,
444
+ tenant: Flag5.String("tenant").pipe(Flag5.withDefault("*"), Flag5.withDescription("The tenant to read, or * for every tenant the operator's grant covers (default *)")),
445
+ actor: Flag5.String("actor").pipe(Flag5.optional, Flag5.withDescription("Only defects of this actor type")),
446
+ since: Flag5.String("since").pipe(Flag5.filterMap(parseSince, () => 'a duration such as 1h, 30m, 2d, 45s or "90 minutes"'), Flag5.optional, Flag5.withDescription("Only defects this recent: 1h, 30m, 2d, 45s, or any duration")),
447
+ limit: Flag5.Int("limit").pipe(Flag5.filter((limit) => limit >= 1 && limit <= 1000, () => "an integer from 1 to 1000"), Flag5.optional, Flag5.withDescription("At most this many of the newest defects, 1 to 1000"))
448
+ };
449
+ var listDefects = Effect5.fnUntraced(function* (options, token) {
450
+ const read = (url) => {
451
+ const query = new URLSearchParams({ tenant: options.tenant });
452
+ if (options.actor !== undefined)
453
+ query.set("actor", options.actor);
454
+ if (options.sinceMs !== undefined)
455
+ query.set("sinceMs", String(Math.floor(options.sinceMs)));
456
+ if (options.limit !== undefined)
457
+ query.set("limit", String(options.limit));
458
+ return operatorRequest({ url, path: `/operator/defects?${query}`, token }).pipe(Effect5.flatMap(decodeDefects));
459
+ };
460
+ const each = yield* Effect5.forEach(options.urls, read, { concurrency: "unbounded" });
461
+ const merged = each.flat().toSorted((a, b) => a.atMs - b.atMs);
462
+ return options.limit === undefined ? merged : merged.slice(-options.limit);
463
+ });
464
+ var formatDefects = ({
465
+ defects,
466
+ json
467
+ }) => {
468
+ if (json)
469
+ return JSON.stringify(defects, null, 2);
470
+ if (defects.length === 0)
471
+ return "No defects.";
472
+ return defects.map((defect) => [
473
+ DateTime.formatIso(DateTime.makeUnsafe(defect.atMs)),
474
+ `${defect.actorType}/${defect.actorId}`,
475
+ `${defect.command} ${defect.commandId}`,
476
+ `tenant=${defect.tenant}`,
477
+ `trace=${defect.traceId}`,
478
+ defect.cause.split(`
479
+ `)[0] ?? ""
480
+ ].join(" ")).join(`
481
+ `);
482
+ };
483
+ var listCommand = Command4.make("list", flags2, (options) => Effect5.gen(function* () {
484
+ const nowMs = yield* Clock2.currentTimeMillis;
485
+ const token = yield* operatorToken(options.tokenEnv);
486
+ const defects = yield* listDefects({
487
+ urls: options.urls,
488
+ tenant: options.tenant,
489
+ actor: Option3.getOrUndefined(options.actor),
490
+ sinceMs: Option3.getOrUndefined(Option3.map(options.since, (since) => nowMs - Duration.toMillis(since))),
491
+ limit: Option3.getOrUndefined(options.limit)
492
+ }, token);
493
+ yield* Console5.log(formatDefects({ defects, json: options.json }));
494
+ }).pipe(Effect5.catchTags({
495
+ RunnerUnreachable: (error) => fail({
496
+ reason: error._tag,
497
+ message: `Cannot read defects from ${error.url}: ${error.message}`
498
+ }),
499
+ OperatorRefused: (error) => fail({
500
+ reason: error._tag,
501
+ message: `Refused (${error.status}): ${error.body}`,
502
+ exitCode: 1
503
+ }),
504
+ SchemaError: (error) => fail({ reason: error._tag, message: `Unexpected answer: ${error.message}` }),
505
+ ConfigError: (error) => fail({ reason: error._tag, message: `Cannot read the operator token: ${error.message}` })
506
+ }))).pipe(Command4.withDescription("List recent defects from each runner named; each keeps only its own recent defect spans"));
507
+
508
+ // src/commands/dev/run.ts
509
+ import { User } from "@rikalabs/akter";
510
+ import { Auth, Database as Database3, Inspector } from "@rikalabs/akter/runtime";
511
+ import { Console as Console6, Effect as Effect7, Layer as Layer3, Option as Option4, Schema as Schema5 } from "effect";
512
+ import { Command as Command5, Flag as Flag6 } from "effect/cli";
513
+ import { HttpRouter as HttpRouter2, HttpServer } from "effect/http";
514
+
515
+ // src/commands/dev/inspector/page.ts
516
+ import { Effect as Effect6, FileSystem } from "effect";
517
+ import { HttpRouter, HttpServerResponse } from "effect/http";
518
+ import { fileURLToPath } from "node:url";
519
+ var STYLES = `
520
+ :root {
521
+ color-scheme: light dark;
522
+ --bg: #f6f7f9; --panel: #ffffff; --ink: #16181d; --muted: #6b7280; --line: #e3e6ea;
523
+ --accent: #3b5bdb; --good: #2b8a3e; --bad: #c92a2a; --warn: #b7791f; --info: #1c7ed6;
524
+ --code: #f1f3f5;
525
+ }
526
+ @media (prefers-color-scheme: dark) {
527
+ :root {
528
+ --bg: #0f1115; --panel: #171a21; --ink: #e8eaed; --muted: #9aa0a6; --line: #2a2f39;
529
+ --accent: #7c95ff; --good: #51cf66; --bad: #ff6b6b; --warn: #fcc419; --info: #4dabf7;
530
+ --code: #1f232b;
531
+ }
532
+ }
533
+ * { box-sizing: border-box; }
534
+ body { margin: 0; font: 14px/1.45 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; background: var(--bg); color: var(--ink); }
535
+ a { color: var(--accent); text-decoration: none; }
536
+ a:hover { text-decoration: underline; }
537
+ code, .mono, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; }
538
+ header.top { display: flex; align-items: center; gap: 16px; padding: 12px 24px; background: var(--panel); border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 1; }
539
+ header.top .brand { font-weight: 650; letter-spacing: -0.01em; }
540
+ header.top .brand small { color: var(--muted); font-weight: 400; margin-left: 6px; }
541
+ header.top .spacer { flex: 1; }
542
+ header.top label, header.top .updated { color: var(--muted); font-size: 12.5px; }
543
+ header.top button { font: inherit; padding: 4px 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); color: var(--ink); cursor: pointer; }
544
+ .layout { display: grid; grid-template-columns: 200px 1fr; min-height: calc(100vh - 53px); }
545
+ nav.sections { padding: 16px 12px; border-right: 1px solid var(--line); display: flex; flex-direction: column; gap: 2px; }
546
+ nav.sections a { padding: 7px 10px; border-radius: 6px; color: var(--ink); }
547
+ nav.sections a.active { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); font-weight: 600; }
548
+ nav.sections .note { margin-top: 16px; padding: 0 10px; color: var(--muted); font-size: 12px; }
549
+ main { padding: 20px 24px 48px; min-width: 0; }
550
+ .tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; margin-bottom: 18px; }
551
+ .tile { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; display: flex; flex-direction: column; color: var(--ink); }
552
+ .tile strong { font-size: 20px; font-variant-numeric: tabular-nums; }
553
+ .tile span { color: var(--muted); font-size: 12px; }
554
+ .tile.alert { border-color: var(--bad); }
555
+ .tile.alert strong { color: var(--bad); }
556
+ .stack { display: flex; flex-direction: column; gap: 16px; }
557
+ .card { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 16px 18px; overflow-x: auto; }
558
+ .card.error { border-color: var(--bad); }
559
+ .card h2 { margin: 0 0 12px; font-size: 15px; display: flex; align-items: center; gap: 8px; }
560
+ .card h2 .count { color: var(--muted); font-weight: 500; font-size: 12.5px; background: var(--code); border-radius: 999px; padding: 1px 8px; }
561
+ .hero h1 { margin: 2px 0 10px; font-size: 20px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
562
+ .crumbs { margin: 0; color: var(--muted); font-size: 12.5px; }
563
+ .facts { display: flex; gap: 6px; flex-wrap: wrap; }
564
+ .hint { color: var(--muted); margin: -4px 0 10px; font-size: 12.5px; }
565
+ .empty { color: var(--muted); margin: 4px 0; }
566
+ .muted { color: var(--muted); }
567
+ table { width: 100%; border-collapse: collapse; }
568
+ th { text-align: left; font-weight: 600; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; padding: 6px 10px; border-bottom: 1px solid var(--line); white-space: nowrap; }
569
+ td { padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
570
+ tr:last-child td { border-bottom: 0; }
571
+ td.num { font-variant-numeric: tabular-nums; text-align: right; }
572
+ td.cause { max-width: 420px; word-break: break-word; }
573
+ td.cause pre.trace { margin: 6px 0 0; font-size: 11.5px; color: var(--muted); white-space: pre-wrap; max-height: 220px; overflow: auto; }
574
+ td pre.json { min-width: 200px; }
575
+ td .due time { white-space: nowrap; }
576
+ .small { font-size: 11.5px; color: var(--muted); word-break: break-all; }
577
+ .badge { display: inline-block; max-width: 280px; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; padding: 1px 8px; border-radius: 999px; font-size: 12px; font-weight: 550; background: var(--code); color: var(--ink); white-space: nowrap; }
578
+ .badge.good { background: color-mix(in srgb, var(--good) 16%, transparent); color: var(--good); }
579
+ .badge.bad { background: color-mix(in srgb, var(--bad) 16%, transparent); color: var(--bad); }
580
+ .badge.warn { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
581
+ .badge.info { background: color-mix(in srgb, var(--info) 16%, transparent); color: var(--info); }
582
+ .chip { display: inline-block; padding: 2px 9px; margin: 0 4px 4px 0; border: 1px solid var(--line); border-radius: 999px; font-size: 12px; color: var(--ink); background: var(--panel); cursor: pointer; font-family: inherit; }
583
+ .chip.active { border-color: var(--accent); color: var(--accent); }
584
+ .filters { margin-bottom: 10px; }
585
+ pre.json { margin: 0; padding: 8px 10px; background: var(--code); border-radius: 6px; max-height: 260px; overflow: auto; white-space: pre-wrap; word-break: break-word; }
586
+ details summary { cursor: pointer; color: var(--accent); font-size: 12.5px; }
587
+ .due small { color: var(--muted); }
588
+ .due.overdue small { color: var(--warn); }
589
+ .state { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
590
+ .state-entry h3 { margin: 0 0 6px; font-size: 13px; }
591
+ ol.timeline { list-style: none; margin: 0; padding: 0 0 0 14px; border-left: 2px solid var(--line); display: flex; flex-direction: column; gap: 10px; }
592
+ li.event { position: relative; }
593
+ li.event::before { content: ""; position: absolute; left: -20px; top: 6px; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); }
594
+ li.event.flash pre.json { outline: 2px solid var(--accent); }
595
+ .event-head { display: flex; gap: 10px; align-items: baseline; margin-bottom: 4px; flex-wrap: wrap; }
596
+ .event-head .seq { font-weight: 650; font-variant-numeric: tabular-nums; }
597
+ article.workflow { border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px; margin-bottom: 12px; overflow-x: auto; }
598
+ article.workflow header { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
599
+ article.workflow h3 { margin: 0; font-size: 14px; }
600
+ article.workflow h4 { margin: 12px 0 6px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
601
+ article.workflow dl { display: grid; grid-template-columns: max-content 1fr; gap: 2px 12px; margin: 10px 0 0; }
602
+ article.workflow dt { color: var(--muted); }
603
+ article.workflow dd { margin: 0; }
604
+ .split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
605
+ @media (max-width: 800px) { .layout { grid-template-columns: 1fr; } nav.sections { flex-direction: row; flex-wrap: wrap; border-right: 0; border-bottom: 1px solid var(--line); } .split { grid-template-columns: 1fr; } }
606
+ `;
607
+ var SECTIONS = [
608
+ ["#/actors", "/actor", "Actors"],
609
+ ["#/outbox", "/outbox", "Outbox and timers"],
610
+ ["#/jobs", "/jobs", "Jobs"],
611
+ ["#/dead-letters", "/dead-letters", "Dead letters"],
612
+ ["#/workflows", "/workflows", "Workflows"]
613
+ ];
614
+ var escape = (text) => text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
615
+ var page = ({
616
+ api,
617
+ script
618
+ }) => `<!doctype html>
619
+ <html lang="en">
620
+ <head>
621
+ <meta charset="utf-8">
622
+ <meta name="viewport" content="width=device-width, initial-scale=1">
623
+ <title>akter dev inspector</title>
624
+ <style>${STYLES}</style>
625
+ </head>
626
+ <body data-api="${escape(api)}">
627
+ <header class="top">
628
+ <span class="brand">akter dev<small>inspector</small></span>
629
+ <span class="badge info">tenant <strong id="tenant">…</strong></span>
630
+ <span class="badge">read-only</span>
631
+ <span class="spacer"></span>
632
+ <span class="updated" id="updated"></span>
633
+ <label><input type="checkbox" id="live"> live</label>
634
+ <button type="button" id="refresh">Refresh</button>
635
+ </header>
636
+ <div class="layout">
637
+ <nav class="sections">
638
+ ${SECTIONS.map(([href, section, label]) => `<a href="${href}" data-section="${section}">${label}</a>`).join(`
639
+ `)}
640
+ <p class="note">Reads the durable inspection views. Nothing here writes.</p>
641
+ </nav>
642
+ <main>
643
+ <div id="tiles"></div>
644
+ <div id="app"><p class="empty">Loading…</p></div>
645
+ </main>
646
+ </div>
647
+ <script type="module" src="${escape(script)}"></script>
648
+ </body>
649
+ </html>`;
650
+ var bundle = Effect6.gen(function* () {
651
+ const fs = yield* FileSystem.FileSystem;
652
+ return yield* fs.readFileString(fileURLToPath(new URL(import.meta.url.endsWith(".ts") ? "../../../../.cache/inspector-client.js" : "./inspector-client.js", import.meta.url)));
653
+ });
654
+ var pageRoutes = (path) => HttpRouter.use(Effect6.fnUntraced(function* (router) {
655
+ const script = yield* bundle;
656
+ const html = page({ api: `${path}/api`, script: `${path}/client.js` });
657
+ yield* router.add("GET", path, () => Effect6.succeed(HttpServerResponse.text(html, {
658
+ contentType: "text/html; charset=utf-8",
659
+ headers: { "cache-control": "no-store" }
660
+ })));
661
+ yield* router.add("GET", `${path}/client.js`, () => Effect6.succeed(HttpServerResponse.text(script, {
662
+ contentType: "text/javascript; charset=utf-8",
663
+ headers: { "cache-control": "no-store" }
664
+ })));
665
+ }));
666
+
667
+ // src/commands/dev/run.ts
668
+ var flags3 = {
669
+ entry: Flag6.File("entry", { mustExist: true }).pipe(Flag6.withDescription("The entry module; it exports `app`, a Layer of its routes")),
670
+ databaseUrl: Flag6.Redacted("database-url").pipe(Flag6.optional, Flag6.withDescription("Run on this Postgres instead of PGlite")),
671
+ dataDir: Flag6.Directory("data-dir").pipe(Flag6.optional, Flag6.withDescription("Where PGlite keeps its files (default in memory)")),
672
+ port: Flag6.Int("port").pipe(Flag6.filter((port) => port >= 0 && port <= 65535, () => "an integer 0-65535"), Flag6.withDefault(3000), Flag6.withDescription("The port to listen on; 0 picks a free one (default 3000)")),
673
+ hostname: Flag6.String("hostname").pipe(Flag6.withDefault("127.0.0.1"), Flag6.withDescription("The address to listen on (default 127.0.0.1)")),
674
+ tenant: Flag6.String("tenant").pipe(Flag6.withDefault("default"), Flag6.withDescription("The one tenant the inspector reads (default default)"))
675
+ };
676
+ var isDevApp = (value) => Layer3.isLayer(value);
677
+ var Entry2 = Schema5.Struct({ app: Schema5.declare(isDevApp) });
678
+ var decodeEntry2 = Schema5.decodeUnknownEffect(Entry2);
679
+ var appOf = ({ module, entry }) => decodeEntry2(module).pipe(Effect7.map(({ app }) => app), Effect7.mapError(() => UsageError.make({
680
+ message: `${entry} must export \`app\`: a Layer of its routes that needs only the database`
681
+ })));
682
+ var INSPECTOR_PATH = "/_akter/inspector";
683
+ var localOperator = (tenant) => Auth.make(() => Effect7.succeed({ tenant, caller: User.make({ subject: "durable-dev" }) }));
684
+ var devRoutes = ({ app, tenant }) => Layer3.mergeAll(app, Inspector.serve({ auth: localOperator(tenant), basePath: `${INSPECTOR_PATH}/api` }), pageRoutes(INSPECTOR_PATH));
685
+ var devCommand = Command5.make("dev", flags3, (options) => Effect7.gen(function* () {
686
+ if (Option4.isSome(options.databaseUrl) && Option4.isSome(options.dataDir))
687
+ return yield* UsageError.make({
688
+ message: "--data-dir is for PGlite; drop it or --database-url"
689
+ });
690
+ const module = yield* loadEntry(options.entry);
691
+ const app = yield* appOf({ module, entry: options.entry });
692
+ const database = Option4.match(options.databaseUrl, {
693
+ onNone: () => Database3.pglite(Option4.match(options.dataDir, { onNone: () => ({}), onSome: (dataDir) => ({ dataDir }) })),
694
+ onSome: (url) => Database3.postgres({ url })
695
+ });
696
+ const storage = Option4.isSome(options.databaseUrl) ? "Postgres" : `PGlite (${Option4.getOrElse(options.dataDir, () => "in memory")})`;
697
+ const banner = Layer3.effectDiscard(HttpServer.addressFormattedWith((origin) => Console6.log([
698
+ `akter dev: ${options.entry} on ${storage}`,
699
+ ` app ${origin}`,
700
+ ` inspector ${origin}${INSPECTOR_PATH} (tenant ${options.tenant})`
701
+ ].join(`
702
+ `))));
703
+ return banner.pipe(Layer3.provideMerge(HttpRouter2.serve(devRoutes({ app, tenant: options.tenant }), { disableListenLog: true })), Layer3.provide(PlatformHttpServer.layer({ port: options.port, hostname: options.hostname })), Layer3.provide(database), Layer3.provide(PlatformCrypto.layer));
704
+ }).pipe(Effect7.flatMap(Layer3.launch), Effect7.catchTag("UsageError", (error) => fail({ reason: error._tag, message: error.message })))).pipe(Command5.withDescription("Run the entry's app locally with a read-only inspector at /_akter/inspector"));
705
+
706
+ // src/commands/export/run.ts
707
+ import { Seed, SeedJson } from "@rikalabs/akter/runtime";
708
+ import { Effect as Effect8, FileSystem as FileSystem2, Schema as Schema6 } from "effect";
709
+ import { Command as Command6, Flag as Flag7 } from "effect/cli";
710
+ var flags4 = {
711
+ actor: actorArgument,
712
+ tenant,
713
+ output: Flag7.File("output").pipe(Flag7.withDescription("The seed file to create; an existing file is never replaced")),
714
+ ...operatorFlags
715
+ };
716
+ var ExportAnswer = Schema6.Struct({
717
+ output: Schema6.String,
718
+ actor: Schema6.String,
719
+ state: Schema6.Finite,
720
+ intents: Schema6.Finite,
721
+ jobs: Schema6.Finite,
722
+ omitted: Schema6.Struct({
723
+ receipts: Schema6.Finite,
724
+ events: Schema6.Finite,
725
+ workflows: Schema6.Finite,
726
+ deadLetters: Schema6.Finite,
727
+ tableRows: Schema6.Finite,
728
+ blobs: Schema6.Finite
729
+ })
730
+ });
731
+ var exportSeed = Effect8.fnUntraced(function* ({
732
+ options,
733
+ token
734
+ }) {
735
+ const answer = yield* operatorRequest({
736
+ url: options.urls[0],
737
+ path: `/operator/actors/${encodeURIComponent(options.actor.actorType)}/${encodeURIComponent(options.actor.actorId)}/export?${new URLSearchParams({ tenant: options.tenant })}`,
738
+ token
739
+ });
740
+ const seed = yield* Schema6.decodeUnknownEffect(Seed)(answer);
741
+ const fs = yield* FileSystem2.FileSystem;
742
+ yield* fs.writeFileString(options.output, `${yield* Schema6.encodeEffect(SeedJson)(seed).pipe(Effect8.orDie)}
743
+ `, { flag: "wx", mode: 384 });
744
+ return {
745
+ output: options.output,
746
+ actor: `${seed.actor.type}/${seed.actor.id}`,
747
+ state: Object.keys(seed.state).length,
748
+ intents: seed.intents.length,
749
+ jobs: seed.jobs.length,
750
+ omitted: seed.omitted
751
+ };
752
+ });
753
+ var formatExport = (answer) => Effect8.map(Schema6.decodeUnknownEffect(ExportAnswer)(answer), (exported) => [
754
+ `Exported ${exported.actor} to ${exported.output}`,
755
+ `carries ${exported.state} state keys, ${exported.intents} pending intents, ${exported.jobs} pending jobs`,
756
+ `omits ${exported.omitted.receipts} receipts, ${exported.omitted.events} events, ${exported.omitted.workflows} workflows, ${exported.omitted.deadLetters} dead letters, ${exported.omitted.tableRows} owned-table rows, ${exported.omitted.blobs} blob entries`
757
+ ].join(`
758
+ `));
759
+ var exportCommand = Command6.make("export", flags4, (options) => operatorCommand({ options, request: exportSeed, format: formatExport })).pipe(Command6.withDescription("Write one actor's state and pending intents and jobs to a new seed file, through the first runner named"));
760
+
761
+ // src/commands/fleet/run.ts
762
+ import { Database as Database4, rebuildFleetView, setupFleet } from "@rikalabs/akter/runtime";
763
+ import { Console as Console7, Effect as Effect9, Layer as Layer4, Predicate } from "effect";
764
+ import { Argument as Argument4, Command as Command7, Flag as Flag8 } from "effect/cli";
765
+ var isView = (value) => Predicate.hasProperty(value, "definitionHash") && Predicate.hasProperty(value, "groupColumns") && Predicate.hasProperty(value, "table");
766
+ var viewsOf = ({ module, entry }) => {
767
+ const fleet = module.fleet;
768
+ return Array.isArray(fleet) && fleet.length > 0 && fleet.every(isView) ? Effect9.succeed(fleet) : Effect9.fail(UsageError.make({ message: `${entry} must export a \`fleet\` array of Fleet.view values` }));
769
+ };
770
+ var setup = (views) => setupFleet(views).pipe(Effect9.map((result) => [
771
+ `Full replica identity and publication durable_fleet: ${result.sources.join(", ")}`,
772
+ `Replication slot durable_fleet: ${result.slot}`
773
+ ].join(`
774
+ `)));
775
+ var rebuild = (view) => rebuildFleetView(view).pipe(Effect9.map((found) => found ? { output: `${view} is building; the maintainer rebuilds it from its source`, exitCode: 0 } : { output: `No fleet view ${view} is registered on this database`, exitCode: 1 }));
776
+ var onDatabase = (databaseUrl, run) => Effect9.gen(function* () {
777
+ const services = yield* Layer4.build(Database4.postgres({ url: databaseUrl }).pipe(Layer4.provideMerge(PlatformCrypto.layer)));
778
+ const { output, exitCode } = yield* run.pipe(Effect9.provideContext(services));
779
+ yield* Console7.log(output);
780
+ if (exitCode !== 0)
781
+ return yield* CommandFailed.make({ exitCode, reason: "Refused" });
782
+ }).pipe(Effect9.scoped);
783
+ var databaseUrl = Flag8.Redacted("database-url").pipe(Flag8.withDescription("The application's Postgres URL"));
784
+ var setupCommand = Command7.make("setup", {
785
+ entry: Flag8.File("entry", { mustExist: true }).pipe(Flag8.withDescription("The entry module; it exports a `fleet` array of Fleet.view values")),
786
+ databaseUrl
787
+ }, (options) => Effect9.gen(function* () {
788
+ const views = yield* viewsOf({
789
+ module: yield* loadEntry(options.entry),
790
+ entry: options.entry
791
+ });
792
+ return yield* onDatabase(options.databaseUrl, setup(views).pipe(Effect9.map((output) => ({ output, exitCode: 0 }))));
793
+ }).pipe(Effect9.catchTags({
794
+ SqlError: (error) => fail({ reason: error._tag, message: `Cannot set up fleet views: ${error.message}` }),
795
+ FleetSetupRefused: (error) => fail({ reason: error._tag, message: error.message }),
796
+ UsageError: (error) => fail({ reason: error._tag, message: error.message })
797
+ }))).pipe(Command7.withDescription("Give the entry's fleet view sources full replica identity, publish them, and create the logical slot; needs wal_level=logical"));
798
+ var rebuildCommand = Command7.make("rebuild", {
799
+ view: Argument4.String("view").pipe(Argument4.withDescription("The fleet view to rebuild")),
800
+ databaseUrl
801
+ }, (options) => onDatabase(options.databaseUrl, rebuild(options.view)).pipe(Effect9.catchTags({
802
+ SqlError: (error) => fail({ reason: error._tag, message: `Cannot rebuild ${options.view}: ${error.message}` })
803
+ }))).pipe(Command7.withDescription("Rebuild a fleet view from its source, clearing its error; exit 1 when no runtime registered it"));
804
+
805
+ // src/commands/inspect/show.ts
806
+ import { Inspection } from "@rikalabs/akter/client";
807
+ import { Effect as Effect10, Schema as Schema7, Struct } from "effect";
808
+ import { Command as Command8, Flag as Flag9 } from "effect/cli";
809
+ var flags5 = {
810
+ actor: actorArgument,
811
+ tenant,
812
+ limit: Flag9.Int("receipts").pipe(Flag9.filter((count) => count >= 1 && count <= 1000, () => "an integer from 1 to 1000"), Flag9.withDefault(20), Flag9.withDescription("How many of the newest receipts to show, 1 to 1000 (default 20)")),
813
+ ...operatorFlags
814
+ };
815
+ var ActorPage = Schema7.Struct({
816
+ actor: Inspection.ActorRow.mapFields(Struct.pick(["actorType", "actorId", "generation", "lastEventSequence"])),
817
+ state: Schema7.Array(Inspection.ActorDetail.fields.state.value.mapFields(Struct.pick(["key", "value"]))),
818
+ receipts: Schema7.Array(Inspection.OperatorActorDetail.fields.receipts.value.mapFields(Struct.pick(["commandId", "command", "outcomeTag", "outcome"]))),
819
+ deadLetters: Schema7.Array(Inspection.DeadLetterRow.mapFields(Struct.pick(["jobId", "job", "attempts", "ambiguous", "cause"]))),
820
+ totals: Inspection.ActorDetail.fields.totals
821
+ });
822
+ var decodeActorPage = Schema7.decodeUnknownEffect(ActorPage);
823
+ var shown = (value) => value === undefined ? "(outcome needs receipts.read)" : value === null ? "null" : ("json" in value) ? JSON.stringify(value.json) : `(undecodable: ${value.undecodable})`;
824
+ var formatActor = (page) => [
825
+ `${page.actor.actorType}/${page.actor.actorId} generation ${page.actor.generation} events through ${page.actor.lastEventSequence}`,
826
+ `totals receipts ${page.totals.receipts} events ${page.totals.events} outbox ${page.totals.outbox} jobs ${page.totals.jobs} dead letters ${page.totals.deadLetters} workflows ${page.totals.workflows}`,
827
+ "state",
828
+ ...page.state.map(({ key, value }) => ` ${key} = ${shown(value)}`),
829
+ "receipts (newest first)",
830
+ ...page.receipts.map((receipt) => ` ${receipt.command} ${receipt.commandId} ${receipt.outcomeTag ?? "?"} ${shown(receipt.outcome)}`),
831
+ ...page.deadLetters.length === 0 ? [] : [
832
+ "dead letters",
833
+ ...page.deadLetters.map((letter) => ` ${letter.jobId} ${letter.job} attempts ${letter.attempts}${letter.ambiguous ? " ambiguous" : ""} ${letter.cause.split(`
834
+ `)[0] ?? ""}`)
835
+ ]
836
+ ].join(`
837
+ `);
838
+ var inspect = ({
839
+ options,
840
+ token
841
+ }) => operatorRequest({
842
+ url: options.urls[0],
843
+ path: `/operator/actors/${encodeURIComponent(options.actor.actorType)}/${encodeURIComponent(options.actor.actorId)}?${new URLSearchParams({ tenant: options.tenant, limit: String(options.limit) })}`,
844
+ token
845
+ });
846
+ var formatInspection = (answer) => Effect10.map(decodeActorPage(answer), formatActor);
847
+ var inspectCommand = Command8.make("inspect", flags5, (options) => operatorCommand({ options, request: inspect, format: formatInspection })).pipe(Command8.withDescription("Read one actor's state, newest receipts, and dead letters through the first runner named"));
848
+
849
+ // src/commands/payloads/run.ts
850
+ import { checkPayloads, clearPayloads, formatPayloadProblem } from "@rikalabs/akter/runtime";
851
+ import { Effect as Effect11 } from "effect";
852
+ import { Command as Command9 } from "effect/cli";
853
+ var checkReport = ({
854
+ problems,
855
+ json
856
+ }) => ({
857
+ output: json ? JSON.stringify({ compatible: problems.length === 0, problems }, null, 2) : problems.length === 0 ? "Every stored event and job payload version decodes" : [
858
+ ...problems.map(formatPayloadProblem),
859
+ `${problems.length} problem${problems.length === 1 ? "" : "s"}; deploy refused (exit 1)`
860
+ ].join(`
861
+ `),
862
+ exitCode: problems.length === 0 ? 0 : 1
863
+ });
864
+ var reasons = {
865
+ cleared: "cleared",
866
+ writer: "not cleared: a runtime wrote it within its writer window and command timeout",
867
+ stored: "not cleared: events of this version are still stored"
868
+ };
869
+ var clearReport = ({
870
+ results,
871
+ json
872
+ }) => ({
873
+ output: json ? JSON.stringify({ results }, null, 2) : results.length === 0 ? "No superseded event version is past its retention horizon" : results.map((result) => `${result.actorType}/${result.tag} version ${result.version} ${reasons[result.outcome]}`).join(`
874
+ `),
875
+ exitCode: results.every((result) => result.outcome === "cleared") ? 0 : 1
876
+ });
877
+ var payloads = ({
878
+ command,
879
+ actors,
880
+ json
881
+ }) => command === "check" ? checkPayloads(actors).pipe(Effect11.map((problems) => checkReport({ problems, json }))) : clearPayloads(actors).pipe(Effect11.map((results) => clearReport({ results, json })));
882
+ var checkCommand2 = Command9.make("check", entryFlags, (options) => entryCommand({
883
+ options,
884
+ reading: "payload versions",
885
+ run: (actors) => payloads({ command: "check", actors, json: options.json })
886
+ })).pipe(Command9.withDescription("Check that every stored event and job payload version still decodes, read-only; exit 1 when a deploy would be refused"));
887
+ var clearCommand = Command9.make("clear", entryFlags, (options) => entryCommand({
888
+ options,
889
+ reading: "payload versions",
890
+ run: (actors) => payloads({ command: "clear", actors, json: options.json })
891
+ })).pipe(Command9.withDescription("Mark superseded event versions past their retention horizon cleared; exit 1 when one stays uncleared"));
892
+
893
+ // src/commands/receipts/show.ts
894
+ import { Command as Command10, Argument as Argument5 } from "effect/cli";
895
+ var flags6 = {
896
+ actor: actorArgument,
897
+ commandId: Argument5.String("commandId").pipe(Argument5.withDescription("The command id whose stored outcome to show")),
898
+ tenant,
899
+ ...operatorFlags
900
+ };
901
+ var showReceipt = ({
902
+ options,
903
+ token
904
+ }) => operatorRequest({
905
+ url: options.urls[0],
906
+ path: `/operator/receipts/${encodeURIComponent(options.actor.actorType)}/${encodeURIComponent(options.actor.actorId)}/${encodeURIComponent(options.commandId)}?${new URLSearchParams({ tenant: options.tenant })}`,
907
+ token
908
+ });
909
+ var showCommand = Command10.make("show", flags6, (options) => operatorCommand({ options, request: showReceipt, format: encodeJson })).pipe(Command10.withDescription("Print one receipt's stored outcome as JSON; the runner never runs the command to answer"));
910
+
911
+ // src/commands/subscriptions/list.ts
912
+ import { Effect as Effect12, Option as Option5, Schema as Schema8 } from "effect";
913
+ import { Command as Command11, Flag as Flag10 } from "effect/cli";
914
+ var count = (name, maximum, description) => Flag10.Int(name).pipe(Flag10.filter((value) => value >= 1 && value <= maximum, () => `an integer from 1 to ${maximum}`), Flag10.optional, Flag10.withDescription(description));
915
+ var flags7 = {
916
+ lagging: Flag10.Boolean("lagging").pipe(Flag10.withDescription("List the rows whose deliveries keep failing; the only listing, so required")),
917
+ tenant,
918
+ minAttempts: count("min-attempts", 1e6, "Only rows with at least this many failed attempts"),
919
+ limit: count("limit", 1000, "At most this many rows, 1 to 1000"),
920
+ ...operatorFlags
921
+ };
922
+ var list = ({
923
+ options,
924
+ token
925
+ }) => {
926
+ const params = new URLSearchParams({ tenant: options.tenant });
927
+ if (Option5.isSome(options.minAttempts))
928
+ params.set("minAttempts", String(options.minAttempts.value));
929
+ if (Option5.isSome(options.limit))
930
+ params.set("limit", String(options.limit.value));
931
+ return operatorRequest({
932
+ url: options.urls[0],
933
+ path: `/operator/subscriptions/lagging?${params}`,
934
+ token
935
+ });
936
+ };
937
+ var Row = Schema8.Struct({
938
+ sourceType: Schema8.String,
939
+ sourceId: Schema8.String,
940
+ subscriberType: Schema8.String,
941
+ subscription: Schema8.String,
942
+ subscriberId: Schema8.String,
943
+ delivered: Schema8.String,
944
+ head: Schema8.String,
945
+ lag: Schema8.String,
946
+ attempts: Schema8.Finite,
947
+ lastError: Schema8.String
948
+ });
949
+ var decodeRows = Schema8.decodeUnknownEffect(Schema8.Array(Row));
950
+ var formatLagging = (answer) => Effect12.map(decodeRows(answer), (rows) => rows.length === 0 ? "no lagging subscriptions" : rows.flatMap((row) => [
951
+ `${row.sourceType}/${row.sourceId} -> ${row.subscriberType}.${row.subscription}/${row.subscriberId} delivered ${row.delivered} of ${row.head} lag ${row.lag} attempts ${row.attempts}`,
952
+ ` ${row.lastError.split(`
953
+ `)[0] ?? ""}`
954
+ ]).join(`
955
+ `));
956
+ var listCommand2 = Command11.make("list", flags7, (options) => operatorCommand({ options, request: list, format: formatLagging })).pipe(Command11.withDescription("List subscription rows whose deliveries keep failing, with their lag and last error"));
957
+
958
+ // src/commands/subscriptions/skip.ts
959
+ import { Command as Command12, Flag as Flag11 } from "effect/cli";
960
+ var flags8 = {
961
+ source: actorFlag({
962
+ name: "source",
963
+ description: "The source actor whose events the row delivers, as <Type>/<id>"
964
+ }),
965
+ subscriber: actorFlag({
966
+ name: "subscriber",
967
+ description: "The subscribing actor, as <Type>/<id>"
968
+ }),
969
+ subscription: Flag11.String("subscription").pipe(Flag11.filter((name) => name.length > 0, () => "a subscription name"), Flag11.withDescription("The subscription's name on the subscriber")),
970
+ through: Flag11.String("through").pipe(Flag11.filter((cursor) => /^[1-9][0-9]{0,17}$/.test(cursor), () => "a positive event cursor"), Flag11.withDescription("Skip every event up to and including this source event cursor")),
971
+ tenant,
972
+ reason,
973
+ ...operatorFlags
974
+ };
975
+ var skip = ({
976
+ options,
977
+ token
978
+ }) => operatorRequest({
979
+ url: options.urls[0],
980
+ path: "/operator/subscriptions/skip",
981
+ token,
982
+ body: {
983
+ tenant: options.tenant,
984
+ sourceType: options.source.actorType,
985
+ sourceId: options.source.actorId,
986
+ subscriberType: options.subscriber.actorType,
987
+ subscription: options.subscription,
988
+ subscriberId: options.subscriber.actorId,
989
+ through: options.through,
990
+ reason: options.reason
991
+ }
992
+ });
993
+ var skipCommand = Command12.make("skip", flags8, (options) => operatorCommand({ options, request: skip, format: encodeJson })).pipe(Command12.withDescription("Skip a stuck subscription row's events through a cursor; the runner audits the skip"));
994
+
995
+ // ../../packages/cloud-api/src/errors.ts
996
+ import { Schema as Schema9 } from "effect";
997
+
998
+ class Unauthorized extends Schema9.TaggedError()("Unauthorized", {
999
+ code: Schema9.Literals(["missing_credentials", "invalid_credentials", "expired"]),
1000
+ message: Schema9.String
1001
+ }, { httpApiStatus: 401 }) {
1002
+ }
1003
+
1004
+ class Forbidden extends Schema9.TaggedError()("Forbidden", { message: Schema9.String }, { httpApiStatus: 403 }) {
1005
+ }
1006
+ var NotFoundResource = Schema9.Literals([
1007
+ "actor",
1008
+ "actor-type",
1009
+ "api-key",
1010
+ "command",
1011
+ "cursor",
1012
+ "deployment",
1013
+ "domain",
1014
+ "environment",
1015
+ "invitation",
1016
+ "live deployment",
1017
+ "member",
1018
+ "organization",
1019
+ "project",
1020
+ "source"
1021
+ ]);
1022
+
1023
+ class NotFound extends Schema9.TaggedError()("NotFound", { resource: NotFoundResource, id: Schema9.String }, { httpApiStatus: 404 }) {
1024
+ }
1025
+
1026
+ class Conflict extends Schema9.TaggedError()("Conflict", { message: Schema9.String }, { httpApiStatus: 409 }) {
1027
+ }
1028
+
1029
+ class NotImplemented extends Schema9.TaggedError()("NotImplemented", { operation: Schema9.String }, { httpApiStatus: 501 }) {
1030
+ }
1031
+ var UnavailableReason = Schema9.Literals(["unknownPlan"]);
1032
+
1033
+ class Unavailable extends Schema9.TaggedError()("Unavailable", {
1034
+ message: Schema9.String,
1035
+ retryAfterSeconds: Schema9.Int.check(Schema9.isGreaterThanOrEqualTo(1)),
1036
+ reason: Schema9.optionalKey(UnavailableReason)
1037
+ }, { httpApiStatus: 503 }) {
1038
+ }
1039
+
1040
+ class PayloadTooLarge extends Schema9.TaggedError()("PayloadTooLarge", { limitBytes: Schema9.Int }, { httpApiStatus: 413 }) {
1041
+ }
1042
+ var ReadErrors = [Forbidden, NotFound, NotImplemented, Unavailable];
1043
+ var WriteErrors = [Forbidden, NotFound, Conflict, NotImplemented, Unavailable];
1044
+ var SelfErrors = [Forbidden, NotImplemented];
1045
+ // ../../packages/cloud-api/src/auth.ts
1046
+ import { Context, Schema as Schema11 } from "effect";
1047
+ import { HttpApiMiddleware, HttpApiSecurity } from "effect/http-api";
1048
+
1049
+ // ../../packages/cloud-api/src/primitives.ts
1050
+ import { Schema as Schema10 } from "effect";
1051
+ var identifier = Schema10.String.pipe(Schema10.check(Schema10.isMinLength(1), Schema10.isMaxLength(128)));
1052
+ var UserId = identifier.pipe(Schema10.brand("UserId"));
1053
+ var OrganizationId = identifier.pipe(Schema10.brand("OrganizationId"));
1054
+ var MemberId = identifier.pipe(Schema10.brand("MemberId"));
1055
+ var InvitationId = identifier.pipe(Schema10.brand("InvitationId"));
1056
+ var ApiKeyId = identifier.pipe(Schema10.brand("ApiKeyId"));
1057
+ var ProjectId = identifier.pipe(Schema10.brand("ProjectId"));
1058
+ var DeploymentId = identifier.pipe(Schema10.brand("DeploymentId"));
1059
+ var DomainId = identifier.pipe(Schema10.brand("DomainId"));
1060
+ var DeadLetterId = identifier.pipe(Schema10.brand("DeadLetterId"));
1061
+ var InvoiceId = identifier.pipe(Schema10.brand("InvoiceId"));
1062
+ var EnvironmentId = identifier.pipe(Schema10.brand("EnvironmentId"));
1063
+ var Slug = Schema10.String.pipe(Schema10.check(Schema10.isPattern(/^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/)), Schema10.brand("Slug"));
1064
+ var Name = Schema10.String.pipe(Schema10.check(Schema10.isPattern(/^\S(?:.{0,98}\S)?$/)), Schema10.brand("Name"));
1065
+ var Email = Schema10.String.pipe(Schema10.check(Schema10.isPattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/), Schema10.isMaxLength(254)), Schema10.brand("Email"));
1066
+ var NonNegativeInt = Schema10.Int.pipe(Schema10.check(Schema10.isGreaterThanOrEqualTo(0)));
1067
+ var NonNegative = Schema10.Finite.pipe(Schema10.check(Schema10.isGreaterThanOrEqualTo(0)));
1068
+ var Timestamp = Schema10.DateTimeUtc;
1069
+ var CalendarDay = Schema10.String.pipe(Schema10.check(Schema10.isPattern(/^\d{4}-\d{2}-\d{2}$/)));
1070
+ var BillingPeriod = Schema10.String.pipe(Schema10.check(Schema10.isPattern(/^\d{4}-(?:0[1-9]|1[0-2])$/)));
1071
+ var Role = Schema10.Literals(["owner", "admin", "member", "viewer"]);
1072
+ var InviteRole = Schema10.Literals(["admin", "member", "viewer"]);
1073
+ var EnvironmentName = Schema10.Literals(["production", "staging", "dev"]);
1074
+ var RegionId = Schema10.Literals(["us-east-1", "us-west-2"]);
1075
+ var CommitSha = Schema10.String.pipe(Schema10.check(Schema10.isPattern(/^[0-9a-f]{7,40}$/)));
1076
+ var ActorAddress = Schema10.String.pipe(Schema10.check(Schema10.isMinLength(3), Schema10.isMaxLength(1024), Schema10.isPattern(/^[^/]+\/.+$/)));
1077
+ var ActorReference = Schema10.Struct({
1078
+ kind: Schema10.Literals(["user", "api-key"]),
1079
+ id: Schema10.String,
1080
+ name: Schema10.NullOr(Schema10.String)
1081
+ });
1082
+ var SeriesPoint = Schema10.Struct({ at: Timestamp, value: Schema10.Finite });
1083
+ var pageQuery = {
1084
+ limit: Schema10.optional(Schema10.Int.pipe(Schema10.check(Schema10.isBetween({ minimum: 1, maximum: 100 })))),
1085
+ cursor: Schema10.optional(Schema10.String)
1086
+ };
1087
+ var Page = (item) => Schema10.Struct({ items: Schema10.Array(item), nextCursor: Schema10.NullOr(Schema10.String) });
1088
+
1089
+ // ../../packages/cloud-api/src/auth.ts
1090
+ var sessionCookieName = "better-auth.session_token";
1091
+ var secureSessionCookieName = "__Secure-better-auth.session_token";
1092
+ var apiKeyHeaderName = "x-api-key";
1093
+ var SessionIdentity = Schema11.TaggedStruct("session", {
1094
+ userId: UserId,
1095
+ sessionId: Schema11.String,
1096
+ activeOrganizationId: Schema11.NullOr(OrganizationId)
1097
+ });
1098
+ var ApiKeyIdentity = Schema11.TaggedStruct("api-key", {
1099
+ keyId: ApiKeyId,
1100
+ organizationId: OrganizationId,
1101
+ permission: Schema11.Literals(["read", "write", "admin"])
1102
+ });
1103
+ var Identity = Schema11.Union([SessionIdentity, ApiKeyIdentity]);
1104
+
1105
+ class CurrentIdentity extends Context.Service()("@akter/cloud-api/auth/CurrentIdentity") {
1106
+ }
1107
+
1108
+ class Authentication extends HttpApiMiddleware.Service()("@akter/cloud-api/Authentication", {
1109
+ security: {
1110
+ session: HttpApiSecurity.apiKey({ key: sessionCookieName, in: "cookie" }),
1111
+ secureSession: HttpApiSecurity.apiKey({ key: secureSessionCookieName, in: "cookie" }),
1112
+ apiKey: HttpApiSecurity.apiKey({ key: apiKeyHeaderName, in: "header" }),
1113
+ bearer: HttpApiSecurity.bearer
1114
+ },
1115
+ error: [Unauthorized, Forbidden, Unavailable]
1116
+ }) {
1117
+ }
1118
+ // ../../packages/cloud-api/src/identity.ts
1119
+ import { Schema as Schema12 } from "effect";
1120
+ var PlanId = Schema12.Literals(["free", "pro", "team", "enterprise"]);
1121
+ var UnboundPlan = Schema12.TaggedStruct("unbound", {});
1122
+ var KnownPlan = Schema12.TaggedStruct("known", { id: PlanId });
1123
+ var UnknownPlan = Schema12.TaggedStruct("unknown", { id: Schema12.String });
1124
+ var OrganizationPlan = Schema12.Union([UnboundPlan, KnownPlan, UnknownPlan]);
1125
+ var User2 = Schema12.Struct({
1126
+ id: UserId,
1127
+ name: Schema12.String,
1128
+ email: Email,
1129
+ emailVerified: Schema12.Boolean,
1130
+ image: Schema12.NullOr(Schema12.String)
1131
+ });
1132
+ var Organization = Schema12.Struct({
1133
+ id: OrganizationId,
1134
+ name: Schema12.String,
1135
+ slug: Slug,
1136
+ plan: OrganizationPlan,
1137
+ createdAt: Timestamp
1138
+ });
1139
+ var OrganizationMembership = Schema12.Struct({
1140
+ organization: Organization,
1141
+ role: Role
1142
+ });
1143
+ var Me = Schema12.Struct({
1144
+ user: Schema12.NullOr(User2),
1145
+ identityKind: Schema12.Literals(["session", "api-key"]),
1146
+ activeOrganizationId: Schema12.NullOr(OrganizationId),
1147
+ organizations: Schema12.Array(OrganizationMembership)
1148
+ });
1149
+ var UpdateProfile = Schema12.Struct({
1150
+ name: Schema12.optional(Name),
1151
+ image: Schema12.optional(Schema12.NullOr(Schema12.String))
1152
+ });
1153
+ var DeleteAccount = Schema12.Struct({ confirmation: Email });
1154
+ var PersonalDataExport = Schema12.Struct({
1155
+ userId: UserId,
1156
+ exportedAt: Timestamp,
1157
+ tables: Schema12.Record(Schema12.String, Schema12.Array(Schema12.Json))
1158
+ });
1159
+ var OrganizationDeletion = Schema12.Struct({
1160
+ organizationId: OrganizationId,
1161
+ phase: Schema12.Literals(["billing", "runners", "databases", "records", "completed"]),
1162
+ blocked: Schema12.Boolean
1163
+ });
1164
+ var SetActiveOrganization = Schema12.Struct({ organizationId: OrganizationId });
1165
+ var CreateOrganization = Schema12.Struct({ name: Name, slug: Slug });
1166
+ var UpdateOrganization = Schema12.Struct({
1167
+ name: Schema12.optional(Name),
1168
+ slug: Schema12.optional(Slug)
1169
+ });
1170
+ var MemberUser = Schema12.Struct({
1171
+ id: UserId,
1172
+ name: Schema12.String,
1173
+ email: Email,
1174
+ image: Schema12.NullOr(Schema12.String)
1175
+ });
1176
+ var Member = Schema12.Struct({
1177
+ id: MemberId,
1178
+ user: MemberUser,
1179
+ role: Role,
1180
+ createdAt: Timestamp,
1181
+ lastActiveAt: Schema12.NullOr(Timestamp)
1182
+ });
1183
+ var UpdateMemberRole = Schema12.Struct({ role: Role });
1184
+ var InvitationStatus = Schema12.Literals([
1185
+ "pending",
1186
+ "accepted",
1187
+ "declined",
1188
+ "canceled",
1189
+ "expired"
1190
+ ]);
1191
+ var Invitation = Schema12.Struct({
1192
+ id: InvitationId,
1193
+ organizationId: OrganizationId,
1194
+ email: Email,
1195
+ role: InviteRole,
1196
+ status: InvitationStatus,
1197
+ invitedBy: Schema12.Struct({ id: UserId, name: Schema12.String }),
1198
+ createdAt: Timestamp,
1199
+ expiresAt: Timestamp
1200
+ });
1201
+ var CreateInvitation = Schema12.Struct({ email: Email, role: InviteRole });
1202
+ var InvitationPreview = Schema12.Struct({
1203
+ id: InvitationId,
1204
+ email: Email,
1205
+ role: InviteRole,
1206
+ status: InvitationStatus,
1207
+ expiresAt: Timestamp,
1208
+ inviterName: Schema12.String,
1209
+ organization: Schema12.Struct({
1210
+ id: OrganizationId,
1211
+ name: Schema12.String,
1212
+ slug: Slug,
1213
+ plan: OrganizationPlan,
1214
+ memberCount: NonNegativeInt
1215
+ })
1216
+ });
1217
+ var ApiKeyPermission = Schema12.Literals(["read", "write", "admin"]);
1218
+ var ApiKey = Schema12.Struct({
1219
+ id: ApiKeyId,
1220
+ organizationId: OrganizationId,
1221
+ name: Schema12.String,
1222
+ prefix: Schema12.String,
1223
+ lastFour: Schema12.String,
1224
+ permission: ApiKeyPermission,
1225
+ projectId: Schema12.NullOr(ProjectId),
1226
+ createdAt: Timestamp,
1227
+ createdBy: ActorReference,
1228
+ lastUsedAt: Schema12.NullOr(Timestamp),
1229
+ expiresAt: Schema12.NullOr(Timestamp),
1230
+ revokedAt: Schema12.NullOr(Timestamp)
1231
+ });
1232
+ var CreateApiKey = Schema12.Struct({
1233
+ name: Name,
1234
+ permission: ApiKeyPermission,
1235
+ projectId: Schema12.optional(ProjectId),
1236
+ expiresAt: Schema12.optional(Timestamp)
1237
+ });
1238
+ var CreatedApiKey = Schema12.Struct({
1239
+ key: ApiKey,
1240
+ secret: Schema12.String
1241
+ });
1242
+ var Theme = Schema12.Literals(["light", "dark", "system"]);
1243
+ var Preferences = Schema12.Struct({
1244
+ defaultEnvironment: EnvironmentName,
1245
+ openActorLinksInNewTab: Schema12.Boolean,
1246
+ timeZone: Schema12.String,
1247
+ pauseLiveTailOnScroll: Schema12.Boolean,
1248
+ showReplayedCommands: Schema12.Boolean,
1249
+ theme: Theme
1250
+ });
1251
+ var UpdatePreferences = Schema12.Struct({
1252
+ defaultEnvironment: Schema12.optional(EnvironmentName),
1253
+ openActorLinksInNewTab: Schema12.optional(Schema12.Boolean),
1254
+ timeZone: Schema12.optional(Schema12.String),
1255
+ pauseLiveTailOnScroll: Schema12.optional(Schema12.Boolean),
1256
+ showReplayedCommands: Schema12.optional(Schema12.Boolean),
1257
+ theme: Schema12.optional(Theme)
1258
+ });
1259
+ var NotificationEvent = Schema12.Literals([
1260
+ "deploy_failed",
1261
+ "dead_letter",
1262
+ "spend_threshold"
1263
+ ]);
1264
+ var NotificationPreference = Schema12.Struct({
1265
+ event: NotificationEvent,
1266
+ email: Schema12.Boolean,
1267
+ slack: Schema12.Boolean
1268
+ });
1269
+ var NotificationSettings = Schema12.Struct({
1270
+ preferences: Schema12.Array(NotificationPreference)
1271
+ });
1272
+ var PinnedActor = Schema12.Struct({
1273
+ projectId: ProjectId,
1274
+ environment: EnvironmentName,
1275
+ address: Schema12.String,
1276
+ status: Schema12.Literals(["awake", "idle", "unknown"]),
1277
+ lastActivityAt: Schema12.NullOr(Timestamp)
1278
+ });
1279
+ var PinActor = Schema12.Struct({
1280
+ projectId: ProjectId,
1281
+ environment: EnvironmentName,
1282
+ address: Schema12.String
1283
+ });
1284
+ // ../../packages/cloud-api/src/projects.ts
1285
+ import { Schema as Schema13 } from "effect";
1286
+ var ProjectStatus = Schema13.Literals(["empty", "live", "deploying", "failed"]);
1287
+ var Region = Schema13.Struct({ id: RegionId, city: Schema13.String });
1288
+ var Project = Schema13.Struct({
1289
+ id: ProjectId,
1290
+ organizationId: OrganizationId,
1291
+ name: Schema13.String,
1292
+ slug: Slug,
1293
+ status: ProjectStatus,
1294
+ homeRegion: RegionId,
1295
+ createdAt: Timestamp
1296
+ });
1297
+ var CreateProject = Schema13.Struct({ name: Name, slug: Slug, homeRegion: RegionId });
1298
+ var UpdateProject = Schema13.Struct({
1299
+ name: Schema13.optional(Name),
1300
+ slug: Schema13.optional(Slug)
1301
+ });
1302
+ var Environment = Schema13.Struct({
1303
+ name: EnvironmentName,
1304
+ projectId: ProjectId,
1305
+ currentDeploymentId: Schema13.NullOr(DeploymentId),
1306
+ database: Schema13.optionalKey(Schema13.Struct({
1307
+ source: Schema13.Literal("customer"),
1308
+ state: Schema13.Literals(["missing", "reachable", "unreachable"]),
1309
+ latency: Schema13.NullOr(NonNegative),
1310
+ latencyWarning: Schema13.Boolean,
1311
+ runnerCap: Schema13.NullOr(Schema13.Int)
1312
+ }))
1313
+ });
1314
+ var CreateEnvironment = Schema13.Struct({ name: EnvironmentName });
1315
+ var EnvVariable = Schema13.Struct({
1316
+ name: Schema13.String,
1317
+ usedBy: Schema13.Array(Schema13.String),
1318
+ updatedAt: Timestamp,
1319
+ updatedBy: Schema13.NullOr(ActorReference)
1320
+ });
1321
+ var EnvVariableName = Schema13.String.pipe(Schema13.check(Schema13.isPattern(/^[A-Za-z_][A-Za-z0-9_]{0,255}$/)));
1322
+ var SetEnvVariable = Schema13.Struct({
1323
+ value: Schema13.String.pipe(Schema13.check(Schema13.isMaxLength(65536)))
1324
+ });
1325
+ var ImportEnvVariables = Schema13.Struct({
1326
+ content: Schema13.String.pipe(Schema13.check(Schema13.isMaxLength(1048576)))
1327
+ });
1328
+ var ImportEnvVariablesResult = Schema13.Struct({
1329
+ created: Schema13.Array(Schema13.String),
1330
+ updated: Schema13.Array(Schema13.String)
1331
+ });
1332
+ var DomainStatus = Schema13.Literals(["pending", "verifying", "active"]);
1333
+ var DnsRecord = Schema13.Struct({
1334
+ type: Schema13.Literals(["A", "AAAA", "CNAME", "TXT"]),
1335
+ name: Schema13.String,
1336
+ value: Schema13.String
1337
+ });
1338
+ var Hostname = Schema13.String.pipe(Schema13.check(Schema13.isMaxLength(253), Schema13.isPattern(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/)));
1339
+ var Domain = Schema13.Struct({
1340
+ id: DomainId,
1341
+ hostname: Hostname,
1342
+ environment: EnvironmentName,
1343
+ status: DomainStatus,
1344
+ dnsRecords: Schema13.Array(DnsRecord),
1345
+ createdAt: Timestamp
1346
+ });
1347
+ var AddDomain = Schema13.Struct({ hostname: Hostname, environment: EnvironmentName });
1348
+ var OwnedTable = Schema13.Struct({
1349
+ name: Schema13.String,
1350
+ actor: Schema13.String,
1351
+ rows: NonNegativeInt,
1352
+ sizeBytes: NonNegativeInt,
1353
+ region: RegionId
1354
+ });
1355
+ var ProjectRegion = Schema13.Struct({
1356
+ region: Region,
1357
+ home: Schema13.Boolean,
1358
+ tenantCount: NonNegativeInt,
1359
+ database: Schema13.Struct({ version: Schema13.String, sizeBytes: NonNegativeInt }),
1360
+ storage: Schema13.Struct({ usedBytes: NonNegativeInt, limitBytes: NonNegativeInt }),
1361
+ cpuPercent: NonNegative,
1362
+ connections: Schema13.Struct({ used: NonNegativeInt, limit: NonNegativeInt }),
1363
+ runners: NonNegativeInt,
1364
+ largestTables: Schema13.Array(OwnedTable)
1365
+ });
1366
+ var AddRegion = Schema13.Struct({ region: RegionId });
1367
+ var SetHomeRegion = Schema13.Struct({ region: RegionId });
1368
+ var ProjectEndpoints = Schema13.Struct({
1369
+ httpBaseUrl: Schema13.String,
1370
+ webSocketUrl: Schema13.String,
1371
+ openApiPath: Schema13.String,
1372
+ mcpPath: Schema13.String
1373
+ });
1374
+ var IntegrationKind = Schema13.Literals([
1375
+ "github",
1376
+ "slack",
1377
+ "datadog",
1378
+ "opentelemetry",
1379
+ "pagerduty"
1380
+ ]);
1381
+ var Integration = Schema13.Struct({
1382
+ kind: IntegrationKind,
1383
+ status: Schema13.Literals(["connected", "disconnected", "error"]),
1384
+ label: Schema13.NullOr(Schema13.String),
1385
+ connectedAt: Schema13.NullOr(Timestamp)
1386
+ });
1387
+ var ConnectIntegration = Schema13.Struct({
1388
+ settings: Schema13.optional(Schema13.Record(Schema13.String, Schema13.String))
1389
+ });
1390
+ var IntegrationConnection = Schema13.Struct({
1391
+ redirectUrl: Schema13.NullOr(Schema13.String),
1392
+ integration: Integration
1393
+ });
1394
+ // ../../packages/cloud-api/src/deployments.ts
1395
+ import { Schema as Schema14 } from "effect";
1396
+ var DeploymentStatus = Schema14.Literals([
1397
+ "in-progress",
1398
+ "live",
1399
+ "drained",
1400
+ "rolled-back",
1401
+ "failed"
1402
+ ]);
1403
+ var RolloutStepName = Schema14.Literals([
1404
+ "build",
1405
+ "migrate",
1406
+ "start-runners",
1407
+ "drain-previous"
1408
+ ]);
1409
+ var RolloutStep = Schema14.Struct({
1410
+ name: RolloutStepName,
1411
+ status: Schema14.Literals(["pending", "running", "succeeded", "failed", "skipped"]),
1412
+ durationMs: Schema14.NullOr(NonNegativeInt),
1413
+ detail: Schema14.NullOr(Schema14.String)
1414
+ });
1415
+ var DeploymentRunner = Schema14.Struct({
1416
+ id: Schema14.String,
1417
+ region: RegionId,
1418
+ actorCount: Schema14.NullOr(NonNegativeInt),
1419
+ cpuPercent: Schema14.NullOr(NonNegative),
1420
+ health: Schema14.Literals(["healthy", "unhealthy", "starting", "draining"])
1421
+ });
1422
+ var DeploymentAuthor = Schema14.Struct({
1423
+ name: Schema14.String,
1424
+ image: Schema14.NullOr(Schema14.String)
1425
+ });
1426
+ var DeploymentSummary = Schema14.Struct({
1427
+ id: DeploymentId,
1428
+ projectId: ProjectId,
1429
+ environment: EnvironmentName,
1430
+ environmentHost: Schema14.optional(Schema14.String),
1431
+ commitSha: CommitSha,
1432
+ message: Schema14.String,
1433
+ author: DeploymentAuthor,
1434
+ regions: Schema14.Array(RegionId),
1435
+ runnerCount: NonNegativeInt,
1436
+ durationMs: Schema14.NullOr(NonNegativeInt),
1437
+ status: DeploymentStatus,
1438
+ rolledBackFrom: Schema14.NullOr(DeploymentId),
1439
+ createdAt: Timestamp
1440
+ });
1441
+ var DeploymentDetail = Schema14.Struct({
1442
+ ...DeploymentSummary.fields,
1443
+ steps: Schema14.Array(RolloutStep),
1444
+ runners: Schema14.Array(DeploymentRunner)
1445
+ });
1446
+ var SourceDigest = Schema14.String.pipe(Schema14.check(Schema14.isPattern(/^sha256:[a-f0-9]{64}$/u)));
1447
+ var MAX_SOURCE_BYTES = 64 * 1024 * 1024;
1448
+ var SourceArchive = Schema14.Struct({
1449
+ digest: SourceDigest,
1450
+ sizeBytes: NonNegativeInt
1451
+ });
1452
+ var SOURCE_ENTRY = "src/app.ts";
1453
+ var DeploymentSource = Schema14.Struct({
1454
+ digest: SourceDigest,
1455
+ dockerfile: Schema14.optionalKey(Schema14.Never.annotate({
1456
+ message: `source.dockerfile is not accepted: the platform builds every source from ${SOURCE_ENTRY}`
1457
+ }))
1458
+ });
1459
+ var CreateDeployment = Schema14.Struct({
1460
+ environment: EnvironmentName,
1461
+ commitSha: CommitSha,
1462
+ message: Schema14.optional(Schema14.String),
1463
+ regions: Schema14.optional(Schema14.Array(RegionId)),
1464
+ source: Schema14.optional(DeploymentSource)
1465
+ });
1466
+ var ImageDigest = Schema14.String.pipe(Schema14.check(Schema14.isPattern(/^(?:[^\s@]+@)?sha256:[a-f0-9]{64}$/u)));
1467
+ var RecordBuild = Schema14.Struct({
1468
+ image: ImageDigest,
1469
+ commitSha: CommitSha,
1470
+ environmentSnapshot: Schema14.Record(Schema14.String, Schema14.String).check(Schema14.makeFilter((snapshot) => Object.keys(snapshot).every((key) => /^[A-Za-z_][A-Za-z0-9_]{0,254}$/u.test(key)) || "Environment variable names must be valid identifiers"))
1471
+ });
1472
+ var FailBuild = Schema14.Struct({
1473
+ reason: Schema14.String.check(Schema14.isMinLength(1), Schema14.isMaxLength(4096))
1474
+ });
1475
+ var BuildLogLine = Schema14.Struct({
1476
+ index: NonNegativeInt,
1477
+ at: Timestamp,
1478
+ stream: Schema14.Literals(["stdout", "stderr"]),
1479
+ text: Schema14.String
1480
+ });
1481
+ var BuildLog = Schema14.Struct({
1482
+ lines: Schema14.Array(BuildLogLine),
1483
+ complete: Schema14.Boolean
1484
+ });
1485
+ // ../../packages/cloud-api/src/runtime.ts
1486
+ import * as Framework from "@rikalabs/akter/client";
1487
+ import { Schema as Schema15, Struct as Struct2 } from "effect";
1488
+ var SidebarCounts = Schema15.Struct({
1489
+ actorTypes: NonNegativeInt,
1490
+ openDeadLetters: NonNegativeInt
1491
+ });
1492
+ var Overview = Schema15.Struct({
1493
+ commands: Schema15.NullOr(Schema15.Struct({
1494
+ perSecond: NonNegative,
1495
+ series24h: Schema15.Array(SeriesPoint),
1496
+ p50Ms: Schema15.NullOr(NonNegative),
1497
+ p99Ms: Schema15.NullOr(NonNegative)
1498
+ })),
1499
+ actors: Schema15.Struct({ awake: Schema15.NullOr(NonNegativeInt), total: NonNegativeInt }),
1500
+ jobs: Schema15.Struct({ inFlight: NonNegativeInt, donePerHour: Schema15.NullOr(NonNegativeInt) }),
1501
+ deadLettersByJobType: Schema15.Array(Schema15.Struct({ jobName: Schema15.String, count: NonNegativeInt })),
1502
+ throughput: Schema15.NullOr(Schema15.Array(SeriesPoint)),
1503
+ p99: Schema15.NullOr(Schema15.Array(SeriesPoint)),
1504
+ health: Schema15.Struct({
1505
+ runners: Schema15.NullOr(Schema15.Struct({ healthy: NonNegativeInt, total: NonNegativeInt })),
1506
+ databaseCpuPercent: Schema15.NullOr(NonNegative),
1507
+ maxMailbox: Schema15.NullOr(Schema15.Struct({ depth: NonNegativeInt, actor: Schema15.NullOr(ActorAddress) })),
1508
+ parkedSockets: Schema15.NullOr(NonNegativeInt),
1509
+ outboxLagP99Ms: Schema15.NullOr(NonNegative),
1510
+ lastDeployAt: Schema15.NullOr(Timestamp)
1511
+ }),
1512
+ recentDeployments: Schema15.NullOr(Schema15.Array(DeploymentSummary))
1513
+ });
1514
+ var ActorTypeSummary = Schema15.Struct({
1515
+ name: Schema15.String,
1516
+ commands: Schema15.NullOr(Schema15.Array(Schema15.String)),
1517
+ instances: NonNegativeInt,
1518
+ awake: Schema15.NullOr(NonNegativeInt),
1519
+ commandsPerSecond: Schema15.NullOr(NonNegative),
1520
+ p99Ms: Schema15.NullOr(NonNegative),
1521
+ maxMailbox: Schema15.NullOr(NonNegativeInt)
1522
+ });
1523
+ var SeriesWindow = Schema15.Literals(["1h", "24h", "7d"]);
1524
+ var CommandVolume = Schema15.Struct({
1525
+ command: Schema15.String,
1526
+ count: NonNegativeInt,
1527
+ perSecond: NonNegative
1528
+ });
1529
+ var ActorTypeActivity = Schema15.Struct({
1530
+ window: SeriesWindow,
1531
+ since: Timestamp,
1532
+ series: Schema15.Array(SeriesPoint),
1533
+ commands: Schema15.Array(CommandVolume)
1534
+ });
1535
+ var LatencyBucket = Schema15.Struct({
1536
+ upToMs: Schema15.NullOr(NonNegative),
1537
+ count: NonNegativeInt
1538
+ });
1539
+ var TurnLatency = Schema15.Struct({
1540
+ window: SeriesWindow,
1541
+ since: Timestamp,
1542
+ buckets: Schema15.Array(LatencyBucket),
1543
+ p50Ms: Schema15.NullOr(NonNegative),
1544
+ p95Ms: Schema15.NullOr(NonNegative),
1545
+ p99Ms: Schema15.NullOr(NonNegative)
1546
+ });
1547
+ var ActorInstance = Schema15.Struct({
1548
+ key: Schema15.String,
1549
+ status: Schema15.NullOr(Schema15.Literals(["awake", "idle"])),
1550
+ lastCommand: Schema15.NullOr(Schema15.String),
1551
+ lastActivityAt: Schema15.NullOr(Timestamp),
1552
+ generation: NonNegativeInt
1553
+ });
1554
+ var OwnedTableRows = Schema15.Struct({
1555
+ table: Schema15.String,
1556
+ columns: Schema15.Array(Schema15.String),
1557
+ rows: Schema15.Array(Schema15.Array(Schema15.Json))
1558
+ });
1559
+ var CommandCaller = Schema15.Struct({
1560
+ kind: Schema15.Literals(["user", "anonymous", "system"]),
1561
+ subject: Schema15.NullOr(Schema15.String),
1562
+ source: Schema15.NullOr(Schema15.String)
1563
+ });
1564
+ var Receipt = Schema15.Struct({
1565
+ commandId: Schema15.String,
1566
+ command: Schema15.String,
1567
+ result: Schema15.NullOr(Schema15.String),
1568
+ caller: Schema15.NullOr(CommandCaller),
1569
+ at: Schema15.NullOr(Timestamp),
1570
+ expiresAt: Timestamp,
1571
+ replayed: Schema15.Boolean
1572
+ });
1573
+ var ActorEvent = Schema15.Struct({
1574
+ name: Schema15.String,
1575
+ cursor: Schema15.String,
1576
+ emittedAt: Timestamp,
1577
+ subscribers: Schema15.NullOr(NonNegativeInt)
1578
+ });
1579
+ var JobStatus = Schema15.Literals(["queued", "running", "retrying", "done", "dead"]);
1580
+ var ActorJob = Schema15.Struct({
1581
+ name: Schema15.String,
1582
+ id: Schema15.String,
1583
+ attempts: NonNegativeInt,
1584
+ status: JobStatus
1585
+ });
1586
+ var ActorTimelineEntry = Schema15.Struct({
1587
+ at: Timestamp,
1588
+ kind: Schema15.Literals(["command", "event", "job"]),
1589
+ label: Schema15.String,
1590
+ detail: Schema15.NullOr(Schema15.String),
1591
+ caller: Schema15.NullOr(CommandCaller)
1592
+ });
1593
+ var ActorInspector = Schema15.Struct({
1594
+ address: ActorAddress,
1595
+ state: Schema15.Json,
1596
+ turn: Schema15.NullOr(NonNegativeInt),
1597
+ tables: Schema15.NullOr(Schema15.Array(OwnedTableRows)),
1598
+ receipts: Schema15.Array(Receipt),
1599
+ events: Schema15.Array(ActorEvent),
1600
+ jobs: Schema15.Array(ActorJob),
1601
+ connections: Schema15.Struct({
1602
+ sockets: Schema15.NullOr(NonNegativeInt),
1603
+ feedCursor: Schema15.NullOr(Schema15.String)
1604
+ }),
1605
+ properties: Schema15.Struct({
1606
+ status: Schema15.NullOr(Schema15.Literals(["awake", "idle"])),
1607
+ type: Schema15.String,
1608
+ generation: NonNegativeInt,
1609
+ runner: Schema15.NullOr(Schema15.String),
1610
+ region: Schema15.NullOr(Schema15.String),
1611
+ tenant: Schema15.String,
1612
+ mailboxDepth: Schema15.NullOr(NonNegativeInt)
1613
+ }),
1614
+ timeline: Schema15.NullOr(Schema15.Array(ActorTimelineEntry))
1615
+ });
1616
+ var CommandOutcome = Schema15.Literals(["ok", "error", "replayed"]);
1617
+ var CommandLogEntry = Schema15.Struct({
1618
+ commandId: Schema15.String,
1619
+ at: Schema15.NullOr(Timestamp),
1620
+ durationMs: Schema15.NullOr(NonNegative),
1621
+ address: ActorAddress,
1622
+ command: Schema15.String,
1623
+ caller: Schema15.NullOr(CommandCaller),
1624
+ payloadPreview: Schema15.NullOr(Schema15.String),
1625
+ outcome: CommandOutcome,
1626
+ errorTag: Schema15.NullOr(Schema15.String)
1627
+ });
1628
+ var commandName = Schema15.String.pipe(Schema15.check(Schema15.isMinLength(1), Schema15.isMaxLength(128)));
1629
+ var SendCommand = Schema15.Struct({
1630
+ address: ActorAddress,
1631
+ command: commandName,
1632
+ payload: Schema15.Json,
1633
+ commandId: Schema15.optional(commandName)
1634
+ });
1635
+ var CommandSent = Schema15.Struct({
1636
+ commandId: Schema15.String,
1637
+ result: Schema15.Json,
1638
+ replayed: Schema15.Boolean
1639
+ });
1640
+
1641
+ class CommandExpired extends Schema15.TaggedError()("CommandExpired", { commandId: Schema15.String }, { httpApiStatus: 410 }) {
1642
+ }
1643
+
1644
+ class CommandStreamGap extends Schema15.TaggedError()("CommandStreamGap", {}) {
1645
+ }
1646
+
1647
+ class RunnerDefect extends Schema15.TaggedError()("RunnerDefect", {}, { httpApiStatus: 502 }) {
1648
+ }
1649
+
1650
+ class CommandRefused extends Schema15.TaggedError()("CommandRefused", {
1651
+ commandId: Schema15.String,
1652
+ reasonTag: Schema15.String,
1653
+ reason: Framework.ActorError.fields.reason
1654
+ }, { httpApiStatus: 422 }) {
1655
+ }
1656
+
1657
+ class CommandFailed2 extends Schema15.TaggedError()("CommandFailed", {
1658
+ commandId: Schema15.String,
1659
+ errorTag: Schema15.String,
1660
+ error: Schema15.Json,
1661
+ replayed: Schema15.Boolean
1662
+ }, { httpApiStatus: 422 }) {
1663
+ }
1664
+
1665
+ class QuotaExceeded2 extends Schema15.TaggedError()("QuotaExceeded", Struct2.omit(Framework.QuotaExceeded.fields, ["_tag"]), { httpApiStatus: 429 }) {
1666
+ }
1667
+
1668
+ class QuotaUnbound extends Schema15.TaggedError()("QuotaUnbound", {
1669
+ deployment: Schema15.String,
1670
+ tenant: Schema15.String,
1671
+ reason: Schema15.Literals(["tenant", "account", "plan"])
1672
+ }, { httpApiStatus: 402 }) {
1673
+ }
1674
+ var QuotaErrors = [QuotaExceeded2, QuotaUnbound];
1675
+ var JobTypeStats = Schema15.Struct({
1676
+ jobName: Schema15.String,
1677
+ done: Schema15.NullOr(NonNegativeInt),
1678
+ retried: NonNegativeInt,
1679
+ dead: NonNegativeInt,
1680
+ p99Ms: Schema15.NullOr(NonNegative)
1681
+ });
1682
+ var JobsSummary = Schema15.Struct({
1683
+ queued: NonNegativeInt,
1684
+ running: Schema15.NullOr(NonNegativeInt),
1685
+ retrying: NonNegativeInt,
1686
+ dead: NonNegativeInt,
1687
+ byType: Schema15.Array(JobTypeStats),
1688
+ throughput: Schema15.NullOr(Schema15.Array(SeriesPoint))
1689
+ });
1690
+ var DeadLetter = Schema15.Struct({
1691
+ id: DeadLetterId,
1692
+ jobName: Schema15.String,
1693
+ jobId: Schema15.String,
1694
+ actor: ActorAddress,
1695
+ attempts: NonNegativeInt,
1696
+ lastError: Schema15.String,
1697
+ since: Timestamp
1698
+ });
1699
+ var Workflow = Schema15.Struct({
1700
+ id: Schema15.String,
1701
+ name: Schema15.String,
1702
+ actor: ActorAddress,
1703
+ step: Schema15.NullOr(Schema15.Struct({
1704
+ index: Schema15.Int.pipe(Schema15.check(Schema15.isGreaterThanOrEqualTo(1))),
1705
+ total: Schema15.NullOr(Schema15.Int.pipe(Schema15.check(Schema15.isGreaterThanOrEqualTo(1)))),
1706
+ name: Schema15.String
1707
+ }).pipe(Schema15.check(Schema15.makeFilter((step) => step.total === null || step.index <= step.total || "step.index must not exceed step.total")))),
1708
+ waitingFor: Schema15.NullOr(Schema15.Struct({ kind: Schema15.Literals(["event", "timer"]), name: Schema15.String })),
1709
+ startedAt: Timestamp,
1710
+ status: Schema15.NullOr(Schema15.Literals(["running", "waiting", "completed", "failed"]))
1711
+ });
1712
+ var TimersSummary = Schema15.Struct({
1713
+ pending: NonNegativeInt,
1714
+ nextFireAt: Schema15.NullOr(Timestamp)
1715
+ });
1716
+ var Schedule = Schema15.Struct({
1717
+ name: Schema15.String,
1718
+ actorPattern: Schema15.String,
1719
+ cron: Schema15.String,
1720
+ lastRun: Schema15.NullOr(Schema15.Struct({
1721
+ at: Timestamp,
1722
+ outcome: Schema15.Literals(["ok", "error"]),
1723
+ durationMs: Schema15.NullOr(NonNegative)
1724
+ })),
1725
+ nextRunAt: Schema15.NullOr(Timestamp)
1726
+ });
1727
+ var ConnectionsSummary = Schema15.Struct({
1728
+ open: NonNegativeInt,
1729
+ parked: Schema15.NullOr(NonNegativeInt),
1730
+ sseStreams: NonNegativeInt,
1731
+ feedSubscribers: NonNegativeInt,
1732
+ replayGaps: Schema15.NullOr(NonNegativeInt),
1733
+ openVersusParked: Schema15.NullOr(Schema15.Array(Schema15.Struct({ at: Timestamp, open: NonNegativeInt, parked: NonNegativeInt }))),
1734
+ byActorType: Schema15.Array(Schema15.Struct({
1735
+ actorType: Schema15.String,
1736
+ open: NonNegativeInt,
1737
+ parked: Schema15.NullOr(NonNegativeInt),
1738
+ sse: NonNegativeInt
1739
+ }))
1740
+ });
1741
+ var SearchResult = Schema15.Struct({
1742
+ kind: Schema15.Literals(["actor-type", "actor", "deployment"]),
1743
+ id: Schema15.String,
1744
+ title: Schema15.String,
1745
+ subtitle: Schema15.NullOr(Schema15.String)
1746
+ });
1747
+ // ../../packages/cloud-api/src/billing.ts
1748
+ import { Schema as Schema16 } from "effect";
1749
+ var Plan = Schema16.TaggedStruct("known", {
1750
+ id: PlanId,
1751
+ name: Schema16.String,
1752
+ basePriceCents: NonNegativeInt,
1753
+ currency: Schema16.Literal("usd"),
1754
+ renewsAt: Schema16.NullOr(Timestamp),
1755
+ monthToDateEstimateCents: NonNegativeInt,
1756
+ provisional: Schema16.optionalKey(Schema16.Boolean),
1757
+ subscribedId: Schema16.optionalKey(PlanId),
1758
+ paymentStatus: Schema16.optionalKey(Schema16.Literals(["free", "active", "past_due", "unpaid", "canceled", "incomplete"]))
1759
+ });
1760
+ var CardPaymentMethod = Schema16.TaggedStruct("card", {
1761
+ brand: Schema16.String,
1762
+ lastFour: Schema16.String,
1763
+ expiryMonth: Schema16.Int.pipe(Schema16.check(Schema16.isBetween({ minimum: 1, maximum: 12 }))),
1764
+ expiryYear: NonNegativeInt
1765
+ });
1766
+ var LinkPaymentMethod = Schema16.TaggedStruct("link", {
1767
+ email: Schema16.NullOr(Schema16.String)
1768
+ });
1769
+ var PaymentMethod = Schema16.Union([CardPaymentMethod, LinkPaymentMethod]);
1770
+ var SpendLimit = Schema16.Struct({
1771
+ limitCents: Schema16.NullOr(NonNegativeInt),
1772
+ currentSpendCents: NonNegativeInt
1773
+ });
1774
+ var capFields = {
1775
+ limit: Schema16.NullOr(NonNegative),
1776
+ used: NonNegative,
1777
+ atCap: Schema16.Boolean,
1778
+ refusing: Schema16.Boolean,
1779
+ reason: Schema16.optionalKey(Schema16.Literal("unbound"))
1780
+ };
1781
+ var CapState = Schema16.Struct({
1782
+ cap: Schema16.Literals(["spend", "connections", "compute"]),
1783
+ ...capFields
1784
+ });
1785
+ var BillingSummary = Schema16.Struct({
1786
+ plan: Schema16.Union([Plan, UnboundPlan]),
1787
+ paymentMethod: Schema16.NullOr(PaymentMethod),
1788
+ billingEmail: Schema16.NullOr(Email),
1789
+ spendLimit: SpendLimit,
1790
+ caps: Schema16.optionalKey(Schema16.Array(CapState))
1791
+ });
1792
+ var SetSpendLimit = Schema16.Struct({ limitCents: Schema16.NullOr(NonNegativeInt) });
1793
+ var Invoice = Schema16.Struct({
1794
+ id: InvoiceId,
1795
+ number: Schema16.String,
1796
+ periodStart: Timestamp,
1797
+ periodEnd: Timestamp,
1798
+ amountCents: NonNegativeInt,
1799
+ currency: Schema16.Literal("usd"),
1800
+ status: Schema16.Literals(["draft", "open", "paid", "void", "uncollectible"]),
1801
+ pdfUrl: Schema16.NullOr(Schema16.String)
1802
+ });
1803
+ var StartCheckout = Schema16.Struct({ plan: Schema16.Literals(["pro", "team", "enterprise"]) });
1804
+ var PlanChange = Schema16.Struct({
1805
+ requestId: Schema16.String,
1806
+ status: Schema16.Literals(["pending", "completed", "failed"])
1807
+ });
1808
+ var HostedSession = Schema16.Struct({ url: Schema16.String });
1809
+ var machineSizeFields = {
1810
+ cpuKind: Schema16.Literals(["shared", "performance"]),
1811
+ cpus: Schema16.Int.check(Schema16.isGreaterThanOrEqualTo(1)),
1812
+ memoryMb: Schema16.Int.check(Schema16.isGreaterThanOrEqualTo(1))
1813
+ };
1814
+ var PlanFeature = Schema16.Literals([
1815
+ "compute-overage",
1816
+ "compute-cap",
1817
+ "byo-database",
1818
+ "checkout"
1819
+ ]);
1820
+ var CatalogPlan = Schema16.Struct({
1821
+ id: PlanId,
1822
+ name: Schema16.String,
1823
+ basePriceCents: NonNegativeInt,
1824
+ currency: Schema16.Literal("usd"),
1825
+ allowances: Schema16.Struct({
1826
+ computeUnitHours: NonNegative,
1827
+ computeUnitHourCap: Schema16.NullOr(NonNegative),
1828
+ concurrentConnections: NonNegativeInt
1829
+ }),
1830
+ overage: Schema16.Struct({
1831
+ computeCentsPerUnitHour: NonNegative
1832
+ }),
1833
+ features: Schema16.Array(PlanFeature),
1834
+ provisional: Schema16.Boolean
1835
+ });
1836
+ var ComputeSize = Schema16.Struct({
1837
+ ...machineSizeFields,
1838
+ unitsPerHour: Schema16.Finite.check(Schema16.isGreaterThan(0))
1839
+ });
1840
+ var PlanCatalog = Schema16.Struct({
1841
+ plans: Schema16.Array(CatalogPlan),
1842
+ computeSizes: Schema16.Array(ComputeSize),
1843
+ provisional: Schema16.Boolean
1844
+ });
1845
+ var UsageMeterName = Schema16.Literal("runnerHours");
1846
+ var UsageMeter = Schema16.Struct({
1847
+ meter: UsageMeterName,
1848
+ used: NonNegative,
1849
+ included: NonNegative,
1850
+ overage: NonNegative,
1851
+ overageCostCents: NonNegative
1852
+ });
1853
+ var UsagePricing = Schema16.Struct({
1854
+ computeCentsPerUnitHour: NonNegative,
1855
+ provisional: Schema16.optionalKey(Schema16.Boolean)
1856
+ });
1857
+ var ComputeUsage = Schema16.Struct({
1858
+ environmentId: EnvironmentId,
1859
+ ...machineSizeFields,
1860
+ machineHours: NonNegative,
1861
+ computeUnitHours: NonNegative
1862
+ });
1863
+ var Usage = Schema16.Struct({
1864
+ period: BillingPeriod,
1865
+ meters: Schema16.Array(UsageMeter),
1866
+ caps: Schema16.Array(CapState),
1867
+ byProject: Schema16.Array(Schema16.Struct({
1868
+ projectId: ProjectId,
1869
+ name: Schema16.String,
1870
+ computeUnitHours: NonNegative,
1871
+ compute: Schema16.Array(ComputeUsage),
1872
+ estimatedCostCents: NonNegative
1873
+ })),
1874
+ pricing: UsagePricing
1875
+ });
1876
+ var AuditEntry = Schema16.Struct({
1877
+ id: Schema16.String,
1878
+ at: Timestamp,
1879
+ actor: ActorReference,
1880
+ action: Schema16.String,
1881
+ target: Schema16.Struct({
1882
+ type: Schema16.String,
1883
+ id: Schema16.NullOr(Schema16.String),
1884
+ name: Schema16.NullOr(Schema16.String)
1885
+ }),
1886
+ ipAddress: Schema16.NullOr(Schema16.String)
1887
+ });
1888
+ // ../../packages/cloud-api/src/contract.ts
1889
+ import { HttpApi, OpenApi as OpenApi3 } from "effect/http-api";
1890
+
1891
+ // ../../packages/cloud-api/src/groups/account.ts
1892
+ import { Schema as Schema17 } from "effect";
1893
+ import { HttpApiEndpoint, HttpApiGroup } from "effect/http-api";
1894
+ var organizationParams = { organizationId: OrganizationId };
1895
+
1896
+ class AccountGroup extends HttpApiGroup.make("account").add(HttpApiEndpoint.get("me", "/me", { success: Me, error: SelfErrors }), HttpApiEndpoint.get("exportData", "/me/export", {
1897
+ success: PersonalDataExport,
1898
+ error: SelfErrors
1899
+ }), HttpApiEndpoint.delete("deleteAccount", "/me", {
1900
+ payload: DeleteAccount,
1901
+ error: WriteErrors
1902
+ }), HttpApiEndpoint.patch("updateProfile", "/me", {
1903
+ payload: UpdateProfile,
1904
+ success: User2,
1905
+ error: SelfErrors
1906
+ }), HttpApiEndpoint.put("setActiveOrganization", "/me/active-organization", {
1907
+ payload: SetActiveOrganization,
1908
+ success: OrganizationMembership,
1909
+ error: WriteErrors
1910
+ }), HttpApiEndpoint.get("getPreferences", "/me/preferences", {
1911
+ success: Preferences,
1912
+ error: SelfErrors
1913
+ }), HttpApiEndpoint.patch("updatePreferences", "/me/preferences", {
1914
+ payload: UpdatePreferences,
1915
+ success: Preferences,
1916
+ error: SelfErrors
1917
+ }), HttpApiEndpoint.get("getNotifications", "/me/notifications", {
1918
+ success: NotificationSettings,
1919
+ error: SelfErrors
1920
+ }), HttpApiEndpoint.put("setNotifications", "/me/notifications", {
1921
+ payload: NotificationSettings,
1922
+ success: NotificationSettings,
1923
+ error: SelfErrors
1924
+ }), HttpApiEndpoint.get("listPinnedActors", "/me/pinned-actors", {
1925
+ query: { projectId: ProjectId, environment: EnvironmentName },
1926
+ success: Schema17.Array(PinnedActor),
1927
+ error: ReadErrors
1928
+ }), HttpApiEndpoint.post("pinActor", "/me/pinned-actors", {
1929
+ payload: PinActor,
1930
+ success: PinnedActor,
1931
+ error: WriteErrors
1932
+ }), HttpApiEndpoint.delete("unpinActor", "/me/pinned-actors", {
1933
+ query: { projectId: ProjectId, environment: EnvironmentName, address: Schema17.String },
1934
+ error: WriteErrors
1935
+ })) {
1936
+ }
1937
+
1938
+ class OrganizationsGroup extends HttpApiGroup.make("organizations").add(HttpApiEndpoint.get("list", "/organizations", {
1939
+ success: Schema17.Array(OrganizationMembership),
1940
+ error: SelfErrors
1941
+ }), HttpApiEndpoint.post("create", "/organizations", {
1942
+ payload: CreateOrganization,
1943
+ success: OrganizationMembership,
1944
+ error: WriteErrors
1945
+ }), HttpApiEndpoint.get("get", "/organizations/:organizationId", {
1946
+ params: organizationParams,
1947
+ success: OrganizationMembership,
1948
+ error: ReadErrors
1949
+ }), HttpApiEndpoint.patch("update", "/organizations/:organizationId", {
1950
+ params: organizationParams,
1951
+ payload: UpdateOrganization,
1952
+ success: Organization,
1953
+ error: WriteErrors
1954
+ }), HttpApiEndpoint.delete("delete", "/organizations/:organizationId", {
1955
+ params: organizationParams,
1956
+ error: WriteErrors
1957
+ }), HttpApiEndpoint.get("getDeletion", "/organizations/:organizationId/deletion", {
1958
+ params: organizationParams,
1959
+ success: OrganizationDeletion,
1960
+ error: ReadErrors
1961
+ })) {
1962
+ }
1963
+
1964
+ class MembersGroup extends HttpApiGroup.make("members").add(HttpApiEndpoint.get("list", "/organizations/:organizationId/members", {
1965
+ params: organizationParams,
1966
+ success: Schema17.Array(Member),
1967
+ error: ReadErrors
1968
+ }), HttpApiEndpoint.patch("updateRole", "/organizations/:organizationId/members/:memberId", {
1969
+ params: { ...organizationParams, memberId: MemberId },
1970
+ payload: UpdateMemberRole,
1971
+ success: Member,
1972
+ error: WriteErrors
1973
+ }), HttpApiEndpoint.delete("remove", "/organizations/:organizationId/members/:memberId", {
1974
+ params: { ...organizationParams, memberId: MemberId },
1975
+ error: WriteErrors
1976
+ })) {
1977
+ }
1978
+
1979
+ class InvitationsGroup extends HttpApiGroup.make("invitations").add(HttpApiEndpoint.get("list", "/organizations/:organizationId/invitations", {
1980
+ params: organizationParams,
1981
+ success: Schema17.Array(Invitation),
1982
+ error: ReadErrors
1983
+ }), HttpApiEndpoint.post("create", "/organizations/:organizationId/invitations", {
1984
+ params: organizationParams,
1985
+ payload: CreateInvitation,
1986
+ success: Invitation,
1987
+ error: WriteErrors
1988
+ }), HttpApiEndpoint.post("resend", "/organizations/:organizationId/invitations/:invitationId/resend", {
1989
+ params: { ...organizationParams, invitationId: InvitationId },
1990
+ success: Invitation,
1991
+ error: WriteErrors
1992
+ }), HttpApiEndpoint.delete("cancel", "/organizations/:organizationId/invitations/:invitationId", {
1993
+ params: { ...organizationParams, invitationId: InvitationId },
1994
+ error: WriteErrors
1995
+ }), HttpApiEndpoint.get("preview", "/invitations/:invitationId", {
1996
+ params: { invitationId: InvitationId },
1997
+ success: InvitationPreview,
1998
+ error: ReadErrors
1999
+ }), HttpApiEndpoint.post("accept", "/invitations/:invitationId/accept", {
2000
+ params: { invitationId: InvitationId },
2001
+ success: OrganizationMembership,
2002
+ error: WriteErrors
2003
+ }), HttpApiEndpoint.post("decline", "/invitations/:invitationId/decline", {
2004
+ params: { invitationId: InvitationId },
2005
+ error: WriteErrors
2006
+ })) {
2007
+ }
2008
+
2009
+ class ApiKeysGroup extends HttpApiGroup.make("apiKeys").add(HttpApiEndpoint.get("list", "/organizations/:organizationId/api-keys", {
2010
+ params: organizationParams,
2011
+ query: { projectId: Schema17.optional(ProjectId) },
2012
+ success: Schema17.Array(ApiKey),
2013
+ error: ReadErrors
2014
+ }), HttpApiEndpoint.post("create", "/organizations/:organizationId/api-keys", {
2015
+ params: organizationParams,
2016
+ payload: CreateApiKey,
2017
+ success: CreatedApiKey,
2018
+ error: WriteErrors
2019
+ }), HttpApiEndpoint.delete("revoke", "/organizations/:organizationId/api-keys/:keyId", {
2020
+ params: { ...organizationParams, keyId: ApiKeyId },
2021
+ error: WriteErrors
2022
+ })) {
2023
+ }
2024
+
2025
+ // ../../packages/cloud-api/src/groups/billing.ts
2026
+ import { Schema as Schema18 } from "effect";
2027
+ import { HttpApiEndpoint as HttpApiEndpoint2, HttpApiGroup as HttpApiGroup2 } from "effect/http-api";
2028
+ var organizationParams2 = { organizationId: OrganizationId };
2029
+
2030
+ class BillingGroup extends HttpApiGroup2.make("billing").add(HttpApiEndpoint2.get("listPlans", "/billing/plans", {
2031
+ success: PlanCatalog,
2032
+ error: SelfErrors
2033
+ }), HttpApiEndpoint2.get("get", "/organizations/:organizationId/billing", {
2034
+ params: organizationParams2,
2035
+ success: BillingSummary,
2036
+ error: ReadErrors
2037
+ }), HttpApiEndpoint2.get("listInvoices", "/organizations/:organizationId/billing/invoices", {
2038
+ params: organizationParams2,
2039
+ success: Schema18.Array(Invoice),
2040
+ error: ReadErrors
2041
+ }), HttpApiEndpoint2.put("setSpendLimit", "/organizations/:organizationId/billing/spend-limit", {
2042
+ params: organizationParams2,
2043
+ payload: SetSpendLimit,
2044
+ success: SpendLimit,
2045
+ error: WriteErrors
2046
+ }), HttpApiEndpoint2.post("startCheckout", "/organizations/:organizationId/billing/checkout", {
2047
+ params: organizationParams2,
2048
+ payload: StartCheckout,
2049
+ success: HostedSession,
2050
+ error: WriteErrors
2051
+ }), HttpApiEndpoint2.post("changePlan", "/organizations/:organizationId/billing/plan", {
2052
+ params: organizationParams2,
2053
+ payload: StartCheckout,
2054
+ success: PlanChange,
2055
+ error: WriteErrors
2056
+ }), HttpApiEndpoint2.post("openPortal", "/organizations/:organizationId/billing/portal", {
2057
+ params: organizationParams2,
2058
+ success: HostedSession,
2059
+ error: WriteErrors
2060
+ })) {
2061
+ }
2062
+
2063
+ class UsageGroup extends HttpApiGroup2.make("usage").add(HttpApiEndpoint2.get("get", "/organizations/:organizationId/usage", {
2064
+ params: organizationParams2,
2065
+ query: { period: Schema18.optional(BillingPeriod) },
2066
+ success: Usage,
2067
+ error: ReadErrors
2068
+ })) {
2069
+ }
2070
+
2071
+ class AuditGroup extends HttpApiGroup2.make("audit").add(HttpApiEndpoint2.get("list", "/organizations/:organizationId/audit-log", {
2072
+ params: organizationParams2,
2073
+ query: {
2074
+ ...pageQuery,
2075
+ action: Schema18.optional(Schema18.String),
2076
+ actorId: Schema18.optional(Schema18.String)
2077
+ },
2078
+ success: Page(AuditEntry),
2079
+ error: ReadErrors
2080
+ })) {
2081
+ }
2082
+
2083
+ // ../../packages/cloud-api/src/groups/deployments.ts
2084
+ import { Schema as Schema20 } from "effect";
2085
+ import { HttpApiEndpoint as HttpApiEndpoint3, HttpApiGroup as HttpApiGroup3, HttpApiSchema, OpenApi } from "effect/http-api";
2086
+
2087
+ // ../../packages/cloud-api/src/logs.ts
2088
+ import { Schema as Schema19 } from "effect";
2089
+ var MAX_LOG_LINES = 200;
2090
+ var MAX_LOG_TEXT_BYTES = 4096;
2091
+ var MAX_LOG_WINDOW_SECONDS = 3600;
2092
+ var MAX_LOG_WAIT_SECONDS = 20;
2093
+ var LogCursor = Schema19.String.check(Schema19.isMinLength(1), Schema19.isMaxLength(2048));
2094
+ var LogLimit = Schema19.Int.check(Schema19.isBetween({ minimum: 1, maximum: MAX_LOG_LINES }));
2095
+ var LogWait = Schema19.Int.check(Schema19.isBetween({ minimum: 0, maximum: MAX_LOG_WAIT_SECONDS }));
2096
+ var logQuery = {
2097
+ since: Schema19.optional(Timestamp),
2098
+ limit: Schema19.optional(LogLimit),
2099
+ cursor: Schema19.optional(LogCursor),
2100
+ wait: Schema19.optional(LogWait)
2101
+ };
2102
+ var RunnerLogLine = Schema19.Struct({
2103
+ id: Schema19.String.check(Schema19.isMinLength(1), Schema19.isMaxLength(512)),
2104
+ deploymentId: DeploymentId,
2105
+ runnerId: Schema19.String.check(Schema19.isMinLength(1), Schema19.isMaxLength(256)),
2106
+ at: Timestamp,
2107
+ stream: Schema19.Literals(["stdout", "stderr", "unknown"]),
2108
+ text: Schema19.String.check(Schema19.makeFilter((text) => new TextEncoder().encode(text).byteLength <= MAX_LOG_TEXT_BYTES || "Log text exceeds the byte limit")),
2109
+ clipped: Schema19.Boolean
2110
+ });
2111
+ var RunnerLogPage = Schema19.Struct({
2112
+ lines: Schema19.Array(RunnerLogLine).check(Schema19.isMaxLength(MAX_LOG_LINES)),
2113
+ cursor: LogCursor,
2114
+ more: Schema19.Boolean
2115
+ });
2116
+
2117
+ // ../../packages/cloud-api/src/groups/deployments.ts
2118
+ var projectParams = { projectId: ProjectId };
2119
+ var deploymentParams = { ...projectParams, deploymentId: DeploymentId };
2120
+
2121
+ class DeploymentsGroup extends HttpApiGroup3.make("deployments").add(HttpApiEndpoint3.get("getEnvironmentLogs", "/projects/:projectId/environments/:environment/logs", {
2122
+ params: { ...projectParams, environment: EnvironmentName },
2123
+ query: logQuery,
2124
+ success: RunnerLogPage,
2125
+ error: ReadErrors
2126
+ }).annotate(OpenApi.Description, "Reads recent stdout/stderr from the environment's current deployment. Authorization is checked on every read, including resumed polls, and suspended organizations remain readable. Defaults: since five minutes ago, limit 100, wait 0. The server clamps since to the past hour through now, so network delay and client clock skew do not reject a window boundary; limit is 1–200 and wait is 0–20 seconds. A cursor, at most 2048 characters, resumes the same authorized resource and takes precedence over since. More means another page is immediately available: continue with the cursor and wait 0 until more is false, then follow with wait 20. A current-deployment change switches environment follow to the new deployment and resets its provider position, retaining the original bounded window. Provider retention is recent and best-effort, not an archive. No current deployment returns an empty page. Text is clipped at 4096 UTF-8 bytes; each line's clipped flag reports that independently of more."), HttpApiEndpoint3.get("getLogs", "/projects/:projectId/deployments/:deploymentId/logs", {
2127
+ params: deploymentParams,
2128
+ query: logQuery,
2129
+ success: RunnerLogPage,
2130
+ error: ReadErrors
2131
+ }).annotate(OpenApi.Description, "Reads only the authorized deployment's customer runner stdout/stderr, with the same bounds and cursor protocol as environment logs. A deployment from another project or organization is not visible. Runner identifiers come only from the deployment's stored runner records, never from the cursor or caller input."), HttpApiEndpoint3.get("list", "/projects/:projectId/deployments", {
2132
+ params: projectParams,
2133
+ query: {
2134
+ ...pageQuery,
2135
+ environment: Schema20.optional(EnvironmentName),
2136
+ status: Schema20.optional(DeploymentStatus)
2137
+ },
2138
+ success: Page(DeploymentSummary),
2139
+ error: ReadErrors
2140
+ }), HttpApiEndpoint3.post("create", "/projects/:projectId/deployments", {
2141
+ params: projectParams,
2142
+ payload: CreateDeployment,
2143
+ success: DeploymentDetail,
2144
+ error: WriteErrors
2145
+ }), HttpApiEndpoint3.post("uploadSource", "/projects/:projectId/sources", {
2146
+ params: projectParams,
2147
+ payload: Schema20.Uint8Array.pipe(HttpApiSchema.asUint8Array({ contentType: "application/gzip" })),
2148
+ success: SourceArchive,
2149
+ error: [...WriteErrors, PayloadTooLarge]
2150
+ }).annotate(OpenApi.Description, "Stores a gzip-compressed tar of a build context for the project and answers its digest, the SHA-256 of the bytes sent. Sending the same bytes again answers the same digest. A deployment created with `source` naming that digest is built from it by the control plane's builder. Access and the builder are checked before the body is read: a caller without write access answers 403 and a control plane without a builder 501 `NotImplemented`. A body over 64 MiB answers 413 `PayloadTooLarge`, before any byte is read when its `content-length` says so, and as soon as it passes the limit otherwise."), HttpApiEndpoint3.get("get", "/projects/:projectId/deployments/:deploymentId", {
2151
+ params: deploymentParams,
2152
+ success: DeploymentDetail,
2153
+ error: ReadErrors
2154
+ }), HttpApiEndpoint3.post("recordBuild", "/projects/:projectId/deployments/:deploymentId/build", {
2155
+ params: deploymentParams,
2156
+ payload: RecordBuild,
2157
+ success: DeploymentDetail,
2158
+ error: WriteErrors
2159
+ }), HttpApiEndpoint3.post("failBuild", "/projects/:projectId/deployments/:deploymentId/build-failure", {
2160
+ params: deploymentParams,
2161
+ payload: FailBuild,
2162
+ success: DeploymentDetail,
2163
+ error: WriteErrors
2164
+ }), HttpApiEndpoint3.get("getBuildLog", "/projects/:projectId/deployments/:deploymentId/build-log", {
2165
+ params: deploymentParams,
2166
+ query: { after: Schema20.optional(NonNegativeInt) },
2167
+ success: BuildLog,
2168
+ error: ReadErrors
2169
+ }), HttpApiEndpoint3.post("rollback", "/projects/:projectId/deployments/:deploymentId/rollback", {
2170
+ params: deploymentParams,
2171
+ success: DeploymentDetail,
2172
+ error: WriteErrors
2173
+ }).annotate(OpenApi.Description, "Rolls the environment back to the earlier deployment named by `deploymentId`. That deployment must have reached `live` before (status `live`, `drained` or `rolled-back`) and must not be the one live now; otherwise the answer is 409. The build is not repeated: a new deployment in the same environment redeploys that deployment's image and environment-variable snapshot, its `rolledBackFrom` is `deploymentId`, and its build and migrate steps are `skipped`. The new deployment starts `in-progress` and becomes `live` or `failed`. When it becomes `live`, the deployment that was live ends `rolled-back`; if it fails, that deployment stays `live`. The target keeps its own status. A second rollout in the environment while one is `in-progress` answers 409. The response is the new deployment."), HttpApiEndpoint3.post("redeploy", "/projects/:projectId/deployments/:deploymentId/redeploy", {
2174
+ params: deploymentParams,
2175
+ success: DeploymentDetail,
2176
+ error: WriteErrors
2177
+ })) {
2178
+ }
2179
+
2180
+ // ../../packages/cloud-api/src/groups/projects.ts
2181
+ import { Schema as Schema21 } from "effect";
2182
+ import { HttpApiEndpoint as HttpApiEndpoint4, HttpApiGroup as HttpApiGroup4 } from "effect/http-api";
2183
+ var projectParams2 = { projectId: ProjectId };
2184
+ var environmentParams = { ...projectParams2, environment: EnvironmentName };
2185
+
2186
+ class ProjectsGroup extends HttpApiGroup4.make("projects").add(HttpApiEndpoint4.get("list", "/organizations/:organizationId/projects", {
2187
+ params: { organizationId: OrganizationId },
2188
+ success: Schema21.Array(Project),
2189
+ error: ReadErrors
2190
+ }), HttpApiEndpoint4.post("create", "/organizations/:organizationId/projects", {
2191
+ params: { organizationId: OrganizationId },
2192
+ payload: CreateProject,
2193
+ success: Project,
2194
+ error: WriteErrors
2195
+ }), HttpApiEndpoint4.get("get", "/projects/:projectId", {
2196
+ params: projectParams2,
2197
+ success: Project,
2198
+ error: ReadErrors
2199
+ }), HttpApiEndpoint4.patch("update", "/projects/:projectId", {
2200
+ params: projectParams2,
2201
+ payload: UpdateProject,
2202
+ success: Project,
2203
+ error: WriteErrors
2204
+ }), HttpApiEndpoint4.delete("delete", "/projects/:projectId", {
2205
+ params: projectParams2,
2206
+ error: WriteErrors
2207
+ }), HttpApiEndpoint4.get("listEnvironments", "/projects/:projectId/environments", {
2208
+ params: projectParams2,
2209
+ success: Schema21.Array(Environment),
2210
+ error: ReadErrors
2211
+ }), HttpApiEndpoint4.post("createEnvironment", "/projects/:projectId/environments", {
2212
+ params: projectParams2,
2213
+ payload: CreateEnvironment,
2214
+ success: Environment,
2215
+ error: WriteErrors
2216
+ }), HttpApiEndpoint4.get("getEnvironment", "/projects/:projectId/environments/:environment", {
2217
+ params: environmentParams,
2218
+ success: Environment,
2219
+ error: ReadErrors
2220
+ }), HttpApiEndpoint4.delete("deleteEnvironment", "/projects/:projectId/environments/:environment", {
2221
+ params: environmentParams,
2222
+ error: WriteErrors
2223
+ }), HttpApiEndpoint4.get("getEndpoints", "/projects/:projectId/environments/:environment/endpoints", {
2224
+ params: environmentParams,
2225
+ success: ProjectEndpoints,
2226
+ error: ReadErrors
2227
+ })) {
2228
+ }
2229
+
2230
+ class EnvironmentVariablesGroup extends HttpApiGroup4.make("environmentVariables").add(HttpApiEndpoint4.get("list", "/projects/:projectId/environments/:environment/variables", {
2231
+ params: environmentParams,
2232
+ success: Schema21.Array(EnvVariable),
2233
+ error: ReadErrors
2234
+ }), HttpApiEndpoint4.put("set", "/projects/:projectId/environments/:environment/variables/:name", {
2235
+ params: { ...environmentParams, name: EnvVariableName },
2236
+ payload: SetEnvVariable,
2237
+ success: EnvVariable,
2238
+ error: WriteErrors
2239
+ }), HttpApiEndpoint4.delete("delete", "/projects/:projectId/environments/:environment/variables/:name", {
2240
+ params: { ...environmentParams, name: EnvVariableName },
2241
+ error: WriteErrors
2242
+ }), HttpApiEndpoint4.post("import", "/projects/:projectId/environments/:environment/variables/import", {
2243
+ params: environmentParams,
2244
+ payload: ImportEnvVariables,
2245
+ success: ImportEnvVariablesResult,
2246
+ error: WriteErrors
2247
+ })) {
2248
+ }
2249
+
2250
+ class DomainsGroup extends HttpApiGroup4.make("domains").add(HttpApiEndpoint4.get("list", "/projects/:projectId/domains", {
2251
+ params: projectParams2,
2252
+ success: Schema21.Array(Domain),
2253
+ error: ReadErrors
2254
+ }), HttpApiEndpoint4.post("add", "/projects/:projectId/domains", {
2255
+ params: projectParams2,
2256
+ payload: AddDomain,
2257
+ success: Domain,
2258
+ error: WriteErrors
2259
+ }), HttpApiEndpoint4.post("verify", "/projects/:projectId/domains/:domainId/verify", {
2260
+ params: { ...projectParams2, domainId: DomainId },
2261
+ success: Domain,
2262
+ error: WriteErrors
2263
+ }), HttpApiEndpoint4.delete("remove", "/projects/:projectId/domains/:domainId", {
2264
+ params: { ...projectParams2, domainId: DomainId },
2265
+ error: WriteErrors
2266
+ })) {
2267
+ }
2268
+
2269
+ class RegionsGroup extends HttpApiGroup4.make("regions").add(HttpApiEndpoint4.get("catalog", "/regions", {
2270
+ success: Schema21.Array(Region),
2271
+ error: ReadErrors
2272
+ }), HttpApiEndpoint4.get("list", "/projects/:projectId/environments/:environment/regions", {
2273
+ params: environmentParams,
2274
+ success: Schema21.Array(ProjectRegion),
2275
+ error: ReadErrors
2276
+ }), HttpApiEndpoint4.post("add", "/projects/:projectId/regions", {
2277
+ params: projectParams2,
2278
+ payload: AddRegion,
2279
+ success: Region,
2280
+ error: WriteErrors
2281
+ }), HttpApiEndpoint4.delete("remove", "/projects/:projectId/regions/:region", {
2282
+ params: { ...projectParams2, region: RegionId },
2283
+ error: WriteErrors
2284
+ }), HttpApiEndpoint4.put("setHome", "/projects/:projectId/home-region", {
2285
+ params: projectParams2,
2286
+ payload: SetHomeRegion,
2287
+ success: Project,
2288
+ error: WriteErrors
2289
+ })) {
2290
+ }
2291
+
2292
+ class IntegrationsGroup extends HttpApiGroup4.make("integrations").add(HttpApiEndpoint4.get("list", "/projects/:projectId/integrations", {
2293
+ params: projectParams2,
2294
+ success: Schema21.Array(Integration),
2295
+ error: ReadErrors
2296
+ }), HttpApiEndpoint4.post("connect", "/projects/:projectId/integrations/:kind", {
2297
+ params: { ...projectParams2, kind: IntegrationKind },
2298
+ payload: ConnectIntegration,
2299
+ success: IntegrationConnection,
2300
+ error: WriteErrors
2301
+ }), HttpApiEndpoint4.delete("disconnect", "/projects/:projectId/integrations/:kind", {
2302
+ params: { ...projectParams2, kind: IntegrationKind },
2303
+ error: WriteErrors
2304
+ })) {
2305
+ }
2306
+
2307
+ // ../../packages/cloud-api/src/groups/runtime.ts
2308
+ import { Schema as Schema22 } from "effect";
2309
+ import { HttpApiEndpoint as HttpApiEndpoint5, HttpApiGroup as HttpApiGroup5, HttpApiSchema as HttpApiSchema2, OpenApi as OpenApi2 } from "effect/http-api";
2310
+ var environmentParams2 = { projectId: ProjectId, environment: EnvironmentName };
2311
+ var actorTypeParams = { ...environmentParams2, actorType: Schema22.String };
2312
+ var actorParams = { ...actorTypeParams, key: Schema22.String };
2313
+ var InspectErrors = [...ReadErrors, RunnerDefect];
2314
+
2315
+ class RuntimeGroup extends HttpApiGroup5.make("runtime").add(HttpApiEndpoint5.get("getOverview", "/projects/:projectId/environments/:environment/runtime/overview", {
2316
+ params: environmentParams2,
2317
+ success: Overview,
2318
+ error: InspectErrors
2319
+ }), HttpApiEndpoint5.get("getSidebarCounts", "/projects/:projectId/environments/:environment/runtime/sidebar-counts", { params: environmentParams2, success: SidebarCounts, error: InspectErrors }), HttpApiEndpoint5.get("search", "/projects/:projectId/environments/:environment/runtime/search", {
2320
+ params: environmentParams2,
2321
+ query: { q: Schema22.String.pipe(Schema22.check(Schema22.isMinLength(1), Schema22.isMaxLength(256))) },
2322
+ success: Schema22.Array(SearchResult),
2323
+ error: InspectErrors
2324
+ }), HttpApiEndpoint5.get("listActorTypes", "/projects/:projectId/environments/:environment/runtime/actor-types", {
2325
+ params: environmentParams2,
2326
+ success: Schema22.Array(ActorTypeSummary),
2327
+ error: InspectErrors
2328
+ }), HttpApiEndpoint5.get("getActorType", "/projects/:projectId/environments/:environment/runtime/actor-types/:actorType", { params: actorTypeParams, success: ActorTypeSummary, error: InspectErrors }), HttpApiEndpoint5.get("getActorTypeActivity", "/projects/:projectId/environments/:environment/runtime/actor-types/:actorType/activity", {
2329
+ params: actorTypeParams,
2330
+ query: { window: Schema22.optional(SeriesWindow) },
2331
+ success: ActorTypeActivity,
2332
+ error: InspectErrors
2333
+ }).annotate(OpenApi2.Description, "Commands per second over the window (default 24h) and the volume of each command, for one actor type, as the serving runner counted them since `since`. `NotImplemented` while more than one runner serves the environment."), HttpApiEndpoint5.get("getActorTypeLatency", "/projects/:projectId/environments/:environment/runtime/actor-types/:actorType/latency", {
2334
+ params: actorTypeParams,
2335
+ query: { window: Schema22.optional(SeriesWindow) },
2336
+ success: TurnLatency,
2337
+ error: InspectErrors
2338
+ }).annotate(OpenApi2.Description, "Turn-latency histogram over the window (default 24h) with p50, p95 and p99, for one actor type, as the serving runner measured them since `since`. `NotImplemented` while more than one runner serves the environment."), HttpApiEndpoint5.get("listActorInstances", "/projects/:projectId/environments/:environment/runtime/actor-types/:actorType/instances", {
2339
+ params: actorTypeParams,
2340
+ query: { ...pageQuery, status: Schema22.optional(Schema22.Literals(["awake", "idle"])) },
2341
+ success: Page(ActorInstance),
2342
+ error: InspectErrors
2343
+ }), HttpApiEndpoint5.get("inspectActor", "/projects/:projectId/environments/:environment/runtime/actors/:actorType/:key", { params: actorParams, success: ActorInspector, error: InspectErrors }), HttpApiEndpoint5.get("listActorTables", "/projects/:projectId/environments/:environment/runtime/actors/:actorType/:key/tables", { params: actorParams, success: Schema22.Array(OwnedTableRows), error: ReadErrors }), HttpApiEndpoint5.get("listActorReceipts", "/projects/:projectId/environments/:environment/runtime/actors/:actorType/:key/receipts", { params: actorParams, query: pageQuery, success: Page(Receipt), error: InspectErrors }), HttpApiEndpoint5.get("listActorEvents", "/projects/:projectId/environments/:environment/runtime/actors/:actorType/:key/events", { params: actorParams, success: Schema22.Array(ActorEvent), error: InspectErrors }), HttpApiEndpoint5.get("listActorJobs", "/projects/:projectId/environments/:environment/runtime/actors/:actorType/:key/jobs", { params: actorParams, success: Schema22.Array(ActorJob), error: InspectErrors }), HttpApiEndpoint5.get("listActorTimeline", "/projects/:projectId/environments/:environment/runtime/actors/:actorType/:key/timeline", {
2344
+ params: actorParams,
2345
+ query: pageQuery,
2346
+ success: Page(ActorTimelineEntry),
2347
+ error: InspectErrors
2348
+ }), HttpApiEndpoint5.get("listCommands", "/projects/:projectId/environments/:environment/runtime/commands", {
2349
+ params: environmentParams2,
2350
+ query: {
2351
+ ...pageQuery,
2352
+ actorType: Schema22.optional(Schema22.String),
2353
+ outcome: Schema22.optional(CommandOutcome)
2354
+ },
2355
+ success: Page(CommandLogEntry),
2356
+ error: InspectErrors
2357
+ }), HttpApiEndpoint5.post("sendCommand", "/projects/:projectId/environments/:environment/runtime/commands", {
2358
+ params: environmentParams2,
2359
+ payload: SendCommand,
2360
+ success: CommandSent,
2361
+ error: [
2362
+ ...WriteErrors,
2363
+ CommandFailed2,
2364
+ CommandRefused,
2365
+ CommandExpired,
2366
+ RunnerDefect,
2367
+ ...QuotaErrors
2368
+ ]
2369
+ }).annotate(OpenApi2.Description, "Sends to an actor address, including one not yet created, without requiring an inspector read. Requires project write permission. `commandId` is an optional client idempotency key, not a runner command id. Its scope is organization/project/environment/actor address/command, independent of deployment. The control plane durably assigns a runner-minted id and stores only a canonical payload hash; the same key and JSON input reuse it and replay the receipt with `replayed: true` within the runner's retry window. Different input returns 409 Conflict. After expiry, the key is retained as a tombstone for 30 days and returns 410 `CommandExpired`; reusing it after that starts a new command. Declared actor errors are 422 CommandFailed; admission refusals are typed 4xx, including 422 CommandRefused. Mailbox backpressure remains 503 Unavailable. Remote defects are opaque, non-retryable 502 RunnerDefect errors. The edge's usage refusals keep the framework's tags and payloads: a full Free command quota is a 429 `QuotaExceeded`, a passed spend limit a 402 `SpendLimitExceeded`, a full connection allowance a 429 `ConnectionLimitExceeded` and a Free tenant at its storage cap a 429 `StorageQuotaExceeded`. A command the edge cannot bill, because the tenant has no organization, the organization no billing account, or its plan is not in the pricing configuration, is a 402 `QuotaUnbound` carrying that reason, never a generic 503."), HttpApiEndpoint5.get("streamCommands", "/projects/:projectId/environments/:environment/runtime/commands/stream", {
2370
+ params: environmentParams2,
2371
+ query: {
2372
+ actorType: Schema22.optional(Schema22.String),
2373
+ outcome: Schema22.optional(CommandOutcome)
2374
+ },
2375
+ success: HttpApiSchema2.StreamSse({
2376
+ data: Schema22.toCodecJson(CommandLogEntry),
2377
+ error: CommandStreamGap
2378
+ }),
2379
+ error: InspectErrors
2380
+ }).annotate(OpenApi2.Description, "Each command the serving runner commits from now on, filtered by actor type and outcome (`replayed` is always empty, since a replay commits no turn), with a payload preview of at most 256 characters that the runner cut and redacted. The control plane resumes across the runner's credential expiry; the stream fails with `CommandStreamGap` when the runner can no longer resume it without missing commands, and ends when the runner goes away; a client reconnects. `NotImplemented` while more than one runner serves the environment."), HttpApiEndpoint5.get("getJobs", "/projects/:projectId/environments/:environment/runtime/jobs", {
2381
+ params: environmentParams2,
2382
+ success: JobsSummary,
2383
+ error: InspectErrors
2384
+ }), HttpApiEndpoint5.get("listDeadLetters", "/projects/:projectId/environments/:environment/runtime/dead-letters", {
2385
+ params: environmentParams2,
2386
+ query: pageQuery,
2387
+ success: Page(DeadLetter),
2388
+ error: InspectErrors
2389
+ }), HttpApiEndpoint5.post("retryDeadLetter", "/projects/:projectId/environments/:environment/runtime/dead-letters/:deadLetterId/retry", {
2390
+ params: { ...environmentParams2, deadLetterId: DeadLetterId },
2391
+ error: WriteErrors
2392
+ }), HttpApiEndpoint5.post("discardDeadLetter", "/projects/:projectId/environments/:environment/runtime/dead-letters/:deadLetterId/discard", {
2393
+ params: { ...environmentParams2, deadLetterId: DeadLetterId },
2394
+ error: WriteErrors
2395
+ }), HttpApiEndpoint5.get("listWorkflows", "/projects/:projectId/environments/:environment/runtime/workflows", {
2396
+ params: environmentParams2,
2397
+ query: {
2398
+ ...pageQuery,
2399
+ status: Schema22.optional(Schema22.Literals(["running", "waiting", "completed", "failed"]))
2400
+ },
2401
+ success: Page(Workflow),
2402
+ error: InspectErrors
2403
+ }), HttpApiEndpoint5.get("getTimers", "/projects/:projectId/environments/:environment/runtime/timers", {
2404
+ params: environmentParams2,
2405
+ success: TimersSummary,
2406
+ error: InspectErrors
2407
+ }), HttpApiEndpoint5.get("listSchedules", "/projects/:projectId/environments/:environment/runtime/schedules", {
2408
+ params: environmentParams2,
2409
+ success: Schema22.Array(Schedule),
2410
+ error: InspectErrors
2411
+ }), HttpApiEndpoint5.get("getConnections", "/projects/:projectId/environments/:environment/runtime/connections", { params: environmentParams2, success: ConnectionsSummary, error: InspectErrors })) {
2412
+ }
2413
+
2414
+ // ../../packages/cloud-api/src/contract.ts
2415
+ class CloudApi extends HttpApi.make("akter-cloud").add(AccountGroup, OrganizationsGroup, MembersGroup, InvitationsGroup, ApiKeysGroup, ProjectsGroup, EnvironmentVariablesGroup, DomainsGroup, RegionsGroup, IntegrationsGroup, DeploymentsGroup, RuntimeGroup, BillingGroup, UsageGroup, AuditGroup).middleware(Authentication).prefix("/api").annotateMerge(OpenApi3.annotations({ title: "Akter Cloud API" })) {
2416
+ }
2417
+ // src/commands/cloud/deploy.ts
2418
+ import { Clock as Clock3, Config as Config3, Console as Console9, Duration as Duration3, Effect as Effect16, Option as Option8, Schema as Schema26, Stream } from "effect";
2419
+ import { Command as Command13, Flag as Flag12 } from "effect/cli";
2420
+ import { ChildProcess, ChildProcessSpawner } from "effect/process";
2421
+
2422
+ // src/commands/cloud/archive.ts
2423
+ import { Effect as Effect13, FileSystem as FileSystem3, Option as Option6, Schema as Schema23 } from "effect";
2424
+ import { gzipSync } from "node:zlib";
2425
+
2426
+ class ContextInvalid extends Schema23.TaggedError()("ContextInvalid", {
2427
+ message: Schema23.String
2428
+ }) {
2429
+ }
2430
+ var compile = (pattern) => {
2431
+ let source = "^";
2432
+ for (let index = 0;index < pattern.length; index++) {
2433
+ const char = pattern[index];
2434
+ if (char === "*" && pattern[index + 1] === "*") {
2435
+ index++;
2436
+ if (pattern[index + 1] === "/")
2437
+ index++;
2438
+ source += index + 1 >= pattern.length ? ".*" : "(.*/)?";
2439
+ } else if (char === "*")
2440
+ source += "[^/]*";
2441
+ else if (char === "?")
2442
+ source += "[^/]";
2443
+ else if (char === "\\" && index + 1 < pattern.length) {
2444
+ index++;
2445
+ source += `\\${pattern[index]}`;
2446
+ } else if (".+()|{}^$".includes(char))
2447
+ source += `\\${char}`;
2448
+ else
2449
+ source += char;
2450
+ }
2451
+ return new RegExp(`${source}$`, "u");
2452
+ };
2453
+ var ignoreRules = (text) => text.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).flatMap((line) => {
2454
+ const exception = line.startsWith("!");
2455
+ const body = exception ? line.slice(1) : line;
2456
+ const directory = body.endsWith("/");
2457
+ const trimmed = body.replace(/\/+$/u, "");
2458
+ const pattern = trimmed.replace(/^\//u, "");
2459
+ if (pattern === "")
2460
+ return [];
2461
+ return [
2462
+ {
2463
+ exception,
2464
+ directory,
2465
+ pattern: compile(trimmed.includes("/") ? pattern : `**/${pattern}`)
2466
+ }
2467
+ ];
2468
+ });
2469
+ var isIgnored = (rules) => (path, directory) => {
2470
+ const segments = path.split("/");
2471
+ return segments.some((_, index) => {
2472
+ const prefix = segments.slice(0, index + 1).join("/");
2473
+ const isDirectory = index < segments.length - 1 || directory;
2474
+ return rules.reduce((ignored, rule) => (!rule.directory || isDirectory) && rule.pattern.test(prefix) ? !rule.exception : ignored, false);
2475
+ });
2476
+ };
2477
+ var BLOCK = 512;
2478
+ var encoder = new TextEncoder;
2479
+ var octal = (value, width) => `${value.toString(8).padStart(width - 1, "0")}\x00`;
2480
+ var headers = (entry) => {
2481
+ const fits = (value) => encoder.encode(value).byteLength <= 100 && /^[\x20-\x7e]*$/u.test(value);
2482
+ const extended = entry.type !== "x" && (!fits(entry.path) || !fits(entry.target)) ? pax([
2483
+ ["path", entry.path],
2484
+ ["linkpath", entry.target]
2485
+ ].filter(([, value]) => value !== "")) : [];
2486
+ const header = new Uint8Array(BLOCK);
2487
+ const put = (offset, value) => header.set(encoder.encode(value).subarray(0, value.length), offset);
2488
+ put(0, extended.length > 0 ? entry.path.slice(0, 99).replace(/[^\x20-\x7e]/gu, "_") : entry.path);
2489
+ put(100, octal(entry.mode, 8));
2490
+ put(108, octal(0, 8));
2491
+ put(116, octal(0, 8));
2492
+ put(124, octal(entry.size, 12));
2493
+ put(136, octal(0, 12));
2494
+ put(148, " ");
2495
+ put(156, entry.type);
2496
+ put(157, extended.length > 0 ? "" : entry.target);
2497
+ put(257, "ustar\x00");
2498
+ put(263, "00");
2499
+ put(148, `${header.reduce((sum, byte) => sum + byte, 0).toString(8).padStart(6, "0")}\x00 `);
2500
+ return [...extended, header];
2501
+ };
2502
+ var pax = (records) => {
2503
+ const body = encoder.encode(records.map(([key, value]) => {
2504
+ const line = ` ${key}=${value}
2505
+ `;
2506
+ const size = encoder.encode(line).byteLength;
2507
+ let digits = 1;
2508
+ while (String(size + digits).length !== digits)
2509
+ digits += 1;
2510
+ return `${size + digits}${line}`;
2511
+ }).join(""));
2512
+ return [
2513
+ ...headers({
2514
+ path: "././@PaxHeader",
2515
+ mode: 420,
2516
+ size: body.byteLength,
2517
+ type: "x",
2518
+ target: ""
2519
+ }),
2520
+ padded(body)
2521
+ ];
2522
+ };
2523
+ var padded = (bytes) => {
2524
+ const block = new Uint8Array(Math.ceil(bytes.byteLength / BLOCK) * BLOCK);
2525
+ block.set(bytes);
2526
+ return block;
2527
+ };
2528
+ var tar = (entries) => {
2529
+ const blocks = entries.flatMap((entry) => ("target" in entry) ? headers({ path: entry.path, mode: 511, size: 0, type: "2", target: entry.target }) : [
2530
+ ...headers({
2531
+ path: entry.path,
2532
+ mode: entry.mode,
2533
+ size: entry.bytes.byteLength,
2534
+ type: "0",
2535
+ target: ""
2536
+ }),
2537
+ padded(entry.bytes)
2538
+ ]);
2539
+ const bytes = new Uint8Array(blocks.reduce((size, block) => size + block.byteLength, 0) + 2 * BLOCK);
2540
+ let offset = 0;
2541
+ for (const block of blocks) {
2542
+ bytes.set(block, offset);
2543
+ offset += block.byteLength;
2544
+ }
2545
+ return gzipSync(bytes);
2546
+ };
2547
+ var GIT_DIRECTORY = ignoreRules(".git");
2548
+ var packContext = (input) => Effect13.gen(function* () {
2549
+ const fs = yield* FileSystem3.FileSystem;
2550
+ const root = input.context.replace(/[\\/]+$/u, "");
2551
+ let rules = [];
2552
+ for (const candidate of [".akterignore", ".gitignore"])
2553
+ if (yield* fs.exists(`${root}/${candidate}`)) {
2554
+ rules = ignoreRules(yield* fs.readFileString(`${root}/${candidate}`));
2555
+ break;
2556
+ }
2557
+ const ignored = isIgnored([...rules, ...GIT_DIRECTORY]);
2558
+ const entries = [];
2559
+ const walk = (directory) => Effect13.gen(function* () {
2560
+ const names = (yield* fs.readDirectory(directory === "" ? root : `${root}/${directory}`)).toSorted();
2561
+ for (const name of names) {
2562
+ const path = directory === "" ? name : `${directory}/${name}`;
2563
+ const link = yield* Effect13.option(fs.readLink(`${root}/${path}`));
2564
+ if (Option6.isSome(link)) {
2565
+ if (!ignored(path, false))
2566
+ entries.push({ path, target: link.value });
2567
+ continue;
2568
+ }
2569
+ const info = yield* fs.stat(`${root}/${path}`);
2570
+ if (info.type === "Directory") {
2571
+ if (!ignored(path, true))
2572
+ yield* walk(path);
2573
+ } else if (info.type === "File" && !ignored(path, false))
2574
+ entries.push({
2575
+ path,
2576
+ mode: info.mode & 511,
2577
+ bytes: yield* fs.readFile(`${root}/${path}`)
2578
+ });
2579
+ }
2580
+ }).pipe(Effect13.catchTag("PlatformError", (error) => Effect13.fail(ContextInvalid.make({ message: `Cannot read ${root}/${directory}: ${error.message}` }))));
2581
+ yield* walk("");
2582
+ return { archive: tar(entries), files: entries.map((entry) => entry.path) };
2583
+ });
2584
+
2585
+ // src/commands/cloud/client.ts
2586
+ import { Console as Console8, Duration as Duration2, Effect as Effect15, Match, Schema as Schema25 } from "effect";
2587
+ import { HttpClient as HttpClient2, HttpClientRequest as HttpClientRequest2 } from "effect/http";
2588
+ import { HttpApiClient } from "effect/http-api";
2589
+
2590
+ // src/commands/cloud/credentials.ts
2591
+ import { homedir } from "node:os";
2592
+ import { Config as Config2, Crypto, Effect as Effect14, FileSystem as FileSystem4, Option as Option7, Schema as Schema24 } from "effect";
2593
+ var isLoopback = (hostname) => hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "[::1]" || /^127(?:\.\d{1,3}){3}$/u.test(hostname);
2594
+ var controlPlaneUrl = (input) => {
2595
+ const url = URL.parse(input);
2596
+ if (url === null || url.username !== "" || url.password !== "" || url.search !== "")
2597
+ return Option7.none();
2598
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback(url.hostname)))
2599
+ return Option7.none();
2600
+ return Option7.some(`${url.origin}${url.pathname}`.replace(/\/+$/u, ""));
2601
+ };
2602
+ var Credentials = Schema24.Struct({
2603
+ apiUrl: Schema24.String.check(Schema24.makeFilter((url) => Option7.isSome(controlPlaneUrl(url)) || "An https URL, or http on a loopback host")),
2604
+ token: Schema24.String,
2605
+ email: Schema24.String
2606
+ });
2607
+
2608
+ class NotLoggedIn extends Schema24.TaggedError()("NotLoggedIn", {
2609
+ path: Schema24.String
2610
+ }) {
2611
+ }
2612
+
2613
+ class CredentialsExposed extends Schema24.TaggedError()("CredentialsExposed", { path: Schema24.String, mode: Schema24.Int }) {
2614
+ }
2615
+
2616
+ class CredentialsUnreadable extends Schema24.TaggedError()("CredentialsUnreadable", { path: Schema24.String }) {
2617
+ }
2618
+ var configDirectory = (location) => {
2619
+ if (location.override !== undefined && location.override !== "")
2620
+ return location.override;
2621
+ if (location.platform === "darwin")
2622
+ return `${location.home}/Library/Application Support/akter`;
2623
+ if (location.platform === "win32")
2624
+ return `${location.appData ?? `${location.home}\\AppData\\Roaming`}\\akter`;
2625
+ return `${location.xdgConfigHome === undefined || location.xdgConfigHome === "" ? `${location.home}/.config` : location.xdgConfigHome}/akter`;
2626
+ };
2627
+ var optional = (name) => Config2.option(Config2.String(name)).pipe(Effect14.map(Option7.getOrUndefined));
2628
+ var credentialsPath = Effect14.gen(function* () {
2629
+ const platform = process.platform;
2630
+ const directory = configDirectory({
2631
+ platform,
2632
+ home: homedir(),
2633
+ override: yield* optional("AKTER_CONFIG_DIR"),
2634
+ xdgConfigHome: yield* optional("XDG_CONFIG_HOME"),
2635
+ appData: yield* optional("APPDATA")
2636
+ });
2637
+ return { directory, file: `${directory}${platform === "win32" ? "\\" : "/"}credentials.json` };
2638
+ });
2639
+ var encode = Schema24.encodeEffect(Schema24.fromJsonString(Credentials));
2640
+ var decode = Schema24.decodeUnknownEffect(Schema24.fromJsonString(Credentials));
2641
+ var saveCredentials = (credentials) => Effect14.gen(function* () {
2642
+ const fs = yield* FileSystem4.FileSystem;
2643
+ const { directory, file } = yield* credentialsPath;
2644
+ const suffix = yield* (yield* Crypto.Crypto).randomUUIDv4.pipe(Effect14.orDie);
2645
+ const pending = `${file}.${suffix}.tmp`;
2646
+ yield* fs.makeDirectory(directory, { recursive: true, mode: 448 });
2647
+ yield* fs.chmod(directory, 448);
2648
+ yield* Effect14.gen(function* () {
2649
+ yield* fs.writeFileString(pending, yield* encode(credentials).pipe(Effect14.orDie), {
2650
+ flag: "wx",
2651
+ mode: 384
2652
+ });
2653
+ yield* fs.chmod(pending, 384);
2654
+ yield* fs.rename(pending, file);
2655
+ }).pipe(Effect14.onError(() => fs.remove(pending, { force: true }).pipe(Effect14.ignore)));
2656
+ return file;
2657
+ });
2658
+ var readCredentials = Effect14.gen(function* () {
2659
+ const fs = yield* FileSystem4.FileSystem;
2660
+ const { file } = yield* credentialsPath;
2661
+ if (!(yield* fs.exists(file)))
2662
+ return yield* NotLoggedIn.make({ path: file });
2663
+ const mode = (yield* fs.stat(file)).mode & 511;
2664
+ const credentials = yield* fs.readFileString(file).pipe(Effect14.flatMap(decode), Effect14.mapError(() => CredentialsUnreadable.make({ path: file })));
2665
+ return { credentials, file, mode };
2666
+ });
2667
+ var loadCredentials = Effect14.gen(function* () {
2668
+ const { credentials, file, mode } = yield* readCredentials;
2669
+ if (process.platform !== "win32" && (mode & 63) !== 0)
2670
+ return yield* CredentialsExposed.make({ path: file, mode });
2671
+ return credentials;
2672
+ });
2673
+ var loadCredentialsToRevoke = Effect14.map(readCredentials, (read) => read.credentials);
2674
+ var removeCredentials = Effect14.gen(function* () {
2675
+ const fs = yield* FileSystem4.FileSystem;
2676
+ const { file } = yield* credentialsPath;
2677
+ if (!(yield* fs.exists(file)))
2678
+ return false;
2679
+ yield* fs.remove(file);
2680
+ return true;
2681
+ });
2682
+
2683
+ // src/commands/cloud/client.ts
2684
+ var DEFAULT_API_URL = "https://api.akter.dev";
2685
+ var cloudClient = (credentials) => HttpApiClient.make(CloudApi, {
2686
+ baseUrl: credentials.apiUrl,
2687
+ transformClient: HttpClient2.mapRequest(HttpClientRequest2.bearerToken(credentials.token))
2688
+ });
2689
+ var signedIn = Effect15.gen(function* () {
2690
+ const credentials = yield* loadCredentials;
2691
+ return { credentials, client: yield* cloudClient(credentials) };
2692
+ });
2693
+ var MAX_READ_RETRIES = 5;
2694
+ var readRetryDelay = (attempt) => Duration2.seconds(Math.min(2 ** (attempt - 1), 8));
2695
+ var isTransient = (error) => Match.value(error).pipe(Match.tag("Unavailable", (unavailable) => unavailable.reason === undefined), Match.tag("HttpClientError", ({ reason }) => Match.value(reason).pipe(Match.tag("TransportError", () => true), Match.tag("StatusCodeError", "DecodeError", "EmptyBodyError", ({ response }) => response.status >= 500), Match.orElse(() => false))), Match.orElse(() => false));
2696
+ var retryTransient = (notice) => {
2697
+ let failures = 0;
2698
+ const read = (effect) => effect.pipe(Effect15.tap(() => Effect15.sync(() => {
2699
+ failures = 0;
2700
+ })), Effect15.catchIf((error) => failures < MAX_READ_RETRIES && isTransient(error), () => Effect15.gen(function* () {
2701
+ failures += 1;
2702
+ yield* Console8.error(notice);
2703
+ yield* Effect15.sleep(readRetryDelay(failures));
2704
+ return yield* read(effect);
2705
+ })));
2706
+ return read;
2707
+ };
2708
+ var octal2 = (mode) => mode.toString(8).padStart(3, "0");
2709
+ var describe = Match.type().pipe(Match.tagsExhaustive({
2710
+ NotLoggedIn: () => ({
2711
+ reason: "NotLoggedIn",
2712
+ message: "Not logged in. Run `akter login` first.",
2713
+ exitCode: 2
2714
+ }),
2715
+ CredentialsExposed: (error) => ({
2716
+ reason: "CredentialsExposed",
2717
+ message: `${error.path} can be read by other users (mode ${octal2(error.mode)}). Run \`chmod 600\` on it or \`akter login\` again.`,
2718
+ exitCode: 2
2719
+ }),
2720
+ CredentialsUnreadable: (error) => ({
2721
+ reason: "CredentialsUnreadable",
2722
+ message: `${error.path} does not hold akter credentials. Run \`akter login\` again.`,
2723
+ exitCode: 2
2724
+ }),
2725
+ Unauthorized: () => ({
2726
+ reason: "Unauthorized",
2727
+ message: "Your session has expired or was revoked. Run `akter login` to sign in again.",
2728
+ exitCode: 1
2729
+ }),
2730
+ Forbidden: (error) => ({
2731
+ reason: "Forbidden",
2732
+ message: `Refused: ${error.message}`,
2733
+ exitCode: 1
2734
+ }),
2735
+ NotFound: (error) => ({
2736
+ reason: "NotFound",
2737
+ message: `No ${error.resource} ${error.id} is visible to you.`,
2738
+ exitCode: 1
2739
+ }),
2740
+ Conflict: (error) => ({
2741
+ reason: "Conflict",
2742
+ message: `Refused: ${error.message}`,
2743
+ exitCode: 1
2744
+ }),
2745
+ NotImplemented: (error) => ({
2746
+ reason: "NotImplemented",
2747
+ message: `This control plane does not support ${error.operation}.`,
2748
+ exitCode: 1
2749
+ }),
2750
+ PayloadTooLarge: (error) => ({
2751
+ reason: "PayloadTooLarge",
2752
+ message: `The control plane accepts at most ${error.limitBytes} bytes; exclude more files in .dockerignore.`,
2753
+ exitCode: 1
2754
+ }),
2755
+ Unavailable: () => ({
2756
+ reason: "Unavailable",
2757
+ message: "The control plane is temporarily unavailable. Try again shortly.",
2758
+ exitCode: 1
2759
+ }),
2760
+ HttpClientError: (error) => ({
2761
+ reason: "Unreachable",
2762
+ message: `Cannot reach the control plane: ${error.message}`,
2763
+ exitCode: 2
2764
+ }),
2765
+ SchemaError: (error) => ({
2766
+ reason: "SchemaError",
2767
+ message: `The control plane's answer was not understood: ${error.message}`,
2768
+ exitCode: 2
2769
+ }),
2770
+ PlatformError: (error) => ({
2771
+ reason: "PlatformError",
2772
+ message: `Cannot use the file system: ${error.message}`,
2773
+ exitCode: 2
2774
+ }),
2775
+ ConfigError: (error) => ({
2776
+ reason: "ConfigError",
2777
+ message: `Cannot read the environment: ${error.message}`,
2778
+ exitCode: 2
2779
+ })
2780
+ }));
2781
+ var reportFailures = (effect) => Effect15.catchIf(effect, (error) => !Schema25.is(CommandFailed)(error), (error) => fail(describe(error)));
2782
+
2783
+ // src/commands/cloud/deploy.ts
2784
+ class DeploymentFailed extends Schema26.TaggedError()("DeploymentFailed", {
2785
+ deploymentId: Schema26.String,
2786
+ step: Schema26.String,
2787
+ detail: Schema26.String
2788
+ }) {
2789
+ }
2790
+
2791
+ class DeploymentTimedOut extends Schema26.TaggedError()("DeploymentTimedOut", { deploymentId: Schema26.String, seconds: Schema26.Int }) {
2792
+ }
2793
+ var flags9 = {
2794
+ project: Flag12.String("project").pipe(Flag12.withFallbackConfig(Config3.String("AKTER_PROJECT")), Flag12.withSchema(ProjectId), Flag12.withDescription("The project to deploy to (default AKTER_PROJECT)")),
2795
+ environment: Flag12.Literals("env", EnvironmentName.literals).pipe(Flag12.withDefault("production"), Flag12.withDescription("The environment to deploy to (default production)")),
2796
+ context: Flag12.Directory("context", { mustExist: true }).pipe(Flag12.withDefault("."), Flag12.withDescription(`The app directory to upload, holding ${SOURCE_ENTRY} (default the current directory)`)),
2797
+ commit: Flag12.String("commit").pipe(Flag12.optional, Flag12.withDescription("The commit SHA the deployment is labeled with (default the context's git HEAD)")),
2798
+ message: Flag12.String("message").pipe(Flag12.optional, Flag12.withDescription("The deployment's message (default the commit's subject)")),
2799
+ timeout: Flag12.Int("timeout").pipe(Flag12.withDefault(900), Flag12.withDescription("Seconds to follow the rollout before giving up on it (default 900)"))
2800
+ };
2801
+ var git = (directory, args) => Effect16.gen(function* () {
2802
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
2803
+ const handle = yield* spawner.spawn(ChildProcess.make("git", ["-C", directory, ...args]));
2804
+ const [out] = yield* Effect16.all([handle.stdout.pipe(Stream.decodeText, Stream.mkString), handle.stderr.pipe(Stream.runDrain)], { concurrency: 2 });
2805
+ return Number(yield* handle.exitCode) === 0 ? out.trim() : undefined;
2806
+ }).pipe(Effect16.scoped, Effect16.orElseSucceed(() => {
2807
+ return;
2808
+ }));
2809
+ var label = Effect16.fnUntraced(function* (input) {
2810
+ const head = yield* git(input.context, ["rev-parse", "HEAD"]);
2811
+ const commitSha = input.commit ?? head ?? input.digest.slice("sha256:".length, 47);
2812
+ if (input.message !== undefined)
2813
+ return { commitSha, message: input.message };
2814
+ if (input.commit !== undefined || head === undefined)
2815
+ return { commitSha, message: "" };
2816
+ const subject = (yield* git(input.context, ["log", "-1", "--format=%s"])) ?? "";
2817
+ const dirty = ((yield* git(input.context, ["status", "--porcelain"])) ?? "") !== "";
2818
+ return { commitSha, message: dirty ? `${subject} (with uncommitted changes)` : subject };
2819
+ });
2820
+ var seconds = (durationMs) => durationMs === null ? "" : ` in ${(durationMs / 1000).toFixed(1)}s`;
2821
+ var follow = Effect16.fnUntraced(function* (input) {
2822
+ const params = { projectId: input.projectId, deploymentId: input.deployment.id };
2823
+ const deadline = (yield* Clock3.currentTimeMillis) + input.timeoutSeconds * 1000;
2824
+ const printed = new Map;
2825
+ let detail = input.deployment;
2826
+ let cursor = 0;
2827
+ let following = false;
2828
+ let logComplete = false;
2829
+ const read = retryTransient("The control plane is unavailable; still following the deployment.");
2830
+ const printLog = Effect16.gen(function* () {
2831
+ const log = yield* read(input.client.deployments.getBuildLog({ params, query: { after: cursor } }));
2832
+ const last = log.lines.at(-1);
2833
+ for (const line of log.lines)
2834
+ yield* Console9.log(` ${line.text}`);
2835
+ if (last !== undefined)
2836
+ cursor = last.index + 1;
2837
+ following = true;
2838
+ logComplete = log.complete;
2839
+ });
2840
+ while (true) {
2841
+ for (const step of detail.steps) {
2842
+ if (step.status === "pending" || printed.get(step.name) === step.status)
2843
+ continue;
2844
+ if (step.name === "build" && step.status === "succeeded" && following && !logComplete)
2845
+ yield* printLog;
2846
+ printed.set(step.name, step.status);
2847
+ yield* Console9.log(` ${step.name} ${step.status}${step.status === "running" ? "" : seconds(step.durationMs)}`);
2848
+ }
2849
+ if (!logComplete && detail.steps.some((step) => step.name === "build" && step.status === "running"))
2850
+ yield* printLog;
2851
+ if (detail.status === "live")
2852
+ return detail;
2853
+ if (detail.status === "failed") {
2854
+ const failed = detail.steps.find((step) => step.status === "failed");
2855
+ if (failed?.name === "build") {
2856
+ const log = yield* read(input.client.deployments.getBuildLog({ params, query: {} }));
2857
+ for (const line of log.lines.slice(-20))
2858
+ yield* Console9.error(` ${line.text}`);
2859
+ }
2860
+ return yield* DeploymentFailed.make({
2861
+ deploymentId: detail.id,
2862
+ step: failed?.name ?? "rollout",
2863
+ detail: failed?.detail ?? "no reason was recorded"
2864
+ });
2865
+ }
2866
+ if ((yield* Clock3.currentTimeMillis) >= deadline)
2867
+ return yield* DeploymentTimedOut.make({
2868
+ deploymentId: detail.id,
2869
+ seconds: input.timeoutSeconds
2870
+ });
2871
+ yield* Effect16.sleep(Duration3.seconds(1));
2872
+ detail = yield* read(input.client.deployments.get({ params }));
2873
+ }
2874
+ });
2875
+ var deployCommand = Command13.make("deploy", flags9, (options) => Effect16.gen(function* () {
2876
+ const { client, credentials } = yield* signedIn;
2877
+ const packed = yield* packContext({ context: options.context });
2878
+ if (!packed.files.includes(SOURCE_ENTRY))
2879
+ return yield* ContextInvalid.make({
2880
+ message: `No ${SOURCE_ENTRY} in ${options.context}, or its .akterignore or .gitignore leaves it out; the platform serves the App that file exports by default`
2881
+ });
2882
+ if (packed.archive.byteLength > MAX_SOURCE_BYTES)
2883
+ return yield* fail({
2884
+ reason: "ContextTooLarge",
2885
+ message: `The app directory is ${packed.archive.byteLength} bytes compressed; the control plane accepts at most ${MAX_SOURCE_BYTES}. Exclude more files in .akterignore.`
2886
+ });
2887
+ yield* Console9.log(`Uploading ${packed.files.length} files (${packed.archive.byteLength} bytes) from ${options.context} to ${credentials.apiUrl}`);
2888
+ const source = yield* client.deployments.uploadSource({
2889
+ params: { projectId: options.project },
2890
+ payload: packed.archive
2891
+ });
2892
+ const { commitSha, message } = yield* label({
2893
+ context: options.context,
2894
+ digest: source.digest,
2895
+ commit: Option8.getOrUndefined(options.commit),
2896
+ message: Option8.getOrUndefined(options.message)
2897
+ });
2898
+ const deployment = yield* client.deployments.create({
2899
+ params: { projectId: options.project },
2900
+ payload: {
2901
+ environment: options.environment,
2902
+ commitSha,
2903
+ message,
2904
+ source: { digest: source.digest }
2905
+ }
2906
+ });
2907
+ yield* Console9.log(`Deployment ${deployment.id} of ${commitSha.slice(0, 7)} to ${options.environment} started`);
2908
+ const live = yield* follow({
2909
+ client,
2910
+ projectId: options.project,
2911
+ deployment,
2912
+ timeoutSeconds: options.timeout
2913
+ });
2914
+ yield* Console9.log(`Deployment ${live.id} is live in ${options.environment}`);
2915
+ const report = yield* retryTransient("The control plane is unavailable; retrying the database report.")(client.projects.getEnvironment({
2916
+ params: { projectId: options.project, environment: options.environment }
2917
+ })).pipe(Effect16.catch(() => Console9.error("Warning: could not read the database report; the deployment is already live.").pipe(Effect16.as(undefined))));
2918
+ if (report === undefined)
2919
+ return;
2920
+ if (report.currentDeploymentId !== live.id)
2921
+ return yield* Console9.error("Warning: the database report is not for this deployment; skipping its diagnostics.");
2922
+ const { database } = report;
2923
+ if (database !== undefined) {
2924
+ if (database.latencyWarning)
2925
+ yield* Console9.error(`Warning: database p50 latency${database.latency === null ? "" : ` (${database.latency} ms)`} exceeds 5 ms from the runner region; deployment is not refused.`);
2926
+ if (database.runnerCap !== null)
2927
+ yield* Console9.log(`Database runner cap: ${database.runnerCap}`);
2928
+ }
2929
+ }).pipe(Effect16.catchTags({
2930
+ ContextInvalid: (error) => fail({ reason: "ContextInvalid", message: error.message }),
2931
+ DeploymentFailed: (error) => fail({
2932
+ reason: "DeploymentFailed",
2933
+ message: `Deployment ${error.deploymentId} failed at ${error.step}: ${error.detail}. The previous deployment, if any, is still serving.`,
2934
+ exitCode: 1
2935
+ }),
2936
+ DeploymentTimedOut: (error) => fail({
2937
+ reason: "DeploymentTimedOut",
2938
+ message: `Deployment ${error.deploymentId} was still rolling out after ${error.seconds}s; it continues on the control plane.`,
2939
+ exitCode: 1
2940
+ })
2941
+ }), reportFailures)).pipe(Command13.withDescription("Upload the app directory, build and roll it out on Akter Cloud, and follow it until it is live"));
2942
+
2943
+ // src/commands/cloud/login.ts
2944
+ import { Clock as Clock4, Config as Config4, Console as Console10, Data, Duration as Duration4, Effect as Effect17, Schema as Schema27 } from "effect";
2945
+ import { Command as Command14, Flag as Flag13 } from "effect/cli";
2946
+ import { HttpClient as HttpClient3, HttpClientRequest as HttpClientRequest3, HttpClientResponse } from "effect/http";
2947
+ var CLIENT_ID = "akter-cli";
2948
+ var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
2949
+ var DeviceCode = Schema27.Struct({
2950
+ device_code: Schema27.String,
2951
+ user_code: Schema27.String,
2952
+ verification_uri: Schema27.String,
2953
+ expires_in: Schema27.Finite,
2954
+ interval: Schema27.Finite
2955
+ });
2956
+ var DeviceToken = Schema27.Struct({ access_token: Schema27.String });
2957
+ var DeviceError = Schema27.Struct({ error: Schema27.String });
2958
+ var displayCode = (code) => code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;
2959
+
2960
+ class LoginDenied extends Schema27.TaggedError()("LoginDenied", {}) {
2961
+ }
2962
+
2963
+ class LoginExpired extends Schema27.TaggedError()("LoginExpired", {}) {
2964
+ }
2965
+
2966
+ class LoginRefused extends Schema27.TaggedError()("LoginRefused", {
2967
+ status: Schema27.Int,
2968
+ error: Schema27.String
2969
+ }) {
2970
+ }
2971
+ var Poll = Data.taggedEnum();
2972
+ var flags10 = {
2973
+ apiUrl: Flag13.String("api-url").pipe(Flag13.withFallbackConfig(Config4.String("AKTER_API_URL")), Flag13.withDefault(DEFAULT_API_URL), Flag13.filterMap(controlPlaneUrl, () => "an https URL, or http on a loopback host"), Flag13.withDescription(`The control plane to sign in to (default AKTER_API_URL, then ${DEFAULT_API_URL})`))
2974
+ };
2975
+ var post = (url, body) => Effect17.flatMap(HttpClient3.HttpClient, (client) => client.execute(HttpClientRequest3.bodyJsonUnsafe(HttpClientRequest3.post(url), body)));
2976
+ var refusal = (response) => HttpClientResponse.schemaBodyJson(DeviceError)(response).pipe(Effect17.orElseSucceed(() => ({ error: `HTTP ${response.status}` })));
2977
+ var deviceLogin = (apiUrl) => Effect17.gen(function* () {
2978
+ const started = yield* post(`${apiUrl}/auth/device/code`, { client_id: CLIENT_ID });
2979
+ if (started.status !== 200) {
2980
+ const { error } = yield* refusal(started);
2981
+ return yield* LoginRefused.make({ status: started.status, error });
2982
+ }
2983
+ const code = yield* HttpClientResponse.schemaBodyJson(DeviceCode)(started);
2984
+ yield* Console10.log(`To sign in, open ${code.verification_uri}
2985
+ and enter the code ${displayCode(code.user_code)}. Waiting for approval…`);
2986
+ const poll = Effect17.gen(function* () {
2987
+ const response = yield* post(`${apiUrl}/auth/device/token`, {
2988
+ grant_type: DEVICE_GRANT,
2989
+ device_code: code.device_code,
2990
+ client_id: CLIENT_ID
2991
+ });
2992
+ if (response.status === 200)
2993
+ return Poll.Granted({
2994
+ token: (yield* HttpClientResponse.schemaBodyJson(DeviceToken)(response)).access_token
2995
+ });
2996
+ const { error } = yield* refusal(response);
2997
+ if (error === "authorization_pending")
2998
+ return Poll.Pending();
2999
+ if (error === "slow_down")
3000
+ return Poll.SlowDown();
3001
+ if (error === "access_denied")
3002
+ return yield* LoginDenied.make({});
3003
+ if (error === "expired_token")
3004
+ return yield* LoginExpired.make({});
3005
+ return yield* LoginRefused.make({ status: response.status, error });
3006
+ });
3007
+ const deadline = (yield* Clock4.currentTimeMillis) + code.expires_in * 1000;
3008
+ let interval = Duration4.seconds(code.interval);
3009
+ while ((yield* Clock4.currentTimeMillis) < deadline) {
3010
+ yield* Effect17.sleep(interval);
3011
+ const answer = yield* poll;
3012
+ if (Poll.$is("Granted")(answer))
3013
+ return answer.token;
3014
+ if (Poll.$is("SlowDown")(answer))
3015
+ interval = Duration4.sum(interval, Duration4.seconds(5));
3016
+ }
3017
+ return yield* LoginExpired.make({});
3018
+ });
3019
+ var revoke = (apiUrl, token) => HttpClient3.HttpClient.pipe(Effect17.flatMap((client) => client.execute(HttpClientRequest3.post(`${apiUrl}/auth/sign-out`).pipe(HttpClientRequest3.bearerToken(token), HttpClientRequest3.bodyJsonUnsafe({})))), Effect17.ignore);
3020
+ var loginCommand = Command14.make("login", flags10, ({ apiUrl }) => Effect17.gen(function* () {
3021
+ const token = yield* deviceLogin(apiUrl);
3022
+ const client = yield* cloudClient({ apiUrl, token, email: "" });
3023
+ const me = yield* client.account.me().pipe(Effect17.tapError(() => revoke(apiUrl, token)));
3024
+ const email = me.user?.email ?? "";
3025
+ const file = yield* saveCredentials({ apiUrl, token, email });
3026
+ yield* Console10.log(`Logged in to ${apiUrl} as ${email}. Credentials saved to ${file}.`);
3027
+ }).pipe(Effect17.catchTags({
3028
+ LoginDenied: () => fail({
3029
+ reason: "LoginDenied",
3030
+ message: "The sign-in was denied in the browser. Nothing was saved.",
3031
+ exitCode: 1
3032
+ }),
3033
+ LoginExpired: () => fail({
3034
+ reason: "LoginExpired",
3035
+ message: "The sign-in code expired before it was approved. Run `akter login` again.",
3036
+ exitCode: 1
3037
+ }),
3038
+ LoginRefused: (error) => fail({
3039
+ reason: "LoginRefused",
3040
+ message: `The control plane refused the sign-in: ${error.error} (${error.status})`,
3041
+ exitCode: 1
3042
+ })
3043
+ }), reportFailures)).pipe(Command14.withDescription("Sign in to Akter Cloud through the browser and store the session for deploy"));
3044
+
3045
+ // src/commands/cloud/logout.ts
3046
+ import { Console as Console11, Effect as Effect18, Option as Option9 } from "effect";
3047
+ import { Command as Command15 } from "effect/cli";
3048
+ import { HttpClient as HttpClient4, HttpClientRequest as HttpClientRequest4 } from "effect/http";
3049
+ var logout = Effect18.gen(function* () {
3050
+ const credentials = yield* loadCredentialsToRevoke.pipe(Effect18.asSome, Effect18.catchTag("CredentialsUnreadable", () => Effect18.succeedNone), Effect18.map(Option9.getOrUndefined));
3051
+ const revoked = credentials === undefined ? false : yield* HttpClient4.HttpClient.pipe(Effect18.flatMap((client) => client.execute(HttpClientRequest4.post(`${credentials.apiUrl}/auth/sign-out`).pipe(HttpClientRequest4.bearerToken(credentials.token), HttpClientRequest4.bodyJsonUnsafe({})))), Effect18.map((response) => response.status === 200), Effect18.orElseSucceed(() => false));
3052
+ yield* removeCredentials;
3053
+ return { credentials, revoked };
3054
+ });
3055
+ var logoutCommand = Command15.make("logout", {}, () => Effect18.gen(function* () {
3056
+ const { credentials, revoked } = yield* logout;
3057
+ if (credentials === undefined)
3058
+ return yield* Console11.log("Removed the stored credentials.");
3059
+ yield* Console11.log(revoked ? `Logged out of ${credentials.apiUrl}.` : `Removed the stored credentials. ${credentials.apiUrl} could not revoke the session, which ends when it expires.`);
3060
+ }).pipe(Effect18.catchTag("NotLoggedIn", () => Console11.log("Not logged in.")), reportFailures)).pipe(Command15.withDescription("Sign out of Akter Cloud and delete the stored session"));
3061
+
3062
+ // src/commands/cloud/whoami.ts
3063
+ import { Console as Console12, Effect as Effect19 } from "effect";
3064
+ import { Command as Command16 } from "effect/cli";
3065
+ var whoamiCommand = Command16.make("whoami", {}, () => Effect19.gen(function* () {
3066
+ const { credentials, client } = yield* signedIn;
3067
+ const me = yield* client.account.me();
3068
+ yield* Console12.log([
3069
+ `${me.user?.email ?? credentials.email} at ${credentials.apiUrl}`,
3070
+ ...me.organizations.map((membership) => ` ${membership.organization.slug} ${membership.role} ${membership.organization.id}`)
3071
+ ].join(`
3072
+ `));
3073
+ }).pipe(reportFailures)).pipe(Command16.withDescription("Show who the stored Akter Cloud session signs in as"));
3074
+
3075
+ // src/commands/cloud/env.ts
3076
+ import { Config as Config5, Console as Console13, DateTime as DateTime2, Effect as Effect20, FileSystem as FileSystem5, Option as Option10, Stdio, Stream as Stream2 } from "effect";
3077
+ import { Argument as Argument6, Command as Command17, Flag as Flag14 } from "effect/cli";
3078
+ var scope = {
3079
+ project: Flag14.String("project").pipe(Flag14.withFallbackConfig(Config5.String("AKTER_PROJECT")), Flag14.withSchema(ProjectId)),
3080
+ environment: Flag14.Literals("env", EnvironmentName.literals).pipe(Flag14.withDefault("production"))
3081
+ };
3082
+ var name = Argument6.String("name").pipe(Argument6.withSchema(EnvVariableName));
3083
+ var input = (path, limit) => Effect20.gen(function* () {
3084
+ const tooLarge = () => fail({
3085
+ reason: "EnvironmentInputTooLarge",
3086
+ message: `Environment input exceeds ${limit} bytes.`,
3087
+ exitCode: 2
3088
+ });
3089
+ if (path !== undefined && path !== "-") {
3090
+ const fs = yield* FileSystem5.FileSystem;
3091
+ const stat = yield* fs.stat(path);
3092
+ if (Number(stat.size) > limit)
3093
+ return yield* tooLarge();
3094
+ const value = yield* fs.readFileString(path);
3095
+ if (new TextEncoder().encode(value).byteLength > limit)
3096
+ return yield* tooLarge();
3097
+ return value;
3098
+ }
3099
+ const stdio = yield* Stdio.Stdio;
3100
+ if (yield* stdio.stdinIsTerminal)
3101
+ return yield* fail({
3102
+ reason: "EnvironmentInputRequired",
3103
+ message: "Pipe the value on stdin or supply --file. Values are never accepted as command arguments.",
3104
+ exitCode: 2
3105
+ });
3106
+ const collected = yield* stdio.stdin.pipe(Stream2.runFoldEffect(() => ({ size: 0, chunks: new Array }), (state, chunk) => {
3107
+ if (state.size + chunk.length > limit)
3108
+ return tooLarge();
3109
+ state.chunks.push(chunk);
3110
+ state.size += chunk.length;
3111
+ return Effect20.succeed(state);
3112
+ }));
3113
+ const bytes = new Uint8Array(collected.size);
3114
+ let offset = 0;
3115
+ for (const chunk of collected.chunks) {
3116
+ bytes.set(chunk, offset);
3117
+ offset += chunk.length;
3118
+ }
3119
+ return new TextDecoder().decode(bytes);
3120
+ });
3121
+ var list2 = Command17.make("list", scope, ({ project, environment }) => Effect20.gen(function* () {
3122
+ const { client } = yield* signedIn;
3123
+ const variables = yield* client.environmentVariables.list({
3124
+ params: { projectId: project, environment }
3125
+ });
3126
+ for (const variable of variables)
3127
+ yield* Console13.log(`${variable.name} ${DateTime2.formatIso(variable.updatedAt)}`);
3128
+ }).pipe(reportFailures)).pipe(Command17.withDescription("List variable names and update times; values cannot be read back"));
3129
+ var set = Command17.make("set", { ...scope, name, file: Flag14.File("file", { mustExist: true }).pipe(Flag14.optional) }, (options) => Effect20.gen(function* () {
3130
+ const { client } = yield* signedIn;
3131
+ const value = yield* input(Option10.getOrUndefined(options.file), 65536);
3132
+ yield* client.environmentVariables.set({
3133
+ params: {
3134
+ projectId: options.project,
3135
+ environment: options.environment,
3136
+ name: options.name
3137
+ },
3138
+ payload: { value }
3139
+ });
3140
+ yield* Console13.log(`Set ${options.name} in ${options.environment}. The value takes effect on the next deployment.`);
3141
+ }).pipe(reportFailures)).pipe(Command17.withDescription("Set a write-only value from piped stdin or --file"));
3142
+ var unset = Command17.make("unset", { ...scope, name }, ({ project, environment, name }) => Effect20.gen(function* () {
3143
+ const { client } = yield* signedIn;
3144
+ yield* client.environmentVariables.delete({ params: { projectId: project, environment, name } });
3145
+ yield* Console13.log(`Unset ${name} in ${environment}. The change takes effect on the next deployment.`);
3146
+ }).pipe(reportFailures)).pipe(Command17.withDescription("Remove an environment variable"));
3147
+ var importVariables = Command17.make("import", { ...scope, file: Argument6.String("file").pipe(Argument6.withDefault("-")) }, ({ project, environment, file }) => Effect20.gen(function* () {
3148
+ const { client } = yield* signedIn;
3149
+ const content = yield* input(file, 1048576);
3150
+ const result = yield* client.environmentVariables.import({
3151
+ params: { projectId: project, environment },
3152
+ payload: { content }
3153
+ });
3154
+ yield* Console13.log(`Imported ${result.created.length} new and ${result.updated.length} updated variables in ${environment}.`);
3155
+ }).pipe(reportFailures)).pipe(Command17.withDescription("Import a dotenv file atomically; use - or no file for stdin"));
3156
+ var envCommand = Command17.make("env").pipe(Command17.withDescription("Manage encrypted, write-only Akter Cloud environment variables"), Command17.withSubcommands([list2, set, unset, importVariables]));
3157
+
3158
+ // src/commands/cloud/logs.ts
3159
+ import { Config as Config6, Console as Console14, DateTime as DateTime3, Effect as Effect21, Option as Option11, Schema as Schema28 } from "effect";
3160
+ import { Command as Command18, Flag as Flag15 } from "effect/cli";
3161
+ var colourSequence = new RegExp(`${String.fromCodePoint(27)}\\[[0-?]*[ -/]*[@-~]`, "gu");
3162
+ var logsCommand = Command18.make("logs", {
3163
+ project: Flag15.String("project").pipe(Flag15.withFallbackConfig(Config6.String("AKTER_PROJECT")), Flag15.withSchema(ProjectId), Flag15.withDescription("The project (default AKTER_PROJECT)")),
3164
+ environment: Flag15.Literals("env", EnvironmentName.literals).pipe(Flag15.withDefault("production"), Flag15.withDescription("The environment (default production)")),
3165
+ deployment: Flag15.String("deployment").pipe(Flag15.withSchema(DeploymentId), Flag15.optional, Flag15.withDescription("Read a deployment instead of the environment's current deployment")),
3166
+ since: Flag15.Int("since").pipe(Flag15.withSchema(Schema28.Int.check(Schema28.isBetween({ minimum: 1, maximum: MAX_LOG_WINDOW_SECONDS }))), Flag15.withDefault(300), Flag15.withDescription("Seconds of recent output, 1–3600 (default 300)")),
3167
+ limit: Flag15.Int("limit").pipe(Flag15.withSchema(LogLimit), Flag15.withDefault(100), Flag15.withDescription("Maximum lines per response, 1–200 (default 100)")),
3168
+ follow: Flag15.Boolean("follow").pipe(Flag15.withDefault(false), Flag15.withDescription("Resume long polls until interrupted"))
3169
+ }, (options) => Effect21.gen(function* () {
3170
+ const { client } = yield* signedIn;
3171
+ const since = DateTime3.subtract(yield* DateTime3.now, { seconds: options.since });
3172
+ let cursor;
3173
+ let more = false;
3174
+ let failures = 0;
3175
+ const retryRead = (error) => Effect21.gen(function* () {
3176
+ if (!options.follow || failures >= MAX_READ_RETRIES)
3177
+ return yield* error;
3178
+ failures += 1;
3179
+ yield* Console14.error("Log read unavailable; reconnecting from the last cursor.");
3180
+ yield* Effect21.sleep(readRetryDelay(failures));
3181
+ return;
3182
+ });
3183
+ while (true) {
3184
+ const query = {
3185
+ since,
3186
+ limit: options.limit,
3187
+ cursor,
3188
+ wait: options.follow && cursor !== undefined && !more ? MAX_LOG_WAIT_SECONDS : 0
3189
+ };
3190
+ const request = Option11.isSome(options.deployment) ? client.deployments.getLogs({
3191
+ params: { projectId: options.project, deploymentId: options.deployment.value },
3192
+ query
3193
+ }) : client.deployments.getEnvironmentLogs({
3194
+ params: { projectId: options.project, environment: options.environment },
3195
+ query
3196
+ });
3197
+ const page = yield* request.pipe(Effect21.catchTags({ HttpClientError: retryRead, Unavailable: retryRead }));
3198
+ if (page === undefined)
3199
+ continue;
3200
+ failures = 0;
3201
+ for (const line of page.lines)
3202
+ yield* Console14.log(`${DateTime3.formatIso(line.at)} ${line.runnerId} ${line.stream} ${line.text.replace(colourSequence, "").replace(/\p{Cc}|[\u061c\u200e\u200f\u2028\u2029\u202a-\u202e\u2066-\u2069]/gu, "�")}${line.clipped ? " …" : ""}`);
3203
+ cursor = page.cursor;
3204
+ more = page.more;
3205
+ if (!options.follow && !more)
3206
+ return;
3207
+ if (page.lines.length === 0 && !more)
3208
+ yield* Effect21.sleep("1 second");
3209
+ }
3210
+ }).pipe(reportFailures)).pipe(Command18.withDescription("Read recent customer runner logs, or follow with resumable long polls"));
3211
+
3212
+ // src/cli.ts
3213
+ var group = (name, description, subcommands) => Command19.make(name).pipe(Command19.withDescription(description), Command19.withSubcommands(subcommands));
3214
+ var akter = Command19.make("akter").pipe(Command19.withDescription("Run actors locally, check a deploy against stored data, adopt existing tables, inspect and repair a running deployment, and deploy to Akter Cloud"), Command19.withSubcommands([
3215
+ {
3216
+ group: "Develop and check",
3217
+ commands: [
3218
+ devCommand,
3219
+ group("workflows", "Check workflow changes against open executions", [
3220
+ checkCommand
3221
+ ]),
3222
+ group("payloads", "Check and clear stored event and job payload versions", [
3223
+ checkCommand2,
3224
+ clearCommand
3225
+ ]),
3226
+ group("adopt", "Adopt existing tables: plan, observe legacy writers, backfill, enforce, and check status", [
3227
+ planCommand,
3228
+ observeCommand,
3229
+ backfillCommand,
3230
+ enforceCommand,
3231
+ statusCommand,
3232
+ releaseCommand
3233
+ ]),
3234
+ group("fleet", "Set up fleet views' change feed and rebuild a view from its source", [
3235
+ setupCommand,
3236
+ rebuildCommand
3237
+ ])
3238
+ ]
3239
+ },
3240
+ {
3241
+ group: "Operate a running deployment",
3242
+ commands: [
3243
+ group("defects", "Read recent defects from runners", [listCommand]),
3244
+ inspectCommand,
3245
+ exportCommand,
3246
+ group("receipts", "Read stored command outcomes", [showCommand]),
3247
+ group("dead-letters", "Repair dead-lettered jobs; the runner audits each repair", [
3248
+ retryCommand,
3249
+ discardCommand
3250
+ ]),
3251
+ group("subscriptions", "List and skip stuck subscription rows", [
3252
+ listCommand2,
3253
+ skipCommand
3254
+ ])
3255
+ ]
3256
+ },
3257
+ {
3258
+ group: "Akter Cloud",
3259
+ commands: [
3260
+ loginCommand,
3261
+ logoutCommand,
3262
+ whoamiCommand,
3263
+ deployCommand,
3264
+ envCommand,
3265
+ logsCommand
3266
+ ]
3267
+ }
3268
+ ]));
3269
+ var isShowHelp = Schema29.is(CliError.ShowHelp);
3270
+ var run2 = (args) => Command19.runWith(akter, { version })(args).pipe(Effect22.catchIf((error) => CliError.isCliError(error) && Runtime2.getErrorExitCode(error) !== 0, (error) => Effect22.fail(CommandFailed.make({
3271
+ exitCode: 2,
3272
+ reason: isShowHelp(error) ? error.errors[0]?._tag ?? "ShowHelp" : error._tag
3273
+ }))));
3274
+
3275
+ // src/main.ts
3276
+ Effect23.gen(function* () {
3277
+ const services = yield* Layer5.build(Layer5.mergeAll(PlatformServices.layer, FetchHttpClient.layer));
3278
+ const args = yield* Context2.get(services, Stdio2.Stdio).args;
3279
+ return yield* run2(args).pipe(Effect23.provideContext(services));
3280
+ }).pipe(Effect23.scoped, PlatformRuntime.runMain);