@microsoft/sentinel-cli 0.2.0 → 0.3.0

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 +2728 -413
  2. package/package.json +6 -1
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ var require_correlationService = __commonJS({
44
44
  Object.defineProperty(exports2, "__esModule", { value: true });
45
45
  exports2.CorrelationService = void 0;
46
46
  var crypto_1 = require("crypto");
47
- var CorrelationService4 = class {
47
+ var CorrelationService17 = class {
48
48
  /**
49
49
  * Creates a new correlation context with a unique ID for request tracking.
50
50
  * @returns A UUID string to be used as a correlation ID in API requests and telemetry.
@@ -61,7 +61,7 @@ var require_correlationService = __commonJS({
61
61
  return response?.headers?.["x-ms-correlation-request-id"];
62
62
  }
63
63
  };
64
- exports2.CorrelationService = CorrelationService4;
64
+ exports2.CorrelationService = CorrelationService17;
65
65
  }
66
66
  });
67
67
 
@@ -159,6 +159,7 @@ var require_requestService = __commonJS({
159
159
  Object.defineProperty(exports2, "__esModule", { value: true });
160
160
  exports2.RequestService = exports2.DEFAULT_RETRY_OPTIONS = void 0;
161
161
  exports2.extractErrorInfo = extractErrorInfo;
162
+ var https_1 = __importDefault(require("https"));
162
163
  var axios_1 = __importDefault(require("axios"));
163
164
  var correlationService_1 = require_correlationService();
164
165
  var httpConstants_1 = require_httpConstants();
@@ -185,13 +186,19 @@ var require_requestService = __commonJS({
185
186
  baseDelayMs: 1e3,
186
187
  maxDelayMs: 3e4,
187
188
  retryableStatusCodes: Object.freeze([429, 500, 502, 503, 504]),
188
- retryableErrors: Object.freeze(["ECONNRESET", "ETIMEDOUT", "ENOTFOUND", "ECONNABORTED"])
189
+ retryableErrors: Object.freeze(["ECONNRESET", "ETIMEDOUT", "ENOTFOUND", "ECONNABORTED"]),
190
+ retryableErrorMessages: Object.freeze([
191
+ "socket disconnected",
192
+ "socket hang up",
193
+ "client network socket disconnected"
194
+ ])
189
195
  });
190
- var RequestService3 = class {
196
+ var HTTPS_AGENT = new https_1.default.Agent({ keepAlive: false });
197
+ var RequestService5 = class {
191
198
  telemetry;
192
199
  headerBuilder;
193
- constructor(telemetry, headerBuilder) {
194
- this.telemetry = telemetry;
200
+ constructor(telemetry2, headerBuilder) {
201
+ this.telemetry = telemetry2;
195
202
  this.headerBuilder = headerBuilder;
196
203
  }
197
204
  async get(options) {
@@ -273,7 +280,12 @@ var require_requestService = __commonJS({
273
280
  mainSpan.addAttributes({ success: "true" });
274
281
  return result;
275
282
  } catch (error) {
276
- if (attempt < clampedMaxRetries && this.shouldRetry(error, retryOptions ?? {})) {
283
+ const decision = this.shouldRetry(error, retryOptions ?? {});
284
+ if (attempt < clampedMaxRetries && decision.retry) {
285
+ mainSpan.addAttributes({
286
+ [`retry.${attempt}.reason`]: decision.reason ?? "",
287
+ [`retry.${attempt}.detail`]: decision.detail ?? ""
288
+ });
277
289
  await this.sleep(this.calculateRetryDelay(attempt, clampedBaseDelayMs, clampedMaxDelayMs));
278
290
  continue;
279
291
  }
@@ -350,7 +362,8 @@ var require_requestService = __commonJS({
350
362
  url,
351
363
  method: options?.method?.toLowerCase() || "get",
352
364
  headers: { ...options?.headers },
353
- data: options?.body
365
+ data: options?.body,
366
+ httpsAgent: HTTPS_AGENT
354
367
  };
355
368
  if (timeoutMs !== void 0) {
356
369
  axiosConfig.timeout = timeoutMs;
@@ -402,15 +415,24 @@ var require_requestService = __commonJS({
402
415
  shouldRetry(error, retryOptions) {
403
416
  const retryableStatusCodes = retryOptions.retryableStatusCodes ?? exports2.DEFAULT_RETRY_OPTIONS.retryableStatusCodes;
404
417
  const retryableErrors = retryOptions.retryableErrors ?? exports2.DEFAULT_RETRY_OPTIONS.retryableErrors;
418
+ const retryableErrorMessages = retryOptions.retryableErrorMessages ?? exports2.DEFAULT_RETRY_OPTIONS.retryableErrorMessages;
405
419
  const httpError = error;
406
420
  if (typeof httpError?.statusCode === "number") {
407
- return retryableStatusCodes.includes(httpError.statusCode);
421
+ return retryableStatusCodes.includes(httpError.statusCode) ? { retry: true, reason: "statusCode", detail: String(httpError.statusCode) } : { retry: false };
408
422
  }
409
423
  const codedError = error;
410
- if (codedError?.code) {
411
- return retryableErrors.includes(codedError.code);
424
+ if (codedError?.code && retryableErrors.includes(codedError.code)) {
425
+ return { retry: true, reason: "errorCode", detail: codedError.code };
412
426
  }
413
- return false;
427
+ const errorMessage = error?.message;
428
+ if (typeof errorMessage === "string" && errorMessage.length > 0) {
429
+ const lowerMessage = errorMessage.toLowerCase();
430
+ const matchedPattern = retryableErrorMessages.find((pattern) => lowerMessage.includes(pattern.toLowerCase()));
431
+ if (matchedPattern) {
432
+ return { retry: true, reason: "errorMessage", detail: matchedPattern };
433
+ }
434
+ }
435
+ return { retry: false };
414
436
  }
415
437
  /**
416
438
  * Exponential backoff with 10% random jitter.
@@ -423,7 +445,7 @@ var require_requestService = __commonJS({
423
445
  return Math.min(exponentialDelay + jitter, maxDelayMs);
424
446
  }
425
447
  sleep(ms) {
426
- return new Promise((resolve) => setTimeout(resolve, ms));
448
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
427
449
  }
428
450
  // -------------------------------------------------------------------------
429
451
  // URL helpers
@@ -439,7 +461,7 @@ var require_requestService = __commonJS({
439
461
  return urlObj.toString();
440
462
  }
441
463
  };
442
- exports2.RequestService = RequestService3;
464
+ exports2.RequestService = RequestService5;
443
465
  function getPathFromUrl(url) {
444
466
  const urlObject = new URL(url);
445
467
  return urlObject.pathname;
@@ -466,7 +488,7 @@ var require_client = __commonJS({
466
488
  o[k2] = m[k];
467
489
  }));
468
490
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
469
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
491
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
470
492
  };
471
493
  Object.defineProperty(exports2, "__esModule", { value: true });
472
494
  __exportStar(require_requestService(), exports2);
@@ -484,7 +506,13 @@ var require_jobEventNames = __commonJS({
484
506
  LOAD_JOBS: "JobsPanel.Load.Jobs",
485
507
  CREATE_JOB: "Jobs.Create.Job",
486
508
  GET_JOB: "Jobs.Get.Job",
487
- DELETE_JOB: "Jobs.Delete.Job"
509
+ DELETE_JOB: "Jobs.Delete.Job",
510
+ DISABLE_JOB: "Jobs.Disable.Job",
511
+ ENABLE_JOB: "Jobs.Enable.Job",
512
+ LIST_JOB_RUNS: "Jobs.List.Runs",
513
+ GET_JOB_RUN: "Jobs.Get.Run",
514
+ START_JOB_RUN: "Jobs.Start.Run",
515
+ CANCEL_JOB_RUN: "Jobs.Cancel.Run"
488
516
  };
489
517
  }
490
518
  });
@@ -522,7 +550,7 @@ var require_constants = __commonJS({
522
550
  o[k2] = m[k];
523
551
  }));
524
552
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
525
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
553
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
526
554
  };
527
555
  Object.defineProperty(exports2, "__esModule", { value: true });
528
556
  exports2.FulfillmentStatus = exports2.PackageManifestIdValues = exports2.PackageManifestTypes = exports2.CANCEL_DEPLOYMENT_REQUEST_TYPE = exports2.NEW_DEPLOYMENT_REQUEST_TYPE = void 0;
@@ -557,26 +585,26 @@ var require_region = __commonJS({
557
585
  init_cjs_shims();
558
586
  Object.defineProperty(exports2, "__esModule", { value: true });
559
587
  exports2.Region = void 0;
560
- var Region2;
561
- (function(Region3) {
562
- Region3["NorwayEast"] = "norwayeast";
563
- Region3["EastUS2EUAP"] = "eastus2euap";
564
- Region3["AustraliaEast"] = "australiaeast";
565
- Region3["CanadaCentral"] = "canadacentral";
566
- Region3["CentralUS"] = "centralus";
567
- Region3["EastUS"] = "eastus";
568
- Region3["EastUS2"] = "eastus2";
569
- Region3["FranceCentral"] = "francecentral";
570
- Region3["JapanEast"] = "japaneast";
571
- Region3["NorthEurope"] = "northeurope";
572
- Region3["SouthCentralUS"] = "southcentralus";
573
- Region3["UKSouth"] = "uksouth";
574
- Region3["WestEurope"] = "westeurope";
575
- Region3["WestUS"] = "westus";
576
- Region3["WestUS2"] = "westus2";
577
- Region3["Global"] = "global";
578
- Region3["Dev"] = "dev";
579
- })(Region2 || (exports2.Region = Region2 = {}));
588
+ var Region14;
589
+ (function(Region15) {
590
+ Region15["NorwayEast"] = "norwayeast";
591
+ Region15["EastUS2EUAP"] = "eastus2euap";
592
+ Region15["AustraliaEast"] = "australiaeast";
593
+ Region15["CanadaCentral"] = "canadacentral";
594
+ Region15["CentralUS"] = "centralus";
595
+ Region15["EastUS"] = "eastus";
596
+ Region15["EastUS2"] = "eastus2";
597
+ Region15["FranceCentral"] = "francecentral";
598
+ Region15["JapanEast"] = "japaneast";
599
+ Region15["NorthEurope"] = "northeurope";
600
+ Region15["SouthCentralUS"] = "southcentralus";
601
+ Region15["UKSouth"] = "uksouth";
602
+ Region15["WestEurope"] = "westeurope";
603
+ Region15["WestUS"] = "westus";
604
+ Region15["WestUS2"] = "westus2";
605
+ Region15["Global"] = "global";
606
+ Region15["Dev"] = "dev";
607
+ })(Region14 || (exports2.Region = Region14 = {}));
580
608
  }
581
609
  });
582
610
 
@@ -587,10 +615,10 @@ var require_securityPlatformEnvironment = __commonJS({
587
615
  init_cjs_shims();
588
616
  Object.defineProperty(exports2, "__esModule", { value: true });
589
617
  exports2.SecurityPlatformEnvironment = void 0;
590
- var SecurityPlatformEnvironment8;
591
- (function(SecurityPlatformEnvironment9) {
592
- SecurityPlatformEnvironment9["Production"] = "production";
593
- })(SecurityPlatformEnvironment8 || (exports2.SecurityPlatformEnvironment = SecurityPlatformEnvironment8 = {}));
618
+ var SecurityPlatformEnvironment21;
619
+ (function(SecurityPlatformEnvironment22) {
620
+ SecurityPlatformEnvironment22["Production"] = "production";
621
+ })(SecurityPlatformEnvironment21 || (exports2.SecurityPlatformEnvironment = SecurityPlatformEnvironment21 = {}));
594
622
  }
595
623
  });
596
624
 
@@ -611,6 +639,8 @@ var require_production = __commonJS({
611
639
  gatewayAuthResourceUri: "73c2949e-da2d-457a-9607-fcc665198967/.default",
612
640
  gatewayEndpoint: "https://api.securityplatform.microsoft.com/",
613
641
  aadClientId: DeviceCodeApplicationId,
642
+ armEndpoint: "https://management.azure.com",
643
+ armScope: "https://management.azure.com/.default",
614
644
  regions: {
615
645
  [region_1.Region.NorwayEast]: buildRegionConfig(region_1.Region.NorwayEast),
616
646
  [region_1.Region.EastUS2EUAP]: buildRegionConfig(region_1.Region.EastUS2EUAP),
@@ -663,7 +693,7 @@ var require_configs = __commonJS({
663
693
  o[k2] = m[k];
664
694
  }));
665
695
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
666
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
696
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
667
697
  };
668
698
  Object.defineProperty(exports2, "__esModule", { value: true });
669
699
  __exportStar(require_production(), exports2);
@@ -692,9 +722,9 @@ var require_api_env = __commonJS({
692
722
  "use strict";
693
723
  init_cjs_shims();
694
724
  Object.defineProperty(exports2, "__esModule", { value: true });
695
- exports2.getApiEnv = getApiEnv8;
725
+ exports2.getApiEnv = getApiEnv21;
696
726
  var helper_1 = require_helper();
697
- function getApiEnv8(env) {
727
+ function getApiEnv21(env) {
698
728
  return (0, helper_1.getEnvironmentConfig)(env);
699
729
  }
700
730
  }
@@ -728,7 +758,7 @@ var require_environments = __commonJS({
728
758
  o[k2] = m[k];
729
759
  }));
730
760
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
731
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
761
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
732
762
  };
733
763
  Object.defineProperty(exports2, "__esModule", { value: true });
734
764
  __exportStar(require_configs(), exports2);
@@ -788,11 +818,11 @@ var require_common = __commonJS({
788
818
  init_cjs_shims();
789
819
  var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P, generator) {
790
820
  function adopt(value) {
791
- return value instanceof P ? value : new P(function(resolve) {
792
- resolve(value);
821
+ return value instanceof P ? value : new P(function(resolve2) {
822
+ resolve2(value);
793
823
  });
794
824
  }
795
- return new (P || (P = Promise))(function(resolve, reject) {
825
+ return new (P || (P = Promise))(function(resolve2, reject) {
796
826
  function fulfilled(value) {
797
827
  try {
798
828
  step(generator.next(value));
@@ -808,7 +838,7 @@ var require_common = __commonJS({
808
838
  }
809
839
  }
810
840
  function step(result) {
811
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
841
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
812
842
  }
813
843
  step((generator = generator.apply(thisArg, _arguments || [])).next());
814
844
  });
@@ -907,11 +937,11 @@ var require_api = __commonJS({
907
937
  init_cjs_shims();
908
938
  var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P, generator) {
909
939
  function adopt(value) {
910
- return value instanceof P ? value : new P(function(resolve) {
911
- resolve(value);
940
+ return value instanceof P ? value : new P(function(resolve2) {
941
+ resolve2(value);
912
942
  });
913
943
  }
914
- return new (P || (P = Promise))(function(resolve, reject) {
944
+ return new (P || (P = Promise))(function(resolve2, reject) {
915
945
  function fulfilled(value) {
916
946
  try {
917
947
  step(generator.next(value));
@@ -927,7 +957,7 @@ var require_api = __commonJS({
927
957
  }
928
958
  }
929
959
  function step(result) {
930
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
960
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
931
961
  }
932
962
  step((generator = generator.apply(thisArg, _arguments || [])).next());
933
963
  });
@@ -2372,7 +2402,7 @@ var require_dist = __commonJS({
2372
2402
  o[k2] = m[k];
2373
2403
  }));
2374
2404
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
2375
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
2405
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
2376
2406
  };
2377
2407
  Object.defineProperty(exports2, "__esModule", { value: true });
2378
2408
  __exportStar(require_api(), exports2);
@@ -2659,7 +2689,7 @@ var require_factories = __commonJS({
2659
2689
  o[k2] = m[k];
2660
2690
  }));
2661
2691
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
2662
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
2692
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
2663
2693
  };
2664
2694
  Object.defineProperty(exports2, "__esModule", { value: true });
2665
2695
  __exportStar(require_jobPayloadFactory(), exports2);
@@ -2766,7 +2796,7 @@ var require_jobs = __commonJS({
2766
2796
  o[k2] = m[k];
2767
2797
  }));
2768
2798
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
2769
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
2799
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
2770
2800
  };
2771
2801
  Object.defineProperty(exports2, "__esModule", { value: true });
2772
2802
  __exportStar(require_analyticsJobDefinition(), exports2);
@@ -2808,7 +2838,7 @@ var require_lake = __commonJS({
2808
2838
  o[k2] = m[k];
2809
2839
  }));
2810
2840
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
2811
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
2841
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
2812
2842
  };
2813
2843
  Object.defineProperty(exports2, "__esModule", { value: true });
2814
2844
  __exportStar(require_database(), exports2);
@@ -2891,7 +2921,7 @@ var require_models = __commonJS({
2891
2921
  o[k2] = m[k];
2892
2922
  }));
2893
2923
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
2894
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
2924
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
2895
2925
  };
2896
2926
  Object.defineProperty(exports2, "__esModule", { value: true });
2897
2927
  __exportStar(require_jobs(), exports2);
@@ -2902,6 +2932,133 @@ var require_models = __commonJS({
2902
2932
  }
2903
2933
  });
2904
2934
 
2935
+ // ../../packages/common/dist/src/services/tenant/types.js
2936
+ var require_types = __commonJS({
2937
+ "../../packages/common/dist/src/services/tenant/types.js"(exports2) {
2938
+ "use strict";
2939
+ init_cjs_shims();
2940
+ Object.defineProperty(exports2, "__esModule", { value: true });
2941
+ }
2942
+ });
2943
+
2944
+ // ../../packages/common/dist/src/services/tenant/tenantApiClient.js
2945
+ var require_tenantApiClient = __commonJS({
2946
+ "../../packages/common/dist/src/services/tenant/tenantApiClient.js"(exports2) {
2947
+ "use strict";
2948
+ init_cjs_shims();
2949
+ Object.defineProperty(exports2, "__esModule", { value: true });
2950
+ exports2.TenantApiClient = void 0;
2951
+ var DEFAULT_ARM_ENDPOINT = "https://management.azure.com";
2952
+ var ARM_API_VERSION = "2020-01-01";
2953
+ var MAX_PAGES = 20;
2954
+ var TENANT_LIST_EVENT = "TenantApiClient.fetchAllTenants";
2955
+ var TenantApiClient2 = class {
2956
+ requestService;
2957
+ tenantsUrl;
2958
+ trustedOrigin;
2959
+ constructor(options) {
2960
+ this.requestService = options.requestService;
2961
+ const armEndpoint = options.armEndpoint ?? DEFAULT_ARM_ENDPOINT;
2962
+ this.tenantsUrl = new URL("/tenants", armEndpoint).toString();
2963
+ this.trustedOrigin = new URL(armEndpoint).origin;
2964
+ }
2965
+ /**
2966
+ * Fetches all tenants accessible to the authenticated user from ARM,
2967
+ * following pagination links until exhausted or the page limit is reached.
2968
+ *
2969
+ * @param accessToken - A valid ARM bearer access token.
2970
+ * @returns An array of {@link ArmTenant} objects.
2971
+ */
2972
+ async fetchAllTenants(accessToken) {
2973
+ const allTenants = [];
2974
+ let nextUrl = this.tenantsUrl;
2975
+ let params = { "api-version": ARM_API_VERSION };
2976
+ let pageCount = 0;
2977
+ while (nextUrl && pageCount < MAX_PAGES) {
2978
+ pageCount++;
2979
+ const page = await this.requestService.get({
2980
+ url: nextUrl,
2981
+ eventName: TENANT_LIST_EVENT,
2982
+ params,
2983
+ token: accessToken
2984
+ });
2985
+ if (Array.isArray(page.value)) {
2986
+ allTenants.push(...page.value.map(mapArmTenant));
2987
+ }
2988
+ if (page.nextLink) {
2989
+ this.validateNextLink(page.nextLink);
2990
+ nextUrl = page.nextLink;
2991
+ params = {};
2992
+ } else {
2993
+ nextUrl = void 0;
2994
+ }
2995
+ }
2996
+ if (nextUrl) {
2997
+ throw new Error(`Tenant list is incomplete: maximum page limit of ${MAX_PAGES} reached.`);
2998
+ }
2999
+ return allTenants;
3000
+ }
3001
+ /**
3002
+ * Validates that a pagination URL returned by the ARM API originates from
3003
+ * the trusted ARM endpoint and targets the /tenants path, preventing SSRF
3004
+ * and bearer-token exfiltration to other origins or endpoints.
3005
+ */
3006
+ validateNextLink(nextLink) {
3007
+ let parsedUrl;
3008
+ try {
3009
+ parsedUrl = new URL(nextLink);
3010
+ } catch {
3011
+ throw new Error(`Invalid nextLink URL: ${nextLink}`);
3012
+ }
3013
+ if (parsedUrl.origin !== this.trustedOrigin) {
3014
+ throw new Error(`Untrusted nextLink origin: expected ${this.trustedOrigin}, got ${parsedUrl.origin}`);
3015
+ }
3016
+ if (parsedUrl.pathname !== "/tenants" && !parsedUrl.pathname.startsWith("/tenants/")) {
3017
+ throw new Error(`Untrusted nextLink path detected: ${parsedUrl.pathname}`);
3018
+ }
3019
+ }
3020
+ };
3021
+ exports2.TenantApiClient = TenantApiClient2;
3022
+ function mapArmTenant(raw) {
3023
+ return {
3024
+ id: raw.id,
3025
+ tenantId: raw.tenantId,
3026
+ displayName: raw.displayName ?? "Unknown Tenant",
3027
+ defaultDomain: raw.defaultDomain ?? "",
3028
+ tenantType: raw.tenantType ?? "AAD",
3029
+ tenantCategory: raw.tenantCategory
3030
+ };
3031
+ }
3032
+ }
3033
+ });
3034
+
3035
+ // ../../packages/common/dist/src/services/tenant/index.js
3036
+ var require_tenant = __commonJS({
3037
+ "../../packages/common/dist/src/services/tenant/index.js"(exports2) {
3038
+ "use strict";
3039
+ init_cjs_shims();
3040
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
3041
+ if (k2 === void 0) k2 = k;
3042
+ var desc = Object.getOwnPropertyDescriptor(m, k);
3043
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
3044
+ desc = { enumerable: true, get: function() {
3045
+ return m[k];
3046
+ } };
3047
+ }
3048
+ Object.defineProperty(o, k2, desc);
3049
+ }) : (function(o, m, k, k2) {
3050
+ if (k2 === void 0) k2 = k;
3051
+ o[k2] = m[k];
3052
+ }));
3053
+ var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
3054
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
3055
+ };
3056
+ Object.defineProperty(exports2, "__esModule", { value: true });
3057
+ __exportStar(require_types(), exports2);
3058
+ __exportStar(require_tenantApiClient(), exports2);
3059
+ }
3060
+ });
3061
+
2905
3062
  // ../../packages/common/dist/src/services/jobs/jobApiClient.js
2906
3063
  var require_jobApiClient = __commonJS({
2907
3064
  "../../packages/common/dist/src/services/jobs/jobApiClient.js"(exports2) {
@@ -2965,6 +3122,111 @@ var require_jobApiClient = __commonJS({
2965
3122
  requestId
2966
3123
  });
2967
3124
  }
3125
+ async disableJob(jobName, token, requestId) {
3126
+ if (!jobName?.trim()) {
3127
+ throw new Error("Job name is required");
3128
+ }
3129
+ const current = await this.getJob(jobName, token, requestId);
3130
+ const updated = { ...current, jobDefinition: { ...current.jobDefinition, isDisabled: true } };
3131
+ return this.requestService.put({
3132
+ url: this.jobUrl(jobName),
3133
+ params: { "api-version": API_VERSION },
3134
+ eventName: jobEventNames_1.JobApiEvents.DISABLE_JOB,
3135
+ body: JSON.stringify(updated),
3136
+ token,
3137
+ requestId
3138
+ });
3139
+ }
3140
+ async enableJob(jobName, token, requestId) {
3141
+ if (!jobName?.trim()) {
3142
+ throw new Error("Job name is required");
3143
+ }
3144
+ const current = await this.getJob(jobName, token, requestId);
3145
+ const updated = { ...current, jobDefinition: { ...current.jobDefinition, isDisabled: false } };
3146
+ return this.requestService.put({
3147
+ url: this.jobUrl(jobName),
3148
+ params: { "api-version": API_VERSION },
3149
+ eventName: jobEventNames_1.JobApiEvents.ENABLE_JOB,
3150
+ body: JSON.stringify(updated),
3151
+ token,
3152
+ requestId
3153
+ });
3154
+ }
3155
+ jobRunsUrl(jobName) {
3156
+ return `${this.jobUrl(jobName)}/runs`;
3157
+ }
3158
+ jobRunUrl(jobName, runId) {
3159
+ return `${this.jobRunsUrl(jobName)}/${encodeURIComponent(runId)}`;
3160
+ }
3161
+ async listJobRuns(jobName, token, requestId) {
3162
+ if (!jobName?.trim()) {
3163
+ throw new Error("Job name is required");
3164
+ }
3165
+ const runs = [];
3166
+ let continuationToken = void 0;
3167
+ do {
3168
+ const params = { "api-version": API_VERSION };
3169
+ if (continuationToken !== null && continuationToken !== void 0) {
3170
+ params.continuationToken = continuationToken;
3171
+ }
3172
+ const data = await this.requestService.get({
3173
+ url: this.jobRunsUrl(jobName),
3174
+ params,
3175
+ eventName: jobEventNames_1.JobApiEvents.LIST_JOB_RUNS,
3176
+ token,
3177
+ requestId
3178
+ });
3179
+ if (data?.results) {
3180
+ runs.push(...data.results);
3181
+ }
3182
+ continuationToken = data?.continuationToken ?? null;
3183
+ } while (continuationToken !== null);
3184
+ return runs;
3185
+ }
3186
+ async getJobRun(jobName, runId, token, requestId) {
3187
+ if (!jobName?.trim()) {
3188
+ throw new Error("Job name is required");
3189
+ }
3190
+ if (!runId?.trim()) {
3191
+ throw new Error("Run ID is required");
3192
+ }
3193
+ return this.requestService.get({
3194
+ url: this.jobRunUrl(jobName, runId),
3195
+ params: { "api-version": API_VERSION },
3196
+ eventName: jobEventNames_1.JobApiEvents.GET_JOB_RUN,
3197
+ token,
3198
+ requestId
3199
+ });
3200
+ }
3201
+ async startJobRun(jobName, token, requestId) {
3202
+ if (!jobName?.trim()) {
3203
+ throw new Error("Job name is required");
3204
+ }
3205
+ return this.requestService.post({
3206
+ url: `${this.jobUrl(jobName)}:runNow`,
3207
+ params: { "api-version": API_VERSION },
3208
+ eventName: jobEventNames_1.JobApiEvents.START_JOB_RUN,
3209
+ body: JSON.stringify({}),
3210
+ token,
3211
+ requestId
3212
+ });
3213
+ }
3214
+ async cancelJobRun(jobName, runId, token, requestId) {
3215
+ if (!jobName?.trim()) {
3216
+ throw new Error("Job name is required");
3217
+ }
3218
+ if (!runId?.trim()) {
3219
+ throw new Error("Run ID is required");
3220
+ }
3221
+ return this.requestService.post({
3222
+ url: `${this.jobRunUrl(jobName, runId)}:cancel`,
3223
+ params: { "api-version": API_VERSION },
3224
+ eventName: jobEventNames_1.JobApiEvents.CANCEL_JOB_RUN,
3225
+ body: JSON.stringify({}),
3226
+ token,
3227
+ requestId
3228
+ });
3229
+ }
2968
3230
  async listJobs(token, requestId) {
2969
3231
  const jobs = [];
2970
3232
  let continuationToken = void 0;
@@ -3221,7 +3483,7 @@ var require_common2 = __commonJS({
3221
3483
  function isNothing(subject) {
3222
3484
  return typeof subject === "undefined" || subject === null;
3223
3485
  }
3224
- function isObject(subject) {
3486
+ function isObject2(subject) {
3225
3487
  return typeof subject === "object" && subject !== null;
3226
3488
  }
3227
3489
  function toArray(sequence) {
@@ -3251,7 +3513,7 @@ var require_common2 = __commonJS({
3251
3513
  return number === 0 && Number.NEGATIVE_INFINITY === 1 / number;
3252
3514
  }
3253
3515
  module2.exports.isNothing = isNothing;
3254
- module2.exports.isObject = isObject;
3516
+ module2.exports.isObject = isObject2;
3255
3517
  module2.exports.toArray = toArray;
3256
3518
  module2.exports.repeat = repeat;
3257
3519
  module2.exports.isNegativeZero = isNegativeZero;
@@ -6079,10 +6341,10 @@ var require_notebookUtils = __commonJS({
6079
6341
  exports2.getJupyterNotebook = getJupyterNotebook;
6080
6342
  exports2.removeCellOutput = removeCellOutput;
6081
6343
  exports2.notebookToBase64 = notebookToBase64;
6082
- var fs3 = __importStar(require("fs/promises"));
6344
+ var fs7 = __importStar(require("fs/promises"));
6083
6345
  async function getJupyterNotebook(filePath) {
6084
6346
  try {
6085
- const notebookFile = await fs3.readFile(filePath, "utf-8");
6347
+ const notebookFile = await fs7.readFile(filePath, "utf-8");
6086
6348
  return JSON.parse(notebookFile);
6087
6349
  } catch (error) {
6088
6350
  throw new Error(`Failed to read or parse the notebook file: ${error}`);
@@ -6160,18 +6422,18 @@ var require_secureExchangeManifestUtils = __commonJS({
6160
6422
  })();
6161
6423
  Object.defineProperty(exports2, "__esModule", { value: true });
6162
6424
  exports2.updateSecureExchangeManifest = updateSecureExchangeManifest;
6163
- var fs3 = __importStar(require("fs-extra"));
6425
+ var fs7 = __importStar(require("fs-extra"));
6164
6426
  var yaml = __importStar(require("yaml"));
6165
6427
  async function updateSecureExchangeManifest(manifestEntries, secureExchangeManifestPath) {
6166
6428
  if (manifestEntries.length > 0) {
6167
- const existingYaml = await fs3.readFile(secureExchangeManifestPath, "utf-8");
6429
+ const existingYaml = await fs7.readFile(secureExchangeManifestPath, "utf-8");
6168
6430
  const parsedYaml = existingYaml ? yaml.parse(existingYaml) || [] : [];
6169
6431
  const newEntries = manifestEntries.filter((newEntry) => !parsedYaml.some((existingEntry) => existingEntry.id === newEntry.id && existingEntry.type === newEntry.type));
6170
6432
  if (newEntries.length === 0) {
6171
6433
  return;
6172
6434
  }
6173
6435
  const updatedYaml = [...parsedYaml, ...newEntries];
6174
- await fs3.writeFile(secureExchangeManifestPath, yaml.stringify(updatedYaml), "utf-8");
6436
+ await fs7.writeFile(secureExchangeManifestPath, yaml.stringify(updatedYaml), "utf-8");
6175
6437
  }
6176
6438
  }
6177
6439
  }
@@ -6787,8 +7049,8 @@ var require_zipDirectory = __commonJS({
6787
7049
  try {
6788
7050
  const output = fs_1.default.createWriteStream(outPath);
6789
7051
  const archive = (0, archiver_1.default)("zip", { zlib: { level: 9 } });
6790
- return new Promise((resolve, reject) => {
6791
- output.on("close", resolve);
7052
+ return new Promise((resolve2, reject) => {
7053
+ output.on("close", resolve2);
6792
7054
  output.on("error", reject);
6793
7055
  archive.on("error", reject);
6794
7056
  archive.pipe(output);
@@ -6865,8 +7127,8 @@ var require_manifestProcessorService = __commonJS({
6865
7127
  Object.defineProperty(exports2, "__esModule", { value: true });
6866
7128
  exports2.ManifestProcessorService = void 0;
6867
7129
  var crypto_1 = require("crypto");
6868
- var fs3 = __importStar(require("fs-extra"));
6869
- var path5 = __importStar(require("path"));
7130
+ var fs7 = __importStar(require("fs-extra"));
7131
+ var path8 = __importStar(require("path"));
6870
7132
  var yaml = __importStar(require("yaml"));
6871
7133
  var constants_1 = require_constants();
6872
7134
  var buildScheduledJobArtifacts_1 = require_buildScheduledJobArtifacts();
@@ -6889,17 +7151,17 @@ var require_manifestProcessorService = __commonJS({
6889
7151
  };
6890
7152
  }
6891
7153
  async buildZipPackage(manifestPath) {
6892
- const manifestDir = path5.dirname(path5.resolve(manifestPath));
6893
- const tempPackageDir = path5.join(manifestDir, ".packaging-temp");
7154
+ const manifestDir = path8.dirname(path8.resolve(manifestPath));
7155
+ const tempPackageDir = path8.join(manifestDir, ".packaging-temp");
6894
7156
  try {
6895
- await fs3.rm(tempPackageDir, { recursive: true, force: true });
6896
- await fs3.mkdir(tempPackageDir, { recursive: true });
7157
+ await fs7.rm(tempPackageDir, { recursive: true, force: true });
7158
+ await fs7.mkdir(tempPackageDir, { recursive: true });
6897
7159
  const manifest = await (0, manifestParser_1.parseManifest)(manifestPath);
6898
- const discoveredFiles = await (0, discoverFiles_1.discoverFiles)(manifest.includePaths, path5.dirname(path5.resolve(manifestPath)));
7160
+ const discoveredFiles = await (0, discoverFiles_1.discoverFiles)(manifest.includePaths, path8.dirname(path8.resolve(manifestPath)));
6899
7161
  console.log("Discovered files:", JSON.stringify(discoveredFiles, null, 2));
6900
7162
  (0, manifestValidator_1.validateSingleScAgent)(discoveredFiles);
6901
- const secureExchangeManifestPath = path5.join(tempPackageDir, "PackageManifest.yaml");
6902
- await fs3.writeFile(secureExchangeManifestPath, "");
7163
+ const secureExchangeManifestPath = path8.join(tempPackageDir, "PackageManifest.yaml");
7164
+ await fs7.writeFile(secureExchangeManifestPath, "");
6903
7165
  console.log("Empty PackageManifest.yaml created at:", secureExchangeManifestPath);
6904
7166
  await (0, processMainTemplate_1.processMainTemplate)(discoveredFiles, tempPackageDir, secureExchangeManifestPath);
6905
7167
  console.log("mainTemplate.json processed and added to PackageManifest.yaml");
@@ -6909,8 +7171,8 @@ var require_manifestProcessorService = __commonJS({
6909
7171
  console.log("SC Agents processed and PackageManifest.yaml updated.");
6910
7172
  await (0, refactorManifest_1.finalizeManifest)(secureExchangeManifestPath, tempPackageDir);
6911
7173
  const zipName = `${manifest.packageName}.zip`;
6912
- const manifestDir2 = path5.dirname(path5.resolve(manifestPath));
6913
- const outputZipPath = path5.join(manifestDir2, zipName);
7174
+ const manifestDir2 = path8.dirname(path8.resolve(manifestPath));
7175
+ const outputZipPath = path8.join(manifestDir2, zipName);
6914
7176
  await (0, zipDirectory_1.zipDirectory)(tempPackageDir, outputZipPath);
6915
7177
  console.log("Package zipped at:", outputZipPath);
6916
7178
  return outputZipPath;
@@ -6922,21 +7184,21 @@ var require_manifestProcessorService = __commonJS({
6922
7184
  }
6923
7185
  } finally {
6924
7186
  try {
6925
- await fs3.rm(tempPackageDir, { recursive: true, force: true });
7187
+ await fs7.rm(tempPackageDir, { recursive: true, force: true });
6926
7188
  } catch (err) {
6927
7189
  console.warn("Please clean the temp directory as Failed to clean up temp directory:", err);
6928
7190
  }
6929
7191
  }
6930
7192
  }
6931
7193
  async validateManifest(manifestPath) {
6932
- const manifestDir = path5.dirname(path5.resolve(manifestPath));
6933
- const tempPackageDir = path5.join(manifestDir, ".packaging-temp");
7194
+ const manifestDir = path8.dirname(path8.resolve(manifestPath));
7195
+ const tempPackageDir = path8.join(manifestDir, ".packaging-temp");
6934
7196
  let parsed;
6935
7197
  try {
6936
- await fs3.rm(tempPackageDir, { recursive: true, force: true });
6937
- const content = await fs3.readFile(manifestPath, "utf8");
7198
+ await fs7.rm(tempPackageDir, { recursive: true, force: true });
7199
+ const content = await fs7.readFile(manifestPath, "utf8");
6938
7200
  parsed = yaml.parse(content);
6939
- const discoveredFiles = await (0, discoverFiles_1.discoverFiles)(parsed.includePaths, path5.dirname(path5.resolve(manifestPath)));
7201
+ const discoveredFiles = await (0, discoverFiles_1.discoverFiles)(parsed.includePaths, path8.dirname(path8.resolve(manifestPath)));
6940
7202
  (0, manifestValidator_1.validateSingleScAgent)(discoveredFiles);
6941
7203
  const yamlType = parsed.yamlType;
6942
7204
  if (!yamlType) {
@@ -6946,8 +7208,8 @@ var require_manifestProcessorService = __commonJS({
6946
7208
  if (!validator) {
6947
7209
  throw new Error(`No validator found for yamlType '${yamlType}'`);
6948
7210
  }
6949
- const absPath = path5.resolve(manifestPath);
6950
- const manifestDir2 = path5.dirname(absPath);
7211
+ const absPath = path8.resolve(manifestPath);
7212
+ const manifestDir2 = path8.dirname(absPath);
6951
7213
  await validator(parsed, manifestDir2);
6952
7214
  } catch (err) {
6953
7215
  if (err instanceof Error) {
@@ -6957,30 +7219,30 @@ var require_manifestProcessorService = __commonJS({
6957
7219
  }
6958
7220
  } finally {
6959
7221
  try {
6960
- await fs3.rm(tempPackageDir, { recursive: true, force: true });
7222
+ await fs7.rm(tempPackageDir, { recursive: true, force: true });
6961
7223
  } catch (err) {
6962
7224
  console.warn("Please clean the temp directory as Failed to clean up temp directory:", err);
6963
7225
  }
6964
7226
  }
6965
7227
  }
6966
7228
  async buildSideLoadRequest(manifestPath) {
6967
- const manifestDir = path5.dirname(path5.resolve(manifestPath));
6968
- const tempPackageDir = path5.join(manifestDir, ".packaging-temp");
6969
- const sentinelLakeDir = path5.join(tempPackageDir, constants_1.PackageManifestIdValues.SentinelLakeId);
7229
+ const manifestDir = path8.dirname(path8.resolve(manifestPath));
7230
+ const tempPackageDir = path8.join(manifestDir, ".packaging-temp");
7231
+ const sentinelLakeDir = path8.join(tempPackageDir, constants_1.PackageManifestIdValues.SentinelLakeId);
6970
7232
  const deploymentId = (0, crypto_1.randomUUID)();
6971
7233
  try {
6972
- await fs3.rm(tempPackageDir, { recursive: true, force: true });
7234
+ await fs7.rm(tempPackageDir, { recursive: true, force: true });
6973
7235
  const manifest = await (0, manifestParser_1.parseManifest)(manifestPath);
6974
- const discoveredFiles = await (0, discoverFiles_1.discoverFiles)(manifest.includePaths, path5.dirname(path5.resolve(manifestPath)));
7236
+ const discoveredFiles = await (0, discoverFiles_1.discoverFiles)(manifest.includePaths, path8.dirname(path8.resolve(manifestPath)));
6975
7237
  console.log("Discovered files:", JSON.stringify(discoveredFiles, null, 2));
6976
- await fs3.mkdir(tempPackageDir, { recursive: true });
7238
+ await fs7.mkdir(tempPackageDir, { recursive: true });
6977
7239
  await (0, buildScheduledJobArtifacts_1.processScheduledJobs)(discoveredFiles, tempPackageDir, "");
6978
- const jobDirs = await fs3.readdir(sentinelLakeDir);
7240
+ const jobDirs = await fs7.readdir(sentinelLakeDir);
6979
7241
  const artifacts = [];
6980
7242
  for (const dir of jobDirs) {
6981
- const deploymentJsonPath = path5.join(sentinelLakeDir, dir, "deployment.json");
6982
- if (await fs3.pathExists(deploymentJsonPath)) {
6983
- const jsonContent = await fs3.readFile(deploymentJsonPath, "utf8");
7243
+ const deploymentJsonPath = path8.join(sentinelLakeDir, dir, "deployment.json");
7244
+ if (await fs7.pathExists(deploymentJsonPath)) {
7245
+ const jsonContent = await fs7.readFile(deploymentJsonPath, "utf8");
6984
7246
  const escapedJson = JSON.stringify(JSON.parse(jsonContent));
6985
7247
  artifacts.push({
6986
7248
  name: "deployment.yaml",
@@ -7003,7 +7265,7 @@ var require_manifestProcessorService = __commonJS({
7003
7265
  console.error("Error building side-load request:", error);
7004
7266
  throw error;
7005
7267
  } finally {
7006
- await fs3.remove(tempPackageDir);
7268
+ await fs7.remove(tempPackageDir);
7007
7269
  }
7008
7270
  }
7009
7271
  };
@@ -7257,9 +7519,10 @@ var require_services = __commonJS({
7257
7519
  o[k2] = m[k];
7258
7520
  }));
7259
7521
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
7260
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
7522
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
7261
7523
  };
7262
7524
  Object.defineProperty(exports2, "__esModule", { value: true });
7525
+ __exportStar(require_tenant(), exports2);
7263
7526
  __exportStar(require_httpConstants(), exports2);
7264
7527
  __exportStar(require_jobs2(), exports2);
7265
7528
  __exportStar(require_lake2(), exports2);
@@ -7291,13 +7554,68 @@ var require_errors = __commonJS({
7291
7554
  o[k2] = m[k];
7292
7555
  }));
7293
7556
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
7294
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
7557
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
7295
7558
  };
7296
7559
  Object.defineProperty(exports2, "__esModule", { value: true });
7297
7560
  __exportStar(require_errorTypes(), exports2);
7298
7561
  }
7299
7562
  });
7300
7563
 
7564
+ // ../../packages/common/dist/src/telemetry/types.js
7565
+ var require_types2 = __commonJS({
7566
+ "../../packages/common/dist/src/telemetry/types.js"(exports2) {
7567
+ "use strict";
7568
+ init_cjs_shims();
7569
+ Object.defineProperty(exports2, "__esModule", { value: true });
7570
+ }
7571
+ });
7572
+
7573
+ // ../../packages/common/dist/src/telemetry/index.js
7574
+ var require_telemetry = __commonJS({
7575
+ "../../packages/common/dist/src/telemetry/index.js"(exports2) {
7576
+ "use strict";
7577
+ init_cjs_shims();
7578
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
7579
+ if (k2 === void 0) k2 = k;
7580
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7581
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7582
+ desc = { enumerable: true, get: function() {
7583
+ return m[k];
7584
+ } };
7585
+ }
7586
+ Object.defineProperty(o, k2, desc);
7587
+ }) : (function(o, m, k, k2) {
7588
+ if (k2 === void 0) k2 = k;
7589
+ o[k2] = m[k];
7590
+ }));
7591
+ var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
7592
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
7593
+ };
7594
+ Object.defineProperty(exports2, "__esModule", { value: true });
7595
+ __exportStar(require_types2(), exports2);
7596
+ }
7597
+ });
7598
+
7599
+ // ../../packages/common/dist/src/utils/decodeBase64Notebook.js
7600
+ var require_decodeBase64Notebook = __commonJS({
7601
+ "../../packages/common/dist/src/utils/decodeBase64Notebook.js"(exports2) {
7602
+ "use strict";
7603
+ init_cjs_shims();
7604
+ Object.defineProperty(exports2, "__esModule", { value: true });
7605
+ exports2.decodeBase64Notebook = decodeBase64Notebook3;
7606
+ var BASE64_RE = /^[A-Za-z0-9+/\r\n]*={0,2}$/;
7607
+ function decodeBase64Notebook3(encoded, label) {
7608
+ if (!encoded?.trim()) {
7609
+ throw new Error(`${label} has no notebook content to decode.`);
7610
+ }
7611
+ if (!BASE64_RE.test(encoded.trim())) {
7612
+ throw new Error(`${label} has an invalid base64-encoded notebook.`);
7613
+ }
7614
+ return Buffer.from(encoded, "base64").toString("utf-8");
7615
+ }
7616
+ }
7617
+ });
7618
+
7301
7619
  // ../../packages/common/dist/src/utils/pathUtils.js
7302
7620
  var require_pathUtils = __commonJS({
7303
7621
  "../../packages/common/dist/src/utils/pathUtils.js"(exports2) {
@@ -7309,8 +7627,49 @@ var require_pathUtils = __commonJS({
7309
7627
  Object.defineProperty(exports2, "__esModule", { value: true });
7310
7628
  exports2.resolvePath = resolvePath2;
7311
7629
  var path_1 = __importDefault(require("path"));
7312
- function resolvePath2(p) {
7313
- return path_1.default.resolve(p);
7630
+ function resolvePath2(p4) {
7631
+ return path_1.default.resolve(p4);
7632
+ }
7633
+ }
7634
+ });
7635
+
7636
+ // ../../packages/common/dist/src/utils/tokenUtils.js
7637
+ var require_tokenUtils = __commonJS({
7638
+ "../../packages/common/dist/src/utils/tokenUtils.js"(exports2) {
7639
+ "use strict";
7640
+ init_cjs_shims();
7641
+ Object.defineProperty(exports2, "__esModule", { value: true });
7642
+ exports2.extractTenantId = extractTenantId2;
7643
+ var TOKEN_PARSING = {
7644
+ SEPARATOR: ".",
7645
+ BASE64_HYPHEN_REPLACEMENT: "+",
7646
+ BASE64_UNDERSCORE_REPLACEMENT: "/",
7647
+ BASE64_HYPHEN_PATTERN: /-/g,
7648
+ BASE64_UNDERSCORE_PATTERN: /_/g,
7649
+ BASE64_ENCODING: "base64",
7650
+ TENANT_ID_CLAIM: "tid",
7651
+ TOKEN_PARTS_LENGTH: 3,
7652
+ PAYLOAD_INDEX: 1
7653
+ };
7654
+ function extractTenantId2(accessToken) {
7655
+ try {
7656
+ const tokenParts = accessToken.split(TOKEN_PARSING.SEPARATOR);
7657
+ if (tokenParts.length !== TOKEN_PARSING.TOKEN_PARTS_LENGTH) {
7658
+ return void 0;
7659
+ }
7660
+ let base64Url = tokenParts[TOKEN_PARSING.PAYLOAD_INDEX].replace(TOKEN_PARSING.BASE64_HYPHEN_PATTERN, TOKEN_PARSING.BASE64_HYPHEN_REPLACEMENT).replace(TOKEN_PARSING.BASE64_UNDERSCORE_PATTERN, TOKEN_PARSING.BASE64_UNDERSCORE_REPLACEMENT);
7661
+ while (base64Url.length % 4 !== 0) {
7662
+ base64Url += "=";
7663
+ }
7664
+ const payload = JSON.parse(Buffer.from(base64Url, TOKEN_PARSING.BASE64_ENCODING).toString());
7665
+ if (typeof payload === "object" && payload !== null) {
7666
+ const tenantId = payload[TOKEN_PARSING.TENANT_ID_CLAIM];
7667
+ return typeof tenantId === "string" ? tenantId : void 0;
7668
+ }
7669
+ return void 0;
7670
+ } catch {
7671
+ return void 0;
7672
+ }
7314
7673
  }
7315
7674
  }
7316
7675
  });
@@ -7334,12 +7693,14 @@ var require_utils = __commonJS({
7334
7693
  o[k2] = m[k];
7335
7694
  }));
7336
7695
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
7337
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
7696
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
7338
7697
  };
7339
7698
  Object.defineProperty(exports2, "__esModule", { value: true });
7340
7699
  __exportStar(require_correlationService(), exports2);
7700
+ __exportStar(require_decodeBase64Notebook(), exports2);
7341
7701
  __exportStar(require_pathUtils(), exports2);
7342
7702
  __exportStar(require_jobPayloadUtils(), exports2);
7703
+ __exportStar(require_tokenUtils(), exports2);
7343
7704
  }
7344
7705
  });
7345
7706
 
@@ -7362,7 +7723,7 @@ var require_src = __commonJS({
7362
7723
  o[k2] = m[k];
7363
7724
  }));
7364
7725
  var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
7365
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
7726
+ for (var p4 in m) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m, p4);
7366
7727
  };
7367
7728
  Object.defineProperty(exports2, "__esModule", { value: true });
7368
7729
  __exportStar(require_client(), exports2);
@@ -7372,6 +7733,7 @@ var require_src = __commonJS({
7372
7733
  __exportStar(require_models(), exports2);
7373
7734
  __exportStar(require_services(), exports2);
7374
7735
  __exportStar(require_errors(), exports2);
7736
+ __exportStar(require_telemetry(), exports2);
7375
7737
  __exportStar(require_utils(), exports2);
7376
7738
  }
7377
7739
  });
@@ -7415,103 +7777,1029 @@ function addCreateZipCommand(packageCommand) {
7415
7777
  });
7416
7778
  }
7417
7779
 
7418
- // src/commands/lake/database.ts
7780
+ // src/commands/jobs/jobDefinitionCreate.ts
7419
7781
  init_cjs_shims();
7420
- var import_common9 = __toESM(require_src());
7421
7782
 
7422
- // src/actions/lake/index.ts
7783
+ // src/actions/jobs/index.ts
7423
7784
  init_cjs_shims();
7424
7785
 
7425
- // src/actions/lake/listDatabases.ts
7786
+ // src/actions/jobs/listJobs.ts
7426
7787
  init_cjs_shims();
7427
7788
  var import_common3 = __toESM(require_src());
7428
- async function listDatabases(databaseService, accessToken) {
7789
+ async function listJobs(jobService, accessToken) {
7429
7790
  const requestId = import_common3.CorrelationService.createCorrelationContext();
7430
- console.info("Fetching databases...");
7431
- return databaseService.listDatabases(accessToken, requestId);
7791
+ console.info("Fetching jobs...");
7792
+ return jobService.listJobs(accessToken, requestId);
7432
7793
  }
7433
7794
 
7434
- // src/actions/lake/listTables.ts
7795
+ // src/actions/jobs/getJob.ts
7435
7796
  init_cjs_shims();
7436
7797
  var import_common4 = __toESM(require_src());
7437
- async function listTables(tableService, accessToken, database) {
7798
+ async function getJob(jobService, accessToken, jobName) {
7438
7799
  const requestId = import_common4.CorrelationService.createCorrelationContext();
7439
- const scope = database ? ` for database "${database.databaseName}"` : "";
7440
- console.info(`Fetching lake tables${scope}...`);
7441
- return tableService.listTables(accessToken, requestId, database);
7800
+ console.info(`Fetching job "${jobName}"...`);
7801
+ return jobService.getJob(jobName, accessToken, requestId);
7442
7802
  }
7443
7803
 
7444
- // src/services/lake/index.ts
7804
+ // src/actions/jobs/deleteJob.ts
7445
7805
  init_cjs_shims();
7806
+ var import_common5 = __toESM(require_src());
7807
+ async function deleteJob(jobService, accessToken, jobName) {
7808
+ const requestId = import_common5.CorrelationService.createCorrelationContext();
7809
+ console.info(`Deleting job "${jobName}"...`);
7810
+ return jobService.deleteJob(jobName, accessToken, requestId);
7811
+ }
7446
7812
 
7447
- // src/services/lake/databaseService.ts
7813
+ // src/actions/jobs/disableJob.ts
7448
7814
  init_cjs_shims();
7449
- var DatabaseService = class {
7450
- constructor(lakeApiClient) {
7451
- this.lakeApiClient = lakeApiClient;
7452
- }
7453
- async listDatabases(accessToken, requestId) {
7454
- return this.lakeApiClient.fetchDatabases(accessToken, requestId);
7455
- }
7456
- };
7815
+ var import_common6 = __toESM(require_src());
7816
+ async function disableJob(jobService, accessToken, jobName) {
7817
+ const requestId = import_common6.CorrelationService.createCorrelationContext();
7818
+ console.info(`Disabling job "${jobName}"...`);
7819
+ return jobService.disableJob(jobName, accessToken, requestId);
7820
+ }
7457
7821
 
7458
- // src/services/lake/tableService.ts
7822
+ // src/actions/jobs/enableJob.ts
7459
7823
  init_cjs_shims();
7460
- var TableService = class {
7461
- constructor(lakeApiClient) {
7462
- this.lakeApiClient = lakeApiClient;
7463
- }
7464
- async listTables(accessToken, requestId, database) {
7465
- return this.lakeApiClient.fetchLakeTables(accessToken, requestId, database);
7466
- }
7467
- };
7824
+ var import_common7 = __toESM(require_src());
7825
+ async function enableJob(jobService, accessToken, jobName) {
7826
+ const requestId = import_common7.CorrelationService.createCorrelationContext();
7827
+ console.info(`Enabling job "${jobName}"...`);
7828
+ return jobService.enableJob(jobName, accessToken, requestId);
7829
+ }
7468
7830
 
7469
- // src/services/lake/databaseLookup.ts
7831
+ // src/actions/jobs/downloadJob.ts
7470
7832
  init_cjs_shims();
7471
- var SYSTEM_TABLES_DISPLAY_NAME = "System tables";
7472
- var DEFAULT_DATABASE = { databaseName: "default" };
7473
- async function lookupDatabase(options, lakeApiClient, accessToken) {
7474
- if (options.databaseId) {
7475
- if (options.databaseId === SYSTEM_TABLES_DISPLAY_NAME) {
7476
- return DEFAULT_DATABASE;
7477
- }
7478
- return lookupDatabaseById(options.databaseId, lakeApiClient, accessToken);
7479
- }
7480
- if (options.databaseName) {
7481
- if (options.databaseName === SYSTEM_TABLES_DISPLAY_NAME) {
7482
- return DEFAULT_DATABASE;
7483
- }
7484
- return lookupDatabaseByName(options.databaseName, lakeApiClient, accessToken);
7485
- }
7486
- return void 0;
7833
+ var import_fs = __toESM(require("fs"));
7834
+ var import_path = __toESM(require("path"));
7835
+ var import_common8 = __toESM(require_src());
7836
+ async function downloadJob(jobService, accessToken, jobName, outputPath) {
7837
+ const requestId = import_common8.CorrelationService.createCorrelationContext();
7838
+ console.info(`Fetching job "${jobName}"...`);
7839
+ const job = await jobService.getJob(jobName, accessToken, requestId);
7840
+ const inlineScript = job.jobDefinition.inlineScript;
7841
+ const decoded = (0, import_common8.decodeBase64Notebook)(inlineScript, `job "${jobName}"`);
7842
+ const sanitizedJobName = import_path.default.basename(jobName).replace(/[^a-zA-Z0-9_-]/g, "_");
7843
+ const resolvedPath = import_path.default.resolve(outputPath ?? import_path.default.join(process.cwd(), `${sanitizedJobName}.ipynb`));
7844
+ await import_fs.default.promises.mkdir(import_path.default.dirname(resolvedPath), { recursive: true });
7845
+ await import_fs.default.promises.writeFile(resolvedPath, decoded, "utf-8");
7846
+ return resolvedPath;
7487
7847
  }
7488
- async function lookupDatabaseById(databaseId, lakeApiClient, accessToken) {
7489
- const databaseService = new DatabaseService(lakeApiClient);
7490
- const databases = await listDatabases(databaseService, accessToken);
7491
- const match = databases.find((db) => db.databaseName === databaseId);
7492
- if (!match) {
7493
- console.error(
7494
- `Error: database "${databaseId}" not found. Run "sentinel database list" to see available databases.`
7495
- );
7496
- process.exit(1);
7497
- }
7498
- return match;
7848
+
7849
+ // src/actions/jobs/listJobRuns.ts
7850
+ init_cjs_shims();
7851
+ var import_common9 = __toESM(require_src());
7852
+ async function listJobRuns(jobService, accessToken, jobName) {
7853
+ const requestId = import_common9.CorrelationService.createCorrelationContext();
7854
+ console.info(`Fetching runs for job "${jobName}"...`);
7855
+ return jobService.listJobRuns(jobName, accessToken, requestId);
7499
7856
  }
7500
- async function lookupDatabaseByName(databaseName, lakeApiClient, accessToken) {
7501
- const databaseService = new DatabaseService(lakeApiClient);
7502
- const databases = await listDatabases(databaseService, accessToken);
7503
- const match = databases.find((db) => db.sentinelWorkspace?.name === databaseName);
7504
- if (!match) {
7505
- console.error(
7506
- `Error: database "${databaseName}" not found. Run "sentinel database list" to see available databases.`
7857
+
7858
+ // src/actions/jobs/getJobRun.ts
7859
+ init_cjs_shims();
7860
+ var import_common10 = __toESM(require_src());
7861
+ async function getJobRun(jobService, accessToken, jobName, runId) {
7862
+ const requestId = import_common10.CorrelationService.createCorrelationContext();
7863
+ console.info(`Fetching run "${runId}" for job "${jobName}"...`);
7864
+ return jobService.getJobRun(jobName, runId, accessToken, requestId);
7865
+ }
7866
+
7867
+ // src/actions/jobs/startJobRun.ts
7868
+ init_cjs_shims();
7869
+ var import_common11 = __toESM(require_src());
7870
+ async function startJobRun(jobService, accessToken, jobName) {
7871
+ const requestId = import_common11.CorrelationService.createCorrelationContext();
7872
+ console.info(`Starting run for job "${jobName}"...`);
7873
+ return jobService.startJobRun(jobName, accessToken, requestId);
7874
+ }
7875
+
7876
+ // src/actions/jobs/cancelJobRun.ts
7877
+ init_cjs_shims();
7878
+ var import_common12 = __toESM(require_src());
7879
+ async function cancelJobRun(jobService, accessToken, jobName, runId) {
7880
+ const requestId = import_common12.CorrelationService.createCorrelationContext();
7881
+ console.info(`Cancelling run "${runId}" for job "${jobName}"...`);
7882
+ return jobService.cancelJobRun(jobName, runId, accessToken, requestId);
7883
+ }
7884
+
7885
+ // src/actions/jobs/downloadJobRunNotebook.ts
7886
+ init_cjs_shims();
7887
+ var import_fs2 = __toESM(require("fs"));
7888
+ var import_path2 = __toESM(require("path"));
7889
+ var import_common13 = __toESM(require_src());
7890
+ async function downloadJobRunNotebook(jobService, accessToken, jobName, runId, outputPath) {
7891
+ const requestId = import_common13.CorrelationService.createCorrelationContext();
7892
+ console.info(`Fetching run "${runId}" for job "${jobName}"...`);
7893
+ const run = await jobService.getJobRun(jobName, runId, accessToken, requestId);
7894
+ const scriptSnapshot = run.scriptSnapshot;
7895
+ const decoded = (0, import_common13.decodeBase64Notebook)(scriptSnapshot, `run "${runId}" for job "${jobName}"`);
7896
+ const sanitizedJobName = import_path2.default.basename(jobName).replace(/[^a-zA-Z0-9_-]/g, "_");
7897
+ const sanitizedRunId = import_path2.default.basename(runId).replace(/[^a-zA-Z0-9_-]/g, "_");
7898
+ const defaultFileName = `${sanitizedJobName}_${sanitizedRunId}.ipynb`;
7899
+ const resolvedPath = import_path2.default.resolve(outputPath ?? import_path2.default.join(process.cwd(), defaultFileName));
7900
+ await import_fs2.default.promises.mkdir(import_path2.default.dirname(resolvedPath), { recursive: true });
7901
+ await import_fs2.default.promises.writeFile(resolvedPath, decoded, "utf-8");
7902
+ return resolvedPath;
7903
+ }
7904
+
7905
+ // src/actions/jobs/createJobDefinition.ts
7906
+ init_cjs_shims();
7907
+ var p2 = __toESM(require("@clack/prompts"));
7908
+ var path3 = __toESM(require("path"));
7909
+
7910
+ // src/utils/validator/jobDefinitionValidator.ts
7911
+ init_cjs_shims();
7912
+ var import_zod = require("zod");
7913
+ var ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})?)?$/;
7914
+ var JOB_TYPES = ["Notebook", "GraphNotebook", "Kql"];
7915
+ var NODE_SIZES = ["small", "medium", "large"];
7916
+ var REPEAT_FREQUENCIES = ["minutes", "hours", "days", "weeks", "months"];
7917
+ var INTERVAL_BOUNDS = {
7918
+ minutes: { min: 15, max: 720 },
7919
+ hours: { min: 1, max: 72 }
7920
+ };
7921
+ var FIXED_INTERVAL_FREQUENCIES = new Set(
7922
+ REPEAT_FREQUENCIES.filter((f) => !(f in INTERVAL_BOUNDS))
7923
+ );
7924
+ var JOB_NAME_PATTERN = /^[a-zA-Z0-9 _-]+$/;
7925
+ var JOB_NAME_PATTERN_MESSAGE = "Job name may only contain letters, numbers, spaces, hyphens, or underscores";
7926
+ var VALID_WEEK_DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
7927
+ function isValidISODate(value) {
7928
+ if (!ISO_DATE_PATTERN.test(value)) {
7929
+ return false;
7930
+ }
7931
+ return !isNaN(new Date(value).getTime());
7932
+ }
7933
+ var timeTableSchema = import_zod.z.object({
7934
+ weekDays: import_zod.z.array(import_zod.z.enum(VALID_WEEK_DAYS)).optional(),
7935
+ monthDays: import_zod.z.array(import_zod.z.number().int().min(1).max(31)).optional()
7936
+ }).optional();
7937
+ var scheduleConfigSchema = import_zod.z.object({
7938
+ isEnabled: import_zod.z.boolean(),
7939
+ repeatFrequency: import_zod.z.enum(REPEAT_FREQUENCIES).optional(),
7940
+ interval: import_zod.z.number().int().positive().optional(),
7941
+ startTime: import_zod.z.string().optional(),
7942
+ endTime: import_zod.z.string().optional(),
7943
+ timeTable: timeTableSchema
7944
+ });
7945
+ var jobDefinitionValidator = import_zod.z.object({
7946
+ jobName: import_zod.z.string().min(1, "Job name is required").regex(JOB_NAME_PATTERN, JOB_NAME_PATTERN_MESSAGE),
7947
+ jobPath: import_zod.z.string().min(1, "Job path is required"),
7948
+ jobType: import_zod.z.enum(JOB_TYPES).optional(),
7949
+ jobDescription: import_zod.z.string().optional(),
7950
+ computeInfo: import_zod.z.object({
7951
+ nodeSize: import_zod.z.enum(NODE_SIZES)
7952
+ }).optional(),
7953
+ scheduleConfig: scheduleConfigSchema.optional(),
7954
+ inputTables: import_zod.z.array(import_zod.z.string()).optional(),
7955
+ outputTables: import_zod.z.array(import_zod.z.string()).optional(),
7956
+ runtimeArgs: import_zod.z.array(import_zod.z.string()).optional(),
7957
+ isDisabled: import_zod.z.boolean().optional()
7958
+ }).refine(
7959
+ (data) => {
7960
+ const jobType = data.jobType ?? "Notebook";
7961
+ if (jobType === "Kql") {
7962
+ return true;
7963
+ }
7964
+ return data.jobPath.toLowerCase().endsWith(".ipynb");
7965
+ },
7966
+ { message: "Job path must be a Python notebook (.ipynb) file", path: ["jobPath"] }
7967
+ ).refine(
7968
+ (data) => {
7969
+ if (data.scheduleConfig?.isEnabled) {
7970
+ return !!data.scheduleConfig.repeatFrequency;
7971
+ }
7972
+ return true;
7973
+ },
7974
+ { message: "repeatFrequency is required for scheduled jobs", path: ["scheduleConfig", "repeatFrequency"] }
7975
+ ).refine(
7976
+ (data) => {
7977
+ const sc = data.scheduleConfig;
7978
+ if (!sc?.isEnabled || !sc.repeatFrequency) {
7979
+ return true;
7980
+ }
7981
+ return sc.interval !== void 0;
7982
+ },
7983
+ { message: "Interval is required for scheduled jobs", path: ["scheduleConfig", "interval"] }
7984
+ ).refine(
7985
+ (data) => {
7986
+ const sc = data.scheduleConfig;
7987
+ if (!sc?.isEnabled || sc.interval === void 0 || !sc.repeatFrequency) {
7988
+ return true;
7989
+ }
7990
+ const bounds = INTERVAL_BOUNDS[sc.repeatFrequency];
7991
+ if (bounds) {
7992
+ return sc.interval >= bounds.min && sc.interval <= bounds.max;
7993
+ }
7994
+ if (FIXED_INTERVAL_FREQUENCIES.has(sc.repeatFrequency)) {
7995
+ return sc.interval === 1;
7996
+ }
7997
+ return true;
7998
+ },
7999
+ {
8000
+ message: "Schedule interval is out of range for the selected frequency",
8001
+ path: ["scheduleConfig", "interval"]
8002
+ }
8003
+ ).refine(
8004
+ (data) => {
8005
+ const sc = data.scheduleConfig;
8006
+ if (!sc?.isEnabled || !sc.startTime) {
8007
+ return true;
8008
+ }
8009
+ return isValidISODate(sc.startTime);
8010
+ },
8011
+ { message: "Start time must be a valid ISO 8601 date", path: ["scheduleConfig", "startTime"] }
8012
+ ).refine(
8013
+ (data) => {
8014
+ const sc = data.scheduleConfig;
8015
+ if (!sc?.isEnabled || !sc.endTime) {
8016
+ return true;
8017
+ }
8018
+ return isValidISODate(sc.endTime);
8019
+ },
8020
+ { message: "End time must be a valid ISO 8601 date", path: ["scheduleConfig", "endTime"] }
8021
+ ).refine(
8022
+ (data) => {
8023
+ const sc = data.scheduleConfig;
8024
+ if (!sc?.isEnabled || !sc.startTime || !sc.endTime) {
8025
+ return true;
8026
+ }
8027
+ const start = new Date(sc.startTime);
8028
+ const end = new Date(sc.endTime);
8029
+ if (isNaN(start.getTime()) || isNaN(end.getTime())) {
8030
+ return true;
8031
+ }
8032
+ return end > start;
8033
+ },
8034
+ { message: "End time must be after start time", path: ["scheduleConfig", "endTime"] }
8035
+ ).refine(
8036
+ (data) => {
8037
+ const sc = data.scheduleConfig;
8038
+ if (!sc?.isEnabled || sc.repeatFrequency !== "weeks") {
8039
+ return true;
8040
+ }
8041
+ const days = sc.timeTable?.weekDays;
8042
+ return Array.isArray(days) && days.length > 0;
8043
+ },
8044
+ { message: "At least one day of the week is required for weekly schedule", path: ["scheduleConfig", "timeTable"] }
8045
+ ).refine(
8046
+ (data) => {
8047
+ const sc = data.scheduleConfig;
8048
+ if (!sc?.isEnabled || sc.repeatFrequency !== "months") {
8049
+ return true;
8050
+ }
8051
+ const days = sc.timeTable?.monthDays;
8052
+ return Array.isArray(days) && days.length > 0;
8053
+ },
8054
+ {
8055
+ message: "At least one day of the month is required for monthly schedule",
8056
+ path: ["scheduleConfig", "timeTable", "monthDays"]
8057
+ }
8058
+ );
8059
+
8060
+ // src/actions/jobs/promptHelpers.ts
8061
+ init_cjs_shims();
8062
+ var fs3 = __toESM(require("fs"));
8063
+ var p = __toESM(require("@clack/prompts"));
8064
+ var DEFAULT_NODE_SIZE = "large";
8065
+ var NODE_SIZE_OPTIONS = NODE_SIZES.map((size) => ({
8066
+ value: size,
8067
+ label: size === DEFAULT_NODE_SIZE ? `${capitalize(size)} (default)` : capitalize(size)
8068
+ }));
8069
+ var FREQUENCY_OPTIONS = REPEAT_FREQUENCIES.map((freq) => ({
8070
+ value: freq,
8071
+ label: capitalize(freq)
8072
+ }));
8073
+ var CONFIGURABLE_INTERVAL_FREQUENCIES = new Set(
8074
+ Object.keys(INTERVAL_BOUNDS)
8075
+ );
8076
+ var WEEKDAY_OPTIONS = [
8077
+ { value: "Monday", label: "Monday" },
8078
+ { value: "Tuesday", label: "Tuesday" },
8079
+ { value: "Wednesday", label: "Wednesday" },
8080
+ { value: "Thursday", label: "Thursday" },
8081
+ { value: "Friday", label: "Friday" },
8082
+ { value: "Saturday", label: "Saturday" },
8083
+ { value: "Sunday", label: "Sunday" }
8084
+ ];
8085
+ function capitalize(s) {
8086
+ return s.charAt(0).toUpperCase() + s.slice(1);
8087
+ }
8088
+ function handleCancel(value) {
8089
+ if (p.isCancel(value)) {
8090
+ p.cancel("Operation cancelled.");
8091
+ process.exit(0);
8092
+ }
8093
+ }
8094
+ async function promptIfMissing(flagValue, promptFn) {
8095
+ if (flagValue !== void 0) {
8096
+ return flagValue;
8097
+ }
8098
+ const result = await promptFn();
8099
+ handleCancel(result);
8100
+ return result;
8101
+ }
8102
+ function validateInterval(frequency) {
8103
+ return (v) => {
8104
+ if (!v || v.trim() === "") {
8105
+ return "Interval is required";
8106
+ }
8107
+ const trimmed = v.trim();
8108
+ if (!/^\d+$/.test(trimmed)) {
8109
+ return "Please enter a whole number";
8110
+ }
8111
+ const n = parseInt(trimmed, 10);
8112
+ const bounds = INTERVAL_BOUNDS[frequency];
8113
+ if (bounds) {
8114
+ if (n < bounds.min) {
8115
+ return `Minimum value is ${bounds.min} ${frequency}`;
8116
+ }
8117
+ if (n > bounds.max) {
8118
+ return `Maximum value is ${bounds.max} ${frequency}`;
8119
+ }
8120
+ } else if (FIXED_INTERVAL_FREQUENCIES.has(frequency)) {
8121
+ if (n !== 1) {
8122
+ return `Interval must be 1 for ${frequency}`;
8123
+ }
8124
+ }
8125
+ return void 0;
8126
+ };
8127
+ }
8128
+ function validateJobPath(v) {
8129
+ if (!v) {
8130
+ return "Job path is required";
8131
+ }
8132
+ if (!v.toLowerCase().endsWith(".ipynb")) {
8133
+ return "Job path must be a Python notebook (.ipynb) file";
8134
+ }
8135
+ if (!fs3.existsSync(v)) {
8136
+ return `File not found at "${v}". Please provide a valid path to an existing .ipynb file`;
8137
+ }
8138
+ return void 0;
8139
+ }
8140
+ function validateISODate(v) {
8141
+ if (!v) {
8142
+ return "Date/time is required";
8143
+ }
8144
+ const iso8601Pattern = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})?)?$/;
8145
+ if (!iso8601Pattern.test(v)) {
8146
+ return "Must be a valid ISO 8601 date (e.g. 2026-01-01T09:00:00Z)";
8147
+ }
8148
+ const d = new Date(v);
8149
+ if (isNaN(d.getTime())) {
8150
+ return "Must be a valid ISO 8601 date (e.g. 2026-01-01T09:00:00Z)";
8151
+ }
8152
+ return void 0;
8153
+ }
8154
+ function validateEndTimeAfterStart(startTime, endTime) {
8155
+ if (!endTime) {
8156
+ return "End time is required";
8157
+ }
8158
+ const start = new Date(startTime);
8159
+ const end = new Date(endTime);
8160
+ if (isNaN(start.getTime()) || isNaN(end.getTime())) {
8161
+ return "Please enter valid date and time values";
8162
+ }
8163
+ if (end <= start) {
8164
+ return "End time must be after start time";
8165
+ }
8166
+ return void 0;
8167
+ }
8168
+ function validateJobName(v) {
8169
+ if (!v) {
8170
+ return "Job name is required";
8171
+ }
8172
+ if (!JOB_NAME_PATTERN.test(v)) {
8173
+ return JOB_NAME_PATTERN_MESSAGE;
8174
+ }
8175
+ return void 0;
8176
+ }
8177
+ function validateEndTime(startTime) {
8178
+ return (v) => {
8179
+ const isoErr = validateISODate(v);
8180
+ if (isoErr) {
8181
+ return isoErr;
8182
+ }
8183
+ return validateEndTimeAfterStart(startTime, v);
8184
+ };
8185
+ }
8186
+
8187
+ // src/utils/yaml/yamlIO.ts
8188
+ init_cjs_shims();
8189
+ var fs4 = __toESM(require("fs"));
8190
+ var YAML = __toESM(require("yaml"));
8191
+ function readJobDefinitionYaml(filePath) {
8192
+ if (!fs4.existsSync(filePath)) {
8193
+ throw new Error(`File not found: ${filePath}`);
8194
+ }
8195
+ let raw;
8196
+ try {
8197
+ const content = fs4.readFileSync(filePath, "utf-8");
8198
+ raw = YAML.parse(content);
8199
+ } catch (err) {
8200
+ const message = err instanceof Error ? err.message : String(err);
8201
+ throw new Error(`Failed to read/parse YAML: ${message}`);
8202
+ }
8203
+ const result = jobDefinitionValidator.safeParse(raw);
8204
+ if (result.success) {
8205
+ return result.data;
8206
+ }
8207
+ const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
8208
+ throw new Error(`Validation failed:
8209
+ ${issues.join("\n")}`);
8210
+ }
8211
+ function writeJobDefinitionYaml(filePath, config) {
8212
+ const result = jobDefinitionValidator.safeParse(config);
8213
+ if (!result.success) {
8214
+ const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
8215
+ return { success: false, issues };
8216
+ }
8217
+ try {
8218
+ const yamlContent = YAML.stringify(result.data, { lineWidth: 0 });
8219
+ fs4.writeFileSync(filePath, yamlContent, "utf-8");
8220
+ return { success: true, issues: [] };
8221
+ } catch (err) {
8222
+ const message = err instanceof Error ? err.message : String(err);
8223
+ return { success: false, issues: [`Failed to write file "${filePath}": ${message}`] };
8224
+ }
8225
+ }
8226
+
8227
+ // src/actions/jobs/createJobDefinition.ts
8228
+ function sanitizeFileName(fileName) {
8229
+ return fileName.replace(/[<>:"/\\|?*]/g, "_").replace(/\s+/g, "_");
8230
+ }
8231
+ function fail(message) {
8232
+ throw new Error(message);
8233
+ }
8234
+ async function collectJobDetails(jobNameArg, options) {
8235
+ const jobName = await promptIfMissing(
8236
+ jobNameArg,
8237
+ () => p2.text({
8238
+ message: "Job name:",
8239
+ validate: validateJobName
8240
+ })
8241
+ );
8242
+ if (jobNameArg !== void 0) {
8243
+ const nameError = validateJobName(jobName);
8244
+ if (nameError) {
8245
+ fail(nameError);
8246
+ }
8247
+ }
8248
+ const jobDescription = await promptIfMissing(
8249
+ options.description,
8250
+ () => p2.text({ message: "Description (optional):", defaultValue: "" })
8251
+ );
8252
+ const jobPath = await promptIfMissing(
8253
+ options.notebook,
8254
+ () => p2.text({
8255
+ message: "Job path (.ipynb):",
8256
+ placeholder: "path/to/notebook.ipynb",
8257
+ validate: validateJobPath
8258
+ })
8259
+ );
8260
+ if (options.notebook !== void 0) {
8261
+ const pathError = validateJobPath(jobPath);
8262
+ if (pathError) {
8263
+ fail(pathError);
8264
+ }
8265
+ }
8266
+ return { jobName, jobDescription, jobPath };
8267
+ }
8268
+ async function collectCompute(options) {
8269
+ if (options.cluster) {
8270
+ const normalized = options.cluster.toLowerCase();
8271
+ if (!NODE_SIZES.includes(normalized)) {
8272
+ fail(`Invalid cluster size "${options.cluster}". Must be one of: ${NODE_SIZES.join(", ")}`);
8273
+ }
8274
+ return normalized;
8275
+ }
8276
+ const result = await p2.select({
8277
+ message: "Cluster size:",
8278
+ options: NODE_SIZE_OPTIONS,
8279
+ initialValue: DEFAULT_NODE_SIZE
8280
+ });
8281
+ handleCancel(result);
8282
+ return result;
8283
+ }
8284
+ async function collectSchedule(options) {
8285
+ let wantsSchedule = !!options.schedule;
8286
+ if (!wantsSchedule) {
8287
+ const result = await p2.confirm({ message: "Schedule this job?", initialValue: true });
8288
+ handleCancel(result);
8289
+ wantsSchedule = result;
8290
+ }
8291
+ if (!wantsSchedule) {
8292
+ return { isEnabled: false };
8293
+ }
8294
+ const repeatFrequency = await promptIfMissing(
8295
+ options.schedule,
8296
+ () => p2.select({ message: "Repeat frequency:", options: FREQUENCY_OPTIONS })
8297
+ );
8298
+ if (options.schedule && !REPEAT_FREQUENCIES.includes(repeatFrequency)) {
8299
+ fail(`Invalid schedule frequency "${options.schedule}". Must be one of: ${REPEAT_FREQUENCIES.join(", ")}`);
8300
+ }
8301
+ let interval = 1;
8302
+ if (CONFIGURABLE_INTERVAL_FREQUENCIES.has(repeatFrequency)) {
8303
+ const intervalStr = await promptIfMissing(
8304
+ options.interval,
8305
+ () => p2.text({
8306
+ message: `Interval (${repeatFrequency}):`,
8307
+ defaultValue: repeatFrequency === "minutes" ? "15" : "1",
8308
+ validate: validateInterval(repeatFrequency)
8309
+ })
7507
8310
  );
7508
- process.exit(1);
8311
+ if (options.interval) {
8312
+ const intervalError = validateInterval(repeatFrequency)(intervalStr);
8313
+ if (intervalError) {
8314
+ fail(intervalError);
8315
+ }
8316
+ }
8317
+ interval = Number(intervalStr);
7509
8318
  }
7510
- return match;
8319
+ let timeTable;
8320
+ if (repeatFrequency === "weeks") {
8321
+ const result = await p2.multiselect({
8322
+ message: "Which days of the week?",
8323
+ options: [...WEEKDAY_OPTIONS],
8324
+ required: true
8325
+ });
8326
+ handleCancel(result);
8327
+ timeTable = { weekDays: result };
8328
+ }
8329
+ const startTime = await promptIfMissing(
8330
+ options.startTime,
8331
+ () => p2.text({
8332
+ message: "Start time (ISO 8601):",
8333
+ defaultValue: (/* @__PURE__ */ new Date()).toISOString(),
8334
+ validate: validateISODate
8335
+ })
8336
+ );
8337
+ if (options.startTime) {
8338
+ const startError = validateISODate(startTime);
8339
+ if (startError) {
8340
+ fail(startError);
8341
+ }
8342
+ }
8343
+ if (repeatFrequency === "months") {
8344
+ const dayOfMonth = new Date(startTime).getDate();
8345
+ timeTable = { monthDays: [dayOfMonth] };
8346
+ }
8347
+ let endTime;
8348
+ if (options.endTime) {
8349
+ endTime = options.endTime;
8350
+ } else {
8351
+ let endTimeResolved = false;
8352
+ while (!endTimeResolved) {
8353
+ const setEndTime = await p2.confirm({ message: "Set an end time?", initialValue: false });
8354
+ handleCancel(setEndTime);
8355
+ if (setEndTime) {
8356
+ const result = await p2.text({
8357
+ message: "End time (ISO 8601):",
8358
+ validate: validateEndTime(startTime)
8359
+ });
8360
+ handleCancel(result);
8361
+ endTime = result;
8362
+ endTimeResolved = true;
8363
+ } else {
8364
+ p2.log.warn("This job will run indefinitely until manually stopped or deleted.");
8365
+ const confirmIndefinite = await p2.confirm({ message: "Run indefinitely?", initialValue: true });
8366
+ handleCancel(confirmIndefinite);
8367
+ if (confirmIndefinite) {
8368
+ endTimeResolved = true;
8369
+ }
8370
+ }
8371
+ }
8372
+ }
8373
+ if (options.endTime) {
8374
+ const endError = validateEndTimeAfterStart(startTime, endTime);
8375
+ if (endError) {
8376
+ fail(endError);
8377
+ }
8378
+ }
8379
+ return { isEnabled: true, repeatFrequency, interval, startTime, endTime, timeTable };
8380
+ }
8381
+ async function createJobDefinition(jobNameArg, options) {
8382
+ p2.intro("Create a job definition");
8383
+ const { jobName, jobDescription, jobPath } = await collectJobDetails(jobNameArg, options);
8384
+ const nodeSize = await collectCompute(options);
8385
+ const schedule = await collectSchedule(options);
8386
+ const config = {
8387
+ jobName,
8388
+ jobPath,
8389
+ jobType: "Notebook",
8390
+ ...jobDescription && { jobDescription },
8391
+ computeInfo: { nodeSize },
8392
+ ...schedule.isEnabled && {
8393
+ scheduleConfig: {
8394
+ isEnabled: true,
8395
+ repeatFrequency: schedule.repeatFrequency,
8396
+ interval: schedule.interval,
8397
+ startTime: schedule.startTime,
8398
+ endTime: schedule.endTime,
8399
+ ...schedule.timeTable && { timeTable: schedule.timeTable }
8400
+ }
8401
+ }
8402
+ };
8403
+ const outputPath = path3.resolve(options.output ?? `${sanitizeFileName(jobName)}.yaml`);
8404
+ const result = writeJobDefinitionYaml(outputPath, config);
8405
+ if (!result.success) {
8406
+ for (const issue of result.issues) {
8407
+ p2.log.error(issue);
8408
+ }
8409
+ throw new Error("Validation failed");
8410
+ }
8411
+ p2.log.success(`Written to ${outputPath}`);
8412
+ p2.outro("Job definition created successfully!");
8413
+ }
8414
+
8415
+ // src/actions/jobs/showJobDefinition.ts
8416
+ init_cjs_shims();
8417
+
8418
+ // src/utils/formatter/outputFormatter.ts
8419
+ init_cjs_shims();
8420
+ var COLUMN_SEPARATOR = " ";
8421
+ var HEADER_RULE_CHAR = "\u2500";
8422
+ var OutputFormatter = class {
8423
+ format;
8424
+ constructor(options = {}) {
8425
+ this.format = options.output ?? "table";
8426
+ }
8427
+ /**
8428
+ * Renders `rows` to stdout using the configured output format.
8429
+ *
8430
+ * @param rows - The data array to render.
8431
+ * @param columns - Column descriptors that define headers and value extractors.
8432
+ */
8433
+ print(rows, columns) {
8434
+ if (this.format === "json") {
8435
+ this.printJson(rows, columns);
8436
+ } else {
8437
+ this.printTable(rows, columns);
8438
+ }
8439
+ }
8440
+ // ---------------------------------------------------------------------------
8441
+ // Table rendering
8442
+ // ---------------------------------------------------------------------------
8443
+ printTable(rows, columns) {
8444
+ if (rows.length === 0) {
8445
+ console.log("(no results)");
8446
+ return;
8447
+ }
8448
+ const widths = columns.map((col) => {
8449
+ const cellWidths = rows.map((row) => String(col.key(row) ?? "").length);
8450
+ return Math.max(col.header.length, ...cellWidths);
8451
+ });
8452
+ const header = columns.map((col, i) => col.header.padEnd(widths[i])).join(COLUMN_SEPARATOR);
8453
+ console.log(header);
8454
+ const rule = widths.map((w) => HEADER_RULE_CHAR.repeat(w)).join(COLUMN_SEPARATOR);
8455
+ console.log(rule);
8456
+ for (const row of rows) {
8457
+ const line = columns.map((col, i) => String(col.key(row) ?? "").padEnd(widths[i])).join(COLUMN_SEPARATOR);
8458
+ console.log(line);
8459
+ }
8460
+ }
8461
+ // ---------------------------------------------------------------------------
8462
+ // JSON rendering
8463
+ // ---------------------------------------------------------------------------
8464
+ printJson(rows, columns) {
8465
+ const records = rows.map((row) => {
8466
+ const record = {};
8467
+ for (const col of columns) {
8468
+ const key = col.jsonKey ?? col.header.toLowerCase();
8469
+ const value = col.key(row);
8470
+ record[key] = value ?? null;
8471
+ }
8472
+ return record;
8473
+ });
8474
+ console.log(JSON.stringify(records, null, 2));
8475
+ }
8476
+ };
8477
+
8478
+ // src/actions/jobs/showJobDefinition.ts
8479
+ var FIELD_COLUMNS = [
8480
+ { header: "FIELD", key: (f) => f.label },
8481
+ { header: "VALUE", key: (f) => f.value }
8482
+ ];
8483
+ function buildJobFields(data) {
8484
+ const fields = [
8485
+ { label: "Name", value: data.jobName },
8486
+ { label: "Description", value: data.jobDescription ?? "" },
8487
+ { label: "Type", value: data.jobType ?? "" },
8488
+ { label: "Job Path", value: data.jobPath },
8489
+ { label: "Cluster", value: data.computeInfo?.nodeSize ? capitalize(data.computeInfo.nodeSize) : "" },
8490
+ { label: "Disabled", value: data.isDisabled ? "Yes" : "No" }
8491
+ ];
8492
+ const schedule = data.scheduleConfig;
8493
+ if (schedule?.isEnabled) {
8494
+ const freq = schedule.repeatFrequency ? capitalize(schedule.repeatFrequency) : "";
8495
+ const interval = schedule.interval ?? 1;
8496
+ fields.push({ label: "Schedule", value: `Every ${interval} ${freq}` });
8497
+ if (schedule.timeTable?.weekDays?.length) {
8498
+ fields.push({ label: "Weekly Days", value: schedule.timeTable.weekDays.join(", ") });
8499
+ }
8500
+ if (schedule.timeTable?.monthDays?.length) {
8501
+ fields.push({ label: "Monthly Days", value: schedule.timeTable.monthDays.join(", ") });
8502
+ }
8503
+ fields.push({ label: "Start Time", value: schedule.startTime ?? "" });
8504
+ fields.push({ label: "End Time", value: schedule.endTime ?? "(indefinite)" });
8505
+ } else {
8506
+ fields.push({ label: "Schedule", value: "On-demand" });
8507
+ }
8508
+ fields.push({ label: "Input Tables", value: data.inputTables?.length ? data.inputTables.join(", ") : "(none)" });
8509
+ fields.push({ label: "Output Tables", value: data.outputTables?.length ? data.outputTables.join(", ") : "(none)" });
8510
+ if (data.runtimeArgs?.length) {
8511
+ fields.push({ label: "Runtime Args", value: data.runtimeArgs.join(", ") });
8512
+ }
8513
+ return fields;
8514
+ }
8515
+ function showJobDefinition(filePath, options) {
8516
+ const data = readJobDefinitionYaml(filePath);
8517
+ const fmt = new OutputFormatter({ output: options.output ?? "table" });
8518
+ const fields = buildJobFields(data);
8519
+ fmt.print(fields, FIELD_COLUMNS);
7511
8520
  }
7512
8521
 
8522
+ // src/actions/jobs/updateJobDefinition.ts
8523
+ init_cjs_shims();
8524
+ var p3 = __toESM(require("@clack/prompts"));
8525
+ var UPDATABLE_FIELDS = [
8526
+ { value: "description", label: "Description" },
8527
+ { value: "jobPath", label: "Job path" },
8528
+ { value: "cluster", label: "Cluster size" },
8529
+ { value: "schedule", label: "Schedule" },
8530
+ { value: "inputTables", label: "Input tables" },
8531
+ { value: "outputTables", label: "Output tables" },
8532
+ { value: "runtimeArgs", label: "Runtime arguments" }
8533
+ ];
8534
+ async function updateDescription(config) {
8535
+ const result = await p3.text({
8536
+ message: "New description:",
8537
+ defaultValue: config.jobDescription ?? ""
8538
+ });
8539
+ handleCancel(result);
8540
+ config.jobDescription = result;
8541
+ }
8542
+ async function updateJobPath(config) {
8543
+ const result = await p3.text({
8544
+ message: "New job path (.ipynb):",
8545
+ defaultValue: config.jobPath,
8546
+ validate: validateJobPath
8547
+ });
8548
+ handleCancel(result);
8549
+ config.jobPath = result;
8550
+ }
8551
+ async function updateCluster(config) {
8552
+ const result = await p3.select({
8553
+ message: "New cluster size:",
8554
+ options: NODE_SIZE_OPTIONS,
8555
+ initialValue: config.computeInfo?.nodeSize ?? DEFAULT_NODE_SIZE
8556
+ });
8557
+ handleCancel(result);
8558
+ config.computeInfo = { nodeSize: result };
8559
+ }
8560
+ async function updateSchedule(config) {
8561
+ const existing = config.scheduleConfig;
8562
+ const wantsSchedule = await p3.confirm({
8563
+ message: "Enable schedule?",
8564
+ initialValue: existing?.isEnabled ?? true
8565
+ });
8566
+ handleCancel(wantsSchedule);
8567
+ if (!wantsSchedule) {
8568
+ config.scheduleConfig = { isEnabled: false };
8569
+ return;
8570
+ }
8571
+ const frequency = await p3.select({
8572
+ message: "Repeat frequency:",
8573
+ options: FREQUENCY_OPTIONS,
8574
+ initialValue: existing?.repeatFrequency ?? "days"
8575
+ });
8576
+ handleCancel(frequency);
8577
+ const repeatFrequency = frequency;
8578
+ let interval = 1;
8579
+ if (CONFIGURABLE_INTERVAL_FREQUENCIES.has(repeatFrequency)) {
8580
+ const defaultInterval = existing?.interval ?? (repeatFrequency === "minutes" ? 15 : 1);
8581
+ const intervalStr = await p3.text({
8582
+ message: `Interval (${repeatFrequency}):`,
8583
+ defaultValue: String(defaultInterval),
8584
+ validate: validateInterval(repeatFrequency)
8585
+ });
8586
+ handleCancel(intervalStr);
8587
+ interval = Number(intervalStr);
8588
+ }
8589
+ let timeTable;
8590
+ if (repeatFrequency === "weeks") {
8591
+ const existingDays = existing?.timeTable?.weekDays ?? [];
8592
+ const result = await p3.multiselect({
8593
+ message: "Which days of the week?",
8594
+ options: WEEKDAY_OPTIONS.map((opt) => ({
8595
+ ...opt,
8596
+ selected: existingDays.includes(opt.value)
8597
+ })),
8598
+ required: true
8599
+ });
8600
+ handleCancel(result);
8601
+ timeTable = { weekDays: result };
8602
+ }
8603
+ const startTime = await p3.text({
8604
+ message: "Start time (ISO 8601):",
8605
+ defaultValue: existing?.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
8606
+ validate: validateISODate
8607
+ });
8608
+ handleCancel(startTime);
8609
+ if (repeatFrequency === "months") {
8610
+ const dayOfMonth = new Date(startTime).getDate();
8611
+ timeTable = { monthDays: [dayOfMonth] };
8612
+ }
8613
+ let endTime;
8614
+ const setEndTime = await p3.confirm({
8615
+ message: "Set an end time?",
8616
+ initialValue: !!existing?.endTime
8617
+ });
8618
+ handleCancel(setEndTime);
8619
+ if (setEndTime) {
8620
+ const result = await p3.text({
8621
+ message: "End time (ISO 8601):",
8622
+ defaultValue: existing?.endTime ?? "",
8623
+ validate: validateEndTime(startTime)
8624
+ });
8625
+ handleCancel(result);
8626
+ endTime = result;
8627
+ }
8628
+ config.scheduleConfig = {
8629
+ isEnabled: true,
8630
+ repeatFrequency,
8631
+ interval,
8632
+ startTime,
8633
+ endTime,
8634
+ ...timeTable && { timeTable }
8635
+ };
8636
+ }
8637
+ async function updateStringArray(config, field, label) {
8638
+ const current = config[field] ?? [];
8639
+ const result = await p3.text({
8640
+ message: `${label} (comma-separated):`,
8641
+ defaultValue: current.join(", ")
8642
+ });
8643
+ handleCancel(result);
8644
+ const raw = result;
8645
+ config[field] = raw.trim() ? raw.split(",").map((s) => s.trim()) : [];
8646
+ }
8647
+ async function applyFieldUpdate(field, config) {
8648
+ switch (field) {
8649
+ case "description":
8650
+ return updateDescription(config);
8651
+ case "jobPath":
8652
+ return updateJobPath(config);
8653
+ case "cluster":
8654
+ return updateCluster(config);
8655
+ case "schedule":
8656
+ return updateSchedule(config);
8657
+ case "inputTables":
8658
+ return updateStringArray(config, "inputTables", "Input tables");
8659
+ case "outputTables":
8660
+ return updateStringArray(config, "outputTables", "Output tables");
8661
+ case "runtimeArgs":
8662
+ return updateStringArray(config, "runtimeArgs", "Runtime arguments");
8663
+ }
8664
+ }
8665
+ async function updateJobDefinition(filePath) {
8666
+ p3.intro("Update job definition");
8667
+ const data = readJobDefinitionYaml(filePath);
8668
+ let editing = true;
8669
+ while (editing) {
8670
+ const field = await p3.select({
8671
+ message: "Which field to update?",
8672
+ options: [...UPDATABLE_FIELDS]
8673
+ });
8674
+ handleCancel(field);
8675
+ await applyFieldUpdate(field, data);
8676
+ const again = await p3.confirm({ message: "Update another field?", initialValue: false });
8677
+ handleCancel(again);
8678
+ editing = again;
8679
+ }
8680
+ const result = writeJobDefinitionYaml(filePath, data);
8681
+ if (!result.success) {
8682
+ for (const issue of result.issues) {
8683
+ p3.log.error(issue);
8684
+ }
8685
+ throw new Error("Validation failed");
8686
+ }
8687
+ p3.log.success(`Updated ${filePath}`);
8688
+ p3.outro("Done");
8689
+ }
8690
+
8691
+ // src/utils/error/handleApiError.ts
8692
+ init_cjs_shims();
8693
+ function hasStatusCode(e) {
8694
+ return e instanceof Error && "statusCode" in e && typeof e.statusCode === "number";
8695
+ }
8696
+ function hasResponseStatus(e) {
8697
+ return e instanceof Error && "response" in e && e.response !== null && typeof e.response?.status === "number";
8698
+ }
8699
+ function getStatusInfo(error) {
8700
+ if (hasStatusCode(error)) {
8701
+ return ` (HTTP ${error.statusCode})`;
8702
+ }
8703
+ if (hasResponseStatus(error)) {
8704
+ return ` (HTTP ${error.response.status})`;
8705
+ }
8706
+ return "";
8707
+ }
8708
+ function handleApiError(context, error) {
8709
+ if (error instanceof Error) {
8710
+ console.error(`Error ${context}${getStatusInfo(error)}:`, error.message);
8711
+ if (process.env.DEBUG && error.stack) {
8712
+ console.error(error.stack);
8713
+ }
8714
+ process.exitCode = 1;
8715
+ throw error;
8716
+ }
8717
+ const wrapped = new Error(`Unknown error ${context}: ${String(error)}`, { cause: error });
8718
+ console.error(wrapped.message);
8719
+ process.exitCode = 1;
8720
+ throw wrapped;
8721
+ }
8722
+
8723
+ // src/commands/jobs/jobDefinitionCreate.ts
8724
+ function addJobDefinitionCreateCommand(definitionCommand) {
8725
+ definitionCommand.command("create [jobName]").description("Interactively create a job definition YAML file").option("--notebook <path>", "Path to the job file (.ipynb)").option("--description <text>", "Job description").option("--cluster <size>", "Cluster size (small | medium | large)").option("--schedule <frequency>", "Schedule frequency (minutes | hours | days | weeks | months)").option("--interval <number>", "Schedule interval").option("--start-time <datetime>", "Schedule start time (ISO 8601)").option("--end-time <datetime>", "Schedule end time (ISO 8601)").option("--output <path>", "Output YAML file path (default: ./<jobName>.yaml)").action(async (jobName, options) => {
8726
+ try {
8727
+ await createJobDefinition(jobName, options);
8728
+ } catch (error) {
8729
+ handleApiError("creating job definition", error);
8730
+ }
8731
+ });
8732
+ }
8733
+
8734
+ // src/commands/jobs/jobDefinitionShow.ts
8735
+ init_cjs_shims();
8736
+ function addJobDefinitionShowCommand(definitionCommand) {
8737
+ definitionCommand.command("show <path>").description("Display a job definition YAML file").option("--output <format>", "Output format (table | json)", "table").action((filePath, options) => {
8738
+ try {
8739
+ showJobDefinition(filePath, options);
8740
+ } catch (error) {
8741
+ handleApiError("showing job definition", error);
8742
+ }
8743
+ });
8744
+ }
8745
+
8746
+ // src/commands/jobs/jobDefinitionUpdate.ts
8747
+ init_cjs_shims();
8748
+ function addJobDefinitionUpdateCommand(definitionCommand) {
8749
+ definitionCommand.command("update <path>").description("Interactively update a job definition YAML file").action(async (filePath) => {
8750
+ try {
8751
+ await updateJobDefinition(filePath);
8752
+ } catch (error) {
8753
+ handleApiError("updating job definition", error);
8754
+ }
8755
+ });
8756
+ }
8757
+
8758
+ // src/commands/jobs/jobDelete.ts
8759
+ init_cjs_shims();
8760
+ var import_common21 = __toESM(require_src());
8761
+
8762
+ // src/services/jobs/index.ts
8763
+ init_cjs_shims();
8764
+
8765
+ // src/services/jobs/jobService.ts
8766
+ init_cjs_shims();
8767
+ var JobService = class {
8768
+ constructor(jobApiClient) {
8769
+ this.jobApiClient = jobApiClient;
8770
+ }
8771
+ async listJobs(accessToken, requestId) {
8772
+ return this.jobApiClient.listJobs(accessToken, requestId);
8773
+ }
8774
+ async getJob(jobName, accessToken, requestId) {
8775
+ return this.jobApiClient.getJob(jobName, accessToken, requestId);
8776
+ }
8777
+ async deleteJob(jobName, accessToken, requestId) {
8778
+ return this.jobApiClient.deleteJob(jobName, accessToken, requestId);
8779
+ }
8780
+ async disableJob(jobName, accessToken, requestId) {
8781
+ return this.jobApiClient.disableJob(jobName, accessToken, requestId);
8782
+ }
8783
+ async enableJob(jobName, accessToken, requestId) {
8784
+ return this.jobApiClient.enableJob(jobName, accessToken, requestId);
8785
+ }
8786
+ async listJobRuns(jobName, accessToken, requestId) {
8787
+ return this.jobApiClient.listJobRuns(jobName, accessToken, requestId);
8788
+ }
8789
+ async getJobRun(jobName, runId, accessToken, requestId) {
8790
+ return this.jobApiClient.getJobRun(jobName, runId, accessToken, requestId);
8791
+ }
8792
+ async startJobRun(jobName, accessToken, requestId) {
8793
+ return this.jobApiClient.startJobRun(jobName, accessToken, requestId);
8794
+ }
8795
+ async cancelJobRun(jobName, runId, accessToken, requestId) {
8796
+ return this.jobApiClient.cancelJobRun(jobName, runId, accessToken, requestId);
8797
+ }
8798
+ };
8799
+
7513
8800
  // src/services/getToken.ts
7514
8801
  init_cjs_shims();
8802
+ var import_common17 = __toESM(require_src());
7515
8803
 
7516
8804
  // src/auth/constants.ts
7517
8805
  init_cjs_shims();
@@ -7538,7 +8826,7 @@ var import_msal_node_extensions2 = require("@azure/msal-node-extensions");
7538
8826
  init_cjs_shims();
7539
8827
  var import_msal_node_extensions = require("@azure/msal-node-extensions");
7540
8828
  var import_os = __toESM(require("os"));
7541
- var import_path = __toESM(require("path"));
8829
+ var import_path3 = __toESM(require("path"));
7542
8830
  var PlatformType = /* @__PURE__ */ ((PlatformType2) => {
7543
8831
  PlatformType2["Windows"] = "win32";
7544
8832
  PlatformType2["MacOS"] = "darwin";
@@ -7546,9 +8834,9 @@ var PlatformType = /* @__PURE__ */ ((PlatformType2) => {
7546
8834
  return PlatformType2;
7547
8835
  })(PlatformType || {});
7548
8836
  async function createPersistence() {
7549
- const cacheFilePath = import_path.default.join(import_os.default.homedir(), ".nsdcli-cache.json");
7550
- const platform4 = import_os.default.platform();
7551
- switch (platform4) {
8837
+ const cacheFilePath = import_path3.default.join(import_os.default.homedir(), ".nsdcli-cache.json");
8838
+ const platform5 = import_os.default.platform();
8839
+ switch (platform5) {
7552
8840
  case "win32" /* Windows */:
7553
8841
  return await import_msal_node_extensions.FilePersistenceWithDataProtection.create(cacheFilePath, import_msal_node_extensions.DataProtectionScope.CurrentUser);
7554
8842
  case "darwin" /* MacOS */:
@@ -7557,7 +8845,7 @@ async function createPersistence() {
7557
8845
  return await import_msal_node_extensions.LibSecretPersistence.create("nsdcli.service", "nsdcli.account", "nsdcli");
7558
8846
  default:
7559
8847
  throw new Error(
7560
- `Unsupported platform: ${platform4}. Supported platforms are: ${Object.values(PlatformType).join(", ")}`
8848
+ `Unsupported platform: ${platform5}. Supported platforms are: ${Object.values(PlatformType).join(", ")}`
7561
8849
  );
7562
8850
  }
7563
8851
  }
@@ -7610,7 +8898,7 @@ var getMsalInstance = async (env, authorityOverride) => {
7610
8898
  };
7611
8899
 
7612
8900
  // src/services/msalAuth.ts
7613
- var import_common6 = __toESM(require_src());
8901
+ var import_common15 = __toESM(require_src());
7614
8902
 
7615
8903
  // src/actions/defaultLogin.ts
7616
8904
  init_cjs_shims();
@@ -7637,24 +8925,24 @@ var DefaultCredentialProvider = class _DefaultCredentialProvider {
7637
8925
 
7638
8926
  // src/actions/brokerLogin.ts
7639
8927
  init_cjs_shims();
7640
- var import_common5 = __toESM(require_src());
7641
- var import_fs = __toESM(require("fs"));
8928
+ var import_common14 = __toESM(require_src());
8929
+ var import_fs3 = __toESM(require("fs"));
7642
8930
  var import_os2 = __toESM(require("os"));
7643
- var import_path2 = __toESM(require("path"));
7644
- var apiEnv = (0, import_common5.getApiEnv)(import_common5.SecurityPlatformEnvironment.Production);
7645
- var SENTINEL_DIR = import_path2.default.join(import_os2.default.homedir(), ".sentinel");
7646
- var BROKER_ACCOUNT_FILE = import_path2.default.join(SENTINEL_DIR, "broker-account.json");
8931
+ var import_path4 = __toESM(require("path"));
8932
+ var apiEnv = (0, import_common14.getApiEnv)(import_common14.SecurityPlatformEnvironment.Production);
8933
+ var SENTINEL_DIR = import_path4.default.join(import_os2.default.homedir(), ".sentinel");
8934
+ var BROKER_ACCOUNT_FILE = import_path4.default.join(SENTINEL_DIR, "broker-account.json");
7647
8935
  function saveBrokerAccount(account, authority) {
7648
- import_fs.default.mkdirSync(SENTINEL_DIR, { recursive: true, mode: 448 });
8936
+ import_fs3.default.mkdirSync(SENTINEL_DIR, { recursive: true, mode: 448 });
7649
8937
  const session = { account, authority };
7650
8938
  const tempFile = `${BROKER_ACCOUNT_FILE}.tmp`;
7651
- import_fs.default.writeFileSync(tempFile, JSON.stringify(session, null, 2), { encoding: "utf8", mode: 384 });
7652
- import_fs.default.renameSync(tempFile, BROKER_ACCOUNT_FILE);
8939
+ import_fs3.default.writeFileSync(tempFile, JSON.stringify(session, null, 2), { encoding: "utf8", mode: 384 });
8940
+ import_fs3.default.renameSync(tempFile, BROKER_ACCOUNT_FILE);
7653
8941
  }
7654
8942
  function loadBrokerAccount() {
7655
8943
  try {
7656
- if (import_fs.default.existsSync(BROKER_ACCOUNT_FILE)) {
7657
- const raw = import_fs.default.readFileSync(BROKER_ACCOUNT_FILE, "utf8");
8944
+ if (import_fs3.default.existsSync(BROKER_ACCOUNT_FILE)) {
8945
+ const raw = import_fs3.default.readFileSync(BROKER_ACCOUNT_FILE, "utf8");
7658
8946
  const session = JSON.parse(raw);
7659
8947
  if (session.account && typeof session.account === "object" && session.authority && typeof session.authority === "string") {
7660
8948
  return session;
@@ -7666,8 +8954,8 @@ function loadBrokerAccount() {
7666
8954
  }
7667
8955
  function deleteBrokerAccount() {
7668
8956
  try {
7669
- if (import_fs.default.existsSync(BROKER_ACCOUNT_FILE)) {
7670
- import_fs.default.unlinkSync(BROKER_ACCOUNT_FILE);
8957
+ if (import_fs3.default.existsSync(BROKER_ACCOUNT_FILE)) {
8958
+ import_fs3.default.unlinkSync(BROKER_ACCOUNT_FILE);
7671
8959
  }
7672
8960
  } catch {
7673
8961
  }
@@ -7727,7 +9015,7 @@ async function brokerLogin(scopes, tenant) {
7727
9015
  }
7728
9016
 
7729
9017
  // src/services/msalAuth.ts
7730
- var apiEnv2 = (0, import_common6.getApiEnv)(import_common6.SecurityPlatformEnvironment.Production);
9018
+ var apiEnv2 = (0, import_common15.getApiEnv)(import_common15.SecurityPlatformEnvironment.Production);
7731
9019
  async function loginAndGetDeviceCodeToken(scopes = [apiEnv2.gatewayAuthResourceUri]) {
7732
9020
  const deviceCodeRequest = {
7733
9021
  scopes,
@@ -7769,6 +9057,14 @@ async function getAccessTokenFromCache(scopes = [apiEnv2.gatewayAuthResourceUri]
7769
9057
  console.log("\u{1F510} Checking MSAL cache for tokens...");
7770
9058
  const savedSession = loadBrokerAccount();
7771
9059
  if (savedSession) {
9060
+ const authoritySegment = (() => {
9061
+ try {
9062
+ return new URL(savedSession.authority).pathname.split("/").filter(Boolean).pop() ?? "";
9063
+ } catch {
9064
+ return "";
9065
+ }
9066
+ })();
9067
+ const expectedTid = authoritySegment && authoritySegment !== "common" && authoritySegment !== "organizations" && authoritySegment !== "consumers" ? authoritySegment : null;
7772
9068
  try {
7773
9069
  const sessionMsal = await getMsalInstance(apiEnv2, savedSession.authority);
7774
9070
  const response = await sessionMsal.acquireTokenSilent({
@@ -7777,14 +9073,19 @@ async function getAccessTokenFromCache(scopes = [apiEnv2.gatewayAuthResourceUri]
7777
9073
  authority: savedSession.authority
7778
9074
  });
7779
9075
  if (response?.accessToken) {
9076
+ if (expectedTid && response.account?.tenantId && response.account.tenantId !== expectedTid) {
9077
+ throw new Error(
9078
+ `Active tenant is ${expectedTid} but silent acquisition returned a token for ${response.account.tenantId}. Run 'sentinel login --tenant ${expectedTid}' (or 'sentinel tenant select') to mint a tenant-bound token.`
9079
+ );
9080
+ }
7780
9081
  console.log("\u2705 Authenticated using MSAL cache");
7781
9082
  return response.accessToken;
7782
9083
  }
7783
- console.warn("\u26A0\uFE0F Saved-session silent acquisition returned no token");
7784
- deleteBrokerAccount();
7785
- } catch {
7786
- console.warn("\u26A0\uFE0F Saved-session silent acquisition failed, falling back to account enumeration");
7787
- deleteBrokerAccount();
9084
+ throw new Error(
9085
+ `Silent token acquisition returned no access token for the active tenant${expectedTid ? ` (${expectedTid})` : ""}. Run 'sentinel login${expectedTid ? ` --tenant ${expectedTid}` : ""}' to refresh credentials.`
9086
+ );
9087
+ } catch (err) {
9088
+ throw err instanceof Error ? err : new Error(`Silent token acquisition failed: ${String(err)}`);
7788
9089
  }
7789
9090
  }
7790
9091
  const msalInstance = await getMsalInstance(apiEnv2);
@@ -7874,40 +9175,379 @@ var MsalCacheTokenProvider = class {
7874
9175
  if (!env) {
7875
9176
  throw new Error("Environment cannot be null or undefined");
7876
9177
  }
7877
- this.env = env;
7878
- this.options = {
7879
- ...options,
7880
- scopes: options?.scopes?.length ? options.scopes : [this.env.gatewayAuthResourceUri]
7881
- };
9178
+ this.env = env;
9179
+ this.options = {
9180
+ ...options,
9181
+ scopes: options?.scopes?.length ? options.scopes : [this.env.gatewayAuthResourceUri]
9182
+ };
9183
+ }
9184
+ async getAccessToken() {
9185
+ const token = await getAccessTokenFromCache(this.options.scopes);
9186
+ if (!token) {
9187
+ throw new Error("No cached authentication token found. Please run `sentinel login` first.");
9188
+ }
9189
+ return token;
9190
+ }
9191
+ };
9192
+
9193
+ // src/auth/provider/TokenProviderFactory.ts
9194
+ var TokenProviderFactory = class {
9195
+ static createProvider(env, method, options) {
9196
+ switch (method) {
9197
+ case "device_code":
9198
+ return new DeviceCodeTokenProvider(env, options);
9199
+ case "managed_identity":
9200
+ if (!options) {
9201
+ throw new Error("UAMI clientId required for user-assigned managed identity");
9202
+ }
9203
+ return new ManagedIdentityTokenProvider(env, options);
9204
+ case "msal_cache":
9205
+ return new MsalCacheTokenProvider(env, options);
9206
+ default:
9207
+ throw new Error(`Unsupported token method: ${method}`);
9208
+ }
9209
+ }
9210
+ };
9211
+
9212
+ // src/telemetry/index.ts
9213
+ init_cjs_shims();
9214
+
9215
+ // src/telemetry/telemetryService.ts
9216
+ init_cjs_shims();
9217
+ var import_common16 = __toESM(require_src());
9218
+ var import_api = require("@opentelemetry/api");
9219
+ var import_monitor_opentelemetry = require("@azure/monitor-opentelemetry");
9220
+
9221
+ // src/telemetry/connectionString.ts
9222
+ init_cjs_shims();
9223
+ var APPLICATION_INSIGHTS_CONNECTION_STRING = "InstrumentationKey=0dadb494-ecd4-44a5-bcc1-baa55b3778be;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/;ApplicationId=8bc9c85a-22a3-45f8-b4f5-0bb6cd6ddad2";
9224
+
9225
+ // src/telemetry/types.ts
9226
+ init_cjs_shims();
9227
+ var TELEMETRY_OPT_OUT_ENV_VARS = ["SENTINEL_TELEMETRY_OPTOUT", "DO_NOT_TRACK"];
9228
+
9229
+ // src/telemetry/commonProperties.ts
9230
+ init_cjs_shims();
9231
+ var os4 = __toESM(require("os"));
9232
+ var import_uuid = require("uuid");
9233
+ function readAppVersion() {
9234
+ return true ? "0.3.0" : "unknown";
9235
+ }
9236
+ function detectCiEnvironment(env = process.env) {
9237
+ if (env.TF_BUILD) {
9238
+ return "AzureDevOps";
9239
+ }
9240
+ if (env.GITHUB_ACTIONS) {
9241
+ return "GitHubActions";
9242
+ }
9243
+ if (env.BUILD_BUILDID) {
9244
+ return "AzureDevOps";
9245
+ }
9246
+ if (env.JENKINS_URL) {
9247
+ return "Jenkins";
9248
+ }
9249
+ if (env.CIRCLECI) {
9250
+ return "CircleCI";
9251
+ }
9252
+ if (env.GITLAB_CI) {
9253
+ return "GitLab";
9254
+ }
9255
+ if (env.CI) {
9256
+ return "Generic";
9257
+ }
9258
+ return "unknown";
9259
+ }
9260
+ function buildCommonProperties(sessionId = (0, import_uuid.v4)()) {
9261
+ return {
9262
+ appName: "sentinel-cli",
9263
+ appVersion: readAppVersion(),
9264
+ nodeVersion: process.versions.node,
9265
+ platform: process.platform,
9266
+ arch: process.arch,
9267
+ osRelease: os4.release(),
9268
+ sessionId,
9269
+ ciEnvironment: detectCiEnvironment()
9270
+ };
9271
+ }
9272
+
9273
+ // src/telemetry/redact.ts
9274
+ init_cjs_shims();
9275
+ var EMAIL_RE = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/gi;
9276
+ var GUID_RE = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
9277
+ var JWT_RE = /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?![A-Za-z0-9_-])/g;
9278
+ var UNIX_PATH_RE = /(?<![A-Za-z0-9_./\\-])(?:\/[^\s]+\/([^\s/]+)|\/([^\s/]+))/g;
9279
+ var WIN_PATH_RE = /(?<![A-Za-z0-9_./\\-])(?:[a-zA-Z]:\\[^\s]+\\([^\s\\]+)|[a-zA-Z]:\\([^\s\\]+))/g;
9280
+ function redactString(input) {
9281
+ if (!input) {
9282
+ return input;
9283
+ }
9284
+ return input.replace(JWT_RE, "<jwt>").replace(EMAIL_RE, "<email>").replace(GUID_RE, "<guid>").replace(UNIX_PATH_RE, (_m, deepBase, rootBase) => `<redacted>/${deepBase ?? rootBase}`).replace(WIN_PATH_RE, (_m, deepBase, rootBase) => `<redacted>\\${deepBase ?? rootBase}`);
9285
+ }
9286
+ function redactError(error) {
9287
+ const name = error.name || "Error";
9288
+ const message = redactString(error.message ?? "");
9289
+ const stack = error.stack ? error.stack.split("\n").slice(0, 4).map((line) => redactString(line)).join("\n") : "";
9290
+ return {
9291
+ errorName: name,
9292
+ errorMessage: message,
9293
+ errorStack: stack
9294
+ };
9295
+ }
9296
+
9297
+ // src/telemetry/telemetryService.ts
9298
+ var TRACER_NAME = "sentinel-cli";
9299
+ var TRACER_VERSION = "1.0.0";
9300
+ var azureMonitorInitialized = false;
9301
+ var TelemetryService = class _TelemetryService {
9302
+ sharedProperties = {};
9303
+ commonProperties;
9304
+ enabled;
9305
+ initialized = false;
9306
+ disposed = false;
9307
+ tracer;
9308
+ constructor(options = {}) {
9309
+ this.enabled = _TelemetryService.resolveEnabled(options);
9310
+ this.commonProperties = buildCommonProperties(options.sessionId);
9311
+ if (this.enabled) {
9312
+ this.initialize(options.connectionString ?? APPLICATION_INSIGHTS_CONNECTION_STRING);
9313
+ }
9314
+ }
9315
+ /** Determine whether telemetry should be active for this process. */
9316
+ static resolveEnabled(options) {
9317
+ if (options.forceDisabled) {
9318
+ return false;
9319
+ }
9320
+ for (const name of TELEMETRY_OPT_OUT_ENV_VARS) {
9321
+ if (process.env[name] === "1" || process.env[name]?.toLowerCase() === "true") {
9322
+ return false;
9323
+ }
9324
+ }
9325
+ return true;
9326
+ }
9327
+ /**
9328
+ * Initialise the Azure Monitor OpenTelemetry distro exactly once per
9329
+ * process, then acquire this instance's tracer. `useAzureMonitor()`
9330
+ * installs global SDK state (TracerProvider, exporters, processors), so
9331
+ * it is guarded by a module-level flag; `trace.getTracer()` is cheap and
9332
+ * idempotent so it is safe to call for every instance.
9333
+ *
9334
+ * Resource configuration is applied via OpenTelemetry env vars, set
9335
+ * programmatically *before* `useAzureMonitor()` so they're observed by
9336
+ * the SDK's resource-detection bootstrap:
9337
+ *
9338
+ * - `OTEL_NODE_RESOURCE_DETECTORS` — restricted to `env,os,serviceinstance`
9339
+ * so the `HostDetector` is **never** registered. This suppresses
9340
+ * `host.name` (device hostname, PII like `User-MacBook-Pro.local`),
9341
+ * `host.id` (stable hardware fingerprint), and `host.arch`.
9342
+ * - `OTEL_SERVICE_NAME` — populates `service.name`, which Azure Monitor
9343
+ * maps to `cloud_RoleName`. Without this, the backend records the OTel
9344
+ * default `"unknown_service:node"`.
9345
+ * - `OTEL_RESOURCE_ATTRIBUTES` — adds `service.version` (no dedicated env
9346
+ * var exists for it).
9347
+ *
9348
+ * Each variable is set with `??=` so any caller-provided override (e.g.
9349
+ * tests, advanced users) wins.
9350
+ */
9351
+ initialize(connectionString) {
9352
+ if (!azureMonitorInitialized) {
9353
+ process.env.OTEL_NODE_RESOURCE_DETECTORS ??= "env,os,serviceinstance";
9354
+ process.env.OTEL_SERVICE_NAME ??= TRACER_NAME;
9355
+ process.env.OTEL_RESOURCE_ATTRIBUTES ??= `service.version=${TRACER_VERSION}`;
9356
+ (0, import_monitor_opentelemetry.useAzureMonitor)({
9357
+ azureMonitorExporterOptions: { connectionString },
9358
+ tracesPerSecond: 0,
9359
+ samplingRatio: 1
9360
+ });
9361
+ azureMonitorInitialized = true;
9362
+ }
9363
+ this.tracer = import_api.trace.getTracer(TRACER_NAME, TRACER_VERSION);
9364
+ this.initialized = true;
9365
+ }
9366
+ /**
9367
+ * Merge common + shared + caller-supplied properties into a flat
9368
+ * {@link Attributes} bag suitable for an OTel span. Primitive values
9369
+ * (`number`, `boolean`) are passed through unchanged so that downstream
9370
+ * Azure Monitor / Log Analytics queries see their native types instead
9371
+ * of stringified versions (which would break numeric aggregations and
9372
+ * boolean comparisons).
9373
+ */
9374
+ mergeAttributes(extra) {
9375
+ const result = {
9376
+ ...this.commonProperties,
9377
+ ...this.sharedProperties
9378
+ };
9379
+ if (extra) {
9380
+ for (const [key, value] of Object.entries(extra)) {
9381
+ if (value === void 0) {
9382
+ continue;
9383
+ }
9384
+ result[key] = value;
9385
+ }
9386
+ }
9387
+ return result;
9388
+ }
9389
+ event(eventName, data) {
9390
+ if (!this.enabled || this.disposed || !this.tracer) {
9391
+ return;
9392
+ }
9393
+ const span = this.tracer.startSpan(eventName, {
9394
+ attributes: this.mergeAttributes(data?.properties)
9395
+ });
9396
+ if (data?.measurements) {
9397
+ for (const [k, v] of Object.entries(data.measurements)) {
9398
+ span.setAttribute(k, v);
9399
+ }
9400
+ }
9401
+ span.end();
9402
+ }
9403
+ span(eventName, initialAttributes) {
9404
+ if (!this.enabled || this.disposed || !this.tracer) {
9405
+ return new NoopSpanContext(eventName, initialAttributes);
9406
+ }
9407
+ return new TelemetrySpanContext(
9408
+ this.tracer,
9409
+ eventName,
9410
+ initialAttributes,
9411
+ (extras) => this.mergeAttributes(extras)
9412
+ );
9413
+ }
9414
+ setSharedProperty(name, value) {
9415
+ if (value === void 0) {
9416
+ delete this.sharedProperties[name];
9417
+ } else {
9418
+ this.sharedProperties[name] = value;
9419
+ }
9420
+ }
9421
+ getSharedProperties() {
9422
+ return { ...this.sharedProperties };
9423
+ }
9424
+ async dispose() {
9425
+ if (this.disposed) {
9426
+ return;
9427
+ }
9428
+ this.disposed = true;
9429
+ if (!this.enabled || !this.initialized) {
9430
+ return;
9431
+ }
9432
+ try {
9433
+ await (0, import_monitor_opentelemetry.shutdownAzureMonitor)();
9434
+ } catch {
9435
+ } finally {
9436
+ this.tracer = void 0;
9437
+ azureMonitorInitialized = false;
9438
+ }
9439
+ }
9440
+ };
9441
+ var TelemetrySpanContext = class {
9442
+ startTime = Date.now();
9443
+ eventName;
9444
+ attributes;
9445
+ otSpan;
9446
+ requestId;
9447
+ mergeAttributes;
9448
+ ended = false;
9449
+ constructor(tracer, eventName, initialAttributes, mergeAttributes) {
9450
+ this.eventName = eventName;
9451
+ this.attributes = { ...initialAttributes ?? {} };
9452
+ this.requestId = this.attributes.requestId ?? import_common16.CorrelationService.createCorrelationContext();
9453
+ this.attributes.requestId = this.requestId;
9454
+ this.mergeAttributes = mergeAttributes;
9455
+ this.otSpan = tracer.startSpan(eventName, {
9456
+ attributes: this.mergeAttributes(this.attributes)
9457
+ });
9458
+ }
9459
+ getRequestId() {
9460
+ return this.requestId;
9461
+ }
9462
+ addAttributes(extra) {
9463
+ this.applyAttributes(extra);
9464
+ }
9465
+ end(extra) {
9466
+ if (this.ended) {
9467
+ return;
9468
+ }
9469
+ this.applyAttributes(extra);
9470
+ this.finalize();
9471
+ }
9472
+ error(err, extra) {
9473
+ if (this.ended) {
9474
+ return;
9475
+ }
9476
+ const redacted = redactError(err);
9477
+ this.applyAttributes({ ...extra, ...redacted, success: false });
9478
+ const sanitized = new Error(redacted.errorMessage);
9479
+ sanitized.name = redacted.errorName;
9480
+ sanitized.stack = redacted.errorStack;
9481
+ this.otSpan.recordException(sanitized);
9482
+ this.otSpan.setStatus({ code: import_api.SpanStatusCode.ERROR, message: redacted.errorMessage });
9483
+ this.finalize();
9484
+ }
9485
+ /**
9486
+ * Merge non-undefined entries into both the local bag and the OTel span.
9487
+ * Values keep their native primitive type (`string | number | boolean`)
9488
+ * so the backing SDK records typed attributes rather than stringified ones.
9489
+ */
9490
+ applyAttributes(extra) {
9491
+ if (!extra) {
9492
+ return;
9493
+ }
9494
+ for (const [k, v] of Object.entries(extra)) {
9495
+ if (v === void 0) {
9496
+ continue;
9497
+ }
9498
+ this.attributes[k] = v;
9499
+ this.otSpan.setAttribute(k, v);
9500
+ }
7882
9501
  }
7883
- async getAccessToken() {
7884
- const token = await getAccessTokenFromCache(this.options.scopes);
7885
- if (!token) {
7886
- throw new Error("No cached authentication token found. Please run `sentinel login` first.");
9502
+ /** Stamp duration, close the OTel span, and flip the ended flag. */
9503
+ finalize() {
9504
+ this.ended = true;
9505
+ const finalAttrs = this.mergeAttributes(this.attributes);
9506
+ for (const [k, v] of Object.entries(finalAttrs)) {
9507
+ if (v !== void 0) {
9508
+ this.otSpan.setAttribute(k, v);
9509
+ }
7887
9510
  }
7888
- return token;
9511
+ this.otSpan.setAttribute("durationMs", Date.now() - this.startTime);
9512
+ this.otSpan.end();
7889
9513
  }
7890
9514
  };
7891
-
7892
- // src/auth/provider/TokenProviderFactory.ts
7893
- var TokenProviderFactory = class {
7894
- static createProvider(env, method, options) {
7895
- switch (method) {
7896
- case "device_code":
7897
- return new DeviceCodeTokenProvider(env, options);
7898
- case "managed_identity":
7899
- if (!options) {
7900
- throw new Error("UAMI clientId required for user-assigned managed identity");
7901
- }
7902
- return new ManagedIdentityTokenProvider(env, options);
7903
- case "msal_cache":
7904
- return new MsalCacheTokenProvider(env, options);
7905
- default:
7906
- throw new Error(`Unsupported token method: ${method}`);
9515
+ var NoopSpanContext = class {
9516
+ startTime = Date.now();
9517
+ eventName;
9518
+ attributes;
9519
+ requestId;
9520
+ constructor(eventName, initialAttributes) {
9521
+ this.eventName = eventName;
9522
+ this.attributes = { ...initialAttributes ?? {} };
9523
+ this.requestId = this.attributes.requestId ?? import_common16.CorrelationService.createCorrelationContext();
9524
+ this.attributes.requestId = this.requestId;
9525
+ }
9526
+ getRequestId() {
9527
+ return this.requestId;
9528
+ }
9529
+ addAttributes(extra) {
9530
+ for (const [k, v] of Object.entries(extra)) {
9531
+ if (v !== void 0) {
9532
+ this.attributes[k] = v;
9533
+ }
7907
9534
  }
7908
9535
  }
9536
+ end() {
9537
+ }
9538
+ error() {
9539
+ }
7909
9540
  };
7910
9541
 
9542
+ // src/telemetry/index.ts
9543
+ var singleton;
9544
+ function getTelemetry(options) {
9545
+ if (!singleton) {
9546
+ singleton = new TelemetryService(options);
9547
+ }
9548
+ return singleton;
9549
+ }
9550
+
7911
9551
  // src/services/getToken.ts
7912
9552
  async function getToken(env, scopes, clientId) {
7913
9553
  let provider = null;
@@ -7921,26 +9561,34 @@ async function getToken(env, scopes, clientId) {
7921
9561
  provider = TokenProviderFactory.createProvider(env, "msal_cache" /* MsalCache */, options);
7922
9562
  }
7923
9563
  const token = await provider.getAccessToken();
9564
+ stampTenantTelemetry(token);
7924
9565
  return token;
7925
9566
  }
9567
+ function stampTenantTelemetry(token) {
9568
+ try {
9569
+ const tenantId = (0, import_common17.extractTenantId)(token);
9570
+ getTelemetry().setSharedProperty("tenantId", tenantId || "");
9571
+ } catch {
9572
+ }
9573
+ }
7926
9574
 
7927
- // src/client/lake/index.ts
9575
+ // src/client/jobs/index.ts
7928
9576
  init_cjs_shims();
7929
- var import_common8 = __toESM(require_src());
9577
+ var import_common19 = __toESM(require_src());
7930
9578
 
7931
9579
  // src/utils/headerBuilder.ts
7932
9580
  init_cjs_shims();
7933
- var import_common7 = __toESM(require_src());
9581
+ var import_common18 = __toESM(require_src());
7934
9582
  function buildApiHeaders(token, requestId, extra) {
7935
9583
  const base = {};
7936
9584
  if (requestId) {
7937
- base[import_common7.HTTP_HEADER.REQUEST_ID] = requestId;
9585
+ base[import_common18.HTTP_HEADER.REQUEST_ID] = requestId;
7938
9586
  }
7939
- if (!extra || !(import_common7.HTTP_HEADER.CONTENT_TYPE in extra)) {
7940
- base[import_common7.HTTP_HEADER.CONTENT_TYPE] = import_common7.HTTP_HEADER.APPLICATION_JSON;
9587
+ if (!extra || !(import_common18.HTTP_HEADER.CONTENT_TYPE in extra)) {
9588
+ base[import_common18.HTTP_HEADER.CONTENT_TYPE] = import_common18.HTTP_HEADER.APPLICATION_JSON;
7941
9589
  }
7942
9590
  if (token) {
7943
- base[import_common7.HTTP_HEADER.AUTHORIZATION] = `${import_common7.HTTP_HEADER.BEARER_PREFIX}${token}`;
9591
+ base[import_common18.HTTP_HEADER.AUTHORIZATION] = `${import_common18.HTTP_HEADER.BEARER_PREFIX}${token}`;
7944
9592
  }
7945
9593
  const merged = { ...base, ...extra || {} };
7946
9594
  return Object.fromEntries(
@@ -7957,136 +9605,599 @@ var noopSpan = {
7957
9605
  },
7958
9606
  error: () => {
7959
9607
  }
7960
- };
7961
- var noopTelemetry = {
7962
- span: () => noopSpan
7963
- };
9608
+ };
9609
+ var noopTelemetry = {
9610
+ span: () => noopSpan
9611
+ };
9612
+
9613
+ // src/client/jobs/index.ts
9614
+ function createJobApiClient(env, region) {
9615
+ const requestService = new import_common19.RequestService(noopTelemetry, buildApiHeaders);
9616
+ return new import_common19.JobApiClient({
9617
+ requestService,
9618
+ baseUrl: env.regions[region].gatewayEndpoint
9619
+ });
9620
+ }
9621
+
9622
+ // src/utils/parseRegion.ts
9623
+ init_cjs_shims();
9624
+ var import_common20 = __toESM(require_src());
9625
+ function parseRegion(value) {
9626
+ if (Object.values(import_common20.Region).includes(value)) {
9627
+ return value;
9628
+ }
9629
+ throw new Error(`Invalid region "${value}". Valid regions: ${Object.values(import_common20.Region).join(", ")}`);
9630
+ }
9631
+
9632
+ // src/commands/jobs/jobDelete.ts
9633
+ function addJobDeleteCommand(jobCommand) {
9634
+ jobCommand.command("delete <jobName>").description("Delete a scheduled job").option("-r, --region <region>", "Target region (default: global)", import_common21.Region.Global).action(async (jobName, options) => {
9635
+ try {
9636
+ const apiEnv4 = (0, import_common21.getApiEnv)(import_common21.SecurityPlatformEnvironment.Production);
9637
+ const accessToken = await getToken(apiEnv4);
9638
+ const region = parseRegion(options.region);
9639
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9640
+ const jobService = new JobService(jobApiClient);
9641
+ await deleteJob(jobService, accessToken, jobName);
9642
+ console.log(`Job "${jobName}" deleted successfully.`);
9643
+ } catch (error) {
9644
+ handleApiError("deleting job", error);
9645
+ }
9646
+ });
9647
+ }
9648
+
9649
+ // src/commands/jobs/jobDisable.ts
9650
+ init_cjs_shims();
9651
+ var import_common22 = __toESM(require_src());
9652
+ function addJobDisableCommand(jobCommand) {
9653
+ jobCommand.command("disable <jobName>").description("Disable a scheduled job").option("-r, --region <region>", "Target region (default: global)", import_common22.Region.Global).action(async (jobName, options) => {
9654
+ try {
9655
+ const apiEnv4 = (0, import_common22.getApiEnv)(import_common22.SecurityPlatformEnvironment.Production);
9656
+ const accessToken = await getToken(apiEnv4);
9657
+ const region = parseRegion(options.region);
9658
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9659
+ const jobService = new JobService(jobApiClient);
9660
+ await disableJob(jobService, accessToken, jobName);
9661
+ console.log(`Job "${jobName}" disabled successfully.`);
9662
+ } catch (error) {
9663
+ handleApiError("disabling job", error);
9664
+ }
9665
+ });
9666
+ }
9667
+
9668
+ // src/commands/jobs/jobDownload.ts
9669
+ init_cjs_shims();
9670
+ var import_common23 = __toESM(require_src());
9671
+ function addJobDownloadCommand(jobCommand) {
9672
+ jobCommand.command("download <jobName>").description("Download the notebook of a scheduled job").option("-o, --output <path>", "Output file path (defaults to <jobName>.ipynb)").option("-r, --region <region>", "Target region (default: global)", import_common23.Region.Global).action(async (jobName, options) => {
9673
+ try {
9674
+ const apiEnv4 = (0, import_common23.getApiEnv)(import_common23.SecurityPlatformEnvironment.Production);
9675
+ const accessToken = await getToken(apiEnv4);
9676
+ const region = parseRegion(options.region);
9677
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9678
+ const jobService = new JobService(jobApiClient);
9679
+ const writtenPath = await downloadJob(jobService, accessToken, jobName, options.output);
9680
+ console.log(`Notebook downloaded to: ${writtenPath}`);
9681
+ } catch (error) {
9682
+ handleApiError("downloading job notebook", error);
9683
+ }
9684
+ });
9685
+ }
9686
+
9687
+ // src/commands/jobs/jobEnable.ts
9688
+ init_cjs_shims();
9689
+ var import_common24 = __toESM(require_src());
9690
+ function addJobEnableCommand(jobCommand) {
9691
+ jobCommand.command("enable <jobName>").description("Enable a scheduled job").option("-r, --region <region>", "Target region (default: global)", import_common24.Region.Global).action(async (jobName, options) => {
9692
+ try {
9693
+ const apiEnv4 = (0, import_common24.getApiEnv)(import_common24.SecurityPlatformEnvironment.Production);
9694
+ const accessToken = await getToken(apiEnv4);
9695
+ const region = parseRegion(options.region);
9696
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9697
+ const jobService = new JobService(jobApiClient);
9698
+ await enableJob(jobService, accessToken, jobName);
9699
+ console.log(`Job "${jobName}" enabled successfully.`);
9700
+ } catch (error) {
9701
+ handleApiError("enabling job", error);
9702
+ }
9703
+ });
9704
+ }
9705
+
9706
+ // src/commands/jobs/jobList.ts
9707
+ init_cjs_shims();
9708
+ var import_common25 = __toESM(require_src());
9709
+ var JOB_COLUMNS = [
9710
+ { header: "NAME", key: (j) => j.jobName },
9711
+ { header: "TYPE", key: (j) => j.jobType },
9712
+ { header: "DISABLED", key: (j) => String(j.isDisabled) }
9713
+ ];
9714
+ function addJobListCommand(jobCommand) {
9715
+ jobCommand.command("list").description("List all scheduled jobs").option("-r, --region <region>", "Target region (default: global)", import_common25.Region.Global).action(async (options) => {
9716
+ try {
9717
+ const apiEnv4 = (0, import_common25.getApiEnv)(import_common25.SecurityPlatformEnvironment.Production);
9718
+ const accessToken = await getToken(apiEnv4);
9719
+ const region = parseRegion(options.region);
9720
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9721
+ const jobService = new JobService(jobApiClient);
9722
+ const jobs = await listJobs(jobService, accessToken);
9723
+ const fmt = new OutputFormatter();
9724
+ fmt.print(jobs, JOB_COLUMNS);
9725
+ } catch (error) {
9726
+ handleApiError("listing jobs", error);
9727
+ }
9728
+ });
9729
+ }
9730
+
9731
+ // src/commands/jobs/jobRunCancel.ts
9732
+ init_cjs_shims();
9733
+ var import_common26 = __toESM(require_src());
9734
+ function addJobRunCancelCommand(runCommand) {
9735
+ runCommand.command("cancel <jobName> <runId>").description("Cancel a specific job run").option("-r, --region <region>", "Target region (default: global)", import_common26.Region.Global).action(async (jobName, runId, options) => {
9736
+ try {
9737
+ const apiEnv4 = (0, import_common26.getApiEnv)(import_common26.SecurityPlatformEnvironment.Production);
9738
+ const accessToken = await getToken(apiEnv4);
9739
+ const region = parseRegion(options.region);
9740
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9741
+ const jobService = new JobService(jobApiClient);
9742
+ await cancelJobRun(jobService, accessToken, jobName, runId);
9743
+ console.log(`Run "${runId}" for job "${jobName}" cancelled successfully.`);
9744
+ } catch (error) {
9745
+ handleApiError("cancelling job run", error);
9746
+ }
9747
+ });
9748
+ }
9749
+
9750
+ // src/commands/jobs/jobRunList.ts
9751
+ init_cjs_shims();
9752
+ var import_common27 = __toESM(require_src());
9753
+ var RUN_COLUMNS = [
9754
+ { header: "RUN ID", key: (r) => r.jobRunId ?? "" },
9755
+ { header: "STATUS", key: (r) => r.status ?? "" },
9756
+ { header: "STARTED", key: (r) => r.startTime ?? "" },
9757
+ { header: "ENDED", key: (r) => r.endTime ?? "" }
9758
+ ];
9759
+ function addJobRunListCommand(runCommand) {
9760
+ runCommand.command("list <jobName>").description("List all runs for a scheduled job").option("-r, --region <region>", "Target region (default: global)", import_common27.Region.Global).action(async (jobName, options) => {
9761
+ try {
9762
+ const apiEnv4 = (0, import_common27.getApiEnv)(import_common27.SecurityPlatformEnvironment.Production);
9763
+ const accessToken = await getToken(apiEnv4);
9764
+ const region = parseRegion(options.region);
9765
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9766
+ const jobService = new JobService(jobApiClient);
9767
+ const runs = await listJobRuns(jobService, accessToken, jobName);
9768
+ const fmt = new OutputFormatter();
9769
+ fmt.print(runs, RUN_COLUMNS);
9770
+ } catch (error) {
9771
+ handleApiError("listing job runs", error);
9772
+ }
9773
+ });
9774
+ }
9775
+
9776
+ // src/commands/jobs/jobRunNotebookDownload.ts
9777
+ init_cjs_shims();
9778
+ var import_common28 = __toESM(require_src());
9779
+ function addJobRunNotebookDownloadCommand(notebookCommand) {
9780
+ notebookCommand.command("download <jobName> <runId>").description("Download the notebook snapshot of a job run").option("-o, --output <path>", "Output file path (defaults to <jobName>_<runId>.ipynb)").option("-r, --region <region>", "Target region (default: global)", import_common28.Region.Global).action(async (jobName, runId, options) => {
9781
+ try {
9782
+ const apiEnv4 = (0, import_common28.getApiEnv)(import_common28.SecurityPlatformEnvironment.Production);
9783
+ const accessToken = await getToken(apiEnv4);
9784
+ const region = parseRegion(options.region);
9785
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9786
+ const jobService = new JobService(jobApiClient);
9787
+ const writtenPath = await downloadJobRunNotebook(jobService, accessToken, jobName, runId, options.output);
9788
+ console.log(`Notebook downloaded to: ${writtenPath}`);
9789
+ } catch (error) {
9790
+ handleApiError("downloading job run notebook", error);
9791
+ }
9792
+ });
9793
+ }
9794
+
9795
+ // src/commands/jobs/jobRunShow.ts
9796
+ init_cjs_shims();
9797
+ var import_common29 = __toESM(require_src());
9798
+ var RUN_DETAIL_COLUMNS = [
9799
+ { header: "FIELD", key: (f) => f.label },
9800
+ { header: "VALUE", key: (f) => f.value }
9801
+ ];
9802
+ function addJobRunShowCommand(runCommand) {
9803
+ runCommand.command("show <jobName> <runId>").description("Show the details of a job run").option("-r, --region <region>", "Target region (default: global)", import_common29.Region.Global).action(async (jobName, runId, options) => {
9804
+ try {
9805
+ const apiEnv4 = (0, import_common29.getApiEnv)(import_common29.SecurityPlatformEnvironment.Production);
9806
+ const accessToken = await getToken(apiEnv4);
9807
+ const region = parseRegion(options.region);
9808
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9809
+ const jobService = new JobService(jobApiClient);
9810
+ const run = await getJobRun(jobService, accessToken, jobName, runId);
9811
+ printRunDetails(run);
9812
+ } catch (error) {
9813
+ handleApiError("showing job run", error);
9814
+ }
9815
+ });
9816
+ }
9817
+ function printRunDetails(run) {
9818
+ const fields = [
9819
+ { label: "Run ID", value: run.jobRunId ?? "" },
9820
+ { label: "Status", value: run.status ?? "" },
9821
+ { label: "Schedule", value: run.scheduleName ?? "" },
9822
+ { label: "Started", value: run.startTime ?? "" },
9823
+ { label: "Ended", value: run.endTime ?? "" },
9824
+ { label: "Error", value: run.error?.errorMessage ?? "" }
9825
+ ];
9826
+ const fmt = new OutputFormatter();
9827
+ fmt.print(fields, RUN_DETAIL_COLUMNS);
9828
+ }
9829
+
9830
+ // src/commands/jobs/jobRunStart.ts
9831
+ init_cjs_shims();
9832
+ var import_common30 = __toESM(require_src());
9833
+ function addJobRunStartCommand(runCommand) {
9834
+ runCommand.command("start <jobName>").description("Start an ad-hoc run of a scheduled job").option("-r, --region <region>", "Target region (default: global)", import_common30.Region.Global).action(async (jobName, options) => {
9835
+ try {
9836
+ const apiEnv4 = (0, import_common30.getApiEnv)(import_common30.SecurityPlatformEnvironment.Production);
9837
+ const accessToken = await getToken(apiEnv4);
9838
+ const region = parseRegion(options.region);
9839
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9840
+ const jobService = new JobService(jobApiClient);
9841
+ const run = await startJobRun(jobService, accessToken, jobName);
9842
+ console.log(`Job run started. Run ID: ${run.jobRunId ?? "(unknown)"}`);
9843
+ } catch (error) {
9844
+ handleApiError("starting job run", error);
9845
+ }
9846
+ });
9847
+ }
9848
+
9849
+ // src/commands/jobs/jobShow.ts
9850
+ init_cjs_shims();
9851
+ var import_common31 = __toESM(require_src());
9852
+ var JOB_DETAIL_COLUMNS = [
9853
+ { header: "FIELD", key: (f) => f.label },
9854
+ { header: "VALUE", key: (f) => f.value }
9855
+ ];
9856
+ function addJobShowCommand(jobCommand) {
9857
+ jobCommand.command("show <jobName>").description("Show the details of a scheduled job").option("-r, --region <region>", "Target region (default: global)", import_common31.Region.Global).action(async (jobName, options) => {
9858
+ try {
9859
+ const apiEnv4 = (0, import_common31.getApiEnv)(import_common31.SecurityPlatformEnvironment.Production);
9860
+ const accessToken = await getToken(apiEnv4);
9861
+ const region = parseRegion(options.region);
9862
+ const jobApiClient = createJobApiClient(apiEnv4, region);
9863
+ const jobService = new JobService(jobApiClient);
9864
+ const job = await getJob(jobService, accessToken, jobName);
9865
+ printJobDetails(job);
9866
+ } catch (error) {
9867
+ handleApiError("showing job", error);
9868
+ }
9869
+ });
9870
+ }
9871
+ function printJobDetails(job) {
9872
+ const def = job.jobDefinition;
9873
+ const fields = [
9874
+ { label: "Name", value: def.jobName ?? "" },
9875
+ { label: "Type", value: def.jobType ?? "" },
9876
+ { label: "Disabled", value: String(def.isDisabled ?? false) },
9877
+ { label: "Description", value: def.description ?? "" },
9878
+ { label: "Spark Pool", value: def.computeInfo?.friendlyName ?? "" },
9879
+ { label: "Created At", value: def.createdAt ?? "" },
9880
+ { label: "Last Updated At", value: def.lastUpdatedAt ?? "" }
9881
+ ];
9882
+ const fmt = new OutputFormatter();
9883
+ fmt.print(fields, JOB_DETAIL_COLUMNS);
9884
+ }
9885
+
9886
+ // src/commands/lake/database.ts
9887
+ init_cjs_shims();
9888
+ var import_common36 = __toESM(require_src());
9889
+
9890
+ // src/actions/lake/index.ts
9891
+ init_cjs_shims();
9892
+
9893
+ // src/actions/lake/listDatabases.ts
9894
+ init_cjs_shims();
9895
+ var import_common32 = __toESM(require_src());
9896
+ async function listDatabases(databaseService, accessToken) {
9897
+ const requestId = import_common32.CorrelationService.createCorrelationContext();
9898
+ console.info("Fetching databases...");
9899
+ return databaseService.listDatabases(accessToken, requestId);
9900
+ }
9901
+
9902
+ // src/actions/lake/listTables.ts
9903
+ init_cjs_shims();
9904
+ var import_common33 = __toESM(require_src());
9905
+ async function listTables(tableService, accessToken, database) {
9906
+ const requestId = import_common33.CorrelationService.createCorrelationContext();
9907
+ const scope = database ? ` for database "${database.databaseName}"` : "";
9908
+ console.info(`Fetching lake tables${scope}...`);
9909
+ return tableService.listTables(accessToken, requestId, database);
9910
+ }
9911
+
9912
+ // src/services/lake/index.ts
9913
+ init_cjs_shims();
9914
+
9915
+ // src/services/lake/databaseService.ts
9916
+ init_cjs_shims();
9917
+ var DatabaseService = class {
9918
+ constructor(lakeApiClient) {
9919
+ this.lakeApiClient = lakeApiClient;
9920
+ }
9921
+ async listDatabases(accessToken, requestId) {
9922
+ return this.lakeApiClient.fetchDatabases(accessToken, requestId);
9923
+ }
9924
+ };
9925
+
9926
+ // src/services/lake/tableService.ts
9927
+ init_cjs_shims();
9928
+ var TableService = class {
9929
+ constructor(lakeApiClient) {
9930
+ this.lakeApiClient = lakeApiClient;
9931
+ }
9932
+ async listTables(accessToken, requestId, database) {
9933
+ return this.lakeApiClient.fetchLakeTables(accessToken, requestId, database);
9934
+ }
9935
+ };
9936
+
9937
+ // src/services/lake/databaseLookup.ts
9938
+ init_cjs_shims();
9939
+ var SYSTEM_TABLES_DISPLAY_NAME = "System tables";
9940
+ var DEFAULT_DATABASE = { databaseName: "default" };
9941
+ async function lookupDatabase(options, lakeApiClient, accessToken) {
9942
+ if (options.databaseId) {
9943
+ if (options.databaseId === SYSTEM_TABLES_DISPLAY_NAME) {
9944
+ return DEFAULT_DATABASE;
9945
+ }
9946
+ return lookupDatabaseById(options.databaseId, lakeApiClient, accessToken);
9947
+ }
9948
+ if (options.databaseName) {
9949
+ if (options.databaseName === SYSTEM_TABLES_DISPLAY_NAME) {
9950
+ return DEFAULT_DATABASE;
9951
+ }
9952
+ return lookupDatabaseByName(options.databaseName, lakeApiClient, accessToken);
9953
+ }
9954
+ return void 0;
9955
+ }
9956
+ async function lookupDatabaseById(databaseId, lakeApiClient, accessToken) {
9957
+ const databaseService = new DatabaseService(lakeApiClient);
9958
+ const databases = await listDatabases(databaseService, accessToken);
9959
+ const match = databases.find((db) => db.databaseName === databaseId);
9960
+ if (!match) {
9961
+ console.error(
9962
+ `Error: database "${databaseId}" not found. Run "sentinel database list" to see available databases.`
9963
+ );
9964
+ process.exit(1);
9965
+ }
9966
+ return match;
9967
+ }
9968
+ async function lookupDatabaseByName(databaseName, lakeApiClient, accessToken) {
9969
+ const databaseService = new DatabaseService(lakeApiClient);
9970
+ const databases = await listDatabases(databaseService, accessToken);
9971
+ const match = databases.find((db) => db.sentinelWorkspace?.name === databaseName);
9972
+ if (!match) {
9973
+ console.error(
9974
+ `Error: database "${databaseName}" not found. Run "sentinel database list" to see available databases.`
9975
+ );
9976
+ process.exit(1);
9977
+ }
9978
+ return match;
9979
+ }
7964
9980
 
7965
9981
  // src/client/lake/index.ts
9982
+ init_cjs_shims();
9983
+ var import_common34 = __toESM(require_src());
7966
9984
  function createLakeApiClient(env) {
7967
- const requestService = new import_common8.RequestService(noopTelemetry, buildApiHeaders);
7968
- return new import_common8.LakeApiClient({
9985
+ const requestService = new import_common34.RequestService(noopTelemetry, buildApiHeaders);
9986
+ return new import_common34.LakeApiClient({
7969
9987
  requestService,
7970
9988
  telemetry: noopTelemetry,
7971
9989
  baseUrl: env.gatewayEndpoint
7972
9990
  });
7973
9991
  }
7974
9992
 
7975
- // src/utils/formatter/outputFormatter.ts
9993
+ // src/telemetry/events.ts
7976
9994
  init_cjs_shims();
7977
- var COLUMN_SEPARATOR = " ";
7978
- var HEADER_RULE_CHAR = "\u2500";
7979
- var OutputFormatter = class {
7980
- format;
7981
- constructor(options = {}) {
7982
- this.format = options.output ?? "table";
9995
+ var CliEvents = {
9996
+ /** Fired exactly once per CLI process, before Commander parses argv. */
9997
+ Invoked: "cli.invoked",
9998
+ // Top-level commands
9999
+ Login: "cli.login",
10000
+ Logout: "cli.logout",
10001
+ Token: "cli.token",
10002
+ Update: "cli.update",
10003
+ // `package` group
10004
+ PackageCreateZip: "cli.package.createzip",
10005
+ PackageValidate: "cli.package.validate",
10006
+ // `job` group
10007
+ JobPublish: "cli.job.publish",
10008
+ // `database` group
10009
+ DatabaseList: "cli.database.list",
10010
+ // `table` group
10011
+ TableList: "cli.table.list",
10012
+ TableShow: "cli.table.show"
10013
+ };
10014
+
10015
+ // src/telemetry/withCommandSpan.ts
10016
+ init_cjs_shims();
10017
+
10018
+ // src/telemetry/commandInterceptor.ts
10019
+ init_cjs_shims();
10020
+ var ATTACHED_FLAG = /* @__PURE__ */ Symbol.for("sentinel.cli.commandTelemetryAttached");
10021
+ function buildCommandPath(cmd) {
10022
+ const segments = [];
10023
+ let cursor = cmd;
10024
+ while (cursor) {
10025
+ segments.unshift(cursor.name());
10026
+ cursor = cursor.parent ?? null;
10027
+ }
10028
+ segments.shift();
10029
+ return segments.join(".");
10030
+ }
10031
+ function buildCommandInvokedAttributes(cmd) {
10032
+ const path8 = buildCommandPath(cmd);
10033
+ const segments = path8.split(".");
10034
+ const command = segments[0];
10035
+ const subcommand = segments.length > 1 ? segments[segments.length - 1] : "";
10036
+ return {
10037
+ command,
10038
+ subcommand,
10039
+ commandPath: path8
10040
+ };
10041
+ }
10042
+ function attachCommandTelemetry(program2, telemetry2) {
10043
+ const flagged = program2;
10044
+ if (flagged[ATTACHED_FLAG]) {
10045
+ return;
7983
10046
  }
7984
- /**
7985
- * Renders `rows` to stdout using the configured output format.
7986
- *
7987
- * @param rows - The data array to render.
7988
- * @param columns - Column descriptors that define headers and value extractors.
7989
- */
7990
- print(rows, columns) {
7991
- if (this.format === "json") {
7992
- this.printJson(rows, columns);
7993
- } else {
7994
- this.printTable(rows, columns);
10047
+ flagged[ATTACHED_FLAG] = true;
10048
+ program2.hook("preAction", (_thisCommand, actionCommand) => {
10049
+ try {
10050
+ const attrs = buildCommandInvokedAttributes(actionCommand);
10051
+ telemetry2.event("Command.Invoked", { properties: attrs });
10052
+ } catch {
7995
10053
  }
10054
+ });
10055
+ }
10056
+
10057
+ // src/telemetry/errorContext.ts
10058
+ init_cjs_shims();
10059
+ var import_common35 = __toESM(require_src());
10060
+ function isObject(v) {
10061
+ return typeof v === "object" && v !== null;
10062
+ }
10063
+ function extractErrorProperties(error) {
10064
+ const err = error instanceof Error ? error : new Error(String(error));
10065
+ const redacted = redactError(err);
10066
+ const props = {
10067
+ errorName: redacted.errorName,
10068
+ errorMessage: redacted.errorMessage,
10069
+ errorStack: redacted.errorStack,
10070
+ isException: "true"
10071
+ };
10072
+ if (!isObject(error)) {
10073
+ return props;
10074
+ }
10075
+ const axiosErr = error;
10076
+ if (!axiosErr.response) {
10077
+ return props;
10078
+ }
10079
+ props.status = axiosErr.response.status !== void 0 ? String(axiosErr.response.status) : "unknown";
10080
+ const method = axiosErr.config?.method;
10081
+ props.method = typeof method === "string" ? method.toUpperCase() : "unknown";
10082
+ const url = axiosErr.config?.url;
10083
+ props.url = typeof url === "string" ? url : "unknown";
10084
+ const correlationId = import_common35.CorrelationService.extractCorrelationId(axiosErr.response);
10085
+ if (correlationId) {
10086
+ props.correlationId = correlationId;
10087
+ }
10088
+ return props;
10089
+ }
10090
+ function toTelemetryProperties(props) {
10091
+ const out = {
10092
+ errorName: props.errorName,
10093
+ errorMessage: props.errorMessage,
10094
+ errorStack: props.errorStack,
10095
+ isException: props.isException
10096
+ };
10097
+ if (props.status !== void 0) {
10098
+ out.status = props.status;
7996
10099
  }
7997
- // ---------------------------------------------------------------------------
7998
- // Table rendering
7999
- // ---------------------------------------------------------------------------
8000
- printTable(rows, columns) {
8001
- if (rows.length === 0) {
8002
- console.log("(no results)");
8003
- return;
8004
- }
8005
- const widths = columns.map((col) => {
8006
- const cellWidths = rows.map((row) => String(col.key(row) ?? "").length);
8007
- return Math.max(col.header.length, ...cellWidths);
8008
- });
8009
- const header = columns.map((col, i) => col.header.padEnd(widths[i])).join(COLUMN_SEPARATOR);
8010
- console.log(header);
8011
- const rule = widths.map((w) => HEADER_RULE_CHAR.repeat(w)).join(COLUMN_SEPARATOR);
8012
- console.log(rule);
8013
- for (const row of rows) {
8014
- const line = columns.map((col, i) => String(col.key(row) ?? "").padEnd(widths[i])).join(COLUMN_SEPARATOR);
8015
- console.log(line);
8016
- }
10100
+ if (props.method !== void 0) {
10101
+ out.method = props.method;
8017
10102
  }
8018
- // ---------------------------------------------------------------------------
8019
- // JSON rendering
8020
- // ---------------------------------------------------------------------------
8021
- printJson(rows, columns) {
8022
- const records = rows.map((row) => {
8023
- const record = {};
8024
- for (const col of columns) {
8025
- const key = col.jsonKey ?? col.header.toLowerCase();
8026
- const value = col.key(row);
8027
- record[key] = value ?? null;
8028
- }
8029
- return record;
8030
- });
8031
- console.log(JSON.stringify(records, null, 2));
10103
+ if (props.url !== void 0) {
10104
+ out.url = props.url;
8032
10105
  }
8033
- };
10106
+ if (props.correlationId !== void 0) {
10107
+ out.correlationId = props.correlationId;
10108
+ }
10109
+ return out;
10110
+ }
8034
10111
 
8035
- // src/utils/error/handleApiError.ts
8036
- init_cjs_shims();
8037
- function hasStatusCode(e) {
8038
- return e instanceof Error && "statusCode" in e && typeof e.statusCode === "number";
10112
+ // src/telemetry/withCommandSpan.ts
10113
+ function withCommandSpan(eventName, handler, telemetry2) {
10114
+ return async (...args) => {
10115
+ let span;
10116
+ try {
10117
+ const command = extractCommand(args);
10118
+ const initialAttributes = command ? buildCommandInvokedAttributes(command) : void 0;
10119
+ const svc = telemetry2 ?? getTelemetry();
10120
+ span = svc.span(eventName, initialAttributes);
10121
+ } catch {
10122
+ span = void 0;
10123
+ }
10124
+ let success = true;
10125
+ try {
10126
+ await handler(...args);
10127
+ } catch (err) {
10128
+ success = false;
10129
+ const props = extractErrorProperties(err);
10130
+ safeError(span, err, toTelemetryProperties(props));
10131
+ throw err;
10132
+ } finally {
10133
+ safeAddAttributes(span, { success });
10134
+ safeEnd(span);
10135
+ }
10136
+ };
8039
10137
  }
8040
- function hasResponseStatus(e) {
8041
- return e instanceof Error && "response" in e && e.response != null && typeof e.response?.status === "number";
10138
+ function extractCommand(args) {
10139
+ const last = args[args.length - 1];
10140
+ if (last && typeof last === "object" && typeof last.name === "function") {
10141
+ return last;
10142
+ }
10143
+ return void 0;
8042
10144
  }
8043
- function getStatusInfo(error) {
8044
- if (hasStatusCode(error)) {
8045
- return ` (HTTP ${error.statusCode})`;
10145
+ function safeAddAttributes(span, extra) {
10146
+ if (!span) {
10147
+ return;
8046
10148
  }
8047
- if (hasResponseStatus(error)) {
8048
- return ` (HTTP ${error.response.status})`;
10149
+ try {
10150
+ span.addAttributes(extra);
10151
+ } catch {
8049
10152
  }
8050
- return "";
8051
10153
  }
8052
- function handleApiError(context, error) {
8053
- if (error instanceof Error) {
8054
- console.error(`Error ${context}${getStatusInfo(error)}:`, error.message);
8055
- if (process.env.DEBUG && error.stack) {
8056
- console.error(error.stack);
8057
- }
8058
- } else {
8059
- console.error(`Unknown error ${context}:`, error);
10154
+ function safeEnd(span, extra) {
10155
+ if (!span) {
10156
+ return;
10157
+ }
10158
+ try {
10159
+ span.end(extra);
10160
+ } catch {
10161
+ }
10162
+ }
10163
+ function safeError(span, err, extra) {
10164
+ if (!span) {
10165
+ return;
10166
+ }
10167
+ try {
10168
+ span.error(err instanceof Error ? err : new Error(String(err)), extra);
10169
+ } catch {
8060
10170
  }
8061
- process.exit(1);
8062
10171
  }
8063
10172
 
8064
10173
  // src/commands/lake/database.ts
8065
10174
  function addDatabaseCommand(databaseCommand) {
8066
- databaseCommand.command("list").description("List all available databases").action(async () => {
8067
- try {
8068
- const apiEnv4 = (0, import_common9.getApiEnv)(import_common9.SecurityPlatformEnvironment.Production);
8069
- const accessToken = await getToken(apiEnv4);
8070
- const lakeApiClient = createLakeApiClient(apiEnv4);
8071
- const databaseService = new DatabaseService(lakeApiClient);
8072
- const databases = await listDatabases(databaseService, accessToken);
8073
- const fmt = new OutputFormatter();
8074
- fmt.print(databases, [
8075
- { header: "ID", key: (db) => db.databaseName === "default" ? "System tables" : db.databaseName },
8076
- {
8077
- header: "NAME",
8078
- key: (db) => db.databaseName === "default" ? "System tables" : db.sentinelWorkspace?.name ?? ""
8079
- }
8080
- ]);
8081
- } catch (error) {
8082
- handleApiError("listing databases", error);
8083
- }
8084
- });
10175
+ databaseCommand.command("list").description("List all available databases").action(
10176
+ withCommandSpan(CliEvents.DatabaseList, async () => {
10177
+ try {
10178
+ const apiEnv4 = (0, import_common36.getApiEnv)(import_common36.SecurityPlatformEnvironment.Production);
10179
+ const accessToken = await getToken(apiEnv4);
10180
+ const lakeApiClient = createLakeApiClient(apiEnv4);
10181
+ const databaseService = new DatabaseService(lakeApiClient);
10182
+ const databases = await listDatabases(databaseService, accessToken);
10183
+ const fmt = new OutputFormatter();
10184
+ fmt.print(databases, [
10185
+ { header: "ID", key: (db) => db.databaseName === "default" ? "System tables" : db.databaseName },
10186
+ {
10187
+ header: "NAME",
10188
+ key: (db) => db.databaseName === "default" ? "System tables" : db.sentinelWorkspace?.name ?? ""
10189
+ }
10190
+ ]);
10191
+ } catch (error) {
10192
+ handleApiError("listing databases", error);
10193
+ }
10194
+ })
10195
+ );
8085
10196
  }
8086
10197
 
8087
10198
  // src/commands/lake/lakeTables.ts
8088
10199
  init_cjs_shims();
8089
- var import_common10 = __toESM(require_src());
10200
+ var import_common37 = __toESM(require_src());
8090
10201
  var TABLE_COLUMNS = [
8091
10202
  { header: "TABLE", key: (t) => t.name ?? "" },
8092
10203
  {
@@ -8096,25 +10207,26 @@ var TABLE_COLUMNS = [
8096
10207
  { header: "CATEGORY", key: (t) => t.category ?? "" }
8097
10208
  ];
8098
10209
  function addLakeTableListCommand(tableCommand) {
8099
- tableCommand.command("list").description("List lake tables").option("--database-id <databaseId>", "Database ID to fetch tables").option("--database-name <databaseName>", "Database name to fetch tables").action(async (options) => {
8100
- validateOptions(options);
8101
- try {
8102
- const apiEnv4 = (0, import_common10.getApiEnv)(import_common10.SecurityPlatformEnvironment.Production);
8103
- const accessToken = await getToken(apiEnv4);
8104
- const lakeApiClient = createLakeApiClient(apiEnv4);
8105
- const database = await lookupDatabase(options, lakeApiClient, accessToken);
8106
- const tableService = new TableService(lakeApiClient);
8107
- const tables = await listTables(tableService, accessToken, database);
8108
- printTables(tables);
8109
- } catch (error) {
8110
- handleApiError("listing lake tables", error);
8111
- }
8112
- });
10210
+ tableCommand.command("list").description("List lake tables").option("--database-id <databaseId>", "Database ID to fetch tables").option("--database-name <databaseName>", "Database name to fetch tables").action(
10211
+ withCommandSpan(CliEvents.TableList, async (options) => {
10212
+ try {
10213
+ validateOptions(options);
10214
+ const apiEnv4 = (0, import_common37.getApiEnv)(import_common37.SecurityPlatformEnvironment.Production);
10215
+ const accessToken = await getToken(apiEnv4);
10216
+ const lakeApiClient = createLakeApiClient(apiEnv4);
10217
+ const database = await lookupDatabase(options, lakeApiClient, accessToken);
10218
+ const tableService = new TableService(lakeApiClient);
10219
+ const tables = await listTables(tableService, accessToken, database);
10220
+ printTables(tables);
10221
+ } catch (error) {
10222
+ handleApiError("listing lake tables", error);
10223
+ }
10224
+ })
10225
+ );
8113
10226
  }
8114
10227
  function validateOptions(options) {
8115
10228
  if (options.databaseId && options.databaseName) {
8116
- console.error("Error: --database-id and --database-name are mutually exclusive. Provide only one.");
8117
- process.exit(1);
10229
+ throw new Error("--database-id and --database-name are mutually exclusive. Provide only one.");
8118
10230
  }
8119
10231
  }
8120
10232
  function printTables(tables) {
@@ -8124,38 +10236,39 @@ function printTables(tables) {
8124
10236
 
8125
10237
  // src/commands/lake/lakeTableSchema.ts
8126
10238
  init_cjs_shims();
8127
- var import_common11 = __toESM(require_src());
10239
+ var import_common38 = __toESM(require_src());
8128
10240
  var SCHEMA_COLUMNS = [
8129
10241
  { header: "COLUMN", key: (col) => col.name },
8130
10242
  { header: "TYPE", key: (col) => col.type }
8131
10243
  ];
8132
10244
  function addLakeTableShowCommand(tableCommand) {
8133
- tableCommand.command("show <tableName>").description("Show the schema of a lake table").option("--database-id <databaseId>", "Database ID to look up the table in").option("--database-name <databaseName>", "Database name to look up the table in").action(async (tableName, options) => {
8134
- validateOptions2(options);
8135
- try {
8136
- const apiEnv4 = (0, import_common11.getApiEnv)(import_common11.SecurityPlatformEnvironment.Production);
8137
- const accessToken = await getToken(apiEnv4);
8138
- const lakeApiClient = createLakeApiClient(apiEnv4);
8139
- const database = await lookupDatabase(options, lakeApiClient, accessToken) ?? DEFAULT_DATABASE;
8140
- const tableService = new TableService(lakeApiClient);
8141
- const tables = await listTables(tableService, accessToken, database);
8142
- const table = findTable(tables, tableName);
8143
- if (!table) {
8144
- console.error(
8145
- `Error: table "${tableName}" not found. Run "sentinel table list" to see available tables.`
8146
- );
8147
- process.exit(1);
10245
+ tableCommand.command("show <tableName>").description("Show the schema of a lake table").option("--database-id <databaseId>", "Database ID to look up the table in").option("--database-name <databaseName>", "Database name to look up the table in").action(
10246
+ withCommandSpan(CliEvents.TableShow, async (tableName, options) => {
10247
+ try {
10248
+ validateOptions2(options);
10249
+ const apiEnv4 = (0, import_common38.getApiEnv)(import_common38.SecurityPlatformEnvironment.Production);
10250
+ const accessToken = await getToken(apiEnv4);
10251
+ const lakeApiClient = createLakeApiClient(apiEnv4);
10252
+ const database = await lookupDatabase(options, lakeApiClient, accessToken) ?? DEFAULT_DATABASE;
10253
+ const tableService = new TableService(lakeApiClient);
10254
+ const tables = await listTables(tableService, accessToken, database);
10255
+ const table = findTable(tables, tableName);
10256
+ if (!table) {
10257
+ console.error(
10258
+ `Error: table "${tableName}" not found. Run "sentinel table list" to see available tables.`
10259
+ );
10260
+ throw new Error(`Table not found: ${tableName}`);
10261
+ }
10262
+ printSchema(table);
10263
+ } catch (error) {
10264
+ handleApiError("showing lake table schema", error);
8148
10265
  }
8149
- printSchema(table);
8150
- } catch (error) {
8151
- handleApiError("showing lake table schema", error);
8152
- }
8153
- });
10266
+ })
10267
+ );
8154
10268
  }
8155
10269
  function validateOptions2(options) {
8156
10270
  if (options.databaseId && options.databaseName) {
8157
- console.error("Error: --database-id and --database-name are mutually exclusive. Provide only one.");
8158
- process.exit(1);
10271
+ throw new Error("--database-id and --database-name are mutually exclusive. Provide only one.");
8159
10272
  }
8160
10273
  }
8161
10274
  function findTable(tables, tableName) {
@@ -8169,21 +10282,21 @@ function printSchema(table) {
8169
10282
 
8170
10283
  // src/commands/login.ts
8171
10284
  init_cjs_shims();
8172
- var os4 = __toESM(require("os"));
10285
+ var os5 = __toESM(require("os"));
8173
10286
 
8174
10287
  // src/actions/authCodeLogin.ts
8175
10288
  init_cjs_shims();
8176
10289
  var import_child_process = require("child_process");
8177
- var import_common12 = __toESM(require_src());
8178
- var apiEnv3 = (0, import_common12.getApiEnv)(import_common12.SecurityPlatformEnvironment.Production);
10290
+ var import_common39 = __toESM(require_src());
10291
+ var apiEnv3 = (0, import_common39.getApiEnv)(import_common39.SecurityPlatformEnvironment.Production);
8179
10292
  function openSystemBrowser(url) {
8180
- return new Promise((resolve, reject) => {
10293
+ return new Promise((resolve2, reject) => {
8181
10294
  const command = process.platform === "darwin" ? "open" : "xdg-open";
8182
10295
  (0, import_child_process.execFile)(command, [url], (error) => {
8183
10296
  if (error) {
8184
10297
  reject(new Error(`Failed to open browser: ${error.message}`));
8185
10298
  } else {
8186
- resolve();
10299
+ resolve2();
8187
10300
  }
8188
10301
  });
8189
10302
  });
@@ -8300,7 +10413,7 @@ function loginCommand(program2) {
8300
10413
  } else {
8301
10414
  console.log("\u{1F3E2} No tenant specified, using default authority.");
8302
10415
  }
8303
- if (os4.platform() === "win32" /* Windows */) {
10416
+ if (os5.platform() === "win32" /* Windows */) {
8304
10417
  console.log("\u{1FA9F} Authenticating via Windows Native Broker (WAM)...");
8305
10418
  await (tenant ? brokerLogin(scope, tenant) : brokerLogin(scope));
8306
10419
  console.log("\u2705 Logged in via Windows Native Broker.");
@@ -8336,64 +10449,53 @@ function logoutCommand(program2) {
8336
10449
 
8337
10450
  // src/commands/publishJob.ts
8338
10451
  init_cjs_shims();
8339
- var import_common15 = __toESM(require_src());
8340
- var import_path4 = __toESM(require("path"));
10452
+ var import_common41 = __toESM(require_src());
10453
+ var import_path6 = __toESM(require("path"));
8341
10454
 
8342
10455
  // src/actions/publishJob.ts
8343
10456
  init_cjs_shims();
8344
- var import_common13 = __toESM(require_src());
8345
- var fs2 = __toESM(require("fs"));
8346
- var import_path3 = __toESM(require("path"));
8347
- var YAML = __toESM(require("yaml"));
10457
+ var import_common40 = __toESM(require_src());
10458
+ var fs6 = __toESM(require("fs"));
10459
+ var import_path5 = __toESM(require("path"));
10460
+ var YAML2 = __toESM(require("yaml"));
8348
10461
  function parseJobConfig(jobConfigPath) {
8349
- const jobConfigContent = fs2.readFileSync(jobConfigPath, "utf-8");
8350
- const ext = import_path3.default.extname(jobConfigPath);
10462
+ const jobConfigContent = fs6.readFileSync(jobConfigPath, "utf-8");
10463
+ const ext = import_path5.default.extname(jobConfigPath);
8351
10464
  switch (ext) {
8352
10465
  case ".json":
8353
10466
  return JSON.parse(jobConfigContent);
8354
10467
  case ".yaml":
8355
10468
  case ".yml":
8356
- return YAML.parse(jobConfigContent);
10469
+ return YAML2.parse(jobConfigContent);
8357
10470
  default:
8358
10471
  throw new Error(`Unsupported file extension: ${ext}`);
8359
10472
  }
8360
10473
  }
8361
10474
  async function publish(notebookPath, jobConfigPath, jobClient, accessToken) {
8362
- if (!fs2.existsSync(notebookPath)) {
10475
+ if (!fs6.existsSync(notebookPath)) {
8363
10476
  console.error(`File not found: ${notebookPath}`);
8364
10477
  throw new Error(`File not found: ${notebookPath}`);
8365
10478
  }
8366
- if (!fs2.existsSync(jobConfigPath)) {
10479
+ if (!fs6.existsSync(jobConfigPath)) {
8367
10480
  console.error(`File not found: ${jobConfigPath}`);
8368
10481
  throw new Error(`File not found: ${jobConfigPath}`);
8369
10482
  }
8370
- const requestId = import_common13.CorrelationService.createCorrelationContext();
8371
- const notebookBase64 = fs2.readFileSync(notebookPath).toString("base64");
10483
+ const requestId = import_common40.CorrelationService.createCorrelationContext();
10484
+ const notebookBase64 = fs6.readFileSync(notebookPath).toString("base64");
8372
10485
  const jobConfig = parseJobConfig(jobConfigPath);
8373
- const payload = (0, import_common13.buildCompleteJobPayload)(jobConfig, notebookBase64);
10486
+ const payload = (0, import_common40.buildCompleteJobPayload)(jobConfig, notebookBase64);
8374
10487
  const jobName = jobConfig.jobName;
8375
10488
  await jobClient.createJob(jobName, payload, accessToken, requestId);
8376
10489
  return jobName;
8377
10490
  }
8378
10491
 
8379
- // src/client/jobs/index.ts
8380
- init_cjs_shims();
8381
- var import_common14 = __toESM(require_src());
8382
- function createJobApiClient(env, region) {
8383
- const requestService = new import_common14.RequestService(noopTelemetry, buildApiHeaders);
8384
- return new import_common14.JobApiClient({
8385
- requestService,
8386
- baseUrl: env.regions[region].gatewayEndpoint
8387
- });
8388
- }
8389
-
8390
10492
  // src/commands/publishJob.ts
8391
10493
  function addPublishCommand(jobCommand) {
8392
- jobCommand.command("publish <notebookPath>").description("Publish a scheduled job.").option("-c, --config <path>", "Path to job YAML config").option("-r, --region <region>", "Target region (e.g. eastus2)", import_common15.Region.Global).action(async (notebookPath, options) => {
10494
+ jobCommand.command("publish <notebookPath>").description("Publish a scheduled job.").option("-c, --config <path>", "Path to job YAML config").option("-r, --region <region>", "Target region (e.g. eastus2)", import_common41.Region.Global).action(async (notebookPath, options) => {
8393
10495
  try {
8394
- const resolvedNotebookPath = import_path4.default.resolve(notebookPath);
8395
- const resolvedConfigPath = import_path4.default.resolve(options.config);
8396
- const apiEnv4 = (0, import_common15.getApiEnv)(import_common15.SecurityPlatformEnvironment.Production);
10496
+ const resolvedNotebookPath = import_path6.default.resolve(notebookPath);
10497
+ const resolvedConfigPath = import_path6.default.resolve(options.config);
10498
+ const apiEnv4 = (0, import_common41.getApiEnv)(import_common41.SecurityPlatformEnvironment.Production);
8397
10499
  const region = options.region;
8398
10500
  const accessToken = await getToken(apiEnv4);
8399
10501
  const jobApiClient = createJobApiClient(apiEnv4, region);
@@ -8405,6 +10507,188 @@ function addPublishCommand(jobCommand) {
8405
10507
  });
8406
10508
  }
8407
10509
 
10510
+ // src/commands/tenant/tenantList.ts
10511
+ init_cjs_shims();
10512
+ var import_common43 = __toESM(require_src());
10513
+
10514
+ // src/services/tenant/index.ts
10515
+ init_cjs_shims();
10516
+
10517
+ // src/services/tenant/tenantService.ts
10518
+ init_cjs_shims();
10519
+ var import_common42 = __toESM(require_src());
10520
+ var TenantService = class {
10521
+ constructor(requestService, armEndpoint) {
10522
+ this.requestService = requestService;
10523
+ this.armEndpoint = armEndpoint;
10524
+ }
10525
+ /**
10526
+ * Fetches all tenants accessible to the authenticated user from ARM.
10527
+ *
10528
+ * @param accessToken - A valid ARM bearer access token.
10529
+ * @returns An array of {@link ArmTenant} objects.
10530
+ */
10531
+ async fetchAllTenants(accessToken) {
10532
+ const client = new import_common42.TenantApiClient({ requestService: this.requestService, armEndpoint: this.armEndpoint });
10533
+ return client.fetchAllTenants(accessToken);
10534
+ }
10535
+ };
10536
+
10537
+ // src/commands/tenant/tenantList.ts
10538
+ var TENANT_COLUMNS = [
10539
+ { header: "DISPLAY NAME", key: (t) => t.displayName },
10540
+ { header: "TENANT ID", key: (t) => t.tenantId },
10541
+ { header: "DEFAULT DOMAIN", key: (t) => t.defaultDomain }
10542
+ ];
10543
+ function addTenantListCommand(tenantCommand) {
10544
+ tenantCommand.command("list").description("List accessible tenants for the signed-in user").action(async () => {
10545
+ try {
10546
+ const apiEnv4 = (0, import_common43.getApiEnv)(import_common43.SecurityPlatformEnvironment.Production);
10547
+ const accessToken = await getToken(apiEnv4, [apiEnv4.armScope]);
10548
+ const requestService = new import_common43.RequestService(noopTelemetry, buildApiHeaders);
10549
+ const tenantService = new TenantService(requestService, apiEnv4.armEndpoint);
10550
+ const tenants = await tenantService.fetchAllTenants(accessToken);
10551
+ printTenants(tenants);
10552
+ } catch (error) {
10553
+ handleApiError("listing tenants", error);
10554
+ }
10555
+ });
10556
+ }
10557
+ function printTenants(tenants) {
10558
+ const fmt = new OutputFormatter();
10559
+ fmt.print(tenants, TENANT_COLUMNS);
10560
+ }
10561
+
10562
+ // src/commands/tenant/tenantSelect.ts
10563
+ init_cjs_shims();
10564
+ var import_common44 = __toESM(require_src());
10565
+ var os6 = __toESM(require("os"));
10566
+ var readline = __toESM(require("readline"));
10567
+ var MAX_PROMPT_ATTEMPTS = 3;
10568
+ function addTenantSelectCommand(tenantCommand) {
10569
+ tenantCommand.command("select").description(
10570
+ "Select an active tenant for this account from an interactive numbered prompt. Subsequent commands will acquire tokens for the selected tenant."
10571
+ ).action(async () => {
10572
+ try {
10573
+ const apiEnv4 = (0, import_common44.getApiEnv)(import_common44.SecurityPlatformEnvironment.Production);
10574
+ const armToken = await getToken(apiEnv4, [apiEnv4.armScope]);
10575
+ const requestService = new import_common44.RequestService(noopTelemetry, buildApiHeaders);
10576
+ const tenantService = new TenantService(requestService, apiEnv4.armEndpoint);
10577
+ const tenants = await tenantService.fetchAllTenants(armToken);
10578
+ if (tenants.length === 0) {
10579
+ console.error("\u274C No tenants accessible for the signed-in account.");
10580
+ process.exit(1);
10581
+ }
10582
+ const activeTenantId = getActiveTenantId();
10583
+ const activeIndex = activeTenantId ? tenants.findIndex((t) => t.tenantId === activeTenantId) : -1;
10584
+ const chosen = await promptForTenant(tenants, activeIndex);
10585
+ if (!chosen) {
10586
+ console.log("\u2139 Active tenant unchanged.");
10587
+ return;
10588
+ }
10589
+ await switchActiveTenant(apiEnv4, chosen);
10590
+ console.log(`\u2705 Active tenant set to ${chosen.tenantId} (${chosen.displayName}).`);
10591
+ } catch (error) {
10592
+ handleApiError("selecting tenant", error);
10593
+ }
10594
+ });
10595
+ }
10596
+ function getActiveTenantId() {
10597
+ const session = loadBrokerAccount();
10598
+ if (!session) {
10599
+ return null;
10600
+ }
10601
+ try {
10602
+ const url = new URL(session.authority);
10603
+ const segment = url.pathname.split("/").filter(Boolean).pop();
10604
+ if (!segment || segment === "common" || segment === "organizations" || segment === "consumers") {
10605
+ return null;
10606
+ }
10607
+ return segment;
10608
+ } catch {
10609
+ return null;
10610
+ }
10611
+ }
10612
+ async function promptForTenant(tenants, activeIndex) {
10613
+ const defaultIdx = activeIndex >= 0 ? activeIndex + 1 : 1;
10614
+ console.log("");
10615
+ console.log(formatTenantTable(tenants, activeIndex));
10616
+ console.log("");
10617
+ if (activeIndex >= 0) {
10618
+ console.log(`The default is marked with an *; the default tenant is '${tenants[activeIndex].tenantId}'.`);
10619
+ console.log("");
10620
+ }
10621
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
10622
+ try {
10623
+ for (let attempt = 0; attempt < MAX_PROMPT_ATTEMPTS; attempt++) {
10624
+ const answer = await new Promise((resolve2) => {
10625
+ rl.question(`Select a tenant by number [${defaultIdx}]: `, (a) => resolve2(a));
10626
+ });
10627
+ const trimmed = answer.trim();
10628
+ if (trimmed === "") {
10629
+ if (activeIndex >= 0) {
10630
+ return null;
10631
+ }
10632
+ return tenants[defaultIdx - 1];
10633
+ }
10634
+ if (/^\d+$/.test(trimmed)) {
10635
+ const idx = Number.parseInt(trimmed, 10);
10636
+ if (idx >= 1 && idx <= tenants.length) {
10637
+ const chosen = tenants[idx - 1];
10638
+ if (activeIndex >= 0 && idx - 1 === activeIndex) {
10639
+ return null;
10640
+ }
10641
+ return chosen;
10642
+ }
10643
+ }
10644
+ console.error(`Invalid selection. Enter a number between 1 and ${tenants.length}.`);
10645
+ }
10646
+ throw new Error("Too many invalid attempts. Aborting.");
10647
+ } finally {
10648
+ rl.close();
10649
+ }
10650
+ }
10651
+ function formatTenantTable(tenants, activeIndex) {
10652
+ const headers = ["No.", "", "Tenant ID", "Tenant Name", "Default Domain"];
10653
+ const rows = tenants.map((t, i) => {
10654
+ const marker = i === activeIndex ? "*" : "";
10655
+ return [`[${i + 1}]`, marker, t.tenantId, t.displayName, t.defaultDomain ?? ""];
10656
+ });
10657
+ const widths = headers.map((h, col) => Math.max(h.length, ...rows.map((r) => r[col].length)));
10658
+ const pad = (s, w) => s + " ".repeat(Math.max(0, w - s.length));
10659
+ const formatRow = (cells) => cells.map((c, i) => pad(c, widths[i])).join(" ");
10660
+ return [formatRow(headers), ...rows.map(formatRow)].join("\n");
10661
+ }
10662
+ async function switchActiveTenant(apiEnv4, chosen) {
10663
+ const newAuthority = `${new URL(apiEnv4.aadEndpoint).origin}/${chosen.tenantId}`;
10664
+ const session = loadBrokerAccount();
10665
+ if (!session) {
10666
+ throw new Error(
10667
+ "No active session found. Run 'sentinel login' first. Note: tenant switching is not supported for managed-identity / workload-identity sign-ins."
10668
+ );
10669
+ }
10670
+ const msalInstance = await getMsalInstance(apiEnv4, newAuthority);
10671
+ try {
10672
+ const result = await msalInstance.acquireTokenSilent({
10673
+ account: session.account,
10674
+ scopes: [apiEnv4.gatewayAuthResourceUri],
10675
+ authority: newAuthority
10676
+ });
10677
+ const acquiredTid = result?.account?.tenantId;
10678
+ if (result?.accessToken && acquiredTid === chosen.tenantId) {
10679
+ saveBrokerAccount(result.account, newAuthority);
10680
+ return;
10681
+ }
10682
+ } catch {
10683
+ }
10684
+ console.log(`\u2139 Silent sign-in to tenant ${chosen.tenantId} not possible \u2014 prompting interactively\u2026`);
10685
+ if (os6.platform() === "win32") {
10686
+ await brokerLogin([apiEnv4.gatewayAuthResourceUri], chosen.tenantId);
10687
+ } else {
10688
+ await browserAuthLogin([apiEnv4.gatewayAuthResourceUri], chosen.tenantId);
10689
+ }
10690
+ }
10691
+
8408
10692
  // src/commands/token.ts
8409
10693
  init_cjs_shims();
8410
10694
  function addTokenCommand(program2) {
@@ -8420,7 +10704,7 @@ init_cjs_shims();
8420
10704
  init_cjs_shims();
8421
10705
  var import_axios = __toESM(require("axios"));
8422
10706
  var import_child_process2 = require("child_process");
8423
- var os5 = __toESM(require("os"));
10707
+ var os7 = __toESM(require("os"));
8424
10708
  var import_semver = __toESM(require("semver"));
8425
10709
  var PACKAGE_NAME = "@microsoft/sentinel-cli";
8426
10710
  var NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
@@ -8437,8 +10721,11 @@ function isNewerVersion(current, latest) {
8437
10721
  return import_semver.default.gt(l, c);
8438
10722
  }
8439
10723
  function runUpdateCommand(version) {
10724
+ if (!import_semver.default.valid(version)) {
10725
+ throw new Error(`Invalid version: ${version}`);
10726
+ }
8440
10727
  const args = ["install", "-g", `${PACKAGE_NAME}@${version}`];
8441
- (0, import_child_process2.execFileSync)("npm", args, { stdio: "inherit" });
10728
+ (0, import_child_process2.execFileSync)("npm", args, { stdio: "inherit", shell: true });
8442
10729
  }
8443
10730
  async function checkAndUpdate(currentVersion) {
8444
10731
  console.log(`\u{1F50D} Current version: ${currentVersion}`);
@@ -8450,7 +10737,7 @@ async function checkAndUpdate(currentVersion) {
8450
10737
  }
8451
10738
  console.log(`\u{1F4E6} New version available: ${latestVersion}`);
8452
10739
  console.log(`\u2B06\uFE0F Updating from ${currentVersion} to ${latestVersion}...`);
8453
- if (os5.platform() !== "win32") {
10740
+ if (os7.platform() !== "win32") {
8454
10741
  console.log(
8455
10742
  "\u2139\uFE0F On macOS/Linux, you may need administrator privileges. If the update fails, try running with sudo."
8456
10743
  );
@@ -8463,7 +10750,7 @@ async function checkAndUpdate(currentVersion) {
8463
10750
  function addUpdateCliCommand(program2) {
8464
10751
  program2.command("update").description("Update the Sentinel CLI to the latest version").action(async () => {
8465
10752
  try {
8466
- await checkAndUpdate("0.2.0");
10753
+ await checkAndUpdate("0.3.0");
8467
10754
  } catch (error) {
8468
10755
  console.error(`\u274C Update failed: ${error instanceof Error ? error.message : error}`);
8469
10756
  process.exitCode = 1;
@@ -8477,12 +10764,12 @@ init_cjs_shims();
8477
10764
 
8478
10765
  // src/actions/validate.ts
8479
10766
  init_cjs_shims();
8480
- var import_common16 = __toESM(require_src());
10767
+ var import_common45 = __toESM(require_src());
8481
10768
  async function validateFromFile(filePath) {
8482
10769
  if (!filePath || typeof filePath !== "string" || filePath.trim().length === 0) {
8483
10770
  throw new Error("Invalid file path provided.");
8484
10771
  }
8485
- const manifestProcessorService = new import_common16.ManifestProcessorService();
10772
+ const manifestProcessorService = new import_common45.ManifestProcessorService();
8486
10773
  console.info("Starting validate process...");
8487
10774
  await manifestProcessorService.validateManifest(filePath);
8488
10775
  }
@@ -8509,6 +10796,23 @@ function addValidateCommand(packageCommand) {
8509
10796
  function registerCommands(program2) {
8510
10797
  const jobCommand = program2.command("job").description("Manage scheduled jobs");
8511
10798
  addPublishCommand(jobCommand);
10799
+ addJobListCommand(jobCommand);
10800
+ addJobShowCommand(jobCommand);
10801
+ addJobDeleteCommand(jobCommand);
10802
+ addJobDisableCommand(jobCommand);
10803
+ addJobEnableCommand(jobCommand);
10804
+ addJobDownloadCommand(jobCommand);
10805
+ const runCommand = jobCommand.command("run").description("Manage job runs");
10806
+ addJobRunListCommand(runCommand);
10807
+ addJobRunShowCommand(runCommand);
10808
+ addJobRunStartCommand(runCommand);
10809
+ addJobRunCancelCommand(runCommand);
10810
+ const notebookCommand = runCommand.command("notebook").description("Manage job run notebooks");
10811
+ addJobRunNotebookDownloadCommand(notebookCommand);
10812
+ const definitionCommand = jobCommand.command("definition").description("Manage job definition YAML files");
10813
+ addJobDefinitionCreateCommand(definitionCommand);
10814
+ addJobDefinitionShowCommand(definitionCommand);
10815
+ addJobDefinitionUpdateCommand(definitionCommand);
8512
10816
  const packageCommand = program2.command("package").description("Manage packages");
8513
10817
  addCreateZipCommand(packageCommand);
8514
10818
  addValidateCommand(packageCommand);
@@ -8517,6 +10821,9 @@ function registerCommands(program2) {
8517
10821
  const tableCommand = program2.command("table").description("Manage sentinel lake tables");
8518
10822
  addLakeTableListCommand(tableCommand);
8519
10823
  addLakeTableShowCommand(tableCommand);
10824
+ const tenantCommand = program2.command("tenant").description("Manage tenants");
10825
+ addTenantListCommand(tenantCommand);
10826
+ addTenantSelectCommand(tenantCommand);
8520
10827
  loginCommand(program2);
8521
10828
  logoutCommand(program2);
8522
10829
  addTokenCommand(program2);
@@ -8524,13 +10831,21 @@ function registerCommands(program2) {
8524
10831
  }
8525
10832
 
8526
10833
  // src/index.ts
8527
- import_commander.program.name("sentinel").description("Sentinel CLI \u2014 manage lake tables, databases, scheduled jobs, packages, and authentication").version("0.2.0");
10834
+ import_commander.program.name("sentinel").description("Sentinel CLI \u2014 manage lake tables, databases, scheduled jobs, packages, and authentication").version("0.3.0");
8528
10835
  registerCommands(import_commander.program);
10836
+ var telemetry = getTelemetry();
10837
+ telemetry.event("cli.invoked", {
10838
+ properties: { command: process.argv[2] ?? "<none>" }
10839
+ });
10840
+ attachCommandTelemetry(import_commander.program, telemetry);
8529
10841
  (async () => {
10842
+ let exitCode = 0;
8530
10843
  try {
8531
10844
  await import_commander.program.parseAsync(process.argv);
8532
- process.exit(0);
8533
10845
  } catch {
8534
- process.exit(1);
10846
+ exitCode = 1;
10847
+ } finally {
10848
+ await telemetry.dispose();
8535
10849
  }
10850
+ process.exit(exitCode);
8536
10851
  })();