codeam-cli 2.2.0 → 2.2.2
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/index.js +106 -13
- 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.
|
|
182
|
+
version: "2.2.2",
|
|
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,104 @@ 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
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2177
|
+
var MAX_WALK_DEPTH = 6;
|
|
2178
|
+
var MAX_VISITED_DIRS = 5e3;
|
|
2179
|
+
var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
|
|
2180
|
+
"node_modules",
|
|
2181
|
+
".git",
|
|
2182
|
+
".next",
|
|
2183
|
+
".expo",
|
|
2184
|
+
"dist",
|
|
2185
|
+
"build",
|
|
2186
|
+
"out",
|
|
2187
|
+
".cache",
|
|
2188
|
+
"coverage",
|
|
2189
|
+
".turbo",
|
|
2190
|
+
".parcel-cache",
|
|
2191
|
+
".idea",
|
|
2192
|
+
".vscode",
|
|
2193
|
+
".vscode-test",
|
|
2194
|
+
"ios",
|
|
2195
|
+
"android",
|
|
2196
|
+
// expo-managed native dirs are huge and rarely interesting
|
|
2197
|
+
".gradle",
|
|
2198
|
+
".cxx",
|
|
2199
|
+
".intellijPlatform",
|
|
2200
|
+
".kotlin",
|
|
2201
|
+
"tmp",
|
|
2202
|
+
"target",
|
|
2203
|
+
"venv",
|
|
2204
|
+
".venv",
|
|
2205
|
+
".mypy_cache",
|
|
2206
|
+
".pytest_cache",
|
|
2207
|
+
"__pycache__"
|
|
2208
|
+
]);
|
|
2209
|
+
function isUnder(parent, candidate) {
|
|
2210
|
+
const rel = path5.relative(parent, candidate);
|
|
2211
|
+
return rel === "" || !rel.startsWith("..") && !path5.isAbsolute(rel);
|
|
2212
|
+
}
|
|
2213
|
+
async function isExistingFile(absPath) {
|
|
2214
|
+
try {
|
|
2215
|
+
const stat2 = await fs5.stat(absPath);
|
|
2216
|
+
return stat2.isFile();
|
|
2217
|
+
} catch {
|
|
2218
|
+
return false;
|
|
2183
2219
|
}
|
|
2184
|
-
|
|
2220
|
+
}
|
|
2221
|
+
async function walkForSuffix(dir, needleVariants, depth, ctx) {
|
|
2222
|
+
if (depth > MAX_WALK_DEPTH) return;
|
|
2223
|
+
if (ctx.visited > MAX_VISITED_DIRS) return;
|
|
2224
|
+
if (ctx.matches.length >= ctx.cap) return;
|
|
2225
|
+
ctx.visited++;
|
|
2226
|
+
let entries = [];
|
|
2227
|
+
try {
|
|
2228
|
+
entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
2229
|
+
} catch {
|
|
2230
|
+
return;
|
|
2231
|
+
}
|
|
2232
|
+
for (const e of entries) {
|
|
2233
|
+
if (!e.isFile()) continue;
|
|
2234
|
+
const full = path5.join(dir, e.name);
|
|
2235
|
+
if (needleVariants.some((needle) => full.endsWith(needle))) {
|
|
2236
|
+
ctx.matches.push(full);
|
|
2237
|
+
if (ctx.matches.length >= ctx.cap) return;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
for (const e of entries) {
|
|
2241
|
+
if (!e.isDirectory()) continue;
|
|
2242
|
+
if (SUBDIR_IGNORE.has(e.name)) continue;
|
|
2243
|
+
if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
|
|
2244
|
+
await walkForSuffix(path5.join(dir, e.name), needleVariants, depth + 1, ctx);
|
|
2245
|
+
if (ctx.matches.length >= ctx.cap) return;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
async function findFile(rawPath) {
|
|
2249
|
+
const cwd = process.cwd();
|
|
2250
|
+
if (path5.isAbsolute(rawPath)) {
|
|
2251
|
+
const abs = path5.normalize(rawPath);
|
|
2252
|
+
if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
|
|
2253
|
+
}
|
|
2254
|
+
const direct = path5.resolve(cwd, rawPath);
|
|
2255
|
+
if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
|
|
2256
|
+
const normalized = path5.normalize(rawPath).replace(/^[./\\]+/, "");
|
|
2257
|
+
const needles = [
|
|
2258
|
+
`${path5.sep}${normalized}`,
|
|
2259
|
+
`/${normalized}`
|
|
2260
|
+
].filter((v, i, a) => a.indexOf(v) === i);
|
|
2261
|
+
const ctx = { visited: 0, matches: [], cap: 16 };
|
|
2262
|
+
await walkForSuffix(cwd, needles, 0, ctx);
|
|
2263
|
+
const candidates = ctx.matches.filter((c2) => isUnder(cwd, c2));
|
|
2264
|
+
if (candidates.length === 0) return null;
|
|
2265
|
+
candidates.sort((a, b) => a.length - b.length);
|
|
2266
|
+
return candidates[0];
|
|
2267
|
+
}
|
|
2268
|
+
async function findWriteTarget(rawPath) {
|
|
2269
|
+
const found = await findFile(rawPath);
|
|
2270
|
+
if (found) return found;
|
|
2271
|
+
const cwd = process.cwd();
|
|
2272
|
+
const fallback = path5.isAbsolute(rawPath) ? path5.normalize(rawPath) : path5.resolve(cwd, rawPath);
|
|
2273
|
+
if (!isUnder(cwd, fallback)) return null;
|
|
2274
|
+
return fallback;
|
|
2185
2275
|
}
|
|
2186
2276
|
function looksBinary(buf) {
|
|
2187
2277
|
const sample = buf.subarray(0, Math.min(8192, buf.length));
|
|
@@ -2192,11 +2282,11 @@ function looksBinary(buf) {
|
|
|
2192
2282
|
}
|
|
2193
2283
|
async function readProjectFile(rawPath) {
|
|
2194
2284
|
try {
|
|
2195
|
-
const abs =
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
return { error: "Not a regular file." };
|
|
2285
|
+
const abs = await findFile(rawPath);
|
|
2286
|
+
if (!abs) {
|
|
2287
|
+
return { error: `File not found in the project tree: ${rawPath}` };
|
|
2199
2288
|
}
|
|
2289
|
+
const stat2 = await fs5.stat(abs);
|
|
2200
2290
|
if (stat2.size > MAX_FILE_BYTES) {
|
|
2201
2291
|
return { error: `File too large (${(stat2.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
2202
2292
|
}
|
|
@@ -2212,7 +2302,10 @@ async function readProjectFile(rawPath) {
|
|
|
2212
2302
|
}
|
|
2213
2303
|
async function writeProjectFile(rawPath, content) {
|
|
2214
2304
|
try {
|
|
2215
|
-
const abs =
|
|
2305
|
+
const abs = await findWriteTarget(rawPath);
|
|
2306
|
+
if (!abs) {
|
|
2307
|
+
return { error: `Path escapes the project root: ${rawPath}` };
|
|
2308
|
+
}
|
|
2216
2309
|
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
2217
2310
|
return { error: "Content too large." };
|
|
2218
2311
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
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": {
|