laohuang 0.8.4 → 0.8.5

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/README.md CHANGED
@@ -4,7 +4,25 @@ A minimal coding-agent CLI for local software work.
4
4
 
5
5
  ## Install
6
6
 
7
- Requires Node.js >=22.19.0.
7
+ Recommended standalone install (bundled Node.js; Bash required):
8
+
9
+ ```bash
10
+ curl -fsSL https://github.com/hxr223/laoHuangCode/releases/latest/download/install.sh | sh
11
+ ```
12
+
13
+ On Windows, install Git for Windows first, then run in PowerShell:
14
+
15
+ ```powershell
16
+ irm https://github.com/hxr223/laoHuangCode/releases/latest/download/install.ps1 | iex
17
+ ```
18
+
19
+ The installer verifies the download and startup, then configures the user PATH.
20
+ Open a new terminal or apply the PATH command printed by the installer.
21
+ Standalone packages target macOS, glibc Linux and Windows on x64 and ARM64.
22
+ See [installation details](https://github.com/hxr223/laoHuangCode/blob/main/INSTALLING.md).
23
+
24
+ Alternatively, install with npm (Node.js >=22.19.0 and Bash required; npm's
25
+ global executable directory must already be on PATH):
8
26
 
9
27
  ```bash
10
28
  npm install -g laohuang
@@ -38,7 +56,11 @@ If no models are available, the CLI points to `/login`.
38
56
  directly, and `/apikey` remains a compatibility alias. Switching models affects
39
57
  only the current session, not the default profile.
40
58
 
41
- `laohuang update` updates the running npm installation to the latest stable
59
+ For standalone installations, `laohuang update` reruns the bundled installer
60
+ against the latest GitHub release, verifies it before switching the launcher,
61
+ and retains the previous version. Restart laohuang after updating.
62
+
63
+ For npm installations, `laohuang update` updates the running installation to the latest stable
42
64
  release and verifies its installed version. Restart the CLI after updating.
43
65
  It supports global npm installations and direct project or workspace npm
44
66
  dependencies; local updates change the owning manifest and lockfile while
package/dist/bin.js CHANGED
@@ -12916,6 +12916,7 @@ var InteractiveTerminalLoop = class {
12916
12916
  input?.on("end", onEnd);
12917
12917
  this.#wakeupEnabled = true;
12918
12918
  try {
12919
+ input?.resume?.();
12919
12920
  if (!this.#exitRequested) {
12920
12921
  await new Promise((resolve8) => {
12921
12922
  this.#exitResolve = resolve8;
@@ -18393,8 +18394,110 @@ function createMcpCommand(runtime, presenter) {
18393
18394
  };
18394
18395
  }
18395
18396
 
18397
+ // src/stdin-prompts.ts
18398
+ import { StringDecoder as StringDecoder2 } from "node:string_decoder";
18399
+ var StdinPrompts = class {
18400
+ #input;
18401
+ #active = false;
18402
+ #skipLf = false;
18403
+ constructor(input) {
18404
+ this.#input = input;
18405
+ }
18406
+ async prompt(message, output, secret = false) {
18407
+ const input = this.#input;
18408
+ if (this.#active || input.listenerCount("data") > 0 || input.listenerCount("readable") > 0) {
18409
+ throw new Error("stdin is already in use by another input reader");
18410
+ }
18411
+ if (input.errored) throw input.errored;
18412
+ if (input.readableEnded || input.destroyed) throw new PromptEofError();
18413
+ this.#active = true;
18414
+ const raw = Boolean(secret && input.isTTY && input.setRawMode);
18415
+ return new Promise((resolve8, reject) => {
18416
+ const decoder = new StringDecoder2("utf8");
18417
+ let answer = "";
18418
+ let settled = false;
18419
+ let restoreRawMode;
18420
+ const finish = (error) => {
18421
+ if (settled) return;
18422
+ settled = true;
18423
+ input.pause();
18424
+ input.removeListener("data", onData);
18425
+ input.removeListener("end", onEnd);
18426
+ input.removeListener("close", onClose);
18427
+ input.removeListener("error", onError);
18428
+ process.removeListener("SIGINT", onInterrupt);
18429
+ try {
18430
+ restoreRawMode?.();
18431
+ } catch (restoreError) {
18432
+ error = error === void 0 ? restoreError : new AggregateError([error, restoreError], "Input and terminal restoration failed");
18433
+ }
18434
+ this.#active = false;
18435
+ try {
18436
+ if (restoreRawMode) output.write("\n");
18437
+ } catch (outputError) {
18438
+ error ??= outputError;
18439
+ }
18440
+ if (error !== void 0) reject(error);
18441
+ else resolve8(answer);
18442
+ };
18443
+ const onData = (data) => {
18444
+ try {
18445
+ const bytes = typeof data === "string" ? Buffer.from(data) : data;
18446
+ for (let index = 0; index < bytes.length; index++) {
18447
+ const value = bytes[index];
18448
+ if (this.#skipLf) {
18449
+ this.#skipLf = false;
18450
+ if (value === 10) continue;
18451
+ }
18452
+ if (raw && (value === 3 || value === 4)) {
18453
+ finish(value === 3 ? new PromptCancelledError() : new PromptEofError());
18454
+ return;
18455
+ }
18456
+ if (value === 10 || value === 13) {
18457
+ answer += decoder.end();
18458
+ if (value === 13) {
18459
+ if (bytes[index + 1] === 10) index++;
18460
+ else this.#skipLf = index + 1 === bytes.length;
18461
+ }
18462
+ input.pause();
18463
+ if (index + 1 < bytes.length) input.unshift(bytes.subarray(index + 1));
18464
+ finish();
18465
+ return;
18466
+ }
18467
+ if (raw && (value === 127 || value === 8)) {
18468
+ answer = [...answer].slice(0, -1).join("");
18469
+ } else {
18470
+ answer += decoder.write(bytes.subarray(index, index + 1));
18471
+ }
18472
+ }
18473
+ } catch (error) {
18474
+ finish(error);
18475
+ }
18476
+ };
18477
+ const onEnd = () => {
18478
+ answer += decoder.end();
18479
+ finish(!raw && answer ? void 0 : new PromptEofError());
18480
+ };
18481
+ const onClose = () => finish(new PromptEofError());
18482
+ const onError = (error) => finish(error);
18483
+ const onInterrupt = () => finish(new PromptCancelledError());
18484
+ try {
18485
+ if (raw) restoreRawMode = enterTerminalRawMode(input);
18486
+ if (message) output.write(message);
18487
+ if (input.isTTY) process.on("SIGINT", onInterrupt);
18488
+ input.on("data", onData);
18489
+ input.on("end", onEnd);
18490
+ input.on("close", onClose);
18491
+ input.on("error", onError);
18492
+ input.resume();
18493
+ } catch (error) {
18494
+ finish(error);
18495
+ }
18496
+ });
18497
+ }
18498
+ };
18499
+
18396
18500
  // src/repl.ts
18397
- import { readSync as readSync2 } from "node:fs";
18398
18501
  function errorMessage11(error) {
18399
18502
  return error instanceof Error ? error.message : String(error);
18400
18503
  }
@@ -18404,86 +18507,14 @@ function isEofError(error) {
18404
18507
  function isInterruptedError(error) {
18405
18508
  return error instanceof PromptCancelledError || error instanceof Error && (error.name === "PromptCancelledError" || error.name === "KeyboardInterrupt");
18406
18509
  }
18407
- var stdinBuffer = { pending: "", eof: false };
18408
- function readLineFromStdin() {
18409
- const chunk = Buffer.alloc(4096);
18410
- for (; ; ) {
18411
- const newline = stdinBuffer.pending.indexOf("\n");
18412
- if (newline !== -1) {
18413
- const line2 = stdinBuffer.pending.slice(0, newline);
18414
- stdinBuffer.pending = stdinBuffer.pending.slice(newline + 1);
18415
- return line2.endsWith("\r") ? line2.slice(0, -1) : line2;
18416
- }
18417
- if (stdinBuffer.eof) {
18418
- const rest = stdinBuffer.pending;
18419
- stdinBuffer.pending = "";
18420
- if (rest) {
18421
- return rest;
18422
- }
18423
- throw new PromptEofError();
18424
- }
18425
- let bytesRead = 0;
18426
- try {
18427
- bytesRead = readSync2(0, chunk, 0, chunk.length, null);
18428
- } catch (error) {
18429
- if (error.code === "EAGAIN") {
18430
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
18431
- continue;
18432
- }
18433
- throw error;
18434
- }
18435
- if (bytesRead === 0) {
18436
- stdinBuffer.eof = true;
18437
- continue;
18438
- }
18439
- stdinBuffer.pending += chunk.toString("utf8", 0, bytesRead);
18440
- }
18441
- }
18510
+ var stdinPrompts;
18442
18511
  function defaultInputFn(prompt) {
18443
- if (prompt) {
18444
- process.stdout.write(prompt);
18445
- }
18446
- return readLineFromStdin();
18512
+ stdinPrompts ??= new StdinPrompts(process.stdin);
18513
+ return stdinPrompts.prompt(prompt, process.stdout);
18447
18514
  }
18448
18515
  function defaultSecretInputFn(prompt) {
18449
- if (prompt) {
18450
- process.stderr.write(prompt);
18451
- }
18452
- if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== "function") {
18453
- return readLineFromStdin();
18454
- }
18455
- const byte = Buffer.alloc(1);
18456
- let answer = "";
18457
- const restoreRawMode = enterTerminalRawMode(process.stdin);
18458
- try {
18459
- for (; ; ) {
18460
- const bytesRead = readSync2(0, byte, 0, 1, null);
18461
- if (bytesRead === 0) {
18462
- throw new PromptEofError();
18463
- }
18464
- const value = byte[0];
18465
- if (value === 3) {
18466
- process.stderr.write("\n");
18467
- throw new PromptCancelledError();
18468
- }
18469
- if (value === 4) {
18470
- process.stderr.write("\n");
18471
- throw new PromptEofError();
18472
- }
18473
- if (value === 10 || value === 13) {
18474
- break;
18475
- }
18476
- if (value === 127 || value === 8) {
18477
- answer = [...answer].slice(0, -1).join("");
18478
- continue;
18479
- }
18480
- answer += byte.toString("utf8", 0, 1);
18481
- }
18482
- } finally {
18483
- restoreRawMode();
18484
- }
18485
- process.stderr.write("\n");
18486
- return answer;
18516
+ stdinPrompts ??= new StdinPrompts(process.stdin);
18517
+ return stdinPrompts.prompt(prompt, process.stderr, true);
18487
18518
  }
18488
18519
  function supportsTerminalUI(options = {}) {
18489
18520
  if (options.inputFn !== void 0 || options.outputFn !== void 0) {