@openscout/scout 0.2.69 → 0.2.72

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.
@@ -8,10 +8,10 @@ import { createRequire as createRequire2 } from "node:module";
8
8
  // packages/web/server/terminal-relay-session.ts
9
9
  import { createRequire } from "module";
10
10
  import { execFileSync, execSync } from "child_process";
11
- import { accessSync, constants, existsSync, mkdirSync, writeFileSync } from "fs";
12
- import { delimiter as pathDelimiter, join, dirname as pathDirname, resolve as pathResolve } from "path";
11
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
12
+ import { join, dirname as pathDirname } from "path";
13
13
  var require2 = createRequire(import.meta.url);
14
- var pty = require2("node-pty");
14
+ var pty = require2("@lydell/node-pty");
15
15
  var DEFAULT_ORPHAN_TTL_MS = 30 * 60 * 1000;
16
16
  var MAX_BUFFER_SIZE = 512 * 1024;
17
17
  var sessions = new Map;
@@ -93,78 +93,17 @@ function findBin(name, envOverride) {
93
93
  return null;
94
94
  }
95
95
  }
96
- function expandHomePath(value) {
97
- const home = process.env.HOME || "/tmp";
98
- if (value === "~")
99
- return home;
100
- if (value.startsWith("~/"))
101
- return join(home, value.slice(2));
102
- return value;
103
- }
104
- function isExecutablePath(candidate) {
105
- if (!candidate)
106
- return false;
107
- try {
108
- accessSync(candidate, constants.X_OK);
109
- return true;
110
- } catch {
111
- return false;
112
- }
113
- }
114
- function findExecutableInDirectories(name, directories) {
115
- const seen = new Set;
116
- for (const directory of directories) {
117
- if (!directory)
118
- continue;
119
- const normalizedDirectory = pathResolve(expandHomePath(directory));
120
- if (seen.has(normalizedDirectory))
121
- continue;
122
- seen.add(normalizedDirectory);
123
- const candidate = join(normalizedDirectory, name);
124
- if (isExecutablePath(candidate))
125
- return candidate;
126
- }
127
- return null;
128
- }
129
- function findExecutableOnPath(name) {
130
- return findExecutableInDirectories(name, (process.env.PATH || "").split(pathDelimiter));
131
- }
132
96
  function findClaudeBin() {
133
- for (const envKey of ["OPENSCOUT_CLAUDE_BIN", "SCOUT_CLAUDE_BIN", "CLAUDE_BIN"]) {
134
- const explicit = process.env[envKey]?.trim();
135
- if (!explicit)
136
- continue;
137
- const expanded = expandHomePath(explicit);
138
- if (isExecutablePath(expanded))
139
- return pathResolve(expanded);
140
- const foundOnPath = findExecutableOnPath(explicit);
141
- if (foundOnPath)
142
- return foundOnPath;
143
- }
144
- const home = process.env.HOME || "/tmp";
145
- return findExecutableInDirectories("claude", [
146
- join(home, ".local", "bin"),
147
- join(home, ".claude", "local"),
148
- "/opt/homebrew/bin",
149
- "/usr/local/bin",
150
- join(home, ".bun", "bin")
151
- ]) ?? findExecutableOnPath("claude");
97
+ return findBin("claude", "CLAUDE_BIN");
152
98
  }
153
99
  function findPiBin() {
154
100
  return findBin("pi", "PI_BIN");
155
101
  }
156
102
  function findShellBin() {
157
- const candidates = [
158
- process.env.SHELL,
159
- "/bin/zsh",
160
- "/bin/bash",
161
- "/bin/sh"
162
- ].filter((candidate) => Boolean(candidate));
163
- for (const candidate of candidates) {
164
- if (existsSync(candidate))
165
- return candidate;
166
- }
167
- return null;
103
+ const configured = process.env.SHELL;
104
+ if (configured && existsSync(configured))
105
+ return configured;
106
+ return findBin("zsh") ?? findBin("bash") ?? findBin("sh") ?? (existsSync("/bin/sh") ? "/bin/sh" : null);
168
107
  }
169
108
  function normalizePiProviderForCli(provider) {
170
109
  if (!provider)
@@ -213,10 +152,10 @@ function bootstrapFiles(cwd, files, sessionId) {
213
152
  }
214
153
  }
215
154
  }
216
- function spawnTmuxSession(tmuxName, cols, rows, cwd, claudeBin, claudeArgs, env) {
155
+ function spawnTmuxSession(tmuxName, cols, rows, cwd, commandBin, commandArgs, env) {
217
156
  const exists = tmuxSessionExists(tmuxName);
218
157
  if (!exists) {
219
- const shellCmd = [claudeBin, ...claudeArgs].map((a) => a.includes(" ") ? `'${a}'` : a).join(" ");
158
+ const shellCmd = [commandBin, ...commandArgs].map((a) => a.includes(" ") ? `'${a}'` : a).join(" ");
220
159
  execSync(`tmux new-session -d -s ${tmuxName} -x ${cols} -y ${rows} -c '${cwd}' '${shellCmd}'`, { env });
221
160
  console.log(`[relay] Created tmux session: ${tmuxName}`);
222
161
  } else {
@@ -242,7 +181,7 @@ function createSession(ws, msg) {
242
181
  if (agent === "shell") {
243
182
  agentBin = findShellBin();
244
183
  if (!agentBin) {
245
- const reason = "Shell not found. Set SHELL or install zsh/bash/sh.";
184
+ const reason = "No login shell found. Set SHELL or install zsh, bash, or sh.";
246
185
  console.error(`[relay] Session ${id} failed: ${reason}`);
247
186
  send(ws, { type: "session:error", error: reason });
248
187
  return null;
@@ -258,7 +197,7 @@ function createSession(ws, msg) {
258
197
  } else {
259
198
  agentBin = findClaudeBin();
260
199
  if (!agentBin) {
261
- const reason = "Claude CLI not found. Install it with: curl -fsSL https://claude.ai/install.sh | bash";
200
+ const reason = "Claude CLI not found. Install it with: npm install -g @anthropic-ai/claude-code";
262
201
  console.error(`[relay] Session ${id} failed: ${reason}`);
263
202
  send(ws, { type: "session:error", error: reason });
264
203
  return null;
@@ -282,7 +221,8 @@ function createSession(ws, msg) {
282
221
  }
283
222
  let agentArgs;
284
223
  if (agent === "shell") {
285
- agentArgs = [];
224
+ const shellName = agentBin.split("/").pop() ?? "";
225
+ agentArgs = shellName === "sh" ? [] : ["-l"];
286
226
  } else if (agent === "pi") {
287
227
  agentArgs = ["--verbose"];
288
228
  const provider = normalizePiProviderForCli(msg.provider);