@debugbundle/cli 0.1.1 → 0.1.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/dist/main.cjs +1217 -238
- package/package.json +2 -2
package/dist/main.cjs
CHANGED
|
@@ -14483,6 +14483,8 @@ var CaptureBreadcrumbsValues = ["local_only", "exception_only", "standalone"];
|
|
|
14483
14483
|
var CaptureBreadcrumbsSchema = external_exports.enum(CaptureBreadcrumbsValues);
|
|
14484
14484
|
var CaptureProbeEventsValues = ["buffer_only", "standalone_when_activated"];
|
|
14485
14485
|
var CaptureProbeEventsSchema = external_exports.enum(CaptureProbeEventsValues);
|
|
14486
|
+
var RequestSignalClassificationValues = ["incident_signal", "context_signal"];
|
|
14487
|
+
var RequestSignalClassificationSchema = external_exports.enum(RequestSignalClassificationValues);
|
|
14486
14488
|
var CapturePolicySchema = external_exports.object({
|
|
14487
14489
|
project_id: external_exports.string().uuid(),
|
|
14488
14490
|
preset: CapturePresetSchema,
|
|
@@ -14499,6 +14501,55 @@ var CapturePolicyUpdateSchema = external_exports.object({
|
|
|
14499
14501
|
capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable().optional(),
|
|
14500
14502
|
capture_probe_events: CaptureProbeEventsSchema.nullable().optional()
|
|
14501
14503
|
});
|
|
14504
|
+
var BALANCED_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([408, 423, 424, 425, 429]);
|
|
14505
|
+
var INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([...BALANCED_IMMEDIATE_REQUEST_STATUSES, 409]);
|
|
14506
|
+
var BALANCED_STANDARD_ANOMALY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 409, 422]);
|
|
14507
|
+
var BALANCED_HIGH_VOLUME_ANOMALY_STATUSES = /* @__PURE__ */ new Set([400, 410]);
|
|
14508
|
+
var INVESTIGATIVE_ANOMALY_STATUSES = /* @__PURE__ */ new Set([...BALANCED_STANDARD_ANOMALY_STATUSES, ...BALANCED_HIGH_VOLUME_ANOMALY_STATUSES]);
|
|
14509
|
+
function classifyRequestStatus(input) {
|
|
14510
|
+
const { responseStatus, capturePreset } = input;
|
|
14511
|
+
if (responseStatus === null || !Number.isFinite(responseStatus)) {
|
|
14512
|
+
return "context_signal";
|
|
14513
|
+
}
|
|
14514
|
+
if (responseStatus >= 500) {
|
|
14515
|
+
return "incident_signal";
|
|
14516
|
+
}
|
|
14517
|
+
if (capturePreset === "investigative") {
|
|
14518
|
+
return INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
|
|
14519
|
+
}
|
|
14520
|
+
if (capturePreset === "balanced") {
|
|
14521
|
+
return BALANCED_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
|
|
14522
|
+
}
|
|
14523
|
+
return "context_signal";
|
|
14524
|
+
}
|
|
14525
|
+
function getRequestAnomalyThreshold(input) {
|
|
14526
|
+
const { responseStatus, capturePreset } = input;
|
|
14527
|
+
if (responseStatus === null || !Number.isFinite(responseStatus) || responseStatus < 400 || responseStatus >= 500) {
|
|
14528
|
+
return null;
|
|
14529
|
+
}
|
|
14530
|
+
if (capturePreset === "minimal") {
|
|
14531
|
+
return null;
|
|
14532
|
+
}
|
|
14533
|
+
if (capturePreset === "investigative") {
|
|
14534
|
+
return INVESTIGATIVE_ANOMALY_STATUSES.has(responseStatus) ? {
|
|
14535
|
+
minimum_occurrences_5m: 8,
|
|
14536
|
+
minimum_ratio_5m_to_1h: 2
|
|
14537
|
+
} : null;
|
|
14538
|
+
}
|
|
14539
|
+
if (BALANCED_STANDARD_ANOMALY_STATUSES.has(responseStatus)) {
|
|
14540
|
+
return {
|
|
14541
|
+
minimum_occurrences_5m: 20,
|
|
14542
|
+
minimum_ratio_5m_to_1h: 3
|
|
14543
|
+
};
|
|
14544
|
+
}
|
|
14545
|
+
if (BALANCED_HIGH_VOLUME_ANOMALY_STATUSES.has(responseStatus)) {
|
|
14546
|
+
return {
|
|
14547
|
+
minimum_occurrences_5m: 50,
|
|
14548
|
+
minimum_ratio_5m_to_1h: 5
|
|
14549
|
+
};
|
|
14550
|
+
}
|
|
14551
|
+
return null;
|
|
14552
|
+
}
|
|
14502
14553
|
|
|
14503
14554
|
// ../../packages/shared-types/src/index.ts
|
|
14504
14555
|
function createUuidV4() {
|
|
@@ -14547,6 +14598,26 @@ var InlineProbeDataSchema = external_exports.object({
|
|
|
14547
14598
|
version: external_exports.literal(1),
|
|
14548
14599
|
items: external_exports.array(InlineProbeDataItemSchema)
|
|
14549
14600
|
}).strict();
|
|
14601
|
+
var RuntimeMemoryStatsSchema = external_exports.object({
|
|
14602
|
+
rss: external_exports.number().nonnegative().nullable(),
|
|
14603
|
+
heap_total: external_exports.number().nonnegative().nullable(),
|
|
14604
|
+
heap_used: external_exports.number().nonnegative().nullable(),
|
|
14605
|
+
external: external_exports.number().nonnegative().nullable(),
|
|
14606
|
+
peak: external_exports.number().nonnegative().nullable()
|
|
14607
|
+
}).strict();
|
|
14608
|
+
var BackendRuntimePayloadSchema = external_exports.object({
|
|
14609
|
+
version: external_exports.string().min(1),
|
|
14610
|
+
platform: external_exports.string().min(1).nullable().optional(),
|
|
14611
|
+
arch: external_exports.string().min(1).nullable().optional(),
|
|
14612
|
+
pid: external_exports.number().int().nonnegative().nullable().optional(),
|
|
14613
|
+
cwd: external_exports.string().min(1).nullable().optional(),
|
|
14614
|
+
uptime_sec: external_exports.number().nonnegative().nullable().optional(),
|
|
14615
|
+
hostname: external_exports.string().min(1).nullable().optional(),
|
|
14616
|
+
thread_id: external_exports.union([external_exports.string(), external_exports.number()]).nullable().optional(),
|
|
14617
|
+
framework_version: external_exports.string().min(1).nullable().optional(),
|
|
14618
|
+
memory: RuntimeMemoryStatsSchema.nullable().optional(),
|
|
14619
|
+
framework_extras: external_exports.record(external_exports.string(), external_exports.unknown()).nullable().optional()
|
|
14620
|
+
}).strict();
|
|
14550
14621
|
var BackendExceptionPayloadSchema = external_exports.object({
|
|
14551
14622
|
name: external_exports.string().min(1),
|
|
14552
14623
|
message: external_exports.string().min(1),
|
|
@@ -14564,9 +14635,7 @@ var BackendExceptionPayloadSchema = external_exports.object({
|
|
|
14564
14635
|
headers: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
14565
14636
|
body: external_exports.unknown().optional()
|
|
14566
14637
|
}),
|
|
14567
|
-
runtime:
|
|
14568
|
-
version: external_exports.string().min(1)
|
|
14569
|
-
}),
|
|
14638
|
+
runtime: BackendRuntimePayloadSchema,
|
|
14570
14639
|
probe_data: InlineProbeDataSchema.optional()
|
|
14571
14640
|
}).strict();
|
|
14572
14641
|
var RequestEventPayloadSchema = external_exports.object({
|
|
@@ -14837,13 +14906,7 @@ var ContextDeploySchema = external_exports.object({
|
|
|
14837
14906
|
deployed_at: external_exports.string().datetime().nullable(),
|
|
14838
14907
|
regression_window: external_exports.boolean().nullable()
|
|
14839
14908
|
});
|
|
14840
|
-
var MemoryStatsSchema =
|
|
14841
|
-
rss: external_exports.number().nonnegative().nullable(),
|
|
14842
|
-
heap_total: external_exports.number().nonnegative().nullable(),
|
|
14843
|
-
heap_used: external_exports.number().nonnegative().nullable(),
|
|
14844
|
-
external: external_exports.number().nonnegative().nullable(),
|
|
14845
|
-
peak: external_exports.number().nonnegative().nullable()
|
|
14846
|
-
});
|
|
14909
|
+
var MemoryStatsSchema = RuntimeMemoryStatsSchema;
|
|
14847
14910
|
var ContextRuntimeSchema = external_exports.object({
|
|
14848
14911
|
version: external_exports.literal(1),
|
|
14849
14912
|
name: external_exports.string().min(1),
|
|
@@ -15112,7 +15175,7 @@ function buildCliReference() {
|
|
|
15112
15175
|
"- `debugbundle ingest <file> --format <format> [--json]`",
|
|
15113
15176
|
"- `debugbundle watch --log <file> --format <format> [--json]`",
|
|
15114
15177
|
"- `debugbundle watch --cloud --log <file> --format <format> [--json]`",
|
|
15115
|
-
"- `debugbundle process [--json]`",
|
|
15178
|
+
"- `debugbundle process [--preset <minimal|balanced|investigative>] [--json]`",
|
|
15116
15179
|
"",
|
|
15117
15180
|
"## Investigation",
|
|
15118
15181
|
"",
|
|
@@ -15658,7 +15721,7 @@ var import_node_path4 = require("node:path");
|
|
|
15658
15721
|
|
|
15659
15722
|
// ../../packages/retrieval-client/src/index.ts
|
|
15660
15723
|
var IncidentReasonSchema = external_exports.object({
|
|
15661
|
-
kind: external_exports.enum(["backend_exception", "frontend_exception", "
|
|
15724
|
+
kind: external_exports.enum(["backend_exception", "frontend_exception", "request_failure", "error_log"]),
|
|
15662
15725
|
description: external_exports.string(),
|
|
15663
15726
|
event_type: external_exports.enum(["backend_exception", "frontend_exception", "request_event", "log_event"]),
|
|
15664
15727
|
event_class: external_exports.literal("incident_signal"),
|
|
@@ -16079,7 +16142,8 @@ function buildRedactionRecord(bundleBody) {
|
|
|
16079
16142
|
function buildVisibilityRecord(input) {
|
|
16080
16143
|
const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
|
|
16081
16144
|
const matchedFields = input.incident.matched_fields.length === 0 ? "none" : input.incident.matched_fields.join(", ");
|
|
16082
|
-
const
|
|
16145
|
+
const isRequestAnomaly = input.incident.matched_fields.includes("request_anomaly");
|
|
16146
|
+
const grouping = input.primarySignal.kind === "request_failure" && input.primarySignal.request_method !== null && routeTarget !== null ? isRequestAnomaly ? `Repeated request-anomaly incidents with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint once the anomaly threshold fires. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `Repeated request-failure incidents with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `This incident groups repeated failures by fingerprint version ${input.incident.fingerprint_version} inside the service and environment boundary, with matched fields ${matchedFields}.`;
|
|
16083
16147
|
const spikeLead = input.incident.spike_detected_at === void 0 || input.incident.spike_detected_at === null ? "This incident is not currently marked as spiking." : `This incident was marked as spiking at ${input.incident.spike_detected_at}.`;
|
|
16084
16148
|
return {
|
|
16085
16149
|
grouping,
|
|
@@ -16090,15 +16154,16 @@ function buildVisibilityRecord(input) {
|
|
|
16090
16154
|
}
|
|
16091
16155
|
function buildSuggestedNextChecks(input) {
|
|
16092
16156
|
const suggestions = [];
|
|
16157
|
+
const isRequestAnomaly = input.incident.matched_fields.includes("request_anomaly");
|
|
16093
16158
|
if (input.bundle.status === "pending") {
|
|
16094
16159
|
suggestions.push("Wait for bundle generation to finish, then rerun the incident context command.");
|
|
16095
16160
|
} else if (input.bundle.status === "failed") {
|
|
16096
16161
|
suggestions.push("Inspect bundle generation status or retry bundle retrieval to recover missing context.");
|
|
16097
16162
|
}
|
|
16098
16163
|
const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
|
|
16099
|
-
if (input.primarySignal.
|
|
16164
|
+
if (input.primarySignal.kind === "request_failure" && input.primarySignal.request_method !== null && routeTarget !== null) {
|
|
16100
16165
|
suggestions.push(
|
|
16101
|
-
`Inspect the ${input.primarySignal.request_method} ${routeTarget
|
|
16166
|
+
isRequestAnomaly ? `Inspect the ${input.primarySignal.request_method} ${routeTarget} handler behind this repeated request-anomaly path.` : `Inspect the ${input.primarySignal.request_method} ${routeTarget} handler behind this request-failure path.`
|
|
16102
16167
|
);
|
|
16103
16168
|
}
|
|
16104
16169
|
const firstApplicationFrame = input.primarySignal.first_application_frame;
|
|
@@ -16187,12 +16252,13 @@ function deriveIncidentReasonFromSignal(input) {
|
|
|
16187
16252
|
};
|
|
16188
16253
|
case "request_event": {
|
|
16189
16254
|
const responseStatus = typeof input.response_status === "number" && Number.isFinite(input.response_status) ? input.response_status : null;
|
|
16255
|
+
const isRequestAnomaly = input.request_anomaly === true;
|
|
16190
16256
|
return {
|
|
16191
|
-
kind: "
|
|
16192
|
-
description: responseStatus !== null
|
|
16257
|
+
kind: "request_failure",
|
|
16258
|
+
description: isRequestAnomaly ? responseStatus !== null ? `request_event response_status=${responseStatus} crossed the repeated request anomaly threshold` : "request_event crossed the repeated request anomaly threshold" : responseStatus !== null ? `request_event response_status=${responseStatus} matched the immediate request failure incident rule` : "request_event matched the immediate request failure incident rule",
|
|
16193
16259
|
event_type: "request_event",
|
|
16194
16260
|
event_class: "incident_signal",
|
|
16195
|
-
matched_policy: "
|
|
16261
|
+
matched_policy: isRequestAnomaly ? "Repeated contextual request failures crossed the request anomaly threshold" : "Immediate request failure statuses bypass capture_request_events suppression"
|
|
16196
16262
|
};
|
|
16197
16263
|
}
|
|
16198
16264
|
case "log_event": {
|
|
@@ -16221,9 +16287,11 @@ function deriveIncidentReasonFromSourceEventTypes(eventTypes) {
|
|
|
16221
16287
|
return null;
|
|
16222
16288
|
}
|
|
16223
16289
|
|
|
16224
|
-
// ../../packages/auth/src/
|
|
16290
|
+
// ../../packages/auth/src/primitives.ts
|
|
16225
16291
|
var import_argon2 = require("@node-rs/argon2");
|
|
16226
|
-
|
|
16292
|
+
|
|
16293
|
+
// ../../packages/auth/src/web-session-auth.ts
|
|
16294
|
+
var DEFAULT_SESSION_LIFETIME_MS = 1e3 * 60 * 60 * 24 * 7;
|
|
16227
16295
|
var DEFAULT_EMAIL_AUTH_CODE_LIFETIME_MS = 1e3 * 60 * 10;
|
|
16228
16296
|
var DEFAULT_GITHUB_OAUTH_STATE_LIFETIME_MS = 1e3 * 60 * 10;
|
|
16229
16297
|
|
|
@@ -16497,7 +16565,7 @@ function getRequestResponseStatus(payload) {
|
|
|
16497
16565
|
const status = payload?.["response_status"];
|
|
16498
16566
|
return typeof status === "number" && Number.isFinite(status) ? status : null;
|
|
16499
16567
|
}
|
|
16500
|
-
function classifyEvent(eventType, logLevel, probeActivationId, payload) {
|
|
16568
|
+
function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal") {
|
|
16501
16569
|
switch (eventType) {
|
|
16502
16570
|
case "backend_exception":
|
|
16503
16571
|
case "frontend_exception":
|
|
@@ -16509,10 +16577,7 @@ function classifyEvent(eventType, logLevel, probeActivationId, payload) {
|
|
|
16509
16577
|
return "context_signal";
|
|
16510
16578
|
case "request_event": {
|
|
16511
16579
|
const responseStatus = getRequestResponseStatus(payload);
|
|
16512
|
-
|
|
16513
|
-
return "incident_signal";
|
|
16514
|
-
}
|
|
16515
|
-
return "context_signal";
|
|
16580
|
+
return classifyRequestStatus({ responseStatus, capturePreset });
|
|
16516
16581
|
}
|
|
16517
16582
|
case "frontend_breadcrumb":
|
|
16518
16583
|
case "deploy_metadata":
|
|
@@ -16551,6 +16616,44 @@ var STORAGE_SCHEMA_MIGRATIONS = [
|
|
|
16551
16616
|
"ALTER TABLE organizations ADD COLUMN IF NOT EXISTS suspended_at timestamptz",
|
|
16552
16617
|
"ALTER TABLE organization_members ADD COLUMN IF NOT EXISTS suspended_at timestamptz"
|
|
16553
16618
|
]
|
|
16619
|
+
}),
|
|
16620
|
+
defineStorageSchemaMigration({
|
|
16621
|
+
id: "202605120001_add_github_device_authorizations",
|
|
16622
|
+
description: "Add persisted GitHub CLI bootstrap state for device-flow login.",
|
|
16623
|
+
statements: [
|
|
16624
|
+
`
|
|
16625
|
+
CREATE TABLE IF NOT EXISTS github_device_authorizations (
|
|
16626
|
+
id uuid PRIMARY KEY,
|
|
16627
|
+
device_code text NOT NULL UNIQUE,
|
|
16628
|
+
user_code text NOT NULL,
|
|
16629
|
+
verification_uri text NOT NULL,
|
|
16630
|
+
interval_seconds integer NOT NULL,
|
|
16631
|
+
expires_at timestamptz NOT NULL,
|
|
16632
|
+
accepted_terms_at timestamptz,
|
|
16633
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
16634
|
+
completed_at timestamptz,
|
|
16635
|
+
claimed_at timestamptz,
|
|
16636
|
+
terminal_error text,
|
|
16637
|
+
user_id uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
16638
|
+
organization_id uuid REFERENCES organizations(id) ON DELETE SET NULL
|
|
16639
|
+
)
|
|
16640
|
+
`,
|
|
16641
|
+
`
|
|
16642
|
+
CREATE INDEX IF NOT EXISTS github_device_authorizations_user_code_idx
|
|
16643
|
+
ON github_device_authorizations (user_code, created_at DESC)
|
|
16644
|
+
`,
|
|
16645
|
+
`
|
|
16646
|
+
CREATE INDEX IF NOT EXISTS github_device_authorizations_expires_at_idx
|
|
16647
|
+
ON github_device_authorizations (expires_at)
|
|
16648
|
+
`
|
|
16649
|
+
]
|
|
16650
|
+
}),
|
|
16651
|
+
defineStorageSchemaMigration({
|
|
16652
|
+
id: "202605130001_allow_synthetic_webhook_test_deliveries_without_incident_fk",
|
|
16653
|
+
description: "Allow webhook test deliveries to persist without requiring a backing incidents row.",
|
|
16654
|
+
statements: [
|
|
16655
|
+
"ALTER TABLE webhook_deliveries ALTER COLUMN incident_id DROP NOT NULL"
|
|
16656
|
+
]
|
|
16554
16657
|
})
|
|
16555
16658
|
];
|
|
16556
16659
|
|
|
@@ -16626,7 +16729,11 @@ function parseLocalIncident(candidate) {
|
|
|
16626
16729
|
}
|
|
16627
16730
|
const serviceRuntime = candidate["service_runtime"];
|
|
16628
16731
|
const serviceFramework = candidate["service_framework"];
|
|
16629
|
-
const incidentReason =
|
|
16732
|
+
const incidentReason = candidate["matched_fields"].includes("request_anomaly") ? deriveIncidentReasonFromSignal({
|
|
16733
|
+
event_type: "request_event",
|
|
16734
|
+
event_class: "incident_signal",
|
|
16735
|
+
request_anomaly: true
|
|
16736
|
+
}) : deriveIncidentReasonFromSourceEventTypes(candidate["source_event_types"]);
|
|
16630
16737
|
if (serviceRuntime !== null && typeof serviceRuntime !== "string") {
|
|
16631
16738
|
throw createReadError(400, "invalid_local_state");
|
|
16632
16739
|
}
|
|
@@ -17289,7 +17396,7 @@ async function countCloudArtifactCache(dependencies) {
|
|
|
17289
17396
|
}
|
|
17290
17397
|
|
|
17291
17398
|
// src/connect-command.ts
|
|
17292
|
-
var
|
|
17399
|
+
var import_promises8 = require("node:fs/promises");
|
|
17293
17400
|
var import_node_path9 = require("node:path");
|
|
17294
17401
|
|
|
17295
17402
|
// ../../packages/project-management-client/src/index.ts
|
|
@@ -17732,7 +17839,8 @@ function createAlertApi(client) {
|
|
|
17732
17839
|
const body = {
|
|
17733
17840
|
project_id: input.projectId,
|
|
17734
17841
|
channel: input.channel,
|
|
17735
|
-
condition_type: input.conditionType
|
|
17842
|
+
condition_type: input.conditionType,
|
|
17843
|
+
config: input.config
|
|
17736
17844
|
};
|
|
17737
17845
|
if (input.serviceId !== void 0) {
|
|
17738
17846
|
body.service_id = input.serviceId;
|
|
@@ -17740,9 +17848,6 @@ function createAlertApi(client) {
|
|
|
17740
17848
|
if (input.severityMin !== void 0) {
|
|
17741
17849
|
body.severity_min = input.severityMin;
|
|
17742
17850
|
}
|
|
17743
|
-
if (input.config !== void 0) {
|
|
17744
|
-
body.config = input.config;
|
|
17745
|
-
}
|
|
17746
17851
|
if (input.isEnabled !== void 0) {
|
|
17747
17852
|
body.is_enabled = input.isEnabled;
|
|
17748
17853
|
}
|
|
@@ -18970,13 +19075,538 @@ async function readConnectionConfig(rootDirectory, readFile) {
|
|
|
18970
19075
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
18971
19076
|
throw new Error(`Missing ${CONNECTION_FILE_PATH}`);
|
|
18972
19077
|
}
|
|
18973
|
-
throw new Error(`Invalid ${CONNECTION_FILE_PATH}`);
|
|
18974
|
-
}
|
|
18975
|
-
const parsed = ConnectionConfigSchema.safeParse(parsedJson);
|
|
18976
|
-
if (!parsed.success) {
|
|
18977
|
-
throw new Error(`Invalid ${CONNECTION_FILE_PATH}`);
|
|
19078
|
+
throw new Error(`Invalid ${CONNECTION_FILE_PATH}`);
|
|
19079
|
+
}
|
|
19080
|
+
const parsed = ConnectionConfigSchema.safeParse(parsedJson);
|
|
19081
|
+
if (!parsed.success) {
|
|
19082
|
+
throw new Error(`Invalid ${CONNECTION_FILE_PATH}`);
|
|
19083
|
+
}
|
|
19084
|
+
return parsed.data;
|
|
19085
|
+
}
|
|
19086
|
+
|
|
19087
|
+
// src/interactive-auth.ts
|
|
19088
|
+
var import_node_process = require("node:process");
|
|
19089
|
+
var import_promises7 = require("node:readline/promises");
|
|
19090
|
+
function isInteractiveTerminal() {
|
|
19091
|
+
return import_node_process.stdin.isTTY === true && import_node_process.stdout.isTTY === true;
|
|
19092
|
+
}
|
|
19093
|
+
async function promptForInteractiveLoginSelection() {
|
|
19094
|
+
const readlineInterface = (0, import_promises7.createInterface)({
|
|
19095
|
+
input: import_node_process.stdin,
|
|
19096
|
+
output: import_node_process.stdout
|
|
19097
|
+
});
|
|
19098
|
+
try {
|
|
19099
|
+
import_node_process.stdout.write(
|
|
19100
|
+
[
|
|
19101
|
+
"Choose an authentication method:",
|
|
19102
|
+
"1. GitHub (auto: use gh if available, otherwise device flow)",
|
|
19103
|
+
"2. GitHub device flow",
|
|
19104
|
+
"3. Existing member token",
|
|
19105
|
+
"4. Cancel"
|
|
19106
|
+
].join("\n") + "\n"
|
|
19107
|
+
);
|
|
19108
|
+
for (; ; ) {
|
|
19109
|
+
const selection = (await readlineInterface.question("Selection [1-4]: ")).trim();
|
|
19110
|
+
if (selection === "1") {
|
|
19111
|
+
return { kind: "github" };
|
|
19112
|
+
}
|
|
19113
|
+
if (selection === "2") {
|
|
19114
|
+
return { kind: "github-device" };
|
|
19115
|
+
}
|
|
19116
|
+
if (selection === "3") {
|
|
19117
|
+
const bearerToken = (await readlineInterface.question("Paste your member token: ")).trim();
|
|
19118
|
+
if (bearerToken.length > 0) {
|
|
19119
|
+
return {
|
|
19120
|
+
kind: "member-token",
|
|
19121
|
+
bearerToken
|
|
19122
|
+
};
|
|
19123
|
+
}
|
|
19124
|
+
import_node_process.stdout.write("Member token cannot be empty.\n");
|
|
19125
|
+
continue;
|
|
19126
|
+
}
|
|
19127
|
+
if (selection === "4") {
|
|
19128
|
+
return { kind: "cancel" };
|
|
19129
|
+
}
|
|
19130
|
+
import_node_process.stdout.write("Enter 1, 2, 3, or 4.\n");
|
|
19131
|
+
}
|
|
19132
|
+
} finally {
|
|
19133
|
+
readlineInterface.close();
|
|
19134
|
+
}
|
|
19135
|
+
}
|
|
19136
|
+
|
|
19137
|
+
// src/login-command.ts
|
|
19138
|
+
var import_node_child_process = require("node:child_process");
|
|
19139
|
+
var import_node_util = require("node:util");
|
|
19140
|
+
var execFile = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
19141
|
+
var DEFAULT_BASE_URL = "https://api.debugbundle.com";
|
|
19142
|
+
var DEFAULT_GITHUB_BOOTSTRAP_LABEL = "GitHub bootstrap";
|
|
19143
|
+
var MIN_DEVICE_POLL_INTERVAL_SECONDS = 7;
|
|
19144
|
+
var LoginCommandInputSchema = external_exports.object({
|
|
19145
|
+
bearerToken: external_exports.string().trim().min(1).optional(),
|
|
19146
|
+
baseUrl: external_exports.string().url().default(DEFAULT_BASE_URL),
|
|
19147
|
+
github: external_exports.boolean().optional(),
|
|
19148
|
+
githubCli: external_exports.boolean().optional(),
|
|
19149
|
+
githubDevice: external_exports.boolean().optional(),
|
|
19150
|
+
label: external_exports.string().trim().min(1).max(120).default(DEFAULT_GITHUB_BOOTSTRAP_LABEL)
|
|
19151
|
+
}).superRefine((value, context) => {
|
|
19152
|
+
const githubModeCount = [value.github, value.githubCli, value.githubDevice].filter((flag) => flag === true).length;
|
|
19153
|
+
if (githubModeCount > 1) {
|
|
19154
|
+
context.addIssue({
|
|
19155
|
+
code: external_exports.ZodIssueCode.custom,
|
|
19156
|
+
message: "github_mode_conflict"
|
|
19157
|
+
});
|
|
19158
|
+
}
|
|
19159
|
+
if (value.bearerToken !== void 0 && githubModeCount > 0) {
|
|
19160
|
+
context.addIssue({
|
|
19161
|
+
code: external_exports.ZodIssueCode.custom,
|
|
19162
|
+
message: "login_mode_conflict"
|
|
19163
|
+
});
|
|
19164
|
+
}
|
|
19165
|
+
if (value.bearerToken === void 0 && githubModeCount === 0) {
|
|
19166
|
+
context.addIssue({
|
|
19167
|
+
code: external_exports.ZodIssueCode.custom,
|
|
19168
|
+
message: "login_mode_missing"
|
|
19169
|
+
});
|
|
19170
|
+
}
|
|
19171
|
+
});
|
|
19172
|
+
var ApiErrorResponseSchema9 = external_exports.object({
|
|
19173
|
+
error: external_exports.string()
|
|
19174
|
+
}).strict();
|
|
19175
|
+
var DeviceStartResponseSchema = external_exports.object({
|
|
19176
|
+
request_id: external_exports.string().uuid(),
|
|
19177
|
+
user_code: external_exports.string().min(1),
|
|
19178
|
+
verification_uri: external_exports.string().url(),
|
|
19179
|
+
interval_seconds: external_exports.number().int().positive(),
|
|
19180
|
+
expires_at: external_exports.string().datetime()
|
|
19181
|
+
}).strict();
|
|
19182
|
+
var DevicePollResponseSchema = external_exports.discriminatedUnion("status", [
|
|
19183
|
+
external_exports.object({
|
|
19184
|
+
status: external_exports.literal("pending"),
|
|
19185
|
+
interval_seconds: external_exports.number().int().positive(),
|
|
19186
|
+
expires_at: external_exports.string().datetime()
|
|
19187
|
+
}).strict(),
|
|
19188
|
+
external_exports.object({
|
|
19189
|
+
status: external_exports.enum(["approved", "claimed"]),
|
|
19190
|
+
expires_at: external_exports.string().datetime()
|
|
19191
|
+
}).strict(),
|
|
19192
|
+
external_exports.object({
|
|
19193
|
+
status: external_exports.enum(["denied", "expired", "rejected"]),
|
|
19194
|
+
reason: external_exports.string(),
|
|
19195
|
+
expires_at: external_exports.string().datetime()
|
|
19196
|
+
}).strict()
|
|
19197
|
+
]);
|
|
19198
|
+
var MemberTokenResponseSchema = external_exports.object({
|
|
19199
|
+
token: external_exports.object({
|
|
19200
|
+
token_id: external_exports.string(),
|
|
19201
|
+
user_id: external_exports.string(),
|
|
19202
|
+
organization_id: external_exports.string(),
|
|
19203
|
+
label: external_exports.string(),
|
|
19204
|
+
created_at: external_exports.string(),
|
|
19205
|
+
last_used_at: external_exports.string().nullable(),
|
|
19206
|
+
revoked_at: external_exports.string().nullable(),
|
|
19207
|
+
expires_at: external_exports.string().nullable(),
|
|
19208
|
+
plaintext: external_exports.string()
|
|
19209
|
+
}).strict()
|
|
19210
|
+
}).strict();
|
|
19211
|
+
var LoginApiError = class extends Error {
|
|
19212
|
+
status;
|
|
19213
|
+
code;
|
|
19214
|
+
constructor(status, code) {
|
|
19215
|
+
super(`login_api_error: ${status}:${code}`);
|
|
19216
|
+
this.status = status;
|
|
19217
|
+
this.code = code;
|
|
19218
|
+
}
|
|
19219
|
+
};
|
|
19220
|
+
function normalizeBaseUrl2(baseUrl) {
|
|
19221
|
+
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
19222
|
+
}
|
|
19223
|
+
function formatLoginOutput(authState, authFilePath) {
|
|
19224
|
+
return [
|
|
19225
|
+
"Authenticated: yes",
|
|
19226
|
+
`Base URL: ${authState.base_url}`,
|
|
19227
|
+
`Auth File: ${authFilePath}`,
|
|
19228
|
+
`Token: ${buildTokenPreview(authState.bearer_token)}`
|
|
19229
|
+
].join("\n");
|
|
19230
|
+
}
|
|
19231
|
+
function validateMemberToken(token) {
|
|
19232
|
+
return token.startsWith("dbundle_mem_");
|
|
19233
|
+
}
|
|
19234
|
+
function resolveLoginMode(input) {
|
|
19235
|
+
if (input.bearerToken !== void 0) {
|
|
19236
|
+
return "token";
|
|
19237
|
+
}
|
|
19238
|
+
if (input.githubCli === true) {
|
|
19239
|
+
return "github-cli";
|
|
19240
|
+
}
|
|
19241
|
+
if (input.githubDevice === true) {
|
|
19242
|
+
return "github-device";
|
|
19243
|
+
}
|
|
19244
|
+
return "github";
|
|
19245
|
+
}
|
|
19246
|
+
function hasExplicitLoginMode(input) {
|
|
19247
|
+
return input.bearerToken !== void 0 || input.github === true || input.githubCli === true || input.githubDevice === true;
|
|
19248
|
+
}
|
|
19249
|
+
async function maybePromptForLoginInput(input, dependencies) {
|
|
19250
|
+
if (hasExplicitLoginMode(input) || input.json === true) {
|
|
19251
|
+
return input;
|
|
19252
|
+
}
|
|
19253
|
+
const interactive = (dependencies?.isInteractiveTerminal ?? isInteractiveTerminal)();
|
|
19254
|
+
if (!interactive) {
|
|
19255
|
+
return input;
|
|
19256
|
+
}
|
|
19257
|
+
const selection = await (dependencies?.promptForInteractiveLogin ?? promptForInteractiveLoginSelection)();
|
|
19258
|
+
if (selection.kind === "cancel") {
|
|
19259
|
+
return {
|
|
19260
|
+
exitCode: 4,
|
|
19261
|
+
output: "Authentication cancelled."
|
|
19262
|
+
};
|
|
19263
|
+
}
|
|
19264
|
+
if (selection.kind === "member-token") {
|
|
19265
|
+
return {
|
|
19266
|
+
...input,
|
|
19267
|
+
bearerToken: selection.bearerToken
|
|
19268
|
+
};
|
|
19269
|
+
}
|
|
19270
|
+
if (selection.kind === "github-device") {
|
|
19271
|
+
return {
|
|
19272
|
+
...input,
|
|
19273
|
+
githubDevice: true
|
|
19274
|
+
};
|
|
19275
|
+
}
|
|
19276
|
+
return {
|
|
19277
|
+
...input,
|
|
19278
|
+
github: true
|
|
19279
|
+
};
|
|
19280
|
+
}
|
|
19281
|
+
function mapInputValidationError(parsedInput) {
|
|
19282
|
+
if (parsedInput.error.issues.some((issue) => issue.message === "github_mode_conflict")) {
|
|
19283
|
+
return "Choose only one of --github, --github-cli, or --github-device.";
|
|
19284
|
+
}
|
|
19285
|
+
if (parsedInput.error.issues.some((issue) => issue.message === "login_mode_conflict")) {
|
|
19286
|
+
return "Use either a member token or a GitHub login mode, not both.";
|
|
19287
|
+
}
|
|
19288
|
+
if (parsedInput.error.issues.some((issue) => issue.message === "login_mode_missing")) {
|
|
19289
|
+
return "Provide either a member token or one of --github, --github-cli, or --github-device.";
|
|
19290
|
+
}
|
|
19291
|
+
return "Invalid login options.";
|
|
19292
|
+
}
|
|
19293
|
+
function mapApiErrorToMessage(error) {
|
|
19294
|
+
switch (error.code) {
|
|
19295
|
+
case "auth_not_configured":
|
|
19296
|
+
return "GitHub login is not configured on this DebugBundle API.";
|
|
19297
|
+
case "github_device_flow_disabled":
|
|
19298
|
+
return "GitHub device flow is not enabled for this DebugBundle API.";
|
|
19299
|
+
case "github_oauth_unavailable":
|
|
19300
|
+
return "GitHub OAuth is temporarily unavailable.";
|
|
19301
|
+
case "invalid_github_token":
|
|
19302
|
+
return "The local GitHub CLI token was rejected by DebugBundle.";
|
|
19303
|
+
case "github_device_request_not_found":
|
|
19304
|
+
return "GitHub device authorization request was not found.";
|
|
19305
|
+
case "github_device_auth_pending":
|
|
19306
|
+
return "GitHub device authorization has not completed yet.";
|
|
19307
|
+
case "github_device_auth_expired":
|
|
19308
|
+
return "GitHub device authorization expired before it could be claimed.";
|
|
19309
|
+
case "github_device_auth_claimed":
|
|
19310
|
+
return "GitHub device authorization was already claimed.";
|
|
19311
|
+
case "github_device_auth_rejected":
|
|
19312
|
+
return "GitHub device authorization was rejected.";
|
|
19313
|
+
case "github_email_unavailable":
|
|
19314
|
+
return "GitHub did not provide a verified primary email address.";
|
|
19315
|
+
case "account_signup_disabled":
|
|
19316
|
+
return "This DebugBundle workspace does not allow new account signups for this GitHub identity.";
|
|
19317
|
+
case "account_suspended":
|
|
19318
|
+
return "This DebugBundle account is suspended.";
|
|
19319
|
+
case "rate_limited":
|
|
19320
|
+
return "GitHub login was rate limited. Please wait and try again.";
|
|
19321
|
+
default:
|
|
19322
|
+
return `GitHub login failed: ${error.code}`;
|
|
19323
|
+
}
|
|
19324
|
+
}
|
|
19325
|
+
async function parseResponseBody2(response) {
|
|
19326
|
+
const rawBody = await response.text();
|
|
19327
|
+
if (rawBody.length === 0) {
|
|
19328
|
+
return null;
|
|
19329
|
+
}
|
|
19330
|
+
try {
|
|
19331
|
+
return JSON.parse(rawBody);
|
|
19332
|
+
} catch {
|
|
19333
|
+
return rawBody;
|
|
19334
|
+
}
|
|
19335
|
+
}
|
|
19336
|
+
async function requestJson(input, dependencies) {
|
|
19337
|
+
const fetchImpl = dependencies?.fetchImpl ?? fetch;
|
|
19338
|
+
const response = await fetchImpl(`${normalizeBaseUrl2(input.baseUrl)}${input.path}`, {
|
|
19339
|
+
method: input.method,
|
|
19340
|
+
headers: {
|
|
19341
|
+
accept: "application/json",
|
|
19342
|
+
"content-type": "application/json"
|
|
19343
|
+
},
|
|
19344
|
+
body: JSON.stringify(input.body)
|
|
19345
|
+
});
|
|
19346
|
+
const body = await parseResponseBody2(response);
|
|
19347
|
+
if (response.status < 200 || response.status >= 300) {
|
|
19348
|
+
const parsedError = ApiErrorResponseSchema9.safeParse(body);
|
|
19349
|
+
throw new LoginApiError(response.status, parsedError.success ? parsedError.data.error : "unknown_error");
|
|
19350
|
+
}
|
|
19351
|
+
return body;
|
|
19352
|
+
}
|
|
19353
|
+
async function readGitHubAccessTokenFromGh() {
|
|
19354
|
+
try {
|
|
19355
|
+
const result = await execFile("gh", ["auth", "token"]);
|
|
19356
|
+
const token = result.stdout.trim();
|
|
19357
|
+
return token.length > 0 ? token : null;
|
|
19358
|
+
} catch {
|
|
19359
|
+
return null;
|
|
19360
|
+
}
|
|
19361
|
+
}
|
|
19362
|
+
async function exchangeGitHubToken(input, dependencies) {
|
|
19363
|
+
const body = await requestJson(
|
|
19364
|
+
{
|
|
19365
|
+
baseUrl: input.baseUrl,
|
|
19366
|
+
method: "POST",
|
|
19367
|
+
path: "/v1/auth/github/token/exchange",
|
|
19368
|
+
body: {
|
|
19369
|
+
github_access_token: input.githubAccessToken,
|
|
19370
|
+
label: input.label,
|
|
19371
|
+
accepted_terms: true
|
|
19372
|
+
}
|
|
19373
|
+
},
|
|
19374
|
+
dependencies
|
|
19375
|
+
);
|
|
19376
|
+
return MemberTokenResponseSchema.parse(body);
|
|
19377
|
+
}
|
|
19378
|
+
async function startGitHubDeviceFlow(input, dependencies) {
|
|
19379
|
+
const body = await requestJson(
|
|
19380
|
+
{
|
|
19381
|
+
baseUrl: input.baseUrl,
|
|
19382
|
+
method: "POST",
|
|
19383
|
+
path: "/v1/auth/github/device/start",
|
|
19384
|
+
body: {
|
|
19385
|
+
accepted_terms: true
|
|
19386
|
+
}
|
|
19387
|
+
},
|
|
19388
|
+
dependencies
|
|
19389
|
+
);
|
|
19390
|
+
return DeviceStartResponseSchema.parse(body);
|
|
19391
|
+
}
|
|
19392
|
+
async function pollGitHubDeviceFlow(input, dependencies) {
|
|
19393
|
+
const body = await requestJson(
|
|
19394
|
+
{
|
|
19395
|
+
baseUrl: input.baseUrl,
|
|
19396
|
+
method: "POST",
|
|
19397
|
+
path: "/v1/auth/github/device/poll",
|
|
19398
|
+
body: {
|
|
19399
|
+
request_id: input.requestId
|
|
19400
|
+
}
|
|
19401
|
+
},
|
|
19402
|
+
dependencies
|
|
19403
|
+
);
|
|
19404
|
+
return DevicePollResponseSchema.parse(body);
|
|
19405
|
+
}
|
|
19406
|
+
async function claimGitHubDeviceFlow(input, dependencies) {
|
|
19407
|
+
const body = await requestJson(
|
|
19408
|
+
{
|
|
19409
|
+
baseUrl: input.baseUrl,
|
|
19410
|
+
method: "POST",
|
|
19411
|
+
path: "/v1/auth/github/device/claim",
|
|
19412
|
+
body: {
|
|
19413
|
+
request_id: input.requestId,
|
|
19414
|
+
label: input.label
|
|
19415
|
+
}
|
|
19416
|
+
},
|
|
19417
|
+
dependencies
|
|
19418
|
+
);
|
|
19419
|
+
return MemberTokenResponseSchema.parse(body);
|
|
19420
|
+
}
|
|
19421
|
+
function formatPersistedSuccess(input, dependencies) {
|
|
19422
|
+
const authState = {
|
|
19423
|
+
bearer_token: input.bearerToken,
|
|
19424
|
+
base_url: input.baseUrl
|
|
19425
|
+
};
|
|
19426
|
+
return dependencies.writeAuthState({
|
|
19427
|
+
...input.authFilePath === void 0 ? {} : { authFilePath: input.authFilePath },
|
|
19428
|
+
authState
|
|
19429
|
+
}).then((persistedPath) => {
|
|
19430
|
+
const authFilePath = persistedPath ?? input.authFilePath ?? "";
|
|
19431
|
+
const payload = {
|
|
19432
|
+
authenticated: true,
|
|
19433
|
+
auth: {
|
|
19434
|
+
base_url: authState.base_url,
|
|
19435
|
+
token_preview: buildTokenPreview(authState.bearer_token)
|
|
19436
|
+
},
|
|
19437
|
+
auth_file_path: authFilePath
|
|
19438
|
+
};
|
|
19439
|
+
return {
|
|
19440
|
+
exitCode: 0,
|
|
19441
|
+
output: input.json ? JSON.stringify(payload) : formatLoginOutput(authState, authFilePath)
|
|
19442
|
+
};
|
|
19443
|
+
});
|
|
19444
|
+
}
|
|
19445
|
+
function buildProgressReporter(dependency) {
|
|
19446
|
+
if (dependency !== void 0) {
|
|
19447
|
+
return dependency;
|
|
19448
|
+
}
|
|
19449
|
+
return (text) => {
|
|
19450
|
+
process.stderr.write(`${text}
|
|
19451
|
+
`);
|
|
19452
|
+
};
|
|
19453
|
+
}
|
|
19454
|
+
async function loginCommand(input, dependencies) {
|
|
19455
|
+
const promptedInput = await maybePromptForLoginInput(input, dependencies);
|
|
19456
|
+
if ("exitCode" in promptedInput) {
|
|
19457
|
+
return promptedInput;
|
|
19458
|
+
}
|
|
19459
|
+
const parsedInput = LoginCommandInputSchema.safeParse({
|
|
19460
|
+
bearerToken: promptedInput.bearerToken,
|
|
19461
|
+
baseUrl: promptedInput.baseUrl ?? DEFAULT_BASE_URL,
|
|
19462
|
+
github: promptedInput.github,
|
|
19463
|
+
githubCli: promptedInput.githubCli,
|
|
19464
|
+
githubDevice: promptedInput.githubDevice,
|
|
19465
|
+
label: promptedInput.label ?? DEFAULT_GITHUB_BOOTSTRAP_LABEL
|
|
19466
|
+
});
|
|
19467
|
+
if (!parsedInput.success) {
|
|
19468
|
+
return {
|
|
19469
|
+
exitCode: 4,
|
|
19470
|
+
output: mapInputValidationError(parsedInput)
|
|
19471
|
+
};
|
|
19472
|
+
}
|
|
19473
|
+
const normalizedInput = parsedInput.data;
|
|
19474
|
+
const writeAuthState = dependencies?.writeAuthState ?? persistCliAuthState;
|
|
19475
|
+
try {
|
|
19476
|
+
if (resolveLoginMode(normalizedInput) === "token") {
|
|
19477
|
+
if (!validateMemberToken(normalizedInput.bearerToken)) {
|
|
19478
|
+
return {
|
|
19479
|
+
exitCode: 4,
|
|
19480
|
+
output: "Invalid member token."
|
|
19481
|
+
};
|
|
19482
|
+
}
|
|
19483
|
+
return await formatPersistedSuccess(
|
|
19484
|
+
{
|
|
19485
|
+
baseUrl: normalizedInput.baseUrl,
|
|
19486
|
+
bearerToken: normalizedInput.bearerToken,
|
|
19487
|
+
...promptedInput.json === void 0 ? {} : { json: promptedInput.json },
|
|
19488
|
+
...promptedInput.authFilePath === void 0 ? {} : { authFilePath: promptedInput.authFilePath }
|
|
19489
|
+
},
|
|
19490
|
+
{ writeAuthState }
|
|
19491
|
+
);
|
|
19492
|
+
}
|
|
19493
|
+
const readGitHubAccessToken = dependencies?.readGitHubAccessToken ?? readGitHubAccessTokenFromGh;
|
|
19494
|
+
const sleep = dependencies?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
19495
|
+
const reportProgress = buildProgressReporter(dependencies?.reportProgress);
|
|
19496
|
+
const loginMode = resolveLoginMode(normalizedInput);
|
|
19497
|
+
if (loginMode !== "github-device") {
|
|
19498
|
+
const githubAccessToken = await readGitHubAccessToken();
|
|
19499
|
+
if (githubAccessToken !== null) {
|
|
19500
|
+
reportProgress("Using existing GitHub CLI authentication.");
|
|
19501
|
+
try {
|
|
19502
|
+
const exchanged = await exchangeGitHubToken(
|
|
19503
|
+
{
|
|
19504
|
+
baseUrl: normalizedInput.baseUrl,
|
|
19505
|
+
githubAccessToken,
|
|
19506
|
+
label: normalizedInput.label
|
|
19507
|
+
},
|
|
19508
|
+
dependencies
|
|
19509
|
+
);
|
|
19510
|
+
return await formatPersistedSuccess(
|
|
19511
|
+
{
|
|
19512
|
+
baseUrl: normalizedInput.baseUrl,
|
|
19513
|
+
bearerToken: exchanged.token.plaintext,
|
|
19514
|
+
...promptedInput.json === void 0 ? {} : { json: promptedInput.json },
|
|
19515
|
+
...promptedInput.authFilePath === void 0 ? {} : { authFilePath: promptedInput.authFilePath }
|
|
19516
|
+
},
|
|
19517
|
+
{ writeAuthState }
|
|
19518
|
+
);
|
|
19519
|
+
} catch (error) {
|
|
19520
|
+
if (!(error instanceof LoginApiError)) {
|
|
19521
|
+
throw error;
|
|
19522
|
+
}
|
|
19523
|
+
if (loginMode === "github-cli" || error.code !== "invalid_github_token") {
|
|
19524
|
+
return {
|
|
19525
|
+
exitCode: error.status === 401 || error.status === 403 ? 4 : 1,
|
|
19526
|
+
output: mapApiErrorToMessage(error)
|
|
19527
|
+
};
|
|
19528
|
+
}
|
|
19529
|
+
reportProgress("The local GitHub CLI token was not accepted. Falling back to device flow.");
|
|
19530
|
+
}
|
|
19531
|
+
} else if (loginMode === "github-cli") {
|
|
19532
|
+
return {
|
|
19533
|
+
exitCode: 4,
|
|
19534
|
+
output: "GitHub CLI is not authenticated on this machine."
|
|
19535
|
+
};
|
|
19536
|
+
} else {
|
|
19537
|
+
reportProgress("GitHub CLI is not authenticated. Falling back to device flow.");
|
|
19538
|
+
}
|
|
19539
|
+
}
|
|
19540
|
+
const started = await startGitHubDeviceFlow(
|
|
19541
|
+
{
|
|
19542
|
+
baseUrl: normalizedInput.baseUrl
|
|
19543
|
+
},
|
|
19544
|
+
dependencies
|
|
19545
|
+
);
|
|
19546
|
+
reportProgress(`Open ${started.verification_uri} and enter code ${started.user_code}.`);
|
|
19547
|
+
reportProgress("Waiting for GitHub approval...");
|
|
19548
|
+
let intervalSeconds = Math.max(started.interval_seconds, MIN_DEVICE_POLL_INTERVAL_SECONDS);
|
|
19549
|
+
for (; ; ) {
|
|
19550
|
+
await sleep(intervalSeconds * 1e3);
|
|
19551
|
+
const polled = await pollGitHubDeviceFlow(
|
|
19552
|
+
{
|
|
19553
|
+
baseUrl: normalizedInput.baseUrl,
|
|
19554
|
+
requestId: started.request_id
|
|
19555
|
+
},
|
|
19556
|
+
dependencies
|
|
19557
|
+
);
|
|
19558
|
+
if (polled.status === "pending") {
|
|
19559
|
+
intervalSeconds = Math.max(polled.interval_seconds, MIN_DEVICE_POLL_INTERVAL_SECONDS);
|
|
19560
|
+
continue;
|
|
19561
|
+
}
|
|
19562
|
+
if (polled.status === "approved") {
|
|
19563
|
+
const claimed = await claimGitHubDeviceFlow(
|
|
19564
|
+
{
|
|
19565
|
+
baseUrl: normalizedInput.baseUrl,
|
|
19566
|
+
requestId: started.request_id,
|
|
19567
|
+
label: normalizedInput.label
|
|
19568
|
+
},
|
|
19569
|
+
dependencies
|
|
19570
|
+
);
|
|
19571
|
+
return await formatPersistedSuccess(
|
|
19572
|
+
{
|
|
19573
|
+
baseUrl: normalizedInput.baseUrl,
|
|
19574
|
+
bearerToken: claimed.token.plaintext,
|
|
19575
|
+
...promptedInput.json === void 0 ? {} : { json: promptedInput.json },
|
|
19576
|
+
...promptedInput.authFilePath === void 0 ? {} : { authFilePath: promptedInput.authFilePath }
|
|
19577
|
+
},
|
|
19578
|
+
{ writeAuthState }
|
|
19579
|
+
);
|
|
19580
|
+
}
|
|
19581
|
+
if (polled.status === "claimed") {
|
|
19582
|
+
return {
|
|
19583
|
+
exitCode: 4,
|
|
19584
|
+
output: "GitHub device authorization was already claimed."
|
|
19585
|
+
};
|
|
19586
|
+
}
|
|
19587
|
+
if (polled.status === "denied" || polled.status === "expired" || polled.status === "rejected") {
|
|
19588
|
+
return {
|
|
19589
|
+
exitCode: 4,
|
|
19590
|
+
output: polled.status === "denied" ? "GitHub device authorization was denied." : polled.status === "expired" ? "GitHub device authorization expired." : `GitHub device authorization was rejected: ${polled.reason}`
|
|
19591
|
+
};
|
|
19592
|
+
}
|
|
19593
|
+
return {
|
|
19594
|
+
exitCode: 1,
|
|
19595
|
+
output: "GitHub device authorization returned an unexpected status."
|
|
19596
|
+
};
|
|
19597
|
+
}
|
|
19598
|
+
} catch (error) {
|
|
19599
|
+
if (error instanceof LoginApiError) {
|
|
19600
|
+
return {
|
|
19601
|
+
exitCode: error.status === 400 || error.status === 401 || error.status === 403 || error.status === 409 ? 4 : 1,
|
|
19602
|
+
output: mapApiErrorToMessage(error)
|
|
19603
|
+
};
|
|
19604
|
+
}
|
|
19605
|
+
return {
|
|
19606
|
+
exitCode: 1,
|
|
19607
|
+
output: error instanceof Error ? error.message : String(error)
|
|
19608
|
+
};
|
|
18978
19609
|
}
|
|
18979
|
-
return parsed.data;
|
|
18980
19610
|
}
|
|
18981
19611
|
|
|
18982
19612
|
// src/connect-command.ts
|
|
@@ -19068,9 +19698,9 @@ function buildValidationFailureResult(input) {
|
|
|
19068
19698
|
}
|
|
19069
19699
|
async function connectCommand(input, dependencies = {}) {
|
|
19070
19700
|
const cwd = dependencies.cwd ?? (() => process.cwd());
|
|
19071
|
-
const readFile = dependencies.readFile ?? ((path) => (0,
|
|
19072
|
-
const stat = dependencies.stat ??
|
|
19073
|
-
const writeFile = dependencies.writeFile ??
|
|
19701
|
+
const readFile = dependencies.readFile ?? ((path) => (0, import_promises8.readFile)(path, "utf8"));
|
|
19702
|
+
const stat = dependencies.stat ?? import_promises8.stat;
|
|
19703
|
+
const writeFile = dependencies.writeFile ?? import_promises8.writeFile;
|
|
19074
19704
|
const readAuthState = dependencies.readAuthState ?? ((authInput) => readCliAuthState(authInput));
|
|
19075
19705
|
const createHttpClient = dependencies.createHttpClient ?? ((clientInput) => {
|
|
19076
19706
|
const httpClientDependencies = {};
|
|
@@ -19081,6 +19711,7 @@ async function connectCommand(input, dependencies = {}) {
|
|
|
19081
19711
|
});
|
|
19082
19712
|
const createProjectApi = dependencies.createProjectManagementApi ?? createProjectManagementApi;
|
|
19083
19713
|
const createTokenApi = dependencies.createTokenManagementApi ?? createTokenManagementApi;
|
|
19714
|
+
const loginCommand2 = dependencies.loginCommand ?? loginCommand;
|
|
19084
19715
|
const rootDirectory = cwd();
|
|
19085
19716
|
const initialChecks = [];
|
|
19086
19717
|
const profileValidation = await readValidatedProfileName(rootDirectory, { readFile, stat });
|
|
@@ -19137,7 +19768,22 @@ async function connectCommand(input, dependencies = {}) {
|
|
|
19137
19768
|
message: `Found ${CONNECTION_FILE_PATH}`
|
|
19138
19769
|
});
|
|
19139
19770
|
try {
|
|
19140
|
-
|
|
19771
|
+
let authState;
|
|
19772
|
+
try {
|
|
19773
|
+
authState = await readAuthState({ ...input.authFilePath === void 0 ? {} : { authFilePath: input.authFilePath } });
|
|
19774
|
+
} catch (error) {
|
|
19775
|
+
const shouldAttemptInteractiveLogin = error instanceof CliAuthStateError && error.code === "auth_state_missing" && input.json !== true && (dependencies.isInteractiveTerminal ?? isInteractiveTerminal)();
|
|
19776
|
+
if (!shouldAttemptInteractiveLogin) {
|
|
19777
|
+
throw error;
|
|
19778
|
+
}
|
|
19779
|
+
const loginResult = await loginCommand2({
|
|
19780
|
+
...input.authFilePath === void 0 ? {} : { authFilePath: input.authFilePath }
|
|
19781
|
+
});
|
|
19782
|
+
if (loginResult.exitCode !== 0) {
|
|
19783
|
+
return loginResult;
|
|
19784
|
+
}
|
|
19785
|
+
authState = await readAuthState({ ...input.authFilePath === void 0 ? {} : { authFilePath: input.authFilePath } });
|
|
19786
|
+
}
|
|
19141
19787
|
const httpClient = createHttpClient({ baseUrl: authState.base_url });
|
|
19142
19788
|
const projectApi = createProjectApi(httpClient);
|
|
19143
19789
|
const tokenApi = createTokenApi(httpClient);
|
|
@@ -19241,7 +19887,7 @@ async function connectCommand(input, dependencies = {}) {
|
|
|
19241
19887
|
message
|
|
19242
19888
|
}
|
|
19243
19889
|
], [
|
|
19244
|
-
exitCode === 2 ? "Run debugbundle login before connecting the project to cloud." : "Resolve the cloud API error and retry debugbundle connect."
|
|
19890
|
+
exitCode === 2 ? "Run debugbundle login to choose an auth flow, or use debugbundle login --github, debugbundle login --github-device, or debugbundle login <dbundle_mem_...> before connecting the project to cloud." : "Resolve the cloud API error and retry debugbundle connect."
|
|
19245
19891
|
]) : `DebugBundle connect failed.
|
|
19246
19892
|
- ${message}`
|
|
19247
19893
|
};
|
|
@@ -19249,7 +19895,7 @@ async function connectCommand(input, dependencies = {}) {
|
|
|
19249
19895
|
}
|
|
19250
19896
|
|
|
19251
19897
|
// src/doctor-command.ts
|
|
19252
|
-
var
|
|
19898
|
+
var import_promises9 = require("node:fs/promises");
|
|
19253
19899
|
var import_node_path10 = require("node:path");
|
|
19254
19900
|
var ProfileSchema2 = external_exports.object({
|
|
19255
19901
|
debugbundle: external_exports.object({
|
|
@@ -19269,7 +19915,7 @@ var RELAY_SPOOL_DELIVERED_MARKER_SUFFIX2 = ".delivered";
|
|
|
19269
19915
|
var RELAY_SPOOL_EVENT_SUFFIX = ".events.json";
|
|
19270
19916
|
var SUGGESTED_ACTIONS = [
|
|
19271
19917
|
"Run debugbundle setup if local scaffold files are missing.",
|
|
19272
|
-
"Run debugbundle login to create ~/.debugbundle/auth.json.",
|
|
19918
|
+
"Run debugbundle login to choose an auth flow, or use debugbundle login --github, debugbundle login --github-device, or debugbundle login <dbundle_mem_...> to create ~/.debugbundle/auth.json.",
|
|
19273
19919
|
"Review .debugbundle/profile.json when architecture changes or the profile becomes stale."
|
|
19274
19920
|
];
|
|
19275
19921
|
async function pathExists3(path, stat) {
|
|
@@ -19382,7 +20028,7 @@ function buildPrivacyPreview() {
|
|
|
19382
20028
|
sample_event_type: sampleEvent.event_type,
|
|
19383
20029
|
sample_event_class: sampleEventClass,
|
|
19384
20030
|
sample_can_create_incident: sampleEventClass === "incident_signal",
|
|
19385
|
-
incident_rule: "request_event
|
|
20031
|
+
incident_rule: "request_event incident classification follows the resolved capture preset: 5xx always create incidents, balanced also promotes 408/423/424/425/429, and investigative also promotes 409.",
|
|
19386
20032
|
redacted_fields,
|
|
19387
20033
|
omitted_fields: [],
|
|
19388
20034
|
retained_metadata: {
|
|
@@ -19471,7 +20117,7 @@ async function loadConnection(rootDirectory, dependencies) {
|
|
|
19471
20117
|
};
|
|
19472
20118
|
}
|
|
19473
20119
|
}
|
|
19474
|
-
function
|
|
20120
|
+
function normalizeBaseUrl3(baseUrl) {
|
|
19475
20121
|
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
19476
20122
|
}
|
|
19477
20123
|
function buildProjectModeCheck(connection) {
|
|
@@ -19571,7 +20217,7 @@ async function buildAuthCheck(input, readAuthStateImpl) {
|
|
|
19571
20217
|
};
|
|
19572
20218
|
}
|
|
19573
20219
|
}
|
|
19574
|
-
async function
|
|
20220
|
+
async function parseResponseBody3(response) {
|
|
19575
20221
|
const rawBody = await response.text();
|
|
19576
20222
|
if (rawBody.length === 0) {
|
|
19577
20223
|
return null;
|
|
@@ -19586,8 +20232,8 @@ async function buildConnectedApiCheck(input) {
|
|
|
19586
20232
|
if (input.connection === null || input.connection.mode !== "connected") {
|
|
19587
20233
|
return null;
|
|
19588
20234
|
}
|
|
19589
|
-
const connectionBaseUrl = input.connection.cloud_base_url === null ? null :
|
|
19590
|
-
const authBaseUrl = input.authState === null ? null :
|
|
20235
|
+
const connectionBaseUrl = input.connection.cloud_base_url === null ? null : normalizeBaseUrl3(input.connection.cloud_base_url);
|
|
20236
|
+
const authBaseUrl = input.authState === null ? null : normalizeBaseUrl3(input.authState.base_url);
|
|
19591
20237
|
const baseUrl = authBaseUrl ?? connectionBaseUrl;
|
|
19592
20238
|
if (baseUrl === null) {
|
|
19593
20239
|
return {
|
|
@@ -19611,7 +20257,7 @@ async function buildConnectedApiCheck(input) {
|
|
|
19611
20257
|
message: `Connected API ${baseUrl} could not be reached: ${error instanceof Error ? error.message : String(error)}`
|
|
19612
20258
|
};
|
|
19613
20259
|
}
|
|
19614
|
-
const parsedHealthBody = await
|
|
20260
|
+
const parsedHealthBody = await parseResponseBody3(healthResponse);
|
|
19615
20261
|
if (healthResponse.status !== 200) {
|
|
19616
20262
|
return {
|
|
19617
20263
|
name: "connected-api",
|
|
@@ -19649,7 +20295,7 @@ async function buildConnectedApiCheck(input) {
|
|
|
19649
20295
|
message: `Connected API ${baseUrl} could not verify member-token auth: ${error instanceof Error ? error.message : String(error)}`
|
|
19650
20296
|
};
|
|
19651
20297
|
}
|
|
19652
|
-
const parsedIncidentsBody = await
|
|
20298
|
+
const parsedIncidentsBody = await parseResponseBody3(incidentsResponse);
|
|
19653
20299
|
if (incidentsResponse.status !== 200) {
|
|
19654
20300
|
return {
|
|
19655
20301
|
name: "connected-api",
|
|
@@ -19732,9 +20378,9 @@ async function doctorCommand(input, dependencies = {}) {
|
|
|
19732
20378
|
const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
19733
20379
|
const fetchImpl = dependencies.fetchImpl ?? fetch;
|
|
19734
20380
|
const readAuthStateImpl = dependencies.readAuthState ?? readCliAuthState;
|
|
19735
|
-
const readdir = dependencies.readdir ??
|
|
19736
|
-
const readFile = dependencies.readFile ?? ((filePath) => (0,
|
|
19737
|
-
const stat = dependencies.stat ??
|
|
20381
|
+
const readdir = dependencies.readdir ?? import_promises9.readdir;
|
|
20382
|
+
const readFile = dependencies.readFile ?? ((filePath) => (0, import_promises9.readFile)(filePath, "utf8"));
|
|
20383
|
+
const stat = dependencies.stat ?? import_promises9.stat;
|
|
19738
20384
|
const rootDirectory = cwd();
|
|
19739
20385
|
const currentTime = now();
|
|
19740
20386
|
const { check: profileCheck, profile } = await loadProfile(rootDirectory, { readFile, stat });
|
|
@@ -19765,7 +20411,7 @@ async function doctorCommand(input, dependencies = {}) {
|
|
|
19765
20411
|
|
|
19766
20412
|
// src/ingest-command.ts
|
|
19767
20413
|
var import_node_crypto5 = require("node:crypto");
|
|
19768
|
-
var
|
|
20414
|
+
var import_promises11 = require("node:fs/promises");
|
|
19769
20415
|
var import_node_path12 = require("node:path");
|
|
19770
20416
|
|
|
19771
20417
|
// ../../packages/log-parser/src/project-id.ts
|
|
@@ -20085,10 +20731,11 @@ function parseLogFile(content, input) {
|
|
|
20085
20731
|
|
|
20086
20732
|
// src/process-command.ts
|
|
20087
20733
|
var import_node_crypto4 = require("node:crypto");
|
|
20088
|
-
var
|
|
20734
|
+
var import_promises10 = require("node:fs/promises");
|
|
20089
20735
|
var import_node_path11 = require("node:path");
|
|
20090
20736
|
|
|
20091
20737
|
// ../../packages/bundle-engine/src/index.ts
|
|
20738
|
+
var DYNAMIC_SEGMENT_PATTERN2 = /^(?:\d+|[0-9a-f]{8}-[0-9a-f-]{27}|[A-Za-z0-9_-]{24,})$/;
|
|
20092
20739
|
function toIsoTimestamp(value) {
|
|
20093
20740
|
return new Date(value).toISOString();
|
|
20094
20741
|
}
|
|
@@ -20162,6 +20809,41 @@ function inferSignalTypeFromSourceEventTypes(sourceEventTypes) {
|
|
|
20162
20809
|
function extractTopFrames(stack) {
|
|
20163
20810
|
return stack.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("at ")).slice(0, 3);
|
|
20164
20811
|
}
|
|
20812
|
+
function decodeRouteSegment2(segment) {
|
|
20813
|
+
try {
|
|
20814
|
+
return decodeURIComponent(segment);
|
|
20815
|
+
} catch {
|
|
20816
|
+
return segment.replace(
|
|
20817
|
+
/%([0-9A-Fa-f]{2})/g,
|
|
20818
|
+
(_match, hexByte) => String.fromCharCode(parseInt(hexByte, 16))
|
|
20819
|
+
);
|
|
20820
|
+
}
|
|
20821
|
+
}
|
|
20822
|
+
function isDynamicRouteSegment2(segment) {
|
|
20823
|
+
if (DYNAMIC_SEGMENT_PATTERN2.test(segment)) {
|
|
20824
|
+
return true;
|
|
20825
|
+
}
|
|
20826
|
+
const decodedSegment = decodeRouteSegment2(segment);
|
|
20827
|
+
if (decodedSegment.includes("/")) {
|
|
20828
|
+
return true;
|
|
20829
|
+
}
|
|
20830
|
+
if (decodedSegment !== segment && DYNAMIC_SEGMENT_PATTERN2.test(decodedSegment)) {
|
|
20831
|
+
return true;
|
|
20832
|
+
}
|
|
20833
|
+
const strippedMalformedPercent = decodedSegment.replace(/%+/g, "");
|
|
20834
|
+
return strippedMalformedPercent !== decodedSegment && DYNAMIC_SEGMENT_PATTERN2.test(strippedMalformedPercent);
|
|
20835
|
+
}
|
|
20836
|
+
function normalizeRouteTemplate(path) {
|
|
20837
|
+
if (path === null || path.length === 0) {
|
|
20838
|
+
return null;
|
|
20839
|
+
}
|
|
20840
|
+
const pathWithoutQueryOrFragment = path.split(/[?#]/, 1)[0] ?? "";
|
|
20841
|
+
if (pathWithoutQueryOrFragment.length === 0) {
|
|
20842
|
+
return "/";
|
|
20843
|
+
}
|
|
20844
|
+
const normalizedSegments = pathWithoutQueryOrFragment.split("/").filter((segment) => segment.length > 0).map((segment) => isDynamicRouteSegment2(segment) ? "{param}" : segment);
|
|
20845
|
+
return normalizedSegments.length === 0 ? "/" : `/${normalizedSegments.join("/")}`;
|
|
20846
|
+
}
|
|
20165
20847
|
function deriveFirstApplicationFrame(errorContext) {
|
|
20166
20848
|
const firstFrame = errorContext?.top_frames[0];
|
|
20167
20849
|
if (firstFrame === void 0) {
|
|
@@ -20255,13 +20937,135 @@ function buildRequestContext(envelopes) {
|
|
|
20255
20937
|
version: 1,
|
|
20256
20938
|
method: exceptionEvent.payload.request.method,
|
|
20257
20939
|
path: exceptionEvent.payload.request.path,
|
|
20258
|
-
route_template:
|
|
20940
|
+
route_template: normalizeRouteTemplate(exceptionEvent.payload.request.path),
|
|
20259
20941
|
query: exceptionEvent.payload.request.query,
|
|
20260
20942
|
headers: exceptionEvent.payload.request.headers,
|
|
20261
20943
|
body: exceptionEvent.payload.request.body ?? null,
|
|
20262
20944
|
request_id: exceptionEvent.correlation?.request_id ?? null
|
|
20263
20945
|
};
|
|
20264
20946
|
}
|
|
20947
|
+
function titleCaseWord(value) {
|
|
20948
|
+
if (value.toLowerCase() === "github") {
|
|
20949
|
+
return "GitHub";
|
|
20950
|
+
}
|
|
20951
|
+
if (value.toLowerCase() === "api") {
|
|
20952
|
+
return "API";
|
|
20953
|
+
}
|
|
20954
|
+
return `${value.slice(0, 1).toUpperCase()}${value.slice(1)}`;
|
|
20955
|
+
}
|
|
20956
|
+
function formatDependencyName(name) {
|
|
20957
|
+
return name.split("_").filter((part) => part.length > 0).map(titleCaseWord).join(" ");
|
|
20958
|
+
}
|
|
20959
|
+
function inferDependencyName(text) {
|
|
20960
|
+
const match = /\b([a-z][a-z0-9]*_api)_(?:invalid_response|error|failure|failed|unavailable|timeout)\b/.exec(text);
|
|
20961
|
+
return match?.[1] ?? null;
|
|
20962
|
+
}
|
|
20963
|
+
function buildDependenciesContext(incident, errorContext, requestContext) {
|
|
20964
|
+
if (errorContext === null) {
|
|
20965
|
+
return null;
|
|
20966
|
+
}
|
|
20967
|
+
const text = `${incident.title} ${errorContext.name} ${errorContext.message}`.toLowerCase();
|
|
20968
|
+
const dependencyName = inferDependencyName(text);
|
|
20969
|
+
if (dependencyName === null) {
|
|
20970
|
+
return null;
|
|
20971
|
+
}
|
|
20972
|
+
const displayName = formatDependencyName(dependencyName);
|
|
20973
|
+
const route = requestContext?.route_template ?? requestContext?.path ?? null;
|
|
20974
|
+
const requestDescription = requestContext !== null ? `${requestContext.method} ${route ?? requestContext.path}` : "the failing request";
|
|
20975
|
+
const invalidResponse = text.includes("invalid_response");
|
|
20976
|
+
return {
|
|
20977
|
+
version: 1,
|
|
20978
|
+
items: [
|
|
20979
|
+
{
|
|
20980
|
+
name: dependencyName,
|
|
20981
|
+
status: "failed",
|
|
20982
|
+
notes: invalidResponse ? `${displayName} returned an unexpected response shape while handling ${requestDescription}.` : `${displayName} failed while handling ${requestDescription}.`
|
|
20983
|
+
}
|
|
20984
|
+
]
|
|
20985
|
+
};
|
|
20986
|
+
}
|
|
20987
|
+
function buildSummaryGuidance(input) {
|
|
20988
|
+
if (input.errorContext === null) {
|
|
20989
|
+
return {
|
|
20990
|
+
likely_cause: null,
|
|
20991
|
+
confidence: 0,
|
|
20992
|
+
recommended_action: null
|
|
20993
|
+
};
|
|
20994
|
+
}
|
|
20995
|
+
const route = input.requestContext?.route_template ?? input.requestContext?.path ?? null;
|
|
20996
|
+
const requestDescription = input.requestContext !== null ? `${input.requestContext.method} ${route ?? input.requestContext.path}` : null;
|
|
20997
|
+
const firstDependency = input.dependenciesContext?.items[0] ?? null;
|
|
20998
|
+
const dependencyDisplayName = firstDependency !== null ? formatDependencyName(firstDependency.name) : null;
|
|
20999
|
+
const firstFrame = input.firstApplicationFrame;
|
|
21000
|
+
const frameDescription = firstFrame?.file !== null && firstFrame?.file !== void 0 ? ` in ${firstFrame.file}` : "";
|
|
21001
|
+
const invalidResponse = input.errorContext.message.toLowerCase().includes("invalid_response");
|
|
21002
|
+
let likelyCause = null;
|
|
21003
|
+
let recommendedAction = null;
|
|
21004
|
+
if (firstDependency !== null && dependencyDisplayName !== null && requestDescription !== null && invalidResponse) {
|
|
21005
|
+
likelyCause = `${dependencyDisplayName} returned a response that did not match the expected schema while handling ${requestDescription}.`;
|
|
21006
|
+
recommendedAction = `Inspect the ${dependencyDisplayName} response handling${frameDescription}, including schema validation and sanitized upstream response shape.`;
|
|
21007
|
+
} else if (firstDependency !== null && dependencyDisplayName !== null && requestDescription !== null) {
|
|
21008
|
+
likelyCause = `${dependencyDisplayName} failed while handling ${requestDescription}.`;
|
|
21009
|
+
recommendedAction = `Inspect the ${dependencyDisplayName} call path${frameDescription} and compare the captured dependency notes with upstream status.`;
|
|
21010
|
+
} else if (requestDescription !== null) {
|
|
21011
|
+
likelyCause = `${input.errorContext.name} occurred while handling ${requestDescription}${frameDescription}.`;
|
|
21012
|
+
recommendedAction = `Inspect the first application frame${frameDescription} and the captured request/response context.`;
|
|
21013
|
+
} else if (firstFrame !== null) {
|
|
21014
|
+
likelyCause = `${input.errorContext.name} originated from the first captured application frame${frameDescription}.`;
|
|
21015
|
+
recommendedAction = `Inspect the first application frame${frameDescription} and surrounding error handling.`;
|
|
21016
|
+
}
|
|
21017
|
+
if (likelyCause === null || recommendedAction === null) {
|
|
21018
|
+
return {
|
|
21019
|
+
likely_cause: null,
|
|
21020
|
+
confidence: 0,
|
|
21021
|
+
recommended_action: null
|
|
21022
|
+
};
|
|
21023
|
+
}
|
|
21024
|
+
let confidence = 0.25;
|
|
21025
|
+
if (input.requestContext !== null) confidence += 0.15;
|
|
21026
|
+
if (input.responseContext !== null) confidence += 0.1;
|
|
21027
|
+
if (firstFrame !== null && firstFrame.file !== null) confidence += 0.1;
|
|
21028
|
+
if (firstDependency !== null) confidence += 0.1;
|
|
21029
|
+
if (input.errorContext.message.length > 0) confidence += 0.05;
|
|
21030
|
+
return {
|
|
21031
|
+
likely_cause: likelyCause,
|
|
21032
|
+
confidence: Math.min(0.8, Number(confidence.toFixed(2))),
|
|
21033
|
+
recommended_action: recommendedAction
|
|
21034
|
+
};
|
|
21035
|
+
}
|
|
21036
|
+
function normalizeBaseUrl4(value) {
|
|
21037
|
+
const trimmed = value?.trim();
|
|
21038
|
+
if (trimmed === void 0 || trimmed.length === 0) {
|
|
21039
|
+
return null;
|
|
21040
|
+
}
|
|
21041
|
+
return trimmed.replace(/\/+$/, "");
|
|
21042
|
+
}
|
|
21043
|
+
function buildLinks(incident, linkBaseUrls) {
|
|
21044
|
+
const apiBaseUrl = normalizeBaseUrl4(linkBaseUrls?.api);
|
|
21045
|
+
const appBaseUrl = normalizeBaseUrl4(linkBaseUrls?.app);
|
|
21046
|
+
const docsBaseUrl = normalizeBaseUrl4(linkBaseUrls?.docs);
|
|
21047
|
+
const incidentPath = `/v1/incidents/${encodeURIComponent(incident.incident_id)}`;
|
|
21048
|
+
const appIncidentPath = `/incidents/${encodeURIComponent(incident.incident_id)}`;
|
|
21049
|
+
const appProjectPath = `/projects/${encodeURIComponent(incident.project_id)}`;
|
|
21050
|
+
return {
|
|
21051
|
+
self: apiBaseUrl !== null ? `${apiBaseUrl}${incidentPath}/bundle` : null,
|
|
21052
|
+
reproduction: apiBaseUrl !== null ? `${apiBaseUrl}${incidentPath}/reproduction` : null,
|
|
21053
|
+
incident: appBaseUrl !== null ? `${appBaseUrl}${appIncidentPath}` : null,
|
|
21054
|
+
project: appBaseUrl !== null ? `${appBaseUrl}${appProjectPath}` : null,
|
|
21055
|
+
docs: docsBaseUrl !== null ? `${docsBaseUrl}/bundles` : null
|
|
21056
|
+
};
|
|
21057
|
+
}
|
|
21058
|
+
function applyBundleRedaction(candidate) {
|
|
21059
|
+
const redactionResult = redact(candidate);
|
|
21060
|
+
const fields = [...new Set(redactionResult.redacted_fields)].sort();
|
|
21061
|
+
const redactedBundle = redactionResult.redacted;
|
|
21062
|
+
redactedBundle["redaction"] = {
|
|
21063
|
+
redacted: true,
|
|
21064
|
+
fields,
|
|
21065
|
+
notes: fields.length > 0 ? "Sensitive bundle fields were redacted before storage." : null
|
|
21066
|
+
};
|
|
21067
|
+
return redactedBundle;
|
|
21068
|
+
}
|
|
20265
21069
|
function buildResponseContext(envelopes) {
|
|
20266
21070
|
const requestEvent = selectLatestEnvelopeByType(envelopes, isRequestEventEnvelope);
|
|
20267
21071
|
if (requestEvent !== null) {
|
|
@@ -20394,17 +21198,31 @@ function buildFrontendContext(envelopes) {
|
|
|
20394
21198
|
dom_context: domContext
|
|
20395
21199
|
};
|
|
20396
21200
|
}
|
|
20397
|
-
function buildDeployContext(envelopes, trigger) {
|
|
21201
|
+
function buildDeployContext(envelopes, trigger, configuredDeploy) {
|
|
20398
21202
|
const envelope = selectLatestEnvelopeByType(envelopes, isDeployMetadataEnvelope);
|
|
20399
|
-
if (envelope
|
|
21203
|
+
if (envelope !== null) {
|
|
21204
|
+
return {
|
|
21205
|
+
version: 1,
|
|
21206
|
+
commit_sha: envelope.payload.commit_sha,
|
|
21207
|
+
deploy_version: envelope.payload.version,
|
|
21208
|
+
branch: envelope.payload.branch,
|
|
21209
|
+
deployed_at: toIsoTimestamp(envelope.payload.deployed_at),
|
|
21210
|
+
regression_window: trigger === "regression_reopen"
|
|
21211
|
+
};
|
|
21212
|
+
}
|
|
21213
|
+
const commitSha = configuredDeploy?.commit_sha ?? null;
|
|
21214
|
+
const deployVersion = configuredDeploy?.deploy_version ?? null;
|
|
21215
|
+
const branch = configuredDeploy?.branch ?? null;
|
|
21216
|
+
const deployedAt = configuredDeploy?.deployed_at ?? null;
|
|
21217
|
+
if (commitSha === null && deployVersion === null && branch === null && deployedAt === null) {
|
|
20400
21218
|
return null;
|
|
20401
21219
|
}
|
|
20402
21220
|
return {
|
|
20403
21221
|
version: 1,
|
|
20404
|
-
commit_sha:
|
|
20405
|
-
deploy_version:
|
|
20406
|
-
branch
|
|
20407
|
-
deployed_at: toIsoTimestamp(
|
|
21222
|
+
commit_sha: commitSha,
|
|
21223
|
+
deploy_version: deployVersion,
|
|
21224
|
+
branch,
|
|
21225
|
+
deployed_at: deployedAt === null ? null : toIsoTimestamp(deployedAt),
|
|
20408
21226
|
regression_window: trigger === "regression_reopen"
|
|
20409
21227
|
};
|
|
20410
21228
|
}
|
|
@@ -20413,34 +21231,49 @@ function buildRuntimeContext(envelopes) {
|
|
|
20413
21231
|
if (backendException === null) {
|
|
20414
21232
|
return null;
|
|
20415
21233
|
}
|
|
21234
|
+
const runtime = backendException.payload.runtime;
|
|
20416
21235
|
return {
|
|
20417
21236
|
version: 1,
|
|
20418
21237
|
name: backendException.service.runtime ?? "unknown",
|
|
20419
|
-
runtime_version:
|
|
20420
|
-
platform: null,
|
|
20421
|
-
arch: null,
|
|
20422
|
-
pid: null,
|
|
20423
|
-
cwd: null,
|
|
20424
|
-
uptime_sec: null,
|
|
20425
|
-
hostname: null,
|
|
20426
|
-
thread_id: null,
|
|
21238
|
+
runtime_version: runtime.version,
|
|
21239
|
+
platform: runtime.platform ?? null,
|
|
21240
|
+
arch: runtime.arch ?? null,
|
|
21241
|
+
pid: runtime.pid ?? null,
|
|
21242
|
+
cwd: runtime.cwd ?? null,
|
|
21243
|
+
uptime_sec: runtime.uptime_sec ?? null,
|
|
21244
|
+
hostname: runtime.hostname ?? null,
|
|
21245
|
+
thread_id: runtime.thread_id ?? null,
|
|
20427
21246
|
framework: backendException.service.framework ?? null,
|
|
20428
|
-
framework_version: null,
|
|
20429
|
-
memory: null,
|
|
20430
|
-
framework_extras: null
|
|
21247
|
+
framework_version: runtime.framework_version ?? null,
|
|
21248
|
+
memory: runtime.memory ?? null,
|
|
21249
|
+
framework_extras: runtime.framework_extras ?? null
|
|
20431
21250
|
};
|
|
20432
21251
|
}
|
|
20433
|
-
function buildGitContext(envelopes) {
|
|
21252
|
+
function buildGitContext(envelopes, configuredDeploy) {
|
|
20434
21253
|
const deployEnvelope = selectLatestEnvelopeByType(envelopes, isDeployMetadataEnvelope);
|
|
20435
21254
|
if (deployEnvelope === null) {
|
|
20436
|
-
|
|
21255
|
+
const commit = configuredDeploy?.commit_sha ?? null;
|
|
21256
|
+
const branch = configuredDeploy?.branch ?? null;
|
|
21257
|
+
const repo = configuredDeploy?.repo ?? null;
|
|
21258
|
+
if (commit === null && branch === null && repo === null) {
|
|
21259
|
+
return null;
|
|
21260
|
+
}
|
|
21261
|
+
return {
|
|
21262
|
+
version: 1,
|
|
21263
|
+
commit,
|
|
21264
|
+
commit_short: commit === null ? null : commit.slice(0, 7),
|
|
21265
|
+
branch,
|
|
21266
|
+
repo,
|
|
21267
|
+
dirty: false,
|
|
21268
|
+
source: "env"
|
|
21269
|
+
};
|
|
20437
21270
|
}
|
|
20438
21271
|
return {
|
|
20439
21272
|
version: 1,
|
|
20440
21273
|
commit: deployEnvelope.payload.commit_sha,
|
|
20441
21274
|
commit_short: deployEnvelope.payload.commit_sha.slice(0, 7),
|
|
20442
21275
|
branch: deployEnvelope.payload.branch,
|
|
20443
|
-
repo: null,
|
|
21276
|
+
repo: configuredDeploy?.repo ?? null,
|
|
20444
21277
|
dirty: false,
|
|
20445
21278
|
source: "env"
|
|
20446
21279
|
};
|
|
@@ -20483,10 +21316,11 @@ function buildBundle(input) {
|
|
|
20483
21316
|
const responseContext = buildResponseContext(sourceEnvelopes);
|
|
20484
21317
|
const logsContext = buildLogsContext(sourceEnvelopes);
|
|
20485
21318
|
const frontendContext = buildFrontendContext(sourceEnvelopes);
|
|
20486
|
-
const deployContext = buildDeployContext(sourceEnvelopes, input.job.trigger);
|
|
21319
|
+
const deployContext = buildDeployContext(sourceEnvelopes, input.job.trigger, input.configuredDeploy);
|
|
20487
21320
|
const runtimeContext = buildRuntimeContext(sourceEnvelopes);
|
|
20488
|
-
const gitContext = buildGitContext(sourceEnvelopes);
|
|
21321
|
+
const gitContext = buildGitContext(sourceEnvelopes, input.configuredDeploy);
|
|
20489
21322
|
const deviceContext = buildDeviceContext(sourceEnvelopes);
|
|
21323
|
+
const dependenciesContext = buildDependenciesContext(input.incident, errorContext, requestContext);
|
|
20490
21324
|
const primarySignalType = primarySignalEnvelope !== null ? mapSignalType(primarySignalEnvelope.event_type) : inferSignalTypeFromSourceEventTypes(sourceEventTypes);
|
|
20491
21325
|
const primarySourceEvent = errorContext?.name ?? sourceEventTypes[0] ?? "backend_exception";
|
|
20492
21326
|
const firstSeenAt = new Date(input.incident.first_seen_at).toISOString();
|
|
@@ -20495,7 +21329,15 @@ function buildBundle(input) {
|
|
|
20495
21329
|
const serviceRuntime = input.incident.service_runtime ?? selectLatestEnvelope(sourceEnvelopes, (envelope) => envelope.event_type !== "probe_event")?.service.runtime ?? null;
|
|
20496
21330
|
const serviceFramework = input.incident.service_framework ?? selectLatestEnvelope(sourceEnvelopes, (envelope) => envelope.event_type !== "probe_event")?.service.framework ?? null;
|
|
20497
21331
|
const customerVisible = frontendContext !== null;
|
|
20498
|
-
|
|
21332
|
+
const firstApplicationFrame = deriveFirstApplicationFrame(errorContext);
|
|
21333
|
+
const summaryGuidance = buildSummaryGuidance({
|
|
21334
|
+
errorContext,
|
|
21335
|
+
requestContext,
|
|
21336
|
+
responseContext,
|
|
21337
|
+
dependenciesContext,
|
|
21338
|
+
firstApplicationFrame
|
|
21339
|
+
});
|
|
21340
|
+
const candidate = {
|
|
20499
21341
|
bundle_version: 1,
|
|
20500
21342
|
bundle_id: `bnd_${input.incident.incident_id}`,
|
|
20501
21343
|
bundle_type: "failure",
|
|
@@ -20530,13 +21372,13 @@ function buildBundle(input) {
|
|
|
20530
21372
|
summary: {
|
|
20531
21373
|
title: input.incident.title,
|
|
20532
21374
|
description: `Deterministic bundle generated from ${input.job.trigger}`,
|
|
20533
|
-
likely_cause:
|
|
20534
|
-
confidence:
|
|
20535
|
-
recommended_action:
|
|
21375
|
+
likely_cause: summaryGuidance.likely_cause,
|
|
21376
|
+
confidence: summaryGuidance.confidence,
|
|
21377
|
+
recommended_action: summaryGuidance.recommended_action,
|
|
20536
21378
|
severity: input.incident.severity,
|
|
20537
21379
|
error_type: primarySourceEvent,
|
|
20538
21380
|
error_message: errorContext?.message ?? input.incident.title,
|
|
20539
|
-
first_application_frame:
|
|
21381
|
+
first_application_frame: firstApplicationFrame,
|
|
20540
21382
|
primary_signal: primarySignalType,
|
|
20541
21383
|
signals: {
|
|
20542
21384
|
new_deploy: input.job.trigger === "deploy_metadata",
|
|
@@ -20561,7 +21403,7 @@ function buildBundle(input) {
|
|
|
20561
21403
|
deploy: deployContext,
|
|
20562
21404
|
runtime: runtimeContext,
|
|
20563
21405
|
git: gitContext,
|
|
20564
|
-
dependencies:
|
|
21406
|
+
dependencies: dependenciesContext,
|
|
20565
21407
|
probe_data: {
|
|
20566
21408
|
version: 1,
|
|
20567
21409
|
items: input.probeDataItems
|
|
@@ -20582,11 +21424,7 @@ function buildBundle(input) {
|
|
|
20582
21424
|
production_verified: false
|
|
20583
21425
|
},
|
|
20584
21426
|
links: {
|
|
20585
|
-
|
|
20586
|
-
reproduction: null,
|
|
20587
|
-
incident: null,
|
|
20588
|
-
project: null,
|
|
20589
|
-
docs: null
|
|
21427
|
+
...buildLinks(input.incident, input.linkBaseUrls)
|
|
20590
21428
|
},
|
|
20591
21429
|
redaction: {
|
|
20592
21430
|
redacted: true,
|
|
@@ -20599,7 +21437,8 @@ function buildBundle(input) {
|
|
|
20599
21437
|
generator_version: "worker-build-bundle-v2",
|
|
20600
21438
|
generation_number: input.bundleMetadata.generation_number
|
|
20601
21439
|
}
|
|
20602
|
-
}
|
|
21440
|
+
};
|
|
21441
|
+
return BundleV1Schema.parse(applyBundleRedaction(candidate));
|
|
20603
21442
|
}
|
|
20604
21443
|
|
|
20605
21444
|
// ../../packages/repro-engine/src/index.ts
|
|
@@ -20619,6 +21458,44 @@ function shellQuote(value) {
|
|
|
20619
21458
|
function sortRecordEntries(record) {
|
|
20620
21459
|
return Object.entries(record).sort(([left], [right]) => left.localeCompare(right));
|
|
20621
21460
|
}
|
|
21461
|
+
var REPLAY_HEADER_PRIORITY = [
|
|
21462
|
+
"authorization",
|
|
21463
|
+
"cookie",
|
|
21464
|
+
"accept",
|
|
21465
|
+
"content-type",
|
|
21466
|
+
"origin",
|
|
21467
|
+
"accept-language",
|
|
21468
|
+
"access-control-request-method",
|
|
21469
|
+
"access-control-request-headers",
|
|
21470
|
+
"x-request-id",
|
|
21471
|
+
"x-correlation-id",
|
|
21472
|
+
"x-debugbundle-trace-id"
|
|
21473
|
+
];
|
|
21474
|
+
var DROPPED_REPLAY_HEADERS = /* @__PURE__ */ new Set([
|
|
21475
|
+
"host",
|
|
21476
|
+
"x-forwarded-host",
|
|
21477
|
+
"x-forwarded-proto",
|
|
21478
|
+
"connection",
|
|
21479
|
+
"keep-alive",
|
|
21480
|
+
"transfer-encoding",
|
|
21481
|
+
"upgrade",
|
|
21482
|
+
"te",
|
|
21483
|
+
"trailer",
|
|
21484
|
+
"proxy-connection",
|
|
21485
|
+
"accept-encoding",
|
|
21486
|
+
"content-length",
|
|
21487
|
+
"cache-control",
|
|
21488
|
+
"pragma",
|
|
21489
|
+
"priority",
|
|
21490
|
+
"sec-ch-ua",
|
|
21491
|
+
"sec-ch-ua-mobile",
|
|
21492
|
+
"sec-ch-ua-platform",
|
|
21493
|
+
"sec-fetch-dest",
|
|
21494
|
+
"sec-fetch-mode",
|
|
21495
|
+
"sec-fetch-site",
|
|
21496
|
+
"sec-fetch-user",
|
|
21497
|
+
"user-agent"
|
|
21498
|
+
]);
|
|
20622
21499
|
function serializeScalarValue(value) {
|
|
20623
21500
|
if (typeof value === "string") {
|
|
20624
21501
|
return value;
|
|
@@ -20697,12 +21574,25 @@ function buildStructuredReplayQuery(query) {
|
|
|
20697
21574
|
return hasStructuredQueryAmbiguity(normalizedQuery) ? normalizedQuery : void 0;
|
|
20698
21575
|
}
|
|
20699
21576
|
function buildReplayHeaders(headers) {
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
|
|
21577
|
+
const entries = sortRecordEntries(headers).filter(([headerName]) => !DROPPED_REPLAY_HEADERS.has(headerName.toLowerCase())).map(([headerName, headerValue]) => [headerName, normalizeHeaderValue(headerValue)]);
|
|
21578
|
+
entries.sort(([left], [right]) => {
|
|
21579
|
+
const leftPriority = REPLAY_HEADER_PRIORITY.indexOf(left.toLowerCase());
|
|
21580
|
+
const rightPriority = REPLAY_HEADER_PRIORITY.indexOf(right.toLowerCase());
|
|
21581
|
+
if (leftPriority !== -1 || rightPriority !== -1) {
|
|
21582
|
+
if (leftPriority === -1) {
|
|
21583
|
+
return 1;
|
|
21584
|
+
}
|
|
21585
|
+
if (rightPriority === -1) {
|
|
21586
|
+
return -1;
|
|
21587
|
+
}
|
|
21588
|
+
return leftPriority - rightPriority;
|
|
21589
|
+
}
|
|
21590
|
+
return left.localeCompare(right);
|
|
21591
|
+
});
|
|
21592
|
+
return Object.fromEntries(entries);
|
|
20703
21593
|
}
|
|
20704
21594
|
function expandHeaderValues(headers) {
|
|
20705
|
-
return
|
|
21595
|
+
return Object.entries(headers).flatMap(([headerName, headerValue]) => {
|
|
20706
21596
|
if (Array.isArray(headerValue)) {
|
|
20707
21597
|
return headerValue.map((item) => [headerName, serializeScalarValue(item)]);
|
|
20708
21598
|
}
|
|
@@ -20840,11 +21730,17 @@ var EVENT_TYPE_SET = new Set(EventTypeValues);
|
|
|
20840
21730
|
function isEventType(value) {
|
|
20841
21731
|
return typeof value === "string" && EVENT_TYPE_SET.has(value);
|
|
20842
21732
|
}
|
|
20843
|
-
function inferSeverity2(
|
|
20844
|
-
if (
|
|
21733
|
+
function inferSeverity2(event, capturePreset, incidentKind = "immediate") {
|
|
21734
|
+
if (incidentKind === "request_anomaly") {
|
|
21735
|
+
return "medium";
|
|
21736
|
+
}
|
|
21737
|
+
if (event.event_type === "request_event") {
|
|
21738
|
+
return classifyRequestStatus({ responseStatus: event.payload.response_status, capturePreset }) === "incident_signal" ? "high" : "low";
|
|
21739
|
+
}
|
|
21740
|
+
if (event.event_type === "backend_exception" || event.event_type === "frontend_exception") {
|
|
20845
21741
|
return "high";
|
|
20846
21742
|
}
|
|
20847
|
-
if (
|
|
21743
|
+
if (event.event_type === "error_suppressed") {
|
|
20848
21744
|
return "medium";
|
|
20849
21745
|
}
|
|
20850
21746
|
return "low";
|
|
@@ -20868,15 +21764,17 @@ function compareEventEnvelopes(left, right) {
|
|
|
20868
21764
|
}
|
|
20869
21765
|
return left.event_id.localeCompare(right.event_id);
|
|
20870
21766
|
}
|
|
20871
|
-
function classifyEnvelope(envelope) {
|
|
21767
|
+
function classifyEnvelope(envelope, capturePreset) {
|
|
20872
21768
|
return classifyEvent(
|
|
20873
21769
|
envelope.event_type,
|
|
20874
21770
|
envelope.event_type === "log_event" ? envelope.payload.level : void 0,
|
|
20875
|
-
envelope.event_type === "probe_event" ? envelope.payload.activation_id : void 0
|
|
21771
|
+
envelope.event_type === "probe_event" ? envelope.payload.activation_id : void 0,
|
|
21772
|
+
envelope.payload,
|
|
21773
|
+
capturePreset
|
|
20876
21774
|
);
|
|
20877
21775
|
}
|
|
20878
|
-
function isIncidentSignalEnvelope(envelope) {
|
|
20879
|
-
return classifyEnvelope(envelope) === "incident_signal";
|
|
21776
|
+
function isIncidentSignalEnvelope(envelope, capturePreset) {
|
|
21777
|
+
return classifyEnvelope(envelope, capturePreset) === "incident_signal";
|
|
20880
21778
|
}
|
|
20881
21779
|
function getTraceId(envelope) {
|
|
20882
21780
|
return envelope.correlation?.trace_id ?? null;
|
|
@@ -20921,7 +21819,10 @@ function mergeAggregateGroup(aggregates) {
|
|
|
20921
21819
|
newEvents: [...canonicalAggregate.newEvents],
|
|
20922
21820
|
mergedIncidentIds: new Set(canonicalAggregate.mergedIncidentIds),
|
|
20923
21821
|
signalEventTypes: new Set(canonicalAggregate.signalEventTypes),
|
|
20924
|
-
traceIds: new Set(canonicalAggregate.traceIds)
|
|
21822
|
+
traceIds: new Set(canonicalAggregate.traceIds),
|
|
21823
|
+
title: canonicalAggregate.title,
|
|
21824
|
+
kind: canonicalAggregate.kind,
|
|
21825
|
+
severity: canonicalAggregate.severity
|
|
20925
21826
|
});
|
|
20926
21827
|
}
|
|
20927
21828
|
function hashIdentifier(parts, prefix, length) {
|
|
@@ -20947,6 +21848,111 @@ function mergeSourceEvents(existingEvents, nextEvents) {
|
|
|
20947
21848
|
}
|
|
20948
21849
|
return [...merged.values()].sort(compareEventEnvelopes);
|
|
20949
21850
|
}
|
|
21851
|
+
function stableJson2(value) {
|
|
21852
|
+
if (value === null || typeof value !== "object") {
|
|
21853
|
+
return JSON.stringify(value);
|
|
21854
|
+
}
|
|
21855
|
+
if (Array.isArray(value)) {
|
|
21856
|
+
return `[${value.map((entry) => stableJson2(entry)).join(",")}]`;
|
|
21857
|
+
}
|
|
21858
|
+
const record = value;
|
|
21859
|
+
const keys = Object.keys(record).sort();
|
|
21860
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableJson2(record[key])}`).join(",")}}`;
|
|
21861
|
+
}
|
|
21862
|
+
function buildRequestAnomalyFingerprint(input) {
|
|
21863
|
+
return (0, import_node_crypto4.createHash)("sha256").update(
|
|
21864
|
+
stableJson2({
|
|
21865
|
+
kind: "request_status_anomaly",
|
|
21866
|
+
project_id: input.projectId,
|
|
21867
|
+
service_name: input.serviceName,
|
|
21868
|
+
environment: input.environment,
|
|
21869
|
+
method: input.method,
|
|
21870
|
+
route_template: input.routeTemplate,
|
|
21871
|
+
response_status: input.responseStatus
|
|
21872
|
+
})
|
|
21873
|
+
).digest("hex");
|
|
21874
|
+
}
|
|
21875
|
+
function buildRequestAnomalyTitle(input) {
|
|
21876
|
+
return `Request anomaly: ${input.method} ${input.routeTemplate} returned ${input.responseStatus} repeatedly`;
|
|
21877
|
+
}
|
|
21878
|
+
function toUnixSeconds(occurredAt) {
|
|
21879
|
+
return Math.floor(new Date(occurredAt).getTime() / 1e3);
|
|
21880
|
+
}
|
|
21881
|
+
function countOccurrencesInWindow(events, windowSeconds) {
|
|
21882
|
+
const latestEvent = events.at(-1);
|
|
21883
|
+
if (latestEvent === void 0) {
|
|
21884
|
+
return 0;
|
|
21885
|
+
}
|
|
21886
|
+
const latestOccurredAt = toUnixSeconds(latestEvent.occurred_at);
|
|
21887
|
+
const lowerBound = latestOccurredAt - windowSeconds + 1;
|
|
21888
|
+
return events.filter((event) => {
|
|
21889
|
+
const occurredAt = toUnixSeconds(event.occurred_at);
|
|
21890
|
+
return occurredAt >= lowerBound && occurredAt <= latestOccurredAt;
|
|
21891
|
+
}).length;
|
|
21892
|
+
}
|
|
21893
|
+
function passesRequestAnomalyThreshold(events, threshold) {
|
|
21894
|
+
const occurrences5m = countOccurrencesInWindow(events, 5 * 60);
|
|
21895
|
+
const occurrences1h = countOccurrencesInWindow(events, 60 * 60);
|
|
21896
|
+
const baseline1hPer5m = occurrences1h / 12;
|
|
21897
|
+
const ratio = occurrences5m / Math.max(baseline1hPer5m, 1);
|
|
21898
|
+
return occurrences5m >= threshold.minimum_occurrences_5m && ratio >= threshold.minimum_ratio_5m_to_1h;
|
|
21899
|
+
}
|
|
21900
|
+
function collectRequestAnomalyAggregates(batches, capturePreset) {
|
|
21901
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
21902
|
+
for (const batch of batches) {
|
|
21903
|
+
for (const event of batch.events) {
|
|
21904
|
+
if (event.event_type !== "request_event" || classifyEnvelope(event, capturePreset) !== "context_signal") {
|
|
21905
|
+
continue;
|
|
21906
|
+
}
|
|
21907
|
+
const normalizedEvent = normalizeEvent(event);
|
|
21908
|
+
const responseStatus = normalizedEvent.http_status;
|
|
21909
|
+
const method = normalizedEvent.http_method;
|
|
21910
|
+
const routeTemplate = normalizedEvent.route_template;
|
|
21911
|
+
const threshold = getRequestAnomalyThreshold({ responseStatus, capturePreset });
|
|
21912
|
+
if (threshold === null || responseStatus === null || method === null || routeTemplate === null) {
|
|
21913
|
+
continue;
|
|
21914
|
+
}
|
|
21915
|
+
const projectId = requireProjectId(event);
|
|
21916
|
+
const incidentFingerprint = buildRequestAnomalyFingerprint({
|
|
21917
|
+
projectId,
|
|
21918
|
+
serviceName: event.service.name,
|
|
21919
|
+
environment: event.service.environment,
|
|
21920
|
+
method,
|
|
21921
|
+
routeTemplate,
|
|
21922
|
+
responseStatus
|
|
21923
|
+
});
|
|
21924
|
+
const incidentId = deriveIncidentId(projectId, event.service.name, event.service.environment, incidentFingerprint);
|
|
21925
|
+
const aggregate = grouped.get(incidentId) ?? {
|
|
21926
|
+
incidentId,
|
|
21927
|
+
projectId,
|
|
21928
|
+
serviceName: event.service.name,
|
|
21929
|
+
environment: event.service.environment,
|
|
21930
|
+
fingerprint: incidentFingerprint,
|
|
21931
|
+
matchedFields: /* @__PURE__ */ new Set(["request_anomaly", "route_template", "http_method", "http_status", "environment"]),
|
|
21932
|
+
newEvents: [],
|
|
21933
|
+
mergedIncidentIds: /* @__PURE__ */ new Set([incidentId]),
|
|
21934
|
+
signalEventTypes: /* @__PURE__ */ new Set(["request_event"]),
|
|
21935
|
+
traceIds: /* @__PURE__ */ new Set(),
|
|
21936
|
+
title: buildRequestAnomalyTitle({ method, routeTemplate, responseStatus }),
|
|
21937
|
+
kind: "request_anomaly",
|
|
21938
|
+
severity: "medium"
|
|
21939
|
+
};
|
|
21940
|
+
aggregate.newEvents.push(event);
|
|
21941
|
+
grouped.set(incidentId, aggregate);
|
|
21942
|
+
}
|
|
21943
|
+
}
|
|
21944
|
+
return [...grouped.values()].filter((aggregate) => {
|
|
21945
|
+
const latestEvent = aggregate.newEvents.at(-1);
|
|
21946
|
+
if (latestEvent === void 0 || latestEvent.event_type !== "request_event") {
|
|
21947
|
+
return false;
|
|
21948
|
+
}
|
|
21949
|
+
const threshold = getRequestAnomalyThreshold({
|
|
21950
|
+
responseStatus: normalizeEvent(latestEvent).http_status,
|
|
21951
|
+
capturePreset
|
|
21952
|
+
});
|
|
21953
|
+
return threshold !== null && passesRequestAnomalyThreshold(aggregate.newEvents, threshold);
|
|
21954
|
+
}).sort((left, right) => left.incidentId.localeCompare(right.incidentId));
|
|
21955
|
+
}
|
|
20950
21956
|
function buildBundleContext(incident) {
|
|
20951
21957
|
return {
|
|
20952
21958
|
incident_id: incident.incident_id,
|
|
@@ -20976,12 +21982,12 @@ function formatServiceSummary(services) {
|
|
|
20976
21982
|
}
|
|
20977
21983
|
function formatProcessOutput(summary) {
|
|
20978
21984
|
if (!summary.processed) {
|
|
20979
|
-
return summary.message
|
|
21985
|
+
return summary.message;
|
|
20980
21986
|
}
|
|
20981
21987
|
return [
|
|
20982
21988
|
`Processed ${summary.events_processed} events from ${summary.files_processed} files into ${summary.incidents_processed} incidents.`,
|
|
20983
21989
|
...formatServiceSummary(summary.services),
|
|
20984
|
-
`Last processed event file: ${summary.last_processed_event_file
|
|
21990
|
+
`Last processed event file: ${summary.last_processed_event_file}`
|
|
20985
21991
|
].join("\n");
|
|
20986
21992
|
}
|
|
20987
21993
|
async function pathExists4(path, stat) {
|
|
@@ -21211,16 +22217,17 @@ function serializeState(state) {
|
|
|
21211
22217
|
}
|
|
21212
22218
|
async function processCommand(input, dependencies = {}) {
|
|
21213
22219
|
const cwd = dependencies.cwd ?? (() => process.cwd());
|
|
21214
|
-
const mkdir = dependencies.mkdir ??
|
|
21215
|
-
const readFile = dependencies.readFile ?? ((filePath) => (0,
|
|
21216
|
-
const readdir = dependencies.readdir ??
|
|
21217
|
-
const stat = dependencies.stat ??
|
|
21218
|
-
const writeFile = dependencies.writeFile ?? ((filePath, content) => (0,
|
|
22220
|
+
const mkdir = dependencies.mkdir ?? import_promises10.mkdir;
|
|
22221
|
+
const readFile = dependencies.readFile ?? ((filePath) => (0, import_promises10.readFile)(filePath, "utf8"));
|
|
22222
|
+
const readdir = dependencies.readdir ?? import_promises10.readdir;
|
|
22223
|
+
const stat = dependencies.stat ?? import_promises10.stat;
|
|
22224
|
+
const writeFile = dependencies.writeFile ?? ((filePath, content) => (0, import_promises10.writeFile)(filePath, content, "utf8"));
|
|
21219
22225
|
const rootDirectory = cwd();
|
|
21220
22226
|
const eventsDirectoryPath = (0, import_node_path11.join)(rootDirectory, LOCAL_EVENTS_DIRECTORY_PATH2);
|
|
21221
22227
|
const statePath = (0, import_node_path11.join)(rootDirectory, LOCAL_STATE_FILE_PATH);
|
|
21222
22228
|
const bundleDirectoryPath = (0, import_node_path11.join)(rootDirectory, LOCAL_BUNDLE_DIRECTORY_PATH3);
|
|
21223
22229
|
const reproductionDirectoryPath = (0, import_node_path11.join)(rootDirectory, LOCAL_REPRODUCTION_DIRECTORY_PATH2);
|
|
22230
|
+
const capturePreset = input.preset ?? "minimal";
|
|
21224
22231
|
await mkdir((0, import_node_path11.join)(rootDirectory, ".debugbundle", "local"), { recursive: true });
|
|
21225
22232
|
await mkdir(bundleDirectoryPath, { recursive: true });
|
|
21226
22233
|
await mkdir(reproductionDirectoryPath, { recursive: true });
|
|
@@ -21228,22 +22235,26 @@ async function processCommand(input, dependencies = {}) {
|
|
|
21228
22235
|
const eventFileNames = await pathExists4(eventsDirectoryPath, stat) ? (await readdir(eventsDirectoryPath)).filter((fileName) => fileName.endsWith(".events.json")).sort() : [];
|
|
21229
22236
|
const lastProcessedEventFile = previousState?.last_processed_event_file ?? null;
|
|
21230
22237
|
const newEventFileNames = lastProcessedEventFile === null ? eventFileNames : eventFileNames.filter((fileName) => fileName > lastProcessedEventFile);
|
|
21231
|
-
|
|
22238
|
+
const processAllEventFiles = input.preset !== void 0;
|
|
22239
|
+
const targetEventFileNames = processAllEventFiles ? eventFileNames : newEventFileNames;
|
|
22240
|
+
if (targetEventFileNames.length === 0) {
|
|
21232
22241
|
const summary2 = buildNoNewEventsSummary(previousState?.last_processed_event_file ?? eventFileNames.at(-1) ?? null);
|
|
21233
22242
|
return {
|
|
21234
22243
|
exitCode: 0,
|
|
21235
22244
|
output: input.json === true ? JSON.stringify(summary2) : formatProcessOutput(summary2)
|
|
21236
22245
|
};
|
|
21237
22246
|
}
|
|
21238
|
-
const batches = await readEventBatches(eventsDirectoryPath,
|
|
21239
|
-
const incidents = new Map(
|
|
22247
|
+
const batches = await readEventBatches(eventsDirectoryPath, targetEventFileNames, readFile);
|
|
22248
|
+
const incidents = new Map(
|
|
22249
|
+
processAllEventFiles ? [] : Object.entries(previousState?.incidents ?? {})
|
|
22250
|
+
);
|
|
21240
22251
|
const aggregates = /* @__PURE__ */ new Map();
|
|
21241
22252
|
const traceCorrelationGroups = /* @__PURE__ */ new Map();
|
|
21242
22253
|
let eventsProcessed = 0;
|
|
21243
22254
|
for (const batch of batches) {
|
|
21244
22255
|
for (const event of batch.events) {
|
|
21245
22256
|
eventsProcessed += 1;
|
|
21246
|
-
if (!isIncidentSignalEnvelope(event)) {
|
|
22257
|
+
if (!isIncidentSignalEnvelope(event, capturePreset)) {
|
|
21247
22258
|
continue;
|
|
21248
22259
|
}
|
|
21249
22260
|
const normalizedEvent = normalizeEvent(event);
|
|
@@ -21260,7 +22271,10 @@ async function processCommand(input, dependencies = {}) {
|
|
|
21260
22271
|
newEvents: [],
|
|
21261
22272
|
mergedIncidentIds: /* @__PURE__ */ new Set([incidentId]),
|
|
21262
22273
|
signalEventTypes: /* @__PURE__ */ new Set(),
|
|
21263
|
-
traceIds: /* @__PURE__ */ new Set()
|
|
22274
|
+
traceIds: /* @__PURE__ */ new Set(),
|
|
22275
|
+
title: normalizedEvent.normalized_message,
|
|
22276
|
+
kind: "immediate",
|
|
22277
|
+
severity: inferSeverity2(event, capturePreset)
|
|
21264
22278
|
};
|
|
21265
22279
|
for (const matchedField of inferMatchedFields(normalizedEvent)) {
|
|
21266
22280
|
aggregate.matchedFields.add(matchedField);
|
|
@@ -21327,14 +22341,16 @@ async function processCommand(input, dependencies = {}) {
|
|
|
21327
22341
|
mergedAggregatesByRoot.set(rootIncidentId, aggregateGroup);
|
|
21328
22342
|
}
|
|
21329
22343
|
const mergedAggregates = [...mergedAggregatesByRoot.values()].map((aggregateGroup) => mergeAggregateGroup(aggregateGroup)).sort((left, right) => left.incidentId.localeCompare(right.incidentId));
|
|
22344
|
+
const requestAnomalyAggregates = input.preset === void 0 ? [] : collectRequestAnomalyAggregates(batches, capturePreset);
|
|
22345
|
+
const finalizedAggregates = [...mergedAggregates, ...requestAnomalyAggregates].sort((left, right) => left.incidentId.localeCompare(right.incidentId));
|
|
21330
22346
|
const services = /* @__PURE__ */ new Map();
|
|
21331
|
-
for (const aggregate of
|
|
22347
|
+
for (const aggregate of finalizedAggregates) {
|
|
21332
22348
|
const incidentId = aggregate.incidentId;
|
|
21333
22349
|
const existingIncidents = [...aggregate.mergedIncidentIds].map((mergedIncidentId) => incidents.get(mergedIncidentId)).filter((incident2) => incident2 !== void 0);
|
|
21334
22350
|
const existing = existingIncidents.find((incident2) => incident2.incident_id === incidentId) ?? existingIncidents[0];
|
|
21335
22351
|
const existingSourceEvents = existingIncidents.flatMap((incident2) => incident2.source_events);
|
|
21336
22352
|
const combinedSourceEvents = mergeSourceEvents(existingSourceEvents, aggregate.newEvents);
|
|
21337
|
-
const signalEvents = combinedSourceEvents.filter(isIncidentSignalEnvelope);
|
|
22353
|
+
const signalEvents = aggregate.kind === "request_anomaly" ? combinedSourceEvents : combinedSourceEvents.filter((event) => isIncidentSignalEnvelope(event, capturePreset));
|
|
21338
22354
|
if (signalEvents.length === 0) {
|
|
21339
22355
|
continue;
|
|
21340
22356
|
}
|
|
@@ -21344,8 +22360,7 @@ async function processCommand(input, dependencies = {}) {
|
|
|
21344
22360
|
continue;
|
|
21345
22361
|
}
|
|
21346
22362
|
const sourceEventTypes = [...new Set(signalEvents.map((event) => event.event_type))].sort();
|
|
21347
|
-
const severity = signalEvents.map((event) => inferSeverity2(event.
|
|
21348
|
-
const latestNormalizedEvent = normalizeEvent(latestSignalEvent);
|
|
22363
|
+
const severity = signalEvents.map((event) => inferSeverity2(event, capturePreset, aggregate.kind)).sort((left, right) => severityRank(right) - severityRank(left))[0] ?? aggregate.severity;
|
|
21349
22364
|
const generationNumber = signalEvents.length;
|
|
21350
22365
|
const bundlePath = `${LOCAL_BUNDLE_DIRECTORY_PATH3}/${incidentId}.bundle.json`;
|
|
21351
22366
|
const reproductionPath = `${LOCAL_REPRODUCTION_DIRECTORY_PATH2}/${incidentId}.reproduction.json`;
|
|
@@ -21360,7 +22375,7 @@ async function processCommand(input, dependencies = {}) {
|
|
|
21360
22375
|
environment: aggregate.environment,
|
|
21361
22376
|
fingerprint: aggregate.fingerprint,
|
|
21362
22377
|
fingerprint_version: FINGERPRINT_VERSION,
|
|
21363
|
-
title:
|
|
22378
|
+
title: aggregate.title,
|
|
21364
22379
|
severity,
|
|
21365
22380
|
status: existingIncidents.some((incidentState) => incidentState.status === "resolved") ? "open" : existing?.status ?? "open",
|
|
21366
22381
|
first_seen_at: firstSignalEvent.occurred_at,
|
|
@@ -21421,21 +22436,22 @@ async function processCommand(input, dependencies = {}) {
|
|
|
21421
22436
|
incidents.set(incidentId, incident);
|
|
21422
22437
|
services.set(incident.service_name, (services.get(incident.service_name) ?? 0) + 1);
|
|
21423
22438
|
}
|
|
22439
|
+
const finalProcessedEventFile = targetEventFileNames[targetEventFileNames.length - 1];
|
|
21424
22440
|
const nextState = {
|
|
21425
22441
|
version: 1,
|
|
21426
|
-
last_processed_event_file:
|
|
22442
|
+
last_processed_event_file: finalProcessedEventFile,
|
|
21427
22443
|
incidents: Object.fromEntries([...incidents.entries()].sort(([left], [right]) => left.localeCompare(right)))
|
|
21428
22444
|
};
|
|
21429
22445
|
await writeFile(statePath, serializeState(nextState));
|
|
21430
22446
|
const summary = buildProcessedSummary({
|
|
21431
22447
|
filesProcessed: newEventFileNames.length,
|
|
21432
22448
|
eventsProcessed,
|
|
21433
|
-
incidentsProcessed:
|
|
22449
|
+
incidentsProcessed: finalizedAggregates.length,
|
|
21434
22450
|
services: [...services.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([service, count]) => ({
|
|
21435
22451
|
service,
|
|
21436
22452
|
incidents: count
|
|
21437
22453
|
})),
|
|
21438
|
-
lastProcessedEventFile:
|
|
22454
|
+
lastProcessedEventFile: finalProcessedEventFile
|
|
21439
22455
|
});
|
|
21440
22456
|
return {
|
|
21441
22457
|
exitCode: 0,
|
|
@@ -21495,11 +22511,11 @@ async function ingestCommand(input, dependencies = {}) {
|
|
|
21495
22511
|
};
|
|
21496
22512
|
}
|
|
21497
22513
|
const cwd = dependencies.cwd ?? (() => process.cwd());
|
|
21498
|
-
const mkdir = dependencies.mkdir ?? (async (path, options) => (0,
|
|
22514
|
+
const mkdir = dependencies.mkdir ?? (async (path, options) => (0, import_promises11.mkdir)(path, options));
|
|
21499
22515
|
const processLocalEvents = dependencies.processCommand ?? processCommand;
|
|
21500
|
-
const readFile = dependencies.readFile ?? (async (path) => (0,
|
|
21501
|
-
const rename = dependencies.rename ?? (async (sourcePath, destinationPath) => (0,
|
|
21502
|
-
const writeFile = dependencies.writeFile ?? (async (path, content) => (0,
|
|
22516
|
+
const readFile = dependencies.readFile ?? (async (path) => (0, import_promises11.readFile)(path, "utf8"));
|
|
22517
|
+
const rename = dependencies.rename ?? (async (sourcePath, destinationPath) => (0, import_promises11.rename)(sourcePath, destinationPath));
|
|
22518
|
+
const writeFile = dependencies.writeFile ?? (async (path, content) => (0, import_promises11.writeFile)(path, content, "utf8"));
|
|
21503
22519
|
try {
|
|
21504
22520
|
const rootDirectory = cwd();
|
|
21505
22521
|
const profile = await readProfile(rootDirectory, readFile);
|
|
@@ -21556,14 +22572,14 @@ async function ingestCommand(input, dependencies = {}) {
|
|
|
21556
22572
|
}
|
|
21557
22573
|
|
|
21558
22574
|
// src/watch-command.ts
|
|
21559
|
-
var
|
|
22575
|
+
var import_promises12 = require("node:fs/promises");
|
|
21560
22576
|
var import_node_path13 = require("node:path");
|
|
21561
|
-
function
|
|
22577
|
+
function normalizeBaseUrl5(baseUrl) {
|
|
21562
22578
|
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
21563
22579
|
}
|
|
21564
22580
|
async function sendEventsToApi(input, dependencies) {
|
|
21565
22581
|
const fetchImpl = dependencies?.fetchImpl ?? fetch;
|
|
21566
|
-
const response = await fetchImpl(`${
|
|
22582
|
+
const response = await fetchImpl(`${normalizeBaseUrl5(input.baseUrl)}/v1/events`, {
|
|
21567
22583
|
method: "POST",
|
|
21568
22584
|
headers: {
|
|
21569
22585
|
accept: "application/json",
|
|
@@ -21600,18 +22616,18 @@ async function watchCommand(input, dependencies = {}) {
|
|
|
21600
22616
|
};
|
|
21601
22617
|
}
|
|
21602
22618
|
const cwd = dependencies.cwd ?? (() => process.cwd());
|
|
21603
|
-
const mkdir = dependencies.mkdir ?? (async (path, options) => (0,
|
|
22619
|
+
const mkdir = dependencies.mkdir ?? (async (path, options) => (0, import_promises12.mkdir)(path, options));
|
|
21604
22620
|
const pollIntervalMs = dependencies.pollIntervalMs ?? 1e3;
|
|
21605
22621
|
const processLocalEvents = dependencies.processCommand ?? processCommand;
|
|
21606
|
-
const readFile = dependencies.readFile ?? (async (path) => (0,
|
|
22622
|
+
const readFile = dependencies.readFile ?? (async (path) => (0, import_promises12.readFile)(path));
|
|
21607
22623
|
const readEnv = dependencies.readEnv ?? ((name) => process.env[name]);
|
|
21608
|
-
const rename = dependencies.rename ?? (async (sourcePath, destinationPath) => (0,
|
|
22624
|
+
const rename = dependencies.rename ?? (async (sourcePath, destinationPath) => (0, import_promises12.rename)(sourcePath, destinationPath));
|
|
21609
22625
|
const fetchDependencies = dependencies.fetchImpl === void 0 ? void 0 : { fetchImpl: dependencies.fetchImpl };
|
|
21610
22626
|
const sendEvents = dependencies.sendEvents ?? ((requestInput) => sendEventsToApi(requestInput, fetchDependencies));
|
|
21611
22627
|
const signal = dependencies.signal;
|
|
21612
22628
|
const sleep = dependencies.sleep ?? (async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
21613
|
-
const stat = dependencies.stat ??
|
|
21614
|
-
const writeFile = dependencies.writeFile ?? (async (path, content) => (0,
|
|
22629
|
+
const stat = dependencies.stat ?? import_promises12.stat;
|
|
22630
|
+
const writeFile = dependencies.writeFile ?? (async (path, content) => (0, import_promises12.writeFile)(path, content, "utf8"));
|
|
21615
22631
|
try {
|
|
21616
22632
|
const rootDirectory = cwd();
|
|
21617
22633
|
const profile = await readProfile(rootDirectory, async (path) => (await readFile(path)).toString("utf8"));
|
|
@@ -21733,7 +22749,7 @@ async function watchCommand(input, dependencies = {}) {
|
|
|
21733
22749
|
}
|
|
21734
22750
|
|
|
21735
22751
|
// src/setup-command.ts
|
|
21736
|
-
var
|
|
22752
|
+
var import_promises13 = require("node:fs/promises");
|
|
21737
22753
|
var import_node_path14 = require("node:path");
|
|
21738
22754
|
var MANAGED_AGENTS_START = "<!-- debugbundle:start -->";
|
|
21739
22755
|
var MANAGED_AGENTS_END = "<!-- debugbundle:end -->";
|
|
@@ -22280,13 +23296,13 @@ async function buildProfile(rootDirectory, dependencies) {
|
|
|
22280
23296
|
async function setupCommand(input, dependencies = {}) {
|
|
22281
23297
|
const cwd = dependencies.cwd ?? (() => process.cwd());
|
|
22282
23298
|
const mkdir = dependencies.mkdir ?? (async (path, options) => {
|
|
22283
|
-
await (0,
|
|
23299
|
+
await (0, import_promises13.mkdir)(path, options);
|
|
22284
23300
|
});
|
|
22285
|
-
const readFile = dependencies.readFile ?? (async (path) => (0,
|
|
22286
|
-
const readdir = dependencies.readdir ?? (async (path) => (0,
|
|
22287
|
-
const remove = dependencies.remove ?? (async (path, options) => (0,
|
|
22288
|
-
const stat = dependencies.stat ?? (async (path) => (0,
|
|
22289
|
-
const writeFile = dependencies.writeFile ?? (async (path, content) => (0,
|
|
23301
|
+
const readFile = dependencies.readFile ?? (async (path) => (0, import_promises13.readFile)(path, "utf8"));
|
|
23302
|
+
const readdir = dependencies.readdir ?? (async (path) => (0, import_promises13.readdir)(path));
|
|
23303
|
+
const remove = dependencies.remove ?? (async (path, options) => (0, import_promises13.rm)(path, options));
|
|
23304
|
+
const stat = dependencies.stat ?? (async (path) => (0, import_promises13.stat)(path));
|
|
23305
|
+
const writeFile = dependencies.writeFile ?? (async (path, content) => (0, import_promises13.writeFile)(path, content, "utf8"));
|
|
22290
23306
|
const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
22291
23307
|
const rootDirectory = cwd();
|
|
22292
23308
|
const packageJson = await readJsonFile2((0, import_node_path14.join)(rootDirectory, "package.json"), readFile);
|
|
@@ -22387,66 +23403,6 @@ async function setupCommand(input, dependencies = {}) {
|
|
|
22387
23403
|
}
|
|
22388
23404
|
}
|
|
22389
23405
|
|
|
22390
|
-
// src/login-command.ts
|
|
22391
|
-
var LoginCommandInputSchema = external_exports.object({
|
|
22392
|
-
bearerToken: external_exports.string().trim().min(1),
|
|
22393
|
-
baseUrl: external_exports.string().url().default("https://api.debugbundle.com")
|
|
22394
|
-
});
|
|
22395
|
-
function formatLoginOutput(authState, authFilePath) {
|
|
22396
|
-
return [
|
|
22397
|
-
"Authenticated: yes",
|
|
22398
|
-
`Base URL: ${authState.base_url}`,
|
|
22399
|
-
`Auth File: ${authFilePath}`,
|
|
22400
|
-
`Token: ${buildTokenPreview(authState.bearer_token)}`
|
|
22401
|
-
].join("\n");
|
|
22402
|
-
}
|
|
22403
|
-
function validateMemberToken(token) {
|
|
22404
|
-
return token.startsWith("dbundle_mem_");
|
|
22405
|
-
}
|
|
22406
|
-
async function loginCommand(input, dependencies) {
|
|
22407
|
-
const parsedInput = LoginCommandInputSchema.safeParse({
|
|
22408
|
-
bearerToken: input.bearerToken,
|
|
22409
|
-
baseUrl: input.baseUrl ?? "https://api.debugbundle.com"
|
|
22410
|
-
});
|
|
22411
|
-
if (!parsedInput.success || !validateMemberToken(parsedInput.data.bearerToken)) {
|
|
22412
|
-
return {
|
|
22413
|
-
exitCode: 4,
|
|
22414
|
-
output: "Invalid member token."
|
|
22415
|
-
};
|
|
22416
|
-
}
|
|
22417
|
-
const authState = {
|
|
22418
|
-
bearer_token: parsedInput.data.bearerToken,
|
|
22419
|
-
base_url: parsedInput.data.baseUrl
|
|
22420
|
-
};
|
|
22421
|
-
const writeAuthState = dependencies?.writeAuthState ?? persistCliAuthState;
|
|
22422
|
-
try {
|
|
22423
|
-
const writeInput = {
|
|
22424
|
-
authState
|
|
22425
|
-
};
|
|
22426
|
-
if (input.authFilePath !== void 0) {
|
|
22427
|
-
writeInput.authFilePath = input.authFilePath;
|
|
22428
|
-
}
|
|
22429
|
-
const persistedPath = await writeAuthState(writeInput) ?? (input.authFilePath ?? "");
|
|
22430
|
-
const payload = {
|
|
22431
|
-
authenticated: true,
|
|
22432
|
-
auth: {
|
|
22433
|
-
base_url: authState.base_url,
|
|
22434
|
-
token_preview: buildTokenPreview(authState.bearer_token)
|
|
22435
|
-
},
|
|
22436
|
-
auth_file_path: persistedPath
|
|
22437
|
-
};
|
|
22438
|
-
return {
|
|
22439
|
-
exitCode: 0,
|
|
22440
|
-
output: input.json ? JSON.stringify(payload) : formatLoginOutput(authState, payload.auth_file_path)
|
|
22441
|
-
};
|
|
22442
|
-
} catch (error) {
|
|
22443
|
-
return {
|
|
22444
|
-
exitCode: 1,
|
|
22445
|
-
output: error instanceof Error ? error.message : String(error)
|
|
22446
|
-
};
|
|
22447
|
-
}
|
|
22448
|
-
}
|
|
22449
|
-
|
|
22450
23406
|
// src/profile-command.ts
|
|
22451
23407
|
function formatProfileValidationErrors(errors) {
|
|
22452
23408
|
return [
|
|
@@ -23350,7 +24306,7 @@ async function listServicesWithAuthCommand(input, dependencies) {
|
|
|
23350
24306
|
}
|
|
23351
24307
|
|
|
23352
24308
|
// src/verify-command.ts
|
|
23353
|
-
var
|
|
24309
|
+
var import_promises14 = require("node:fs/promises");
|
|
23354
24310
|
var import_node_path15 = require("node:path");
|
|
23355
24311
|
function resolveOverallStatus2(checks) {
|
|
23356
24312
|
if (checks.some((check) => check.status === "error" || check.status === "missing")) {
|
|
@@ -23423,7 +24379,7 @@ function buildCloudSuggestedActions(status, incidentId, mode = "passive_recent_i
|
|
|
23423
24379
|
];
|
|
23424
24380
|
}
|
|
23425
24381
|
return [
|
|
23426
|
-
"Run debugbundle login to create ~/.debugbundle/auth.json before verifying cloud traffic.",
|
|
24382
|
+
"Run debugbundle login to choose an auth flow, or use debugbundle login --github, debugbundle login --github-device, or debugbundle login <dbundle_mem_...> to create ~/.debugbundle/auth.json before verifying cloud traffic.",
|
|
23427
24383
|
"Generate a live cloud request, then re-run debugbundle verify cloud with the correct project and service filters."
|
|
23428
24384
|
];
|
|
23429
24385
|
}
|
|
@@ -23480,14 +24436,14 @@ function localFailureStepName(checks) {
|
|
|
23480
24436
|
function cloudVerificationRunId(now) {
|
|
23481
24437
|
return now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
23482
24438
|
}
|
|
23483
|
-
function
|
|
24439
|
+
function requestFailureReason() {
|
|
23484
24440
|
const incidentReason = deriveIncidentReasonFromSignal({
|
|
23485
24441
|
event_type: "request_event",
|
|
23486
24442
|
event_class: "incident_signal",
|
|
23487
24443
|
response_status: 503
|
|
23488
24444
|
});
|
|
23489
24445
|
if (incidentReason === null) {
|
|
23490
|
-
throw new Error("
|
|
24446
|
+
throw new Error("request_failure_reason_unavailable");
|
|
23491
24447
|
}
|
|
23492
24448
|
return incidentReason;
|
|
23493
24449
|
}
|
|
@@ -23608,14 +24564,14 @@ async function writeVerificationEventBatch(input, dependencies) {
|
|
|
23608
24564
|
async function verifyLocalCommand(input, dependencies = {}) {
|
|
23609
24565
|
const cwd = dependencies.cwd ?? (() => process.cwd());
|
|
23610
24566
|
const mkdir = dependencies.mkdir ?? (async (path, options) => {
|
|
23611
|
-
await (0,
|
|
24567
|
+
await (0, import_promises14.mkdir)(path, options);
|
|
23612
24568
|
});
|
|
23613
24569
|
const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
23614
24570
|
const processLocal = dependencies.processCommand ?? processCommand;
|
|
23615
24571
|
const readVerifiedLocalState = dependencies.readLocalState ?? readLocalState;
|
|
23616
24572
|
const readVerifiedLocalBundle = dependencies.getLocalBundle ?? getLocalBundle;
|
|
23617
|
-
const rename = dependencies.rename ?? (async (sourcePath, destinationPath) => (0,
|
|
23618
|
-
const writeFile = dependencies.writeFile ?? (async (path, content) => (0,
|
|
24573
|
+
const rename = dependencies.rename ?? (async (sourcePath, destinationPath) => (0, import_promises14.rename)(sourcePath, destinationPath));
|
|
24574
|
+
const writeFile = dependencies.writeFile ?? (async (path, content) => (0, import_promises14.writeFile)(path, content, "utf8"));
|
|
23619
24575
|
const checks = [];
|
|
23620
24576
|
function finalize(exitCode, errors, incidentId) {
|
|
23621
24577
|
return formatResult(input, exitCode, checks, errors, incidentId);
|
|
@@ -23636,7 +24592,7 @@ async function verifyLocalCommand(input, dependencies = {}) {
|
|
|
23636
24592
|
});
|
|
23637
24593
|
try {
|
|
23638
24594
|
const rootDirectory = cwd();
|
|
23639
|
-
const profile = await readProfile(rootDirectory, async (path) => (0,
|
|
24595
|
+
const profile = await readProfile(rootDirectory, async (path) => (0, import_promises14.readFile)(path, "utf8"));
|
|
23640
24596
|
const projectId = buildProjectId(profile);
|
|
23641
24597
|
const verificationNow = now();
|
|
23642
24598
|
const runId = verificationNow.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
@@ -23762,7 +24718,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
|
|
|
23762
24718
|
const verification = {
|
|
23763
24719
|
mode: "active_5xx",
|
|
23764
24720
|
bundle_status: "unknown",
|
|
23765
|
-
classification_reason:
|
|
24721
|
+
classification_reason: requestFailureReason()
|
|
23766
24722
|
};
|
|
23767
24723
|
const errors = [];
|
|
23768
24724
|
let exitCode = 0;
|
|
@@ -23814,7 +24770,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
|
|
|
23814
24770
|
if (candidate !== void 0) {
|
|
23815
24771
|
incidentId = candidate.incident_id;
|
|
23816
24772
|
verification.incident_id = candidate.incident_id;
|
|
23817
|
-
verification.classification_reason = candidate.incident_reason ??
|
|
24773
|
+
verification.classification_reason = candidate.incident_reason ?? requestFailureReason();
|
|
23818
24774
|
break;
|
|
23819
24775
|
}
|
|
23820
24776
|
if (attempt < pollAttempts) {
|
|
@@ -24089,13 +25045,17 @@ var CLI_USAGE_LINES = [
|
|
|
24089
25045
|
" debugbundle connect [--auth-file <path>] [--json]",
|
|
24090
25046
|
" debugbundle ingest <file> --format <debugbundle-ndjson|php-error|apache-error> [--json]",
|
|
24091
25047
|
" debugbundle watch [--cloud] --log <file> --format <debugbundle-ndjson|php-error|apache-error> [--json]",
|
|
24092
|
-
" debugbundle process [--json]",
|
|
25048
|
+
" debugbundle process [--preset <minimal|balanced|investigative>] [--json]",
|
|
24093
25049
|
" debugbundle clean [--events] [--bundles] [--all] [--older-than <Nd>] [--json]",
|
|
24094
25050
|
" debugbundle validate [--fix] [--json]",
|
|
24095
25051
|
" debugbundle verify local [--json]",
|
|
24096
25052
|
" debugbundle verify cloud --project-id <id> [--trigger-5xx] [--service <name>] [--environment <name>] [--max-age-minutes <n>] [--auth-file <path>] [--json]",
|
|
24097
25053
|
" debugbundle smoke --project-id <id> [--service <name>] [--environment <name>] [--max-age-minutes <n>] [--auth-file <path>] [--json]",
|
|
25054
|
+
" debugbundle login [--base-url <url>] [--auth-file <path>] [--json]",
|
|
24098
25055
|
" debugbundle login <member-token> [--base-url <url>] [--auth-file <path>] [--json]",
|
|
25056
|
+
" debugbundle login --github [--label <label>] [--base-url <url>] [--auth-file <path>] [--json]",
|
|
25057
|
+
" debugbundle login --github-cli [--label <label>] [--base-url <url>] [--auth-file <path>] [--json]",
|
|
25058
|
+
" debugbundle login --github-device [--label <label>] [--base-url <url>] [--auth-file <path>] [--json]",
|
|
24099
25059
|
" debugbundle profile validate [--json]",
|
|
24100
25060
|
" debugbundle whoami [--auth-file <path>] [--json]",
|
|
24101
25061
|
" debugbundle incidents [--source <local|cloud>] [--project-id <id>] [--environment <name>] [--service <name>] [--status <status>] [--severity <severity>] [--cursor <cursor>] [--limit <n>] [--auth-file <path>] [--json]",
|
|
@@ -24132,7 +25092,7 @@ var CLI_USAGE_LINES = [
|
|
|
24132
25092
|
" debugbundle token member create --label <label> [--auth-file <path>] [--json]",
|
|
24133
25093
|
" debugbundle token member revoke <token-id> [--auth-file <path>] [--json]",
|
|
24134
25094
|
" debugbundle alert list --project-id <id> [--limit <n>] [--auth-file <path>] [--json]",
|
|
24135
|
-
" debugbundle alert create --project-id <id> --channel <channel> --condition <condition> [--service-id <id>] [--severity-min <level>]
|
|
25095
|
+
" debugbundle alert create --project-id <id> --channel <channel> --condition <condition> [--service-id <id>] [--severity-min <level>] --config-json <json> [--is-enabled <true|false>] [--auth-file <path>] [--json]",
|
|
24136
25096
|
" debugbundle alert update <alert-id> [--service-id <id|null>] [--channel <channel>] [--condition <condition>] [--severity-min <level|null>] [--config-json <json|null>] [--is-enabled <true|false>] [--auth-file <path>] [--json]",
|
|
24137
25097
|
" debugbundle alert delete <alert-id> [--auth-file <path>] [--json]",
|
|
24138
25098
|
" debugbundle webhook list --project-id <id> [--limit <n>] [--auth-file <path>] [--json]",
|
|
@@ -24163,7 +25123,7 @@ function formatUsage() {
|
|
|
24163
25123
|
}
|
|
24164
25124
|
|
|
24165
25125
|
// src/validate-command.ts
|
|
24166
|
-
var
|
|
25126
|
+
var import_promises15 = require("node:fs/promises");
|
|
24167
25127
|
var import_node_path16 = require("node:path");
|
|
24168
25128
|
var SUGGESTED_ACTIONS2 = [
|
|
24169
25129
|
"Run debugbundle setup if .debugbundle/profile.json is missing.",
|
|
@@ -24306,11 +25266,11 @@ ${managedSection}
|
|
|
24306
25266
|
async function validateCommand(input, dependencies = {}) {
|
|
24307
25267
|
const cwd = dependencies.cwd ?? (() => process.cwd());
|
|
24308
25268
|
const mkdir = dependencies.mkdir ?? (async (path, options) => {
|
|
24309
|
-
await (0,
|
|
25269
|
+
await (0, import_promises15.mkdir)(path, options);
|
|
24310
25270
|
});
|
|
24311
|
-
const readFile = dependencies.readFile ?? ((filePath) => (0,
|
|
24312
|
-
const stat = dependencies.stat ??
|
|
24313
|
-
const writeFile = dependencies.writeFile ?? (async (filePath, content) => (0,
|
|
25271
|
+
const readFile = dependencies.readFile ?? ((filePath) => (0, import_promises15.readFile)(filePath, "utf8"));
|
|
25272
|
+
const stat = dependencies.stat ?? import_promises15.stat;
|
|
25273
|
+
const writeFile = dependencies.writeFile ?? (async (filePath, content) => (0, import_promises15.writeFile)(filePath, content, "utf8"));
|
|
24314
25274
|
const rootDirectory = cwd();
|
|
24315
25275
|
const profileValidation = await validateProfile(rootDirectory, { readFile, stat });
|
|
24316
25276
|
const checks = [
|
|
@@ -24419,7 +25379,7 @@ function parseArgv(argv) {
|
|
|
24419
25379
|
positionals.push(token);
|
|
24420
25380
|
continue;
|
|
24421
25381
|
}
|
|
24422
|
-
if (token === "--fix" || token === "--json" || token === "--check-relay" || token === "--privacy" || token === "--help" || token === "--non-interactive" || token === "--local" || token === "--cloud" || token === "--events" || token === "--bundles" || token === "--all" || token === "--trigger-5xx") {
|
|
25382
|
+
if (token === "--fix" || token === "--json" || token === "--check-relay" || token === "--privacy" || token === "--help" || token === "--non-interactive" || token === "--local" || token === "--cloud" || token === "--events" || token === "--bundles" || token === "--all" || token === "--trigger-5xx" || token === "--github" || token === "--github-cli" || token === "--github-device") {
|
|
24423
25383
|
options.set(token.slice(2), true);
|
|
24424
25384
|
continue;
|
|
24425
25385
|
}
|
|
@@ -24660,7 +25620,8 @@ async function createAlertCommand(input, api) {
|
|
|
24660
25620
|
bearerToken: input.bearerToken,
|
|
24661
25621
|
projectId: input.projectId,
|
|
24662
25622
|
channel: input.channel,
|
|
24663
|
-
conditionType: input.conditionType
|
|
25623
|
+
conditionType: input.conditionType,
|
|
25624
|
+
config: input.config
|
|
24664
25625
|
};
|
|
24665
25626
|
if (input.serviceId !== void 0) {
|
|
24666
25627
|
requestInput.serviceId = input.serviceId;
|
|
@@ -24668,9 +25629,6 @@ async function createAlertCommand(input, api) {
|
|
|
24668
25629
|
if (input.severityMin !== void 0) {
|
|
24669
25630
|
requestInput.severityMin = input.severityMin;
|
|
24670
25631
|
}
|
|
24671
|
-
if (input.config !== void 0) {
|
|
24672
|
-
requestInput.config = input.config;
|
|
24673
|
-
}
|
|
24674
25632
|
if (input.isEnabled !== void 0) {
|
|
24675
25633
|
requestInput.isEnabled = input.isEnabled;
|
|
24676
25634
|
}
|
|
@@ -24692,7 +25650,8 @@ async function createAlertWithAuthCommand(input, dependencies) {
|
|
|
24692
25650
|
bearerToken: authState.bearer_token,
|
|
24693
25651
|
projectId: input.projectId,
|
|
24694
25652
|
channel: input.channel,
|
|
24695
|
-
conditionType: input.conditionType
|
|
25653
|
+
conditionType: input.conditionType,
|
|
25654
|
+
config: input.config
|
|
24696
25655
|
};
|
|
24697
25656
|
if (input.serviceId !== void 0) {
|
|
24698
25657
|
commandInput.serviceId = input.serviceId;
|
|
@@ -24700,9 +25659,6 @@ async function createAlertWithAuthCommand(input, dependencies) {
|
|
|
24700
25659
|
if (input.severityMin !== void 0) {
|
|
24701
25660
|
commandInput.severityMin = input.severityMin;
|
|
24702
25661
|
}
|
|
24703
|
-
if (input.config !== void 0) {
|
|
24704
|
-
commandInput.config = input.config;
|
|
24705
|
-
}
|
|
24706
25662
|
if (input.isEnabled !== void 0) {
|
|
24707
25663
|
commandInput.isEnabled = input.isEnabled;
|
|
24708
25664
|
}
|
|
@@ -27701,9 +28657,10 @@ async function handleAlertCommand(parsedArgv, dependencies) {
|
|
|
27701
28657
|
input.severityMin = severityMin;
|
|
27702
28658
|
}
|
|
27703
28659
|
const config = readJsonOption(parsedArgv, "config-json");
|
|
27704
|
-
if (config
|
|
27705
|
-
|
|
28660
|
+
if (config === void 0 || typeof config !== "object" || config === null) {
|
|
28661
|
+
throw new CliInputError("Missing required option --config-json.");
|
|
27706
28662
|
}
|
|
28663
|
+
input.config = config;
|
|
27707
28664
|
const isEnabled = readBooleanStringOption(parsedArgv, "is-enabled");
|
|
27708
28665
|
if (isEnabled !== void 0) {
|
|
27709
28666
|
input.isEnabled = isEnabled;
|
|
@@ -27992,10 +28949,17 @@ ${formatUsage()}`
|
|
|
27992
28949
|
});
|
|
27993
28950
|
}
|
|
27994
28951
|
if (command === "process") {
|
|
27995
|
-
expectNoUnknownOptions(parsedArgv, ["json"]);
|
|
28952
|
+
expectNoUnknownOptions(parsedArgv, ["json", "preset"]);
|
|
27996
28953
|
ensureNoExtraPositionals(parsedArgv, 1);
|
|
27997
28954
|
const json = readBooleanOption(parsedArgv, "json");
|
|
27998
|
-
|
|
28955
|
+
const preset = readStringOption(parsedArgv, "preset");
|
|
28956
|
+
if (preset !== void 0 && !CapturePresetSchema.safeParse(preset).success) {
|
|
28957
|
+
throw new CliInputError("Invalid value for --preset.");
|
|
28958
|
+
}
|
|
28959
|
+
return await (dependencies.processCommand ?? processCommand)({
|
|
28960
|
+
...json === true ? { json: true } : {},
|
|
28961
|
+
...preset !== void 0 ? { preset } : {}
|
|
28962
|
+
});
|
|
27999
28963
|
}
|
|
28000
28964
|
if (command === "clean") {
|
|
28001
28965
|
expectNoUnknownOptions(parsedArgv, ["events", "bundles", "all", "older-than", "json"]);
|
|
@@ -28082,15 +29046,30 @@ ${formatUsage()}`
|
|
|
28082
29046
|
return await (dependencies.smokeCommand ?? smokeCommand)(input);
|
|
28083
29047
|
}
|
|
28084
29048
|
if (command === "login") {
|
|
28085
|
-
expectNoUnknownOptions(parsedArgv, ["auth-file", "base-url", "json"]);
|
|
29049
|
+
expectNoUnknownOptions(parsedArgv, ["auth-file", "base-url", "json", "github", "github-cli", "github-device", "label"]);
|
|
28086
29050
|
ensureNoExtraPositionals(parsedArgv, 2);
|
|
28087
|
-
const
|
|
28088
|
-
|
|
28089
|
-
|
|
29051
|
+
const bearerToken = parsedArgv.positionals[1];
|
|
29052
|
+
const input = appendCommonAuthOptions(parsedArgv, {});
|
|
29053
|
+
if (bearerToken !== void 0) {
|
|
29054
|
+
input.bearerToken = bearerToken;
|
|
29055
|
+
}
|
|
28090
29056
|
const baseUrl = readStringOption(parsedArgv, "base-url");
|
|
28091
29057
|
if (baseUrl !== void 0) {
|
|
28092
29058
|
input.baseUrl = baseUrl;
|
|
28093
29059
|
}
|
|
29060
|
+
if (readBooleanOption(parsedArgv, "github") === true) {
|
|
29061
|
+
input.github = true;
|
|
29062
|
+
}
|
|
29063
|
+
if (readBooleanOption(parsedArgv, "github-cli") === true) {
|
|
29064
|
+
input.githubCli = true;
|
|
29065
|
+
}
|
|
29066
|
+
if (readBooleanOption(parsedArgv, "github-device") === true) {
|
|
29067
|
+
input.githubDevice = true;
|
|
29068
|
+
}
|
|
29069
|
+
const label = readStringOption(parsedArgv, "label");
|
|
29070
|
+
if (label !== void 0) {
|
|
29071
|
+
input.label = label;
|
|
29072
|
+
}
|
|
28094
29073
|
return await (dependencies.loginCommand ?? loginCommand)(input);
|
|
28095
29074
|
}
|
|
28096
29075
|
if (command === "whoami") {
|
|
@@ -28295,7 +29274,7 @@ ${formatUsage()}`
|
|
|
28295
29274
|
}
|
|
28296
29275
|
async function main(dependencies = {}) {
|
|
28297
29276
|
const argv = dependencies.argv ?? process.argv.slice(2);
|
|
28298
|
-
const
|
|
29277
|
+
const stdout2 = dependencies.stdout ?? ((text) => process.stdout.write(text));
|
|
28299
29278
|
const stderr = dependencies.stderr ?? ((text) => process.stderr.write(text));
|
|
28300
29279
|
const setExitCode = dependencies.setExitCode ?? ((code) => {
|
|
28301
29280
|
process.exitCode = code;
|
|
@@ -28303,7 +29282,7 @@ async function main(dependencies = {}) {
|
|
|
28303
29282
|
const result = await runCli(argv, dependencies);
|
|
28304
29283
|
if (result.output.length > 0) {
|
|
28305
29284
|
if (result.exitCode === 0) {
|
|
28306
|
-
|
|
29285
|
+
stdout2(`${result.output}
|
|
28307
29286
|
`);
|
|
28308
29287
|
} else {
|
|
28309
29288
|
stderr(`${result.output}
|