@akterjs/cli 0.0.0-stage → 0.1.1

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.
Files changed (109) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +47 -2
  3. package/dist/Argument-D81dqizB.mjs +57 -0
  4. package/dist/Array-B4kuTQeW.mjs +2323 -0
  5. package/dist/Boolean-kILIvmhE.mjs +26 -0
  6. package/dist/ChildProcess-BCIwxPGc.mjs +577 -0
  7. package/dist/CliError-Cf5xqkkj.mjs +633 -0
  8. package/dist/Command-Dzq9KM-h.mjs +5135 -0
  9. package/dist/Config-B6OND9WR.mjs +854 -0
  10. package/dist/Console-BKHUkH6a.mjs +141 -0
  11. package/dist/Data-CeCULr0-.mjs +251 -0
  12. package/dist/Digest-CBd3uv5B.mjs +348 -0
  13. package/dist/Duration-EH9Vy4U6.mjs +791 -0
  14. package/dist/Effect-CKFpqbmw.mjs +12911 -0
  15. package/dist/FileSystem-4pPcEb_i.mjs +1366 -0
  16. package/dist/HttpClientError-BUeMWx70.mjs +303 -0
  17. package/dist/HttpClientResponse-D5ZKICVV.mjs +855 -0
  18. package/dist/Match-CGPLZYrV.mjs +434 -0
  19. package/dist/MutableHashMap-f8nW8AJR.mjs +376 -0
  20. package/dist/MutableHashSet-_HI572Ya.mjs +155 -0
  21. package/dist/MutableRef-D4Qv592C.mjs +107 -0
  22. package/dist/Number-BvkRkfc-.mjs +137 -0
  23. package/dist/Option-DkxSnBhp.mjs +3967 -0
  24. package/dist/Order-C1JC991I.mjs +481 -0
  25. package/dist/Path-Djdv_eF5.mjs +130 -0
  26. package/dist/Queue-CO5UQ6os.mjs +1394 -0
  27. package/dist/Redacted-RkJrG5Me.mjs +160 -0
  28. package/dist/Ref-BIPOxJNH.mjs +267 -0
  29. package/dist/Runtime-BInIAwK3.mjs +297 -0
  30. package/dist/Schema-MiKt5K1v.mjs +10999 -0
  31. package/dist/SecureRandom-B_U4ysO3.mjs +279 -0
  32. package/dist/Semaphore-DOVJ4ZGS.mjs +177 -0
  33. package/dist/Stdio-KylmOBX_.mjs +74 -0
  34. package/dist/Stream-bK9QIETN.mjs +4012 -0
  35. package/dist/String-q5dcsVe4.mjs +351 -0
  36. package/dist/TerminalCapabilities-w_44MAca.mjs +1769 -0
  37. package/dist/WebCrypto-ZI_GNVlw.mjs +33 -0
  38. package/dist/actor-call.service-CGM_vD9u.mjs +1269 -0
  39. package/dist/actor-client.adapter-Coem7gF1.mjs +3691 -0
  40. package/dist/akter-program.config-D8yb9xQP.mjs +7034 -0
  41. package/dist/akter.cli.mjs +5 -0
  42. package/dist/app-cli.config.mjs +49 -0
  43. package/dist/app-entry.pure.mjs +31 -0
  44. package/dist/archive/akter-archive.schema.mjs +65 -0
  45. package/dist/archive/akter-archive.service.mjs +309 -0
  46. package/dist/archive/archive-cli.config.mjs +45 -0
  47. package/dist/archive/archive-invalid.errors.mjs +5 -0
  48. package/dist/archive/archived-lineage.pure.mjs +13 -0
  49. package/dist/archive/tar.pure.mjs +291 -0
  50. package/dist/archive/zstd.adapter.mjs +46 -0
  51. package/dist/bench/bench-report.pure.mjs +120 -0
  52. package/dist/bench/bench-runner.pure.mjs +89 -0
  53. package/dist/bench/bench.service.mjs +301 -0
  54. package/dist/cli-runtime.layer-BqK2mmVL.mjs +1191 -0
  55. package/dist/cli-runtime.layer.mjs +1 -0
  56. package/dist/cloud-client.adapter-Cy1BjDbG.mjs +3186 -0
  57. package/dist/cloud-client.adapter-DOVMoYAj.mjs +304 -0
  58. package/dist/command/akter-commands.config.mjs +32 -0
  59. package/dist/command/akter-program.config.mjs +1 -0
  60. package/dist/credentials/credentials.service.mjs +226 -0
  61. package/dist/credentials/keychain.adapter.mjs +92 -0
  62. package/dist/credentials/login.config.mjs +251 -0
  63. package/dist/deploy/auth/api-attempt.service.mjs +30 -0
  64. package/dist/deploy/auth/token-refresh.errors.mjs +4 -0
  65. package/dist/deploy/build-bundle.adapter.mjs +133 -0
  66. package/dist/deploy/build-manifest.pure.mjs +44 -0
  67. package/dist/deploy/cloud-client.adapter.mjs +1 -0
  68. package/dist/deploy/cloud-url.pure.mjs +17 -0
  69. package/dist/deploy/deploy-failed.errors.mjs +17 -0
  70. package/dist/deploy/deploy.service.mjs +156 -0
  71. package/dist/deploy/request-failure.pure.mjs +87 -0
  72. package/dist/dev-runner.pure.mjs +30 -0
  73. package/dist/docs/doc-topics.pure.mjs +21 -0
  74. package/dist/docs/docs.config.mjs +1 -0
  75. package/dist/docs.config-D2amjsje.mjs +190 -0
  76. package/dist/docs.config-D3zzLzLR.mjs +190 -0
  77. package/dist/export/actor-export.service.mjs +267 -0
  78. package/dist/export/export-failed.errors.mjs +4 -0
  79. package/dist/export/export-failure.pure.mjs +18 -0
  80. package/dist/export/ordered-lines.pure.mjs +43 -0
  81. package/dist/export/ranged-download.pure.mjs +66 -0
  82. package/dist/export/snapshot-download.pure.mjs +86 -0
  83. package/dist/history-runner.pure.mjs +186 -0
  84. package/dist/history-since.pure.mjs +17 -0
  85. package/dist/hosted-runtime.layer-Cx3IPsEd.mjs +16358 -0
  86. package/dist/local/environment-cli.config.mjs +90 -0
  87. package/dist/local/environment-runner.pure.mjs +75 -0
  88. package/dist/local/local-cli.config.mjs +159 -0
  89. package/dist/logs/log-follow.config.mjs +21 -0
  90. package/dist/logs/log-follow.service.mjs +87 -0
  91. package/dist/logs/log-lines.pure.mjs +44 -0
  92. package/dist/migrate-runner.pure.mjs +28 -0
  93. package/dist/operation-input.pure-CS9iUhqK.mjs +1383 -0
  94. package/dist/operation-registry.pure-DVC8EDgj.mjs +283 -0
  95. package/dist/registry/actor-call.service.mjs +1 -0
  96. package/dist/registry/actor-commands.config.mjs +127 -0
  97. package/dist/registry/actor-refusal.pure.mjs +34 -0
  98. package/dist/registry/approval.service.mjs +80 -0
  99. package/dist/registry/cloud-commands.config.mjs +151 -0
  100. package/dist/registry/cloud-served.pure.mjs +31 -0
  101. package/dist/registry/operation-call.service.mjs +262 -0
  102. package/dist/registry/operation-input.pure.mjs +1 -0
  103. package/dist/registry/operation-output.pure.mjs +91 -0
  104. package/dist/registry/served-command.config.mjs +57 -0
  105. package/dist/runner-exited.errors.mjs +10 -0
  106. package/dist/terminal/cli-exit.errors.mjs +38 -0
  107. package/dist/terminal/cli-failure.pure.mjs +31 -0
  108. package/dist/terminal/confirm.adapter.mjs +27 -0
  109. package/package.json +61 -3
@@ -0,0 +1,90 @@
1
+ import { U as fn } from "../Effect-CKFpqbmw.mjs";
2
+ import { x as match } from "../Option-DkxSnBhp.mjs";
3
+ import { t as Path } from "../Path-Djdv_eF5.mjs";
4
+ import { F as String, M as Record, pt as fromJsonString, st as encodeEffect } from "../Schema-MiKt5K1v.mjs";
5
+ import { a as withSubcommands, f as optional, i as withDescription$1, l as String$1, m as withDescription, o as Boolean, p as withDefault, t as make } from "../Command-Dzq9KM-h.mjs";
6
+ import { RunnerExitedError } from "../runner-exited.errors.mjs";
7
+ import { environmentRunner } from "./environment-runner.pure.mjs";
8
+ import { dev, dir, history, migrate, runRunner, writeRunner } from "./local-cli.config.mjs";
9
+ // src/local/environment-cli.config.ts
10
+ const exitedCleanly = 0;
11
+ const jsString = encodeEffect(fromJsonString(String));
12
+ const jsFields = encodeEffect(fromJsonString(Record(String, String)));
13
+ const fieldOf = (key, value) => match(value, {
14
+ onNone: () => ({}),
15
+ onSome: (given) => ({ [key]: given })
16
+ });
17
+ const runOperation = fn("runOperation")(function* runOperation(operation, fields, run) {
18
+ const path = yield* Path;
19
+ const directory = yield* jsString(path.resolve(run.dir));
20
+ const input = yield* jsFields(fields);
21
+ const named = yield* jsString(operation);
22
+ const source = environmentRunner({
23
+ directory,
24
+ input,
25
+ json: run.json,
26
+ operation: named
27
+ });
28
+ const runner = yield* writeRunner("environments.mjs", source);
29
+ const code = yield* runRunner([runner]);
30
+ if (code === exitedCleanly) return code;
31
+ return yield* RunnerExitedError.make({ code });
32
+ });
33
+ const json = Boolean("json").pipe(withDefault(false), withDescription("Print the result as JSON, in the shape Akter Cloud's operation answers"));
34
+ const environment = String$1("environment").pipe(optional, withDescription("The environment the snapshot is of, the one promoted to serve when missing"));
35
+ const snapshotName = String$1("name").pipe(withDescription("The snapshot's name"));
36
+ const environmentName = String$1("name").pipe(withDescription("The environment's name"));
37
+ const snapshotOperation = (operation, description) => make(operation, {
38
+ dir,
39
+ environment,
40
+ json,
41
+ name: snapshotName
42
+ }, (options) => runOperation(`snapshots ${operation}`, {
43
+ name: options.name,
44
+ ...fieldOf("environment", options.environment)
45
+ }, options)).pipe(withDescription$1(description));
46
+ const environmentOperation = (operation, description) => make(operation, {
47
+ dir,
48
+ json,
49
+ name: environmentName
50
+ }, (options) => runOperation(`environments ${operation}`, { name: options.name }, options)).pipe(withDescription$1(description));
51
+ const listSnapshots = make("list", {
52
+ dir,
53
+ environment,
54
+ json
55
+ }, (options) => runOperation("snapshots list", fieldOf("environment", options.environment), options)).pipe(withDescription$1("List an environment's live snapshots"));
56
+ const listEnvironments = make("list", {
57
+ dir,
58
+ json
59
+ }, (options) => runOperation("environments list", {}, options)).pipe(withDescription$1("List the folder's environments, deleted ones included, and which one serves"));
60
+ const branch = make("branch", {
61
+ dir,
62
+ from: environment,
63
+ json,
64
+ name: environmentName,
65
+ snapshot: String$1("snapshot").pipe(withDescription("The Ready snapshot to branch from"))
66
+ }, (options) => runOperation("environments branch", {
67
+ name: options.name,
68
+ snapshot: options.snapshot,
69
+ ...fieldOf("from", options.from)
70
+ }, options)).pipe(withDescription$1("Create a quiet environment from a snapshot: it serves calls with --environment, and its timers, outbox and jobs wait until it is promoted"));
71
+ const snapshots = make("snapshots").pipe(withDescription$1("Snapshot an environment of the local folder: each actor's database and the files"), withSubcommands([
72
+ snapshotOperation("create", "Snapshot an environment; akter dev serving it takes the snapshot, under each actor's command lock"),
73
+ listSnapshots,
74
+ snapshotOperation("get", "Show one live snapshot"),
75
+ snapshotOperation("delete", "Delete a snapshot: its copy is removed, and its id is never used again")
76
+ ]));
77
+ const environments = make("environments").pipe(withDescription$1("Branch, promote and delete environments of the local folder"), withSubcommands([
78
+ listEnvironments,
79
+ environmentOperation("get", "Show one environment"),
80
+ branch,
81
+ environmentOperation("promote", "Serve this environment instead: its quiet lifts, so its jobs resume and its due timers fire; promote the old one to switch back"),
82
+ environmentOperation("delete", "Delete an environment that doesn't serve: its data is removed and its name stays taken")
83
+ ]));
84
+ /** The commands on the local folder: `akter dev` with its environments and snapshots, `history` and `migrate`. */
85
+ const localCommands = [
86
+ dev.pipe(withSubcommands([environments, snapshots])),
87
+ history,
88
+ migrate
89
+ ];
90
+ export { localCommands };
@@ -0,0 +1,75 @@
1
+ import { T as join } from "../Array-B4kuTQeW.mjs";
2
+ // src/local/environment-runner.pure.ts
3
+ const inputLines = [
4
+ "const Of = Schema.Struct({ environment: Schema.optionalKey(EnvironmentName) });",
5
+ "const Snapshotting = Schema.Struct({ environment: Schema.optionalKey(EnvironmentName), name: SnapshotName });",
6
+ "const Naming = Schema.Struct({ name: EnvironmentName });",
7
+ "const Branching = Schema.Struct({ from: Schema.optionalKey(EnvironmentName), name: EnvironmentName, snapshot: SnapshotName });"
8
+ ];
9
+ const lineLines = [
10
+ "const iso = (time) => DateTime.formatIso(time);",
11
+ "const snapshotLine = (snapshot) => [",
12
+ " snapshot.id, snapshot.name, snapshot.status, 'of ' + snapshot.environment,",
13
+ " ...(snapshot.takenAt === undefined ? [] : ['taken ' + iso(snapshot.takenAt)]),",
14
+ " ...(snapshot.sizeBytes === undefined ? [] : [snapshot.sizeBytes + ' bytes']),",
15
+ " ...(snapshot.reason === undefined ? [] : ['(' + snapshot.reason + ')']),",
16
+ " ...(snapshot.status === 'Pending' ? ['(akter dev serving ' + snapshot.environment + ' takes it)'] : []),",
17
+ "].join(' ');",
18
+ "const environmentLine = (environment) => [",
19
+ " environment.name, environment.status,",
20
+ " ...(environment.serving ? ['serving'] : []), ...(environment.quiet ? ['quiet'] : []),",
21
+ " ...(environment.source === undefined ? [] : ['from ' + environment.source]), environment.directory,",
22
+ "].join(' ');",
23
+ String.raw`const each = (line) => (values) => values.map(line).join('\n');`
24
+ ];
25
+ const operationLines = [
26
+ "const operations = {",
27
+ " 'snapshots create': { input: Snapshotting, run: (local, input) => local.createSnapshot(input), output: LocalSnapshot, line: snapshotLine },",
28
+ " 'snapshots list': { input: Of, run: (local, input) => local.snapshots(input.environment), output: Schema.Array(LocalSnapshot), line: each(snapshotLine) },",
29
+ " 'snapshots get': { input: Snapshotting, run: (local, input) => local.snapshot(input), output: LocalSnapshot, line: snapshotLine },",
30
+ " 'snapshots delete': { input: Snapshotting, run: (local, input) => local.deleteSnapshot(input), output: LocalSnapshot, line: snapshotLine },",
31
+ " 'environments list': { input: Of, run: (local) => local.environments, output: Schema.Array(LocalEnvironment), line: each(environmentLine) },",
32
+ " 'environments get': { input: Naming, run: (local, input) => local.environment(input.name), output: LocalEnvironment, line: environmentLine },",
33
+ " 'environments branch': { input: Branching, run: (local, input) => local.branch(input), output: LocalEnvironment, line: environmentLine },",
34
+ " 'environments promote': { input: Naming, run: (local, input) => local.promote(input.name), output: Promotion, line: (promotion) => promotion.notice },",
35
+ " 'environments delete': { input: Naming, run: (local, input) => local.deleteEnvironment(input.name), output: LocalEnvironment, line: environmentLine },",
36
+ "};"
37
+ ];
38
+ const refusalLines = (json) => [
39
+ "const refused = (code, reason, message) => Effect.sync(() => {",
40
+ ` if (${json}) console.log(JSON.stringify({ code, reason, message }));`,
41
+ " else console.error(command + ': ' + message);",
42
+ " process.exitCode = code;",
43
+ "});",
44
+ "const refusals = {",
45
+ " AlreadyExistsError: (error) => refused(1, 'AlreadyExists', 'the ' + error.resource + ' name ' + error.key + ' is taken'),",
46
+ " InUseError: (error) => refused(1, 'InUse', error.key + ': ' + error.reason),",
47
+ " NotFoundError: (error) => refused(1, 'NotFound', 'no live ' + error.resource + ' is named ' + error.key),",
48
+ " SchemaError: (error) => refused(2, 'InvalidInput', error.message),",
49
+ " StoreUnreadableError: (error) => refused(1, 'StoreUnreadable', error.message),",
50
+ "};"
51
+ ];
52
+ /** The runner `akter dev environments` and `akter dev snapshots` write and run: one operation through `Akter.environments`. */
53
+ const environmentRunner = ({ directory, input, json, operation }) => join([
54
+ "import { DateTime, Effect, Schema } from \"effect\";",
55
+ "import { Akter, EnvironmentName, LocalEnvironment, LocalEnvironments, LocalSnapshot, Promotion, SnapshotName } from \"@akterjs/effect\";",
56
+ "",
57
+ `const command = "akter dev " + ${operation};`,
58
+ ...inputLines,
59
+ ...lineLines,
60
+ ...operationLines,
61
+ ...refusalLines(json),
62
+ `const chosen = operations[${operation}];`,
63
+ `const shown = (value) => ${json} ? Effect.map(Schema.encodeEffect(Schema.toCodecJson(chosen.output))(value), JSON.stringify) : Effect.succeed(chosen.line(value));`,
64
+ `const program = Schema.decodeUnknownEffect(chosen.input)(${input}).pipe(`,
65
+ " Effect.flatMap((decoded) => LocalEnvironments.use((local) => chosen.run(local, decoded))),",
66
+ " Effect.flatMap(shown),",
67
+ " Effect.flatMap((text) => Effect.sync(() => { if (text !== '') console.log(text); })),",
68
+ " Effect.catchTags(refusals),",
69
+ ` Effect.provide(Akter.environments(${directory})),`,
70
+ ");",
71
+ "",
72
+ "await Effect.runPromise(program);",
73
+ ""
74
+ ], "\n");
75
+ export { environmentRunner };
@@ -0,0 +1,159 @@
1
+ import { n as Port, o as withDefault } from "../Config-B6OND9WR.mjs";
2
+ import { t as error } from "../Console-BKHUkH6a.mjs";
3
+ import { U as fn, _n as when } from "../Effect-CKFpqbmw.mjs";
4
+ import { t as FileSystem } from "../FileSystem-4pPcEb_i.mjs";
5
+ import { _ as isSome, b as map, c as flatten, f as getOrElse, h as isNone, p as getOrNull, r as exists } from "../Option-DkxSnBhp.mjs";
6
+ import { t as Path } from "../Path-Djdv_eF5.mjs";
7
+ import { F as String, O as NullOr, g as Finite, n as ArraySchema, pt as fromJsonString, st as encodeEffect } from "../Schema-MiKt5K1v.mjs";
8
+ import { r as withDescription, t as String$1 } from "../Argument-D81dqizB.mjs";
9
+ import { c as Int, f as optional, h as withFallbackConfig, i as withDescription$2, l as String$2, m as withDescription$1, o as Boolean, p as withDefault$1, t as make, u as atLeast } from "../Command-Dzq9KM-h.mjs";
10
+ import { a as ChildProcessSpawner, n as make$1 } from "../ChildProcess-BCIwxPGc.mjs";
11
+ import { importInto } from "../archive/archive-cli.config.mjs";
12
+ import { entry } from "../app-cli.config.mjs";
13
+ import { devRunner } from "../dev-runner.pure.mjs";
14
+ import { historyRunner } from "../history-runner.pure.mjs";
15
+ import { millisOf } from "../history-since.pure.mjs";
16
+ import { migrateRunner } from "../migrate-runner.pure.mjs";
17
+ import { RunnerExitedError } from "../runner-exited.errors.mjs";
18
+ import { Confirmation } from "../terminal/confirm.adapter.mjs";
19
+ // src/local/local-cli.config.ts
20
+ const defaultPort = 4e3;
21
+ const anyPort = 0;
22
+ const nextPort = 1;
23
+ const noneRequired = 0;
24
+ const portFromEnvironment = Port("PORT").pipe(withDefault(defaultPort));
25
+ const exitedCleanly = 0;
26
+ const usageError = 2;
27
+ const jsString = encodeEffect(fromJsonString(String));
28
+ const jsSince = encodeEffect(fromJsonString(NullOr(Finite)));
29
+ const jsNames = encodeEffect(fromJsonString(ArraySchema(String)));
30
+ const jsEnvironment = encodeEffect(fromJsonString(NullOr(String)));
31
+ const emptyFolder = fn("emptyFolder")(function* emptyFolder(directory) {
32
+ yield* Confirmation.use((confirmation) => confirmation.confirm({
33
+ question: `Delete everything in ${directory}?`,
34
+ unattended: `akter dev --reset deletes everything in ${directory} and needs a person to confirm it, and no terminal is attached to ask. Delete the folder yourself, then run akter dev.`
35
+ }));
36
+ yield* (yield* FileSystem).remove(directory, {
37
+ force: true,
38
+ recursive: true
39
+ });
40
+ });
41
+ const appSource = fn("appSource")(function* appSource(options) {
42
+ const path = yield* Path;
43
+ const entry = yield* path.toFileUrl(path.resolve(options.entry));
44
+ return {
45
+ directory: yield* jsString(path.resolve(options.dir)),
46
+ entry: yield* jsString(entry.href)
47
+ };
48
+ });
49
+ const writeRunner = fn("writeRunner")(function* writeRunner(name, source) {
50
+ const fileSystem = yield* FileSystem;
51
+ const path = yield* Path;
52
+ const runner = path.resolve("node_modules", ".akter", name);
53
+ const existing = yield* when(fileSystem.readFileString(runner), fileSystem.exists(runner));
54
+ if (exists(existing, (written) => written === source)) return runner;
55
+ yield* fileSystem.makeDirectory(path.dirname(runner), { recursive: true });
56
+ yield* fileSystem.writeFileString(runner, source);
57
+ return runner;
58
+ });
59
+ const runRunner = fn("runRunner")(function* runRunner(args) {
60
+ return yield* (yield* ChildProcessSpawner).exitCode(make$1(globalThis.process.execPath, args, {
61
+ stderr: "inherit",
62
+ stdin: "inherit",
63
+ stdout: "inherit"
64
+ }));
65
+ });
66
+ const inspectorPortOf = ({ inspectorPort, port }) => getOrElse(inspectorPort, () => {
67
+ if (port === anyPort) return anyPort;
68
+ return port + nextPort;
69
+ });
70
+ const portsOf = (options) => ({
71
+ inspectorPort: inspectorPortOf(options),
72
+ port: options.port
73
+ });
74
+ const serveDev = fn("serveDev")(function* serveDev(options) {
75
+ if (options.reset) yield* emptyFolder(options.dir);
76
+ if (isSome(options.from)) yield* importInto({
77
+ archive: options.from.value,
78
+ dir: options.dir
79
+ });
80
+ const app = yield* appSource(options);
81
+ const allowedHosts = yield* jsNames(options.allowedHosts);
82
+ const environment = yield* jsEnvironment(getOrNull(options.environment));
83
+ const source = devRunner({
84
+ ...app,
85
+ allowedHosts,
86
+ environment,
87
+ ...portsOf(options)
88
+ });
89
+ const runner = yield* writeRunner("dev.mjs", source);
90
+ return yield* runRunner(["--watch", runner]);
91
+ });
92
+ const migrateStore = fn("migrateStore")(function* migrateStore(options) {
93
+ if (!options.all) {
94
+ yield* error("akter migrate needs --all: migrating a single actor is not supported yet.");
95
+ return yield* RunnerExitedError.make({ code: usageError });
96
+ }
97
+ const app = yield* appSource(options);
98
+ const runner = yield* writeRunner("migrate.mjs", migrateRunner({
99
+ ...app,
100
+ dryRun: options.dryRun
101
+ }));
102
+ const code = yield* runRunner([runner]);
103
+ if (code === exitedCleanly) return code;
104
+ return yield* RunnerExitedError.make({ code });
105
+ });
106
+ const historyWindow = fn("historyWindow")(function* historyWindow({ since, verify }) {
107
+ if (verify && isSome(since)) {
108
+ yield* error("akter history: --verify replays the whole kept history, so it takes no --since.");
109
+ return yield* RunnerExitedError.make({ code: usageError });
110
+ }
111
+ const millis = map(since, millisOf);
112
+ if (exists(millis, isNone)) {
113
+ yield* error("akter history: --since takes a whole number and a unit, like 90s, 30m, 1h or 7d.");
114
+ return yield* RunnerExitedError.make({ code: usageError });
115
+ }
116
+ return yield* jsSince(getOrNull(flatten(millis)));
117
+ });
118
+ const readHistory = fn("readHistory")(function* readHistory(options) {
119
+ const window = yield* historyWindow(options);
120
+ const app = yield* appSource(options);
121
+ const address = yield* jsString(options.actor);
122
+ const source = historyRunner({
123
+ ...app,
124
+ address,
125
+ json: options.json,
126
+ since: window,
127
+ verify: options.verify
128
+ });
129
+ const runner = yield* writeRunner("history.mjs", source);
130
+ const code = yield* runRunner([runner]);
131
+ if (code === exitedCleanly) return code;
132
+ return yield* RunnerExitedError.make({ code });
133
+ });
134
+ const dir = String$2("dir").pipe(withDefault$1(".akter"), withDescription$1("The folder that holds the actors"));
135
+ const dev = make("dev", {
136
+ allowedHosts: String$2("allowed-host").pipe(atLeast(noneRequired), withDescription$1("A name besides localhost, 127.0.0.1 and [::1] that /rpc and the inspector answer in the Host header, such as a dev proxy's hostname; repeat it for more")),
137
+ dir,
138
+ entry,
139
+ environment: String$2("environment").pipe(optional, withDescription$1("Serve this environment of the folder, such as a quiet branch to call before promoting it, instead of the one promoted to serve")),
140
+ from: String$2("from").pipe(optional, withDescription$1("An .akter archive to read into --dir first, which must not exist yet")),
141
+ inspectorPort: Int("inspector-port").pipe(optional, withDescription$1("The port the read-only actor inspector is served on, --port + 1 when missing")),
142
+ port: Int("port").pipe(withFallbackConfig(portFromEnvironment), withDescription$1("The port /rpc is served on, PORT when the flag is missing")),
143
+ reset: Boolean("reset").pipe(withDefault$1(false), withDescription$1("Empty the folder first, after a confirmation"))
144
+ }, serveDev).pipe(withDescription$2("Run the app on the local engine, restarting when its files change"));
145
+ const migrate = make("migrate", {
146
+ all: Boolean("all").pipe(withDefault$1(false), withDescription$1("Apply pending migrations to every actor in the folder now")),
147
+ dir,
148
+ dryRun: Boolean("dry-run").pipe(withDefault$1(false), withDescription$1("List each actor's old and new version without writing")),
149
+ entry
150
+ }, migrateStore).pipe(withDescription$2("Apply pending actor state migrations to the local folder"));
151
+ const history = make("history", {
152
+ actor: String$1("actor").pipe(withDescription("The actor to read, as <Actor>/<key>, like Room/lobby")),
153
+ dir,
154
+ entry,
155
+ json: Boolean("json").pipe(withDefault$1(false), withDescription$1("Print one JSON object per command instead of one line of text")),
156
+ since: String$2("since").pipe(optional, withDescription$1("Only the commands from this long ago until now, like 30m, 1h or 7d")),
157
+ verify: Boolean("verify").pipe(withDefault$1(false), withDescription$1("Rerun the kept history against the current handlers and print the first command that differs"))
158
+ }, readHistory).pipe(withDescription$2("Print the commands a local actor kept, or replay them with --verify"));
159
+ export { dev, dir, history, migrate, runRunner, writeRunner };
@@ -0,0 +1,21 @@
1
+ import { Mt as provideServiceEffect } from "../Effect-CKFpqbmw.mjs";
2
+ import { c as Int, f as optional, i as withDescription$1, l as String, m as withDescription, o as Boolean, p as withDefault, t as make } from "../Command-Dzq9KM-h.mjs";
3
+ import { LogFollow } from "./log-follow.service.mjs";
4
+ // src/logs/log-follow.config.ts
5
+ const described = (flag, description) => flag.pipe(optional, withDescription(description));
6
+ const followNamed = (name) => make(name, {
7
+ app: String("app").pipe(withDescription("The app whose lines to print")),
8
+ count: Int("count").pipe(optional, withDescription("Stop after this many lines")),
9
+ deploy: described(String("deploy"), "Only the lines about this deploy, such as dep_123"),
10
+ json: Boolean("json").pipe(withDefault(false), withDescription("Print each line as one line of JSON in the API's LogLine schema")),
11
+ organization: described(String("organization"), "The organization; defaults to AKTER_ORGANIZATION, then the one saved by akter login"),
12
+ since: described(String("since"), "Only the lines since this time, such as 2026-11-11T00:00:00Z"),
13
+ source: described(String("source"), "Only the lines this source wrote: deploy or computer"),
14
+ url: described(String("url"), "The Cloud API's URL; defaults to AKTER_CLOUD_URL, then the one saved by akter login, then https://api.akter.dev")
15
+ }, (options) => LogFollow.use((logs) => logs.follow(options)).pipe(provideServiceEffect(LogFollow, LogFollow.make))).pipe(withDescription$1("Print an app's log lines as they arrive, oldest first, until interrupted or --count lines have printed"));
16
+ const logCommands = { logs: {
17
+ commands: [followNamed("follow")],
18
+ description: "Read an app's log lines",
19
+ serves: []
20
+ } };
21
+ export { logCommands };
@@ -0,0 +1,87 @@
1
+ import { C as isReadonlyArrayEmpty } from "../Array-B4kuTQeW.mjs";
2
+ import { t as match } from "../Boolean-kILIvmhE.mjs";
3
+ import { n as log, t as error } from "../Console-BKHUkH6a.mjs";
4
+ import { At as provideContext, B as flatMap, Et as orDie, Lt as retry, T as context, U as fn, Vn as effect, Xt as succeed, Yt as sleep, _n as when, aa as Service, et as gen, ht as mapError, ln as try_, o as andThen, x as catchTag, y as catchIf } from "../Effect-CKFpqbmw.mjs";
5
+ import { C as orElse, S as none, d as fromUndefinedOr, w as some, x as match$1 } from "../Option-DkxSnBhp.mjs";
6
+ import { I as Struct, cr as getSomes, pt as fromJsonString, st as encodeEffect, tt as decodeEffect } from "../Schema-MiKt5K1v.mjs";
7
+ import { P as paginate, W as runForEach, Y as take } from "../Stream-bK9QIETN.mjs";
8
+ import { c as LogLine, l as LogsReadParams, t as AppName, u as OrganizationName } from "../cloud-client.adapter-Cy1BjDbG.mjs";
9
+ import { Credentials } from "../credentials/credentials.service.mjs";
10
+ import { t as CloudClient } from "../cloud-client.adapter-DOVMoYAj.mjs";
11
+ import { progressWait, reconnectBackoff, retryable } from "../deploy/request-failure.pure.mjs";
12
+ import { lineText, retryNotice, stopped } from "./log-lines.pure.mjs";
13
+ import { reported } from "../registry/operation-output.pure.mjs";
14
+ import { CliExitError } from "../terminal/cli-exit.errors.mjs";
15
+ // src/logs/log-follow.service.ts
16
+ const Path = Struct({
17
+ app: AppName,
18
+ organization: OrganizationName
19
+ });
20
+ const LineJson = fromJsonString(LogLine);
21
+ const idleWait = "2 seconds";
22
+ const usage = (message) => CliExitError.make({
23
+ code: CliExitError.codes.usage,
24
+ message,
25
+ reason: "InvalidInput"
26
+ });
27
+ const paramsOf = (options) => mapError(decodeEffect(LogsReadParams)(getSomes({
28
+ deploy: options.deploy,
29
+ since: options.since,
30
+ source: options.source
31
+ })), (failure) => usage(`Invalid --deploy, --since or --source: ${failure.message}`));
32
+ const pageAfter = ({ cloud, params, path }) => (cursor) => {
33
+ const read = cloud.request(cloud.client.logsRead(path.organization, path.app, { params: match$1(cursor, {
34
+ onNone: () => params,
35
+ onSome: (after) => ({
36
+ ...params,
37
+ cursor: after
38
+ })
39
+ }) }), progressWait);
40
+ return read.pipe(catchIf(retryable, (failure) => error(retryNotice(failure)).pipe(andThen(retry(read, {
41
+ schedule: reconnectBackoff,
42
+ while: retryable
43
+ })))), mapError(stopped));
44
+ };
45
+ const linesOf = (reading) => paginate(none(), (cursor) => gen(function* nextPage() {
46
+ const page = yield* pageAfter(reading)(cursor);
47
+ const next = orElse(fromUndefinedOr(page.nextCursor), () => cursor);
48
+ yield* when(sleep(idleWait), succeed(isReadonlyArrayEmpty(page.items)));
49
+ return [page.items, some(next)];
50
+ }));
51
+ const printed = (json) => (line) => match(json, {
52
+ onFalse: () => log(lineText(line)),
53
+ onTrue: () => flatMap(orDie(encodeEffect(LineJson)(line)), (text) => log(text))
54
+ });
55
+ const followed = fn("followed")(function* followed(options) {
56
+ const target = yield* Credentials.use((credentials) => credentials.target(options));
57
+ const path = yield* mapError(decodeEffect(Path)({
58
+ ...target,
59
+ app: options.app
60
+ }), () => usage("--app and --organization must be valid Cloud names."));
61
+ const params = yield* paramsOf(options);
62
+ const url = yield* try_({
63
+ catch: () => usage(`${target.url} is not a URL.`),
64
+ try: () => new globalThis.URL(target.url)
65
+ });
66
+ const cloud = yield* CloudClient.use((factory) => factory.connect({
67
+ apiKey: target.apiKey,
68
+ url
69
+ }));
70
+ const lines = linesOf({
71
+ cloud,
72
+ params,
73
+ path
74
+ });
75
+ const limited = match$1(options.count, {
76
+ onNone: () => lines,
77
+ onSome: (count) => take(lines, count)
78
+ });
79
+ yield* runForEach(limited, printed(options.json));
80
+ });
81
+ var LogFollow = class extends Service()("@akterjs/cli/logs/log-follow.service/LogFollow", { make: gen(function* makeLogFollow() {
82
+ const context$1 = yield* context();
83
+ return { follow: (options) => followed(options).pipe(catchTag("CliExitError", reported(options.json)), provideContext(context$1)) };
84
+ }) }) {
85
+ static layer = effect(this, this.make);
86
+ };
87
+ export { LogFollow };
@@ -0,0 +1,44 @@
1
+ import { ti as isTimeoutError } from "../Effect-CKFpqbmw.mjs";
2
+ import { h as isNone, x as match } from "../Option-DkxSnBhp.mjs";
3
+ import { Xt as isSchemaError, mt as is } from "../Schema-MiKt5K1v.mjs";
4
+ import { l as isHttpClientError } from "../HttpClientError-BUeMWx70.mjs";
5
+ import { TokenRefreshError } from "../deploy/auth/token-refresh.errors.mjs";
6
+ import { DeployFailedError } from "../deploy/deploy-failed.errors.mjs";
7
+ import { progressStatus, progressWait, statusOf } from "../deploy/request-failure.pure.mjs";
8
+ import { unanswered } from "../registry/operation-output.pure.mjs";
9
+ import { CliExitError } from "../terminal/cli-exit.errors.mjs";
10
+ // src/logs/log-lines.pure.ts
11
+ const command = "akter logs follow";
12
+ const lineText = ({ at, level, message, source, subject }) => `${at} ${level} [${source} ${subject}] ${message}`;
13
+ const refused = (failure) => CliExitError.make({
14
+ code: match(progressStatus(failure), {
15
+ onNone: () => CliExitError.codes.failed,
16
+ onSome: CliExitError.ofStatus
17
+ }),
18
+ message: `${command} was refused (${statusOf(failure)}).`,
19
+ reason: "Refused"
20
+ });
21
+ const stopped = (failure) => {
22
+ if (isSchemaError(failure)) return CliExitError.make({
23
+ code: CliExitError.codes.failed,
24
+ message: `${command} read a page that breaks the API's schema, so this CLI and the API disagree.`,
25
+ reason: "ResponseMismatch"
26
+ });
27
+ if (is(DeployFailedError)(failure) || is(TokenRefreshError)(failure) || isTimeoutError(failure)) return unanswered({
28
+ command,
29
+ failure,
30
+ wait: progressWait
31
+ });
32
+ if (isHttpClientError(failure) && isNone(progressStatus(failure))) return unanswered({
33
+ command,
34
+ failure,
35
+ wait: progressWait
36
+ });
37
+ return refused(failure);
38
+ };
39
+ const retryNotice = (failure) => {
40
+ if (is(TokenRefreshError)(failure)) return `${failure.message} Retrying.`;
41
+ if (isTimeoutError(failure)) return `${command} got no answer within ${progressWait}. Retrying.`;
42
+ return `${command} could not read the next lines (${statusOf(failure)}). Retrying.`;
43
+ };
44
+ export { lineText, retryNotice, stopped };
@@ -0,0 +1,28 @@
1
+ import { T as join } from "./Array-B4kuTQeW.mjs";
2
+ import { appEntry } from "./app-entry.pure.mjs";
3
+ // src/migrate-runner.pure.ts
4
+ const migrateRunner = ({ directory, dryRun, entry }) => join([
5
+ "import { Console, Effect, Layer, Stream } from \"effect\";",
6
+ "import { Akter } from \"@akterjs/effect\";",
7
+ ...appEntry({
8
+ command: "migrate",
9
+ entry
10
+ }),
11
+ "const named = (actor) => actor.actor + \" \" + JSON.stringify(actor.key);",
12
+ "const report = (actor) => Console.log(named(actor) + \": version \" + actor.from + \" -> \" + actor.to);",
13
+ "const failed = (line) => Effect.andThen(Console.error(line), Effect.sync(() => { process.exitCode = 1; }));",
14
+ "const stopped = (error) =>",
15
+ " failed(\"akter migrate: stopped at \" + named(error) + \": \" + error.message + \". The actors after it were not touched.\");",
16
+ "const unreadable = (error) => failed(\"akter migrate: could not read \" + error.directory + \": \" + error.message);",
17
+ `const migrations = Akter.migrate({ directory: ${directory}, dryRun: ${dryRun} });`,
18
+ "",
19
+ `if (${dryRun}) console.log("akter migrate --dry-run: nothing is written");`,
20
+ "await Effect.runPromise(",
21
+ " Stream.runForEach(migrations, report).pipe(",
22
+ " Effect.catchTags({ ActorNotMigratedError: stopped, StoreUnreadableError: unreadable }),",
23
+ " Effect.provide(app.layer.pipe(Layer.provide(Akter.jobs))),",
24
+ " ),",
25
+ ");",
26
+ ""
27
+ ], "\n");
28
+ export { migrateRunner };