@robinmordasiewicz/f5xc-xcsh 2.0.21-2601100343 → 2.0.21-2601100619

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +827 -67
  2. package/package.json +6 -1
package/dist/index.js CHANGED
@@ -7941,7 +7941,7 @@ var require_react_reconciler_development = __commonJS({
7941
7941
  var ContextConsumer = 9;
7942
7942
  var ContextProvider = 10;
7943
7943
  var ForwardRef = 11;
7944
- var Profiler = 12;
7944
+ var Profiler2 = 12;
7945
7945
  var SuspenseComponent = 13;
7946
7946
  var MemoComponent = 14;
7947
7947
  var SimpleMemoComponent = 15;
@@ -8095,7 +8095,7 @@ var require_react_reconciler_development = __commonJS({
8095
8095
  return "Mode";
8096
8096
  case OffscreenComponent:
8097
8097
  return "Offscreen";
8098
- case Profiler:
8098
+ case Profiler2:
8099
8099
  return "Profiler";
8100
8100
  case ScopeComponent:
8101
8101
  return "Scope";
@@ -14477,7 +14477,7 @@ var require_react_reconciler_development = __commonJS({
14477
14477
  var root = parentFiber.stateNode;
14478
14478
  root.effectDuration += elapsedTime;
14479
14479
  return;
14480
- case Profiler:
14480
+ case Profiler2:
14481
14481
  var parentStateNode = parentFiber.stateNode;
14482
14482
  parentStateNode.effectDuration += elapsedTime;
14483
14483
  return;
@@ -14499,7 +14499,7 @@ var require_react_reconciler_development = __commonJS({
14499
14499
  root.passiveEffectDuration += elapsedTime;
14500
14500
  }
14501
14501
  return;
14502
- case Profiler:
14502
+ case Profiler2:
14503
14503
  var parentStateNode = parentFiber.stateNode;
14504
14504
  if (parentStateNode !== null) {
14505
14505
  parentStateNode.passiveEffectDuration += elapsedTime;
@@ -16964,7 +16964,7 @@ var require_react_reconciler_development = __commonJS({
16964
16964
  pushProvider(workInProgress2, context, newValue);
16965
16965
  break;
16966
16966
  }
16967
- case Profiler:
16967
+ case Profiler2:
16968
16968
  {
16969
16969
  var hasChildWork = includesSomeLane(renderLanes2, workInProgress2.childLanes);
16970
16970
  if (hasChildWork) {
@@ -17108,7 +17108,7 @@ var require_react_reconciler_development = __commonJS({
17108
17108
  return updateFragment(current2, workInProgress2, renderLanes2);
17109
17109
  case Mode:
17110
17110
  return updateMode(current2, workInProgress2, renderLanes2);
17111
- case Profiler:
17111
+ case Profiler2:
17112
17112
  return updateProfiler(current2, workInProgress2, renderLanes2);
17113
17113
  case ContextProvider:
17114
17114
  return updateContextProvider(current2, workInProgress2, renderLanes2);
@@ -17543,7 +17543,7 @@ var require_react_reconciler_development = __commonJS({
17543
17543
  case ForwardRef:
17544
17544
  case Fragment2:
17545
17545
  case Mode:
17546
- case Profiler:
17546
+ case Profiler2:
17547
17547
  case ContextConsumer:
17548
17548
  case MemoComponent:
17549
17549
  bubbleProperties(workInProgress2);
@@ -18383,7 +18383,7 @@ var require_react_reconciler_development = __commonJS({
18383
18383
  {
18384
18384
  if ((finishedWork.flags & Update) !== NoFlags) {
18385
18385
  switch (finishedWork.tag) {
18386
- case Profiler: {
18386
+ case Profiler2: {
18387
18387
  var passiveEffectDuration = finishedWork.stateNode.passiveEffectDuration;
18388
18388
  var _finishedWork$memoize = finishedWork.memoizedProps, id = _finishedWork$memoize.id, onPostCommit = _finishedWork$memoize.onPostCommit;
18389
18389
  var commitTime2 = getCommitTime();
@@ -18403,7 +18403,7 @@ var require_react_reconciler_development = __commonJS({
18403
18403
  var root = parentFiber.stateNode;
18404
18404
  root.passiveEffectDuration += passiveEffectDuration;
18405
18405
  break outer;
18406
- case Profiler:
18406
+ case Profiler2:
18407
18407
  var parentStateNode = parentFiber.stateNode;
18408
18408
  parentStateNode.passiveEffectDuration += passiveEffectDuration;
18409
18409
  break outer;
@@ -18536,7 +18536,7 @@ var require_react_reconciler_development = __commonJS({
18536
18536
  case HostPortal: {
18537
18537
  break;
18538
18538
  }
18539
- case Profiler: {
18539
+ case Profiler2: {
18540
18540
  {
18541
18541
  var _finishedWork$memoize2 = finishedWork.memoizedProps, onCommit = _finishedWork$memoize2.onCommit, onRender = _finishedWork$memoize2.onRender;
18542
18542
  var effectDuration = finishedWork.stateNode.effectDuration;
@@ -18562,7 +18562,7 @@ var require_react_reconciler_development = __commonJS({
18562
18562
  var root = parentFiber.stateNode;
18563
18563
  root.effectDuration += effectDuration;
18564
18564
  break outer;
18565
- case Profiler:
18565
+ case Profiler2:
18566
18566
  var parentStateNode = parentFiber.stateNode;
18567
18567
  parentStateNode.effectDuration += effectDuration;
18568
18568
  break outer;
@@ -22319,7 +22319,7 @@ var require_react_reconciler_development = __commonJS({
22319
22319
  error('Profiler must specify an "id" of type `string` as a prop. Received the type `%s` instead.', typeof pendingProps.id);
22320
22320
  }
22321
22321
  }
22322
- var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
22322
+ var fiber = createFiber(Profiler2, pendingProps, key, mode | ProfileMode);
22323
22323
  fiber.elementType = REACT_PROFILER_TYPE;
22324
22324
  fiber.lanes = lanes;
22325
22325
  {
@@ -40058,6 +40058,582 @@ var require_cli_spinners = __commonJS({
40058
40058
  }
40059
40059
  });
40060
40060
 
40061
+ // src/profiling/profiler.ts
40062
+ var DEFAULT_THRESHOLDS = {
40063
+ slowPhaseMs: 100,
40064
+ slowNetworkMs: 500,
40065
+ memorySpikeMB: 50
40066
+ };
40067
+ function getProfileLevel() {
40068
+ const level = process.env.XCSH_PROFILE_LEVEL;
40069
+ if (level === "basic" || level === "detailed" || level === "full") {
40070
+ return level;
40071
+ }
40072
+ if (process.env.XCSH_PROFILE === "true") {
40073
+ return "basic";
40074
+ }
40075
+ return "none";
40076
+ }
40077
+ var Profiler = class {
40078
+ config;
40079
+ spans = /* @__PURE__ */ new Map();
40080
+ networkSpans = /* @__PURE__ */ new Map();
40081
+ memorySnapshots = [];
40082
+ startTime;
40083
+ spanCounter = 0;
40084
+ rootSpanId = null;
40085
+ constructor(config) {
40086
+ const level = getProfileLevel();
40087
+ this.config = {
40088
+ enabled: level !== "none",
40089
+ level,
40090
+ thresholds: {
40091
+ ...DEFAULT_THRESHOLDS,
40092
+ ...config?.thresholds
40093
+ }
40094
+ };
40095
+ this.startTime = Date.now();
40096
+ if (this.isEnabled()) {
40097
+ this.memorySnapshot("profiler_init");
40098
+ }
40099
+ }
40100
+ /**
40101
+ * Check if profiling is enabled
40102
+ */
40103
+ isEnabled() {
40104
+ return this.config.enabled;
40105
+ }
40106
+ /**
40107
+ * Get current profiling level
40108
+ */
40109
+ getLevel() {
40110
+ return this.config.level;
40111
+ }
40112
+ /**
40113
+ * Get elapsed time since profiler start
40114
+ */
40115
+ elapsed() {
40116
+ return Date.now() - this.startTime;
40117
+ }
40118
+ /**
40119
+ * Generate unique span ID
40120
+ */
40121
+ generateSpanId() {
40122
+ return `span_${++this.spanCounter}`;
40123
+ }
40124
+ /**
40125
+ * Start a new profiling span
40126
+ */
40127
+ startSpan(name, label, parentId) {
40128
+ if (!this.isEnabled()) return "";
40129
+ const id = this.generateSpanId();
40130
+ const span = {
40131
+ id,
40132
+ name,
40133
+ label: label ?? name,
40134
+ parentId: parentId ?? void 0,
40135
+ startTime: this.elapsed(),
40136
+ endTime: void 0,
40137
+ duration: void 0,
40138
+ metadata: {},
40139
+ isNetwork: void 0,
40140
+ children: []
40141
+ };
40142
+ this.spans.set(id, span);
40143
+ if (!parentId && !this.rootSpanId) {
40144
+ this.rootSpanId = id;
40145
+ }
40146
+ if (parentId) {
40147
+ const parent = this.spans.get(parentId);
40148
+ if (parent) {
40149
+ parent.children.push(span);
40150
+ }
40151
+ }
40152
+ if (this.config.level === "basic") {
40153
+ console.error(`[PROFILE] ${name}:start: ${span.startTime}ms`);
40154
+ }
40155
+ return id;
40156
+ }
40157
+ /**
40158
+ * End a profiling span
40159
+ */
40160
+ endSpan(spanId, metadata) {
40161
+ if (!this.isEnabled() || !spanId) return;
40162
+ const span = this.spans.get(spanId);
40163
+ if (!span) return;
40164
+ span.endTime = this.elapsed();
40165
+ span.duration = span.endTime - span.startTime;
40166
+ if (metadata) {
40167
+ span.metadata = { ...span.metadata, ...metadata };
40168
+ }
40169
+ if (this.config.level === "basic") {
40170
+ console.error(
40171
+ `[PROFILE] ${span.name}:end: ${span.endTime}ms (${span.duration}ms)`
40172
+ );
40173
+ }
40174
+ }
40175
+ /**
40176
+ * Create a checkpoint within current context (simple timing marker)
40177
+ */
40178
+ checkpoint(name) {
40179
+ if (!this.isEnabled()) return;
40180
+ const elapsed = this.elapsed();
40181
+ if (this.config.level === "basic") {
40182
+ console.error(`[PROFILE] ${name}: ${elapsed}ms`);
40183
+ }
40184
+ }
40185
+ /**
40186
+ * Start a network span with additional request details
40187
+ */
40188
+ startNetworkSpan(url, method, parentId) {
40189
+ if (!this.isEnabled()) return "";
40190
+ const id = this.generateSpanId();
40191
+ const span = {
40192
+ id,
40193
+ name: `network:${method}`,
40194
+ label: `${method} ${this.truncateUrl(url)}`,
40195
+ parentId: parentId ?? void 0,
40196
+ startTime: this.elapsed(),
40197
+ endTime: void 0,
40198
+ duration: void 0,
40199
+ metadata: {},
40200
+ children: [],
40201
+ isNetwork: true,
40202
+ url,
40203
+ method,
40204
+ statusCode: void 0,
40205
+ requestSize: void 0,
40206
+ responseSize: void 0,
40207
+ retryCount: 0,
40208
+ error: void 0
40209
+ };
40210
+ this.networkSpans.set(id, span);
40211
+ this.spans.set(id, span);
40212
+ if (parentId) {
40213
+ const parent = this.spans.get(parentId);
40214
+ if (parent) {
40215
+ parent.children.push(span);
40216
+ }
40217
+ }
40218
+ if (this.config.level !== "none") {
40219
+ console.error(
40220
+ `[PROFILE] network:${method}:start: ${span.startTime}ms - ${this.truncateUrl(url)}`
40221
+ );
40222
+ }
40223
+ return id;
40224
+ }
40225
+ /**
40226
+ * End a network span with response details
40227
+ */
40228
+ endNetworkSpan(spanId, details) {
40229
+ if (!this.isEnabled() || !spanId) return;
40230
+ const span = this.networkSpans.get(spanId);
40231
+ if (!span) return;
40232
+ span.endTime = this.elapsed();
40233
+ span.duration = span.endTime - span.startTime;
40234
+ span.statusCode = details.statusCode;
40235
+ span.responseSize = details.responseSize;
40236
+ span.retryCount = details.retryCount ?? 0;
40237
+ span.error = details.error;
40238
+ if (this.config.level !== "none") {
40239
+ const status = details.error ? `ERROR: ${details.error}` : `${details.statusCode ?? "?"}`;
40240
+ console.error(
40241
+ `[PROFILE] network:end: ${span.endTime}ms (${span.duration}ms) - ${status}`
40242
+ );
40243
+ }
40244
+ }
40245
+ /**
40246
+ * Take a memory snapshot
40247
+ */
40248
+ memorySnapshot(label) {
40249
+ if (!this.isEnabled()) return;
40250
+ if (this.config.level === "basic") return;
40251
+ const memory = process.memoryUsage();
40252
+ const snapshot = {
40253
+ timestamp: this.elapsed(),
40254
+ label,
40255
+ heapUsedMB: Math.round(memory.heapUsed / 1024 / 1024 * 100) / 100,
40256
+ heapTotalMB: Math.round(memory.heapTotal / 1024 / 1024 * 100) / 100,
40257
+ externalMB: Math.round(memory.external / 1024 / 1024 * 100) / 100,
40258
+ rssMB: Math.round(memory.rss / 1024 / 1024 * 100) / 100
40259
+ };
40260
+ this.memorySnapshots.push(snapshot);
40261
+ if (this.config.level === "detailed" || this.config.level === "full") {
40262
+ console.error(
40263
+ `[PROFILE] memory:${label}: heap=${snapshot.heapUsedMB}MB rss=${snapshot.rssMB}MB`
40264
+ );
40265
+ }
40266
+ }
40267
+ /**
40268
+ * Identify bottlenecks based on configured thresholds
40269
+ */
40270
+ identifyBottlenecks() {
40271
+ const bottlenecks = [];
40272
+ for (const span of this.spans.values()) {
40273
+ if (!span.duration) continue;
40274
+ const isNetwork = this.networkSpans.has(span.id);
40275
+ const threshold = isNetwork ? this.config.thresholds.slowNetworkMs : this.config.thresholds.slowPhaseMs;
40276
+ if (span.duration > threshold) {
40277
+ const suggestion = isNetwork ? "Check network connectivity or API server status" : "Consider lazy loading or parallelizing this operation";
40278
+ bottlenecks.push({
40279
+ spanId: span.id,
40280
+ spanName: span.name,
40281
+ duration: span.duration,
40282
+ threshold,
40283
+ reason: `Duration ${span.duration}ms exceeds threshold ${threshold}ms`,
40284
+ suggestion
40285
+ });
40286
+ }
40287
+ }
40288
+ if (this.memorySnapshots.length >= 2) {
40289
+ const first = this.memorySnapshots[0];
40290
+ const peak = Math.max(...this.memorySnapshots.map((s) => s.rssMB));
40291
+ const spike = peak - (first?.rssMB ?? 0);
40292
+ if (spike > this.config.thresholds.memorySpikeMB) {
40293
+ bottlenecks.push({
40294
+ spanId: "memory",
40295
+ spanName: "memory_spike",
40296
+ duration: spike,
40297
+ threshold: this.config.thresholds.memorySpikeMB,
40298
+ reason: `Memory increased by ${spike.toFixed(1)}MB`,
40299
+ suggestion: "Review large object allocations during startup"
40300
+ });
40301
+ }
40302
+ }
40303
+ return bottlenecks.sort((a, b) => b.duration - a.duration);
40304
+ }
40305
+ /**
40306
+ * Get all root-level spans (no parent)
40307
+ */
40308
+ getRootSpans() {
40309
+ return Array.from(this.spans.values()).filter((span) => !span.parentId);
40310
+ }
40311
+ /**
40312
+ * Generate complete profile report
40313
+ */
40314
+ generateReport() {
40315
+ const spans = this.getRootSpans();
40316
+ const networkSpans = Array.from(this.networkSpans.values());
40317
+ const bottlenecks = this.identifyBottlenecks();
40318
+ const memoryStart = this.memorySnapshots[0]?.rssMB ?? 0;
40319
+ const memoryEnd = this.memorySnapshots[this.memorySnapshots.length - 1]?.rssMB ?? 0;
40320
+ const memoryPeak = Math.max(
40321
+ ...this.memorySnapshots.map((s) => s.rssMB),
40322
+ 0
40323
+ );
40324
+ return {
40325
+ startupTimeMs: this.elapsed(),
40326
+ spans,
40327
+ networkSpans,
40328
+ memorySnapshots: this.memorySnapshots,
40329
+ memoryPeakMB: memoryPeak,
40330
+ memoryDeltaMB: memoryEnd - memoryStart,
40331
+ bottlenecks,
40332
+ level: this.config.level
40333
+ };
40334
+ }
40335
+ /**
40336
+ * Truncate URL for display
40337
+ */
40338
+ truncateUrl(url) {
40339
+ try {
40340
+ const parsed = new URL(url);
40341
+ return parsed.pathname.slice(0, 40) + (parsed.pathname.length > 40 ? "..." : "");
40342
+ } catch {
40343
+ return url.slice(0, 40);
40344
+ }
40345
+ }
40346
+ };
40347
+ var globalProfiler = null;
40348
+ function getProfiler() {
40349
+ if (!globalProfiler) {
40350
+ globalProfiler = new Profiler();
40351
+ }
40352
+ return globalProfiler;
40353
+ }
40354
+ function initProfiler(config) {
40355
+ globalProfiler = new Profiler(config);
40356
+ return globalProfiler;
40357
+ }
40358
+
40359
+ // src/profiling/reporter.ts
40360
+ function formatDuration(ms) {
40361
+ if (ms < 1e3) {
40362
+ return `${Math.round(ms)}ms`;
40363
+ }
40364
+ return `${(ms / 1e3).toFixed(2)}s`;
40365
+ }
40366
+ function formatSpanText(span, indent = 0, totalTime) {
40367
+ const lines = [];
40368
+ const prefix = " ".repeat(indent);
40369
+ const duration = span.duration ?? 0;
40370
+ const percentage = totalTime > 0 ? (duration / totalTime * 100).toFixed(1) : "0";
40371
+ const networkTag = span.isNetwork ? " [N]" : "";
40372
+ const warning = duration > 500 ? " !!" : duration > 100 ? " !" : "";
40373
+ lines.push(
40374
+ `${prefix}${span.label}${networkTag}: ${formatDuration(duration)} (${percentage}%)${warning}`
40375
+ );
40376
+ if (Object.keys(span.metadata).length > 0) {
40377
+ const metaStr = Object.entries(span.metadata).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
40378
+ lines.push(`${prefix} metadata: ${metaStr}`);
40379
+ }
40380
+ for (const child of span.children) {
40381
+ lines.push(...formatSpanText(child, indent + 1, totalTime));
40382
+ }
40383
+ return lines;
40384
+ }
40385
+ function formatBottlenecksText(bottlenecks) {
40386
+ if (bottlenecks.length === 0) {
40387
+ return [" No bottlenecks detected"];
40388
+ }
40389
+ const lines = [];
40390
+ for (let i = 0; i < bottlenecks.length; i++) {
40391
+ const b = bottlenecks[i];
40392
+ if (!b) continue;
40393
+ lines.push(` ${i + 1}. ${b.spanName}: ${formatDuration(b.duration)}`);
40394
+ lines.push(` Threshold: ${formatDuration(b.threshold)}`);
40395
+ lines.push(` Suggestion: ${b.suggestion}`);
40396
+ }
40397
+ return lines;
40398
+ }
40399
+ function formatMemoryText(snapshots) {
40400
+ if (snapshots.length === 0) {
40401
+ return [" No memory snapshots"];
40402
+ }
40403
+ const first = snapshots[0];
40404
+ const last = snapshots[snapshots.length - 1];
40405
+ const peak = Math.max(...snapshots.map((s) => s.rssMB));
40406
+ const lines = [];
40407
+ lines.push(
40408
+ ` Start: ${first?.rssMB.toFixed(1)}MB -> Peak: ${peak.toFixed(1)}MB -> End: ${last?.rssMB.toFixed(1)}MB`
40409
+ );
40410
+ lines.push(
40411
+ ` Delta: ${((last?.rssMB ?? 0) - (first?.rssMB ?? 0)).toFixed(1)}MB`
40412
+ );
40413
+ return lines;
40414
+ }
40415
+ function formatReportText(report) {
40416
+ const lines = [];
40417
+ lines.push("=".repeat(60));
40418
+ lines.push(`XCSH Startup Profile Report`);
40419
+ lines.push(`Total Time: ${formatDuration(report.startupTimeMs)}`);
40420
+ lines.push(`Profile Level: ${report.level}`);
40421
+ lines.push("=".repeat(60));
40422
+ lines.push("");
40423
+ lines.push("PHASES:");
40424
+ lines.push("-".repeat(40));
40425
+ for (const span of report.spans) {
40426
+ lines.push(...formatSpanText(span, 0, report.startupTimeMs));
40427
+ }
40428
+ if (report.networkSpans.length > 0) {
40429
+ lines.push("");
40430
+ lines.push("NETWORK CALLS:");
40431
+ lines.push("-".repeat(40));
40432
+ for (const span of report.networkSpans) {
40433
+ const status = span.error ? `ERROR: ${span.error}` : `${span.statusCode ?? "?"}`;
40434
+ const retries = span.retryCount > 0 ? ` (${span.retryCount} retries)` : "";
40435
+ lines.push(
40436
+ ` ${span.method} ${span.url.slice(0, 50)}: ${formatDuration(span.duration ?? 0)} - ${status}${retries}`
40437
+ );
40438
+ }
40439
+ }
40440
+ if (report.memorySnapshots.length > 0) {
40441
+ lines.push("");
40442
+ lines.push("MEMORY:");
40443
+ lines.push("-".repeat(40));
40444
+ lines.push(...formatMemoryText(report.memorySnapshots));
40445
+ }
40446
+ if (report.bottlenecks.length > 0) {
40447
+ lines.push("");
40448
+ lines.push("BOTTLENECKS:");
40449
+ lines.push("-".repeat(40));
40450
+ lines.push(...formatBottlenecksText(report.bottlenecks));
40451
+ }
40452
+ lines.push("");
40453
+ lines.push("=".repeat(60));
40454
+ lines.push(
40455
+ "[N] = Network call !! = Critical (>500ms) ! = Warning (>100ms)"
40456
+ );
40457
+ lines.push("=".repeat(60));
40458
+ return lines.join("\n");
40459
+ }
40460
+
40461
+ // src/profiling/waterfall.ts
40462
+ var BOX_CHARS = {
40463
+ topLeft: "\u250C",
40464
+ topRight: "\u2510",
40465
+ bottomLeft: "\u2514",
40466
+ bottomRight: "\u2518",
40467
+ horizontal: "\u2500",
40468
+ vertical: "\u2502",
40469
+ cross: "\u253C",
40470
+ teeDown: "\u252C",
40471
+ teeUp: "\u2534",
40472
+ teeRight: "\u251C",
40473
+ teeLeft: "\u2524"
40474
+ };
40475
+ var TREE_CHARS = {
40476
+ branch: "\u251C",
40477
+ last: "\u2514",
40478
+ vertical: "\u2502",
40479
+ horizontal: "\u2500"
40480
+ };
40481
+ function flattenSpans(spans, depth = 0, bottleneckIds) {
40482
+ const result = [];
40483
+ for (let i = 0; i < spans.length; i++) {
40484
+ const span = spans[i];
40485
+ if (!span) continue;
40486
+ const isLast = i === spans.length - 1;
40487
+ result.push({
40488
+ name: span.name,
40489
+ label: span.label,
40490
+ startTime: span.startTime,
40491
+ duration: span.duration ?? 0,
40492
+ depth,
40493
+ isNetwork: span.isNetwork ?? false,
40494
+ isBottleneck: bottleneckIds.has(span.id),
40495
+ isLast
40496
+ });
40497
+ if (span.children.length > 0) {
40498
+ result.push(
40499
+ ...flattenSpans(span.children, depth + 1, bottleneckIds)
40500
+ );
40501
+ }
40502
+ }
40503
+ return result;
40504
+ }
40505
+ function createTreePrefix(depth, isLast) {
40506
+ if (depth === 0) return "";
40507
+ const indent = " ".repeat(depth - 1);
40508
+ const branch = isLast ? TREE_CHARS.last : TREE_CHARS.branch;
40509
+ return indent + branch + TREE_CHARS.horizontal;
40510
+ }
40511
+ function renderBar(startTime, duration, totalTime, barWidth) {
40512
+ if (totalTime === 0) return " ".repeat(barWidth);
40513
+ const startPos = Math.floor(startTime / totalTime * barWidth);
40514
+ const barLen = Math.max(1, Math.ceil(duration / totalTime * barWidth));
40515
+ const before = " ".repeat(Math.min(startPos, barWidth));
40516
+ const bar = "\u2588".repeat(Math.min(barLen, barWidth - startPos));
40517
+ const after = " ".repeat(Math.max(0, barWidth - startPos - barLen));
40518
+ return (before + bar + after).slice(0, barWidth);
40519
+ }
40520
+ function formatDuration2(ms) {
40521
+ if (ms < 1e3) {
40522
+ return `${Math.round(ms)}ms`;
40523
+ }
40524
+ return `${(ms / 1e3).toFixed(1)}s`;
40525
+ }
40526
+ function renderWaterfall(report) {
40527
+ const lines = [];
40528
+ const totalTime = report.startupTimeMs;
40529
+ const labelWidth = 28;
40530
+ const barWidth = 35;
40531
+ const timeWidth = 10;
40532
+ const totalWidth = labelWidth + barWidth + timeWidth + 6;
40533
+ const bottleneckIds = new Set(report.bottlenecks.map((b) => b.spanId));
40534
+ const flatSpans = flattenSpans(report.spans, 0, bottleneckIds);
40535
+ lines.push(
40536
+ BOX_CHARS.topLeft + BOX_CHARS.horizontal.repeat(totalWidth - 2) + BOX_CHARS.topRight
40537
+ );
40538
+ lines.push(
40539
+ BOX_CHARS.vertical + ` XCSH Startup Profile`.padEnd(totalWidth - 14) + `Total: ${formatDuration2(totalTime)}`.padStart(12) + BOX_CHARS.vertical
40540
+ );
40541
+ lines.push(
40542
+ BOX_CHARS.teeRight + BOX_CHARS.horizontal.repeat(totalWidth - 2) + BOX_CHARS.teeLeft
40543
+ );
40544
+ const headerPhase = "Phase".padEnd(labelWidth);
40545
+ const headerTimeline = "Timeline".padEnd(barWidth);
40546
+ const headerTime = "Time".padStart(timeWidth);
40547
+ lines.push(
40548
+ BOX_CHARS.vertical + ` ${headerPhase}${BOX_CHARS.vertical} ${headerTimeline}${BOX_CHARS.vertical}${headerTime} ` + BOX_CHARS.vertical
40549
+ );
40550
+ lines.push(
40551
+ BOX_CHARS.teeRight + BOX_CHARS.horizontal.repeat(labelWidth + 1) + BOX_CHARS.cross + BOX_CHARS.horizontal.repeat(barWidth + 1) + BOX_CHARS.cross + BOX_CHARS.horizontal.repeat(timeWidth + 1) + BOX_CHARS.teeLeft
40552
+ );
40553
+ for (const span of flatSpans) {
40554
+ const prefix = createTreePrefix(span.depth, span.isLast);
40555
+ const networkTag = span.isNetwork ? " [N]" : "";
40556
+ const label = (prefix + span.label + networkTag).slice(0, labelWidth).padEnd(labelWidth);
40557
+ const bar = renderBar(
40558
+ span.startTime,
40559
+ span.duration,
40560
+ totalTime,
40561
+ barWidth
40562
+ );
40563
+ const time = formatDuration2(span.duration).padStart(timeWidth - 2);
40564
+ const warning = span.isBottleneck ? " !!" : span.duration > 100 ? " !" : " ";
40565
+ lines.push(
40566
+ BOX_CHARS.vertical + ` ${label}${BOX_CHARS.vertical} ${bar}${BOX_CHARS.vertical}${time}${warning}` + BOX_CHARS.vertical
40567
+ );
40568
+ }
40569
+ lines.push(
40570
+ BOX_CHARS.bottomLeft + BOX_CHARS.horizontal.repeat(totalWidth - 2) + BOX_CHARS.bottomRight
40571
+ );
40572
+ lines.push(
40573
+ "[N] = Network call !! = Bottleneck (>500ms) ! = Slow (>100ms)"
40574
+ );
40575
+ if (report.memorySnapshots.length > 0) {
40576
+ const first = report.memorySnapshots[0];
40577
+ const last = report.memorySnapshots[report.memorySnapshots.length - 1];
40578
+ lines.push("");
40579
+ lines.push(
40580
+ `Memory: ${first?.rssMB.toFixed(1)}MB -> Peak ${report.memoryPeakMB.toFixed(1)}MB -> ${last?.rssMB.toFixed(1)}MB (${report.memoryDeltaMB >= 0 ? "+" : ""}${report.memoryDeltaMB.toFixed(1)}MB)`
40581
+ );
40582
+ }
40583
+ if (report.bottlenecks.length > 0) {
40584
+ lines.push("");
40585
+ lines.push("Bottlenecks Identified:");
40586
+ for (let i = 0; i < Math.min(report.bottlenecks.length, 3); i++) {
40587
+ const b = report.bottlenecks[i];
40588
+ if (!b) continue;
40589
+ lines.push(
40590
+ ` ${i + 1}. ${b.spanName}: ${formatDuration2(b.duration)} - ${b.suggestion}`
40591
+ );
40592
+ }
40593
+ }
40594
+ return lines.join("\n");
40595
+ }
40596
+ function renderCompactWaterfall(report) {
40597
+ const lines = [];
40598
+ const totalTime = report.startupTimeMs;
40599
+ lines.push(`
40600
+ [PROFILE] Startup: ${formatDuration2(totalTime)}`);
40601
+ for (const span of report.spans) {
40602
+ const duration = span.duration ?? 0;
40603
+ const percentage = (duration / totalTime * 100).toFixed(0);
40604
+ const bar = "\u2588".repeat(
40605
+ Math.min(20, Math.ceil(duration / totalTime * 20))
40606
+ );
40607
+ lines.push(
40608
+ ` ${span.label.padEnd(20)} ${bar.padEnd(20)} ${formatDuration2(duration).padStart(8)} (${percentage}%)`
40609
+ );
40610
+ }
40611
+ return lines.join("\n");
40612
+ }
40613
+
40614
+ // src/profiling/index.ts
40615
+ function printProfileReport() {
40616
+ const profiler2 = getProfiler();
40617
+ if (!profiler2.isEnabled()) return;
40618
+ const report = profiler2.generateReport();
40619
+ const level = profiler2.getLevel();
40620
+ let output;
40621
+ switch (level) {
40622
+ case "full":
40623
+ output = renderWaterfall(report);
40624
+ break;
40625
+ case "detailed":
40626
+ output = formatReportText(report);
40627
+ break;
40628
+ case "basic":
40629
+ output = renderCompactWaterfall(report);
40630
+ break;
40631
+ default:
40632
+ return;
40633
+ }
40634
+ console.error("\n" + output);
40635
+ }
40636
+
40061
40637
  // node_modules/ink/build/render.js
40062
40638
  import { Stream } from "stream";
40063
40639
  import process12 from "process";
@@ -48639,8 +49215,8 @@ function getLogoModeFromEnv(envPrefix) {
48639
49215
  var CLI_NAME = "xcsh";
48640
49216
  var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
48641
49217
  function getVersion() {
48642
- if ("v2.0.21-2601100343") {
48643
- return "v2.0.21-2601100343";
49218
+ if ("v2.0.21-2601100619") {
49219
+ return "v2.0.21-2601100619";
48644
49220
  }
48645
49221
  if (process.env.XCSH_VERSION) {
48646
49222
  return process.env.XCSH_VERSION;
@@ -49040,6 +49616,9 @@ var DEFAULT_RETRY_CONFIG = {
49040
49616
  backoffMultiplier: 2,
49041
49617
  jitter: true
49042
49618
  };
49619
+ var STARTUP_TIMEOUT = 3e3;
49620
+ var STARTUP_MAX_RETRIES = 0;
49621
+ var CONNECTIVITY_TIMEOUT = 2e3;
49043
49622
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([
49044
49623
  408,
49045
49624
  // Request Timeout
@@ -49095,18 +49674,33 @@ var APIClient = class {
49095
49674
  /**
49096
49675
  * Validate the API token by making a lightweight API call.
49097
49676
  * Returns true if token is valid, false otherwise.
49677
+ *
49678
+ * @param options.startupMode - Use aggressive timeouts for fast-fail during startup
49098
49679
  */
49099
- async validateToken() {
49680
+ async validateToken(options) {
49681
+ const startupMode = options?.startupMode ?? false;
49100
49682
  if (!this.apiToken) {
49101
49683
  this._isValidated = false;
49102
49684
  this._validationError = "No API token configured";
49103
49685
  return { valid: false, error: this._validationError };
49104
49686
  }
49105
49687
  if (this.debug) {
49106
- console.error(`DEBUG: Validating token against ${this.serverUrl}`);
49688
+ console.error(
49689
+ `DEBUG: Validating token against ${this.serverUrl}${startupMode ? " (startup mode)" : ""}`
49690
+ );
49107
49691
  }
49108
49692
  try {
49109
- await this.get("/api/web/namespaces");
49693
+ const requestOptions = {
49694
+ method: "GET",
49695
+ path: "/api/web/namespaces"
49696
+ };
49697
+ if (startupMode) {
49698
+ requestOptions.timeout = STARTUP_TIMEOUT;
49699
+ }
49700
+ await this.requestWithOptions(
49701
+ requestOptions,
49702
+ startupMode ? { maxRetries: STARTUP_MAX_RETRIES } : void 0
49703
+ );
49110
49704
  this._isValidated = true;
49111
49705
  this._validationError = null;
49112
49706
  return { valid: true };
@@ -49120,6 +49714,20 @@ var APIClient = class {
49120
49714
  this._isValidated = false;
49121
49715
  this._validationError = "Token lacks required permissions";
49122
49716
  return { valid: false, error: this._validationError };
49717
+ } else if (error.statusCode === 408 || error.statusCode === 0) {
49718
+ if (startupMode) {
49719
+ this._isValidated = false;
49720
+ this._validationError = "API endpoint unreachable - request timed out";
49721
+ return { valid: false, error: this._validationError };
49722
+ }
49723
+ if (this.debug) {
49724
+ console.error(
49725
+ `DEBUG: Validation timed out, assuming token is valid`
49726
+ );
49727
+ }
49728
+ this._isValidated = true;
49729
+ this._validationError = null;
49730
+ return { valid: true };
49123
49731
  } else {
49124
49732
  if (this.debug) {
49125
49733
  console.error(
@@ -49131,6 +49739,11 @@ var APIClient = class {
49131
49739
  return { valid: true };
49132
49740
  }
49133
49741
  } else {
49742
+ if (startupMode) {
49743
+ this._isValidated = false;
49744
+ this._validationError = `API endpoint unreachable - ${error instanceof Error ? error.message : "Unknown error"}`;
49745
+ return { valid: false, error: this._validationError };
49746
+ }
49134
49747
  if (this.debug) {
49135
49748
  console.error(
49136
49749
  `DEBUG: Validation error: ${error instanceof Error ? error.message : "Unknown"}, assuming token is valid`
@@ -49167,6 +49780,30 @@ var APIClient = class {
49167
49780
  getServerUrl() {
49168
49781
  return this.serverUrl;
49169
49782
  }
49783
+ /**
49784
+ * Check connectivity to the API server.
49785
+ * Uses a lightweight HEAD request with aggressive timeout for fast-fail.
49786
+ * This is used during startup to quickly detect if API is unreachable.
49787
+ */
49788
+ async checkConnectivity() {
49789
+ const start = Date.now();
49790
+ const controller = new AbortController();
49791
+ const timeoutId = setTimeout(
49792
+ () => controller.abort(),
49793
+ CONNECTIVITY_TIMEOUT
49794
+ );
49795
+ try {
49796
+ await fetch(this.serverUrl, {
49797
+ method: "HEAD",
49798
+ signal: controller.signal
49799
+ });
49800
+ clearTimeout(timeoutId);
49801
+ return { reachable: true, latencyMs: Date.now() - start };
49802
+ } catch {
49803
+ clearTimeout(timeoutId);
49804
+ return { reachable: false };
49805
+ }
49806
+ }
49170
49807
  /**
49171
49808
  * Build full URL from path and query parameters
49172
49809
  */
@@ -49271,11 +49908,25 @@ var APIClient = class {
49271
49908
  throw error;
49272
49909
  }
49273
49910
  }
49911
+ /**
49912
+ * Execute an HTTP request with retry logic and optional retry override
49913
+ */
49914
+ async requestWithOptions(options, retryOverride) {
49915
+ return this.requestInternal(options, retryOverride);
49916
+ }
49274
49917
  /**
49275
49918
  * Execute an HTTP request with retry logic
49276
49919
  */
49277
49920
  async request(options) {
49921
+ return this.requestInternal(options);
49922
+ }
49923
+ /**
49924
+ * Internal request implementation with optional retry override
49925
+ */
49926
+ async requestInternal(options, retryOverride) {
49278
49927
  const url = this.buildUrl(options.path, options.query);
49928
+ const profiler2 = getProfiler();
49929
+ const networkSpan = profiler2.startNetworkSpan(url, options.method);
49279
49930
  const headers = {
49280
49931
  "Content-Type": "application/json",
49281
49932
  Accept: "application/json",
@@ -49292,19 +49943,28 @@ var APIClient = class {
49292
49943
  }
49293
49944
  }
49294
49945
  let lastError;
49295
- for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
49946
+ let retryCount = 0;
49947
+ const maxRetries = retryOverride?.maxRetries ?? this.retryConfig.maxRetries;
49948
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
49296
49949
  try {
49297
- return await this.executeRequest(
49950
+ const result = await this.executeRequest(
49298
49951
  options,
49299
49952
  url,
49300
49953
  headers,
49301
49954
  body
49302
49955
  );
49956
+ profiler2.endNetworkSpan(networkSpan, {
49957
+ statusCode: result.statusCode,
49958
+ responseSize: JSON.stringify(result.data).length,
49959
+ retryCount
49960
+ });
49961
+ return result;
49303
49962
  } catch (error) {
49304
49963
  lastError = error instanceof Error ? error : new Error(String(error));
49305
49964
  const isRetryable = this.isRetryableError(error);
49306
- const hasRetriesLeft = attempt < this.retryConfig.maxRetries;
49965
+ const hasRetriesLeft = attempt < maxRetries;
49307
49966
  if (isRetryable && hasRetriesLeft) {
49967
+ retryCount++;
49308
49968
  const delay = calculateBackoffDelay(
49309
49969
  attempt,
49310
49970
  this.retryConfig
@@ -49312,12 +49972,17 @@ var APIClient = class {
49312
49972
  if (this.debug) {
49313
49973
  const statusInfo = error instanceof APIError ? ` (${error.statusCode})` : "";
49314
49974
  console.error(
49315
- `DEBUG: Request failed${statusInfo}, retrying in ${delay}ms (attempt ${attempt + 1}/${this.retryConfig.maxRetries})`
49975
+ `DEBUG: Request failed${statusInfo}, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`
49316
49976
  );
49317
49977
  }
49318
49978
  await sleep(delay);
49319
49979
  continue;
49320
49980
  }
49981
+ profiler2.endNetworkSpan(networkSpan, {
49982
+ statusCode: error instanceof APIError ? error.statusCode : 0,
49983
+ retryCount,
49984
+ error: lastError.message
49985
+ });
49321
49986
  throw error;
49322
49987
  }
49323
49988
  }
@@ -75275,6 +75940,8 @@ var REPLSession = class {
75275
75940
  // Fallback tracking for improved error messages
75276
75941
  _fallbackAttempted = false;
75277
75942
  _fallbackReason = null;
75943
+ // Connection status for graceful offline handling
75944
+ _connectionStatus = "unknown";
75278
75945
  constructor(config = {}) {
75279
75946
  this._namespace = config.namespace ?? this.getDefaultNamespace();
75280
75947
  this._contextPath = new ContextPath();
@@ -75304,48 +75971,91 @@ var REPLSession = class {
75304
75971
  }
75305
75972
  /**
75306
75973
  * Initialize the session (async operations)
75974
+ * Uses fast-fail connectivity check to avoid long waits when API is unreachable.
75307
75975
  */
75308
75976
  async initialize() {
75309
- const startTime = Date.now();
75310
- const profile = (label) => {
75311
- if (this._debug || process.env.XCSH_PROFILE === "true") {
75312
- console.error(
75313
- `[PROFILE] ${label}: ${Date.now() - startTime}ms`
75314
- );
75315
- }
75316
- };
75317
- profile("initialize:start");
75977
+ const profiler2 = getProfiler();
75978
+ const historySpan = profiler2.startSpan(
75979
+ "history_load",
75980
+ "History Loading"
75981
+ );
75318
75982
  try {
75319
75983
  this._history = await HistoryManager.create(
75320
75984
  getHistoryFilePath(),
75321
75985
  1e3
75322
75986
  );
75323
- profile("history:loaded");
75987
+ profiler2.endSpan(historySpan, { status: "loaded" });
75324
75988
  } catch (error) {
75325
75989
  console.error("Warning: could not initialize history:", error);
75326
75990
  this._history = new HistoryManager(getHistoryFilePath(), 1e3);
75327
- profile("history:fallback");
75991
+ profiler2.endSpan(historySpan, {
75992
+ status: "fallback",
75993
+ error: String(error)
75994
+ });
75328
75995
  }
75996
+ const profileSpan = profiler2.startSpan(
75997
+ "profile_load",
75998
+ "Profile Loading"
75999
+ );
75329
76000
  await this.loadActiveProfile();
75330
- profile("profile:loaded");
76001
+ profiler2.endSpan(profileSpan, { profileName: this._activeProfileName });
75331
76002
  if (this._apiClient?.isAuthenticated()) {
75332
- profile("token_validation:start");
76003
+ const connectivitySpan = profiler2.startSpan(
76004
+ "connectivity_check",
76005
+ "Connectivity Check"
76006
+ );
76007
+ const connectivity = await this._apiClient.checkConnectivity();
76008
+ profiler2.endSpan(connectivitySpan, {
76009
+ reachable: connectivity.reachable,
76010
+ latencyMs: connectivity.latencyMs
76011
+ });
76012
+ if (!connectivity.reachable) {
76013
+ this._connectionStatus = "offline";
76014
+ this._validationError = "API endpoint unreachable - running in offline mode";
76015
+ debugProtocol.auth("connectivity_failed", {
76016
+ serverUrl: this._serverUrl,
76017
+ status: "offline"
76018
+ });
76019
+ return;
76020
+ }
76021
+ const tokenSpan = profiler2.startSpan(
76022
+ "token_validation",
76023
+ "Token Validation"
76024
+ );
75333
76025
  debugProtocol.auth("token_validation_start", {
75334
76026
  serverUrl: this._serverUrl,
75335
76027
  hasApiClient: true,
75336
- authSource: this._authSource
76028
+ authSource: this._authSource,
76029
+ startupMode: true
76030
+ });
76031
+ const result = await this._apiClient.validateToken({
76032
+ startupMode: true
75337
76033
  });
75338
- const result = await this._apiClient.validateToken();
75339
76034
  this._tokenValidated = result.valid;
75340
76035
  this._validationError = result.error ?? null;
75341
- profile("token_validation:complete");
76036
+ if (result.valid) {
76037
+ this._connectionStatus = "connected";
76038
+ } else if (result.error?.includes("unreachable") || result.error?.includes("timed out")) {
76039
+ this._connectionStatus = "offline";
76040
+ } else {
76041
+ this._connectionStatus = "error";
76042
+ }
76043
+ profiler2.endSpan(tokenSpan, {
76044
+ valid: result.valid,
76045
+ error: result.error,
76046
+ connectionStatus: this._connectionStatus
76047
+ });
75342
76048
  debugProtocol.auth("token_validation_complete", {
75343
76049
  valid: result.valid,
75344
76050
  error: result.error,
75345
76051
  tokenValidated: this._tokenValidated,
75346
76052
  validationError: this._validationError,
75347
- authSource: this._authSource
76053
+ authSource: this._authSource,
76054
+ connectionStatus: this._connectionStatus
75348
76055
  });
76056
+ if (this._connectionStatus === "offline") {
76057
+ return;
76058
+ }
75349
76059
  if (!result.valid && (this._authSource === "env" || this._authSource === "mixed")) {
75350
76060
  if (this._activeProfile?.apiToken && this._activeProfile.apiToken !== this._apiToken) {
75351
76061
  this._fallbackAttempted = true;
@@ -75366,20 +76076,29 @@ var REPLSession = class {
75366
76076
  apiToken: this._apiToken,
75367
76077
  debug: this._debug
75368
76078
  });
75369
- profile("fallback_validation:start");
75370
- const fallbackResult = await this._apiClient.validateToken();
75371
- profile("fallback_validation:complete");
76079
+ const fallbackSpan = profiler2.startSpan(
76080
+ "fallback_validation",
76081
+ "Fallback Token Validation"
76082
+ );
76083
+ const fallbackResult = await this._apiClient.validateToken({
76084
+ startupMode: true
76085
+ });
76086
+ profiler2.endSpan(fallbackSpan, {
76087
+ valid: fallbackResult.valid
76088
+ });
75372
76089
  if (fallbackResult.valid) {
75373
76090
  this._tokenValidated = true;
75374
76091
  this._validationError = null;
75375
76092
  this._authSource = "profile-fallback";
75376
76093
  this._fallbackReason = null;
76094
+ this._connectionStatus = "connected";
75377
76095
  debugProtocol.auth("token_fallback_success", {
75378
76096
  authSource: this._authSource,
75379
76097
  profileName: this._activeProfileName
75380
76098
  });
75381
76099
  } else {
75382
76100
  this._fallbackReason = `Profile '${this._activeProfileName}' credentials are also invalid`;
76101
+ this._connectionStatus = "error";
75383
76102
  debugProtocol.auth("token_fallback_failed", {
75384
76103
  error: fallbackResult.error,
75385
76104
  profileName: this._activeProfileName
@@ -75403,13 +76122,15 @@ var REPLSession = class {
75403
76122
  });
75404
76123
  }
75405
76124
  }
75406
- if (this._tokenValidated) {
75407
- profile("user_info:start");
76125
+ if (this._tokenValidated && this._connectionStatus === "connected") {
76126
+ const userInfoSpan = profiler2.startSpan(
76127
+ "user_info",
76128
+ "User Info Fetch"
76129
+ );
75408
76130
  await this.fetchUserInfo();
75409
- profile("user_info:complete");
76131
+ profiler2.endSpan(userInfoSpan, { username: this._username });
75410
76132
  }
75411
76133
  }
75412
- profile("initialize:complete");
75413
76134
  }
75414
76135
  /**
75415
76136
  * Fetch user info from the API
@@ -75613,6 +76334,18 @@ var REPLSession = class {
75613
76334
  getFallbackReason() {
75614
76335
  return this._fallbackReason;
75615
76336
  }
76337
+ /**
76338
+ * Get the current connection status
76339
+ */
76340
+ getConnectionStatus() {
76341
+ return this._connectionStatus;
76342
+ }
76343
+ /**
76344
+ * Check if running in offline mode (API unreachable)
76345
+ */
76346
+ isOfflineMode() {
76347
+ return this._connectionStatus === "offline";
76348
+ }
75616
76349
  /**
75617
76350
  * Get the API client
75618
76351
  */
@@ -85491,29 +86224,26 @@ var HeadlessController = class {
85491
86224
 
85492
86225
  // src/index.tsx
85493
86226
  var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
85494
- var _startupTime = Date.now();
85495
- var _profile = (label) => {
85496
- if (process.env.XCSH_PROFILE === "true") {
85497
- console.error(`[STARTUP] ${label}: ${Date.now() - _startupTime}ms`);
85498
- }
85499
- };
85500
- _profile("script:start");
85501
- _profile("import:ink");
85502
- _profile("import:commander");
85503
- _profile("import:repl");
85504
- _profile("import:branding");
85505
- _profile("import:executor");
85506
- _profile("import:session");
85507
- _profile("import:help");
85508
- _profile("import:config");
85509
- _profile("import:output-types");
85510
- _profile("import:banner");
85511
- _profile("import:debug");
85512
- _profile("import:spec");
85513
- _profile("import:headless");
85514
- _profile("imports:complete");
86227
+ var profiler = initProfiler();
86228
+ var importsSpan = profiler.startSpan("imports", "Module Imports");
86229
+ profiler.memorySnapshot("script_start");
86230
+ profiler.checkpoint("import:ink");
86231
+ profiler.checkpoint("import:commander");
86232
+ profiler.checkpoint("import:repl");
86233
+ profiler.checkpoint("import:branding");
86234
+ profiler.checkpoint("import:executor");
86235
+ profiler.checkpoint("import:session");
86236
+ profiler.checkpoint("import:help");
86237
+ profiler.checkpoint("import:config");
86238
+ profiler.checkpoint("import:output-types");
86239
+ profiler.checkpoint("import:banner");
86240
+ profiler.checkpoint("import:debug");
86241
+ profiler.checkpoint("import:spec");
86242
+ profiler.checkpoint("import:headless");
86243
+ profiler.endSpan(importsSpan);
86244
+ profiler.memorySnapshot("imports_complete");
85515
86245
  var program2 = new Command();
85516
- _profile("commander:created");
86246
+ profiler.checkpoint("commander:created");
85517
86247
  program2.configureHelp({
85518
86248
  formatHelp: () => formatRootHelp().join("\n")
85519
86249
  });
@@ -85559,12 +86289,28 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
85559
86289
  }
85560
86290
  const cliLogoMode = options.logo && isValidLogoMode(options.logo) ? options.logo : void 0;
85561
86291
  process.stdout.write("Initializing...");
86292
+ const sessionInitSpan = profiler.startSpan(
86293
+ "session_init",
86294
+ "Session Initialization"
86295
+ );
85562
86296
  const session = new REPLSession();
85563
86297
  await session.initialize();
86298
+ profiler.endSpan(sessionInitSpan);
85564
86299
  debugProtocol.session("init", { mode: "repl" });
85565
86300
  emitSessionState(session);
85566
86301
  process.stdout.write("\r\x1B[K");
86302
+ profiler.memorySnapshot("pre_banner");
86303
+ printProfileReport();
85567
86304
  renderBanner(cliLogoMode, "startup");
86305
+ if (session.isOfflineMode()) {
86306
+ console.log("");
86307
+ console.log(
86308
+ `${colors.yellow}\u26A0\uFE0F Offline Mode: API endpoint unreachable${colors.reset}`
86309
+ );
86310
+ console.log(
86311
+ `${colors.dim} Commands requiring API access will fail. Check network and try again.${colors.reset}`
86312
+ );
86313
+ }
85568
86314
  if (session.getAuthSource() === "profile-fallback") {
85569
86315
  const profileName = session.getActiveProfileName();
85570
86316
  console.log("");
@@ -85629,13 +86375,27 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
85629
86375
  }
85630
86376
  );
85631
86377
  async function executeNonInteractive(args) {
86378
+ const sessionInitSpan = profiler.startSpan(
86379
+ "session_init",
86380
+ "Session Initialization"
86381
+ );
85632
86382
  const session = new REPLSession();
85633
86383
  await session.initialize();
86384
+ profiler.endSpan(sessionInitSpan);
86385
+ printProfileReport();
85634
86386
  debugProtocol.session("init", {
85635
86387
  mode: "non-interactive",
85636
86388
  command: args.join(" ")
85637
86389
  });
85638
86390
  emitSessionState(session);
86391
+ if (session.isOfflineMode()) {
86392
+ console.error(
86393
+ `${colors.yellow}\u26A0\uFE0F Offline Mode: API endpoint unreachable${colors.reset}`
86394
+ );
86395
+ console.error(
86396
+ `${colors.dim} Commands requiring API access will fail.${colors.reset}`
86397
+ );
86398
+ }
85639
86399
  if (session.getAuthSource() === "profile-fallback") {
85640
86400
  const profileName = session.getActiveProfileName();
85641
86401
  console.error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robinmordasiewicz/f5xc-xcsh",
3
- "version": "2.0.21-2601100343",
3
+ "version": "2.0.21-2601100619",
4
4
  "description": "F5 Distributed Cloud Shell - Interactive CLI for F5 XC",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,8 +25,13 @@
25
25
  "build:binaries": "./scripts/build-binaries.sh",
26
26
  "build:release": "npm run build && npm run build:binaries",
27
27
  "dev": "tsx src/index.tsx",
28
+ "dev:profile": "XCSH_PROFILE_LEVEL=detailed tsx src/index.tsx",
29
+ "dev:profile:full": "XCSH_PROFILE_LEVEL=full tsx src/index.tsx",
28
30
  "build:dev": "npm run build && ${HOME}/.bun/bin/bun build dist/index.js --compile --outfile ./xcsh",
31
+ "build:dev:debug": "tsup --sourcemap && bun build dist/index.js --compile --outfile ./xcsh-debug",
29
32
  "start": "node dist/index.js",
33
+ "start:profile": "XCSH_PROFILE_LEVEL=detailed node dist/index.js",
34
+ "start:profile:full": "XCSH_PROFILE_LEVEL=full node dist/index.js",
30
35
  "test": "vitest",
31
36
  "test:coverage": "vitest --coverage",
32
37
  "lint": "eslint src",