codeam-cli 2.2.0 → 2.2.1

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.
Files changed (2) hide show
  1. package/dist/index.js +66 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -179,7 +179,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
179
179
  // package.json
180
180
  var package_default = {
181
181
  name: "codeam-cli",
182
- version: "2.2.0",
182
+ version: "2.2.1",
183
183
  description: "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands \u2014 from anywhere.",
184
184
  main: "dist/index.js",
185
185
  bin: {
@@ -2174,14 +2174,65 @@ function parsePayload(schema, raw) {
2174
2174
  var fs5 = __toESM(require("fs/promises"));
2175
2175
  var path5 = __toESM(require("path"));
2176
2176
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
2177
- function resolveSafe(rawPath) {
2177
+ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
2178
+ "node_modules",
2179
+ ".git",
2180
+ ".next",
2181
+ ".expo",
2182
+ "dist",
2183
+ "build",
2184
+ "out",
2185
+ ".cache",
2186
+ "coverage",
2187
+ ".turbo",
2188
+ ".parcel-cache",
2189
+ ".idea",
2190
+ ".vscode",
2191
+ "ios",
2192
+ "android"
2193
+ // expo-managed native dirs are huge and rarely interesting
2194
+ ]);
2195
+ async function listSubdirs(dir) {
2196
+ try {
2197
+ const entries = await fs5.readdir(dir, { withFileTypes: true });
2198
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") ? true : !SUBDIR_IGNORE.has(e.name)).filter((e) => e.isDirectory() && !SUBDIR_IGNORE.has(e.name)).map((e) => path5.join(dir, e.name));
2199
+ } catch {
2200
+ return [];
2201
+ }
2202
+ }
2203
+ function isUnder(parent, candidate) {
2204
+ const rel = path5.relative(parent, candidate);
2205
+ return !rel.startsWith("..") && !path5.isAbsolute(rel);
2206
+ }
2207
+ async function findFile(rawPath) {
2178
2208
  const cwd = process.cwd();
2179
- const absolute = path5.isAbsolute(rawPath) ? path5.normalize(rawPath) : path5.resolve(cwd, rawPath);
2180
- const relativeFromCwd = path5.relative(cwd, absolute);
2181
- if (relativeFromCwd.startsWith("..") || path5.isAbsolute(relativeFromCwd)) {
2182
- throw new Error(`Path escapes the project root: ${rawPath}`);
2209
+ const candidates = [];
2210
+ if (path5.isAbsolute(rawPath)) {
2211
+ candidates.push(path5.normalize(rawPath));
2212
+ } else {
2213
+ candidates.push(path5.resolve(cwd, rawPath));
2214
+ const subdirs = await listSubdirs(cwd);
2215
+ for (const sub of subdirs) {
2216
+ candidates.push(path5.resolve(sub, rawPath));
2217
+ }
2183
2218
  }
2184
- return absolute;
2219
+ for (const cand of candidates) {
2220
+ if (!isUnder(cwd, cand)) continue;
2221
+ try {
2222
+ const stat2 = await fs5.stat(cand);
2223
+ if (stat2.isFile()) return cand;
2224
+ } catch {
2225
+ }
2226
+ }
2227
+ return null;
2228
+ }
2229
+ async function findWriteTarget(rawPath) {
2230
+ const found = await findFile(rawPath);
2231
+ if (found) return found;
2232
+ const cwd = process.cwd();
2233
+ const fallback = path5.isAbsolute(rawPath) ? path5.normalize(rawPath) : path5.resolve(cwd, rawPath);
2234
+ if (!isUnder(cwd, fallback)) return null;
2235
+ return fallback;
2185
2236
  }
2186
2237
  function looksBinary(buf) {
2187
2238
  const sample = buf.subarray(0, Math.min(8192, buf.length));
@@ -2192,11 +2243,11 @@ function looksBinary(buf) {
2192
2243
  }
2193
2244
  async function readProjectFile(rawPath) {
2194
2245
  try {
2195
- const abs = resolveSafe(rawPath);
2196
- const stat2 = await fs5.stat(abs);
2197
- if (!stat2.isFile()) {
2198
- return { error: "Not a regular file." };
2246
+ const abs = await findFile(rawPath);
2247
+ if (!abs) {
2248
+ return { error: `File not found in the project tree: ${rawPath}` };
2199
2249
  }
2250
+ const stat2 = await fs5.stat(abs);
2200
2251
  if (stat2.size > MAX_FILE_BYTES) {
2201
2252
  return { error: `File too large (${(stat2.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
2202
2253
  }
@@ -2212,7 +2263,10 @@ async function readProjectFile(rawPath) {
2212
2263
  }
2213
2264
  async function writeProjectFile(rawPath, content) {
2214
2265
  try {
2215
- const abs = resolveSafe(rawPath);
2266
+ const abs = await findWriteTarget(rawPath);
2267
+ if (!abs) {
2268
+ return { error: `Path escapes the project root: ${rawPath}` };
2269
+ }
2216
2270
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
2217
2271
  return { error: "Content too large." };
2218
2272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands — from anywhere.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {