openmagic 0.36.2 → 0.36.4
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/cli.js +147 -72
- package/dist/cli.js.map +1 -1
- package/dist/toolbar/index.global.js +19 -19
- package/dist/toolbar/index.global.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import open from "open";
|
|
7
|
-
import { resolve as resolve3, join as
|
|
7
|
+
import { resolve as resolve3, join as join5 } from "path";
|
|
8
8
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
9
9
|
import { spawn as spawn4 } from "child_process";
|
|
10
10
|
import { createInterface } from "readline";
|
|
@@ -32,7 +32,7 @@ function validateToken(token) {
|
|
|
32
32
|
|
|
33
33
|
// src/server.ts
|
|
34
34
|
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
|
|
35
|
-
import { join as
|
|
35
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
36
36
|
import { fileURLToPath } from "url";
|
|
37
37
|
import { WebSocketServer, WebSocket } from "ws";
|
|
38
38
|
|
|
@@ -1532,12 +1532,21 @@ async function chatClaudeCode(messages, context, onChunk, onDone, onError) {
|
|
|
1532
1532
|
"--output-format",
|
|
1533
1533
|
"stream-json",
|
|
1534
1534
|
"--verbose",
|
|
1535
|
+
"--include-partial-messages",
|
|
1535
1536
|
"--max-turns",
|
|
1536
1537
|
"5"
|
|
1537
1538
|
],
|
|
1538
1539
|
{
|
|
1539
1540
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1540
|
-
cwd: process.cwd()
|
|
1541
|
+
cwd: process.cwd(),
|
|
1542
|
+
env: {
|
|
1543
|
+
...process.env,
|
|
1544
|
+
// Generous timeouts — Claude may read files and think between turns
|
|
1545
|
+
CLAUDE_STREAM_IDLE_TIMEOUT_MS: "300000",
|
|
1546
|
+
// 5 min idle timeout between chunks
|
|
1547
|
+
API_TIMEOUT_MS: "600000"
|
|
1548
|
+
// 10 min overall API timeout
|
|
1549
|
+
}
|
|
1541
1550
|
}
|
|
1542
1551
|
);
|
|
1543
1552
|
proc.stdin.write(`${SYSTEM_PROMPT}
|
|
@@ -1602,7 +1611,7 @@ ${fullPrompt}`);
|
|
|
1602
1611
|
} else {
|
|
1603
1612
|
const err = errOutput.trim();
|
|
1604
1613
|
if (err.includes("not authenticated") || err.includes("login")) {
|
|
1605
|
-
onError("Claude CLI is not authenticated. Run `claude
|
|
1614
|
+
onError("Claude CLI is not authenticated. Run `claude` in your terminal to log in.");
|
|
1606
1615
|
} else if (err.includes("ENOENT") || err.includes("not found")) {
|
|
1607
1616
|
onError("Claude CLI not found. Install it with: npm install -g @anthropic-ai/claude-code");
|
|
1608
1617
|
} else {
|
|
@@ -1631,13 +1640,6 @@ function extractText(event) {
|
|
|
1631
1640
|
|
|
1632
1641
|
// src/llm/codex-cli.ts
|
|
1633
1642
|
import { spawn as spawn2 } from "child_process";
|
|
1634
|
-
import { writeFileSync as writeFileSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
1635
|
-
import { join as join3 } from "path";
|
|
1636
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
1637
|
-
import { randomBytes as randomBytes2 } from "crypto";
|
|
1638
|
-
function stripAnsi(text) {
|
|
1639
|
-
return text.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "");
|
|
1640
|
-
}
|
|
1641
1643
|
async function chatCodexCli(messages, context, onChunk, onDone, onError) {
|
|
1642
1644
|
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
1643
1645
|
const userPrompt = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "Help me with this element.";
|
|
@@ -1645,48 +1647,40 @@ async function chatCodexCli(messages, context, onChunk, onDone, onError) {
|
|
|
1645
1647
|
const fullPrompt = `${SYSTEM_PROMPT}
|
|
1646
1648
|
|
|
1647
1649
|
${buildUserMessage(userPrompt, contextParts)}`;
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
proc = spawn2("script", ["-q", "/dev/null", "codex", "--full-auto", "--quiet", "-"], {
|
|
1653
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1654
|
-
cwd: process.cwd()
|
|
1655
|
-
});
|
|
1656
|
-
proc.stdin.write(fullPrompt);
|
|
1657
|
-
proc.stdin.end();
|
|
1658
|
-
} else if (process.platform === "linux") {
|
|
1659
|
-
proc = spawn2("script", ["-qc", `codex --full-auto --quiet -`, "/dev/null"], {
|
|
1660
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1661
|
-
cwd: process.cwd()
|
|
1662
|
-
});
|
|
1663
|
-
proc.stdin.write(fullPrompt);
|
|
1664
|
-
proc.stdin.end();
|
|
1665
|
-
} else {
|
|
1666
|
-
proc = spawn2("codex", ["--full-auto", "--quiet", "-"], {
|
|
1650
|
+
const proc = spawn2(
|
|
1651
|
+
"codex",
|
|
1652
|
+
["exec", "--full-auto", "--json", "--skip-git-repo-check", "-"],
|
|
1653
|
+
{
|
|
1667
1654
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1668
1655
|
cwd: process.cwd()
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1658
|
+
proc.stdin.write(fullPrompt);
|
|
1659
|
+
proc.stdin.end();
|
|
1673
1660
|
let fullContent = "";
|
|
1661
|
+
let buffer = "";
|
|
1674
1662
|
let errOutput = "";
|
|
1675
1663
|
proc.stdout.on("data", (data) => {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1664
|
+
buffer += data.toString();
|
|
1665
|
+
const lines = buffer.split("\n");
|
|
1666
|
+
buffer = lines.pop() || "";
|
|
1667
|
+
for (const line of lines) {
|
|
1668
|
+
if (!line.trim()) continue;
|
|
1669
|
+
try {
|
|
1670
|
+
const event = JSON.parse(line);
|
|
1671
|
+
const text = extractCodexText(event);
|
|
1672
|
+
if (text) {
|
|
1673
|
+
fullContent += text;
|
|
1674
|
+
onChunk(text);
|
|
1675
|
+
}
|
|
1676
|
+
} catch {
|
|
1677
|
+
}
|
|
1680
1678
|
}
|
|
1681
1679
|
});
|
|
1682
1680
|
proc.stderr.on("data", (data) => {
|
|
1683
1681
|
errOutput += data.toString();
|
|
1684
1682
|
});
|
|
1685
1683
|
proc.on("error", (err) => {
|
|
1686
|
-
try {
|
|
1687
|
-
unlinkSync2(tmpFile);
|
|
1688
|
-
} catch {
|
|
1689
|
-
}
|
|
1690
1684
|
if (err.message.includes("ENOENT")) {
|
|
1691
1685
|
onError("Codex CLI not found. Install it with: npm install -g @openai/codex");
|
|
1692
1686
|
} else {
|
|
@@ -1694,24 +1688,38 @@ ${buildUserMessage(userPrompt, contextParts)}`;
|
|
|
1694
1688
|
}
|
|
1695
1689
|
});
|
|
1696
1690
|
proc.on("close", (code) => {
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1691
|
+
if (buffer.trim()) {
|
|
1692
|
+
try {
|
|
1693
|
+
const event = JSON.parse(buffer);
|
|
1694
|
+
const text = extractCodexText(event);
|
|
1695
|
+
if (text) fullContent += text;
|
|
1696
|
+
} catch {
|
|
1697
|
+
}
|
|
1700
1698
|
}
|
|
1701
1699
|
if (code === 0 || fullContent.trim()) {
|
|
1702
1700
|
onDone({ content: fullContent });
|
|
1703
1701
|
} else {
|
|
1704
|
-
const err =
|
|
1702
|
+
const err = errOutput.trim();
|
|
1705
1703
|
if (err.includes("OPENAI_API_KEY") || err.includes("api key") || err.includes("unauthorized")) {
|
|
1706
1704
|
onError("Codex CLI requires OPENAI_API_KEY in your environment. Set it with: export OPENAI_API_KEY=sk-...");
|
|
1707
|
-
} else if (err.includes("stdin is not a terminal") || err.includes("not a tty")) {
|
|
1708
|
-
onError("Codex CLI requires a terminal. This platform may not support the PTY wrapper. Try using OpenAI provider instead.");
|
|
1709
1705
|
} else {
|
|
1710
1706
|
onError(err.slice(0, 500) || `Codex CLI exited with code ${code}`);
|
|
1711
1707
|
}
|
|
1712
1708
|
}
|
|
1713
1709
|
});
|
|
1714
1710
|
}
|
|
1711
|
+
function extractCodexText(event) {
|
|
1712
|
+
if (event.type === "item.completed" || event.type === "item.updated" || event.type === "item.started") {
|
|
1713
|
+
const item = event.item;
|
|
1714
|
+
if (item?.type === "agent_message" && typeof item.text === "string") {
|
|
1715
|
+
return item.text;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
if (event.type === "error" && typeof event.message === "string") {
|
|
1719
|
+
return void 0;
|
|
1720
|
+
}
|
|
1721
|
+
return void 0;
|
|
1722
|
+
}
|
|
1715
1723
|
|
|
1716
1724
|
// src/llm/gemini-cli.ts
|
|
1717
1725
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -1722,18 +1730,55 @@ async function chatGeminiCli(messages, context, onChunk, onDone, onError) {
|
|
|
1722
1730
|
const fullPrompt = `${SYSTEM_PROMPT}
|
|
1723
1731
|
|
|
1724
1732
|
${buildUserMessage(userPrompt, contextParts)}`;
|
|
1725
|
-
const proc = spawn3(
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1733
|
+
const proc = spawn3(
|
|
1734
|
+
"gemini",
|
|
1735
|
+
[
|
|
1736
|
+
"-p",
|
|
1737
|
+
userPrompt,
|
|
1738
|
+
"--output-format",
|
|
1739
|
+
"stream-json",
|
|
1740
|
+
"--yolo"
|
|
1741
|
+
],
|
|
1742
|
+
{
|
|
1743
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1744
|
+
cwd: process.cwd()
|
|
1745
|
+
}
|
|
1746
|
+
);
|
|
1747
|
+
proc.stdin.write(`${SYSTEM_PROMPT}
|
|
1748
|
+
|
|
1749
|
+
${buildUserMessage("", contextParts)}`);
|
|
1730
1750
|
proc.stdin.end();
|
|
1731
1751
|
let fullContent = "";
|
|
1752
|
+
let resultContent = "";
|
|
1753
|
+
let buffer = "";
|
|
1732
1754
|
let errOutput = "";
|
|
1733
1755
|
proc.stdout.on("data", (data) => {
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1756
|
+
buffer += data.toString();
|
|
1757
|
+
const lines = buffer.split("\n");
|
|
1758
|
+
buffer = lines.pop() || "";
|
|
1759
|
+
for (const line of lines) {
|
|
1760
|
+
if (!line.trim()) continue;
|
|
1761
|
+
try {
|
|
1762
|
+
const event = JSON.parse(line);
|
|
1763
|
+
if (event.type === "result") {
|
|
1764
|
+
if (typeof event.result === "string") {
|
|
1765
|
+
resultContent = event.result;
|
|
1766
|
+
}
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
const text = extractGeminiText(event);
|
|
1770
|
+
if (text) {
|
|
1771
|
+
fullContent += text;
|
|
1772
|
+
onChunk(text);
|
|
1773
|
+
}
|
|
1774
|
+
} catch {
|
|
1775
|
+
const trimmed = line.trim();
|
|
1776
|
+
if (trimmed) {
|
|
1777
|
+
fullContent += trimmed + "\n";
|
|
1778
|
+
onChunk(trimmed + "\n");
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1737
1782
|
});
|
|
1738
1783
|
proc.stderr.on("data", (data) => {
|
|
1739
1784
|
errOutput += data.toString();
|
|
@@ -1746,18 +1791,48 @@ ${buildUserMessage(userPrompt, contextParts)}`;
|
|
|
1746
1791
|
}
|
|
1747
1792
|
});
|
|
1748
1793
|
proc.on("close", (code) => {
|
|
1749
|
-
if (
|
|
1750
|
-
|
|
1794
|
+
if (buffer.trim()) {
|
|
1795
|
+
try {
|
|
1796
|
+
const event = JSON.parse(buffer);
|
|
1797
|
+
if (event.type === "result" && typeof event.result === "string") {
|
|
1798
|
+
resultContent = event.result;
|
|
1799
|
+
} else {
|
|
1800
|
+
const text = extractGeminiText(event);
|
|
1801
|
+
if (text) fullContent += text;
|
|
1802
|
+
}
|
|
1803
|
+
} catch {
|
|
1804
|
+
const trimmed = buffer.trim();
|
|
1805
|
+
if (trimmed) fullContent += trimmed;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
const finalContent = resultContent || fullContent;
|
|
1809
|
+
if (code === 0 || finalContent.trim()) {
|
|
1810
|
+
onDone({ content: finalContent });
|
|
1751
1811
|
} else {
|
|
1752
1812
|
const err = errOutput.trim();
|
|
1753
|
-
if (err.includes("auth") || err.includes("
|
|
1754
|
-
onError("Gemini CLI requires
|
|
1813
|
+
if (err.includes("auth") || err.includes("GEMINI_API_KEY") || err.includes("credentials") || err.includes("login")) {
|
|
1814
|
+
onError("Gemini CLI requires authentication. Set GEMINI_API_KEY in your environment, or run `gemini` interactively to log in with Google.");
|
|
1755
1815
|
} else {
|
|
1756
1816
|
onError(err.slice(0, 500) || `Gemini CLI exited with code ${code}`);
|
|
1757
1817
|
}
|
|
1758
1818
|
}
|
|
1759
1819
|
});
|
|
1760
1820
|
}
|
|
1821
|
+
function extractGeminiText(event) {
|
|
1822
|
+
if (event.type === "assistant" || event.type === "message") {
|
|
1823
|
+
const content = event.content ?? event.message?.content;
|
|
1824
|
+
if (Array.isArray(content)) {
|
|
1825
|
+
return content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("");
|
|
1826
|
+
}
|
|
1827
|
+
if (typeof content === "string") return content;
|
|
1828
|
+
if (typeof event.text === "string") return event.text;
|
|
1829
|
+
}
|
|
1830
|
+
if (event.type === "content_block_delta") {
|
|
1831
|
+
const delta = event.delta;
|
|
1832
|
+
if (typeof delta?.text === "string") return delta.text;
|
|
1833
|
+
}
|
|
1834
|
+
return void 0;
|
|
1835
|
+
}
|
|
1761
1836
|
|
|
1762
1837
|
// src/llm/proxy.ts
|
|
1763
1838
|
var OPENAI_COMPATIBLE_PROVIDERS = /* @__PURE__ */ new Set([
|
|
@@ -2147,7 +2222,7 @@ async function handleMessage(ws, msg, state, roots) {
|
|
|
2147
2222
|
sendError(ws, "invalid_payload", "Missing pattern", msg.id);
|
|
2148
2223
|
break;
|
|
2149
2224
|
}
|
|
2150
|
-
const searchRoot = payload.path ?
|
|
2225
|
+
const searchRoot = payload.path ? join3(roots[0] || process.cwd(), payload.path) : roots[0] || process.cwd();
|
|
2151
2226
|
const results = grepFiles(payload.pattern, searchRoot, roots);
|
|
2152
2227
|
send(ws, { id: msg.id, type: "fs.grep.result", payload: { results } });
|
|
2153
2228
|
break;
|
|
@@ -2183,8 +2258,8 @@ function sendError(ws, code, message, id) {
|
|
|
2183
2258
|
}
|
|
2184
2259
|
function serveToolbarBundle(res) {
|
|
2185
2260
|
const bundlePaths = [
|
|
2186
|
-
|
|
2187
|
-
|
|
2261
|
+
join3(__dirname, "toolbar", "index.global.js"),
|
|
2262
|
+
join3(__dirname, "..", "dist", "toolbar", "index.global.js")
|
|
2188
2263
|
];
|
|
2189
2264
|
for (const bundlePath of bundlePaths) {
|
|
2190
2265
|
try {
|
|
@@ -2333,7 +2408,7 @@ function buildInjectionScript(token) {
|
|
|
2333
2408
|
// src/detect.ts
|
|
2334
2409
|
import { createConnection } from "net";
|
|
2335
2410
|
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
2336
|
-
import { join as
|
|
2411
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
2337
2412
|
import { execSync } from "child_process";
|
|
2338
2413
|
var COMMON_DEV_PORTS = [
|
|
2339
2414
|
3e3,
|
|
@@ -2490,7 +2565,7 @@ var FRAMEWORK_PATTERNS = [
|
|
|
2490
2565
|
];
|
|
2491
2566
|
var DEV_SCRIPT_NAMES = ["dev", "start", "serve", "develop", "dev:start", "start:dev", "server", "dev:server", "web", "frontend"];
|
|
2492
2567
|
function detectDevScripts(cwd = process.cwd()) {
|
|
2493
|
-
const pkgPath =
|
|
2568
|
+
const pkgPath = join4(cwd, "package.json");
|
|
2494
2569
|
if (!existsSync4(pkgPath)) return [];
|
|
2495
2570
|
let pkg;
|
|
2496
2571
|
try {
|
|
@@ -2558,7 +2633,7 @@ function checkNodeCompatibility(framework) {
|
|
|
2558
2633
|
function checkEnvPort(cwd = process.cwd()) {
|
|
2559
2634
|
const envFiles = [".env.local", ".env.development.local", ".env.development", ".env"];
|
|
2560
2635
|
for (const envFile of envFiles) {
|
|
2561
|
-
const envPath =
|
|
2636
|
+
const envPath = join4(cwd, envFile);
|
|
2562
2637
|
if (!existsSync4(envPath)) continue;
|
|
2563
2638
|
try {
|
|
2564
2639
|
const content = readFileSync4(envPath, "utf-8");
|
|
@@ -2578,7 +2653,7 @@ function scanParentLockfiles(projectDir) {
|
|
|
2578
2653
|
let dir = resolve2(project, "..");
|
|
2579
2654
|
while (dir.length > 1 && dir.length >= home.length) {
|
|
2580
2655
|
for (const lockfile of LOCKFILE_NAMES) {
|
|
2581
|
-
const p =
|
|
2656
|
+
const p = join4(dir, lockfile);
|
|
2582
2657
|
if (existsSync4(p)) found.push(p);
|
|
2583
2658
|
}
|
|
2584
2659
|
const parent = resolve2(dir, "..");
|
|
@@ -2588,7 +2663,7 @@ function scanParentLockfiles(projectDir) {
|
|
|
2588
2663
|
return found;
|
|
2589
2664
|
}
|
|
2590
2665
|
function getProjectName(cwd = process.cwd()) {
|
|
2591
|
-
const pkgPath =
|
|
2666
|
+
const pkgPath = join4(cwd, "package.json");
|
|
2592
2667
|
if (!existsSync4(pkgPath)) return "this project";
|
|
2593
2668
|
try {
|
|
2594
2669
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
@@ -2611,10 +2686,10 @@ var INSTALL_COMMANDS = {
|
|
|
2611
2686
|
bun: "bun install"
|
|
2612
2687
|
};
|
|
2613
2688
|
function checkDependenciesInstalled(cwd = process.cwd()) {
|
|
2614
|
-
const hasNodeModules = existsSync4(
|
|
2689
|
+
const hasNodeModules = existsSync4(join4(cwd, "node_modules"));
|
|
2615
2690
|
let pm = "npm";
|
|
2616
2691
|
for (const { file, pm: detectedPm } of LOCK_FILES) {
|
|
2617
|
-
if (existsSync4(
|
|
2692
|
+
if (existsSync4(join4(cwd, file))) {
|
|
2618
2693
|
pm = detectedPm;
|
|
2619
2694
|
break;
|
|
2620
2695
|
}
|
|
@@ -2962,7 +3037,7 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
2962
3037
|
const projectName = getProjectName();
|
|
2963
3038
|
const scripts = detectDevScripts();
|
|
2964
3039
|
if (scripts.length === 0) {
|
|
2965
|
-
const htmlPath =
|
|
3040
|
+
const htmlPath = join5(process.cwd(), "index.html");
|
|
2966
3041
|
if (existsSync5(htmlPath)) {
|
|
2967
3042
|
console.log(
|
|
2968
3043
|
chalk.dim(" No dev scripts found, but index.html detected.")
|
|
@@ -3227,7 +3302,7 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
3227
3302
|
);
|
|
3228
3303
|
console.log("");
|
|
3229
3304
|
try {
|
|
3230
|
-
const pkgPath =
|
|
3305
|
+
const pkgPath = join5(process.cwd(), "package.json");
|
|
3231
3306
|
if (existsSync5(pkgPath)) {
|
|
3232
3307
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
3233
3308
|
if (pkg.engines?.node) {
|