autotel-cli 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # autotel-cli
2
2
 
3
- CLI for autotel — setup wizard, diagnostics, and incremental features.
3
+ CLI for autotel. Setup wizard, diagnostics, and incremental features.
4
4
 
5
5
  Designed for **both humans and AI agents**: every command supports `--json`,
6
6
  errors are returned as a structured envelope with stable `AUTOTEL_E_*` codes,
@@ -46,34 +46,35 @@ individually.
46
46
 
47
47
  **Options:**
48
48
 
49
- | Flag | What it does |
50
- | --- | --- |
51
- | `--yes, -y` | Auto-apply detected items; no prompts |
52
- | `--preset <name>` | Use a named quick preset (skips detection) |
53
- | `--dry-run` | Print plan; write nothing |
54
- | `--no-install` | Generate files only; print install command |
55
- | `--force` | Overwrite a hand-edited instrumentation file (backup written) |
56
- | `--no-detect` | Skip auto-detection; requires `--plan`, `--input`, or `--preset` |
57
- | `--detect-only` | Print the detected plan and exit |
58
- | `--plan <path>` | Apply a pre-built `InitPlan` JSON file |
59
- | `--input -` / `--input <path>` | Read `InitPlan` JSON from stdin or a file |
60
- | `--scan-env` | Consent to reading `.env` / `.env.local` for backend detection |
61
- | `--json` | Machine-readable JSON instead of human output |
62
- | `--output-file <path>` | Persist JSON output to a file |
63
- | `--no-secrets-in-output` | Redact secret-shaped values (`*KEY*`, `*TOKEN*`, etc.) |
64
- | `--no-interactive` | Fail fast instead of prompting |
49
+ | Flag | What it does |
50
+ | ------------------------------ | ---------------------------------------------------------------- |
51
+ | `--yes, -y` | Auto-apply detected items; no prompts |
52
+ | `--preset <name>` | Use a named quick preset (skips detection) |
53
+ | `--dry-run` | Print plan; write nothing |
54
+ | `--no-install` | Generate files only; print install command |
55
+ | `--force` | Overwrite a hand-edited instrumentation file (backup written) |
56
+ | `--no-detect` | Skip auto-detection; requires `--plan`, `--input`, or `--preset` |
57
+ | `--detect-only` | Print the detected plan and exit |
58
+ | `--plan <path>` | Apply a pre-built `InitPlan` JSON file |
59
+ | `--input -` / `--input <path>` | Read `InitPlan` JSON from stdin or a file |
60
+ | `--scan-env` | Consent to reading `.env` / `.env.local` for backend detection |
61
+ | `--json` | Machine-readable JSON instead of human output |
62
+ | `--output-file <path>` | Persist JSON output to a file |
63
+ | `--no-secrets-in-output` | Redact secret-shaped values (`*KEY*`, `*TOKEN*`, etc.) |
64
+ | `--no-interactive` | Fail fast instead of prompting |
65
65
 
66
66
  **Detection coverage:**
67
67
 
68
68
  - **Frameworks (auto-wired):** Hono, MCP servers/clients, TanStack Start
69
69
  - **Frameworks (via `auto-instrumentations-node`):** Express, Fastify, NestJS, Next.js, pg, mysql/2, redis, ioredis, GraphQL, AWS SDK
70
- - **Loggers:** Pino (first-class — `init({ logger })`), Winston/Bunyan (auto-instrumented)
70
+ - **Loggers:** Pino (first-class: `init({ logger })`), Winston/Bunyan (auto-instrumented)
71
71
  - **Subscribers:** PostHog, Mixpanel, Amplitude, Segment, Slack
72
72
  - **Plugins:** Mongoose, Drizzle, Sentry
73
73
  - **Platforms:** Cloudflare (from `wrangler.toml`), AWS Lambda, Edge
74
74
  - **Backends from env vars:** `DD_API_KEY` → Datadog, `HONEYCOMB_API_KEY` → Honeycomb, `OTEL_EXPORTER_OTLP_ENDPOINT` → OTLP
75
75
 
76
76
  **Quick presets:**
77
+
77
78
  - `node-datadog-pino` - Node.js + Datadog + Pino logging
78
79
  - `node-datadog-agent` - Node.js + Datadog Agent (local development)
79
80
  - `node-honeycomb` - Node.js + Honeycomb
@@ -128,12 +129,14 @@ npx autotel doctor --list-checks
128
129
  ```
129
130
 
130
131
  **Options:**
132
+
131
133
  - `--json` - Output machine-readable JSON
132
134
  - `--fix` - Auto-fix resolvable issues
133
135
  - `--list-checks` - List all available checks
134
136
  - `--env-file <path>` - Specify env file to check
135
137
 
136
138
  **Exit codes:**
139
+
137
140
  - `0` - All checks passed
138
141
  - `1` - Warnings found
139
142
  - `2` - Errors found
@@ -163,12 +166,14 @@ npx autotel add backend datadog --help
163
166
  ```
164
167
 
165
168
  **Types:**
169
+
166
170
  - `backend` - Telemetry backends (Datadog, Honeycomb, OTLP, etc.)
167
171
  - `subscriber` - Event subscribers (PostHog, Mixpanel, Segment, Slack, etc.)
168
172
  - `plugin` - Database/ORM plugins (Mongoose, Drizzle, etc.)
169
173
  - `platform` - Platform support (AWS Lambda, Cloudflare Workers, etc.)
170
174
 
171
175
  **Options:**
176
+
172
177
  - `--list` - List available presets
173
178
  - `--dry-run` - Skip installation and print what would be done
174
179
  - `--force` - Overwrite non-CLI-owned config (creates backup first)
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
 
2
- export { };
2
+ export {}
package/dist/index.js CHANGED
@@ -3942,8 +3942,14 @@ function transformFile(content, filePath, options) {
3942
3942
  skip(spanName, "already wrapped");
3943
3943
  continue;
3944
3944
  }
3945
- const mod = fn.getModifiers().map((m) => m.getText()).join(" ");
3946
- const newText = `${mod ? mod + " " : ""}const ${name} = trace('${spanName}', function ${name}${fn.getText().replace(/^function\s*\w*\s*/, "")};`;
3945
+ const modifiers = fn.getModifiers().map((m) => m.getText());
3946
+ const isAsync = modifiers.includes("async");
3947
+ const declModifiers = modifiers.filter((m) => m !== "async");
3948
+ const modPrefix = declModifiers.length > 0 ? declModifiers.join(" ") + " " : "";
3949
+ const fnText = fn.getText();
3950
+ const rest = fnText.replace(/^(?:\b(?:export|default|async|declare)\b\s*)*function\s*(\*?)\s*\w*\s*/, "");
3951
+ const star = /^(?:\b(?:export|default|async|declare)\b\s*)*function\s*\*/.test(fnText) ? "*" : "";
3952
+ const newText = `${modPrefix}const ${name} = trace('${spanName}', ${isAsync ? "async " : ""}function${star} ${name}${rest});`;
3947
3953
  edits.push({
3948
3954
  node: fn,
3949
3955
  newText
@@ -3958,7 +3964,7 @@ function transformFile(content, filePath, options) {
3958
3964
  if (shouldSkipName(spanName, options)) skip(spanName, "name match");
3959
3965
  else {
3960
3966
  const fn = defaultFn;
3961
- const full = `const ${name} = trace('${spanName}', function ${name}(${(fn.getParameters?.() ?? []).map((p) => p.getText()).join(", ")}) ${(fn.getBody?.())?.getText() ?? "{}"};
3967
+ const full = `const ${name} = trace('${spanName}', function ${name}(${(fn.getParameters?.() ?? []).map((p) => p.getText()).join(", ")}) ${(fn.getBody?.())?.getText() ?? "{}"});
3962
3968
  export default ` + name + ";";
3963
3969
  edits.push({
3964
3970
  node: defaultFn,
@@ -3975,7 +3981,7 @@ export default ` + name + ";";
3975
3981
  const isArrow = kind === SyntaxKind.ArrowFunction;
3976
3982
  const isFnExpr = kind === SyntaxKind.FunctionExpression;
3977
3983
  if (kind === SyntaxKind.CallExpression) {
3978
- if (init.getExpression().getText() === "trace") {
3984
+ if (init.asKindOrThrow(SyntaxKind.CallExpression).getExpression().getText() === "trace") {
3979
3985
  const name = decl.getName();
3980
3986
  if (typeof name === "string") {
3981
3987
  const spanName = getSpanName(name, filePath, options);
@@ -4050,7 +4056,7 @@ export default ` + name + ";";
4050
4056
  for (const stmt of sourceFile.getVariableStatements()) for (const decl of stmt.getDeclarationList().getDeclarations()) {
4051
4057
  const init = decl.getInitializer();
4052
4058
  if (!init || init.getKind() !== SyntaxKind.ObjectLiteralExpression) continue;
4053
- const obj = init;
4059
+ const obj = init.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
4054
4060
  for (const prop of obj.getProperties()) {
4055
4061
  if (prop.getKind() !== SyntaxKind.MethodDeclaration) continue;
4056
4062
  const method = prop;
@@ -4764,30 +4770,60 @@ COMMANDS.push(...[
4764
4770
  name: "telemetry",
4765
4771
  description: "Manage opt-in CLI usage telemetry (parent)",
4766
4772
  mutating: false,
4773
+ network: false,
4774
+ writesFiles: false,
4775
+ supportsDryRun: false,
4776
+ requiresPackageJson: false,
4777
+ mayReadEnv: false,
4778
+ supportsJson: false,
4767
4779
  flags: []
4768
4780
  },
4769
4781
  {
4770
4782
  name: "telemetry status",
4771
4783
  description: "Show telemetry consent status",
4772
4784
  mutating: false,
4785
+ network: false,
4786
+ writesFiles: false,
4787
+ supportsDryRun: false,
4788
+ requiresPackageJson: false,
4789
+ mayReadEnv: false,
4790
+ supportsJson: true,
4773
4791
  flags: []
4774
4792
  },
4775
4793
  {
4776
4794
  name: "telemetry enable",
4777
4795
  description: "Enable telemetry for this tool",
4778
4796
  mutating: true,
4797
+ network: false,
4798
+ writesFiles: true,
4799
+ supportsDryRun: false,
4800
+ requiresPackageJson: false,
4801
+ mayReadEnv: false,
4802
+ supportsJson: false,
4779
4803
  flags: []
4780
4804
  },
4781
4805
  {
4782
4806
  name: "telemetry disable",
4783
4807
  description: "Disable telemetry and purge undelivered events",
4784
4808
  mutating: true,
4809
+ network: false,
4810
+ writesFiles: true,
4811
+ supportsDryRun: false,
4812
+ requiresPackageJson: false,
4813
+ mayReadEnv: false,
4814
+ supportsJson: false,
4785
4815
  flags: []
4786
4816
  },
4787
4817
  {
4788
4818
  name: "telemetry disclosure",
4789
4819
  description: "Print telemetry disclosure markdown",
4790
4820
  mutating: false,
4821
+ network: false,
4822
+ writesFiles: false,
4823
+ supportsDryRun: false,
4824
+ requiresPackageJson: false,
4825
+ mayReadEnv: false,
4826
+ supportsJson: false,
4791
4827
  flags: []
4792
4828
  }
4793
4829
  ]);
@@ -6407,6 +6443,11 @@ function createDebugDrain() {
6407
6443
  if (process.env.AUTOTEL_TELEMETRY_DEBUG === "1") for (const event of events) console.error("[autotel-telemetry]", JSON.stringify(event));
6408
6444
  };
6409
6445
  }
6446
+ const RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([
6447
+ 408,
6448
+ 425,
6449
+ 429
6450
+ ]);
6410
6451
  function createHttpDrain(endpoint) {
6411
6452
  return async (events) => {
6412
6453
  if (events.length === 0) return;
@@ -6415,7 +6456,9 @@ function createHttpDrain(endpoint) {
6415
6456
  headers: { "content-type": "application/json" },
6416
6457
  body: JSON.stringify({ events })
6417
6458
  });
6418
- if (!response.ok) throw new Error(`Telemetry delivery failed with HTTP ${response.status}`);
6459
+ if (response.ok) return;
6460
+ if (response.status >= 400 && response.status < 500 && !RETRYABLE_STATUS_CODES.has(response.status)) return;
6461
+ throw new Error(`Telemetry delivery failed with HTTP ${response.status}`);
6419
6462
  };
6420
6463
  }
6421
6464
  function composeDrains(...drains) {
@@ -6794,10 +6837,14 @@ function createProgram() {
6794
6837
  * which the top-level handler in `index.ts` converts to an envelope.
6795
6838
  */
6796
6839
  async function run() {
6797
- const base = createProgram();
6798
- const program = withCommanderTelemetry(base, {
6840
+ const program = createProgram();
6841
+ const parseWithTelemetry = withCommanderTelemetry({
6842
+ name: () => program.name(),
6843
+ version: () => program.version() ?? "",
6844
+ parseAsync: (argv) => program.parseAsync(argv)
6845
+ }, {
6799
6846
  name: "autotel",
6800
- version: base.version()
6847
+ version: program.version()
6801
6848
  });
6802
6849
  program.exitOverride();
6803
6850
  const argvJoined = process.argv.slice(2).join(" ");
@@ -6810,7 +6857,7 @@ async function run() {
6810
6857
  if (isJsonOnly) cmd.configureOutput({ writeErr: () => {} });
6811
6858
  stack.push(...cmd.commands);
6812
6859
  }
6813
- await program.parseAsync(process.argv);
6860
+ await parseWithTelemetry.parseAsync(process.argv);
6814
6861
  }
6815
6862
  //#endregion
6816
6863
  //#region src/lib/commander-error.ts