@capxul/cli 4.20.0-beta.1 → 4.20.0-beta.3
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 +20 -4
- package/dist/main.mjs +1674 -356
- package/dist/main.mjs.map +1 -1
- package/package.json +3 -3
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");
|
|
@@ -11120,6 +11484,9 @@ const moneyExecutionContract = {
|
|
|
11120
11484
|
//#region ../../packages/sdk/src/ports/indexer-read.ts
|
|
11121
11485
|
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
11122
11486
|
const INDEXER_MAX_LIMIT = 1e3;
|
|
11487
|
+
function validTime(value) {
|
|
11488
|
+
return Number.isSafeInteger(value) && value >= 0 && value <= Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
|
|
11489
|
+
}
|
|
11123
11490
|
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
11124
11491
|
/**
|
|
11125
11492
|
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
@@ -11127,12 +11494,21 @@ const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
|
11127
11494
|
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
11128
11495
|
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
11129
11496
|
*/
|
|
11497
|
+
function assertEventLookup(input, id) {
|
|
11498
|
+
if (input.limit !== 1 || (input.offset ?? 0) !== 0 || input.timeline !== void 0 || input.settlements !== void 0 || !/^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(id)) throw Errors.invalidInput("eventId", "must identify one logical event without another cursor");
|
|
11499
|
+
}
|
|
11130
11500
|
function assertIndexerPage(input) {
|
|
11131
11501
|
const safe = String(input.safe).toLowerCase();
|
|
11132
11502
|
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
11133
11503
|
if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw Errors.invalidInput("limit", `must be an integer from 1 to ${INDEXER_MAX_LIMIT}`);
|
|
11134
11504
|
const offset = input.offset ?? 0;
|
|
11135
11505
|
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
11506
|
+
const timeline = input.timeline;
|
|
11507
|
+
if (input.eventId !== void 0) assertEventLookup(input, input.eventId);
|
|
11508
|
+
if (input.settlements !== void 0 && (offset !== 0 || timeline !== void 0 || input.settlements.length < 1 || input.settlements.length > 50 || !input.settlements.every((id) => /^0x[0-9a-f]{64}$/.test(id)))) throw Errors.invalidInput("settlements", "must hold 1 to 50 settlement IDs without another cursor");
|
|
11509
|
+
if (timeline !== void 0) {
|
|
11510
|
+
if (offset !== 0 || !validTime(timeline.from) || !validTime(timeline.to) || timeline.from > timeline.to || timeline.before !== void 0 && (!validTime(timeline.before.timestamp) || !/^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(timeline.before.id))) throw Errors.invalidInput("timeline", "must be a bounded time window with a logical event cursor and no offset");
|
|
11511
|
+
}
|
|
11136
11512
|
return safe;
|
|
11137
11513
|
}
|
|
11138
11514
|
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
@@ -11162,20 +11538,6 @@ function directionFor(safe, transfer) {
|
|
|
11162
11538
|
if (from === safe && to === safe) return "self";
|
|
11163
11539
|
return to === safe ? "in" : "out";
|
|
11164
11540
|
}
|
|
11165
|
-
/**
|
|
11166
|
-
* The Transfer a protocol Payment moved is the same money as its `payment`
|
|
11167
|
-
* row. One actor sees one item for it, so the Transfer is claimed and does not
|
|
11168
|
-
* become a second row. Another transfer in the same transaction that this
|
|
11169
|
-
* payment did not move stays unclaimed and keeps its own row.
|
|
11170
|
-
*/
|
|
11171
|
-
function claimedTransferIds(transfers, payments) {
|
|
11172
|
-
const claimed = /* @__PURE__ */ new Set();
|
|
11173
|
-
for (const payment of payments) {
|
|
11174
|
-
const match = transfers.find((transfer) => !claimed.has(transfer.id) && transfer.txHash === payment.txHash && transfer.token.toLowerCase() === payment.token.toLowerCase() && transfer.amount === payment.amount && transfer.to.toLowerCase() === payment.recipient.toLowerCase());
|
|
11175
|
-
if (match !== void 0) claimed.add(match.id);
|
|
11176
|
-
}
|
|
11177
|
-
return claimed;
|
|
11178
|
-
}
|
|
11179
11541
|
function withAnnotation(item, annotation) {
|
|
11180
11542
|
return annotation?.counterpartyLabel === void 0 ? item : {
|
|
11181
11543
|
...item,
|
|
@@ -11186,10 +11548,14 @@ function withAnnotation(item, annotation) {
|
|
|
11186
11548
|
* An indexed settlement confirms the intent. A missing row can be outside
|
|
11187
11549
|
* this page or still backfilling; only the correction owner can prove rollback.
|
|
11188
11550
|
*/
|
|
11189
|
-
function confirmPayment(intent, chain) {
|
|
11190
|
-
if (chain !== void 0) return {
|
|
11551
|
+
function confirmPayment(intent, chain, expected) {
|
|
11552
|
+
if (chain !== void 0 && matchesPaymentFact(chain, expected)) return {
|
|
11191
11553
|
...intent,
|
|
11192
11554
|
status: "settled",
|
|
11555
|
+
...intent.execution === void 0 ? {} : { execution: {
|
|
11556
|
+
status: "settled",
|
|
11557
|
+
retryRequired: false
|
|
11558
|
+
} },
|
|
11193
11559
|
receipt: {
|
|
11194
11560
|
chainId: chain.chainId,
|
|
11195
11561
|
transactionHash: chain.txHash
|
|
@@ -11198,16 +11564,22 @@ function confirmPayment(intent, chain) {
|
|
|
11198
11564
|
};
|
|
11199
11565
|
return intent;
|
|
11200
11566
|
}
|
|
11567
|
+
function matchesPaymentFact(chain, expected) {
|
|
11568
|
+
return chain !== void 0 && expected !== void 0 && chain.chainId === expected.chainId && chain.contract.toLowerCase() === expected.contract.toLowerCase() && chain.sender.toLowerCase() === expected.sender.toLowerCase() && chain.recipient.toLowerCase() === expected.recipient.toLowerCase() && chain.token.toLowerCase() === expected.token.toLowerCase() && chain.amount.toString() === expected.rawAmount && chain.documentHash.toLowerCase() === expected.documentHash.toLowerCase() && chain.kind === expected.kind;
|
|
11569
|
+
}
|
|
11201
11570
|
/**
|
|
11202
11571
|
* One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
|
|
11203
11572
|
* denominate its asset. The indexer serves only configured assets, so a row
|
|
11204
11573
|
* the registry does not know means the SDK and that deployment disagree; it is
|
|
11205
11574
|
* dropped rather than shown with a guessed currency.
|
|
11206
11575
|
*/
|
|
11576
|
+
function isActivityVisible(item) {
|
|
11577
|
+
return !(item.kind === "payment" && item.direction === "in" && item.execution?.notSent);
|
|
11578
|
+
}
|
|
11207
11579
|
function transferItem(safe, transfer, annotation) {
|
|
11208
11580
|
const asset = configuredMoneyAssetById(transfer.assetId);
|
|
11209
11581
|
if (asset === null) return null;
|
|
11210
|
-
const direction = directionFor(safe, transfer);
|
|
11582
|
+
const direction = directionFor(safe.toLowerCase(), transfer);
|
|
11211
11583
|
const counterparty = direction === "in" ? transfer.from : transfer.to;
|
|
11212
11584
|
const at = blockMillis(transfer);
|
|
11213
11585
|
return withAnnotation({
|
|
@@ -11229,47 +11601,6 @@ function transferItem(safe, transfer, annotation) {
|
|
|
11229
11601
|
updatedAt: at
|
|
11230
11602
|
}, annotation);
|
|
11231
11603
|
}
|
|
11232
|
-
/** Newest first. A Payment precedes a Movement at one instant, as the ledger reader orders them. */
|
|
11233
|
-
function compareItems(left, right) {
|
|
11234
|
-
if (right.createdAt !== left.createdAt) return right.createdAt - left.createdAt;
|
|
11235
|
-
if (left.kind !== right.kind) return left.kind === "payment" ? -1 : 1;
|
|
11236
|
-
if (left.id === right.id) return 0;
|
|
11237
|
-
return left.id < right.id ? 1 : -1;
|
|
11238
|
-
}
|
|
11239
|
-
function stitchActivity(input) {
|
|
11240
|
-
const safe = input.safe.toLowerCase();
|
|
11241
|
-
const chainBySettlement = /* @__PURE__ */ new Map();
|
|
11242
|
-
for (const row of input.payments) chainBySettlement.set(row.settlementId.toLowerCase(), row);
|
|
11243
|
-
const chainByPaymentId = /* @__PURE__ */ new Map();
|
|
11244
|
-
for (const link of input.links) {
|
|
11245
|
-
const row = chainBySettlement.get(link.settlementId.toLowerCase());
|
|
11246
|
-
if (row !== void 0) chainByPaymentId.set(link.paymentId, row);
|
|
11247
|
-
}
|
|
11248
|
-
const annotations = /* @__PURE__ */ new Map();
|
|
11249
|
-
for (const annotation of input.annotations) annotations.set(`${annotation.reference.kind}:${annotation.reference.id}`, annotation);
|
|
11250
|
-
const items = [];
|
|
11251
|
-
const claiming = [];
|
|
11252
|
-
for (const intent of input.intents) {
|
|
11253
|
-
if (intent.kind !== "payment") continue;
|
|
11254
|
-
const chain = chainByPaymentId.get(intent.id);
|
|
11255
|
-
if (chain !== void 0) claiming.push(chain);
|
|
11256
|
-
items.push(withAnnotation(confirmPayment(intent, chain), annotations.get(`payment:${intent.id}`)));
|
|
11257
|
-
}
|
|
11258
|
-
const claimed = claimedTransferIds(input.transfers, claiming);
|
|
11259
|
-
for (const transfer of input.transfers) {
|
|
11260
|
-
if (claimed.has(transfer.id)) continue;
|
|
11261
|
-
const item = transferItem(safe, transfer, annotations.get(`movement:${transfer.id}`));
|
|
11262
|
-
if (item !== null) items.push(item);
|
|
11263
|
-
}
|
|
11264
|
-
return items.sort(compareItems);
|
|
11265
|
-
}
|
|
11266
|
-
/** The reference ids one stitched page needs its Convex overlay for. */
|
|
11267
|
-
function overlayReferences(items) {
|
|
11268
|
-
return items.map((item) => ({
|
|
11269
|
-
kind: item.kind,
|
|
11270
|
-
id: item.id
|
|
11271
|
-
}));
|
|
11272
|
-
}
|
|
11273
11604
|
/**
|
|
11274
11605
|
* Money in, money out and the pending share, one bucket per currency and
|
|
11275
11606
|
* precision. Two assets that peg to the same currency at different precision
|
|
@@ -11288,7 +11619,7 @@ function summarizeItems(items) {
|
|
|
11288
11619
|
};
|
|
11289
11620
|
const raw = rawUnits(item.amount.value, item.amount.decimals);
|
|
11290
11621
|
if (item.kind === "payment" && item.status !== "settled") {
|
|
11291
|
-
if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed") bucket.pendingIn += raw;
|
|
11622
|
+
if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed" && !item.execution?.notSent) bucket.pendingIn += raw;
|
|
11292
11623
|
} else if (item.direction === "in") bucket.in += raw;
|
|
11293
11624
|
else if (item.direction === "out") bucket.out += raw;
|
|
11294
11625
|
buckets.set(key, bucket);
|
|
@@ -11311,179 +11642,412 @@ function rawUnits(value, decimals) {
|
|
|
11311
11642
|
}
|
|
11312
11643
|
//#endregion
|
|
11313
11644
|
//#region ../../packages/sdk/src/surface/activity-read.ts
|
|
11314
|
-
|
|
11315
|
-
const INTENT_WINDOW = 100;
|
|
11316
|
-
/** movement/annotations:overlay bounds each identifier array to 200. */
|
|
11645
|
+
const BATCH = 100;
|
|
11317
11646
|
const MAX_OVERLAY_IDS = 200;
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11647
|
+
function actorKey(actor) {
|
|
11648
|
+
return actor.kind === "account" ? `account:${actor.accountId}` : `organization:${actor.orgId}`;
|
|
11649
|
+
}
|
|
11650
|
+
function scopeKey(scope) {
|
|
11651
|
+
return `${scope.principalAccountId ?? "legacy"}|${actorKey(scope.actor)}|${scope.safe?.toLowerCase() ?? "none"}`;
|
|
11652
|
+
}
|
|
11653
|
+
function selectionKey(params) {
|
|
11654
|
+
const filter = params?.filter;
|
|
11655
|
+
return JSON.stringify([
|
|
11656
|
+
params?.range?.from ?? null,
|
|
11657
|
+
params?.range?.to ?? null,
|
|
11658
|
+
filter?.kind ?? null,
|
|
11659
|
+
filter?.direction ?? null,
|
|
11660
|
+
filter?.status === void 0 ? null : [...new Set(filter.status)].sort()
|
|
11661
|
+
]);
|
|
11662
|
+
}
|
|
11663
|
+
function validString(value, max) {
|
|
11664
|
+
return typeof value === "string" && value.length <= max;
|
|
11328
11665
|
}
|
|
11329
|
-
function
|
|
11330
|
-
|
|
11331
|
-
|
|
11666
|
+
function validPosition(position) {
|
|
11667
|
+
return position === null || position !== void 0 && typeof position === "object" && Number.isSafeInteger(position.timestamp) && position.timestamp >= 0 && /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(position.id);
|
|
11668
|
+
}
|
|
11669
|
+
function parseCursor(value, scope, selection) {
|
|
11670
|
+
if (value === void 0) return null;
|
|
11671
|
+
if (value.length > 4e4) throw Errors.invalidInput("cursor", "invalid Activity cursor");
|
|
11672
|
+
const cursor = JSON.parse(value);
|
|
11673
|
+
if (cursor === null || typeof cursor !== "object" || cursor.version !== 1 || cursor.scope !== scope || cursor.selection !== selection || !Number.isSafeInteger(cursor.checkpoint) || cursor.checkpoint < 0 || cursor.source !== "indexed" && cursor.source !== "fallback" || cursor.paymentCursor !== null && !validString(cursor.paymentCursor, 2048) || typeof cursor.paymentDone !== "boolean" || typeof cursor.transferDone !== "boolean" || typeof cursor.degraded !== "boolean" || !Array.isArray(cursor.paymentPending) || cursor.paymentPending.length > BATCH || new Set(cursor.paymentPending).size !== cursor.paymentPending.length || !cursor.paymentPending.every((id) => validString(id, 256)) || !validPosition(cursor.transferBefore)) throw Errors.invalidInput("cursor", "Activity scope or filters changed; restart from the first page");
|
|
11674
|
+
return cursor;
|
|
11675
|
+
}
|
|
11676
|
+
function requireParams(params) {
|
|
11677
|
+
const limit = params?.limit ?? 25;
|
|
11678
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > BATCH) throw Errors.invalidInput("limit", "must be an integer from 1 to 100");
|
|
11679
|
+
for (const value of [params?.range?.from, params?.range?.to]) if (value !== void 0 && !Number.isSafeInteger(value)) throw Errors.invalidInput("range", "must contain integer epoch milliseconds");
|
|
11680
|
+
const f = params?.filter;
|
|
11681
|
+
if (f?.kind !== void 0 && f.kind !== "payment" && f.kind !== "movement" || f?.direction !== void 0 && ![
|
|
11682
|
+
"in",
|
|
11683
|
+
"out",
|
|
11684
|
+
"self"
|
|
11685
|
+
].includes(f.direction) || f?.status !== void 0 && (!Array.isArray(f.status) || !f.status.every((status) => PAYMENT_STATUSES.includes(status)))) throw Errors.invalidInput("filter", "invalid Activity filter");
|
|
11332
11686
|
}
|
|
11333
|
-
/** The caller's filter applied to the merged page. The join owns the order. */
|
|
11334
11687
|
function matchesFilter(item, filter) {
|
|
11335
|
-
|
|
11336
|
-
if (filter.kind !== void 0 && item.kind !== filter.kind) return false;
|
|
11337
|
-
if (filter.direction !== void 0 && item.direction !== filter.direction) return false;
|
|
11338
|
-
if (filter.status === void 0) return true;
|
|
11339
|
-
return item.kind === "payment" && filter.status.includes(item.status);
|
|
11688
|
+
return (filter?.kind === void 0 || item.kind === filter.kind) && (filter?.direction === void 0 || item.direction === filter.direction) && (filter?.status === void 0 || item.kind === "payment" && filter.status.includes(item.status));
|
|
11340
11689
|
}
|
|
11341
11690
|
function overlay(deps, actor, controls, input) {
|
|
11342
11691
|
const count = Math.max(input.references?.length ?? 0, input.settlementIds?.length ?? 0);
|
|
11343
|
-
const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_,
|
|
11692
|
+
const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_, i) => i * MAX_OVERLAY_IDS);
|
|
11344
11693
|
return Effect.forEach(offsets, (offset) => deps.convexCall.query(deps.functions.activityOverlay, copyInvocationObservation(controls, { input: {
|
|
11345
11694
|
...actor === void 0 ? {} : { actor },
|
|
11346
11695
|
...input.references === void 0 ? {} : { references: input.references.slice(offset, offset + MAX_OVERLAY_IDS) },
|
|
11347
11696
|
...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds.slice(offset, offset + MAX_OVERLAY_IDS) }
|
|
11348
|
-
} })), { concurrency: 2 }).pipe(Effect.
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11697
|
+
} })), { concurrency: 2 }).pipe(Effect.flatMap((pages) => {
|
|
11698
|
+
const first = pages[0];
|
|
11699
|
+
if (pages.some((page) => scopeKey(page) !== scopeKey(first))) return Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
11700
|
+
return Effect.succeed({
|
|
11701
|
+
...first,
|
|
11702
|
+
links: pages.flatMap((page) => page.links),
|
|
11703
|
+
annotations: pages.flatMap((page) => page.annotations),
|
|
11704
|
+
...pages.every((page) => page.payments !== void 0) ? { payments: pages.flatMap((page) => page.payments ?? []) } : {}
|
|
11705
|
+
});
|
|
11706
|
+
}));
|
|
11353
11707
|
}
|
|
11354
11708
|
function convexPage(deps, actor, controls, input) {
|
|
11355
11709
|
return deps.convexCall.query(deps.functions.activityList, copyInvocationObservation(controls, { input: {
|
|
11356
|
-
...
|
|
11357
|
-
|
|
11358
|
-
...input.limit === void 0 ? {} : { limit: input.limit },
|
|
11359
|
-
...input.range === void 0 ? {} : { range: input.range }
|
|
11710
|
+
...input,
|
|
11711
|
+
actor
|
|
11360
11712
|
} }));
|
|
11361
11713
|
}
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11714
|
+
function chainRead(deps, table, input) {
|
|
11715
|
+
return deps.indexer.read(table, input).pipe(Effect.catch((error) => {
|
|
11716
|
+
if (error.publicError.details?.reason === "scope-refused" || ![
|
|
11717
|
+
"NETWORK_ERROR",
|
|
11718
|
+
"RATE_LIMITED",
|
|
11719
|
+
"PROVIDER_ERROR",
|
|
11720
|
+
"CAPABILITY_UNAVAILABLE",
|
|
11721
|
+
"TIMEOUT"
|
|
11722
|
+
].includes(error.publicCode)) return Effect.fail({ publicError: error.publicError });
|
|
11723
|
+
return Effect.succeed(null);
|
|
11724
|
+
}));
|
|
11372
11725
|
}
|
|
11373
|
-
|
|
11374
|
-
function stitchedWindow(deps, actor, controls, window, params) {
|
|
11726
|
+
function fallbackPage(deps, scope, controls, params, state, degraded) {
|
|
11375
11727
|
return Effect.gen(function* () {
|
|
11376
|
-
const
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11728
|
+
const page = yield* convexPage(deps, scope.actor, controls, {
|
|
11729
|
+
limit: params?.limit ?? 25,
|
|
11730
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor },
|
|
11731
|
+
range: {
|
|
11732
|
+
...params?.range?.from === void 0 ? {} : { from: params.range.from },
|
|
11733
|
+
to: Math.min(state.checkpoint, params?.range?.to ?? state.checkpoint)
|
|
11734
|
+
},
|
|
11735
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
11380
11736
|
});
|
|
11381
|
-
const
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11737
|
+
const decoration = yield* overlay(deps, scope.actor, controls, { references: page.items.map((item) => ({
|
|
11738
|
+
kind: item.kind,
|
|
11739
|
+
id: item.id
|
|
11740
|
+
})) });
|
|
11741
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during fallback") });
|
|
11742
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
11743
|
+
yield* Effect.annotateCurrentSpan({
|
|
11744
|
+
read_source: "convex",
|
|
11745
|
+
...degraded ? { read_warning: "indexer-unavailable" } : {}
|
|
11746
|
+
});
|
|
11747
|
+
return {
|
|
11748
|
+
...page,
|
|
11749
|
+
items: page.items.filter(isActivityVisible).map((item) => withAnnotation(item, annotations.get(`${item.kind}:${item.id}`))),
|
|
11750
|
+
checkpoint: state.checkpoint,
|
|
11751
|
+
cursor: page.cursor === null ? null : JSON.stringify({
|
|
11752
|
+
...state,
|
|
11753
|
+
source: "fallback",
|
|
11754
|
+
paymentCursor: page.cursor,
|
|
11755
|
+
degraded
|
|
11756
|
+
}),
|
|
11757
|
+
...degraded ? { readWarning: "indexer-unavailable" } : {}
|
|
11758
|
+
};
|
|
11759
|
+
});
|
|
11760
|
+
}
|
|
11761
|
+
function boundedRange(range, checkpoint) {
|
|
11762
|
+
return {
|
|
11763
|
+
...range?.from === void 0 ? {} : { from: range.from },
|
|
11764
|
+
to: Math.min(checkpoint, range?.to ?? checkpoint)
|
|
11765
|
+
};
|
|
11766
|
+
}
|
|
11767
|
+
function unavailablePage(deps, scope, controls, params, state, first) {
|
|
11768
|
+
if (first) return fallbackPage(deps, scope, controls, params, state, true);
|
|
11769
|
+
return Effect.annotateCurrentSpan({
|
|
11770
|
+
read_source: "indexer",
|
|
11771
|
+
read_warning: "indexer-unavailable"
|
|
11772
|
+
}).pipe(Effect.as({
|
|
11773
|
+
items: [],
|
|
11774
|
+
cursor: null,
|
|
11775
|
+
checkpoint: state.checkpoint,
|
|
11776
|
+
observedAt: Date.now(),
|
|
11777
|
+
readWarning: "indexer-unavailable"
|
|
11778
|
+
}));
|
|
11779
|
+
}
|
|
11780
|
+
function readTransfers(deps, safe, range, state, wanted, first) {
|
|
11781
|
+
const from = Math.max(0, Math.ceil((range.from ?? 0) / 1e3));
|
|
11782
|
+
const to = Math.floor(range.to / 1e3);
|
|
11783
|
+
if (state.transferDone || from > to || !wanted && !first) return Effect.succeed([]);
|
|
11784
|
+
return chainRead(deps, "transfer", {
|
|
11785
|
+
safe: toAddress(safe),
|
|
11786
|
+
limit: wanted ? BATCH : 1,
|
|
11787
|
+
timeline: {
|
|
11788
|
+
from,
|
|
11789
|
+
to,
|
|
11790
|
+
...state.transferBefore === null ? {} : { before: state.transferBefore }
|
|
11392
11791
|
}
|
|
11393
|
-
|
|
11394
|
-
|
|
11395
|
-
|
|
11792
|
+
});
|
|
11793
|
+
}
|
|
11794
|
+
function readPayments(deps, scope, controls, range, state, wanted) {
|
|
11795
|
+
const done = state.paymentDone || !wanted;
|
|
11796
|
+
if (state.paymentPending.length > 0 || done) return Effect.succeed({
|
|
11797
|
+
items: [],
|
|
11798
|
+
pending: state.paymentPending,
|
|
11799
|
+
cursor: state.paymentCursor,
|
|
11800
|
+
done
|
|
11801
|
+
});
|
|
11802
|
+
return convexPage(deps, scope.actor, controls, {
|
|
11803
|
+
limit: BATCH,
|
|
11804
|
+
range,
|
|
11805
|
+
filter: { kind: "payment" },
|
|
11806
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor }
|
|
11807
|
+
}).pipe(Effect.map((page) => ({
|
|
11808
|
+
items: page.items,
|
|
11809
|
+
pending: page.items.map((item) => item.id),
|
|
11810
|
+
cursor: page.cursor,
|
|
11811
|
+
done: page.cursor === null
|
|
11812
|
+
})));
|
|
11813
|
+
}
|
|
11814
|
+
function readPaymentFacts(deps, safe, settlements) {
|
|
11815
|
+
const offsets = Array.from({ length: Math.ceil(settlements.length / 50) }, (_, i) => i * 50);
|
|
11816
|
+
return Effect.forEach(offsets, (offset) => chainRead(deps, "payment", {
|
|
11817
|
+
safe: toAddress(safe),
|
|
11818
|
+
limit: 50,
|
|
11819
|
+
settlements: settlements.slice(offset, offset + 50)
|
|
11820
|
+
}), { concurrency: 2 }).pipe(Effect.map((pages) => pages.some((page) => page === null) ? null : pages.flatMap((page) => page ?? [])));
|
|
11821
|
+
}
|
|
11822
|
+
function decoratePage(deps, scope, controls, transfers, page, range) {
|
|
11823
|
+
return Effect.gen(function* () {
|
|
11824
|
+
const decoration = yield* overlay(deps, scope.actor, controls, {
|
|
11825
|
+
references: [...page.pending.map((id) => ({
|
|
11826
|
+
kind: "payment",
|
|
11827
|
+
id
|
|
11828
|
+
})), ...transfers.map((row) => ({
|
|
11396
11829
|
kind: "movement",
|
|
11397
11830
|
id: row.id
|
|
11398
11831
|
}))],
|
|
11399
|
-
settlementIds:
|
|
11832
|
+
settlementIds: [...new Set(transfers.flatMap((row) => row.paymentSettlementId ? [row.paymentSettlementId] : []))]
|
|
11400
11833
|
});
|
|
11401
|
-
|
|
11402
|
-
const
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
|
|
11406
|
-
|
|
11407
|
-
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
|
|
11834
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
11835
|
+
const byId = new Map([...page.items, ...decoration.payments ?? []].map((item) => [item.id, item]));
|
|
11836
|
+
const settlements = [...new Set(decoration.links.map((link) => link.settlementId))];
|
|
11837
|
+
const facts = yield* readPaymentFacts(deps, scope.safe, settlements);
|
|
11838
|
+
if (facts === null) return null;
|
|
11839
|
+
const bySettlement = new Map(facts.map((row) => [row.settlementId, row]));
|
|
11840
|
+
const byPayment = new Map(decoration.links.map((link) => [link.paymentId, {
|
|
11841
|
+
fact: bySettlement.get(link.settlementId),
|
|
11842
|
+
expected: link.expected
|
|
11843
|
+
}]));
|
|
11844
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
11845
|
+
return {
|
|
11846
|
+
payments: page.pending.flatMap((id) => {
|
|
11847
|
+
const item = byId.get(id);
|
|
11848
|
+
if (item === void 0 || item.kind !== "payment" || item.createdAt > range.to || range.from !== void 0 && item.createdAt < range.from) return [];
|
|
11849
|
+
const confirmed = withAnnotation(confirmPayment(item, byPayment.get(id)?.fact, byPayment.get(id)?.expected), annotations.get(`payment:${id}`));
|
|
11850
|
+
return isActivityVisible(confirmed) ? [confirmed] : [];
|
|
11851
|
+
}),
|
|
11852
|
+
annotations,
|
|
11853
|
+
linked: new Set(decoration.links.filter((link) => matchesPaymentFact(bySettlement.get(link.settlementId), link.expected)).map((link) => link.settlementId))
|
|
11854
|
+
};
|
|
11855
|
+
});
|
|
11856
|
+
}
|
|
11857
|
+
function mergePage(safe, filter, limit, state, page, decoration, transfers, wantsTransfers) {
|
|
11858
|
+
const { payments, annotations, linked } = decoration;
|
|
11859
|
+
let pi = 0;
|
|
11860
|
+
let ti = 0;
|
|
11861
|
+
const items = [];
|
|
11862
|
+
const transferEnd = !wantsTransfers || transfers.length < BATCH;
|
|
11863
|
+
let transferBefore = state.transferBefore;
|
|
11864
|
+
while (items.length < limit) {
|
|
11865
|
+
if (pi === payments.length && !page.done || ti === transfers.length && !transferEnd) break;
|
|
11866
|
+
const payment = payments[pi];
|
|
11867
|
+
const row = transfers[ti];
|
|
11868
|
+
if (payment === void 0 && row === void 0) break;
|
|
11869
|
+
if (payment !== void 0 && (row === void 0 || payment.createdAt >= Number(row.timestamp) * 1e3)) {
|
|
11870
|
+
pi += 1;
|
|
11871
|
+
if (matchesFilter(payment, filter)) items.push(payment);
|
|
11872
|
+
} else if (row !== void 0) {
|
|
11873
|
+
ti += 1;
|
|
11874
|
+
transferBefore = {
|
|
11875
|
+
timestamp: Number(row.timestamp),
|
|
11876
|
+
id: row.id
|
|
11877
|
+
};
|
|
11878
|
+
if (row.paymentSettlementId && linked.has(row.paymentSettlementId)) continue;
|
|
11879
|
+
const item = transferItem(safe, row, annotations.get(`movement:${row.id}`));
|
|
11880
|
+
if (item !== null && matchesFilter(item, filter)) items.push(item);
|
|
11413
11881
|
}
|
|
11414
|
-
|
|
11415
|
-
|
|
11882
|
+
}
|
|
11883
|
+
return {
|
|
11884
|
+
items,
|
|
11885
|
+
next: {
|
|
11886
|
+
...state,
|
|
11887
|
+
paymentCursor: page.cursor,
|
|
11888
|
+
paymentDone: page.done,
|
|
11889
|
+
paymentPending: payments.slice(pi).map((item) => item.id),
|
|
11890
|
+
transferBefore,
|
|
11891
|
+
transferDone: ti === transfers.length && transferEnd
|
|
11892
|
+
}
|
|
11893
|
+
};
|
|
11894
|
+
}
|
|
11895
|
+
function indexedPage(deps, scope, controls, params, state, first) {
|
|
11896
|
+
return Effect.gen(function* () {
|
|
11897
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
11898
|
+
const wantsTransfers = params?.filter?.kind !== "payment" && params?.filter?.status === void 0;
|
|
11899
|
+
const found = yield* readTransfers(deps, scope.safe, range, state, wantsTransfers, first);
|
|
11900
|
+
if (found === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
11901
|
+
const transfers = wantsTransfers ? found : [];
|
|
11902
|
+
const page = yield* readPayments(deps, scope, controls, range, state, params?.filter?.kind !== "movement");
|
|
11903
|
+
const decoration = yield* decoratePage(deps, scope, controls, transfers, page, range);
|
|
11904
|
+
if (decoration === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
11905
|
+
const { items, next } = mergePage(scope.safe, params?.filter, params?.limit ?? 25, state, page, decoration, transfers, wantsTransfers);
|
|
11906
|
+
const cursor = next.paymentDone && next.paymentPending.length === 0 && next.transferDone ? null : JSON.stringify(next);
|
|
11907
|
+
if (cursor !== null && cursor === params?.cursor) return yield* Effect.fail({ publicError: Errors.invalidInput("cursor", "Activity source did not advance") });
|
|
11908
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
11416
11909
|
return {
|
|
11417
|
-
items
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
intents,
|
|
11422
|
-
links: decoration.links,
|
|
11423
|
-
annotations: [...decoration.annotations, ...olderDecoration]
|
|
11424
|
-
}).filter((item) => inRange(item, params.range) && matchesFilter(item, params.filter)),
|
|
11425
|
-
stitched: true
|
|
11910
|
+
items,
|
|
11911
|
+
cursor,
|
|
11912
|
+
checkpoint: state.checkpoint,
|
|
11913
|
+
observedAt: Date.now()
|
|
11426
11914
|
};
|
|
11427
11915
|
});
|
|
11428
11916
|
}
|
|
11429
11917
|
function readActivityPage(deps, actor, controls, params) {
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
const
|
|
11437
|
-
|
|
11438
|
-
|
|
11918
|
+
return Effect.gen(function* () {
|
|
11919
|
+
yield* Effect.try({
|
|
11920
|
+
try: () => requireParams(params),
|
|
11921
|
+
catch: (cause) => ({ publicError: cause })
|
|
11922
|
+
});
|
|
11923
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
11924
|
+
const selection = selectionKey(params);
|
|
11925
|
+
const cursor = yield* Effect.try({
|
|
11926
|
+
try: () => parseCursor(params?.cursor, scopeKey(scope), selection),
|
|
11927
|
+
catch: () => ({ publicError: Errors.invalidInput("cursor", "invalid or obsolete Activity cursor; restart the read") })
|
|
11928
|
+
});
|
|
11929
|
+
const state = cursor ?? {
|
|
11930
|
+
version: 1,
|
|
11931
|
+
scope: scopeKey(scope),
|
|
11932
|
+
selection,
|
|
11933
|
+
checkpoint: Date.now(),
|
|
11934
|
+
source: "indexed",
|
|
11935
|
+
paymentCursor: null,
|
|
11936
|
+
paymentDone: false,
|
|
11937
|
+
paymentPending: [],
|
|
11938
|
+
transferBefore: null,
|
|
11939
|
+
transferDone: false,
|
|
11940
|
+
degraded: false
|
|
11941
|
+
};
|
|
11942
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
11943
|
+
if (range.from !== void 0 && range.from > range.to) return {
|
|
11944
|
+
items: [],
|
|
11439
11945
|
cursor: null,
|
|
11440
|
-
checkpoint:
|
|
11441
|
-
observedAt
|
|
11442
|
-
...readWarning === void 0 ? {} : { readWarning }
|
|
11946
|
+
checkpoint: state.checkpoint,
|
|
11947
|
+
observedAt: Date.now()
|
|
11443
11948
|
};
|
|
11949
|
+
if (state.source === "fallback" || scope.safe === null) return yield* fallbackPage(deps, scope, controls, params, state, state.degraded);
|
|
11950
|
+
if (scope.payments === void 0) return yield* unavailablePage(deps, scope, controls, params, state, cursor === null);
|
|
11951
|
+
return yield* indexedPage(deps, scope, controls, params, state, cursor === null);
|
|
11952
|
+
});
|
|
11953
|
+
}
|
|
11954
|
+
function readActivitySummary(deps, actor, controls, params) {
|
|
11955
|
+
return Effect.gen(function* () {
|
|
11956
|
+
const totals = /* @__PURE__ */ new Map();
|
|
11957
|
+
let cursor;
|
|
11958
|
+
let count = 0;
|
|
11959
|
+
let readWarning;
|
|
11960
|
+
do {
|
|
11961
|
+
const page = yield* readActivityPage(deps, actor, controls, {
|
|
11962
|
+
limit: BATCH,
|
|
11963
|
+
...cursor === void 0 ? {} : { cursor },
|
|
11964
|
+
...params?.window === void 0 ? {} : { range: params.window }
|
|
11965
|
+
});
|
|
11966
|
+
count += page.items.length;
|
|
11967
|
+
readWarning ??= page.readWarning;
|
|
11968
|
+
for (const total of summarizeItems(page.items)) {
|
|
11969
|
+
const key = `${total.currency}:${total.decimals}`;
|
|
11970
|
+
const old = totals.get(key);
|
|
11971
|
+
totals.set(key, old === void 0 ? total : {
|
|
11972
|
+
...total,
|
|
11973
|
+
in: String(BigInt(old.in) + BigInt(total.in)),
|
|
11974
|
+
out: String(BigInt(old.out) + BigInt(total.out)),
|
|
11975
|
+
net: String(BigInt(old.net) + BigInt(total.net)),
|
|
11976
|
+
pendingIn: String(BigInt(old.pendingIn) + BigInt(total.pendingIn))
|
|
11977
|
+
});
|
|
11978
|
+
}
|
|
11979
|
+
cursor = page.cursor ?? void 0;
|
|
11980
|
+
} while (cursor !== void 0);
|
|
11444
11981
|
return {
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11982
|
+
window: {
|
|
11983
|
+
from: params?.window?.from ?? null,
|
|
11984
|
+
to: params?.window?.to ?? null
|
|
11985
|
+
},
|
|
11986
|
+
totals: [...totals.values()],
|
|
11987
|
+
count,
|
|
11988
|
+
observedAt: Date.now(),
|
|
11989
|
+
...readWarning === void 0 ? {} : { readWarning }
|
|
11449
11990
|
};
|
|
11450
|
-
})
|
|
11991
|
+
});
|
|
11451
11992
|
}
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
11993
|
+
/** An indexed Movement has no Convex materialization. Read its exact native row. */
|
|
11994
|
+
function readIndexedMovement(deps, actor, controls, eventId) {
|
|
11995
|
+
return Effect.gen(function* () {
|
|
11996
|
+
const scope = yield* overlay(deps, actor, controls, { references: [{
|
|
11997
|
+
kind: "movement",
|
|
11998
|
+
id: eventId
|
|
11999
|
+
}] });
|
|
12000
|
+
if (scope.safe === null) return null;
|
|
12001
|
+
const safe = scope.safe.toLowerCase();
|
|
12002
|
+
const row = (yield* deps.indexer.read("transfer", {
|
|
12003
|
+
safe: toAddress(safe),
|
|
12004
|
+
limit: 1,
|
|
12005
|
+
eventId
|
|
12006
|
+
}).pipe(Effect.catch((error) => Effect.fail({ publicError: error.publicError }))))[0];
|
|
12007
|
+
if (row === void 0) return null;
|
|
12008
|
+
const blockNumber = Number(row.blockNumber);
|
|
12009
|
+
if (row.id !== eventId || row.from.toLowerCase() !== safe && row.to.toLowerCase() !== safe || !Number.isSafeInteger(blockNumber) || blockNumber < 0 || !Number.isSafeInteger(row.logIndex) || row.logIndex < 0) return yield* Effect.fail({ publicError: Errors.invalidInput("activity", "indexed Movement does not match the requested scope or evidence") });
|
|
12010
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
12011
|
+
return {
|
|
12012
|
+
kind: "movement",
|
|
12013
|
+
movementId: row.id,
|
|
12014
|
+
viewer: scope.actor,
|
|
12015
|
+
annotation: scope.annotations.find((annotation) => annotation.reference.kind === "movement" && annotation.reference.id === row.id) ?? null,
|
|
12016
|
+
evidence: {
|
|
12017
|
+
chainId: row.chainId,
|
|
12018
|
+
contractAddress: row.token,
|
|
12019
|
+
tokenAddress: row.token,
|
|
12020
|
+
transactionHash: row.txHash,
|
|
12021
|
+
blockNumber,
|
|
12022
|
+
blockHash: row.blockHash,
|
|
12023
|
+
inclusion: "included",
|
|
12024
|
+
verification: "verified",
|
|
12025
|
+
canonicalState: "finalized",
|
|
12026
|
+
transferLogIndex: row.logIndex,
|
|
12027
|
+
source: row.from,
|
|
12028
|
+
destination: row.to,
|
|
12029
|
+
rawAmount: row.amount.toString(),
|
|
12030
|
+
link: {
|
|
12031
|
+
movementId: row.id,
|
|
12032
|
+
depositPaymentId: null
|
|
12033
|
+
}
|
|
12034
|
+
}
|
|
12035
|
+
};
|
|
12036
|
+
});
|
|
11463
12037
|
}
|
|
11464
|
-
/**
|
|
11465
|
-
* Tell the caller when the indexed page for this Safe changed.
|
|
11466
|
-
*
|
|
11467
|
-
* The transport is the indexer's one SSE connection, wired in the Ponder
|
|
11468
|
-
* adapter under `activity.subscribe`. A reactive snapshot is not a product
|
|
11469
|
-
* event, so nothing here emits telemetry: the adapter reports a teardown, and
|
|
11470
|
-
* the caller re-reads.
|
|
11471
|
-
*/
|
|
11472
12038
|
function subscribeActivity(deps, actor, controls, onChange) {
|
|
11473
12039
|
return Effect.gen(function* () {
|
|
11474
12040
|
const scope = yield* overlay(deps, actor, controls, {});
|
|
11475
12041
|
if (scope.safe === null) return () => {};
|
|
11476
|
-
const page = {
|
|
11477
|
-
safe: scope.safe,
|
|
11478
|
-
limit: INTENT_WINDOW
|
|
11479
|
-
};
|
|
11480
12042
|
let delivered = false;
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
}
|
|
11485
|
-
|
|
11486
|
-
|
|
12043
|
+
return yield* deps.indexer.subscribe("transfer", {
|
|
12044
|
+
safe: toAddress(scope.safe),
|
|
12045
|
+
limit: BATCH
|
|
12046
|
+
}, (snapshot) => {
|
|
12047
|
+
if (snapshot.status === "ok") {
|
|
12048
|
+
if (delivered) onChange();
|
|
12049
|
+
delivered = true;
|
|
12050
|
+
}
|
|
11487
12051
|
if (snapshot.status === "error") {
|
|
11488
12052
|
delivered = true;
|
|
11489
12053
|
onChange();
|
|
@@ -11493,7 +12057,7 @@ function subscribeActivity(deps, actor, controls, onChange) {
|
|
|
11493
12057
|
}
|
|
11494
12058
|
//#endregion
|
|
11495
12059
|
//#region ../../packages/sdk/package.json
|
|
11496
|
-
var version$1 = "4.20.0-beta.
|
|
12060
|
+
var version$1 = "4.20.0-beta.3";
|
|
11497
12061
|
//#endregion
|
|
11498
12062
|
//#region ../../packages/sdk/src/telemetry/exception-projection.ts
|
|
11499
12063
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -13059,7 +13623,10 @@ function makeFinancialOpsMethods(deps) {
|
|
|
13059
13623
|
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
13060
13624
|
...params?.range === void 0 ? {} : { range: params.range },
|
|
13061
13625
|
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
13062
|
-
} })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.
|
|
13626
|
+
} })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.map((page) => ({
|
|
13627
|
+
...page,
|
|
13628
|
+
items: page.items.filter(isActivityVisible)
|
|
13629
|
+
})), Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
13063
13630
|
},
|
|
13064
13631
|
summary: (params, options) => {
|
|
13065
13632
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
@@ -13080,7 +13647,10 @@ function makeFinancialOpsMethods(deps) {
|
|
|
13080
13647
|
actor: scope,
|
|
13081
13648
|
referenceActor: reference.kind === "payment" ? activityActorField(reference.actor).actor : void 0
|
|
13082
13649
|
});
|
|
13083
|
-
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() =>
|
|
13650
|
+
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => indexerDeps !== void 0 && reference.kind === "movement" && /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(reference.id) ? readIndexedMovement(indexerDeps, actor?.kind === "org" ? {
|
|
13651
|
+
kind: "organization",
|
|
13652
|
+
orgId: actor.orgId
|
|
13653
|
+
} : void 0, controls, reference.id) : deps.convexCall.query(fns.activityGet, copyInvocationObservation(controls, {
|
|
13084
13654
|
input: {
|
|
13085
13655
|
kind: reference.kind,
|
|
13086
13656
|
id: reference.id,
|
|
@@ -13547,6 +14117,381 @@ async function recoverRawDigestSigner(input) {
|
|
|
13547
14117
|
}
|
|
13548
14118
|
}
|
|
13549
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
|
|
13550
14495
|
//#region ../../packages/observability/src/engineering.ts
|
|
13551
14496
|
const ENGINEERING_CAPXUL_ENVS = [
|
|
13552
14497
|
"development",
|
|
@@ -13721,10 +14666,13 @@ const MINIMAL_NUMBERS = /* @__PURE__ */ new Set([
|
|
|
13721
14666
|
const MINIMAL_CORRELATIONS = /* @__PURE__ */ new Set([
|
|
13722
14667
|
"correlation_id",
|
|
13723
14668
|
"journey_id",
|
|
13724
|
-
"invocation_id"
|
|
14669
|
+
"invocation_id",
|
|
14670
|
+
"posthogDistinctId"
|
|
13725
14671
|
]);
|
|
13726
14672
|
const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
|
|
13727
14673
|
"command",
|
|
14674
|
+
"capability",
|
|
14675
|
+
"invocation_kind",
|
|
13728
14676
|
"outcome",
|
|
13729
14677
|
"capxul_env",
|
|
13730
14678
|
"unit"
|
|
@@ -13732,7 +14680,7 @@ const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
|
|
|
13732
14680
|
const MINIMAL_METRIC_NAMES = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
|
|
13733
14681
|
const traceId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{32}$/u.test(value);
|
|
13734
14682
|
const spanId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{16}$/u.test(value);
|
|
13735
|
-
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";
|
|
13736
14684
|
const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
|
|
13737
14685
|
["outcome", [
|
|
13738
14686
|
"succeeded",
|
|
@@ -13745,7 +14693,10 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
|
|
|
13745
14693
|
["failure_mode", FAILURE_MODES$1],
|
|
13746
14694
|
["capxul_env", ENGINEERING_CAPXUL_ENVS],
|
|
13747
14695
|
["producer", ["browser", "server"]],
|
|
13748
|
-
["command",
|
|
14696
|
+
["command", CLI_COMMAND_NAMES],
|
|
14697
|
+
["capability", CLI_CAPABILITIES],
|
|
14698
|
+
["invocation_kind", CLI_INVOCATION_KINDS],
|
|
14699
|
+
["cli_identity_kind", ["anonymous"]],
|
|
13749
14700
|
["output_mode", ["human", "json"]],
|
|
13750
14701
|
["unit", [
|
|
13751
14702
|
"1",
|
|
@@ -13963,10 +14914,17 @@ function minimalSerialization(config) {
|
|
|
13963
14914
|
}
|
|
13964
14915
|
/** Attempt to drain all engineering signals; completion does not prove ingestion. */
|
|
13965
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
|
+
};
|
|
13966
14923
|
/** Shared browser/server OTLP layer. OtlpLogger merges with incumbent loggers once. */
|
|
13967
14924
|
const makeEngineeringTelemetryLayer = (config) => {
|
|
13968
14925
|
const validated = validateEngineeringTelemetryConfig(config);
|
|
13969
14926
|
return Layer.suspend(() => {
|
|
14927
|
+
const bounded = validated.deliveryBudget === void 0 ? void 0 : cliEngineeringDelivery(validated.deliveryBudget, validated.serviceName === "capxul-cli");
|
|
13970
14928
|
const endpoints = postHogOtlpEndpoints(validated.host);
|
|
13971
14929
|
let disabled = false;
|
|
13972
14930
|
const enabled = () => {
|
|
@@ -13980,7 +14938,12 @@ const makeEngineeringTelemetryLayer = (config) => {
|
|
|
13980
14938
|
};
|
|
13981
14939
|
const delivery = {
|
|
13982
14940
|
...validated.shutdownTimeout === void 0 ? {} : { shutdownTimeout: validated.shutdownTimeout },
|
|
13983
|
-
...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
|
+
}
|
|
13984
14947
|
};
|
|
13985
14948
|
const resource = {
|
|
13986
14949
|
serviceName: validated.serviceName ?? "capxul-sdk",
|
|
@@ -13996,22 +14959,38 @@ const makeEngineeringTelemetryLayer = (config) => {
|
|
|
13996
14959
|
headers: validated.headers,
|
|
13997
14960
|
resource,
|
|
13998
14961
|
...delivery
|
|
13999
|
-
}).pipe(Effect.map((tracer) => makeLeakSafeEngineeringTracer(tracer, enabled)))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
14000
|
-
const
|
|
14962
|
+
}).pipe(Effect.map((tracer) => makeLeakSafeEngineeringTracer(bounded?.tracer(tracer) ?? tracer, enabled)))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
14963
|
+
const ownedLogging = Layer.effect(EngineeringLog, OtlpLogger.make({
|
|
14001
14964
|
url: endpoints.logs,
|
|
14002
14965
|
headers: validated.headers,
|
|
14003
14966
|
resource,
|
|
14004
|
-
...delivery
|
|
14005
|
-
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
|
|
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 = {
|
|
14009
14977
|
url: endpoints.metrics,
|
|
14010
14978
|
headers: validated.headers,
|
|
14011
14979
|
resource,
|
|
14012
14980
|
temporality: "delta",
|
|
14013
14981
|
...delivery
|
|
14014
|
-
}
|
|
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));
|
|
14015
14994
|
const headerPolicy = Layer.merge(Layer.succeed(HttpClient.TracerHeaderFilter, traceHeaderFilter), Layer.succeed(Headers.CurrentRedactedNames, [credentialHeaderPattern]));
|
|
14016
14995
|
const browserLifecycle = Layer.effectDiscard(Effect.gen(function* () {
|
|
14017
14996
|
if (validated.producer !== "browser" || !hasBrowserDocument()) return;
|
|
@@ -14034,17 +15013,25 @@ const makeEngineeringTelemetryLayer = (config) => {
|
|
|
14034
15013
|
}));
|
|
14035
15014
|
}));
|
|
14036
15015
|
const transport = Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, (client) => {
|
|
14037
|
-
const
|
|
14038
|
-
|
|
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
|
+
}));
|
|
14039
15023
|
})).pipe(Layer.provide(FetchHttpClient.layer));
|
|
14040
15024
|
const serialization = Layer.effect(OtlpSerialization.OtlpSerialization, Effect.map(OtlpSerialization.OtlpSerialization, (json) => {
|
|
14041
15025
|
const projected = minimalSerialization(validated);
|
|
14042
|
-
|
|
15026
|
+
const selected = validated.privacy === "minimal" ? projected : {
|
|
14043
15027
|
...json,
|
|
14044
15028
|
metrics: projected.metrics
|
|
14045
15029
|
};
|
|
15030
|
+
return bounded?.serialization(selected) ?? selected;
|
|
14046
15031
|
})).pipe(Layer.provide(OtlpSerialization.layerJson));
|
|
14047
|
-
|
|
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())));
|
|
14048
15035
|
});
|
|
14049
15036
|
};
|
|
14050
15037
|
//#endregion
|
|
@@ -47563,6 +48550,7 @@ function postHogObservability(client, options = {}) {
|
|
|
47563
48550
|
product: postHogProductTelemetry(policy)
|
|
47564
48551
|
};
|
|
47565
48552
|
Object.defineProperty(module, HOST_ENGINEERING_POLICY, { value: Object.freeze({
|
|
48553
|
+
...options.engineeringDeliveryBudget === void 0 ? {} : { engineeringDeliveryBudget: options.engineeringDeliveryBudget },
|
|
47566
48554
|
...options.engineeringEnabled === void 0 ? {} : { engineeringEnabled: options.engineeringEnabled },
|
|
47567
48555
|
...options.engineeringPrivacy === void 0 ? {} : { engineeringPrivacy: options.engineeringPrivacy }
|
|
47568
48556
|
}) });
|
|
@@ -47600,6 +48588,7 @@ function isPromiseLike(value) {
|
|
|
47600
48588
|
function hostEngineeringPolicy(observability) {
|
|
47601
48589
|
const policy = observability?.[HOST_ENGINEERING_POLICY];
|
|
47602
48590
|
return {
|
|
48591
|
+
...policy?.engineeringDeliveryBudget === void 0 ? {} : { deliveryBudget: policy.engineeringDeliveryBudget },
|
|
47603
48592
|
...policy?.engineeringEnabled === void 0 ? {} : { enabled: policy.engineeringEnabled },
|
|
47604
48593
|
...policy?.engineeringPrivacy === void 0 ? {} : { privacy: policy.engineeringPrivacy }
|
|
47605
48594
|
};
|
|
@@ -47668,7 +48657,8 @@ function decodeTransfer(value) {
|
|
|
47668
48657
|
token: toAddress(hex(row, "token")),
|
|
47669
48658
|
from: toAddress(hex(row, "from")),
|
|
47670
48659
|
to: toAddress(hex(row, "to")),
|
|
47671
|
-
amount: big(row, "amount")
|
|
48660
|
+
amount: big(row, "amount"),
|
|
48661
|
+
..."payment_settlement_id" in row ? { paymentSettlementId: row.payment_settlement_id === null ? null : hex(row, "payment_settlement_id") } : {}
|
|
47672
48662
|
};
|
|
47673
48663
|
}
|
|
47674
48664
|
function decodePayment(value) {
|
|
@@ -47754,6 +48744,60 @@ const ACTOR_PARAMS = {
|
|
|
47754
48744
|
*/
|
|
47755
48745
|
function ponderQuery(table, input) {
|
|
47756
48746
|
const safe = assertIndexerPage(input);
|
|
48747
|
+
if (input.eventId !== void 0) {
|
|
48748
|
+
if (table !== "transfer") throw Errors.invalidInput("eventId", "only transfer reads accept a logical event ID");
|
|
48749
|
+
return {
|
|
48750
|
+
sql: `${HEADS.transfer.slice(0, HEADS.transfer.indexOf(" from \"transfer\""))} from "transfer" where (("transfer"."from" = $1 or "transfer"."to" = $2) and "transfer"."id" = $3) limit $4`,
|
|
48751
|
+
params: [
|
|
48752
|
+
safe,
|
|
48753
|
+
safe,
|
|
48754
|
+
input.eventId,
|
|
48755
|
+
1
|
|
48756
|
+
],
|
|
48757
|
+
typings: [
|
|
48758
|
+
"none",
|
|
48759
|
+
"none",
|
|
48760
|
+
"none",
|
|
48761
|
+
"none"
|
|
48762
|
+
]
|
|
48763
|
+
};
|
|
48764
|
+
}
|
|
48765
|
+
if (input.settlements !== void 0) {
|
|
48766
|
+
if (table !== "payment") throw Errors.invalidInput("settlements", "only Payment reads accept settlement IDs");
|
|
48767
|
+
const params = [
|
|
48768
|
+
safe,
|
|
48769
|
+
safe,
|
|
48770
|
+
...input.settlements,
|
|
48771
|
+
input.limit
|
|
48772
|
+
];
|
|
48773
|
+
return {
|
|
48774
|
+
sql: `${HEADS.payment.slice(0, HEADS.payment.indexOf("where "))}where (("payment"."sender" = $1 or "payment"."recipient" = $2) and "payment"."settlement_id" in (${input.settlements.map((_, i) => `$${i + 3}`).join(", ")})) limit $${params.length}`,
|
|
48775
|
+
params,
|
|
48776
|
+
typings: params.map(() => "none")
|
|
48777
|
+
};
|
|
48778
|
+
}
|
|
48779
|
+
if (input.timeline !== void 0) {
|
|
48780
|
+
if (table !== "transfer") throw Errors.invalidInput("timeline", "only transfer reads have a timeline");
|
|
48781
|
+
const { from, to, before } = input.timeline;
|
|
48782
|
+
const params = [
|
|
48783
|
+
safe,
|
|
48784
|
+
safe,
|
|
48785
|
+
from,
|
|
48786
|
+
to
|
|
48787
|
+
];
|
|
48788
|
+
const select = HEADS.transfer.slice(0, HEADS.transfer.indexOf(" from \"transfer\"")) + ", \"payment_settlement_id\" from \"activity_transfer\" ";
|
|
48789
|
+
let where = "(\"activity_transfer\".\"from\" = $1 or \"activity_transfer\".\"to\" = $2) and \"activity_transfer\".\"timestamp\" >= $3 and \"activity_transfer\".\"timestamp\" <= $4";
|
|
48790
|
+
if (before !== void 0) {
|
|
48791
|
+
params.push(before.timestamp, before.timestamp, before.id);
|
|
48792
|
+
where += " and (\"activity_transfer\".\"timestamp\" < $5 or (\"activity_transfer\".\"timestamp\" = $6 and \"activity_transfer\".\"id\" < $7))";
|
|
48793
|
+
}
|
|
48794
|
+
params.push(input.limit);
|
|
48795
|
+
return {
|
|
48796
|
+
sql: `${select}where (${where}) order by "activity_transfer"."timestamp" desc, "activity_transfer"."id" desc limit $${params.length}`,
|
|
48797
|
+
params,
|
|
48798
|
+
typings: params.map(() => "none")
|
|
48799
|
+
};
|
|
48800
|
+
}
|
|
47757
48801
|
const offset = input.offset ?? 0;
|
|
47758
48802
|
const actors = ACTOR_PARAMS[table];
|
|
47759
48803
|
const params = [];
|
|
@@ -48759,6 +49803,82 @@ function captureException(telemetry, error, context) {
|
|
|
48759
49803
|
}).pipe(Effect.flatten), () => Effect.void);
|
|
48760
49804
|
}
|
|
48761
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
|
|
48762
49882
|
//#region src/platform/state-files.ts
|
|
48763
49883
|
const Settings = Schema.Struct({
|
|
48764
49884
|
version: Schema.Literal(1),
|
|
@@ -48902,11 +50022,12 @@ const withLock = (file, body) => Effect.scoped(Effect.gen(function* () {
|
|
|
48902
50022
|
}));
|
|
48903
50023
|
return yield* body(path.join(lock, `${owner}.data`));
|
|
48904
50024
|
}));
|
|
48905
|
-
const replace = (file,
|
|
50025
|
+
const replace = (file, update) => Effect.gen(function* () {
|
|
48906
50026
|
const fs = yield* FileSystem.FileSystem;
|
|
48907
|
-
if (new TextEncoder().encode(content).length > 65536) return yield* Effect.fail(fail("Corrupt"));
|
|
48908
50027
|
return yield* withLock(file, (temporary) => Effect.gen(function* () {
|
|
48909
|
-
yield*
|
|
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"));
|
|
48910
50031
|
const handle = yield* fs.open(temporary, {
|
|
48911
50032
|
flag: "wx",
|
|
48912
50033
|
mode: 384
|
|
@@ -48918,6 +50039,7 @@ const replace = (file, content, validate) => Effect.gen(function* () {
|
|
|
48918
50039
|
const path = yield* Path.Path;
|
|
48919
50040
|
yield* (yield* fs.open(path.dirname(file))).sync;
|
|
48920
50041
|
}));
|
|
50042
|
+
return value;
|
|
48921
50043
|
}));
|
|
48922
50044
|
});
|
|
48923
50045
|
const setCollectionEnabled = (file, enabled) => Effect.gen(function* () {
|
|
@@ -48926,9 +50048,33 @@ const setCollectionEnabled = (file, enabled) => Effect.gen(function* () {
|
|
|
48926
50048
|
version: 1,
|
|
48927
50049
|
collection: { enabled }
|
|
48928
50050
|
};
|
|
48929
|
-
yield* replace(location,
|
|
50051
|
+
yield* replace(location, readSettings(location).pipe(Effect.as({
|
|
50052
|
+
content: JSON.stringify(settings),
|
|
50053
|
+
value: void 0
|
|
50054
|
+
})));
|
|
48930
50055
|
return settings;
|
|
48931
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" });
|
|
48932
50078
|
//#endregion
|
|
48933
50079
|
//#region src/platform/collection-policy.ts
|
|
48934
50080
|
const decode = Schema.decodeUnknownOption(Schema.fromJsonString(Settings), { onExcessProperty: "error" });
|
|
@@ -49038,6 +50184,8 @@ function parsedCommand(cause) {
|
|
|
49038
50184
|
}
|
|
49039
50185
|
//#endregion
|
|
49040
50186
|
//#region src/runtime/config.ts
|
|
50187
|
+
const defaultHost = "https://us.i.posthog.com";
|
|
50188
|
+
const defaultProjectToken = Redacted.make("phc_BZuAVofrkNXzwpJkuDG7Rdfurir45fj2dudQvJZk847e");
|
|
49041
50189
|
const optionalText = (name) => Config.option(Config.string(name));
|
|
49042
50190
|
function httpOrigin(value, field) {
|
|
49043
50191
|
return Effect.try({
|
|
@@ -49051,14 +50199,12 @@ function httpOrigin(value, field) {
|
|
|
49051
50199
|
catch: () => configurationFailure(`The ${field} URL must be a valid approved origin.`)
|
|
49052
50200
|
});
|
|
49053
50201
|
}
|
|
49054
|
-
const
|
|
50202
|
+
const loadObservationConfig = Effect.gen(function* () {
|
|
49055
50203
|
const path = yield* Path.Path;
|
|
49056
50204
|
const values = yield* Config.all({
|
|
49057
50205
|
cliHome: optionalText("CAPXUL_CLI_HOME"),
|
|
49058
50206
|
home: optionalText("HOME"),
|
|
49059
50207
|
xdg: optionalText("XDG_CONFIG_HOME"),
|
|
49060
|
-
key: Config.option(Config.redacted("CAPXUL_PUBLISHABLE_KEY")),
|
|
49061
|
-
bootstrap: Config.string("CAPXUL_BOOTSTRAP_URL").pipe(Config.withDefault("https://api.staging.capxul.com")),
|
|
49062
50208
|
host: optionalText("CAPXUL_POSTHOG_HOST"),
|
|
49063
50209
|
token: Config.option(Config.redacted("CAPXUL_POSTHOG_PROJECT_TOKEN")),
|
|
49064
50210
|
disabled: Config.boolean("CAPXUL_TELEMETRY_DISABLED").pipe(Config.withDefault(false))
|
|
@@ -49072,19 +50218,29 @@ const loadConfig = Effect.gen(function* () {
|
|
|
49072
50218
|
else return yield* Effect.fail(configurationFailure("Set HOME or CAPXUL_CLI_HOME."));
|
|
49073
50219
|
}
|
|
49074
50220
|
const settingsPath = path.join(path.resolve(directory), "settings.json");
|
|
49075
|
-
const
|
|
49076
|
-
const posthogHost = Option.isSome(values.host) ? yield* httpOrigin(values.host.value, "PostHog") : void 0;
|
|
50221
|
+
const host = Option.getOrUndefined(values.host) ?? defaultHost;
|
|
49077
50222
|
return {
|
|
49078
50223
|
settingsPath,
|
|
49079
|
-
publishableKey: Option.getOrUndefined(values.key),
|
|
49080
|
-
bootstrapBaseUrl,
|
|
49081
50224
|
environment: "staging",
|
|
49082
|
-
posthogHost,
|
|
49083
|
-
projectToken: Option.getOrUndefined(values.token),
|
|
50225
|
+
posthogHost: host === void 0 ? void 0 : yield* httpOrigin(host, "PostHog"),
|
|
50226
|
+
projectToken: Option.getOrUndefined(values.token) ?? defaultProjectToken,
|
|
49084
50227
|
forcedDisabled: values.disabled,
|
|
49085
50228
|
collectionEnabled: collectionPolicy(settingsPath, values.disabled)
|
|
49086
50229
|
};
|
|
49087
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
|
+
});
|
|
49088
50244
|
const collectionStatus = (config) => Effect.gen(function* () {
|
|
49089
50245
|
const stored = (yield* readSettings(config.settingsPath))?.collection.enabled ?? null;
|
|
49090
50246
|
const enabled = config.collectionEnabled();
|
|
@@ -49108,33 +50264,13 @@ const codes = [
|
|
|
49108
50264
|
];
|
|
49109
50265
|
const identifier = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u));
|
|
49110
50266
|
const version = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u));
|
|
49111
|
-
const outcomeSchema = Schema.Literals([
|
|
49112
|
-
"succeeded",
|
|
49113
|
-
"refused",
|
|
49114
|
-
"failed",
|
|
49115
|
-
"cancelled"
|
|
49116
|
-
]);
|
|
49117
50267
|
const completionSchema = Schema.Struct({
|
|
49118
|
-
|
|
49119
|
-
command: Schema.Literal("doctor"),
|
|
49120
|
-
invocation_id: identifier,
|
|
50268
|
+
...CliCommandCompletedTelemetryEventSchema.fields.props.fields,
|
|
49121
50269
|
correlation_id: identifier,
|
|
49122
50270
|
journey_id: identifier,
|
|
49123
|
-
|
|
49124
|
-
"development",
|
|
49125
|
-
"staging",
|
|
49126
|
-
"production",
|
|
49127
|
-
"local"
|
|
49128
|
-
]),
|
|
49129
|
-
producer: Schema.Literal("server"),
|
|
49130
|
-
cli_version: version,
|
|
49131
|
-
sdk_version: version,
|
|
49132
|
-
outcome: outcomeSchema,
|
|
49133
|
-
duration_ms: Schema.Number,
|
|
50271
|
+
posthogDistinctId: Schema.optional(identifier),
|
|
49134
50272
|
wait_duration_ms: Schema.Number,
|
|
49135
|
-
retry_count: Schema.Literal(0)
|
|
49136
|
-
output_mode: Schema.Literals(["human", "json"]),
|
|
49137
|
-
error_code: Schema.optional(Schema.Literals(codes))
|
|
50273
|
+
retry_count: Schema.Literal(0)
|
|
49138
50274
|
});
|
|
49139
50275
|
const CAPTURE_CAPACITY = 32;
|
|
49140
50276
|
const CAPTURE_BYTES = 8192;
|
|
@@ -49148,13 +50284,15 @@ const own = (value, key) => {
|
|
|
49148
50284
|
return;
|
|
49149
50285
|
}
|
|
49150
50286
|
};
|
|
49151
|
-
const inactiveHost = () => postHogObservability(null, {
|
|
50287
|
+
const inactiveHost = (budget) => postHogObservability(null, {
|
|
49152
50288
|
enabled: false,
|
|
49153
|
-
engineeringEnabled: false
|
|
50289
|
+
engineeringEnabled: false,
|
|
50290
|
+
engineeringDeliveryBudget: budget
|
|
49154
50291
|
});
|
|
49155
50292
|
/** One invocation owns its records, metric registry, callback queue, and total flush deadline. */
|
|
49156
50293
|
function observeInvocation(input, work) {
|
|
49157
50294
|
return Effect.suspend(() => {
|
|
50295
|
+
const deliveryBudget = makeCliEngineeringBudget();
|
|
49158
50296
|
const finalizers = [];
|
|
49159
50297
|
const onFinalize = (finalizer) => {
|
|
49160
50298
|
finalizers.push(finalizer);
|
|
@@ -49163,7 +50301,7 @@ function observeInvocation(input, work) {
|
|
|
49163
50301
|
concurrency: "unbounded",
|
|
49164
50302
|
discard: true
|
|
49165
50303
|
}));
|
|
49166
|
-
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));
|
|
49167
50305
|
let disabled = false;
|
|
49168
50306
|
const enabled = () => {
|
|
49169
50307
|
if (disabled) return false;
|
|
@@ -49174,12 +50312,13 @@ function observeInvocation(input, work) {
|
|
|
49174
50312
|
}
|
|
49175
50313
|
return !disabled;
|
|
49176
50314
|
};
|
|
49177
|
-
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;
|
|
49178
50316
|
const host = input.host;
|
|
49179
50317
|
const token = Redacted.value(input.projectToken);
|
|
49180
50318
|
let engineering;
|
|
49181
50319
|
try {
|
|
49182
50320
|
Schema.decodeUnknownSync(identifier)(input.invocationId);
|
|
50321
|
+
if (input.anonymousIdentity !== void 0) Schema.decodeUnknownSync(identifier)(input.anonymousIdentity.id);
|
|
49183
50322
|
Schema.decodeUnknownSync(version)(input.cliVersion);
|
|
49184
50323
|
engineering = makeEngineeringTelemetryLayer({
|
|
49185
50324
|
host,
|
|
@@ -49188,6 +50327,7 @@ function observeInvocation(input, work) {
|
|
|
49188
50327
|
producer: "server",
|
|
49189
50328
|
sdkVersion: input.sdkVersion,
|
|
49190
50329
|
serviceName: "capxul-cli",
|
|
50330
|
+
deliveryBudget,
|
|
49191
50331
|
enabled,
|
|
49192
50332
|
shutdownTimeout: 0,
|
|
49193
50333
|
exportInterval: "1 hour"
|
|
@@ -49195,7 +50335,7 @@ function observeInvocation(input, work) {
|
|
|
49195
50335
|
} catch {
|
|
49196
50336
|
return inactiveWork;
|
|
49197
50337
|
}
|
|
49198
|
-
const layer = Layer.merge(engineering, FetchHttpClient.layer).pipe(Layer.provideMerge(Layer.succeed(
|
|
50338
|
+
const layer = Layer.merge(engineering, FetchHttpClient.layer).pipe(Layer.provideMerge(Layer.succeed(Logger.LogToStderr, true)));
|
|
49199
50339
|
return Effect.scoped(Effect.gen(function* () {
|
|
49200
50340
|
const built = yield* Effect.exit(Layer.build(layer));
|
|
49201
50341
|
if (Exit.isFailure(built)) return yield* inactiveWork;
|
|
@@ -49211,30 +50351,47 @@ function observeInvocation(input, work) {
|
|
|
49211
50351
|
producer: "server",
|
|
49212
50352
|
cli_version: input.cliVersion,
|
|
49213
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
|
+
},
|
|
49214
50368
|
invocation_id: input.invocationId,
|
|
49215
50369
|
correlation_id: input.invocationId,
|
|
49216
50370
|
journey_id: input.invocationId
|
|
49217
50371
|
};
|
|
49218
|
-
const capture = (event, properties) => {
|
|
50372
|
+
const capture = (event, properties, identity) => {
|
|
49219
50373
|
if (!enabled()) {
|
|
49220
50374
|
dropped += 1;
|
|
49221
50375
|
return;
|
|
49222
50376
|
}
|
|
50377
|
+
const occurredAt = identity?.observedAt ?? clock.currentTimeMillisUnsafe();
|
|
49223
50378
|
const record = sanitizePostHogBrowserEvent({
|
|
49224
50379
|
event,
|
|
49225
50380
|
properties: {
|
|
49226
50381
|
...properties,
|
|
49227
50382
|
...envelope,
|
|
50383
|
+
occurred_at: occurredAt,
|
|
49228
50384
|
...nativeContext
|
|
49229
50385
|
}
|
|
49230
50386
|
});
|
|
49231
50387
|
const body = JSON.stringify({
|
|
49232
50388
|
api_key: token,
|
|
49233
50389
|
...record,
|
|
49234
|
-
uuid: crypto.randomUUID(),
|
|
50390
|
+
uuid: identity?.eventId ?? crypto.randomUUID(),
|
|
50391
|
+
timestamp: new Date(occurredAt).toISOString(),
|
|
49235
50392
|
properties: {
|
|
49236
50393
|
...record.properties,
|
|
49237
|
-
distinct_id: input.invocationId,
|
|
50394
|
+
distinct_id: input.anonymousIdentity?.id ?? input.invocationId,
|
|
49238
50395
|
$process_person_profile: false
|
|
49239
50396
|
}
|
|
49240
50397
|
});
|
|
@@ -49267,9 +50424,40 @@ function observeInvocation(input, work) {
|
|
|
49267
50424
|
...envelope
|
|
49268
50425
|
}
|
|
49269
50426
|
});
|
|
49270
|
-
if (Option.isSome(decoded) && Queue.sizeUnsafe(queue) <
|
|
50427
|
+
if (Option.isSome(decoded) && Queue.sizeUnsafe(queue) < 29) capture(event, { ...decoded.value.props });
|
|
49271
50428
|
else dropped += 1;
|
|
49272
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;
|
|
49273
50461
|
let commandResult;
|
|
49274
50462
|
yield* Effect.gen(function* () {
|
|
49275
50463
|
const span = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
@@ -49282,15 +50470,39 @@ function observeInvocation(input, work) {
|
|
|
49282
50470
|
enabled,
|
|
49283
50471
|
engineeringEnabled: enabled,
|
|
49284
50472
|
engineeringPrivacy: "minimal",
|
|
50473
|
+
engineeringDeliveryBudget: deliveryBudget,
|
|
49285
50474
|
capxulEnv: input.environment,
|
|
49286
50475
|
context: {
|
|
49287
50476
|
application: "capxul-cli",
|
|
50477
|
+
...input.anonymousIdentity === void 0 ? {} : { anonymousId: input.anonymousIdentity.id },
|
|
49288
50478
|
release: input.cliVersion,
|
|
49289
50479
|
correlationId: input.invocationId,
|
|
49290
50480
|
journeyId: input.invocationId,
|
|
49291
50481
|
traceparent
|
|
49292
50482
|
}
|
|
49293
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
|
+
}
|
|
49294
50506
|
const waitStarted = clock.currentTimeMillisUnsafe();
|
|
49295
50507
|
const result = yield* Effect.exit(restore(Effect.suspend(() => work(observation, onFinalize))));
|
|
49296
50508
|
commandResult = result;
|
|
@@ -49303,8 +50515,12 @@ function observeInvocation(input, work) {
|
|
|
49303
50515
|
const duration = Math.max(0, clock.currentTimeMillisUnsafe() - started);
|
|
49304
50516
|
const completion = Schema.decodeUnknownSync(completionSchema)({
|
|
49305
50517
|
...envelope,
|
|
50518
|
+
...input.anonymousIdentity === void 0 ? {} : { posthogDistinctId: input.anonymousIdentity.id },
|
|
50519
|
+
occurred_at: clock.currentTimeMillisUnsafe(),
|
|
49306
50520
|
schema_version: 1,
|
|
49307
50521
|
command: input.command,
|
|
50522
|
+
capability: input.capability,
|
|
50523
|
+
invocation_kind: input.kind,
|
|
49308
50524
|
outcome,
|
|
49309
50525
|
duration_ms: duration,
|
|
49310
50526
|
wait_duration_ms: waitDuration,
|
|
@@ -49313,13 +50529,15 @@ function observeInvocation(input, work) {
|
|
|
49313
50529
|
...errorCode === void 0 ? {} : { error_code: errorCode }
|
|
49314
50530
|
});
|
|
49315
50531
|
yield* Effect.annotateCurrentSpan(completion);
|
|
49316
|
-
yield*
|
|
50532
|
+
yield* logEngineeringInfo("cli.command.completed", completion);
|
|
49317
50533
|
for (const diagnostic of sdkDiagnostics) yield* Effect.logInfo("cli.sdk.operation.failed").pipe(Effect.annotateLogs({
|
|
49318
50534
|
...envelope,
|
|
49319
50535
|
...diagnostic
|
|
49320
50536
|
}));
|
|
49321
50537
|
const metricAttributes = {
|
|
49322
50538
|
command: input.command,
|
|
50539
|
+
capability: input.capability,
|
|
50540
|
+
invocation_kind: input.kind,
|
|
49323
50541
|
outcome,
|
|
49324
50542
|
capxul_env: input.environment
|
|
49325
50543
|
};
|
|
@@ -49339,15 +50557,27 @@ function observeInvocation(input, work) {
|
|
|
49339
50557
|
unit: "s"
|
|
49340
50558
|
}
|
|
49341
50559
|
}), duration / 1e3);
|
|
49342
|
-
const
|
|
49343
|
-
name: "
|
|
50560
|
+
const terminal = Schema.decodeUnknownSync(CliCommandCompletedTelemetryEventSchema)({
|
|
50561
|
+
name: "cli_command_completed",
|
|
49344
50562
|
props: {
|
|
49345
50563
|
...envelope,
|
|
49346
|
-
|
|
50564
|
+
occurred_at: clock.currentTimeMillisUnsafe(),
|
|
50565
|
+
outcome,
|
|
50566
|
+
duration_ms: duration,
|
|
50567
|
+
...errorCode === void 0 ? {} : { error_code: errorCode }
|
|
49347
50568
|
}
|
|
49348
|
-
};
|
|
49349
|
-
|
|
49350
|
-
|
|
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
|
+
}
|
|
49351
50581
|
if (Exit.isFailure(result)) {
|
|
49352
50582
|
const defect = result.cause.reasons.find(Cause.isDieReason);
|
|
49353
50583
|
if (defect !== void 0) {
|
|
@@ -49358,7 +50588,7 @@ function observeInvocation(input, work) {
|
|
|
49358
50588
|
if (typeof stack === "string") error.stack = stack.slice(0, 8192);
|
|
49359
50589
|
} catch {}
|
|
49360
50590
|
yield* captureException({
|
|
49361
|
-
emit: (exception) => Effect.sync(() => capture("$exception", projectException(exception.props))),
|
|
50591
|
+
emit: (exception) => Effect.sync(() => capture("$exception", projectException(exception.props, input.command))),
|
|
49362
50592
|
identify: () => Effect.void,
|
|
49363
50593
|
group: () => Effect.void,
|
|
49364
50594
|
reset: () => Effect.void
|
|
@@ -49367,41 +50597,12 @@ function observeInvocation(input, work) {
|
|
|
49367
50597
|
}
|
|
49368
50598
|
}).pipe(Effect.catchCause(() => Effect.void));
|
|
49369
50599
|
if (Exit.isFailure(result)) return yield* Effect.fail("CLI command failed");
|
|
49370
|
-
}).pipe(Effect.withSpan(
|
|
49371
|
-
const clientHttp = HttpClient.transform(yield* HttpClient.HttpClient, (response, request) => Effect.suspend(() => {
|
|
49372
|
-
if (enabled()) return response;
|
|
49373
|
-
dropped += 1;
|
|
49374
|
-
return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
|
|
49375
|
-
})).pipe(HttpClient.filterStatusOk, HttpClient.retryTransient({
|
|
49376
|
-
times: 1,
|
|
49377
|
-
schedule: Schedule.spaced("100 millis")
|
|
49378
|
-
}));
|
|
49379
|
-
const flushCaptures = Effect.gen(function* () {
|
|
49380
|
-
const pending = yield* Queue.clear(queue);
|
|
49381
|
-
if (!enabled()) {
|
|
49382
|
-
dropped += pending.length;
|
|
49383
|
-
return;
|
|
49384
|
-
}
|
|
49385
|
-
pendingCaptures = pending.length;
|
|
49386
|
-
yield* Effect.forEach(pending, (item) => Effect.suspend(() => {
|
|
49387
|
-
if (!enabled()) {
|
|
49388
|
-
dropped += 1;
|
|
49389
|
-
return Effect.void;
|
|
49390
|
-
}
|
|
49391
|
-
return clientHttp.execute(HttpClientRequest.post(`${host.replace(/\/+$/, "")}/capture/`).pipe(HttpClientRequest.bodyText(item, "application/json"))).pipe(Effect.asVoid, Effect.catchCause(() => Effect.sync(() => {
|
|
49392
|
-
dropped += 1;
|
|
49393
|
-
})));
|
|
49394
|
-
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
49395
|
-
pendingCaptures -= 1;
|
|
49396
|
-
}))), {
|
|
49397
|
-
concurrency: 4,
|
|
49398
|
-
discard: true
|
|
49399
|
-
});
|
|
49400
|
-
}).pipe(Effect.withTracerEnabled(false), Effect.provideService(HttpClient.TracerPropagationEnabled, false));
|
|
50600
|
+
}).pipe(Effect.withSpan(`cli.command.${input.command}`), Effect.exit);
|
|
49401
50601
|
yield* Effect.all([
|
|
49402
50602
|
finalize,
|
|
49403
50603
|
flushCaptures,
|
|
49404
|
-
flushEngineeringTelemetry
|
|
50604
|
+
flushEngineeringTelemetry,
|
|
50605
|
+
startDelivery === void 0 ? Effect.void : Fiber.join(startDelivery)
|
|
49405
50606
|
], {
|
|
49406
50607
|
concurrency: "unbounded",
|
|
49407
50608
|
discard: true
|
|
@@ -49411,6 +50612,7 @@ function observeInvocation(input, work) {
|
|
|
49411
50612
|
yield* Queue.shutdown(queue);
|
|
49412
50613
|
try {
|
|
49413
50614
|
input.onDropped?.(dropped);
|
|
50615
|
+
input.onEngineeringDropped?.(deliveryBudget.snapshot().dropped);
|
|
49414
50616
|
} catch {}
|
|
49415
50617
|
if (commandResult === void 0) return yield* Effect.die("CLI observation could not start");
|
|
49416
50618
|
return yield* commandResult;
|
|
@@ -49445,7 +50647,7 @@ function projectSdkProperties(properties) {
|
|
|
49445
50647
|
if (failureMode !== void 0) output.failure_mode = failureMode;
|
|
49446
50648
|
return output;
|
|
49447
50649
|
}
|
|
49448
|
-
function projectException(properties) {
|
|
50650
|
+
function projectException(properties, command) {
|
|
49449
50651
|
const sanitized = sanitizePostHogBrowserEvent({
|
|
49450
50652
|
event: "$exception",
|
|
49451
50653
|
properties: { ...properties }
|
|
@@ -49469,8 +50671,8 @@ function projectException(properties) {
|
|
|
49469
50671
|
$exception_type: "CLI_DEFECT",
|
|
49470
50672
|
$exception_message: "CLI command failed",
|
|
49471
50673
|
$exception_level: "error",
|
|
49472
|
-
$exception_fingerprint: `cli.
|
|
49473
|
-
$issue_name:
|
|
50674
|
+
$exception_fingerprint: `cli.${command}.CLI_DEFECT:${frames[0]?.filename ?? "boundary"}:${frames[0]?.function ?? "unknown"}`,
|
|
50675
|
+
$issue_name: `CLI ${command} defect`,
|
|
49474
50676
|
$exception_list: [{
|
|
49475
50677
|
type: "CLI_DEFECT",
|
|
49476
50678
|
value: "CLI command failed",
|
|
@@ -49496,41 +50698,101 @@ function renderResult(mode, envelope, lines = []) {
|
|
|
49496
50698
|
}
|
|
49497
50699
|
//#endregion
|
|
49498
50700
|
//#region src/version.ts
|
|
49499
|
-
const cliVersion = "4.20.0-beta.
|
|
49500
|
-
const sdkVersion = "4.20.0-beta.
|
|
50701
|
+
const cliVersion = "4.20.0-beta.3";
|
|
50702
|
+
const sdkVersion = "4.20.0-beta.3";
|
|
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
|
+
}
|
|
49501
50721
|
//#endregion
|
|
49502
50722
|
//#region src/runtime/run-command.ts
|
|
49503
50723
|
var CliRuntime = class extends Context.Service()("capxul-cli/Runtime") {};
|
|
49504
|
-
const
|
|
49505
|
-
|
|
49506
|
-
const
|
|
49507
|
-
yield*
|
|
49508
|
-
|
|
49509
|
-
|
|
49510
|
-
|
|
49511
|
-
const
|
|
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);
|
|
49512
50743
|
let dropped = 0;
|
|
49513
|
-
|
|
49514
|
-
|
|
49515
|
-
|
|
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
|
+
} },
|
|
49516
50755
|
cliVersion,
|
|
49517
50756
|
sdkVersion,
|
|
49518
|
-
environment: config
|
|
49519
|
-
|
|
49520
|
-
|
|
49521
|
-
|
|
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),
|
|
49522
50765
|
outputMode: runtime.mode,
|
|
49523
50766
|
onDropped: (count) => {
|
|
49524
50767
|
dropped = count;
|
|
50768
|
+
},
|
|
50769
|
+
onEngineeringDropped: (count) => {
|
|
50770
|
+
engineeringDropped = count;
|
|
49525
50771
|
}
|
|
49526
|
-
}, (
|
|
49527
|
-
|
|
49528
|
-
|
|
49529
|
-
|
|
49530
|
-
|
|
49531
|
-
|
|
49532
|
-
|
|
49533
|
-
}
|
|
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);
|
|
49534
50796
|
});
|
|
49535
50797
|
//#endregion
|
|
49536
50798
|
//#region src/platform/capxul.ts
|
|
@@ -49614,42 +50876,63 @@ const checkDoctor = (config, options) => Effect.gen(function* () {
|
|
|
49614
50876
|
});
|
|
49615
50877
|
//#endregion
|
|
49616
50878
|
//#region src/commands/doctor.ts
|
|
50879
|
+
const metadata = {
|
|
50880
|
+
name: "doctor",
|
|
50881
|
+
capability: "diagnostics",
|
|
50882
|
+
observation: "command"
|
|
50883
|
+
};
|
|
49617
50884
|
const doctor = Command.make("doctor", {
|
|
49618
50885
|
online: Flag.boolean("online").pipe(Flag.withDefault(false), Flag.withDescription("Also check the backend through the public SDK")),
|
|
49619
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)"))
|
|
49620
50887
|
}, ({ online, timeoutMs }) => runCommand({
|
|
49621
|
-
|
|
50888
|
+
...metadata,
|
|
49622
50889
|
network: online
|
|
49623
50890
|
}, (config, observation, onFinalize) => checkDoctor(config, {
|
|
49624
50891
|
online,
|
|
49625
50892
|
timeoutMs,
|
|
49626
50893
|
...observation === void 0 ? {} : { observation },
|
|
49627
50894
|
...onFinalize === void 0 ? {} : { onFinalize }
|
|
49628
|
-
}))).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"));
|
|
49629
50896
|
//#endregion
|
|
49630
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
|
+
};
|
|
49631
50911
|
const report = (config) => collectionStatus(config).pipe(Effect.map((status) => ({
|
|
49632
50912
|
data: status,
|
|
49633
50913
|
lines: [
|
|
49634
50914
|
`Remote CLI observation ${status.enabled ? "enabled" : "disabled"}.`,
|
|
49635
50915
|
...status.configured ? [] : ["Remote delivery is not configured."],
|
|
49636
50916
|
...config.forcedDisabled ? ["CAPXUL_TELEMETRY_DISABLED overrides the saved preference."] : [],
|
|
49637
|
-
"
|
|
50917
|
+
"Commands collect usage and diagnostics. Collection controls and completions send nothing."
|
|
49638
50918
|
]
|
|
49639
50919
|
})));
|
|
49640
50920
|
const status = Command.make("status", {}, () => runCommand({
|
|
49641
|
-
|
|
50921
|
+
...statusMetadata,
|
|
49642
50922
|
network: false
|
|
49643
|
-
}, report));
|
|
50923
|
+
}, report)).pipe(Command.annotate(CliCommandMetadata, statusMetadata));
|
|
49644
50924
|
const enable = Command.make("enable", {}, () => runCommand({
|
|
49645
|
-
|
|
50925
|
+
...enableMetadata,
|
|
49646
50926
|
network: false
|
|
49647
|
-
}, (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));
|
|
49648
50928
|
const disable = Command.make("disable", {}, () => runCommand({
|
|
49649
|
-
|
|
50929
|
+
...disableMetadata,
|
|
49650
50930
|
network: false
|
|
49651
|
-
}, (config) => setCollectionEnabled(config.settingsPath, false).pipe(Effect.andThen(report(config)))));
|
|
49652
|
-
const telemetry = Command.make("telemetry").pipe(Command.
|
|
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([
|
|
49653
50936
|
status,
|
|
49654
50937
|
enable,
|
|
49655
50938
|
disable
|
|
@@ -49679,52 +50962,87 @@ const outputMode = (args) => Effect.gen(function* () {
|
|
|
49679
50962
|
});
|
|
49680
50963
|
//#endregion
|
|
49681
50964
|
//#region src/command.ts
|
|
49682
|
-
const command = Command.make("capxul").pipe(Command.
|
|
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]));
|
|
49683
50970
|
//#endregion
|
|
49684
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
|
+
};
|
|
49685
50977
|
const runCli = (platform) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
49686
50978
|
const stdio = yield* Stdio.Stdio;
|
|
50979
|
+
const stateServices = yield* Effect.context().pipe(Effect.map(Context.pick(Crypto.Crypto, FileSystem.FileSystem, Path.Path)));
|
|
49687
50980
|
const args = yield* stdio.args;
|
|
49688
50981
|
const mode = yield* outputMode(args);
|
|
49689
50982
|
const interactive = yield* stdio.stdinIsTerminal;
|
|
49690
50983
|
const cliConfig = yield* CliConfig.CliConfig;
|
|
49691
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
|
+
};
|
|
49692
50998
|
const nativeConsole = yield* Console.Console;
|
|
49693
50999
|
const output = [];
|
|
49694
51000
|
const capture = (...values) => {
|
|
49695
51001
|
output.push(values.map(String).join(" "));
|
|
49696
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);
|
|
49697
51019
|
const exit = yield* restore(Command.runWith(command, {
|
|
49698
51020
|
version: cliVersion,
|
|
49699
51021
|
renderErrors: false
|
|
49700
|
-
})(args).pipe(Effect.provideService(CliRuntime, {
|
|
49701
|
-
mode,
|
|
49702
|
-
current,
|
|
49703
|
-
...platform
|
|
49704
|
-
}), 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, {
|
|
49705
51023
|
...nativeConsole,
|
|
49706
51024
|
log: capture,
|
|
49707
51025
|
info: capture
|
|
49708
51026
|
}))).pipe(Effect.exit);
|
|
49709
51027
|
if (Exit.isSuccess(exit)) {
|
|
49710
|
-
if (output.length > 0)
|
|
49711
|
-
|
|
49712
|
-
version: 1,
|
|
49713
|
-
command: "capxul",
|
|
49714
|
-
invocationId: null,
|
|
49715
|
-
outcome: "succeeded",
|
|
49716
|
-
data: { text: output.join("\n") }
|
|
49717
|
-
});
|
|
49718
|
-
else yield* writeStdout(`${output.join("\n")}\n`);
|
|
49719
|
-
}
|
|
51028
|
+
if ((yield* Ref.get(current)) === null && output.length > 0) yield* runInvocation(runtime, metadataAtPath(command, [command.name]), "help", () => flushNative);
|
|
51029
|
+
else yield* flushNative;
|
|
49720
51030
|
return 0;
|
|
49721
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
|
+
}
|
|
49722
51040
|
const invocation = yield* Ref.get(current);
|
|
49723
51041
|
const failure = classifyFailure(exit.cause);
|
|
49724
51042
|
yield* renderResult(mode, {
|
|
49725
51043
|
version: 1,
|
|
49726
|
-
command: invocation?.command ?? parsedCommand(exit.cause),
|
|
49727
|
-
invocationId: invocation?.invocationId
|
|
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,
|
|
49728
51046
|
outcome: failure.outcome,
|
|
49729
51047
|
error: {
|
|
49730
51048
|
code: failure.code,
|