@capxul/cli 4.20.0-beta.2 → 4.20.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import process$1, { exit, platform, versions } from "node:process";
4
- import { Cause, Clock, Config, Console, Context, Crypto, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, FileSystem, Layer, Logger, Metric, Option, Path, Queue, Redacted, Ref, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stdio, Stream, Tracer } from "effect";
3
+ import process$1, { arch, exit, platform, versions } from "node:process";
4
+ import { Cause, Clock, Config, Console, Context, Crypto, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, FileSystem, Layer, LogLevel, Logger, Metric, Option, Path, Queue, Redacted, Ref, References, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stdio, Stream, Tracer } from "effect";
5
5
  import { NodeRuntime, NodeServices } from "@effect/platform-node";
6
6
  import { CliConfig, CliError, Command, Flag, GlobalFlag, Primitive } from "effect/unstable/cli";
7
7
  import * as nc from "node:crypto";
8
- import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientRequest, HttpClientResponse, HttpTraceContext } from "effect/unstable/http";
8
+ import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse, HttpTraceContext } from "effect/unstable/http";
9
9
  import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
10
10
  import { resolve } from "path";
11
11
  import util from "util";
@@ -473,7 +473,12 @@ const CREDENTIAL_FIELDS = /* @__PURE__ */ new Set([
473
473
  "authcode",
474
474
  "authorizationcode",
475
475
  "oauthcode",
476
- "codeverifier"
476
+ "codeverifier",
477
+ "devicecode",
478
+ "usercode",
479
+ "continuitytoken",
480
+ "refreshattemptid",
481
+ "revocationtoken"
477
482
  ]);
478
483
  /** Match credential fields by meaning, without hiding token addresses or replay IDs. */
479
484
  function isCredentialField(name, context) {
@@ -2398,6 +2403,8 @@ const COUNTRY_CODE_RE = /^[A-Z]{2}$/;
2398
2403
  const ACCOUNT_ID_RE = /^account_[0-9A-Za-z]+$/;
2399
2404
  const PARTY_ID_RE$1 = /^party_[0-9A-Za-z]+$/;
2400
2405
  const APP_ID_RE = /^app_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
2406
+ const CLIENT_REQUEST_ID_RE = /^creq_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
2407
+ const CLIENT_GRANT_ID_RE = /^cgrant_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
2401
2408
  const TX_HASH_RE = /^0x[0-9a-f]{64}$/i;
2402
2409
  const WEI_RE = /^[0-9]+$/;
2403
2410
  Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
@@ -6531,6 +6538,91 @@ const ContactActionProps = {
6531
6538
  reason_code: OptionalReasonCode
6532
6539
  };
6533
6540
  /** One completed online CLI diagnostic. No command input or provider text. */
6541
+ const CLI_COMMAND_NAMES = [
6542
+ "capxul",
6543
+ "doctor",
6544
+ "telemetry",
6545
+ "telemetry.status",
6546
+ "telemetry.enable",
6547
+ "telemetry.disable",
6548
+ "unknown"
6549
+ ];
6550
+ const CLI_CAPABILITIES = [
6551
+ "cli",
6552
+ "diagnostics",
6553
+ "collection"
6554
+ ];
6555
+ const CLI_INVOCATION_KINDS = [
6556
+ "command",
6557
+ "help",
6558
+ "version",
6559
+ "completion",
6560
+ "parse_refusal",
6561
+ "collection_control"
6562
+ ];
6563
+ const CliRelease = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u));
6564
+ const CliInvocationProps = {
6565
+ ...TelemetryEnvelopeProps,
6566
+ schema_version: Schema.Literal(1),
6567
+ command: Schema.Literals(CLI_COMMAND_NAMES),
6568
+ capability: Schema.Literals(CLI_CAPABILITIES),
6569
+ invocation_kind: Schema.Literals(CLI_INVOCATION_KINDS),
6570
+ invocation_id: Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u)),
6571
+ cli_identity_kind: Schema.optional(Schema.Literal("anonymous")),
6572
+ anonymous_id: Schema.optional(Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u))),
6573
+ occurred_at: Schema.Number,
6574
+ cli_version: CliRelease,
6575
+ sdk_version: CliRelease,
6576
+ release: CliRelease,
6577
+ platform: Schema.Literals([
6578
+ "darwin",
6579
+ "linux",
6580
+ "unknown"
6581
+ ]),
6582
+ architecture: Schema.Literals([
6583
+ "arm64",
6584
+ "x64",
6585
+ "unknown"
6586
+ ]),
6587
+ ci: Schema.Boolean,
6588
+ tty: Schema.Boolean,
6589
+ output_mode: Schema.Literals(["human", "json"])
6590
+ };
6591
+ const CliFirstUsedTelemetryEventSchema = Schema.Struct({
6592
+ name: Schema.Literal("cli_first_used"),
6593
+ props: Schema.Struct(CliInvocationProps)
6594
+ });
6595
+ const CliCommandStartedTelemetryEventSchema = Schema.Struct({
6596
+ name: Schema.Literal("cli_command_started"),
6597
+ props: Schema.Struct({
6598
+ ...CliInvocationProps,
6599
+ invocation_kind: Schema.Literals([
6600
+ "command",
6601
+ "help",
6602
+ "version"
6603
+ ])
6604
+ })
6605
+ });
6606
+ const CliCommandCompletedTelemetryEventSchema = Schema.Struct({
6607
+ name: Schema.Literal("cli_command_completed"),
6608
+ props: Schema.Struct({
6609
+ ...CliInvocationProps,
6610
+ outcome: Schema.Literals([
6611
+ "succeeded",
6612
+ "refused",
6613
+ "failed",
6614
+ "cancelled"
6615
+ ]),
6616
+ duration_ms: Schema.Number,
6617
+ error_code: Schema.optional(Schema.Literals([
6618
+ ...CAPXUL_ERROR_CODES,
6619
+ "CLI_USAGE",
6620
+ "CLI_CONFIG",
6621
+ "CLI_STORAGE",
6622
+ "CLI_DEFECT"
6623
+ ]))
6624
+ })
6625
+ });
6534
6626
  const CliDiagnosticCompletedTelemetryEventSchema = Schema.Struct({
6535
6627
  name: Schema.Literal("cli_diagnostic_completed"),
6536
6628
  props: Schema.Struct({
@@ -6832,6 +6924,9 @@ const TELEMETRY_EVENT_SCHEMAS = {
6832
6924
  }),
6833
6925
  ui_contact_action_completed: UiContactActionCompletedTelemetryEventSchema,
6834
6926
  cli_diagnostic_completed: CliDiagnosticCompletedTelemetryEventSchema,
6927
+ cli_first_used: CliFirstUsedTelemetryEventSchema,
6928
+ cli_command_started: CliCommandStartedTelemetryEventSchema,
6929
+ cli_command_completed: CliCommandCompletedTelemetryEventSchema,
6835
6930
  activity_annotation_saved: ActivityAnnotationSavedTelemetryEventSchema,
6836
6931
  ui_activity_action_completed: UiActivityActionCompletedTelemetryEventSchema,
6837
6932
  auth_otp_requested: AuthOtpRequestedTelemetryEventSchema,
@@ -7540,6 +7635,30 @@ const PAYMENT_DOCUMENT_KINDS = [
7540
7635
  "receipt",
7541
7636
  "withdrawal"
7542
7637
  ];
7638
+ /** Native browser projection, distinct from the issuer's persisted request state. */
7639
+ const CLIENT_AUTHORIZATION_PHASE = {
7640
+ OTP_REQUIRED: "otp_required",
7641
+ REVIEW: "review",
7642
+ PROCESSING: "processing",
7643
+ CONNECTED: "connected",
7644
+ REFUSED: "refused"
7645
+ };
7646
+ CLIENT_AUTHORIZATION_PHASE.OTP_REQUIRED, CLIENT_AUTHORIZATION_PHASE.REVIEW, CLIENT_AUTHORIZATION_PHASE.PROCESSING, CLIENT_AUTHORIZATION_PHASE.CONNECTED, CLIENT_AUTHORIZATION_PHASE.REFUSED;
7647
+ /** Closed native refusals; existing Capxul error codes retain their own meaning. */
7648
+ const CLIENT_AUTHORIZATION_REFUSALS = [
7649
+ "signup_required",
7650
+ "setup_required",
7651
+ "already_signed_up",
7652
+ "identity_mismatch",
7653
+ "invalid_request",
7654
+ "request_expired",
7655
+ "request_denied",
7656
+ "cancelled",
7657
+ "grant_revoked",
7658
+ "grant_expired",
7659
+ "refresh_reused",
7660
+ "revoke_unconfirmed"
7661
+ ];
7543
7662
  //#endregion
7544
7663
  //#region ../../packages/wire/src/financial-ops.ts
7545
7664
  const MINOR_UNIT_STRING_RE = /^[0-9]+$/;
@@ -9241,6 +9360,251 @@ function isSafeField(field, value) {
9241
9360
  return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
9242
9361
  }
9243
9362
  //#endregion
9363
+ //#region ../../packages/wire/src/client-authorization.ts
9364
+ const NATIVE_CLIENT_ID = "capxul-cli";
9365
+ const NATIVE_CLIENT_SCOPE = "account:read";
9366
+ const NATIVE_CLIENT_LIMITS = {
9367
+ accessMs: 36e5,
9368
+ renewalMs: 2592e6,
9369
+ absoluteMs: 7776e6,
9370
+ requestMs: 6e5,
9371
+ pollSeconds: 5,
9372
+ imageBytes: 5242880
9373
+ };
9374
+ const strict = { parseOptions: { onExcessProperty: "error" } };
9375
+ const text$2 = (max) => Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(max));
9376
+ const nonNegativeInt = Schema.Number.check(Schema.makeFilter((n) => Number.isSafeInteger(n) && n >= 0));
9377
+ const normalize = (f) => Schema.String.pipe(Schema.decodeTo(Schema.String, {
9378
+ decode: SchemaGetter.transform(f),
9379
+ encode: SchemaGetter.transform((s) => s)
9380
+ }));
9381
+ const ClientRequestIdSchema = Schema.String.pipe(Schema.refine((s) => CLIENT_REQUEST_ID_RE.test(s)));
9382
+ const ClientGrantIdSchema = Schema.String.pipe(Schema.refine((s) => CLIENT_GRANT_ID_RE.test(s)));
9383
+ const authUserId = Schema.String.pipe(Schema.refine((s) => s.length > 0));
9384
+ const keyId = Schema.String.pipe(Schema.refine((s) => s.length > 0));
9385
+ const email = normalize((s) => s.toLowerCase()).pipe(Schema.refine((s) => EMAIL_RE$1.test(s))).check(Schema.isMaxLength(254));
9386
+ const handle = Schema.String.pipe(Schema.refine((s) => HANDLE_RE$1.test(s)));
9387
+ const country = normalize((s) => s.toUpperCase()).pipe(Schema.refine((s) => COUNTRY_CODE_RE.test(s)));
9388
+ const credential = Schema.RedactedFromValue(Schema.String.check(Schema.isMinLength(32), Schema.isMaxLength(8192), Schema.isPattern(/^[A-Za-z0-9._~-]+$/u)), { label: "native authorization credential" });
9389
+ const otp = Schema.RedactedFromValue(Schema.String.check(Schema.isPattern(/^[0-9]{6}$/u)), { label: "OTP" });
9390
+ const scopes = Schema.Tuple([Schema.Literal(NATIVE_CLIENT_SCOPE)]);
9391
+ /** Exact HTTPS origin, no credentials/path/query and no retired cloud deployment. */
9392
+ const NativeHttpsOriginSchema = Schema.String.check(Schema.makeFilter((value) => {
9393
+ try {
9394
+ const url = new URL(value);
9395
+ const host = url.hostname.toLowerCase().replace(/\.$/u, "");
9396
+ return url.protocol === "https:" && value === url.origin && !url.username && !url.password && !url.search && !url.hash && host !== "convex.cloud" && !host.endsWith(".convex.cloud") && host !== "convex.site" && !host.endsWith(".convex.site");
9397
+ } catch {
9398
+ return false;
9399
+ }
9400
+ }, { message: "must be a canonical credential-free HTTPS origin outside retired Convex cloud hosts" }));
9401
+ const NativeClientConfigurationSchema = Schema.Struct({
9402
+ clientId: Schema.Literal(NATIVE_CLIENT_ID),
9403
+ applicationId: AppIdSchema,
9404
+ keyId,
9405
+ environment: Schema.Literals([
9406
+ "development",
9407
+ "staging",
9408
+ "production"
9409
+ ]),
9410
+ keyEnvironment: Schema.Literals(["test", "live"]),
9411
+ issuer: NativeHttpsOriginSchema,
9412
+ verificationOrigin: NativeHttpsOriginSchema
9413
+ }).annotate(strict).check(Schema.makeFilter((c) => c.keyEnvironment === (c.environment === "production" ? "live" : "test"), { message: "key environment must match the configured deployment" })).annotate(strict);
9414
+ const NativeSignupProfileSchema = Schema.Struct({
9415
+ displayName: normalize((s) => s.trim()).check(Schema.isMinLength(1), Schema.isMaxLength(200)),
9416
+ country,
9417
+ handle,
9418
+ payoutAddresses: Schema.optional(Schema.Array(Schema.Struct({
9419
+ chain: Schema.Literals([
9420
+ "evm",
9421
+ "solana",
9422
+ "starknet"
9423
+ ]),
9424
+ address: text$2(256)
9425
+ }).annotate(strict)).check(Schema.isMaxLength(10))),
9426
+ image: Schema.optional(Schema.Struct({
9427
+ mediaType: Schema.Literals([
9428
+ "image/jpeg",
9429
+ "image/png",
9430
+ "image/webp"
9431
+ ]),
9432
+ byteLength: Schema.Number.check(Schema.isInt(), Schema.isBetween({
9433
+ minimum: 1,
9434
+ maximum: NATIVE_CLIENT_LIMITS.imageBytes
9435
+ })),
9436
+ sha256: Schema.String.check(Schema.isPattern(/^[0-9a-f]{64}$/u))
9437
+ }).annotate(strict))
9438
+ }).annotate(strict);
9439
+ const ClientAuthorizationIntentSchema = Schema.Union([Schema.Struct({ mode: Schema.Literal("login") }).annotate(strict), Schema.Struct({
9440
+ mode: Schema.Literal("signup"),
9441
+ profile: NativeSignupProfileSchema
9442
+ }).annotate(strict)]).annotate(strict);
9443
+ Schema.Struct({
9444
+ clientId: Schema.Literal(NATIVE_CLIENT_ID),
9445
+ email,
9446
+ intent: ClientAuthorizationIntentSchema,
9447
+ waitMs: Schema.Number.check(Schema.isInt(), Schema.isBetween({
9448
+ minimum: 1,
9449
+ maximum: NATIVE_CLIENT_LIMITS.requestMs
9450
+ })),
9451
+ continuityToken: Schema.optional(credential)
9452
+ }).annotate(strict);
9453
+ const request = {
9454
+ requestId: ClientRequestIdSchema,
9455
+ revision: nonNegativeInt,
9456
+ expiresAt: EpochMsSchema
9457
+ };
9458
+ const principal = {
9459
+ authUserId,
9460
+ email
9461
+ };
9462
+ const profileSummary = {
9463
+ displayName: text$2(200),
9464
+ handle,
9465
+ country
9466
+ };
9467
+ Schema.Union([
9468
+ Schema.Struct({
9469
+ ...request,
9470
+ phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.OTP_REQUIRED),
9471
+ email,
9472
+ resendAt: EpochMsSchema
9473
+ }).annotate(strict),
9474
+ Schema.Struct({
9475
+ ...request,
9476
+ phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.REVIEW),
9477
+ ...principal,
9478
+ clientLabel: Schema.Literal("Capxul CLI"),
9479
+ scopes,
9480
+ intent: ClientAuthorizationIntentSchema,
9481
+ setupState: Schema.Literals([
9482
+ "not_required",
9483
+ "new",
9484
+ "resumable"
9485
+ ])
9486
+ }).annotate(strict).check(Schema.makeFilter((v) => v.intent.mode === "login" === (v.setupState === "not_required"), { message: "login review cannot permit account setup" })),
9487
+ Schema.Struct({
9488
+ ...request,
9489
+ phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.PROCESSING),
9490
+ ...principal,
9491
+ purpose: Schema.Literals(["login", "signup"]),
9492
+ step: Schema.Literals([
9493
+ "profile",
9494
+ "wallet",
9495
+ "account",
9496
+ "delivery"
9497
+ ])
9498
+ }).annotate(strict).check(Schema.makeFilter((v) => v.purpose === "signup" || v.step === "delivery", { message: "login may wait for delivery but cannot enter signup processing" })),
9499
+ Schema.Struct({
9500
+ ...request,
9501
+ phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.CONNECTED),
9502
+ ...principal,
9503
+ ...profileSummary,
9504
+ accountId: AccountIdSchema,
9505
+ grantId: ClientGrantIdSchema
9506
+ }).annotate(strict),
9507
+ Schema.Struct({
9508
+ ...request,
9509
+ phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.REFUSED),
9510
+ reason: Schema.Literals(CLIENT_AUTHORIZATION_REFUSALS),
9511
+ code: Schema.Literals(CAPXUL_ERROR_CODES),
9512
+ field: Schema.optional(Schema.Literals([
9513
+ "email",
9514
+ "displayName",
9515
+ "country",
9516
+ "handle",
9517
+ "image",
9518
+ "payoutAddresses"
9519
+ ])),
9520
+ retryable: Schema.Boolean
9521
+ }).annotate(strict)
9522
+ ]).annotate(strict);
9523
+ Schema.Struct({
9524
+ requestId: ClientRequestIdSchema,
9525
+ revision: nonNegativeInt
9526
+ }).annotate(strict);
9527
+ Schema.Struct({
9528
+ requestId: ClientRequestIdSchema,
9529
+ code: otp
9530
+ }).annotate(strict);
9531
+ Schema.Struct({
9532
+ client_id: Schema.Literal(NATIVE_CLIENT_ID),
9533
+ grant_type: Schema.Literal("refresh_token"),
9534
+ refresh_token: credential,
9535
+ refreshAttemptId: Schema.RedactedFromValue(Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]{32,128}$/u)), { label: "refresh attempt" })
9536
+ }).annotate(strict);
9537
+ Schema.Struct({
9538
+ client_id: Schema.Literal(NATIVE_CLIENT_ID),
9539
+ applicationId: AppIdSchema,
9540
+ resource: text$2(2048),
9541
+ token_type: Schema.Literal("Bearer"),
9542
+ access_token: credential,
9543
+ refresh_token: credential,
9544
+ scope: Schema.Literal(NATIVE_CLIENT_SCOPE),
9545
+ expires_in: Schema.Number.check(Schema.isInt(), Schema.isBetween({
9546
+ minimum: 1,
9547
+ maximum: NATIVE_CLIENT_LIMITS.accessMs / 1e3
9548
+ })),
9549
+ grantId: ClientGrantIdSchema,
9550
+ ...principal,
9551
+ accountId: AccountIdSchema,
9552
+ issuedAt: EpochMsSchema,
9553
+ grantCreatedAt: EpochMsSchema,
9554
+ accessExpiresAt: EpochMsSchema,
9555
+ renewalExpiresAt: EpochMsSchema,
9556
+ absoluteExpiresAt: EpochMsSchema,
9557
+ revision: nonNegativeInt
9558
+ }).annotate(strict).check(Schema.makeFilter((t) => t.grantCreatedAt <= t.issuedAt && t.absoluteExpiresAt - t.grantCreatedAt === NATIVE_CLIENT_LIMITS.absoluteMs && t.renewalExpiresAt <= Math.min(t.issuedAt + NATIVE_CLIENT_LIMITS.renewalMs, t.absoluteExpiresAt) && t.accessExpiresAt <= Math.min(t.issuedAt + NATIVE_CLIENT_LIMITS.accessMs, t.renewalExpiresAt) && t.expires_in === Math.floor((t.accessExpiresAt - t.issuedAt) / 1e3), { message: "native credentials must obey the approved access, renewal and absolute deadlines" })).annotate(strict);
9559
+ /** One endpoint mapping; consumers cannot substitute callback or resource URLs. */
9560
+ function nativeClientEndpoints(config) {
9561
+ const resource = `${config.issuer}/api/auth/native`;
9562
+ return {
9563
+ resource,
9564
+ device_authorization_endpoint: `${resource}/device/code`,
9565
+ token_endpoint: `${resource}/token`,
9566
+ revocation_endpoint: `${resource}/revoke`,
9567
+ session_endpoint: `${resource}/session`,
9568
+ verification_uri: `${config.verificationOrigin}/connect`
9569
+ };
9570
+ }
9571
+ Schema.Struct({
9572
+ protocol: Schema.Literal("capxul.client-authorization"),
9573
+ version: Schema.Literal(1),
9574
+ configuration: NativeClientConfigurationSchema,
9575
+ scopes_supported: scopes,
9576
+ grant_types_supported: Schema.Tuple([Schema.Literal("urn:ietf:params:oauth:grant-type:device_code"), Schema.Literal("refresh_token")]),
9577
+ resource: text$2(2048),
9578
+ device_authorization_endpoint: text$2(2048),
9579
+ token_endpoint: text$2(2048),
9580
+ revocation_endpoint: text$2(2048),
9581
+ session_endpoint: text$2(2048),
9582
+ verification_uri: text$2(2048)
9583
+ }).annotate(strict).check(Schema.makeFilter((m) => {
9584
+ const expected = nativeClientEndpoints(m.configuration);
9585
+ return m.resource === expected.resource && m.device_authorization_endpoint === expected.device_authorization_endpoint && m.token_endpoint === expected.token_endpoint && m.revocation_endpoint === expected.revocation_endpoint && m.session_endpoint === expected.session_endpoint && m.verification_uri === expected.verification_uri;
9586
+ }, { message: "native endpoints must match their configured issuer and verification origin" })).annotate(strict);
9587
+ Schema.Struct({
9588
+ client_id: Schema.Literal(NATIVE_CLIENT_ID),
9589
+ grant_type: Schema.Literal("urn:ietf:params:oauth:grant-type:device_code"),
9590
+ device_code: credential
9591
+ }).annotate(strict);
9592
+ Schema.Struct({
9593
+ requestId: ClientRequestIdSchema,
9594
+ device_code: credential,
9595
+ user_code: Schema.RedactedFromValue(Schema.String.check(Schema.isPattern(/^[A-Z0-9]{4}-[A-Z0-9]{4}$/u)), { label: "device user code" }),
9596
+ continuityToken: credential,
9597
+ verification_uri: text$2(2048),
9598
+ expires_in: Schema.Number.check(Schema.isInt(), Schema.isBetween({
9599
+ minimum: 1,
9600
+ maximum: NATIVE_CLIENT_LIMITS.requestMs / 1e3
9601
+ })),
9602
+ interval: Schema.Number.check(Schema.isInt(), Schema.isBetween({
9603
+ minimum: NATIVE_CLIENT_LIMITS.pollSeconds,
9604
+ maximum: NATIVE_CLIENT_LIMITS.requestMs / 1e3
9605
+ }))
9606
+ }).annotate(strict);
9607
+ //#endregion
9244
9608
  //#region ../../packages/sdk/src/internal/invocation-observation.ts
9245
9609
  const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
9246
9610
  const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
@@ -11693,7 +12057,7 @@ function subscribeActivity(deps, actor, controls, onChange) {
11693
12057
  }
11694
12058
  //#endregion
11695
12059
  //#region ../../packages/sdk/package.json
11696
- var version$1 = "4.20.0-beta.2";
12060
+ var version$1 = "4.20.0-beta.4";
11697
12061
  //#endregion
11698
12062
  //#region ../../packages/sdk/src/telemetry/exception-projection.ts
11699
12063
  /** Fixed fallback for failures that have no safe message. */
@@ -13753,6 +14117,381 @@ async function recoverRawDigestSigner(input) {
13753
14117
  }
13754
14118
  }
13755
14119
  //#endregion
14120
+ //#region ../../packages/observability/src/engineering-delivery.ts
14121
+ /** Fixed admission limits for one CLI invocation and its SDK client. */
14122
+ const CLI_ENGINEERING_LIMITS = Object.freeze({
14123
+ records: 80,
14124
+ ordinaryRecords: 64,
14125
+ recordBytes: 16384,
14126
+ requests: 5,
14127
+ ordinaryRequests: 2,
14128
+ requestBytes: 1572864,
14129
+ metricSeries: 16,
14130
+ requestTimeoutMs: 1e3
14131
+ });
14132
+ const metricNames = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
14133
+ const metricValues = {
14134
+ command: CLI_COMMAND_NAMES,
14135
+ capability: CLI_CAPABILITIES,
14136
+ invocation_kind: CLI_INVOCATION_KINDS,
14137
+ outcome: [
14138
+ "succeeded",
14139
+ "refused",
14140
+ "failed",
14141
+ "cancelled"
14142
+ ],
14143
+ capxul_env: [
14144
+ "development",
14145
+ "staging",
14146
+ "production",
14147
+ "local"
14148
+ ],
14149
+ unit: [
14150
+ "1",
14151
+ "s",
14152
+ "ms"
14153
+ ]
14154
+ };
14155
+ /** Only this bounded registry is supplied to the CLI's metric producers. */
14156
+ var CliMetricRegistry = class extends Map {
14157
+ dropped;
14158
+ enabled;
14159
+ constructor(dropped, enabled = true) {
14160
+ super();
14161
+ this.dropped = dropped;
14162
+ this.enabled = enabled;
14163
+ }
14164
+ set(key, entry) {
14165
+ if (!this.enabled || !metricNames.has(entry.id) || !this.has(key) && this.size >= CLI_ENGINEERING_LIMITS.metricSeries) {
14166
+ this.dropped();
14167
+ return this;
14168
+ }
14169
+ const labels = Object.entries(entry.attributes ?? {});
14170
+ const state = entry.hooks.get(Context.empty());
14171
+ const buckets = typeof state === "object" && state !== null && "buckets" in state ? state.buckets : void 0;
14172
+ if (!["Counter", "Histogram"].includes(entry.type) || (entry.description?.length ?? 0) > 128 || labels.length > 6 || labels.some(([name, value]) => !metricValues[name]?.includes(String(value))) || entry.type === "Histogram" && (!Array.isArray(buckets) || buckets.length > 32)) {
14173
+ this.dropped();
14174
+ return this;
14175
+ }
14176
+ return super.set(key, entry);
14177
+ }
14178
+ };
14179
+ /** Shared, closure-owned admission state; no queue or mutable counters are exposed. */
14180
+ function makeCliEngineeringBudget() {
14181
+ let records = 0, peakRecords = 0, activeRequests = 0, requestBytes = 0;
14182
+ let peakRequests = 0, peakRequestBytes = 0, dropped = 0;
14183
+ const drop = () => {
14184
+ dropped += 1;
14185
+ };
14186
+ const metricRegistry = new CliMetricRegistry(drop);
14187
+ return Object.freeze({
14188
+ metricRegistry,
14189
+ emptyMetricRegistry: () => new CliMetricRegistry(drop, false),
14190
+ drop,
14191
+ admitRecord(terminal) {
14192
+ if (records >= (terminal ? CLI_ENGINEERING_LIMITS.records : CLI_ENGINEERING_LIMITS.ordinaryRecords)) {
14193
+ drop();
14194
+ return;
14195
+ }
14196
+ records += 1;
14197
+ peakRecords = Math.max(peakRecords, records);
14198
+ let released = false;
14199
+ return () => {
14200
+ if (!released) {
14201
+ released = true;
14202
+ records -= 1;
14203
+ }
14204
+ };
14205
+ },
14206
+ admitRequest(bytes, terminal) {
14207
+ if (bytes > CLI_ENGINEERING_LIMITS.requestBytes || activeRequests >= (terminal ? CLI_ENGINEERING_LIMITS.requests : CLI_ENGINEERING_LIMITS.ordinaryRequests)) {
14208
+ drop();
14209
+ return;
14210
+ }
14211
+ activeRequests += 1;
14212
+ requestBytes += bytes;
14213
+ peakRequests = Math.max(peakRequests, activeRequests);
14214
+ peakRequestBytes = Math.max(peakRequestBytes, requestBytes);
14215
+ return () => {
14216
+ activeRequests -= 1;
14217
+ requestBytes -= bytes;
14218
+ };
14219
+ },
14220
+ snapshot() {
14221
+ return {
14222
+ records,
14223
+ reservedRecordBytes: records * CLI_ENGINEERING_LIMITS.recordBytes,
14224
+ peakRecords,
14225
+ peakReservedRecordBytes: peakRecords * CLI_ENGINEERING_LIMITS.recordBytes,
14226
+ activeRequests,
14227
+ requestBytes,
14228
+ peakRequests,
14229
+ peakRequestBytes,
14230
+ dropped,
14231
+ metricSeries: metricRegistry.size
14232
+ };
14233
+ }
14234
+ });
14235
+ }
14236
+ const text$1 = (value) => typeof value === "string" ? value.slice(0, 128) : "capxul.engineering";
14237
+ const attributes = (input, limit = 32) => {
14238
+ const out = {};
14239
+ let count = 0;
14240
+ let bytes = 0;
14241
+ try {
14242
+ for (const key in input) {
14243
+ if (count++ >= limit) break;
14244
+ if (key.length > 64) continue;
14245
+ const value = Object.getOwnPropertyDescriptor(input, key)?.value;
14246
+ const scalar = typeof value === "string" ? value.slice(0, 128) : typeof value === "boolean" || typeof value === "number" && Number.isFinite(value) ? value : void 0;
14247
+ if (scalar === void 0) continue;
14248
+ bytes += new TextEncoder().encode(JSON.stringify([key, scalar])).length;
14249
+ if (bytes > (limit >= 24 ? 2e3 : 300)) break;
14250
+ out[key] = scalar;
14251
+ }
14252
+ } catch {}
14253
+ return out;
14254
+ };
14255
+ const terminalName = (name) => name === "cli.command.completed" || CLI_COMMAND_NAMES.some((command) => name === `cli.command.${command}`);
14256
+ /** Native exporters retain their serialization and scheduling; this owns admission only. */
14257
+ function cliEngineeringDelivery(budget, ownsMetrics) {
14258
+ const closed = Deferred.makeUnsafe();
14259
+ let isClosed = false;
14260
+ let metricsRunning = false;
14261
+ const logs = [];
14262
+ const spans = /* @__PURE__ */ new Map();
14263
+ const terminalBodies = /* @__PURE__ */ new WeakSet();
14264
+ const bodyLeases = /* @__PURE__ */ new WeakMap();
14265
+ const pendingBodies = /* @__PURE__ */ new Set();
14266
+ const holdBody = (body, releases) => {
14267
+ if (releases.length === 0) return;
14268
+ const lease = {
14269
+ started: false,
14270
+ release: () => {
14271
+ if (!pendingBodies.delete(lease)) return;
14272
+ bodyLeases.delete(body);
14273
+ for (const release of releases) release();
14274
+ }
14275
+ };
14276
+ bodyLeases.set(body, lease);
14277
+ pendingBodies.add(lease);
14278
+ };
14279
+ const discardBody = (body) => bodyLeases.get(body)?.release();
14280
+ const drainLogs = () => {
14281
+ for (const release of logs.splice(0)) release();
14282
+ };
14283
+ const close = () => {
14284
+ isClosed = true;
14285
+ Deferred.doneUnsafe(closed, Effect.void);
14286
+ for (const lease of pendingBodies) if (!lease.started) lease.release();
14287
+ drainLogs();
14288
+ for (const release of spans.values()) release();
14289
+ spans.clear();
14290
+ if (ownsMetrics) budget.metricRegistry.clear();
14291
+ };
14292
+ return {
14293
+ close,
14294
+ discardBody,
14295
+ metrics(run) {
14296
+ return Effect.suspend(() => {
14297
+ if (isClosed || metricsRunning) return Effect.void;
14298
+ metricsRunning = true;
14299
+ return run.pipe(Effect.ensuring(Effect.sync(() => {
14300
+ metricsRunning = false;
14301
+ })));
14302
+ });
14303
+ },
14304
+ logger(delegate) {
14305
+ return Logger.make((options) => {
14306
+ if (isClosed) return;
14307
+ let release;
14308
+ try {
14309
+ const message = text$1(Array.isArray(options.message) ? Object.getOwnPropertyDescriptor(options.message, "0")?.value : options.message);
14310
+ release = budget.admitRecord(terminalName(message));
14311
+ if (release === void 0) return;
14312
+ const context = Context.add(options.fiber.context, References.CurrentLogAnnotations, attributes(options.fiber.getRef(References.CurrentLogAnnotations)));
14313
+ const fiber = Object.create(options.fiber);
14314
+ Object.defineProperty(fiber, "getRef", { value: (ref) => Context.get(context, ref) });
14315
+ logs.push(release);
14316
+ delegate.log({
14317
+ ...options,
14318
+ message,
14319
+ cause: Cause.empty,
14320
+ fiber
14321
+ });
14322
+ } catch {
14323
+ if (release !== void 0) {
14324
+ const index = logs.indexOf(release);
14325
+ if (index !== -1) logs.splice(index, 1);
14326
+ release();
14327
+ }
14328
+ budget.drop();
14329
+ }
14330
+ });
14331
+ },
14332
+ tracer(delegate) {
14333
+ return Tracer.make({
14334
+ span(options) {
14335
+ const name = text$1(options.name);
14336
+ const release = isClosed ? void 0 : budget.admitRecord(terminalName(name));
14337
+ const spanOptions = {
14338
+ ...options,
14339
+ name,
14340
+ annotations: Context.empty(),
14341
+ links: []
14342
+ };
14343
+ const span = release === void 0 ? new Tracer.NativeSpan({
14344
+ ...spanOptions,
14345
+ sampled: false
14346
+ }) : delegate.span(spanOptions);
14347
+ if (release !== void 0) spans.set(span.spanId, release);
14348
+ const wrapped = Object.create(span);
14349
+ let fields = 0, events = 0, links = 0, attributeBytes = 0;
14350
+ let ended = false;
14351
+ Object.defineProperties(wrapped, {
14352
+ attribute: { value: (key, value) => {
14353
+ if (fields >= 32 && !span.attributes.has(key)) {
14354
+ budget.drop();
14355
+ return;
14356
+ }
14357
+ const safe = attributes({ [key]: value }, 1);
14358
+ if (!Object.hasOwn(safe, key)) return;
14359
+ const previous = span.attributes.has(key) ? new TextEncoder().encode(JSON.stringify([key, span.attributes.get(key)])).length : 0;
14360
+ const next = new TextEncoder().encode(JSON.stringify([key, safe[key]])).length;
14361
+ if (attributeBytes - previous + next > 2e3) {
14362
+ budget.drop();
14363
+ return;
14364
+ }
14365
+ attributeBytes += next - previous;
14366
+ if (!span.attributes.has(key)) fields += 1;
14367
+ span.attribute(key, safe[key]);
14368
+ } },
14369
+ event: { value: (eventName, time, attrs) => {
14370
+ if (events++ >= 2) {
14371
+ budget.drop();
14372
+ return;
14373
+ }
14374
+ span.event(text$1(eventName), time, attributes(attrs ?? {}, 4));
14375
+ } },
14376
+ addLinks: { value: (items) => {
14377
+ for (const item of items.slice(0, Math.max(0, 2 - links))) {
14378
+ if (!/^[a-f0-9]{32}$/u.test(item.span.traceId) || !/^[a-f0-9]{16}$/u.test(item.span.spanId)) {
14379
+ budget.drop();
14380
+ continue;
14381
+ }
14382
+ span.addLinks([{
14383
+ span: Tracer.externalSpan({
14384
+ traceId: item.span.traceId,
14385
+ spanId: item.span.spanId,
14386
+ sampled: item.span.sampled
14387
+ }),
14388
+ attributes: attributes(item.attributes, 4)
14389
+ }]);
14390
+ links += 1;
14391
+ }
14392
+ } },
14393
+ end: { value: (time, exit) => {
14394
+ if (ended) return;
14395
+ ended = true;
14396
+ span.end(time, Exit.isFailure(exit) ? Exit.fail("CLI operation failed") : Exit.void);
14397
+ } }
14398
+ });
14399
+ wrapped.addLinks(options.links ?? []);
14400
+ return wrapped;
14401
+ },
14402
+ ...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
14403
+ });
14404
+ },
14405
+ serialization(delegate) {
14406
+ return {
14407
+ logs(data) {
14408
+ const releases = logs.splice(0);
14409
+ try {
14410
+ const body = delegate.logs(data);
14411
+ if (data.resourceLogs.some((r) => r.scopeLogs.some((s) => s.logRecords?.some((l) => l.body?.stringValue === "cli.command.completed")))) terminalBodies.add(body);
14412
+ holdBody(body, releases);
14413
+ return body;
14414
+ } catch (error) {
14415
+ for (const release of releases) release();
14416
+ throw error;
14417
+ }
14418
+ },
14419
+ traces(data) {
14420
+ const releases = [];
14421
+ for (const resource of data.resourceSpans) for (const scope of resource.scopeSpans) for (const span of scope.spans) {
14422
+ const release = spans.get(span.spanId);
14423
+ if (release !== void 0) releases.push(release);
14424
+ spans.delete(span.spanId);
14425
+ }
14426
+ try {
14427
+ const body = delegate.traces(data);
14428
+ if (data.resourceSpans.some((r) => r.scopeSpans.some((s) => s.spans.some((span) => terminalName(span.name))))) terminalBodies.add(body);
14429
+ holdBody(body, releases);
14430
+ return body;
14431
+ } catch (error) {
14432
+ for (const release of releases) release();
14433
+ throw error;
14434
+ }
14435
+ },
14436
+ metrics(data) {
14437
+ const body = delegate.metrics(data);
14438
+ terminalBodies.add(body);
14439
+ return body;
14440
+ }
14441
+ };
14442
+ },
14443
+ transport(client, enabled = () => true) {
14444
+ const retrying = HttpClient.transform(client, (response, request) => Effect.suspend(() => {
14445
+ if (enabled()) return response;
14446
+ budget.drop();
14447
+ return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
14448
+ })).pipe(HttpClient.transformResponse((response) => Effect.tap(response, (result) => {
14449
+ let received = 0;
14450
+ return Stream.runForEach(result.stream, (chunk) => Effect.sync(() => {
14451
+ received += chunk.byteLength;
14452
+ if (received > 65536) throw new Error("Engineering response limit exceeded");
14453
+ })).pipe(Effect.catchTag("HttpClientError", (error) => error.reason._tag === "EmptyBodyError" ? Effect.void : Effect.fail(error)));
14454
+ }))).pipe(HttpClient.filterStatusOk, HttpClient.retryTransient({
14455
+ times: 1,
14456
+ schedule: Schedule.spaced("100 millis").pipe(Schedule.addDelay(({ input }) => Effect.map(Clock.currentTimeMillis, (now) => {
14457
+ if (!HttpClientError.isHttpClientError(input) || input.reason._tag !== "StatusCodeError" || input.reason.response.status !== 429) return Duration.zero;
14458
+ const header = input.reason.response.headers["retry-after"];
14459
+ const numeric = Number(header);
14460
+ const delay = header === void 0 ? 2e3 : Number.isFinite(numeric) ? numeric * 1e3 : Date.parse(header) - now;
14461
+ return Duration.millis(Number.isFinite(delay) ? Math.min(2e3, Math.max(0, delay)) : 2e3);
14462
+ })))
14463
+ }));
14464
+ return HttpClient.transform(retrying, (response, request) => Effect.suspend(() => {
14465
+ if (isClosed) {
14466
+ discardBody(request.body);
14467
+ return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
14468
+ }
14469
+ const droppedResponse = () => HttpClientResponse.fromWeb(request, new Response(null, { status: 200 }));
14470
+ if (request.body._tag === "Empty") {
14471
+ discardBody(request.body);
14472
+ return Effect.succeed(droppedResponse());
14473
+ }
14474
+ const bytes = request.body.contentLength;
14475
+ const release = budget.admitRequest(bytes ?? Number.POSITIVE_INFINITY, terminalBodies.has(request.body));
14476
+ if (release === void 0) {
14477
+ discardBody(request.body);
14478
+ return Effect.succeed(droppedResponse());
14479
+ }
14480
+ const lease = bodyLeases.get(request.body);
14481
+ if (lease !== void 0) lease.started = true;
14482
+ return response.pipe(Effect.asVoid, Effect.timeoutOption(CLI_ENGINEERING_LIMITS.requestTimeoutMs), Effect.raceFirst(Deferred.await(closed).pipe(Effect.andThen(Effect.interrupt))), Effect.tap((result) => Effect.sync(() => {
14483
+ if (Option.isNone(result)) budget.drop();
14484
+ })), Effect.catchCause(() => Effect.sync(() => {
14485
+ budget.drop();
14486
+ })), Effect.as(droppedResponse()), Effect.ensuring(Effect.sync(() => {
14487
+ release();
14488
+ discardBody(request.body);
14489
+ })));
14490
+ }));
14491
+ }
14492
+ };
14493
+ }
14494
+ //#endregion
13756
14495
  //#region ../../packages/observability/src/engineering.ts
13757
14496
  const ENGINEERING_CAPXUL_ENVS = [
13758
14497
  "development",
@@ -13927,10 +14666,13 @@ const MINIMAL_NUMBERS = /* @__PURE__ */ new Set([
13927
14666
  const MINIMAL_CORRELATIONS = /* @__PURE__ */ new Set([
13928
14667
  "correlation_id",
13929
14668
  "journey_id",
13930
- "invocation_id"
14669
+ "invocation_id",
14670
+ "posthogDistinctId"
13931
14671
  ]);
13932
14672
  const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
13933
14673
  "command",
14674
+ "capability",
14675
+ "invocation_kind",
13934
14676
  "outcome",
13935
14677
  "capxul_env",
13936
14678
  "unit"
@@ -13938,7 +14680,7 @@ const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
13938
14680
  const MINIMAL_METRIC_NAMES = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
13939
14681
  const traceId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{32}$/u.test(value);
13940
14682
  const spanId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{16}$/u.test(value);
13941
- const minimalName = (value) => typeof value === "string" && (MINIMAL_LOG_NAMES.has(value) || isCapxulOperation(value)) ? value : "capxul.engineering";
14683
+ const minimalName = (value) => typeof value === "string" && (MINIMAL_LOG_NAMES.has(value) || isCapxulOperation(value) || CLI_COMMAND_NAMES.some((command) => value === `cli.command.${command}`)) ? value : "capxul.engineering";
13942
14684
  const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
13943
14685
  ["outcome", [
13944
14686
  "succeeded",
@@ -13951,7 +14693,10 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
13951
14693
  ["failure_mode", FAILURE_MODES$1],
13952
14694
  ["capxul_env", ENGINEERING_CAPXUL_ENVS],
13953
14695
  ["producer", ["browser", "server"]],
13954
- ["command", ["doctor"]],
14696
+ ["command", CLI_COMMAND_NAMES],
14697
+ ["capability", CLI_CAPABILITIES],
14698
+ ["invocation_kind", CLI_INVOCATION_KINDS],
14699
+ ["cli_identity_kind", ["anonymous"]],
13955
14700
  ["output_mode", ["human", "json"]],
13956
14701
  ["unit", [
13957
14702
  "1",
@@ -14169,10 +14914,17 @@ function minimalSerialization(config) {
14169
14914
  }
14170
14915
  /** Attempt to drain all engineering signals; completion does not prove ingestion. */
14171
14916
  const flushEngineeringTelemetry = Effect.flatMap(OtlpExporter.Flusher, (flusher) => flusher.flush);
14917
+ var EngineeringLog = class extends Context.Service()("capxul/EngineeringLog") {};
14918
+ /** Deliver an owned engineering record without changing the caller's console verbosity. */
14919
+ const logEngineeringInfo = (message, attributes) => {
14920
+ const record = Effect.logInfo(message).pipe(Effect.annotateLogs(attributes));
14921
+ return Effect.flatMap(LogLevel.isEnabled("Info"), (visible) => visible ? record : Effect.flatMap(EngineeringLog, (logger) => record.pipe(Effect.provideService(Logger.CurrentLoggers, /* @__PURE__ */ new Set([logger])), Effect.provideService(References.MinimumLogLevel, "All"))));
14922
+ };
14172
14923
  /** Shared browser/server OTLP layer. OtlpLogger merges with incumbent loggers once. */
14173
14924
  const makeEngineeringTelemetryLayer = (config) => {
14174
14925
  const validated = validateEngineeringTelemetryConfig(config);
14175
14926
  return Layer.suspend(() => {
14927
+ const bounded = validated.deliveryBudget === void 0 ? void 0 : cliEngineeringDelivery(validated.deliveryBudget, validated.serviceName === "capxul-cli");
14176
14928
  const endpoints = postHogOtlpEndpoints(validated.host);
14177
14929
  let disabled = false;
14178
14930
  const enabled = () => {
@@ -14186,7 +14938,12 @@ const makeEngineeringTelemetryLayer = (config) => {
14186
14938
  };
14187
14939
  const delivery = {
14188
14940
  ...validated.shutdownTimeout === void 0 ? {} : { shutdownTimeout: validated.shutdownTimeout },
14189
- ...validated.exportInterval === void 0 ? {} : { exportInterval: validated.exportInterval }
14941
+ ...validated.exportInterval === void 0 ? {} : { exportInterval: validated.exportInterval },
14942
+ ...bounded === void 0 ? {} : {
14943
+ maxBatchSize: Number.MAX_SAFE_INTEGER,
14944
+ exportInterval: 1e3,
14945
+ shutdownTimeout: validated.serviceName === "capxul-cli" ? 0 : 500
14946
+ }
14190
14947
  };
14191
14948
  const resource = {
14192
14949
  serviceName: validated.serviceName ?? "capxul-sdk",
@@ -14202,22 +14959,38 @@ const makeEngineeringTelemetryLayer = (config) => {
14202
14959
  headers: validated.headers,
14203
14960
  resource,
14204
14961
  ...delivery
14205
- }).pipe(Effect.map((tracer) => makeLeakSafeEngineeringTracer(tracer, enabled)))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
14206
- const logging = Logger.layer([OtlpLogger.make({
14962
+ }).pipe(Effect.map((tracer) => makeLeakSafeEngineeringTracer(bounded?.tracer(tracer) ?? tracer, enabled)))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
14963
+ const ownedLogging = Layer.effect(EngineeringLog, OtlpLogger.make({
14207
14964
  url: endpoints.logs,
14208
14965
  headers: validated.headers,
14209
14966
  resource,
14210
- ...delivery
14211
- }).pipe(Effect.map((logger) => Logger.make((options) => {
14212
- if (enabled()) logger.log(options);
14213
- })))], { mergeWithExisting: true }).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
14214
- const metrics = OtlpMetrics.layer({
14967
+ ...delivery,
14968
+ ...bounded === void 0 ? {} : { excludeLogSpans: true }
14969
+ }).pipe(Effect.map((nativeLogger) => {
14970
+ const logger = bounded?.logger(nativeLogger) ?? nativeLogger;
14971
+ return Logger.make((options) => {
14972
+ if (enabled()) logger.log(options);
14973
+ });
14974
+ }))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
14975
+ const logging = Logger.layer([EngineeringLog], { mergeWithExisting: true }).pipe(Layer.provideMerge(ownedLogging));
14976
+ const metricConfig = {
14215
14977
  url: endpoints.metrics,
14216
14978
  headers: validated.headers,
14217
14979
  resource,
14218
14980
  temporality: "delta",
14219
14981
  ...delivery
14220
- });
14982
+ };
14983
+ const metrics = bounded === void 0 ? OtlpMetrics.layer(metricConfig) : validated.serviceName !== "capxul-cli" ? Layer.empty : Layer.effectDiscard(Effect.gen(function* () {
14984
+ const flusher = yield* OtlpExporter.Flusher;
14985
+ yield* OtlpMetrics.make({
14986
+ ...metricConfig,
14987
+ exportInterval: Number.POSITIVE_INFINITY,
14988
+ shutdownTimeout: 0
14989
+ }).pipe(Effect.provideService(OtlpExporter.Flusher, {
14990
+ ...flusher,
14991
+ register: (run) => flusher.register(bounded.metrics(run))
14992
+ }));
14993
+ })).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
14221
14994
  const headerPolicy = Layer.merge(Layer.succeed(HttpClient.TracerHeaderFilter, traceHeaderFilter), Layer.succeed(Headers.CurrentRedactedNames, [credentialHeaderPattern]));
14222
14995
  const browserLifecycle = Layer.effectDiscard(Effect.gen(function* () {
14223
14996
  if (validated.producer !== "browser" || !hasBrowserDocument()) return;
@@ -14240,17 +15013,25 @@ const makeEngineeringTelemetryLayer = (config) => {
14240
15013
  }));
14241
15014
  }));
14242
15015
  const transport = Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, (client) => {
14243
- const selectedTransport = validated.producer === "browser" && hasBrowserDocument() ? browserExportClient(client) : client;
14244
- return HttpClient.transform(selectedTransport, (response, request) => Effect.suspend(() => enabled() && request.body._tag !== "Empty" ? response : Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null, { status: 200 })))));
15016
+ const originalTransport = validated.producer === "browser" && hasBrowserDocument() ? browserExportClient(client) : client;
15017
+ const selectedTransport = bounded?.transport(originalTransport, enabled) ?? originalTransport;
15018
+ return HttpClient.transform(selectedTransport, (response, request) => Effect.suspend(() => {
15019
+ if (enabled() && request.body._tag !== "Empty") return response;
15020
+ bounded?.discardBody(request.body);
15021
+ return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null, { status: 200 })));
15022
+ }));
14245
15023
  })).pipe(Layer.provide(FetchHttpClient.layer));
14246
15024
  const serialization = Layer.effect(OtlpSerialization.OtlpSerialization, Effect.map(OtlpSerialization.OtlpSerialization, (json) => {
14247
15025
  const projected = minimalSerialization(validated);
14248
- return validated.privacy === "minimal" ? projected : {
15026
+ const selected = validated.privacy === "minimal" ? projected : {
14249
15027
  ...json,
14250
15028
  metrics: projected.metrics
14251
15029
  };
15030
+ return bounded?.serialization(selected) ?? selected;
14252
15031
  })).pipe(Layer.provide(OtlpSerialization.layerJson));
14253
- return browserLifecycle.pipe(Layer.provideMerge(Layer.mergeAll(tracing, logging, metrics, headerPolicy)), Layer.provide(serialization), Layer.provide(transport));
15032
+ const combined = browserLifecycle.pipe(Layer.provideMerge(Layer.mergeAll(tracing, logging, metrics, headerPolicy)), Layer.provide(serialization), Layer.provide(transport));
15033
+ const scoped = bounded === void 0 ? combined : combined.pipe(Layer.provideMerge(Layer.effectDiscard(Effect.addFinalizer(() => Effect.sync(bounded.close)))));
15034
+ return validated.deliveryBudget === void 0 ? scoped : scoped.pipe(Layer.provideMerge(Layer.succeed(Metric.MetricRegistry, validated.serviceName === "capxul-cli" ? validated.deliveryBudget.metricRegistry : validated.deliveryBudget.emptyMetricRegistry())));
14254
15035
  });
14255
15036
  };
14256
15037
  //#endregion
@@ -47769,6 +48550,7 @@ function postHogObservability(client, options = {}) {
47769
48550
  product: postHogProductTelemetry(policy)
47770
48551
  };
47771
48552
  Object.defineProperty(module, HOST_ENGINEERING_POLICY, { value: Object.freeze({
48553
+ ...options.engineeringDeliveryBudget === void 0 ? {} : { engineeringDeliveryBudget: options.engineeringDeliveryBudget },
47772
48554
  ...options.engineeringEnabled === void 0 ? {} : { engineeringEnabled: options.engineeringEnabled },
47773
48555
  ...options.engineeringPrivacy === void 0 ? {} : { engineeringPrivacy: options.engineeringPrivacy }
47774
48556
  }) });
@@ -47806,6 +48588,7 @@ function isPromiseLike(value) {
47806
48588
  function hostEngineeringPolicy(observability) {
47807
48589
  const policy = observability?.[HOST_ENGINEERING_POLICY];
47808
48590
  return {
48591
+ ...policy?.engineeringDeliveryBudget === void 0 ? {} : { deliveryBudget: policy.engineeringDeliveryBudget },
47809
48592
  ...policy?.engineeringEnabled === void 0 ? {} : { enabled: policy.engineeringEnabled },
47810
48593
  ...policy?.engineeringPrivacy === void 0 ? {} : { privacy: policy.engineeringPrivacy }
47811
48594
  };
@@ -49020,6 +49803,82 @@ function captureException(telemetry, error, context) {
49020
49803
  }).pipe(Effect.flatten), () => Effect.void);
49021
49804
  }
49022
49805
  //#endregion
49806
+ //#region src/platform/observation-identity.ts
49807
+ const uuid = Schema.String.check(Schema.isPattern(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u));
49808
+ const person = Schema.String.check(Schema.isPattern(/^[^\s]{1,256}$/u));
49809
+ const binding = Schema.Struct({
49810
+ authUserId: person,
49811
+ eventId: uuid
49812
+ });
49813
+ const epoch = Schema.Struct({
49814
+ id: uuid,
49815
+ binding: Schema.NullOr(binding)
49816
+ });
49817
+ const ObservationIdentity = Schema.Struct({
49818
+ version: Schema.Literal(1),
49819
+ firstUse: Schema.Struct({
49820
+ eventId: uuid,
49821
+ observedAt: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
49822
+ }),
49823
+ epoch
49824
+ });
49825
+ const ObservationIdentityChange = Schema.Union([
49826
+ Schema.Struct({ kind: Schema.Literal("acquire") }),
49827
+ Schema.Struct({
49828
+ kind: Schema.Literal("bind"),
49829
+ expectedEpoch: uuid,
49830
+ authUserId: person
49831
+ }),
49832
+ Schema.Struct({
49833
+ kind: Schema.Literal("rotate"),
49834
+ expectedEpoch: uuid
49835
+ })
49836
+ ]);
49837
+ /** Observation bookkeeping only. The caller must obtain verified person facts from AUTH. */
49838
+ function transitionObservationIdentity(previous, change, fresh) {
49839
+ if (change.kind !== "acquire" && previous?.epoch.id !== change.expectedEpoch) return {
49840
+ state: previous,
49841
+ result: { status: "stale" }
49842
+ };
49843
+ let state = previous ?? {
49844
+ version: 1,
49845
+ firstUse: {
49846
+ eventId: fresh.eventId,
49847
+ observedAt: fresh.observedAt
49848
+ },
49849
+ epoch: {
49850
+ id: fresh.epochId,
49851
+ binding: null
49852
+ }
49853
+ };
49854
+ if (change.kind === "rotate" || change.kind === "acquire" && state.epoch.binding !== null) state = {
49855
+ ...state,
49856
+ epoch: {
49857
+ id: fresh.epochId,
49858
+ binding: null
49859
+ }
49860
+ };
49861
+ else if (change.kind === "bind" && state.epoch.binding?.authUserId !== change.authUserId) state = {
49862
+ ...state,
49863
+ epoch: {
49864
+ id: state.epoch.binding === null ? state.epoch.id : fresh.epochId,
49865
+ binding: {
49866
+ authUserId: change.authUserId,
49867
+ eventId: fresh.eventId
49868
+ }
49869
+ }
49870
+ };
49871
+ return {
49872
+ state,
49873
+ result: {
49874
+ status: "ready",
49875
+ firstUse: state.firstUse,
49876
+ firstUseClaimed: previous === null,
49877
+ epoch: state.epoch
49878
+ }
49879
+ };
49880
+ }
49881
+ //#endregion
49023
49882
  //#region src/platform/state-files.ts
49024
49883
  const Settings = Schema.Struct({
49025
49884
  version: Schema.Literal(1),
@@ -49163,11 +50022,12 @@ const withLock = (file, body) => Effect.scoped(Effect.gen(function* () {
49163
50022
  }));
49164
50023
  return yield* body(path.join(lock, `${owner}.data`));
49165
50024
  }));
49166
- const replace = (file, content, validate) => Effect.gen(function* () {
50025
+ const replace = (file, update) => Effect.gen(function* () {
49167
50026
  const fs = yield* FileSystem.FileSystem;
49168
- if (new TextEncoder().encode(content).length > 65536) return yield* Effect.fail(fail("Corrupt"));
49169
50027
  return yield* withLock(file, (temporary) => Effect.gen(function* () {
49170
- yield* validate;
50028
+ const { content, value } = yield* update;
50029
+ if (content === null) return value;
50030
+ if (new TextEncoder().encode(content).length > 65536) return yield* Effect.fail(fail("Corrupt"));
49171
50031
  const handle = yield* fs.open(temporary, {
49172
50032
  flag: "wx",
49173
50033
  mode: 384
@@ -49179,6 +50039,7 @@ const replace = (file, content, validate) => Effect.gen(function* () {
49179
50039
  const path = yield* Path.Path;
49180
50040
  yield* (yield* fs.open(path.dirname(file))).sync;
49181
50041
  }));
50042
+ return value;
49182
50043
  }));
49183
50044
  });
49184
50045
  const setCollectionEnabled = (file, enabled) => Effect.gen(function* () {
@@ -49187,9 +50048,33 @@ const setCollectionEnabled = (file, enabled) => Effect.gen(function* () {
49187
50048
  version: 1,
49188
50049
  collection: { enabled }
49189
50050
  };
49190
- yield* replace(location, JSON.stringify(settings), readSettings(location));
50051
+ yield* replace(location, readSettings(location).pipe(Effect.as({
50052
+ content: JSON.stringify(settings),
50053
+ value: void 0
50054
+ })));
49191
50055
  return settings;
49192
50056
  }).pipe(Effect.mapError(storageError));
50057
+ const changeObservationIdentity = (directory, input) => Effect.gen(function* () {
50058
+ const change = yield* Schema.decodeUnknownEffect(ObservationIdentityChange, { onExcessProperty: "error" })(input).pipe(Effect.mapError(() => fail("Corrupt")));
50059
+ const path = yield* Path.Path;
50060
+ const location = yield* locate(path.join(directory, "observation.json"), true);
50061
+ return yield* replace(location, Effect.gen(function* () {
50062
+ const content = yield* readFile(location);
50063
+ const previous = content === null ? null : yield* Schema.decodeUnknownEffect(Schema.fromJsonString(ObservationIdentity), { onExcessProperty: "error" })(content).pipe(Effect.mapError(() => fail("Corrupt")));
50064
+ const crypto = yield* Crypto.Crypto;
50065
+ const next = transitionObservationIdentity(previous, change, {
50066
+ epochId: yield* crypto.randomUUIDv4,
50067
+ eventId: yield* crypto.randomUUIDv4,
50068
+ observedAt: yield* Clock.currentTimeMillis
50069
+ });
50070
+ return {
50071
+ content: next.state === previous ? null : JSON.stringify(next.state),
50072
+ value: next.result
50073
+ };
50074
+ }));
50075
+ }).pipe(Effect.mapError(storageError));
50076
+ /** Call only for eligible collection. This never infers AUTH identity from stored state. */
50077
+ const acquireObservationIdentity = (directory) => changeObservationIdentity(directory, { kind: "acquire" });
49193
50078
  //#endregion
49194
50079
  //#region src/platform/collection-policy.ts
49195
50080
  const decode = Schema.decodeUnknownOption(Schema.fromJsonString(Settings), { onExcessProperty: "error" });
@@ -49299,6 +50184,8 @@ function parsedCommand(cause) {
49299
50184
  }
49300
50185
  //#endregion
49301
50186
  //#region src/runtime/config.ts
50187
+ const defaultHost = "https://us.i.posthog.com";
50188
+ const defaultProjectToken = Redacted.make("phc_BZuAVofrkNXzwpJkuDG7Rdfurir45fj2dudQvJZk847e");
49302
50189
  const optionalText = (name) => Config.option(Config.string(name));
49303
50190
  function httpOrigin(value, field) {
49304
50191
  return Effect.try({
@@ -49312,14 +50199,12 @@ function httpOrigin(value, field) {
49312
50199
  catch: () => configurationFailure(`The ${field} URL must be a valid approved origin.`)
49313
50200
  });
49314
50201
  }
49315
- const loadConfig = Effect.gen(function* () {
50202
+ const loadObservationConfig = Effect.gen(function* () {
49316
50203
  const path = yield* Path.Path;
49317
50204
  const values = yield* Config.all({
49318
50205
  cliHome: optionalText("CAPXUL_CLI_HOME"),
49319
50206
  home: optionalText("HOME"),
49320
50207
  xdg: optionalText("XDG_CONFIG_HOME"),
49321
- key: Config.option(Config.redacted("CAPXUL_PUBLISHABLE_KEY")),
49322
- bootstrap: Config.string("CAPXUL_BOOTSTRAP_URL").pipe(Config.withDefault("https://api.staging.capxul.com")),
49323
50208
  host: optionalText("CAPXUL_POSTHOG_HOST"),
49324
50209
  token: Config.option(Config.redacted("CAPXUL_POSTHOG_PROJECT_TOKEN")),
49325
50210
  disabled: Config.boolean("CAPXUL_TELEMETRY_DISABLED").pipe(Config.withDefault(false))
@@ -49333,19 +50218,29 @@ const loadConfig = Effect.gen(function* () {
49333
50218
  else return yield* Effect.fail(configurationFailure("Set HOME or CAPXUL_CLI_HOME."));
49334
50219
  }
49335
50220
  const settingsPath = path.join(path.resolve(directory), "settings.json");
49336
- const bootstrapBaseUrl = yield* httpOrigin(values.bootstrap, "bootstrap");
49337
- const posthogHost = Option.isSome(values.host) ? yield* httpOrigin(values.host.value, "PostHog") : void 0;
50221
+ const host = Option.getOrUndefined(values.host) ?? defaultHost;
49338
50222
  return {
49339
50223
  settingsPath,
49340
- publishableKey: Option.getOrUndefined(values.key),
49341
- bootstrapBaseUrl,
49342
50224
  environment: "staging",
49343
- posthogHost,
49344
- projectToken: Option.getOrUndefined(values.token),
50225
+ posthogHost: host === void 0 ? void 0 : yield* httpOrigin(host, "PostHog"),
50226
+ projectToken: Option.getOrUndefined(values.token) ?? defaultProjectToken,
49345
50227
  forcedDisabled: values.disabled,
49346
50228
  collectionEnabled: collectionPolicy(settingsPath, values.disabled)
49347
50229
  };
49348
50230
  });
50231
+ const loadConfig = Effect.gen(function* () {
50232
+ const observation = yield* loadObservationConfig;
50233
+ const values = yield* Config.all({
50234
+ key: Config.option(Config.redacted("CAPXUL_PUBLISHABLE_KEY")),
50235
+ bootstrap: Config.string("CAPXUL_BOOTSTRAP_URL").pipe(Config.withDefault("https://api.staging.capxul.com"))
50236
+ }).pipe(Effect.mapError(() => configurationFailure("CLI environment configuration is invalid.")));
50237
+ const bootstrapBaseUrl = yield* httpOrigin(values.bootstrap, "bootstrap");
50238
+ return {
50239
+ ...observation,
50240
+ bootstrapBaseUrl,
50241
+ publishableKey: Option.getOrUndefined(values.key)
50242
+ };
50243
+ });
49349
50244
  const collectionStatus = (config) => Effect.gen(function* () {
49350
50245
  const stored = (yield* readSettings(config.settingsPath))?.collection.enabled ?? null;
49351
50246
  const enabled = config.collectionEnabled();
@@ -49369,33 +50264,13 @@ const codes = [
49369
50264
  ];
49370
50265
  const identifier = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u));
49371
50266
  const version = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u));
49372
- const outcomeSchema = Schema.Literals([
49373
- "succeeded",
49374
- "refused",
49375
- "failed",
49376
- "cancelled"
49377
- ]);
49378
50267
  const completionSchema = Schema.Struct({
49379
- schema_version: Schema.Literal(1),
49380
- command: Schema.Literal("doctor"),
49381
- invocation_id: identifier,
50268
+ ...CliCommandCompletedTelemetryEventSchema.fields.props.fields,
49382
50269
  correlation_id: identifier,
49383
50270
  journey_id: identifier,
49384
- capxul_env: Schema.Literals([
49385
- "development",
49386
- "staging",
49387
- "production",
49388
- "local"
49389
- ]),
49390
- producer: Schema.Literal("server"),
49391
- cli_version: version,
49392
- sdk_version: version,
49393
- outcome: outcomeSchema,
49394
- duration_ms: Schema.Number,
50271
+ posthogDistinctId: Schema.optional(identifier),
49395
50272
  wait_duration_ms: Schema.Number,
49396
- retry_count: Schema.Literal(0),
49397
- output_mode: Schema.Literals(["human", "json"]),
49398
- error_code: Schema.optional(Schema.Literals(codes))
50273
+ retry_count: Schema.Literal(0)
49399
50274
  });
49400
50275
  const CAPTURE_CAPACITY = 32;
49401
50276
  const CAPTURE_BYTES = 8192;
@@ -49409,13 +50284,15 @@ const own = (value, key) => {
49409
50284
  return;
49410
50285
  }
49411
50286
  };
49412
- const inactiveHost = () => postHogObservability(null, {
50287
+ const inactiveHost = (budget) => postHogObservability(null, {
49413
50288
  enabled: false,
49414
- engineeringEnabled: false
50289
+ engineeringEnabled: false,
50290
+ engineeringDeliveryBudget: budget
49415
50291
  });
49416
50292
  /** One invocation owns its records, metric registry, callback queue, and total flush deadline. */
49417
50293
  function observeInvocation(input, work) {
49418
50294
  return Effect.suspend(() => {
50295
+ const deliveryBudget = makeCliEngineeringBudget();
49419
50296
  const finalizers = [];
49420
50297
  const onFinalize = (finalizer) => {
49421
50298
  finalizers.push(finalizer);
@@ -49424,7 +50301,7 @@ function observeInvocation(input, work) {
49424
50301
  concurrency: "unbounded",
49425
50302
  discard: true
49426
50303
  }));
49427
- const inactiveWork = Effect.acquireUseRelease(Effect.void, () => Effect.suspend(() => work(inactiveHost(), onFinalize)), () => finalize.pipe(Effect.interruptible, Effect.timeoutOption("2 seconds"), Effect.asVoid));
50304
+ const inactiveWork = Effect.acquireUseRelease(Effect.void, () => Effect.suspend(() => work(inactiveHost(deliveryBudget), onFinalize)), () => finalize.pipe(Effect.interruptible, Effect.timeoutOption("2 seconds"), Effect.asVoid));
49428
50305
  let disabled = false;
49429
50306
  const enabled = () => {
49430
50307
  if (disabled) return false;
@@ -49435,12 +50312,13 @@ function observeInvocation(input, work) {
49435
50312
  }
49436
50313
  return !disabled;
49437
50314
  };
49438
- if (input.host === void 0 || input.projectToken === void 0 || !enabled()) return inactiveWork;
50315
+ if (input.kind === "completion" || input.kind === "collection_control" || input.host === void 0 || input.projectToken === void 0 || !enabled()) return inactiveWork;
49439
50316
  const host = input.host;
49440
50317
  const token = Redacted.value(input.projectToken);
49441
50318
  let engineering;
49442
50319
  try {
49443
50320
  Schema.decodeUnknownSync(identifier)(input.invocationId);
50321
+ if (input.anonymousIdentity !== void 0) Schema.decodeUnknownSync(identifier)(input.anonymousIdentity.id);
49444
50322
  Schema.decodeUnknownSync(version)(input.cliVersion);
49445
50323
  engineering = makeEngineeringTelemetryLayer({
49446
50324
  host,
@@ -49449,6 +50327,7 @@ function observeInvocation(input, work) {
49449
50327
  producer: "server",
49450
50328
  sdkVersion: input.sdkVersion,
49451
50329
  serviceName: "capxul-cli",
50330
+ deliveryBudget,
49452
50331
  enabled,
49453
50332
  shutdownTimeout: 0,
49454
50333
  exportInterval: "1 hour"
@@ -49456,7 +50335,7 @@ function observeInvocation(input, work) {
49456
50335
  } catch {
49457
50336
  return inactiveWork;
49458
50337
  }
49459
- const layer = Layer.merge(engineering, FetchHttpClient.layer).pipe(Layer.provideMerge(Layer.succeed(Metric.MetricRegistry, /* @__PURE__ */ new Map())), Layer.provideMerge(Layer.succeed(Logger.LogToStderr, true)));
50338
+ const layer = Layer.merge(engineering, FetchHttpClient.layer).pipe(Layer.provideMerge(Layer.succeed(Logger.LogToStderr, true)));
49460
50339
  return Effect.scoped(Effect.gen(function* () {
49461
50340
  const built = yield* Effect.exit(Layer.build(layer));
49462
50341
  if (Exit.isFailure(built)) return yield* inactiveWork;
@@ -49472,30 +50351,47 @@ function observeInvocation(input, work) {
49472
50351
  producer: "server",
49473
50352
  cli_version: input.cliVersion,
49474
50353
  sdk_version: input.sdkVersion,
50354
+ schema_version: 1,
50355
+ command: input.command,
50356
+ capability: input.capability,
50357
+ invocation_kind: input.kind,
50358
+ release: input.cliVersion,
50359
+ platform: input.platform ?? "unknown",
50360
+ architecture: input.architecture ?? "unknown",
50361
+ ci: input.ci ?? false,
50362
+ tty: input.tty ?? false,
50363
+ output_mode: input.outputMode ?? "human",
50364
+ ...input.anonymousIdentity === void 0 ? {} : {
50365
+ anonymous_id: input.anonymousIdentity.id,
50366
+ cli_identity_kind: "anonymous"
50367
+ },
49475
50368
  invocation_id: input.invocationId,
49476
50369
  correlation_id: input.invocationId,
49477
50370
  journey_id: input.invocationId
49478
50371
  };
49479
- const capture = (event, properties) => {
50372
+ const capture = (event, properties, identity) => {
49480
50373
  if (!enabled()) {
49481
50374
  dropped += 1;
49482
50375
  return;
49483
50376
  }
50377
+ const occurredAt = identity?.observedAt ?? clock.currentTimeMillisUnsafe();
49484
50378
  const record = sanitizePostHogBrowserEvent({
49485
50379
  event,
49486
50380
  properties: {
49487
50381
  ...properties,
49488
50382
  ...envelope,
50383
+ occurred_at: occurredAt,
49489
50384
  ...nativeContext
49490
50385
  }
49491
50386
  });
49492
50387
  const body = JSON.stringify({
49493
50388
  api_key: token,
49494
50389
  ...record,
49495
- uuid: crypto.randomUUID(),
50390
+ uuid: identity?.eventId ?? crypto.randomUUID(),
50391
+ timestamp: new Date(occurredAt).toISOString(),
49496
50392
  properties: {
49497
50393
  ...record.properties,
49498
- distinct_id: input.invocationId,
50394
+ distinct_id: input.anonymousIdentity?.id ?? input.invocationId,
49499
50395
  $process_person_profile: false
49500
50396
  }
49501
50397
  });
@@ -49528,9 +50424,40 @@ function observeInvocation(input, work) {
49528
50424
  ...envelope
49529
50425
  }
49530
50426
  });
49531
- if (Option.isSome(decoded) && Queue.sizeUnsafe(queue) < 30) capture(event, { ...decoded.value.props });
50427
+ if (Option.isSome(decoded) && Queue.sizeUnsafe(queue) < 29) capture(event, { ...decoded.value.props });
49532
50428
  else dropped += 1;
49533
50429
  } };
50430
+ const clientHttp = HttpClient.transform(yield* HttpClient.HttpClient, (response, request) => Effect.suspend(() => {
50431
+ if (enabled()) return response;
50432
+ dropped += 1;
50433
+ return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
50434
+ })).pipe(HttpClient.filterStatusOk, HttpClient.retryTransient({
50435
+ times: 1,
50436
+ schedule: Schedule.spaced("100 millis")
50437
+ }));
50438
+ const flushCaptures = Effect.gen(function* () {
50439
+ const pending = yield* Queue.clear(queue);
50440
+ if (!enabled()) {
50441
+ dropped += pending.length;
50442
+ return;
50443
+ }
50444
+ pendingCaptures += pending.length;
50445
+ yield* Effect.forEach(pending, (item) => Effect.suspend(() => {
50446
+ if (!enabled()) {
50447
+ dropped += 1;
50448
+ return Effect.void;
50449
+ }
50450
+ return clientHttp.execute(HttpClientRequest.post(`${host.replace(/\/+$/, "")}/capture/`).pipe(HttpClientRequest.bodyText(item, "application/json"))).pipe(Effect.asVoid, Effect.catchCause(() => Effect.sync(() => {
50451
+ dropped += 1;
50452
+ })));
50453
+ }).pipe(Effect.ensuring(Effect.sync(() => {
50454
+ pendingCaptures -= 1;
50455
+ }))), {
50456
+ concurrency: 4,
50457
+ discard: true
50458
+ });
50459
+ }).pipe(Effect.withTracerEnabled(false), Effect.provideService(HttpClient.TracerPropagationEnabled, false));
50460
+ let startDelivery;
49534
50461
  let commandResult;
49535
50462
  yield* Effect.gen(function* () {
49536
50463
  const span = yield* Effect.currentSpan.pipe(Effect.orDie);
@@ -49543,15 +50470,39 @@ function observeInvocation(input, work) {
49543
50470
  enabled,
49544
50471
  engineeringEnabled: enabled,
49545
50472
  engineeringPrivacy: "minimal",
50473
+ engineeringDeliveryBudget: deliveryBudget,
49546
50474
  capxulEnv: input.environment,
49547
50475
  context: {
49548
50476
  application: "capxul-cli",
50477
+ ...input.anonymousIdentity === void 0 ? {} : { anonymousId: input.anonymousIdentity.id },
49549
50478
  release: input.cliVersion,
49550
50479
  correlationId: input.invocationId,
49551
50480
  journeyId: input.invocationId,
49552
50481
  traceparent
49553
50482
  }
49554
50483
  });
50484
+ const firstUse = input.anonymousIdentity?.firstUse;
50485
+ if (firstUse !== void 0) {
50486
+ const first = Schema.decodeUnknownSync(CliFirstUsedTelemetryEventSchema)({
50487
+ name: "cli_first_used",
50488
+ props: {
50489
+ ...envelope,
50490
+ occurred_at: firstUse.observedAt
50491
+ }
50492
+ });
50493
+ capture("cli_first_used", { ...first.props }, firstUse);
50494
+ }
50495
+ if (input.kind !== "parse_refusal") {
50496
+ const start = Schema.decodeUnknownSync(CliCommandStartedTelemetryEventSchema)({
50497
+ name: "cli_command_started",
50498
+ props: {
50499
+ ...envelope,
50500
+ occurred_at: clock.currentTimeMillisUnsafe()
50501
+ }
50502
+ });
50503
+ capture("cli_command_started", { ...start.props });
50504
+ startDelivery = yield* Effect.forkScoped(flushCaptures.pipe(Effect.interruptible), { startImmediately: true });
50505
+ }
49555
50506
  const waitStarted = clock.currentTimeMillisUnsafe();
49556
50507
  const result = yield* Effect.exit(restore(Effect.suspend(() => work(observation, onFinalize))));
49557
50508
  commandResult = result;
@@ -49564,8 +50515,12 @@ function observeInvocation(input, work) {
49564
50515
  const duration = Math.max(0, clock.currentTimeMillisUnsafe() - started);
49565
50516
  const completion = Schema.decodeUnknownSync(completionSchema)({
49566
50517
  ...envelope,
50518
+ ...input.anonymousIdentity === void 0 ? {} : { posthogDistinctId: input.anonymousIdentity.id },
50519
+ occurred_at: clock.currentTimeMillisUnsafe(),
49567
50520
  schema_version: 1,
49568
50521
  command: input.command,
50522
+ capability: input.capability,
50523
+ invocation_kind: input.kind,
49569
50524
  outcome,
49570
50525
  duration_ms: duration,
49571
50526
  wait_duration_ms: waitDuration,
@@ -49574,13 +50529,15 @@ function observeInvocation(input, work) {
49574
50529
  ...errorCode === void 0 ? {} : { error_code: errorCode }
49575
50530
  });
49576
50531
  yield* Effect.annotateCurrentSpan(completion);
49577
- yield* Effect.logInfo("cli.command.completed").pipe(Effect.annotateLogs(completion));
50532
+ yield* logEngineeringInfo("cli.command.completed", completion);
49578
50533
  for (const diagnostic of sdkDiagnostics) yield* Effect.logInfo("cli.sdk.operation.failed").pipe(Effect.annotateLogs({
49579
50534
  ...envelope,
49580
50535
  ...diagnostic
49581
50536
  }));
49582
50537
  const metricAttributes = {
49583
50538
  command: input.command,
50539
+ capability: input.capability,
50540
+ invocation_kind: input.kind,
49584
50541
  outcome,
49585
50542
  capxul_env: input.environment
49586
50543
  };
@@ -49600,15 +50557,27 @@ function observeInvocation(input, work) {
49600
50557
  unit: "s"
49601
50558
  }
49602
50559
  }), duration / 1e3);
49603
- const event = {
49604
- name: "cli_diagnostic_completed",
50560
+ const terminal = Schema.decodeUnknownSync(CliCommandCompletedTelemetryEventSchema)({
50561
+ name: "cli_command_completed",
49605
50562
  props: {
49606
50563
  ...envelope,
49607
- outcome
50564
+ occurred_at: clock.currentTimeMillisUnsafe(),
50565
+ outcome,
50566
+ duration_ms: duration,
50567
+ ...errorCode === void 0 ? {} : { error_code: errorCode }
49608
50568
  }
49609
- };
49610
- const decoded = Schema.decodeUnknownSync(CliDiagnosticCompletedTelemetryEventSchema)(event);
49611
- capture(event.name, { ...decoded.props });
50569
+ });
50570
+ capture("cli_command_completed", { ...terminal.props });
50571
+ if (input.command === "doctor" && input.kind === "command" && input.network) {
50572
+ const diagnostic = Schema.decodeUnknownSync(CliDiagnosticCompletedTelemetryEventSchema)({
50573
+ name: "cli_diagnostic_completed",
50574
+ props: {
50575
+ ...envelope,
50576
+ outcome
50577
+ }
50578
+ });
50579
+ capture("cli_diagnostic_completed", { ...diagnostic.props });
50580
+ }
49612
50581
  if (Exit.isFailure(result)) {
49613
50582
  const defect = result.cause.reasons.find(Cause.isDieReason);
49614
50583
  if (defect !== void 0) {
@@ -49619,7 +50588,7 @@ function observeInvocation(input, work) {
49619
50588
  if (typeof stack === "string") error.stack = stack.slice(0, 8192);
49620
50589
  } catch {}
49621
50590
  yield* captureException({
49622
- emit: (exception) => Effect.sync(() => capture("$exception", projectException(exception.props))),
50591
+ emit: (exception) => Effect.sync(() => capture("$exception", projectException(exception.props, input.command))),
49623
50592
  identify: () => Effect.void,
49624
50593
  group: () => Effect.void,
49625
50594
  reset: () => Effect.void
@@ -49628,41 +50597,12 @@ function observeInvocation(input, work) {
49628
50597
  }
49629
50598
  }).pipe(Effect.catchCause(() => Effect.void));
49630
50599
  if (Exit.isFailure(result)) return yield* Effect.fail("CLI command failed");
49631
- }).pipe(Effect.withSpan("cli.command.doctor"), Effect.exit);
49632
- const clientHttp = HttpClient.transform(yield* HttpClient.HttpClient, (response, request) => Effect.suspend(() => {
49633
- if (enabled()) return response;
49634
- dropped += 1;
49635
- return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
49636
- })).pipe(HttpClient.filterStatusOk, HttpClient.retryTransient({
49637
- times: 1,
49638
- schedule: Schedule.spaced("100 millis")
49639
- }));
49640
- const flushCaptures = Effect.gen(function* () {
49641
- const pending = yield* Queue.clear(queue);
49642
- if (!enabled()) {
49643
- dropped += pending.length;
49644
- return;
49645
- }
49646
- pendingCaptures = pending.length;
49647
- yield* Effect.forEach(pending, (item) => Effect.suspend(() => {
49648
- if (!enabled()) {
49649
- dropped += 1;
49650
- return Effect.void;
49651
- }
49652
- return clientHttp.execute(HttpClientRequest.post(`${host.replace(/\/+$/, "")}/capture/`).pipe(HttpClientRequest.bodyText(item, "application/json"))).pipe(Effect.asVoid, Effect.catchCause(() => Effect.sync(() => {
49653
- dropped += 1;
49654
- })));
49655
- }).pipe(Effect.ensuring(Effect.sync(() => {
49656
- pendingCaptures -= 1;
49657
- }))), {
49658
- concurrency: 4,
49659
- discard: true
49660
- });
49661
- }).pipe(Effect.withTracerEnabled(false), Effect.provideService(HttpClient.TracerPropagationEnabled, false));
50600
+ }).pipe(Effect.withSpan(`cli.command.${input.command}`), Effect.exit);
49662
50601
  yield* Effect.all([
49663
50602
  finalize,
49664
50603
  flushCaptures,
49665
- flushEngineeringTelemetry
50604
+ flushEngineeringTelemetry,
50605
+ startDelivery === void 0 ? Effect.void : Fiber.join(startDelivery)
49666
50606
  ], {
49667
50607
  concurrency: "unbounded",
49668
50608
  discard: true
@@ -49672,6 +50612,7 @@ function observeInvocation(input, work) {
49672
50612
  yield* Queue.shutdown(queue);
49673
50613
  try {
49674
50614
  input.onDropped?.(dropped);
50615
+ input.onEngineeringDropped?.(deliveryBudget.snapshot().dropped);
49675
50616
  } catch {}
49676
50617
  if (commandResult === void 0) return yield* Effect.die("CLI observation could not start");
49677
50618
  return yield* commandResult;
@@ -49706,7 +50647,7 @@ function projectSdkProperties(properties) {
49706
50647
  if (failureMode !== void 0) output.failure_mode = failureMode;
49707
50648
  return output;
49708
50649
  }
49709
- function projectException(properties) {
50650
+ function projectException(properties, command) {
49710
50651
  const sanitized = sanitizePostHogBrowserEvent({
49711
50652
  event: "$exception",
49712
50653
  properties: { ...properties }
@@ -49730,8 +50671,8 @@ function projectException(properties) {
49730
50671
  $exception_type: "CLI_DEFECT",
49731
50672
  $exception_message: "CLI command failed",
49732
50673
  $exception_level: "error",
49733
- $exception_fingerprint: `cli.doctor.CLI_DEFECT:${frames[0]?.filename ?? "boundary"}:${frames[0]?.function ?? "unknown"}`,
49734
- $issue_name: "CLI doctor defect",
50674
+ $exception_fingerprint: `cli.${command}.CLI_DEFECT:${frames[0]?.filename ?? "boundary"}:${frames[0]?.function ?? "unknown"}`,
50675
+ $issue_name: `CLI ${command} defect`,
49735
50676
  $exception_list: [{
49736
50677
  type: "CLI_DEFECT",
49737
50678
  value: "CLI command failed",
@@ -49757,41 +50698,101 @@ function renderResult(mode, envelope, lines = []) {
49757
50698
  }
49758
50699
  //#endregion
49759
50700
  //#region src/version.ts
49760
- const cliVersion = "4.20.0-beta.2";
49761
- const sdkVersion = "4.20.0-beta.2";
50701
+ const cliVersion = "4.20.0-beta.4";
50702
+ const sdkVersion = "4.20.0-beta.4";
50703
+ //#endregion
50704
+ //#region src/runtime/command-metadata.ts
50705
+ var CliCommandMetadata = class extends Context.Service()("capxul-cli/CommandMetadata") {};
50706
+ const collectionControl = (name) => name === "telemetry" || name === "telemetry.status" || name === "telemetry.enable" || name === "telemetry.disable";
50707
+ function metadataAtPath(root, path) {
50708
+ let selected = root;
50709
+ for (const part of path.slice(1)) {
50710
+ const child = selected.subcommands.flatMap((group) => group.commands).find((command) => command.name === part || command.alias === part);
50711
+ if (child === void 0) break;
50712
+ selected = child;
50713
+ }
50714
+ const metadata = Context.getOption(selected.annotations, CliCommandMetadata);
50715
+ return Option.isSome(metadata) && CLI_COMMAND_NAMES.includes(metadata.value.name) ? metadata.value : {
50716
+ name: "unknown",
50717
+ capability: "cli",
50718
+ observation: "command"
50719
+ };
50720
+ }
49762
50721
  //#endregion
49763
50722
  //#region src/runtime/run-command.ts
49764
50723
  var CliRuntime = class extends Context.Service()("capxul-cli/Runtime") {};
49765
- const runCommand = (options, work) => Effect.gen(function* () {
49766
- const runtime = yield* CliRuntime;
49767
- const invocationId = yield* (yield* Crypto.Crypto).randomUUIDv4;
49768
- yield* Ref.set(runtime.current, {
49769
- command: options.name,
49770
- invocationId
49771
- });
49772
- const config = yield* loadConfig;
50724
+ const acquireInvocationIdentity = (config, eligible) => Effect.gen(function* () {
50725
+ if (!eligible || config?.posthogHost === void 0 || config.projectToken === void 0 || !config.collectionEnabled()) return void 0;
50726
+ const path = yield* Path.Path;
50727
+ const acquired = yield* acquireObservationIdentity(path.dirname(config.settingsPath));
50728
+ return acquired.status === "ready" ? acquired : void 0;
50729
+ }).pipe(Effect.catchCause(() => Effect.succeed(void 0)));
50730
+ const runInvocation = (runtime, metadata, kind, work, network = false) => Effect.gen(function* () {
50731
+ const current = yield* Ref.get(runtime.current);
50732
+ if (current !== null) return yield* work(current.host, current.onFinalize);
50733
+ const invocationKind = kind === "completion" ? kind : collectionControl(metadata.name) ? "collection_control" : kind;
50734
+ const silent = invocationKind === "completion" || invocationKind === "collection_control";
50735
+ const invocation = {
50736
+ command: metadata.name,
50737
+ invocationId: runtime.invocationId,
50738
+ kind
50739
+ };
50740
+ yield* Ref.set(runtime.current, invocation);
50741
+ const config = runtime.observation;
50742
+ const identity = yield* acquireInvocationIdentity(config, !silent);
49773
50743
  let dropped = 0;
49774
- const result = options.network ? yield* observeInvocation({
49775
- command: "doctor",
49776
- invocationId,
50744
+ let engineeringDropped = 0;
50745
+ return yield* observeInvocation({
50746
+ command: metadata.name,
50747
+ capability: metadata.capability,
50748
+ kind: invocationKind,
50749
+ network,
50750
+ invocationId: runtime.invocationId,
50751
+ ...identity === void 0 ? {} : { anonymousIdentity: {
50752
+ id: identity.epoch.id,
50753
+ ...identity.firstUseClaimed ? { firstUse: identity.firstUse } : {}
50754
+ } },
49777
50755
  cliVersion,
49778
50756
  sdkVersion,
49779
- environment: config.environment,
49780
- ...config.posthogHost === void 0 ? {} : { host: config.posthogHost },
49781
- ...config.projectToken === void 0 ? {} : { projectToken: config.projectToken },
49782
- enabled: config.collectionEnabled,
50757
+ environment: config?.environment ?? "staging",
50758
+ platform: runtime.platform === "darwin" || runtime.platform === "linux" ? runtime.platform : "unknown",
50759
+ architecture: runtime.architecture === "arm64" || runtime.architecture === "x64" ? runtime.architecture : "unknown",
50760
+ ci: runtime.ci,
50761
+ tty: runtime.tty,
50762
+ ...!silent && config?.posthogHost !== void 0 ? { host: config.posthogHost } : {},
50763
+ ...!silent && config?.projectToken !== void 0 ? { projectToken: config.projectToken } : {},
50764
+ enabled: identity === void 0 ? () => false : config?.collectionEnabled ?? (() => false),
49783
50765
  outputMode: runtime.mode,
49784
50766
  onDropped: (count) => {
49785
50767
  dropped = count;
50768
+ },
50769
+ onEngineeringDropped: (count) => {
50770
+ engineeringDropped = count;
49786
50771
  }
49787
- }, (observation, onFinalize) => work(config, observation, onFinalize)).pipe(Effect.onExit(() => dropped > 0 ? writeStderr(`Telemetry dropped ${dropped} records.\n`) : Effect.void)) : yield* work(config);
49788
- yield* renderResult(runtime.mode, {
49789
- version: 1,
49790
- command: options.name,
49791
- invocationId,
49792
- outcome: "succeeded",
49793
- data: result.data
49794
- }, result.lines);
50772
+ }, (host, onFinalize) => Effect.gen(function* () {
50773
+ yield* Ref.set(runtime.current, {
50774
+ ...invocation,
50775
+ host,
50776
+ onFinalize
50777
+ });
50778
+ return yield* work(host, onFinalize);
50779
+ })).pipe(Effect.onExit(() => {
50780
+ const notice = (dropped > 0 ? `Telemetry dropped ${dropped} records.\n` : "") + (engineeringDropped > 0 ? `Engineering telemetry dropped ${engineeringDropped} items.\n` : "");
50781
+ return notice === "" ? Effect.void : writeStderr(notice).pipe(Effect.catchCause(() => Effect.void));
50782
+ }));
50783
+ });
50784
+ const runCommand = (options, work) => Effect.gen(function* () {
50785
+ const runtime = yield* CliRuntime;
50786
+ return yield* runInvocation(runtime, options, "command", (host, onFinalize) => Effect.gen(function* () {
50787
+ const result = yield* work(yield* loadConfig, host, onFinalize);
50788
+ yield* renderResult(runtime.mode, {
50789
+ version: 1,
50790
+ command: options.name,
50791
+ invocationId: runtime.invocationId,
50792
+ outcome: "succeeded",
50793
+ data: result.data
50794
+ }, result.lines);
50795
+ }), options.network);
49795
50796
  });
49796
50797
  //#endregion
49797
50798
  //#region src/platform/capxul.ts
@@ -49875,42 +50876,63 @@ const checkDoctor = (config, options) => Effect.gen(function* () {
49875
50876
  });
49876
50877
  //#endregion
49877
50878
  //#region src/commands/doctor.ts
50879
+ const metadata = {
50880
+ name: "doctor",
50881
+ capability: "diagnostics",
50882
+ observation: "command"
50883
+ };
49878
50884
  const doctor = Command.make("doctor", {
49879
50885
  online: Flag.boolean("online").pipe(Flag.withDefault(false), Flag.withDescription("Also check the backend through the public SDK")),
49880
50886
  timeoutMs: Flag.integer("timeout-ms").pipe(Flag.filter((value) => value >= 1 && value <= 3e5, () => "Use --timeout-ms between 1 and 300000."), Flag.withDefault(3e4), Flag.withDescription("Online check deadline in milliseconds (1 to 300000)"))
49881
50887
  }, ({ online, timeoutMs }) => runCommand({
49882
- name: "doctor",
50888
+ ...metadata,
49883
50889
  network: online
49884
50890
  }, (config, observation, onFinalize) => checkDoctor(config, {
49885
50891
  online,
49886
50892
  timeoutMs,
49887
50893
  ...observation === void 0 ? {} : { observation },
49888
50894
  ...onFinalize === void 0 ? {} : { onFinalize }
49889
- }))).pipe(Command.withDescription("Check local configuration; --online also checks the backend"));
50895
+ }))).pipe(Command.annotate(CliCommandMetadata, metadata), Command.withDescription("Check local configuration; --online also checks the backend"));
49890
50896
  //#endregion
49891
50897
  //#region src/commands/telemetry.ts
50898
+ const statusMetadata = {
50899
+ name: "telemetry.status",
50900
+ capability: "collection",
50901
+ observation: "collection-control"
50902
+ };
50903
+ const enableMetadata = {
50904
+ ...statusMetadata,
50905
+ name: "telemetry.enable"
50906
+ };
50907
+ const disableMetadata = {
50908
+ ...statusMetadata,
50909
+ name: "telemetry.disable"
50910
+ };
49892
50911
  const report = (config) => collectionStatus(config).pipe(Effect.map((status) => ({
49893
50912
  data: status,
49894
50913
  lines: [
49895
50914
  `Remote CLI observation ${status.enabled ? "enabled" : "disabled"}.`,
49896
50915
  ...status.configured ? [] : ["Remote delivery is not configured."],
49897
50916
  ...config.forcedDisabled ? ["CAPXUL_TELEMETRY_DISABLED overrides the saved preference."] : [],
49898
- "Local commands send nothing. Use capxul telemetry disable to opt out."
50917
+ "Commands collect usage and diagnostics. Collection controls and completions send nothing."
49899
50918
  ]
49900
50919
  })));
49901
50920
  const status = Command.make("status", {}, () => runCommand({
49902
- name: "telemetry.status",
50921
+ ...statusMetadata,
49903
50922
  network: false
49904
- }, report));
50923
+ }, report)).pipe(Command.annotate(CliCommandMetadata, statusMetadata));
49905
50924
  const enable = Command.make("enable", {}, () => runCommand({
49906
- name: "telemetry.enable",
50925
+ ...enableMetadata,
49907
50926
  network: false
49908
- }, (config) => setCollectionEnabled(config.settingsPath, true).pipe(Effect.andThen(report(config)))));
50927
+ }, (config) => setCollectionEnabled(config.settingsPath, true).pipe(Effect.andThen(report(config))))).pipe(Command.annotate(CliCommandMetadata, enableMetadata));
49909
50928
  const disable = Command.make("disable", {}, () => runCommand({
49910
- name: "telemetry.disable",
50929
+ ...disableMetadata,
49911
50930
  network: false
49912
- }, (config) => setCollectionEnabled(config.settingsPath, false).pipe(Effect.andThen(report(config)))));
49913
- const telemetry = Command.make("telemetry").pipe(Command.withDescription("Show or change this CLI's collection preference"), Command.withSubcommands([
50931
+ }, (config) => setCollectionEnabled(config.settingsPath, false).pipe(Effect.andThen(report(config))))).pipe(Command.annotate(CliCommandMetadata, disableMetadata));
50932
+ const telemetry = Command.make("telemetry").pipe(Command.annotate(CliCommandMetadata, {
50933
+ ...statusMetadata,
50934
+ name: "telemetry"
50935
+ }), Command.withDescription("Show or change this CLI's collection preference"), Command.withSubcommands([
49914
50936
  status,
49915
50937
  enable,
49916
50938
  disable
@@ -49940,52 +50962,87 @@ const outputMode = (args) => Effect.gen(function* () {
49940
50962
  });
49941
50963
  //#endregion
49942
50964
  //#region src/command.ts
49943
- const command = Command.make("capxul").pipe(Command.withDescription("Capxul diagnostics and configuration"), Command.withSubcommands([doctor, telemetry]), Command.withGlobalFlags([JsonOutput]));
50965
+ const command = Command.make("capxul").pipe(Command.annotate(CliCommandMetadata, {
50966
+ name: "capxul",
50967
+ capability: "cli",
50968
+ observation: "command"
50969
+ }), Command.withDescription("Capxul diagnostics and configuration"), Command.withSubcommands([doctor, telemetry]), Command.withGlobalFlags([JsonOutput]));
49944
50970
  //#endregion
49945
50971
  //#region src/runtime/entry.ts
50972
+ const completionFailure = (error) => {
50973
+ if (!CliError.isCliError(error)) return false;
50974
+ if (error._tag === "ShowHelp") return error.errors.some(completionFailure);
50975
+ return (error._tag === "InvalidValue" || error._tag === "MissingOption" || error._tag === "DuplicateOption") && (error.option === "completions" || error.option === "--completions");
50976
+ };
49946
50977
  const runCli = (platform) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
49947
50978
  const stdio = yield* Stdio.Stdio;
50979
+ const stateServices = yield* Effect.context().pipe(Effect.map(Context.pick(Crypto.Crypto, FileSystem.FileSystem, Path.Path)));
49948
50980
  const args = yield* stdio.args;
49949
50981
  const mode = yield* outputMode(args);
49950
50982
  const interactive = yield* stdio.stdinIsTerminal;
49951
50983
  const cliConfig = yield* CliConfig.CliConfig;
49952
50984
  const current = yield* Ref.make(null);
50985
+ const invocationId = yield* (yield* Crypto.Crypto).randomUUIDv4;
50986
+ const observation = yield* loadObservationConfig.pipe(Effect.catch(() => Effect.succeed(void 0)));
50987
+ const ci = yield* Config.string("CI").pipe(Config.withDefault("false"), Effect.map((value) => value !== "" && value !== "false" && value !== "0"), Effect.catch(() => Effect.succeed(false)));
50988
+ const runtime = {
50989
+ mode,
50990
+ current,
50991
+ invocationId,
50992
+ observation,
50993
+ ...platform,
50994
+ architecture: arch,
50995
+ ci,
50996
+ tty: interactive
50997
+ };
49953
50998
  const nativeConsole = yield* Console.Console;
49954
50999
  const output = [];
49955
51000
  const capture = (...values) => {
49956
51001
  output.push(values.map(String).join(" "));
49957
51002
  };
51003
+ const flushNative = Effect.suspend(() => {
51004
+ if (output.length === 0) return Effect.void;
51005
+ const text = output.splice(0).join("\n");
51006
+ return mode === "json" ? renderResult(mode, {
51007
+ version: 1,
51008
+ command: "capxul",
51009
+ invocationId: null,
51010
+ outcome: "succeeded",
51011
+ data: { text }
51012
+ }) : writeStdout(`${text}\n`);
51013
+ });
51014
+ const decorate = (flag, kind) => GlobalFlag.action({
51015
+ flag: flag.flag,
51016
+ run: (value, context) => runInvocation(runtime, metadataAtPath(context.command, context.commandPath), kind, () => flag.run(value, context).pipe(Effect.andThen(flushNative))).pipe(Effect.provide(stateServices), Effect.provideService(Stdio.Stdio, stdio), Effect.orDie)
51017
+ });
51018
+ const builtIns = cliConfig.builtIns.filter((flag) => interactive && !ci && mode === "human" || flag !== GlobalFlag.Wizard).map((flag) => flag === GlobalFlag.Help ? decorate(GlobalFlag.Help, "help") : flag === GlobalFlag.Version ? decorate(GlobalFlag.Version, "version") : flag === GlobalFlag.Completions ? decorate(GlobalFlag.Completions, "completion") : flag);
49958
51019
  const exit = yield* restore(Command.runWith(command, {
49959
51020
  version: cliVersion,
49960
51021
  renderErrors: false
49961
- })(args).pipe(Effect.provideService(CliRuntime, {
49962
- mode,
49963
- current,
49964
- ...platform
49965
- }), Effect.provideService(CliConfig.CliConfig, { builtIns: interactive && mode === "human" ? cliConfig.builtIns : cliConfig.builtIns.filter((flag) => flag !== GlobalFlag.Wizard) }), Effect.provideService(Console.Console, {
51022
+ })(args).pipe(Effect.provideService(CliRuntime, runtime), Effect.provideService(CliConfig.CliConfig, { builtIns }), Effect.provideService(Console.Console, {
49966
51023
  ...nativeConsole,
49967
51024
  log: capture,
49968
51025
  info: capture
49969
51026
  }))).pipe(Effect.exit);
49970
51027
  if (Exit.isSuccess(exit)) {
49971
- if (output.length > 0) {
49972
- if (mode === "json") yield* renderResult(mode, {
49973
- version: 1,
49974
- command: "capxul",
49975
- invocationId: null,
49976
- outcome: "succeeded",
49977
- data: { text: output.join("\n") }
49978
- });
49979
- else yield* writeStdout(`${output.join("\n")}\n`);
49980
- }
51028
+ if ((yield* Ref.get(current)) === null && output.length > 0) yield* runInvocation(runtime, metadataAtPath(command, [command.name]), "help", () => flushNative);
51029
+ else yield* flushNative;
49981
51030
  return 0;
49982
51031
  }
51032
+ const error = Cause.squash(exit.cause);
51033
+ if ((yield* Ref.get(current)) === null) {
51034
+ const path = CliError.isCliError(error) && error._tag === "ShowHelp" ? error.commandPath : [];
51035
+ if (path.length > 0) {
51036
+ const metadata = metadataAtPath(command, path);
51037
+ if (metadata.name !== "unknown") yield* runInvocation(runtime, metadata, completionFailure(error) ? "completion" : "parse_refusal", () => Effect.failCause(exit.cause)).pipe(Effect.exit);
51038
+ }
51039
+ }
49983
51040
  const invocation = yield* Ref.get(current);
49984
51041
  const failure = classifyFailure(exit.cause);
49985
51042
  yield* renderResult(mode, {
49986
51043
  version: 1,
49987
- command: invocation?.command ?? parsedCommand(exit.cause),
49988
- invocationId: invocation?.invocationId ?? null,
51044
+ command: invocation?.kind === "parse_refusal" || invocation?.kind === "completion" || invocation?.kind === "collection_control" ? parsedCommand(exit.cause) : invocation?.command ?? parsedCommand(exit.cause),
51045
+ invocationId: invocation?.kind === "command" ? invocation.invocationId : null,
49989
51046
  outcome: failure.outcome,
49990
51047
  error: {
49991
51048
  code: failure.code,