@usecanary/cli 0.2.0 → 0.4.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/dist/cli.js CHANGED
@@ -7468,15 +7468,15 @@ var require_help = __commonJS({
7468
7468
  * @return {string}
7469
7469
  *
7470
7470
  */
7471
- wrap(str, width, indent, minColumnWidth = 40) {
7471
+ wrap(str, width, indent2, minColumnWidth = 40) {
7472
7472
  const indents = " \\f\\t\\v\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF";
7473
7473
  const manualIndent = new RegExp(`[\\n][${indents}]+`);
7474
7474
  if (str.match(manualIndent)) return str;
7475
- const columnWidth = width - indent;
7475
+ const columnWidth = width - indent2;
7476
7476
  if (columnWidth < minColumnWidth) return str;
7477
- const leadingStr = str.slice(0, indent);
7478
- const columnText = str.slice(indent).replace("\r\n", "\n");
7479
- const indentString = " ".repeat(indent);
7477
+ const leadingStr = str.slice(0, indent2);
7478
+ const columnText = str.slice(indent2).replace("\r\n", "\n");
7479
+ const indentString = " ".repeat(indent2);
7480
7480
  const zeroWidthSpace = "\u200B";
7481
7481
  const breaks = `\\s${zeroWidthSpace}`;
7482
7482
  const regex = new RegExp(
@@ -9965,6 +9965,181 @@ function createLogger(opts = {}) {
9965
9965
 
9966
9966
  // ../../packages/cli-kit/src/index.ts
9967
9967
  var import_pino_pretty = __toESM(require_pino_pretty(), 1);
9968
+
9969
+ // ../../packages/cli-kit/src/snippets.generated.ts
9970
+ var API_BROWSER = "- `browser.getPage(nameOrId)` \u2014 get-or-create a named page, or attach to an existing tab by the\n `id` from `listPages()`. Named pages persist across steps in a session \u2014 call with the same\n name to reuse the tab.\n- `browser.newPage()` \u2014 an anonymous page, auto-closed when the script ends; does not persist.\n- `browser.listPages()` \u2014 list every open tab: `[{ id, url, title, name }]` (`name` is `null`\n for tabs you never named).\n- `browser.closePage(name)` \u2014 close and forget a named page.";
9971
+ var API_CONSOLE = "- `console.log` / `console.info` write to stdout; `console.warn` / `console.error` write to\n stderr. Top-level `console.log` is your script's output channel.\n- `console.log` inside `page.evaluate(() => \u2026)` runs in the page and is captured into the\n session's console artifact instead.";
9972
+ var API_FILE_HELPERS = 'All file I/O is async (await it), sandboxed to `~/.canary/tmp/` (no filesystem escape), and\nreturns the full path to the file:\n\n- `saveScreenshot(buffer, name)` \u2014 persist a screenshot buffer; buffer first:\n `const path = await saveScreenshot(await page.screenshot(), "home.png");`\n- `writeFile(name, data)` \u2014 write a small file (e.g. JSON state):\n `await writeFile("results.json", JSON.stringify(data));`\n- `readFile(name)` \u2014 read it back (returns the contents as a string):\n `const data = JSON.parse(await readFile("results.json"));`';
9973
+ var API_GLOBALS = "Every script gets these globals:\n\n- `browser` \u2014 pre-connected browser handle (see the script API)\n- `console` \u2014 `log` / `info` / `warn` / `error`, captured per run\n- `setTimeout` / `clearTimeout` \u2014 basic timers\n- `saveScreenshot(buffer, name)` \u2014 save a screenshot buffer (async \u2014 await it)\n- `writeFile(name, data)` / `readFile(name)` \u2014 small-file persistence (async \u2014 await them)";
9974
+ var API_PLAYWRIGHT_METHODS = '- `page.goto(url, { waitUntil: "domcontentloaded" })` \u2014 navigate; `waitUntil` is `"load"` /\n `"domcontentloaded"` / `"networkidle"` (prefer `"domcontentloaded"` on dev servers)\n- `page.title()` / `page.url()` \u2014 current title / URL\n- `page.snapshotForAI(options)` \u2014 AI-optimized page outline; returns `{ full, incremental? }`;\n options `{ track?, depth?, timeout? }`\n- `page.getByRole(role, { name })` / `page.getByText(text)` \u2014 semantic locators (survive re-renders)\n- `page.textContent(sel)` / `page.innerText(sel)` / `page.innerHTML(sel)` /\n `page.getAttribute(sel, name)` \u2014 read by selector\n- `page.inputValue(sel)` / `page.isChecked(sel)` / `page.isVisible(sel)` / `page.isHidden(sel)` \u2014\n input and visibility state\n- `page.fill(sel, value)` / `page.click(sel)` / `page.type(sel, text)` / `page.press(sel, key)` \u2014\n act on elements\n- `page.waitForSelector(sel, { state, timeout })` (`state`: `"attached"` / `"visible"` /\n `"hidden"` / `"detached"`) / `page.waitForURL(pattern)` / `page.waitForLoadState(state)` /\n `page.waitForFunction(fn)` / `page.waitForTimeout(ms)` \u2014 waiting\n- `page.screenshot({ fullPage })` \u2014 capture a screenshot Buffer; save it with `saveScreenshot(...)`\n- `page.evaluate(fn[, arg])` / `page.$eval(sel, fn)` / `page.$$eval(sel, fn)` \u2014 run plain\n JavaScript in the page context (real DOM; args/returns must be serializable)\n- `page.locator(sel)` \u2014 a Locator for chained actions (`.click()`, `.fill(value)`,\n `.textContent()`, `.first()`, \u2026)\n- `page.keyboard.press/type/down/up(...)` / `page.mouse.move/click/down/up(...)` \u2014 low-level input\n- `page.reload()` / `page.goBack()` / `page.goForward()` \u2014 history;\n `page.content()` / `page.setContent(html)` \u2014 full HTML\n- `page.on("console", handler)` \u2014 observe page console events';
9975
+ var API_PLAYWRIGHT_NOTE = "Pages returned by `browser.getPage()` and `browser.newPage()` are full Playwright Page objects \u2014\nthe same API (`goto`, `click`, `fill`, `locator`, `evaluate`, `getByRole`, `waitForSelector`, \u2026):\nhttps://playwright.dev/docs/api/class-page";
9976
+ var API_SANDBOX_ENV = "Scripts execute inside a QuickJS WASM sandbox with no arbitrary access to the host system.\nThis is NOT Node.js \u2014 there is no module system and no Node API:\n\n- `require()` / `import()` \u2014 no module loading; inline any helpers in the script\n- `process`, `fs` / `path` / `os` \u2014 no process or direct filesystem access (use the file helpers)\n- `fetch` / `WebSocket` \u2014 no direct network access (the page does the networking)\n- `__dirname` / `__filename` \u2014 no path globals\n\nMemory and CPU limits are enforced, and both CPU time and wall-clock time are bounded \u2014 infinite\nloops or never-settling promises abort the script. Values crossing `evaluate` / `$eval` must be\nJSON-serializable.";
9977
+ var API_SNAPSHOT = '- `page.snapshotForAI()` returns `{ full, incremental? }` \u2014 `full` is an aria outline of the\n page: roles, accessible names, `[ref=eN]` markers on actionable nodes. Read it to pick a\n semantic selector \u2014 `page.getByRole("button", { name: "Continue" })`,\n `page.getByText("Sign in")` \u2014 then act.\n- Options `{ track?, depth?, timeout? }`: re-run `page.snapshotForAI({ track: "main" })` after\n the page changes to get just the `incremental` diff; `{ depth: N }` caps the tree on huge\n pages; `timeout` bounds the walk.\n- `page.locator("aria-ref=e12")` works for an immediate action in the same script only \u2014 refs go\n stale across steps and after navigations. Prefer re-deriving a semantic selector.';
9978
+ var EX_INSPECT_TABS = "const tabs = await browser.listPages();\nconsole.log(JSON.stringify(tabs, null, 2));";
9979
+ var EX_SNAPSHOT = 'const page = await browser.getPage("main");\nconst snap = await page.snapshotForAI(); // { full, incremental? }\nconsole.log(page.url(), await page.title());\nconsole.log(snap.full); // aria outline \u2014 pick a role/text selector from this\n// then act: await page.getByRole("button", { name: "Continue" }).click();\n// after changes, page.snapshotForAI({ track: "main" }) returns just the incremental diff';
9980
+ var RULE_DATA_PASSING = '- Browser state persists across steps: named pages (and their cookies) stay open between scripts\n within a session \u2014 reuse the same page name so each step picks up where the last left off.\n- Anonymous `newPage()` tabs are closed when each script ends.\n- To pass values between steps: `writeFile("state.json", JSON.stringify(x))` in one step,\n `JSON.parse(await readFile("state.json"))` in the next.';
9981
+ var RULE_DEV_SERVER = 'For local dev servers (Next.js, Vite, \u2026) prefer\n`await page.goto(url, { waitUntil: "domcontentloaded" })` \u2014 the default `"load"` wait can hang\non HMR, streaming, or other long-lived dev-server connections. Use `"load"` only when you\nspecifically need every subresource to finish loading.';
9982
+ var RULE_FAIL_FAST = "- End each script by logging the state you need for the next decision \u2014 stdout is your\n observation channel.\n- Use short timeouts (`canary run --timeout 10`) so a step fails fast instead of hanging on a\n missing element.\n- In assertion / extraction steps, degrade gracefully \u2014 log a `WARN` / `FAIL` line instead of\n crashing, so the step still records its evidence. While exploring, a missed selector means\n look again (snapshot, fix, retry as a new step), not a silent fallback.\n- End before you stop: `canary stop` shuts the daemon down and aborts any live session,\n skipping its report.html \u2014 always `canary session end <id>` first.";
9983
+ var RULE_OBSERVE_FIRST = "- Unknown page? Snapshot first, then act: read `(await page.snapshotForAI()).full` to see what\n is there, pick a semantic selector from it (`getByRole`, `getByText`), then interact. Never\n guess selectors blind.\n- Known page or selectors? Skip the snapshot and use direct selectors \u2014 faster and more reliable.";
9984
+ var RULE_SCREENSHOT = "After each `canary run --step`, the daemon auto-captures ONE screenshot of the step's\nlast-opened tab and binds it to that step in the report. So:\n\n- Keep one primary named page per step \u2014 the report screenshot is always the page you mean.\n- If a step opens several tabs, open the one you want featured last.\n- `saveScreenshot(...)` images land in `~/.canary/tmp/` and are NOT in the report \u2014 they're\n extras for debugging.";
9985
+
9986
+ // ../../packages/cli-kit/src/scripting-help.ts
9987
+ var indent = (text, prefix) => text.split("\n").map((line) => line.length > 0 ? prefix + line : line).join("\n");
9988
+ var SANDBOX_ENVIRONMENT = `SANDBOX ENVIRONMENT:
9989
+ ${indent(API_SANDBOX_ENV, " ")}
9990
+
9991
+ ${indent(API_GLOBALS, " ")}`;
9992
+ var SCRIPT_API = `Script API available inside every script:
9993
+ ${indent(API_BROWSER, " ")}
9994
+
9995
+ ${indent(API_FILE_HELPERS, " ")}
9996
+
9997
+ ${indent(API_CONSOLE, " ")}`;
9998
+ var PLAYWRIGHT_PAGE_NOTE = API_PLAYWRIGHT_NOTE;
9999
+ function sandboxReference() {
10000
+ return `${SANDBOX_ENVIRONMENT}
10001
+
10002
+ ${SCRIPT_API}
10003
+
10004
+ ${PLAYWRIGHT_PAGE_NOTE}`;
10005
+ }
10006
+ var sessionExample = (scriptBody, opts) => [
10007
+ `canary run --session "$id" --step ${opts?.step ?? "<name>"} <<'EOF'`,
10008
+ scriptBody,
10009
+ "EOF"
10010
+ ].join("\n");
10011
+ var INSPECT_PAGE_BODY = `const page = await browser.getPage("TARGET_ID_HERE");
10012
+ console.log(JSON.stringify({
10013
+ url: page.url(),
10014
+ title: await page.title(),
10015
+ }, null, 2));`;
10016
+ var SCREENSHOT_BODY = `const page = await browser.getPage("main");
10017
+ const buf = await page.screenshot();
10018
+ const path = await saveScreenshot(buf, "debug.png");
10019
+ console.log(path);`;
10020
+ var WAITING_BODY = `const page = await browser.getPage("search-results");
10021
+ await page.waitForSelector(".results");
10022
+ await page.waitForURL("**/success");
10023
+ console.log(JSON.stringify({
10024
+ url: page.url(),
10025
+ title: await page.title(),
10026
+ }, null, 2));`;
10027
+ var ERROR_RECOVERY_BODY = `const page = await browser.getPage("checkout");
10028
+ const path = await saveScreenshot(await page.screenshot(), "debug.png");
10029
+ console.log(JSON.stringify({
10030
+ screenshot: path,
10031
+ url: page.url(),
10032
+ title: await page.title(),
10033
+ }, null, 2));`;
10034
+ var POWERSHELL_BLOCK = ` On Windows/PowerShell, use here-strings to pipe multiline scripts:
10035
+ @"
10036
+ const page = await browser.getPage("main");
10037
+ console.log(await page.title());
10038
+ "@ | canary-browser --connect`;
10039
+ var CONNECTING_SECTION = ` Connecting to a running Chrome instance:
10040
+ Auto-discover Chrome with debugging enabled:
10041
+ canary-browser --connect <<'EOF'
10042
+ const page = await browser.getPage("main");
10043
+ console.log(await page.title());
10044
+ EOF
10045
+
10046
+ Connect to a specific CDP endpoint:
10047
+ canary-browser --connect http://localhost:9222 <<'EOF'
10048
+ const page = await browser.getPage("main");
10049
+ console.log(await page.title());
10050
+ EOF
10051
+
10052
+ To launch Chrome with debugging enabled:
10053
+ chrome.exe --remote-debugging-port=9222
10054
+ google-chrome --remote-debugging-port=9222
10055
+
10056
+ Or visit chrome://inspect/#remote-debugging to configure.`;
10057
+ function buildScriptingGuide(options) {
10058
+ const { heading, example } = options;
10059
+ const browserExtras = options.browserExtras === true;
10060
+ const intro = [
10061
+ `${heading}`,
10062
+ " Write small, focused scripts. Each script should do ONE thing: navigate, click, fill, or check.",
10063
+ " End each script by logging the state you need for the next decision.",
10064
+ ' Use descriptive page names like "login", "checkout", or "results" instead of "page1".',
10065
+ ' Named pages from browser.getPage("name") persist between script runs, so you usually do not need to re-navigate.',
10066
+ " Inside page.evaluate(...), write plain JavaScript only - no TypeScript syntax in the browser context.",
10067
+ ...browserExtras ? [POWERSHELL_BLOCK] : []
10068
+ ].join("\n");
10069
+ const quickInspection = [
10070
+ " Quick inspection:",
10071
+ indent(
10072
+ example(EX_INSPECT_TABS, { step: "inspect", connect: true }),
10073
+ " "
10074
+ ),
10075
+ "",
10076
+ indent(
10077
+ example(INSPECT_PAGE_BODY, { step: "inspect", connect: true }),
10078
+ " "
10079
+ )
10080
+ ].join("\n");
10081
+ const aiSnapshots = [
10082
+ " AI snapshots for element discovery:",
10083
+ indent(example(EX_SNAPSHOT, { step: "discover" }), " "),
10084
+ "",
10085
+ indent(API_SNAPSHOT, " ")
10086
+ ].join("\n");
10087
+ const choosingApproach = [
10088
+ " Choosing your approach:",
10089
+ indent(RULE_OBSERVE_FIRST, " ")
10090
+ ].join("\n");
10091
+ const screenshots = [
10092
+ " Screenshots for visual state:",
10093
+ indent(example(SCREENSHOT_BODY, { step: "capture" }), " ")
10094
+ ].join("\n");
10095
+ const waiting = [
10096
+ " Waiting patterns:",
10097
+ indent(example(WAITING_BODY, { step: "wait" }), " ")
10098
+ ].join("\n");
10099
+ const devServer = [
10100
+ " Dev server navigation:",
10101
+ indent(RULE_DEV_SERVER, " ")
10102
+ ].join("\n");
10103
+ const errorRecovery = [
10104
+ " Error recovery:",
10105
+ " If a script fails, the page usually stays where it stopped.",
10106
+ " Reconnect to the same page name, take a screenshot, and log the URL/title:",
10107
+ indent(example(ERROR_RECOVERY_BODY, { step: "recover" }), " ")
10108
+ ].join("\n");
10109
+ const playwrightMethods = [
10110
+ " Common Playwright Page methods:",
10111
+ indent(API_PLAYWRIGHT_METHODS, " ")
10112
+ ].join("\n");
10113
+ const tips = [
10114
+ " Tips:",
10115
+ " - Use console.log(JSON.stringify(...)) for structured output.",
10116
+ " - Prefer page.snapshotForAI() for structure; use screenshots when visual layout or styling matters.",
10117
+ " - Keep page names stable across scripts so you can resume work after failures.",
10118
+ ...browserExtras ? [
10119
+ " - Each --browser name maps to a separate daemon-managed browser instance.",
10120
+ " - Use --connect to attach to an existing browser; omit the URL to auto-discover Chrome with debugging enabled."
10121
+ ] : [],
10122
+ " - Use short timeouts (--timeout 10) so scripts fail fast instead of hanging on missing elements.",
10123
+ ...browserExtras ? [
10124
+ " - Add --headless for unattended automation; omit it when you want to watch the browser window."
10125
+ ] : []
10126
+ ].join("\n");
10127
+ return [
10128
+ intro,
10129
+ quickInspection,
10130
+ aiSnapshots,
10131
+ choosingApproach,
10132
+ screenshots,
10133
+ waiting,
10134
+ devServer,
10135
+ errorRecovery,
10136
+ playwrightMethods,
10137
+ ...browserExtras ? [CONNECTING_SECTION] : [],
10138
+ tips
10139
+ ].join("\n\n");
10140
+ }
10141
+
10142
+ // ../../packages/cli-kit/src/index.ts
9968
10143
  function requestId(prefix) {
9969
10144
  return `${prefix}-${Date.now()}-${process.pid}`;
9970
10145
  }
@@ -15032,19 +15207,19 @@ WHAT IS CAPTURED (per session; toggle on \`session start\`):
15032
15207
  screenshots one per step, auto-captured from the step's last-opened page
15033
15208
 
15034
15209
  Artifacts live under ~/.canary/sessions/<id>/ (session.json, results.json, report.html, trace.zip, \u2026).
15035
- Scripts use the same sandboxed API as the engine \u2014 see \`canary-browser --help\` for the full reference.`;
15210
+ Scripts run in a QuickJS sandbox (not Node.js) with a pre-connected \`browser\` global \u2014 the full
15211
+ reference follows; the SCRIPTING GUIDE after the command list has worked examples.
15212
+
15213
+ ${sandboxReference()}`;
15036
15214
  var USAGE_GUIDE = `SESSION WORKFLOW GUIDE:
15037
15215
  Structure a session as a sequence of small steps \u2014 one script per step (open, act, assert).
15038
15216
  Each \`canary run --step <name>\` is one step in the report, with its own trace group and ONE
15039
- auto-captured screenshot (taken from the LAST page opened during that step). So:
15040
- - Use ONE primary named page per step.
15041
- - Reuse the same page name across steps to "click through" like a user \u2014 named pages persist
15042
- across steps within a session.
15043
- - If a step opens extra tabs, open the one you want featured in the report LAST.
15217
+ auto-captured screenshot.
15218
+
15219
+ ${indent(RULE_SCREENSHOT, " ")}
15044
15220
 
15045
15221
  Passing data between steps:
15046
- - Browser state (named pages, cookies) persists across steps automatically.
15047
- - For values: writeFile("state.json", JSON.stringify(x)) in one step, readFile it in the next.
15222
+ ${indent(RULE_DATA_PASSING, " ")}
15048
15223
 
15049
15224
  Reading results:
15050
15225
  canary session list List sessions (table; --json for machine output)
@@ -15052,12 +15227,18 @@ var USAGE_GUIDE = `SESSION WORKFLOW GUIDE:
15052
15227
  open ~/.canary/sessions/<id>/report.html The self-contained report
15053
15228
  canary ui Browse, search, and organize all sessions
15054
15229
 
15230
+ Step discipline:
15231
+ ${indent(RULE_FAIL_FAST, " ")}
15232
+
15055
15233
  Tips:
15056
15234
  - \`--json\` (global) emits machine-readable JSON on stdout; \`-v\`/\`--verbose\` raises stderr logging.
15057
- - \`canary run --timeout 30\` fails a step fast instead of hanging on a missing element.
15058
15235
  - \`canary session end --stop-daemon\` shuts the daemon down if nothing else is using it.
15059
- - \`canary stop\` shuts the whole background daemon down (and every browser it's running).
15060
- - Need a quick one-off with NO recording? Use \`canary-browser run\` instead of a session.`;
15236
+ - Need a quick one-off with NO recording? Use \`canary-browser run\` instead of a session.
15237
+
15238
+ ${buildScriptingGuide({
15239
+ example: sessionExample,
15240
+ heading: "SCRIPTING GUIDE:"
15241
+ })}`;
15061
15242
  var SESSION_START_LONG_ABOUT = `Start a capture-enabled session and print its id.
15062
15243
 
15063
15244
  Capture is on by default \u2014 disable per stream with --no-trace / --no-video / --no-har / --no-console.
@@ -15067,10 +15248,15 @@ Use --headless for unattended runs; omit it to watch the browser window.
15067
15248
  id=$(canary session start --name "smoke" --headless --no-video)`;
15068
15249
  var RUN_LONG_ABOUT = `Run a script as one step inside a session.
15069
15250
 
15070
- The script (a FILE, or stdin if omitted) executes in the sandbox the same way \`canary-browser run\`
15071
- does \u2014 top-level await, with \`browser\` and \`console\`. The step's name labels it in the report and
15072
- owns one auto-captured screenshot (from the last page opened during the step).
15251
+ The script (a FILE, or stdin if omitted) executes as top-level JavaScript with \`await\` in a
15252
+ sandboxed QuickJS runtime \u2014 full reference below. The step's name labels it in the report and
15253
+ owns ONE auto-captured screenshot (taken from the LAST page opened during the step). Named
15254
+ pages persist across steps within the session, so each step picks up where the last left off;
15255
+ pass values between steps with writeFile/readFile.
15256
+
15257
+ ${sandboxReference()}
15073
15258
 
15259
+ Examples:
15074
15260
  canary run open.js --session "$id" --step open
15075
15261
  echo 'const p = await browser.getPage("home"); await p.goto("https://example.com");' \\
15076
15262
  | canary run --session "$id" --step home --timeout 30`;
@@ -17034,26 +17220,25 @@ function findWorkspaceUiDir() {
17034
17220
  async function resolveUiServer() {
17035
17221
  const override = process.env.CANARY_UI_SERVER;
17036
17222
  if (override && await isFile(override)) {
17037
- return { kind: "standalone", serverJs: override };
17223
+ return { kind: "standalone", serverEntry: override };
17038
17224
  }
17039
17225
  const workspaceDir = findWorkspaceUiDir();
17040
17226
  if (!workspaceDir) {
17041
17227
  return null;
17042
17228
  }
17043
- const serverJs = path5.join(
17229
+ const serverEntry = path5.join(workspaceDir, "dist", "server", "entry.mjs");
17230
+ if (await isFile(serverEntry)) {
17231
+ return { kind: "standalone", serverEntry };
17232
+ }
17233
+ const astroBin = path5.join(
17044
17234
  workspaceDir,
17045
- ".next",
17046
- "standalone",
17047
- "apps",
17048
- "canary-ui",
17049
- "server.js"
17235
+ "node_modules",
17236
+ "astro",
17237
+ "bin",
17238
+ "astro.mjs"
17050
17239
  );
17051
- if (await isFile(serverJs)) {
17052
- return { kind: "standalone", serverJs };
17053
- }
17054
- const nextBin = path5.join(workspaceDir, "node_modules", ".bin", "next");
17055
- if (await isFile(nextBin)) {
17056
- return { kind: "dev", nextBin, workspaceDir };
17240
+ if (await isFile(astroBin)) {
17241
+ return { kind: "dev", astroBin, workspaceDir };
17057
17242
  }
17058
17243
  return null;
17059
17244
  }
@@ -17114,9 +17299,10 @@ async function uiCommand(args) {
17114
17299
  const baseEnv = { ...process.env, CANARY_UI_ROOT: root };
17115
17300
  let child;
17116
17301
  if (resolved.kind === "standalone") {
17117
- child = spawn4(process.execPath, [resolved.serverJs], {
17118
- cwd: path6.dirname(resolved.serverJs),
17119
- env: { ...baseEnv, HOSTNAME: host, PORT: String(port) },
17302
+ child = spawn4(process.execPath, [resolved.serverEntry], {
17303
+ cwd: path6.dirname(resolved.serverEntry),
17304
+ // Astro's node adapter reads HOST (the old Next server read HOSTNAME).
17305
+ env: { ...baseEnv, HOST: host, PORT: String(port) },
17120
17306
  stdio: ["ignore", "inherit", "inherit"]
17121
17307
  });
17122
17308
  } else {
@@ -17125,7 +17311,7 @@ async function uiCommand(args) {
17125
17311
  );
17126
17312
  child = spawn4(
17127
17313
  process.execPath,
17128
- [resolved.nextBin, "dev", "-p", String(port), "-H", host],
17314
+ [resolved.astroBin, "dev", "--port", String(port), "--host", host],
17129
17315
  {
17130
17316
  cwd: resolved.workspaceDir,
17131
17317
  env: baseEnv,
@@ -17173,7 +17359,7 @@ async function uiCommand(args) {
17173
17359
 
17174
17360
  // src/cli.ts
17175
17361
  var { Command: Command2, InvalidArgumentError: InvalidArgumentError2 } = esm_exports;
17176
- var VERSION = "0.2.0";
17362
+ var VERSION = "0.4.0";
17177
17363
  var ExitCodeError = class extends Error {
17178
17364
  code;
17179
17365
  constructor(code) {