extension 3.14.0-next.0 → 3.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -5152,7 +5152,8 @@ var __webpack_exports__ = {};
5152
5152
  preview: 'Previews the extension in production mode without building',
5153
5153
  build: 'Builds the extension for packaging/distribution',
5154
5154
  install: 'Installs a managed browser binary into Extension.js cache',
5155
- uninstall: 'Removes managed browser binaries from Extension.js cache'
5155
+ uninstall: 'Removes managed browser binaries from Extension.js cache',
5156
+ telemetry: 'Manage anonymous telemetry consent (enable, disable, or show status)'
5156
5157
  };
5157
5158
  function unhandledError(err) {
5158
5159
  const message = err instanceof Error ? err.stack || err.message : 'string' == typeof err ? err : fmt.truncate(err);
@@ -5212,11 +5213,14 @@ Available Commands
5212
5213
  - ${code('extension uninstall --where')}
5213
5214
  Prints the managed browser cache root (or browser install path(s) when --browser/--all is provided)
5214
5215
 
5216
+ - ${code('extension telemetry ' + messages_arg('<enable|disable|status>'))}
5217
+ ${commandDescriptions.telemetry}
5218
+
5215
5219
  Common Options
5216
5220
  - ${code('--browser')} ${messages_arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chromium)
5217
5221
  - ${code('--profile')} ${messages_arg('<path|boolean>')} Browser profile configuration
5218
5222
  - ${code('--polyfill')} ${messages_arg('[boolean]')} Enable/disable cross-browser polyfill
5219
- - ${code('--no-telemetry')} Disable anonymous telemetry for this run
5223
+ - ${code('--no-telemetry')} Disable anonymous telemetry for this run (persistent toggle: ${code('extension telemetry disable')}, or ${code('EXTENSION_TELEMETRY=0')})
5220
5224
  - ${code('--ai-help')} Show AI-assistant oriented help and tips
5221
5225
  - ${code('--format')} ${messages_arg('<pretty|json>')} Output format for ${code('--ai-help')} (default: pretty)
5222
5226
  - ${code('--help')} Show help output
@@ -5485,6 +5489,11 @@ Cross-Browser Compatibility
5485
5489
  name: 'uninstall',
5486
5490
  summary: commandDescriptions.uninstall,
5487
5491
  supportsSourceInspection: false
5492
+ },
5493
+ {
5494
+ name: 'telemetry',
5495
+ summary: commandDescriptions.telemetry,
5496
+ supportsSourceInspection: false
5488
5497
  }
5489
5498
  ],
5490
5499
  globalOptions: [
@@ -5503,7 +5512,7 @@ Cross-Browser Compatibility
5503
5512
  },
5504
5513
  {
5505
5514
  name: '--no-telemetry',
5506
- description: 'Disable anonymous telemetry for this run'
5515
+ description: 'Disable anonymous telemetry for this run. Persistent toggle: `extension telemetry disable`. Hard override: `EXTENSION_TELEMETRY=0`.'
5507
5516
  },
5508
5517
  {
5509
5518
  name: '--browser',
@@ -5783,6 +5792,12 @@ Cross-Browser Compatibility
5783
5792
  else obj[key] = value;
5784
5793
  return obj;
5785
5794
  }
5795
+ const DEFAULT_SAMPLE_RATE = Number(process.env.EXTENSION_TELEMETRY_SAMPLE_RATE || 0.2);
5796
+ const DEFAULT_MAX_EVENTS = Number(process.env.EXTENSION_TELEMETRY_MAX_EVENTS || 3);
5797
+ const DEFAULT_DEBOUNCE_MS = Number(process.env.EXTENSION_TELEMETRY_DEBOUNCE_MS || 60000);
5798
+ const DEFAULT_TIMEOUT_MS = Number(process.env.EXTENSION_TELEMETRY_TIMEOUT_MS || 300);
5799
+ const DEFAULT_POSTHOG_KEY = process.env.POSTHOG_KEY || '';
5800
+ const DEFAULT_POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://us.i.posthog.com';
5786
5801
  function isCI() {
5787
5802
  const v = process.env;
5788
5803
  return Boolean(v.CI || v.GITHUB_ACTIONS || v.GITLAB_CI || v.BUILDKITE || v.CIRCLECI || v.TRAVIS);
@@ -5826,16 +5841,6 @@ Cross-Browser Compatibility
5826
5841
  return false;
5827
5842
  }
5828
5843
  }
5829
- function loadOrCreateId(file) {
5830
- try {
5831
- if (external_node_fs_default().existsSync(file)) return external_node_fs_default().readFileSync(file, 'utf8').trim();
5832
- } catch {}
5833
- const id = external_node_crypto_default().randomUUID();
5834
- if (ensureDir(external_node_path_default().dirname(file))) try {
5835
- external_node_fs_default().writeFileSync(file, id, 'utf8');
5836
- } catch {}
5837
- return id;
5838
- }
5839
5844
  function telemetryCandidates() {
5840
5845
  const candidates = [
5841
5846
  configDir(),
@@ -5857,44 +5862,107 @@ Cross-Browser Compatibility
5857
5862
  }
5858
5863
  return null;
5859
5864
  }
5860
- const DEFAULT_FLUSH_AT = Number(process.env.EXTENSION_TELEMETRY_FLUSH_AT || 10);
5861
- const DEFAULT_FLUSH_INTERVAL = Number(process.env.EXTENSION_TELEMETRY_FLUSH_INTERVAL || 2000);
5862
- const DEFAULT_TIMEOUT_MS = Number(process.env.EXTENSION_TELEMETRY_TIMEOUT_MS || 200);
5863
- const DEFAULT_POSTHOG_KEY = process.env.POSTHOG_KEY || 'phc_Np5x3Jg3h2V7kTFtNch2uz6QBaWDycQpIidzX5PetaN';
5864
- const DEFAULT_POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://us.i.posthog.com';
5865
- class Telemetry {
5866
- track(event, props = {}) {
5867
- if (this.disabled || !this.storage) return;
5868
- const payload = {
5869
- event,
5870
- properties: {
5871
- ...this.common,
5872
- ...props,
5873
- $ip: null
5874
- },
5875
- distinct_id: this.anonId
5865
+ function loadOrCreateId(file) {
5866
+ try {
5867
+ if (external_node_fs_default().existsSync(file)) return external_node_fs_default().readFileSync(file, 'utf8').trim();
5868
+ } catch {}
5869
+ const id = external_node_crypto_default().randomUUID();
5870
+ if (ensureDir(external_node_path_default().dirname(file))) try {
5871
+ external_node_fs_default().writeFileSync(file, id, 'utf8');
5872
+ } catch {}
5873
+ return id;
5874
+ }
5875
+ function readConsentFile(file) {
5876
+ try {
5877
+ const raw = external_node_fs_default().readFileSync(file, 'utf8').trim().toLowerCase();
5878
+ if ('enabled' === raw || 'ok' === raw || 'on' === raw || '1' === raw) return 'enabled';
5879
+ if ('disabled' === raw || 'off' === raw || '0' === raw || 'no' === raw) return 'disabled';
5880
+ } catch {}
5881
+ return null;
5882
+ }
5883
+ function envDisables() {
5884
+ const disabled = String(process.env.EXTENSION_TELEMETRY_DISABLED ?? '').trim().toLowerCase();
5885
+ if ('1' === disabled || 'true' === disabled || 'on' === disabled || 'yes' === disabled) return true;
5886
+ const raw = String(process.env.EXTENSION_TELEMETRY ?? '').trim().toLowerCase();
5887
+ if (!raw) return false;
5888
+ return '0' === raw || 'false' === raw || 'off' === raw || 'no' === raw;
5889
+ }
5890
+ function resolveTelemetryConsent(argv = process.argv) {
5891
+ if (envDisables()) return {
5892
+ enabled: false,
5893
+ source: 'env'
5894
+ };
5895
+ if (argv.includes('--no-telemetry')) return {
5896
+ enabled: false,
5897
+ source: 'flag'
5898
+ };
5899
+ const storage = resolveTelemetryStorage();
5900
+ if (storage) {
5901
+ const stored = readConsentFile(storage.consentFile);
5902
+ if ('enabled' === stored) return {
5903
+ enabled: true,
5904
+ source: 'config'
5905
+ };
5906
+ if ('disabled' === stored) return {
5907
+ enabled: false,
5908
+ source: 'config'
5876
5909
  };
5910
+ }
5911
+ return {
5912
+ enabled: true,
5913
+ source: 'default'
5914
+ };
5915
+ }
5916
+ function writeConsent(value) {
5917
+ const storage = resolveTelemetryStorage();
5918
+ if (!storage) return false;
5919
+ try {
5920
+ external_node_fs_default().writeFileSync(storage.consentFile, value, 'utf8');
5921
+ return true;
5922
+ } catch {
5923
+ return false;
5924
+ }
5925
+ }
5926
+ class Telemetry {
5927
+ get isEnabled() {
5928
+ return !this.disabled;
5929
+ }
5930
+ track(event, props) {
5877
5931
  try {
5878
- external_node_fs_default().appendFileSync(this.storage.auditFile, JSON.stringify(payload) + '\n');
5879
- } catch {
5880
- this.disabled = true;
5881
- return;
5882
- }
5883
- if (this.debug) console.error('[telemetry]', JSON.stringify(payload));
5884
- if (!this.apiKey || !this.host) return;
5885
- if ('cli_shutdown' === event) return void this.buffer.push(payload);
5886
- this.buffer.push(payload);
5887
- if (this.buffer.length >= DEFAULT_FLUSH_AT) return void this.flush();
5888
- if (!this.timer) this.timer = setTimeout(()=>{
5889
- this.timer = null;
5890
- this.flush();
5891
- }, DEFAULT_FLUSH_INTERVAL);
5932
+ if (this.disabled) return;
5933
+ if (this.sent >= this.maxEventsPerRun) return;
5934
+ const key = `${event}|${props.command}|${props.success}`;
5935
+ const now = Date.now();
5936
+ const last = this.recent.get(key);
5937
+ if (null != last && now - last < this.debounceMs) return;
5938
+ this.recent.set(key, now);
5939
+ const enforcedProps = {
5940
+ command: String(props.command ?? 'unknown').slice(0, 32),
5941
+ success: Boolean(props.success),
5942
+ version: String(props.version ?? this.version).slice(0, 32)
5943
+ };
5944
+ const payload = {
5945
+ event,
5946
+ properties: {
5947
+ ...enforcedProps,
5948
+ ...this.common,
5949
+ app: this.app,
5950
+ $ip: null
5951
+ },
5952
+ distinct_id: this.anonId
5953
+ };
5954
+ this.writeAudit(payload);
5955
+ if ('command_executed' === event && Math.random() > this.sampleRate) return;
5956
+ if (!this.apiKey || !this.host) return;
5957
+ this.buffer.push(payload);
5958
+ this.sent += 1;
5959
+ } catch {}
5892
5960
  }
5893
5961
  async flush() {
5894
- if (this.disabled || !this.apiKey || !this.host) return;
5895
- if (0 === this.buffer.length) return;
5896
- const batch = this.buffer.splice(0, this.buffer.length);
5897
5962
  try {
5963
+ if (this.disabled || !this.apiKey || !this.host) return;
5964
+ if (0 === this.buffer.length) return;
5965
+ const batch = this.buffer.splice(0, this.buffer.length);
5898
5966
  const ac = new AbortController();
5899
5967
  const t = setTimeout(()=>ac.abort(), DEFAULT_TIMEOUT_MS);
5900
5968
  const url = new URL('/capture/', this.host);
@@ -5918,299 +5986,140 @@ Cross-Browser Compatibility
5918
5986
  } catch {}
5919
5987
  }
5920
5988
  shutdown() {}
5989
+ writeAudit(payload) {
5990
+ if (!this.storage) return;
5991
+ try {
5992
+ external_node_fs_default().appendFileSync(this.storage.auditFile, JSON.stringify(payload) + '\n');
5993
+ } catch {
5994
+ this.disabled = true;
5995
+ }
5996
+ if (this.debug) console.error('[telemetry]', JSON.stringify(payload));
5997
+ }
5921
5998
  constructor(init){
5922
- _define_property(this, "anonId", void 0);
5923
- _define_property(this, "common", void 0);
5924
- _define_property(this, "debug", void 0);
5925
5999
  _define_property(this, "disabled", void 0);
6000
+ _define_property(this, "version", void 0);
6001
+ _define_property(this, "app", void 0);
5926
6002
  _define_property(this, "apiKey", void 0);
5927
6003
  _define_property(this, "host", void 0);
6004
+ _define_property(this, "anonId", 'disabled');
5928
6005
  _define_property(this, "storage", null);
6006
+ _define_property(this, "sent", 0);
6007
+ _define_property(this, "sampleRate", void 0);
6008
+ _define_property(this, "maxEventsPerRun", void 0);
6009
+ _define_property(this, "debounceMs", void 0);
6010
+ _define_property(this, "debug", void 0);
6011
+ _define_property(this, "common", void 0);
6012
+ _define_property(this, "recent", new Map());
5929
6013
  _define_property(this, "buffer", []);
5930
- _define_property(this, "timer", null);
5931
6014
  this.debug = '1' === process.env.EXTENSION_TELEMETRY_DEBUG;
5932
6015
  this.disabled = Boolean(init.disabled);
5933
- this.anonId = 'disabled';
5934
- if (!this.disabled) {
5935
- this.storage = resolveTelemetryStorage();
5936
- if (this.storage) this.anonId = loadOrCreateId(this.storage.idFile);
5937
- else this.disabled = true;
5938
- }
6016
+ this.app = init.app;
6017
+ this.version = init.version;
6018
+ this.apiKey = init.apiKey ?? DEFAULT_POSTHOG_KEY;
6019
+ this.host = init.host ?? DEFAULT_POSTHOG_HOST;
6020
+ this.sampleRate = clamp(init.sampleRate ?? DEFAULT_SAMPLE_RATE, 0, 1);
6021
+ this.maxEventsPerRun = Math.max(0, init.maxEventsPerRun ?? DEFAULT_MAX_EVENTS);
6022
+ this.debounceMs = Math.max(0, init.debounceMs ?? DEFAULT_DEBOUNCE_MS);
5939
6023
  this.common = {
5940
- app: init.app,
5941
- version: init.version,
5942
6024
  os: process.platform,
5943
6025
  arch: process.arch,
5944
- node: process.versions.node,
5945
- is_ci: isCI(),
5946
- schema_version: 2
6026
+ node_major: Number(String(process.versions.node).split('.')[0]) || 0,
6027
+ is_ci: isCI()
5947
6028
  };
5948
- this.apiKey = init.apiKey || DEFAULT_POSTHOG_KEY;
5949
- this.host = init.host || DEFAULT_POSTHOG_HOST;
5950
- if (!this.disabled && this.storage) {
5951
- const consentPath = this.storage.consentFile;
5952
- try {
5953
- if (!external_node_fs_default().existsSync(consentPath)) {
5954
- external_node_fs_default().writeFileSync(consentPath, 'ok', 'utf8');
5955
- this.track('cli_telemetry_consent', {
5956
- value: 'implicit_opt_in'
5957
- });
5958
- console.log(`${external_pintor_default().gray('⏵⏵⏵')} Telemetry is enabled for Extension.js. To opt out, run with --no-telemetry. Learn more in TELEMETRY.md.`);
5959
- }
5960
- } catch {}
6029
+ if (!this.disabled) {
6030
+ this.storage = resolveTelemetryStorage();
6031
+ if (this.storage) this.anonId = loadOrCreateId(this.storage.idFile);
5961
6032
  }
5962
6033
  }
5963
6034
  }
5964
- function summarizeManifest(manifest) {
5965
- const mv = manifest?.manifest_version === 2 ? 2 : 3;
5966
- const permissions = Array.isArray(manifest?.permissions) ? manifest.permissions : [];
5967
- const optionalPermissions = Array.isArray(manifest?.optional_permissions) ? manifest.optional_permissions : [];
5968
- const hostPermissions = Array.isArray(manifest?.host_permissions) ? manifest.host_permissions : [];
5969
- const usesAllUrls = [
5970
- ...permissions,
5971
- ...hostPermissions
5972
- ].includes('<all_urls>');
5973
- const usesDeclarativeNetRequest = permissions.includes('declarativeNetRequest') || permissions.includes('declarativeNetRequestWithHostAccess');
5974
- const background = manifest?.background;
5975
- let backgroundType = 'none';
5976
- if (3 === mv && background?.service_worker) backgroundType = 'service_worker';
5977
- else if (2 === mv && (Array.isArray(background?.scripts) && background.scripts.length > 0 || background?.page)) backgroundType = 'event_page';
5978
- const contentScriptsCount = Array.isArray(manifest?.content_scripts) ? manifest.content_scripts.length : 0;
5979
- const hasDevtoolsPage = Boolean(manifest?.devtools_page);
5980
- const hasActionPopup = Boolean(manifest?.action?.default_popup);
5981
- return {
5982
- mv,
5983
- permissions_count: permissions.length,
5984
- optional_permissions_count: optionalPermissions.length,
5985
- host_permissions_count: hostPermissions.length,
5986
- uses_all_urls: usesAllUrls,
5987
- uses_declarative_net_request: usesDeclarativeNetRequest,
5988
- background_type: backgroundType,
5989
- content_scripts_count: contentScriptsCount,
5990
- has_devtools_page: hasDevtoolsPage,
5991
- has_action_popup: hasActionPopup
5992
- };
5993
- }
5994
- function safeReadJson(filePath) {
5995
- try {
5996
- if (!external_node_fs_default().existsSync(filePath)) return null;
5997
- return JSON.parse(external_node_fs_default().readFileSync(filePath, 'utf8'));
5998
- } catch {
5999
- return null;
6000
- }
6035
+ function clamp(n, min, max) {
6036
+ if (!Number.isFinite(n)) return min;
6037
+ return Math.min(Math.max(n, min), max);
6001
6038
  }
6002
- function findNearestPackageJson(startPath) {
6003
- let current = external_node_path_default().resolve(startPath);
6004
- for(let i = 0; i < 6; i += 1){
6005
- const candidate = external_node_path_default().join(current, 'package.json');
6006
- if (external_node_fs_default().existsSync(candidate)) return candidate;
6007
- const parent = external_node_path_default().dirname(current);
6008
- if (parent === current) break;
6009
- current = parent;
6039
+ const KNOWN_COMMANDS = new Set([
6040
+ 'create',
6041
+ 'dev',
6042
+ 'start',
6043
+ 'preview',
6044
+ 'build',
6045
+ 'install',
6046
+ 'uninstall',
6047
+ 'telemetry',
6048
+ 'unknown'
6049
+ ]);
6050
+ function detectInvokedCommand(argv) {
6051
+ for(let i = 2; i < argv.length; i += 1){
6052
+ const arg = argv[i];
6053
+ if (!(!arg || arg.startsWith('-'))) {
6054
+ if (KNOWN_COMMANDS.has(arg)) return arg;
6055
+ break;
6056
+ }
6010
6057
  }
6011
- return null;
6012
- }
6013
- function getDependencies(pkg) {
6014
- return {
6015
- ...pkg.dependencies || {},
6016
- ...pkg.devDependencies || {},
6017
- ...pkg.peerDependencies || {}
6018
- };
6019
- }
6020
- function hasDependency(pkg, dependencyId) {
6021
- const deps = getDependencies(pkg);
6022
- return Boolean(deps[dependencyId]);
6023
- }
6024
- function detectPackageManager(projectRoot, pkg) {
6025
- const declared = String(pkg.packageManager || '').trim().toLowerCase();
6026
- if (declared.startsWith('pnpm@')) return 'pnpm';
6027
- if (declared.startsWith('yarn@')) return 'yarn';
6028
- if (declared.startsWith('npm@')) return 'npm';
6029
- if (declared.startsWith('bun@')) return 'bun';
6030
- if (external_node_fs_default().existsSync(external_node_path_default().join(projectRoot, 'pnpm-lock.yaml'))) return 'pnpm';
6031
- if (external_node_fs_default().existsSync(external_node_path_default().join(projectRoot, 'yarn.lock'))) return 'yarn';
6032
- if (external_node_fs_default().existsSync(external_node_path_default().join(projectRoot, 'package-lock.json'))) return 'npm';
6033
- if (external_node_fs_default().existsSync(external_node_path_default().join(projectRoot, 'bun.lockb'))) return 'bun';
6034
- if (external_node_fs_default().existsSync(external_node_path_default().join(projectRoot, 'bun.lock'))) return 'bun';
6035
- const userAgent = String(process.env.npm_config_user_agent || '').toLowerCase();
6036
- if (userAgent.includes('pnpm')) return 'pnpm';
6037
- if (userAgent.includes('yarn')) return 'yarn';
6038
- if (userAgent.includes('bun')) return 'bun';
6039
- if (userAgent.includes('npm')) return 'npm';
6040
- return 'unknown';
6041
- }
6042
- function detectFrameworkPrimary(pkg) {
6043
- if (hasDependency(pkg, 'preact')) return 'preact';
6044
- if (hasDependency(pkg, 'react')) return 'react';
6045
- if (hasDependency(pkg, 'vue')) return 'vue';
6046
- if (hasDependency(pkg, 'svelte')) return 'svelte';
6047
- if (hasDependency(pkg, 'solid-js')) return 'solid';
6048
- if (hasDependency(pkg, '@angular/core')) return 'angular';
6049
6058
  return 'unknown';
6050
6059
  }
6051
- function detectMonorepo(projectRoot, pkg) {
6052
- return Boolean(pkg.workspaces || external_node_fs_default().existsSync(external_node_path_default().join(projectRoot, 'pnpm-workspace.yaml')) || external_node_fs_default().existsSync(external_node_path_default().join(projectRoot, 'turbo.json')));
6053
- }
6054
- function toPermissionBucket(count) {
6055
- if (count <= 0) return '0';
6056
- if (count <= 3) return '1_3';
6057
- if (count <= 10) return '4_10';
6058
- return '11_plus';
6059
- }
6060
- function toManifestSurface(summary) {
6061
- if (!summary) return 'unknown';
6062
- const surfaces = [
6063
- summary.has_action_popup,
6064
- summary.has_devtools_page,
6065
- summary.content_scripts_count > 0,
6066
- 'none' !== summary.background_type
6067
- ].filter(Boolean).length;
6068
- if (surfaces > 1) return 'multi_surface';
6069
- if (summary.has_action_popup) return 'action_popup';
6070
- if (summary.has_devtools_page) return 'devtools';
6071
- if (summary.content_scripts_count > 0) return "content_scripts";
6072
- if ('none' !== summary.background_type) return 'background_only';
6073
- return 'other';
6060
+ const consent = resolveTelemetryConsent(process.argv);
6061
+ const invoked = detectInvokedCommand(process.argv);
6062
+ const telemetry_cli_version = getCliPackageJson().version;
6063
+ const telemetry = new Telemetry({
6064
+ app: 'extension',
6065
+ version: telemetry_cli_version,
6066
+ disabled: !consent.enabled
6067
+ });
6068
+ function getTelemetryConsent() {
6069
+ return consent;
6074
6070
  }
6075
- function collectProjectProfile(projectRoot, summary) {
6076
- const packageJsonPath = findNearestPackageJson(projectRoot);
6077
- const pkg = (packageJsonPath ? safeReadJson(packageJsonPath) : null) || {
6078
- dependencies: {},
6079
- devDependencies: {},
6080
- peerDependencies: {}
6081
- };
6082
- const resolvedRoot = packageJsonPath ? external_node_path_default().dirname(packageJsonPath) : projectRoot;
6083
- const hasPackageSignals = Boolean(packageJsonPath);
6084
- const hasManifestSignals = Boolean(summary);
6085
- if (!hasPackageSignals && !hasManifestSignals) return null;
6071
+ function setTelemetryConsent(value) {
6072
+ const ok = writeConsent(value);
6073
+ const storage = resolveTelemetryStorage();
6086
6074
  return {
6087
- package_manager: detectPackageManager(resolvedRoot, pkg),
6088
- framework_primary: detectFrameworkPrimary(pkg),
6089
- has_typescript: hasDependency(pkg, "typescript") || external_node_fs_default().existsSync(external_node_path_default().join(resolvedRoot, 'tsconfig.json')),
6090
- is_monorepo: detectMonorepo(resolvedRoot, pkg),
6091
- has_next_dependency: hasDependency(pkg, 'next'),
6092
- has_turbo_dependency: hasDependency(pkg, 'turbo') || external_node_fs_default().existsSync(external_node_path_default().join(resolvedRoot, 'turbo.json')),
6093
- manifest_surface: toManifestSurface(summary),
6094
- permissions_bucket: toPermissionBucket(summary?.permissions_count || 0),
6095
- host_permissions_bucket: toPermissionBucket(summary?.host_permissions_count || 0)
6075
+ ok,
6076
+ path: storage?.consentFile ?? null
6096
6077
  };
6097
6078
  }
6098
- function isTelemetryDisabledFromArgs(argv) {
6099
- return argv.includes('--no-telemetry');
6100
- }
6101
- const telemetryDisabled = isTelemetryDisabledFromArgs(process.argv);
6102
- function findManifestJson(projectRoot) {
6103
- const stack = [
6104
- projectRoot
6105
- ];
6106
- while(stack.length > 0){
6107
- const dir = stack.pop();
6108
- if (!dir) continue;
6109
- let entries;
6110
- try {
6111
- entries = external_fs_default().readdirSync(dir, {
6112
- withFileTypes: true
6113
- });
6114
- } catch {
6115
- continue;
6116
- }
6117
- for (const entry of entries){
6118
- if (entry.isFile() && 'manifest.json' === entry.name) return external_path_default().join(dir, entry.name);
6119
- if (entry.isDirectory() && !entry.name.startsWith('.') && 'node_modules' !== entry.name && 'dist' !== entry.name) stack.push(external_path_default().join(dir, entry.name));
6120
- }
6121
- }
6122
- return null;
6079
+ let tracked = false;
6080
+ function markTracked() {
6081
+ if (tracked) return false;
6082
+ tracked = true;
6083
+ return true;
6123
6084
  }
6124
- const telemetry_cli_telemetry = new Telemetry({
6125
- app: 'extension',
6126
- version: getCliPackageJson().version,
6127
- disabled: telemetryDisabled
6128
- });
6129
- function detectInvokedCommand(argv) {
6130
- const known = new Set([
6131
- 'create',
6132
- 'dev',
6133
- 'start',
6134
- 'preview',
6135
- 'build',
6136
- 'install',
6137
- 'uninstall',
6138
- 'cleanup',
6139
- 'unknown'
6140
- ]);
6141
- return argv.slice(2).find((a)=>known.has(a)) || 'unknown';
6085
+ function markCommandSuccess(command = invoked) {
6086
+ if (!markTracked()) return;
6087
+ telemetry.track('command_executed', {
6088
+ command,
6089
+ success: true,
6090
+ version: telemetry_cli_version
6091
+ });
6142
6092
  }
6143
- if (!telemetryDisabled) {
6144
- const startedAt = Date.now();
6145
- let shutdownTracked = false;
6146
- const invoked = detectInvokedCommand(process.argv);
6147
- telemetry_cli_telemetry.track('cli_boot', {
6148
- command_guess: invoked
6093
+ function markCommandFailure(command = invoked) {
6094
+ if (!markTracked()) return;
6095
+ telemetry.track('command_failed', {
6096
+ command,
6097
+ success: false,
6098
+ version: telemetry_cli_version
6149
6099
  });
6150
- const manifestPath = findManifestJson(process.cwd());
6151
- let manifestSummary = null;
6152
- if (manifestPath) try {
6153
- const raw = external_fs_default().readFileSync(manifestPath, 'utf8');
6154
- const json = JSON.parse(raw);
6155
- manifestSummary = summarizeManifest(json);
6156
- telemetry_cli_telemetry.track('manifest_summary', manifestSummary);
6157
- } catch {}
6158
- const projectProfile = collectProjectProfile(process.cwd(), manifestSummary);
6159
- if (projectProfile) telemetry_cli_telemetry.track('project_profile', projectProfile);
6100
+ }
6101
+ function printOptOutNoticeIfFirstRun() {
6102
+ if (!consent.enabled || 'default' !== consent.source) return;
6103
+ const storage = resolveTelemetryStorage();
6104
+ if (!storage) return;
6105
+ const written = writeConsent('enabled');
6106
+ if (!written) return;
6107
+ console.log(`${external_pintor_default().gray('⏵⏵⏵')} Extension.js collects anonymous, opt-out telemetry (two events: ${external_pintor_default().cyan('command_executed')} + ${external_pintor_default().cyan('command_failed')}). Disable with ${external_pintor_default().cyan('extension telemetry disable')}, ${external_pintor_default().cyan('EXTENSION_TELEMETRY=0')}, or ${external_pintor_default().cyan('--no-telemetry')}. See TELEMETRY.md.`);
6108
+ }
6109
+ if (consent.enabled) {
6110
+ printOptOutNoticeIfFirstRun();
6160
6111
  process.on('beforeExit', async function() {
6161
- if (shutdownTracked) return;
6162
- shutdownTracked = true;
6163
- telemetry_cli_telemetry.track('cli_shutdown', {
6164
- command_guess: invoked,
6165
- duration_ms: Date.now() - startedAt,
6166
- exit_code: process.exitCode ?? 0
6167
- });
6168
- await telemetry_cli_telemetry.flush();
6112
+ if (!tracked) if ((process.exitCode ?? 0) === 0) markCommandSuccess();
6113
+ else markCommandFailure();
6114
+ await telemetry.flush();
6169
6115
  });
6170
- process.on('uncaughtException', function(err) {
6171
- telemetry_cli_telemetry.track('cli_error', {
6172
- command_guess: invoked,
6173
- error_name: String(err?.name || 'Error').slice(0, 64)
6174
- });
6116
+ process.on('uncaughtException', function() {
6117
+ markCommandFailure();
6175
6118
  });
6176
- process.on('unhandledRejection', function(reason) {
6177
- telemetry_cli_telemetry.track('cli_error', {
6178
- command_guess: invoked,
6179
- error_name: String(reason?.name || 'PromiseRejection').slice(0, 64)
6180
- });
6119
+ process.on('unhandledRejection', function() {
6120
+ markCommandFailure();
6181
6121
  });
6182
6122
  }
6183
- function primaryReason(automationReasons, shippingReasons) {
6184
- if (automationReasons.length > 0) return automationReasons[0];
6185
- if (shippingReasons.length > 0) return shippingReasons[0];
6186
- return 'none';
6187
- }
6188
- function collectWorkflowProfile(context) {
6189
- const shippingReasons = [];
6190
- const automationReasons = [];
6191
- if ('build' === context.command || 'start' === context.command || 'preview' === context.command) shippingReasons.push('production_command');
6192
- if (context.isMultiBrowser) shippingReasons.push('multi_browser');
6193
- if ('zip' === context.artifactKind || 'source_zip' === context.artifactKind || 'zip_and_source' === context.artifactKind) shippingReasons.push('artifact_output');
6194
- if (context.companionExtensionsProvided) shippingReasons.push('companion_extensions');
6195
- if (context.isWaitMode || context.isNoBrowserMode) automationReasons.push('headless_sync');
6196
- if (context.usesMachineReadableOutput) automationReasons.push('machine_readable_output');
6197
- if (context.sourceInspectionRequested) automationReasons.push('source_inspection');
6198
- if (context.whereMode) automationReasons.push('where_mode');
6199
- const hasAutomationIntent = automationReasons.length > 0;
6200
- const hasShippingIntent = shippingReasons.length > 0;
6201
- return {
6202
- workflow_cohort: hasAutomationIntent ? 'automation_heavy' : hasShippingIntent ? 'shipping' : 'local_only',
6203
- has_shipping_intent: hasShippingIntent,
6204
- has_automation_intent: hasAutomationIntent,
6205
- shipping_signal_count: shippingReasons.length,
6206
- automation_signal_count: automationReasons.length,
6207
- primary_workflow_signal: primaryReason(automationReasons, shippingReasons),
6208
- package_manager: context.packageManager,
6209
- framework_primary: context.frameworkPrimary,
6210
- has_next_dependency: context.hasNextDependency,
6211
- has_turbo_dependency: context.hasTurboDependency
6212
- };
6213
- }
6214
6123
  function parseOptionalBoolean(value) {
6215
6124
  if (void 0 === value) return true;
6216
6125
  const normalized = String(value).trim().toLowerCase();
@@ -6245,63 +6154,27 @@ Cross-Browser Compatibility
6245
6154
  }
6246
6155
  }
6247
6156
  const create_require = (0, external_module_.createRequire)(__rslib_import_meta_url__);
6248
- function registerCreateCommand(program, telemetry) {
6249
- program.command('create').arguments('<project-name|project-path>').usage('create <project-name|project-path> [options]').description(commandDescriptions.create).option('-t, --template <template-name>', 'specify a template for the created project').option('--install [boolean]', 'whether or not to install the dependencies after creating the project (enabled by default)', parseOptionalBoolean, true).action(async function(pathOrRemoteUrl, { template, install }) {
6250
- const startedAt = Date.now();
6251
- const templateValue = String(template ?? "javascript");
6252
- const isRemoteTemplate = /^https?:/i.test(templateValue);
6253
- const workflowProfile = collectWorkflowProfile({
6254
- command: 'create',
6255
- isRemoteInput: isRemoteTemplate
6256
- });
6257
- telemetry.track('workflow_profile', {
6258
- command: 'create',
6259
- ...workflowProfile
6260
- });
6261
- telemetry.track('cli_command_start', {
6262
- command: 'create',
6263
- template: templateValue,
6264
- template_source: isRemoteTemplate ? 'remote' : 'built_in',
6265
- install: Boolean(install),
6266
- ...workflowProfile
6267
- });
6268
- try {
6269
- if (!process.env.EXTENSION_CREATE_DEVELOP_ROOT) try {
6270
- process.env.EXTENSION_CREATE_DEVELOP_ROOT = resolveExtensionDevelopRoot();
6157
+ function registerCreateCommand(program) {
6158
+ program.command('create').arguments('<project-name|project-path>').usage('create <project-name|project-path> [options]').description(commandDescriptions.create).option('-t, --template <template-name>', 'specify a template for the created project').option('--install [boolean]', 'whether or not to install the dependencies after creating the project (disabled by default — pass --install to opt in)', parseOptionalBoolean, false).action(async function(pathOrRemoteUrl, { template, install }) {
6159
+ if (!process.env.EXTENSION_CREATE_DEVELOP_ROOT) try {
6160
+ process.env.EXTENSION_CREATE_DEVELOP_ROOT = resolveExtensionDevelopRoot();
6161
+ } catch {
6162
+ try {
6163
+ const developPkg = create_require.resolve('extension-develop/package.json');
6164
+ process.env.EXTENSION_CREATE_DEVELOP_ROOT = external_path_.dirname(developPkg);
6271
6165
  } catch {
6272
6166
  try {
6273
- const developPkg = create_require.resolve('extension-develop/package.json');
6274
- process.env.EXTENSION_CREATE_DEVELOP_ROOT = external_path_.dirname(developPkg);
6275
- } catch {
6276
- try {
6277
- const developEntry = create_require.resolve('extension-develop');
6278
- process.env.EXTENSION_CREATE_DEVELOP_ROOT = external_path_.dirname(external_path_.dirname(developEntry));
6279
- } catch {}
6280
- }
6167
+ const developEntry = create_require.resolve('extension-develop');
6168
+ process.env.EXTENSION_CREATE_DEVELOP_ROOT = external_path_.dirname(external_path_.dirname(developEntry));
6169
+ } catch {}
6281
6170
  }
6282
- const { extensionCreate } = await import("extension-create");
6283
- await extensionCreate(pathOrRemoteUrl, {
6284
- template,
6285
- install,
6286
- cliVersion: getCliPackageJson().version
6287
- });
6288
- telemetry.track('cli_command_finish', {
6289
- command: 'create',
6290
- duration_ms: Date.now() - startedAt,
6291
- success: true,
6292
- exit_code: 0,
6293
- ...workflowProfile
6294
- });
6295
- } catch (err) {
6296
- telemetry.track('cli_command_finish', {
6297
- command: 'create',
6298
- duration_ms: Date.now() - startedAt,
6299
- success: false,
6300
- exit_code: 1,
6301
- ...workflowProfile
6302
- });
6303
- throw err;
6304
6171
  }
6172
+ const { extensionCreate } = await import("extension-create");
6173
+ await extensionCreate(pathOrRemoteUrl, {
6174
+ template,
6175
+ install,
6176
+ cliVersion: getCliPackageJson().version
6177
+ });
6305
6178
  });
6306
6179
  }
6307
6180
  function isHttpUrl(value) {
@@ -6514,50 +6387,13 @@ Cross-Browser Compatibility
6514
6387
  const values = String(raw).split(',').map((value)=>value.trim()).filter((value)=>value.length > 0);
6515
6388
  return values.length > 0 ? values : void 0;
6516
6389
  }
6517
- function registerDevCommand(program, telemetry) {
6390
+ function registerDevCommand(program) {
6518
6391
  program.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', '\nAdditional options:\n --no-browser do not launch the browser (dev server still starts)\n --wait wait for ready contract and exit\n --wait-format pretty|json output for wait mode\n').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('-b, --browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--no-open', 'do not open the browser automatically (default: open)').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--watch-source [boolean]', '[experimental] re-print HTML on rebuilds or file changes (defaults to true when --source is present)', parseOptionalBoolean).option('--source-format <pretty|json|ndjson>', '[experimental] output format for source HTML (defaults to --log-format when present, otherwise JSON when --source is used)').option('--source-summary [boolean]', '[experimental] output a compact summary instead of full HTML', parseOptionalBoolean).option('--source-meta [boolean]', '[experimental] output page metadata (readyState, viewport, frames)', parseOptionalBoolean).option('--source-probe <selectors>', '[experimental] comma-separated CSS selectors to probe').option('--source-tree <off|root-only>', '[experimental] output a compact extension root tree').option('--source-console [boolean]', '[experimental] output console summary (best-effort)', parseOptionalBoolean).option('--source-dom [boolean]', '[experimental] output DOM snapshots and diffs (default: true when watch is enabled)', parseOptionalBoolean).option('--source-max-bytes <bytes>', '[experimental] limit HTML output size in bytes (0 disables truncation)').option('--source-redact <off|safe|strict>', '[experimental] redact sensitive content in HTML output (default: safe for JSON/NDJSON)').option('--source-include-shadow <off|open-only|all>', '[experimental] control Shadow DOM inclusion in HTML output (default: open-only)').option('--source-diff [boolean]', '[experimental] include diff metadata on watch updates (default: true when watch is enabled)', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--wait-format <pretty|json>', 'output format for --wait results (default: pretty)').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
6519
6392
  if (devOptions.author || devOptions['authorMode']) {
6520
6393
  process.env.EXTENSION_AUTHOR_MODE = 'true';
6521
6394
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
6522
6395
  }
6523
- const cmdStart = Date.now();
6524
6396
  const list = vendors(browser);
6525
- const isRemoteInput = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
6526
- const projectProfile = collectProjectProfile(!isRemoteInput && pathOrRemoteUrl ? pathOrRemoteUrl : process.cwd());
6527
- const workflowProfile = collectWorkflowProfile({
6528
- command: 'dev',
6529
- isMultiBrowser: list.length > 1,
6530
- isRemoteInput: isRemoteInput,
6531
- isWaitMode: Boolean(devOptions.wait),
6532
- isNoBrowserMode: '1' === process.env.EXTENSION_CLI_NO_BROWSER,
6533
- usesMachineReadableOutput: 'json' === devOptions.waitFormat || 'json' === devOptions.logFormat || 'ndjson' === devOptions.logFormat || 'json' === devOptions.sourceFormat || 'ndjson' === devOptions.sourceFormat,
6534
- sourceInspectionRequested: Boolean(devOptions.source || devOptions.watchSource),
6535
- companionExtensionsProvided: Boolean(devOptions.extensions),
6536
- packageManager: projectProfile?.package_manager,
6537
- frameworkPrimary: projectProfile?.framework_primary,
6538
- hasNextDependency: projectProfile?.has_next_dependency,
6539
- hasTurboDependency: projectProfile?.has_turbo_dependency
6540
- });
6541
- telemetry.track('workflow_profile', {
6542
- command: 'dev',
6543
- ...workflowProfile
6544
- });
6545
- telemetry.track('cli_command_start', {
6546
- command: 'dev',
6547
- vendors: list,
6548
- browser_count: list.length,
6549
- is_multi_browser: list.length > 1,
6550
- is_remote_input: isRemoteInput,
6551
- is_wait_mode: Boolean(devOptions.wait),
6552
- is_no_browser_mode: '1' === process.env.EXTENSION_CLI_NO_BROWSER,
6553
- polyfill_used: devOptions.polyfill?.toString() !== 'false',
6554
- source_inspection_requested: Boolean(devOptions.source || devOptions.watchSource),
6555
- companion_extensions_provided: Boolean(devOptions.extensions),
6556
- log_level: devOptions.logLevel || 'off',
6557
- log_format: devOptions.logFormat || 'pretty',
6558
- custom_binary_used: Boolean(devOptions.chromiumBinary || devOptions.geckoBinary),
6559
- ...workflowProfile
6560
- });
6561
6397
  validateVendorsOrExit(list, (invalid, supported)=>{
6562
6398
  console.error(unsupportedBrowserFlag(invalid, supported));
6563
6399
  });
@@ -6576,13 +6412,6 @@ Cross-Browser Compatibility
6576
6412
  browsers: waitResult.browsers,
6577
6413
  results: waitResult.results
6578
6414
  }));
6579
- telemetry.track('cli_command_finish', {
6580
- command: 'dev',
6581
- duration_ms: Date.now() - cmdStart,
6582
- success: true,
6583
- exit_code: 0,
6584
- ...workflowProfile
6585
- });
6586
6415
  return;
6587
6416
  }
6588
6417
  const normalizedSource = normalizeSourceOption(devOptions.source, devOptions.startingUrl);
@@ -6610,11 +6439,6 @@ Cross-Browser Compatibility
6610
6439
  const { extensionDev } = loadExtensionDevelopModule();
6611
6440
  const noBrowser = '1' === process.env.EXTENSION_CLI_NO_BROWSER;
6612
6441
  for (const vendor of list){
6613
- const vendorStart = Date.now();
6614
- telemetry.track('cli_vendor_start', {
6615
- command: 'dev',
6616
- vendor
6617
- });
6618
6442
  const logsOption = devOptions.logs;
6619
6443
  const logContextOption = devOptions.logContext;
6620
6444
  const logContexts = parseLogContexts(logContextOption);
@@ -6654,61 +6478,17 @@ Cross-Browser Compatibility
6654
6478
  launcher: noBrowser ? void 0 : browsers.launchBrowser
6655
6479
  };
6656
6480
  await extensionDev(pathOrRemoteUrl, devArgs);
6657
- telemetry.track('cli_vendor_finish', {
6658
- command: 'dev',
6659
- vendor,
6660
- duration_ms: Date.now() - vendorStart
6661
- });
6662
6481
  }
6663
- telemetry.track('cli_command_finish', {
6664
- command: 'dev',
6665
- duration_ms: Date.now() - cmdStart,
6666
- success: 0 === process.exitCode || null == process.exitCode,
6667
- exit_code: process.exitCode ?? 0,
6668
- ...workflowProfile
6669
- });
6670
6482
  });
6671
6483
  }
6672
6484
  var run_only = __webpack_require__("./browsers/run-only.ts");
6673
- function registerStartCommand(program, telemetry) {
6485
+ function registerStartCommand(program) {
6674
6486
  program.command('start').arguments('[project-path|remote-url]').usage('start [project-path|remote-url] [options]').description(commandDescriptions.start).addHelpText('after', '\nAdditional options:\n --no-browser do not launch the browser (build still runs)\n --wait wait for ready contract and exit\n --wait-format pretty|json output for wait mode\n').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[experimental] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--wait-format <pretty|json>', 'output format for --wait results (default: pretty)').option('--author, --author-mode', '[experimental] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
6675
6487
  if (startOptions.author || startOptions.authorMode) {
6676
6488
  process.env.EXTENSION_AUTHOR_MODE = 'true';
6677
6489
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
6678
6490
  }
6679
- const cmdStart = Date.now();
6680
6491
  const list = vendors(browser);
6681
- const isRemoteInput = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
6682
- const projectProfile = collectProjectProfile(!isRemoteInput && pathOrRemoteUrl ? pathOrRemoteUrl : process.cwd());
6683
- const workflowProfile = collectWorkflowProfile({
6684
- command: 'start',
6685
- isMultiBrowser: list.length > 1,
6686
- isRemoteInput: isRemoteInput,
6687
- isWaitMode: Boolean(startOptions.wait),
6688
- isNoBrowserMode: '1' === process.env.EXTENSION_CLI_NO_BROWSER,
6689
- usesMachineReadableOutput: 'json' === startOptions.waitFormat || 'json' === startOptions.logFormat || 'ndjson' === startOptions.logFormat,
6690
- companionExtensionsProvided: Boolean(startOptions.extensions),
6691
- packageManager: projectProfile?.package_manager,
6692
- frameworkPrimary: projectProfile?.framework_primary,
6693
- hasNextDependency: projectProfile?.has_next_dependency,
6694
- hasTurboDependency: projectProfile?.has_turbo_dependency
6695
- });
6696
- telemetry.track('workflow_profile', {
6697
- command: 'start',
6698
- ...workflowProfile
6699
- });
6700
- telemetry.track('cli_command_start', {
6701
- command: 'start',
6702
- vendors: list,
6703
- browser_count: list.length,
6704
- is_multi_browser: list.length > 1,
6705
- is_remote_input: isRemoteInput,
6706
- is_wait_mode: Boolean(startOptions.wait),
6707
- is_no_browser_mode: '1' === process.env.EXTENSION_CLI_NO_BROWSER,
6708
- companion_extensions_provided: Boolean(startOptions.extensions),
6709
- polyfill_used: startOptions.polyfill?.toString() !== 'false',
6710
- ...workflowProfile
6711
- });
6712
6492
  validateVendorsOrExit(list, (invalid, supported)=>{
6713
6493
  console.error(unsupportedBrowserFlag(invalid, supported));
6714
6494
  });
@@ -6727,22 +6507,10 @@ Cross-Browser Compatibility
6727
6507
  browsers: waitResult.browsers,
6728
6508
  results: waitResult.results
6729
6509
  }));
6730
- telemetry.track('cli_command_finish', {
6731
- command: 'start',
6732
- duration_ms: Date.now() - cmdStart,
6733
- success: true,
6734
- exit_code: 0,
6735
- ...workflowProfile
6736
- });
6737
6510
  return;
6738
6511
  }
6739
6512
  const { extensionBuild } = loadExtensionDevelopModule();
6740
6513
  for (const vendor of list){
6741
- const vendorStart = Date.now();
6742
- telemetry.track('cli_vendor_start', {
6743
- command: 'start',
6744
- vendor
6745
- });
6746
6514
  const logsOption = startOptions.logs;
6747
6515
  const logContextOption = startOptions.logContext;
6748
6516
  const logContexts = parseLogContexts(logContextOption);
@@ -6755,14 +6523,7 @@ Cross-Browser Compatibility
6755
6523
  silent: true
6756
6524
  });
6757
6525
  const noBrowser = '1' === process.env.EXTENSION_CLI_NO_BROWSER;
6758
- if (noBrowser) {
6759
- telemetry.track('cli_vendor_finish', {
6760
- command: 'start',
6761
- vendor,
6762
- duration_ms: Date.now() - vendorStart
6763
- });
6764
- continue;
6765
- }
6526
+ if (noBrowser) continue;
6766
6527
  const { extensionPreview } = loadExtensionDevelopPreviewModule();
6767
6528
  await extensionPreview(pathOrRemoteUrl, {
6768
6529
  mode: 'production',
@@ -6783,57 +6544,16 @@ Cross-Browser Compatibility
6783
6544
  logUrl: startOptions.logUrl,
6784
6545
  logTab: startOptions.logTab
6785
6546
  }, (opts)=>(0, run_only.E)(opts));
6786
- telemetry.track('cli_vendor_finish', {
6787
- command: 'start',
6788
- vendor,
6789
- duration_ms: Date.now() - vendorStart
6790
- });
6791
6547
  }
6792
- telemetry.track('cli_command_finish', {
6793
- command: 'start',
6794
- duration_ms: Date.now() - cmdStart,
6795
- success: 0 === process.exitCode || null == process.exitCode,
6796
- exit_code: process.exitCode ?? 0,
6797
- ...workflowProfile
6798
- });
6799
6548
  });
6800
6549
  }
6801
- function registerPreviewCommand(program, telemetry) {
6550
+ function registerPreviewCommand(program) {
6802
6551
  program.command('preview').arguments('[project-name]').usage('preview [path-to-remote-extension] [options]').description(commandDescriptions.preview).addHelpText('after', '\nAdditional option:\n --no-browser do not launch the browser\n').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...previewOptions }) {
6803
6552
  if (previewOptions.author || previewOptions['authorMode']) {
6804
6553
  process.env.EXTENSION_AUTHOR_MODE = 'true';
6805
6554
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
6806
6555
  }
6807
- const cmdStart = Date.now();
6808
6556
  const list = vendors(browser);
6809
- const isRemoteInput = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
6810
- const projectProfile = collectProjectProfile(!isRemoteInput && pathOrRemoteUrl ? pathOrRemoteUrl : process.cwd());
6811
- const workflowProfile = collectWorkflowProfile({
6812
- command: 'preview',
6813
- isMultiBrowser: list.length > 1,
6814
- isRemoteInput: isRemoteInput,
6815
- isNoBrowserMode: '1' === process.env.EXTENSION_CLI_NO_BROWSER,
6816
- usesMachineReadableOutput: 'json' === previewOptions.logFormat || 'ndjson' === previewOptions.logFormat,
6817
- companionExtensionsProvided: Boolean(previewOptions.extensions),
6818
- packageManager: projectProfile?.package_manager,
6819
- frameworkPrimary: projectProfile?.framework_primary,
6820
- hasNextDependency: projectProfile?.has_next_dependency,
6821
- hasTurboDependency: projectProfile?.has_turbo_dependency
6822
- });
6823
- telemetry.track('workflow_profile', {
6824
- command: 'preview',
6825
- ...workflowProfile
6826
- });
6827
- telemetry.track('cli_command_start', {
6828
- command: 'preview',
6829
- vendors: list,
6830
- browser_count: list.length,
6831
- is_multi_browser: list.length > 1,
6832
- is_remote_input: isRemoteInput,
6833
- is_no_browser_mode: '1' === process.env.EXTENSION_CLI_NO_BROWSER,
6834
- companion_extensions_provided: Boolean(previewOptions.extensions),
6835
- ...workflowProfile
6836
- });
6837
6557
  validateVendorsOrExit(list, (invalid, supported)=>{
6838
6558
  console.error(unsupportedBrowserFlag(invalid, supported));
6839
6559
  });
@@ -6843,11 +6563,6 @@ Cross-Browser Compatibility
6843
6563
  }
6844
6564
  const { extensionPreview } = loadExtensionDevelopPreviewModule();
6845
6565
  for (const vendor of list){
6846
- const vendorStart = Date.now();
6847
- telemetry.track('cli_vendor_start', {
6848
- command: 'preview',
6849
- vendor
6850
- });
6851
6566
  const logsOption = previewOptions.logs;
6852
6567
  const logContextOption = previewOptions.logContext;
6853
6568
  const logContexts = parseLogContexts(logContextOption);
@@ -6869,99 +6584,29 @@ Cross-Browser Compatibility
6869
6584
  logUrl: previewOptions.logUrl,
6870
6585
  logTab: previewOptions.logTab
6871
6586
  }, (opts)=>(0, run_only.E)(opts));
6872
- telemetry.track('cli_vendor_finish', {
6873
- command: 'preview',
6874
- vendor,
6875
- duration_ms: Date.now() - vendorStart
6876
- });
6877
6587
  }
6878
- telemetry.track('cli_command_finish', {
6879
- command: 'preview',
6880
- duration_ms: Date.now() - cmdStart,
6881
- success: 0 === process.exitCode || null == process.exitCode,
6882
- exit_code: process.exitCode ?? 0,
6883
- ...workflowProfile
6884
- });
6885
6588
  });
6886
6589
  }
6887
- function registerBuildCommand(program, telemetry) {
6590
+ function registerBuildCommand(program) {
6888
6591
  program.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description(commandDescriptions.build).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
6889
6592
  if (buildOptions.author || buildOptions['authorMode']) {
6890
6593
  process.env.EXTENSION_AUTHOR_MODE = 'true';
6891
6594
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
6892
6595
  }
6893
- const cmdStart = Date.now();
6894
6596
  const list = vendors(browser);
6895
- const isRemoteInput = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
6896
- const artifactKind = buildOptions.zipSource ? buildOptions.zip ? 'zip_and_source' : 'source_zip' : buildOptions.zip ? 'zip' : 'directory';
6897
- const projectProfile = collectProjectProfile(!isRemoteInput && pathOrRemoteUrl ? pathOrRemoteUrl : process.cwd());
6898
- const workflowProfile = collectWorkflowProfile({
6899
- command: 'build',
6900
- isMultiBrowser: list.length > 1,
6901
- isRemoteInput: isRemoteInput,
6902
- companionExtensionsProvided: Boolean(buildOptions.extensions),
6903
- artifactKind,
6904
- packageManager: projectProfile?.package_manager,
6905
- frameworkPrimary: projectProfile?.framework_primary,
6906
- hasNextDependency: projectProfile?.has_next_dependency,
6907
- hasTurboDependency: projectProfile?.has_turbo_dependency
6908
- });
6909
- telemetry.track('workflow_profile', {
6910
- command: 'build',
6911
- ...workflowProfile
6912
- });
6913
- telemetry.track('cli_command_start', {
6914
- command: 'build',
6915
- vendors: list,
6916
- browser_count: list.length,
6917
- is_multi_browser: list.length > 1,
6918
- is_remote_input: isRemoteInput,
6919
- polyfill_used: buildOptions.polyfill || false,
6920
- zip: buildOptions.zip || false,
6921
- zip_source: buildOptions.zipSource || false,
6922
- artifact_kind: artifactKind,
6923
- ...workflowProfile
6924
- });
6925
6597
  validateVendorsOrExit(list, (invalid, supported)=>{
6926
6598
  console.error(unsupportedBrowserFlag(invalid, supported));
6927
6599
  });
6928
6600
  const { extensionBuild } = loadExtensionDevelopModule();
6929
- for (const vendor of list){
6930
- const vendorStart = Date.now();
6931
- telemetry.track('cli_vendor_start', {
6932
- command: 'build',
6933
- vendor
6934
- });
6935
- const buildSummary = await extensionBuild(pathOrRemoteUrl, {
6936
- browser: vendor,
6937
- polyfill: buildOptions.polyfill,
6938
- zip: buildOptions.zip,
6939
- zipSource: buildOptions.zipSource,
6940
- zipFilename: buildOptions.zipFilename,
6941
- silent: buildOptions.silent,
6942
- install: buildOptions.install,
6943
- extensions: parseExtensionsList(buildOptions.extensions)
6944
- });
6945
- telemetry.track('cli_build_summary', {
6946
- ...buildSummary,
6947
- browser_count: list.length,
6948
- is_multi_browser: list.length > 1,
6949
- is_remote_input: isRemoteInput,
6950
- artifact_kind: artifactKind,
6951
- ...workflowProfile
6952
- });
6953
- telemetry.track('cli_vendor_finish', {
6954
- command: 'build',
6955
- vendor,
6956
- duration_ms: Date.now() - vendorStart
6957
- });
6958
- }
6959
- telemetry.track('cli_command_finish', {
6960
- command: 'build',
6961
- duration_ms: Date.now() - cmdStart,
6962
- success: 0 === process.exitCode || null == process.exitCode,
6963
- exit_code: process.exitCode ?? 0,
6964
- ...workflowProfile
6601
+ for (const vendor of list)await extensionBuild(pathOrRemoteUrl, {
6602
+ browser: vendor,
6603
+ polyfill: buildOptions.polyfill,
6604
+ zip: buildOptions.zip,
6605
+ zipSource: buildOptions.zipSource,
6606
+ zipFilename: buildOptions.zipFilename,
6607
+ silent: buildOptions.silent,
6608
+ install: buildOptions.install,
6609
+ extensions: parseExtensionsList(buildOptions.extensions)
6965
6610
  });
6966
6611
  });
6967
6612
  }
@@ -6995,118 +6640,79 @@ Cross-Browser Compatibility
6995
6640
  if ('chrome' === value || 'chromium' === value || 'edge' === value || 'firefox' === value) return value;
6996
6641
  return 'chromium';
6997
6642
  }
6998
- function registerInstallCommand(program, telemetry) {
6643
+ function registerInstallCommand(program) {
6999
6644
  program.command('install').arguments('[browser-name]').usage('[browser-name] [options]').description(commandDescriptions.install).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based | all>', 'override the positional browser name. Supports comma-separated values and `all`.').option('--where', 'print the resolved managed browser cache root').action(async function(browserArg, options) {
7000
- const startedAt = Date.now();
7001
6645
  const selectedBrowser = options.browser || browserArg || 'chromium';
7002
6646
  const browserList = vendors(selectedBrowser);
7003
- const workflowProfile = collectWorkflowProfile({
7004
- command: 'install',
7005
- isMultiBrowser: browserList.length > 1,
7006
- whereMode: Boolean(options.where)
7007
- });
7008
6647
  validateVendorsOrExit(browserList, (invalid, supported)=>{
7009
6648
  console.error(unsupportedBrowserFlag(invalid, supported));
7010
6649
  });
7011
- telemetry.track('workflow_profile', {
7012
- command: 'install',
7013
- ...workflowProfile
7014
- });
7015
- telemetry.track('cli_command_start', {
7016
- command: 'install',
7017
- vendors: browserList,
7018
- browser_count: browserList.length,
7019
- is_multi_browser: browserList.length > 1,
7020
- where: Boolean(options.where),
7021
- ...workflowProfile
7022
- });
7023
- try {
7024
- if (options.where) {
7025
- const root = resolveManagedBrowsersCacheRoot();
7026
- if (options.browser || browserArg) for (const browser of browserList)console.log(external_node_path_default().join(root, normalizeInstallVendor(browser)));
7027
- else console.log(root);
7028
- } else {
7029
- const { extensionInstall } = await import("extension-install");
7030
- for (const browser of browserList)await extensionInstall({
7031
- browser
7032
- });
7033
- }
7034
- telemetry.track('cli_command_finish', {
7035
- command: 'install',
7036
- duration_ms: Date.now() - startedAt,
7037
- success: true,
7038
- exit_code: 0,
7039
- ...workflowProfile
7040
- });
7041
- } catch (err) {
7042
- telemetry.track('cli_command_finish', {
7043
- command: 'install',
7044
- duration_ms: Date.now() - startedAt,
7045
- success: false,
7046
- exit_code: 1,
7047
- ...workflowProfile
7048
- });
7049
- throw err;
6650
+ if (options.where) {
6651
+ const root = resolveManagedBrowsersCacheRoot();
6652
+ if (options.browser || browserArg) for (const browser of browserList)console.log(external_node_path_default().join(root, normalizeInstallVendor(browser)));
6653
+ else console.log(root);
6654
+ return;
7050
6655
  }
6656
+ const { extensionInstall } = await import("extension-install");
6657
+ for (const browser of browserList)await extensionInstall({
6658
+ browser
6659
+ });
7051
6660
  });
7052
6661
  program.command('uninstall').usage('uninstall <browser-name> | uninstall --all | uninstall --where').description(commandDescriptions.uninstall).option('--browser <browser-name>', 'browser to uninstall').option('--all', 'remove all managed browser binaries').option('--where', 'print the resolved managed browser cache root').argument('[browser-name]').action(async function(browserArg, { browser, all, where }) {
7053
- const startedAt = Date.now();
7054
6662
  const target = browserArg || browser;
7055
- const workflowProfile = collectWorkflowProfile({
7056
- command: 'uninstall',
7057
- whereMode: Boolean(where)
7058
- });
7059
- telemetry.track('workflow_profile', {
7060
- command: 'uninstall',
7061
- ...workflowProfile
7062
- });
7063
- telemetry.track('cli_command_start', {
7064
- command: 'uninstall',
6663
+ if (where) {
6664
+ const root = resolveManagedBrowsersCacheRoot();
6665
+ if (all) for (const browser of [
6666
+ 'chrome',
6667
+ 'chromium',
6668
+ 'edge',
6669
+ 'firefox'
6670
+ ])console.log(external_node_path_default().join(root, browser));
6671
+ else if (target) {
6672
+ const list = vendors(target);
6673
+ validateVendorsOrExit(list, (invalid, supported)=>{
6674
+ console.error(unsupportedBrowserFlag(invalid, supported));
6675
+ });
6676
+ for (const browser of list)console.log(external_node_path_default().join(root, normalizeInstallVendor(browser)));
6677
+ } else console.log(root);
6678
+ return;
6679
+ }
6680
+ const { extensionUninstall } = await import("extension-install");
6681
+ await extensionUninstall({
7065
6682
  browser: target,
7066
- all: Boolean(all),
7067
- where: Boolean(where),
7068
- ...workflowProfile
6683
+ all
7069
6684
  });
7070
- try {
7071
- if (where) {
7072
- const root = resolveManagedBrowsersCacheRoot();
7073
- if (all) for (const browser of [
7074
- 'chrome',
7075
- 'chromium',
7076
- 'edge',
7077
- 'firefox'
7078
- ])console.log(external_node_path_default().join(root, browser));
7079
- else if (target) {
7080
- const list = vendors(target);
7081
- validateVendorsOrExit(list, (invalid, supported)=>{
7082
- console.error(unsupportedBrowserFlag(invalid, supported));
7083
- });
7084
- for (const browser of list)console.log(external_node_path_default().join(root, normalizeInstallVendor(browser)));
7085
- } else console.log(root);
7086
- } else {
7087
- const { extensionUninstall } = await import("extension-install");
7088
- await extensionUninstall({
7089
- browser: target,
7090
- all
7091
- });
6685
+ });
6686
+ }
6687
+ function registerTelemetryCommand(program) {
6688
+ program.command('telemetry').argument('[action]', 'enable | disable | status (default: status)').description('Manage anonymous telemetry consent (enable, disable, or show status)').action((action)=>{
6689
+ const normalized = String(action || 'status').trim().toLowerCase();
6690
+ if ('enable' === normalized) {
6691
+ const { ok, path } = setTelemetryConsent('enabled');
6692
+ if (ok) {
6693
+ console.log(`${external_pintor_default().green('✓')} Telemetry enabled${path ? ` (${path})` : ''}.`);
6694
+ process.exit(0);
7092
6695
  }
7093
- telemetry.track('cli_command_finish', {
7094
- command: 'uninstall',
7095
- duration_ms: Date.now() - startedAt,
7096
- success: true,
7097
- exit_code: 0,
7098
- ...workflowProfile
7099
- });
7100
- } catch (err) {
7101
- telemetry.track('cli_command_finish', {
7102
- command: 'uninstall',
7103
- duration_ms: Date.now() - startedAt,
7104
- success: false,
7105
- exit_code: 1,
7106
- ...workflowProfile
7107
- });
7108
- throw err;
6696
+ console.error(`${external_pintor_default().red('')} Could not write telemetry consent file.`);
6697
+ process.exit(1);
7109
6698
  }
6699
+ if ('disable' === normalized) {
6700
+ const { ok, path } = setTelemetryConsent('disabled');
6701
+ if (ok) {
6702
+ console.log(`${external_pintor_default().green('✓')} Telemetry disabled${path ? ` (${path})` : ''}.`);
6703
+ process.exit(0);
6704
+ }
6705
+ console.error(`${external_pintor_default().red('✗')} Could not write telemetry consent file.`);
6706
+ process.exit(1);
6707
+ }
6708
+ if ('status' === normalized) {
6709
+ const { enabled, source } = getTelemetryConsent();
6710
+ const label = enabled ? external_pintor_default().green('enabled') : external_pintor_default().red('disabled');
6711
+ console.log(`Telemetry: ${label} (source: ${source})`);
6712
+ process.exit(0);
6713
+ }
6714
+ console.error(`Unknown telemetry action: ${action}. Expected: enable | disable | status.`);
6715
+ process.exit(1);
7110
6716
  });
7111
6717
  }
7112
6718
  const cliPackageJson = getCliPackageJson();
@@ -7201,12 +6807,13 @@ Cross-Browser Compatibility
7201
6807
  });
7202
6808
  const extensionJs = external_commander_namespaceObject.program;
7203
6809
  extensionJs.name(cliPackageJson.name).description(cliPackageJson.description).version(cliPackageJson.version).option('--no-telemetry', 'disable anonymous telemetry for this run').option('--ai-help', 'show AI-assistant oriented help and tips').option('--format <pretty|json>', 'output format for --ai-help', 'pretty').addHelpText('after', programUserHelp()).showHelpAfterError(true).showSuggestionAfterError(true);
7204
- registerCreateCommand(extensionJs, telemetry_cli_telemetry);
7205
- registerDevCommand(extensionJs, telemetry_cli_telemetry);
7206
- registerStartCommand(extensionJs, telemetry_cli_telemetry);
7207
- registerPreviewCommand(extensionJs, telemetry_cli_telemetry);
7208
- registerBuildCommand(extensionJs, telemetry_cli_telemetry);
7209
- registerInstallCommand(extensionJs, telemetry_cli_telemetry);
6810
+ registerCreateCommand(extensionJs);
6811
+ registerDevCommand(extensionJs);
6812
+ registerStartCommand(extensionJs);
6813
+ registerPreviewCommand(extensionJs);
6814
+ registerBuildCommand(extensionJs);
6815
+ registerInstallCommand(extensionJs);
6816
+ registerTelemetryCommand(extensionJs);
7210
6817
  extensionJs.on('option:ai-help', function() {
7211
6818
  const format = resolveAIHelpFormatFromArgv(process.argv).trim().toLowerCase();
7212
6819
  if ('json' === format) {
@@ -7227,7 +6834,10 @@ Cross-Browser Compatibility
7227
6834
  const index_argv = applyNoBrowserArgvShim(process.argv);
7228
6835
  guardSourceInspectionFlags(index_argv);
7229
6836
  guardSourceWithWaitOrNoBrowser(index_argv);
7230
- extensionJs.parseAsync(index_argv).catch((err)=>{
6837
+ extensionJs.parseAsync(index_argv).then(()=>{
6838
+ markCommandSuccess();
6839
+ }).catch((err)=>{
6840
+ markCommandFailure();
7231
6841
  console.error(unhandledError(err));
7232
6842
  process.exit(1);
7233
6843
  });