@curl-runner/cli 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curl-runner/cli",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "A powerful CLI tool for HTTP request management using YAML configuration",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -14,6 +14,7 @@
14
14
  "format": "biome format --write .",
15
15
  "lint": "biome lint .",
16
16
  "check": "biome check --write .",
17
+ "typecheck": "tsc --noEmit",
17
18
  "test": "bun test"
18
19
  },
19
20
  "keywords": [
@@ -52,6 +52,7 @@ function createSummary(total: number, failed: number): ExecutionSummary {
52
52
  total,
53
53
  successful: total - failed,
54
54
  failed,
55
+ skipped: 0,
55
56
  duration: 1000,
56
57
  results: [],
57
58
  };
package/src/cli.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import { Glob } from 'bun';
4
+ import { showUpgradeHelp, UpgradeCommand } from './commands/upgrade';
4
5
  import { BaselineManager, DiffFormatter, DiffOrchestrator } from './diff';
5
6
  import { ProfileExecutor } from './executor/profile-executor';
6
7
  import { RequestExecutor } from './executor/request-executor';
@@ -38,9 +39,9 @@ class CurlRunnerCLI {
38
39
  if (await file.exists()) {
39
40
  const yamlContent = await YamlParser.parseFile(filename);
40
41
  // Extract global config from the YAML file
41
- const config = yamlContent.global || yamlContent;
42
+ const config = yamlContent.global || {};
42
43
  this.logger.logInfo(`Loaded configuration from ${filename}`);
43
- return config;
44
+ return config as Partial<GlobalConfig>;
44
45
  }
45
46
  } catch (error) {
46
47
  this.logger.logWarning(`Failed to load configuration from ${filename}: ${error}`);
@@ -318,6 +319,17 @@ class CurlRunnerCLI {
318
319
  return;
319
320
  }
320
321
 
322
+ // Handle upgrade subcommand: curl-runner upgrade [options]
323
+ if (args[0] === 'upgrade') {
324
+ if (args.includes('--help') || args.includes('-h')) {
325
+ showUpgradeHelp();
326
+ return;
327
+ }
328
+ const upgradeCmd = new UpgradeCommand();
329
+ await upgradeCmd.run(args.slice(1));
330
+ return;
331
+ }
332
+
321
333
  // Handle diff subcommand: curl-runner diff <label1> <label2> [file]
322
334
  if (args[0] === 'diff' && args.length >= 3) {
323
335
  await this.executeDiffSubcommand(args.slice(1), options);
@@ -373,16 +385,16 @@ class CurlRunnerCLI {
373
385
  globalConfig.maxConcurrency = options.maxConcurrent as number;
374
386
  }
375
387
  if (options.continueOnError !== undefined) {
376
- globalConfig.continueOnError = options.continueOnError;
388
+ globalConfig.continueOnError = options.continueOnError as boolean;
377
389
  }
378
390
  if (options.verbose !== undefined) {
379
- globalConfig.output = { ...globalConfig.output, verbose: options.verbose };
391
+ globalConfig.output = { ...globalConfig.output, verbose: options.verbose as boolean };
380
392
  }
381
393
  if (options.quiet !== undefined) {
382
394
  globalConfig.output = { ...globalConfig.output, verbose: false };
383
395
  }
384
396
  if (options.output) {
385
- globalConfig.output = { ...globalConfig.output, saveToFile: options.output };
397
+ globalConfig.output = { ...globalConfig.output, saveToFile: options.output as string };
386
398
  }
387
399
  if (options.outputFormat) {
388
400
  globalConfig.output = {
@@ -397,21 +409,27 @@ class CurlRunnerCLI {
397
409
  };
398
410
  }
399
411
  if (options.showHeaders !== undefined) {
400
- globalConfig.output = { ...globalConfig.output, showHeaders: options.showHeaders };
412
+ globalConfig.output = {
413
+ ...globalConfig.output,
414
+ showHeaders: options.showHeaders as boolean,
415
+ };
401
416
  }
402
417
  if (options.showBody !== undefined) {
403
- globalConfig.output = { ...globalConfig.output, showBody: options.showBody };
418
+ globalConfig.output = { ...globalConfig.output, showBody: options.showBody as boolean };
404
419
  }
405
420
  if (options.showMetrics !== undefined) {
406
- globalConfig.output = { ...globalConfig.output, showMetrics: options.showMetrics };
421
+ globalConfig.output = {
422
+ ...globalConfig.output,
423
+ showMetrics: options.showMetrics as boolean,
424
+ };
407
425
  }
408
426
 
409
427
  // Apply timeout and retry settings to defaults
410
428
  if (options.timeout) {
411
- globalConfig.defaults = { ...globalConfig.defaults, timeout: options.timeout };
429
+ globalConfig.defaults = { ...globalConfig.defaults, timeout: options.timeout as number };
412
430
  }
413
431
  if (options.retries || options.noRetry) {
414
- const retryCount = options.noRetry ? 0 : options.retries || 0;
432
+ const retryCount = options.noRetry ? 0 : (options.retries as number) || 0;
415
433
  globalConfig.defaults = {
416
434
  ...globalConfig.defaults,
417
435
  retry: {
@@ -425,7 +443,7 @@ class CurlRunnerCLI {
425
443
  ...globalConfig.defaults,
426
444
  retry: {
427
445
  ...globalConfig.defaults?.retry,
428
- delay: options.retryDelay,
446
+ delay: options.retryDelay as number,
429
447
  },
430
448
  };
431
449
  }
@@ -670,13 +688,15 @@ class CurlRunnerCLI {
670
688
  }
671
689
 
672
690
  // Create combined summary
673
- const successful = allResults.filter((r) => r.success).length;
674
- const failed = allResults.filter((r) => !r.success).length;
691
+ const successful = allResults.filter((r) => r.success && !r.skipped).length;
692
+ const failed = allResults.filter((r) => !r.success && !r.skipped).length;
693
+ const skipped = allResults.filter((r) => r.skipped).length;
675
694
 
676
695
  summary = {
677
696
  total: allResults.length,
678
697
  successful,
679
698
  failed,
699
+ skipped,
680
700
  duration: totalDuration,
681
701
  results: allResults,
682
702
  };
@@ -1081,7 +1101,7 @@ class CurlRunnerCLI {
1081
1101
  variables: Record<string, string>,
1082
1102
  defaults: Partial<RequestConfig>,
1083
1103
  ): RequestConfig {
1084
- const interpolated = YamlParser.interpolateVariables(request, variables);
1104
+ const interpolated = YamlParser.interpolateVariables(request, variables) as RequestConfig;
1085
1105
  return YamlParser.mergeConfigs(defaults, interpolated);
1086
1106
  }
1087
1107
 
@@ -1214,6 +1234,11 @@ ${this.logger.color('DIFF SUBCOMMAND:', 'yellow')}
1214
1234
  curl-runner diff <label1> <label2> [file.yaml]
1215
1235
  Compare two stored baselines without making requests
1216
1236
 
1237
+ ${this.logger.color('UPGRADE:', 'yellow')}
1238
+ curl-runner upgrade Upgrade to latest version (auto-detects install method)
1239
+ curl-runner upgrade --dry-run Preview upgrade command without executing
1240
+ curl-runner upgrade --force Force reinstall even if up to date
1241
+
1217
1242
  ${this.logger.color('EXAMPLES:', 'yellow')}
1218
1243
  # Run all YAML files in current directory
1219
1244
  curl-runner
@@ -0,0 +1,262 @@
1
+ // Upgrade command for curl-runner
2
+ // Automatically detects installation source and upgrades to latest version
3
+
4
+ import { color } from '../utils/colors';
5
+ import {
6
+ type DetectionResult,
7
+ detectInstallationSource,
8
+ getUpgradeCommand,
9
+ getUpgradeCommandWindows,
10
+ type InstallationSource,
11
+ isWindows,
12
+ } from '../utils/installation-detector';
13
+ import { getVersion } from '../version';
14
+
15
+ const NPM_REGISTRY_URL = 'https://registry.npmjs.org/@curl-runner/cli/latest';
16
+ const GITHUB_API_URL = 'https://api.github.com/repos/alexvcasillas/curl-runner/releases/latest';
17
+
18
+ interface UpgradeOptions {
19
+ dryRun?: boolean;
20
+ force?: boolean;
21
+ }
22
+
23
+ export class UpgradeCommand {
24
+ async run(args: string[]): Promise<void> {
25
+ const options = this.parseArgs(args);
26
+
27
+ console.log();
28
+ console.log(color('curl-runner upgrade', 'bright'));
29
+ console.log();
30
+
31
+ // Detect installation source
32
+ const detection = detectInstallationSource();
33
+ console.log(`${color('Installation:', 'cyan')} ${this.formatSource(detection.source)}`);
34
+ console.log(`${color('Current version:', 'cyan')} ${getVersion()}`);
35
+
36
+ // Fetch latest version
37
+ const latestVersion = await this.fetchLatestVersion(detection.source);
38
+ if (!latestVersion) {
39
+ console.log(color('Failed to fetch latest version', 'red'));
40
+ process.exit(1);
41
+ }
42
+ console.log(`${color('Latest version:', 'cyan')} ${latestVersion}`);
43
+ console.log();
44
+
45
+ // Compare versions
46
+ const currentVersion = getVersion();
47
+ if (!options.force && !this.isNewerVersion(currentVersion, latestVersion)) {
48
+ console.log(color('Already up to date!', 'green'));
49
+ return;
50
+ }
51
+
52
+ // Get upgrade command
53
+ const upgradeCmd = isWindows()
54
+ ? getUpgradeCommandWindows(detection.source)
55
+ : getUpgradeCommand(detection.source);
56
+
57
+ if (options.dryRun) {
58
+ console.log(color('Dry run - would execute:', 'yellow'));
59
+ console.log(` ${color(upgradeCmd, 'cyan')}`);
60
+ return;
61
+ }
62
+
63
+ // Execute upgrade
64
+ console.log(`${color('Upgrading...', 'yellow')}`);
65
+ console.log();
66
+
67
+ await this.executeUpgrade(detection, upgradeCmd);
68
+ }
69
+
70
+ private parseArgs(args: string[]): UpgradeOptions {
71
+ const options: UpgradeOptions = {};
72
+
73
+ for (const arg of args) {
74
+ if (arg === '--dry-run' || arg === '-n') {
75
+ options.dryRun = true;
76
+ } else if (arg === '--force' || arg === '-f') {
77
+ options.force = true;
78
+ }
79
+ }
80
+
81
+ return options;
82
+ }
83
+
84
+ private formatSource(source: InstallationSource): string {
85
+ switch (source) {
86
+ case 'bun':
87
+ return 'bun (global)';
88
+ case 'npm':
89
+ return 'npm (global)';
90
+ case 'curl':
91
+ return 'curl installer';
92
+ case 'standalone':
93
+ return 'standalone binary';
94
+ }
95
+ }
96
+
97
+ private async fetchLatestVersion(source: InstallationSource): Promise<string | null> {
98
+ try {
99
+ // For npm/bun, use npm registry
100
+ if (source === 'bun' || source === 'npm') {
101
+ const response = await fetch(NPM_REGISTRY_URL, {
102
+ signal: AbortSignal.timeout(5000),
103
+ });
104
+ if (!response.ok) {
105
+ return null;
106
+ }
107
+ const data = (await response.json()) as { version: string };
108
+ return data.version;
109
+ }
110
+
111
+ // For curl/standalone, use GitHub releases
112
+ const response = await fetch(GITHUB_API_URL, {
113
+ signal: AbortSignal.timeout(5000),
114
+ headers: {
115
+ Accept: 'application/vnd.github.v3+json',
116
+ },
117
+ });
118
+ if (!response.ok) {
119
+ return null;
120
+ }
121
+ const data = (await response.json()) as { tag_name: string };
122
+ return data.tag_name.replace(/^v/, '');
123
+ } catch {
124
+ return null;
125
+ }
126
+ }
127
+
128
+ private isNewerVersion(current: string, latest: string): boolean {
129
+ const currentVersion = current.replace(/^v/, '');
130
+ const latestVersion = latest.replace(/^v/, '');
131
+
132
+ const currentParts = currentVersion.split('.').map(Number);
133
+ const latestParts = latestVersion.split('.').map(Number);
134
+
135
+ for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
136
+ const currentPart = currentParts[i] || 0;
137
+ const latestPart = latestParts[i] || 0;
138
+
139
+ if (latestPart > currentPart) {
140
+ return true;
141
+ }
142
+ if (latestPart < currentPart) {
143
+ return false;
144
+ }
145
+ }
146
+
147
+ return false;
148
+ }
149
+
150
+ private async executeUpgrade(detection: DetectionResult, upgradeCmd: string): Promise<void> {
151
+ const { source } = detection;
152
+
153
+ try {
154
+ if (source === 'bun') {
155
+ await this.runBunUpgrade();
156
+ } else if (source === 'npm') {
157
+ await this.runNpmUpgrade();
158
+ } else {
159
+ // curl or standalone - run shell command
160
+ await this.runShellUpgrade(upgradeCmd);
161
+ }
162
+ } catch (error) {
163
+ const errorMsg = error instanceof Error ? error.message : String(error);
164
+
165
+ // Check for permission errors
166
+ if (errorMsg.includes('EACCES') || errorMsg.includes('permission')) {
167
+ console.log();
168
+ console.log(color('Permission denied. Try running with sudo:', 'yellow'));
169
+ console.log(` ${color(`sudo ${upgradeCmd}`, 'cyan')}`);
170
+ process.exit(1);
171
+ }
172
+
173
+ console.log(color(`Upgrade failed: ${errorMsg}`, 'red'));
174
+ console.log();
175
+ console.log(color('Manual upgrade:', 'yellow'));
176
+ console.log(` ${color(upgradeCmd, 'cyan')}`);
177
+ process.exit(1);
178
+ }
179
+ }
180
+
181
+ private async runBunUpgrade(): Promise<void> {
182
+ const proc = Bun.spawn(['bun', 'install', '-g', '@curl-runner/cli@latest'], {
183
+ stdout: 'inherit',
184
+ stderr: 'inherit',
185
+ });
186
+
187
+ const exitCode = await proc.exited;
188
+ if (exitCode !== 0) {
189
+ throw new Error(`bun install failed with exit code ${exitCode}`);
190
+ }
191
+
192
+ this.showSuccess();
193
+ }
194
+
195
+ private async runNpmUpgrade(): Promise<void> {
196
+ const proc = Bun.spawn(['npm', 'install', '-g', '@curl-runner/cli@latest'], {
197
+ stdout: 'inherit',
198
+ stderr: 'inherit',
199
+ });
200
+
201
+ const exitCode = await proc.exited;
202
+ if (exitCode !== 0) {
203
+ throw new Error(`npm install failed with exit code ${exitCode}`);
204
+ }
205
+
206
+ this.showSuccess();
207
+ }
208
+
209
+ private async runShellUpgrade(cmd: string): Promise<void> {
210
+ let proc: ReturnType<typeof Bun.spawn>;
211
+
212
+ if (isWindows()) {
213
+ // PowerShell for Windows
214
+ proc = Bun.spawn(['powershell', '-Command', cmd], {
215
+ stdout: 'inherit',
216
+ stderr: 'inherit',
217
+ });
218
+ } else {
219
+ // Bash for Unix
220
+ proc = Bun.spawn(['bash', '-c', cmd], {
221
+ stdout: 'inherit',
222
+ stderr: 'inherit',
223
+ });
224
+ }
225
+
226
+ const exitCode = await proc.exited;
227
+ if (exitCode !== 0) {
228
+ throw new Error(`Upgrade script failed with exit code ${exitCode}`);
229
+ }
230
+
231
+ this.showSuccess();
232
+ }
233
+
234
+ private showSuccess(): void {
235
+ console.log();
236
+ console.log(color('Upgrade complete!', 'green'));
237
+ console.log();
238
+ console.log('Run `curl-runner --version` to verify.');
239
+ }
240
+ }
241
+
242
+ export function showUpgradeHelp(): void {
243
+ console.log(`
244
+ ${color('curl-runner upgrade', 'bright')}
245
+
246
+ Automatically upgrade curl-runner to the latest version.
247
+ Detects installation source (bun, npm, curl) and uses appropriate method.
248
+
249
+ ${color('USAGE:', 'yellow')}
250
+ curl-runner upgrade [options]
251
+
252
+ ${color('OPTIONS:', 'yellow')}
253
+ -n, --dry-run Show what would be executed without running
254
+ -f, --force Force upgrade even if already on latest version
255
+ -h, --help Show this help message
256
+
257
+ ${color('EXAMPLES:', 'yellow')}
258
+ curl-runner upgrade # Upgrade to latest
259
+ curl-runner upgrade --dry-run # Preview upgrade command
260
+ curl-runner upgrade --force # Force reinstall latest
261
+ `);
262
+ }
@@ -1,4 +1,3 @@
1
- import { YAML } from 'bun';
2
1
  import type { RequestConfig, ResponseStoreContext, YamlFile } from '../types/config';
3
2
 
4
3
  // Using class for organization, but could be refactored to functions
@@ -6,11 +5,11 @@ export class YamlParser {
6
5
  static async parseFile(filepath: string): Promise<YamlFile> {
7
6
  const file = Bun.file(filepath);
8
7
  const content = await file.text();
9
- return YAML.parse(content) as YamlFile;
8
+ return Bun.YAML.parse(content) as YamlFile;
10
9
  }
11
10
 
12
11
  static parse(content: string): YamlFile {
13
- return YAML.parse(content) as YamlFile;
12
+ return Bun.YAML.parse(content) as YamlFile;
14
13
  }
15
14
 
16
15
  /**
@@ -0,0 +1,11 @@
1
+ // Type declarations for Bun.YAML which is not in bun-types
2
+ declare global {
3
+ namespace Bun {
4
+ const YAML: {
5
+ parse(content: string): unknown;
6
+ stringify(value: unknown): string;
7
+ };
8
+ }
9
+ }
10
+
11
+ export {};
@@ -133,7 +133,15 @@ export class CurlBuilder {
133
133
  status?: number;
134
134
  headers?: Record<string, string>;
135
135
  body?: string;
136
- metrics?: CurlMetrics;
136
+ metrics?: {
137
+ duration: number;
138
+ size?: number;
139
+ dnsLookup?: number;
140
+ tcpConnection?: number;
141
+ tlsHandshake?: number;
142
+ firstByte?: number;
143
+ download?: number;
144
+ };
137
145
  error?: string;
138
146
  }> {
139
147
  try {
@@ -0,0 +1,52 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { getUpgradeCommand, getUpgradeCommandWindows, isWindows } from './installation-detector';
3
+
4
+ describe('getUpgradeCommand', () => {
5
+ test('returns bun command for bun source', () => {
6
+ expect(getUpgradeCommand('bun')).toBe('bun install -g @curl-runner/cli@latest');
7
+ });
8
+
9
+ test('returns npm command for npm source', () => {
10
+ expect(getUpgradeCommand('npm')).toBe('npm install -g @curl-runner/cli@latest');
11
+ });
12
+
13
+ test('returns curl command for curl source', () => {
14
+ expect(getUpgradeCommand('curl')).toBe(
15
+ 'curl -fsSL https://www.curl-runner.com/install.sh | bash',
16
+ );
17
+ });
18
+
19
+ test('returns curl command for standalone source', () => {
20
+ expect(getUpgradeCommand('standalone')).toBe(
21
+ 'curl -fsSL https://www.curl-runner.com/install.sh | bash',
22
+ );
23
+ });
24
+ });
25
+
26
+ describe('getUpgradeCommandWindows', () => {
27
+ test('returns bun command for bun source', () => {
28
+ expect(getUpgradeCommandWindows('bun')).toBe('bun install -g @curl-runner/cli@latest');
29
+ });
30
+
31
+ test('returns npm command for npm source', () => {
32
+ expect(getUpgradeCommandWindows('npm')).toBe('npm install -g @curl-runner/cli@latest');
33
+ });
34
+
35
+ test('returns PowerShell command for curl source', () => {
36
+ expect(getUpgradeCommandWindows('curl')).toBe(
37
+ 'irm https://www.curl-runner.com/install.ps1 | iex',
38
+ );
39
+ });
40
+
41
+ test('returns PowerShell command for standalone source', () => {
42
+ expect(getUpgradeCommandWindows('standalone')).toBe(
43
+ 'irm https://www.curl-runner.com/install.ps1 | iex',
44
+ );
45
+ });
46
+ });
47
+
48
+ describe('isWindows', () => {
49
+ test('returns boolean based on platform', () => {
50
+ expect(typeof isWindows()).toBe('boolean');
51
+ });
52
+ });
@@ -0,0 +1,123 @@
1
+ // Installation source detection for curl-runner upgrade command
2
+
3
+ export type InstallationSource = 'bun' | 'npm' | 'curl' | 'standalone';
4
+
5
+ export interface DetectionResult {
6
+ source: InstallationSource;
7
+ path: string;
8
+ canAutoUpgrade: boolean;
9
+ }
10
+
11
+ const CURL_INSTALL_PATHS = [
12
+ `${process.env.HOME}/.local/bin/curl-runner`,
13
+ `${process.env.USERPROFILE}\\.local\\bin\\curl-runner.exe`,
14
+ ];
15
+
16
+ export function detectInstallationSource(): DetectionResult {
17
+ const execPath = process.execPath;
18
+ const argv0 = process.argv[0];
19
+
20
+ // Check for bun installation
21
+ if (isBunInstall(execPath)) {
22
+ return {
23
+ source: 'bun',
24
+ path: execPath,
25
+ canAutoUpgrade: true,
26
+ };
27
+ }
28
+
29
+ // Check for npm installation
30
+ if (isNpmInstall(execPath, argv0)) {
31
+ return {
32
+ source: 'npm',
33
+ path: execPath,
34
+ canAutoUpgrade: true,
35
+ };
36
+ }
37
+
38
+ // Check for curl installation (binary in ~/.local/bin)
39
+ if (isCurlInstall(execPath)) {
40
+ return {
41
+ source: 'curl',
42
+ path: execPath,
43
+ canAutoUpgrade: true,
44
+ };
45
+ }
46
+
47
+ // Standalone binary
48
+ return {
49
+ source: 'standalone',
50
+ path: execPath,
51
+ canAutoUpgrade: true,
52
+ };
53
+ }
54
+
55
+ function isBunInstall(execPath: string): boolean {
56
+ // Check if running from bun's global install directory
57
+ if (execPath.includes('.bun')) {
58
+ return true;
59
+ }
60
+ if (process.env.BUN_INSTALL && execPath.includes(process.env.BUN_INSTALL)) {
61
+ return true;
62
+ }
63
+
64
+ // Check for bun-specific paths
65
+ const bunPaths = ['/bun/install/', '/.bun/bin/', '/bun/bin/'];
66
+ return bunPaths.some((p) => execPath.includes(p));
67
+ }
68
+
69
+ function isNpmInstall(execPath: string, argv0: string): boolean {
70
+ // Check for npm global install indicators
71
+ if (execPath.includes('node_modules')) {
72
+ return true;
73
+ }
74
+ if (argv0.includes('node_modules')) {
75
+ return true;
76
+ }
77
+
78
+ // Check for npm config env vars (set when running via npm)
79
+ if (process.env.npm_config_prefix) {
80
+ return true;
81
+ }
82
+ if (process.env.npm_execpath) {
83
+ return true;
84
+ }
85
+
86
+ // Check common npm global paths
87
+ const npmPaths = ['/lib/node_modules/', '/node_modules/.bin/'];
88
+ return npmPaths.some((p) => execPath.includes(p) || argv0.includes(p));
89
+ }
90
+
91
+ function isCurlInstall(execPath: string): boolean {
92
+ // Check if binary is in curl installer's default location
93
+ return CURL_INSTALL_PATHS.some((p) => execPath === p || execPath.startsWith(p));
94
+ }
95
+
96
+ export function getUpgradeCommand(source: InstallationSource): string {
97
+ switch (source) {
98
+ case 'bun':
99
+ return 'bun install -g @curl-runner/cli@latest';
100
+ case 'npm':
101
+ return 'npm install -g @curl-runner/cli@latest';
102
+ case 'curl':
103
+ return 'curl -fsSL https://www.curl-runner.com/install.sh | bash';
104
+ case 'standalone':
105
+ return 'curl -fsSL https://www.curl-runner.com/install.sh | bash';
106
+ }
107
+ }
108
+
109
+ export function getUpgradeCommandWindows(source: InstallationSource): string {
110
+ switch (source) {
111
+ case 'bun':
112
+ return 'bun install -g @curl-runner/cli@latest';
113
+ case 'npm':
114
+ return 'npm install -g @curl-runner/cli@latest';
115
+ case 'curl':
116
+ case 'standalone':
117
+ return 'irm https://www.curl-runner.com/install.ps1 | iex';
118
+ }
119
+ }
120
+
121
+ export function isWindows(): boolean {
122
+ return process.platform === 'win32';
123
+ }
@@ -106,7 +106,7 @@ export class Logger {
106
106
  };
107
107
  }
108
108
 
109
- private color(text: string, color: keyof typeof this.colors): string {
109
+ color(text: string, color: keyof typeof this.colors): string {
110
110
  return `${this.colors[color]}${text}${this.colors.reset}`;
111
111
  }
112
112
 
@@ -69,45 +69,38 @@ export class VersionChecker {
69
69
  private compareVersions(current: string, latest: string): void {
70
70
  if (this.isNewerVersion(current, latest)) {
71
71
  console.log();
72
- console.log(color('╭────────────────────────────────────────────────────────╮', 'yellow'));
72
+ console.log(color('╭───────────────────────────────────────────────╮', 'yellow'));
73
73
  console.log(
74
74
  color('│', 'yellow') +
75
- ' ' +
75
+ ' ' +
76
76
  color('│', 'yellow'),
77
77
  );
78
78
  console.log(
79
79
  color('│', 'yellow') +
80
80
  ' ' +
81
- color('📦 New version available!', 'bright') +
81
+ color('Update available!', 'bright') +
82
82
  ` ${color(current, 'red')} → ${color(latest, 'green')}` +
83
- ' ' +
83
+ ' ' +
84
84
  color('│', 'yellow'),
85
85
  );
86
86
  console.log(
87
87
  color('│', 'yellow') +
88
- ' ' +
88
+ ' ' +
89
89
  color('│', 'yellow'),
90
90
  );
91
91
  console.log(
92
92
  color('│', 'yellow') +
93
- ' Update with: ' +
94
- color('npm install -g @curl-runner/cli', 'cyan') +
95
- ' ' +
93
+ ' Run ' +
94
+ color('curl-runner upgrade', 'cyan') +
95
+ ' to update ' +
96
96
  color('│', 'yellow'),
97
97
  );
98
98
  console.log(
99
99
  color('│', 'yellow') +
100
- ' or: ' +
101
- color('bun install -g @curl-runner/cli', 'cyan') +
102
- ' ' +
100
+ ' ' +
103
101
  color('│', 'yellow'),
104
102
  );
105
- console.log(
106
- color('│', 'yellow') +
107
- ' ' +
108
- color('│', 'yellow'),
109
- );
110
- console.log(color('╰────────────────────────────────────────────────────────╯', 'yellow'));
103
+ console.log(color('╰───────────────────────────────────────────────╯', 'yellow'));
111
104
  console.log();
112
105
  }
113
106
  }