codeam-cli 2.1.1 → 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.
- package/dist/index.js +225 -73
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -87,9 +87,9 @@ var require_src = __commonJS({
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
// src/commands/start.ts
|
|
90
|
-
var
|
|
90
|
+
var fs6 = __toESM(require("fs"));
|
|
91
91
|
var os5 = __toESM(require("os"));
|
|
92
|
-
var
|
|
92
|
+
var path6 = __toESM(require("path"));
|
|
93
93
|
var import_crypto = require("crypto");
|
|
94
94
|
var import_child_process3 = require("child_process");
|
|
95
95
|
var import_picocolors2 = __toESM(require("picocolors"));
|
|
@@ -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.
|
|
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: {
|
|
@@ -298,6 +298,16 @@ function showPairingCode(code, expiresAt) {
|
|
|
298
298
|
|
|
299
299
|
// src/services/websocket.service.ts
|
|
300
300
|
var import_ws = __toESM(require("ws"));
|
|
301
|
+
|
|
302
|
+
// src/lib/poll-delay.ts
|
|
303
|
+
var MAX_DELAY_MS = 3e4;
|
|
304
|
+
function computePollDelay({ baseMs, failures }) {
|
|
305
|
+
const exp = Math.min(MAX_DELAY_MS, baseMs * Math.pow(2, failures));
|
|
306
|
+
const jitter = exp * (0.9 + Math.random() * 0.2);
|
|
307
|
+
return Math.round(jitter);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/services/websocket.service.ts
|
|
301
311
|
var API_BASE = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
302
312
|
var WS_URL = API_BASE.replace("https://", "wss://").replace("http://", "ws://") + "/api/ws";
|
|
303
313
|
var HEARTBEAT_MS = 3e4;
|
|
@@ -350,7 +360,7 @@ var WebSocketService = class {
|
|
|
350
360
|
this.handlers.forEach((h) => h.onDisconnected());
|
|
351
361
|
if (this.reconnectAttempts < MAX_RECONNECT) {
|
|
352
362
|
this.reconnectAttempts++;
|
|
353
|
-
const delay =
|
|
363
|
+
const delay = computePollDelay({ baseMs: 1e3, failures: this.reconnectAttempts });
|
|
354
364
|
this.reconnectTimer = setTimeout(() => this.connect(), delay);
|
|
355
365
|
}
|
|
356
366
|
});
|
|
@@ -393,16 +403,6 @@ var WebSocketService = class {
|
|
|
393
403
|
var https = __toESM(require("https"));
|
|
394
404
|
var http = __toESM(require("http"));
|
|
395
405
|
var os2 = __toESM(require("os"));
|
|
396
|
-
|
|
397
|
-
// src/lib/poll-delay.ts
|
|
398
|
-
var MAX_DELAY_MS = 3e4;
|
|
399
|
-
function computePollDelay({ baseMs, failures }) {
|
|
400
|
-
const exp = Math.min(MAX_DELAY_MS, baseMs * Math.pow(2, failures));
|
|
401
|
-
const jitter = exp * (0.9 + Math.random() * 0.2);
|
|
402
|
-
return Math.round(jitter);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// src/services/pairing.service.ts
|
|
406
406
|
var API_BASE2 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
407
407
|
async function requestCode(pluginId) {
|
|
408
408
|
try {
|
|
@@ -476,7 +476,7 @@ var _transport = {
|
|
|
476
476
|
getJson: _getJson
|
|
477
477
|
};
|
|
478
478
|
async function _postJson(url, body) {
|
|
479
|
-
return new Promise((
|
|
479
|
+
return new Promise((resolve2, reject) => {
|
|
480
480
|
const data = JSON.stringify(body);
|
|
481
481
|
const u2 = new URL(url);
|
|
482
482
|
const transport = u2.protocol === "https:" ? https : http;
|
|
@@ -504,9 +504,9 @@ async function _postJson(url, body) {
|
|
|
504
504
|
return;
|
|
505
505
|
}
|
|
506
506
|
try {
|
|
507
|
-
|
|
507
|
+
resolve2(JSON.parse(body2));
|
|
508
508
|
} catch {
|
|
509
|
-
|
|
509
|
+
resolve2(null);
|
|
510
510
|
}
|
|
511
511
|
});
|
|
512
512
|
}
|
|
@@ -521,7 +521,7 @@ async function _postJson(url, body) {
|
|
|
521
521
|
});
|
|
522
522
|
}
|
|
523
523
|
async function _getJson(url) {
|
|
524
|
-
return new Promise((
|
|
524
|
+
return new Promise((resolve2, reject) => {
|
|
525
525
|
const u2 = new URL(url);
|
|
526
526
|
const transport = u2.protocol === "https:" ? https : http;
|
|
527
527
|
const req = transport.request(
|
|
@@ -544,9 +544,9 @@ async function _getJson(url) {
|
|
|
544
544
|
return;
|
|
545
545
|
}
|
|
546
546
|
try {
|
|
547
|
-
|
|
547
|
+
resolve2(JSON.parse(body));
|
|
548
548
|
} catch {
|
|
549
|
-
|
|
549
|
+
resolve2(null);
|
|
550
550
|
}
|
|
551
551
|
});
|
|
552
552
|
}
|
|
@@ -1661,20 +1661,20 @@ var OutputService = class _OutputService {
|
|
|
1661
1661
|
if (this.pluginAuthToken) {
|
|
1662
1662
|
headers["X-Plugin-Auth-Token"] = this.pluginAuthToken;
|
|
1663
1663
|
}
|
|
1664
|
-
return new Promise((
|
|
1664
|
+
return new Promise((resolve2) => {
|
|
1665
1665
|
const attempt = (attemptsLeft) => {
|
|
1666
1666
|
_transport2.sendOutputChunk(`${API_BASE4}/api/commands/output`, headers, payload).then(({ statusCode, body: resBody }) => {
|
|
1667
1667
|
if (statusCode >= 400) {
|
|
1668
1668
|
process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
|
|
1669
1669
|
`);
|
|
1670
1670
|
}
|
|
1671
|
-
|
|
1671
|
+
resolve2();
|
|
1672
1672
|
}).catch(() => {
|
|
1673
1673
|
if (attemptsLeft > 0) {
|
|
1674
1674
|
const delay = 200 * (maxRetries - attemptsLeft + 1);
|
|
1675
1675
|
setTimeout(() => attempt(attemptsLeft - 1), delay);
|
|
1676
1676
|
} else {
|
|
1677
|
-
|
|
1677
|
+
resolve2();
|
|
1678
1678
|
}
|
|
1679
1679
|
});
|
|
1680
1680
|
};
|
|
@@ -1686,7 +1686,7 @@ var _transport2 = {
|
|
|
1686
1686
|
sendOutputChunk: _sendOutputChunk
|
|
1687
1687
|
};
|
|
1688
1688
|
function _sendOutputChunk(url, headers, payload) {
|
|
1689
|
-
return new Promise((
|
|
1689
|
+
return new Promise((resolve2, reject) => {
|
|
1690
1690
|
let settled = false;
|
|
1691
1691
|
const u2 = new URL(url);
|
|
1692
1692
|
const transport = u2.protocol === "https:" ? https2 : http2;
|
|
@@ -1710,7 +1710,7 @@ function _sendOutputChunk(url, headers, payload) {
|
|
|
1710
1710
|
res.on("end", () => {
|
|
1711
1711
|
if (settled) return;
|
|
1712
1712
|
settled = true;
|
|
1713
|
-
|
|
1713
|
+
resolve2({ statusCode: res.statusCode ?? 0, body: resData });
|
|
1714
1714
|
});
|
|
1715
1715
|
}
|
|
1716
1716
|
);
|
|
@@ -1733,6 +1733,7 @@ var path4 = __toESM(require("path"));
|
|
|
1733
1733
|
var os4 = __toESM(require("os"));
|
|
1734
1734
|
var https3 = __toESM(require("https"));
|
|
1735
1735
|
var http3 = __toESM(require("http"));
|
|
1736
|
+
var import_zod = require("zod");
|
|
1736
1737
|
|
|
1737
1738
|
// src/services/logger.ts
|
|
1738
1739
|
var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
@@ -1754,6 +1755,16 @@ var log = {
|
|
|
1754
1755
|
};
|
|
1755
1756
|
|
|
1756
1757
|
// src/services/history.service.ts
|
|
1758
|
+
var historyRecordSchema = import_zod.z.object({
|
|
1759
|
+
type: import_zod.z.string().optional(),
|
|
1760
|
+
timestamp: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
|
|
1761
|
+
uuid: import_zod.z.string().optional(),
|
|
1762
|
+
isMeta: import_zod.z.boolean().optional(),
|
|
1763
|
+
message: import_zod.z.object({
|
|
1764
|
+
// Claude content is either a string or an array of typed blocks.
|
|
1765
|
+
content: import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.unknown())]).optional()
|
|
1766
|
+
}).passthrough().optional()
|
|
1767
|
+
}).passthrough();
|
|
1757
1768
|
var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
|
|
1758
1769
|
function encodeCwd(cwd) {
|
|
1759
1770
|
return cwd.replace(/\//g, "-");
|
|
@@ -1779,28 +1790,35 @@ function parseJsonl(filePath) {
|
|
|
1779
1790
|
}
|
|
1780
1791
|
const lines = raw.split("\n").filter(Boolean);
|
|
1781
1792
|
for (const line of lines) {
|
|
1793
|
+
let parsedJson;
|
|
1782
1794
|
try {
|
|
1783
|
-
|
|
1784
|
-
const type = record["type"];
|
|
1785
|
-
const msg = record["message"];
|
|
1786
|
-
const ts = record["timestamp"];
|
|
1787
|
-
const timestamp = typeof ts === "string" ? new Date(ts).getTime() : typeof ts === "number" ? ts : Date.now();
|
|
1788
|
-
const uuid = record["uuid"] ?? `${Date.now()}-${Math.random()}`;
|
|
1789
|
-
if (record["isMeta"]) continue;
|
|
1790
|
-
if (type === "user" && msg) {
|
|
1791
|
-
const text = extractText(msg["content"]).trim();
|
|
1792
|
-
if (text) messages.push({ id: uuid, role: "user", text, timestamp });
|
|
1793
|
-
} else if (type === "assistant" && msg) {
|
|
1794
|
-
const text = extractText(msg["content"]).trim();
|
|
1795
|
-
if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
|
|
1796
|
-
}
|
|
1795
|
+
parsedJson = JSON.parse(line);
|
|
1797
1796
|
} catch {
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
const result = historyRecordSchema.safeParse(parsedJson);
|
|
1800
|
+
if (!result.success) {
|
|
1801
|
+
log.warn("history:parseJsonl", `record failed schema validation in ${filePath}`, result.error.issues);
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
const record = result.data;
|
|
1805
|
+
if (record.isMeta) continue;
|
|
1806
|
+
const ts = record.timestamp;
|
|
1807
|
+
const timestamp = typeof ts === "string" ? new Date(ts).getTime() : typeof ts === "number" ? ts : Date.now();
|
|
1808
|
+
const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
|
|
1809
|
+
const msg = record.message;
|
|
1810
|
+
if (record.type === "user" && msg) {
|
|
1811
|
+
const text = extractText(msg.content).trim();
|
|
1812
|
+
if (text) messages.push({ id: uuid, role: "user", text, timestamp });
|
|
1813
|
+
} else if (record.type === "assistant" && msg) {
|
|
1814
|
+
const text = extractText(msg.content).trim();
|
|
1815
|
+
if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
|
|
1798
1816
|
}
|
|
1799
1817
|
}
|
|
1800
1818
|
return messages;
|
|
1801
1819
|
}
|
|
1802
1820
|
function post(endpoint, body) {
|
|
1803
|
-
return new Promise((
|
|
1821
|
+
return new Promise((resolve2) => {
|
|
1804
1822
|
const payload = JSON.stringify(body);
|
|
1805
1823
|
const u2 = new URL(`${API_BASE5}${endpoint}`);
|
|
1806
1824
|
const transport = u2.protocol === "https:" ? https3 : http3;
|
|
@@ -1820,17 +1838,17 @@ function post(endpoint, body) {
|
|
|
1820
1838
|
res.resume();
|
|
1821
1839
|
const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
|
|
1822
1840
|
if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
|
|
1823
|
-
|
|
1841
|
+
resolve2(ok);
|
|
1824
1842
|
}
|
|
1825
1843
|
);
|
|
1826
1844
|
req.on("error", (err) => {
|
|
1827
1845
|
log.warn("history:post", `${endpoint} network error`, err);
|
|
1828
|
-
|
|
1846
|
+
resolve2(false);
|
|
1829
1847
|
});
|
|
1830
1848
|
req.on("timeout", () => {
|
|
1831
1849
|
log.warn("history:post", `${endpoint} timeout after 15s`);
|
|
1832
1850
|
req.destroy();
|
|
1833
|
-
|
|
1851
|
+
resolve2(false);
|
|
1834
1852
|
});
|
|
1835
1853
|
req.write(payload);
|
|
1836
1854
|
req.end();
|
|
@@ -2127,32 +2145,146 @@ var HistoryService = class {
|
|
|
2127
2145
|
};
|
|
2128
2146
|
|
|
2129
2147
|
// src/lib/payload.ts
|
|
2130
|
-
var
|
|
2131
|
-
var fileEntrySchema =
|
|
2132
|
-
filename:
|
|
2133
|
-
mimeType:
|
|
2134
|
-
base64:
|
|
2148
|
+
var import_zod2 = require("zod");
|
|
2149
|
+
var fileEntrySchema = import_zod2.z.object({
|
|
2150
|
+
filename: import_zod2.z.string().min(1).max(256),
|
|
2151
|
+
mimeType: import_zod2.z.string(),
|
|
2152
|
+
base64: import_zod2.z.string()
|
|
2135
2153
|
});
|
|
2136
|
-
var startCommandSchema =
|
|
2137
|
-
prompt:
|
|
2138
|
-
files:
|
|
2139
|
-
input:
|
|
2140
|
-
index:
|
|
2141
|
-
from:
|
|
2142
|
-
id:
|
|
2143
|
-
auto:
|
|
2154
|
+
var startCommandSchema = import_zod2.z.object({
|
|
2155
|
+
prompt: import_zod2.z.string().optional(),
|
|
2156
|
+
files: import_zod2.z.array(fileEntrySchema).optional(),
|
|
2157
|
+
input: import_zod2.z.string().optional(),
|
|
2158
|
+
index: import_zod2.z.number().optional(),
|
|
2159
|
+
from: import_zod2.z.number().optional(),
|
|
2160
|
+
id: import_zod2.z.string().optional(),
|
|
2161
|
+
auto: import_zod2.z.boolean().optional(),
|
|
2162
|
+
// `read_file` / `write_file` for the mobile + landing mini-IDE modal.
|
|
2163
|
+
// `path` is bounded to 4096 chars (a comfortable POSIX path max) so a
|
|
2164
|
+
// malformed payload can't blow up the disk-side validator.
|
|
2165
|
+
path: import_zod2.z.string().min(1).max(4096).optional(),
|
|
2166
|
+
content: import_zod2.z.string().optional()
|
|
2144
2167
|
});
|
|
2145
2168
|
function parsePayload(schema, raw) {
|
|
2146
2169
|
const result = schema.safeParse(raw);
|
|
2147
2170
|
return result.success ? result.data : null;
|
|
2148
2171
|
}
|
|
2149
2172
|
|
|
2173
|
+
// src/services/file-ops.service.ts
|
|
2174
|
+
var fs5 = __toESM(require("fs/promises"));
|
|
2175
|
+
var path5 = __toESM(require("path"));
|
|
2176
|
+
var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
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) {
|
|
2208
|
+
const cwd = process.cwd();
|
|
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
|
+
}
|
|
2218
|
+
}
|
|
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;
|
|
2236
|
+
}
|
|
2237
|
+
function looksBinary(buf) {
|
|
2238
|
+
const sample = buf.subarray(0, Math.min(8192, buf.length));
|
|
2239
|
+
for (let i = 0; i < sample.length; i++) {
|
|
2240
|
+
if (sample[i] === 0) return true;
|
|
2241
|
+
}
|
|
2242
|
+
return false;
|
|
2243
|
+
}
|
|
2244
|
+
async function readProjectFile(rawPath) {
|
|
2245
|
+
try {
|
|
2246
|
+
const abs = await findFile(rawPath);
|
|
2247
|
+
if (!abs) {
|
|
2248
|
+
return { error: `File not found in the project tree: ${rawPath}` };
|
|
2249
|
+
}
|
|
2250
|
+
const stat2 = await fs5.stat(abs);
|
|
2251
|
+
if (stat2.size > MAX_FILE_BYTES) {
|
|
2252
|
+
return { error: `File too large (${(stat2.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
|
|
2253
|
+
}
|
|
2254
|
+
const buf = await fs5.readFile(abs);
|
|
2255
|
+
if (looksBinary(buf)) {
|
|
2256
|
+
return { error: "Binary file \u2014 refusing to open in a code editor." };
|
|
2257
|
+
}
|
|
2258
|
+
return { content: buf.toString("utf-8") };
|
|
2259
|
+
} catch (e) {
|
|
2260
|
+
const msg = e instanceof Error ? e.message : "Read failed";
|
|
2261
|
+
return { error: msg };
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
async function writeProjectFile(rawPath, content) {
|
|
2265
|
+
try {
|
|
2266
|
+
const abs = await findWriteTarget(rawPath);
|
|
2267
|
+
if (!abs) {
|
|
2268
|
+
return { error: `Path escapes the project root: ${rawPath}` };
|
|
2269
|
+
}
|
|
2270
|
+
if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
|
|
2271
|
+
return { error: "Content too large." };
|
|
2272
|
+
}
|
|
2273
|
+
await fs5.mkdir(path5.dirname(abs), { recursive: true });
|
|
2274
|
+
await fs5.writeFile(abs, content, "utf-8");
|
|
2275
|
+
return { ok: true };
|
|
2276
|
+
} catch (e) {
|
|
2277
|
+
const msg = e instanceof Error ? e.message : "Write failed";
|
|
2278
|
+
return { error: msg };
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2150
2282
|
// src/commands/start.ts
|
|
2151
2283
|
function saveFilesTemp(files) {
|
|
2152
2284
|
return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
|
|
2153
2285
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
2154
|
-
const tmpPath =
|
|
2155
|
-
|
|
2286
|
+
const tmpPath = path6.join(os5.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
|
|
2287
|
+
fs6.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
|
|
2156
2288
|
return tmpPath;
|
|
2157
2289
|
});
|
|
2158
2290
|
}
|
|
@@ -2225,8 +2357,8 @@ try:
|
|
|
2225
2357
|
sys.exit((st>>8)&0xFF)
|
|
2226
2358
|
except Exception:sys.exit(0)
|
|
2227
2359
|
`;
|
|
2228
|
-
const helperPath =
|
|
2229
|
-
|
|
2360
|
+
const helperPath = path6.join(os5.tmpdir(), "codeam-quota-helper.py");
|
|
2361
|
+
fs6.writeFileSync(helperPath, helperScript, { mode: 420 });
|
|
2230
2362
|
const python = findInPath("python3") ?? findInPath("python");
|
|
2231
2363
|
if (!python) {
|
|
2232
2364
|
quotaFetchInProgress = false;
|
|
@@ -2258,7 +2390,7 @@ except Exception:sys.exit(0)
|
|
|
2258
2390
|
} catch {
|
|
2259
2391
|
}
|
|
2260
2392
|
try {
|
|
2261
|
-
|
|
2393
|
+
fs6.unlinkSync(helperPath);
|
|
2262
2394
|
} catch {
|
|
2263
2395
|
}
|
|
2264
2396
|
quotaFetchInProgress = false;
|
|
@@ -2308,7 +2440,7 @@ except Exception:sys.exit(0)
|
|
|
2308
2440
|
setTimeout(() => {
|
|
2309
2441
|
for (const p2 of paths) {
|
|
2310
2442
|
try {
|
|
2311
|
-
|
|
2443
|
+
fs6.unlinkSync(p2);
|
|
2312
2444
|
} catch {
|
|
2313
2445
|
}
|
|
2314
2446
|
}
|
|
@@ -2380,6 +2512,26 @@ except Exception:sys.exit(0)
|
|
|
2380
2512
|
await relay.sendResult(cmd.id, "completed", { models });
|
|
2381
2513
|
break;
|
|
2382
2514
|
}
|
|
2515
|
+
case "read_file": {
|
|
2516
|
+
const { path: filePath } = parsed;
|
|
2517
|
+
if (!filePath) {
|
|
2518
|
+
await relay.sendResult(cmd.id, "failed", { error: "Missing path" });
|
|
2519
|
+
break;
|
|
2520
|
+
}
|
|
2521
|
+
const result = await readProjectFile(filePath);
|
|
2522
|
+
await relay.sendResult(cmd.id, "completed", result);
|
|
2523
|
+
break;
|
|
2524
|
+
}
|
|
2525
|
+
case "write_file": {
|
|
2526
|
+
const { path: filePath, content } = parsed;
|
|
2527
|
+
if (!filePath || typeof content !== "string") {
|
|
2528
|
+
await relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
|
|
2529
|
+
break;
|
|
2530
|
+
}
|
|
2531
|
+
const result = await writeProjectFile(filePath, content);
|
|
2532
|
+
await relay.sendResult(cmd.id, "completed", result);
|
|
2533
|
+
break;
|
|
2534
|
+
}
|
|
2383
2535
|
}
|
|
2384
2536
|
});
|
|
2385
2537
|
ws.addHandler({
|
|
@@ -2407,7 +2559,7 @@ except Exception:sys.exit(0)
|
|
|
2407
2559
|
setTimeout(() => {
|
|
2408
2560
|
for (const p2 of paths) {
|
|
2409
2561
|
try {
|
|
2410
|
-
|
|
2562
|
+
fs6.unlinkSync(p2);
|
|
2411
2563
|
} catch {
|
|
2412
2564
|
}
|
|
2413
2565
|
}
|
|
@@ -2509,7 +2661,7 @@ __export(dist_exports, {
|
|
|
2509
2661
|
S_ERROR: () => ge,
|
|
2510
2662
|
S_INFO: () => he,
|
|
2511
2663
|
S_PASSWORD_MASK: () => xe,
|
|
2512
|
-
S_RADIO_ACTIVE: () =>
|
|
2664
|
+
S_RADIO_ACTIVE: () => z4,
|
|
2513
2665
|
S_RADIO_INACTIVE: () => H2,
|
|
2514
2666
|
S_STEP_ACTIVE: () => _e,
|
|
2515
2667
|
S_STEP_CANCEL: () => oe,
|
|
@@ -2988,7 +3140,7 @@ function w(r, t2) {
|
|
|
2988
3140
|
const e = r;
|
|
2989
3141
|
e.isTTY && e.setRawMode(t2);
|
|
2990
3142
|
}
|
|
2991
|
-
function
|
|
3143
|
+
function z3({ input: r = import_node_process.stdin, output: t2 = import_node_process.stdout, overwrite: e = true, hideCursor: s = true } = {}) {
|
|
2992
3144
|
const i = _.createInterface({ input: r, output: t2, prompt: "", tabSize: 1 });
|
|
2993
3145
|
_.emitKeypressEvents(r, i), r instanceof import_node_tty.ReadStream && r.isTTY && r.setRawMode(true);
|
|
2994
3146
|
const n = (o, { name: a, sequence: h }) => {
|
|
@@ -3600,7 +3752,7 @@ var d2 = w2("\u2502", "|");
|
|
|
3600
3752
|
var E2 = w2("\u2514", "\u2014");
|
|
3601
3753
|
var Ie = w2("\u2510", "T");
|
|
3602
3754
|
var Ee = w2("\u2518", "\u2014");
|
|
3603
|
-
var
|
|
3755
|
+
var z4 = w2("\u25CF", ">");
|
|
3604
3756
|
var H2 = w2("\u25CB", " ");
|
|
3605
3757
|
var te = w2("\u25FB", "[\u2022]");
|
|
3606
3758
|
var U = w2("\u25FC", "[+]");
|
|
@@ -3692,7 +3844,7 @@ var Ae = (e) => new H({ options: e.options, initialValue: e.initialValue ? [e.in
|
|
|
3692
3844
|
const $2 = Me(a), y2 = a.hint && a.value === this.focusedValue ? (0, import_node_util2.styleText)("dim", ` (${a.hint})`) : "";
|
|
3693
3845
|
switch (l) {
|
|
3694
3846
|
case "active":
|
|
3695
|
-
return `${(0, import_node_util2.styleText)("green",
|
|
3847
|
+
return `${(0, import_node_util2.styleText)("green", z4)} ${$2}${y2}`;
|
|
3696
3848
|
case "inactive":
|
|
3697
3849
|
return `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", $2)}`;
|
|
3698
3850
|
case "disabled":
|
|
@@ -3805,9 +3957,9 @@ ${(0, import_node_util2.styleText)("gray", d2)}` : ""}`;
|
|
|
3805
3957
|
}
|
|
3806
3958
|
default: {
|
|
3807
3959
|
const l = r ? `${(0, import_node_util2.styleText)("cyan", d2)} ` : "", $2 = r ? (0, import_node_util2.styleText)("cyan", E2) : "";
|
|
3808
|
-
return `${c2}${l}${this.value ? `${(0, import_node_util2.styleText)("green",
|
|
3960
|
+
return `${c2}${l}${this.value ? `${(0, import_node_util2.styleText)("green", z4)} ${i}` : `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", i)}`}${e.vertical ? r ? `
|
|
3809
3961
|
${(0, import_node_util2.styleText)("cyan", d2)} ` : `
|
|
3810
|
-
` : ` ${(0, import_node_util2.styleText)("dim", "/")} `}${this.value ? `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", s)}` : `${(0, import_node_util2.styleText)("green",
|
|
3962
|
+
` : ` ${(0, import_node_util2.styleText)("dim", "/")} `}${this.value ? `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", s)}` : `${(0, import_node_util2.styleText)("green", z4)} ${s}`}
|
|
3811
3963
|
${$2}
|
|
3812
3964
|
`;
|
|
3813
3965
|
}
|
|
@@ -4138,7 +4290,7 @@ var fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, canc
|
|
|
4138
4290
|
const A2 = (performance.now() - _2) / 1e3, k2 = Math.floor(A2 / 60), L2 = Math.floor(A2 % 60);
|
|
4139
4291
|
return k2 > 0 ? `[${k2}m ${L2}s]` : `[${L2}s]`;
|
|
4140
4292
|
}, D2 = a.withGuide ?? u.withGuide, ie = (_2 = "") => {
|
|
4141
|
-
p2 = true, $2 =
|
|
4293
|
+
p2 = true, $2 = z3({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${(0, import_node_util2.styleText)("gray", d2)}
|
|
4142
4294
|
`);
|
|
4143
4295
|
let A2 = 0, k2 = 0;
|
|
4144
4296
|
x(), y2 = setInterval(() => {
|
|
@@ -4209,7 +4361,7 @@ var _t = (e) => {
|
|
|
4209
4361
|
case "selected":
|
|
4210
4362
|
return `${re(u2, (n) => (0, import_node_util2.styleText)("dim", n))}`;
|
|
4211
4363
|
case "active":
|
|
4212
|
-
return `${(0, import_node_util2.styleText)("green",
|
|
4364
|
+
return `${(0, import_node_util2.styleText)("green", z4)} ${u2}${s.hint ? ` ${(0, import_node_util2.styleText)("dim", `(${s.hint})`)}` : ""}`;
|
|
4213
4365
|
case "cancelled":
|
|
4214
4366
|
return `${re(u2, (n) => (0, import_node_util2.styleText)(["strikethrough", "dim"], n))}`;
|
|
4215
4367
|
default:
|
|
@@ -4437,7 +4589,7 @@ async function pair() {
|
|
|
4437
4589
|
console.log("");
|
|
4438
4590
|
const waitSpin = dist_exports.spinner();
|
|
4439
4591
|
waitSpin.start("Waiting for mobile app...");
|
|
4440
|
-
await new Promise((
|
|
4592
|
+
await new Promise((resolve2) => {
|
|
4441
4593
|
let stopPolling = null;
|
|
4442
4594
|
function sigintHandler() {
|
|
4443
4595
|
stopPolling?.();
|
|
@@ -4460,7 +4612,7 @@ async function pair() {
|
|
|
4460
4612
|
});
|
|
4461
4613
|
showSuccess(`Paired with ${info.userName} (${info.plan})`);
|
|
4462
4614
|
console.log("");
|
|
4463
|
-
|
|
4615
|
+
resolve2();
|
|
4464
4616
|
},
|
|
4465
4617
|
() => {
|
|
4466
4618
|
waitSpin.stop("Timed out");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
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": {
|