alchemy 2.0.0-beta.17 → 2.0.0-beta.19

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 (182) hide show
  1. package/bin/alchemy.js +428 -80
  2. package/bin/alchemy.js.map +1 -1
  3. package/bin/alchemy.ts +2 -0
  4. package/bin/commands/_shared.ts +34 -0
  5. package/bin/commands/bootstrap.ts +10 -3
  6. package/bin/commands/deploy.ts +23 -3
  7. package/bin/commands/login.ts +18 -3
  8. package/bin/commands/logs.ts +11 -2
  9. package/bin/commands/profile.ts +11 -5
  10. package/bin/commands/state.ts +21 -13
  11. package/bin/commands/tail.ts +10 -2
  12. package/lib/AWS/CloudFront/CachePolicy.d.ts +106 -0
  13. package/lib/AWS/CloudFront/CachePolicy.d.ts.map +1 -0
  14. package/lib/AWS/CloudFront/CachePolicy.js +147 -0
  15. package/lib/AWS/CloudFront/CachePolicy.js.map +1 -0
  16. package/lib/AWS/CloudFront/KeyGroup.d.ts +72 -0
  17. package/lib/AWS/CloudFront/KeyGroup.d.ts.map +1 -0
  18. package/lib/AWS/CloudFront/KeyGroup.js +136 -0
  19. package/lib/AWS/CloudFront/KeyGroup.js.map +1 -0
  20. package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts +84 -0
  21. package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts.map +1 -0
  22. package/lib/AWS/CloudFront/OriginRequestPolicy.js +139 -0
  23. package/lib/AWS/CloudFront/OriginRequestPolicy.js.map +1 -0
  24. package/lib/AWS/CloudFront/PublicKey.d.ts +72 -0
  25. package/lib/AWS/CloudFront/PublicKey.d.ts.map +1 -0
  26. package/lib/AWS/CloudFront/PublicKey.js +137 -0
  27. package/lib/AWS/CloudFront/PublicKey.js.map +1 -0
  28. package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts +114 -0
  29. package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts.map +1 -0
  30. package/lib/AWS/CloudFront/ResponseHeadersPolicy.js +162 -0
  31. package/lib/AWS/CloudFront/ResponseHeadersPolicy.js.map +1 -0
  32. package/lib/AWS/CloudFront/index.d.ts +5 -0
  33. package/lib/AWS/CloudFront/index.d.ts.map +1 -1
  34. package/lib/AWS/CloudFront/index.js +5 -0
  35. package/lib/AWS/CloudFront/index.js.map +1 -1
  36. package/lib/AWS/ECS/CapacityProvider.d.ts +102 -0
  37. package/lib/AWS/ECS/CapacityProvider.d.ts.map +1 -0
  38. package/lib/AWS/ECS/CapacityProvider.js +217 -0
  39. package/lib/AWS/ECS/CapacityProvider.js.map +1 -0
  40. package/lib/AWS/ECS/index.d.ts +1 -0
  41. package/lib/AWS/ECS/index.d.ts.map +1 -1
  42. package/lib/AWS/ECS/index.js +1 -0
  43. package/lib/AWS/ECS/index.js.map +1 -1
  44. package/lib/AWS/Providers.d.ts.map +1 -1
  45. package/lib/AWS/Providers.js +7 -1
  46. package/lib/AWS/Providers.js.map +1 -1
  47. package/lib/Agent/lsp/client.d.ts +2 -2
  48. package/lib/Apply.d.ts.map +1 -1
  49. package/lib/Apply.js +41 -9
  50. package/lib/Apply.js.map +1 -1
  51. package/lib/Axiom/Annotation.d.ts +49 -0
  52. package/lib/Axiom/Annotation.d.ts.map +1 -0
  53. package/lib/Axiom/Annotation.js +74 -0
  54. package/lib/Axiom/Annotation.js.map +1 -0
  55. package/lib/Axiom/ApiToken.d.ts +62 -0
  56. package/lib/Axiom/ApiToken.d.ts.map +1 -0
  57. package/lib/Axiom/ApiToken.js +98 -0
  58. package/lib/Axiom/ApiToken.js.map +1 -0
  59. package/lib/Axiom/AuthProvider.d.ts +48 -0
  60. package/lib/Axiom/AuthProvider.d.ts.map +1 -0
  61. package/lib/Axiom/AuthProvider.js +177 -0
  62. package/lib/Axiom/AuthProvider.js.map +1 -0
  63. package/lib/Axiom/Chart.d.ts +72 -0
  64. package/lib/Axiom/Chart.d.ts.map +1 -0
  65. package/lib/Axiom/Chart.js +14 -0
  66. package/lib/Axiom/Chart.js.map +1 -0
  67. package/lib/Axiom/Credentials.d.ts +12 -0
  68. package/lib/Axiom/Credentials.d.ts.map +1 -0
  69. package/lib/Axiom/Credentials.js +34 -0
  70. package/lib/Axiom/Credentials.js.map +1 -0
  71. package/lib/Axiom/Dashboard.d.ts +124 -0
  72. package/lib/Axiom/Dashboard.d.ts.map +1 -0
  73. package/lib/Axiom/Dashboard.js +140 -0
  74. package/lib/Axiom/Dashboard.js.map +1 -0
  75. package/lib/Axiom/Dataset.d.ts +101 -0
  76. package/lib/Axiom/Dataset.d.ts.map +1 -0
  77. package/lib/Axiom/Dataset.js +183 -0
  78. package/lib/Axiom/Dataset.js.map +1 -0
  79. package/lib/Axiom/Monitor.d.ts +74 -0
  80. package/lib/Axiom/Monitor.d.ts.map +1 -0
  81. package/lib/Axiom/Monitor.js +103 -0
  82. package/lib/Axiom/Monitor.js.map +1 -0
  83. package/lib/Axiom/Notifier.d.ts +63 -0
  84. package/lib/Axiom/Notifier.d.ts.map +1 -0
  85. package/lib/Axiom/Notifier.js +83 -0
  86. package/lib/Axiom/Notifier.js.map +1 -0
  87. package/lib/Axiom/Providers.d.ts +15 -0
  88. package/lib/Axiom/Providers.d.ts.map +1 -0
  89. package/lib/Axiom/Providers.js +32 -0
  90. package/lib/Axiom/Providers.js.map +1 -0
  91. package/lib/Axiom/View.d.ts +55 -0
  92. package/lib/Axiom/View.d.ts.map +1 -0
  93. package/lib/Axiom/View.js +79 -0
  94. package/lib/Axiom/View.js.map +1 -0
  95. package/lib/Axiom/VirtualField.d.ts +42 -0
  96. package/lib/Axiom/VirtualField.d.ts.map +1 -0
  97. package/lib/Axiom/VirtualField.js +71 -0
  98. package/lib/Axiom/VirtualField.js.map +1 -0
  99. package/lib/Axiom/index.d.ts +13 -0
  100. package/lib/Axiom/index.d.ts.map +1 -0
  101. package/lib/Axiom/index.js +13 -0
  102. package/lib/Axiom/index.js.map +1 -0
  103. package/lib/Bundle/Bundle.d.ts +20 -2
  104. package/lib/Bundle/Bundle.d.ts.map +1 -1
  105. package/lib/Bundle/Bundle.js +18 -3
  106. package/lib/Bundle/Bundle.js.map +1 -1
  107. package/lib/Bundle/PurePlugin.d.ts +109 -0
  108. package/lib/Bundle/PurePlugin.d.ts.map +1 -0
  109. package/lib/Bundle/PurePlugin.js +469 -0
  110. package/lib/Bundle/PurePlugin.js.map +1 -0
  111. package/lib/Bundle/index.d.ts +1 -0
  112. package/lib/Bundle/index.d.ts.map +1 -1
  113. package/lib/Bundle/index.js +1 -0
  114. package/lib/Bundle/index.js.map +1 -1
  115. package/lib/Cloudflare/Workers/Worker.d.ts +12 -0
  116. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  117. package/lib/Cloudflare/Workers/Worker.js +19 -4
  118. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  119. package/lib/GitHub/Secrets.d.ts +45 -0
  120. package/lib/GitHub/Secrets.d.ts.map +1 -0
  121. package/lib/GitHub/Secrets.js +30 -0
  122. package/lib/GitHub/Secrets.js.map +1 -0
  123. package/lib/GitHub/index.d.ts +1 -0
  124. package/lib/GitHub/index.d.ts.map +1 -1
  125. package/lib/GitHub/index.js +1 -0
  126. package/lib/GitHub/index.js.map +1 -1
  127. package/lib/Output.d.ts.map +1 -1
  128. package/lib/Output.js +1 -1
  129. package/lib/Output.js.map +1 -1
  130. package/lib/Plan.d.ts.map +1 -1
  131. package/lib/Plan.js +10 -4
  132. package/lib/Plan.js.map +1 -1
  133. package/lib/Resource.d.ts +2 -2
  134. package/lib/Resource.d.ts.map +1 -1
  135. package/lib/Telemetry/Attributes.d.ts +43 -0
  136. package/lib/Telemetry/Attributes.d.ts.map +1 -0
  137. package/lib/Telemetry/Attributes.js +144 -0
  138. package/lib/Telemetry/Attributes.js.map +1 -0
  139. package/lib/Telemetry/Layer.d.ts +14 -0
  140. package/lib/Telemetry/Layer.d.ts.map +1 -0
  141. package/lib/Telemetry/Layer.js +50 -0
  142. package/lib/Telemetry/Layer.js.map +1 -0
  143. package/lib/Telemetry/Metrics.d.ts +34 -0
  144. package/lib/Telemetry/Metrics.d.ts.map +1 -0
  145. package/lib/Telemetry/Metrics.js +57 -0
  146. package/lib/Telemetry/Metrics.js.map +1 -0
  147. package/package.json +12 -1
  148. package/src/AWS/CloudFront/CachePolicy.ts +271 -0
  149. package/src/AWS/CloudFront/KeyGroup.ts +233 -0
  150. package/src/AWS/CloudFront/OriginRequestPolicy.ts +256 -0
  151. package/src/AWS/CloudFront/PublicKey.ts +245 -0
  152. package/src/AWS/CloudFront/ResponseHeadersPolicy.ts +308 -0
  153. package/src/AWS/CloudFront/index.ts +11 -0
  154. package/src/AWS/ECS/CapacityProvider.ts +336 -0
  155. package/src/AWS/ECS/index.ts +4 -0
  156. package/src/AWS/Providers.ts +12 -0
  157. package/src/Apply.ts +118 -9
  158. package/src/Axiom/Annotation.ts +95 -0
  159. package/src/Axiom/ApiToken.ts +125 -0
  160. package/src/Axiom/AuthProvider.ts +341 -0
  161. package/src/Axiom/Chart.ts +94 -0
  162. package/src/Axiom/Credentials.ts +70 -0
  163. package/src/Axiom/Dashboard.ts +192 -0
  164. package/src/Axiom/Dataset.ts +291 -0
  165. package/src/Axiom/Monitor.ts +122 -0
  166. package/src/Axiom/Notifier.ts +104 -0
  167. package/src/Axiom/Providers.ts +58 -0
  168. package/src/Axiom/View.ts +106 -0
  169. package/src/Axiom/VirtualField.ts +90 -0
  170. package/src/Axiom/index.ts +12 -0
  171. package/src/Bundle/Bundle.ts +39 -1
  172. package/src/Bundle/PurePlugin.ts +556 -0
  173. package/src/Bundle/index.ts +1 -0
  174. package/src/Cloudflare/Workers/Worker.ts +37 -6
  175. package/src/GitHub/Secrets.ts +66 -0
  176. package/src/GitHub/index.ts +1 -0
  177. package/src/Output.ts +2 -1
  178. package/src/Plan.ts +13 -4
  179. package/src/Resource.ts +2 -2
  180. package/src/Telemetry/Attributes.ts +216 -0
  181. package/src/Telemetry/Layer.ts +65 -0
  182. package/src/Telemetry/Metrics.ts +101 -0
package/bin/alchemy.js CHANGED
@@ -10,14 +10,21 @@ import * as Path$1 from "effect/Path";
10
10
  import { Path } from "effect/Path";
11
11
  import { PassThrough, Stream } from "node:stream";
12
12
  import process$1, { cwd, env } from "node:process";
13
- import * as NodeOs from "node:os";
13
+ import * as os$1 from "node:os";
14
14
  import os from "node:os";
15
- import { execFileSync } from "node:child_process";
16
- import * as fs$1 from "node:fs";
15
+ import { exec, execFileSync } from "node:child_process";
16
+ import * as fs$2 from "node:fs";
17
17
  import fs from "node:fs";
18
18
  import tty from "node:tty";
19
19
  import { EventEmitter } from "node:events";
20
20
  import { Buffer as Buffer$1 } from "node:buffer";
21
+ import * as OtlpMetrics from "effect/unstable/observability/OtlpMetrics";
22
+ import * as OtlpSerialization from "effect/unstable/observability/OtlpSerialization";
23
+ import * as OtlpTracer from "effect/unstable/observability/OtlpTracer";
24
+ import * as crypto$2 from "node:crypto";
25
+ import crypto$1 from "node:crypto";
26
+ import * as fs$1 from "node:fs/promises";
27
+ import * as nodePath from "node:path";
21
28
  import * as Cause from "effect/Cause";
22
29
  import * as Config from "effect/Config";
23
30
  import * as Console from "effect/Console";
@@ -26,6 +33,9 @@ import * as Schema from "effect/Schema";
26
33
  import * as Argument$1 from "effect/unstable/cli/Argument";
27
34
  import * as CliError from "effect/unstable/cli/CliError";
28
35
  import * as Flag$1 from "effect/unstable/cli/Flag";
36
+ import * as Duration from "effect/Duration";
37
+ import * as Exit from "effect/Exit";
38
+ import * as Metric from "effect/Metric";
29
39
  import * as p from "@clack/prompts";
30
40
  import * as Data from "effect/Data";
31
41
  import { ChildProcess } from "effect/unstable/process";
@@ -43,11 +53,8 @@ import { SingleShotGen } from "effect/Utils";
43
53
  import { pipeArguments } from "effect/Pipeable";
44
54
  import { Argument, Command, Flag } from "effect/unstable/cli";
45
55
  import * as Match from "effect/Match";
46
- import * as NodeCrypto from "node:crypto";
47
- import crypto$1 from "node:crypto";
48
56
  import * as cfAccounts from "@distilled.cloud/cloudflare/accounts";
49
57
  import * as CfCredentialsModule from "@distilled.cloud/cloudflare/Credentials";
50
- import * as Duration from "effect/Duration";
51
58
  import http from "node:http";
52
59
  import * as Stream$1 from "effect/Stream";
53
60
 
@@ -82,15 +89,15 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
82
89
 
83
90
  //#endregion
84
91
  //#region package.json
85
- var version = "2.0.0-beta.17";
92
+ var version = "2.0.0-beta.19";
86
93
 
87
94
  //#endregion
88
95
  //#region src/AlchemyContext.ts
89
96
  var AlchemyContext = class extends EffectContext.Service()("alchemy/Context") {};
90
97
  const AlchemyContextLive = Layer.effect(AlchemyContext, Effect.gen(function* () {
91
- const fs$2 = yield* FileSystem.FileSystem;
98
+ const fs$3 = yield* FileSystem.FileSystem;
92
99
  const dir = (yield* Path$1.Path).join(process.cwd(), ".alchemy");
93
- yield* fs$2.makeDirectory(dir, { recursive: true });
100
+ yield* fs$3.makeDirectory(dir, { recursive: true });
94
101
  return {
95
102
  dotAlchemy: dir,
96
103
  updateStateStore: false
@@ -4241,7 +4248,7 @@ const stringVisibleTrimSpacesRight = (string) => {
4241
4248
  if (last === words.length) return string;
4242
4249
  return words.slice(0, last).join(" ") + words.slice(last).join("");
4243
4250
  };
4244
- const exec$1 = (string, columns, options$2 = {}) => {
4251
+ const exec$2 = (string, columns, options$2 = {}) => {
4245
4252
  if (options$2.trim !== false && string.trim() === "") return "";
4246
4253
  let returnValue = "";
4247
4254
  let escapeCode;
@@ -4307,14 +4314,14 @@ const exec$1 = (string, columns, options$2 = {}) => {
4307
4314
  return returnValue;
4308
4315
  };
4309
4316
  function wrapAnsi(string, columns, options$2) {
4310
- return String(string).normalize().replaceAll("\r\n", "\n").split("\n").map((line) => exec$1(line, columns, options$2)).join("\n");
4317
+ return String(string).normalize().replaceAll("\r\n", "\n").split("\n").map((line) => exec$2(line, columns, options$2)).join("\n");
4311
4318
  }
4312
4319
 
4313
4320
  //#endregion
4314
4321
  //#region ../../node_modules/.bun/terminal-size@4.0.1/node_modules/terminal-size/index.js
4315
4322
  const defaultColumns = 80;
4316
4323
  const defaultRows = 24;
4317
- const exec = (command, arguments_, { shell, env: env$2 } = {}) => execFileSync(command, arguments_, {
4324
+ const exec$1 = (command, arguments_, { shell, env: env$2 } = {}) => execFileSync(command, arguments_, {
4318
4325
  encoding: "utf8",
4319
4326
  stdio: [
4320
4327
  "ignore",
@@ -4379,11 +4386,11 @@ const devTty = () => {
4379
4386
  };
4380
4387
  const tput = () => {
4381
4388
  try {
4382
- const columns = exec("tput", ["cols"], { env: {
4389
+ const columns = exec$1("tput", ["cols"], { env: {
4383
4390
  TERM: "dumb",
4384
4391
  ...process$1.env
4385
4392
  } });
4386
- const rows = exec("tput", ["lines"], { env: {
4393
+ const rows = exec$1("tput", ["lines"], { env: {
4387
4394
  TERM: "dumb",
4388
4395
  ...process$1.env
4389
4396
  } });
@@ -4393,7 +4400,7 @@ const tput = () => {
4393
4400
  const resize = () => {
4394
4401
  try {
4395
4402
  if (!isForegroundProcess()) return;
4396
- const size = exec("resize", ["-u"]).match(/\d+/g);
4403
+ const size = exec$1("resize", ["-u"]).match(/\d+/g);
4397
4404
  if (size.length === 2) return createIfNotDefault(size[0], size[1]);
4398
4405
  } catch {}
4399
4406
  };
@@ -40981,8 +40988,8 @@ function ErrorOverview({ error }) {
40981
40988
  const filePath = cleanupPath(origin?.file);
40982
40989
  let excerpt;
40983
40990
  let lineWidth = 0;
40984
- if (filePath && origin?.line && fs$1.existsSync(filePath)) {
40985
- excerpt = dist_default$1(fs$1.readFileSync(filePath, "utf8"), origin.line);
40991
+ if (filePath && origin?.line && fs$2.existsSync(filePath)) {
40992
+ excerpt = dist_default$1(fs$2.readFileSync(filePath, "utf8"), origin.line);
40986
40993
  if (excerpt) for (const { line } of excerpt) lineWidth = Math.max(lineWidth, String(line).length);
40987
40994
  }
40988
40995
  return import_react.createElement(Box_default, {
@@ -43148,6 +43155,215 @@ const startApplySession = Effect.fn(function* (plan) {
43148
43155
  };
43149
43156
  });
43150
43157
 
43158
+ //#endregion
43159
+ //#region src/Telemetry/Attributes.ts
43160
+ const ALCHEMY_DIR = nodePath.join(os$1.homedir(), ".alchemy");
43161
+ const ID_PATH = nodePath.join(ALCHEMY_DIR, "id");
43162
+ const DISABLED_PATH = nodePath.join(ALCHEMY_DIR, "telemetry-disabled");
43163
+ const tryRead = (path) => Effect.tryPromise({
43164
+ try: () => fs$1.readFile(path, "utf-8").then((s) => s.trim()),
43165
+ catch: () => null
43166
+ }).pipe(Effect.catch(() => Effect.succeed(null)));
43167
+ const sha256Hex = (input) => crypto$2.createHash("sha256").update(input).digest("hex");
43168
+ const hashStringOrEmpty = (input) => input == null || input === "" ? "" : sha256Hex(input);
43169
+ const execCapture = (cmd) => Effect.callback((resume$1) => {
43170
+ exec(cmd, { timeout: 1500 }, (err, stdout) => {
43171
+ if (err) {
43172
+ resume$1(Effect.succeed(null));
43173
+ return;
43174
+ }
43175
+ resume$1(Effect.succeed(stdout.toString().trim()));
43176
+ });
43177
+ });
43178
+ const getOrCreateUserId = Effect.gen(function* () {
43179
+ const existing = yield* tryRead(ID_PATH);
43180
+ if (existing) return existing;
43181
+ const id = crypto$2.randomUUID();
43182
+ yield* Effect.tryPromise({
43183
+ try: async () => {
43184
+ await fs$1.mkdir(ALCHEMY_DIR, { recursive: true });
43185
+ await fs$1.writeFile(ID_PATH, id);
43186
+ },
43187
+ catch: () => null
43188
+ }).pipe(Effect.catch(() => Effect.void));
43189
+ return id;
43190
+ });
43191
+ const getRootCommitHash = execCapture(process.platform === "win32" ? `git rev-list --max-parents=0 HEAD | ForEach-Object { if (-not (git cat-file -p $_ | Select-String "^parent ")) { $_ } }` : `git rev-list --max-parents=0 HEAD | xargs -r -I{} sh -c 'git cat-file -p {} | grep -q "^parent " || echo {}'`);
43192
+ const getGitOriginUrl = execCapture("git config --get remote.origin.url");
43193
+ const getBranchName = execCapture("git rev-parse --abbrev-ref HEAD");
43194
+ const getRuntime = () => {
43195
+ const g = globalThis;
43196
+ if (typeof g.Bun !== "undefined") return {
43197
+ name: "bun",
43198
+ version: g.Bun.version
43199
+ };
43200
+ if (typeof g.Deno !== "undefined") return {
43201
+ name: "deno",
43202
+ version: g.Deno.version?.deno ?? ""
43203
+ };
43204
+ if (globalThis.process?.versions?.node) return {
43205
+ name: "node",
43206
+ version: process.versions.node
43207
+ };
43208
+ return {
43209
+ name: "",
43210
+ version: ""
43211
+ };
43212
+ };
43213
+ const CI_PROVIDERS = [
43214
+ {
43215
+ env: "GITHUB_ACTIONS",
43216
+ provider: "GitHub Actions",
43217
+ isCI: true
43218
+ },
43219
+ {
43220
+ env: "GITLAB_CI",
43221
+ provider: "GitLab CI",
43222
+ isCI: true
43223
+ },
43224
+ {
43225
+ env: "CIRCLECI",
43226
+ provider: "CircleCI",
43227
+ isCI: true
43228
+ },
43229
+ {
43230
+ env: "JENKINS_URL",
43231
+ provider: "Jenkins",
43232
+ isCI: true
43233
+ },
43234
+ {
43235
+ env: "TRAVIS",
43236
+ provider: "Travis CI",
43237
+ isCI: true
43238
+ },
43239
+ {
43240
+ env: "BUILDKITE",
43241
+ provider: "Buildkite",
43242
+ isCI: true
43243
+ },
43244
+ {
43245
+ env: "NOW_BUILDER",
43246
+ provider: "Vercel",
43247
+ isCI: true
43248
+ },
43249
+ {
43250
+ env: "VERCEL",
43251
+ provider: "Vercel",
43252
+ isCI: false
43253
+ }
43254
+ ];
43255
+ const getCIEnvironment = () => {
43256
+ for (const p$1 of CI_PROVIDERS) if (process.env[p$1.env]) return {
43257
+ provider: p$1.provider,
43258
+ isCI: p$1.isCI
43259
+ };
43260
+ return {
43261
+ provider: "",
43262
+ isCI: !!process.env.CI
43263
+ };
43264
+ };
43265
+ const TELEMETRY_DISABLED_ENV = () => !!process.env.ALCHEMY_TELEMETRY_DISABLED || !!process.env.DO_NOT_TRACK || !!process.env.NO_TRACK;
43266
+ /**
43267
+ * `true` if telemetry is disabled via environment variable
43268
+ * (`DO_NOT_TRACK`, `NO_TRACK`, `ALCHEMY_TELEMETRY_DISABLED`) or via a
43269
+ * persisted opt-out file at `~/.alchemy/telemetry-disabled`.
43270
+ */
43271
+ const isTelemetryDisabled = Effect.gen(function* () {
43272
+ if (TELEMETRY_DISABLED_ENV()) return true;
43273
+ return (yield* tryRead(DISABLED_PATH)) === "true";
43274
+ });
43275
+ /**
43276
+ * Persists an opt-out so future invocations skip telemetry without needing
43277
+ * an env var.
43278
+ */
43279
+ const setTelemetryDisabled = Effect.tryPromise({
43280
+ try: async () => {
43281
+ await fs$1.mkdir(ALCHEMY_DIR, { recursive: true });
43282
+ await fs$1.writeFile(DISABLED_PATH, "true");
43283
+ },
43284
+ catch: () => null
43285
+ }).pipe(Effect.catch(() => Effect.void));
43286
+ const setTelemetryEnabled = Effect.tryPromise({
43287
+ try: () => fs$1.rm(DISABLED_PATH, { force: true }),
43288
+ catch: () => null
43289
+ }).pipe(Effect.catch(() => Effect.void));
43290
+ const collectAttributesUncached = Effect.gen(function* () {
43291
+ const [userId, rootCommit, originUrl, branch] = yield* Effect.all([
43292
+ getOrCreateUserId,
43293
+ getRootCommitHash,
43294
+ getGitOriginUrl,
43295
+ getBranchName
43296
+ ], { concurrency: "unbounded" });
43297
+ const runtime = getRuntime();
43298
+ const ci = getCIEnvironment();
43299
+ return {
43300
+ "alchemy.user.id": userId,
43301
+ "alchemy.session.id": process.env.ALCHEMY_TELEMETRY_SESSION_ID ?? crypto$2.randomUUID(),
43302
+ "alchemy.version": version,
43303
+ "alchemy.git.root_commit": rootCommit ?? "",
43304
+ "alchemy.git.origin_hash": hashStringOrEmpty(originUrl),
43305
+ "alchemy.git.branch_hash": hashStringOrEmpty(branch),
43306
+ "alchemy.runtime.name": runtime.name,
43307
+ "alchemy.runtime.version": runtime.version,
43308
+ "alchemy.ci.provider": ci.provider,
43309
+ "alchemy.ci": ci.isCI,
43310
+ "host.arch": os$1.arch(),
43311
+ "os.type": os$1.platform(),
43312
+ "os.version": os$1.release(),
43313
+ "host.cpus": os$1.cpus().length,
43314
+ "host.memory_mb": Math.round(os$1.totalmem() / 1024 / 1024)
43315
+ };
43316
+ });
43317
+ let cachedAttrs;
43318
+ /**
43319
+ * Resolves the {@link TelemetryAttributes} for the current process. Cached
43320
+ * so file I/O and `git` invocations only run once per CLI invocation; every
43321
+ * subsequent caller gets the same record (and therefore the same session
43322
+ * id).
43323
+ */
43324
+ const collectAttributes = Effect.suspend(() => cachedAttrs !== void 0 ? Effect.succeed(cachedAttrs) : collectAttributesUncached.pipe(Effect.tap((a) => Effect.sync(() => {
43325
+ cachedAttrs = a;
43326
+ }))));
43327
+
43328
+ //#endregion
43329
+ //#region src/Telemetry/Layer.ts
43330
+ const TRACES_URL = "https://otel.alchemy.run/v1/traces";
43331
+ const METRICS_URL = "https://otel.alchemy.run/v1/metrics";
43332
+ const SERVICE_NAME = "alchemy-cli";
43333
+ const buildOtlpLayer = (attrs) => {
43334
+ const resource = {
43335
+ serviceName: SERVICE_NAME,
43336
+ serviceVersion: version,
43337
+ attributes: attrs
43338
+ };
43339
+ const tracer = OtlpTracer.layer({
43340
+ url: TRACES_URL,
43341
+ resource,
43342
+ exportInterval: "1 second"
43343
+ });
43344
+ const metrics = OtlpMetrics.layer({
43345
+ url: METRICS_URL,
43346
+ resource,
43347
+ exportInterval: "1 second"
43348
+ });
43349
+ return Layer.mergeAll(tracer, metrics).pipe(Layer.provide(OtlpSerialization.layerJson), Layer.provide(FetchHttpClient.layer));
43350
+ };
43351
+ /**
43352
+ * The CLI's telemetry layer. Builds an OTLP HTTP exporter that ships spans
43353
+ * to {@link TRACES_URL} and metrics to {@link METRICS_URL}, attaching
43354
+ * {@link collectAttributes} as resource-level attributes so every signal
43355
+ * carries user/project/runtime context.
43356
+ *
43357
+ * If the user has opted out (via `DO_NOT_TRACK`, `NO_TRACK`,
43358
+ * `ALCHEMY_TELEMETRY_DISABLED`, or `~/.alchemy/telemetry-disabled`), this
43359
+ * resolves to {@link Layer.empty}. Effect's default `Tracer` is a no-op,
43360
+ * so all `withSpan`/`Effect.fn` instrumentation in core stays free.
43361
+ */
43362
+ const TelemetryLive = Layer.unwrap(Effect.gen(function* () {
43363
+ if (yield* isTelemetryDisabled) return Layer.empty;
43364
+ return buildOtlpLayer(yield* collectAttributes);
43365
+ }));
43366
+
43151
43367
  //#endregion
43152
43368
  //#region src/Util/PlatformServices.ts
43153
43369
  const isBun = typeof Bun !== "undefined";
@@ -43190,6 +43406,62 @@ const AuthProvider = () => (name, impl) => Effect.gen(function* () {
43190
43406
  */
43191
43407
  const AuthProviderLayer = () => (name, impl) => Layer.effectDiscard(AuthProvider()(name, impl));
43192
43408
 
43409
+ //#endregion
43410
+ //#region src/Telemetry/Metrics.ts
43411
+ /**
43412
+ * Counter for resource lifecycle operations. Tagged per call with
43413
+ * `resource_type`, `op` (`precreate`/`create`/`update`/`delete`/`read`),
43414
+ * and `status` (`success`/`error`).
43415
+ */
43416
+ const resourceCounter = Metric.counter("alchemy.resource.operations", {
43417
+ description: "Number of resource lifecycle operations dispatched by alchemy apply.",
43418
+ incremental: true
43419
+ });
43420
+ /**
43421
+ * Histogram of how long each lifecycle operation takes.
43422
+ */
43423
+ const resourceDuration = Metric.timer("alchemy.resource.duration", { description: "Wall-clock duration of resource lifecycle operations dispatched by alchemy apply." });
43424
+ /**
43425
+ * Counter for CLI command invocations.
43426
+ */
43427
+ const cliCounter = Metric.counter("alchemy.cli.invocations", {
43428
+ description: "Number of alchemy CLI command invocations.",
43429
+ incremental: true
43430
+ });
43431
+ /**
43432
+ * Wraps a resource lifecycle Effect to record a counter + timer entry,
43433
+ * tagged with `resource_type`, `op`, and `status` (`success`/`error`).
43434
+ *
43435
+ * Usage:
43436
+ * ```ts
43437
+ * provider.create(input).pipe(recordResourceOp(node.resource.Type, "create"))
43438
+ * ```
43439
+ */
43440
+ const recordResourceOp = (resourceType, op) => (self$1) => Effect.suspend(() => {
43441
+ const startNs = process.hrtime.bigint();
43442
+ const baseAttrs = {
43443
+ resource_type: resourceType,
43444
+ op
43445
+ };
43446
+ return self$1.pipe(Effect.onExit((exit) => recordOutcome(baseAttrs, Exit.isSuccess(exit) ? "success" : "error", elapsed(startNs))));
43447
+ });
43448
+ /**
43449
+ * Wraps a CLI command Effect to bump {@link cliCounter} with the
43450
+ * outcome (`success`/`error`).
43451
+ */
43452
+ const recordCli = (command) => (self$1) => self$1.pipe(Effect.onExit((exit) => Metric.update(Metric.withAttributes(cliCounter, {
43453
+ command,
43454
+ status: Exit.isSuccess(exit) ? "success" : "error"
43455
+ }), 1)));
43456
+ const elapsed = (startNs) => Duration.nanos(process.hrtime.bigint() - startNs);
43457
+ const recordOutcome = (baseAttrs, status, duration) => {
43458
+ const attrs = {
43459
+ ...baseAttrs,
43460
+ status
43461
+ };
43462
+ return Effect.all([Metric.update(Metric.withAttributes(resourceCounter, attrs), 1), Metric.update(Metric.withAttributes(resourceDuration, attrs), duration)], { discard: true });
43463
+ };
43464
+
43193
43465
  //#endregion
43194
43466
  //#region src/Util/Clank.ts
43195
43467
  var PromptCancelled = class extends Data.TaggedError("PromptCancelled") {};
@@ -43319,6 +43591,26 @@ const parseSince = (value) => {
43319
43591
  if (isNaN(parsed.getTime())) throw new Error(`Invalid --since value: '${value}'. Use a duration (e.g. '1h', '30m') or ISO date.`);
43320
43592
  return parsed;
43321
43593
  };
43594
+ /**
43595
+ * Wraps a CLI command handler with a top-level OpenTelemetry span
43596
+ * (`cli.<command>`) and bumps the `alchemy.cli.invocations` counter.
43597
+ *
43598
+ * `attrs` runs against the parsed command args and contributes
43599
+ * additional attributes to the span (e.g. stage, profile, dry-run flag).
43600
+ *
43601
+ * Usage:
43602
+ * ```ts
43603
+ * Command.make(
43604
+ * "deploy",
43605
+ * { ...flags },
43606
+ * instrumentCommand("deploy", (a) => ({
43607
+ * "alchemy.stage": a.stage,
43608
+ * "alchemy.profile": a.profile,
43609
+ * }))(execStack),
43610
+ * );
43611
+ * ```
43612
+ */
43613
+ const instrumentCommand = (command, attrs = () => ({})) => (handler) => (args) => handler(args).pipe(Effect.withSpan(`cli.${command}`, { attributes: attrs(args) }), recordCli(command));
43322
43614
  const importStack = Effect.fn(function* (main$1) {
43323
43615
  const path = yield* Path$1.Path;
43324
43616
  const stackEffect = (yield* Effect.promise(() => import(path.resolve(process.cwd(), main$1)))).default;
@@ -43560,10 +43852,10 @@ const loadConfigProvider = (envFile$1) => {
43560
43852
  //#region src/Util/FileLogger.ts
43561
43853
  const fileLogger = Effect.fnUntraced(function* (...segments) {
43562
43854
  const { dotAlchemy } = yield* AlchemyContext;
43563
- const fs$2 = yield* FileSystem.FileSystem;
43855
+ const fs$3 = yield* FileSystem.FileSystem;
43564
43856
  const path = yield* Path;
43565
43857
  const logFile = path.join(dotAlchemy, "log", ...segments);
43566
- yield* fs$2.makeDirectory(path.dirname(logFile), { recursive: true });
43858
+ yield* fs$3.makeDirectory(path.dirname(logFile), { recursive: true });
43567
43859
  return yield* Logger.formatLogFmt.pipe(Logger.toFile(logFile, { flag: "a" }));
43568
43860
  });
43569
43861
 
@@ -43577,7 +43869,11 @@ const bootstrapCommand = Command$1.make("bootstrap", {
43577
43869
  profile: awsProfile,
43578
43870
  region: awsRegion,
43579
43871
  destroy: bootstrapDestroy
43580
- }, Effect.fnUntraced(function* ({ envFile: envFile$1, profile: profile$1, region, destroy }) {
43872
+ }, instrumentCommand("bootstrap", (a) => ({
43873
+ "alchemy.profile": a.profile,
43874
+ "alchemy.region": a.region ?? "",
43875
+ "alchemy.destroy": a.destroy
43876
+ }))(Effect.fnUntraced(function* ({ envFile: envFile$1, profile: profile$1, region, destroy }) {
43581
43877
  const logger = Logger.layer([fileLogger("bootstrap.txt")]);
43582
43878
  return yield* Effect.gen(function* () {
43583
43879
  const ssoProfile = yield* Auth.loadProfile(profile$1);
@@ -43600,7 +43896,7 @@ const bootstrapCommand = Command$1.make("bootstrap", {
43600
43896
  yield* bootstrap().pipe(Effect.tap(({ bucketName, created }) => created ? Console.log(`✓ Created assets bucket: ${bucketName}`) : Console.log(`✓ Assets bucket already exists: ${bucketName}`)), Effect.provide(bootstrapLayer));
43601
43897
  });
43602
43898
  }).pipe(Effect.provide(logger));
43603
- }));
43899
+ })));
43604
43900
 
43605
43901
  //#endregion
43606
43902
  //#region src/Artifacts.ts
@@ -43914,7 +44210,7 @@ function proxy(self$1) {
43914
44210
  });
43915
44211
  const proxy$1 = new Proxy(target, {
43916
44212
  has: (_, prop) => prop === ExprSymbol || prop === inspect ? true : prop in self$1,
43917
- get: (target$1, prop) => prop === Symbol.toPrimitive ? (hint) => hint === "string" ? self$1.toString() : self$1 : prop === ExprSymbol ? self$1 : prop === inspect ? target$1[inspect] : isResourceExpr(self$1) && self$1.stables && prop in self$1.stables ? self$1.stables[prop] : prop === "apply" ? self$1[prop] : prop in self$1 ? typeof self$1[prop] === "function" && !("kind" in self$1) ? new PropExpr(proxy$1, prop) : self$1[prop] : new PropExpr(proxy$1, prop),
44213
+ get: (target$1, prop) => prop === Symbol.toPrimitive ? (hint) => hint === "number" ? NaN : self$1.toString() : prop === ExprSymbol ? self$1 : prop === inspect ? target$1[inspect] : isResourceExpr(self$1) && self$1.stables && prop in self$1.stables ? self$1.stables[prop] : prop === "apply" ? self$1[prop] : prop in self$1 ? typeof self$1[prop] === "function" && !("kind" in self$1) ? new PropExpr(proxy$1, prop) : self$1[prop] : new PropExpr(proxy$1, prop),
43918
44214
  apply: (_, thisArg, args) => {
43919
44215
  if (isPropExpr(self$1)) {
43920
44216
  if (self$1.identifier === "apply") return new ApplyExpr(self$1.expr, args[0]);
@@ -44086,7 +44382,7 @@ const reviveState = (_key, value) => {
44086
44382
  //#region src/State/LocalState.ts
44087
44383
  const localState = () => Layer.effect(State, makeLocalState());
44088
44384
  const makeLocalState = () => Effect.gen(function* () {
44089
- const fs$2 = yield* FileSystem.FileSystem;
44385
+ const fs$3 = yield* FileSystem.FileSystem;
44090
44386
  const path = yield* Path$1.Path;
44091
44387
  const dotAlchemy = path.join(process.cwd(), ".alchemy");
44092
44388
  const stateDir = path.join(dotAlchemy, "state");
@@ -44098,11 +44394,11 @@ const makeLocalState = () => Effect.gen(function* () {
44098
44394
  const stageDir = ({ stack, stage: stage$1 }) => path.join(stateDir, stack, stage$1);
44099
44395
  const resource = ({ stack, stage: stage$1, fqn }) => path.join(stateDir, stack, stage$1, `${encodeFqn(fqn)}.json`);
44100
44396
  const created = /* @__PURE__ */ new Set();
44101
- const ensure = (dir) => created.has(dir) ? Effect.succeed(void 0) : fs$2.makeDirectory(dir, { recursive: true }).pipe(Effect.tap(() => Effect.sync(() => created.add(dir))));
44397
+ const ensure = (dir) => created.has(dir) ? Effect.succeed(void 0) : fs$3.makeDirectory(dir, { recursive: true }).pipe(Effect.tap(() => Effect.sync(() => created.add(dir))));
44102
44398
  const state = {
44103
- listStacks: () => fs$2.readDirectory(stateDir).pipe(recover, Effect.map((files) => files ?? [])),
44104
- listStages: (stack) => fs$2.readDirectory(path.join(stateDir, stack)).pipe(recover, Effect.map((files) => files ?? [])),
44105
- get: (request) => fs$2.readFile(resource(request)).pipe(Effect.map((file) => JSON.parse(file.toString(), reviveState)), recover),
44399
+ listStacks: () => fs$3.readDirectory(stateDir).pipe(recover, Effect.map((files) => files ?? [])),
44400
+ listStages: (stack) => fs$3.readDirectory(path.join(stateDir, stack)).pipe(recover, Effect.map((files) => files ?? [])),
44401
+ get: (request) => fs$3.readFile(resource(request)).pipe(Effect.map((file) => JSON.parse(file.toString(), reviveState)), recover),
44106
44402
  getReplacedResources: Effect.fnUntraced(function* (request) {
44107
44403
  return (yield* Effect.all((yield* state.list(request)).map((fqn) => state.get({
44108
44404
  stack: request.stack,
@@ -44110,13 +44406,13 @@ const makeLocalState = () => Effect.gen(function* () {
44110
44406
  fqn
44111
44407
  })))).filter((r) => r?.status === "replaced");
44112
44408
  }),
44113
- set: (request) => ensure(stageDir(request)).pipe(Effect.flatMap(() => fs$2.writeFileString(resource(request), JSON.stringify(encodeState(request.value), null, 2))), recover, Effect.map(() => request.value)),
44114
- delete: (request) => fs$2.remove(resource(request)).pipe(recover),
44115
- deleteStack: ({ stack, stage: stage$1 }) => fs$2.remove(stage$1 === void 0 ? path.join(stateDir, stack) : stageDir({
44409
+ set: (request) => ensure(stageDir(request)).pipe(Effect.flatMap(() => fs$3.writeFileString(resource(request), JSON.stringify(encodeState(request.value), null, 2))), recover, Effect.map(() => request.value)),
44410
+ delete: (request) => fs$3.remove(resource(request)).pipe(recover),
44411
+ deleteStack: ({ stack, stage: stage$1 }) => fs$3.remove(stage$1 === void 0 ? path.join(stateDir, stack) : stageDir({
44116
44412
  stack,
44117
44413
  stage: stage$1
44118
44414
  }), { recursive: true }).pipe(recover),
44119
- list: (request) => fs$2.readDirectory(stageDir(request)).pipe(recover, Effect.map((files) => files?.map((file) => decodeFqn(file.replace(/\.json$/, ""))) ?? []))
44415
+ list: (request) => fs$3.readDirectory(stageDir(request)).pipe(recover, Effect.map((files) => files?.map((file) => decodeFqn(file.replace(/\.json$/, ""))) ?? []))
44120
44416
  };
44121
44417
  return state;
44122
44418
  });
@@ -44124,6 +44420,23 @@ const makeLocalState = () => Effect.gen(function* () {
44124
44420
  //#endregion
44125
44421
  //#region src/Apply.ts
44126
44422
  const provideLifecycleScope = (fqn, instanceId) => (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrElse(createArtifactStore)), Effect.flatMap((store) => effect.pipe(Effect.provideService(Artifacts, makeScopedArtifacts(store, fqn)), Effect.provideService(InstanceId, instanceId))));
44423
+ /**
44424
+ * Instruments a single provider lifecycle call with an OTel span
44425
+ * (`provider.<op>`), the resource counter / duration histogram, and the
44426
+ * scoped artifacts/instance services normally supplied by
44427
+ * {@link provideLifecycleScope}.
44428
+ *
44429
+ * This is the only call site through which provider lifecycle methods
44430
+ * are dispatched, so wrapping it here gives us a fully-instrumented
44431
+ * toolchain without touching any individual provider implementation.
44432
+ */
44433
+ const instrumentLifecycle = (op, fqn, resourceType, logicalId, instanceId) => (effect) => effect.pipe(provideLifecycleScope(fqn, instanceId), recordResourceOp(resourceType, op), Effect.withSpan(`provider.${op}`, { attributes: {
44434
+ "alchemy.resource.fqn": fqn,
44435
+ "alchemy.resource.type": resourceType,
44436
+ "alchemy.resource.logical_id": logicalId,
44437
+ "alchemy.resource.instance_id": instanceId,
44438
+ "alchemy.resource.op": op
44439
+ } }));
44127
44440
  const apply = (plan) => Effect.gen(function* () {
44128
44441
  const session = yield* (yield* Cli).startApplySession(plan);
44129
44442
  const state = yield* State;
@@ -44145,7 +44458,10 @@ const apply = (plan) => Effect.gen(function* () {
44145
44458
  if (!plan.output) return;
44146
44459
  const outputs = Object.fromEntries(Object.entries(tracker).map(([fqn, t]) => [fqn, t.output]));
44147
44460
  return yield* evaluate(plan.output, outputs);
44148
- }).pipe(ensureArtifactStore);
44461
+ }).pipe(ensureArtifactStore, Effect.withSpan("apply", { attributes: {
44462
+ "alchemy.resources.count": Object.keys(plan.resources).length,
44463
+ "alchemy.deletions.count": Object.keys(plan.deletions).length
44464
+ } }));
44149
44465
  const executePlan = Effect.fnUntraced(function* (plan, tracker, terminalStatuses, session, state, stackName, stage$1) {
44150
44466
  const ready = Object.fromEntries(yield* Effect.all(Object.keys(plan.resources).map((fqn) => Effect.map(Deferred.make(), (d) => [fqn, d]))));
44151
44467
  const getOutputs = () => Object.fromEntries(Object.entries(tracker).map(([fqn, t]) => [fqn, t.output]));
@@ -44271,7 +44587,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
44271
44587
  session: scopedSession,
44272
44588
  instanceId,
44273
44589
  bindings: excludeDeletedBindings(node.bindings)
44274
- }).pipe(provideLifecycleScope(fqn, instanceId));
44590
+ }).pipe(instrumentLifecycle("precreate", fqn, node.resource.Type, logicalId, instanceId));
44275
44591
  yield* commit({
44276
44592
  status: "creating",
44277
44593
  fqn,
@@ -44304,7 +44620,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
44304
44620
  bindings: bindingOutputs,
44305
44621
  session: scopedSession,
44306
44622
  output: attr
44307
- }).pipe(provideLifecycleScope(fqn, instanceId));
44623
+ }).pipe(instrumentLifecycle("create", fqn, node.resource.Type, logicalId, instanceId));
44308
44624
  yield* commit({
44309
44625
  status: "created",
44310
44626
  fqn,
@@ -44367,7 +44683,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
44367
44683
  session: scopedSession,
44368
44684
  olds: previousProps,
44369
44685
  output: node.state.attr
44370
- }).pipe(provideLifecycleScope(fqn, instanceId));
44686
+ }).pipe(instrumentLifecycle("update", fqn, node.resource.Type, logicalId, instanceId));
44371
44687
  if (node.state.status === "replaced") yield* commit({
44372
44688
  ...node.state,
44373
44689
  attr,
@@ -44439,7 +44755,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
44439
44755
  session: scopedSession,
44440
44756
  instanceId,
44441
44757
  bindings: excludeDeletedBindings(node.bindings)
44442
- }).pipe(provideLifecycleScope(fqn, instanceId));
44758
+ }).pipe(instrumentLifecycle("precreate", fqn, node.resource.Type, logicalId, instanceId));
44443
44759
  yield* commit({
44444
44760
  status: "replacing",
44445
44761
  fqn,
@@ -44474,7 +44790,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
44474
44790
  bindings: bindingOutputs,
44475
44791
  session: scopedSession,
44476
44792
  output: attr
44477
- }).pipe(provideLifecycleScope(fqn, instanceId));
44793
+ }).pipe(instrumentLifecycle("create", fqn, node.resource.Type, logicalId, instanceId));
44478
44794
  yield* commit({
44479
44795
  status: "replaced",
44480
44796
  fqn,
@@ -44502,7 +44818,12 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
44502
44818
  }
44503
44819
  return yield* Effect.die(`Unknown action: ${node.action}`);
44504
44820
  });
44505
- });
44821
+ }).pipe(Effect.withSpan("apply.resource", { attributes: {
44822
+ "alchemy.resource.fqn": fqn,
44823
+ "alchemy.resource.type": node.resource.Type,
44824
+ "alchemy.resource.logical_id": node.resource.LogicalId,
44825
+ "alchemy.resource.action": node.action
44826
+ } }));
44506
44827
  const converge = Effect.fnUntraced(function* (plan, tracker, terminalStatuses, session, state, stackName, stage$1) {
44507
44828
  for (;;) {
44508
44829
  let anyUpdated = false;
@@ -44537,7 +44858,7 @@ const converge = Effect.fnUntraced(function* (plan, tracker, terminalStatuses, s
44537
44858
  session: scopedSession,
44538
44859
  olds: oldProps,
44539
44860
  output: tracker[fqn].output
44540
- }).pipe(provideLifecycleScope(fqn, instanceId));
44861
+ }).pipe(instrumentLifecycle("update", fqn, node.resource.Type, logicalId, instanceId));
44541
44862
  tracker[fqn] = {
44542
44863
  output: attr,
44543
44864
  props: newProps,
@@ -44658,7 +44979,7 @@ const collectGarbage = Effect.fnUntraced(function* (plan, session) {
44658
44979
  output: attr,
44659
44980
  session: scopedSession,
44660
44981
  bindings: []
44661
- }).pipe(provideLifecycleScope(fqn, instanceId));
44982
+ }).pipe(instrumentLifecycle("delete", fqn, resourceType, logicalId, instanceId));
44662
44983
  if (isDeleteNode(node)) {
44663
44984
  yield* state.delete({
44664
44985
  stack: stackName,
@@ -45185,7 +45506,7 @@ const emptyConfig = () => ({
45185
45506
  version: CONFIG_VERSION,
45186
45507
  profiles: {}
45187
45508
  });
45188
- const readConfig = FileSystem.FileSystem.asEffect().pipe(Effect.flatMap((fs$2) => fs$2.readFileString(configFilePath)), Effect.flatMap((data) => Effect.try({
45509
+ const readConfig = FileSystem.FileSystem.asEffect().pipe(Effect.flatMap((fs$3) => fs$3.readFileString(configFilePath)), Effect.flatMap((data) => Effect.try({
45189
45510
  try: () => {
45190
45511
  const parsed = JSON.parse(data);
45191
45512
  if (parsed?.version !== CONFIG_VERSION) return emptyConfig();
@@ -45193,7 +45514,7 @@ const readConfig = FileSystem.FileSystem.asEffect().pipe(Effect.flatMap((fs$2) =
45193
45514
  },
45194
45515
  catch: emptyConfig
45195
45516
  })), Effect.orElseSucceed(emptyConfig));
45196
- const writeConfig = (config) => FileSystem.FileSystem.asEffect().pipe(Effect.tap((fs$2) => fs$2.makeDirectory(posix.dirname(configFilePath), { recursive: true })), Effect.flatMap((fs$2) => fs$2.writeFileString(configFilePath, JSON.stringify(config, null, 2))));
45517
+ const writeConfig = (config) => FileSystem.FileSystem.asEffect().pipe(Effect.tap((fs$3) => fs$3.makeDirectory(posix.dirname(configFilePath), { recursive: true })), Effect.flatMap((fs$3) => fs$3.writeFileString(configFilePath, JSON.stringify(config, null, 2))));
45197
45518
  const getProfile = (name) => readConfig.pipe(Effect.map((config) => config.profiles[name]));
45198
45519
  const setProfile = (name, profile$1) => readConfig.pipe(Effect.tap((config) => Effect.sync(() => config.profiles[name] = profile$1)), Effect.flatMap(writeConfig));
45199
45520
  /**
@@ -45311,7 +45632,7 @@ const make = (stack, options$2 = {}) => Effect.gen(function* () {
45311
45632
  return [fqn, (rawUpstreamDependencies[fqn] ?? []).filter((dep) => newResourceFqns.has(dep))];
45312
45633
  }));
45313
45634
  const newDownstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.FQN, Object.entries(newUpstreamDependencies).filter(([_, upstream$1]) => upstream$1.includes(resource.FQN)).map(([depFqn]) => depFqn)]));
45314
- const resourceGraph = Object.fromEntries((yield* Effect.all(resources.map(Effect.fn(function* (resource) {
45635
+ const resourceGraph = Object.fromEntries((yield* Effect.all(resources.map(Effect.fn("plan.diff.resource")(function* (resource) {
45315
45636
  const provider = yield* findProviderByType(resource.Type);
45316
45637
  const id = resource.LogicalId;
45317
45638
  const fqn = resource.FQN;
@@ -45484,7 +45805,7 @@ const make = (stack, options$2 = {}) => Effect.gen(function* () {
45484
45805
  const deletions = Object.fromEntries((yield* Effect.all((yield* state.list({
45485
45806
  stack: stackName,
45486
45807
  stage: stage$1
45487
- })).map(Effect.fn(function* (fqn) {
45808
+ })).map(Effect.fn("plan.diff.deletion")(function* (fqn) {
45488
45809
  if (newResourceFqns.has(fqn)) return;
45489
45810
  const oldState = yield* state.get({
45490
45811
  stack: stackName,
@@ -45546,13 +45867,26 @@ const make = (stack, options$2 = {}) => Effect.gen(function* () {
45546
45867
  deletions,
45547
45868
  output: stack.output
45548
45869
  };
45549
- }).pipe(ensureArtifactStore);
45870
+ }).pipe(ensureArtifactStore, Effect.withSpan("plan.make", { attributes: {
45871
+ "alchemy.stack": stack.name,
45872
+ "alchemy.stage": stack.stage,
45873
+ "alchemy.resources.count": Object.keys(stack.resources).length,
45874
+ "alchemy.force": !!options$2.force
45875
+ } }));
45550
45876
  const providePlanScope = (fqn, instanceId) => (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrElse(createArtifactStore)), Effect.flatMap((store) => effect.pipe(Effect.provideService(Artifacts, makeScopedArtifacts(store, fqn)), Effect.provideService(InstanceId, instanceId))));
45551
45877
  var DeleteResourceHasDownstreamDependencies = class extends Data.TaggedError("DeleteResourceHasDownstreamDependencies") {};
45552
45878
  var UnsatisfiedResourceCycle = class extends Data.TaggedError("UnsatisfiedResourceCycle") {};
45553
45879
 
45554
45880
  //#endregion
45555
45881
  //#region bin/commands/deploy.ts
45882
+ const stackSpanAttrs = (args) => ({
45883
+ "alchemy.stage": args.stage,
45884
+ "alchemy.profile": args.profile,
45885
+ "alchemy.main": args.main,
45886
+ "alchemy.dry_run": !!args.dryRun,
45887
+ "alchemy.force": !!args.force,
45888
+ "alchemy.destroy": !!args.destroy
45889
+ });
45556
45890
  const execStack = Effect.fn(function* ({ main: main$1, stage: stage$1, envFile: envFile$1, profile: profile$1, dryRun: dryRun$1 = false, force: force$1 = false, yes: yes$1 = false, destroy = false }) {
45557
45891
  const stackEffect = yield* importStack(main$1);
45558
45892
  const services$1 = Layer.mergeAll(Layer.succeed(ArtifactStore, createArtifactStore()), Layer.succeed(AuthProviders, yield* Effect.serviceOption(AuthProviders).pipe(Effect.map(Option.getOrElse(() => ({}))))), ConfigProvider.layer(withProfileOverride(yield* loadConfigProvider(envFile$1), profile$1)), Logger.layer([fileLogger("out")]), Layer.succeed(Stage, stage$1));
@@ -45586,7 +45920,7 @@ const deployCommand = Command$1.make("deploy", {
45586
45920
  stage,
45587
45921
  yes,
45588
45922
  profile
45589
- }, execStack);
45923
+ }, instrumentCommand("deploy", stackSpanAttrs)(execStack));
45590
45924
  const destroyCommand = Command$1.make("destroy", {
45591
45925
  dryRun,
45592
45926
  main,
@@ -45594,19 +45928,19 @@ const destroyCommand = Command$1.make("destroy", {
45594
45928
  stage,
45595
45929
  yes,
45596
45930
  profile
45597
- }, (args) => execStack({
45931
+ }, instrumentCommand("destroy", stackSpanAttrs)((args) => execStack({
45598
45932
  ...args,
45599
45933
  destroy: true
45600
- }));
45934
+ })));
45601
45935
  const planCommand = Command$1.make("plan", {
45602
45936
  main,
45603
45937
  envFile,
45604
45938
  stage,
45605
45939
  profile
45606
- }, (args) => execStack({
45940
+ }, instrumentCommand("plan", stackSpanAttrs)((args) => execStack({
45607
45941
  ...args,
45608
45942
  dryRun: true
45609
- }));
45943
+ })));
45610
45944
 
45611
45945
  //#endregion
45612
45946
  //#region bin/commands/login.ts
@@ -45617,7 +45951,12 @@ const loginCommand = Command.make("login", {
45617
45951
  stage,
45618
45952
  profile,
45619
45953
  configure: loginConfigure
45620
- }, Effect.fnUntraced(function* ({ main: main$1, stage: stage$1, envFile: envFile$1, profile: profile$1, configure }) {
45954
+ }, instrumentCommand("login", (a) => ({
45955
+ "alchemy.stage": a.stage,
45956
+ "alchemy.profile": a.profile,
45957
+ "alchemy.main": a.main,
45958
+ "alchemy.configure": a.configure
45959
+ }))(Effect.fnUntraced(function* ({ main: main$1, stage: stage$1, envFile: envFile$1, profile: profile$1, configure }) {
45621
45960
  const stackEffect = yield* importStack(main$1);
45622
45961
  const authProviders = {};
45623
45962
  const services$1 = Layer.mergeAll(Layer.succeed(AuthProviders, authProviders), ConfigProvider.layer(withProfileOverride(yield* loadConfigProvider(envFile$1), profile$1)), Logger.layer([fileLogger("out")]), Layer.succeed(Stage, stage$1), localState());
@@ -45644,7 +45983,7 @@ const loginCommand = Command.make("login", {
45644
45983
  yield* provider.prettyPrint(profile$1, cfg);
45645
45984
  }), { discard: true });
45646
45985
  }).pipe(Effect.provide(services$1));
45647
- }));
45986
+ })));
45648
45987
 
45649
45988
  //#endregion
45650
45989
  //#region bin/commands/logs.ts
@@ -45658,7 +45997,12 @@ const logsCommand = Command.make("logs", {
45658
45997
  filter: resourceFilter,
45659
45998
  limit: logsLimit,
45660
45999
  since: logsSince
45661
- }, Effect.fnUntraced(function* ({ main: main$1, stage: stage$1, envFile: envFile$1, profile: profile$1, filter, limit, since }) {
46000
+ }, instrumentCommand("logs", (a) => ({
46001
+ "alchemy.stage": a.stage,
46002
+ "alchemy.profile": a.profile,
46003
+ "alchemy.main": a.main,
46004
+ "alchemy.limit": a.limit
46005
+ }))(Effect.fnUntraced(function* ({ main: main$1, stage: stage$1, envFile: envFile$1, profile: profile$1, filter, limit, since }) {
45662
46006
  const stackEffect = yield* importStack(main$1);
45663
46007
  const services$1 = Layer.mergeAll(ConfigProvider.layer(withProfileOverride(yield* loadConfigProvider(envFile$1), profile$1)), Layer.succeed(AuthProviders, {}), Layer.succeed(Stage, stage$1), Logger.layer([fileLogger("out")]), localState());
45664
46008
  const sinceDate = since ? parseSince(since) : void 0;
@@ -45714,7 +46058,7 @@ const logsCommand = Command.make("logs", {
45714
46058
  for (const entry of merged) yield* Console.log(entry.formatted);
45715
46059
  }).pipe(Effect.provide(stack.services));
45716
46060
  }).pipe(Effect.provide(services$1));
45717
- }));
46061
+ })));
45718
46062
 
45719
46063
  //#endregion
45720
46064
  //#region src/Auth/Credentials.ts
@@ -45731,10 +46075,10 @@ const readCredentials = Effect.fnUntraced(function* (profile$1, provider) {
45731
46075
  }
45732
46076
  });
45733
46077
  const writeCredentials = Effect.fnUntraced(function* (profile$1, provider, credentials) {
45734
- const fs$2 = yield* FileSystem.FileSystem;
46078
+ const fs$3 = yield* FileSystem.FileSystem;
45735
46079
  const filePath = credentialsFilePath(profile$1, provider);
45736
- yield* fs$2.makeDirectory(posix.dirname(filePath), { recursive: true });
45737
- yield* fs$2.writeFileString(filePath, JSON.stringify(credentials, null, 2));
46080
+ yield* fs$3.makeDirectory(posix.dirname(filePath), { recursive: true });
46081
+ yield* fs$3.writeFileString(filePath, JSON.stringify(credentials, null, 2));
45738
46082
  });
45739
46083
  const deleteCredentials = Effect.fnUntraced(function* (profile$1, provider) {
45740
46084
  yield* (yield* FileSystem.FileSystem).remove(credentialsFilePath(profile$1, provider)).pipe(Effect.catch(() => Effect.void));
@@ -45864,11 +46208,11 @@ const prettyPrint$1 = (profileName, config) => resolveCredentials$1(profileName,
45864
46208
  const clearDistilledSsoCache = (ssoProfile) => Effect.gen(function* () {
45865
46209
  const ssoSession = (yield* (yield* Auth.Default).loadProfile(ssoProfile)).sso_session;
45866
46210
  if (!ssoSession) return;
45867
- const fs$2 = yield* FileSystem.FileSystem;
46211
+ const fs$3 = yield* FileSystem.FileSystem;
45868
46212
  const path = yield* Path$1.Path;
45869
- const hash = NodeCrypto.createHash("sha1").update(ssoSession).digest("hex");
45870
- const cacheFile = path.join(NodeOs.homedir(), ".aws", "sso", "cache", `${hash}.credentials.json`);
45871
- yield* fs$2.remove(cacheFile).pipe(Effect.catch(() => Effect.void));
46213
+ const hash = crypto$2.createHash("sha1").update(ssoSession).digest("hex");
46214
+ const cacheFile = path.join(os$1.homedir(), ".aws", "sso", "cache", `${hash}.credentials.json`);
46215
+ yield* fs$3.remove(cacheFile).pipe(Effect.catch(() => Effect.void));
45872
46216
  }).pipe(Effect.catch(() => Effect.void));
45873
46217
  const logout$1 = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "sso" }, (config$1) => info(`AWS: running 'aws sso logout --profile ${config$1.ssoProfile}'...`).pipe(Effect.zip(runSsoCommand("logout", config$1.ssoProfile)), Effect.zip(clearDistilledSsoCache(config$1.ssoProfile)), Effect.match({
45874
46218
  onSuccess: () => success("AWS: SSO logout complete"),
@@ -46495,7 +46839,7 @@ const OAUTH_ENDPOINTS = {
46495
46839
  const showCommand = Command.make("show", {
46496
46840
  profile,
46497
46841
  envFile
46498
- }, Effect.fnUntraced(function* ({ profile: profile$1, envFile: envFile$1 }) {
46842
+ }, instrumentCommand("profile.show", (a) => ({ "alchemy.profile": a.profile }))(Effect.fnUntraced(function* ({ profile: profile$1, envFile: envFile$1 }) {
46499
46843
  const stored = yield* getProfile(profile$1);
46500
46844
  if (stored == null) {
46501
46845
  const config = yield* readConfig;
@@ -46529,13 +46873,13 @@ const showCommand = Command.make("show", {
46529
46873
  yield* provider.prettyPrint(profile$1, cfg);
46530
46874
  }
46531
46875
  }).pipe(Effect.provide(services$1));
46532
- }));
46533
- const clearCommand$1 = Command.make("clear", { profile }, Effect.fnUntraced(function* ({ profile: profile$1 }) {
46876
+ })));
46877
+ const clearCommand$1 = Command.make("clear", { profile }, instrumentCommand("profile.clear", (a) => ({ "alchemy.profile": a.profile }))(Effect.fnUntraced(function* ({ profile: profile$1 }) {
46534
46878
  const removed = yield* deleteProfile(profile$1);
46535
46879
  yield* deleteProfileCredentials(profile$1);
46536
46880
  if (removed) yield* Console.log(`Cleared profile '${profile$1}' and all its credentials.`);
46537
46881
  else yield* Console.log(`Profile '${profile$1}' not found in profiles.json; removed any stray credentials directory.`);
46538
- }));
46882
+ })));
46539
46883
  const profileCommand = Command.make("profile", {}).pipe(Command.withSubcommands([showCommand, clearCommand$1]));
46540
46884
 
46541
46885
  //#endregion
@@ -46574,7 +46918,7 @@ const stacksCommand = Command.make("stacks", {
46574
46918
  stage,
46575
46919
  profile,
46576
46920
  local: localFlag
46577
- }, Effect.fnUntraced(function* (args) {
46921
+ }, instrumentCommand("state.stacks")(Effect.fnUntraced(function* (args) {
46578
46922
  yield* withStateService(args, (state) => Effect.gen(function* () {
46579
46923
  const stacks = yield* state.listStacks();
46580
46924
  if (stacks.length === 0) {
@@ -46583,7 +46927,7 @@ const stacksCommand = Command.make("stacks", {
46583
46927
  }
46584
46928
  for (const s of [...stacks].sort()) yield* Console.log(s);
46585
46929
  }));
46586
- }));
46930
+ })));
46587
46931
  const stagesCommand = Command.make("stages", {
46588
46932
  stack: stackArg,
46589
46933
  main,
@@ -46591,7 +46935,7 @@ const stagesCommand = Command.make("stages", {
46591
46935
  stage,
46592
46936
  profile,
46593
46937
  local: localFlag
46594
- }, Effect.fnUntraced(function* ({ stack: stackName,...rest }) {
46938
+ }, instrumentCommand("state.stages")(Effect.fnUntraced(function* ({ stack: stackName,...rest }) {
46595
46939
  yield* withStateService(rest, (state) => Effect.gen(function* () {
46596
46940
  const stages = yield* state.listStages(stackName);
46597
46941
  if (stages.length === 0) {
@@ -46600,7 +46944,7 @@ const stagesCommand = Command.make("stages", {
46600
46944
  }
46601
46945
  for (const s of [...stages].sort()) yield* Console.log(s);
46602
46946
  }));
46603
- }));
46947
+ })));
46604
46948
  const resourcesCommand = Command.make("resources", {
46605
46949
  stack: stackArg,
46606
46950
  stageName: Argument.string("stage").pipe(Argument.withDescription("Stage to list resources from")),
@@ -46609,7 +46953,7 @@ const resourcesCommand = Command.make("resources", {
46609
46953
  stage,
46610
46954
  profile,
46611
46955
  local: localFlag
46612
- }, Effect.fnUntraced(function* ({ stack: stackName, stageName,...rest }) {
46956
+ }, instrumentCommand("state.resources")(Effect.fnUntraced(function* ({ stack: stackName, stageName,...rest }) {
46613
46957
  yield* withStateService(rest, (state) => Effect.gen(function* () {
46614
46958
  const fqns = yield* state.list({
46615
46959
  stack: stackName,
@@ -46621,7 +46965,7 @@ const resourcesCommand = Command.make("resources", {
46621
46965
  }
46622
46966
  for (const f of [...fqns].sort()) yield* Console.log(f);
46623
46967
  }));
46624
- }));
46968
+ })));
46625
46969
  const getCommand = Command.make("get", {
46626
46970
  stack: stackArg,
46627
46971
  stageName: Argument.string("stage").pipe(Argument.withDescription("Stage the resource lives in")),
@@ -46631,7 +46975,7 @@ const getCommand = Command.make("get", {
46631
46975
  stage,
46632
46976
  profile,
46633
46977
  local: localFlag
46634
- }, Effect.fnUntraced(function* ({ stack: stackName, stageName, fqn,...rest }) {
46978
+ }, instrumentCommand("state.get")(Effect.fnUntraced(function* ({ stack: stackName, stageName, fqn,...rest }) {
46635
46979
  yield* withStateService(rest, (state) => Effect.gen(function* () {
46636
46980
  const value = yield* state.get({
46637
46981
  stack: stackName,
@@ -46644,14 +46988,14 @@ const getCommand = Command.make("get", {
46644
46988
  }
46645
46989
  yield* Console.log(JSON.stringify(encodeState(value), null, 2));
46646
46990
  }));
46647
- }));
46991
+ })));
46648
46992
  const treeCommand = Command.make("tree", {
46649
46993
  main,
46650
46994
  envFile,
46651
46995
  stage,
46652
46996
  profile,
46653
46997
  local: localFlag
46654
- }, Effect.fnUntraced(function* (args) {
46998
+ }, instrumentCommand("state.tree")(Effect.fnUntraced(function* (args) {
46655
46999
  yield* withStateService(args, (state) => Effect.gen(function* () {
46656
47000
  const stacks = [...yield* state.listStacks()].sort();
46657
47001
  if (stacks.length === 0) {
@@ -46678,7 +47022,7 @@ const treeCommand = Command.make("tree", {
46678
47022
  }
46679
47023
  }
46680
47024
  }));
46681
- }));
47025
+ })));
46682
47026
  const clearStackArg = Argument.string("stack").pipe(Argument.withDescription("Stack name to clear. Omit to clear ALL stacks in the store."), Argument.optional);
46683
47027
  const clearStageArg = Argument.string("stage").pipe(Argument.withDescription("Stage to clear within the stack. Omit to clear all stages in the stack."), Argument.optional);
46684
47028
  /**
@@ -46695,7 +47039,7 @@ const clearCommand = Command.make("clear", {
46695
47039
  profile,
46696
47040
  local: localFlag,
46697
47041
  yes
46698
- }, Effect.fnUntraced(function* ({ stack: stackOpt, stageName: stageOpt, yes: yesFlag,...rest }) {
47042
+ }, instrumentCommand("state.clear")(Effect.fnUntraced(function* ({ stack: stackOpt, stageName: stageOpt, yes: yesFlag,...rest }) {
46699
47043
  const stackName = Option.getOrUndefined(stackOpt);
46700
47044
  const stageName = Option.getOrUndefined(stageOpt);
46701
47045
  if (stageName !== void 0 && stackName === void 0) {
@@ -46726,7 +47070,7 @@ const clearCommand = Command.make("clear", {
46726
47070
  yield* Console.log(`cleared ${target.stack}${target.stage ? `/${target.stage}` : ""}`);
46727
47071
  }
46728
47072
  }));
46729
- }));
47073
+ })));
46730
47074
  const stateCommand = Command.make("state", {}).pipe(Command.withSubcommands([
46731
47075
  stacksCommand,
46732
47076
  stagesCommand,
@@ -46744,7 +47088,11 @@ const tailCommand = Command$1.make("tail", {
46744
47088
  stage,
46745
47089
  profile,
46746
47090
  filter: resourceFilter
46747
- }, Effect.fnUntraced(function* ({ main: main$1, stage: stage$1, envFile: envFile$1, profile: profile$1, filter }) {
47091
+ }, instrumentCommand("tail", (a) => ({
47092
+ "alchemy.stage": a.stage,
47093
+ "alchemy.profile": a.profile,
47094
+ "alchemy.main": a.main
47095
+ }))(Effect.fnUntraced(function* ({ main: main$1, stage: stage$1, envFile: envFile$1, profile: profile$1, filter }) {
46748
47096
  const stackEffect = yield* importStack(main$1);
46749
47097
  const services$1 = Layer.mergeAll(ConfigProvider.layer(withProfileOverride(yield* loadConfigProvider(envFile$1), profile$1)), Layer.succeed(AuthProviders, {}), Layer.succeed(Stage, stage$1), Logger.layer([fileLogger("out")]), localState());
46750
47098
  yield* Effect.gen(function* () {
@@ -46794,7 +47142,7 @@ const tailCommand = Command$1.make("tail", {
46794
47142
  yield* Stream$1.mergeAll(taggedStreams, { concurrency: "unbounded" }).pipe(Stream$1.runForEach((line) => Console.log(line)));
46795
47143
  }).pipe(Effect.provide(stack.services));
46796
47144
  }).pipe(Effect.provide(services$1));
46797
- }));
47145
+ })));
46798
47146
 
46799
47147
  //#endregion
46800
47148
  //#region bin/alchemy.ts
@@ -46810,7 +47158,7 @@ const root = Command$1.make("alchemy", {}).pipe(Command$1.withSubcommands([
46810
47158
  stateCommand
46811
47159
  ]));
46812
47160
  const cli = Command$1.run(root, { version });
46813
- const services = Layer.mergeAll(Layer.provideMerge(AlchemyContextLive, PlatformServices), FetchHttpClient.layer, ConfigProvider.layer(ConfigProvider.fromEnv()), inkCLI());
47161
+ const services = Layer.mergeAll(Layer.provideMerge(AlchemyContextLive, PlatformServices), FetchHttpClient.layer, ConfigProvider.layer(ConfigProvider.fromEnv()), TelemetryLive, inkCLI());
46814
47162
  cli.pipe(Effect.provide(services), Effect.scoped, handleCancellation, runMain);
46815
47163
 
46816
47164
  //#endregion