openmagic 0.36.1 → 0.36.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/cli.js +117 -32
- package/dist/cli.js.map +1 -1
- package/dist/toolbar/index.global.js +1 -1
- 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 join6 } 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 join4, dirname as dirname2 } from "path";
|
|
36
36
|
import { fileURLToPath } from "url";
|
|
37
37
|
import { WebSocketServer, WebSocket } from "ws";
|
|
38
38
|
|
|
@@ -1533,8 +1533,7 @@ async function chatClaudeCode(messages, context, onChunk, onDone, onError) {
|
|
|
1533
1533
|
"stream-json",
|
|
1534
1534
|
"--verbose",
|
|
1535
1535
|
"--max-turns",
|
|
1536
|
-
"
|
|
1537
|
-
// Single turn — OpenMagic manages its own retry loop
|
|
1536
|
+
"5"
|
|
1538
1537
|
],
|
|
1539
1538
|
{
|
|
1540
1539
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1546,6 +1545,7 @@ async function chatClaudeCode(messages, context, onChunk, onDone, onError) {
|
|
|
1546
1545
|
${fullPrompt}`);
|
|
1547
1546
|
proc.stdin.end();
|
|
1548
1547
|
let fullContent = "";
|
|
1548
|
+
let resultContent = "";
|
|
1549
1549
|
let buffer = "";
|
|
1550
1550
|
let errOutput = "";
|
|
1551
1551
|
proc.stdout.on("data", (data) => {
|
|
@@ -1556,6 +1556,12 @@ ${fullPrompt}`);
|
|
|
1556
1556
|
if (!line.trim()) continue;
|
|
1557
1557
|
try {
|
|
1558
1558
|
const event = JSON.parse(line);
|
|
1559
|
+
if (event.type === "result") {
|
|
1560
|
+
if (typeof event.result === "string") {
|
|
1561
|
+
resultContent = event.result;
|
|
1562
|
+
}
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1559
1565
|
const text = extractText(event);
|
|
1560
1566
|
if (text) {
|
|
1561
1567
|
fullContent += text;
|
|
@@ -1581,16 +1587,18 @@ ${fullPrompt}`);
|
|
|
1581
1587
|
if (buffer.trim()) {
|
|
1582
1588
|
try {
|
|
1583
1589
|
const event = JSON.parse(buffer);
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1590
|
+
if (event.type === "result" && typeof event.result === "string") {
|
|
1591
|
+
resultContent = event.result;
|
|
1592
|
+
} else {
|
|
1593
|
+
const text = extractText(event);
|
|
1594
|
+
if (text) fullContent += text;
|
|
1588
1595
|
}
|
|
1589
1596
|
} catch {
|
|
1590
1597
|
}
|
|
1591
1598
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1599
|
+
const finalContent = resultContent || fullContent;
|
|
1600
|
+
if (code === 0 || finalContent) {
|
|
1601
|
+
onDone({ content: finalContent });
|
|
1594
1602
|
} else {
|
|
1595
1603
|
const err = errOutput.trim();
|
|
1596
1604
|
if (err.includes("not authenticated") || err.includes("login")) {
|
|
@@ -1623,6 +1631,13 @@ function extractText(event) {
|
|
|
1623
1631
|
|
|
1624
1632
|
// src/llm/codex-cli.ts
|
|
1625
1633
|
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
|
+
}
|
|
1626
1641
|
async function chatCodexCli(messages, context, onChunk, onDone, onError) {
|
|
1627
1642
|
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
1628
1643
|
const userPrompt = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "Help me with this element.";
|
|
@@ -1630,21 +1645,48 @@ async function chatCodexCli(messages, context, onChunk, onDone, onError) {
|
|
|
1630
1645
|
const fullPrompt = `${SYSTEM_PROMPT}
|
|
1631
1646
|
|
|
1632
1647
|
${buildUserMessage(userPrompt, contextParts)}`;
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1648
|
+
const tmpFile = join3(tmpdir2(), `openmagic-codex-${randomBytes2(8).toString("hex")}.txt`);
|
|
1649
|
+
writeFileSync3(tmpFile, fullPrompt);
|
|
1650
|
+
let proc;
|
|
1651
|
+
if (process.platform === "darwin") {
|
|
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", "-"], {
|
|
1667
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1668
|
+
cwd: process.cwd()
|
|
1669
|
+
});
|
|
1670
|
+
proc.stdin.write(fullPrompt);
|
|
1671
|
+
proc.stdin.end();
|
|
1672
|
+
}
|
|
1637
1673
|
let fullContent = "";
|
|
1638
1674
|
let errOutput = "";
|
|
1639
1675
|
proc.stdout.on("data", (data) => {
|
|
1640
|
-
const text = data.toString();
|
|
1641
|
-
|
|
1642
|
-
|
|
1676
|
+
const text = stripAnsi(data.toString());
|
|
1677
|
+
if (text) {
|
|
1678
|
+
fullContent += text;
|
|
1679
|
+
onChunk(text);
|
|
1680
|
+
}
|
|
1643
1681
|
});
|
|
1644
1682
|
proc.stderr.on("data", (data) => {
|
|
1645
1683
|
errOutput += data.toString();
|
|
1646
1684
|
});
|
|
1647
1685
|
proc.on("error", (err) => {
|
|
1686
|
+
try {
|
|
1687
|
+
unlinkSync2(tmpFile);
|
|
1688
|
+
} catch {
|
|
1689
|
+
}
|
|
1648
1690
|
if (err.message.includes("ENOENT")) {
|
|
1649
1691
|
onError("Codex CLI not found. Install it with: npm install -g @openai/codex");
|
|
1650
1692
|
} else {
|
|
@@ -1652,12 +1694,18 @@ ${buildUserMessage(userPrompt, contextParts)}`;
|
|
|
1652
1694
|
}
|
|
1653
1695
|
});
|
|
1654
1696
|
proc.on("close", (code) => {
|
|
1655
|
-
|
|
1697
|
+
try {
|
|
1698
|
+
unlinkSync2(tmpFile);
|
|
1699
|
+
} catch {
|
|
1700
|
+
}
|
|
1701
|
+
if (code === 0 || fullContent.trim()) {
|
|
1656
1702
|
onDone({ content: fullContent });
|
|
1657
1703
|
} else {
|
|
1658
|
-
const err = errOutput.trim();
|
|
1704
|
+
const err = stripAnsi(errOutput.trim());
|
|
1659
1705
|
if (err.includes("OPENAI_API_KEY") || err.includes("api key") || err.includes("unauthorized")) {
|
|
1660
1706
|
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.");
|
|
1661
1709
|
} else {
|
|
1662
1710
|
onError(err.slice(0, 500) || `Codex CLI exited with code ${code}`);
|
|
1663
1711
|
}
|
|
@@ -2099,7 +2147,7 @@ async function handleMessage(ws, msg, state, roots) {
|
|
|
2099
2147
|
sendError(ws, "invalid_payload", "Missing pattern", msg.id);
|
|
2100
2148
|
break;
|
|
2101
2149
|
}
|
|
2102
|
-
const searchRoot = payload.path ?
|
|
2150
|
+
const searchRoot = payload.path ? join4(roots[0] || process.cwd(), payload.path) : roots[0] || process.cwd();
|
|
2103
2151
|
const results = grepFiles(payload.pattern, searchRoot, roots);
|
|
2104
2152
|
send(ws, { id: msg.id, type: "fs.grep.result", payload: { results } });
|
|
2105
2153
|
break;
|
|
@@ -2135,8 +2183,8 @@ function sendError(ws, code, message, id) {
|
|
|
2135
2183
|
}
|
|
2136
2184
|
function serveToolbarBundle(res) {
|
|
2137
2185
|
const bundlePaths = [
|
|
2138
|
-
|
|
2139
|
-
|
|
2186
|
+
join4(__dirname, "toolbar", "index.global.js"),
|
|
2187
|
+
join4(__dirname, "..", "dist", "toolbar", "index.global.js")
|
|
2140
2188
|
];
|
|
2141
2189
|
for (const bundlePath of bundlePaths) {
|
|
2142
2190
|
try {
|
|
@@ -2285,7 +2333,7 @@ function buildInjectionScript(token) {
|
|
|
2285
2333
|
// src/detect.ts
|
|
2286
2334
|
import { createConnection } from "net";
|
|
2287
2335
|
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
2288
|
-
import { join as
|
|
2336
|
+
import { join as join5, resolve as resolve2 } from "path";
|
|
2289
2337
|
import { execSync } from "child_process";
|
|
2290
2338
|
var COMMON_DEV_PORTS = [
|
|
2291
2339
|
3e3,
|
|
@@ -2442,7 +2490,7 @@ var FRAMEWORK_PATTERNS = [
|
|
|
2442
2490
|
];
|
|
2443
2491
|
var DEV_SCRIPT_NAMES = ["dev", "start", "serve", "develop", "dev:start", "start:dev", "server", "dev:server", "web", "frontend"];
|
|
2444
2492
|
function detectDevScripts(cwd = process.cwd()) {
|
|
2445
|
-
const pkgPath =
|
|
2493
|
+
const pkgPath = join5(cwd, "package.json");
|
|
2446
2494
|
if (!existsSync4(pkgPath)) return [];
|
|
2447
2495
|
let pkg;
|
|
2448
2496
|
try {
|
|
@@ -2510,7 +2558,7 @@ function checkNodeCompatibility(framework) {
|
|
|
2510
2558
|
function checkEnvPort(cwd = process.cwd()) {
|
|
2511
2559
|
const envFiles = [".env.local", ".env.development.local", ".env.development", ".env"];
|
|
2512
2560
|
for (const envFile of envFiles) {
|
|
2513
|
-
const envPath =
|
|
2561
|
+
const envPath = join5(cwd, envFile);
|
|
2514
2562
|
if (!existsSync4(envPath)) continue;
|
|
2515
2563
|
try {
|
|
2516
2564
|
const content = readFileSync4(envPath, "utf-8");
|
|
@@ -2530,7 +2578,7 @@ function scanParentLockfiles(projectDir) {
|
|
|
2530
2578
|
let dir = resolve2(project, "..");
|
|
2531
2579
|
while (dir.length > 1 && dir.length >= home.length) {
|
|
2532
2580
|
for (const lockfile of LOCKFILE_NAMES) {
|
|
2533
|
-
const p =
|
|
2581
|
+
const p = join5(dir, lockfile);
|
|
2534
2582
|
if (existsSync4(p)) found.push(p);
|
|
2535
2583
|
}
|
|
2536
2584
|
const parent = resolve2(dir, "..");
|
|
@@ -2540,7 +2588,7 @@ function scanParentLockfiles(projectDir) {
|
|
|
2540
2588
|
return found;
|
|
2541
2589
|
}
|
|
2542
2590
|
function getProjectName(cwd = process.cwd()) {
|
|
2543
|
-
const pkgPath =
|
|
2591
|
+
const pkgPath = join5(cwd, "package.json");
|
|
2544
2592
|
if (!existsSync4(pkgPath)) return "this project";
|
|
2545
2593
|
try {
|
|
2546
2594
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
@@ -2563,10 +2611,10 @@ var INSTALL_COMMANDS = {
|
|
|
2563
2611
|
bun: "bun install"
|
|
2564
2612
|
};
|
|
2565
2613
|
function checkDependenciesInstalled(cwd = process.cwd()) {
|
|
2566
|
-
const hasNodeModules = existsSync4(
|
|
2614
|
+
const hasNodeModules = existsSync4(join5(cwd, "node_modules"));
|
|
2567
2615
|
let pm = "npm";
|
|
2568
2616
|
for (const { file, pm: detectedPm } of LOCK_FILES) {
|
|
2569
|
-
if (existsSync4(
|
|
2617
|
+
if (existsSync4(join5(cwd, file))) {
|
|
2570
2618
|
pm = detectedPm;
|
|
2571
2619
|
break;
|
|
2572
2620
|
}
|
|
@@ -2863,12 +2911,49 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
2863
2911
|
});
|
|
2864
2912
|
}
|
|
2865
2913
|
});
|
|
2914
|
+
let shuttingDown = false;
|
|
2866
2915
|
const shutdown = () => {
|
|
2916
|
+
if (shuttingDown) return;
|
|
2917
|
+
shuttingDown = true;
|
|
2867
2918
|
console.log("");
|
|
2868
2919
|
console.log(chalk.dim(" Shutting down OpenMagic..."));
|
|
2869
2920
|
cleanupBackups();
|
|
2870
2921
|
proxyServer.close();
|
|
2871
|
-
|
|
2922
|
+
for (const cp of childProcesses) {
|
|
2923
|
+
try {
|
|
2924
|
+
cp.kill("SIGTERM");
|
|
2925
|
+
} catch {
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
const forceExit = setTimeout(() => {
|
|
2929
|
+
for (const cp of childProcesses) {
|
|
2930
|
+
try {
|
|
2931
|
+
cp.kill("SIGKILL");
|
|
2932
|
+
} catch {
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
process.exit(0);
|
|
2936
|
+
}, 2e3);
|
|
2937
|
+
forceExit.unref();
|
|
2938
|
+
const allDead = childProcesses.every((cp) => cp.killed || cp.exitCode !== null);
|
|
2939
|
+
if (allDead) {
|
|
2940
|
+
clearTimeout(forceExit);
|
|
2941
|
+
process.exit(0);
|
|
2942
|
+
}
|
|
2943
|
+
let remaining = childProcesses.filter((cp) => !cp.killed && cp.exitCode === null).length;
|
|
2944
|
+
if (remaining === 0) {
|
|
2945
|
+
clearTimeout(forceExit);
|
|
2946
|
+
process.exit(0);
|
|
2947
|
+
}
|
|
2948
|
+
for (const cp of childProcesses) {
|
|
2949
|
+
cp.once("exit", () => {
|
|
2950
|
+
remaining--;
|
|
2951
|
+
if (remaining <= 0) {
|
|
2952
|
+
clearTimeout(forceExit);
|
|
2953
|
+
process.exit(0);
|
|
2954
|
+
}
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2872
2957
|
};
|
|
2873
2958
|
process.on("SIGINT", shutdown);
|
|
2874
2959
|
process.on("SIGTERM", shutdown);
|
|
@@ -2877,7 +2962,7 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
2877
2962
|
const projectName = getProjectName();
|
|
2878
2963
|
const scripts = detectDevScripts();
|
|
2879
2964
|
if (scripts.length === 0) {
|
|
2880
|
-
const htmlPath =
|
|
2965
|
+
const htmlPath = join6(process.cwd(), "index.html");
|
|
2881
2966
|
if (existsSync5(htmlPath)) {
|
|
2882
2967
|
console.log(
|
|
2883
2968
|
chalk.dim(" No dev scripts found, but index.html detected.")
|
|
@@ -3142,7 +3227,7 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
3142
3227
|
);
|
|
3143
3228
|
console.log("");
|
|
3144
3229
|
try {
|
|
3145
|
-
const pkgPath =
|
|
3230
|
+
const pkgPath = join6(process.cwd(), "package.json");
|
|
3146
3231
|
if (existsSync5(pkgPath)) {
|
|
3147
3232
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
3148
3233
|
if (pkg.engines?.node) {
|