@co0ontty/wand 1.67.0 → 1.67.1-beta.g8cd88fc

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "commit": "bdf490ea0abbcd603f15e39745919fe6fb50f866",
3
- "builtAt": "2026-06-15T22:11:01.570Z",
4
- "version": "1.67.0",
5
- "channel": "stable"
2
+ "commit": "8cd88fc496f9d767b9d1ac0fe3782391eb8329c7",
3
+ "builtAt": "2026-06-16T01:15:08.993Z",
4
+ "version": "1.67.1-beta.g8cd88fc",
5
+ "channel": "beta"
6
6
  }
@@ -3,34 +3,50 @@
3
3
  // posix_spawn against a non-executable file returns EACCES and node-pty
4
4
  // throws "posix_spawnp failed." — every PTY session fails before it starts.
5
5
  //
6
- // Run once on server startup. Locates node-pty wherever it actually lives
7
- // (dev repo, hoisted global, pnpm store …) via require.resolve, finds the
8
- // per-arch prebuilds dir the loaded binary is in, and ensures the helper
9
- // next to it is +x. Idempotent and best-effort: silent on the happy path,
10
- // warns on failure but never blocks startup.
6
+ // Locates node-pty wherever it actually lives (dev repo, hoisted global,
7
+ // pnpm store …) via require.resolve, finds the per-arch prebuilds dir the
8
+ // loaded binary is in, and ensures the helper next to it is +x. Idempotent
9
+ // and best-effort: silent on the happy path, warns on failure but never
10
+ // blocks startup.
11
+ //
12
+ // Called both once at server startup AND right before every PTY spawn. The
13
+ // per-spawn call is what makes this self-heal: a self-update (`npm install
14
+ // -g`) reinstalls node-pty and re-drops the bit, and that extraction can
15
+ // land *after* the relaunched server already ran its startup chmod — so a
16
+ // startup-only fix leaves every subsequent PTY launch broken until the next
17
+ // restart. Re-checking before each spawn closes that race (the resolve is
18
+ // cached, the happy path is a single stat + early return).
11
19
  import { chmodSync, existsSync, statSync } from "node:fs";
12
20
  import { createRequire } from "node:module";
13
21
  import path from "node:path";
14
22
  import process from "node:process";
15
23
  import { getErrorMessage } from "./error-utils.js";
16
24
  const requireFromHere = createRequire(import.meta.url);
17
- export function ensureNodePtyHelperExecutable() {
25
+ // Cache the resolved helper path: `null` = not yet resolved, `""` = resolved
26
+ // to "no helper on this platform/install" (don't retry).
27
+ let cachedHelperPath = null;
28
+ function resolveHelperPath() {
29
+ if (cachedHelperPath !== null)
30
+ return cachedHelperPath;
18
31
  // spawn-helper is only used on Unix-likes. Windows uses winpty / conpty.
19
32
  if (process.platform === "win32")
20
- return;
33
+ return (cachedHelperPath = "");
21
34
  let nodePtyEntry;
22
35
  try {
23
36
  nodePtyEntry = requireFromHere.resolve("node-pty");
24
37
  }
25
38
  catch {
26
- return;
39
+ return (cachedHelperPath = "");
27
40
  }
28
41
  // node-pty's lib/index.js sits at <pkg>/lib/index.js; helper lives at
29
42
  // <pkg>/prebuilds/<platform>-<arch>/spawn-helper.
30
43
  const pkgRoot = path.resolve(path.dirname(nodePtyEntry), "..");
31
44
  const arch = `${process.platform}-${process.arch}`;
32
- const helper = path.join(pkgRoot, "prebuilds", arch, "spawn-helper");
33
- if (!existsSync(helper))
45
+ return (cachedHelperPath = path.join(pkgRoot, "prebuilds", arch, "spawn-helper"));
46
+ }
47
+ export function ensureNodePtyHelperExecutable() {
48
+ const helper = resolveHelperPath();
49
+ if (!helper || !existsSync(helper))
34
50
  return;
35
51
  let mode;
36
52
  try {
@@ -10,6 +10,7 @@ import { ClaudePtyBridge } from "./claude-pty-bridge.js";
10
10
  import { truncateMessagesForTransport } from "./message-truncator.js";
11
11
  import { appendWindow, hasExplicitConfirmSyntax, hasPermissionActionContext, normalizePromptText, PTY_OUTPUT_MAX_SIZE } from "./pty-text-utils.js";
12
12
  import { buildChildEnv, isRunningAsRoot } from "./env-utils.js";
13
+ import { ensureNodePtyHelperExecutable } from "./ensure-node-pty-helper.js";
13
14
  import { buildLanguageDirective, buildManagedAutonomyDirective } from "./language-prompt.js";
14
15
  import { prepareSessionWorktree } from "./git-worktree.js";
15
16
  import { getCodexResumeCommandSessionId, getResumeCommandSessionId } from "./resume-policy.js";
@@ -873,6 +874,10 @@ export class ProcessManager extends EventEmitter {
873
874
  }
874
875
  this.cleanupOldSessions();
875
876
  const shellArgs = this.buildShellArgs(processedCommand);
877
+ // Self-heal node-pty's spawn-helper +x bit before every spawn: a self-update
878
+ // can re-drop it after this server already ran its startup chmod, which would
879
+ // otherwise make every PTY launch throw "posix_spawnp failed." until restart.
880
+ ensureNodePtyHelperExecutable();
876
881
  let child;
877
882
  try {
878
883
  child = pty.spawn(this.config.shell, shellArgs, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.67.0",
3
+ "version": "1.67.1-beta.g8cd88fc",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {