openmagic 0.36.0 → 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 +118 -35
- 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
|
|
|
@@ -1531,9 +1531,9 @@ async function chatClaudeCode(messages, context, onChunk, onDone, onError) {
|
|
|
1531
1531
|
"-p",
|
|
1532
1532
|
"--output-format",
|
|
1533
1533
|
"stream-json",
|
|
1534
|
+
"--verbose",
|
|
1534
1535
|
"--max-turns",
|
|
1535
|
-
"
|
|
1536
|
-
// Single turn — OpenMagic manages its own retry loop
|
|
1536
|
+
"5"
|
|
1537
1537
|
],
|
|
1538
1538
|
{
|
|
1539
1539
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1545,6 +1545,7 @@ async function chatClaudeCode(messages, context, onChunk, onDone, onError) {
|
|
|
1545
1545
|
${fullPrompt}`);
|
|
1546
1546
|
proc.stdin.end();
|
|
1547
1547
|
let fullContent = "";
|
|
1548
|
+
let resultContent = "";
|
|
1548
1549
|
let buffer = "";
|
|
1549
1550
|
let errOutput = "";
|
|
1550
1551
|
proc.stdout.on("data", (data) => {
|
|
@@ -1555,6 +1556,12 @@ ${fullPrompt}`);
|
|
|
1555
1556
|
if (!line.trim()) continue;
|
|
1556
1557
|
try {
|
|
1557
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
|
+
}
|
|
1558
1565
|
const text = extractText(event);
|
|
1559
1566
|
if (text) {
|
|
1560
1567
|
fullContent += text;
|
|
@@ -1580,16 +1587,18 @@ ${fullPrompt}`);
|
|
|
1580
1587
|
if (buffer.trim()) {
|
|
1581
1588
|
try {
|
|
1582
1589
|
const event = JSON.parse(buffer);
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
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;
|
|
1587
1595
|
}
|
|
1588
1596
|
} catch {
|
|
1589
1597
|
}
|
|
1590
1598
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1599
|
+
const finalContent = resultContent || fullContent;
|
|
1600
|
+
if (code === 0 || finalContent) {
|
|
1601
|
+
onDone({ content: finalContent });
|
|
1593
1602
|
} else {
|
|
1594
1603
|
const err = errOutput.trim();
|
|
1595
1604
|
if (err.includes("not authenticated") || err.includes("login")) {
|
|
@@ -1622,6 +1631,13 @@ function extractText(event) {
|
|
|
1622
1631
|
|
|
1623
1632
|
// src/llm/codex-cli.ts
|
|
1624
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
|
+
}
|
|
1625
1641
|
async function chatCodexCli(messages, context, onChunk, onDone, onError) {
|
|
1626
1642
|
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
1627
1643
|
const userPrompt = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "Help me with this element.";
|
|
@@ -1629,24 +1645,48 @@ async function chatCodexCli(messages, context, onChunk, onDone, onError) {
|
|
|
1629
1645
|
const fullPrompt = `${SYSTEM_PROMPT}
|
|
1630
1646
|
|
|
1631
1647
|
${buildUserMessage(userPrompt, contextParts)}`;
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
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
|
+
}
|
|
1639
1673
|
let fullContent = "";
|
|
1640
1674
|
let errOutput = "";
|
|
1641
1675
|
proc.stdout.on("data", (data) => {
|
|
1642
|
-
const text = data.toString();
|
|
1643
|
-
|
|
1644
|
-
|
|
1676
|
+
const text = stripAnsi(data.toString());
|
|
1677
|
+
if (text) {
|
|
1678
|
+
fullContent += text;
|
|
1679
|
+
onChunk(text);
|
|
1680
|
+
}
|
|
1645
1681
|
});
|
|
1646
1682
|
proc.stderr.on("data", (data) => {
|
|
1647
1683
|
errOutput += data.toString();
|
|
1648
1684
|
});
|
|
1649
1685
|
proc.on("error", (err) => {
|
|
1686
|
+
try {
|
|
1687
|
+
unlinkSync2(tmpFile);
|
|
1688
|
+
} catch {
|
|
1689
|
+
}
|
|
1650
1690
|
if (err.message.includes("ENOENT")) {
|
|
1651
1691
|
onError("Codex CLI not found. Install it with: npm install -g @openai/codex");
|
|
1652
1692
|
} else {
|
|
@@ -1654,12 +1694,18 @@ ${buildUserMessage(userPrompt, contextParts)}`;
|
|
|
1654
1694
|
}
|
|
1655
1695
|
});
|
|
1656
1696
|
proc.on("close", (code) => {
|
|
1657
|
-
|
|
1697
|
+
try {
|
|
1698
|
+
unlinkSync2(tmpFile);
|
|
1699
|
+
} catch {
|
|
1700
|
+
}
|
|
1701
|
+
if (code === 0 || fullContent.trim()) {
|
|
1658
1702
|
onDone({ content: fullContent });
|
|
1659
1703
|
} else {
|
|
1660
|
-
const err = errOutput.trim();
|
|
1704
|
+
const err = stripAnsi(errOutput.trim());
|
|
1661
1705
|
if (err.includes("OPENAI_API_KEY") || err.includes("api key") || err.includes("unauthorized")) {
|
|
1662
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.");
|
|
1663
1709
|
} else {
|
|
1664
1710
|
onError(err.slice(0, 500) || `Codex CLI exited with code ${code}`);
|
|
1665
1711
|
}
|
|
@@ -2101,7 +2147,7 @@ async function handleMessage(ws, msg, state, roots) {
|
|
|
2101
2147
|
sendError(ws, "invalid_payload", "Missing pattern", msg.id);
|
|
2102
2148
|
break;
|
|
2103
2149
|
}
|
|
2104
|
-
const searchRoot = payload.path ?
|
|
2150
|
+
const searchRoot = payload.path ? join4(roots[0] || process.cwd(), payload.path) : roots[0] || process.cwd();
|
|
2105
2151
|
const results = grepFiles(payload.pattern, searchRoot, roots);
|
|
2106
2152
|
send(ws, { id: msg.id, type: "fs.grep.result", payload: { results } });
|
|
2107
2153
|
break;
|
|
@@ -2137,8 +2183,8 @@ function sendError(ws, code, message, id) {
|
|
|
2137
2183
|
}
|
|
2138
2184
|
function serveToolbarBundle(res) {
|
|
2139
2185
|
const bundlePaths = [
|
|
2140
|
-
|
|
2141
|
-
|
|
2186
|
+
join4(__dirname, "toolbar", "index.global.js"),
|
|
2187
|
+
join4(__dirname, "..", "dist", "toolbar", "index.global.js")
|
|
2142
2188
|
];
|
|
2143
2189
|
for (const bundlePath of bundlePaths) {
|
|
2144
2190
|
try {
|
|
@@ -2287,7 +2333,7 @@ function buildInjectionScript(token) {
|
|
|
2287
2333
|
// src/detect.ts
|
|
2288
2334
|
import { createConnection } from "net";
|
|
2289
2335
|
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
2290
|
-
import { join as
|
|
2336
|
+
import { join as join5, resolve as resolve2 } from "path";
|
|
2291
2337
|
import { execSync } from "child_process";
|
|
2292
2338
|
var COMMON_DEV_PORTS = [
|
|
2293
2339
|
3e3,
|
|
@@ -2444,7 +2490,7 @@ var FRAMEWORK_PATTERNS = [
|
|
|
2444
2490
|
];
|
|
2445
2491
|
var DEV_SCRIPT_NAMES = ["dev", "start", "serve", "develop", "dev:start", "start:dev", "server", "dev:server", "web", "frontend"];
|
|
2446
2492
|
function detectDevScripts(cwd = process.cwd()) {
|
|
2447
|
-
const pkgPath =
|
|
2493
|
+
const pkgPath = join5(cwd, "package.json");
|
|
2448
2494
|
if (!existsSync4(pkgPath)) return [];
|
|
2449
2495
|
let pkg;
|
|
2450
2496
|
try {
|
|
@@ -2512,7 +2558,7 @@ function checkNodeCompatibility(framework) {
|
|
|
2512
2558
|
function checkEnvPort(cwd = process.cwd()) {
|
|
2513
2559
|
const envFiles = [".env.local", ".env.development.local", ".env.development", ".env"];
|
|
2514
2560
|
for (const envFile of envFiles) {
|
|
2515
|
-
const envPath =
|
|
2561
|
+
const envPath = join5(cwd, envFile);
|
|
2516
2562
|
if (!existsSync4(envPath)) continue;
|
|
2517
2563
|
try {
|
|
2518
2564
|
const content = readFileSync4(envPath, "utf-8");
|
|
@@ -2532,7 +2578,7 @@ function scanParentLockfiles(projectDir) {
|
|
|
2532
2578
|
let dir = resolve2(project, "..");
|
|
2533
2579
|
while (dir.length > 1 && dir.length >= home.length) {
|
|
2534
2580
|
for (const lockfile of LOCKFILE_NAMES) {
|
|
2535
|
-
const p =
|
|
2581
|
+
const p = join5(dir, lockfile);
|
|
2536
2582
|
if (existsSync4(p)) found.push(p);
|
|
2537
2583
|
}
|
|
2538
2584
|
const parent = resolve2(dir, "..");
|
|
@@ -2542,7 +2588,7 @@ function scanParentLockfiles(projectDir) {
|
|
|
2542
2588
|
return found;
|
|
2543
2589
|
}
|
|
2544
2590
|
function getProjectName(cwd = process.cwd()) {
|
|
2545
|
-
const pkgPath =
|
|
2591
|
+
const pkgPath = join5(cwd, "package.json");
|
|
2546
2592
|
if (!existsSync4(pkgPath)) return "this project";
|
|
2547
2593
|
try {
|
|
2548
2594
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
@@ -2565,10 +2611,10 @@ var INSTALL_COMMANDS = {
|
|
|
2565
2611
|
bun: "bun install"
|
|
2566
2612
|
};
|
|
2567
2613
|
function checkDependenciesInstalled(cwd = process.cwd()) {
|
|
2568
|
-
const hasNodeModules = existsSync4(
|
|
2614
|
+
const hasNodeModules = existsSync4(join5(cwd, "node_modules"));
|
|
2569
2615
|
let pm = "npm";
|
|
2570
2616
|
for (const { file, pm: detectedPm } of LOCK_FILES) {
|
|
2571
|
-
if (existsSync4(
|
|
2617
|
+
if (existsSync4(join5(cwd, file))) {
|
|
2572
2618
|
pm = detectedPm;
|
|
2573
2619
|
break;
|
|
2574
2620
|
}
|
|
@@ -2865,12 +2911,49 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
2865
2911
|
});
|
|
2866
2912
|
}
|
|
2867
2913
|
});
|
|
2914
|
+
let shuttingDown = false;
|
|
2868
2915
|
const shutdown = () => {
|
|
2916
|
+
if (shuttingDown) return;
|
|
2917
|
+
shuttingDown = true;
|
|
2869
2918
|
console.log("");
|
|
2870
2919
|
console.log(chalk.dim(" Shutting down OpenMagic..."));
|
|
2871
2920
|
cleanupBackups();
|
|
2872
2921
|
proxyServer.close();
|
|
2873
|
-
|
|
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
|
+
}
|
|
2874
2957
|
};
|
|
2875
2958
|
process.on("SIGINT", shutdown);
|
|
2876
2959
|
process.on("SIGTERM", shutdown);
|
|
@@ -2879,7 +2962,7 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
2879
2962
|
const projectName = getProjectName();
|
|
2880
2963
|
const scripts = detectDevScripts();
|
|
2881
2964
|
if (scripts.length === 0) {
|
|
2882
|
-
const htmlPath =
|
|
2965
|
+
const htmlPath = join6(process.cwd(), "index.html");
|
|
2883
2966
|
if (existsSync5(htmlPath)) {
|
|
2884
2967
|
console.log(
|
|
2885
2968
|
chalk.dim(" No dev scripts found, but index.html detected.")
|
|
@@ -3144,7 +3227,7 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
3144
3227
|
);
|
|
3145
3228
|
console.log("");
|
|
3146
3229
|
try {
|
|
3147
|
-
const pkgPath =
|
|
3230
|
+
const pkgPath = join6(process.cwd(), "package.json");
|
|
3148
3231
|
if (existsSync5(pkgPath)) {
|
|
3149
3232
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
3150
3233
|
if (pkg.engines?.node) {
|