@socketsecurity/cli-with-sentry 1.1.48 → 1.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/cli.js +121 -15
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/npm/cmd-npm.d.mts.map +1 -1
- package/dist/types/commands/npx/cmd-npx.d.mts.map +1 -1
- package/dist/types/commands/pnpm/cmd-pnpm.d.mts.map +1 -1
- package/dist/types/commands/yarn/cmd-yarn.d.mts.map +1 -1
- package/dist/types/utils/ecosystem.d.mts +6 -3
- package/dist/types/utils/ecosystem.d.mts.map +1 -1
- package/dist/types/utils/sdk.d.mts.map +1 -1
- package/dist/types/utils/telemetry/integration.d.mts +146 -0
- package/dist/types/utils/telemetry/integration.d.mts.map +1 -0
- package/dist/types/utils/telemetry/service.d.mts +65 -0
- package/dist/types/utils/telemetry/service.d.mts.map +1 -0
- package/dist/types/utils/telemetry/types.d.mts +40 -0
- package/dist/types/utils/telemetry/types.d.mts.map +1 -0
- package/dist/utils.js +915 -16
- package/dist/utils.js.map +1 -1
- package/dist/vendor.js +124 -20
- package/package.json +4 -4
package/dist/utils.js
CHANGED
|
@@ -23,10 +23,13 @@ var require$$13 = require('../external/@socketsecurity/registry/lib/url');
|
|
|
23
23
|
var agent = require('../external/@socketsecurity/registry/lib/agent');
|
|
24
24
|
var bin = require('../external/@socketsecurity/registry/lib/bin');
|
|
25
25
|
var packages = require('../external/@socketsecurity/registry/lib/packages');
|
|
26
|
-
var require$$0 = require('node:url');
|
|
26
|
+
var require$$0$1 = require('node:url');
|
|
27
27
|
var globs = require('../external/@socketsecurity/registry/lib/globs');
|
|
28
28
|
var streams = require('../external/@socketsecurity/registry/lib/streams');
|
|
29
29
|
var promises = require('node:timers/promises');
|
|
30
|
+
var os = require('node:os');
|
|
31
|
+
var process$1 = require('node:process');
|
|
32
|
+
var require$$0 = require('node:crypto');
|
|
30
33
|
|
|
31
34
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
32
35
|
/**
|
|
@@ -545,7 +548,7 @@ function updateConfigValue(configKey, value) {
|
|
|
545
548
|
* - Used for permission validation and help text
|
|
546
549
|
*/
|
|
547
550
|
|
|
548
|
-
const require$3 = require$$5.createRequire((typeof document === 'undefined' ? require$$0.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
551
|
+
const require$3 = require$$5.createRequire((typeof document === 'undefined' ? require$$0$1.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
549
552
|
let _requirements;
|
|
550
553
|
function getRequirements() {
|
|
551
554
|
if (_requirements === undefined) {
|
|
@@ -561,6 +564,849 @@ function getRequirementsKey(cmdPath) {
|
|
|
561
564
|
return cmdPath.replace(/^socket[: ]/, '').replace(/ +/g, ':');
|
|
562
565
|
}
|
|
563
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Telemetry service for Socket CLI.
|
|
569
|
+
* Manages event collection, batching, and submission to Socket API.
|
|
570
|
+
*
|
|
571
|
+
* IMPORTANT: Telemetry is ALWAYS scoped to an organization.
|
|
572
|
+
* Cannot track telemetry without an org context.
|
|
573
|
+
*
|
|
574
|
+
* Features:
|
|
575
|
+
* - Singleton pattern (one instance per process)
|
|
576
|
+
* - Organization-scoped tracking (required)
|
|
577
|
+
* - Event batching (auto-flush at batch size)
|
|
578
|
+
* - Exit handlers (auto-flush on process exit)
|
|
579
|
+
* - Automatic session ID assignment
|
|
580
|
+
* - Explicit finalization via destroy() for controlled cleanup
|
|
581
|
+
* - Graceful degradation (errors don't block CLI)
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```typescript
|
|
585
|
+
* // Get telemetry client (returns singleton instance)
|
|
586
|
+
* const telemetry = await TelemetryService.getTelemetryClient('my-org')
|
|
587
|
+
*
|
|
588
|
+
* // Track an event (session_id is auto-set)
|
|
589
|
+
* telemetry.track({
|
|
590
|
+
* event_sender_created_at: new Date().toISOString(),
|
|
591
|
+
* event_type: 'cli_start',
|
|
592
|
+
* context: {
|
|
593
|
+
* version: '2.2.15',
|
|
594
|
+
* platform: process.platform,
|
|
595
|
+
* node_version: process.version,
|
|
596
|
+
* arch: process.arch,
|
|
597
|
+
* argv: process.argv.slice(2)
|
|
598
|
+
* }
|
|
599
|
+
* })
|
|
600
|
+
*
|
|
601
|
+
* // Flush happens automatically on batch size and exit
|
|
602
|
+
* // Can also be called manually if needed
|
|
603
|
+
* await telemetry.flush()
|
|
604
|
+
*
|
|
605
|
+
* // Always call destroy() before exit to flush remaining events
|
|
606
|
+
* await telemetry.destroy()
|
|
607
|
+
* ```
|
|
608
|
+
*/
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Debug wrapper for telemetry service.
|
|
612
|
+
* Wraps debugFn to provide a simpler API.
|
|
613
|
+
*/
|
|
614
|
+
const debug$1 = message => {
|
|
615
|
+
require$$9.debugFn('socket:telemetry:service', message);
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* DebugDir wrapper for telemetry service.
|
|
620
|
+
*/
|
|
621
|
+
const debugDirWrapper = obj => {
|
|
622
|
+
require$$9.debugDir('socket:telemetry:service', obj);
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Process-wide session ID.
|
|
627
|
+
* Generated once per CLI invocation and shared across all telemetry instances.
|
|
628
|
+
*/
|
|
629
|
+
const SESSION_ID = require$$0.randomUUID();
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Default telemetry configuration.
|
|
633
|
+
* Used as fallback if API config fetch fails.
|
|
634
|
+
*/
|
|
635
|
+
const DEFAULT_TELEMETRY_CONFIG = {
|
|
636
|
+
telemetry: {
|
|
637
|
+
enabled: false
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Static configuration for telemetry service behavior.
|
|
643
|
+
*/
|
|
644
|
+
const TELEMETRY_SERVICE_CONFIG = {
|
|
645
|
+
batch_size: 10,
|
|
646
|
+
// Auto-flush when queue reaches this size.
|
|
647
|
+
flush_timeout: 2_000 // 2 second maximum for flush operations.
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Singleton instance holder.
|
|
652
|
+
*/
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Singleton telemetry service instance holder.
|
|
656
|
+
* Only one instance exists per process.
|
|
657
|
+
*/
|
|
658
|
+
const telemetryServiceInstance = {
|
|
659
|
+
current: null
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Wrap a promise with a timeout.
|
|
664
|
+
* Rejects if promise doesn't resolve within timeout.
|
|
665
|
+
*
|
|
666
|
+
* @param promise Promise to wrap.
|
|
667
|
+
* @param timeoutMs Timeout in milliseconds.
|
|
668
|
+
* @param errorMessage Error message if timeout occurs.
|
|
669
|
+
* @returns Promise that resolves or times out.
|
|
670
|
+
*/
|
|
671
|
+
function withTimeout(promise, timeoutMs, errorMessage) {
|
|
672
|
+
return Promise.race([promise, new Promise((_, reject) => {
|
|
673
|
+
setTimeout(() => {
|
|
674
|
+
reject(new Error(errorMessage));
|
|
675
|
+
}, timeoutMs);
|
|
676
|
+
})]);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Centralized telemetry service for Socket CLI.
|
|
681
|
+
* Telemetry is always scoped to an organization.
|
|
682
|
+
* Singleton pattern ensures only one instance exists per process.
|
|
683
|
+
*
|
|
684
|
+
* NOTE: Only one telemetry instance exists per process.
|
|
685
|
+
* If getTelemetryClient() is called with a different organization slug,
|
|
686
|
+
* it returns the existing instance for the original organization.
|
|
687
|
+
* Switching organizations mid-execution is not supported - the first
|
|
688
|
+
* organization to initialize telemetry will be used for the entire process.
|
|
689
|
+
*
|
|
690
|
+
* This is intended, since we can't switch an org during command execution.
|
|
691
|
+
*/
|
|
692
|
+
class TelemetryService {
|
|
693
|
+
config = null;
|
|
694
|
+
eventQueue = [];
|
|
695
|
+
isDestroyed = false;
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Private constructor.
|
|
699
|
+
* Requires organization slug.
|
|
700
|
+
*
|
|
701
|
+
* @param orgSlug - Organization identifier.
|
|
702
|
+
*/
|
|
703
|
+
constructor(orgSlug) {
|
|
704
|
+
this.orgSlug = orgSlug;
|
|
705
|
+
debug$1(`Telemetry service created for org '${orgSlug}' with session ID: ${SESSION_ID}`);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Get the current telemetry instance if one exists.
|
|
710
|
+
* Does not create a new instance.
|
|
711
|
+
*
|
|
712
|
+
* @returns Current telemetry instance or null if none exists.
|
|
713
|
+
*/
|
|
714
|
+
static getCurrentInstance() {
|
|
715
|
+
return telemetryServiceInstance.current;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Get telemetry client for an organization.
|
|
720
|
+
* Creates and initializes client if it doesn't exist.
|
|
721
|
+
* Returns existing instance if already initialized.
|
|
722
|
+
*
|
|
723
|
+
* @param orgSlug - Organization identifier (required).
|
|
724
|
+
* @returns Initialized telemetry service instance.
|
|
725
|
+
*/
|
|
726
|
+
static async getTelemetryClient(orgSlug) {
|
|
727
|
+
// Return existing instance if already initialized.
|
|
728
|
+
if (telemetryServiceInstance.current) {
|
|
729
|
+
debug$1(`Telemetry already initialized for org: ${telemetryServiceInstance.current.orgSlug}`);
|
|
730
|
+
return telemetryServiceInstance.current;
|
|
731
|
+
}
|
|
732
|
+
const instance = new TelemetryService(orgSlug);
|
|
733
|
+
try {
|
|
734
|
+
const sdkResult = await setupSdk();
|
|
735
|
+
if (!sdkResult.ok) {
|
|
736
|
+
debug$1('Failed to setup SDK for telemetry, using default config');
|
|
737
|
+
instance.config = DEFAULT_TELEMETRY_CONFIG;
|
|
738
|
+
telemetryServiceInstance.current = instance;
|
|
739
|
+
return instance;
|
|
740
|
+
}
|
|
741
|
+
const sdk = sdkResult.data;
|
|
742
|
+
const configResult = await sdk.getOrgTelemetryConfig(orgSlug);
|
|
743
|
+
if (configResult.success) {
|
|
744
|
+
instance.config = configResult.data;
|
|
745
|
+
debug$1(`Telemetry configuration fetched successfully: enabled=${instance.config.telemetry.enabled}`);
|
|
746
|
+
debugDirWrapper({
|
|
747
|
+
config: instance.config
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
// Periodic flush will start automatically when first event is tracked.
|
|
751
|
+
} else {
|
|
752
|
+
debug$1(`Failed to fetch telemetry config: ${configResult.error}`);
|
|
753
|
+
instance.config = DEFAULT_TELEMETRY_CONFIG;
|
|
754
|
+
}
|
|
755
|
+
} catch (e) {
|
|
756
|
+
debug$1(`Error initializing telemetry: ${e}`);
|
|
757
|
+
instance.config = DEFAULT_TELEMETRY_CONFIG;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Only set singleton instance after full initialization.
|
|
761
|
+
telemetryServiceInstance.current = instance;
|
|
762
|
+
return instance;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Track a telemetry event.
|
|
767
|
+
* Adds event to queue for batching and eventual submission.
|
|
768
|
+
* Auto-flushes when batch size is reached.
|
|
769
|
+
*
|
|
770
|
+
* @param event - Telemetry event to track (session_id is optional and will be auto-set).
|
|
771
|
+
*/
|
|
772
|
+
track(event) {
|
|
773
|
+
debug$1('Incoming track event request');
|
|
774
|
+
if (this.isDestroyed) {
|
|
775
|
+
debug$1('Telemetry service destroyed, ignoring event');
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
if (!this.config?.telemetry.enabled) {
|
|
779
|
+
debug$1(`Telemetry disabled, skipping event: ${event.event_type}`);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Create complete event with session_id and org_slug.
|
|
784
|
+
const completeEvent = {
|
|
785
|
+
...event,
|
|
786
|
+
session_id: SESSION_ID
|
|
787
|
+
};
|
|
788
|
+
debug$1(`Tracking telemetry event: ${completeEvent.event_type}`);
|
|
789
|
+
debugDirWrapper(completeEvent);
|
|
790
|
+
this.eventQueue.push(completeEvent);
|
|
791
|
+
|
|
792
|
+
// Auto-flush if batch size reached.
|
|
793
|
+
const batchSize = TELEMETRY_SERVICE_CONFIG.batch_size;
|
|
794
|
+
if (this.eventQueue.length >= batchSize) {
|
|
795
|
+
debug$1(`Batch size reached (${batchSize}), flushing events`);
|
|
796
|
+
void this.flush();
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Flush all queued events to the API.
|
|
802
|
+
* Returns immediately if no events queued or telemetry disabled.
|
|
803
|
+
* Times out after configured flush_timeout to prevent blocking CLI exit.
|
|
804
|
+
*/
|
|
805
|
+
async flush() {
|
|
806
|
+
if (this.isDestroyed) {
|
|
807
|
+
debug$1('Telemetry service destroyed, cannot flush');
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (this.eventQueue.length === 0) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
if (!this.config?.telemetry.enabled) {
|
|
814
|
+
debug$1('Telemetry disabled, clearing queue without sending');
|
|
815
|
+
this.eventQueue = [];
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
const eventsToSend = [...this.eventQueue];
|
|
819
|
+
this.eventQueue = [];
|
|
820
|
+
debug$1(`Flushing ${eventsToSend.length} telemetry events`);
|
|
821
|
+
const flushStartTime = Date.now();
|
|
822
|
+
try {
|
|
823
|
+
await withTimeout(this.sendEvents(eventsToSend), TELEMETRY_SERVICE_CONFIG.flush_timeout, `Telemetry flush timed out after ${TELEMETRY_SERVICE_CONFIG.flush_timeout}ms`);
|
|
824
|
+
const flushDuration = Date.now() - flushStartTime;
|
|
825
|
+
debug$1(`Telemetry events sent successfully (${eventsToSend.length} events in ${flushDuration}ms)`);
|
|
826
|
+
} catch (e) {
|
|
827
|
+
const flushDuration = Date.now() - flushStartTime;
|
|
828
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
829
|
+
|
|
830
|
+
// Check if this is a timeout error.
|
|
831
|
+
if (errorMessage.includes('timed out') || flushDuration >= TELEMETRY_SERVICE_CONFIG.flush_timeout) {
|
|
832
|
+
debug$1(`Telemetry flush timed out after ${TELEMETRY_SERVICE_CONFIG.flush_timeout}ms`);
|
|
833
|
+
debug$1(`Failed to send ${eventsToSend.length} events due to timeout`);
|
|
834
|
+
} else {
|
|
835
|
+
debug$1(`Error flushing telemetry: ${errorMessage}`);
|
|
836
|
+
debug$1(`Failed to send ${eventsToSend.length} events due to error`);
|
|
837
|
+
}
|
|
838
|
+
// Events are discarded on error to prevent infinite growth.
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Send events to the API.
|
|
844
|
+
* Extracted as separate method for timeout wrapping.
|
|
845
|
+
*
|
|
846
|
+
* @param events Events to send.
|
|
847
|
+
*/
|
|
848
|
+
async sendEvents(events) {
|
|
849
|
+
const sdkResult = await setupSdk();
|
|
850
|
+
if (!sdkResult.ok) {
|
|
851
|
+
debug$1('Failed to setup SDK for flush, events discarded');
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
const sdk = sdkResult.data;
|
|
855
|
+
|
|
856
|
+
// Track flush statistics.
|
|
857
|
+
let successCount = 0;
|
|
858
|
+
let failureCount = 0;
|
|
859
|
+
|
|
860
|
+
// Send events in parallel for faster flush.
|
|
861
|
+
// Use allSettled to ensure all sends are attempted even if some fail.
|
|
862
|
+
const results = await Promise.allSettled(events.map(async event => {
|
|
863
|
+
const result = await sdk.postOrgTelemetry(this.orgSlug, event);
|
|
864
|
+
return {
|
|
865
|
+
event,
|
|
866
|
+
result
|
|
867
|
+
};
|
|
868
|
+
}));
|
|
869
|
+
|
|
870
|
+
// Log results and collect statistics.
|
|
871
|
+
for (const settledResult of results) {
|
|
872
|
+
if (settledResult.status === 'fulfilled') {
|
|
873
|
+
const {
|
|
874
|
+
event,
|
|
875
|
+
result
|
|
876
|
+
} = settledResult.value;
|
|
877
|
+
if (result.success) {
|
|
878
|
+
successCount++;
|
|
879
|
+
debug$1('Telemetry sent to telemetry:');
|
|
880
|
+
debugDirWrapper(event);
|
|
881
|
+
} else {
|
|
882
|
+
failureCount++;
|
|
883
|
+
debug$1(`Failed to send telemetry event: ${result.error}`);
|
|
884
|
+
}
|
|
885
|
+
} else {
|
|
886
|
+
failureCount++;
|
|
887
|
+
debug$1(`Telemetry request failed: ${settledResult.reason}`);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Log flush statistics.
|
|
892
|
+
debug$1(`Flush stats: ${successCount} succeeded, ${failureCount} failed out of ${events.length} total`);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Destroy the telemetry service for this organization.
|
|
897
|
+
* Flushes remaining events and clears all state.
|
|
898
|
+
* Idempotent - safe to call multiple times.
|
|
899
|
+
*/
|
|
900
|
+
async destroy() {
|
|
901
|
+
if (this.isDestroyed) {
|
|
902
|
+
debug$1('Telemetry service already destroyed, skipping');
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
debug$1(`Destroying telemetry service for org: ${this.orgSlug}`);
|
|
906
|
+
|
|
907
|
+
// Mark as destroyed immediately to prevent concurrent destroy() calls.
|
|
908
|
+
this.isDestroyed = true;
|
|
909
|
+
|
|
910
|
+
// Flush remaining events with timeout.
|
|
911
|
+
const eventsToFlush = [...this.eventQueue];
|
|
912
|
+
this.eventQueue = [];
|
|
913
|
+
if (eventsToFlush.length > 0 && this.config?.telemetry.enabled) {
|
|
914
|
+
debug$1(`Flushing ${eventsToFlush.length} events before destroy`);
|
|
915
|
+
const flushStartTime = Date.now();
|
|
916
|
+
try {
|
|
917
|
+
await withTimeout(this.sendEvents(eventsToFlush), TELEMETRY_SERVICE_CONFIG.flush_timeout, `Telemetry flush during destroy timed out after ${TELEMETRY_SERVICE_CONFIG.flush_timeout}ms`);
|
|
918
|
+
const flushDuration = Date.now() - flushStartTime;
|
|
919
|
+
debug$1(`Events flushed successfully during destroy (${flushDuration}ms)`);
|
|
920
|
+
} catch (e) {
|
|
921
|
+
const flushDuration = Date.now() - flushStartTime;
|
|
922
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
923
|
+
|
|
924
|
+
// Check if this is a timeout error.
|
|
925
|
+
if (errorMessage.includes('timed out') || flushDuration >= TELEMETRY_SERVICE_CONFIG.flush_timeout) {
|
|
926
|
+
debug$1(`Telemetry flush during destroy timed out after ${TELEMETRY_SERVICE_CONFIG.flush_timeout}ms`);
|
|
927
|
+
debug$1(`Failed to send ${eventsToFlush.length} events during destroy due to timeout`);
|
|
928
|
+
} else {
|
|
929
|
+
debug$1(`Error flushing telemetry during destroy: ${errorMessage}`);
|
|
930
|
+
debug$1(`Failed to send ${eventsToFlush.length} events during destroy due to error`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
this.config = null;
|
|
935
|
+
|
|
936
|
+
// Clear singleton instance.
|
|
937
|
+
telemetryServiceInstance.current = null;
|
|
938
|
+
debug$1(`Telemetry service destroyed for org: ${this.orgSlug}`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Telemetry integration helpers for Socket CLI.
|
|
944
|
+
* Provides utilities for tracking common CLI events and subprocess executions.
|
|
945
|
+
*
|
|
946
|
+
* Usage:
|
|
947
|
+
* ```typescript
|
|
948
|
+
* import {
|
|
949
|
+
* setupTelemetryExitHandlers,
|
|
950
|
+
* finalizeTelemetry,
|
|
951
|
+
* finalizeTelemetrySync,
|
|
952
|
+
* trackCliStart,
|
|
953
|
+
* trackCliEvent,
|
|
954
|
+
* trackCliComplete,
|
|
955
|
+
* trackCliError,
|
|
956
|
+
* trackSubprocessStart,
|
|
957
|
+
* trackSubprocessComplete,
|
|
958
|
+
* trackSubprocessError
|
|
959
|
+
* } from './utils/telemetry/integration.mts'
|
|
960
|
+
*
|
|
961
|
+
* // Set up exit handlers once during CLI initialization.
|
|
962
|
+
* setupTelemetryExitHandlers()
|
|
963
|
+
*
|
|
964
|
+
* // Track main CLI execution.
|
|
965
|
+
* const startTime = await trackCliStart(process.argv)
|
|
966
|
+
* await trackCliComplete(process.argv, startTime, 0)
|
|
967
|
+
*
|
|
968
|
+
* // Track custom event with optional metadata.
|
|
969
|
+
* await trackCliEvent('custom_event', process.argv, { key: 'value' })
|
|
970
|
+
*
|
|
971
|
+
* // Track subprocess/forked CLI execution.
|
|
972
|
+
* const subStart = await trackSubprocessStart('npm', { cwd: '/path' })
|
|
973
|
+
* await trackSubprocessComplete('npm', subStart, 0, { stdout_length: 1234 })
|
|
974
|
+
*
|
|
975
|
+
* // On subprocess error.
|
|
976
|
+
* await trackSubprocessError('npm', subStart, error, 1)
|
|
977
|
+
*
|
|
978
|
+
* // Manual finalization (usually not needed if exit handlers are set up).
|
|
979
|
+
* await finalizeTelemetry() // Async version.
|
|
980
|
+
* finalizeTelemetrySync() // Sync version (best-effort).
|
|
981
|
+
* ```
|
|
982
|
+
*/
|
|
983
|
+
/**
|
|
984
|
+
* Debug wrapper for telemetry integration.
|
|
985
|
+
*/
|
|
986
|
+
const debug = message => {
|
|
987
|
+
require$$9.debugFn('socket:telemetry:integration', message);
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Finalize telemetry and clean up resources (async version).
|
|
992
|
+
* This should be called before process.exit to ensure telemetry is sent and resources are cleaned up.
|
|
993
|
+
* Use this in async contexts like beforeExit handlers.
|
|
994
|
+
*
|
|
995
|
+
* @returns Promise that resolves when finalization completes.
|
|
996
|
+
*/
|
|
997
|
+
async function finalizeTelemetry() {
|
|
998
|
+
const instance = TelemetryService.getCurrentInstance();
|
|
999
|
+
if (instance) {
|
|
1000
|
+
debug('Flushing telemetry');
|
|
1001
|
+
await instance.flush();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Finalize telemetry synchronously (best-effort).
|
|
1007
|
+
* This triggers a flush without awaiting it.
|
|
1008
|
+
* Use this in synchronous contexts like signal handlers where async operations are not possible.
|
|
1009
|
+
*
|
|
1010
|
+
* Note: This is best-effort only. Events may be lost if the process exits before flush completes.
|
|
1011
|
+
* Prefer finalizeTelemetry() (async version) when possible.
|
|
1012
|
+
*/
|
|
1013
|
+
function finalizeTelemetrySync() {
|
|
1014
|
+
const instance = TelemetryService.getCurrentInstance();
|
|
1015
|
+
if (instance) {
|
|
1016
|
+
debug('Triggering sync flush (best-effort)');
|
|
1017
|
+
void instance.flush();
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// Track whether exit handlers have been set up to prevent duplicate registration.
|
|
1022
|
+
let exitHandlersRegistered = false;
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Set up exit handlers for telemetry finalization.
|
|
1026
|
+
* This registers handlers for both normal exits (beforeExit) and common fatal signals.
|
|
1027
|
+
*
|
|
1028
|
+
* Flushing strategy:
|
|
1029
|
+
* - Batch-based: Auto-flush when queue reaches 10 events.
|
|
1030
|
+
* - beforeExit: Async handler for clean shutdowns (when event loop empties).
|
|
1031
|
+
* - Fatal signals (SIGINT, SIGTERM, SIGHUP): Best-effort sync flush.
|
|
1032
|
+
* - Accepts that forced exits (SIGKILL, process.exit()) may lose final events.
|
|
1033
|
+
*
|
|
1034
|
+
* Call this once during CLI initialization to ensure telemetry is flushed on exit.
|
|
1035
|
+
* Safe to call multiple times - only registers handlers once.
|
|
1036
|
+
*
|
|
1037
|
+
* @example
|
|
1038
|
+
* ```typescript
|
|
1039
|
+
* // In src/cli.mts
|
|
1040
|
+
* setupTelemetryExitHandlers()
|
|
1041
|
+
* ```
|
|
1042
|
+
*/
|
|
1043
|
+
function setupTelemetryExitHandlers() {
|
|
1044
|
+
// Prevent duplicate handler registration.
|
|
1045
|
+
if (exitHandlersRegistered) {
|
|
1046
|
+
debug('Telemetry exit handlers already registered, skipping');
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
exitHandlersRegistered = true;
|
|
1050
|
+
|
|
1051
|
+
// Use beforeExit for async finalization during clean shutdowns.
|
|
1052
|
+
// This fires when the event loop empties but before process actually exits.
|
|
1053
|
+
process$1.on('beforeExit', () => {
|
|
1054
|
+
debug('beforeExit handler triggered');
|
|
1055
|
+
void finalizeTelemetry();
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
// Register handlers for common fatal signals as best-effort fallback.
|
|
1059
|
+
// These are synchronous contexts, so we can only trigger flush without awaiting.
|
|
1060
|
+
const fatalSignals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
|
1061
|
+
for (const signal of fatalSignals) {
|
|
1062
|
+
try {
|
|
1063
|
+
process$1.on(signal, () => {
|
|
1064
|
+
debug(`Signal ${signal} received, attempting sync flush`);
|
|
1065
|
+
finalizeTelemetrySync();
|
|
1066
|
+
});
|
|
1067
|
+
} catch (e) {
|
|
1068
|
+
// Some signals may not be available on all platforms.
|
|
1069
|
+
debug(`Failed to register handler for signal ${signal}: ${e}`);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
debug('Telemetry exit handlers registered (beforeExit + common signals)');
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Track subprocess exit and finalize telemetry.
|
|
1077
|
+
* This is a convenience function that tracks completion/error based on exit code
|
|
1078
|
+
* and ensures telemetry is flushed before returning.
|
|
1079
|
+
*
|
|
1080
|
+
* Note: Only tracks subprocess-level events. CLI-level events (cli_complete, cli_error)
|
|
1081
|
+
* are tracked by the main CLI entry point in src/cli.mts.
|
|
1082
|
+
*
|
|
1083
|
+
* @param command - Command name (e.g., 'npm', 'pip').
|
|
1084
|
+
* @param startTime - Start timestamp from trackSubprocessStart.
|
|
1085
|
+
* @param exitCode - Process exit code (null treated as error).
|
|
1086
|
+
* @returns Promise that resolves when tracking and flush complete.
|
|
1087
|
+
*
|
|
1088
|
+
* @example
|
|
1089
|
+
* ```typescript
|
|
1090
|
+
* await trackSubprocessExit(NPM, subprocessStartTime, code)
|
|
1091
|
+
* ```
|
|
1092
|
+
*/
|
|
1093
|
+
async function trackSubprocessExit(command, startTime, exitCode) {
|
|
1094
|
+
// Track subprocess completion or error based on exit code.
|
|
1095
|
+
if (exitCode !== null && exitCode !== 0) {
|
|
1096
|
+
const error = new Error(`${command} exited with code ${exitCode}`);
|
|
1097
|
+
await trackSubprocessError(command, startTime, error, exitCode);
|
|
1098
|
+
} else if (exitCode === 0) {
|
|
1099
|
+
await trackSubprocessComplete(command, startTime, exitCode);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Flush telemetry to ensure events are sent before exit.
|
|
1103
|
+
await finalizeTelemetry();
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// Add other subcommands
|
|
1107
|
+
const WRAPPER_CLI = new Set(['bun', 'npm', 'npx', 'pip', 'pnpm', 'vlt', 'yarn']);
|
|
1108
|
+
|
|
1109
|
+
// Add other sensitive flags
|
|
1110
|
+
const API_TOKEN_FLAGS = new Set(['--api-token', '--token', '-t']);
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* Calculate duration from start timestamp.
|
|
1114
|
+
*
|
|
1115
|
+
* @param startTime - Start timestamp from Date.now().
|
|
1116
|
+
* @returns Duration in milliseconds.
|
|
1117
|
+
*/
|
|
1118
|
+
function calculateDuration(startTime) {
|
|
1119
|
+
return Date.now() - startTime;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Normalize exit code to a number with default fallback.
|
|
1124
|
+
*
|
|
1125
|
+
* @param exitCode - Exit code (may be string, number, null, or undefined).
|
|
1126
|
+
* @param defaultValue - Default value if exitCode is not a number.
|
|
1127
|
+
* @returns Normalized exit code.
|
|
1128
|
+
*/
|
|
1129
|
+
function normalizeExitCode(exitCode, defaultValue) {
|
|
1130
|
+
return typeof exitCode === 'number' ? exitCode : defaultValue;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* Normalize error to Error object.
|
|
1135
|
+
*
|
|
1136
|
+
* @param error - Unknown error value.
|
|
1137
|
+
* @returns Error object.
|
|
1138
|
+
*/
|
|
1139
|
+
function normalizeError(error) {
|
|
1140
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Build context for the current telemetry entry.
|
|
1145
|
+
*
|
|
1146
|
+
* The context contains the current execution context, in which all CLI invocation should have access to.
|
|
1147
|
+
*
|
|
1148
|
+
* @param argv Command line arguments.
|
|
1149
|
+
* @returns Telemetry context object.
|
|
1150
|
+
*/
|
|
1151
|
+
function buildContext(argv) {
|
|
1152
|
+
return {
|
|
1153
|
+
arch: process$1.arch,
|
|
1154
|
+
argv: sanitizeArgv(argv),
|
|
1155
|
+
node_version: process$1.version,
|
|
1156
|
+
platform: process$1.platform,
|
|
1157
|
+
version: constants.default.ENV.INLINED_SOCKET_CLI_VERSION
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Sanitize argv to remove sensitive information.
|
|
1163
|
+
* Removes API tokens, file paths with usernames, and other PII.
|
|
1164
|
+
* Also strips arguments after wrapper CLIs to avoid leaking package names.
|
|
1165
|
+
*
|
|
1166
|
+
* @param argv Raw command line arguments (full process.argv including execPath and script).
|
|
1167
|
+
* @returns Sanitized argv array.
|
|
1168
|
+
*
|
|
1169
|
+
* @example
|
|
1170
|
+
* // Input: ['node', 'socket', 'npm', 'install', '@my/private-package', '--token', 'sktsec_abc123']
|
|
1171
|
+
* // Output: ['npm', 'install']
|
|
1172
|
+
*/
|
|
1173
|
+
function sanitizeArgv(argv) {
|
|
1174
|
+
// Strip the first two values to drop the execPath and script.
|
|
1175
|
+
const withoutPathAndScript = argv.slice(2);
|
|
1176
|
+
|
|
1177
|
+
// Then strip arguments after wrapper CLIs to avoid leaking package names.
|
|
1178
|
+
const wrapperIndex = withoutPathAndScript.findIndex(arg => WRAPPER_CLI.has(arg));
|
|
1179
|
+
let strippedArgv = withoutPathAndScript;
|
|
1180
|
+
if (wrapperIndex !== -1) {
|
|
1181
|
+
// Keep only wrapper + first command (e.g., ['npm']).
|
|
1182
|
+
const endIndex = wrapperIndex + 1;
|
|
1183
|
+
strippedArgv = withoutPathAndScript.slice(0, endIndex);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// Then sanitize remaining arguments.
|
|
1187
|
+
return strippedArgv.map((arg, index) => {
|
|
1188
|
+
// Check if previous arg was an API token flag.
|
|
1189
|
+
if (index > 0) {
|
|
1190
|
+
const prevArg = strippedArgv[index - 1];
|
|
1191
|
+
if (prevArg && API_TOKEN_FLAGS.has(prevArg)) {
|
|
1192
|
+
return '[REDACTED]';
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// Redact anything that looks like a socket API token.
|
|
1197
|
+
if (arg.startsWith('sktsec_') || arg.match(/^[a-f0-9]{32,}$/i)) {
|
|
1198
|
+
return '[REDACTED]';
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// Remove user home directory from file paths.
|
|
1202
|
+
const homeDir = os.homedir();
|
|
1203
|
+
if (homeDir) {
|
|
1204
|
+
return arg.replace(new RegExp(regexps.escapeRegExp(homeDir), 'g'), '~');
|
|
1205
|
+
}
|
|
1206
|
+
return arg;
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
/**
|
|
1211
|
+
* Sanitize error attribute to remove user specific paths.
|
|
1212
|
+
* Replaces user home directory and other sensitive paths.
|
|
1213
|
+
*
|
|
1214
|
+
* @param input Raw input.
|
|
1215
|
+
* @returns Sanitized input.
|
|
1216
|
+
*/
|
|
1217
|
+
function sanitizeErrorAttribute(input) {
|
|
1218
|
+
if (!input) {
|
|
1219
|
+
return undefined;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// Remove user home directory.
|
|
1223
|
+
const homeDir = os.homedir();
|
|
1224
|
+
if (homeDir) {
|
|
1225
|
+
return input.replace(new RegExp(regexps.escapeRegExp(homeDir), 'g'), '~');
|
|
1226
|
+
}
|
|
1227
|
+
return input;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* Generic event tracking function.
|
|
1232
|
+
* Tracks any telemetry event with optional error details and explicit flush.
|
|
1233
|
+
*
|
|
1234
|
+
* Events are automatically flushed via batch size or exit handlers.
|
|
1235
|
+
* Use the flush option only when immediate submission is required.
|
|
1236
|
+
*
|
|
1237
|
+
* @param eventType Type of event to track.
|
|
1238
|
+
* @param context Event context.
|
|
1239
|
+
* @param metadata Event metadata.
|
|
1240
|
+
* @param options Optional configuration.
|
|
1241
|
+
* @returns Promise that resolves when tracking completes.
|
|
1242
|
+
*/
|
|
1243
|
+
async function trackEvent(eventType, context, metadata = {}, options = {}) {
|
|
1244
|
+
// Skip telemetry in test environments.
|
|
1245
|
+
if (constants.default.ENV.VITEST) {
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
try {
|
|
1249
|
+
const orgSlug = getConfigValueOrUndef(constants.CONFIG_KEY_DEFAULT_ORG);
|
|
1250
|
+
if (orgSlug) {
|
|
1251
|
+
const telemetry = await TelemetryService.getTelemetryClient(orgSlug);
|
|
1252
|
+
debug(`Got telemetry service for org: ${orgSlug}`);
|
|
1253
|
+
const event = {
|
|
1254
|
+
context,
|
|
1255
|
+
event_sender_created_at: new Date().toISOString(),
|
|
1256
|
+
event_type: eventType,
|
|
1257
|
+
...(Object.keys(metadata).length > 0 && {
|
|
1258
|
+
metadata
|
|
1259
|
+
}),
|
|
1260
|
+
...(options.error && {
|
|
1261
|
+
error: {
|
|
1262
|
+
message: sanitizeErrorAttribute(options.error.message),
|
|
1263
|
+
stack: sanitizeErrorAttribute(options.error.stack),
|
|
1264
|
+
type: options.error.constructor.name
|
|
1265
|
+
}
|
|
1266
|
+
})
|
|
1267
|
+
};
|
|
1268
|
+
telemetry.track(event);
|
|
1269
|
+
|
|
1270
|
+
// Flush events if requested.
|
|
1271
|
+
if (options.flush) {
|
|
1272
|
+
await telemetry.flush();
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
// Telemetry errors should never block CLI execution.
|
|
1277
|
+
debug(`Failed to track event ${eventType}: ${err}`);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Track CLI initialization event.
|
|
1283
|
+
* Should be called at the start of CLI execution.
|
|
1284
|
+
*
|
|
1285
|
+
* @param argv Command line arguments (process.argv).
|
|
1286
|
+
* @returns Start timestamp for duration calculation.
|
|
1287
|
+
*/
|
|
1288
|
+
async function trackCliStart(argv) {
|
|
1289
|
+
debug('Capture start of command');
|
|
1290
|
+
const startTime = Date.now();
|
|
1291
|
+
await trackEvent('cli_start', buildContext(argv));
|
|
1292
|
+
return startTime;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* Track a generic CLI event with optional metadata.
|
|
1297
|
+
* Use this for tracking custom events during CLI execution.
|
|
1298
|
+
*
|
|
1299
|
+
* @param eventType Type of event to track.
|
|
1300
|
+
* @param argv Command line arguments (process.argv).
|
|
1301
|
+
* @param metadata Optional additional metadata to include with the event.
|
|
1302
|
+
*/
|
|
1303
|
+
async function trackCliEvent(eventType, argv, metadata) {
|
|
1304
|
+
debug(`Tracking CLI event: ${eventType}`);
|
|
1305
|
+
await trackEvent(eventType, buildContext(argv), metadata);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Track CLI completion event.
|
|
1310
|
+
* Should be called on successful CLI exit.
|
|
1311
|
+
* Flushes immediately since this is typically the last event before process exit.
|
|
1312
|
+
*
|
|
1313
|
+
* @param argv
|
|
1314
|
+
* @param startTime Start timestamp from trackCliStart.
|
|
1315
|
+
* @param exitCode Process exit code (default: 0).
|
|
1316
|
+
*/
|
|
1317
|
+
async function trackCliComplete(argv, startTime, exitCode) {
|
|
1318
|
+
debug('Capture end of command');
|
|
1319
|
+
await trackEvent('cli_complete', buildContext(argv), {
|
|
1320
|
+
duration: calculateDuration(startTime),
|
|
1321
|
+
exit_code: normalizeExitCode(exitCode, 0)
|
|
1322
|
+
}, {
|
|
1323
|
+
flush: true
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* Track CLI error event.
|
|
1329
|
+
* Should be called when CLI exits with an error.
|
|
1330
|
+
* Flushes immediately since this is typically the last event before process exit.
|
|
1331
|
+
*
|
|
1332
|
+
* @param argv
|
|
1333
|
+
* @param startTime Start timestamp from trackCliStart.
|
|
1334
|
+
* @param error Error that occurred.
|
|
1335
|
+
* @param exitCode Process exit code (default: 1).
|
|
1336
|
+
*/
|
|
1337
|
+
async function trackCliError(argv, startTime, error, exitCode) {
|
|
1338
|
+
debug('Capture error and stack trace of command');
|
|
1339
|
+
await trackEvent('cli_error', buildContext(argv), {
|
|
1340
|
+
duration: calculateDuration(startTime),
|
|
1341
|
+
exit_code: normalizeExitCode(exitCode, 1)
|
|
1342
|
+
}, {
|
|
1343
|
+
error: normalizeError(error),
|
|
1344
|
+
flush: true
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Track subprocess/command start event.
|
|
1350
|
+
*
|
|
1351
|
+
* Use this when spawning external commands like npm, npx, coana, cdxgen, etc.
|
|
1352
|
+
*
|
|
1353
|
+
* @param command Command being executed (e.g., 'npm', 'npx', 'coana').
|
|
1354
|
+
* @param metadata Optional additional metadata (e.g., cwd, purpose).
|
|
1355
|
+
* @returns Start timestamp for duration calculation.
|
|
1356
|
+
*/
|
|
1357
|
+
async function trackSubprocessStart(command, metadata) {
|
|
1358
|
+
debug(`Tracking subprocess start: ${command}`);
|
|
1359
|
+
const startTime = Date.now();
|
|
1360
|
+
await trackEvent('subprocess_start', buildContext(process$1.argv), {
|
|
1361
|
+
command,
|
|
1362
|
+
...metadata
|
|
1363
|
+
});
|
|
1364
|
+
return startTime;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* Track subprocess/command completion event.
|
|
1369
|
+
*
|
|
1370
|
+
* Should be called when spawned command completes successfully.
|
|
1371
|
+
*
|
|
1372
|
+
* @param command Command that was executed.
|
|
1373
|
+
* @param startTime Start timestamp from trackSubprocessStart.
|
|
1374
|
+
* @param exitCode Process exit code.
|
|
1375
|
+
* @param metadata Optional additional metadata (e.g., stdout length, stderr length).
|
|
1376
|
+
*/
|
|
1377
|
+
async function trackSubprocessComplete(command, startTime, exitCode, metadata) {
|
|
1378
|
+
debug(`Tracking subprocess complete: ${command}`);
|
|
1379
|
+
await trackEvent('subprocess_complete', buildContext(process$1.argv), {
|
|
1380
|
+
command,
|
|
1381
|
+
duration: calculateDuration(startTime),
|
|
1382
|
+
exit_code: normalizeExitCode(exitCode, 0),
|
|
1383
|
+
...metadata
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Track subprocess/command error event.
|
|
1389
|
+
*
|
|
1390
|
+
* Should be called when spawned command fails or throws error.
|
|
1391
|
+
*
|
|
1392
|
+
* @param command Command that was executed.
|
|
1393
|
+
* @param startTime Start timestamp from trackSubprocessStart.
|
|
1394
|
+
* @param error Error that occurred.
|
|
1395
|
+
* @param exitCode Process exit code.
|
|
1396
|
+
* @param metadata Optional additional metadata.
|
|
1397
|
+
*/
|
|
1398
|
+
async function trackSubprocessError(command, startTime, error, exitCode, metadata) {
|
|
1399
|
+
debug(`Tracking subprocess error: ${command}`);
|
|
1400
|
+
await trackEvent('subprocess_error', buildContext(process$1.argv), {
|
|
1401
|
+
command,
|
|
1402
|
+
duration: calculateDuration(startTime),
|
|
1403
|
+
exit_code: normalizeExitCode(exitCode, 1),
|
|
1404
|
+
...metadata
|
|
1405
|
+
}, {
|
|
1406
|
+
error: normalizeError(error)
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
|
|
564
1410
|
/**
|
|
565
1411
|
* Socket SDK utilities for Socket CLI.
|
|
566
1412
|
* Manages SDK initialization and configuration for API communication.
|
|
@@ -673,17 +1519,54 @@ async function setupSdk(options) {
|
|
|
673
1519
|
version: constants.default.ENV.INLINED_SOCKET_CLI_VERSION,
|
|
674
1520
|
homepage: constants.default.ENV.INLINED_SOCKET_CLI_HOMEPAGE
|
|
675
1521
|
}),
|
|
676
|
-
// Add HTTP request hooks for
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
1522
|
+
// Add HTTP request hooks for telemetry and debugging.
|
|
1523
|
+
hooks: {
|
|
1524
|
+
onRequest: info => {
|
|
1525
|
+
// Skip tracking for telemetry submission endpoints to prevent infinite loop.
|
|
1526
|
+
const isTelemetryEndpoint = info.url.includes('/telemetry');
|
|
1527
|
+
if (constants.default.ENV.SOCKET_CLI_DEBUG) {
|
|
1528
|
+
// Debug logging.
|
|
680
1529
|
debugApiRequest(info.method, info.url, info.timeout);
|
|
681
|
-
}
|
|
682
|
-
|
|
1530
|
+
}
|
|
1531
|
+
if (!isTelemetryEndpoint) {
|
|
1532
|
+
// Track API request event.
|
|
1533
|
+
void trackCliEvent('api_request', process.argv, {
|
|
1534
|
+
method: info.method,
|
|
1535
|
+
timeout: info.timeout,
|
|
1536
|
+
url: info.url
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
},
|
|
1540
|
+
onResponse: info => {
|
|
1541
|
+
// Skip tracking for telemetry submission endpoints to prevent infinite loop.
|
|
1542
|
+
const isTelemetryEndpoint = info.url.includes('/telemetry');
|
|
1543
|
+
if (!isTelemetryEndpoint) {
|
|
1544
|
+
// Track API response event.
|
|
1545
|
+
const metadata = {
|
|
1546
|
+
duration: info.duration,
|
|
1547
|
+
method: info.method,
|
|
1548
|
+
status: info.status,
|
|
1549
|
+
statusText: info.statusText,
|
|
1550
|
+
url: info.url
|
|
1551
|
+
};
|
|
1552
|
+
if (info.error) {
|
|
1553
|
+
// Track as error event if request failed.
|
|
1554
|
+
void trackCliEvent('api_error', process.argv, {
|
|
1555
|
+
...metadata,
|
|
1556
|
+
error_message: info.error.message,
|
|
1557
|
+
error_type: info.error.constructor.name
|
|
1558
|
+
});
|
|
1559
|
+
} else {
|
|
1560
|
+
// Track as successful response.
|
|
1561
|
+
void trackCliEvent('api_response', process.argv, metadata);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
if (constants.default.ENV.SOCKET_CLI_DEBUG) {
|
|
1565
|
+
// Debug logging.
|
|
683
1566
|
debugApiResponse(info.method, info.url, info.status, info.error, info.duration, info.headers);
|
|
684
1567
|
}
|
|
685
1568
|
}
|
|
686
|
-
}
|
|
1569
|
+
}
|
|
687
1570
|
};
|
|
688
1571
|
if (constants.default.ENV.SOCKET_CLI_DEBUG) {
|
|
689
1572
|
logger.logger.info(`[DEBUG] ${new Date().toISOString()} SDK options: ${JSON.stringify(sdkOptions)}`);
|
|
@@ -3460,7 +4343,7 @@ function isYarnBerry() {
|
|
|
3460
4343
|
* - Configures environment for third-party tools
|
|
3461
4344
|
*/
|
|
3462
4345
|
|
|
3463
|
-
const require$2 = require$$5.createRequire((typeof document === 'undefined' ? require$$0.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
4346
|
+
const require$2 = require$$5.createRequire((typeof document === 'undefined' ? require$$0$1.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
3464
4347
|
const {
|
|
3465
4348
|
PACKAGE_LOCK_JSON,
|
|
3466
4349
|
PNPM_LOCK_YAML,
|
|
@@ -4329,10 +5212,11 @@ function isPnpmLockfileScanCommand(command) {
|
|
|
4329
5212
|
* - PURL_Type: Package URL type from Socket SDK
|
|
4330
5213
|
*
|
|
4331
5214
|
* Supported Ecosystems:
|
|
4332
|
-
* - apk, bitbucket, cargo, chrome, cocoapods, composer
|
|
5215
|
+
* - alpm, apk, bitbucket, cargo, chrome, cocoapods, composer
|
|
4333
5216
|
* - conan, conda, cran, deb, docker, gem, generic
|
|
4334
5217
|
* - github, gitlab, go, hackage, hex, huggingface
|
|
4335
|
-
* - maven, mlflow, npm, nuget, oci, pub, pypi,
|
|
5218
|
+
* - maven, mlflow, npm, nuget, oci, pub, pypi, qpkg, rpm
|
|
5219
|
+
* - swift, swid, unknown, vscode
|
|
4336
5220
|
*
|
|
4337
5221
|
* Usage:
|
|
4338
5222
|
* - Validates ecosystem types
|
|
@@ -4340,7 +5224,15 @@ function isPnpmLockfileScanCommand(command) {
|
|
|
4340
5224
|
* - Ensures type safety for ecosystem operations
|
|
4341
5225
|
*/
|
|
4342
5226
|
|
|
4343
|
-
|
|
5227
|
+
|
|
5228
|
+
// Temporarily commented out due to dependency version mismatch.
|
|
5229
|
+
// SDK has "alpm" but registry's EcosystemString doesn't yet.
|
|
5230
|
+
// type MissingInEcosystemString = Exclude<PURL_Type, EcosystemString>
|
|
5231
|
+
|
|
5232
|
+
// export type _Check_EcosystemString_has_all_purl_types =
|
|
5233
|
+
// ExpectNever<MissingInEcosystemString>
|
|
5234
|
+
|
|
5235
|
+
const ALL_ECOSYSTEMS = ['alpm', 'apk', 'bitbucket', 'cargo', 'chrome', 'cocoapods', 'composer', 'conan', 'conda', 'cran', 'deb', 'docker', 'gem', 'generic', 'github', 'golang', 'hackage', 'hex', 'huggingface', 'maven', 'mlflow', constants.NPM, 'nuget', 'oci', 'pub', 'pypi', 'qpkg', 'rpm', 'swift', 'swid', 'unknown', 'vscode'];
|
|
4344
5236
|
new Set(ALL_ECOSYSTEMS);
|
|
4345
5237
|
function getEcosystemChoicesForMeow() {
|
|
4346
5238
|
return [...ALL_ECOSYSTEMS];
|
|
@@ -5191,7 +6083,7 @@ function isPnpmBinPathShadowed() {
|
|
|
5191
6083
|
* - Preserves original binary functionality
|
|
5192
6084
|
*/
|
|
5193
6085
|
|
|
5194
|
-
const __filename$1 = require$$0.fileURLToPath((typeof document === 'undefined' ? require$$0.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
6086
|
+
const __filename$1 = require$$0$1.fileURLToPath((typeof document === 'undefined' ? require$$0$1.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
5195
6087
|
const __dirname$1 = path.dirname(__filename$1);
|
|
5196
6088
|
async function installNpmLinks(shadowBinPath) {
|
|
5197
6089
|
// Find npm being shadowed by this process.
|
|
@@ -5472,7 +6364,7 @@ class ColorOrMarkdown {
|
|
|
5472
6364
|
}
|
|
5473
6365
|
}
|
|
5474
6366
|
|
|
5475
|
-
const require$1 = require$$5.createRequire((typeof document === 'undefined' ? require$$0.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
6367
|
+
const require$1 = require$$5.createRequire((typeof document === 'undefined' ? require$$0$1.pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('utils.js', document.baseURI).href)));
|
|
5476
6368
|
let _translations;
|
|
5477
6369
|
function getTranslations() {
|
|
5478
6370
|
if (_translations === undefined) {
|
|
@@ -6124,6 +7016,7 @@ exports.fetchGhsaDetails = fetchGhsaDetails;
|
|
|
6124
7016
|
exports.fetchOrganization = fetchOrganization;
|
|
6125
7017
|
exports.fileLink = fileLink;
|
|
6126
7018
|
exports.filterFlags = filterFlags;
|
|
7019
|
+
exports.finalizeTelemetry = finalizeTelemetry;
|
|
6127
7020
|
exports.findUp = findUp;
|
|
6128
7021
|
exports.formatErrorWithDetail = formatErrorWithDetail;
|
|
6129
7022
|
exports.getAlertsMapFromPnpmLockfile = getAlertsMapFromPnpmLockfile;
|
|
@@ -6207,6 +7100,7 @@ exports.sendApiRequest = sendApiRequest;
|
|
|
6207
7100
|
exports.serializeResultJson = serializeResultJson;
|
|
6208
7101
|
exports.setGitRemoteGithubRepoUrl = setGitRemoteGithubRepoUrl;
|
|
6209
7102
|
exports.setupSdk = setupSdk;
|
|
7103
|
+
exports.setupTelemetryExitHandlers = setupTelemetryExitHandlers;
|
|
6210
7104
|
exports.socketDashboardLink = socketDashboardLink;
|
|
6211
7105
|
exports.socketDevLink = socketDevLink;
|
|
6212
7106
|
exports.socketDocsLink = socketDocsLink;
|
|
@@ -6217,9 +7111,14 @@ exports.spawnSynpDlx = spawnSynpDlx;
|
|
|
6217
7111
|
exports.suggestOrgSlug = suggestOrgSlug;
|
|
6218
7112
|
exports.tildify = tildify;
|
|
6219
7113
|
exports.toFilterConfig = toFilterConfig;
|
|
7114
|
+
exports.trackCliComplete = trackCliComplete;
|
|
7115
|
+
exports.trackCliError = trackCliError;
|
|
7116
|
+
exports.trackCliStart = trackCliStart;
|
|
7117
|
+
exports.trackSubprocessExit = trackSubprocessExit;
|
|
7118
|
+
exports.trackSubprocessStart = trackSubprocessStart;
|
|
6220
7119
|
exports.updateConfigValue = updateConfigValue;
|
|
6221
7120
|
exports.walkNestedMap = walkNestedMap;
|
|
6222
7121
|
exports.webLink = webLink;
|
|
6223
7122
|
exports.writeSocketJson = writeSocketJson;
|
|
6224
|
-
//# debugId=
|
|
7123
|
+
//# debugId=a083299b-d999-403d-b54b-00740d629c69
|
|
6225
7124
|
//# sourceMappingURL=utils.js.map
|