openmagic 0.11.0 → 0.13.0
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 +44 -75
- package/dist/cli.js.map +1 -1
- package/dist/toolbar/index.global.js +48 -28
- package/dist/toolbar/index.global.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -68,8 +68,9 @@ function saveConfig(updates) {
|
|
|
68
68
|
const tmpFile = CONFIG_FILE + ".tmp";
|
|
69
69
|
writeFileSync(tmpFile, JSON.stringify(merged, null, 2), { encoding: "utf-8", mode: 384 });
|
|
70
70
|
renameSync(tmpFile, CONFIG_FILE);
|
|
71
|
+
return { ok: true };
|
|
71
72
|
} catch (e) {
|
|
72
|
-
|
|
73
|
+
return { ok: false, error: e.message };
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -78,10 +79,11 @@ import {
|
|
|
78
79
|
readFileSync as readFileSync2,
|
|
79
80
|
writeFileSync as writeFileSync2,
|
|
80
81
|
existsSync as existsSync2,
|
|
81
|
-
|
|
82
|
+
lstatSync,
|
|
82
83
|
readdirSync,
|
|
83
84
|
copyFileSync,
|
|
84
|
-
mkdirSync as mkdirSync2
|
|
85
|
+
mkdirSync as mkdirSync2,
|
|
86
|
+
realpathSync
|
|
85
87
|
} from "fs";
|
|
86
88
|
import { join as join2, resolve, relative, dirname, extname } from "path";
|
|
87
89
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -117,10 +119,17 @@ var IGNORED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
117
119
|
]);
|
|
118
120
|
function isPathSafe(filePath, roots) {
|
|
119
121
|
const resolved = resolve(filePath);
|
|
122
|
+
let real;
|
|
123
|
+
try {
|
|
124
|
+
real = realpathSync(resolved);
|
|
125
|
+
} catch {
|
|
126
|
+
real = resolved;
|
|
127
|
+
}
|
|
120
128
|
return roots.some((root) => {
|
|
121
129
|
const resolvedRoot = resolve(root);
|
|
122
130
|
const rel = relative(resolvedRoot, resolved);
|
|
123
|
-
|
|
131
|
+
const realRel = relative(resolvedRoot, real);
|
|
132
|
+
return !rel.startsWith("..") && !rel.startsWith("/") && !rel.startsWith("\\") && (!realRel.startsWith("..") && !realRel.startsWith("/") && !realRel.startsWith("\\"));
|
|
124
133
|
});
|
|
125
134
|
}
|
|
126
135
|
function readFileSafe(filePath, roots) {
|
|
@@ -176,10 +185,11 @@ function listFiles(rootPath, roots, maxDepth = 4) {
|
|
|
176
185
|
const fullPath = join2(dir, item);
|
|
177
186
|
let stat;
|
|
178
187
|
try {
|
|
179
|
-
stat =
|
|
188
|
+
stat = lstatSync(fullPath);
|
|
180
189
|
} catch {
|
|
181
190
|
continue;
|
|
182
191
|
}
|
|
192
|
+
if (stat.isSymbolicLink()) continue;
|
|
183
193
|
const relPath = relative(rootPath, fullPath);
|
|
184
194
|
if (stat.isDirectory()) {
|
|
185
195
|
entries.push({ path: relPath, type: "dir", name: item });
|
|
@@ -902,6 +912,18 @@ You MUST respond with valid JSON in this exact format:
|
|
|
902
912
|
6. If the change involves multiple files, include all modifications in the array
|
|
903
913
|
7. ALWAYS respond with the JSON format above, even for explanations (put them in the "explanation" field)
|
|
904
914
|
8. If you cannot make the requested change, set modifications to an empty array and explain why`;
|
|
915
|
+
function buildContextParts(context) {
|
|
916
|
+
const parts = {};
|
|
917
|
+
if (context.selectedElement) parts.selectedElement = context.selectedElement.outerHTML;
|
|
918
|
+
if (context.files?.length) {
|
|
919
|
+
parts.filePath = context.files[0].path;
|
|
920
|
+
parts.fileContent = context.files[0].content;
|
|
921
|
+
}
|
|
922
|
+
if (context.projectTree) parts.projectTree = context.projectTree;
|
|
923
|
+
if (context.networkLogs) parts.networkLogs = context.networkLogs.map((l) => `${l.method} ${l.url} \u2192 ${l.status || "pending"}`).join("\n");
|
|
924
|
+
if (context.consoleLogs) parts.consoleLogs = context.consoleLogs.map((l) => `[${l.level}] ${l.args.join(" ")}`).join("\n");
|
|
925
|
+
return parts;
|
|
926
|
+
}
|
|
905
927
|
function buildUserMessage(userPrompt, context) {
|
|
906
928
|
const parts = [];
|
|
907
929
|
if (context.projectTree) {
|
|
@@ -957,24 +979,7 @@ async function chatOpenAICompatible(provider, model, apiKey, messages, context,
|
|
|
957
979
|
for (let i = 0; i < messages.length; i++) {
|
|
958
980
|
const msg = messages[i];
|
|
959
981
|
if (msg.role === "user" && typeof msg.content === "string" && i === lastUserIdx) {
|
|
960
|
-
const
|
|
961
|
-
if (context.selectedElement) {
|
|
962
|
-
contextParts.selectedElement = context.selectedElement.outerHTML;
|
|
963
|
-
}
|
|
964
|
-
if (context.files && context.files.length > 0) {
|
|
965
|
-
contextParts.filePath = context.files[0].path;
|
|
966
|
-
contextParts.fileContent = context.files[0].content;
|
|
967
|
-
}
|
|
968
|
-
if (context.projectTree) {
|
|
969
|
-
contextParts.projectTree = context.projectTree;
|
|
970
|
-
}
|
|
971
|
-
if (context.networkLogs) {
|
|
972
|
-
contextParts.networkLogs = context.networkLogs.map((l) => `${l.method} ${l.url} \u2192 ${l.status || "pending"}`).join("\n");
|
|
973
|
-
}
|
|
974
|
-
if (context.consoleLogs) {
|
|
975
|
-
contextParts.consoleLogs = context.consoleLogs.map((l) => `[${l.level}] ${l.args.join(" ")}`).join("\n");
|
|
976
|
-
}
|
|
977
|
-
const enrichedContent = buildUserMessage(msg.content, contextParts);
|
|
982
|
+
const enrichedContent = buildUserMessage(msg.content, buildContextParts(context));
|
|
978
983
|
const modelInfo2 = providerConfig.models.find((m) => m.id === model);
|
|
979
984
|
if (context.screenshot && modelInfo2?.vision) {
|
|
980
985
|
apiMessages.push({
|
|
@@ -1088,24 +1093,7 @@ async function chatAnthropic(model, apiKey, messages, context, onChunk, onDone,
|
|
|
1088
1093
|
const msg = messages[i];
|
|
1089
1094
|
if (msg.role === "system") continue;
|
|
1090
1095
|
if (msg.role === "user" && typeof msg.content === "string" && i === lastUserIdx) {
|
|
1091
|
-
const
|
|
1092
|
-
if (context.selectedElement) {
|
|
1093
|
-
contextParts.selectedElement = context.selectedElement.outerHTML;
|
|
1094
|
-
}
|
|
1095
|
-
if (context.files && context.files.length > 0) {
|
|
1096
|
-
contextParts.filePath = context.files[0].path;
|
|
1097
|
-
contextParts.fileContent = context.files[0].content;
|
|
1098
|
-
}
|
|
1099
|
-
if (context.projectTree) {
|
|
1100
|
-
contextParts.projectTree = context.projectTree;
|
|
1101
|
-
}
|
|
1102
|
-
if (context.networkLogs) {
|
|
1103
|
-
contextParts.networkLogs = context.networkLogs.map((l) => `${l.method} ${l.url} \u2192 ${l.status || "pending"}`).join("\n");
|
|
1104
|
-
}
|
|
1105
|
-
if (context.consoleLogs) {
|
|
1106
|
-
contextParts.consoleLogs = context.consoleLogs.map((l) => `[${l.level}] ${l.args.join(" ")}`).join("\n");
|
|
1107
|
-
}
|
|
1108
|
-
const enrichedContent = buildUserMessage(msg.content, contextParts);
|
|
1096
|
+
const enrichedContent = buildUserMessage(msg.content, buildContextParts(context));
|
|
1109
1097
|
if (context.screenshot) {
|
|
1110
1098
|
const base64Data = context.screenshot.replace(
|
|
1111
1099
|
/^data:image\/\w+;base64,/,
|
|
@@ -1218,18 +1206,7 @@ async function chatGoogle(model, apiKey, messages, context, onChunk, onDone, onE
|
|
|
1218
1206
|
if (msg.role === "system") continue;
|
|
1219
1207
|
const role = msg.role === "assistant" ? "model" : "user";
|
|
1220
1208
|
if (msg.role === "user" && typeof msg.content === "string" && i === lastUserIdx) {
|
|
1221
|
-
const
|
|
1222
|
-
if (context.selectedElement) {
|
|
1223
|
-
contextParts.selectedElement = context.selectedElement.outerHTML;
|
|
1224
|
-
}
|
|
1225
|
-
if (context.files && context.files.length > 0) {
|
|
1226
|
-
contextParts.filePath = context.files[0].path;
|
|
1227
|
-
contextParts.fileContent = context.files[0].content;
|
|
1228
|
-
}
|
|
1229
|
-
if (context.projectTree) {
|
|
1230
|
-
contextParts.projectTree = context.projectTree;
|
|
1231
|
-
}
|
|
1232
|
-
const enrichedContent = buildUserMessage(msg.content, contextParts);
|
|
1209
|
+
const enrichedContent = buildUserMessage(msg.content, buildContextParts(context));
|
|
1233
1210
|
const parts = [
|
|
1234
1211
|
{ text: enrichedContent }
|
|
1235
1212
|
];
|
|
@@ -1381,7 +1358,7 @@ async function handleLlmChat(params, onChunk, onDone, onError) {
|
|
|
1381
1358
|
}
|
|
1382
1359
|
|
|
1383
1360
|
// src/server.ts
|
|
1384
|
-
var VERSION = "0.
|
|
1361
|
+
var VERSION = "0.13.0";
|
|
1385
1362
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
1386
1363
|
function attachOpenMagic(httpServer, roots) {
|
|
1387
1364
|
function handleRequest(req, res) {
|
|
@@ -1463,7 +1440,7 @@ async function handleMessage(ws, msg, state, roots) {
|
|
|
1463
1440
|
provider: config.provider,
|
|
1464
1441
|
model: config.model,
|
|
1465
1442
|
hasApiKey: !!config.apiKey,
|
|
1466
|
-
apiKeys: config.apiKeys || {}
|
|
1443
|
+
apiKeys: Object.fromEntries(Object.entries(config.apiKeys || {}).map(([k]) => [k, true]))
|
|
1467
1444
|
}
|
|
1468
1445
|
}
|
|
1469
1446
|
});
|
|
@@ -1529,7 +1506,7 @@ async function handleMessage(ws, msg, state, roots) {
|
|
|
1529
1506
|
await handleLlmChat(
|
|
1530
1507
|
{
|
|
1531
1508
|
provider,
|
|
1532
|
-
model: payload.model || config.model || "gpt-4o",
|
|
1509
|
+
model: payload.model || config.model || MODEL_REGISTRY[provider]?.models[0]?.id || "gpt-4o",
|
|
1533
1510
|
apiKey,
|
|
1534
1511
|
messages: payload.messages,
|
|
1535
1512
|
context: payload.context
|
|
@@ -1577,12 +1554,12 @@ async function handleMessage(ws, msg, state, roots) {
|
|
|
1577
1554
|
} else if (payload.apiKey !== void 0) {
|
|
1578
1555
|
updates.apiKey = payload.apiKey;
|
|
1579
1556
|
}
|
|
1580
|
-
saveConfig(updates);
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
payload: { ok: true }
|
|
1585
|
-
}
|
|
1557
|
+
const result = saveConfig(updates);
|
|
1558
|
+
if (!result.ok) {
|
|
1559
|
+
sendError(ws, "config_error", result.error || "Failed to save", msg.id);
|
|
1560
|
+
} else {
|
|
1561
|
+
send(ws, { id: msg.id, type: "config.saved", payload: { ok: true } });
|
|
1562
|
+
}
|
|
1586
1563
|
break;
|
|
1587
1564
|
}
|
|
1588
1565
|
default:
|
|
@@ -1662,6 +1639,7 @@ function createProxyServer(targetHost, targetPort, roots) {
|
|
|
1662
1639
|
delete headers["x-content-security-policy"];
|
|
1663
1640
|
delete headers["etag"];
|
|
1664
1641
|
delete headers["last-modified"];
|
|
1642
|
+
headers["cache-control"] = "no-store";
|
|
1665
1643
|
res.writeHead(proxyRes.statusCode || 200, headers);
|
|
1666
1644
|
res.end(body);
|
|
1667
1645
|
}).catch(() => {
|
|
@@ -1728,18 +1706,7 @@ async function collectBody(stream) {
|
|
|
1728
1706
|
}
|
|
1729
1707
|
}
|
|
1730
1708
|
function buildInjectionScript(token) {
|
|
1731
|
-
return
|
|
1732
|
-
<script data-openmagic="true">
|
|
1733
|
-
(function() {
|
|
1734
|
-
if (window.__OPENMAGIC_LOADED__) return;
|
|
1735
|
-
window.__OPENMAGIC_LOADED__ = true;
|
|
1736
|
-
window.__OPENMAGIC_TOKEN__ = "${token}";
|
|
1737
|
-
var s = document.createElement("script");
|
|
1738
|
-
s.src = "/__openmagic__/toolbar.js";
|
|
1739
|
-
s.dataset.openmagic = "true";
|
|
1740
|
-
document.body.appendChild(s);
|
|
1741
|
-
})();
|
|
1742
|
-
</script>`;
|
|
1709
|
+
return `<script src="/__openmagic__/toolbar.js?v=${Date.now()}" data-openmagic="true" data-openmagic-token="${token}" defer></script>`;
|
|
1743
1710
|
}
|
|
1744
1711
|
|
|
1745
1712
|
// src/detect.ts
|
|
@@ -1765,6 +1732,8 @@ var COMMON_DEV_PORTS = [
|
|
|
1765
1732
|
// Common alternate
|
|
1766
1733
|
4e3,
|
|
1767
1734
|
// Phoenix, generic
|
|
1735
|
+
5e3,
|
|
1736
|
+
// Flask
|
|
1768
1737
|
1234,
|
|
1769
1738
|
// Parcel
|
|
1770
1739
|
4321,
|
|
@@ -1916,7 +1885,7 @@ process.on("uncaughtException", (err) => {
|
|
|
1916
1885
|
process.exit(1);
|
|
1917
1886
|
});
|
|
1918
1887
|
var childProcesses = [];
|
|
1919
|
-
var VERSION2 = "0.
|
|
1888
|
+
var VERSION2 = "0.13.0";
|
|
1920
1889
|
function ask(question) {
|
|
1921
1890
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1922
1891
|
return new Promise((resolve3) => {
|