@wolfstar/cli 2.6.0 → 2.6.1-next-20261007182959

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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runMain } from "./run-BkfjbY7l.js";
2
+ import { t as runMain } from "./run-Bs6e9_6S.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  await runMain();
@@ -0,0 +1,3 @@
1
+ import { t as commands } from "./commands-D9i-ZgpA.js";
2
+
3
+ export { commands };
@@ -1,6 +1,6 @@
1
1
  //#region src/commands/index.ts
2
2
  const commands = {
3
- dev: () => import("./dev-Vv2-8hUB.js").then((module) => module.default),
3
+ dev: () => import("./dev-BLlgQwl6.js").then((module) => module.default),
4
4
  build: () => import("./build-BbIEbOu3.js").then((module) => module.default),
5
5
  info: () => import("./info-C_YDcVul.js").then((module) => module.default),
6
6
  codegen: () => import("./codegen-C2PY7RXA.js").then((module) => module.default),
@@ -8,9 +8,9 @@ const commands = {
8
8
  typecheck: () => import("./typecheck-p_2rj9lL.js").then((module) => module.default),
9
9
  commands: () => import("./commands-C-i3qYOz.js").then((module) => module.default),
10
10
  doctor: () => import("./doctor-CMGfbXS5.js").then((module) => module.default),
11
- completions: () => import("./completions-5E5jiT_A.js").then((module) => module.default)
11
+ completions: () => import("./completions-DA-NZAcY.js").then((module) => module.default)
12
12
  };
13
13
 
14
14
  //#endregion
15
15
  export { commands as t };
16
- //# sourceMappingURL=commands-BiY_99sF.js.map
16
+ //# sourceMappingURL=commands-D9i-ZgpA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands-BiY_99sF.js","names":[],"sources":["../src/commands/index.ts"],"sourcesContent":["import type { CommandDef } from 'citty';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nexport const commands: Record<string, () => Promise<AnyCommand>> = {\n\tdev: () => import('./dev.js').then((module) => module.default),\n\tbuild: () => import('./build.js').then((module) => module.default),\n\tinfo: () => import('./info.js').then((module) => module.default),\n\tcodegen: () => import('./codegen.js').then((module) => module.default),\n\tprepare: () => import('./prepare.js').then((module) => module.default),\n\ttypecheck: () => import('./typecheck.js').then((module) => module.default),\n\tcommands: () => import('./commands.js').then((module) => module.default),\n\tdoctor: () => import('./doctor.js').then((module) => module.default),\n\tcompletions: () => import('./completions.js').then((module) => module.default)\n};\n"],"mappings":";AAMA,MAAa,WAAsD;CAClE,WAAW,OAAO,oBAAW,CAAC,MAAM,WAAW,OAAO,OAAO;CAC7D,aAAa,OAAO,sBAAa,CAAC,MAAM,WAAW,OAAO,OAAO;CACjE,YAAY,OAAO,qBAAY,CAAC,MAAM,WAAW,OAAO,OAAO;CAC/D,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,iBAAiB,OAAO,0BAAiB,CAAC,MAAM,WAAW,OAAO,OAAO;CACzE,gBAAgB,OAAO,yBAAgB,CAAC,MAAM,WAAW,OAAO,OAAO;CACvE,cAAc,OAAO,uBAAc,CAAC,MAAM,WAAW,OAAO,OAAO;CACnE,mBAAmB,OAAO,4BAAmB,CAAC,MAAM,WAAW,OAAO,OAAO;AAC9E"}
1
+ {"version":3,"file":"commands-D9i-ZgpA.js","names":[],"sources":["../src/commands/index.ts"],"sourcesContent":["import type { CommandDef } from 'citty';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nexport const commands: Record<string, () => Promise<AnyCommand>> = {\n\tdev: () => import('./dev.js').then((module) => module.default),\n\tbuild: () => import('./build.js').then((module) => module.default),\n\tinfo: () => import('./info.js').then((module) => module.default),\n\tcodegen: () => import('./codegen.js').then((module) => module.default),\n\tprepare: () => import('./prepare.js').then((module) => module.default),\n\ttypecheck: () => import('./typecheck.js').then((module) => module.default),\n\tcommands: () => import('./commands.js').then((module) => module.default),\n\tdoctor: () => import('./doctor.js').then((module) => module.default),\n\tcompletions: () => import('./completions.js').then((module) => module.default)\n};\n"],"mappings":";AAMA,MAAa,WAAsD;CAClE,WAAW,OAAO,oBAAW,CAAC,MAAM,WAAW,OAAO,OAAO;CAC7D,aAAa,OAAO,sBAAa,CAAC,MAAM,WAAW,OAAO,OAAO;CACjE,YAAY,OAAO,qBAAY,CAAC,MAAM,WAAW,OAAO,OAAO;CAC/D,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,iBAAiB,OAAO,0BAAiB,CAAC,MAAM,WAAW,OAAO,OAAO;CACzE,gBAAgB,OAAO,yBAAgB,CAAC,MAAM,WAAW,OAAO,OAAO;CACvE,cAAc,OAAO,uBAAc,CAAC,MAAM,WAAW,OAAO,OAAO;CACnE,mBAAmB,OAAO,4BAAmB,CAAC,MAAM,WAAW,OAAO,OAAO;AAC9E"}
@@ -138,7 +138,7 @@ function generateCompletions(shell, commands) {
138
138
  return GENERATORS[shell](commands);
139
139
  }
140
140
  async function runCompletions(options) {
141
- const { commands } = await import("./commands-DuiQDJUH.js");
141
+ const { commands } = await import("./commands-BfNA1-eZ.js");
142
142
  (options.stdout ?? process.stdout).write(generateCompletions(options.shell, await describeCommands(commands)));
143
143
  }
144
144
  var completions_default = defineCommand({
@@ -158,4 +158,4 @@ var completions_default = defineCommand({
158
158
 
159
159
  //#endregion
160
160
  export { completions_default as default };
161
- //# sourceMappingURL=completions-5E5jiT_A.js.map
161
+ //# sourceMappingURL=completions-DA-NZAcY.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"completions-5E5jiT_A.js","names":[],"sources":["../src/commands/completions.ts"],"sourcesContent":["import { defineCommand, type ArgsDef, type CommandDef } from 'citty';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\n\n// citty types each command by its own arguments; the tree only needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nexport interface CompletionFlag {\n\tname: string;\n\tdescription: string;\n\t/** Short forms, without the dash (`c` for `-c`). */\n\taliases: string[];\n\t/** A boolean that is on by default also has a `--no-<name>` form. */\n\tnegatable: boolean;\n}\n\nexport interface CompletionCommand {\n\tname: string;\n\tdescription: string;\n\tflags: CompletionFlag[];\n\tsubcommands: CompletionCommand[];\n}\n\nexport const SHELLS = ['bash', 'zsh', 'fish'] as const;\nexport type Shell = (typeof SHELLS)[number];\n\nasync function resolve<T>(value: T | Promise<T> | (() => T | Promise<T>) | undefined): Promise<T | undefined> {\n\treturn typeof value === 'function' ? (value as () => T | Promise<T>)() : value;\n}\n\n/** Walks the commands the CLI registers, so the completions never drift from what `--help` prints. */\nexport async function describeCommands(commands: Record<string, () => Promise<AnyCommand>>): Promise<CompletionCommand[]> {\n\tconst describe = async (name: string, command: AnyCommand): Promise<CompletionCommand> => {\n\t\tconst meta = (await resolve(command.meta)) ?? {};\n\t\tconst args = ((await resolve(command.args)) ?? {}) as ArgsDef;\n\t\tconst children = ((await resolve(command.subCommands)) ?? {}) as Record<string, AnyCommand | (() => Promise<AnyCommand>)>;\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription: meta.description ?? '',\n\t\t\tflags: Object.entries(args)\n\t\t\t\t.filter(([, arg]) => arg.type !== 'positional')\n\t\t\t\t.map(([flag, arg]) => ({\n\t\t\t\t\tname: flag,\n\t\t\t\t\tdescription: arg.description ?? '',\n\t\t\t\t\taliases: [(arg as { alias?: string | string[] }).alias ?? []].flat(),\n\t\t\t\t\tnegatable: arg.type === 'boolean' && arg.default === true\n\t\t\t\t})),\n\t\t\tsubcommands: await Promise.all(Object.entries(children).map(async ([child, value]) => describe(child, (await resolve(value))!)))\n\t\t};\n\t};\n\n\treturn Promise.all(Object.entries(commands).map(async ([name, load]) => describe(name, await load())));\n}\n\n/** Every spelling of a flag: `--name`, `--no-name` when it can be switched off, and its short forms. */\nconst spellings = (flag: CompletionFlag) => [\n\t`--${flag.name}`,\n\t...(flag.negatable ? [`--no-${flag.name}`] : []),\n\t...flag.aliases.map((alias) => (alias.length === 1 ? `-${alias}` : `--${alias}`))\n];\n\nconst flagsOf = (command: CompletionCommand) => [...command.flags.flatMap(spellings), '--help'].join(' ');\n\nfunction bash(commands: readonly CompletionCommand[]): string {\n\tconst cases = commands.map((command) => {\n\t\tif (command.subcommands.length === 0) return `\\t\\t${command.name}) words=\"${flagsOf(command)}\" ;;`;\n\t\tconst nested = command.subcommands.map((child) => `\\t\\t\\t\\t${child.name}) words=\"${flagsOf(child)}\" ;;`).join('\\n');\n\t\treturn [\n\t\t\t`\\t\\t${command.name})`,\n\t\t\t// The subcommand is the first of its names after the command: options of the parent may come before it.\n\t\t\t'\\t\\t\\tsub=\"\"',\n\t\t\t'\\t\\t\\tfor ((i = 2; i < COMP_CWORD; i++)); do',\n\t\t\t`\\t\\t\\t\\tcase \"\\${COMP_WORDS[i]}\" in ${command.subcommands.map((child) => child.name).join('|')}) sub=\"\\${COMP_WORDS[i]}\"; break ;; esac`,\n\t\t\t'\\t\\t\\tdone',\n\t\t\t'\\t\\t\\tcase \"$sub\" in',\n\t\t\tnested,\n\t\t\t`\\t\\t\\t\\t*) words=\"${command.subcommands.map((child) => child.name).join(' ')} ${flagsOf(command)}\" ;;`,\n\t\t\t'\\t\\t\\tesac ;;'\n\t\t].join('\\n');\n\t});\n\n\treturn `# stars completions for bash. Load them with: eval \"$(stars completions bash)\"\n_stars() {\n\tlocal cur words sub i\n\tcur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n\tif [ \"$COMP_CWORD\" -eq 1 ]; then\n\t\twords=\"${commands.map((command) => command.name).join(' ')} --help --version\"\n\telse\n\t\tcase \"\\${COMP_WORDS[1]}\" in\n${cases.join('\\n')}\n\t\t\t*) words=\"\" ;;\n\t\tesac\n\tfi\n\tCOMPREPLY=($(compgen -W \"$words\" -- \"$cur\"))\n}\ncomplete -o default -F _stars stars\n`;\n}\n\nconst quote = (text: string) => `'${text.replaceAll(\"'\", \"'\\\\''\")}'`;\n/** A zsh `_describe` entry: the colon separates the value from its description, so one in the text is escaped. */\nconst described = (name: string, description: string) => quote(`${name}:${description.replaceAll(':', '\\\\:')}`);\n\nfunction zsh(commands: readonly CompletionCommand[]): string {\n\tconst flags = (command: CompletionCommand) =>\n\t\t[\n\t\t\t...command.flags.flatMap((flag) => spellings(flag).map((spelling) => described(spelling, flag.description))),\n\t\t\tdescribed('--help', 'Show the usage')\n\t\t].join(' ');\n\tconst cases = commands.map((command) => {\n\t\tif (command.subcommands.length === 0) return `\\t\\t${command.name}) items=(${flags(command)}) ;;`;\n\t\tconst nested = command.subcommands.map((child) => `\\t\\t\\t\\t${child.name}) items=(${flags(child)}) ;;`).join('\\n');\n\t\tconst children = command.subcommands.map((child) => described(child.name, child.description)).join(' ');\n\t\treturn [\n\t\t\t`\\t\\t${command.name})`,\n\t\t\t'\\t\\t\\tsub=\"\"',\n\t\t\t'\\t\\t\\tfor word in ${words[3,CURRENT-1]}; do',\n\t\t\t`\\t\\t\\t\\tcase \"$word\" in ${command.subcommands.map((child) => child.name).join('|')}) sub=\"$word\"; break ;; esac`,\n\t\t\t'\\t\\t\\tdone',\n\t\t\t'\\t\\t\\tcase \"$sub\" in',\n\t\t\tnested,\n\t\t\t`\\t\\t\\t\\t*) items=(${children} ${flags(command)}) ;;`,\n\t\t\t'\\t\\t\\tesac ;;'\n\t\t].join('\\n');\n\t});\n\n\treturn `#compdef stars\n# stars completions for zsh. Load them with: eval \"$(stars completions zsh)\"\n_stars() {\n\tlocal -a items\n\tlocal sub word\n\tif (( CURRENT == 2 )); then\n\t\titems=(${commands.map((command) => described(command.name, command.description)).join(' ')})\n\telse\n\t\tcase \"$words[2]\" in\n${cases.join('\\n')}\n\t\tesac\n\tfi\n\t_describe 'stars' items\n}\ncompdef _stars stars\n`;\n}\n\nfunction fishFlag(condition: string, flag: CompletionFlag): string[] {\n\tconst short = flag.aliases\n\t\t.filter((alias) => alias.length === 1)\n\t\t.map((alias) => ` -s ${alias}`)\n\t\t.join('');\n\tconst line = (name: string) => `complete -c stars -n \"${condition}\" -l ${name}${name === flag.name ? short : ''} -d ${quote(flag.description)}`;\n\treturn [line(flag.name), ...(flag.negatable ? [line(`no-${flag.name}`)] : [])];\n}\n\nfunction fish(commands: readonly CompletionCommand[]): string {\n\tconst lines = ['# stars completions for fish. Load them with: stars completions fish | source', 'complete -c stars -f'];\n\tconst names = commands.map((command) => command.name).join(' ');\n\tfor (const command of commands) {\n\t\tlines.push(`complete -c stars -n \"not __fish_seen_subcommand_from ${names}\" -a ${command.name} -d ${quote(command.description)}`);\n\t\tconst children = command.subcommands.map((child) => child.name).join(' ');\n\t\tfor (const flag of command.flags) lines.push(...fishFlag(`__fish_seen_subcommand_from ${command.name}`, flag));\n\n\t\tfor (const child of command.subcommands) {\n\t\t\tlines.push(\n\t\t\t\t`complete -c stars -n \"__fish_seen_subcommand_from ${command.name}; and not __fish_seen_subcommand_from ${children}\" -a ${child.name} -d ${quote(child.description)}`\n\t\t\t);\n\t\t\tfor (const flag of child.flags.filter((candidate) => !command.flags.some((own) => own.name === candidate.name))) {\n\t\t\t\tlines.push(...fishFlag(`__fish_seen_subcommand_from ${command.name}; and __fish_seen_subcommand_from ${child.name}`, flag));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn `${lines.join('\\n')}\\n`;\n}\n\nconst GENERATORS: Record<Shell, (commands: readonly CompletionCommand[]) => string> = { bash, zsh, fish };\n\n/** The completion script of a shell, for the given commands. */\nexport function generateCompletions(shell: string, commands: readonly CompletionCommand[]): string {\n\tif (!(SHELLS as readonly string[]).includes(shell)) throw cliDiagnostics.UNKNOWN_SHELL({ shell });\n\treturn GENERATORS[shell as Shell](commands);\n}\n\nexport interface CompletionsOptions {\n\tshell: string;\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport async function runCompletions(options: CompletionsOptions): Promise<void> {\n\t// Imported here: the registry imports this module, as it does every command.\n\tconst { commands } = await import('./index.js');\n\t(options.stdout ?? process.stdout).write(generateCompletions(options.shell, await describeCommands(commands)));\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'completions',\n\t\tdescription: 'Print the shell completion script for bash, zsh or fish'\n\t},\n\targs: {\n\t\tshell: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: `The shell to print completions for: ${SHELLS.join(', ')}`,\n\t\t\trequired: true\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tawait runCompletions({ shell: args.shell });\n\t}\n});\n"],"mappings":";;;;AAuBA,MAAa,SAAS;CAAC;CAAQ;CAAO;AAAM;AAG5C,eAAe,QAAW,OAAoF;CAC7G,OAAO,OAAO,UAAU,aAAc,MAA+B,IAAI;AAC1E;;AAGA,eAAsB,iBAAiB,UAAmF;CACzH,MAAM,WAAW,OAAO,MAAc,YAAoD;EACzF,MAAM,OAAQ,MAAM,QAAQ,QAAQ,IAAI,KAAM,CAAC;EAC/C,MAAM,OAAS,MAAM,QAAQ,QAAQ,IAAI,KAAM,CAAC;EAChD,MAAM,WAAa,MAAM,QAAQ,QAAQ,WAAW,KAAM,CAAC;EAC3D,OAAO;GACN;GACA,aAAa,KAAK,eAAe;GACjC,OAAO,OAAO,QAAQ,IAAI,CAAC,CACzB,QAAQ,GAAG,SAAS,IAAI,SAAS,YAAY,CAAC,CAC9C,KAAK,CAAC,MAAM,UAAU;IACtB,MAAM;IACN,aAAa,IAAI,eAAe;IAChC,SAAS,CAAE,IAAsC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK;IACnE,WAAW,IAAI,SAAS,aAAa,IAAI,YAAY;GACtD,EAAE;GACH,aAAa,MAAM,QAAQ,IAAI,OAAO,QAAQ,QAAQ,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,WAAW,SAAS,OAAQ,MAAM,QAAQ,KAAK,CAAG,CAAC,CAAC;EAChI;CACD;CAEA,OAAO,QAAQ,IAAI,OAAO,QAAQ,QAAQ,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,UAAU,SAAS,MAAM,MAAM,KAAK,CAAC,CAAC,CAAC;AACtG;;AAGA,MAAM,aAAa,SAAyB;CAC3C,KAAK,KAAK;CACV,GAAI,KAAK,YAAY,CAAC,QAAQ,KAAK,MAAM,IAAI,CAAC;CAC9C,GAAG,KAAK,QAAQ,KAAK,UAAW,MAAM,WAAW,IAAI,IAAI,UAAU,KAAK,OAAQ;AACjF;AAEA,MAAM,WAAW,YAA+B,CAAC,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG,QAAQ,CAAC,CAAC,KAAK,GAAG;AAExG,SAAS,KAAK,UAAgD;CAC7D,MAAM,QAAQ,SAAS,KAAK,YAAY;EACvC,IAAI,QAAQ,YAAY,WAAW,GAAG,OAAO,OAAO,QAAQ,KAAK,WAAW,QAAQ,OAAO,EAAE;EAC7F,MAAM,SAAS,QAAQ,YAAY,KAAK,UAAU,WAAW,MAAM,KAAK,WAAW,QAAQ,KAAK,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI;EAClH,OAAO;GACN,OAAO,QAAQ,KAAK;GAEpB;GACA;GACA,uCAAuC,QAAQ,YAAY,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE;GAChG;GACA;GACA;GACA,qBAAqB,QAAQ,YAAY,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,QAAQ,OAAO,EAAE;GAClG;EACD,CAAC,CAAC,KAAK,IAAI;CACZ,CAAC;CAED,OAAO;;;;;WAKG,SAAS,KAAK,YAAY,QAAQ,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE;;;EAG3D,MAAM,KAAK,IAAI,EAAE;;;;;;;;AAQnB;AAEA,MAAM,SAAS,SAAiB,IAAI,KAAK,WAAW,KAAK,OAAO,EAAE;;AAElE,MAAM,aAAa,MAAc,gBAAwB,MAAM,GAAG,KAAK,GAAG,YAAY,WAAW,KAAK,KAAK,GAAG;AAE9G,SAAS,IAAI,UAAgD;CAC5D,MAAM,SAAS,YACd,CACC,GAAG,QAAQ,MAAM,SAAS,SAAS,UAAU,IAAI,CAAC,CAAC,KAAK,aAAa,UAAU,UAAU,KAAK,WAAW,CAAC,CAAC,GAC3G,UAAU,UAAU,gBAAgB,CACrC,CAAC,CAAC,KAAK,GAAG;CACX,MAAM,QAAQ,SAAS,KAAK,YAAY;EACvC,IAAI,QAAQ,YAAY,WAAW,GAAG,OAAO,OAAO,QAAQ,KAAK,WAAW,MAAM,OAAO,EAAE;EAC3F,MAAM,SAAS,QAAQ,YAAY,KAAK,UAAU,WAAW,MAAM,KAAK,WAAW,MAAM,KAAK,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI;EAChH,MAAM,WAAW,QAAQ,YAAY,KAAK,UAAU,UAAU,MAAM,MAAM,MAAM,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG;EACtG,OAAO;GACN,OAAO,QAAQ,KAAK;GACpB;GACA;GACA,2BAA2B,QAAQ,YAAY,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE;GACpF;GACA;GACA;GACA,qBAAqB,SAAS,GAAG,MAAM,OAAO,EAAE;GAChD;EACD,CAAC,CAAC,KAAK,IAAI;CACZ,CAAC;CAED,OAAO;;;;;;WAMG,SAAS,KAAK,YAAY,UAAU,QAAQ,MAAM,QAAQ,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;;;EAG3F,MAAM,KAAK,IAAI,EAAE;;;;;;;AAOnB;AAEA,SAAS,SAAS,WAAmB,MAAgC;CACpE,MAAM,QAAQ,KAAK,QACjB,QAAQ,UAAU,MAAM,WAAW,CAAC,CAAC,CACrC,KAAK,UAAU,OAAO,OAAO,CAAC,CAC9B,KAAK,EAAE;CACT,MAAM,QAAQ,SAAiB,yBAAyB,UAAU,OAAO,OAAO,SAAS,KAAK,OAAO,QAAQ,GAAG,MAAM,MAAM,KAAK,WAAW;CAC5I,OAAO,CAAC,KAAK,KAAK,IAAI,GAAG,GAAI,KAAK,YAAY,CAAC,KAAK,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAE;AAC9E;AAEA,SAAS,KAAK,UAAgD;CAC7D,MAAM,QAAQ,CAAC,iFAAiF,sBAAsB;CACtH,MAAM,QAAQ,SAAS,KAAK,YAAY,QAAQ,IAAI,CAAC,CAAC,KAAK,GAAG;CAC9D,KAAK,MAAM,WAAW,UAAU;EAC/B,MAAM,KAAK,yDAAyD,MAAM,OAAO,QAAQ,KAAK,MAAM,MAAM,QAAQ,WAAW,GAAG;EAChI,MAAM,WAAW,QAAQ,YAAY,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG;EACxE,KAAK,MAAM,QAAQ,QAAQ,OAAO,MAAM,KAAK,GAAG,SAAS,+BAA+B,QAAQ,QAAQ,IAAI,CAAC;EAE7G,KAAK,MAAM,SAAS,QAAQ,aAAa;GACxC,MAAM,KACL,qDAAqD,QAAQ,KAAK,wCAAwC,SAAS,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM,WAAW,GACnK;GACA,KAAK,MAAM,QAAQ,MAAM,MAAM,QAAQ,cAAc,CAAC,QAAQ,MAAM,MAAM,QAAQ,IAAI,SAAS,UAAU,IAAI,CAAC,GAC7G,MAAM,KAAK,GAAG,SAAS,+BAA+B,QAAQ,KAAK,oCAAoC,MAAM,QAAQ,IAAI,CAAC;EAE5H;CACD;CAEA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC5B;AAEA,MAAM,aAAgF;CAAE;CAAM;CAAK;AAAK;;AAGxG,SAAgB,oBAAoB,OAAe,UAAgD;CAClG,IAAI,CAAE,OAA6B,SAAS,KAAK,GAAG,MAAM,eAAe,cAAc,EAAE,MAAM,CAAC;CAChG,OAAO,WAAW,MAAe,CAAC,QAAQ;AAC3C;AAOA,eAAsB,eAAe,SAA4C;CAEhF,MAAM,EAAE,aAAa,MAAM,OAAO;CAClC,CAAC,QAAQ,UAAU,QAAQ,OAAM,CAAE,MAAM,oBAAoB,QAAQ,OAAO,MAAM,iBAAiB,QAAQ,CAAC,CAAC;AAC9G;AAEA,0BAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM,EACL,OAAO;EACN,MAAM;EACN,aAAa,uCAAuC,OAAO,KAAK,IAAI;EACpE,UAAU;CACX,EACD;CACA,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,eAAe,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AACD,CAAC"}
1
+ {"version":3,"file":"completions-DA-NZAcY.js","names":[],"sources":["../src/commands/completions.ts"],"sourcesContent":["import { defineCommand, type ArgsDef, type CommandDef } from 'citty';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\n\n// citty types each command by its own arguments; the tree only needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nexport interface CompletionFlag {\n\tname: string;\n\tdescription: string;\n\t/** Short forms, without the dash (`c` for `-c`). */\n\taliases: string[];\n\t/** A boolean that is on by default also has a `--no-<name>` form. */\n\tnegatable: boolean;\n}\n\nexport interface CompletionCommand {\n\tname: string;\n\tdescription: string;\n\tflags: CompletionFlag[];\n\tsubcommands: CompletionCommand[];\n}\n\nexport const SHELLS = ['bash', 'zsh', 'fish'] as const;\nexport type Shell = (typeof SHELLS)[number];\n\nasync function resolve<T>(value: T | Promise<T> | (() => T | Promise<T>) | undefined): Promise<T | undefined> {\n\treturn typeof value === 'function' ? (value as () => T | Promise<T>)() : value;\n}\n\n/** Walks the commands the CLI registers, so the completions never drift from what `--help` prints. */\nexport async function describeCommands(commands: Record<string, () => Promise<AnyCommand>>): Promise<CompletionCommand[]> {\n\tconst describe = async (name: string, command: AnyCommand): Promise<CompletionCommand> => {\n\t\tconst meta = (await resolve(command.meta)) ?? {};\n\t\tconst args = ((await resolve(command.args)) ?? {}) as ArgsDef;\n\t\tconst children = ((await resolve(command.subCommands)) ?? {}) as Record<string, AnyCommand | (() => Promise<AnyCommand>)>;\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription: meta.description ?? '',\n\t\t\tflags: Object.entries(args)\n\t\t\t\t.filter(([, arg]) => arg.type !== 'positional')\n\t\t\t\t.map(([flag, arg]) => ({\n\t\t\t\t\tname: flag,\n\t\t\t\t\tdescription: arg.description ?? '',\n\t\t\t\t\taliases: [(arg as { alias?: string | string[] }).alias ?? []].flat(),\n\t\t\t\t\tnegatable: arg.type === 'boolean' && arg.default === true\n\t\t\t\t})),\n\t\t\tsubcommands: await Promise.all(Object.entries(children).map(async ([child, value]) => describe(child, (await resolve(value))!)))\n\t\t};\n\t};\n\n\treturn Promise.all(Object.entries(commands).map(async ([name, load]) => describe(name, await load())));\n}\n\n/** Every spelling of a flag: `--name`, `--no-name` when it can be switched off, and its short forms. */\nconst spellings = (flag: CompletionFlag) => [\n\t`--${flag.name}`,\n\t...(flag.negatable ? [`--no-${flag.name}`] : []),\n\t...flag.aliases.map((alias) => (alias.length === 1 ? `-${alias}` : `--${alias}`))\n];\n\nconst flagsOf = (command: CompletionCommand) => [...command.flags.flatMap(spellings), '--help'].join(' ');\n\nfunction bash(commands: readonly CompletionCommand[]): string {\n\tconst cases = commands.map((command) => {\n\t\tif (command.subcommands.length === 0) return `\\t\\t${command.name}) words=\"${flagsOf(command)}\" ;;`;\n\t\tconst nested = command.subcommands.map((child) => `\\t\\t\\t\\t${child.name}) words=\"${flagsOf(child)}\" ;;`).join('\\n');\n\t\treturn [\n\t\t\t`\\t\\t${command.name})`,\n\t\t\t// The subcommand is the first of its names after the command: options of the parent may come before it.\n\t\t\t'\\t\\t\\tsub=\"\"',\n\t\t\t'\\t\\t\\tfor ((i = 2; i < COMP_CWORD; i++)); do',\n\t\t\t`\\t\\t\\t\\tcase \"\\${COMP_WORDS[i]}\" in ${command.subcommands.map((child) => child.name).join('|')}) sub=\"\\${COMP_WORDS[i]}\"; break ;; esac`,\n\t\t\t'\\t\\t\\tdone',\n\t\t\t'\\t\\t\\tcase \"$sub\" in',\n\t\t\tnested,\n\t\t\t`\\t\\t\\t\\t*) words=\"${command.subcommands.map((child) => child.name).join(' ')} ${flagsOf(command)}\" ;;`,\n\t\t\t'\\t\\t\\tesac ;;'\n\t\t].join('\\n');\n\t});\n\n\treturn `# stars completions for bash. Load them with: eval \"$(stars completions bash)\"\n_stars() {\n\tlocal cur words sub i\n\tcur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n\tif [ \"$COMP_CWORD\" -eq 1 ]; then\n\t\twords=\"${commands.map((command) => command.name).join(' ')} --help --version\"\n\telse\n\t\tcase \"\\${COMP_WORDS[1]}\" in\n${cases.join('\\n')}\n\t\t\t*) words=\"\" ;;\n\t\tesac\n\tfi\n\tCOMPREPLY=($(compgen -W \"$words\" -- \"$cur\"))\n}\ncomplete -o default -F _stars stars\n`;\n}\n\nconst quote = (text: string) => `'${text.replaceAll(\"'\", \"'\\\\''\")}'`;\n/** A zsh `_describe` entry: the colon separates the value from its description, so one in the text is escaped. */\nconst described = (name: string, description: string) => quote(`${name}:${description.replaceAll(':', '\\\\:')}`);\n\nfunction zsh(commands: readonly CompletionCommand[]): string {\n\tconst flags = (command: CompletionCommand) =>\n\t\t[\n\t\t\t...command.flags.flatMap((flag) => spellings(flag).map((spelling) => described(spelling, flag.description))),\n\t\t\tdescribed('--help', 'Show the usage')\n\t\t].join(' ');\n\tconst cases = commands.map((command) => {\n\t\tif (command.subcommands.length === 0) return `\\t\\t${command.name}) items=(${flags(command)}) ;;`;\n\t\tconst nested = command.subcommands.map((child) => `\\t\\t\\t\\t${child.name}) items=(${flags(child)}) ;;`).join('\\n');\n\t\tconst children = command.subcommands.map((child) => described(child.name, child.description)).join(' ');\n\t\treturn [\n\t\t\t`\\t\\t${command.name})`,\n\t\t\t'\\t\\t\\tsub=\"\"',\n\t\t\t'\\t\\t\\tfor word in ${words[3,CURRENT-1]}; do',\n\t\t\t`\\t\\t\\t\\tcase \"$word\" in ${command.subcommands.map((child) => child.name).join('|')}) sub=\"$word\"; break ;; esac`,\n\t\t\t'\\t\\t\\tdone',\n\t\t\t'\\t\\t\\tcase \"$sub\" in',\n\t\t\tnested,\n\t\t\t`\\t\\t\\t\\t*) items=(${children} ${flags(command)}) ;;`,\n\t\t\t'\\t\\t\\tesac ;;'\n\t\t].join('\\n');\n\t});\n\n\treturn `#compdef stars\n# stars completions for zsh. Load them with: eval \"$(stars completions zsh)\"\n_stars() {\n\tlocal -a items\n\tlocal sub word\n\tif (( CURRENT == 2 )); then\n\t\titems=(${commands.map((command) => described(command.name, command.description)).join(' ')})\n\telse\n\t\tcase \"$words[2]\" in\n${cases.join('\\n')}\n\t\tesac\n\tfi\n\t_describe 'stars' items\n}\ncompdef _stars stars\n`;\n}\n\nfunction fishFlag(condition: string, flag: CompletionFlag): string[] {\n\tconst short = flag.aliases\n\t\t.filter((alias) => alias.length === 1)\n\t\t.map((alias) => ` -s ${alias}`)\n\t\t.join('');\n\tconst line = (name: string) => `complete -c stars -n \"${condition}\" -l ${name}${name === flag.name ? short : ''} -d ${quote(flag.description)}`;\n\treturn [line(flag.name), ...(flag.negatable ? [line(`no-${flag.name}`)] : [])];\n}\n\nfunction fish(commands: readonly CompletionCommand[]): string {\n\tconst lines = ['# stars completions for fish. Load them with: stars completions fish | source', 'complete -c stars -f'];\n\tconst names = commands.map((command) => command.name).join(' ');\n\tfor (const command of commands) {\n\t\tlines.push(`complete -c stars -n \"not __fish_seen_subcommand_from ${names}\" -a ${command.name} -d ${quote(command.description)}`);\n\t\tconst children = command.subcommands.map((child) => child.name).join(' ');\n\t\tfor (const flag of command.flags) lines.push(...fishFlag(`__fish_seen_subcommand_from ${command.name}`, flag));\n\n\t\tfor (const child of command.subcommands) {\n\t\t\tlines.push(\n\t\t\t\t`complete -c stars -n \"__fish_seen_subcommand_from ${command.name}; and not __fish_seen_subcommand_from ${children}\" -a ${child.name} -d ${quote(child.description)}`\n\t\t\t);\n\t\t\tfor (const flag of child.flags.filter((candidate) => !command.flags.some((own) => own.name === candidate.name))) {\n\t\t\t\tlines.push(...fishFlag(`__fish_seen_subcommand_from ${command.name}; and __fish_seen_subcommand_from ${child.name}`, flag));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn `${lines.join('\\n')}\\n`;\n}\n\nconst GENERATORS: Record<Shell, (commands: readonly CompletionCommand[]) => string> = { bash, zsh, fish };\n\n/** The completion script of a shell, for the given commands. */\nexport function generateCompletions(shell: string, commands: readonly CompletionCommand[]): string {\n\tif (!(SHELLS as readonly string[]).includes(shell)) throw cliDiagnostics.UNKNOWN_SHELL({ shell });\n\treturn GENERATORS[shell as Shell](commands);\n}\n\nexport interface CompletionsOptions {\n\tshell: string;\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport async function runCompletions(options: CompletionsOptions): Promise<void> {\n\t// Imported here: the registry imports this module, as it does every command.\n\tconst { commands } = await import('./index.js');\n\t(options.stdout ?? process.stdout).write(generateCompletions(options.shell, await describeCommands(commands)));\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'completions',\n\t\tdescription: 'Print the shell completion script for bash, zsh or fish'\n\t},\n\targs: {\n\t\tshell: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: `The shell to print completions for: ${SHELLS.join(', ')}`,\n\t\t\trequired: true\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tawait runCompletions({ shell: args.shell });\n\t}\n});\n"],"mappings":"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"}
@@ -119,6 +119,7 @@ var DevService = class extends EventEmitter {
119
119
  #lastExit = null;
120
120
  #restartTimer = null;
121
121
  #healthTimer = null;
122
+ /** The restart a build asked for that has not happened yet: it outlives the timer, which the next build start clears. */
122
123
  #pendingReason = null;
123
124
  #stopped = false;
124
125
  #queue = Promise.resolve();
@@ -145,6 +146,8 @@ var DevService = class extends EventEmitter {
145
146
  #deployed = null;
146
147
  /** A refresh of the commands asked for while the bot was not listening; sent once it is. */
147
148
  #refreshPending = false;
149
+ /** Restarts waiting for the running build to end, see {@link DevService.#buildSettled}. */
150
+ #buildWaiters = [];
148
151
  constructor(config, options) {
149
152
  super();
150
153
  this.config = config;
@@ -163,6 +166,7 @@ var DevService = class extends EventEmitter {
163
166
  startedAt: Date.now(),
164
167
  readyMs: null
165
168
  };
169
+ this.#clearRestartTimer();
166
170
  this.#setBuild("building");
167
171
  this.#startHook("build:before", this.config);
168
172
  });
@@ -264,6 +268,7 @@ var DevService = class extends EventEmitter {
264
268
  if (this.#stopped) return;
265
269
  await this.#queueHook("dev:restart", reason, this.config);
266
270
  if (this.#stopped) return;
271
+ if (!await this.#buildSettled(reason)) return;
267
272
  if (reason === "manual" || reason === "crash") this.#progress = {
268
273
  fraction: 0,
269
274
  message: "restarting the bot",
@@ -276,7 +281,8 @@ var DevService = class extends EventEmitter {
276
281
  this.log("stars", "info", `Restarting (${describeReason(reason)})`);
277
282
  await this.supervisor.stop();
278
283
  } else this.log("stars", "info", `Starting (${describeReason(reason)})`);
279
- if (this.#stopped) return;
284
+ if (this.#stopped || !await this.#buildSettled(reason)) return;
285
+ this.#pendingReason = null;
280
286
  this.#progress = {
281
287
  ...this.#progress,
282
288
  fraction: .75,
@@ -305,13 +311,51 @@ var DevService = class extends EventEmitter {
305
311
  /** Settles the pending {@link DevPrompt}: `true` does what it asks, `false` dismisses it. */
306
312
  answerPrompt(accept) {
307
313
  if (this.#prompt === null) return;
314
+ if (accept && this.#refreshBlocked()) return;
308
315
  this.#prompt = null;
309
316
  if (accept) this.refreshCommands();
310
317
  else this.log("stars", "info", "Kept the deployed commands as they are", { channel: "commands" });
311
318
  this.#emitStatus();
312
319
  }
320
+ /**
321
+ * Whether a refresh has to wait, saying why. While a build rewrites the output a bot that starts loads a part of the
322
+ * commands, and registering that would unregister the rest from Discord.
323
+ */
324
+ #refreshBlocked() {
325
+ if (this.#build !== "building") return false;
326
+ this.log("stars", "warn", "A build is running, the commands of the bot may be incomplete: answer again once it has finished", { channel: "commands" });
327
+ return true;
328
+ }
329
+ /**
330
+ * Resolves once no build is running, `false` when the restart is not worth doing any more: the session stopped, or
331
+ * an automatic restart (`build`, `initial`) had to wait for a build, whose own end already decides what happens next
332
+ * (a restart if it succeeded, nothing if it failed: the output would be the previous, possibly half written one).
333
+ * The dropped restart stays owed in {@link DevService.#pendingReason}, so that build's end restarts the bot even when
334
+ * it could be hot reloaded: the change the dropped restart was for is not part of what that build rewrote.
335
+ * A manual restart or one after a crash is still what was asked for.
336
+ */
337
+ async #buildSettled(reason) {
338
+ const automatic = reason === "build" || reason === "initial";
339
+ let waited = false;
340
+ while (this.#build === "building" && !this.#stopped) {
341
+ waited = true;
342
+ await new Promise((resolve) => this.#buildWaiters.push(resolve));
343
+ }
344
+ if (this.#stopped) return false;
345
+ if (waited && automatic) {
346
+ this.#pendingReason ??= reason;
347
+ return false;
348
+ }
349
+ return true;
350
+ }
351
+ #releaseBuildWaiters() {
352
+ const waiters = this.#buildWaiters;
353
+ this.#buildWaiters = [];
354
+ for (const resolve of waiters) resolve();
355
+ }
313
356
  /** Asks the bot to register its commands with Discord again. The bot answers on the `commands` channel. */
314
357
  refreshCommands() {
358
+ if (this.#refreshBlocked()) return;
315
359
  if (!this.#ready) {
316
360
  this.#refreshPending = true;
317
361
  const when = this.supervisor.running ? "once the bot is ready" : "when the bot starts again";
@@ -338,6 +382,7 @@ var DevService = class extends EventEmitter {
338
382
  async stop() {
339
383
  this.#stopped = true;
340
384
  this.#clearRestartTimer();
385
+ this.#releaseBuildWaiters();
341
386
  if (this.#healthTimer) clearInterval(this.#healthTimer);
342
387
  this.#healthTimer = null;
343
388
  await this.#enqueue(async () => {
@@ -356,6 +401,7 @@ var DevService = class extends EventEmitter {
356
401
  kill() {
357
402
  this.#stopped = true;
358
403
  this.#clearRestartTimer();
404
+ this.#releaseBuildWaiters();
359
405
  this.supervisor.kill();
360
406
  this.typechecker.close();
361
407
  this.tunnel.close();
@@ -387,7 +433,7 @@ var DevService = class extends EventEmitter {
387
433
  this.typechecker.check();
388
434
  this.log("stars", "success", this.builder.tool === "none" ? "Sources changed" : `Build succeeded in ${outcome.durationMs}ms`);
389
435
  if (this.#paused) return;
390
- if (hot !== null) {
436
+ if (hot !== null && this.#pendingReason === null) {
391
437
  for (const file of hot) this.log("stars", "trace", `UPDATE ${displayPath(this.config.root, file)}`, { channel: "hmr" });
392
438
  this.#settleProgress();
393
439
  this.#emitStatus();
@@ -457,6 +503,7 @@ var DevService = class extends EventEmitter {
457
503
  #setBuild(state) {
458
504
  if (this.#build === state) return;
459
505
  this.#build = state;
506
+ if (state !== "building") this.#releaseBuildWaiters();
460
507
  this.#emitStatus();
461
508
  }
462
509
  async #checkHealth() {
@@ -572,8 +619,10 @@ var DevService = class extends EventEmitter {
572
619
  this.log("stars", "debug", `${verb} ${message.names.join(", ")} from ${relative(message.path)}`, { channel: "hmr" });
573
620
  }
574
621
  /**
575
- * Compares the commands the bot registers with the ones it reported before. The first report of a session is the
576
- * baseline; a later one that differs is acted on as `dev.commands.refresh` says.
622
+ * Compares the commands the bot registers with the ones Discord is assumed to have. The first report of a session is
623
+ * the baseline; a later one that differs from the report before is acted on as `dev.commands.refresh` says, with
624
+ * what differs from the deployed commands rather than from that report: a restart that caught the build half written
625
+ * reports a part of the commands, and the next one reports them all again, which is no change at all.
577
626
  */
578
627
  #onCommands(snapshot) {
579
628
  const previous = this.#commands;
@@ -591,8 +640,16 @@ var DevService = class extends EventEmitter {
591
640
  });
592
641
  return;
593
642
  }
594
- const changes = diffSnapshots(previous, snapshot);
595
- if (changes.length === 0) return;
643
+ if (diffSnapshots(previous, snapshot).length === 0) return;
644
+ const changes = diffSnapshots(this.#deployed ?? previous, snapshot);
645
+ if (changes.length === 0) {
646
+ if (this.#prompt !== null) {
647
+ this.#prompt = null;
648
+ this.log("stars", "info", "The commands match the deployed ones again", { channel: "commands" });
649
+ this.#emitStatus();
650
+ }
651
+ return;
652
+ }
596
653
  const detail = changes.map((change) => `${change.kind} ${describeCommand(change)}`);
597
654
  const mode = this.config.dev.commands?.refresh ?? "prompt";
598
655
  if (mode === "auto") {
@@ -612,7 +669,7 @@ var DevService = class extends EventEmitter {
612
669
  }
613
670
  this.#prompt = {
614
671
  kind: "commands",
615
- changes: mergeChanges(this.#prompt?.changes ?? [], changes)
672
+ changes
616
673
  };
617
674
  this.log("stars", "info", "Commands updated", {
618
675
  channel: "commands",
@@ -698,13 +755,6 @@ const BRIDGE_LEVELS = /* @__PURE__ */ new Set([
698
755
  function toLogLevel(level) {
699
756
  return BRIDGE_LEVELS.has(level) ? level : "info";
700
757
  }
701
- /** The latest change of each command wins: `added` then `changed` is still one line of the prompt. */
702
- function mergeChanges(previous, next) {
703
- const key = (change) => `${change.guild ?? ""}:${change.type}:${change.name}`;
704
- const merged = new Map(previous.map((change) => [key(change), change]));
705
- for (const change of next) merged.set(key(change), change);
706
- return [...merged.values()];
707
- }
708
758
  function describeReason(reason) {
709
759
  switch (reason) {
710
760
  case "initial": return "first build";
@@ -980,4 +1030,4 @@ var dev_default = defineCommand({
980
1030
 
981
1031
  //#endregion
982
1032
  export { dev_default as default };
983
- //# sourceMappingURL=dev-Vv2-8hUB.js.map
1033
+ //# sourceMappingURL=dev-BLlgQwl6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-BLlgQwl6.js","names":[],"sources":["../src/dev/changed-files.ts","../src/dev/dev-service.ts","../src/dev/host.ts","../src/dev/log-file.ts","../src/commands/dev.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { basename, extname, join, sep } from 'node:path';\n\nconst SKIPPED_DIRECTORIES = new Set(['node_modules', '.git']);\n/**\n * `tsc -b` rewrites its build info on every build, whatever changed; a source map only ever changes with the file it\n * maps, and is most of what there would be to read.\n */\nconst SKIPPED_FILES = /\\.(?:tsbuildinfo|map)$/;\n\n/** The content hash of every file under the roots, by absolute path. */\nexport type FileSnapshot = ReadonlyMap<string, string>;\n\n/** What was last read of each file, so one that was not touched since is not read again. */\nexport type HashCache = Map<string, { size: number; mtimeMs: number; hash: string }>;\n\n/**\n * Hashes every file under `roots`, skipping `node_modules`, `.git` and everything under `exclude`.\n *\n * The builders only report that a build finished, and a bundler rewrites files it did not change (`tsdown` writes\n * its whole output on every rebuild), so neither an event nor a modification time says what a build changed: two\n * snapshots do (see {@link changedFiles}).\n */\nexport function snapshotFiles(roots: readonly string[], exclude: readonly string[] = [], cache: HashCache = new Map()): FileSnapshot {\n\tconst snapshot = new Map<string, string>();\n\tconst visit = (directory: string) => {\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(directory, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tconst path = join(directory, entry.name);\n\t\t\tif (exclude.some((excluded) => isInside(path, excluded))) continue;\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (!SKIPPED_DIRECTORIES.has(entry.name)) visit(path);\n\t\t\t} else if (entry.isFile() && !SKIPPED_FILES.test(entry.name)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { size, mtimeMs } = statSync(path);\n\t\t\t\t\tconst known = cache.get(path);\n\t\t\t\t\tconst hash =\n\t\t\t\t\t\tknown?.size === size && known.mtimeMs === mtimeMs\n\t\t\t\t\t\t\t? known.hash\n\t\t\t\t\t\t\t: createHash('sha1').update(readFileSync(path)).digest('base64');\n\t\t\t\t\tcache.set(path, { size, mtimeMs, hash });\n\t\t\t\t\tsnapshot.set(path, hash);\n\t\t\t\t} catch {\n\t\t\t\t\t// Removed between the listing and the read: the next build reports it.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tfor (const root of new Set(roots)) visit(root);\n\treturn snapshot;\n}\n\n/** The files that were added, removed, or whose content differs between two snapshots. */\nexport function changedFiles(previous: FileSnapshot, next: FileSnapshot): string[] {\n\tconst changed = [...next].filter(([path, hash]) => previous.get(path) !== hash).map(([path]) => path);\n\tfor (const path of previous.keys()) {\n\t\tif (!next.has(path)) changed.push(path);\n\t}\n\n\treturn changed;\n}\n\nconst PIECE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts']);\n\n/**\n * Whether a file a store has not loaded yet could be a piece, by the rule the stores filter files with: a script\n * that is not a declaration file and whose name does not start with `_`.\n */\nexport function couldBePiece(path: string): boolean {\n\tconst extension = extname(path);\n\treturn PIECE_EXTENSIONS.has(extension) && !path.endsWith('.d.ts') && !basename(path, extension).startsWith('_');\n}\n\n/** Whether `path` is `directory` itself or lies under it. */\nexport function isInside(path: string, directory: string): boolean {\n\treturn path === directory || path.startsWith(directory.endsWith(sep) ? directory : `${directory}${sep}`);\n}\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from '../builders/types.js';\nimport { displayPath, type ResolvedStarsConfig, type StarsHooks, type StarsRestartReason } from '@wolfstar/schema';\nimport { describeCommand, diffSnapshots, type CommandChange } from '../utils/command-diff.js';\nimport { envImportArgs, moduleImportArgs } from '../utils/env-import.js';\nimport type { StarsHookable } from '../utils/hooks.js';\nimport { classifyAppLine, LogBuffer, type LogLevel, type LogSource } from '../utils/log-buffer.js';\nimport { Locales } from '../utils/locales.js';\nimport { ProcessSupervisor, type ProcessExit, type ProcessState } from '../utils/process-supervisor.js';\nimport { bridgeImportArgs, CLI_SOURCE, parseBridgeMessage, type BridgeMessage, type CommandSnapshot, type RefreshRequest } from './bridge.js';\nimport { changedFiles, couldBePiece, isInside, snapshotFiles, type FileSnapshot, type HashCache } from './changed-files.js';\nimport { Tunnel, type TunnelState } from './tunnel.js';\nimport { Typechecker, type TypecheckState } from './typechecker.js';\n\nexport type BuildState = 'idle' | 'building' | 'ok' | 'failed';\nexport type HealthState = 'unknown' | 'ok' | 'down';\nexport type RestartReason = StarsRestartReason;\n\n/** A question the dev UI asks the user; {@link DevService.answerPrompt} settles it. */\nexport interface DevPrompt {\n\treadonly kind: 'commands';\n\treadonly changes: readonly CommandChange[];\n}\n\nexport interface LogExtra {\n\t/** The channel to log on instead of the source's own. */\n\tchannel?: string;\n\tdetail?: readonly string[];\n}\n\nexport interface DevStatus {\n\treadonly progress: { readonly fraction: number; readonly message: string; readonly startedAt: number; readonly readyMs: number | null };\n\treadonly process: ProcessState;\n\treadonly build: BuildState;\n\treadonly health: HealthState;\n\treadonly pid: number | null;\n\treadonly startedAt: number | null;\n\treadonly restarts: number;\n\treadonly lastRestartReason: RestartReason | null;\n\treadonly lastBuild: BuildOutcome | null;\n\treadonly lastExit: ProcessExit | null;\n\treadonly url: string | null;\n\treadonly typecheck: TypecheckState;\n\treadonly typeErrors: number;\n\treadonly tunnel: TunnelState;\n\treadonly tunnelUrl: string | null;\n\t/** Whether the running bot reported that it is listening (see the bridge); always `false` without the bridge. */\n\treadonly ready: boolean;\n\t/** The port the bot reported it listens on. */\n\treadonly port: number | null;\n\t/** Whether the running bot reported that it hot reloads its pieces. */\n\treadonly hmr: boolean;\n\t/** Whether the bot was stopped on purpose (the `d` key) and waits for a restart. */\n\treadonly paused: boolean;\n\treadonly prompt: DevPrompt | null;\n}\n\nexport interface DevServiceEvents {\n\tstatus: [status: DevStatus];\n}\n\nexport interface DevServiceOptions {\n\tbuilder: Builder;\n\t/** The `stars.config` hooks; `build:*` and `dev:restart` run from here. */\n\thooks?: StarsHookable;\n\tlogs?: LogBuffer;\n\t/** Overrides for tests. */\n\tsupervisor?: ProcessSupervisor;\n\ttypechecker?: Typechecker;\n\ttunnel?: Tunnel;\n\thealthInterval?: number;\n\t/**\n\t * Whether the tunnel opens at start: `true` opens it whatever `dev.tunnel` says (a quick tunnel when it is off),\n\t * `false` keeps it closed, and `undefined` leaves it to `dev.tunnel`. The `--tunnel`/`--no-tunnel` flags.\n\t */\n\topenTunnel?: boolean;\n}\n\n/**\n * The headless heart of `stars dev`: builds, (re)starts the bot and exposes state and logs.\n * Renderers (plain or TUI) only subscribe to it, they never own behaviour.\n */\nexport class DevService extends EventEmitter<DevServiceEvents> {\n\tpublic readonly logs: LogBuffer;\n\tpublic readonly builder: Builder;\n\tpublic readonly supervisor: ProcessSupervisor;\n\tpublic readonly typechecker: Typechecker;\n\tpublic readonly tunnel: Tunnel;\n\tpublic readonly locales: Locales;\n\t/** Whether something can answer a {@link DevPrompt}: the interactive UI sets it, the plain renderer cannot. */\n\tpublic promptable = false;\n\treadonly #hooks: StarsHookable | null;\n\treadonly #openTunnel: boolean | undefined;\n\t/** Runs hooks one at a time, so an async `build:done` settles before the next build's hooks or a restart. */\n\t#hookChain: Promise<void> = Promise.resolve();\n\n\t#build: BuildState = 'idle';\n\t#health: HealthState = 'unknown';\n\t#restarts = 0;\n\t#lastRestartReason: RestartReason | null = null;\n\t#lastBuild: BuildOutcome | null = null;\n\t#lastExit: ProcessExit | null = null;\n\t#restartTimer: NodeJS.Timeout | null = null;\n\t#healthTimer: NodeJS.Timeout | null = null;\n\t/** The restart a build asked for that has not happened yet: it outlives the timer, which the next build start clears. */\n\t#pendingReason: RestartReason | null = null;\n\t#stopped = false;\n\t#queue: Promise<void> = Promise.resolve();\n\t#progress = { fraction: 0, message: 'preparing your app', startedAt: Date.now(), readyMs: null as number | null };\n\t#ready = false;\n\t#port: number | null = null;\n\t/** The store paths the running bot watches for hot reloads, empty when it does not. */\n\t#hmrPaths: string[] = [];\n\t#paused = false;\n\t#prompt: DevPrompt | null = null;\n\t/** The commands the bot last reported, kept across restarts: a restart is how most changes arrive. */\n\t#commands: CommandSnapshot | null = null;\n\t/** What the build output held after the previous build, `null` before the first one. */\n\t#files: FileSnapshot | null = null;\n\treadonly #hashes: HashCache = new Map();\n\t/** The files the running bot loaded pieces from: what its hot reload can replace. */\n\t#pieces = new Set<string>();\n\t/** The commands Discord is assumed to have: the first report of the session, then whatever was last deployed. */\n\t#deployed: CommandSnapshot | null = null;\n\t/** A refresh of the commands asked for while the bot was not listening; sent once it is. */\n\t#refreshPending = false;\n\t/** Restarts waiting for the running build to end, see {@link DevService.#buildSettled}. */\n\t#buildWaiters: Array<() => void> = [];\n\n\tpublic constructor(\n\t\tpublic readonly config: ResolvedStarsConfig,\n\t\toptions: DevServiceOptions\n\t) {\n\t\tsuper();\n\t\tthis.logs = options.logs ?? new LogBuffer();\n\t\tthis.builder = options.builder;\n\t\tthis.supervisor = options.supervisor ?? createSupervisor(config);\n\t\tthis.typechecker = options.typechecker ?? new Typechecker(config);\n\t\tthis.tunnel = options.tunnel ?? new Tunnel(config);\n\t\tthis.locales = new Locales(config);\n\t\tthis.#hooks = options.hooks ?? null;\n\t\tthis.#openTunnel = options.openTunnel;\n\n\t\tthis.builder.on('start', () => {\n\t\t\tthis.#progress = { fraction: 0, message: 'preparing build', startedAt: Date.now(), readyMs: null };\n\t\t\t// A restart armed by the previous build must not fire while this one rewrites the output; it stays owed.\n\t\t\tthis.#clearRestartTimer();\n\t\t\tthis.#setBuild('building');\n\t\t\tvoid this.#startHook('build:before', this.config);\n\t\t});\n\t\tthis.builder.on('progress', (fraction, message) => {\n\t\t\tthis.#progress = { ...this.#progress, fraction: Math.max(this.#progress.fraction, Math.min(0.75, fraction)), message };\n\t\t\tthis.#emitStatus();\n\t\t});\n\t\tthis.builder.on('success', (outcome) => this.#onBuildSuccess(outcome));\n\t\tthis.builder.on('failure', (outcome) => this.#onBuildFailure(outcome));\n\t\tthis.builder.on('log', (level, text) => this.log('build', level, text));\n\n\t\tthis.supervisor.on('state', () => {\n\t\t\tif (this.supervisor.state === 'running') {\n\t\t\t\tthis.#settleProgress();\n\t\t\t\tvoid this.#checkHealth();\n\t\t\t}\n\t\t\tthis.#emitStatus();\n\t\t});\n\t\tthis.supervisor.on('stdout', (line) => this.log('app', classifyAppLine(line, 'info'), line));\n\t\tthis.supervisor.on('stderr', (line) => this.log('app', classifyAppLine(line, 'error'), line));\n\t\tthis.supervisor.on('message', (message) => {\n\t\t\tconst parsed = parseBridgeMessage(message);\n\t\t\tif (parsed === null) return;\n\t\t\ttry {\n\t\t\t\tthis.#onBridge(parsed);\n\t\t\t} catch (error) {\n\t\t\t\t// Whatever the bot sends, it must not take the session down.\n\t\t\t\tthis.log('stars', 'warn', `Ignored a ${parsed.type} message from the bot: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t}\n\t\t});\n\t\tthis.supervisor.on('error', (error) => this.log('stars', 'error', `Failed to start the bot: ${error.message}`));\n\t\tthis.supervisor.on('exit', (exit) => this.#onExit(exit));\n\n\t\tthis.typechecker.on('log', (level, text) => this.log('tsc', level, text));\n\t\tthis.typechecker.on('state', () => this.#emitStatus());\n\t\tthis.tunnel.on('log', (level, text) => this.log('tunnel', level, text));\n\t\tthis.tunnel.on('state', () => this.#emitStatus());\n\t\tthis.locales.on('log', (level, text) => this.log('build', level, text));\n\t\tthis.locales.on('change', () => {\n\t\t\tif (this.#build === 'ok') this.#scheduleRestart('build');\n\t\t});\n\n\t\tif (config.dev.url && config.dev.health) {\n\t\t\tconst interval = options.healthInterval ?? 5000;\n\t\t\tthis.#healthTimer = setInterval(() => void this.#checkHealth(), interval);\n\t\t\tthis.#healthTimer.unref();\n\t\t}\n\t}\n\n\tpublic get status(): DevStatus {\n\t\treturn {\n\t\t\tprogress: this.#progress,\n\t\t\tprocess: this.supervisor.state,\n\t\t\tbuild: this.#build,\n\t\t\thealth: this.#health,\n\t\t\tpid: this.supervisor.pid,\n\t\t\tstartedAt: this.supervisor.startedAt,\n\t\t\trestarts: this.#restarts,\n\t\t\tlastRestartReason: this.#lastRestartReason,\n\t\t\tlastBuild: this.#lastBuild,\n\t\t\tlastExit: this.#lastExit,\n\t\t\turl: this.config.dev.url,\n\t\t\ttypecheck: this.typechecker.state,\n\t\t\ttypeErrors: this.typechecker.errors,\n\t\t\ttunnel: this.tunnel.state,\n\t\t\ttunnelUrl: this.tunnel.url,\n\t\t\tready: this.#ready,\n\t\t\tport: this.#port,\n\t\t\thmr: this.#hmrPaths.length > 0,\n\t\t\tpaused: this.#paused,\n\t\t\tprompt: this.#prompt\n\t\t};\n\t}\n\n\tpublic log(source: LogSource, level: LogLevel, text: string, extra: LogExtra = {}): void {\n\t\tthis.logs.push({ source, level, text, ...extra });\n\t}\n\n\t/**\n\t * Starts watching; the bot starts after the first successful build.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tthis.#stopped = false;\n\t\tconst entry = displayPath(this.config.root, this.config.entry);\n\t\tthis.log('stars', 'info', this.config.build.tool === 'none' ? `Watching ${entry}` : `Watching ${entry} with ${this.config.build.tool}`);\n\t\tif (this.config.dev.typecheck.enabled) this.typechecker.start();\n\t\tawait this.locales.watch();\n\t\t// The tunnel comes up next to the build: neither waits for the other, and a failed tunnel never stops the bot.\n\t\t// `--no-tunnel` only keeps it closed at start: the `t` key still opens one.\n\t\tif (this.#openTunnel !== false) void this.tunnel.start(this.#openTunnel === true);\n\t\tawait this.builder.watch();\n\t}\n\n\t/**\n\t * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).\n\t */\n\tpublic restart(reason: RestartReason = 'manual'): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped) return;\n\t\t\tawait this.#queueHook('dev:restart', reason, this.config);\n\t\t\tif (this.#stopped) return;\n\t\t\tif (!(await this.#buildSettled(reason))) return;\n\t\t\tif (reason === 'manual' || reason === 'crash') {\n\t\t\t\tthis.#progress = { fraction: 0, message: 'restarting the bot', startedAt: Date.now(), readyMs: null };\n\t\t\t}\n\t\t\tthis.#lastRestartReason = reason;\n\t\t\tif (this.supervisor.running) {\n\t\t\t\tthis.#restarts++;\n\t\t\t\tthis.log('stars', 'info', `Restarting (${describeReason(reason)})`);\n\t\t\t\tawait this.supervisor.stop();\n\t\t\t} else {\n\t\t\t\tthis.log('stars', 'info', `Starting (${describeReason(reason)})`);\n\t\t\t}\n\n\t\t\t// Stopping the bot takes a while, and a build may have begun meanwhile.\n\t\t\tif (this.#stopped || !(await this.#buildSettled(reason))) return;\n\t\t\t// The bot that starts now runs the latest output, so no restart is owed any more.\n\t\t\tthis.#pendingReason = null;\n\t\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#paused = false;\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/**\n\t * Stops the bot and leaves it stopped (the `d` key): builds keep running, but nothing starts the bot again until\n\t * {@link restart}. For when the bot must be off without ending the session.\n\t */\n\tpublic disconnect(): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped || this.#paused) return;\n\t\t\tthis.#paused = true;\n\t\t\tthis.log('stars', 'info', 'Stopped the bot, press r to start it again');\n\t\t\tawait this.supervisor.stop();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/** Settles the pending {@link DevPrompt}: `true` does what it asks, `false` dismisses it. */\n\tpublic answerPrompt(accept: boolean): void {\n\t\tconst prompt = this.#prompt;\n\t\tif (prompt === null) return;\n\t\t// The question stays open: the answer is not given up, only the redeploy is not safe yet.\n\t\tif (accept && this.#refreshBlocked()) return;\n\t\tthis.#prompt = null;\n\t\tif (accept) this.refreshCommands();\n\t\telse this.log('stars', 'info', 'Kept the deployed commands as they are', { channel: 'commands' });\n\t\tthis.#emitStatus();\n\t}\n\n\t/**\n\t * Whether a refresh has to wait, saying why. While a build rewrites the output a bot that starts loads a part of the\n\t * commands, and registering that would unregister the rest from Discord.\n\t */\n\t#refreshBlocked(): boolean {\n\t\tif (this.#build !== 'building') return false;\n\t\tthis.log('stars', 'warn', 'A build is running, the commands of the bot may be incomplete: answer again once it has finished', {\n\t\t\tchannel: 'commands'\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * Resolves once no build is running, `false` when the restart is not worth doing any more: the session stopped, or\n\t * an automatic restart (`build`, `initial`) had to wait for a build, whose own end already decides what happens next\n\t * (a restart if it succeeded, nothing if it failed: the output would be the previous, possibly half written one).\n\t * The dropped restart stays owed in {@link DevService.#pendingReason}, so that build's end restarts the bot even when\n\t * it could be hot reloaded: the change the dropped restart was for is not part of what that build rewrote.\n\t * A manual restart or one after a crash is still what was asked for.\n\t */\n\tasync #buildSettled(reason: RestartReason): Promise<boolean> {\n\t\tconst automatic = reason === 'build' || reason === 'initial';\n\t\tlet waited = false;\n\t\twhile (this.#build === 'building' && !this.#stopped) {\n\t\t\twaited = true;\n\t\t\tawait new Promise<void>((resolve) => this.#buildWaiters.push(resolve));\n\t\t}\n\n\t\tif (this.#stopped) return false;\n\t\tif (waited && automatic) {\n\t\t\tthis.#pendingReason ??= reason;\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t#releaseBuildWaiters(): void {\n\t\tconst waiters = this.#buildWaiters;\n\t\tthis.#buildWaiters = [];\n\t\tfor (const resolve of waiters) resolve();\n\t}\n\n\t/** Asks the bot to register its commands with Discord again. The bot answers on the `commands` channel. */\n\tpublic refreshCommands(): void {\n\t\tif (this.#refreshBlocked()) return;\n\t\t// A bot that is stopped or restarting has no client to register anything with: the refresh waits for it to listen.\n\t\tif (!this.#ready) {\n\t\t\tthis.#refreshPending = true;\n\t\t\tconst when = this.supervisor.running ? 'once the bot is ready' : 'when the bot starts again';\n\t\t\tthis.log('stars', 'info', `Refreshing commands ${when}…`, { channel: 'commands' });\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#refreshPending = false;\n\t\tconst current = this.#commands?.guilds ?? {};\n\t\tconst request: RefreshRequest = {\n\t\t\tsource: CLI_SOURCE,\n\t\t\ttype: 'commands:refresh',\n\t\t\tclearGuilds: Object.keys(this.#deployed?.guilds ?? {}).filter((guild) => !Object.hasOwn(current, guild))\n\t\t};\n\t\tif (this.supervisor.send(request)) {\n\t\t\tthis.log('stars', 'info', 'Refreshing commands…', { channel: 'commands' });\n\t\t} else {\n\t\t\tthis.log('stars', 'warn', 'Could not reach the bot to refresh its commands', { channel: 'commands' });\n\t\t}\n\t}\n\n\t/** Opens or closes the public tunnel (used by the `t` key). */\n\tpublic toggleTunnel(): Promise<void> {\n\t\treturn this.tunnel.toggle();\n\t}\n\n\t/**\n\t * Stops the watcher and the bot. Safe to call more than once.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\t// A restart waiting for a build must not hold the queue that closes the session.\n\t\tthis.#releaseBuildWaiters();\n\t\tif (this.#healthTimer) clearInterval(this.#healthTimer);\n\t\tthis.#healthTimer = null;\n\t\tawait this.#enqueue(async () => {\n\t\t\tawait Promise.allSettled([\n\t\t\t\tthis.builder.close(),\n\t\t\t\tthis.supervisor.stop(),\n\t\t\t\tthis.typechecker.close(),\n\t\t\t\tthis.tunnel.close(),\n\t\t\t\tthis.locales.close()\n\t\t\t]);\n\t\t});\n\t}\n\n\t/**\n\t * Kills the bot and the helper processes without waiting for them. Only for a forced shutdown, {@link stop} is the graceful path.\n\t */\n\tpublic kill(): void {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tthis.#releaseBuildWaiters();\n\t\tthis.supervisor.kill();\n\t\tvoid this.typechecker.close();\n\t\tvoid this.tunnel.close();\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\t// Before the locales are copied: the copy rewrites every file, which is not what the build changed.\n\t\tconst hot = this.#hotReloadable();\n\t\ttry {\n\t\t\tthis.locales.copy();\n\t\t} catch (error) {\n\t\t\tthis.#onBuildFailure({\n\t\t\t\t...outcome,\n\t\t\t\tok: false,\n\t\t\t\tmessage: `Failed to copy locales: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\tthis.#setBuild('ok');\n\t\t// Only once locales are copied: until then the build is not done, and may still fail.\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\n\t\tif (this.#stopped) return;\n\t\t// A checker without a watch mode (`tsz`) only knows about the change once the build is through.\n\t\tthis.typechecker.check();\n\t\tthis.log('stars', 'success', this.builder.tool === 'none' ? 'Sources changed' : `Build succeeded in ${outcome.durationMs}ms`);\n\t\tif (this.#paused) return;\n\t\t// A restart an earlier build asked for (and a build start cleared) is still owed: its change is not in `hot`.\n\t\tif (hot !== null && this.#pendingReason === null) {\n\t\t\tfor (const file of hot) this.log('stars', 'trace', `UPDATE ${displayPath(this.config.root, file)}`, { channel: 'hmr' });\n\t\t\tthis.#settleProgress();\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\t\tthis.#scheduleRestart(this.supervisor.state === 'idle' ? 'initial' : 'build');\n\t}\n\n\t/**\n\t * The files the build rewrote when all of them are pieces the running bot hot reloads, `null` when the bot has to\n\t * be restarted instead: it is not running, it does not hot reload, `dev.hmr` is off, nothing changed (a rebuild for\n\t * a reason that cannot be seen from the output), or a file changed that the bot cannot replace. What a build changed\n\t * is the difference in content with the previous output, since a bundler rewrites files it did not change.\n\t *\n\t * The bot replaces the files it loaded pieces from, and loads a new file that could be one. A helper module next to\n\t * the pieces (`_shared.js`, or any file no piece came from) is imported once and stays as it was: changing it\n\t * needs a restart, even though it lies in a store path.\n\t */\n\t#hotReloadable(): string[] | null {\n\t\tif (this.config.dev.hmr === false) return null;\n\n\t\tconst roots = this.config.build.tool === 'none' ? this.config.dev.watch : [this.config.build.outDir];\n\t\tconst previous = this.#files;\n\t\tthis.#files = snapshotFiles(roots, [this.locales.destination], this.#hashes);\n\t\tif (previous === null || this.#hmrPaths.length === 0 || this.supervisor.state !== 'running') return null;\n\n\t\tconst changed = changedFiles(previous, this.#files);\n\t\tconst replaceable = (file: string) =>\n\t\t\tthis.#hmrPaths.some((path) => isInside(file, path)) && (this.#pieces.has(file) || (!previous.has(file) && couldBePiece(file)));\n\t\treturn changed.length > 0 && changed.every(replaceable) ? changed : null;\n\t}\n\n\t#onBuildFailure(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\n\t\tthis.#setBuild('failed');\n\t\tthis.#clearRestartTimer();\n\t\tthis.log('stars', 'error', `Build failed${outcome.message ? `: ${outcome.message}` : ''}, waiting for changes`);\n\t}\n\n\t#onExit(exit: ProcessExit): void {\n\t\tthis.#lastExit = exit;\n\t\tthis.#health = 'unknown';\n\t\tthis.#ready = false;\n\t\tthis.#port = null;\n\t\tthis.#hmrPaths = [];\n\t\tthis.#pieces.clear();\n\t\t// A pending prompt stays: the commands still differ from what Discord has, and the next process can deploy them.\n\t\tif (!exit.requested) {\n\t\t\tconst how = exit.signal ? `signal ${exit.signal}` : `code ${exit.code}`;\n\t\t\tthis.log('stars', exit.code === 0 ? 'warn' : 'error', `The bot exited with ${how}, waiting for changes (press r to restart)`);\n\t\t}\n\t\tthis.#emitStatus();\n\t}\n\n\t#scheduleRestart(reason: RestartReason): void {\n\t\tif (this.#paused) return;\n\t\tthis.#pendingReason = reason;\n\t\tthis.#clearRestartTimer();\n\t\tthis.#restartTimer = setTimeout(() => {\n\t\t\tthis.#restartTimer = null;\n\t\t\tconst pending = this.#pendingReason ?? reason;\n\t\t\tthis.#pendingReason = null;\n\t\t\tvoid this.restart(pending);\n\t\t}, this.config.dev.debounce);\n\t}\n\n\t#clearRestartTimer(): void {\n\t\tif (this.#restartTimer) clearTimeout(this.#restartTimer);\n\t\tthis.#restartTimer = null;\n\t}\n\n\t#enqueue(task: () => Promise<void>): Promise<void> {\n\t\tthis.#queue = this.#queue.then(task, task);\n\t\treturn this.#queue;\n\t}\n\n\t#setBuild(state: BuildState): void {\n\t\tif (this.#build === state) return;\n\t\tthis.#build = state;\n\t\tif (state !== 'building') this.#releaseBuildWaiters();\n\t\tthis.#emitStatus();\n\t}\n\n\tasync #checkHealth(): Promise<void> {\n\t\tif (!this.supervisor.running || !this.config.dev.url || !this.config.dev.health) return;\n\t\tconst pid = this.supervisor.pid;\n\n\t\tlet next: HealthState;\n\t\ttry {\n\t\t\tconst response = await fetch(new URL(this.config.dev.health, this.config.dev.url), { signal: AbortSignal.timeout(2000) });\n\t\t\tnext = response.status < 500 ? 'ok' : 'down';\n\t\t} catch {\n\t\t\tnext = 'down';\n\t\t}\n\n\t\tif (this.#stopped || pid !== this.supervisor.pid) return;\n\t\tif (next !== this.#health) {\n\t\t\tthis.#health = next;\n\t\t\tif (next === 'ok') this.#settleProgress();\n\t\t\tthis.#emitStatus();\n\t\t}\n\t}\n\n\t#settleProgress(): void {\n\t\tif (this.config.dev.health && this.#health !== 'ok') return;\n\t\tif (this.#build !== 'ok') return;\n\t\tthis.#progress = {\n\t\t\t...this.#progress,\n\t\t\tfraction: 1,\n\t\t\tmessage: 'watching for changes',\n\t\t\treadyMs: this.#progress.readyMs ?? Date.now() - this.#progress.startedAt\n\t\t};\n\t}\n\n\t/** Turns what the bot reports over the bridge into log entries and status. */\n\t#onBridge(message: BridgeMessage): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tswitch (message.type) {\n\t\t\tcase 'ready':\n\t\t\t\tthis.#ready = true;\n\t\t\t\tthis.#port = message.port;\n\t\t\t\tthis.log('stars', 'success', message.port === null ? 'The bot is ready' : `Listening on port ${message.port}`, {\n\t\t\t\t\tchannel: 'lifecycle'\n\t\t\t\t});\n\t\t\t\tif (this.#refreshPending) this.refreshCommands();\n\t\t\t\tthis.#emitStatus();\n\t\t\t\treturn;\n\t\t\tcase 'pieces':\n\t\t\t\tfor (const piece of message.pieces) this.#pieces.add(piece.path);\n\t\t\t\tif (message.pieces.length === 0) return;\n\t\t\t\tthis.log('stars', 'debug', `Loaded ${message.store}: ${message.pieces.length} ${message.pieces.length === 1 ? 'piece' : 'pieces'}`, {\n\t\t\t\t\tchannel: message.store === 'commands' ? 'commands' : 'lifecycle',\n\t\t\t\t\tdetail: message.pieces.map((piece) => `${piece.name} ${relative(piece.path)}`)\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'hmr':\n\t\t\t\tthis.#onHmr(message);\n\t\t\t\treturn;\n\t\t\tcase 'commands':\n\t\t\t\tthis.#onCommands({ global: message.global, guilds: message.guilds });\n\t\t\t\treturn;\n\t\t\tcase 'dispatch':\n\t\t\t\tif (message.phase === 'run') {\n\t\t\t\t\tthis.log('stars', 'debug', `Processing ${message.route} with ${message.piece}`, { channel: 'interactions' });\n\t\t\t\t} else if (message.phase === 'success') {\n\t\t\t\t\tthis.log('stars', 'trace', `${message.route} handled${formatMs(message.ms)}`, { channel: 'interactions' });\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `${message.route} failed${formatMs(message.ms)}: ${message.error.message}`, {\n\t\t\t\t\t\tchannel: 'interactions',\n\t\t\t\t\t\tdetail: message.error.stack\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'request':\n\t\t\t\tthis.log('stars', message.status >= 500 ? 'error' : message.status >= 400 ? 'warn' : 'trace', formatRequest(message), {\n\t\t\t\t\tchannel: 'http'\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'refreshed':\n\t\t\t\tif (message.ok) {\n\t\t\t\t\tthis.#deployed = this.#commands;\n\t\t\t\t\tconst guilds =\n\t\t\t\t\t\t(message.guilds > 0 ? `, ${message.guilds} guild ${message.guilds === 1 ? 'group' : 'groups'}` : '') +\n\t\t\t\t\t\t(message.cleared > 0 ? `, cleared ${message.cleared} ${message.cleared === 1 ? 'guild' : 'guilds'}` : '');\n\t\t\t\t\tthis.log('stars', 'success', `Deployed ${message.global} global ${message.global === 1 ? 'command' : 'commands'}${guilds}`, {\n\t\t\t\t\t\tchannel: 'commands'\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `Could not refresh the commands: ${message.message}`, { channel: 'commands', detail: message.stack });\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'log':\n\t\t\t\tthis.log('app', toLogLevel(message.level), message.text, { channel: message.channel, detail: message.detail });\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t#onHmr(message: Extract<BridgeMessage, { type: 'hmr' }>): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tif (message.event === 'start') {\n\t\t\tthis.#hmrPaths = message.paths;\n\t\t\tthis.log('stars', 'debug', `Hot reload on, watching ${message.paths.length} ${message.paths.length === 1 ? 'path' : 'paths'}`, {\n\t\t\t\tchannel: 'hmr',\n\t\t\t\tdetail: message.paths.map(relative)\n\t\t\t});\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'stop') {\n\t\t\t// Nothing in the bot watches any more: the next change needs a restart again.\n\t\t\tthis.#hmrPaths = [];\n\t\t\tthis.log('stars', 'debug', 'Hot reload off', { channel: 'hmr' });\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'error') {\n\t\t\tthis.log('stars', 'error', `Could not reload ${relative(message.path)}: ${message.message}`, { channel: 'hmr', detail: message.stack });\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'unloaded') this.#pieces.delete(message.path);\n\t\telse this.#pieces.add(message.path);\n\t\tconst verb = message.event === 'reloaded' ? 'Reloaded' : message.event === 'loaded' ? 'Loaded' : 'Unloaded';\n\t\tthis.log('stars', 'debug', `${verb} ${message.names.join(', ')} from ${relative(message.path)}`, { channel: 'hmr' });\n\t}\n\n\t/**\n\t * Compares the commands the bot registers with the ones Discord is assumed to have. The first report of a session is\n\t * the baseline; a later one that differs from the report before is acted on as `dev.commands.refresh` says, with\n\t * what differs from the deployed commands rather than from that report: a restart that caught the build half written\n\t * reports a part of the commands, and the next one reports them all again, which is no change at all.\n\t */\n\t#onCommands(snapshot: CommandSnapshot): void {\n\t\tconst previous = this.#commands;\n\t\tthis.#commands = snapshot;\n\t\tif (previous === null) {\n\t\t\tthis.#deployed = snapshot;\n\t\t\tconst guilds = Object.keys(snapshot.guilds).length;\n\t\t\tthis.log('stars', 'debug', `Loaded commands: ${snapshot.global.length} global, ${guilds} guild ${guilds === 1 ? 'group' : 'groups'}`, {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: snapshot.global.map((command) => describeCommand({ name: command.name, type: command.type ?? 1, guild: null }))\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// The same report again (a restart for a file that is not a command) has nothing new to say.\n\t\tif (diffSnapshots(previous, snapshot).length === 0) return;\n\n\t\tconst changes = diffSnapshots(this.#deployed ?? previous, snapshot);\n\t\tif (changes.length === 0) {\n\t\t\tif (this.#prompt !== null) {\n\t\t\t\tthis.#prompt = null;\n\t\t\t\tthis.log('stars', 'info', 'The commands match the deployed ones again', { channel: 'commands' });\n\t\t\t\tthis.#emitStatus();\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst detail = changes.map((change) => `${change.kind} ${describeCommand(change)}`);\n\t\tconst mode = this.config.dev.commands?.refresh ?? 'prompt';\n\t\tif (mode === 'auto') {\n\t\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\t\tthis.refreshCommands();\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode === 'off' || !this.promptable) {\n\t\t\tthis.log('stars', 'warn', 'Commands updated, Discord still has the previous ones', {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: [...detail, mode === 'off' ? 'dev.commands.refresh is off' : 'Set dev.commands.refresh to \"auto\" to redeploy without asking']\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// A second change before the first was answered asks once, about all of it: `changes` already holds both.\n\t\tthis.#prompt = { kind: 'commands', changes };\n\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\tthis.#emitStatus();\n\t}\n\n\t/**\n\t * Runs a hook behind the ones already running (see {@link DevService.#hookChain}), e.g. `dev:close` so shutdown\n\t * waits for an unfinished `build:done`.\n\t */\n\tpublic runHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\treturn this.#queueHook(name, ...args);\n\t}\n\n\t/** Queues a hook behind the ones already running; see {@link DevService.#hookChain}. */\n\t#queueHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tthis.#hookChain = this.#hookChain.then(() => this.#callHook(name, ...args));\n\t\treturn this.#hookChain;\n\t}\n\n\t/**\n\t * Starts a hook right away, at the moment it describes, instead of queueing it: `build:before` belongs to the\n\t * builder's `start` boundary, and a builder such as `none` finishes in the same call stack. Its synchronous part\n\t * runs here; later hooks still wait for the rest.\n\t */\n\t#startHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tconst running = this.#callHook(name, ...args);\n\t\tthis.#hookChain = Promise.all([this.#hookChain, running]).then(() => undefined);\n\t\treturn running;\n\t}\n\n\t/**\n\t * Runs a `stars.config` hook. A failing hook is logged rather than thrown: a broken hook must never stop the\n\t * watcher or keep the bot from restarting.\n\t */\n\tasync #callHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tif (this.#hooks === null) return;\n\t\ttry {\n\t\t\tawait this.#hooks.callHook(name, ...args);\n\t\t} catch (error) {\n\t\t\tthis.log('stars', 'error', `Hook ${name} failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\t}\n\n\t#emitStatus(): void {\n\t\tthis.emit('status', this.status);\n\t}\n}\n\nexport function createSupervisor(config: ResolvedStarsConfig): ProcessSupervisor {\n\treturn new ProcessSupervisor({\n\t\tcommand: process.execPath,\n\t\targs: [\n\t\t\t...envImportArgs(config),\n\t\t\t...moduleImportArgs(config),\n\t\t\t...bridgeImportArgs(config),\n\t\t\t...config.dev.nodeArgs,\n\t\t\tconfig.build.output,\n\t\t\t...config.dev.args\n\t\t],\n\t\tcwd: config.root,\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...config.dev.env,\n\t\t\tSTARS_DEV: '1',\n\t\t\tNODE_ENV: 'development',\n\t\t\tFORCE_COLOR: process.env.NO_COLOR !== undefined ? undefined : (process.env.FORCE_COLOR ?? (process.stdout.isTTY ? '1' : undefined))\n\t\t},\n\t\tkillTimeout: config.dev.killTimeout,\n\t\tipc: true\n\t});\n}\n\nfunction formatMs(ms: number | null): string {\n\treturn ms === null ? '' : ` in ${ms}ms`;\n}\n\nfunction formatRequest(message: Extract<BridgeMessage, { type: 'request' }>): string {\n\treturn `${message.method} ${message.path} ${message.status} in ${message.ms}ms`;\n}\n\nconst BRIDGE_LEVELS = new Set<string>(['trace', 'debug', 'info', 'success', 'warn', 'error']);\n\nfunction toLogLevel(level: string): LogLevel {\n\treturn BRIDGE_LEVELS.has(level) ? (level as LogLevel) : 'info';\n}\n\nexport function describeReason(reason: RestartReason): string {\n\tswitch (reason) {\n\t\tcase 'initial':\n\t\t\treturn 'first build';\n\t\tcase 'build':\n\t\t\treturn 'sources changed';\n\t\tcase 'manual':\n\t\t\treturn 'manual restart';\n\t\tcase 'crash':\n\t\t\treturn 'after crash';\n\t}\n}\n","import { lookup } from 'node:dns/promises';\n\n/**\n * Resolves `localhost` the way Vite's dev server does: Node's default DNS result order (`dns.lookup('localhost')`)\n * can disagree with the OS's own, \"verbatim\" resolution order — a well known source of a dev URL that prints fine\n * but is not the address the bot actually bound to (e.g. an IPv6-first system where the server listens on IPv4\n * only). When the two disagree this returns the OS's own address instead of the literal `localhost` hostname.\n */\nexport async function resolveLocalhost(): Promise<string> {\n\ttry {\n\t\tconst [ordered, verbatim] = await Promise.all([lookup('localhost'), lookup('localhost', { verbatim: true })]);\n\t\treturn ordered.family === verbatim.family && ordered.address === verbatim.address ? 'localhost' : verbatim.address;\n\t} catch {\n\t\treturn 'localhost';\n\t}\n}\n\n/**\n * Replaces a `localhost` hostname in `url` with {@link resolveLocalhost}'s result. Any other hostname (an explicit\n * `dev.url` override, a LAN address, …) is returned untouched.\n */\nexport async function withResolvedLocalhost(url: string): Promise<string> {\n\tconst parsed = new URL(url);\n\tif (parsed.hostname !== 'localhost') return url;\n\n\tconst host = await resolveLocalhost();\n\tif (host === 'localhost') return url;\n\n\tparsed.hostname = host.includes(':') ? `[${host}]` : host;\n\treturn parsed.href;\n}\n","import { createWriteStream, mkdirSync, readdirSync, rmSync, type WriteStream } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { stripVTControlCharacters } from 'node:util';\nimport type { LogBuffer, LogEntry } from '../utils/log-buffer.js';\n\n/**\n * Mirrors a dev session's logs into a file, so a run can be read back after the terminal UI is gone (the TUI takes\n * over the alternate screen and its scrollback disappears on exit). `dev.logFile` is a single file truncated on every\n * run; `dev.logs.dir` adds one file per run (see {@link runLogFile}).\n */\nexport class LogFileWriter {\n\t#stream: WriteStream | null = null;\n\n\tpublic constructor(\n\t\tpublic readonly file: string,\n\t\tprivate readonly logs: LogBuffer\n\t) {}\n\n\tpublic open(): void {\n\t\tif (this.#stream) return;\n\n\t\tmkdirSync(dirname(this.file), { recursive: true });\n\t\tthis.#stream = createWriteStream(this.file, { flags: 'w' });\n\t\t// A broken pipe or a read-only directory must never take the dev session down with it.\n\t\tthis.#stream.on('error', () => this.close());\n\n\t\tfor (const entry of this.logs.entries()) this.#write(entry);\n\t\tthis.logs.on('entry', this.#write);\n\t}\n\n\tpublic close(): void {\n\t\tconst stream = this.#stream;\n\t\tthis.#stream = null;\n\t\tif (!stream) return;\n\n\t\tthis.logs.off('entry', this.#write);\n\t\tstream.end();\n\t}\n\n\t#write = (entry: LogEntry): void => {\n\t\tthis.#stream?.write(`${formatLogLine(entry)}\\n`);\n\t};\n}\n\n/** One line per entry, then its detail lines indented under it. Never filtered: the file is the whole session. */\nexport function formatLogLine(entry: LogEntry): string {\n\tconst head = `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.channel.padEnd(12)} ${stripVTControlCharacters(entry.text)}`;\n\treturn [head, ...(entry.detail ?? []).map((line) => ` ${stripVTControlCharacters(line)}`)].join('\\n');\n}\n\nconst RUN_FILE = /^dev-.+\\.log$/;\n\n/**\n * The file of this run inside `dev.logs.dir` (`dev-<timestamp>.log`), after deleting the oldest ones so the directory\n * holds at most `keep` files with the new one. The names sort by time, so no file needs to be opened to order them.\n */\nexport function runLogFile(directory: string, keep: number, now: Date = new Date()): string {\n\tlet existing: string[] = [];\n\ttry {\n\t\texisting = readdirSync(directory)\n\t\t\t.filter((name) => RUN_FILE.test(name))\n\t\t\t.sort();\n\t} catch {\n\t\t// The directory does not exist yet: nothing to prune.\n\t}\n\n\tfor (const name of existing.slice(0, Math.max(0, existing.length - Math.max(0, keep - 1)))) {\n\t\trmSync(join(directory, name), { force: true });\n\t}\n\n\treturn join(directory, `dev-${now.toISOString().replaceAll(':', '-')}.log`);\n}\n","import type { ResolvedStarsConfig } from '@wolfstar/schema';\nimport { defineCommand } from 'citty';\nimport { createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev/dev-service.js';\nimport { withResolvedLocalhost } from '../dev/host.js';\nimport { LogFileWriter, runLogFile } from '../dev/log-file.js';\nimport { initialLogView, isLogLevel } from '../dev/tui/log-view.js';\nimport { collectFlag, projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport { ExitCode, renderCrashReport } from '../utils/errors.js';\nimport { applyEnvOptions, loadProject, withProjectEnv } from '../utils/hooks.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../utils/output-mode.js';\nimport { THEME_SETTINGS, isThemeSetting, readSavedTheme, resolveThemeSetting, saveTheme } from '../utils/theme.js';\nimport { prepareProject, reportWarnings } from './_shared.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n\t/** `--theme`. */\n\ttheme?: string;\n\t/** `--layout`. */\n\tlayout?: string;\n\t/** `--channel`, each value possibly a comma-separated list. */\n\tchannel?: string[];\n\t/** `--level`. */\n\tlevel?: string;\n\t/** `--tunnel` opens the tunnel at start whatever `dev.tunnel` says, `--no-tunnel` keeps it closed. */\n\ttunnel?: boolean;\n\t/** `--no-mouse` leaves the mouse to the terminal whatever `dev.mouse` says. */\n\tmouse?: boolean;\n}\n\nconst LAYOUTS = ['auto', 'dashboard', 'panel'] as const;\nconst LEVELS = ['trace', 'debug', 'info', 'warn', 'error'] as const;\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tif (options.theme !== undefined && !isThemeSetting(options.theme)) {\n\t\tthrow cliDiagnostics.INVALID_THEME({ theme: options.theme, themes: THEME_SETTINGS.join(', ') });\n\t}\n\n\tconst layout = options.layout as (typeof LAYOUTS)[number] | undefined;\n\tif (layout !== undefined && !LAYOUTS.includes(layout)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--layout', value: options.layout!, allowed: LAYOUTS.join(', ') });\n\t}\n\n\tif (options.level !== undefined && !isLogLevel(options.level)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--level', value: options.level, allowed: LEVELS.join(', ') });\n\t}\n\n\t// Dev mode applies to config evaluation, build plugins, and the supervised application — not only the child.\n\tprocess.env.NODE_ENV = 'development';\n\tconst project = await loadProject({ cwd: resolveCwd(options), configFile: options.config, env: { ...process.env, NODE_ENV: 'development' } });\n\tconst hooks = project.hooks;\n\t// `env:options` first: it may pick env files with another `HTTP_PORT`, which `dev.url` is derived from.\n\tconst config = await resolveDevConfig(withProjectEnv(await applyEnvOptions(project.config, hooks)));\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\tconst theme = resolveThemeSetting({ flag: options.theme, saved: readSavedTheme() });\n\n\tconst builder = await createBuilder(config, hooks);\n\tawait hooks.callHook('builder:created', builder, config);\n\tconst service = new DevService(config, { builder, hooks, openTunnel: options.tunnel });\n\tawait reportWarnings(config, (text) => service.log('stars', 'warn', text), { production: false });\n\t// The files receive every entry: the filter below only decides what the terminal shows.\n\tconst logFiles = [\n\t\t...(config.dev.logFile ? [config.dev.logFile] : []),\n\t\t...(config.dev.logs?.dir ? [runLogFile(config.dev.logs.dir, config.dev.logs.keep)] : [])\n\t].map((file) => new LogFileWriter(file, service.logs));\n\tfor (const file of logFiles) file.open();\n\tconst filter = initialLogView(config, { channels: options.channel, level: options.level });\n\tconst renderer =\n\t\tmode === 'tui'\n\t\t\t? (await import('../dev/tui/tui.js')).createTuiRenderer(service, {\n\t\t\t\t\tcolor,\n\t\t\t\t\ttheme,\n\t\t\t\t\tlayout,\n\t\t\t\t\tfilter,\n\t\t\t\t\tmouse: options.mouse,\n\t\t\t\t\treducedMotion: prefersReducedMotion(),\n\t\t\t\t\tonThemeSave: (setting) => {\n\t\t\t\t\t\tif (!saveTheme(setting)) service.log('stars', 'warn', 'Could not save the theme preference.');\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t: (await import('../dev/tui/plain.js')).createPlainRenderer(service, { color, filter });\n\n\tlet exiting: Promise<never> | null = null;\n\tconst shutdown = (code: ExitCode): Promise<never> => {\n\t\texiting ??= (async () => {\n\t\t\trenderer.stop();\n\t\t\t// A failing hook must not keep the bot running.\n\t\t\t// Behind any hook still running (an async `build:done`), and logged rather than thrown if it fails.\n\t\t\tawait service.runHook('dev:close', config);\n\t\t\tawait service.stop();\n\t\t\tfor (const file of logFiles) file.close();\n\t\t\tprocess.exit(code);\n\t\t})();\n\t\treturn exiting;\n\t};\n\n\t// Like Turborepo: the first signal shuts down gracefully, a second one gives up waiting and kills everything.\n\tlet signalled = false;\n\tconst onSignal = (code: ExitCode) => () => {\n\t\tif (signalled) {\n\t\t\tprocess.stderr.write('\\nForcing shutdown.\\n');\n\t\t\tservice.kill();\n\t\t\tprocess.exit(code);\n\t\t}\n\n\t\tsignalled = true;\n\t\tvoid shutdown(code);\n\t\tprocess.stderr.write('\\nShutting down gracefully, press Ctrl+C again to force quit.\\n');\n\t};\n\n\tprocess.on('SIGINT', onSignal(ExitCode.Interrupted));\n\tprocess.on('SIGTERM', onSignal(ExitCode.Terminated));\n\tprocess.on('SIGHUP', onSignal(ExitCode.Terminated));\n\tif (process.platform !== 'win32') process.on('SIGUSR2', () => void service.restart('manual'));\n\n\tconst finished = renderer.start().then(() => shutdown(ExitCode.Ok));\n\ttry {\n\t\tawait prepareProject(config, hooks);\n\t\tawait service.start();\n\t\tawait hooks.callHook('dev:start', config);\n\t} catch (error) {\n\t\tservice.log('stars', 'error', error instanceof Error ? await renderCrashReport(error, config.root) : String(error));\n\t\tawait shutdown(ExitCode.Error);\n\t}\n\tawait finished;\n}\n\n/**\n * Resolves `dev.url`'s `localhost` to the address that is actually reachable (see {@link withResolvedLocalhost}),\n * the way Vite's dev server does when it starts. Only `stars dev` pays this DNS lookup; `stars info`/`stars build`\n * show or use the unresolved config as-is, since they never talk to the bot.\n */\nasync function resolveDevConfig(config: ResolvedStarsConfig): Promise<ResolvedStarsConfig> {\n\tif (!config.dev.url) return config;\n\n\tconst url = await withResolvedLocalhost(config.dev.url);\n\treturn url === config.dev.url ? config : { ...config, dev: { ...config.dev, url } };\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'dev',\n\t\tdescription: 'Build, run and restart the bot on changes'\n\t},\n\targs: {\n\t\t...projectArgs,\n\t\ttui: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Interactive terminal UI; use --no-tui (or STARS_TUI=plain) for plain line output',\n\t\t\tdefault: true\n\t\t},\n\t\ttheme: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Colour theme of the interactive UI: ${THEME_SETTINGS.join(', ')} (or STARS_THEME; press T in the UI to pick one)`\n\t\t},\n\t\tlayout: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Layout of the interactive UI: ${LAYOUTS.join(', ')} (defaults to dev.layout; press v in the UI to switch)`\n\t\t},\n\t\tchannel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Only show these log channels at start (repeatable, or comma-separated; defaults to dev.logs.channels)'\n\t\t},\n\t\tlevel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Only show this log level and the more severe ones at start: ${LEVELS.join(', ')} (defaults to dev.logs.levels)`\n\t\t},\n\t\t// No default: left out, `dev.tunnel` decides.\n\t\ttunnel: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Open the public tunnel at start, whatever dev.tunnel says',\n\t\t\tnegativeDescription: 'Keep the public tunnel closed at start even when dev.tunnel enables it'\n\t\t},\n\t\t// No default: left out, `dev.mouse` decides.\n\t\tmouse: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Clickable dashboard and wheel scrolling (defaults to dev.mouse)',\n\t\t\tnegativeDescription: 'Leave the mouse to the terminal even when dev.mouse is on'\n\t\t}\n\t},\n\tasync run({ args, rawArgs }) {\n\t\tconst channel = collectFlag(rawArgs, 'channel');\n\t\tawait runDev({\n\t\t\tconfig: args.config,\n\t\t\tcwd: args.cwd,\n\t\t\ttui: args.tui ? undefined : false,\n\t\t\ttheme: args.theme,\n\t\t\tlayout: args.layout,\n\t\t\tchannel: channel.length > 0 ? channel : undefined,\n\t\t\tlevel: args.level,\n\t\t\ttunnel: args.tunnel,\n\t\t\tmouse: args.mouse\n\t\t});\n\t}\n});\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as runMain } from "./run-BkfjbY7l.js";
1
+ import { t as runMain } from "./run-Bs6e9_6S.js";
2
2
  import { i as shouldUseColor, n as prefersReducedMotion, r as resolveOutputMode, t as isCIEnvironment } from "./output-mode-Bp7Da8vJ.js";
3
3
  import { n as exitCodeOf, r as formatError, t as ExitCode } from "./errors-COFWKKKw.js";
4
4
  import { t as cliDiagnostics } from "./diagnostics-Dn3CXa8J.js";
@@ -1,4 +1,4 @@
1
- import { t as commands } from "./commands-BiY_99sF.js";
1
+ import { t as commands } from "./commands-D9i-ZgpA.js";
2
2
  import { t as readOwnPackageJson } from "./version-BzclCE19.js";
3
3
  import { n as exitCodeOf, r as formatError } from "./errors-COFWKKKw.js";
4
4
  import { defineCommand, renderUsage, runCommand } from "citty";
@@ -52,4 +52,4 @@ async function runMain(argv = process.argv.slice(2)) {
52
52
 
53
53
  //#endregion
54
54
  export { runMain as t };
55
- //# sourceMappingURL=run-BkfjbY7l.js.map
55
+ //# sourceMappingURL=run-Bs6e9_6S.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-BkfjbY7l.js","names":["_runCommand"],"sources":["../src/main.ts","../src/run.ts"],"sourcesContent":["import { defineCommand, type CommandDef } from 'citty';\nimport { commands } from './commands/index.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nconst packageJson = readOwnPackageJson();\n\nexport const main = defineCommand({\n\tmeta: {\n\t\tname: 'stars',\n\t\tversion: packageJson.version,\n\t\tdescription: packageJson.description\n\t},\n\tsubCommands: commands\n}) as AnyCommand;\n","import { renderUsage, runCommand as _runCommand, type CommandDef } from 'citty';\nimport process from 'node:process';\nimport { commands } from './commands/index.js';\nimport { main } from './main.js';\nimport { exitCodeOf, formatError } from './utils/errors.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\n/**\n * Walks the command names of `stars <command> [<subcommand>] --help` so nested commands (`stars commands clean`)\n * render their own usage instead of their parent's.\n */\nasync function resolveHelpTarget(names: string[]): Promise<AnyCommand | null> {\n\tconst [first, ...rest] = names;\n\tif (!first || !Object.hasOwn(commands, first)) return null;\n\n\tlet command = await commands[first]!();\n\tfor (const name of rest) {\n\t\tconst resolved = typeof command.subCommands === 'function' ? await command.subCommands() : await command.subCommands;\n\t\tconst child: unknown = (resolved as Record<string, unknown> | undefined)?.[name];\n\t\tif (!child) break;\n\t\tcommand = (typeof child === 'function' ? await child() : child) as AnyCommand;\n\t}\n\n\treturn command;\n}\n\nexport async function runMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n\tif (argv.includes('--version') || argv.includes('-V')) {\n\t\tconsole.log(readOwnPackageJson().version);\n\t\tprocess.exit(0);\n\t}\n\n\tif (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {\n\t\t// citty's showUsage logs through consola, which is muted in some environments; render and print directly.\n\t\tconst command = await resolveHelpTarget(argv.filter((argument) => !argument.startsWith('-')));\n\t\tconsole.log(await (command ? renderUsage(command, main) : renderUsage(main)));\n\t\tprocess.exit(0);\n\t}\n\n\ttry {\n\t\tawait _runCommand(main, { rawArgs: argv });\n\t} catch (error) {\n\t\tconsole.error(await formatError(error));\n\t\tprocess.exit(exitCodeOf(error));\n\t}\n}\n"],"mappings":";;;;;;;AAQA,MAAM,cAAc,mBAAmB;AAEvC,MAAa,OAAO,cAAc;CACjC,MAAM;EACL,MAAM;EACN,SAAS,YAAY;EACrB,aAAa,YAAY;CAC1B;CACA,aAAa;AACd,CAAC;;;;;;;;ACFD,eAAe,kBAAkB,OAA6C;CAC7E,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,UAAU,KAAK,GAAG,OAAO;CAEtD,IAAI,UAAU,MAAM,SAAS,MAAM,CAAE;CACrC,KAAK,MAAM,QAAQ,MAAM;EAExB,MAAM,SADW,OAAO,QAAQ,gBAAgB,aAAa,MAAM,QAAQ,YAAY,IAAI,MAAM,QAAQ,YACzE,GAA2C;EAC3E,IAAI,CAAC,OAAO;EACZ,UAAW,OAAO,UAAU,aAAa,MAAM,MAAM,IAAI;CAC1D;CAEA,OAAO;AACR;AAEA,eAAsB,QAAQ,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAkB;CACpF,IAAI,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;EACtD,QAAQ,IAAI,mBAAmB,CAAC,CAAC,OAAO;EACxC,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;EAExE,MAAM,UAAU,MAAM,kBAAkB,KAAK,QAAQ,aAAa,CAAC,SAAS,WAAW,GAAG,CAAC,CAAC;EAC5F,QAAQ,IAAI,OAAO,UAAU,YAAY,SAAS,IAAI,IAAI,YAAY,IAAI,EAAE;EAC5E,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI;EACH,MAAMA,WAAY,MAAM,EAAE,SAAS,KAAK,CAAC;CAC1C,SAAS,OAAO;EACf,QAAQ,MAAM,MAAM,YAAY,KAAK,CAAC;EACtC,QAAQ,KAAK,WAAW,KAAK,CAAC;CAC/B;AACD"}
1
+ {"version":3,"file":"run-Bs6e9_6S.js","names":["_runCommand"],"sources":["../src/main.ts","../src/run.ts"],"sourcesContent":["import { defineCommand, type CommandDef } from 'citty';\nimport { commands } from './commands/index.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nconst packageJson = readOwnPackageJson();\n\nexport const main = defineCommand({\n\tmeta: {\n\t\tname: 'stars',\n\t\tversion: packageJson.version,\n\t\tdescription: packageJson.description\n\t},\n\tsubCommands: commands\n}) as AnyCommand;\n","import { renderUsage, runCommand as _runCommand, type CommandDef } from 'citty';\nimport process from 'node:process';\nimport { commands } from './commands/index.js';\nimport { main } from './main.js';\nimport { exitCodeOf, formatError } from './utils/errors.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\n/**\n * Walks the command names of `stars <command> [<subcommand>] --help` so nested commands (`stars commands clean`)\n * render their own usage instead of their parent's.\n */\nasync function resolveHelpTarget(names: string[]): Promise<AnyCommand | null> {\n\tconst [first, ...rest] = names;\n\tif (!first || !Object.hasOwn(commands, first)) return null;\n\n\tlet command = await commands[first]!();\n\tfor (const name of rest) {\n\t\tconst resolved = typeof command.subCommands === 'function' ? await command.subCommands() : await command.subCommands;\n\t\tconst child: unknown = (resolved as Record<string, unknown> | undefined)?.[name];\n\t\tif (!child) break;\n\t\tcommand = (typeof child === 'function' ? await child() : child) as AnyCommand;\n\t}\n\n\treturn command;\n}\n\nexport async function runMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n\tif (argv.includes('--version') || argv.includes('-V')) {\n\t\tconsole.log(readOwnPackageJson().version);\n\t\tprocess.exit(0);\n\t}\n\n\tif (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {\n\t\t// citty's showUsage logs through consola, which is muted in some environments; render and print directly.\n\t\tconst command = await resolveHelpTarget(argv.filter((argument) => !argument.startsWith('-')));\n\t\tconsole.log(await (command ? renderUsage(command, main) : renderUsage(main)));\n\t\tprocess.exit(0);\n\t}\n\n\ttry {\n\t\tawait _runCommand(main, { rawArgs: argv });\n\t} catch (error) {\n\t\tconsole.error(await formatError(error));\n\t\tprocess.exit(exitCodeOf(error));\n\t}\n}\n"],"mappings":";;;;;;;AAQA,MAAM,cAAc,mBAAmB;AAEvC,MAAa,OAAO,cAAc;CACjC,MAAM;EACL,MAAM;EACN,SAAS,YAAY;EACrB,aAAa,YAAY;CAC1B;CACA,aAAa;AACd,CAAC;;;;;;;;ACFD,eAAe,kBAAkB,OAA6C;CAC7E,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,UAAU,KAAK,GAAG,OAAO;CAEtD,IAAI,UAAU,MAAM,SAAS,MAAM,CAAE;CACrC,KAAK,MAAM,QAAQ,MAAM;EAExB,MAAM,SADW,OAAO,QAAQ,gBAAgB,aAAa,MAAM,QAAQ,YAAY,IAAI,MAAM,QAAQ,YACzE,GAA2C;EAC3E,IAAI,CAAC,OAAO;EACZ,UAAW,OAAO,UAAU,aAAa,MAAM,MAAM,IAAI;CAC1D;CAEA,OAAO;AACR;AAEA,eAAsB,QAAQ,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAkB;CACpF,IAAI,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;EACtD,QAAQ,IAAI,mBAAmB,CAAC,CAAC,OAAO;EACxC,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;EAExE,MAAM,UAAU,MAAM,kBAAkB,KAAK,QAAQ,aAAa,CAAC,SAAS,WAAW,GAAG,CAAC,CAAC;EAC5F,QAAQ,IAAI,OAAO,UAAU,YAAY,SAAS,IAAI,IAAI,YAAY,IAAI,EAAE;EAC5E,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI;EACH,MAAMA,WAAY,MAAM,EAAE,SAAS,KAAK,CAAC;CAC1C,SAAS,OAAO;EACf,QAAQ,MAAM,MAAM,YAAY,KAAK,CAAC;EACtC,QAAQ,KAAK,WAAW,KAAK,CAAC;CAC/B;AACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wolfstar/cli",
3
- "version": "2.6.0",
3
+ "version": "2.6.1-next-20261007182959",
4
4
  "description": "The stars command line interface for @wolfstar/http-framework projects: typed configuration, dev server, build and project info",
5
5
  "keywords": [
6
6
  "api",
@@ -1,3 +0,0 @@
1
- import { t as commands } from "./commands-BiY_99sF.js";
2
-
3
- export { commands };
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev-Vv2-8hUB.js","names":[],"sources":["../src/dev/changed-files.ts","../src/dev/dev-service.ts","../src/dev/host.ts","../src/dev/log-file.ts","../src/commands/dev.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { basename, extname, join, sep } from 'node:path';\n\nconst SKIPPED_DIRECTORIES = new Set(['node_modules', '.git']);\n/**\n * `tsc -b` rewrites its build info on every build, whatever changed; a source map only ever changes with the file it\n * maps, and is most of what there would be to read.\n */\nconst SKIPPED_FILES = /\\.(?:tsbuildinfo|map)$/;\n\n/** The content hash of every file under the roots, by absolute path. */\nexport type FileSnapshot = ReadonlyMap<string, string>;\n\n/** What was last read of each file, so one that was not touched since is not read again. */\nexport type HashCache = Map<string, { size: number; mtimeMs: number; hash: string }>;\n\n/**\n * Hashes every file under `roots`, skipping `node_modules`, `.git` and everything under `exclude`.\n *\n * The builders only report that a build finished, and a bundler rewrites files it did not change (`tsdown` writes\n * its whole output on every rebuild), so neither an event nor a modification time says what a build changed: two\n * snapshots do (see {@link changedFiles}).\n */\nexport function snapshotFiles(roots: readonly string[], exclude: readonly string[] = [], cache: HashCache = new Map()): FileSnapshot {\n\tconst snapshot = new Map<string, string>();\n\tconst visit = (directory: string) => {\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(directory, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tconst path = join(directory, entry.name);\n\t\t\tif (exclude.some((excluded) => isInside(path, excluded))) continue;\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (!SKIPPED_DIRECTORIES.has(entry.name)) visit(path);\n\t\t\t} else if (entry.isFile() && !SKIPPED_FILES.test(entry.name)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { size, mtimeMs } = statSync(path);\n\t\t\t\t\tconst known = cache.get(path);\n\t\t\t\t\tconst hash =\n\t\t\t\t\t\tknown?.size === size && known.mtimeMs === mtimeMs\n\t\t\t\t\t\t\t? known.hash\n\t\t\t\t\t\t\t: createHash('sha1').update(readFileSync(path)).digest('base64');\n\t\t\t\t\tcache.set(path, { size, mtimeMs, hash });\n\t\t\t\t\tsnapshot.set(path, hash);\n\t\t\t\t} catch {\n\t\t\t\t\t// Removed between the listing and the read: the next build reports it.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tfor (const root of new Set(roots)) visit(root);\n\treturn snapshot;\n}\n\n/** The files that were added, removed, or whose content differs between two snapshots. */\nexport function changedFiles(previous: FileSnapshot, next: FileSnapshot): string[] {\n\tconst changed = [...next].filter(([path, hash]) => previous.get(path) !== hash).map(([path]) => path);\n\tfor (const path of previous.keys()) {\n\t\tif (!next.has(path)) changed.push(path);\n\t}\n\n\treturn changed;\n}\n\nconst PIECE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts']);\n\n/**\n * Whether a file a store has not loaded yet could be a piece, by the rule the stores filter files with: a script\n * that is not a declaration file and whose name does not start with `_`.\n */\nexport function couldBePiece(path: string): boolean {\n\tconst extension = extname(path);\n\treturn PIECE_EXTENSIONS.has(extension) && !path.endsWith('.d.ts') && !basename(path, extension).startsWith('_');\n}\n\n/** Whether `path` is `directory` itself or lies under it. */\nexport function isInside(path: string, directory: string): boolean {\n\treturn path === directory || path.startsWith(directory.endsWith(sep) ? directory : `${directory}${sep}`);\n}\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from '../builders/types.js';\nimport { displayPath, type ResolvedStarsConfig, type StarsHooks, type StarsRestartReason } from '@wolfstar/schema';\nimport { describeCommand, diffSnapshots, type CommandChange } from '../utils/command-diff.js';\nimport { envImportArgs, moduleImportArgs } from '../utils/env-import.js';\nimport type { StarsHookable } from '../utils/hooks.js';\nimport { classifyAppLine, LogBuffer, type LogLevel, type LogSource } from '../utils/log-buffer.js';\nimport { Locales } from '../utils/locales.js';\nimport { ProcessSupervisor, type ProcessExit, type ProcessState } from '../utils/process-supervisor.js';\nimport { bridgeImportArgs, CLI_SOURCE, parseBridgeMessage, type BridgeMessage, type CommandSnapshot, type RefreshRequest } from './bridge.js';\nimport { changedFiles, couldBePiece, isInside, snapshotFiles, type FileSnapshot, type HashCache } from './changed-files.js';\nimport { Tunnel, type TunnelState } from './tunnel.js';\nimport { Typechecker, type TypecheckState } from './typechecker.js';\n\nexport type BuildState = 'idle' | 'building' | 'ok' | 'failed';\nexport type HealthState = 'unknown' | 'ok' | 'down';\nexport type RestartReason = StarsRestartReason;\n\n/** A question the dev UI asks the user; {@link DevService.answerPrompt} settles it. */\nexport interface DevPrompt {\n\treadonly kind: 'commands';\n\treadonly changes: readonly CommandChange[];\n}\n\nexport interface LogExtra {\n\t/** The channel to log on instead of the source's own. */\n\tchannel?: string;\n\tdetail?: readonly string[];\n}\n\nexport interface DevStatus {\n\treadonly progress: { readonly fraction: number; readonly message: string; readonly startedAt: number; readonly readyMs: number | null };\n\treadonly process: ProcessState;\n\treadonly build: BuildState;\n\treadonly health: HealthState;\n\treadonly pid: number | null;\n\treadonly startedAt: number | null;\n\treadonly restarts: number;\n\treadonly lastRestartReason: RestartReason | null;\n\treadonly lastBuild: BuildOutcome | null;\n\treadonly lastExit: ProcessExit | null;\n\treadonly url: string | null;\n\treadonly typecheck: TypecheckState;\n\treadonly typeErrors: number;\n\treadonly tunnel: TunnelState;\n\treadonly tunnelUrl: string | null;\n\t/** Whether the running bot reported that it is listening (see the bridge); always `false` without the bridge. */\n\treadonly ready: boolean;\n\t/** The port the bot reported it listens on. */\n\treadonly port: number | null;\n\t/** Whether the running bot reported that it hot reloads its pieces. */\n\treadonly hmr: boolean;\n\t/** Whether the bot was stopped on purpose (the `d` key) and waits for a restart. */\n\treadonly paused: boolean;\n\treadonly prompt: DevPrompt | null;\n}\n\nexport interface DevServiceEvents {\n\tstatus: [status: DevStatus];\n}\n\nexport interface DevServiceOptions {\n\tbuilder: Builder;\n\t/** The `stars.config` hooks; `build:*` and `dev:restart` run from here. */\n\thooks?: StarsHookable;\n\tlogs?: LogBuffer;\n\t/** Overrides for tests. */\n\tsupervisor?: ProcessSupervisor;\n\ttypechecker?: Typechecker;\n\ttunnel?: Tunnel;\n\thealthInterval?: number;\n\t/**\n\t * Whether the tunnel opens at start: `true` opens it whatever `dev.tunnel` says (a quick tunnel when it is off),\n\t * `false` keeps it closed, and `undefined` leaves it to `dev.tunnel`. The `--tunnel`/`--no-tunnel` flags.\n\t */\n\topenTunnel?: boolean;\n}\n\n/**\n * The headless heart of `stars dev`: builds, (re)starts the bot and exposes state and logs.\n * Renderers (plain or TUI) only subscribe to it, they never own behaviour.\n */\nexport class DevService extends EventEmitter<DevServiceEvents> {\n\tpublic readonly logs: LogBuffer;\n\tpublic readonly builder: Builder;\n\tpublic readonly supervisor: ProcessSupervisor;\n\tpublic readonly typechecker: Typechecker;\n\tpublic readonly tunnel: Tunnel;\n\tpublic readonly locales: Locales;\n\t/** Whether something can answer a {@link DevPrompt}: the interactive UI sets it, the plain renderer cannot. */\n\tpublic promptable = false;\n\treadonly #hooks: StarsHookable | null;\n\treadonly #openTunnel: boolean | undefined;\n\t/** Runs hooks one at a time, so an async `build:done` settles before the next build's hooks or a restart. */\n\t#hookChain: Promise<void> = Promise.resolve();\n\n\t#build: BuildState = 'idle';\n\t#health: HealthState = 'unknown';\n\t#restarts = 0;\n\t#lastRestartReason: RestartReason | null = null;\n\t#lastBuild: BuildOutcome | null = null;\n\t#lastExit: ProcessExit | null = null;\n\t#restartTimer: NodeJS.Timeout | null = null;\n\t#healthTimer: NodeJS.Timeout | null = null;\n\t#pendingReason: RestartReason | null = null;\n\t#stopped = false;\n\t#queue: Promise<void> = Promise.resolve();\n\t#progress = { fraction: 0, message: 'preparing your app', startedAt: Date.now(), readyMs: null as number | null };\n\t#ready = false;\n\t#port: number | null = null;\n\t/** The store paths the running bot watches for hot reloads, empty when it does not. */\n\t#hmrPaths: string[] = [];\n\t#paused = false;\n\t#prompt: DevPrompt | null = null;\n\t/** The commands the bot last reported, kept across restarts: a restart is how most changes arrive. */\n\t#commands: CommandSnapshot | null = null;\n\t/** What the build output held after the previous build, `null` before the first one. */\n\t#files: FileSnapshot | null = null;\n\treadonly #hashes: HashCache = new Map();\n\t/** The files the running bot loaded pieces from: what its hot reload can replace. */\n\t#pieces = new Set<string>();\n\t/** The commands Discord is assumed to have: the first report of the session, then whatever was last deployed. */\n\t#deployed: CommandSnapshot | null = null;\n\t/** A refresh of the commands asked for while the bot was not listening; sent once it is. */\n\t#refreshPending = false;\n\n\tpublic constructor(\n\t\tpublic readonly config: ResolvedStarsConfig,\n\t\toptions: DevServiceOptions\n\t) {\n\t\tsuper();\n\t\tthis.logs = options.logs ?? new LogBuffer();\n\t\tthis.builder = options.builder;\n\t\tthis.supervisor = options.supervisor ?? createSupervisor(config);\n\t\tthis.typechecker = options.typechecker ?? new Typechecker(config);\n\t\tthis.tunnel = options.tunnel ?? new Tunnel(config);\n\t\tthis.locales = new Locales(config);\n\t\tthis.#hooks = options.hooks ?? null;\n\t\tthis.#openTunnel = options.openTunnel;\n\n\t\tthis.builder.on('start', () => {\n\t\t\tthis.#progress = { fraction: 0, message: 'preparing build', startedAt: Date.now(), readyMs: null };\n\t\t\tthis.#setBuild('building');\n\t\t\tvoid this.#startHook('build:before', this.config);\n\t\t});\n\t\tthis.builder.on('progress', (fraction, message) => {\n\t\t\tthis.#progress = { ...this.#progress, fraction: Math.max(this.#progress.fraction, Math.min(0.75, fraction)), message };\n\t\t\tthis.#emitStatus();\n\t\t});\n\t\tthis.builder.on('success', (outcome) => this.#onBuildSuccess(outcome));\n\t\tthis.builder.on('failure', (outcome) => this.#onBuildFailure(outcome));\n\t\tthis.builder.on('log', (level, text) => this.log('build', level, text));\n\n\t\tthis.supervisor.on('state', () => {\n\t\t\tif (this.supervisor.state === 'running') {\n\t\t\t\tthis.#settleProgress();\n\t\t\t\tvoid this.#checkHealth();\n\t\t\t}\n\t\t\tthis.#emitStatus();\n\t\t});\n\t\tthis.supervisor.on('stdout', (line) => this.log('app', classifyAppLine(line, 'info'), line));\n\t\tthis.supervisor.on('stderr', (line) => this.log('app', classifyAppLine(line, 'error'), line));\n\t\tthis.supervisor.on('message', (message) => {\n\t\t\tconst parsed = parseBridgeMessage(message);\n\t\t\tif (parsed === null) return;\n\t\t\ttry {\n\t\t\t\tthis.#onBridge(parsed);\n\t\t\t} catch (error) {\n\t\t\t\t// Whatever the bot sends, it must not take the session down.\n\t\t\t\tthis.log('stars', 'warn', `Ignored a ${parsed.type} message from the bot: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t}\n\t\t});\n\t\tthis.supervisor.on('error', (error) => this.log('stars', 'error', `Failed to start the bot: ${error.message}`));\n\t\tthis.supervisor.on('exit', (exit) => this.#onExit(exit));\n\n\t\tthis.typechecker.on('log', (level, text) => this.log('tsc', level, text));\n\t\tthis.typechecker.on('state', () => this.#emitStatus());\n\t\tthis.tunnel.on('log', (level, text) => this.log('tunnel', level, text));\n\t\tthis.tunnel.on('state', () => this.#emitStatus());\n\t\tthis.locales.on('log', (level, text) => this.log('build', level, text));\n\t\tthis.locales.on('change', () => {\n\t\t\tif (this.#build === 'ok') this.#scheduleRestart('build');\n\t\t});\n\n\t\tif (config.dev.url && config.dev.health) {\n\t\t\tconst interval = options.healthInterval ?? 5000;\n\t\t\tthis.#healthTimer = setInterval(() => void this.#checkHealth(), interval);\n\t\t\tthis.#healthTimer.unref();\n\t\t}\n\t}\n\n\tpublic get status(): DevStatus {\n\t\treturn {\n\t\t\tprogress: this.#progress,\n\t\t\tprocess: this.supervisor.state,\n\t\t\tbuild: this.#build,\n\t\t\thealth: this.#health,\n\t\t\tpid: this.supervisor.pid,\n\t\t\tstartedAt: this.supervisor.startedAt,\n\t\t\trestarts: this.#restarts,\n\t\t\tlastRestartReason: this.#lastRestartReason,\n\t\t\tlastBuild: this.#lastBuild,\n\t\t\tlastExit: this.#lastExit,\n\t\t\turl: this.config.dev.url,\n\t\t\ttypecheck: this.typechecker.state,\n\t\t\ttypeErrors: this.typechecker.errors,\n\t\t\ttunnel: this.tunnel.state,\n\t\t\ttunnelUrl: this.tunnel.url,\n\t\t\tready: this.#ready,\n\t\t\tport: this.#port,\n\t\t\thmr: this.#hmrPaths.length > 0,\n\t\t\tpaused: this.#paused,\n\t\t\tprompt: this.#prompt\n\t\t};\n\t}\n\n\tpublic log(source: LogSource, level: LogLevel, text: string, extra: LogExtra = {}): void {\n\t\tthis.logs.push({ source, level, text, ...extra });\n\t}\n\n\t/**\n\t * Starts watching; the bot starts after the first successful build.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tthis.#stopped = false;\n\t\tconst entry = displayPath(this.config.root, this.config.entry);\n\t\tthis.log('stars', 'info', this.config.build.tool === 'none' ? `Watching ${entry}` : `Watching ${entry} with ${this.config.build.tool}`);\n\t\tif (this.config.dev.typecheck.enabled) this.typechecker.start();\n\t\tawait this.locales.watch();\n\t\t// The tunnel comes up next to the build: neither waits for the other, and a failed tunnel never stops the bot.\n\t\t// `--no-tunnel` only keeps it closed at start: the `t` key still opens one.\n\t\tif (this.#openTunnel !== false) void this.tunnel.start(this.#openTunnel === true);\n\t\tawait this.builder.watch();\n\t}\n\n\t/**\n\t * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).\n\t */\n\tpublic restart(reason: RestartReason = 'manual'): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped) return;\n\t\t\tawait this.#queueHook('dev:restart', reason, this.config);\n\t\t\tif (this.#stopped) return;\n\t\t\tif (reason === 'manual' || reason === 'crash') {\n\t\t\t\tthis.#progress = { fraction: 0, message: 'restarting the bot', startedAt: Date.now(), readyMs: null };\n\t\t\t}\n\t\t\tthis.#lastRestartReason = reason;\n\t\t\tif (this.supervisor.running) {\n\t\t\t\tthis.#restarts++;\n\t\t\t\tthis.log('stars', 'info', `Restarting (${describeReason(reason)})`);\n\t\t\t\tawait this.supervisor.stop();\n\t\t\t} else {\n\t\t\t\tthis.log('stars', 'info', `Starting (${describeReason(reason)})`);\n\t\t\t}\n\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#paused = false;\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/**\n\t * Stops the bot and leaves it stopped (the `d` key): builds keep running, but nothing starts the bot again until\n\t * {@link restart}. For when the bot must be off without ending the session.\n\t */\n\tpublic disconnect(): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped || this.#paused) return;\n\t\t\tthis.#paused = true;\n\t\t\tthis.log('stars', 'info', 'Stopped the bot, press r to start it again');\n\t\t\tawait this.supervisor.stop();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/** Settles the pending {@link DevPrompt}: `true` does what it asks, `false` dismisses it. */\n\tpublic answerPrompt(accept: boolean): void {\n\t\tconst prompt = this.#prompt;\n\t\tif (prompt === null) return;\n\t\tthis.#prompt = null;\n\t\tif (accept) this.refreshCommands();\n\t\telse this.log('stars', 'info', 'Kept the deployed commands as they are', { channel: 'commands' });\n\t\tthis.#emitStatus();\n\t}\n\n\t/** Asks the bot to register its commands with Discord again. The bot answers on the `commands` channel. */\n\tpublic refreshCommands(): void {\n\t\t// A bot that is stopped or restarting has no client to register anything with: the refresh waits for it to listen.\n\t\tif (!this.#ready) {\n\t\t\tthis.#refreshPending = true;\n\t\t\tconst when = this.supervisor.running ? 'once the bot is ready' : 'when the bot starts again';\n\t\t\tthis.log('stars', 'info', `Refreshing commands ${when}…`, { channel: 'commands' });\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#refreshPending = false;\n\t\tconst current = this.#commands?.guilds ?? {};\n\t\tconst request: RefreshRequest = {\n\t\t\tsource: CLI_SOURCE,\n\t\t\ttype: 'commands:refresh',\n\t\t\tclearGuilds: Object.keys(this.#deployed?.guilds ?? {}).filter((guild) => !Object.hasOwn(current, guild))\n\t\t};\n\t\tif (this.supervisor.send(request)) {\n\t\t\tthis.log('stars', 'info', 'Refreshing commands…', { channel: 'commands' });\n\t\t} else {\n\t\t\tthis.log('stars', 'warn', 'Could not reach the bot to refresh its commands', { channel: 'commands' });\n\t\t}\n\t}\n\n\t/** Opens or closes the public tunnel (used by the `t` key). */\n\tpublic toggleTunnel(): Promise<void> {\n\t\treturn this.tunnel.toggle();\n\t}\n\n\t/**\n\t * Stops the watcher and the bot. Safe to call more than once.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tif (this.#healthTimer) clearInterval(this.#healthTimer);\n\t\tthis.#healthTimer = null;\n\t\tawait this.#enqueue(async () => {\n\t\t\tawait Promise.allSettled([\n\t\t\t\tthis.builder.close(),\n\t\t\t\tthis.supervisor.stop(),\n\t\t\t\tthis.typechecker.close(),\n\t\t\t\tthis.tunnel.close(),\n\t\t\t\tthis.locales.close()\n\t\t\t]);\n\t\t});\n\t}\n\n\t/**\n\t * Kills the bot and the helper processes without waiting for them. Only for a forced shutdown, {@link stop} is the graceful path.\n\t */\n\tpublic kill(): void {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tthis.supervisor.kill();\n\t\tvoid this.typechecker.close();\n\t\tvoid this.tunnel.close();\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\t// Before the locales are copied: the copy rewrites every file, which is not what the build changed.\n\t\tconst hot = this.#hotReloadable();\n\t\ttry {\n\t\t\tthis.locales.copy();\n\t\t} catch (error) {\n\t\t\tthis.#onBuildFailure({\n\t\t\t\t...outcome,\n\t\t\t\tok: false,\n\t\t\t\tmessage: `Failed to copy locales: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\tthis.#setBuild('ok');\n\t\t// Only once locales are copied: until then the build is not done, and may still fail.\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\n\t\tif (this.#stopped) return;\n\t\t// A checker without a watch mode (`tsz`) only knows about the change once the build is through.\n\t\tthis.typechecker.check();\n\t\tthis.log('stars', 'success', this.builder.tool === 'none' ? 'Sources changed' : `Build succeeded in ${outcome.durationMs}ms`);\n\t\tif (this.#paused) return;\n\t\tif (hot !== null) {\n\t\t\tfor (const file of hot) this.log('stars', 'trace', `UPDATE ${displayPath(this.config.root, file)}`, { channel: 'hmr' });\n\t\t\tthis.#settleProgress();\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\t\tthis.#scheduleRestart(this.supervisor.state === 'idle' ? 'initial' : 'build');\n\t}\n\n\t/**\n\t * The files the build rewrote when all of them are pieces the running bot hot reloads, `null` when the bot has to\n\t * be restarted instead: it is not running, it does not hot reload, `dev.hmr` is off, nothing changed (a rebuild for\n\t * a reason that cannot be seen from the output), or a file changed that the bot cannot replace. What a build changed\n\t * is the difference in content with the previous output, since a bundler rewrites files it did not change.\n\t *\n\t * The bot replaces the files it loaded pieces from, and loads a new file that could be one. A helper module next to\n\t * the pieces (`_shared.js`, or any file no piece came from) is imported once and stays as it was: changing it\n\t * needs a restart, even though it lies in a store path.\n\t */\n\t#hotReloadable(): string[] | null {\n\t\tif (this.config.dev.hmr === false) return null;\n\n\t\tconst roots = this.config.build.tool === 'none' ? this.config.dev.watch : [this.config.build.outDir];\n\t\tconst previous = this.#files;\n\t\tthis.#files = snapshotFiles(roots, [this.locales.destination], this.#hashes);\n\t\tif (previous === null || this.#hmrPaths.length === 0 || this.supervisor.state !== 'running') return null;\n\n\t\tconst changed = changedFiles(previous, this.#files);\n\t\tconst replaceable = (file: string) =>\n\t\t\tthis.#hmrPaths.some((path) => isInside(file, path)) && (this.#pieces.has(file) || (!previous.has(file) && couldBePiece(file)));\n\t\treturn changed.length > 0 && changed.every(replaceable) ? changed : null;\n\t}\n\n\t#onBuildFailure(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\n\t\tthis.#setBuild('failed');\n\t\tthis.#clearRestartTimer();\n\t\tthis.log('stars', 'error', `Build failed${outcome.message ? `: ${outcome.message}` : ''}, waiting for changes`);\n\t}\n\n\t#onExit(exit: ProcessExit): void {\n\t\tthis.#lastExit = exit;\n\t\tthis.#health = 'unknown';\n\t\tthis.#ready = false;\n\t\tthis.#port = null;\n\t\tthis.#hmrPaths = [];\n\t\tthis.#pieces.clear();\n\t\t// A pending prompt stays: the commands still differ from what Discord has, and the next process can deploy them.\n\t\tif (!exit.requested) {\n\t\t\tconst how = exit.signal ? `signal ${exit.signal}` : `code ${exit.code}`;\n\t\t\tthis.log('stars', exit.code === 0 ? 'warn' : 'error', `The bot exited with ${how}, waiting for changes (press r to restart)`);\n\t\t}\n\t\tthis.#emitStatus();\n\t}\n\n\t#scheduleRestart(reason: RestartReason): void {\n\t\tif (this.#paused) return;\n\t\tthis.#pendingReason = reason;\n\t\tthis.#clearRestartTimer();\n\t\tthis.#restartTimer = setTimeout(() => {\n\t\t\tthis.#restartTimer = null;\n\t\t\tconst pending = this.#pendingReason ?? reason;\n\t\t\tthis.#pendingReason = null;\n\t\t\tvoid this.restart(pending);\n\t\t}, this.config.dev.debounce);\n\t}\n\n\t#clearRestartTimer(): void {\n\t\tif (this.#restartTimer) clearTimeout(this.#restartTimer);\n\t\tthis.#restartTimer = null;\n\t}\n\n\t#enqueue(task: () => Promise<void>): Promise<void> {\n\t\tthis.#queue = this.#queue.then(task, task);\n\t\treturn this.#queue;\n\t}\n\n\t#setBuild(state: BuildState): void {\n\t\tif (this.#build === state) return;\n\t\tthis.#build = state;\n\t\tthis.#emitStatus();\n\t}\n\n\tasync #checkHealth(): Promise<void> {\n\t\tif (!this.supervisor.running || !this.config.dev.url || !this.config.dev.health) return;\n\t\tconst pid = this.supervisor.pid;\n\n\t\tlet next: HealthState;\n\t\ttry {\n\t\t\tconst response = await fetch(new URL(this.config.dev.health, this.config.dev.url), { signal: AbortSignal.timeout(2000) });\n\t\t\tnext = response.status < 500 ? 'ok' : 'down';\n\t\t} catch {\n\t\t\tnext = 'down';\n\t\t}\n\n\t\tif (this.#stopped || pid !== this.supervisor.pid) return;\n\t\tif (next !== this.#health) {\n\t\t\tthis.#health = next;\n\t\t\tif (next === 'ok') this.#settleProgress();\n\t\t\tthis.#emitStatus();\n\t\t}\n\t}\n\n\t#settleProgress(): void {\n\t\tif (this.config.dev.health && this.#health !== 'ok') return;\n\t\tif (this.#build !== 'ok') return;\n\t\tthis.#progress = {\n\t\t\t...this.#progress,\n\t\t\tfraction: 1,\n\t\t\tmessage: 'watching for changes',\n\t\t\treadyMs: this.#progress.readyMs ?? Date.now() - this.#progress.startedAt\n\t\t};\n\t}\n\n\t/** Turns what the bot reports over the bridge into log entries and status. */\n\t#onBridge(message: BridgeMessage): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tswitch (message.type) {\n\t\t\tcase 'ready':\n\t\t\t\tthis.#ready = true;\n\t\t\t\tthis.#port = message.port;\n\t\t\t\tthis.log('stars', 'success', message.port === null ? 'The bot is ready' : `Listening on port ${message.port}`, {\n\t\t\t\t\tchannel: 'lifecycle'\n\t\t\t\t});\n\t\t\t\tif (this.#refreshPending) this.refreshCommands();\n\t\t\t\tthis.#emitStatus();\n\t\t\t\treturn;\n\t\t\tcase 'pieces':\n\t\t\t\tfor (const piece of message.pieces) this.#pieces.add(piece.path);\n\t\t\t\tif (message.pieces.length === 0) return;\n\t\t\t\tthis.log('stars', 'debug', `Loaded ${message.store}: ${message.pieces.length} ${message.pieces.length === 1 ? 'piece' : 'pieces'}`, {\n\t\t\t\t\tchannel: message.store === 'commands' ? 'commands' : 'lifecycle',\n\t\t\t\t\tdetail: message.pieces.map((piece) => `${piece.name} ${relative(piece.path)}`)\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'hmr':\n\t\t\t\tthis.#onHmr(message);\n\t\t\t\treturn;\n\t\t\tcase 'commands':\n\t\t\t\tthis.#onCommands({ global: message.global, guilds: message.guilds });\n\t\t\t\treturn;\n\t\t\tcase 'dispatch':\n\t\t\t\tif (message.phase === 'run') {\n\t\t\t\t\tthis.log('stars', 'debug', `Processing ${message.route} with ${message.piece}`, { channel: 'interactions' });\n\t\t\t\t} else if (message.phase === 'success') {\n\t\t\t\t\tthis.log('stars', 'trace', `${message.route} handled${formatMs(message.ms)}`, { channel: 'interactions' });\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `${message.route} failed${formatMs(message.ms)}: ${message.error.message}`, {\n\t\t\t\t\t\tchannel: 'interactions',\n\t\t\t\t\t\tdetail: message.error.stack\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'request':\n\t\t\t\tthis.log('stars', message.status >= 500 ? 'error' : message.status >= 400 ? 'warn' : 'trace', formatRequest(message), {\n\t\t\t\t\tchannel: 'http'\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'refreshed':\n\t\t\t\tif (message.ok) {\n\t\t\t\t\tthis.#deployed = this.#commands;\n\t\t\t\t\tconst guilds =\n\t\t\t\t\t\t(message.guilds > 0 ? `, ${message.guilds} guild ${message.guilds === 1 ? 'group' : 'groups'}` : '') +\n\t\t\t\t\t\t(message.cleared > 0 ? `, cleared ${message.cleared} ${message.cleared === 1 ? 'guild' : 'guilds'}` : '');\n\t\t\t\t\tthis.log('stars', 'success', `Deployed ${message.global} global ${message.global === 1 ? 'command' : 'commands'}${guilds}`, {\n\t\t\t\t\t\tchannel: 'commands'\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `Could not refresh the commands: ${message.message}`, { channel: 'commands', detail: message.stack });\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'log':\n\t\t\t\tthis.log('app', toLogLevel(message.level), message.text, { channel: message.channel, detail: message.detail });\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t#onHmr(message: Extract<BridgeMessage, { type: 'hmr' }>): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tif (message.event === 'start') {\n\t\t\tthis.#hmrPaths = message.paths;\n\t\t\tthis.log('stars', 'debug', `Hot reload on, watching ${message.paths.length} ${message.paths.length === 1 ? 'path' : 'paths'}`, {\n\t\t\t\tchannel: 'hmr',\n\t\t\t\tdetail: message.paths.map(relative)\n\t\t\t});\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'stop') {\n\t\t\t// Nothing in the bot watches any more: the next change needs a restart again.\n\t\t\tthis.#hmrPaths = [];\n\t\t\tthis.log('stars', 'debug', 'Hot reload off', { channel: 'hmr' });\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'error') {\n\t\t\tthis.log('stars', 'error', `Could not reload ${relative(message.path)}: ${message.message}`, { channel: 'hmr', detail: message.stack });\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'unloaded') this.#pieces.delete(message.path);\n\t\telse this.#pieces.add(message.path);\n\t\tconst verb = message.event === 'reloaded' ? 'Reloaded' : message.event === 'loaded' ? 'Loaded' : 'Unloaded';\n\t\tthis.log('stars', 'debug', `${verb} ${message.names.join(', ')} from ${relative(message.path)}`, { channel: 'hmr' });\n\t}\n\n\t/**\n\t * Compares the commands the bot registers with the ones it reported before. The first report of a session is the\n\t * baseline; a later one that differs is acted on as `dev.commands.refresh` says.\n\t */\n\t#onCommands(snapshot: CommandSnapshot): void {\n\t\tconst previous = this.#commands;\n\t\tthis.#commands = snapshot;\n\t\tif (previous === null) {\n\t\t\tthis.#deployed = snapshot;\n\t\t\tconst guilds = Object.keys(snapshot.guilds).length;\n\t\t\tthis.log('stars', 'debug', `Loaded commands: ${snapshot.global.length} global, ${guilds} guild ${guilds === 1 ? 'group' : 'groups'}`, {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: snapshot.global.map((command) => describeCommand({ name: command.name, type: command.type ?? 1, guild: null }))\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst changes = diffSnapshots(previous, snapshot);\n\t\tif (changes.length === 0) return;\n\n\t\tconst detail = changes.map((change) => `${change.kind} ${describeCommand(change)}`);\n\t\tconst mode = this.config.dev.commands?.refresh ?? 'prompt';\n\t\tif (mode === 'auto') {\n\t\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\t\tthis.refreshCommands();\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode === 'off' || !this.promptable) {\n\t\t\tthis.log('stars', 'warn', 'Commands updated, Discord still has the previous ones', {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: [...detail, mode === 'off' ? 'dev.commands.refresh is off' : 'Set dev.commands.refresh to \"auto\" to redeploy without asking']\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// A second change before the first was answered asks once, about all of it.\n\t\tthis.#prompt = { kind: 'commands', changes: mergeChanges(this.#prompt?.changes ?? [], changes) };\n\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\tthis.#emitStatus();\n\t}\n\n\t/**\n\t * Runs a hook behind the ones already running (see {@link DevService.#hookChain}), e.g. `dev:close` so shutdown\n\t * waits for an unfinished `build:done`.\n\t */\n\tpublic runHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\treturn this.#queueHook(name, ...args);\n\t}\n\n\t/** Queues a hook behind the ones already running; see {@link DevService.#hookChain}. */\n\t#queueHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tthis.#hookChain = this.#hookChain.then(() => this.#callHook(name, ...args));\n\t\treturn this.#hookChain;\n\t}\n\n\t/**\n\t * Starts a hook right away, at the moment it describes, instead of queueing it: `build:before` belongs to the\n\t * builder's `start` boundary, and a builder such as `none` finishes in the same call stack. Its synchronous part\n\t * runs here; later hooks still wait for the rest.\n\t */\n\t#startHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tconst running = this.#callHook(name, ...args);\n\t\tthis.#hookChain = Promise.all([this.#hookChain, running]).then(() => undefined);\n\t\treturn running;\n\t}\n\n\t/**\n\t * Runs a `stars.config` hook. A failing hook is logged rather than thrown: a broken hook must never stop the\n\t * watcher or keep the bot from restarting.\n\t */\n\tasync #callHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tif (this.#hooks === null) return;\n\t\ttry {\n\t\t\tawait this.#hooks.callHook(name, ...args);\n\t\t} catch (error) {\n\t\t\tthis.log('stars', 'error', `Hook ${name} failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\t}\n\n\t#emitStatus(): void {\n\t\tthis.emit('status', this.status);\n\t}\n}\n\nexport function createSupervisor(config: ResolvedStarsConfig): ProcessSupervisor {\n\treturn new ProcessSupervisor({\n\t\tcommand: process.execPath,\n\t\targs: [\n\t\t\t...envImportArgs(config),\n\t\t\t...moduleImportArgs(config),\n\t\t\t...bridgeImportArgs(config),\n\t\t\t...config.dev.nodeArgs,\n\t\t\tconfig.build.output,\n\t\t\t...config.dev.args\n\t\t],\n\t\tcwd: config.root,\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...config.dev.env,\n\t\t\tSTARS_DEV: '1',\n\t\t\tNODE_ENV: 'development',\n\t\t\tFORCE_COLOR: process.env.NO_COLOR !== undefined ? undefined : (process.env.FORCE_COLOR ?? (process.stdout.isTTY ? '1' : undefined))\n\t\t},\n\t\tkillTimeout: config.dev.killTimeout,\n\t\tipc: true\n\t});\n}\n\nfunction formatMs(ms: number | null): string {\n\treturn ms === null ? '' : ` in ${ms}ms`;\n}\n\nfunction formatRequest(message: Extract<BridgeMessage, { type: 'request' }>): string {\n\treturn `${message.method} ${message.path} ${message.status} in ${message.ms}ms`;\n}\n\nconst BRIDGE_LEVELS = new Set<string>(['trace', 'debug', 'info', 'success', 'warn', 'error']);\n\nfunction toLogLevel(level: string): LogLevel {\n\treturn BRIDGE_LEVELS.has(level) ? (level as LogLevel) : 'info';\n}\n\n/** The latest change of each command wins: `added` then `changed` is still one line of the prompt. */\nfunction mergeChanges(previous: readonly CommandChange[], next: readonly CommandChange[]): CommandChange[] {\n\tconst key = (change: CommandChange) => `${change.guild ?? ''}:${change.type}:${change.name}`;\n\tconst merged = new Map(previous.map((change) => [key(change), change]));\n\tfor (const change of next) merged.set(key(change), change);\n\treturn [...merged.values()];\n}\n\nexport function describeReason(reason: RestartReason): string {\n\tswitch (reason) {\n\t\tcase 'initial':\n\t\t\treturn 'first build';\n\t\tcase 'build':\n\t\t\treturn 'sources changed';\n\t\tcase 'manual':\n\t\t\treturn 'manual restart';\n\t\tcase 'crash':\n\t\t\treturn 'after crash';\n\t}\n}\n","import { lookup } from 'node:dns/promises';\n\n/**\n * Resolves `localhost` the way Vite's dev server does: Node's default DNS result order (`dns.lookup('localhost')`)\n * can disagree with the OS's own, \"verbatim\" resolution order — a well known source of a dev URL that prints fine\n * but is not the address the bot actually bound to (e.g. an IPv6-first system where the server listens on IPv4\n * only). When the two disagree this returns the OS's own address instead of the literal `localhost` hostname.\n */\nexport async function resolveLocalhost(): Promise<string> {\n\ttry {\n\t\tconst [ordered, verbatim] = await Promise.all([lookup('localhost'), lookup('localhost', { verbatim: true })]);\n\t\treturn ordered.family === verbatim.family && ordered.address === verbatim.address ? 'localhost' : verbatim.address;\n\t} catch {\n\t\treturn 'localhost';\n\t}\n}\n\n/**\n * Replaces a `localhost` hostname in `url` with {@link resolveLocalhost}'s result. Any other hostname (an explicit\n * `dev.url` override, a LAN address, …) is returned untouched.\n */\nexport async function withResolvedLocalhost(url: string): Promise<string> {\n\tconst parsed = new URL(url);\n\tif (parsed.hostname !== 'localhost') return url;\n\n\tconst host = await resolveLocalhost();\n\tif (host === 'localhost') return url;\n\n\tparsed.hostname = host.includes(':') ? `[${host}]` : host;\n\treturn parsed.href;\n}\n","import { createWriteStream, mkdirSync, readdirSync, rmSync, type WriteStream } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { stripVTControlCharacters } from 'node:util';\nimport type { LogBuffer, LogEntry } from '../utils/log-buffer.js';\n\n/**\n * Mirrors a dev session's logs into a file, so a run can be read back after the terminal UI is gone (the TUI takes\n * over the alternate screen and its scrollback disappears on exit). `dev.logFile` is a single file truncated on every\n * run; `dev.logs.dir` adds one file per run (see {@link runLogFile}).\n */\nexport class LogFileWriter {\n\t#stream: WriteStream | null = null;\n\n\tpublic constructor(\n\t\tpublic readonly file: string,\n\t\tprivate readonly logs: LogBuffer\n\t) {}\n\n\tpublic open(): void {\n\t\tif (this.#stream) return;\n\n\t\tmkdirSync(dirname(this.file), { recursive: true });\n\t\tthis.#stream = createWriteStream(this.file, { flags: 'w' });\n\t\t// A broken pipe or a read-only directory must never take the dev session down with it.\n\t\tthis.#stream.on('error', () => this.close());\n\n\t\tfor (const entry of this.logs.entries()) this.#write(entry);\n\t\tthis.logs.on('entry', this.#write);\n\t}\n\n\tpublic close(): void {\n\t\tconst stream = this.#stream;\n\t\tthis.#stream = null;\n\t\tif (!stream) return;\n\n\t\tthis.logs.off('entry', this.#write);\n\t\tstream.end();\n\t}\n\n\t#write = (entry: LogEntry): void => {\n\t\tthis.#stream?.write(`${formatLogLine(entry)}\\n`);\n\t};\n}\n\n/** One line per entry, then its detail lines indented under it. Never filtered: the file is the whole session. */\nexport function formatLogLine(entry: LogEntry): string {\n\tconst head = `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.channel.padEnd(12)} ${stripVTControlCharacters(entry.text)}`;\n\treturn [head, ...(entry.detail ?? []).map((line) => ` ${stripVTControlCharacters(line)}`)].join('\\n');\n}\n\nconst RUN_FILE = /^dev-.+\\.log$/;\n\n/**\n * The file of this run inside `dev.logs.dir` (`dev-<timestamp>.log`), after deleting the oldest ones so the directory\n * holds at most `keep` files with the new one. The names sort by time, so no file needs to be opened to order them.\n */\nexport function runLogFile(directory: string, keep: number, now: Date = new Date()): string {\n\tlet existing: string[] = [];\n\ttry {\n\t\texisting = readdirSync(directory)\n\t\t\t.filter((name) => RUN_FILE.test(name))\n\t\t\t.sort();\n\t} catch {\n\t\t// The directory does not exist yet: nothing to prune.\n\t}\n\n\tfor (const name of existing.slice(0, Math.max(0, existing.length - Math.max(0, keep - 1)))) {\n\t\trmSync(join(directory, name), { force: true });\n\t}\n\n\treturn join(directory, `dev-${now.toISOString().replaceAll(':', '-')}.log`);\n}\n","import type { ResolvedStarsConfig } from '@wolfstar/schema';\nimport { defineCommand } from 'citty';\nimport { createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev/dev-service.js';\nimport { withResolvedLocalhost } from '../dev/host.js';\nimport { LogFileWriter, runLogFile } from '../dev/log-file.js';\nimport { initialLogView, isLogLevel } from '../dev/tui/log-view.js';\nimport { collectFlag, projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport { ExitCode, renderCrashReport } from '../utils/errors.js';\nimport { applyEnvOptions, loadProject, withProjectEnv } from '../utils/hooks.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../utils/output-mode.js';\nimport { THEME_SETTINGS, isThemeSetting, readSavedTheme, resolveThemeSetting, saveTheme } from '../utils/theme.js';\nimport { prepareProject, reportWarnings } from './_shared.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n\t/** `--theme`. */\n\ttheme?: string;\n\t/** `--layout`. */\n\tlayout?: string;\n\t/** `--channel`, each value possibly a comma-separated list. */\n\tchannel?: string[];\n\t/** `--level`. */\n\tlevel?: string;\n\t/** `--tunnel` opens the tunnel at start whatever `dev.tunnel` says, `--no-tunnel` keeps it closed. */\n\ttunnel?: boolean;\n\t/** `--no-mouse` leaves the mouse to the terminal whatever `dev.mouse` says. */\n\tmouse?: boolean;\n}\n\nconst LAYOUTS = ['auto', 'dashboard', 'panel'] as const;\nconst LEVELS = ['trace', 'debug', 'info', 'warn', 'error'] as const;\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tif (options.theme !== undefined && !isThemeSetting(options.theme)) {\n\t\tthrow cliDiagnostics.INVALID_THEME({ theme: options.theme, themes: THEME_SETTINGS.join(', ') });\n\t}\n\n\tconst layout = options.layout as (typeof LAYOUTS)[number] | undefined;\n\tif (layout !== undefined && !LAYOUTS.includes(layout)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--layout', value: options.layout!, allowed: LAYOUTS.join(', ') });\n\t}\n\n\tif (options.level !== undefined && !isLogLevel(options.level)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--level', value: options.level, allowed: LEVELS.join(', ') });\n\t}\n\n\t// Dev mode applies to config evaluation, build plugins, and the supervised application — not only the child.\n\tprocess.env.NODE_ENV = 'development';\n\tconst project = await loadProject({ cwd: resolveCwd(options), configFile: options.config, env: { ...process.env, NODE_ENV: 'development' } });\n\tconst hooks = project.hooks;\n\t// `env:options` first: it may pick env files with another `HTTP_PORT`, which `dev.url` is derived from.\n\tconst config = await resolveDevConfig(withProjectEnv(await applyEnvOptions(project.config, hooks)));\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\tconst theme = resolveThemeSetting({ flag: options.theme, saved: readSavedTheme() });\n\n\tconst builder = await createBuilder(config, hooks);\n\tawait hooks.callHook('builder:created', builder, config);\n\tconst service = new DevService(config, { builder, hooks, openTunnel: options.tunnel });\n\tawait reportWarnings(config, (text) => service.log('stars', 'warn', text), { production: false });\n\t// The files receive every entry: the filter below only decides what the terminal shows.\n\tconst logFiles = [\n\t\t...(config.dev.logFile ? [config.dev.logFile] : []),\n\t\t...(config.dev.logs?.dir ? [runLogFile(config.dev.logs.dir, config.dev.logs.keep)] : [])\n\t].map((file) => new LogFileWriter(file, service.logs));\n\tfor (const file of logFiles) file.open();\n\tconst filter = initialLogView(config, { channels: options.channel, level: options.level });\n\tconst renderer =\n\t\tmode === 'tui'\n\t\t\t? (await import('../dev/tui/tui.js')).createTuiRenderer(service, {\n\t\t\t\t\tcolor,\n\t\t\t\t\ttheme,\n\t\t\t\t\tlayout,\n\t\t\t\t\tfilter,\n\t\t\t\t\tmouse: options.mouse,\n\t\t\t\t\treducedMotion: prefersReducedMotion(),\n\t\t\t\t\tonThemeSave: (setting) => {\n\t\t\t\t\t\tif (!saveTheme(setting)) service.log('stars', 'warn', 'Could not save the theme preference.');\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t: (await import('../dev/tui/plain.js')).createPlainRenderer(service, { color, filter });\n\n\tlet exiting: Promise<never> | null = null;\n\tconst shutdown = (code: ExitCode): Promise<never> => {\n\t\texiting ??= (async () => {\n\t\t\trenderer.stop();\n\t\t\t// A failing hook must not keep the bot running.\n\t\t\t// Behind any hook still running (an async `build:done`), and logged rather than thrown if it fails.\n\t\t\tawait service.runHook('dev:close', config);\n\t\t\tawait service.stop();\n\t\t\tfor (const file of logFiles) file.close();\n\t\t\tprocess.exit(code);\n\t\t})();\n\t\treturn exiting;\n\t};\n\n\t// Like Turborepo: the first signal shuts down gracefully, a second one gives up waiting and kills everything.\n\tlet signalled = false;\n\tconst onSignal = (code: ExitCode) => () => {\n\t\tif (signalled) {\n\t\t\tprocess.stderr.write('\\nForcing shutdown.\\n');\n\t\t\tservice.kill();\n\t\t\tprocess.exit(code);\n\t\t}\n\n\t\tsignalled = true;\n\t\tvoid shutdown(code);\n\t\tprocess.stderr.write('\\nShutting down gracefully, press Ctrl+C again to force quit.\\n');\n\t};\n\n\tprocess.on('SIGINT', onSignal(ExitCode.Interrupted));\n\tprocess.on('SIGTERM', onSignal(ExitCode.Terminated));\n\tprocess.on('SIGHUP', onSignal(ExitCode.Terminated));\n\tif (process.platform !== 'win32') process.on('SIGUSR2', () => void service.restart('manual'));\n\n\tconst finished = renderer.start().then(() => shutdown(ExitCode.Ok));\n\ttry {\n\t\tawait prepareProject(config, hooks);\n\t\tawait service.start();\n\t\tawait hooks.callHook('dev:start', config);\n\t} catch (error) {\n\t\tservice.log('stars', 'error', error instanceof Error ? await renderCrashReport(error, config.root) : String(error));\n\t\tawait shutdown(ExitCode.Error);\n\t}\n\tawait finished;\n}\n\n/**\n * Resolves `dev.url`'s `localhost` to the address that is actually reachable (see {@link withResolvedLocalhost}),\n * the way Vite's dev server does when it starts. Only `stars dev` pays this DNS lookup; `stars info`/`stars build`\n * show or use the unresolved config as-is, since they never talk to the bot.\n */\nasync function resolveDevConfig(config: ResolvedStarsConfig): Promise<ResolvedStarsConfig> {\n\tif (!config.dev.url) return config;\n\n\tconst url = await withResolvedLocalhost(config.dev.url);\n\treturn url === config.dev.url ? config : { ...config, dev: { ...config.dev, url } };\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'dev',\n\t\tdescription: 'Build, run and restart the bot on changes'\n\t},\n\targs: {\n\t\t...projectArgs,\n\t\ttui: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Interactive terminal UI; use --no-tui (or STARS_TUI=plain) for plain line output',\n\t\t\tdefault: true\n\t\t},\n\t\ttheme: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Colour theme of the interactive UI: ${THEME_SETTINGS.join(', ')} (or STARS_THEME; press T in the UI to pick one)`\n\t\t},\n\t\tlayout: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Layout of the interactive UI: ${LAYOUTS.join(', ')} (defaults to dev.layout; press v in the UI to switch)`\n\t\t},\n\t\tchannel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Only show these log channels at start (repeatable, or comma-separated; defaults to dev.logs.channels)'\n\t\t},\n\t\tlevel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Only show this log level and the more severe ones at start: ${LEVELS.join(', ')} (defaults to dev.logs.levels)`\n\t\t},\n\t\t// No default: left out, `dev.tunnel` decides.\n\t\ttunnel: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Open the public tunnel at start, whatever dev.tunnel says',\n\t\t\tnegativeDescription: 'Keep the public tunnel closed at start even when dev.tunnel enables it'\n\t\t},\n\t\t// No default: left out, `dev.mouse` decides.\n\t\tmouse: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Clickable dashboard and wheel scrolling (defaults to dev.mouse)',\n\t\t\tnegativeDescription: 'Leave the mouse to the terminal even when dev.mouse is on'\n\t\t}\n\t},\n\tasync run({ args, rawArgs }) {\n\t\tconst channel = collectFlag(rawArgs, 'channel');\n\t\tawait runDev({\n\t\t\tconfig: args.config,\n\t\t\tcwd: args.cwd,\n\t\t\ttui: args.tui ? undefined : false,\n\t\t\ttheme: args.theme,\n\t\t\tlayout: args.layout,\n\t\t\tchannel: channel.length > 0 ? channel : undefined,\n\t\t\tlevel: args.level,\n\t\t\ttunnel: args.tunnel,\n\t\t\tmouse: args.mouse\n\t\t});\n\t}\n});\n"],"mappings":"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"}