From c213254b5151a8a3406a40df2eace5152b9d8478 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 19 Jun 2026 00:03:34 +0100 Subject: [PATCH 01/15] fix: ngrok error logging by adding real-time output It was really hard to figure out if there was something wrong with ngrok without proper logging. Ngrok does actually have a --log option. so we can use this to stream live logging output directly to the terminal. - Implemented `streamCommandOutput` method in `CommandLine` to handle command output in real time, while also optionally collecting errors for later checks, and format errors as valet errors. - Enhanced `Ngrok` class to utilize the new streaming method with the --log options for better error handling and output visibility. - Updated `error` helper function to support additional `newline` and `escapeOutput` parameters for output formatting, and changed the Symfony's `writeln` to `write` so we can toggle on/off the writing of a newline at the end of the output. --- cli/Valet/CommandLine.php | 46 ++++++++++++++++++++++++++++++++++ cli/Valet/ShareTools/Ngrok.php | 21 ++++++++++------ cli/includes/helpers.php | 19 +++++++++++--- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/cli/Valet/CommandLine.php b/cli/Valet/CommandLine.php index f719867a5..c7eeb3e77 100644 --- a/cli/Valet/CommandLine.php +++ b/cli/Valet/CommandLine.php @@ -27,6 +27,52 @@ public function shellExec($command) { return shell_exec($command); } + /** + * Stream command output in real time and optionally collect matching lines. + * + * @param string $command + * @param callable|null $lineMatches Callback to check matching lines; must return `true` + * to collect the line for post-run analysis. + * @param callable|null $lineIsError Callback to check whether a line should be + * rendered as an error in real time. If omitted, the capture matcher is reused. + * + * @return array The collected output lines or an empty array if no lines were collected. + */ + public function streamCommandOutput($command, ?callable $lineMatches = null, ?callable $lineIsError = null): array { + $capturedLines = []; + $lineIsError = $lineIsError ?: $lineMatches; + + // Open a process to execute the command and read its output. + $handle = popen("$command 2>&1", 'r'); + while ($handle && !feof($handle)) { + $line = fgets($handle); + if ($line === false) { + break; + } + + // Keep raw command output unless caller explicitly marks this line as an error. + if ($lineIsError && $lineIsError($line)) { + error($line, false, false, true); + } + else { + echo $line; + } + + // If a callback is provided and the line matches the condition, + // then collect the line for post-run analysis. + if ($lineMatches && $lineMatches($line)) { + $capturedLines[] = trim($line); + } + } + + // Close the process. + if ($handle) { + pclose($handle); + } + + return $capturedLines; + } + /** * Pass the given Valet command to the command line with elevated privileges using gsudo. * diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php index 010cecba0..0c98e205b 100644 --- a/cli/Valet/ShareTools/Ngrok.php +++ b/cli/Valet/ShareTools/Ngrok.php @@ -38,19 +38,26 @@ public function start(string $site, int $port, array $options = []) { $ngrok = realpath(valetBinPath() . 'ngrok.exe'); - $ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options"; + // Log to stdout, log level info, and log format term for real-time output. + $logging = "--log=stdout --log-level=info --log-format=term"; + + $ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options $logging"; info("Sharing $site...\n"); info("To output the public URL, please open a new terminal and run `valet fetch-share-url $site`"); - $output = $this->cli->shellExec("$ngrokCommand 2>&1"); + // Stream ngrok output in real time and collect error lines for post-run analysis. + // Shared matcher: use the same rule for live error styling and for post-run capture. + $isErrorLine = function ($line) { + return strpos($line, 'ERROR:') !== false; + }; - if ($errors = strstr($output, "ERROR")) { - error($errors . PHP_EOL); + // Pass the same matcher once; CommandLine reuses it for error styling when no separate + // error callback is supplied. + $errorLines = $this->cli->streamCommandOutput($ngrokCommand, $isErrorLine); - if (strpos($errors, 'ERR_NGROK_121') !== false) { - info("To update ngrok yourself, please run `valet ngrok update` and then upgrade the config file by running `valet ngrok config upgrade`\n"); - } + if (!empty($errorLines) && strpos(implode("\n", $errorLines), 'ERR_NGROK_121') !== false) { + info("\nTo update ngrok yourself, please run `valet ngrok update` and then upgrade the config file by running `valet ngrok config upgrade`\n"); } } diff --git a/cli/includes/helpers.php b/cli/includes/helpers.php index 667780c45..93f686e32 100644 --- a/cli/includes/helpers.php +++ b/cli/includes/helpers.php @@ -11,6 +11,7 @@ use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Formatter\OutputFormatter; if (!isset($_SERVER['HOME'])) { $_SERVER['HOME'] = $_SERVER['USERPROFILE']; @@ -62,11 +63,16 @@ function warning($output) { * * @param string $output * @param bool $exception Optionally pass a boolean to indicate whether to throw an exception. If `true`, the error will be thrown as a `ValetException`. [default: `false`] + * @param bool $newline Whether to append a newline after the error output. [default: `true`] + * @param bool $escapeOutput Whether to escape the output to prevent formatting issues. [default: `false`] * * @throws RuntimeException * @throws ValetException */ -function error(string $output, $exception = false) { +function error(string $output, bool $exception = false, bool $newline = true, bool $escapeOutput = false) { + + $errorOutput = (new ConsoleOutput())->getErrorOutput(); + if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { throw new RuntimeException($output); } @@ -78,12 +84,17 @@ function error(string $output, $exception = false) { usleep(1); // Print the error message to the console. - (new ConsoleOutput())->getErrorOutput()->writeln("\n\n$errors"); + $errorOutput->write("\n\n$errors", $newline); exit(); } else { - (new ConsoleOutput())->getErrorOutput()->writeln("$output"); + // If escapeOutput is true, then escape the output to prevent any formatting issues. + if ($escapeOutput) { + $output = OutputFormatter::escape($output); + } + + $errorOutput->write("$output", $newline); } } @@ -385,4 +396,4 @@ function str_contains_any($haystack, $needles) { } } return false; -} +} \ No newline at end of file From 49cad724d5dc8d0110e987d37bf987bbb0c918a7 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 27 Jun 2026 20:59:10 +0100 Subject: [PATCH 02/15] fix: improve ngrok error message formatting & add newline characters for clarity --- cli/Valet/ShareTools/Ngrok.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php index 0c98e205b..212972153 100644 --- a/cli/Valet/ShareTools/Ngrok.php +++ b/cli/Valet/ShareTools/Ngrok.php @@ -22,9 +22,7 @@ class Ngrok extends ShareTool { */ public function start(string $site, int $port, array $options = []) { if ($port === 443 && !$this->hasAuthToken()) { - output('Forwarding to local port 443 or a local https:// URL is only available after you sign up. -Sign up at: https://ngrok.com/signup -Then use: valet set-ngrok-token [token]'); + output("Forwarding to local port 443 or a local https:// URL is only available after you sign up.\nSign up at: https://ngrok.com/signup\nThen use: valet set-ngrok-token [token]"); exit(1); } From 5faee51ac28192fcc0f7332eeec354e6cbcca355 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 28 Jun 2026 01:04:16 +0100 Subject: [PATCH 03/15] refactor: `output` helper function to toggle newlines on/off. - Updated `output` helper function to accept a `newline` parameter, and changed the Symfony's `writeln` to `write` so we can toggle on/off the writing of a newline at the end of the output. --- cli/includes/helpers.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/includes/helpers.php b/cli/includes/helpers.php index 93f686e32..1f8d5ee1f 100644 --- a/cli/includes/helpers.php +++ b/cli/includes/helpers.php @@ -102,12 +102,13 @@ function error(string $output, bool $exception = false, bool $newline = true, bo * Output the given text to the console. * * @param string $output + * @param bool $newline Whether to append a newline after the output. [default: `true`] */ -function output($output) { +function output($output, bool $newline = true) { if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { return; } - (new ConsoleOutput())->writeln($output); + (new ConsoleOutput())->write($output, $newline); } /** @@ -396,4 +397,4 @@ function str_contains_any($haystack, $needles) { } } return false; -} \ No newline at end of file +} From 3f6af7dc85dacfbeab1e385642cc296841a18a35 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 28 Jun 2026 02:53:03 +0100 Subject: [PATCH 04/15] refactor: callback params in `streamCommandOutput` method to an array. - Refactored `CommandLine::streamCommandOutput` method: - Removed the callback `lineMatches` and `lineIsError` params. - Added a `callbacks` array param to define the many callbacks that may be used, for better flexibility. - Updated the callback defaults. - Updated ngrok integration to utilize the new callback structure for error line handling. --- cli/Valet/CommandLine.php | 13 +++++++------ cli/Valet/ShareTools/Ngrok.php | 7 ++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cli/Valet/CommandLine.php b/cli/Valet/CommandLine.php index c7eeb3e77..a2775a1b7 100644 --- a/cli/Valet/CommandLine.php +++ b/cli/Valet/CommandLine.php @@ -31,16 +31,17 @@ public function shellExec($command) { * Stream command output in real time and optionally collect matching lines. * * @param string $command - * @param callable|null $lineMatches Callback to check matching lines; must return `true` - * to collect the line for post-run analysis. - * @param callable|null $lineIsError Callback to check whether a line should be - * rendered as an error in real time. If omitted, the capture matcher is reused. + * @param array $callbacks Optional callbacks: + * - matches (callable): return true to collect line for post-run analysis. + * - isError (callable): return true to render line as an error. Defaults to matches. * * @return array The collected output lines or an empty array if no lines were collected. */ - public function streamCommandOutput($command, ?callable $lineMatches = null, ?callable $lineIsError = null): array { + public function streamCommandOutput($command, array $callbacks = []): array { + $lineMatches = $callbacks['matches'] ?? null; + $lineIsError = $callbacks['isError'] ?? $lineMatches; + $capturedLines = []; - $lineIsError = $lineIsError ?: $lineMatches; // Open a process to execute the command and read its output. $handle = popen("$command 2>&1", 'r'); diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php index 212972153..a52301f8c 100644 --- a/cli/Valet/ShareTools/Ngrok.php +++ b/cli/Valet/ShareTools/Ngrok.php @@ -50,9 +50,10 @@ public function start(string $site, int $port, array $options = []) { return strpos($line, 'ERROR:') !== false; }; - // Pass the same matcher once; CommandLine reuses it for error styling when no separate - // error callback is supplied. - $errorLines = $this->cli->streamCommandOutput($ngrokCommand, $isErrorLine); + // Stream ngrok output in real time and collect error lines for post-run analysis. + $errorLines = $this->cli->streamCommandOutput($ngrokCommand, [ + 'matches' => $isErrorLine + ]); if (!empty($errorLines) && strpos(implode("\n", $errorLines), 'ERR_NGROK_121') !== false) { info("\nTo update ngrok yourself, please run `valet ngrok update` and then upgrade the config file by running `valet ngrok config upgrade`\n"); From 82288052455b10e2594f58f51671357320802279 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 28 Jun 2026 15:57:59 +0100 Subject: [PATCH 05/15] fix: enhance ngrok default options handling when they're not set by users - Refactored the host-header options conditional check in favour of defining the default options including the log options and their values in an array, and looping through them only adding them if they're not in the user options array. - Allowed the logging options to be overridden by users by removing the hardcoded logging options from the ngrok command in favour of the options array. --- cli/Valet/ShareTools/Ngrok.php | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php index a52301f8c..cda9d27fd 100644 --- a/cli/Valet/ShareTools/Ngrok.php +++ b/cli/Valet/ShareTools/Ngrok.php @@ -26,20 +26,31 @@ public function start(string $site, int $port, array $options = []) { exit(1); } - // If host-header is not specified, - // then set it into the array with a default value of rewrite. - if (!stripos(json_encode($options), 'host-header')) { - array_push($options, "host-header=rewrite"); + // Apply defaults for various options the user has not already specified. + $defaults = [ + 'host-header' => 'rewrite', + // Logging options: log to stdout at info level, enables real-time output + // and post-run error analysis. + // Logging options are undocumented for the http command, but is defined as + // API flags but still works for the http command. See ngrok docs for more details: + // https://ngrok.com/docs/agent/cli-api#flags-2 + 'log' => 'stdout', + 'log-level' => 'info', + 'log-format' => 'term' + ]; + + // Merge defaults with user-specified options, giving precedence to user-specified options. + foreach ($defaults as $key => $value) { + if (!array_filter($options, fn($opt) => strpos($opt, "$key=") === 0)) { + $options[] = "$key=$value"; + } } $options = prefixOptions($options); $ngrok = realpath(valetBinPath() . 'ngrok.exe'); - // Log to stdout, log level info, and log format term for real-time output. - $logging = "--log=stdout --log-level=info --log-format=term"; - - $ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options $logging"; + $ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options"; info("Sharing $site...\n"); info("To output the public URL, please open a new terminal and run `valet fetch-share-url $site`"); From 476dcc0253b26a9f16fc36a57c24e6fb0ffa8f0a Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 28 Jun 2026 23:15:58 +0100 Subject: [PATCH 06/15] feat: enhance ngrok output handling to extract the public URL for ease - Added a `lineHandler` callback option to the `CommandLine::streamCommandOutput` method, and allow it to be called after the line has been outputted to the terminal. - Added a line handler closure function in `Ngrok::start` method to find and extract the public URL when the tunnel starts and output it as a Valet info message, so the user can easily identify the URL without having to use the `fetch-share-url` valet command. - Added a conditional to detect if the options string doesn't contain the log stdout/stderr option, then it'll output the info message to inform users they can use the `fetch-share-url` valet command. This message doesn't need to be outputted if it is logging to stdout/stderr as it tells the use about the url anyway. - Also added a notes about why stdout and stderr values are the same - because `CommandLine::streamCommandOutput` method uses `2>&1` to redirect stderr to stdout. --- cli/Valet/CommandLine.php | 9 +++++++++ cli/Valet/ShareTools/Ngrok.php | 30 +++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/cli/Valet/CommandLine.php b/cli/Valet/CommandLine.php index a2775a1b7..42d501903 100644 --- a/cli/Valet/CommandLine.php +++ b/cli/Valet/CommandLine.php @@ -32,18 +32,21 @@ public function shellExec($command) { * * @param string $command * @param array $callbacks Optional callbacks: + * - onLine (callable): receives every raw line after it is written. * - matches (callable): return true to collect line for post-run analysis. * - isError (callable): return true to render line as an error. Defaults to matches. * * @return array The collected output lines or an empty array if no lines were collected. */ public function streamCommandOutput($command, array $callbacks = []): array { + $lineHandler = $callbacks['onLine'] ?? null; $lineMatches = $callbacks['matches'] ?? null; $lineIsError = $callbacks['isError'] ?? $lineMatches; $capturedLines = []; // Open a process to execute the command and read its output. + // 2>&1 redirects stderr to stdout so we can capture both. $handle = popen("$command 2>&1", 'r'); while ($handle && !feof($handle)) { $line = fgets($handle); @@ -59,6 +62,12 @@ public function streamCommandOutput($command, array $callbacks = []): array { echo $line; } + // Invoke the optional line handler after writing output so callers can append + // follow-up messages in display order. + if ($lineHandler) { + $lineHandler($line); + } + // If a callback is provided and the line matches the condition, // then collect the line for post-run analysis. if ($lineMatches && $lineMatches($line)) { diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php index cda9d27fd..15f784c83 100644 --- a/cli/Valet/ShareTools/Ngrok.php +++ b/cli/Valet/ShareTools/Ngrok.php @@ -34,6 +34,9 @@ public function start(string $site, int $port, array $options = []) { // Logging options are undocumented for the http command, but is defined as // API flags but still works for the http command. See ngrok docs for more details: // https://ngrok.com/docs/agent/cli-api#flags-2 + // + // (Note: Both `stdout` and `stderr` values capture the same output since + // `CommandLine::streamCommandOutput` method uses `2>&1` to redirect stderr to stdout.) 'log' => 'stdout', 'log-level' => 'info', 'log-format' => 'term' @@ -53,7 +56,12 @@ public function start(string $site, int $port, array $options = []) { $ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options"; info("Sharing $site...\n"); - info("To output the public URL, please open a new terminal and run `valet fetch-share-url $site`"); + + // If the options string doesn't contain the `--log` option with values of either `stdout` + // or `stderr`,then inform the user that they can fetch the public URL in a new terminal. + if (strpos($options, '--log=stdout') === false && strpos($options, '--log=stderr') === false) { + info("To output the public URL, please open a new terminal and run `valet fetch-share-url $site`"); + } // Stream ngrok output in real time and collect error lines for post-run analysis. // Shared matcher: use the same rule for live error styling and for post-run capture. @@ -61,8 +69,28 @@ public function start(string $site, int $port, array $options = []) { return strpos($line, 'ERROR:') !== false; }; + $didOutputShareUrl = false; + + // Line handler: check each line for the "started tunnel" log line to find and + // extract the public URL. + $lineHandler = function ($line) use ($site, &$didOutputShareUrl) { + // If the share URL has already been output, skip further processing. + if ($didOutputShareUrl) { + return; + } + + // If the line contains the 'msg="started tunnel"' message AND has a 'url=' key... + if (strpos($line, 'msg="started tunnel"') !== false && preg_match('/\burl=(\S+)/', $line, $matches)) { + // Set the flag to true to avoid further processing of lines. + $didOutputShareUrl = true; + // Output an info message with extracted public URL. + info("The public URL for $site is: $matches[1]"); + } + }; + // Stream ngrok output in real time and collect error lines for post-run analysis. $errorLines = $this->cli->streamCommandOutput($ngrokCommand, [ + 'onLine' => $lineHandler, 'matches' => $isErrorLine ]); From eea5b05121d0a5bf8a60dfa5256ee63a41c60499 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 28 Jun 2026 23:29:58 +0100 Subject: [PATCH 07/15] fix: outputting the URL info before the line is even outputted from ngrok ngrok's command output is now processed through the `output` helper function which uses Symfony's `ConsoleOutput` instead of relying on PHP's `echo`. This fixes the output race where the URL info in the lineHandler was outputted before ngrok's own output was shown. By changing it to our helper, everything goes through Symfony's buffer system and output them in order. --- cli/Valet/CommandLine.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/Valet/CommandLine.php b/cli/Valet/CommandLine.php index 42d501903..8c0d7320c 100644 --- a/cli/Valet/CommandLine.php +++ b/cli/Valet/CommandLine.php @@ -54,12 +54,13 @@ public function streamCommandOutput($command, array $callbacks = []): array { break; } - // Keep raw command output unless caller explicitly marks this line as an error. + // If the line is an error, output it as an error. if ($lineIsError && $lineIsError($line)) { error($line, false, false, true); } + // Otherwise, output it normally. else { - echo $line; + output($line, false); } // Invoke the optional line handler after writing output so callers can append @@ -207,4 +208,4 @@ public function runCommand($command, ?callable $onError = null, $realTimeOutput return new ProcessOutput($process); } } -} \ No newline at end of file +} From daa51e3ad0f4d1f7f8abe4e4ff73de5be6cde930 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 28 Jun 2026 23:37:20 +0100 Subject: [PATCH 08/15] feat: add clipboard functionality for public URL in ngrok sharing - Implemented `ShareTool::copyUrlToClipboard` method to copy the specified URL to the clipboard, and changed the `ShareTool::currentTunnelUrl` method to use this new method instead of duplicating the code. - Removed the info message from the `fetch-share-url` command, and added it into the new `ShareTool::copyUrlToClipboard` method. - Updated `Ngrok:start` method to use the new `ShareTool::copyUrlToClipboard` method for copying the URL in the `lineHandler`. --- cli/Valet/ShareTools/Ngrok.php | 5 ++++- cli/Valet/ShareTools/ShareTool.php | 13 +++++++++++-- cli/valet.php | 1 - 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php index 15f784c83..854c5cc14 100644 --- a/cli/Valet/ShareTools/Ngrok.php +++ b/cli/Valet/ShareTools/Ngrok.php @@ -85,6 +85,9 @@ public function start(string $site, int $port, array $options = []) { $didOutputShareUrl = true; // Output an info message with extracted public URL. info("The public URL for $site is: $matches[1]"); + + // Copy the public URL to the clipboard for ease. + $this->copyUrlToClipboard($matches[1]); } }; @@ -169,4 +172,4 @@ protected function hasAuthToken(): bool { } return false; } -} +} \ No newline at end of file diff --git a/cli/Valet/ShareTools/ShareTool.php b/cli/Valet/ShareTools/ShareTool.php index e29e914d0..b22afe991 100644 --- a/cli/Valet/ShareTools/ShareTool.php +++ b/cli/Valet/ShareTools/ShareTool.php @@ -90,8 +90,7 @@ public function currentTunnelUrl(string $site) { if (isset($body->tunnels) && count($body->tunnels) > 0) { // If the tunnel URL is NOT null, return the URL. if ($tunnelUrl = $this->findHttpTunnelUrl($body->tunnels, $site)) { - // Use | clip to copy the URL to the clipboard. - $this->cli->passthru("echo $tunnelUrl | clip"); + $this->copyUrlToClipboard($tunnelUrl); return $tunnelUrl; } @@ -132,4 +131,14 @@ public function findHttpTunnelUrl(array $tunnels, ?string $site = null) { } return null; } + + /** + * Copy the public URL to the clipboard. + * + * @param string $url The public URL to copy. + */ + public function copyUrlToClipboard(string $url) { + $this->cli->passthru("echo $url | clip"); + info("It has been copied to your clipboard."); + } } diff --git a/cli/valet.php b/cli/valet.php index 1a23e90aa..2c49eaf9b 100644 --- a/cli/valet.php +++ b/cli/valet.php @@ -978,7 +978,6 @@ $url = Share::shareTool()->currentTunnelUrl($site); info("The public URL for $site is: $url"); - info("It has been copied to your clipboard."); })->setAliases(["url"])->descriptions('Get and copy the public URL of the current working directory site that is currently being shared', [ "site" => "Optionally, specify a site" From 0b6b776ad7ccefd51f8aaf087d83db13a64b79ff Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 29 Jun 2026 22:04:55 +0100 Subject: [PATCH 09/15] fix: `copyUrlToClipboard` method to use PowerShell's `Set-Clipboard`. - Fixed `ShareTool::copyUrlToClipboard` method to use PowerShell's `Set-Clipboard` instead of CMD's `clip`. This is so that we can take advantage of PowerShell's single quotes to prevent command injection with shell metacharacters (e.g. &, |, >) as they are only interpreted as string characters. So if somehow the public url contains the metacharacters, they will not be used as shell operators. --- cli/Valet/ShareTools/ShareTool.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cli/Valet/ShareTools/ShareTool.php b/cli/Valet/ShareTools/ShareTool.php index b22afe991..7ad10a162 100644 --- a/cli/Valet/ShareTools/ShareTool.php +++ b/cli/Valet/ShareTools/ShareTool.php @@ -138,7 +138,12 @@ public function findHttpTunnelUrl(array $tunnels, ?string $site = null) { * @param string $url The public URL to copy. */ public function copyUrlToClipboard(string $url) { - $this->cli->passthru("echo $url | clip"); + // Escape single quotes in the URL for PowerShell. + $escapedUrl = str_replace("'", "''", $url); + // The single quotes around the URL are necessary to ensure that PowerShell treats it as + // a literal string, even if it contains spaces or special shell characters and prevents + // command injection. + $this->cli->powershell("'{$escapedUrl}' | Set-Clipboard"); info("It has been copied to your clipboard."); } } From ceb51b4b8f0447166d7b14aa9a18c96767b2a063 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 29 Jun 2026 22:32:57 +0100 Subject: [PATCH 10/15] fix: error line detection in ngrok output handling in `Ngrok::start`. - Added detection for 'ERR_NGROK_' in addition to 'ERROR:' to improve error logging and error appearance for easier skim-reading. --- cli/Valet/ShareTools/Ngrok.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php index 854c5cc14..377749189 100644 --- a/cli/Valet/ShareTools/Ngrok.php +++ b/cli/Valet/ShareTools/Ngrok.php @@ -66,7 +66,7 @@ public function start(string $site, int $port, array $options = []) { // Stream ngrok output in real time and collect error lines for post-run analysis. // Shared matcher: use the same rule for live error styling and for post-run capture. $isErrorLine = function ($line) { - return strpos($line, 'ERROR:') !== false; + return strpos($line, 'ERROR:') !== false || strpos($line, 'ERR_NGROK_') !== false; }; $didOutputShareUrl = false; @@ -172,4 +172,4 @@ protected function hasAuthToken(): bool { } return false; } -} \ No newline at end of file +} From 62749a4ed9d27887a22310a25149a0831f268274 Mon Sep 17 00:00:00 2001 From: Stu <31927084+yCodeTech@users.noreply.github.com> Date: Mon, 29 Jun 2026 23:14:15 +0100 Subject: [PATCH 11/15] fix: `streamCommandOutput` method to output an error if the command failed - Fixed `CommandLine::streamCommandOutput` method to output an error to the user if the command failed to execute. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- cli/Valet/CommandLine.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/Valet/CommandLine.php b/cli/Valet/CommandLine.php index 8c0d7320c..cd9e84eb5 100644 --- a/cli/Valet/CommandLine.php +++ b/cli/Valet/CommandLine.php @@ -48,6 +48,9 @@ public function streamCommandOutput($command, array $callbacks = []): array { // Open a process to execute the command and read its output. // 2>&1 redirects stderr to stdout so we can capture both. $handle = popen("$command 2>&1", 'r'); + if ($handle === false) { + error('Failed to start command for streaming output.', true); + } while ($handle && !feof($handle)) { $line = fgets($handle); if ($line === false) { From 86f529474aeffbab3100934c3e0d7a25d09114b3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 29 Jun 2026 23:30:30 +0100 Subject: [PATCH 12/15] docs: update README with ngrok real-time logging for the `share` command - Clarified that prior to v3.4.4, the public URL wasn't displayed and required the `fetch-share-url` command. - Added information about the new real-time logging feature in ngrok and how to disable it if needed. --- README.md | 10 +++++++++- cli/Valet/CommandLine.php | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f6f2a17a..1da8e85fd 100644 --- a/README.md +++ b/README.md @@ -1108,7 +1108,15 @@ Before sharing a site with ngrok, you must first set the authtoken using Valet's > [!NOTE] > -> The public URL won't be displayed, however, in a separate terminal, you can use the [`fetch-share-url` command](#fetch-share-url) to get the url and copy it to the clipboard. +> Prior to v3.4.4, the public URL wasn't displayed and the [`fetch-share-url` command](#fetch-share-url) had to be used instead to get the URL and copy it to the clipboard. +> +> As of v3.4.4, the public URL **will** be displayed and automatically copied to your clipboard (requires ngrok's real-time logging). You can still use the `fetch-share-url` command in a separate terminal though. + +> [!IMPORTANT] +> +> ngrok will now output logging information directly to the terminal in real-time by default using the `--log=stdout` option. Valet redirects stderr to stdout, so all logging information will be outputted to the terminal. This is useful for debugging and seeing what ngrok is doing in real-time. For this reason the `--log=stderr` is exactly the same output as `--log=stdout`. +> +> If you wish to disable real-time logging, you can use the valet's `--options` argument to pass ngrok's `log=false` option; this will disable all logging output to the terminal, including the public URL. ###### share --options diff --git a/cli/Valet/CommandLine.php b/cli/Valet/CommandLine.php index cd9e84eb5..2124465ca 100644 --- a/cli/Valet/CommandLine.php +++ b/cli/Valet/CommandLine.php @@ -48,9 +48,12 @@ public function streamCommandOutput($command, array $callbacks = []): array { // Open a process to execute the command and read its output. // 2>&1 redirects stderr to stdout so we can capture both. $handle = popen("$command 2>&1", 'r'); + + // If the process failed to start, throw an error. if ($handle === false) { error('Failed to start command for streaming output.', true); } + while ($handle && !feof($handle)) { $line = fgets($handle); if ($line === false) { From a34c69ef888658e1a34b7f3a15793aeb2b4d95f3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 3 Jul 2026 00:13:16 +0100 Subject: [PATCH 13/15] chore(editorconfig): change indent size for md files to be inline with prettier --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index e87171768..07c6fd18b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,7 @@ trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false indent_style = space -indent_size = 2 +indent_size = 4 [*.yml] indent_size = 2 From 0ac55db0be2781f5f03d212302ea0d497a5bcd50 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 3 Jul 2026 00:14:11 +0100 Subject: [PATCH 14/15] style: auto format readme --- README.md | 196 +++++++++++++++++++++++++++--------------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 1da8e85fd..1f4427f26 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ Also make sure to open your preferred terminal (Windows Terminal, CMD, Git Bash, --- -- If you don't have PHP installed, make sure to [install](https://windows.php.net/download) it. +- If you don't have PHP installed, make sure to [install](https://windows.php.net/download) it. Download the Zip file and unzip into a directory of your choosing. The recommended directory is: `C:/php/`. @@ -211,9 +211,9 @@ Also make sure to open your preferred terminal (Windows Terminal, CMD, Git Bash, > For NTS binaries the widespread use case is interaction with a web server through the FastCGI protocol, utilizing no multithreading (but also for example CLI). -- If you don't have Composer installed, make sure to [install](https://getcomposer.org/doc/00-intro.md#installation-windows) it. +- If you don't have Composer installed, make sure to [install](https://getcomposer.org/doc/00-intro.md#installation-windows) it. -- Install Valet with Composer via `composer global require ycodetech/valet-windows`. +- Install Valet with Composer via `composer global require ycodetech/valet-windows`.

@@ -221,9 +221,9 @@ Also make sure to open your preferred terminal (Windows Terminal, CMD, Git Bash, > > **If you're coming from cretueusebiu/valet-windows, then you need to make sure to fully uninstall it from your computer, deleting all configs, and removing from composer with `composer global remove cretueusebiu/valet-windows`, before installing this 3.0 version.** -- Install Valet by running the `valet install` command, or alternatively `valet sudo install` with administrator elevation. This will configure and install Valet and register Valet's daemon to launch when your system starts. Once installed, Valet will automatically start it's services. +- Install Valet by running the `valet install` command, or alternatively `valet sudo install` with administrator elevation. This will configure and install Valet and register Valet's daemon to launch when your system starts. Once installed, Valet will automatically start it's services. -- If you're installing on Windows 10/11, you may need to [manually configure](https://mayakron.altervista.org/support/acrylic/Windows10Configuration.htm) Windows to use the [Acrylic DNS Proxy](https://mayakron.altervista.org/support/acrylic/Home.htm). +- If you're installing on Windows 10/11, you may need to [manually configure](https://mayakron.altervista.org/support/acrylic/Windows10Configuration.htm) Windows to use the [Acrylic DNS Proxy](https://mayakron.altervista.org/support/acrylic/Home.htm). Valet will automatically start its daemon each time your machine boots. There is no need to run `valet start` or `valet install` ever again once the initial Valet installation is complete. @@ -248,11 +248,11 @@ Valet installed and started successfully! This installs all Valet services: -- Nginx -- PHP CGI -- PHP Xdebug CGI [optional] -- Acrylic DNS -- Ansicon +- Nginx +- PHP CGI +- PHP Xdebug CGI [optional] +- Acrylic DNS +- Ansicon And it's configs in `C:/Users/Username/.config/valet`. @@ -1462,13 +1462,13 @@ The nginx error pages are `on` (enabled) by default, which means that Valet's cu These are **important notes** for the commands that have the `--options` or `--valetOptions`. -- The `--options`, `--valetOptions` (shortcut `-o`) options can be used to pass options/flags to the service related to that command. +- The `--options`, `--valetOptions` (shortcut `-o`) options can be used to pass options/flags to the service related to that command. Just pass the option name without the `--` prefix eg. `--options=config=C:/path/ngrok.yml` (example for the `ngrok` command). This is so that Valet doesn't get confused with it's own options. All options/flags that are passed will be prefixed with `--` after Valet has processed the command, unless it's a shortcut of a single character, then it will be prefixed with `-`. The example above will run as `--config=C:/path/ngrok.yml`. -- The `=` immediately after the command option is optional, if it's omitted, you must use a space instead. +- The `=` immediately after the command option is optional, if it's omitted, you must use a space instead. ``` --options=option1 @@ -1477,7 +1477,7 @@ These are **important notes** for the commands that have the `--options` or `--v --valetOptions option1 ``` -- The options also have `-o` shortcuts and it cannot have the `=` character, it must use a space for separation. +- The options also have `-o` shortcuts and it cannot have the `=` character, it must use a space for separation. ```console -o option1 @@ -1489,7 +1489,7 @@ These are **important notes** for the commands that have the `--options` or `--v > ###### Note that to comply with the docopt standard, long options can specify their values after a whitespace or an `=` sign (e.g. `--iterations 5` or `--iterations=5`), but short options can only use whitespaces or no separation at all (e.g. `-i 5` or `-i5`). -- The options also allows multiple options to be passed, they just need to be separated with double slashes `//`. +- The options also allows multiple options to be passed, they just need to be separated with double slashes `//`. ```console --valetOptions=option1//option2//option3 @@ -1501,46 +1501,46 @@ These are **important notes** for the commands that have the `--options` or `--v Commands that have been tested and made parity: -- [ ] composer - not applicable -- [x] diagnose -- [x] directory-listing -- [x] fetch-share-url -- [x] forget -- [x] install -- [x] isolate -- [x] isolated -- [x] link -- [x] links -- [x] log -- [ ] loopback (the localhost IP) - not applicable -- [x] on-latest-version -- [x] open -- [x] park -- [x] parked -- [x] paths -- [x] php (proxying commands to PHP CLI) - renamed to `php:proxy` with alias of `php` -- [x] proxies -- [x] proxy -- [ ] renew (Renews all domains with a trusted TLS certificate) - TBD -- [x] restart -- [x] secure -- [x] secured -- [x] set-ngrok-token -- [x] share -- [x] share-tool -- [x] start -- [x] status - renamed to `services` -- [x] stop -- [x] tld -- [ ] trust - not applicable -- [x] uninstall -- [x] unisolate -- [x] unlink -- [x] unproxy -- [x] unsecure -- [x] use -- [x] which -- [x] which-php - renamed to `php:which` +- [ ] composer - not applicable +- [x] diagnose +- [x] directory-listing +- [x] fetch-share-url +- [x] forget +- [x] install +- [x] isolate +- [x] isolated +- [x] link +- [x] links +- [x] log +- [ ] loopback (the localhost IP) - not applicable +- [x] on-latest-version +- [x] open +- [x] park +- [x] parked +- [x] paths +- [x] php (proxying commands to PHP CLI) - renamed to `php:proxy` with alias of `php` +- [x] proxies +- [x] proxy +- [ ] renew (Renews all domains with a trusted TLS certificate) - TBD +- [x] restart +- [x] secure +- [x] secured +- [x] set-ngrok-token +- [x] share +- [x] share-tool +- [x] start +- [x] status - renamed to `services` +- [x] stop +- [x] tld +- [ ] trust - not applicable +- [x] uninstall +- [x] unisolate +- [x] unlink +- [x] unproxy +- [x] unsecure +- [x] use +- [x] which +- [x] which-php - renamed to `php:which` To see a calculation of how much parity has been achieved, see the [parity command](#parity). @@ -1596,56 +1596,56 @@ All services will have been stopped and removed and you can then be able to run Upon installation, Valet creates the following directories and config files: -- `~/.config/valet` - Contains all of Valet's config files. This resides in the home directory (`C:/Users/Username/`) indicated by `~`. +- `~/.config/valet` + Contains all of Valet's config files. This resides in the home directory (`C:/Users/Username/`) indicated by `~`. -- `~/.config/valet/CA` - Contains Valet's generated self-signed Root CA certificate, of which all site TLS certificates are signed with. +- `~/.config/valet/CA` + Contains Valet's generated self-signed Root CA certificate, of which all site TLS certificates are signed with. -- `~/.config/valet/Certificates` - Contains all the TLS certificates for the secured sites. These files are rebuilt when running the `install` and `secure` commands. +- `~/.config/valet/Certificates` + Contains all the TLS certificates for the secured sites. These files are rebuilt when running the `install` and `secure` commands. -- `~/.config/valet/Drivers` - Contains any user-defined custom Valet drivers. Drivers determine how a particular framework / CMS is served. See the [Valet Docs](https://laravel.com/docs/12.x/valet#custom-valet-drivers) for information on how to create a custom driver. +- `~/.config/valet/Drivers` + Contains any user-defined custom Valet drivers. Drivers determine how a particular framework / CMS is served. See the [Valet Docs](https://laravel.com/docs/12.x/valet#custom-valet-drivers) for information on how to create a custom driver. -- `~/.config/valet/Drivers/SampleValetDriver.php` - A sample custom driver. +- `~/.config/valet/Drivers/SampleValetDriver.php` + A sample custom driver. -- `~/.config/valet/Extensions` - Contains custom Valet extensions/commands. You can extend Valet and add your own commands or change existing ones. See [this comment](https://github.com/laravel/valet/issues/804#issuecomment-569731561) for more info and links to examples. +- `~/.config/valet/Extensions` + Contains custom Valet extensions/commands. You can extend Valet and add your own commands or change existing ones. See [this comment](https://github.com/laravel/valet/issues/804#issuecomment-569731561) for more info and links to examples. -- `~/.config/valet/Log` - Contains all error logs. +- `~/.config/valet/Log` + Contains all error logs. -- `~/.config/valet/Nginx` - Contains site-specific Nginx configs for any site that is isolated or secured. These files are rebuilt when running the `install`, `tld`, and `secure` commands. +- `~/.config/valet/Nginx` + Contains site-specific Nginx configs for any site that is isolated or secured. These files are rebuilt when running the `install`, `tld`, and `secure` commands. -- `~/.config/valet/Ngrok` - Contains the `ngrok.yml` config file for the ngrok executable to be able to `share` sites. This directory and file will only be created when the `set-ngrok-token` command is run. +- `~/.config/valet/Ngrok` + Contains the `ngrok.yml` config file for the ngrok executable to be able to `share` sites. This directory and file will only be created when the `set-ngrok-token` command is run. -- `~/.config/valet/Services` - Contains the Nginx and PHP config and executable files to be able to run them as Windows services. These files are rebuilt when running the `install` command. +- `~/.config/valet/Services` + Contains the Nginx and PHP config and executable files to be able to run them as Windows services. These files are rebuilt when running the `install` command. -- `~/.config/valet/Sites` - Contains all of the symbolic links for any `link`ed sites. +- `~/.config/valet/Sites` + Contains all of the symbolic links for any `link`ed sites. -- `~/.config/valet/stubs` - A user-created directory to contain custom stubs. Only used in Valet if it exists, and overrides Valet's internal stubs. +- `~/.config/valet/stubs` + A user-created directory to contain custom stubs. Only used in Valet if it exists, and overrides Valet's internal stubs. -- `~/.config/valet/Xdebug` - Contains the output files of Xdebug profiling. +- `~/.config/valet/Xdebug` + Contains the output files of Xdebug profiling. -- `~/.config/valet/config.json` - This is the main Valet config file. +- `~/.config/valet/config.json` + This is the main Valet config file. -- `~/.config/valet/Emergency Uninstall` - Contains the emergency uninstall files. +- `~/.config/valet/Emergency Uninstall` + Contains the emergency uninstall files. -- `~/.config/valet/Emergency Uninstall/ansicon` - Contains the Ansicon files and executable to help uninstall it. +- `~/.config/valet/Emergency Uninstall/ansicon` + Contains the Ansicon files and executable to help uninstall it. -- `~/.config/valet/Emergency Uninstall/emergency_uninstall_services.bat` - This is an batch file to do an emergency stop and uninstall of all services. See the [Emergency Stop and Uninstall Services section](#emergency-stop-and-uninstall-services). +- `~/.config/valet/Emergency Uninstall/emergency_uninstall_services.bat` + This is an batch file to do an emergency stop and uninstall of all services. See the [Emergency Stop and Uninstall Services section](#emergency-stop-and-uninstall-services). > [!WARNING] > @@ -1657,12 +1657,12 @@ Upon installation, Valet creates the following directories and config files: ## Known Issues -- WSL2 distros fail because of Acrylic DNS Proxy ([microsoft/wsl#4929](https://github.com/microsoft/WSL/issues/4929)). Use `valet stop`, start WSL2 then `valet start`. -- The PHP-CGI process uses port 9001. If it's already used change it in `~/.config/valet/config.json` and run `valet install` again. -- When sharing sites the url will not be copied to the clipboard. -- ~~You must run the `valet` commands from the drive where Valet is installed, except for park and link. See [#12](https://github.com/cretueusebiu/valet-windows/issues/12#issuecomment-283111834).~~ All commands seem to work fine on all drives. -- If your machine is not connected to the internet you'll have to manually add the domains in your `hosts` file or you can install the [Microsoft Loopback Adapter](https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/install-microsoft-loopback-adapter) as this simulates an active local network interface that Valet can bind too. -- When trying to run Valet on PHP 7.4 and you get this error: +- WSL2 distros fail because of Acrylic DNS Proxy ([microsoft/wsl#4929](https://github.com/microsoft/WSL/issues/4929)). Use `valet stop`, start WSL2 then `valet start`. +- The PHP-CGI process uses port 9001. If it's already used change it in `~/.config/valet/config.json` and run `valet install` again. +- When sharing sites the url will not be copied to the clipboard. +- ~~You must run the `valet` commands from the drive where Valet is installed, except for park and link. See [#12](https://github.com/cretueusebiu/valet-windows/issues/12#issuecomment-283111834).~~ All commands seem to work fine on all drives. +- If your machine is not connected to the internet you'll have to manually add the domains in your `hosts` file or you can install the [Microsoft Loopback Adapter](https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/install-microsoft-loopback-adapter) as this simulates an active local network interface that Valet can bind too. +- When trying to run Valet on PHP 7.4 and you get this error: > Composer detected issues in your platform: > @@ -1684,13 +1684,13 @@ Upon installation, Valet creates the following directories and config files: > > Make sure you uninstall Valet before `composer global update`, to make sure all services have been stopped and uninstalled before composer removes and updates them. Otherwise errors occur and composer can't update in-use files. If this does happen please refer to the [Emergency Stop and Uninstall Services](#emergency-stop-and-uninstall-services) section. -- If you're using a framework that uses a .env file and sets the domain name, such as `WP_HOME` for Laravel Bedrock, then make sure the TLD is the same as the one set for Valet. Otherwise, when trying to reach a site, the site will auto redirect to use the TLD in set in the .env. +- If you're using a framework that uses a .env file and sets the domain name, such as `WP_HOME` for Laravel Bedrock, then make sure the TLD is the same as the one set for Valet. Otherwise, when trying to reach a site, the site will auto redirect to use the TLD in set in the .env. Example: `WP_HOME='http://mySite.test'`, Valet gets a request to `http://mySite.dev`, the site will auto redirect to `http://mySite.test`. If this still happens after changing the TLD, then it has been cached by the browser, despite NGINX specifying headers not to cache. To rectify try `"Empty cache and hard reload"` option of the page reload button. -- On rare occasions, you may encounter a WMI error: +- On rare occasions, you may encounter a WMI error: > FATAL - WMI Operation failure: InvalidServiceControl >
WMI.WmiException: InvalidServiceControl @@ -1704,9 +1704,9 @@ Upon installation, Valet creates the following directories and config files: If the WMI error does occur, try running the command again. If different WMI errors occur, please submit an issue with all relevant details. -- If there is a large error and it's file trace output to the terminal, the top of the error may be cut off/overwritten. Apparently Symfony can only write to the terminal that is viewable, if it goes outside of the viewable area (ie. you need to scroll up to view) then the output is overwritten and the most important part of the error, the description at the start is cut off. (See https://github.com/symfony/symfony/issues/35012). If this happens, make the terminal larger in height and try the command again to try and view the full error. +- If there is a large error and it's file trace output to the terminal, the top of the error may be cut off/overwritten. Apparently Symfony can only write to the terminal that is viewable, if it goes outside of the viewable area (ie. you need to scroll up to view) then the output is overwritten and the most important part of the error, the description at the start is cut off. (See https://github.com/symfony/symfony/issues/35012). If this happens, make the terminal larger in height and try the command again to try and view the full error. -- When trying to install Valet after updating via Composer, and you receive this error or similiar: `The system cannot find the path specified`; it could be that Ansicon hasn't uninstalled properly and left it's path in the registry. Because it happens unpredictably, there is no way of debugging or fixing it. +- When trying to install Valet after updating via Composer, and you receive this error or similiar: `The system cannot find the path specified`; it could be that Ansicon hasn't uninstalled properly and left it's path in the registry. Because it happens unpredictably, there is no way of debugging or fixing it. The only "workaround" is to use the emergency uninstall script (`emergency_uninstall_services.bat`) in the `~/.config/valet/Emergency Uninstall` directory to allow Ansicon another chance to uninstall itself properly. (See [issue 28](https://github.com/yCodeTech/valet-windows/issues/28)). See the [Emergency Stop and Uninstall Services section](#emergency-stop-and-uninstall-services). From 9ad00acce89e0453e4ab70a6ade0a171dabc0d1c Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 3 Jul 2026 00:22:02 +0100 Subject: [PATCH 15/15] docs(readme): update the known issues section re the share url coping. Updated the known issue to state the share url will now be copied to the clipboard automatically as of v3.4.4. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f4427f26..83d17794c 100644 --- a/README.md +++ b/README.md @@ -1659,7 +1659,7 @@ Upon installation, Valet creates the following directories and config files: - WSL2 distros fail because of Acrylic DNS Proxy ([microsoft/wsl#4929](https://github.com/microsoft/WSL/issues/4929)). Use `valet stop`, start WSL2 then `valet start`. - The PHP-CGI process uses port 9001. If it's already used change it in `~/.config/valet/config.json` and run `valet install` again. -- When sharing sites the url will not be copied to the clipboard. +- ~~When sharing sites the url will not be copied to the clipboard.~~ The URL will now be copied to the clipboard automatically as of v3.4.4; see the [share command](#share) for more information. - ~~You must run the `valet` commands from the drive where Valet is installed, except for park and link. See [#12](https://github.com/cretueusebiu/valet-windows/issues/12#issuecomment-283111834).~~ All commands seem to work fine on all drives. - If your machine is not connected to the internet you'll have to manually add the domains in your `hosts` file or you can install the [Microsoft Loopback Adapter](https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/install-microsoft-loopback-adapter) as this simulates an active local network interface that Valet can bind too. - When trying to run Valet on PHP 7.4 and you get this error: