@akterjs/cli 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) 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-C4cXx4Fr.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-_21Xi6HU.mjs +190 -0
  76. package/dist/export/actor-export.service.mjs +267 -0
  77. package/dist/export/export-failed.errors.mjs +4 -0
  78. package/dist/export/export-failure.pure.mjs +18 -0
  79. package/dist/export/ordered-lines.pure.mjs +43 -0
  80. package/dist/export/ranged-download.pure.mjs +66 -0
  81. package/dist/export/snapshot-download.pure.mjs +86 -0
  82. package/dist/history-runner.pure.mjs +186 -0
  83. package/dist/history-since.pure.mjs +17 -0
  84. package/dist/hosted-runtime.layer-Cx3IPsEd.mjs +16358 -0
  85. package/dist/local/environment-cli.config.mjs +90 -0
  86. package/dist/local/environment-runner.pure.mjs +75 -0
  87. package/dist/local/local-cli.config.mjs +159 -0
  88. package/dist/logs/log-follow.config.mjs +21 -0
  89. package/dist/logs/log-follow.service.mjs +87 -0
  90. package/dist/logs/log-lines.pure.mjs +44 -0
  91. package/dist/migrate-runner.pure.mjs +28 -0
  92. package/dist/operation-input.pure-CS9iUhqK.mjs +1383 -0
  93. package/dist/operation-registry.pure-DVC8EDgj.mjs +283 -0
  94. package/dist/registry/actor-call.service.mjs +1 -0
  95. package/dist/registry/actor-commands.config.mjs +127 -0
  96. package/dist/registry/actor-refusal.pure.mjs +34 -0
  97. package/dist/registry/approval.service.mjs +80 -0
  98. package/dist/registry/cloud-commands.config.mjs +151 -0
  99. package/dist/registry/cloud-served.pure.mjs +31 -0
  100. package/dist/registry/operation-call.service.mjs +262 -0
  101. package/dist/registry/operation-input.pure.mjs +1 -0
  102. package/dist/registry/operation-output.pure.mjs +91 -0
  103. package/dist/registry/served-command.config.mjs +57 -0
  104. package/dist/runner-exited.errors.mjs +10 -0
  105. package/dist/terminal/cli-exit.errors.mjs +38 -0
  106. package/dist/terminal/cli-failure.pure.mjs +31 -0
  107. package/dist/terminal/confirm.adapter.mjs +27 -0
  108. package/package.json +61 -3
@@ -0,0 +1,283 @@
1
+ import { C as isReadonlyArrayEmpty, M as match, N as matchLeft, P as min, R as some, i as dedupe, j as map, l as filter, m as flatten, p as flatMap, r as contains, w as isReadonlyArrayNonEmpty } from "./Array-B4kuTQeW.mjs";
2
+ import { t as match$1 } from "./Boolean-kILIvmhE.mjs";
3
+ import { K as forEach, W as fnUntraced, dt as map$1 } from "./Effect-CKFpqbmw.mjs";
4
+ import { S as none, T as toArray, Tn as pipe, _ as isSome, b as map$2, cn as isString, d as fromUndefinedOr, f as getOrElse, i as filter$1, o as flatMap$1, r as exists, v as liftPredicate, x as match$2 } from "./Option-DkxSnBhp.mjs";
5
+ import { i as Number$1 } from "./Order-C1JC991I.mjs";
6
+ import { F as String$1, I as Struct, M as Record, Q as brand, _r as values, a as Boolean, an as optionalKey, at as decodeUnknownEffect, b as JsonObject, dr as keys, fr as map$3, hr as toEntries, mr as set, mt as is, n as ArraySchema, or as fromEntries, ot as decodeUnknownOption, qt as isPattern, sr as get, ur as isEmptyRecord, y as Json } from "./Schema-MiKt5K1v.mjs";
7
+ import { _ as toUpperCase, f as replaceAll, h as startsWith, m as split, n as camelToSnake, p as slice, s as isNonEmpty } from "./String-q5dcsVe4.mjs";
8
+ // ../../node_modules/.pnpm/@akterjs+cloud-api@file+..+akter-cloud+packages+cloud-api_effect@4.1.2_patch_hash=0e611_59e8fdba67d150970876667b4296bdf6/node_modules/@akterjs/cloud-api/src/flags/flag-name.schema.ts
9
+ /**
10
+ * A feature flag's name (ADR 0030): 2 to 63 characters, a lowercase letter, then letters or digits, in camelCase, such
11
+ * as `cloudFork`. The spec's `x-akter` hint, `FeatureNotEnabledError` and control's overrides all use it, so a flag is
12
+ * never renamed: it is retired and a new one added.
13
+ */
14
+ const FlagName = String$1.check(isPattern(/^[a-z][A-Za-z0-9]{1,62}$/u)).pipe(brand("FlagName")).annotate({
15
+ description: "A feature flag's name: a lowercase letter, then 1 to 62 letters or digits, such as cloudFork.",
16
+ identifier: "FlagName"
17
+ });
18
+ // ../../node_modules/.pnpm/@akterjs+cloud-api@file+..+akter-cloud+packages+cloud-api_effect@4.1.2_patch_hash=0e611_59e8fdba67d150970876667b4296bdf6/node_modules/@akterjs/cloud-api/src/operations/operation-registry.pure.ts
19
+ const Media = Record(String$1, Struct({ schema: optionalKey(JsonObject) }));
20
+ const Hints = Struct({
21
+ confirm: optionalKey(Boolean),
22
+ flag: optionalKey(FlagName),
23
+ idempotent: optionalKey(Boolean),
24
+ openWorld: optionalKey(Boolean),
25
+ servedAs: optionalKey(String$1)
26
+ });
27
+ const Parameter = Struct({
28
+ description: optionalKey(String$1),
29
+ in: String$1,
30
+ name: String$1,
31
+ required: optionalKey(Boolean),
32
+ schema: JsonObject
33
+ });
34
+ const RequestBody = Struct({ content: Media });
35
+ const Response = Struct({ content: optionalKey(Media) });
36
+ const ComponentsSpec = Struct({ schemas: optionalKey(Record(String$1, JsonObject)) });
37
+ const Names = ArraySchema(String$1);
38
+ const Tag = Struct({
39
+ description: optionalKey(String$1),
40
+ name: String$1
41
+ });
42
+ const OperationSpec = Struct({
43
+ description: optionalKey(String$1),
44
+ operationId: String$1,
45
+ parameters: optionalKey(ArraySchema(Parameter)),
46
+ requestBody: optionalKey(RequestBody),
47
+ responses: Record(String$1, Response),
48
+ security: optionalKey(ArraySchema(JsonObject)),
49
+ summary: optionalKey(String$1),
50
+ "x-akter": optionalKey(Hints)
51
+ });
52
+ const Spec = Struct({
53
+ components: optionalKey(ComponentsSpec),
54
+ paths: Record(String$1, JsonObject),
55
+ tags: optionalKey(ArraySchema(Tag))
56
+ });
57
+ const methods = [
58
+ "get",
59
+ "put",
60
+ "post",
61
+ "delete",
62
+ "patch",
63
+ "head"
64
+ ];
65
+ const componentRef = "#/components/schemas/";
66
+ const definitionRef = "#/$defs/";
67
+ const firstSuccess = 200;
68
+ const firstRedirect = 300;
69
+ const isJsonObject = is(JsonObject);
70
+ const isJsonArray = is(ArraySchema(Json));
71
+ const kebab = (name) => replaceAll("_", "-")(camelToSnake(name));
72
+ const refName = (schema) => pipe(get(schema, "$ref"), filter$1(isString), filter$1(startsWith(componentRef)), map$2(slice(21)));
73
+ const localRef = (key, value) => {
74
+ if (key === "$ref" && isString(value) && startsWith(componentRef)(value)) return `${definitionRef}${slice(21)(value)}`;
75
+ return value;
76
+ };
77
+ const rewriteRefs = (value) => {
78
+ if (isJsonObject(value)) return map$3(value, (inner, key) => localRef(key, rewriteRefs(inner)));
79
+ if (isJsonArray(value)) return map(value, rewriteRefs);
80
+ return value;
81
+ };
82
+ const refsIn = (value) => {
83
+ if (isJsonObject(value)) return [...toArray(refName(value)), ...flatMap(values(value), refsIn)];
84
+ if (isJsonArray(value)) return flatMap(value, refsIn);
85
+ return [];
86
+ };
87
+ const definitionsOf = (components, roots) => {
88
+ const found = /* @__PURE__ */ new Map();
89
+ let pending = flatMap(roots, refsIn);
90
+ while (isReadonlyArrayNonEmpty(pending)) {
91
+ const [name, ...rest] = pending;
92
+ const schema = get(components, name).pipe(filter$1(() => !found.has(name)));
93
+ pending = rest;
94
+ if (isSome(schema)) {
95
+ found.set(name, schema.value);
96
+ pending = [...pending, ...refsIn(schema.value)];
97
+ }
98
+ }
99
+ return map$3(fromEntries(found), rewriteRefs);
100
+ };
101
+ const withDefinitions = (schema, definitions) => {
102
+ if (isEmptyRecord(definitions)) return schema;
103
+ return set(schema, "$defs", definitions);
104
+ };
105
+ const documentOf = (components, schema) => {
106
+ const root = rewriteRefs(schema);
107
+ const rooted = getOrElse(liftPredicate(root, isJsonObject), () => ({}));
108
+ return withDefinitions(rooted, definitionsOf(components, [schema]));
109
+ };
110
+ const resolved = (components, schema) => getOrElse(flatMap$1(refName(schema), (name) => get(components, name)), () => schema);
111
+ const stringOf = (schema, key) => pipe(get(schema, key), filter$1(isString), getOrElse(() => ""));
112
+ const scalarKinds = {
113
+ boolean: "boolean",
114
+ integer: "integer",
115
+ number: "number",
116
+ string: "string"
117
+ };
118
+ const scalarKind = (type) => getOrElse(get(scalarKinds, type), () => "json");
119
+ const typeOf = (components, schema) => {
120
+ const anyOf = getOrElse(filter$1(get(schema, "anyOf"), isJsonArray), () => []);
121
+ const types = dedupe(map(filter(anyOf, isJsonObject), (branch) => stringOf(resolved(components, branch), "type")));
122
+ return getOrElse(matchLeft(types, {
123
+ onEmpty: none,
124
+ onNonEmpty: (type, others) => liftPredicate(type, () => isReadonlyArrayEmpty(others))
125
+ }), () => stringOf(schema, "type"));
126
+ };
127
+ const kindOf = (components, schema) => {
128
+ const target = resolved(components, schema);
129
+ const type = typeOf(components, target);
130
+ if (type !== "array") return scalarKind(type);
131
+ const items = getOrElse(filter$1(get(target, "items"), isJsonObject), () => ({}));
132
+ if (typeOf(components, resolved(components, items)) === "string") return "strings";
133
+ return "json";
134
+ };
135
+ const descriptionOf = (components, schema, fallback) => {
136
+ const own = isNonEmpty(stringOf(schema, "description"));
137
+ const target = stringOf(resolved(components, schema), "description");
138
+ if (own) return stringOf(schema, "description");
139
+ return getOrElse(liftPredicate(target, isNonEmpty), () => fallback);
140
+ };
141
+ const parameterField = (components) => (parameter) => {
142
+ const inPath = parameter.in === "path";
143
+ return {
144
+ description: descriptionOf(components, parameter.schema, parameter.description ?? ""),
145
+ in: match$1(inPath, {
146
+ onFalse: () => "query",
147
+ onTrue: () => "path"
148
+ }),
149
+ kind: kindOf(components, parameter.schema),
150
+ name: parameter.name,
151
+ required: parameter.required === true || inPath,
152
+ schema: parameter.schema
153
+ };
154
+ };
155
+ const parameterFields = ({ components, spec }) => pipe(spec.parameters ?? [], filter((parameter) => parameter.in === "path" || parameter.in === "query"), map(parameterField(components)));
156
+ const jsonSchemaOf = (content) => pipe(content, flatMap$1(get("application/json")), flatMap$1((media) => fromUndefinedOr(media.schema)));
157
+ const propertyField = (components, required) => ([name, property]) => {
158
+ const schema = getOrElse(liftPredicate(property, isJsonObject), () => ({}));
159
+ return {
160
+ description: descriptionOf(components, schema, ""),
161
+ in: "body",
162
+ kind: kindOf(components, schema),
163
+ name,
164
+ required: contains(required, name),
165
+ schema
166
+ };
167
+ };
168
+ const objectFields = (components, schema) => {
169
+ const target = resolved(components, schema);
170
+ const properties = filter$1(get(target, "properties"), isJsonObject);
171
+ const required = pipe(get(target, "required"), flatMap$1(decodeUnknownOption(Names)), getOrElse(() => []));
172
+ return match$2(properties, {
173
+ onNone: () => [{
174
+ description: "The request body as JSON.",
175
+ in: "payload",
176
+ kind: "json",
177
+ name: "payload",
178
+ required: true,
179
+ schema
180
+ }],
181
+ onSome: (fields) => map(toEntries(fields), propertyField(components, required))
182
+ });
183
+ };
184
+ const bodyFields = ({ components, spec }) => {
185
+ const content = map$2(fromUndefinedOr(spec.requestBody), (body) => body.content);
186
+ return match$2(jsonSchemaOf(content), {
187
+ onNone: () => [],
188
+ onSome: (schema) => objectFields(components, schema)
189
+ });
190
+ };
191
+ const inputOf = (components, fields) => {
192
+ const properties = fromEntries(map(fields, (field) => [field.name, rewriteRefs(field.schema)]));
193
+ const required = map(filter(fields, (field) => field.required), (field) => field.name);
194
+ const definitions = definitionsOf(components, map(fields, (field) => field.schema));
195
+ return withDefinitions({
196
+ additionalProperties: false,
197
+ properties,
198
+ required,
199
+ type: "object"
200
+ }, definitions);
201
+ };
202
+ const successAt = (located, status) => {
203
+ const content = flatMap$1(get(located.spec.responses, String(status)), (response) => fromUndefinedOr(response.content));
204
+ const json = jsonSchemaOf(content);
205
+ if (isSome(json)) return {
206
+ kind: "json",
207
+ schema: documentOf(located.components, json.value),
208
+ status
209
+ };
210
+ if (exists(content, (media) => !isEmptyRecord(media))) return {
211
+ kind: "binary",
212
+ schema: {
213
+ format: "binary",
214
+ type: "string"
215
+ },
216
+ status
217
+ };
218
+ return {
219
+ kind: "empty",
220
+ schema: {},
221
+ status
222
+ };
223
+ };
224
+ const successOf = (located) => {
225
+ const statuses = pipe(keys(located.spec.responses), map(Number), filter((status) => status >= firstSuccess && status < firstRedirect));
226
+ const lowest = match(statuses, {
227
+ onEmpty: () => firstSuccess,
228
+ onNonEmpty: (found) => min(found, Number$1)
229
+ });
230
+ return successAt(located, lowest);
231
+ };
232
+ const annotationsOf = ({ method, spec }) => {
233
+ const hints = spec["x-akter"] ?? {};
234
+ const readOnly = method === "GET" || method === "HEAD";
235
+ const destructive = method === "DELETE";
236
+ return {
237
+ confirm: hints.confirm ?? destructive,
238
+ destructive,
239
+ idempotent: hints.idempotent ?? (readOnly || destructive || method === "PUT"),
240
+ openWorld: hints.openWorld ?? false,
241
+ readOnly
242
+ };
243
+ };
244
+ const operationOf = (groups) => (located) => {
245
+ const { components, method, path, spec } = located;
246
+ const [group, name = group] = split(spec.operationId, ".");
247
+ const [served, servedName = served] = split(spec["x-akter"]?.servedAs ?? spec.operationId, ".");
248
+ const fields = [...parameterFields(located), ...bodyFields(located)];
249
+ return {
250
+ annotations: annotationsOf(located),
251
+ command: [kebab(served), kebab(servedName)],
252
+ description: spec.description ?? spec.summary ?? "",
253
+ fields,
254
+ flag: fromUndefinedOr(spec["x-akter"]?.flag),
255
+ group,
256
+ groupDescription: getOrElse(get(groups, served), () => ""),
257
+ id: spec.operationId,
258
+ input: inputOf(components, fields),
259
+ method,
260
+ name,
261
+ path,
262
+ secured: some(spec.security ?? [], (requirement) => !isEmptyRecord(requirement)),
263
+ success: successOf(located),
264
+ tool: `${camelToSnake(served)}_${camelToSnake(servedName)}`
265
+ };
266
+ };
267
+ const locate = (components, path) => ([method, operation]) => map$1(decodeUnknownEffect(OperationSpec)(operation), (spec) => ({
268
+ components,
269
+ method: toUpperCase(method),
270
+ path,
271
+ spec
272
+ }));
273
+ const operationsOf = fnUntraced(function* operationsOf(input) {
274
+ const spec = yield* decodeUnknownEffect(Spec)(input);
275
+ const components = spec.components?.schemas ?? {};
276
+ const groups = fromEntries(map(spec.tags ?? [], (tag) => [tag.name, tag.description ?? ""]));
277
+ const located = yield* forEach(toEntries(spec.paths), ([path, item]) => {
278
+ const operations = filter(toEntries(item), ([method]) => contains(methods, method));
279
+ return forEach(operations, locate(components, path));
280
+ });
281
+ return map(flatten(located), operationOf(groups));
282
+ });
283
+ export { operationsOf as n, kebab as t };
@@ -0,0 +1 @@
1
+ export { t as ActorCall } from "../actor-call.service-CGM_vD9u.mjs";
@@ -0,0 +1,127 @@
1
+ import { T as join, _ as getSomes, c as every, j as map, p as flatMap, v as groupBy } from "../Array-B4kuTQeW.mjs";
2
+ import { Xt as succeed, _n as when, d as asVoid, o as andThen, x as catchTag } from "../Effect-CKFpqbmw.mjs";
3
+ import { S as none, T as toArray, _ as isSome, c as flatten, f as getOrElse, v as liftPredicate, x as match } from "../Option-DkxSnBhp.mjs";
4
+ import { fr as map$1, or as fromEntries, sr as get } from "../Schema-MiKt5K1v.mjs";
5
+ import { c as Int, f as optional, i as withDescription$1, l as String, m as withDescription, o as Boolean, p as withDefault, r as unlisted, t as make } from "../Command-Dzq9KM-h.mjs";
6
+ import { n as actorOperations, t as ActorCall } from "../actor-call.service-CGM_vD9u.mjs";
7
+ import { t as kebab } from "../operation-registry.pure-DVC8EDgj.mjs";
8
+ import { servedBy } from "./cloud-served.pure.mjs";
9
+ import { servedCommandOf } from "./served-command.config.mjs";
10
+ import { reported } from "./operation-output.pure.mjs";
11
+ import { Confirmation } from "../terminal/confirm.adapter.mjs";
12
+ // src/registry/actor-commands.config.ts
13
+ const help = {
14
+ actor: "The actor's name in the app, such as Sale. Required.",
15
+ after: "Only log lines after this seq, the last one printed, to read on from there",
16
+ commandId: "An id for this call, so a retry with the same id runs it once; a new one is made when it is missing",
17
+ computer: "The computer's name, as the actor named it. Required.",
18
+ computerPath: "Relative path in the computer's files directory. Required.",
19
+ content: "The file to upload: @<path>, - for stdin, or its bytes as base64. Required.",
20
+ cursor: "Where the page starts, from the cursor the page before printed",
21
+ direction: "toComputer or fromComputer; copy-back returns a receipt for an actor command to attach. Required.",
22
+ into: "The new actor's key as JSON; a bare word is taken as a string key. Required.",
23
+ key: "The actor's key as JSON; a bare word is taken as a string key. Required.",
24
+ length: "How many bytes to read from --offset; pass both or neither",
25
+ limit: "The most entries to print",
26
+ member: "The command or query to run. Required.",
27
+ offset: "The byte to start reading at; pass it with --length",
28
+ path: "The file's path in the actor, such as photos/cat.png. Required.",
29
+ payload: "The payload as JSON, as the actor's schema encodes it",
30
+ prefix: "Only paths that start with this",
31
+ step: "The step's id, as akter steps list prints it. Required."
32
+ };
33
+ const json = Boolean("json").pipe(withDefault(false), withDescription("Print the actor's answer, or each update, as one line of JSON, and failures as JSON too"));
34
+ const url = String("url").pipe(optional, withDescription("The app's URL, such as the one akter dev prints; defaults to AKTER_APP_URL"));
35
+ const dryRun = Boolean("dry-run").pipe(withDefault(false), withDescription("Print the RPC request instead of sending it"));
36
+ const count = Int("count").pipe(optional, withDescription("Stop after this many updates; without it, watch until stopped"));
37
+ const fieldFlag = (name) => String(kebab(name)).pipe(optional, withDescription(help[name]));
38
+ const shared = (operation) => ({
39
+ fields: fromEntries(map(operation.fields, (name) => [name, fieldFlag(name)])),
40
+ json,
41
+ url
42
+ });
43
+ const targetFields = [
44
+ "actor",
45
+ "key",
46
+ "computer"
47
+ ];
48
+ const confirmedFirst = (operation, options, extra) => {
49
+ const command = `akter ${join(operation.command, " ")}`;
50
+ const given = flatMap(targetFields, (name) => toArray(flatten(get(options.fields, name))));
51
+ const asking = Confirmation.use((confirmation) => confirmation.confirm({
52
+ question: `${operation.description} Run ${command} for ${join(given, " ")}?`,
53
+ unattended: `${command} needs a person to confirm it, and no terminal is attached to ask. Run it in a terminal.`
54
+ }));
55
+ return when(asking, succeed(operation.annotations.confirm && !extra.dryRun)).pipe(asVoid, catchTag("CliExitError", reported(options.json)));
56
+ };
57
+ const run = (operation, options, extra) => andThen(confirmedFirst(operation, options, extra), ActorCall.use((service) => service.call(operation, {
58
+ ...extra,
59
+ json: options.json,
60
+ url: options.url,
61
+ values: options.fields
62
+ })));
63
+ const describedCommandOf = (operation) => {
64
+ const [, name] = operation.command;
65
+ const asks = liftPredicate(" Asks a person to confirm.", () => operation.annotations.confirm);
66
+ const described = withDescription$1(`${operation.description}${getOrElse(asks, () => "")}`);
67
+ if (operation.kind === "watch") return make(name, {
68
+ ...shared(operation),
69
+ count
70
+ }, (options) => run(operation, options, {
71
+ count: options.count,
72
+ dryRun: false
73
+ })).pipe(described);
74
+ if (operation.annotations.readOnly) return make(name, shared(operation), (options) => run(operation, options, {
75
+ count: none(),
76
+ dryRun: false
77
+ })).pipe(described);
78
+ return make(name, {
79
+ ...shared(operation),
80
+ dryRun
81
+ }, (options) => run(operation, options, {
82
+ count: none(),
83
+ dryRun: options.dryRun
84
+ })).pipe(described);
85
+ };
86
+ const commandOf = (operation) => {
87
+ const command = describedCommandOf(operation);
88
+ return match(operation.flag, {
89
+ onNone: () => command,
90
+ onSome: () => unlisted(command)
91
+ });
92
+ };
93
+ const servedCommandFor = (served) => servedCommandOf({
94
+ flags: {
95
+ ...shared(served.actor),
96
+ dryRun
97
+ },
98
+ local: (options) => run(served.actor, options, {
99
+ count: none(),
100
+ dryRun: options.dryRun
101
+ }),
102
+ served
103
+ });
104
+ const commandFor = (operations) => (actor) => match(servedBy(operations)(actor), {
105
+ onNone: () => commandOf(actor),
106
+ onSome: (cloud) => servedCommandFor({
107
+ actor,
108
+ cloud
109
+ })
110
+ });
111
+ const groupDescriptions = {
112
+ actors: "Call, query, watch and create an app's actors",
113
+ computers: "See, pause, resume, stop and delete the computers an app's actors own, and read their logs",
114
+ files: "Upload, list and download the files an app's actors hold",
115
+ steps: "Read the durable computer steps an app's actors' jobs ran"
116
+ };
117
+ const servedIds = (operations, members) => {
118
+ const served = getSomes(map(members, servedBy(operations)));
119
+ return map(served, ({ id }) => id);
120
+ };
121
+ const actorCommands = (operations) => map$1(groupBy(actorOperations, ({ command: [owner] }) => owner), (members, owner) => ({
122
+ commands: map(members, commandFor(operations)),
123
+ description: getOrElse(get(groupDescriptions, owner), () => owner),
124
+ serves: servedIds(operations, members),
125
+ unlisted: every(members, ({ flag }) => isSome(flag))
126
+ }));
127
+ export { actorCommands };
@@ -0,0 +1,34 @@
1
+ import { R as some, u as findFirst } from "../Array-B4kuTQeW.mjs";
2
+ import { Xt as isError, b as map, f as getOrElse, un as isTagged, v as liftPredicate } from "../Option-DkxSnBhp.mjs";
3
+ import { CliExitError } from "../terminal/cli-exit.errors.mjs";
4
+ // src/registry/actor-refusal.pure.ts
5
+ const refusals = [
6
+ [[
7
+ "CallerNotAllowedError",
8
+ "FileAccessDeniedError",
9
+ "UnauthenticatedError"
10
+ ], CliExitError.codes.authentication],
11
+ [["InvalidRequestError"], CliExitError.codes.usage],
12
+ [[
13
+ "ActorUnavailableError",
14
+ "RpcClientError",
15
+ "TimeoutError"
16
+ ], CliExitError.codes.unavailable]
17
+ ];
18
+ const refusalCode = (failure) => getOrElse(map(findFirst(refusals, ([tags]) => some(tags, (tag) => isTagged(failure, tag))), ([, code]) => code), () => CliExitError.codes.failed);
19
+ const unansweredReasons = [["RpcClientError", "Unreachable"], ["TimeoutError", "TimedOut"]];
20
+ const unansweredOf = (tag) => (failure) => map(liftPredicate(failure, isError), (thrown) => {
21
+ const known = findFirst(unansweredReasons, ([name]) => isTagged(failure, name));
22
+ const reason = getOrElse(map(known, ([, why]) => why), () => "Failed");
23
+ return CliExitError.make({
24
+ code: refusalCode(failure),
25
+ message: `${tag} got no answer: ${thrown.message}`,
26
+ reason
27
+ });
28
+ });
29
+ const rejectedRequest = (tag) => (defect) => CliExitError.make({
30
+ code: CliExitError.codes.failed,
31
+ message: `${tag} was refused: ${defect}`,
32
+ reason: "RequestRefused"
33
+ });
34
+ export { refusalCode, rejectedRequest, unansweredOf };
@@ -0,0 +1,80 @@
1
+ import { T as join } from "../Array-B4kuTQeW.mjs";
2
+ import { t as match } from "../Boolean-kILIvmhE.mjs";
3
+ import { t as error } from "../Console-BKHUkH6a.mjs";
4
+ import { At as provideContext, T as context, Vn as effect, W as fnUntraced, Wn as merge, Xt as succeed, _n as when, aa as Service, d as asVoid, et as gen, ht as mapError, in as timeout } from "../Effect-CKFpqbmw.mjs";
5
+ import { b as map, i as filter, in as isNumber, qt as hasProperty, un as isTagged, v as liftPredicate, x as match$1 } from "../Option-DkxSnBhp.mjs";
6
+ import { ot as decodeUnknownOption } from "../Schema-MiKt5K1v.mjs";
7
+ import { v as trim } from "../String-q5dcsVe4.mjs";
8
+ import { c as String, d as run, t as TerminalCapabilities } from "../TerminalCapabilities-w_44MAca.mjs";
9
+ import { d as make, r as ConfirmationRequiredErrorEncoded } from "../cloud-client.adapter-Cy1BjDbG.mjs";
10
+ import { CliExitError } from "../terminal/cli-exit.errors.mjs";
11
+ import { Confirmation } from "../terminal/confirm.adapter.mjs";
12
+ // src/registry/approval.service.ts
13
+ const approvalWait = "30 seconds";
14
+ const unattended = ({ command, organization, required }) => CliExitError.make({
15
+ code: CliExitError.codes.confirmation,
16
+ message: join([
17
+ `${command} runs with an API key, so the key's creator has to approve it, and no terminal is attached to ask for their code.`,
18
+ `Ask for an approval: akter approvals request --organization ${organization} --operation ${required.operation} --target ${required.target}`,
19
+ `Turn the emailed code into a token: akter approvals confirm --organization ${organization} --request <id> --code <code>`,
20
+ `Then run ${command} again with --approval <token>.`
21
+ ], "\n"),
22
+ reason: "ApprovalRequired"
23
+ });
24
+ const statusOf = (failure) => liftPredicate(failure, hasProperty("response")).pipe(map(({ response }) => response), filter(hasProperty("status")), map(({ status }) => status), filter(isNumber));
25
+ const failedStep = (step) => (failure) => {
26
+ const status = statusOf(failure);
27
+ return CliExitError.make({
28
+ code: match$1(status, {
29
+ onNone: () => CliExitError.codes.unavailable,
30
+ onSome: CliExitError.ofStatus
31
+ }),
32
+ message: `${step} failed${match$1(status, {
33
+ onNone: () => "",
34
+ onSome: (code) => ` with status ${code}`
35
+ })}, so nothing was changed.`,
36
+ reason: "ApprovalFailed"
37
+ });
38
+ };
39
+ const notTyped = () => CliExitError.make({
40
+ code: CliExitError.codes.confirmation,
41
+ message: "No approval code was typed, so nothing was changed.",
42
+ reason: "NotConfirmed"
43
+ });
44
+ const rejected = () => CliExitError.make({
45
+ code: CliExitError.codes.authentication,
46
+ message: "The approval code was refused, so nothing was changed. A third wrong code ends the approval.",
47
+ reason: "CodeRejected"
48
+ });
49
+ const confirmFailure = (failure) => match(hasProperty(failure, "cause") && isTagged(failure.cause, "CodeRejectedError"), {
50
+ onFalse: () => failedStep("Turning the approval code into a token")(failure),
51
+ onTrue: rejected
52
+ });
53
+ const approve = fnUntraced(function* approve(asking) {
54
+ const { canPrompt } = yield* TerminalCapabilities;
55
+ if (!canPrompt) return yield* unattended(asking);
56
+ const { http, organization, required } = asking;
57
+ const client = make(http);
58
+ const sent = yield* client.approvalsRequest(organization, { payload: {
59
+ operation: required.operation,
60
+ target: required.target
61
+ } }).pipe(timeout(approvalWait), mapError(failedStep("Asking for an approval")));
62
+ yield* error(`${asking.command} needs approval. Emailed a code to ${sent.sentTo}; it works for 15 minutes.`);
63
+ const code = yield* run(String({ message: "Approval code" })).pipe(mapError(notTyped));
64
+ return (yield* client.approvalsConfirm(organization, sent.id, { payload: { code: trim(code) } }).pipe(timeout(approvalWait), mapError(confirmFailure))).token;
65
+ });
66
+ var Approval = class extends Service()("@akterjs/cli/registry/approval.service/Approval", { make: gen(function* makeApproval() {
67
+ const context$1 = yield* context();
68
+ const confirmation = yield* Confirmation.make;
69
+ return {
70
+ confirm: ({ command, needed, question }) => when(confirmation.confirm({
71
+ question,
72
+ unattended: `${command} needs a person to confirm it, and no terminal is attached to ask. Run it in a terminal, or pass --approval with a token: an API key gets one from akter approvals request and akter approvals confirm.`
73
+ }), succeed(needed)).pipe(asVoid),
74
+ isRequired: decodeUnknownOption(ConfirmationRequiredErrorEncoded),
75
+ token: (asking) => provideContext(approve(asking), context$1)
76
+ };
77
+ }) }) {
78
+ static layer = effect(this, this.make).pipe(merge(Confirmation.layer));
79
+ };
80
+ export { Approval };
@@ -0,0 +1,151 @@
1
+ import { C as isReadonlyArrayEmpty, R as some$1, c as every, j as map, l as filter, r as contains, v as groupBy, w as isReadonlyArrayNonEmpty } from "../Array-B4kuTQeW.mjs";
2
+ import { t as match } from "../Boolean-kILIvmhE.mjs";
3
+ import { S as none, _ as isSome, f as getOrElse, i as filter$1, w as some$2, x as match$1 } from "../Option-DkxSnBhp.mjs";
4
+ import { ar as filter$2, hr as toEntries, lr as has, or as fromEntries, pt as fromJsonString, sr as get, y as Json } from "../Schema-MiKt5K1v.mjs";
5
+ import { v as trim } from "../String-q5dcsVe4.mjs";
6
+ import { a as withSubcommands, c as Int, d as map$1, f as optional, g as withSchema, i as withDescription$1, l as String$1, m as withDescription, o as Boolean, p as withDefault, r as unlisted, s as Finite, t as make, u as atLeast } from "../Command-Dzq9KM-h.mjs";
7
+ import { OperationCall } from "./operation-call.service.mjs";
8
+ import { t as kebab } from "../operation-registry.pure-DVC8EDgj.mjs";
9
+ // src/registry/cloud-commands.config.ts
10
+ const noneRequired = 0;
11
+ const reservedFlags = [
12
+ "all",
13
+ "approval",
14
+ "dry-run",
15
+ "input",
16
+ "json",
17
+ "url"
18
+ ];
19
+ const JsonText = fromJsonString(Json);
20
+ const kindHelp = {
21
+ boolean: "",
22
+ integer: "",
23
+ json: " As JSON.",
24
+ number: "",
25
+ string: "",
26
+ strings: " Repeat it for more."
27
+ };
28
+ const organizationHelp = " Defaults to AKTER_ORGANIZATION, then the organization saved by akter login.";
29
+ const when = (condition, text) => match(condition, {
30
+ onFalse: () => "",
31
+ onTrue: () => text
32
+ });
33
+ const helpOf = (field) => {
34
+ const required = when(field.required, " Required.");
35
+ const fallback = when(field.name === "organization", organizationHelp);
36
+ return trim(`${field.description}${kindHelp[field.kind]}${required}${fallback}`);
37
+ };
38
+ const some = (value) => value;
39
+ const listed = (items) => filter$1(some$2(map(items, String)), () => isReadonlyArrayNonEmpty(items));
40
+ const flagsByKind = {
41
+ boolean: (name) => Boolean(name).pipe(optional, map$1(some)),
42
+ integer: (name) => Int(name).pipe(optional, map$1(some)),
43
+ json: (name) => String$1(name).pipe(withSchema(JsonText), optional, map$1(some)),
44
+ number: (name) => Finite(name).pipe(optional, map$1(some)),
45
+ string: (name) => String$1(name).pipe(optional, map$1(some)),
46
+ strings: (name) => String$1(name).pipe(atLeast(noneRequired), map$1((items) => listed(items)))
47
+ };
48
+ const flagOf = (field) => withDescription(flagsByKind[field.kind](kebab(field.name)), helpOf(field));
49
+ const input = String$1("input").pipe(optional, withDescription("The whole input as one JSON object, the same arguments as the operation's MCP tool; @file reads a file and - reads stdin. Flags override its fields."));
50
+ const json = Boolean("json").pipe(withDefault(false), withDescription("Print the API's answer as one line of JSON in its own schema, and failures as JSON too"));
51
+ const url = String$1("url").pipe(optional, withDescription("The Cloud API's URL; defaults to AKTER_CLOUD_URL, then the URL saved by akter login, then https://api.akter.dev"));
52
+ const dryRun = Boolean("dry-run").pipe(withDefault(false), withDescription("Check the input and print the request instead of sending it"));
53
+ const all = Boolean("all").pipe(withDefault(false), withDescription("Read every page, sending each page's next as after until a page has none, and print one answer holding them all"));
54
+ const approval = String$1("approval").pipe(optional, withDescription("A token from an approved request, to run this without a person at the terminal"));
55
+ const shared = (operation) => ({
56
+ fields: fromEntries(map(operation.fields, (field) => [field.name, flagOf(field)])),
57
+ input,
58
+ json,
59
+ url
60
+ });
61
+ const run = (operation, options, extra) => OperationCall.use((service) => service.call(operation, {
62
+ ...extra,
63
+ input: options.input,
64
+ json: options.json,
65
+ url: options.url,
66
+ values: options.fields
67
+ }));
68
+ const readCommand = (operation) => {
69
+ const [, name] = operation.command;
70
+ return make(name, shared(operation), (options) => run(operation, options, {
71
+ all: false,
72
+ approval: none(),
73
+ dryRun: false
74
+ })).pipe(withDescription$1(operation.description));
75
+ };
76
+ const pagedCommand = (operation) => {
77
+ const [, name] = operation.command;
78
+ return make(name, {
79
+ ...shared(operation),
80
+ all
81
+ }, (options) => run(operation, options, {
82
+ all: options.all,
83
+ approval: none(),
84
+ dryRun: false
85
+ })).pipe(withDescription$1(operation.description));
86
+ };
87
+ const writeCommand = (operation) => {
88
+ const [, name] = operation.command;
89
+ return make(name, {
90
+ ...shared(operation),
91
+ dryRun
92
+ }, (options) => run(operation, options, {
93
+ all: false,
94
+ approval: none(),
95
+ dryRun: options.dryRun
96
+ })).pipe(withDescription$1(operation.description));
97
+ };
98
+ const confirmedCommand = (operation) => {
99
+ const [, name] = operation.command;
100
+ return make(name, {
101
+ ...shared(operation),
102
+ approval,
103
+ dryRun
104
+ }, (options) => run(operation, options, {
105
+ all: false,
106
+ approval: options.approval,
107
+ dryRun: options.dryRun
108
+ })).pipe(withDescription$1(`${operation.description} Asks a person to confirm.`));
109
+ };
110
+ const commandOf = (operation) => {
111
+ if (operation.annotations.readOnly && some$1(operation.fields, (field) => field.name === "after")) return pagedCommand(operation);
112
+ if (operation.annotations.readOnly) return readCommand(operation);
113
+ if (operation.annotations.confirm) return confirmedCommand(operation);
114
+ return writeCommand(operation);
115
+ };
116
+ const listedCommand = (operation) => {
117
+ const command = commandOf(operation);
118
+ return match$1(operation.flag, {
119
+ onNone: () => command,
120
+ onSome: () => unlisted(command)
121
+ });
122
+ };
123
+ const groupCommand = ({ group, joined, members }) => {
124
+ const [first] = members;
125
+ const nothingJoined = {
126
+ commands: [],
127
+ description: "",
128
+ serves: []
129
+ };
130
+ const { commands, serves } = getOrElse(get(joined, group), () => nothingJoined);
131
+ const own = filter(members, ({ id }) => !contains(serves, id));
132
+ const command = make(group).pipe(withDescription$1(first.groupDescription), withSubcommands([...map(own, listedCommand), ...commands]));
133
+ if (isReadonlyArrayEmpty(commands) && every(members, ({ flag }) => isSome(flag))) return unlisted(command);
134
+ return command;
135
+ };
136
+ const cloudCommands = ({ joined, operations }) => {
137
+ const groups = groupBy(operations, ({ command: [group] }) => group);
138
+ const own = map(toEntries(filter$2(joined, (_group, name) => !has(groups, name))), ([group, { commands, description, unlisted: unlisted$1 = false }]) => {
139
+ const command = make(group).pipe(withDescription$1(description), withSubcommands([...commands]));
140
+ return match(unlisted$1, {
141
+ onFalse: () => command,
142
+ onTrue: () => unlisted(command)
143
+ });
144
+ });
145
+ return [...map(toEntries(groups), ([group, members]) => groupCommand({
146
+ group,
147
+ joined,
148
+ members
149
+ })), ...own];
150
+ };
151
+ export { cloudCommands, reservedFlags };